Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (8.6 kB)

1
# Copyright 2011, 2012, 2013 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
from astakos.quotaholder.callpoint import QuotaholderDjangoDBCallpoint
46
from synnefo.util.number import strbigdec
47
from astakos.im.settings import QUOTAHOLDER_POOLSIZE
48

    
49
from astakos.quotaholder.api import QH_PRACTICALLY_INFINITE
50

    
51
logger = logging.getLogger(__name__)
52

    
53
inf = float('inf')
54

    
55
clientkey = 'astakos'
56

    
57
_client = None
58

    
59

    
60
def get_client():
61
    global _client
62
    if _client:
63
        return _client
64
    _client = QuotaholderDjangoDBCallpoint()
65
    return _client
66

    
67

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

    
78

    
79
def quotas_per_user_from_get(lst):
80
    limits = {}
81
    usage = {}
82
    for holder, resource, c, imp_min, imp_max, st_min, st_max, flags in lst:
83
        userlimits = limits.get(holder, {})
84
        userlimits[resource] = c
85
        limits[holder] = userlimits
86

    
87
        user_usage = usage.get(holder, {})
88
        user_usage[resource] = imp_max
89
        usage[holder] = user_usage
90
    return limits, usage
91

    
92

    
93
def qh_get_quota(users, resources):
94
    c = get_client()
95
    if not c:
96
        return
97
    payload = []
98
    append = payload.append
99
    for user in users:
100
        for resource in resources:
101
            append((user.uuid, str(resource)),)
102

    
103
    if payload == []:
104
        return []
105
    result = c.get_quota(context={}, get_quota=payload)
106
    logger.debug('get_quota: %s rejected: %s' % (payload, result))
107
    return result
108

    
109

    
110
def qh_get_quotas(users, resources):
111
    result = qh_get_quota(users, resources)
112
    return quotas_per_user_from_get(result)
113

    
114

    
115
SetQuotaPayload = namedtuple('SetQuotaPayload', ('holder',
116
                                                 'resource',
117
                                                 'capacity',
118
                                                 'flags'))
119

    
120
QuotaLimits = namedtuple('QuotaLimits', ('holder',
121
                                         'resource',
122
                                         'capacity',
123
                                         ))
124

    
125

    
126
def register_quotas(quotas):
127
    if not quotas:
128
        return
129

    
130
    payload = []
131
    append = payload.append
132
    for uuid, userquotas in quotas.iteritems():
133
        for resource, capacity in userquotas.iteritems():
134
            append(SetQuotaPayload(
135
                    holder=uuid,
136
                    resource=str(resource),
137
                    capacity=capacity,
138
                    flags=0))
139
    return set_quota(payload)
140

    
141

    
142
def send_quotas(userquotas):
143
    if not userquotas:
144
        return
145

    
146
    payload = []
147
    append = payload.append
148
    for holder, quotas in userquotas.iteritems():
149
        for resource, q in quotas.iteritems():
150
            append(SetQuotaPayload(
151
                    holder=holder,
152
                    resource=str(resource),
153
                    capacity=q.capacity,
154
                    flags=0))
155
    return set_quota(payload)
156

    
157

    
158
def initial_commission(resources):
159
    c = get_client()
160
    if not c:
161
        return
162

    
163
    for resource in resources:
164
        s = c.issue_commission(
165
            clientkey=clientkey,
166
            target=str(resource.service),
167
            name='initialization',
168
            provisions=[(None, str(resource), QH_PRACTICALLY_INFINITE)])
169

    
170
        c.accept_commission(clientkey=clientkey, serials=[s], reason='')
171

    
172

    
173
def register_resources(resources):
174

    
175
    payload = list(SetQuotaPayload(
176
            holder=str(resource.service),
177
            resource=str(resource),
178
            capacity=QH_PRACTICALLY_INFINITE,
179
            flags=0) for resource in resources)
180
    set_quota(payload)
181
    initial_commission(resources)
182

    
183

    
184
def qh_add_quota(sub_list, add_list):
185
    context = {}
186
    c = get_client()
187

    
188
    result = c.add_quota(context=context,
189
                         sub_quota=sub_list,
190
                         add_quota=add_list)
191

    
192
    return result
193

    
194

    
195
from datetime import datetime
196

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

    
200
SECOND_RESOLUTION = 1
201

    
202

    
203
def total_seconds(timedelta_object):
204
    return timedelta_object.seconds + timedelta_object.days * 86400
205

    
206

    
207
def iter_timeline(timeline, before):
208
    if not timeline:
209
        return
210

    
211
    for t in timeline:
212
        yield t
213

    
214
    t = dict(t)
215
    t['issue_time'] = before
216
    yield t
217

    
218

    
219
def _usage_units(timeline, after, before, details=0):
220

    
221
    t_total = 0
222
    uu_total = 0
223
    t_after = strptime(after, timefmt)
224
    t_before = strptime(before, timefmt)
225
    t0 = t_after
226
    u0 = 0
227

    
228
    for point in iter_timeline(timeline, before):
229
        issue_time = point['issue_time']
230

    
231
        if issue_time <= after:
232
            u0 = point['target_allocated_through']
233
            continue
234

    
235
        t = strptime(issue_time, timefmt) if issue_time <= before else t_before
236
        t_diff = int(total_seconds(t - t0) * SECOND_RESOLUTION)
237
        t_total += t_diff
238
        uu_cost = u0 * t_diff
239
        uu_total += uu_cost
240
        t0 = t
241
        u0 = point['target_allocated_through']
242

    
243
        target = point['target']
244
        if details:
245
            yield (target,
246
                   point['resource'],
247
                   point['name'],
248
                   issue_time,
249
                   uu_cost,
250
                   uu_total)
251

    
252
    if not t_total:
253
        return
254

    
255
    yield (target,
256
           'total',
257
           point['resource'],
258
           issue_time,
259
           uu_total / t_total,
260
           uu_total)
261

    
262

    
263
def usage_units(timeline, after, before, details=0):
264
    return list(_usage_units(timeline, after, before, details=details))
265

    
266

    
267
def traffic_units(timeline, after, before, details=0):
268
    tu_total = 0
269
    target = None
270
    issue_time = None
271

    
272
    for point in timeline:
273
        issue_time = point['issue_time']
274
        if issue_time <= after:
275
            continue
276
        if issue_time > before:
277
            break
278

    
279
        target = point['target']
280
        tu = point['target_allocated_through']
281
        tu_total += tu
282

    
283
        if details:
284
            yield (target,
285
                   point['resource'],
286
                   point['name'],
287
                   issue_time,
288
                   tu,
289
                   tu_total)
290

    
291
    if not tu_total:
292
        return
293

    
294
    yield (target,
295
           'total',
296
           point['resource'],
297
           issue_time,
298
           tu_total // len(timeline),
299
           tu_total)
300

    
301

    
302
def timeline_charge(entity, resource, after, before, details, charge_type):
303
    if charge_type == 'charge_usage':
304
        charge_units = usage_units
305
    elif charge_type == 'charge_traffic':
306
        charge_units = traffic_units
307
    else:
308
        m = 'charge type %s not supported' % charge_type
309
        raise ValueError(m)
310

    
311
    quotaholder = QuotaholderClient(QUOTAHOLDER_URL, token=QUOTAHOLDER_TOKEN,
312
                                    poolsize=QUOTAHOLDER_POOLSIZE)
313
    timeline = quotaholder.get_timeline(
314
        context={},
315
        after=after,
316
        before=before,
317
        get_timeline=[[entity, resource]])
318
    cu = charge_units(timeline, after, before, details=details)
319
    return cu