Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / quotas.py @ 45112d5a

History | View | Annotate | Download (9.6 kB)

1
# Copyright 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
from synnefo.util import units
35
from astakos.im.models import (
36
    Resource, AstakosUserQuota, AstakosUser, Service,
37
    Project, ProjectMembership, ProjectResourceGrant, ProjectApplication)
38
import astakos.quotaholder_app.callpoint as qh
39
from astakos.quotaholder_app.exception import NoCapacityError
40
from django.db.models import Q
41

    
42

    
43
def from_holding(holding):
44
    limit, usage_min, usage_max = holding
45
    body = {'limit':       limit,
46
            'usage':       usage_max,
47
            'pending':     usage_max-usage_min,
48
            }
49
    return body
50

    
51

    
52
def limits_only(holding):
53
    limit, usage_min, usage_max = holding
54
    return limit
55

    
56

    
57
def transform_data(holdings, func=None):
58
    if func is None:
59
        func = from_holding
60

    
61
    quota = {}
62
    for (holder, source, resource), value in holdings.iteritems():
63
        holder_quota = quota.get(holder, {})
64
        source_quota = holder_quota.get(source, {})
65
        body = func(value)
66
        source_quota[resource] = body
67
        holder_quota[source] = source_quota
68
        quota[holder] = holder_quota
69
    return quota
70

    
71

    
72
def get_counters(users, resources=None, sources=None, flt=None):
73
    uuids = [user.uuid for user in users]
74

    
75
    counters = qh.get_quota(holders=uuids,
76
                            resources=resources,
77
                            sources=sources,
78
                            flt=flt)
79
    return counters
80

    
81

    
82
def get_users_quotas(users, resources=None, sources=None, flt=None):
83
    counters = get_counters(users, resources, sources, flt=flt)
84
    quotas = transform_data(counters)
85
    return quotas
86

    
87

    
88
def get_users_quota_limits(users, resources=None, sources=None):
89
    counters = get_counters(users, resources, sources)
90
    limits = transform_data(counters, limits_only)
91
    return limits
92

    
93

    
94
def get_user_quotas(user, resources=None, sources=None):
95
    quotas = get_users_quotas([user], resources, sources)
96
    return quotas.get(user.uuid, {})
97

    
98

    
99
def service_get_quotas(component, users=None):
100
    name_values = Service.objects.filter(
101
        component=component).values_list('name')
102
    service_names = [t for (t,) in name_values]
103
    resources = Resource.objects.filter(service_origin__in=service_names)
104
    resource_names = [r.name for r in resources]
105
    counters = qh.get_quota(holders=users, resources=resource_names)
106
    return transform_data(counters)
107

    
108

    
109
def _level_quota_dict(quotas):
110
    lst = []
111
    for holder, holder_quota in quotas.iteritems():
112
        for source, source_quota in holder_quota.iteritems():
113
            for resource, limit in source_quota.iteritems():
114
                key = (holder, source, resource)
115
                lst.append((key, limit))
116
    return lst
117

    
118

    
119
def _set_user_quota(quotas):
120
    q = _level_quota_dict(quotas)
121
    qh.set_quota(q)
122

    
123

    
124
SYSTEM = 'system'
125
PENDING_APP_RESOURCE = 'astakos.pending_app'
126

    
127

    
128
def register_pending_apps(user, quantity, force=False):
129
    provision = (user.uuid, SYSTEM, PENDING_APP_RESOURCE), quantity
130
    try:
131
        s = qh.issue_commission(clientkey='astakos',
132
                                force=force,
133
                                provisions=[provision])
134
    except NoCapacityError as e:
135
        limit = e.data['limit']
136
        return False, limit
137
    qh.resolve_pending_commission('astakos', s)
138
    return True, None
139

    
140

    
141
def get_pending_app_quota(user):
142
    quota = get_user_quotas(user)
143
    return quota[SYSTEM][PENDING_APP_RESOURCE]
144

    
145

    
146
def update_base_quota(users, resource, value):
147
    userids = [user.pk for user in users]
148
    AstakosUserQuota.objects.\
149
        filter(resource__name=resource, user__pk__in=userids).\
150
        update(capacity=value)
151
    qh_sync_locked_users(users)
152

    
153

    
154
def initial_quotas(users, flt=None):
155
    if flt is None:
156
        flt = Q()
157

    
158
    userids = [user.pk for user in users]
159
    objs = AstakosUserQuota.objects.select_related()
160
    orig_quotas = objs.filter(user__pk__in=userids).filter(flt)
161
    initial = {}
162
    for user_quota in orig_quotas:
163
        uuid = user_quota.user.uuid
164
        user_init = initial.get(uuid, {})
165
        source_quota = user_init.get(SYSTEM, {})
166
        resource = user_quota.resource.full_name()
167
        source_quota[resource] = user_quota.capacity
168
        user_init[SYSTEM] = source_quota
169
        initial[uuid] = user_init
170

    
171
    return initial
172

    
173

    
174
def get_grant_source(grant):
175
    return SYSTEM
176

    
177

    
178
def add_limits(x, y):
179
    return min(x+y, units.PRACTICALLY_INFINITE)
180

    
181

    
182
def astakos_users_quotas(users):
183
    users = list(users)
184
    quotas = initial_quotas(users)
185

    
186
    userids = [user.pk for user in users]
187
    ACTUALLY_ACCEPTED = ProjectMembership.ACTUALLY_ACCEPTED
188
    objs = ProjectMembership.objects.select_related(
189
        'project', 'person', 'project__application')
190
    memberships = objs.filter(
191
        person__pk__in=userids,
192
        state__in=ACTUALLY_ACCEPTED,
193
        project__state=Project.NORMAL,
194
        project__application__state=ProjectApplication.APPROVED)
195

    
196
    apps = set(m.project.application_id for m in memberships)
197

    
198
    objs = ProjectResourceGrant.objects.select_related()
199
    grants = objs.filter(project_application__in=apps)
200

    
201
    for membership in memberships:
202
        uuid = membership.person.uuid
203
        userquotas = quotas.get(uuid, {})
204

    
205
        application = membership.project.application
206

    
207
        for grant in grants:
208
            if grant.project_application_id != application.id:
209
                continue
210

    
211
            source = get_grant_source(grant)
212
            source_quotas = userquotas.get(source, {})
213

    
214
            resource = grant.resource.full_name()
215
            prev = source_quotas.get(resource, 0)
216
            new = add_limits(prev, grant.member_capacity)
217
            source_quotas[resource] = new
218
            userquotas[source] = source_quotas
219
        quotas[uuid] = userquotas
220

    
221
    return quotas
222

    
223

    
224
def list_user_quotas(users, qhflt=None, initflt=None):
225
    qh_quotas = get_users_quotas(users, flt=qhflt)
226
    astakos_initial = initial_quotas(users, flt=initflt)
227
    return qh_quotas, astakos_initial
228

    
229

    
230
# Syncing to quotaholder
231

    
232
def get_users_for_update(user_ids):
233
    uids = sorted(user_ids)
234
    objs = AstakosUser.objects
235
    return list(objs.filter(id__in=uids).order_by('id').select_for_update())
236

    
237

    
238
def get_user_for_update(user_id):
239
    return get_users_for_update([user_id])[0]
240

    
241

    
242
def qh_sync_locked_users(users):
243
    astakos_quotas = astakos_users_quotas(users)
244
    _set_user_quota(astakos_quotas)
245

    
246

    
247
def qh_sync_users(users):
248
    uids = [user.id for user in users]
249
    users = get_users_for_update(uids)
250
    qh_sync_locked_users(users)
251

    
252

    
253
def qh_sync_users_diffs(users, sync=True):
254
    uids = [user.id for user in users]
255
    if sync:
256
        users = get_users_for_update(uids)
257

    
258
    astakos_quotas = astakos_users_quotas(users)
259
    qh_limits = get_users_quota_limits(users)
260
    diff_quotas = {}
261
    for holder, local in astakos_quotas.iteritems():
262
        registered = qh_limits.get(holder, None)
263
        if local != registered:
264
            diff_quotas[holder] = dict(local)
265

    
266
    if sync:
267
        _set_user_quota(diff_quotas)
268
    return qh_limits, diff_quotas
269

    
270

    
271
def qh_sync_locked_user(user):
272
    qh_sync_locked_users([user])
273

    
274

    
275
def qh_sync_user(user):
276
    qh_sync_users([user])
277

    
278

    
279
def qh_sync_new_users(users):
280
    entries = []
281
    for resource in Resource.objects.all():
282
        for user in users:
283
            entries.append(
284
                AstakosUserQuota(user=user, resource=resource,
285
                                 capacity=resource.uplimit))
286
    AstakosUserQuota.objects.bulk_create(entries)
287
    qh_sync_users(users)
288

    
289

    
290
def qh_sync_new_user(user):
291
    qh_sync_new_users([user])
292

    
293

    
294
def members_to_sync(project):
295
    objs = ProjectMembership.objects.select_related('person')
296
    memberships = objs.filter(project=project,
297
                              state__in=ProjectMembership.ACTUALLY_ACCEPTED)
298
    return set(m.person for m in memberships)
299

    
300

    
301
def qh_sync_project(project):
302
    users = members_to_sync(project)
303
    qh_sync_users(users)
304

    
305

    
306
def qh_sync_new_resource(resource):
307
    users = AstakosUser.objects.filter(
308
        moderated=True, is_rejected=False).order_by('id').select_for_update()
309

    
310
    entries = []
311
    for user in users:
312
        entries.append(
313
            AstakosUserQuota(user=user, resource=resource,
314
                             capacity=resource.uplimit))
315
    AstakosUserQuota.objects.bulk_create(entries)
316
    qh_sync_users(users)