Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (6.7 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
from datetime import datetime
36

    
37
from django.core.management.base import BaseCommand, CommandError
38
from django.db import transaction
39

    
40
from snf_django.utils import reconcile
41
from snf_django.management.utils import pprint_table
42
from astakos.im.models import Component, AstakosUser
43
from astakos.im import quotas
44
from astakos.im.functions import count_pending_app
45
import astakos.quotaholder_app.callpoint as qh
46
import astakos.quotaholder_app.exception as qh_exception
47

    
48

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

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

55
    """
56

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

    
74
    @transaction.commit_on_success
75
    def handle(self, *args, **options):
76
        write = self.stderr.write
77
        force = options['force']
78
        userid = options['userid']
79
        project = options['project']
80

    
81
        RESOURCES = [quotas.PENDING_APP_RESOURCE]
82

    
83
        try:
84
            astakos = Component.objects.get(name="astakos")
85
        except Component.DoesNotExist:
86
            raise CommandError("Component 'astakos' not found.")
87

    
88
        query = [userid] if userid is not None else None
89
        qh_holdings = quotas.service_get_quotas(astakos, query)
90
        query = [project] if project is not None else None
91
        qh_project_holdings = quotas.service_get_project_quotas(astakos, query)
92

    
93
        if userid is None:
94
            users = AstakosUser.objects.accepted().select_related(
95
                'base_project')
96
        else:
97
            try:
98
                user = AstakosUser.objects.get(uuid=userid)
99
            except AstakosUser.DoesNotExist:
100
                raise CommandError("There is no user with uuid '%s'." % userid)
101
            if not user.is_accepted():
102
                raise CommandError("%s is not an accepted user." % userid)
103
            users = [user]
104

    
105
        db_holdings = count_pending_app(users)
106

    
107
        db_project_holdings = {}
108
        for user, user_holdings in db_holdings.iteritems():
109
            db_project_holdings.update(user_holdings)
110

    
111
        unsynced_users, users_pending, users_unknown =\
112
            reconcile.check_users(self.stderr, RESOURCES,
113
                                  db_holdings, qh_holdings)
114

    
115
        unsynced_projects, projects_pending, projects_unknown =\
116
            reconcile.check_projects(self.stderr, RESOURCES,
117
                                     db_project_holdings, qh_project_holdings)
118
        pending_exists = users_pending or projects_pending
119
        unknown_exists = users_unknown or projects_unknown
120

    
121
        headers = ("Type", "Holder", "Source", "Resource",
122
                   "Astakos", "Quotaholder")
123
        unsynced = unsynced_users + unsynced_projects
124
        if unsynced:
125
            pprint_table(self.stdout, unsynced, headers)
126
            if options["fix"]:
127
                user_provisions = create_user_provisions(unsynced_users)
128
                project_provisions = create_project_provisions(
129
                    unsynced_projects)
130
                provisions = user_provisions + project_provisions
131
                name = ("client: reconcile-resources-astakos, time: %s"
132
                        % datetime.now())
133
                try:
134
                    s = qh.issue_commission('astakos', provisions,
135
                                            name=name, 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_exists):
148
            write("Everything in sync.\n")
149

    
150

    
151
def create_user_provisions(provision_list):
152
    provisions = []
153
    for _, holder, source, resource, db_value, qh_value in provision_list:
154
        value = db_value - qh_value
155
        provisions.append(
156
            quotas.mk_user_provision(holder, source, resource, value))
157
    return provisions
158

    
159

    
160
def create_project_provisions(provision_list):
161
    provisions = []
162
    for _, holder, _, resource, db_value, qh_value in provision_list:
163
        value = db_value - qh_value
164
        provisions.append(
165
            quotas.mk_project_provision(holder, resource, value))
166
    return provisions