Statistics
| Branch: | Tag: | Revision:

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

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

    
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
def call(func_name):
56
    """Decorator function for Quotaholder client calls."""
57
    def decorator(payload_func):
58
        @wraps(payload_func)
59
        def wrapper(entities=(), client=None, **kwargs):
60
            if not entities:
61
                return client, ()
62

    
63
            if not QUOTAHOLDER_URL:
64
                return client, ()
65

    
66
            c = client or QuotaholderClient(QUOTAHOLDER_URL, token=QUOTAHOLDER_TOKEN)
67
            func = c.__dict__.get(func_name)
68
            if not func:
69
                return c, ()
70

    
71
            data = payload_func(entities, client, **kwargs)
72
            if not data:
73
                return c, data
74

    
75
            funcname = func.__name__
76
            kwargs = {'context': {}, funcname: data}
77
            rejected = func(**kwargs)
78
            msg = _('%s: %s - Rejected: %s' % (funcname, data, rejected,))
79
            logger.log(LOGGING_LEVEL, msg)
80
            return c, rejected
81
        return wrapper
82
    return decorator
83

    
84

    
85
@call('set_quota')
86
def send_quota(users, client=None):
87
    data = []
88
    append = data.append
89
    for user in users:
90
        for resource, uplimit in user.quota.iteritems():
91
            key = ENTITY_KEY
92
            quantity = None
93
            capacity = uplimit if uplimit != inf else None
94
            import_limit = None
95
            export_limit = None
96
            flags = 0
97
            args = (
98
                user.email, resource, key, quantity, capacity, import_limit,
99
                export_limit, flags)
100
            append(args)
101
    return data
102

    
103

    
104
@call('set_quota')
105
def send_resource_quantities(resources, client=None):
106
    data = []
107
    append = data.append
108
    for resource in resources:
109
        key = ENTITY_KEY
110
        quantity = resource.meta.filter(key='quantity') or None
111
        capacity = None
112
        import_limit = None
113
        export_limit = None
114
        flags = 0
115
        args = (resource.service, resource, key, quantity, capacity,
116
                import_limit, export_limit, flags)
117
        append(args)
118
    return data
119

    
120

    
121
@call('get_quota')
122
def get_quota(users, client=None):
123
    data = []
124
    append = data.append
125
    for user in users:
126
        try:
127
            entity = user.email
128
        except AttributeError:
129
            continue
130
        else:
131
            for r in user.quota.keys():
132
                args = entity, r, ENTITY_KEY
133
                append(args)
134
    return data
135

    
136

    
137
@call('create_entity')
138
def create_entities(entities, client=None, field=''):
139
    data = []
140
    append = data.append
141
    for entity in entities:
142
        try:
143
            entity = entity.__getattribute__(field)
144
        except AttributeError:
145
            continue
146
        owner = 'system'
147
        key = ENTITY_KEY
148
        ownerkey = ''
149
        args = entity, owner, key, ownerkey
150
        append(args)
151
    return data
152

    
153

    
154
def register_users(users, client=None):
155
    users, copy = itertools.tee(users)
156
    client, rejected = create_entities(entities=users,
157
                                       client=client,
158
                                       field='email')
159
    created = (e for e in copy if unicode(e) not in rejected)
160
    return send_quota(created, client)
161

    
162

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

    
171

    
172
from datetime import datetime
173

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

    
177
SECOND_RESOLUTION = 1
178

    
179

    
180
def total_seconds(timedelta_object):
181
    return timedelta_object.seconds + timedelta_object.days * 86400
182

    
183

    
184
def iter_timeline(timeline, before):
185
    if not timeline:
186
        return
187

    
188
    for t in timeline:
189
        yield t
190

    
191
    t = dict(t)
192
    t['issue_time'] = before
193
    yield t
194

    
195

    
196
def _usage_units(timeline, after, before, details=0):
197

    
198
    t_total = 0
199
    uu_total = 0
200
    t_after = strptime(after, timefmt)
201
    t_before = strptime(before, timefmt)
202
    t0 = t_after
203
    u0 = 0
204

    
205
    for point in iter_timeline(timeline, before):
206
        issue_time = point['issue_time']
207

    
208
        if issue_time <= after:
209
            u0 = point['target_allocated_through']
210
            continue
211

    
212
        t = strptime(issue_time, timefmt) if issue_time <= before else t_before
213
        t_diff = int(total_seconds(t - t0) * SECOND_RESOLUTION)
214
        t_total += t_diff
215
        uu_cost = u0 * t_diff
216
        uu_total += uu_cost
217
        t0 = t
218
        u0 = point['target_allocated_through']
219

    
220
        target = point['target']
221
        if details:
222
            yield  (target,
223
                    point['resource'],
224
                    point['name'],
225
                    issue_time,
226
                    uu_cost,
227
                    uu_total)
228

    
229
    if not t_total:
230
        return
231

    
232
    yield  (target,
233
            'total',
234
            point['resource'],
235
            issue_time,
236
            uu_total / t_total,
237
            uu_total)
238

    
239

    
240
def usage_units(timeline, after, before, details=0):
241
    return list(_usage_units(timeline, after, before, details=details))
242

    
243

    
244
def traffic_units(timeline, after, before, details=0):
245
    tu_total = 0
246
    target = None
247
    issue_time = None
248

    
249
    for point in timeline:
250
        issue_time = point['issue_time']
251
        if issue_time <= after:
252
            continue
253
        if issue_time > before:
254
            break
255

    
256
        target = point['target']
257
        tu = point['target_allocated_through']
258
        tu_total += tu
259

    
260
        if details:
261
            yield  (target,
262
                    point['resource'],
263
                    point['name'],
264
                    issue_time,
265
                    tu,
266
                    tu_total)
267

    
268
    if not tu_total:
269
        return
270

    
271
    yield  (target,
272
            'total',
273
            point['resource'],
274
            issue_time,
275
            tu_total // len(timeline),
276
            tu_total)
277

    
278

    
279
def timeline_charge(entity, resource, after, before, details, charge_type):
280
    key = '1'
281
    if charge_type == 'charge_usage':
282
        charge_units = usage_units
283
    elif charge_type == 'charge_traffic':
284
        charge_units = traffic_units
285
    else:
286
        m = 'charge type %s not supported' % charge_type
287
        raise ValueError(m)
288

    
289
    quotaholder = QuotaholderClient(QUOTAHOLDER_URL, token=QUOTAHOLDER_TOKEN)
290
    timeline = quotaholder.get_timeline(
291
        context={},
292
        after=after,
293
        before=before,
294
        get_timeline=[[entity, resource, key]])
295
    cu = charge_units(timeline, after, before, details=details)
296
    return cu