Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / quotas / management / commands / reconcile-resources-cyclades.py @ 87726194

History | View | Annotate | Download (5.9 kB)

1 629acc65 Giorgos Korfiatis
# Copyright 2012, 2013 GRNET S.A. All rights reserved.
2 600a3d4d Christos Stavrakakis
#
3 600a3d4d Christos Stavrakakis
# Redistribution and use in source and binary forms, with or
4 600a3d4d Christos Stavrakakis
# without modification, are permitted provided that the following
5 600a3d4d Christos Stavrakakis
# conditions are met:
6 600a3d4d Christos Stavrakakis
#
7 600a3d4d Christos Stavrakakis
#   1. Redistributions of source code must retain the above
8 600a3d4d Christos Stavrakakis
#      copyright notice, this list of conditions and the following
9 600a3d4d Christos Stavrakakis
#      disclaimer.
10 600a3d4d Christos Stavrakakis
#
11 600a3d4d Christos Stavrakakis
#   2. Redistributions in binary form must reproduce the above
12 600a3d4d Christos Stavrakakis
#      copyright notice, this list of conditions and the following
13 600a3d4d Christos Stavrakakis
#      disclaimer in the documentation and/or other materials
14 600a3d4d Christos Stavrakakis
#      provided with the distribution.
15 600a3d4d Christos Stavrakakis
#
16 600a3d4d Christos Stavrakakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 600a3d4d Christos Stavrakakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 600a3d4d Christos Stavrakakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 600a3d4d Christos Stavrakakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 600a3d4d Christos Stavrakakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 600a3d4d Christos Stavrakakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 600a3d4d Christos Stavrakakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 600a3d4d Christos Stavrakakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 600a3d4d Christos Stavrakakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 600a3d4d Christos Stavrakakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 600a3d4d Christos Stavrakakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 600a3d4d Christos Stavrakakis
# POSSIBILITY OF SUCH DAMAGE.
28 600a3d4d Christos Stavrakakis
#
29 600a3d4d Christos Stavrakakis
# The views and conclusions contained in the software and
30 600a3d4d Christos Stavrakakis
# documentation are those of the authors and should not be
31 600a3d4d Christos Stavrakakis
# interpreted as representing official policies, either expressed
32 600a3d4d Christos Stavrakakis
# or implied, of GRNET S.A.
33 600a3d4d Christos Stavrakakis
34 9122ffab Christos Stavrakakis
from datetime import datetime
35 600a3d4d Christos Stavrakakis
from django.core.management.base import BaseCommand
36 600a3d4d Christos Stavrakakis
from optparse import make_option
37 600a3d4d Christos Stavrakakis
38 ccfbf77b Christos Stavrakakis
39 ccfbf77b Christos Stavrakakis
from synnefo import quotas
40 629acc65 Giorgos Korfiatis
from synnefo.quotas.util import (get_db_holdings, get_quotaholder_holdings,
41 629acc65 Giorgos Korfiatis
                                 transform_quotas)
42 d758784b Christos Stavrakakis
from snf_django.management.utils import pprint_table
43 600a3d4d Christos Stavrakakis
44 600a3d4d Christos Stavrakakis
45 600a3d4d Christos Stavrakakis
class Command(BaseCommand):
46 62c86226 Christos Stavrakakis
    help = """Reconcile resource usage of Astakos with Cyclades DB.
47 f8955d79 Georgios D. Tsoukalas

48 62c86226 Christos Stavrakakis
    Detect unsynchronized usage between Astakos and Cyclades DB resources and
49 ccfbf77b Christos Stavrakakis
    synchronize them if specified so.
50 f8955d79 Georgios D. Tsoukalas

51 f8955d79 Georgios D. Tsoukalas
    """
52 600a3d4d Christos Stavrakakis
    option_list = BaseCommand.option_list + (
53 600a3d4d Christos Stavrakakis
        make_option("--userid", dest="userid",
54 600a3d4d Christos Stavrakakis
                    default=None,
55 ccfbf77b Christos Stavrakakis
                    help="Reconcile resources only for this user"),
56 ccfbf77b Christos Stavrakakis
        make_option("--fix", dest="fix",
57 ccfbf77b Christos Stavrakakis
                    default=False,
58 ccfbf77b Christos Stavrakakis
                    action="store_true",
59 ccfbf77b Christos Stavrakakis
                    help="Synchronize Astakos quotas with Cyclades DB."),
60 ccfbf77b Christos Stavrakakis
        make_option("--force",
61 ccfbf77b Christos Stavrakakis
                    default=False,
62 ccfbf77b Christos Stavrakakis
                    action="store_true",
63 ccfbf77b Christos Stavrakakis
                    help="Override Astakos quotas. Force Astakos to impose"
64 ccfbf77b Christos Stavrakakis
                         " the Cyclades quota, independently of their value.")
65 600a3d4d Christos Stavrakakis
    )
66 600a3d4d Christos Stavrakakis
67 600a3d4d Christos Stavrakakis
    def handle(self, *args, **options):
68 600a3d4d Christos Stavrakakis
        write = self.stdout.write
69 600a3d4d Christos Stavrakakis
        userid = options['userid']
70 600a3d4d Christos Stavrakakis
71 ccfbf77b Christos Stavrakakis
        # Get holdings from Cyclades DB
72 ccfbf77b Christos Stavrakakis
        db_holdings = get_db_holdings(userid)
73 ccfbf77b Christos Stavrakakis
        # Get holdings from QuotaHolder
74 629acc65 Giorgos Korfiatis
        qh_holdings = get_quotaholder_holdings(userid)
75 600a3d4d Christos Stavrakakis
76 ccfbf77b Christos Stavrakakis
        users = set(db_holdings.keys())
77 ccfbf77b Christos Stavrakakis
        users.update(qh_holdings.keys())
78 ccfbf77b Christos Stavrakakis
        # Remove 'None' user
79 ccfbf77b Christos Stavrakakis
        users.discard(None)
80 600a3d4d Christos Stavrakakis
81 39d44eba Christos Stavrakakis
        if userid and userid not in users:
82 39d44eba Christos Stavrakakis
            write("User '%s' does not exist in Quotaholder!", userid)
83 39d44eba Christos Stavrakakis
            return
84 39d44eba Christos Stavrakakis
85 9b964eff Christos Stavrakakis
        pending_exists = False
86 9b964eff Christos Stavrakakis
        unknown_user_exists = False
87 600a3d4d Christos Stavrakakis
        unsynced = []
88 600a3d4d Christos Stavrakakis
        for user in users:
89 ccfbf77b Christos Stavrakakis
            db = db_holdings.get(user, {})
90 39d44eba Christos Stavrakakis
            try:
91 39d44eba Christos Stavrakakis
                qh_all = qh_holdings[user]
92 39d44eba Christos Stavrakakis
            except KeyError:
93 39d44eba Christos Stavrakakis
                write("User '%s' does not exist in Quotaholder!\n" %
94 39d44eba Christos Stavrakakis
                      user)
95 9b964eff Christos Stavrakakis
                unknown_user_exists = True
96 39d44eba Christos Stavrakakis
                continue
97 39d44eba Christos Stavrakakis
98 629acc65 Giorgos Korfiatis
            # Assuming only one source
99 ccfbf77b Christos Stavrakakis
            qh = qh_all.get(quotas.DEFAULT_SOURCE, {})
100 629acc65 Giorgos Korfiatis
            qh = transform_quotas(qh)
101 ccfbf77b Christos Stavrakakis
            for resource in quotas.RESOURCES:
102 ccfbf77b Christos Stavrakakis
                db_value = db.pop(resource, 0)
103 39d44eba Christos Stavrakakis
                try:
104 39d44eba Christos Stavrakakis
                    qh_value, _, qh_pending = qh[resource]
105 39d44eba Christos Stavrakakis
                except KeyError:
106 39d44eba Christos Stavrakakis
                    write("Resource '%s' does not exist in Quotaholder"
107 39d44eba Christos Stavrakakis
                          " for user '%s'!\n" % (resource, user))
108 39d44eba Christos Stavrakakis
                    continue
109 ccfbf77b Christos Stavrakakis
                if qh_pending:
110 ccfbf77b Christos Stavrakakis
                    write("Pending commission. User '%s', resource '%s'.\n" %
111 ccfbf77b Christos Stavrakakis
                          (user, resource))
112 9b964eff Christos Stavrakakis
                    pending_exists = True
113 629acc65 Giorgos Korfiatis
                    continue
114 ccfbf77b Christos Stavrakakis
                if db_value != qh_value:
115 ccfbf77b Christos Stavrakakis
                    data = (user, resource, db_value, qh_value)
116 629acc65 Giorgos Korfiatis
                    unsynced.append(data)
117 629acc65 Giorgos Korfiatis
118 ccfbf77b Christos Stavrakakis
        headers = ("User", "Resource", "Database", "Quotaholder")
119 600a3d4d Christos Stavrakakis
        if unsynced:
120 600a3d4d Christos Stavrakakis
            pprint_table(self.stderr, unsynced, headers)
121 ccfbf77b Christos Stavrakakis
            if options["fix"]:
122 ccfbf77b Christos Stavrakakis
                qh = quotas.Quotaholder.get()
123 ccfbf77b Christos Stavrakakis
                request = {}
124 ccfbf77b Christos Stavrakakis
                request["force"] = options["force"]
125 ccfbf77b Christos Stavrakakis
                request["auto_accept"] = True
126 9122ffab Christos Stavrakakis
                request["name"] = \
127 9122ffab Christos Stavrakakis
                    ("client: reconcile-resources-cyclades, time: %s"
128 9122ffab Christos Stavrakakis
                     % datetime.now())
129 ccfbf77b Christos Stavrakakis
                request["provisions"] = map(create_provision, unsynced)
130 205cc8d3 Giorgos Korfiatis
                try:
131 e407f159 Ilias Tsitsimpis
                    qh.issue_commission(request)
132 87726194 Giorgos Korfiatis
                except quotas.errors.QuotaLimit:
133 205cc8d3 Giorgos Korfiatis
                    write("Reconciling failed because a limit has been "
134 205cc8d3 Giorgos Korfiatis
                          "reached. Use --force to ignore the check.\n")
135 205cc8d3 Giorgos Korfiatis
                    return
136 9b964eff Christos Stavrakakis
                write("Fixed unsynced resources\n")
137 9b964eff Christos Stavrakakis
138 9b964eff Christos Stavrakakis
        if pending_exists:
139 9b964eff Christos Stavrakakis
            write("Found pending commissions. Run 'snf-manage"
140 9b964eff Christos Stavrakakis
                  " reconcile-commissions-cyclades'\n")
141 9b964eff Christos Stavrakakis
        elif not (unsynced or unknown_user_exists):
142 ccfbf77b Christos Stavrakakis
            write("Everything in sync.\n")
143 ccfbf77b Christos Stavrakakis
144 ccfbf77b Christos Stavrakakis
145 ccfbf77b Christos Stavrakakis
def create_provision(provision_info):
146 ccfbf77b Christos Stavrakakis
    user, resource, db_value, qh_value = provision_info
147 ccfbf77b Christos Stavrakakis
    return {"holder": user,
148 ccfbf77b Christos Stavrakakis
            "source": quotas.DEFAULT_SOURCE,
149 ccfbf77b Christos Stavrakakis
            "resource": resource,
150 ccfbf77b Christos Stavrakakis
            "quantity": db_value - qh_value}