Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / quotas.py @ f72ba65d

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

    
43

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

    
52

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

    
57

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

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

    
72

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

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

    
81

    
82
def get_users_quotas(users, resources=None, sources=None):
83
    counters = get_counters(users, resources, sources)
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(quota, capacity):
147
    quota.capacity = capacity
148
    quota.save()
149
    qh_sync_locked_user(quota.user)
150

    
151

    
152
def initial_quotas(users):
153
    userids = [user.pk for user in users]
154
    objs = AstakosUserQuota.objects.select_related()
155
    orig_quotas = objs.filter(user__pk__in=userids)
156
    initial = {}
157
    for user_quota in orig_quotas:
158
        uuid = user_quota.user.uuid
159
        user_init = initial.get(uuid, {})
160
        source_quota = user_init.get(SYSTEM, {})
161
        resource = user_quota.resource.full_name()
162
        source_quota[resource] = user_quota.capacity
163
        user_init[SYSTEM] = source_quota
164
        initial[uuid] = user_init
165

    
166
    return initial
167

    
168

    
169
def get_grant_source(grant):
170
    return SYSTEM
171

    
172

    
173
def add_limits(x, y):
174
    return min(x+y, units.PRACTICALLY_INFINITE)
175

    
176

    
177
def astakos_users_quotas(users):
178
    users = list(users)
179
    quotas = initial_quotas(users)
180

    
181
    userids = [user.pk for user in users]
182
    ACTUALLY_ACCEPTED = ProjectMembership.ACTUALLY_ACCEPTED
183
    objs = ProjectMembership.objects.select_related(
184
        'project', 'person', 'project__application')
185
    memberships = objs.filter(
186
        person__pk__in=userids,
187
        state__in=ACTUALLY_ACCEPTED,
188
        project__state=Project.NORMAL,
189
        project__application__state=ProjectApplication.APPROVED)
190

    
191
    apps = set(m.project.application_id for m in memberships)
192

    
193
    objs = ProjectResourceGrant.objects.select_related()
194
    grants = objs.filter(project_application__in=apps)
195

    
196
    for membership in memberships:
197
        uuid = membership.person.uuid
198
        userquotas = quotas.get(uuid, {})
199

    
200
        application = membership.project.application
201

    
202
        for grant in grants:
203
            if grant.project_application_id != application.id:
204
                continue
205

    
206
            source = get_grant_source(grant)
207
            source_quotas = userquotas.get(source, {})
208

    
209
            resource = grant.resource.full_name()
210
            prev = source_quotas.get(resource, 0)
211
            new = add_limits(prev, grant.member_capacity)
212
            source_quotas[resource] = new
213
            userquotas[source] = source_quotas
214
        quotas[uuid] = userquotas
215

    
216
    return quotas
217

    
218

    
219
def list_user_quotas(users):
220
    qh_quotas = get_users_quotas(users)
221
    astakos_initial = initial_quotas(users)
222
    return qh_quotas, astakos_initial
223

    
224

    
225
# Syncing to quotaholder
226

    
227
def get_users_for_update(user_ids):
228
    uids = sorted(user_ids)
229
    objs = AstakosUser.objects
230
    return list(objs.filter(id__in=uids).order_by('id').select_for_update())
231

    
232

    
233
def get_user_for_update(user_id):
234
    return get_users_for_update([user_id])[0]
235

    
236

    
237
def qh_sync_locked_users(users):
238
    astakos_quotas = astakos_users_quotas(users)
239
    _set_user_quota(astakos_quotas)
240

    
241

    
242
def qh_sync_users(users):
243
    uids = [user.id for user in users]
244
    users = get_users_for_update(uids)
245
    qh_sync_locked_users(users)
246

    
247

    
248
def qh_sync_users_diffs(users, sync=True):
249
    uids = [user.id for user in users]
250
    if sync:
251
        users = get_users_for_update(uids)
252

    
253
    astakos_quotas = astakos_users_quotas(users)
254
    qh_limits = get_users_quota_limits(users)
255
    diff_quotas = {}
256
    for holder, local in astakos_quotas.iteritems():
257
        registered = qh_limits.get(holder, None)
258
        if local != registered:
259
            diff_quotas[holder] = dict(local)
260

    
261
    if sync:
262
        _set_user_quota(diff_quotas)
263
    return qh_limits, diff_quotas
264

    
265

    
266
def qh_sync_locked_user(user):
267
    qh_sync_locked_users([user])
268

    
269

    
270
def qh_sync_user(user):
271
    qh_sync_users([user])
272

    
273

    
274
def qh_sync_new_users(users):
275
    entries = []
276
    for resource in Resource.objects.all():
277
        for user in users:
278
            entries.append(
279
                AstakosUserQuota(user=user, resource=resource,
280
                                 capacity=resource.uplimit))
281
    AstakosUserQuota.objects.bulk_create(entries)
282
    qh_sync_users(users)
283

    
284

    
285
def qh_sync_new_user(user):
286
    qh_sync_new_users([user])
287

    
288

    
289
def members_to_sync(project):
290
    objs = ProjectMembership.objects.select_related('person')
291
    memberships = objs.filter(project=project,
292
                              state__in=ProjectMembership.ACTUALLY_ACCEPTED)
293
    return set(m.person for m in memberships)
294

    
295

    
296
def qh_sync_project(project):
297
    users = members_to_sync(project)
298
    qh_sync_users(users)
299

    
300

    
301
def qh_sync_new_resource(resource):
302
    users = AstakosUser.objects.filter(
303
        moderated=True, is_rejected=False).order_by('id').select_for_update()
304

    
305
    entries = []
306
    for user in users:
307
        entries.append(
308
            AstakosUserQuota(user=user, resource=resource,
309
                             capacity=resource.uplimit))
310
    AstakosUserQuota.objects.bulk_create(entries)
311
    qh_sync_users(users)