Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (10.3 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

    
48
ENTITY_KEY = '1'
49
PRACTICALLY_INF = pow(2, 70)
50

    
51
inf = float('inf')
52

    
53
logger = logging.getLogger(__name__)
54

    
55
inf = float('inf')
56

    
57
clientkey = 'astakos'
58

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

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

    
77
def get_quota(user):
78
    c = get_client()
79
    if not c:
80
        return
81
    payload = []
82
    append = payload.append
83
    for r in user.quota.keys():
84
        append((user.uuid, r, ENTITY_KEY),)
85
    result = c.get_quota(context={}, clientkey=clientkey, get_quota=payload)
86
    logger.info('get_quota: %s rejected: %s' % (payload, result))
87
    return result
88

    
89
def create_entity(payload):
90
    c = get_client()
91
    if not c:
92
        return
93
    result = c.create_entity(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
def qh_register_user(user):
121
    return register_users([user])
122

    
123
def register_users(users):
124
    payload = list(CreateEntityPayload(
125
                    entity=u.uuid,
126
                    owner='system',
127
                    key=ENTITY_KEY,
128
                    ownerkey='') for u in users)
129
    rejected = create_entity(payload)
130
    if not rejected:
131
        payload = []
132
        append = payload.append
133
        for u in users:
134
            for resource, uplimit in u.quota.iteritems():
135
                append( SetQuotaPayload(
136
                                holder=u.uuid,
137
                                resource=resource,
138
                                key=ENTITY_KEY,
139
                                quantity=0,
140
                                capacity=uplimit if uplimit != inf else None,
141
                                import_limit=PRACTICALLY_INF,
142
                                export_limit=PRACTICALLY_INF,
143
                                flags=0))
144
        return set_quota(payload)
145

    
146

    
147
def register_resources(resources):
148
    rdata = ((r.service, r) for r in resources)
149
    services = set(r.service for r in resources)
150
    payload = list(CreateEntityPayload(
151
                    entity=service,
152
                    owner='system',
153
                    key=ENTITY_KEY,
154
                    ownerkey='') for service in set(services))
155
    rejected = create_entity(payload)
156
    if not rejected:
157
        payload = list(SetQuotaPayload(
158
                        holder=resource.service,
159
                        resource=resource,
160
                        key=ENTITY_KEY,
161
                        quantity=PRACTICALLY_INF,
162
                        capacity=0,
163
                        import_limit=PRACTICALLY_INF,
164
                        export_limit=PRACTICALLY_INF,
165
                        flags=0) for resource in resources)
166
        return set_quota(payload)
167

    
168
def qh_add_quota(serial, sub_list, add_list):
169
    if not QUOTAHOLDER_URL:
170
        return ()
171

    
172
    context = {}
173
    c = get_client()
174

    
175
    sub_quota = []
176
    sub_append = sub_quota.append
177
    add_quota = []
178
    add_append = add_quota.append
179

    
180
    for ql in sub_list:
181
        args = (ql.holder, ql.resource, ENTITY_KEY,
182
                0, ql.capacity, ql.import_limit, ql.export_limit)
183
        sub_append(args)
184

    
185
    for ql in add_list:
186
        args = (ql.holder, ql.resource, ENTITY_KEY,
187
                0, ql.capacity, ql.import_limit, ql.export_limit)
188
        add_append(args)
189

    
190
    result = c.add_quota(context=context,
191
                         clientkey=clientkey,
192
                         serial=serial,
193
                         sub_quota=sub_quota,
194
                         add_quota=add_quota)
195

    
196
    return result
197

    
198
def qh_query_serials(serials):
199
    if not QUOTAHOLDER_URL:
200
        return ()
201

    
202
    context = {}
203
    c = get_client()
204
    result = c.query_serials(context=context,
205
                             clientkey=clientkey,
206
                             serials=serials)
207
    return result
208

    
209
def qh_ack_serials(serials):
210
    if not QUOTAHOLDER_URL:
211
        return ()
212

    
213
    context = {}
214
    c = get_client()
215
    result = c.ack_serials(context=context,
216
                           clientkey=clientkey,
217
                           serials=serials)
218
    return
219

    
220
from datetime import datetime
221

    
222
strptime = datetime.strptime
223
timefmt = '%Y-%m-%dT%H:%M:%S.%f'
224

    
225
SECOND_RESOLUTION = 1
226

    
227

    
228
def total_seconds(timedelta_object):
229
    return timedelta_object.seconds + timedelta_object.days * 86400
230

    
231

    
232
def iter_timeline(timeline, before):
233
    if not timeline:
234
        return
235

    
236
    for t in timeline:
237
        yield t
238

    
239
    t = dict(t)
240
    t['issue_time'] = before
241
    yield t
242

    
243

    
244
def _usage_units(timeline, after, before, details=0):
245

    
246
    t_total = 0
247
    uu_total = 0
248
    t_after = strptime(after, timefmt)
249
    t_before = strptime(before, timefmt)
250
    t0 = t_after
251
    u0 = 0
252

    
253
    for point in iter_timeline(timeline, before):
254
        issue_time = point['issue_time']
255

    
256
        if issue_time <= after:
257
            u0 = point['target_allocated_through']
258
            continue
259

    
260
        t = strptime(issue_time, timefmt) if issue_time <= before else t_before
261
        t_diff = int(total_seconds(t - t0) * SECOND_RESOLUTION)
262
        t_total += t_diff
263
        uu_cost = u0 * t_diff
264
        uu_total += uu_cost
265
        t0 = t
266
        u0 = point['target_allocated_through']
267

    
268
        target = point['target']
269
        if details:
270
            yield  (target,
271
                    point['resource'],
272
                    point['name'],
273
                    issue_time,
274
                    uu_cost,
275
                    uu_total)
276

    
277
    if not t_total:
278
        return
279

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

    
287

    
288
def usage_units(timeline, after, before, details=0):
289
    return list(_usage_units(timeline, after, before, details=details))
290

    
291

    
292
def traffic_units(timeline, after, before, details=0):
293
    tu_total = 0
294
    target = None
295
    issue_time = None
296

    
297
    for point in timeline:
298
        issue_time = point['issue_time']
299
        if issue_time <= after:
300
            continue
301
        if issue_time > before:
302
            break
303

    
304
        target = point['target']
305
        tu = point['target_allocated_through']
306
        tu_total += tu
307

    
308
        if details:
309
            yield  (target,
310
                    point['resource'],
311
                    point['name'],
312
                    issue_time,
313
                    tu,
314
                    tu_total)
315

    
316
    if not tu_total:
317
        return
318

    
319
    yield  (target,
320
            'total',
321
            point['resource'],
322
            issue_time,
323
            tu_total // len(timeline),
324
            tu_total)
325

    
326

    
327
def timeline_charge(entity, resource, after, before, details, charge_type):
328
    key = '1'
329
    if charge_type == 'charge_usage':
330
        charge_units = usage_units
331
    elif charge_type == 'charge_traffic':
332
        charge_units = traffic_units
333
    else:
334
        m = 'charge type %s not supported' % charge_type
335
        raise ValueError(m)
336

    
337
    quotaholder = QuotaholderClient(QUOTAHOLDER_URL, token=QUOTAHOLDER_TOKEN)
338
    timeline = quotaholder.get_timeline(
339
        context={},
340
        after=after,
341
        before=before,
342
        get_timeline=[[entity, resource, key]])
343
    cu = charge_units(timeline, after, before, details=details)
344
    return cu