Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / endpoints / qh.py @ 73fbaec4

History | View | Annotate | Download (8.4 kB)

1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
import logging
35
import itertools
36

    
37
from functools import wraps
38

    
39
from django.utils.translation import ugettext as _
40

    
41
from astakos.im.settings import (
42
        QUOTAHOLDER_URL, QUOTAHOLDER_TOKEN, LOGGING_LEVEL)
43

    
44
if QUOTAHOLDER_URL:
45
    from kamaki.clients.quotaholder import QuotaholderClient
46

    
47
ENTITY_KEY = '1'
48

    
49
inf = float('inf')
50

    
51
logger = logging.getLogger(__name__)
52

    
53
inf = float('inf')
54

    
55
_client = None
56
def get_client():
57
    global _client
58
    if _client:
59
        return _client
60
    if not QUOTAHOLDER_URL:
61
        return
62
    _client = QuotaholderClient(QUOTAHOLDER_URL, token=QUOTAHOLDER_TOKEN)
63

    
64
def call(func_name):
65
    """Decorator function for Quotaholder client calls."""
66
    def decorator(payload_func):
67
        @wraps(payload_func)
68
        def wrapper(entities=(), **kwargs):
69
            if not entities:
70
                return ()
71

    
72
            if not QUOTAHOLDER_URL:
73
                return ()
74

    
75
            c = get_client() 
76
            func = c.__dict__.get(func_name)
77
            if not func:
78
                return ()
79

    
80
            data = payload_func(entities, client, **kwargs)
81
            if not data:
82
                return data
83

    
84
            funcname = func.__name__
85
            kwargs = {'context': {}, funcname: data}
86
            rejected = func(**kwargs)
87
            msg = _('%s: %s - Rejected: %s' % (funcname, data, rejected,))
88
            logger.log(LOGGING_LEVEL, msg)
89
            return rejected
90
        return wrapper
91
    return decorator
92

    
93

    
94
@call('set_quota')
95
def send_quota(users, client=None):
96
    data = []
97
    append = data.append
98
    for user in users:
99
        for resource, uplimit in user.quota.iteritems():
100
            key = ENTITY_KEY
101
            quantity = None
102
            capacity = uplimit if uplimit != inf else None
103
            import_limit = None
104
            export_limit = None
105
            flags = 0
106
            args = (
107
                user.email, resource, key, quantity, capacity, import_limit,
108
                export_limit, flags)
109
            append(args)
110
    return data
111

    
112

    
113
@call('set_quota')
114
def send_resource_quantities(resources, client=None):
115
    data = []
116
    append = data.append
117
    for resource in resources:
118
        key = ENTITY_KEY
119
        quantity = resource.meta.filter(key='quantity') or None
120
        capacity = None
121
        import_limit = None
122
        export_limit = None
123
        flags = 0
124
        args = (resource.service, resource, key, quantity, capacity,
125
                import_limit, export_limit, flags)
126
        append(args)
127
    return data
128

    
129

    
130
@call('get_quota')
131
def get_quota(users, client=None):
132
    data = []
133
    append = data.append
134
    for user in users:
135
        try:
136
            entity = user.email
137
        except AttributeError:
138
            continue
139
        else:
140
            for r in user.quota.keys():
141
                args = entity, r, ENTITY_KEY
142
                append(args)
143
    return data
144

    
145

    
146
@call('create_entity')
147
def create_entities(entities, client=None, field=''):
148
    data = []
149
    append = data.append
150
    for entity in entities:
151
        try:
152
            entity = entity.__getattribute__(field)
153
        except AttributeError:
154
            continue
155
        owner = 'system'
156
        key = ENTITY_KEY
157
        ownerkey = ''
158
        args = entity, owner, key, ownerkey
159
        append(args)
160
    return data
161

    
162

    
163
def register_users(users, client=None):
164
    users, copy = itertools.tee(users)
165
    rejected = create_entities(entities=users,
166
                                       client=client,
167
                                       field='email')
168
    created = (e for e in copy if unicode(e) not in rejected)
169
    return send_quota(created)
170

    
171

    
172
def register_resources(resources, client=None):
173
    resources, copy = itertools.tee(resources)
174
    rejected = create_entities(entities=resources,
175
                                       client=client,
176
                                       field='service')
177
    created = (e for e in copy if unicode(e) not in rejected)
178
    return send_resource_quantities(created)
179

    
180

    
181
from datetime import datetime
182

    
183
strptime = datetime.strptime
184
timefmt = '%Y-%m-%dT%H:%M:%S.%f'
185

    
186
SECOND_RESOLUTION = 1
187

    
188

    
189
def total_seconds(timedelta_object):
190
    return timedelta_object.seconds + timedelta_object.days * 86400
191

    
192

    
193
def iter_timeline(timeline, before):
194
    if not timeline:
195
        return
196

    
197
    for t in timeline:
198
        yield t
199

    
200
    t = dict(t)
201
    t['issue_time'] = before
202
    yield t
203

    
204

    
205
def _usage_units(timeline, after, before, details=0):
206

    
207
    t_total = 0
208
    uu_total = 0
209
    t_after = strptime(after, timefmt)
210
    t_before = strptime(before, timefmt)
211
    t0 = t_after
212
    u0 = 0
213

    
214
    for point in iter_timeline(timeline, before):
215
        issue_time = point['issue_time']
216

    
217
        if issue_time <= after:
218
            u0 = point['target_allocated_through']
219
            continue
220

    
221
        t = strptime(issue_time, timefmt) if issue_time <= before else t_before
222
        t_diff = int(total_seconds(t - t0) * SECOND_RESOLUTION)
223
        t_total += t_diff
224
        uu_cost = u0 * t_diff
225
        uu_total += uu_cost
226
        t0 = t
227
        u0 = point['target_allocated_through']
228

    
229
        target = point['target']
230
        if details:
231
            yield  (target,
232
                    point['resource'],
233
                    point['name'],
234
                    issue_time,
235
                    uu_cost,
236
                    uu_total)
237

    
238
    if not t_total:
239
        return
240

    
241
    yield  (target,
242
            'total',
243
            point['resource'],
244
            issue_time,
245
            uu_total / t_total,
246
            uu_total)
247

    
248

    
249
def usage_units(timeline, after, before, details=0):
250
    return list(_usage_units(timeline, after, before, details=details))
251

    
252

    
253
def traffic_units(timeline, after, before, details=0):
254
    tu_total = 0
255
    target = None
256
    issue_time = None
257

    
258
    for point in timeline:
259
        issue_time = point['issue_time']
260
        if issue_time <= after:
261
            continue
262
        if issue_time > before:
263
            break
264

    
265
        target = point['target']
266
        tu = point['target_allocated_through']
267
        tu_total += tu
268

    
269
        if details:
270
            yield  (target,
271
                    point['resource'],
272
                    point['name'],
273
                    issue_time,
274
                    tu,
275
                    tu_total)
276

    
277
    if not tu_total:
278
        return
279

    
280
    yield  (target,
281
            'total',
282
            point['resource'],
283
            issue_time,
284
            tu_total // len(timeline),
285
            tu_total)
286

    
287

    
288
def timeline_charge(entity, resource, after, before, details, charge_type):
289
    key = '1'
290
    if charge_type == 'charge_usage':
291
        charge_units = usage_units
292
    elif charge_type == 'charge_traffic':
293
        charge_units = traffic_units
294
    else:
295
        m = 'charge type %s not supported' % charge_type
296
        raise ValueError(m)
297

    
298
    quotaholder = QuotaholderClient(QUOTAHOLDER_URL, token=QUOTAHOLDER_TOKEN)
299
    timeline = quotaholder.get_timeline(
300
        context={},
301
        after=after,
302
        before=before,
303
        get_timeline=[[entity, resource, key]])
304
    cu = charge_units(timeline, after, before, details=details)
305
    return cu