Revision 84a3f701

b/snf-astakos-app/astakos/im/api/backends/lib/django/__init__.py
231 231
    @safe
232 232
    def get_resource_usage(self, user_id):
233 233
        user = self._lookup_user(user_id)
234
        data = get_quota(user)
234
        data = get_quota([user])
235 235
        if not data:
236 236
            return ()
237 237
        resources = []
b/snf-astakos-app/astakos/im/endpoints/qh.py
73 73
    logger.info('set_quota: %s rejected: %s' % (payload, result))
74 74
    return result
75 75

  
76
def qh_get_quota(user, resources):
76
def get_entity(payload):
77
    c = get_client()
78
    if not c:
79
        return
80
    result = c.get_entity(context={}, get_entity=payload)
81
    logger.info('get_entity: %s reply: %s' % (payload, result))
82
    return result
83

  
84
def get_holding(payload):
85
    c = get_client()
86
    if not c:
87
        return
88
    result = c.get_holding(context={}, get_holding=payload)
89
    logger.info('get_holding: %s reply: %s' % (payload, result))
90
    return result
91

  
92
def qh_get_holdings(users, resources):
93
    payload = []
94
    append = payload.append
95
    for user in users:
96
        for resource in resources:
97
            append((user.uuid, resource, ENTITY_KEY),)
98
    result = get_holding(payload)
99
    return result
100

  
101
def qh_get_quota(users, resources):
77 102
    c = get_client()
78 103
    if not c:
79 104
        return
80 105
    payload = []
81 106
    append = payload.append
82
    for r in resources:
83
        append((user.uuid, r, ENTITY_KEY),)
107
    for user in users:
108
        for resource in resources:
109
            append((user.uuid, resource, ENTITY_KEY),)
110

  
84 111
    result = c.get_quota(context={}, clientkey=clientkey, get_quota=payload)
85 112
    logger.info('get_quota: %s rejected: %s' % (payload, result))
86 113
    return result
87 114

  
115
def qh_get_quota_limits(users, resources):
116
    result = qh_get_quota(users, resources)
117
    return result
118

  
88 119
def create_entity(payload):
89 120
    c = get_client()
90 121
    if not c:
......
129 160
        import_limit = q1.import_limit + q2.import_limit,
130 161
        export_limit = q1.export_limit + q2.export_limit)
131 162

  
132
def qh_register_user(user):
133
    return register_users([user])
163
def qh_register_user_with_quotas(user):
164
    return register_users_with_quotas([user])
134 165

  
135
def register_users(users):
136
    rejected = create_users(users)
166
def register_users_with_quotas(users):
167
    rejected = register_users(users)
137 168
    if not rejected:
138 169
        register_quotas(users)
139 170

  
140
def create_users(users):
171
def register_users(users):
172
    if not users:
173
        return
174

  
141 175
    payload = list(CreateEntityPayload(
142 176
                    entity=u.uuid,
143 177
                    owner='system',
......
146 180
    return create_entity(payload)
147 181

  
148 182
def register_quotas(users):
183
    if not users:
184
        return
185

  
149 186
    payload = []
150 187
    append = payload.append
151 188
    for u in users:
......
186 223
            flags=0) for resource in resources)
187 224
    return set_quota(payload)
188 225

  
226
def qh_check_users(users):
227
    payload = [(u.uuid, ENTITY_KEY) for u in users]
228
    result = get_entity(payload)
229
    uuids = [entity for (entity, owner) in result]
230

  
231
    existing = []
232
    nonexisting = []
233
    for u in users:
234
        if u.uuid in uuids:
235
            existing.append(u)
236
        else:
237
            nonexisting.append(u)
238
    return (existing, nonexisting)
239

  
189 240
def qh_add_quota(serial, sub_list, add_list):
190 241
    if not QUOTAHOLDER_URL:
191 242
        return ()
b/snf-astakos-app/astakos/im/functions.py
66 66
from astakos.im.notifications import build_notification, NotificationError
67 67
from astakos.im.models import (
68 68
    AstakosUser, ProjectMembership, ProjectApplication, Project,
69
    trigger_sync, PendingMembershipError, get_resource_names)
69
    sync_projects, PendingMembershipError, get_resource_names)
70 70
from astakos.im.models import submit_application as models_submit_application
71 71
from astakos.im.project_notif import (
72 72
    membership_change_notify,
73 73
    application_submit_notify, application_approve_notify,
74 74
    application_deny_notify,
75 75
    project_termination_notify, project_suspension_notify)
76
from astakos.im.endpoints.qh import qh_register_user, qh_get_quota
76
from astakos.im.endpoints.qh import qh_register_user_with_quotas, qh_get_quota
77 77

  
78 78
import astakos.im.messages as astakos_messages
79 79

  
......
299 299
    if not user.activation_sent:
300 300
        user.activation_sent = datetime.now()
301 301
    user.save()
302
    qh_register_user(user)
302
    qh_register_user_with_quotas(user)
303 303
    send_helpdesk_notification(user, helpdesk_email_template_name)
304 304
    send_greeting(user, email_template_name)
305 305

  
......
377 377
        super(SendNotificationError, self).__init__()
378 378

  
379 379

  
380
def get_quota(user):
380
def get_quota(users):
381 381
    resources = get_resource_names()
382
    return qh_get_quota(user, resources)
382
    return qh_get_quota(users, resources)
383 383

  
384 384

  
385 385
### PROJECT VIEWS ###
......
507 507

  
508 508
    membership = get_membership_for_update(project, user)
509 509
    membership.accept()
510
    trigger_sync()
510
    sync_projects()
511 511

  
512 512
    membership_change_notify(project, membership.person, 'accepted')
513 513

  
......
560 560

  
561 561
    membership = get_membership_for_update(project, user)
562 562
    membership.remove()
563
    trigger_sync()
563
    sync_projects()
564 564

  
565 565
    membership_change_notify(project, membership.person, 'removed')
566 566

  
......
576 576

  
577 577
    membership = create_membership(project_id, user)
578 578
    membership.accept()
579
    trigger_sync()
579
    sync_projects()
580 580

  
581 581
    # TODO send proper notification
582 582
    return membership
......
606 606
    leave_policy = project.application.member_leave_policy
607 607
    if leave_policy == AUTO_ACCEPT_POLICY:
608 608
        membership.remove()
609
        trigger_sync()
609
        sync_projects()
610 610
    else:
611 611
        membership.leave_request_date = datetime.now()
612 612
        membership.save()
......
638 638
    if (join_policy == AUTO_ACCEPT_POLICY and
639 639
        not project.violates_members_limit(adding=1)):
640 640
        membership.accept()
641
        trigger_sync()
641
        sync_projects()
642 642
    return membership
643 643

  
644 644
def submit_application(kw, request_user=None):
......
698 698
        raise PermissionDenied()
699 699

  
700 700
    application.approve()
701
    trigger_sync()
701
    sync_projects()
702 702

  
703 703
    application_approve_notify(application)
704 704

  
......
716 716
    checkAlive(project)
717 717

  
718 718
    project.terminate()
719
    trigger_sync()
719
    sync_projects()
720 720

  
721 721
    project_termination_notify(project)
722 722

  
......
725 725
    checkAlive(project)
726 726

  
727 727
    project.suspend()
728
    trigger_sync()
728
    sync_projects()
729 729

  
730 730
    project_suspension_notify(project)
731 731

  
......
737 737
        raise PermissionDenied(m)
738 738

  
739 739
    project.resume()
740
    trigger_sync()
740
    sync_projects()
b/snf-astakos-app/astakos/im/management/commands/astakos-qh-sync.py
31 31
# interpreted as representing official policies, either expressed
32 32
# or implied, of GRNET S.A.
33 33

  
34
from django.core.management.base import NoArgsCommand, CommandError
34
from optparse import make_option
35
from django.core.management.base import BaseCommand, CommandError
36
from django.db import transaction
35 37

  
36
from astakos.im.models import AstakosUser, Resource
37
from astakos.im.endpoints.qh import register_users, register_resources
38
from astakos.im.models import sync_all_users, sync_projects
38 39

  
39 40
import logging
40 41
logger = logging.getLogger(__name__)
41 42

  
43
class Command(BaseCommand):
44
    help = "Inspect quotaholder status and sync"
42 45

  
43
class Command(NoArgsCommand):
44
    help = "Send user information and resource quota in the Quotaholder"
46
    option_list = BaseCommand.option_list + (
47
        make_option('--users',
48
                    action='store_true',
49
                    dest='users',
50
                    default=False,
51
                    help="Check if users and their quotas are in sync with quotaholder"),
52
        make_option('--projects',
53
                    action='store_true',
54
                    dest='projects',
55
                    default=False,
56
                    help="Check if projects are in sync with quotaholder"),
57
        make_option('--execute',
58
                    action='store_true',
59
                    dest='execute',
60
                    default=False,
61
                    help="Perform the actual operation"),
62
    )
63

  
64
    @transaction.commit_on_success
65
    def handle(self, *args, **options):
66
        execute = options['execute']
45 67

  
46
    def handle_noargs(self, **options):
47 68
        try:
48
            resources = list(Resource.objects.all())
49
	    print("Registering resources")
50
            register_resources(resources)
51
	    print("Registering users")
52
            users = list(AstakosUser.objects.verified().all())
53
            if users:
54
                register_users(users)
55
            else:
56
                print(" -> No verified users found.")
69
            if options['users']:
70
                log = sync_all_users(execute=execute)
71
            elif options['projects']:
72
                log = sync_projects(execute=execute)
57 73
        except BaseException, e:
58 74
            logger.exception(e)
59 75
            raise CommandError("Syncing failed.")
60

  
b/snf-astakos-app/astakos/im/models.py
70 70
    SITENAME, SERVICES, MODERATION_ENABLED, RESOURCES_PRESENTATION_DATA)
71 71
from astakos.im import settings as astakos_settings
72 72
from astakos.im.endpoints.qh import (
73
    register_users, register_quotas, qh_check_users, qh_get_quota_limits,
73 74
    register_services, register_resources, qh_add_quota, QuotaLimits,
74 75
    qh_query_serials, qh_ack_serials,
75 76
    QuotaValues, add_quota_values)
......
2010 2011
    qh_ack_serials(list(serials_to_ack))
2011 2012
    return len(memberships)
2012 2013

  
2013
def pre_sync():
2014
def pre_sync_projects():
2014 2015
    ACCEPTED = ProjectMembership.ACCEPTED
2015 2016
    PROJECT_DEACTIVATED = ProjectMembership.PROJECT_DEACTIVATED
2016 2017
    psfu = Project.objects.select_for_update()
......
2044 2045
            membership.state = PROJECT_DEACTIVATED
2045 2046
            membership.save()
2046 2047

  
2047
def do_sync():
2048
def do_sync_projects():
2048 2049

  
2049 2050
    ACCEPTED = ProjectMembership.ACCEPTED
2050 2051
    objects = ProjectMembership.objects.select_for_update()
......
2085 2086

  
2086 2087
    return serial
2087 2088

  
2088
def post_sync():
2089
def post_sync_projects():
2089 2090
    ACCEPTED = ProjectMembership.ACCEPTED
2090 2091
    PROJECT_DEACTIVATED = ProjectMembership.PROJECT_DEACTIVATED
2091 2092
    psfu = Project.objects.select_for_update()
......
2120 2121

  
2121 2122
    transaction.commit()
2122 2123

  
2123
def sync_projects():
2124
def _sync_projects(execute):
2124 2125
    sync_finish_serials()
2125
    pre_sync()
2126
    serial = do_sync()
2126
    if not execute:
2127
        # Do some reporting and
2128
        return
2129

  
2130
    pre_sync_projects()
2131
    serial = do_sync_projects()
2127 2132
    sync_finish_serials([serial])
2128
    post_sync()
2133
    post_sync_projects()
2134

  
2135
def sync_projects(execute=True, retries=3, retry_wait=1.0):
2136
    return lock_sync(_sync_projects,
2137
                     args=[execute],
2138
                     retries=retries,
2139
                     retry_wait=retry_wait)
2140

  
2141
def _sync_users(users, execute):
2142
    sync_finish_serials()
2143

  
2144
    existing, nonexisting = qh_check_users(users)
2145
    resources = get_resource_names()
2146
    quotas = qh_get_quota_limits(existing, resources)
2129 2147

  
2130
def trigger_sync(retries=3, retry_wait=1.0):
2148
    if execute:
2149
        r = register_users(nonexisting)
2150
        r = register_quotas(users)
2151

  
2152
    # TODO: some proper reporting
2153
    return (existing, nonexisting, quotas)
2154

  
2155
def sync_users(users, execute=True, retries=3, retry_wait=1.0):
2156
    return lock_sync(_sync_users,
2157
                     args=[users, execute],
2158
                     retries=retries,
2159
                     retry_wait=retry_wait)
2160

  
2161
def sync_all_users(execute=True, retries=3, retry_wait=1.0):
2162
    users = AstakosUser.objects.all()
2163
    return sync_users(users, execute, retries=retries, retry_wait=retry_wait)
2164

  
2165
def lock_sync(func, args=[], kwargs={}, retries=3, retry_wait=1.0):
2131 2166
    transaction.commit()
2132 2167

  
2133 2168
    cursor = connection.cursor()
......
2148 2183
                return False
2149 2184
            sleep(retry_wait)
2150 2185

  
2151
        sync_projects()
2152
        return True
2186
        return func(*args, **kwargs)
2153 2187

  
2154 2188
    finally:
2155 2189
        if locked:

Also available in: Unified diff