Statistics
| Branch: | Tag: | Revision:

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

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 socket
35
import logging
36
import itertools
37

    
38
from functools import wraps
39
from itertools import tee
40

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

    
43
from astakos.im.settings import QUOTA_HOLDER_URL, LOGGING_LEVEL
44

    
45
if QUOTA_HOLDER_URL:
46
    from quotaholder.clients.kamaki import quotaholder_client
47

    
48
ENTITY_KEY = '1'
49

    
50
inf = float('inf')
51

    
52
logger = logging.getLogger(__name__)
53

    
54
inf = float('inf')
55

    
56
def call(func_name):
57
    """Decorator function for Quotaholder client calls."""
58
    def decorator(payload_func):
59
        @wraps(payload_func)
60
        def wrapper(entities=(), client=None, **kwargs):
61
            if not entities:
62
                return client, ()
63

    
64
            if not QUOTA_HOLDER_URL:
65
                return client, ()
66

    
67
            c = client or quotaholder_client(QUOTA_HOLDER_URL)
68
            func = c.__dict__.get(func_name)
69
            if not func:
70
                return c, ()
71

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

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

    
85

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

    
104

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

    
121

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

    
137

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

    
154

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

    
163

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

    
172

    
173
from datetime import datetime
174

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

    
178
SECOND_RESOLUTION = 1
179

    
180

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

    
184

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

    
189
    for t in timeline:
190
        yield t
191

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

    
196

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

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

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

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

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

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

    
230
    if not t_total:
231
        return
232

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

    
240

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

    
244

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

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

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

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

    
269
    if not tu_total:
270
        return
271

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

    
279

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

    
290
    quotaholder = quotaholder_client(QUOTA_HOLDER_URL)
291
    timeline = quotaholder.get_timeline(
292
        context={},
293
        after=after,
294
        before=before,
295
        get_timeline=[[entity, resource, key]])
296
    cu = charge_units(timeline, after, before, details=details)
297
    return cu