Revision a53ec93b
b/snf-astakos-app/astakos/im/functions.py | ||
---|---|---|
448 | 448 |
accept_membership_checks(membership, request_user) |
449 | 449 |
user = membership.person |
450 | 450 |
membership.perform_action("accept", actor=request_user, reason=reason) |
451 |
quotas.qh_sync_user(user)
|
|
451 |
quotas.qh_sync_membership(membership)
|
|
452 | 452 |
logger.info("User %s has been accepted in %s." % |
453 | 453 |
(user.log_display, project)) |
454 | 454 |
|
... | ... | |
519 | 519 |
remove_membership_checks(membership, request_user) |
520 | 520 |
user = membership.person |
521 | 521 |
membership.perform_action("remove", actor=request_user, reason=reason) |
522 |
quotas.qh_sync_user(user)
|
|
522 |
quotas.qh_sync_membership(membership)
|
|
523 | 523 |
logger.info("User %s has been removed from %s." % |
524 | 524 |
(user.log_display, project)) |
525 | 525 |
|
... | ... | |
552 | 552 |
membership = new_membership(project, user, actor=request_user, |
553 | 553 |
enroll=True) |
554 | 554 |
|
555 |
quotas.qh_sync_user(user)
|
|
555 |
quotas.qh_sync_membership(membership)
|
|
556 | 556 |
logger.info("User %s has been enrolled in %s." % |
557 | 557 |
(membership.person.log_display, project)) |
558 | 558 |
|
... | ... | |
595 | 595 |
leave_policy = project.member_leave_policy |
596 | 596 |
if leave_policy == AUTO_ACCEPT_POLICY: |
597 | 597 |
membership.perform_action("remove", actor=request_user, reason=reason) |
598 |
quotas.qh_sync_user(request_user)
|
|
598 |
quotas.qh_sync_membership(membership)
|
|
599 | 599 |
logger.info("User %s has left %s." % |
600 | 600 |
(request_user.log_display, project)) |
601 | 601 |
auto_accepted = True |
... | ... | |
661 | 661 |
if (join_policy == AUTO_ACCEPT_POLICY and ( |
662 | 662 |
not project.violates_members_limit(adding=1))): |
663 | 663 |
membership.perform_action("accept", actor=request_user, reason=reason) |
664 |
quotas.qh_sync_user(request_user)
|
|
664 |
quotas.qh_sync_membership(membership)
|
|
665 | 665 |
logger.info("User %s joined %s." % |
666 | 666 |
(request_user.log_display, project)) |
667 | 667 |
else: |
... | ... | |
945 | 945 |
if application.name: |
946 | 946 |
check_conflicting_projects(project, application.name) |
947 | 947 |
|
948 |
# Pre-lock members and owner together in order to impose an ordering |
|
949 |
# on locking users |
|
950 |
members = quotas.members_to_sync(project) |
|
951 |
uids_to_sync = [member.id for member in members] |
|
952 |
applicant = application.applicant |
|
953 |
uids_to_sync.append(applicant.id) |
|
954 |
quotas.get_users_for_update(uids_to_sync) |
|
955 |
|
|
956 |
qh_release_pending_app(applicant, locked=True) |
|
948 |
qh_release_pending_app(application.applicant) |
|
957 | 949 |
application.approve(actor=request_user, reason=reason) |
958 | 950 |
|
959 | 951 |
if project.state == Project.UNINITIALIZED: |
... | ... | |
962 | 954 |
_apply_modifications(project, application) |
963 | 955 |
project.activate(actor=request_user, reason=reason) |
964 | 956 |
|
965 |
quotas.qh_sync_locked_users(members)
|
|
957 |
quotas.qh_sync_project(project)
|
|
966 | 958 |
logger.info("%s has been approved." % (application.log_display)) |
967 | 959 |
project_notif.application_notify(application, "approve") |
968 | 960 |
return project |
... | ... | |
1122 | 1114 |
return diff |
1123 | 1115 |
|
1124 | 1116 |
|
1125 |
def qh_add_pending_app(user, project=None, force=False): |
|
1126 |
user = AstakosUser.objects.select_for_update().get(id=user.id) |
|
1117 |
def qh_add_pending_app(user, project=None, force=False, assign_project=None): |
|
1118 |
if assign_project is None: |
|
1119 |
assign_project = user.base_project |
|
1127 | 1120 |
diff = get_pending_app_diff(project) |
1128 |
return quotas.register_pending_apps(user, diff, force) |
|
1121 |
return quotas.register_pending_apps(user, assign_project, |
|
1122 |
diff, force=force) |
|
1129 | 1123 |
|
1130 | 1124 |
|
1131 | 1125 |
def check_pending_app_quota(user, project=None): |
... | ... | |
1138 | 1132 |
return True, None |
1139 | 1133 |
|
1140 | 1134 |
|
1141 |
def qh_release_pending_app(user, locked=False): |
|
1142 |
if not locked: |
|
1143 |
user = AstakosUser.objects.select_for_update().get(id=user.id) |
|
1144 |
quotas.register_pending_apps(user, -1) |
|
1135 |
def qh_release_pending_app(user, assign_project=None): |
|
1136 |
if assign_project is None: |
|
1137 |
assign_project = user.base_project |
|
1138 |
quotas.register_pending_apps(user, assign_project, -1) |
b/snf-astakos-app/astakos/im/management/commands/_common.py | ||
---|---|---|
197 | 197 |
return units.show(number, unit, style) |
198 | 198 |
|
199 | 199 |
|
200 |
def collect_holder_quotas(holder_quotas, h_initial, style=None):
|
|
200 |
def collect_holder_quotas(holder_quotas, style=None): |
|
201 | 201 |
print_data = [] |
202 | 202 |
for source, source_quotas in holder_quotas.iteritems(): |
203 |
try: |
|
204 |
s_initial = h_initial[source] |
|
205 |
except KeyError: |
|
206 |
continue |
|
207 | 203 |
for resource, values in source_quotas.iteritems(): |
208 |
try: |
|
209 |
initial = s_initial[resource] |
|
210 |
except KeyError: |
|
211 |
continue |
|
212 |
initial = show_resource_value(initial, resource, style) |
|
213 | 204 |
limit = show_resource_value(values['limit'], resource, style) |
214 | 205 |
usage = show_resource_value(values['usage'], resource, style) |
215 |
fields = (source, resource, initial, limit, usage)
|
|
206 |
fields = (source, resource, limit, usage) |
|
216 | 207 |
print_data.append(fields) |
217 | 208 |
return print_data |
218 | 209 |
|
219 | 210 |
|
220 |
def show_user_quotas(holder_quotas, h_initial, style=None):
|
|
221 |
labels = ('source', 'resource', 'base_quota', 'total_quota', 'usage')
|
|
222 |
print_data = collect_holder_quotas(holder_quotas, h_initial, style=style)
|
|
211 |
def show_user_quotas(holder_quotas, style=None): |
|
212 |
labels = ('source', 'resource', 'limit', 'usage')
|
|
213 |
print_data = collect_holder_quotas(holder_quotas, style=style) |
|
223 | 214 |
return print_data, labels |
224 | 215 |
|
225 | 216 |
|
226 |
def show_quotas(qh_quotas, astakos_initial, info=None, style=None): |
|
227 |
labels = ('user', 'source', 'resource', 'base_quota', 'total_quota', |
|
228 |
'usage') |
|
217 |
def show_quotas(qh_quotas, info=None, style=None): |
|
218 |
labels = ('holder', 'source', 'resource', 'limit', 'usage') |
|
229 | 219 |
if info is not None: |
230 | 220 |
labels = ('displayname',) + labels |
231 | 221 |
|
232 | 222 |
print_data = [] |
233 | 223 |
for holder, holder_quotas in qh_quotas.iteritems(): |
234 |
h_initial = astakos_initial.get(holder) |
|
235 |
if h_initial is None: |
|
236 |
continue |
|
237 |
|
|
238 | 224 |
if info is not None: |
239 | 225 |
email = info.get(holder, "") |
240 | 226 |
|
241 |
h_data = collect_holder_quotas(holder_quotas, h_initial, style=style)
|
|
227 |
h_data = collect_holder_quotas(holder_quotas, style=style) |
|
242 | 228 |
if info is not None: |
243 | 229 |
h_data = [(email, holder) + fields for fields in h_data] |
244 | 230 |
else: |
b/snf-astakos-app/astakos/im/management/commands/project-show.py | ||
---|---|---|
38 | 38 |
from snf_django.management.commands import SynnefoCommand |
39 | 39 |
from snf_django.management import utils |
40 | 40 |
from astakos.im.models import ProjectApplication, Project |
41 |
from astakos.im import quotas |
|
41 | 42 |
from ._common import show_resource_value, style_options, check_style |
42 | 43 |
from synnefo.util import units |
43 | 44 |
|
... | ... | |
60 | 61 |
default=False, |
61 | 62 |
help=("Show a list of project memberships") |
62 | 63 |
), |
64 |
make_option('--quota', |
|
65 |
action='store_true', |
|
66 |
dest='list_quotas', |
|
67 |
default=False, |
|
68 |
help="List project quota"), |
|
63 | 69 |
make_option('--unit-style', |
64 | 70 |
default='mb', |
65 | 71 |
help=("Specify display unit for resource values " |
... | ... | |
81 | 87 |
id_ = args[0] |
82 | 88 |
if True: |
83 | 89 |
project = get_chain_state(id_) |
84 |
self.print_project(project) |
|
90 |
self.print_project(project, show_quota)
|
|
85 | 91 |
if show_members and project is not None: |
86 | 92 |
self.stdout.write("\n") |
87 | 93 |
fields, labels = members_fields(project) |
... | ... | |
105 | 111 |
self.pprint_dict(app_info) |
106 | 112 |
self.print_app_resources(app) |
107 | 113 |
|
108 |
def print_project(self, project): |
|
114 |
def print_project(self, project, show_quota=False):
|
|
109 | 115 |
self.pprint_dict(project_fields(project)) |
110 |
self.print_resources(project) |
|
116 |
quota = (quotas.get_project_quota(project) |
|
117 |
if show_quota else None) |
|
118 |
self.print_resources(project, quota=quota) |
|
111 | 119 |
|
112 |
def print_resources(self, project): |
|
120 |
def print_resources(self, project, quota=None):
|
|
113 | 121 |
policies = project.projectresourcequota_set.all() |
114 |
fields, labels = resource_fields(policies, self.unit_style) |
|
122 |
fields, labels = resource_fields(policies, quota, self.unit_style)
|
|
115 | 123 |
if fields: |
116 | 124 |
self.stdout.write("\n") |
117 | 125 |
self.pprint_table(fields, labels, title="Resource limits") |
... | ... | |
131 | 139 |
raise CommandError("Project with id %s not found." % project_id) |
132 | 140 |
|
133 | 141 |
|
134 |
def resource_fields(policies, style): |
|
135 |
labels = ('name', 'description', 'max per member') |
|
142 |
def resource_fields(policies, quota, style): |
|
143 |
labels = ('name', 'max per member', 'max per project') |
|
144 |
if quota: |
|
145 |
labels += ('usage',) |
|
136 | 146 |
collect = [] |
137 | 147 |
for policy in policies: |
138 | 148 |
name = policy.resource.name |
139 |
desc = policy.resource.desc |
|
140 | 149 |
capacity = policy.member_capacity |
141 |
collect.append((name, desc, |
|
142 |
show_resource_value(capacity, name, style))) |
|
150 |
p_capacity = policy.project_capacity |
|
151 |
row = (name, |
|
152 |
show_resource_value(capacity, name, style), |
|
153 |
show_resource_value(p_capacity, name, style)) |
|
154 |
if quota: |
|
155 |
r_quota = quota.get(name) |
|
156 |
usage = r_quota.get('project_usage') |
|
157 |
row += (show_resource_value(usage, name, style),) |
|
158 |
collect.append(row) |
|
143 | 159 |
return collect, labels |
144 | 160 |
|
145 | 161 |
|
b/snf-astakos-app/astakos/im/management/commands/quota-list.py | ||
---|---|---|
58 | 58 |
make_option('--overlimit', |
59 | 59 |
action='store_true', |
60 | 60 |
help="Show quota that is over limit"), |
61 |
make_option('--with-custom', |
|
62 |
metavar='True|False', |
|
63 |
help=("Filter quota different from the default or " |
|
64 |
"equal to it")), |
|
65 | 61 |
make_option('--filter-by', |
66 | 62 |
help="Filter by field; " |
67 | 63 |
"e.g. \"user=uuid,usage>=10M,base_quota<inf\""), |
... | ... | |
71 | 67 |
) |
72 | 68 |
|
73 | 69 |
QHFLT = { |
74 |
"total_quota": ("limit", filtering.parse_with_unit),
|
|
70 |
"limit": ("limit", filtering.parse_with_unit),
|
|
75 | 71 |
"usage": ("usage_max", filtering.parse_with_unit), |
76 | 72 |
"user": ("holder", lambda x: x), |
77 | 73 |
"resource": ("resource", lambda x: x), |
78 | 74 |
"source": ("source", lambda x: x), |
79 | 75 |
} |
80 | 76 |
|
81 |
INITFLT = { |
|
82 |
"base_quota": ("capacity", filtering.parse_with_unit), |
|
83 |
} |
|
84 |
|
|
85 | 77 |
@transaction.commit_on_success |
86 | 78 |
def handle(self, *args, **options): |
87 | 79 |
output_format = options["output_format"] |
... | ... | |
95 | 87 |
else: |
96 | 88 |
filters = [] |
97 | 89 |
|
98 |
QHQ, INITQ = Q(), Q()
|
|
90 |
QHQ = Q()
|
|
99 | 91 |
for flt in filters: |
100 | 92 |
q = filtering.make_query(flt, self.QHFLT) |
101 | 93 |
if q is not None: |
102 | 94 |
QHQ &= q |
103 |
q = filtering.make_query(flt, self.INITFLT) |
|
104 |
if q is not None: |
|
105 |
INITQ &= q |
|
106 | 95 |
|
107 | 96 |
overlimit = bool(options["overlimit"]) |
108 | 97 |
if overlimit: |
109 | 98 |
QHQ &= Q(usage_max__gt=F("limit")) |
110 | 99 |
|
111 |
with_custom = options["with_custom"] |
|
112 |
if with_custom is not None: |
|
113 |
qeq = Q(capacity=F("resource__uplimit")) |
|
114 |
try: |
|
115 |
INITQ &= ~qeq if utils.parse_bool(with_custom) else qeq |
|
116 |
except ValueError as e: |
|
117 |
raise CommandError(e) |
|
118 |
|
|
119 | 100 |
users = AstakosUser.objects.accepted() |
120 |
qh_quotas, astakos_i = list_user_quotas(
|
|
121 |
users, qhflt=QHQ, initflt=INITQ)
|
|
101 |
qh_quotas = list_user_quotas( |
|
102 |
users, qhflt=QHQ) |
|
122 | 103 |
|
123 | 104 |
if displayname: |
124 | 105 |
info = {} |
... | ... | |
128 | 109 |
info = None |
129 | 110 |
|
130 | 111 |
print_data, labels = common.show_quotas( |
131 |
qh_quotas, astakos_i, info, style=unit_style)
|
|
112 |
qh_quotas, info, style=unit_style) |
|
132 | 113 |
utils.pprint_table(self.stdout, print_data, labels, output_format) |
b/snf-astakos-app/astakos/im/management/commands/user-show.py | ||
---|---|---|
36 | 36 |
|
37 | 37 |
from django.db.models import Q |
38 | 38 |
from astakos.im.models import AstakosUser, get_latest_terms, Project |
39 |
from astakos.im.quotas import list_user_quotas
|
|
39 |
from astakos.im.quotas import get_user_quotas
|
|
40 | 40 |
|
41 | 41 |
from synnefo.lib.ordereddict import OrderedDict |
42 | 42 |
from snf_django.management.commands import SynnefoCommand |
... | ... | |
127 | 127 |
unit_style = options["unit_style"] |
128 | 128 |
check_style(unit_style) |
129 | 129 |
|
130 |
quotas, initial = list_user_quotas([user]) |
|
131 |
h_quotas = quotas[user.uuid] |
|
132 |
h_initial = initial[user.uuid] |
|
130 |
quotas = get_user_quotas(user) |
|
133 | 131 |
if quotas: |
134 | 132 |
self.stdout.write("\n") |
135 |
print_data, labels = show_user_quotas(h_quotas, h_initial,
|
|
133 |
print_data, labels = show_user_quotas(quotas,
|
|
136 | 134 |
style=unit_style) |
137 | 135 |
utils.pprint_table(self.stdout, print_data, labels, |
138 | 136 |
options["output_format"], |
b/snf-astakos-app/astakos/im/models.py | ||
---|---|---|
1850 | 1850 |
q = self.model.Q_ACCEPTED_STATES |
1851 | 1851 |
return self.filter(q) |
1852 | 1852 |
|
1853 |
def actually_accepted(self): |
|
1853 |
def actually_accepted(self, projects=None):
|
|
1854 | 1854 |
q = self.model.Q_ACTUALLY_ACCEPTED |
1855 |
if projects is not None: |
|
1856 |
q &= Q(project__in=projects) |
|
1855 | 1857 |
return self.filter(q) |
1856 | 1858 |
|
1857 | 1859 |
def initialized(self, projects=None): |
b/snf-astakos-app/astakos/im/quotas.py | ||
---|---|---|
31 | 31 |
# interpreted as representing official policies, either expressed |
32 | 32 |
# or implied, of GRNET S.A. |
33 | 33 |
|
34 |
from synnefo.util import units |
|
35 | 34 |
from astakos.im.models import ( |
36 | 35 |
Resource, AstakosUserQuota, AstakosUser, Service, |
37 |
Project, ProjectMembership, ProjectResourceQuota, ProjectApplication)
|
|
36 |
Project, ProjectMembership, ProjectResourceQuota) |
|
38 | 37 |
import astakos.quotaholder_app.callpoint as qh |
39 | 38 |
from astakos.quotaholder_app.exception import NoCapacityError |
40 | 39 |
from django.db.models import Q |
40 |
from synnefo.util.keypath import set_path |
|
41 | 41 |
|
42 | 42 |
|
43 |
def from_holding(holding): |
|
43 |
PROJECT_TAG = "project:" |
|
44 |
USER_TAG = "user:" |
|
45 |
|
|
46 |
def project_ref(value): |
|
47 |
return PROJECT_TAG + value |
|
48 |
|
|
49 |
|
|
50 |
def get_project_ref(project): |
|
51 |
return project_ref(project.uuid) |
|
52 |
|
|
53 |
|
|
54 |
def user_ref(value): |
|
55 |
return USER_TAG + value |
|
56 |
|
|
57 |
|
|
58 |
def get_user_ref(user): |
|
59 |
return user_ref(user.uuid) |
|
60 |
|
|
61 |
|
|
62 |
def from_holding(holding, is_project=False): |
|
44 | 63 |
limit, usage_min, usage_max = holding |
45 |
body = {'limit': limit, |
|
46 |
'usage': usage_max, |
|
47 |
'pending': usage_max-usage_min, |
|
64 |
prefix = 'project_' if is_project else '' |
|
65 |
body = {prefix+'limit': limit, |
|
66 |
prefix+'usage': usage_max, |
|
67 |
prefix+'pending': usage_max-usage_min, |
|
48 | 68 |
} |
49 | 69 |
return body |
50 | 70 |
|
51 | 71 |
|
52 |
def limits_only(holding): |
|
53 |
limit, usage_min, usage_max = holding |
|
54 |
return limit |
|
72 |
def get_user_counters(users, resources=None, sources=None, flt=None): |
|
73 |
holders = [get_user_ref(user) for user in users] |
|
74 |
return qh.get_quota(holders=holders, |
|
75 |
resources=resources, |
|
76 |
sources=sources, |
|
77 |
flt=flt) |
|
55 | 78 |
|
56 | 79 |
|
57 |
def transform_data(holdings, func=None): |
|
58 |
if func is None: |
|
59 |
func = from_holding |
|
80 |
def get_project_counters(projects, resources=None, sources=None): |
|
81 |
holders = [get_project_ref(project) for project in projects] |
|
82 |
return qh.get_quota(holders=holders, |
|
83 |
resources=resources, |
|
84 |
sources=sources) |
|
60 | 85 |
|
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 | 86 |
|
87 |
def strip_names(counters): |
|
88 |
stripped = {} |
|
89 |
for ((holder, source, resource), value) in counters.iteritems(): |
|
90 |
prefix, sep, holder = holder.partition(":") |
|
91 |
assert prefix in ["user", "project"] |
|
92 |
if source is not None: |
|
93 |
prefix, sep, source = source.partition(":") |
|
94 |
assert prefix == "project" |
|
95 |
stripped[(holder, source, resource)] = value |
|
96 |
return stripped |
|
71 | 97 |
|
72 |
def get_counters(users, resources=None, sources=None, flt=None): |
|
73 |
uuids = [user.uuid for user in users] |
|
74 | 98 |
|
75 |
counters = qh.get_quota(holders=uuids,
|
|
76 |
resources=resources,
|
|
77 |
sources=sources,
|
|
78 |
flt=flt)
|
|
79 |
return counters
|
|
99 |
def get_related_sources(counters):
|
|
100 |
projects = set()
|
|
101 |
for (holder, source, resource) in counters.iterkeys():
|
|
102 |
projects.add(source)
|
|
103 |
return list(projects)
|
|
80 | 104 |
|
81 | 105 |
|
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 |
|
106 |
def mk_quota_dict(users_counters, project_counters): |
|
107 |
quota = {} |
|
108 |
for (holder, source, resource), u_value in users_counters.iteritems(): |
|
109 |
p_value = project_counters[(source, None, resource)] |
|
110 |
values_dict = from_holding(u_value) |
|
111 |
values_dict.update(from_holding(p_value, is_project=True)) |
|
112 |
set_path(quota, [holder, source, resource], values_dict, |
|
113 |
createpath=True) |
|
114 |
return quota |
|
115 |
|
|
86 | 116 |
|
117 |
def get_users_quotas_counters(users, resources=None, sources=None, flt=None): |
|
118 |
user_counters = get_user_counters(users, resources, sources, flt=flt) |
|
119 |
projects = get_related_sources(user_counters) |
|
120 |
project_counters = qh.get_quota(holders=projects, resources=resources) |
|
121 |
return strip_names(user_counters), strip_names(project_counters) |
|
87 | 122 |
|
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
|
|
123 |
|
|
124 |
def get_users_quotas(users, resources=None, sources=None, flt=None):
|
|
125 |
u_c, p_c = get_users_quotas_counters(users, resources, sources, flt=flt)
|
|
126 |
return mk_quota_dict(u_c, p_c)
|
|
92 | 127 |
|
93 | 128 |
|
94 | 129 |
def get_user_quotas(user, resources=None, sources=None): |
... | ... | |
102 | 137 |
service_names = [t for (t,) in name_values] |
103 | 138 |
resources = Resource.objects.filter(service_origin__in=service_names) |
104 | 139 |
resource_names = [r.name for r in resources] |
105 |
counters = qh.get_quota(holders=users, resources=resource_names) |
|
106 |
return transform_data(counters) |
|
140 |
astakosusers = AstakosUser.objects.verified() |
|
141 |
if users is not None: |
|
142 |
astakosusers = astakosusers.filter(uuid__in=users) |
|
143 |
return get_users_quotas(astakosusers, resources=resource_names) |
|
144 |
|
|
145 |
|
|
146 |
def mk_limits_dict(counters): |
|
147 |
quota = {} |
|
148 |
for key, (limit, _, _) in counters.iteritems(): |
|
149 |
path = list(key) |
|
150 |
set_path(quota, path, limit, createpath=True) |
|
151 |
return quota |
|
152 |
|
|
153 |
|
|
154 |
def mk_project_quota_dict(project_counters): |
|
155 |
quota = {} |
|
156 |
for (holder, _, resource), p_value in project_counters.iteritems(): |
|
157 |
values_dict = from_holding(p_value, is_project=True) |
|
158 |
set_path(quota, [holder, resource], values_dict, |
|
159 |
createpath=True) |
|
160 |
return quota |
|
161 |
|
|
162 |
|
|
163 |
def get_projects_quota(projects, resources=None, sources=None): |
|
164 |
project_counters = get_project_counters(projects, resources, sources) |
|
165 |
return mk_project_quota_dict(strip_names(project_counters)) |
|
166 |
|
|
167 |
|
|
168 |
def get_project_quota(project, resources=None, sources=None): |
|
169 |
quotas = get_projects_quota([project], resources, sources) |
|
170 |
return quotas.get(project.uuid, {}) |
|
171 |
|
|
172 |
|
|
173 |
def get_projects_quota_limits(): |
|
174 |
project_counters = qh.get_quota(flt=Q(holder__startswith=PROJECT_TAG)) |
|
175 |
user_counters = qh.get_quota(flt=Q(holder__startswith=USER_TAG)) |
|
176 |
return mk_limits_dict(project_counters), mk_limits_dict(user_counters) |
|
107 | 177 |
|
108 | 178 |
|
109 | 179 |
def _level_quota_dict(quotas): |
... | ... | |
116 | 186 |
return lst |
117 | 187 |
|
118 | 188 |
|
119 |
def _set_user_quota(quotas, resource=None):
|
|
189 |
def set_quota(quotas, resource=None):
|
|
120 | 190 |
q = _level_quota_dict(quotas) |
121 | 191 |
qh.set_quota(q, resource=resource) |
122 | 192 |
|
123 | 193 |
|
124 |
SYSTEM = 'system' |
|
125 | 194 |
PENDING_APP_RESOURCE = 'astakos.pending_app' |
126 | 195 |
|
127 | 196 |
|
128 |
def register_pending_apps(user, quantity, force=False): |
|
129 |
provision = (user.uuid, SYSTEM, PENDING_APP_RESOURCE), quantity |
|
197 |
def mk_user_provision(user, source, resource, quantity): |
|
198 |
holder = user_ref(user) |
|
199 |
source = project_ref(source) |
|
200 |
return (holder, source, resource), quantity |
|
201 |
|
|
202 |
|
|
203 |
def mk_project_provision(project, resource, quantity): |
|
204 |
holder = project_ref(project) |
|
205 |
return (holder, None, resource), quantity |
|
206 |
|
|
207 |
|
|
208 |
def _mk_provisions(holder, source, resource, quantity): |
|
209 |
return [((holder, source, resource), quantity), |
|
210 |
((source, None, resource), quantity)] |
|
211 |
|
|
212 |
|
|
213 |
def register_pending_apps(user, project, quantity, force=False): |
|
214 |
provisions = _mk_provisions(get_user_ref(user), get_project_ref(project), |
|
215 |
PENDING_APP_RESOURCE, quantity) |
|
130 | 216 |
try: |
131 | 217 |
s = qh.issue_commission(clientkey='astakos', |
132 | 218 |
force=force, |
133 |
provisions=[provision])
|
|
219 |
provisions=provisions)
|
|
134 | 220 |
except NoCapacityError as e: |
135 | 221 |
limit = e.data['limit'] |
136 | 222 |
return False, limit |
... | ... | |
140 | 226 |
|
141 | 227 |
def get_pending_app_quota(user): |
142 | 228 |
quota = get_user_quotas(user) |
143 |
return quota[SYSTEM][PENDING_APP_RESOURCE] |
|
229 |
source = user.base_project.uuid |
|
230 |
return quota[source][PENDING_APP_RESOURCE] |
|
144 | 231 |
|
145 | 232 |
|
146 | 233 |
def update_base_quota(users, resource, value): |
... | ... | |
161 | 248 |
return d |
162 | 249 |
|
163 | 250 |
|
164 |
def initial_quotas(users, flt=None): |
|
165 |
if flt is None: |
|
166 |
flt = Q() |
|
167 |
|
|
168 |
userids = [user.pk for user in users] |
|
169 |
objs = AstakosUserQuota.objects.select_related('resource') |
|
170 |
orig_quotas = objs.filter(user__pk__in=userids).filter(flt) |
|
171 |
orig_quotas = _partition_by(lambda q: q.user_id, orig_quotas) |
|
172 |
|
|
173 |
initial = {} |
|
174 |
for user in users: |
|
175 |
qs = {} |
|
176 |
for q in orig_quotas.get(user.pk, []): |
|
177 |
qs[q.resource.name] = q.capacity |
|
178 |
initial[user.uuid] = {SYSTEM: qs} |
|
179 |
return initial |
|
180 |
|
|
181 |
|
|
182 |
def get_grant_source(grant): |
|
183 |
return SYSTEM |
|
184 |
|
|
185 |
|
|
186 |
def add_limits(x, y): |
|
187 |
return min(x+y, units.PRACTICALLY_INFINITE) |
|
188 |
|
|
189 |
|
|
190 |
def astakos_users_quotas(users, resource=None): |
|
191 |
users = list(users) |
|
192 |
flt = Q(resource__name=resource) if resource is not None else Q() |
|
193 |
quotas = initial_quotas(users, flt=flt) |
|
194 |
|
|
195 |
userids = [user.pk for user in users] |
|
196 |
ACTUALLY_ACCEPTED = ProjectMembership.ACTUALLY_ACCEPTED |
|
197 |
objs = ProjectMembership.objects.select_related( |
|
198 |
'project', 'person') |
|
199 |
memberships = objs.filter( |
|
200 |
person__pk__in=userids, |
|
201 |
state__in=ACTUALLY_ACCEPTED, |
|
202 |
project__state=Project.NORMAL, |
|
203 |
) |
|
204 |
|
|
205 |
projs = set(m.project_id for m in memberships) |
|
206 |
|
|
251 |
def astakos_project_quotas(projects, resource=None): |
|
207 | 252 |
objs = ProjectResourceQuota.objects.select_related() |
208 |
grants = objs.filter(project__in=projs).filter(flt) |
|
209 |
|
|
210 |
for membership in memberships: |
|
211 |
uuid = membership.person.uuid |
|
212 |
userquotas = quotas.get(uuid, {}) |
|
213 |
|
|
214 |
proj = membership.project |
|
215 |
for grant in grants: |
|
216 |
if grant.project_id != proj.id: |
|
217 |
continue |
|
218 |
|
|
219 |
source = get_grant_source(grant) |
|
220 |
source_quotas = userquotas.get(source, {}) |
|
221 |
|
|
222 |
resource = grant.resource.full_name() |
|
223 |
prev = source_quotas.get(resource, 0) |
|
224 |
new = add_limits(prev, grant.member_capacity) |
|
225 |
source_quotas[resource] = new |
|
226 |
userquotas[source] = source_quotas |
|
227 |
quotas[uuid] = userquotas |
|
228 |
|
|
229 |
return quotas |
|
230 |
|
|
231 |
|
|
232 |
def list_user_quotas(users, qhflt=None, initflt=None): |
|
253 |
flt = Q(resource__name=resource) if resource is not None else Q() |
|
254 |
grants = objs.filter(project__in=projects).filter(flt) |
|
255 |
grants_d = _partition_by(lambda g: g.project_id, grants) |
|
256 |
|
|
257 |
objs = ProjectMembership.objects |
|
258 |
memberships = objs.initialized(projects).select_related( |
|
259 |
"person", "project") |
|
260 |
memberships_d = _partition_by(lambda m: m.project_id, memberships) |
|
261 |
|
|
262 |
user_quota = {} |
|
263 |
project_quota = {} |
|
264 |
|
|
265 |
for project in projects: |
|
266 |
pr_ref = get_project_ref(project) |
|
267 |
state = project.state |
|
268 |
if state not in Project.INITIALIZED_STATES: |
|
269 |
continue |
|
270 |
|
|
271 |
project_grants = grants_d.get(project.id, []) |
|
272 |
project_memberships = memberships_d.get(project.id, []) |
|
273 |
for grant in project_grants: |
|
274 |
resource = grant.resource.name |
|
275 |
path = [pr_ref, None, resource] |
|
276 |
val = grant.project_capacity if state == Project.NORMAL else 0 |
|
277 |
set_path(project_quota, path, val, createpath=True) |
|
278 |
for membership in project_memberships: |
|
279 |
u_ref = get_user_ref(membership.person) |
|
280 |
path = [u_ref, pr_ref, resource] |
|
281 |
val = grant.member_capacity if membership.is_active() else 0 |
|
282 |
set_path(user_quota, path, val, createpath=True) |
|
283 |
|
|
284 |
return project_quota, user_quota |
|
285 |
|
|
286 |
|
|
287 |
def list_user_quotas(users, qhflt=None): |
|
233 | 288 |
qh_quotas = get_users_quotas(users, flt=qhflt) |
234 |
astakos_initial = initial_quotas(users, flt=initflt) |
|
235 |
return qh_quotas, astakos_initial |
|
236 |
|
|
237 |
|
|
238 |
# Syncing to quotaholder |
|
239 |
|
|
240 |
def get_users_for_update(user_ids): |
|
241 |
uids = sorted(user_ids) |
|
242 |
objs = AstakosUser.objects |
|
243 |
return list(objs.filter(id__in=uids).order_by('id').select_for_update()) |
|
289 |
return qh_quotas |
|
244 | 290 |
|
245 | 291 |
|
246 |
def get_user_for_update(user_id): |
|
247 |
return get_users_for_update([user_id])[0] |
|
292 |
def qh_sync_projects(projects, resource=None): |
|
293 |
p_quota, u_quota = astakos_project_quotas(projects, resource=resource) |
|
294 |
p_quota.update(u_quota) |
|
295 |
set_quota(p_quota, resource=resource) |
|
248 | 296 |
|
249 | 297 |
|
250 |
def qh_sync_locked_users(users, resource=None): |
|
251 |
astakos_quotas = astakos_users_quotas(users, resource=resource) |
|
252 |
_set_user_quota(astakos_quotas, resource=resource) |
|
253 |
|
|
254 |
|
|
255 |
def qh_sync_users(users, resource=None): |
|
256 |
uids = [user.id for user in users] |
|
257 |
users = get_users_for_update(uids) |
|
258 |
qh_sync_locked_users(users, resource=resource) |
|
259 |
|
|
298 |
def qh_sync_project(project): |
|
299 |
qh_sync_projects([project]) |
|
260 | 300 |
|
261 |
def qh_sync_users_diffs(users, sync=True): |
|
262 |
uids = [user.id for user in users] |
|
263 |
if sync: |
|
264 |
users = get_users_for_update(uids) |
|
265 | 301 |
|
266 |
astakos_quotas = astakos_users_quotas(users) |
|
267 |
qh_limits = get_users_quota_limits(users) |
|
268 |
diff_quotas = {} |
|
269 |
for holder, local in astakos_quotas.iteritems(): |
|
270 |
registered = qh_limits.get(holder, None) |
|
271 |
if local != registered: |
|
272 |
diff_quotas[holder] = dict(local) |
|
302 |
def membership_quota(membership): |
|
303 |
project = membership.project |
|
304 |
pr_ref = get_project_ref(project) |
|
305 |
u_ref = get_user_ref(membership.person) |
|
306 |
objs = ProjectResourceQuota.objects.select_related() |
|
307 |
grants = objs.filter(project=project) |
|
308 |
user_quota = {} |
|
309 |
is_active = membership.is_active() |
|
310 |
for grant in grants: |
|
311 |
resource = grant.resource.name |
|
312 |
path = [u_ref, pr_ref, resource] |
|
313 |
value = grant.member_capacity if is_active else 0 |
|
314 |
set_path(user_quota, path, value, createpath=True) |
|
315 |
return user_quota |
|
316 |
|
|
317 |
|
|
318 |
def qh_sync_membership(membership): |
|
319 |
quota = membership_quota(membership) |
|
320 |
set_quota(quota) |
|
321 |
|
|
322 |
|
|
323 |
def compute_diff_quota(local, registered): |
|
324 |
diff = {} |
|
325 |
for holder, h_quota in local.iteritems(): |
|
326 |
for source, value in h_quota.iteritems(): |
|
327 |
reg_h_quota = registered.get(holder, {}) |
|
328 |
reg_value = reg_h_quota.get(source, {}) |
|
329 |
if value != reg_value: |
|
330 |
set_path(diff, [holder, source], value, createpath=True) |
|
331 |
return diff |
|
332 |
|
|
333 |
|
|
334 |
def qh_sync_projects_diffs(projects, sync=True): |
|
335 |
local_project, local_user = astakos_project_quotas(projects) |
|
336 |
registered_project, registered_user = get_projects_quota_limits(projects) |
|
337 |
diff_quotas = compute_diff_quota(local_project, registered_project) |
|
338 |
diff_quotas.update(compute_diff_quota(local_user, registered_user)) |
|
273 | 339 |
|
274 | 340 |
if sync: |
275 |
_set_user_quota(diff_quotas) |
|
276 |
return qh_limits, diff_quotas |
|
277 |
|
|
278 |
|
|
279 |
def qh_sync_locked_user(user): |
|
280 |
qh_sync_locked_users([user]) |
|
281 |
|
|
341 |
set_quota(diff_quotas) |
|
282 | 342 |
|
283 |
def qh_sync_user(user): |
|
284 |
qh_sync_users([user]) |
|
285 |
|
|
286 |
|
|
287 |
def qh_sync_new_users(users): |
|
288 |
entries = [] |
|
289 |
for resource in Resource.objects.all(): |
|
290 |
for user in users: |
|
291 |
entries.append( |
|
292 |
AstakosUserQuota(user=user, resource=resource, |
|
293 |
capacity=resource.uplimit)) |
|
294 |
AstakosUserQuota.objects.bulk_create(entries) |
|
295 |
qh_sync_users(users) |
|
296 |
|
|
297 |
|
|
298 |
def qh_sync_new_user(user): |
|
299 |
qh_sync_new_users([user]) |
|
300 |
|
|
301 |
|
|
302 |
def members_to_sync(project): |
|
303 |
objs = ProjectMembership.objects.select_related('person') |
|
304 |
memberships = objs.filter(project=project, |
|
305 |
state__in=ProjectMembership.ACTUALLY_ACCEPTED) |
|
306 |
return set(m.person for m in memberships) |
|
307 |
|
|
308 |
|
|
309 |
def qh_sync_project(project): |
|
310 |
users = members_to_sync(project) |
|
311 |
qh_sync_users(users) |
|
343 |
all_registered = registered_project |
|
344 |
all_registered.update(registered_user) |
|
345 |
return all_registered, diff_quotas |
|
312 | 346 |
|
313 | 347 |
|
314 | 348 |
def pick_limit_scheme(project, resource): |
... | ... | |
316 | 350 |
|
317 | 351 |
|
318 | 352 |
def qh_sync_new_resource(resource): |
319 |
users = AstakosUser.objects.filter(
|
|
320 |
moderated=True, is_rejected=False).order_by('id').select_for_update()
|
|
353 |
projects = Project.objects.filter(state__in=Project.INITIALIZED_STATES).\
|
|
354 |
select_for_update() |
|
321 | 355 |
|
322 | 356 |
entries = [] |
323 |
for user in users: |
|
357 |
for project in projects: |
|
358 |
limit = pick_limit_scheme(project, resource) |
|
324 | 359 |
entries.append( |
325 |
AstakosUserQuota(user=user, resource=resource, |
|
326 |
capacity=resource.uplimit)) |
|
327 |
AstakosUserQuota.objects.bulk_create(entries) |
|
328 |
qh_sync_users(users, resource=resource.name) |
|
360 |
ProjectResourceQuota( |
|
361 |
project=project, |
|
362 |
resource=resource, |
|
363 |
project_capacity=limit, |
|
364 |
member_capacity=limit)) |
|
365 |
|
|
366 |
ProjectResourceQuota.objects.bulk_create(entries) |
|
367 |
qh_sync_projects(projects, resource=resource.name) |
Also available in: Unified diff