Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / quotas.py @ 68b991bc

History | View | Annotate | Download (8.4 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 astakos.im.models import (
35
    Resource, AstakosUserQuota, AstakosUser,
36
    Project, ProjectMembership, ProjectResourceGrant, ProjectApplication)
37
import astakos.quotaholder.callpoint as qh
38
from django.db.models import Q
39

    
40

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

    
49

    
50
def limits_only(holding):
51
    limit, usage_min, usage_max = holding
52
    return limit
53

    
54

    
55
def transform_data(holdings, func=None):
56
    if func is None:
57
        func = from_holding
58

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

    
69

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

    
73
    counters = qh.get_quota(holders=uuids,
74
                            resources=resources,
75
                            sources=sources)
76
    return counters
77

    
78

    
79
def get_users_quotas(users, resources=None, sources=None):
80
    counters = get_counters(users, resources, sources)
81
    quotas = transform_data(counters)
82
    return quotas
83

    
84

    
85
def get_users_quotas_and_limits(users, resources=None, sources=None):
86
    counters = get_counters(users, resources, sources)
87
    quotas = transform_data(counters)
88
    limits = transform_data(counters, limits_only)
89
    return quotas, limits
90

    
91

    
92
def get_user_quotas(user, resources=None, sources=None):
93
    quotas = get_users_quotas([user], resources, sources)
94
    return quotas[user.uuid]
95

    
96

    
97
def service_get_quotas(service, users=None):
98
    resources = Resource.objects.filter(service=service)
99
    resource_names = [r.name for r in resources]
100
    counters = qh.get_quota(holders=users, resources=resource_names)
101
    return transform_data(counters)
102

    
103

    
104
def _level_quota_dict(quotas):
105
    lst = []
106
    for holder, holder_quota in quotas.iteritems():
107
        for source, source_quota in holder_quota.iteritems():
108
            for resource, limit in source_quota.iteritems():
109
                key = (holder, source, resource)
110
                lst.append((key, limit))
111
    return lst
112

    
113

    
114
def set_user_quota(quotas):
115
    q = _level_quota_dict(quotas)
116
    qh.set_quota(q)
117

    
118

    
119
def get_default_quota():
120
    _DEFAULT_QUOTA = {}
121
    resources = Resource.objects.select_related('service').all()
122
    for resource in resources:
123
        capacity = resource.uplimit
124
        _DEFAULT_QUOTA[resource.full_name()] = capacity
125

    
126
    return _DEFAULT_QUOTA
127

    
128

    
129
SYSTEM = 'system'
130

    
131

    
132
def resolve_pending_serial(serial, accept=True):
133
    return qh.resolve_pending_commission('astakos', serial, accept)
134

    
135

    
136
def register_pending_apps(user, quantity, force=False, name=""):
137
    provision = (user.uuid, SYSTEM, 'astakos.pending_app'), quantity
138

    
139
    s = qh.issue_commission(clientkey='astakos',
140
                            force=force,
141
                            name=name,
142
                            provisions=[provision])
143
    return s
144

    
145

    
146
def initial_quotas(users):
147
    initial = {}
148
    default_quotas = get_default_quota()
149

    
150
    for user in users:
151
        uuid = user.uuid
152
        source_quota = {SYSTEM: dict(default_quotas)}
153
        initial[uuid] = source_quota
154

    
155
    objs = AstakosUserQuota.objects.select_related()
156
    orig_quotas = objs.filter(user__in=users)
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 astakos_users_quotas(users, initial=None):
174
    if initial is None:
175
        quotas = initial_quotas(users)
176
    else:
177
        quotas = copy.deepcopy(initial)
178

    
179
    ACTUALLY_ACCEPTED = ProjectMembership.ACTUALLY_ACCEPTED
180
    objs = ProjectMembership.objects.select_related('project', 'person')
181
    memberships = objs.filter(person__in=users,
182
                              state__in=ACTUALLY_ACCEPTED,
183
                              project__state=Project.APPROVED)
184

    
185
    project_ids = set(m.project_id for m in memberships)
186
    objs = ProjectApplication.objects.select_related('project')
187
    apps = objs.filter(project__in=project_ids)
188

    
189
    project_dict = {}
190
    for app in apps:
191
        project_dict[app.project] = app
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 = project_dict[membership.project]
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 = 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 astakos_user_quotas(user):
220
    quotas = astakos_users_quotas([user])
221
    try:
222
        return quotas[user.uuid]
223
    except KeyError:
224
        raise ValueError("could not compute quotas")
225

    
226

    
227
def list_user_quotas(users):
228
    qh_quotas, qh_limits = get_users_quotas_and_limits(users)
229
    astakos_initial = initial_quotas(users)
230
    astakos_quotas = astakos_users_quotas(users)
231

    
232
    diff_quotas = {}
233
    for holder, local in astakos_quotas.iteritems():
234
        registered = qh_limits.get(holder, None)
235
        if local != registered:
236
            diff_quotas[holder] = dict(local)
237

    
238
    return (qh_limits, qh_quotas,
239
            astakos_initial, diff_quotas)
240

    
241

    
242
def qh_add_resource_limit(resource, diff):
243
    objs = AstakosUser.forupdate.filter(Q(email_verified=True) &
244
                                        ~Q(policy=resource))
245
    users = objs.select_for_update()
246
    uuids = [u.uuid for u in users]
247
    qh.add_resource_limit(holders=uuids, sources=[SYSTEM],
248
                          resources=[resource.name], diff=diff)
249

    
250

    
251
def qh_sync_new_resource(resource, limit):
252
    users = AstakosUser.forupdate.filter(
253
        email_verified=True).select_for_update()
254

    
255
    resource_name = resource.name
256
    data = []
257
    for user in users:
258
        uuid = user.uuid
259
        key = uuid, SYSTEM, resource_name
260
        data.append((key, limit))
261

    
262
    qh.set_quota(data)
263

    
264

    
265
def qh_sync_users(user_ids):
266
    users = AstakosUser.forupdate.filter(id__in=user_ids).select_for_update()
267
    astakos_quotas = astakos_users_quotas(list(users))
268
    set_user_quota(astakos_quotas)
269

    
270

    
271
def qh_sync_user(user_id):
272
    qh_sync_users([user_id])