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

import logging
35
36
37
import itertools

from functools import wraps
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
38
from collections import namedtuple
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
39
40
41

from django.utils.translation import ugettext as _

42
from astakos.im.settings import (
Georgios D. Tsoukalas's avatar
fix tab    
Georgios D. Tsoukalas committed
43
        QUOTAHOLDER_URL, QUOTAHOLDER_TOKEN, LOGGING_LEVEL)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
44

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

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
48
ENTITY_KEY = '1'
49
PRACTICALLY_INF = pow(2, 70)
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
50

Olga Brani's avatar
Olga Brani committed
51
52
inf = float('inf')

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
53
54
logger = logging.getLogger(__name__)

Olga Brani's avatar
Olga Brani committed
55
inf = float('inf')
56

57
58
clientkey = 'astakos'

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

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

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

def create_entity(payload):
    c = get_client()
    if not c:
        return
    result = c.create_entity(context={}, clientkey=clientkey, create_entity=payload)
    logger.info('create_entity: %s rejected: %s' % (payload, result))
    return result
96

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
97
98
99
100
101
102
103
104
105
106
107
108
109
110
SetQuotaPayload = namedtuple('SetQuotaPayload', ('holder',
                                                 'resource',
                                                 'key',
                                                 'quantity',
                                                 'capacity',
                                                 'import_limit',
                                                 'export_limit',
                                                 'flags'))

GetQuotaPayload = namedtuple('GetQuotaPayload', ('holder',
                                                 'resource',
                                                 'key'))

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

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
def register_users(users):
    payload = list(CreateEntityPayload(
                    entity=u.uuid,
                    owner='system',
                    key=ENTITY_KEY,
                    ownerkey='') for u in users)
    rejected = create_entity(payload)
    if not rejected:
        payload = []
        append = payload.append
        for u in users:
            for resource, uplimit in u.quota.iteritems():
                append( SetQuotaPayload(
                                holder=u.uuid,
                                resource=resource,
                                key=ENTITY_KEY,
136
                                quantity=0,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
137
                                capacity=uplimit if uplimit != inf else None,
138
139
                                import_limit=PRACTICALLY_INF,
                                export_limit=PRACTICALLY_INF,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
                                flags=0))
        return set_quota(payload)


def register_resources(resources):
    rdata = ((r.service, r) for r in resources)
    services = set(r.service for r in resources)
    payload = list(CreateEntityPayload(
                    entity=service,
                    owner='system',
                    key=ENTITY_KEY,
                    ownerkey='') for service in set(services))
    rejected = create_entity(payload)
    if not rejected:
        payload = list(SetQuotaPayload(
                        holder=resource.service,
                        resource=resource,
                        key=ENTITY_KEY,
158
159
160
161
                        quantity=0,
                        capacity=0,
                        import_limit=PRACTICALLY_INF,
                        export_limit=PRACTICALLY_INF,
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
162
163
164
                        flags=0) for resource in resources)
        return set_quota(payload)

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

    context = {}
    c = get_client()
171
    
172
173
174
175
176
    sub_quota = []
    sub_append = sub_quota.append
    add_quota = []
    add_append = add_quota.append

177
    for ql in sub_list:
178
179
180
181
        args = (ql.holder, ql.resource, ENTITY_KEY,
                0, ql.capacity, ql.import_limit, ql.export_limit)
        sub_append(args)

182
    for ql in add_list:
183
184
        args = (ql.holder, ql.resource, ENTITY_KEY,
                0, ql.capacity, ql.import_limit, ql.export_limit)
185
        add_append(args)
186

187
188
189
    result = c.add_quota(context=context,
                         clientkey=clientkey,
                         serial=serial,
190
191
                         sub_quota=sub_quota,
                         add_quota=add_quota)
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215

    return result

def qh_query_serials(serials):
    if not QUOTAHOLDER_URL:
        return ()

    context = {}
    c = get_client()
    result = c.query_serials(context=context,
                             clientkey=clientkey,
                             serials=serials)
    return result

def qh_ack_serials(serials):
    if not QUOTAHOLDER_URL:
        return ()

    context = {}
    c = get_client()
    result = c.ack_serials(context=context,
                           clientkey=clientkey,
                           serials=serials)
    return
216

217
218
219
220
221
from datetime import datetime

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

222
223
SECOND_RESOLUTION = 1

Olga Brani's avatar
Olga Brani committed
224

225
226
227
def total_seconds(timedelta_object):
    return timedelta_object.seconds + timedelta_object.days * 86400

Olga Brani's avatar
Olga Brani committed
228

229
230
231
232
233
234
235
236
237
238
239
def iter_timeline(timeline, before):
    if not timeline:
        return

    for t in timeline:
        yield t

    t = dict(t)
    t['issue_time'] = before
    yield t

Olga Brani's avatar
Olga Brani committed
240

241
242
def _usage_units(timeline, after, before, details=0):

243
    t_total = 0
244
245
246
247
248
    uu_total = 0
    t_after = strptime(after, timefmt)
    t_before = strptime(before, timefmt)
    t0 = t_after
    u0 = 0
249

250
    for point in iter_timeline(timeline, before):
251
252
        issue_time = point['issue_time']

253
254
        if issue_time <= after:
            u0 = point['target_allocated_through']
255
256
            continue

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

265
        target = point['target']
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
        if details:
            yield  (target,
                    point['resource'],
                    point['name'],
                    issue_time,
                    uu_cost,
                    uu_total)

    if not t_total:
        return

    yield  (target,
            'total',
            point['resource'],
            issue_time,
Olga Brani's avatar
Olga Brani committed
281
            uu_total / t_total,
282
283
            uu_total)

Olga Brani's avatar
Olga Brani committed
284

285
286
def usage_units(timeline, after, before, details=0):
    return list(_usage_units(timeline, after, before, details=details))
287

Olga Brani's avatar
Olga Brani committed
288

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

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

        target = point['target']
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
        tu = point['target_allocated_through']
        tu_total += tu

        if details:
            yield  (target,
                    point['resource'],
                    point['name'],
                    issue_time,
                    tu,
                    tu_total)

    if not tu_total:
        return

    yield  (target,
            'total',
            point['resource'],
            issue_time,
Olga Brani's avatar
Olga Brani committed
320
            tu_total // len(timeline),
321
            tu_total)
322

Olga Brani's avatar
Olga Brani committed
323

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

334
    quotaholder = QuotaholderClient(QUOTAHOLDER_URL, token=QUOTAHOLDER_TOKEN)
335
    timeline = quotaholder.get_timeline(
Olga Brani's avatar
Olga Brani committed
336
337
338
339
        context={},
        after=after,
        before=before,
        get_timeline=[[entity, resource, key]])
340
    cu = charge_units(timeline, after, before, details=details)
341
    return cu