Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-app / pithos / api / management / commands / reconcile-resources-pithos.py @ bab17726

History | View | Annotate | Download (6.4 kB)

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

52 30aca88f Sofia Papagiannaki
    Detect unsynchronized usage between Astakos and Pithos DB resources and
53 30aca88f Sofia Papagiannaki
    synchronize them if specified so.
54 d1e7d2b4 Sofia Papagiannaki

55 30aca88f Sofia Papagiannaki
    """
56 d1e7d2b4 Sofia Papagiannaki
    option_list = NoArgsCommand.option_list + (
57 30aca88f Sofia Papagiannaki
        make_option("--userid", dest="userid",
58 30aca88f Sofia Papagiannaki
                    default=None,
59 30aca88f Sofia Papagiannaki
                    help="Reconcile resources only for this user"),
60 30aca88f Sofia Papagiannaki
        make_option("--fix", dest="fix",
61 d1e7d2b4 Sofia Papagiannaki
                    default=False,
62 d1e7d2b4 Sofia Papagiannaki
                    action="store_true",
63 30aca88f Sofia Papagiannaki
                    help="Synchronize Astakos quotas with Pithos DB."),
64 30aca88f Sofia Papagiannaki
        make_option("--force",
65 d1e7d2b4 Sofia Papagiannaki
                    default=False,
66 30aca88f Sofia Papagiannaki
                    action="store_true",
67 bab17726 Sofia Papagiannaki
                    help="Override Astakos quotas. Force Astakos to impose "
68 bab17726 Sofia Papagiannaki
                         "the Pithos quota, independently of their value.")
69 d1e7d2b4 Sofia Papagiannaki
    )
70 d1e7d2b4 Sofia Papagiannaki
71 d1e7d2b4 Sofia Papagiannaki
    def handle_noargs(self, **options):
72 d1e7d2b4 Sofia Papagiannaki
        try:
73 bab17726 Sofia Papagiannaki
            qh_result = backend.astakosclient.service_get_quotas(
74 bab17726 Sofia Papagiannaki
                backend.service_token)
75 bab17726 Sofia Papagiannaki
76 30aca88f Sofia Papagiannaki
            users = (options['userid'],) if options['userid'] else None
77 30aca88f Sofia Papagiannaki
            account_nodes = backend.node.node_accounts(users)
78 d1e7d2b4 Sofia Papagiannaki
            if not account_nodes:
79 d1e7d2b4 Sofia Papagiannaki
                raise CommandError('No users found.')
80 d1e7d2b4 Sofia Papagiannaki
81 d1e7d2b4 Sofia Papagiannaki
            db_usage = {}
82 d1e7d2b4 Sofia Papagiannaki
            for path, node in account_nodes:
83 d1e7d2b4 Sofia Papagiannaki
                size = backend.node.node_account_usage(node, CLUSTER_NORMAL)
84 d1e7d2b4 Sofia Papagiannaki
                db_usage[path] = size or 0
85 d1e7d2b4 Sofia Papagiannaki
86 bab17726 Sofia Papagiannaki
            users = set(qh_result.keys())
87 bab17726 Sofia Papagiannaki
            users.update(db_usage.keys())
88 d1e7d2b4 Sofia Papagiannaki
89 30aca88f Sofia Papagiannaki
            pending_exists = False
90 30aca88f Sofia Papagiannaki
            unknown_user_exists = False
91 30aca88f Sofia Papagiannaki
            unsynced = []
92 bab17726 Sofia Papagiannaki
            for uuid in users:
93 bab17726 Sofia Papagiannaki
                db_value = db_usage.get(uuid, 0)
94 d1e7d2b4 Sofia Papagiannaki
                try:
95 30aca88f Sofia Papagiannaki
                    qh_all = qh_result[uuid]
96 d1e7d2b4 Sofia Papagiannaki
                except KeyError:
97 30aca88f Sofia Papagiannaki
                    self.stdout.write(
98 30aca88f Sofia Papagiannaki
                        "User '%s' does not exist in Quotaholder!\n" % uuid
99 30aca88f Sofia Papagiannaki
                    )
100 30aca88f Sofia Papagiannaki
                    unknown_user_exists = True
101 d1e7d2b4 Sofia Papagiannaki
                    continue
102 d1e7d2b4 Sofia Papagiannaki
                else:
103 30aca88f Sofia Papagiannaki
                    qh = qh_all.get(DEFAULT_SOURCE, {})
104 bab17726 Sofia Papagiannaki
                    for resource in [r['name'] for r in resources]:
105 bab17726 Sofia Papagiannaki
                        try:
106 bab17726 Sofia Papagiannaki
                            qh_resource = qh[resource]
107 bab17726 Sofia Papagiannaki
                        except KeyError:
108 bab17726 Sofia Papagiannaki
                            self.stdout.write(
109 bab17726 Sofia Papagiannaki
                                "Resource '%s' does not exist in Quotaholder "
110 bab17726 Sofia Papagiannaki
                                "for user '%s'!\n" % (resource, uuid))
111 bab17726 Sofia Papagiannaki
                            continue
112 bab17726 Sofia Papagiannaki
113 bab17726 Sofia Papagiannaki
                        if qh_resource['pending']:
114 bab17726 Sofia Papagiannaki
                            self.stdout.write(
115 bab17726 Sofia Papagiannaki
                                "Pending commission. "
116 bab17726 Sofia Papagiannaki
                                "User '%s', resource '%s'.\n" %
117 bab17726 Sofia Papagiannaki
                                (uuid, resource)
118 bab17726 Sofia Papagiannaki
                            )
119 bab17726 Sofia Papagiannaki
                            pending_exists = True
120 bab17726 Sofia Papagiannaki
                            continue
121 30aca88f Sofia Papagiannaki
122 bab17726 Sofia Papagiannaki
                        qh_value = qh_resource['usage']
123 30aca88f Sofia Papagiannaki
124 bab17726 Sofia Papagiannaki
                        if  db_value != qh_value:
125 bab17726 Sofia Papagiannaki
                            data = (uuid, resource, db_value, qh_value)
126 bab17726 Sofia Papagiannaki
                            unsynced.append(data)
127 30aca88f Sofia Papagiannaki
128 30aca88f Sofia Papagiannaki
            if unsynced:
129 30aca88f Sofia Papagiannaki
                headers = ("User", "Resource", "Database", "Quotaholder")
130 30aca88f Sofia Papagiannaki
                utils.pprint_table(self.stdout, unsynced, headers)
131 30aca88f Sofia Papagiannaki
                if options['fix']:
132 30aca88f Sofia Papagiannaki
                    request = {}
133 30aca88f Sofia Papagiannaki
                    request['force'] = options['force']
134 30aca88f Sofia Papagiannaki
                    request['auto_accept'] = True
135 c8a79b3a Giorgos Korfiatis
                    request['name'] = "RECONCILE"
136 30aca88f Sofia Papagiannaki
                    request['provisions'] = map(create_provision, unsynced)
137 205cc8d3 Giorgos Korfiatis
                    try:
138 205cc8d3 Giorgos Korfiatis
                        backend.astakosclient.issue_commission(
139 bab17726 Sofia Papagiannaki
                            backend.service_token, request)
140 205cc8d3 Giorgos Korfiatis
                    except QuotaLimit:
141 205cc8d3 Giorgos Korfiatis
                        self.stdout.write(
142 205cc8d3 Giorgos Korfiatis
                            "Reconciling failed because a limit has been "
143 205cc8d3 Giorgos Korfiatis
                            "reached. Use --force to ignore the check.\n")
144 205cc8d3 Giorgos Korfiatis
                        return
145 205cc8d3 Giorgos Korfiatis
146 30aca88f Sofia Papagiannaki
            if pending_exists:
147 30aca88f Sofia Papagiannaki
                self.stdout.write(
148 30aca88f Sofia Papagiannaki
                    "Found pending commissions. Run 'snf-manage"
149 30aca88f Sofia Papagiannaki
                    " reconcile-commissions-pithos'\n"
150 30aca88f Sofia Papagiannaki
                )
151 30aca88f Sofia Papagiannaki
            elif not (unsynced or unknown_user_exists):
152 30aca88f Sofia Papagiannaki
                self.stdout.write("Everything in sync.\n")
153 d1e7d2b4 Sofia Papagiannaki
        finally:
154 d1e7d2b4 Sofia Papagiannaki
            backend.close()
155 30aca88f Sofia Papagiannaki
156 30aca88f Sofia Papagiannaki
157 30aca88f Sofia Papagiannaki
def create_provision(provision_info):
158 30aca88f Sofia Papagiannaki
    user, resource, db_value, qh_value = provision_info
159 30aca88f Sofia Papagiannaki
    return {"holder": user,
160 30aca88f Sofia Papagiannaki
            "source": DEFAULT_SOURCE,
161 30aca88f Sofia Papagiannaki
            "resource": resource,
162 30aca88f Sofia Papagiannaki
            "quantity": db_value - qh_value}