Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / endpoints / qh.py @ 84a3f701

History | View | Annotate | Download (12 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
from collections import namedtuple
39

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

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

    
45
if QUOTAHOLDER_URL:
46
    from kamaki.clients.quotaholder import QuotaholderClient
47
    from kamaki.clients.quotaholder import QH_PRACTICALLY_INFINITE
48

    
49
ENTITY_KEY = '1'
50

    
51

    
52
logger = logging.getLogger(__name__)
53

    
54
inf = float('inf')
55

    
56
clientkey = 'astakos'
57

    
58
_client = None
59
def get_client():
60
    global _client
61
    if _client:
62
        return _client
63
    if not QUOTAHOLDER_URL:
64
        return
65
    _client = QuotaholderClient(QUOTAHOLDER_URL, token=QUOTAHOLDER_TOKEN)
66
    return _client
67

    
68
def set_quota(payload):
69
    c = get_client()
70
    if not c:
71
        return
72
    result = c.set_quota(context={}, clientkey=clientkey, set_quota=payload)
73
    logger.info('set_quota: %s rejected: %s' % (payload, result))
74
    return result
75

    
76
def get_entity(payload):
77
    c = get_client()
78
    if not c:
79
        return
80
    result = c.get_entity(context={}, get_entity=payload)
81
    logger.info('get_entity: %s reply: %s' % (payload, result))
82
    return result
83

    
84
def get_holding(payload):
85
    c = get_client()
86
    if not c:
87
        return
88
    result = c.get_holding(context={}, get_holding=payload)
89
    logger.info('get_holding: %s reply: %s' % (payload, result))
90
    return result
91

    
92
def qh_get_holdings(users, resources):
93
    payload = []
94
    append = payload.append
95
    for user in users:
96
        for resource in resources:
97
            append((user.uuid, resource, ENTITY_KEY),)
98
    result = get_holding(payload)
99
    return result
100

    
101
def qh_get_quota(users, resources):
102
    c = get_client()
103
    if not c:
104
        return
105
    payload = []
106
    append = payload.append
107
    for user in users:
108
        for resource in resources:
109
            append((user.uuid, resource, ENTITY_KEY),)
110

    
111
    result = c.get_quota(context={}, clientkey=clientkey, get_quota=payload)
112
    logger.info('get_quota: %s rejected: %s' % (payload, result))
113
    return result
114

    
115
def qh_get_quota_limits(users, resources):
116
    result = qh_get_quota(users, resources)
117
    return result
118

    
119
def create_entity(payload):
120
    c = get_client()
121
    if not c:
122
        return
123
    result = c.create_entity(
124
        context={}, clientkey=clientkey, create_entity=payload)
125
    logger.info('create_entity: %s rejected: %s' % (payload, result))
126
    return result
127

    
128
SetQuotaPayload = namedtuple('SetQuotaPayload', ('holder',
129
                                                 'resource',
130
                                                 'key',
131
                                                 'quantity',
132
                                                 'capacity',
133
                                                 'import_limit',
134
                                                 'export_limit',
135
                                                 'flags'))
136

    
137
GetQuotaPayload = namedtuple('GetQuotaPayload', ('holder',
138
                                                 'resource',
139
                                                 'key'))
140

    
141
CreateEntityPayload = namedtuple('CreateEntityPayload', ('entity',
142
                                                         'owner',
143
                                                         'key',
144
                                                         'ownerkey'))
145
QuotaLimits = namedtuple('QuotaLimits', ('holder',
146
                                         'resource',
147
                                         'capacity',
148
                                         'import_limit',
149
                                         'export_limit'))
150

    
151
QuotaValues = namedtuple('QuotaValues', ('quantity',
152
                                         'capacity',
153
                                         'import_limit',
154
                                         'export_limit'))
155

    
156
def add_quota_values(q1, q2):
157
    return QuotaValues(
158
        quantity = q1.quantity + q2.quantity,
159
        capacity = q1.capacity + q2.capacity,
160
        import_limit = q1.import_limit + q2.import_limit,
161
        export_limit = q1.export_limit + q2.export_limit)
162

    
163
def qh_register_user_with_quotas(user):
164
    return register_users_with_quotas([user])
165

    
166
def register_users_with_quotas(users):
167
    rejected = register_users(users)
168
    if not rejected:
169
        register_quotas(users)
170

    
171
def register_users(users):
172
    if not users:
173
        return
174

    
175
    payload = list(CreateEntityPayload(
176
                    entity=u.uuid,
177
                    owner='system',
178
                    key=ENTITY_KEY,
179
                    ownerkey='') for u in users)
180
    return create_entity(payload)
181

    
182
def register_quotas(users):
183
    if not users:
184
        return
185

    
186
    payload = []
187
    append = payload.append
188
    for u in users:
189
        for resource, q in u.all_quotas().iteritems():
190
            append( SetQuotaPayload(
191
                    holder=u.uuid,
192
                    resource=resource,
193
                    key=ENTITY_KEY,
194
                    quantity=q.quantity,
195
                    capacity=q.capacity,
196
                    import_limit=q.import_limit,
197
                    export_limit=q.export_limit,
198
                    flags=0))
199
    return set_quota(payload)
200

    
201
def register_services(services):
202
    payload = list(CreateEntityPayload(
203
                    entity=service,
204
                    owner='system',
205
                    key=ENTITY_KEY,
206
                    ownerkey='') for service in set(services))
207
    return create_entity(payload)
208

    
209
def register_resources(resources):
210
    try:
211
        QH_PRACTICALLY_INFINITE
212
    except NameError:
213
        return
214

    
215
    payload = list(SetQuotaPayload(
216
            holder=resource.service,
217
            resource=resource,
218
            key=ENTITY_KEY,
219
            quantity=QH_PRACTICALLY_INFINITE,
220
            capacity=0,
221
            import_limit=QH_PRACTICALLY_INFINITE,
222
            export_limit=QH_PRACTICALLY_INFINITE,
223
            flags=0) for resource in resources)
224
    return set_quota(payload)
225

    
226
def qh_check_users(users):
227
    payload = [(u.uuid, ENTITY_KEY) for u in users]
228
    result = get_entity(payload)
229
    uuids = [entity for (entity, owner) in result]
230

    
231
    existing = []
232
    nonexisting = []
233
    for u in users:
234
        if u.uuid in uuids:
235
            existing.append(u)
236
        else:
237
            nonexisting.append(u)
238
    return (existing, nonexisting)
239

    
240
def qh_add_quota(serial, sub_list, add_list):
241
    if not QUOTAHOLDER_URL:
242
        return ()
243

    
244
    context = {}
245
    c = get_client()
246

    
247
    sub_quota = []
248
    sub_append = sub_quota.append
249
    add_quota = []
250
    add_append = add_quota.append
251

    
252
    for ql in sub_list:
253
        args = (ql.holder, ql.resource, ENTITY_KEY,
254
                0, ql.capacity, ql.import_limit, ql.export_limit)
255
        sub_append(args)
256

    
257
    for ql in add_list:
258
        args = (ql.holder, ql.resource, ENTITY_KEY,
259
                0, ql.capacity, ql.import_limit, ql.export_limit)
260
        add_append(args)
261

    
262
    result = c.add_quota(context=context,
263
                         clientkey=clientkey,
264
                         serial=serial,
265
                         sub_quota=sub_quota,
266
                         add_quota=add_quota)
267

    
268
    return result
269

    
270
def qh_query_serials(serials):
271
    if not QUOTAHOLDER_URL:
272
        return ()
273

    
274
    context = {}
275
    c = get_client()
276
    result = c.query_serials(context=context,
277
                             clientkey=clientkey,
278
                             serials=serials)
279
    return result
280

    
281
def qh_ack_serials(serials):
282
    if not QUOTAHOLDER_URL:
283
        return ()
284

    
285
    context = {}
286
    c = get_client()
287
    result = c.ack_serials(context=context,
288
                           clientkey=clientkey,
289
                           serials=serials)
290
    return
291

    
292
from datetime import datetime
293

    
294
strptime = datetime.strptime
295
timefmt = '%Y-%m-%dT%H:%M:%S.%f'
296

    
297
SECOND_RESOLUTION = 1
298

    
299

    
300
def total_seconds(timedelta_object):
301
    return timedelta_object.seconds + timedelta_object.days * 86400
302

    
303

    
304
def iter_timeline(timeline, before):
305
    if not timeline:
306
        return
307

    
308
    for t in timeline:
309
        yield t
310

    
311
    t = dict(t)
312
    t['issue_time'] = before
313
    yield t
314

    
315

    
316
def _usage_units(timeline, after, before, details=0):
317

    
318
    t_total = 0
319
    uu_total = 0
320
    t_after = strptime(after, timefmt)
321
    t_before = strptime(before, timefmt)
322
    t0 = t_after
323
    u0 = 0
324

    
325
    for point in iter_timeline(timeline, before):
326
        issue_time = point['issue_time']
327

    
328
        if issue_time <= after:
329
            u0 = point['target_allocated_through']
330
            continue
331

    
332
        t = strptime(issue_time, timefmt) if issue_time <= before else t_before
333
        t_diff = int(total_seconds(t - t0) * SECOND_RESOLUTION)
334
        t_total += t_diff
335
        uu_cost = u0 * t_diff
336
        uu_total += uu_cost
337
        t0 = t
338
        u0 = point['target_allocated_through']
339

    
340
        target = point['target']
341
        if details:
342
            yield  (target,
343
                    point['resource'],
344
                    point['name'],
345
                    issue_time,
346
                    uu_cost,
347
                    uu_total)
348

    
349
    if not t_total:
350
        return
351

    
352
    yield  (target,
353
            'total',
354
            point['resource'],
355
            issue_time,
356
            uu_total / t_total,
357
            uu_total)
358

    
359

    
360
def usage_units(timeline, after, before, details=0):
361
    return list(_usage_units(timeline, after, before, details=details))
362

    
363

    
364
def traffic_units(timeline, after, before, details=0):
365
    tu_total = 0
366
    target = None
367
    issue_time = None
368

    
369
    for point in timeline:
370
        issue_time = point['issue_time']
371
        if issue_time <= after:
372
            continue
373
        if issue_time > before:
374
            break
375

    
376
        target = point['target']
377
        tu = point['target_allocated_through']
378
        tu_total += tu
379

    
380
        if details:
381
            yield  (target,
382
                    point['resource'],
383
                    point['name'],
384
                    issue_time,
385
                    tu,
386
                    tu_total)
387

    
388
    if not tu_total:
389
        return
390

    
391
    yield  (target,
392
            'total',
393
            point['resource'],
394
            issue_time,
395
            tu_total // len(timeline),
396
            tu_total)
397

    
398

    
399
def timeline_charge(entity, resource, after, before, details, charge_type):
400
    key = '1'
401
    if charge_type == 'charge_usage':
402
        charge_units = usage_units
403
    elif charge_type == 'charge_traffic':
404
        charge_units = traffic_units
405
    else:
406
        m = 'charge type %s not supported' % charge_type
407
        raise ValueError(m)
408

    
409
    quotaholder = QuotaholderClient(QUOTAHOLDER_URL, token=QUOTAHOLDER_TOKEN)
410
    timeline = quotaholder.get_timeline(
411
        context={},
412
        after=after,
413
        before=before,
414
        get_timeline=[[entity, resource, key]])
415
    cu = charge_units(timeline, after, before, details=details)
416
    return cu