Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / endpoints / quotaholder.py @ f46c95c4

History | View | Annotate | Download (8.2 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 commissioning.clients.quotaholder import QuotaholderHTTP
47

    
48
ENTITY_KEY = '1'
49

    
50
logger = logging.getLogger(__name__)
51

    
52

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

    
61
            if not QUOTA_HOLDER_URL:
62
                return ()
63

    
64
            c = client or QuotaholderHTTP(QUOTA_HOLDER_URL)
65
            func = c.__dict__.get(func_name)
66
            if not func:
67
                return c,
68

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

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

    
82

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

    
101

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

    
118

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

    
134

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

    
151

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

    
160

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

    
169

    
170
from datetime import datetime
171

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

    
175
SECOND_RESOLUTION = 1
176

    
177

    
178
def total_seconds(timedelta_object):
179
    return timedelta_object.seconds + timedelta_object.days * 86400
180

    
181

    
182
def iter_timeline(timeline, before):
183
    if not timeline:
184
        return
185

    
186
    for t in timeline:
187
        yield t
188

    
189
    t = dict(t)
190
    t['issue_time'] = before
191
    yield t
192

    
193

    
194
def _usage_units(timeline, after, before, details=0):
195

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

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

    
206
        if issue_time <= after:
207
            u0 = point['target_allocated_through']
208
            continue
209

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

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

    
227
    if not t_total:
228
        return
229

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

    
237

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

    
241

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

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

    
254
        target = point['target']
255
        tu = point['target_allocated_through']
256
        tu_total += tu
257

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

    
266
    if not tu_total:
267
        return
268

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

    
276

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

    
287
    quotaholder = QuotaholderHTTP(QUOTA_HOLDER_URL)
288
    timeline = quotaholder.get_timeline(
289
        context={},
290
        after=after,
291
        before=before,
292
        get_timeline=[[entity, resource, key]])
293
    cu = charge_units(timeline, after, before, details=details)
294
    return cu