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