Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / endpoints / qh.py @ 05375e71

History | View | Annotate | Download (10.7 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 qh_get_quota(user, resources):
77
    c = get_client()
78
    if not c:
79
        return
80
    payload = []
81
    append = payload.append
82
    for r in resources:
83
        append((user.uuid, r, ENTITY_KEY),)
84
    result = c.get_quota(context={}, clientkey=clientkey, get_quota=payload)
85
    logger.info('get_quota: %s rejected: %s' % (payload, result))
86
    return result
87

    
88
def create_entity(payload):
89
    c = get_client()
90
    if not c:
91
        return
92
    result = c.create_entity(
93
        context={}, clientkey=clientkey, create_entity=payload)
94
    logger.info('create_entity: %s rejected: %s' % (payload, result))
95
    return result
96

    
97
SetQuotaPayload = namedtuple('SetQuotaPayload', ('holder',
98
                                                 'resource',
99
                                                 'key',
100
                                                 'quantity',
101
                                                 'capacity',
102
                                                 'import_limit',
103
                                                 'export_limit',
104
                                                 'flags'))
105

    
106
GetQuotaPayload = namedtuple('GetQuotaPayload', ('holder',
107
                                                 'resource',
108
                                                 'key'))
109

    
110
CreateEntityPayload = namedtuple('CreateEntityPayload', ('entity',
111
                                                         'owner',
112
                                                         'key',
113
                                                         'ownerkey'))
114
QuotaLimits = namedtuple('QuotaLimits', ('holder',
115
                                         'resource',
116
                                         'capacity',
117
                                         'import_limit',
118
                                         'export_limit'))
119

    
120
QuotaValues = namedtuple('QuotaValues', ('quantity',
121
                                         'capacity',
122
                                         'import_limit',
123
                                         'export_limit'))
124

    
125
def add_quota_values(q1, q2):
126
    return QuotaValues(
127
        quantity = q1.quantity + q2.quantity,
128
        capacity = q1.capacity + q2.capacity,
129
        import_limit = q1.import_limit + q2.import_limit,
130
        export_limit = q1.export_limit + q2.export_limit)
131

    
132
def qh_register_user(user):
133
    return register_users([user])
134

    
135
def register_users(users):
136
    rejected = create_users(users)
137
    if not rejected:
138
        register_quotas(users)
139

    
140
def create_users(users):
141
    payload = list(CreateEntityPayload(
142
                    entity=u.uuid,
143
                    owner='system',
144
                    key=ENTITY_KEY,
145
                    ownerkey='') for u in users)
146
    return create_entity(payload)
147

    
148
def register_quotas(users):
149
    payload = []
150
    append = payload.append
151
    for u in users:
152
        for resource, q in u.all_quotas().iteritems():
153
            append( SetQuotaPayload(
154
                    holder=u.uuid,
155
                    resource=resource,
156
                    key=ENTITY_KEY,
157
                    quantity=q.quantity,
158
                    capacity=q.capacity,
159
                    import_limit=q.import_limit,
160
                    export_limit=q.export_limit,
161
                    flags=0))
162
    return set_quota(payload)
163

    
164
def register_services(services):
165
    payload = list(CreateEntityPayload(
166
                    entity=service,
167
                    owner='system',
168
                    key=ENTITY_KEY,
169
                    ownerkey='') for service in set(services))
170
    return create_entity(payload)
171

    
172
def register_resources(resources):
173
    try:
174
        QH_PRACTICALLY_INFINITE
175
    except NameError:
176
        return
177

    
178
    payload = list(SetQuotaPayload(
179
            holder=resource.service,
180
            resource=resource,
181
            key=ENTITY_KEY,
182
            quantity=QH_PRACTICALLY_INFINITE,
183
            capacity=0,
184
            import_limit=QH_PRACTICALLY_INFINITE,
185
            export_limit=QH_PRACTICALLY_INFINITE,
186
            flags=0) for resource in resources)
187
    return set_quota(payload)
188

    
189
def qh_add_quota(serial, sub_list, add_list):
190
    if not QUOTAHOLDER_URL:
191
        return ()
192

    
193
    context = {}
194
    c = get_client()
195

    
196
    sub_quota = []
197
    sub_append = sub_quota.append
198
    add_quota = []
199
    add_append = add_quota.append
200

    
201
    for ql in sub_list:
202
        args = (ql.holder, ql.resource, ENTITY_KEY,
203
                0, ql.capacity, ql.import_limit, ql.export_limit)
204
        sub_append(args)
205

    
206
    for ql in add_list:
207
        args = (ql.holder, ql.resource, ENTITY_KEY,
208
                0, ql.capacity, ql.import_limit, ql.export_limit)
209
        add_append(args)
210

    
211
    result = c.add_quota(context=context,
212
                         clientkey=clientkey,
213
                         serial=serial,
214
                         sub_quota=sub_quota,
215
                         add_quota=add_quota)
216

    
217
    return result
218

    
219
def qh_query_serials(serials):
220
    if not QUOTAHOLDER_URL:
221
        return ()
222

    
223
    context = {}
224
    c = get_client()
225
    result = c.query_serials(context=context,
226
                             clientkey=clientkey,
227
                             serials=serials)
228
    return result
229

    
230
def qh_ack_serials(serials):
231
    if not QUOTAHOLDER_URL:
232
        return ()
233

    
234
    context = {}
235
    c = get_client()
236
    result = c.ack_serials(context=context,
237
                           clientkey=clientkey,
238
                           serials=serials)
239
    return
240

    
241
from datetime import datetime
242

    
243
strptime = datetime.strptime
244
timefmt = '%Y-%m-%dT%H:%M:%S.%f'
245

    
246
SECOND_RESOLUTION = 1
247

    
248

    
249
def total_seconds(timedelta_object):
250
    return timedelta_object.seconds + timedelta_object.days * 86400
251

    
252

    
253
def iter_timeline(timeline, before):
254
    if not timeline:
255
        return
256

    
257
    for t in timeline:
258
        yield t
259

    
260
    t = dict(t)
261
    t['issue_time'] = before
262
    yield t
263

    
264

    
265
def _usage_units(timeline, after, before, details=0):
266

    
267
    t_total = 0
268
    uu_total = 0
269
    t_after = strptime(after, timefmt)
270
    t_before = strptime(before, timefmt)
271
    t0 = t_after
272
    u0 = 0
273

    
274
    for point in iter_timeline(timeline, before):
275
        issue_time = point['issue_time']
276

    
277
        if issue_time <= after:
278
            u0 = point['target_allocated_through']
279
            continue
280

    
281
        t = strptime(issue_time, timefmt) if issue_time <= before else t_before
282
        t_diff = int(total_seconds(t - t0) * SECOND_RESOLUTION)
283
        t_total += t_diff
284
        uu_cost = u0 * t_diff
285
        uu_total += uu_cost
286
        t0 = t
287
        u0 = point['target_allocated_through']
288

    
289
        target = point['target']
290
        if details:
291
            yield  (target,
292
                    point['resource'],
293
                    point['name'],
294
                    issue_time,
295
                    uu_cost,
296
                    uu_total)
297

    
298
    if not t_total:
299
        return
300

    
301
    yield  (target,
302
            'total',
303
            point['resource'],
304
            issue_time,
305
            uu_total / t_total,
306
            uu_total)
307

    
308

    
309
def usage_units(timeline, after, before, details=0):
310
    return list(_usage_units(timeline, after, before, details=details))
311

    
312

    
313
def traffic_units(timeline, after, before, details=0):
314
    tu_total = 0
315
    target = None
316
    issue_time = None
317

    
318
    for point in timeline:
319
        issue_time = point['issue_time']
320
        if issue_time <= after:
321
            continue
322
        if issue_time > before:
323
            break
324

    
325
        target = point['target']
326
        tu = point['target_allocated_through']
327
        tu_total += tu
328

    
329
        if details:
330
            yield  (target,
331
                    point['resource'],
332
                    point['name'],
333
                    issue_time,
334
                    tu,
335
                    tu_total)
336

    
337
    if not tu_total:
338
        return
339

    
340
    yield  (target,
341
            'total',
342
            point['resource'],
343
            issue_time,
344
            tu_total // len(timeline),
345
            tu_total)
346

    
347

    
348
def timeline_charge(entity, resource, after, before, details, charge_type):
349
    key = '1'
350
    if charge_type == 'charge_usage':
351
        charge_units = usage_units
352
    elif charge_type == 'charge_traffic':
353
        charge_units = traffic_units
354
    else:
355
        m = 'charge type %s not supported' % charge_type
356
        raise ValueError(m)
357

    
358
    quotaholder = QuotaholderClient(QUOTAHOLDER_URL, token=QUOTAHOLDER_TOKEN)
359
    timeline = quotaholder.get_timeline(
360
        context={},
361
        after=after,
362
        before=before,
363
        get_timeline=[[entity, resource, key]])
364
    cu = charge_units(timeline, after, before, details=details)
365
    return cu