Revision 3f5851eb

b/snf-astakos-app/astakos/im/functions.py
908 908
    UserSetting.objects.filter(user=user_id, setting=key).delete()
909 909

  
910 910

  
911
def _partition_by(f, l):
912
    d = {}
913
    for x in l:
914
        group = f(x)
915
        group_l = d.get(group, [])
916
        group_l.append(x)
917
        d[group] = group_l
918
    return d
919

  
920

  
921
def count_pending_app(users):
922
    apps = ProjectApplication.objects.filter(state=ProjectApplication.PENDING,
923
                                             owner__in=users)
924
    apps_d = _partition_by(lambda a: a.owner.uuid, apps)
925

  
926
    usage = {}
927
    for user in users:
928
        uuid = user.uuid
929
        usage[uuid] = len(apps_d.get(uuid, []))
930
    return usage
931

  
932

  
911 933
def qh_add_pending_app(user, precursor=None, force=False, dry_run=False):
912 934
    if precursor is None:
913 935
        diff = 1
b/snf-astakos-app/astakos/im/management/commands/reconcile-resources-astakos.py
1
# Copyright 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

  
34
from optparse import make_option
35

  
36
from django.core.management.base import BaseCommand, CommandError
37

  
38
from synnefo.webproject.management.utils import pprint_table
39
from snf_django.lib.db.transaction import commit_on_success_strict
40
from astakos.im.models import Service, AstakosUser
41
from astakos.im.quotas import service_get_quotas, SYSTEM
42
from astakos.im.functions import count_pending_app
43
import astakos.quotaholder_app.callpoint as qh
44

  
45

  
46
class Command(BaseCommand):
47
    help = """Reconcile resource usage of Quotaholder with Astakos DB.
48

  
49
    Detect unsynchronized usage between Quotaholder and Astakos DB resources
50
    and synchronize them if specified so.
51

  
52
    """
53

  
54
    option_list = BaseCommand.option_list + (
55
        make_option("--userid", dest="userid",
56
                    default=None,
57
                    help="Reconcile resources only for this user"),
58
        make_option("--fix", dest="fix",
59
                    default=False,
60
                    action="store_true",
61
                    help="Synchronize Quotaholder with Astakos DB."),
62
        make_option("--force",
63
                    default=False,
64
                    action="store_true",
65
                    help="Override Quotaholder. Force Quotaholder to impose"
66
                         " the quota, independently of their value.")
67
    )
68

  
69
    @commit_on_success_strict()
70
    def handle(self, *args, **options):
71
        write = self.stdout.write
72
        force = options['force']
73
        userid = options['userid']
74

  
75
        try:
76
            astakos = Service.objects.get(name="astakos")
77
        except Service.DoesNotExist:
78
            raise CommandError("Service 'astakos' not found.")
79

  
80
        query = [userid] if userid is not None else None
81
        qh_holdings = service_get_quotas(astakos, query)
82

  
83
        if userid is None:
84
            users = AstakosUser.objects.verified()
85
        else:
86
            try:
87
                users = [AstakosUser.objects.get(uuid=userid)]
88
            except AstakosUser.DoesNotExist:
89
                raise CommandError("There is no user with uuid '%s'." % userid)
90

  
91
        db_holdings = count_pending_app(users)
92

  
93
        pending_exists = False
94
        unknown_user_exists = False
95
        unsynced = []
96
        for user in users:
97
            uuid = user.uuid
98
            db_value = db_holdings.get(uuid, 0)
99
            try:
100
                qh_all = qh_holdings[uuid]
101
            except KeyError:
102
                write("User '%s' does not exist in Quotaholder!\n" % uuid)
103
                unknown_user_exists = True
104
                continue
105

  
106
            # Assuming only one source
107
            system_qh = qh_all.get(SYSTEM, {})
108
            # Assuming only one resource
109
            resource = 'astakos.pending_app'
110
            try:
111
                qh_values = system_qh[resource]
112
                qh_value = qh_values['usage']
113
                qh_pending = qh_values['pending']
114
            except KeyError:
115
                write("Resource '%s' does not exist in Quotaholder"
116
                      " for user '%s'!\n" % (resource, uuid))
117
                continue
118
            if qh_pending:
119
                write("Pending commission. User '%s', resource '%s'.\n" %
120
                      (uuid, resource))
121
                pending_exists = True
122
                continue
123
            if db_value != qh_value:
124
                data = (uuid, resource, db_value, qh_value)
125
                unsynced.append(data)
126

  
127
        headers = ("User", "Resource", "Astakos", "Quotaholder")
128
        if unsynced:
129
            pprint_table(self.stderr, unsynced, headers)
130
            if options["fix"]:
131
                provisions = map(create_provision, unsynced)
132
                s = qh.issue_commission('astakos', provisions,
133
                                        name='RECONCILE', force=force)
134
                qh.resolve_pending_commission('astakos', s)
135
                write("Fixed unsynced resources\n")
136

  
137
        if pending_exists:
138
            write("Found pending commissions. "
139
                  "This is probably a bug. Please report.\n")
140
        elif not (unsynced or unknown_user_exists):
141
            write("Everything in sync.\n")
142

  
143

  
144
def create_provision(provision_info):
145
    user, resource, db_value, qh_value = provision_info
146
    return (user, SYSTEM, resource), (db_value - qh_value)

Also available in: Unified diff