Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / management / commands / reconcile-resources-astakos.py @ a3e3917f

History | View | Annotate | Download (5.9 kB)

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
from django.db import transaction
38

    
39
from snf_django.management.utils import pprint_table
40
from astakos.im.models import Component, 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
import astakos.quotaholder_app.exception as qh_exception
45

    
46

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

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

53
    """
54

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

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

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

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

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

    
92
        db_holdings = count_pending_app(users)
93

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

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

    
128
        headers = ("User", "Resource", "Astakos", "Quotaholder")
129
        if unsynced:
130
            pprint_table(self.stderr, unsynced, headers)
131
            if options["fix"]:
132
                provisions = map(create_provision, unsynced)
133
                try:
134
                    s = qh.issue_commission('astakos', provisions,
135
                                            name='RECONCILE', force=force)
136
                except qh_exception.NoCapacityError:
137
                    write("Reconciling failed because a limit has been "
138
                          "reached. Use --force to ignore the check.\n")
139
                    return
140

    
141
                qh.resolve_pending_commission('astakos', s)
142
                write("Fixed unsynced resources\n")
143

    
144
        if pending_exists:
145
            write("Found pending commissions. "
146
                  "This is probably a bug. Please report.\n")
147
        elif not (unsynced or unknown_user_exists):
148
            write("Everything in sync.\n")
149

    
150

    
151
def create_provision(provision_info):
152
    user, resource, db_value, qh_value = provision_info
153
    return (user, SYSTEM, resource), (db_value - qh_value)