Revision f72ba65d

b/docs/admin-guide.rst
300 300

  
301 301
Set default quota
302 302
`````````````````
303
To inspect current default base quota limits, run::
303 304

  
304
In 20-snf-astakos-app-settings.conf, 
305
uncomment the default setting ``ASTAKOS_SERVICES``
306
and customize the ``'uplimit'`` values.
307
These are the default base quota for all users.
305
   # snf-manage resource-list
308 306

  
309 307
You can modify the default base quota limit for all future users with::
310 308

  
b/snf-astakos-app/astakos/im/activation_backends.py
40 40
from astakos.im import settings
41 41
from astakos.im import forms
42 42

  
43
from astakos.im.quotas import qh_sync_user
43
from astakos.im.quotas import qh_sync_new_user
44 44

  
45 45
import astakos.im.messages as astakos_messages
46 46

  
......
257 257
                                         default=lambda obj:
258 258
                                         str(obj))
259 259
        user.save()
260
        qh_sync_user(user)
260
        qh_sync_new_user(user)
261 261

  
262 262
        if user.is_rejected:
263 263
            logger.warning("User has previously been "
b/snf-astakos-app/astakos/im/management/commands/resource-modify.py
37 37

  
38 38
from snf_django.management import utils
39 39
from astakos.im.models import Resource
40
from astakos.im.register import update_resource
40
from astakos.im.register import update_resources
41 41
from ._common import show_resource_value, style_options, check_style, units
42 42

  
43 43

  
......
139 139
        else:
140 140
            resources = [self.get_resource(resource_name)]
141 141

  
142
        updates = []
142 143
        for resource in resources:
143 144
            limit = config.get(resource.name)
144 145
            if limit is not None:
145
                self.change_resource_limit(resource, limit)
146
                limit = self.parse_limit(limit)
147
                updates.append((resource, limit))
148
        if updates:
149
            update_resources(updates)
146 150

  
147 151
    def change_interactive(self, resource_name, _placeholder):
148 152
        if resource_name is None:
......
150 154
        else:
151 155
            resources = [self.get_resource(resource_name)]
152 156

  
157
        updates = []
153 158
        for resource in resources:
154 159
            self.stdout.write("Resource '%s' (%s)\n" %
155 160
                              (resource.name, resource.desc))
......
166 171
                        value = units.parse(response)
167 172
                    except units.ParseError:
168 173
                        continue
169
                    update_resource(resource, value)
174
                    updates.append((resource, value))
170 175
                    break
176
        if updates:
177
            self.stdout.write("Updating...\n")
178
            update_resources(updates)
179

  
180
    def parse_limit(self, limit):
181
        try:
182
            if isinstance(limit, (int, long)):
183
                return limit
184
            if isinstance(limit, basestring):
185
                return units.parse(limit)
186
            raise units.ParseError()
187
        except units.ParseError:
188
            m = ("Limit should be an integer, optionally followed by a unit,"
189
                 " or 'inf'.")
190
            raise CommandError(m)
171 191

  
172 192
    def change_resource_limit(self, resource, limit):
173
        if not isinstance(limit, (int, long)):
174
            try:
175
                limit = units.parse(limit)
176
            except units.ParseError:
177
                m = ("Limit should be an integer, optionally followed "
178
                     "by a unit.")
179
                raise CommandError(m)
180
            update_resource(resource, limit)
193
        limit = self.parse_limit(limit)
194
        update_resources([(resource, limit)])
b/snf-astakos-app/astakos/im/management/commands/user-modify.py
43 43
from django.core.validators import validate_email
44 44

  
45 45
from synnefo.util import units
46
from astakos.im.models import AstakosUser, Resource
46
from astakos.im.models import AstakosUser, AstakosUserQuota
47 47
from astakos.im import quotas
48 48
from astakos.im import activation_backends
49 49
from ._common import (remove_user_permission, add_user_permission, is_uuid,
......
345 345
                raise CommandError(m)
346 346

  
347 347
        try:
348
            quota, default_capacity = user.get_resource_policy(resource)
349
        except Resource.DoesNotExist:
348
            quota = user.get_resource_policy(resource)
349
        except AstakosUserQuota.DoesNotExist:
350 350
            raise CommandError("No such resource: %s" % resource)
351 351

  
352
        default_capacity = quota.resource.uplimit
352 353
        if not force:
353 354
            s_default = show_resource_value(default_capacity, resource, style)
354
            s_current = (show_resource_value(quota.capacity, resource, style)
355
                         if quota is not None else 'default')
355
            s_current = show_resource_value(quota.capacity, resource, style)
356 356
            s_capacity = (show_resource_value(capacity, resource, style)
357 357
                          if capacity != 'default' else capacity)
358 358
            self.stdout.write("user: %s (%s)\n" % (user.uuid, user.username))
......
366 366
                return
367 367

  
368 368
        if capacity == 'default':
369
            try:
370
                quotas.remove_base_quota(user, resource)
371
            except Exception as e:
372
                import traceback
373
                traceback.print_exc()
374
                raise CommandError("Failed to remove policy: %s" % e)
375
        else:
376
            try:
377
                quotas.add_base_quota(user, resource, capacity)
378
            except Exception as e:
379
                raise CommandError("Failed to add policy: %s" % e)
369
            capacity = default_capacity
370
        quotas.update_base_quota(quota, capacity)
b/snf-astakos-app/astakos/im/migrations/0056_user_quota.py
1
# -*- coding: utf-8 -*-
2
import datetime
3
from south.db import db
4
from south.v2 import DataMigration
5
from django.db import models
6

  
7
class Migration(DataMigration):
8

  
9
    def forwards(self, orm):
10
        custom = set(orm.AstakosUserQuota.objects.all().\
11
                         values_list("user__uuid", "resource__name"))
12

  
13
        users = orm.AstakosUser.objects.filter(
14
            moderated=True, is_rejected=False)
15
        entries = []
16
        for resource in orm.Resource.objects.all():
17
            name = resource.name
18
            for user in users:
19
                if (user.uuid, name) not in custom:
20
                    entries.append(
21
                        orm.AstakosUserQuota(user=user, resource=resource,
22
                                             capacity=resource.uplimit))
23
        orm.AstakosUserQuota.objects.bulk_create(entries)
24

  
25
    def backwards(self, orm):
26
        "Write your backwards methods here."
27

  
28
    models = {
29
        'auth.group': {
30
            'Meta': {'object_name': 'Group'},
31
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
32
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
33
            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
34
        },
35
        'auth.permission': {
36
            'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
37
            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
38
            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
39
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
40
            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
41
        },
42
        'auth.user': {
43
            'Meta': {'object_name': 'User'},
44
            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
45
            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
46
            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
47
            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
48
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
49
            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
50
            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
51
            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
52
            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
53
            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
54
            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
55
            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
56
            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
57
        },
58
        'contenttypes.contenttype': {
59
            'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
60
            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
61
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
62
            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
63
            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
64
        },
65
        'im.additionalmail': {
66
            'Meta': {'object_name': 'AdditionalMail'},
67
            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
68
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
69
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
70
        },
71
        'im.approvalterms': {
72
            'Meta': {'object_name': 'ApprovalTerms'},
73
            'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
74
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
75
            'location': ('django.db.models.fields.CharField', [], {'max_length': '255'})
76
        },
77
        'im.astakosuser': {
78
            'Meta': {'object_name': 'AstakosUser', '_ormbases': ['auth.User']},
79
            'accepted_email': ('django.db.models.fields.EmailField', [], {'default': 'None', 'max_length': '75', 'null': 'True', 'blank': 'True'}),
80
            'accepted_policy': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
81
            'activation_sent': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
82
            'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
83
            'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
84
            'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
85
            'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
86
            'date_signed_terms': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
87
            'deactivated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
88
            'deactivated_reason': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True'}),
89
            'disturbed_quota': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
90
            'email_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
91
            'has_credits': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
92
            'has_signed_terms': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
93
            'invitations': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
94
            'is_rejected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
95
            'is_verified': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
96
            'level': ('django.db.models.fields.IntegerField', [], {'default': '4'}),
97
            'moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
98
            'moderated_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
99
            'moderated_data': ('django.db.models.fields.TextField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
100
            'policy': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.AstakosUserQuota']", 'symmetrical': 'False'}),
101
            'rejected_reason': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
102
            'updated': ('django.db.models.fields.DateTimeField', [], {}),
103
            'user_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'primary_key': 'True'}),
104
            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True', 'null': 'True'}),
105
            'verification_code': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True', 'null': 'True'}),
106
            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'})
107
        },
108
        'im.astakosuserauthprovider': {
109
            'Meta': {'ordering': "('module', 'created')", 'unique_together': "(('identifier', 'module', 'user'),)", 'object_name': 'AstakosUserAuthProvider'},
110
            'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
111
            'affiliation': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
112
            'auth_backend': ('django.db.models.fields.CharField', [], {'default': "'astakos'", 'max_length': '255'}),
113
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
114
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
115
            'identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
116
            'info_data': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
117
            'module': ('django.db.models.fields.CharField', [], {'default': "'local'", 'max_length': '255'}),
118
            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_providers'", 'to': "orm['im.AstakosUser']"})
119
        },
120
        'im.astakosuserquota': {
121
            'Meta': {'unique_together': "(('resource', 'user'),)", 'object_name': 'AstakosUserQuota'},
122
            'capacity': ('django.db.models.fields.BigIntegerField', [], {}),
123
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
124
            'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"}),
125
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"})
126
        },
127
        'im.authproviderpolicyprofile': {
128
            'Meta': {'ordering': "['priority']", 'object_name': 'AuthProviderPolicyProfile'},
129
            'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
130
            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'authpolicy_profiles'", 'symmetrical': 'False', 'to': "orm['auth.Group']"}),
131
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
132
            'is_exclusive': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
133
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
134
            'policy_add': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
135
            'policy_automoderate': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
136
            'policy_create': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
137
            'policy_limit': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True'}),
138
            'policy_login': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
139
            'policy_remove': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
140
            'policy_required': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
141
            'policy_switch': ('django.db.models.fields.NullBooleanField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
142
            'priority': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
143
            'provider': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
144
            'users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'authpolicy_profiles'", 'symmetrical': 'False', 'to': "orm['im.AstakosUser']"})
145
        },
146
        'im.chain': {
147
            'Meta': {'object_name': 'Chain'},
148
            'chain': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
149
        },
150
        'im.component': {
151
            'Meta': {'object_name': 'Component'},
152
            'auth_token': ('django.db.models.fields.CharField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
153
            'auth_token_created': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
154
            'auth_token_expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
155
            'base_url': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}),
156
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
157
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
158
            'url': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'})
159
        },
160
        'im.emailchange': {
161
            'Meta': {'object_name': 'EmailChange'},
162
            'activation_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40', 'db_index': 'True'}),
163
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
164
            'new_email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
165
            'requested_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
166
            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'emailchanges'", 'unique': 'True', 'to': "orm['im.AstakosUser']"})
167
        },
168
        'im.endpoint': {
169
            'Meta': {'object_name': 'Endpoint'},
170
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
171
            'service': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'endpoints'", 'to': "orm['im.Service']"})
172
        },
173
        'im.endpointdata': {
174
            'Meta': {'unique_together': "(('endpoint', 'key'),)", 'object_name': 'EndpointData'},
175
            'endpoint': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'data'", 'to': "orm['im.Endpoint']"}),
176
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
177
            'key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
178
            'value': ('django.db.models.fields.CharField', [], {'max_length': '1024'})
179
        },
180
        'im.invitation': {
181
            'Meta': {'object_name': 'Invitation'},
182
            'code': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}),
183
            'consumed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
184
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
185
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
186
            'inviter': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invitations_sent'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
187
            'is_consumed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
188
            'realname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
189
            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
190
        },
191
        'im.pendingthirdpartyuser': {
192
            'Meta': {'unique_together': "(('provider', 'third_party_identifier'),)", 'object_name': 'PendingThirdPartyUser'},
193
            'affiliation': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
194
            'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
195
            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
196
            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
197
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
198
            'info': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
199
            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
200
            'provider': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
201
            'third_party_identifier': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
202
            'token': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
203
            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
204
        },
205
        'im.project': {
206
            'Meta': {'object_name': 'Project'},
207
            'application': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'project'", 'unique': 'True', 'to': "orm['im.ProjectApplication']"}),
208
            'creation_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
209
            'id': ('django.db.models.fields.BigIntegerField', [], {'primary_key': 'True', 'db_column': "'id'"}),
210
            'members': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['im.AstakosUser']", 'through': "orm['im.ProjectMembership']", 'symmetrical': 'False'}),
211
            'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True', 'null': 'True', 'db_index': 'True'}),
212
            'state': ('django.db.models.fields.IntegerField', [], {'default': '1', 'db_index': 'True'})
213
        },
214
        'im.projectapplication': {
215
            'Meta': {'unique_together': "(('chain', 'id'),)", 'object_name': 'ProjectApplication'},
216
            'applicant': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projects_applied'", 'to': "orm['im.AstakosUser']"}),
217
            'chain': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'chained_apps'", 'db_column': "'chain'", 'to': "orm['im.Project']"}),
218
            'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
219
            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
220
            'end_date': ('django.db.models.fields.DateTimeField', [], {}),
221
            'homepage': ('django.db.models.fields.URLField', [], {'max_length': '255', 'null': 'True'}),
222
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
223
            'issue_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
224
            'limit_on_members_number': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
225
            'member_join_policy': ('django.db.models.fields.IntegerField', [], {}),
226
            'member_leave_policy': ('django.db.models.fields.IntegerField', [], {}),
227
            'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
228
            'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projects_owned'", 'to': "orm['im.AstakosUser']"}),
229
            'resource_grants': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['im.Resource']", 'null': 'True', 'through': "orm['im.ProjectResourceGrant']", 'blank': 'True'}),
230
            'response': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
231
            'response_actor': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'responded_apps'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
232
            'response_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
233
            'start_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
234
            'state': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
235
            'waive_actor': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'waived_apps'", 'null': 'True', 'to': "orm['im.AstakosUser']"}),
236
            'waive_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
237
            'waive_reason': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
238
        },
239
        'im.projectlock': {
240
            'Meta': {'object_name': 'ProjectLock'},
241
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
242
        },
243
        'im.projectlog': {
244
            'Meta': {'object_name': 'ProjectLog'},
245
            'actor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']", 'null': 'True'}),
246
            'comments': ('django.db.models.fields.TextField', [], {'null': 'True'}),
247
            'date': ('django.db.models.fields.DateTimeField', [], {}),
248
            'from_state': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
249
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
250
            'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'log'", 'to': "orm['im.Project']"}),
251
            'reason': ('django.db.models.fields.TextField', [], {'null': 'True'}),
252
            'to_state': ('django.db.models.fields.IntegerField', [], {})
253
        },
254
        'im.projectmembership': {
255
            'Meta': {'unique_together': "(('person', 'project'),)", 'object_name': 'ProjectMembership'},
256
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
257
            'person': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"}),
258
            'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Project']"}),
259
            'state': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'})
260
        },
261
        'im.projectmembershiplog': {
262
            'Meta': {'object_name': 'ProjectMembershipLog'},
263
            'actor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']", 'null': 'True'}),
264
            'comments': ('django.db.models.fields.TextField', [], {'null': 'True'}),
265
            'date': ('django.db.models.fields.DateTimeField', [], {}),
266
            'from_state': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
267
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
268
            'membership': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'log'", 'to': "orm['im.ProjectMembership']"}),
269
            'reason': ('django.db.models.fields.TextField', [], {'null': 'True'}),
270
            'to_state': ('django.db.models.fields.IntegerField', [], {})
271
        },
272
        'im.projectresourcegrant': {
273
            'Meta': {'unique_together': "(('resource', 'project_application'),)", 'object_name': 'ProjectResourceGrant'},
274
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
275
            'member_capacity': ('django.db.models.fields.BigIntegerField', [], {'default': '0'}),
276
            'project_application': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.ProjectApplication']", 'null': 'True'}),
277
            'project_capacity': ('django.db.models.fields.BigIntegerField', [], {'null': 'True'}),
278
            'resource': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Resource']"})
279
        },
280
        'im.resource': {
281
            'Meta': {'object_name': 'Resource'},
282
            'allow_in_projects': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
283
            'desc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
284
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
285
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
286
            'service_origin': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
287
            'service_type': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
288
            'unit': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
289
            'uplimit': ('django.db.models.fields.BigIntegerField', [], {'default': '0'})
290
        },
291
        'im.service': {
292
            'Meta': {'object_name': 'Service'},
293
            'component': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.Component']"}),
294
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
295
            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
296
            'type': ('django.db.models.fields.CharField', [], {'max_length': '255'})
297
        },
298
        'im.sessioncatalog': {
299
            'Meta': {'object_name': 'SessionCatalog'},
300
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
301
            'session_key': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
302
            'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sessions'", 'null': 'True', 'to': "orm['im.AstakosUser']"})
303
        },
304
        'im.usersetting': {
305
            'Meta': {'unique_together': "(('user', 'setting'),)", 'object_name': 'UserSetting'},
306
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
307
            'setting': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
308
            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['im.AstakosUser']"}),
309
            'value': ('django.db.models.fields.IntegerField', [], {})
310
        }
311
    }
312

  
313
    complete_apps = ['im']
314
    symmetrical = True
b/snf-astakos-app/astakos/im/models.py
519 519
        return self.astakosuserquota_set.select_related().all()
520 520

  
521 521
    def get_resource_policy(self, resource):
522
        resource = Resource.objects.get(name=resource)
523
        default_capacity = resource.uplimit
524
        try:
525
            policy = AstakosUserQuota.objects.get(user=self, resource=resource)
526
            return policy, default_capacity
527
        except AstakosUserQuota.DoesNotExist:
528
            return None, default_capacity
522
        return AstakosUserQuota.objects.select_related("resource").\
523
            get(user=self, resource__name=resource)
529 524

  
530 525
    def update_uuid(self):
531 526
        while not self.uuid:
b/snf-astakos-app/astakos/im/quotas.py
121 121
    qh.set_quota(q)
122 122

  
123 123

  
124
def get_default_quota():
125
    _DEFAULT_QUOTA = {}
126
    resources = Resource.objects.select_related('service').all()
127
    for resource in resources:
128
        capacity = resource.uplimit
129
        _DEFAULT_QUOTA[resource.full_name()] = capacity
130

  
131
    return _DEFAULT_QUOTA
132

  
133

  
134 124
SYSTEM = 'system'
135 125
PENDING_APP_RESOURCE = 'astakos.pending_app'
136 126

  
......
153 143
    return quota[SYSTEM][PENDING_APP_RESOURCE]
154 144

  
155 145

  
156
def add_base_quota(user, resource, capacity):
157
    resource = Resource.objects.get(name=resource)
158
    user = get_user_for_update(user.id)
159
    obj, created = AstakosUserQuota.objects.get_or_create(
160
        user=user, resource=resource, defaults={
161
            'capacity': capacity,
162
        })
163

  
164
    if not created:
165
        obj.capacity = capacity
166
        obj.save()
167
    qh_sync_locked_user(user)
168

  
169

  
170
def remove_base_quota(user, resource):
171
    user = get_user_for_update(user.id)
172
    AstakosUserQuota.objects.filter(
173
        user=user, resource__name=resource).delete()
174
    qh_sync_locked_user(user)
146
def update_base_quota(quota, capacity):
147
    quota.capacity = capacity
148
    quota.save()
149
    qh_sync_locked_user(quota.user)
175 150

  
176 151

  
177 152
def initial_quotas(users):
178
    users = list(users)
179
    initial = {}
180
    default_quotas = get_default_quota()
181

  
182
    for user in users:
183
        uuid = user.uuid
184
        source_quota = {SYSTEM: dict(default_quotas)}
185
        initial[uuid] = source_quota
186

  
187 153
    userids = [user.pk for user in users]
188 154
    objs = AstakosUserQuota.objects.select_related()
189 155
    orig_quotas = objs.filter(user__pk__in=userids)
156
    initial = {}
190 157
    for user_quota in orig_quotas:
191 158
        uuid = user_quota.user.uuid
192 159
        user_init = initial.get(uuid, {})
......
304 271
    qh_sync_users([user])
305 272

  
306 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

  
307 289
def members_to_sync(project):
308 290
    objs = ProjectMembership.objects.select_related('person')
309 291
    memberships = objs.filter(project=project,
......
316 298
    qh_sync_users(users)
317 299

  
318 300

  
319
def qh_change_resource_limit(resource):
320
    objs = AstakosUser.objects.filter(
321
        Q(moderated=True, is_rejected=False) & ~Q(policy=resource))
322
    users = objs.order_by('id').select_for_update()
323
    quota = astakos_users_quotas(users)
324
    _set_user_quota(quota)
325

  
326

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

  
331
    resource_name = resource.name
332
    limit = resource.uplimit
333
    data = []
305
    entries = []
334 306
    for user in users:
335
        uuid = user.uuid
336
        key = uuid, SYSTEM, resource_name
337
        data.append((key, limit))
338

  
339
    qh.set_quota(data)
307
        entries.append(
308
            AstakosUserQuota(user=user, resource=resource,
309
                             capacity=resource.uplimit))
310
    AstakosUserQuota.objects.bulk_create(entries)
311
    qh_sync_users(users)
b/snf-astakos-app/astakos/im/register.py
104 104
    return r, exists
105 105

  
106 106

  
107
def update_resource(resource, uplimit):
108
    old_uplimit = resource.uplimit
109
    if uplimit == old_uplimit:
110
        logger.info("Resource %s has limit %s; no need to update."
111
                    % (resource.name, uplimit))
112
        return []
113
    else:
114
        resource.uplimit = uplimit
115
        resource.save()
116
        logger.info("Updated resource %s with limit %s."
117
                    % (resource.name, uplimit))
118
        affected = quotas.qh_change_resource_limit(resource)
119
        return affected
107
def update_resources(updates):
108
    resources = []
109
    for resource, uplimit in updates:
110
        resources.append(resource)
111
        old_uplimit = resource.uplimit
112
        if uplimit == old_uplimit:
113
            logger.info("Resource %s has limit %s; no need to update."
114
                        % (resource.name, uplimit))
115
        else:
116
            resource.uplimit = uplimit
117
            resource.save()
118
            logger.info("Updated resource %s with limit %s."
119
                        % (resource.name, uplimit))
120 120

  
121 121

  
122 122
def get_resources(resources=None, services=None):
b/snf-astakos-app/astakos/im/tests/api.py
64 64
                      "service_origin": "service1",
65 65
                      "allow_in_projects": True}
66 66
        r, _ = register.add_resource(resource11)
67
        register.update_resource(r, 100)
67
        register.update_resources([(r, 100)])
68 68
        resource12 = {"name": "service1.resource12",
69 69
                      "desc": "resource11 desc",
70 70
                      "service_type": "type1",
71 71
                      "service_origin": "service1",
72 72
                      "unit": "bytes"}
73 73
        r, _ = register.add_resource(resource12)
74
        register.update_resource(r, 1024)
74
        register.update_resources([(r, 1024)])
75 75

  
76 76
        # create user
77 77
        user = get_local_user('test@grnet.gr')
......
91 91
                      "service_origin": "service2",
92 92
                      "allow_in_projects": False}
93 93
        r, _ = register.add_resource(resource21)
94
        register.update_resource(r, 3)
94
        register.update_resources([(r, 3)])
95 95

  
96 96
        resource_names = [r['name'] for r in
97 97
                          [resource11, resource12, resource21]]
b/snf-astakos-app/astakos/im/tests/projects.py
48 48
                      "service_origin": "service1",
49 49
                      "allow_in_projects": True}
50 50
        r, _ = register.add_resource(resource11)
51
        register.update_resource(r, 100)
51
        register.update_resources([(r, 100)])
52 52
        resource12 = {"name": "service1.resource12",
53 53
                      "desc": "resource11 desc",
54 54
                      "service_type": "type1",
55 55
                      "service_origin": "service1",
56 56
                      "unit": "bytes"}
57 57
        r, _ = register.add_resource(resource12)
58
        register.update_resource(r, 1024)
58
        register.update_resources([(r, 1024)])
59 59

  
60 60
        # create user
61 61
        self.user1 = get_local_user("test@grnet.gr", moderated=True)
......
76 76
                       "service_origin": "astakos_account",
77 77
                       "allow_in_projects": False}
78 78
        r, _ = register.add_resource(pending_app)
79
        register.update_resource(r, 3)
79
        register.update_resources([(r, 3)])
80 80

  
81 81
    def create(self, app, headers):
82 82
        dump = json.dumps(app)
......
618 618
        self.member_client = get_user_client("member@synnefo.org")
619 619
        self.member2_client = get_user_client("member2@synnefo.org")
620 620

  
621
        quotas.qh_sync_users(AstakosUser.objects.all())
621
        quotas.qh_sync_new_users(AstakosUser.objects.all())
622 622

  
623 623
    def tearDown(self):
624 624
        Service.objects.all().delete()
......
671 671
    @im_settings(PROJECT_ADMINS=['uuid1'])
672 672
    def test_applications(self):
673 673
        # let user have 2 pending applications
674
        quotas.add_base_quota(self.user, 'astakos.pending_app', 2)
674
        q = self.user.get_resource_policy('astakos.pending_app')
675
        quotas.update_base_quota(q, 2)
675 676

  
676 677
        r = self.user_client.get(reverse('project_add'), follow=True)
677 678
        self.assertRedirects(r, reverse('project_add'))

Also available in: Unified diff