diff --git a/snf-cyclades-app/synnefo/quotas/__init__.py b/snf-cyclades-app/synnefo/quotas/__init__.py index 4451d4b1cddf2b61f319f55d2ed3e89d18bda969..747c8d28a6478df5d76838af8751034aba322713 100644 --- a/snf-cyclades-app/synnefo/quotas/__init__.py +++ b/snf-cyclades-app/synnefo/quotas/__init__.py @@ -148,7 +148,7 @@ def issue_commission(user, source, provisions, try: serial = qh.issue_one_commission(ASTAKOS_TOKEN, user, source, provisions, - force, auto_accept) + force=force, auto_accept=auto_accept) except QuotaLimit as e: msg, details = render_overlimit_exception(e) raise faults.OverLimit(msg, details=details) diff --git a/snf-cyclades-app/synnefo/quotas/management/commands/cyclades-reset-usage.py b/snf-cyclades-app/synnefo/quotas/management/commands/cyclades-reset-usage.py deleted file mode 100644 index e9bafd8c3e1b7f09038a584c5bdb3de815dc00cf..0000000000000000000000000000000000000000 --- a/snf-cyclades-app/synnefo/quotas/management/commands/cyclades-reset-usage.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright 2012, 2013 GRNET S.A. All rights reserved. -# -# Redistribution and use in source and binary forms, with or -# without modification, are permitted provided that the following -# conditions are met: -# -# 1. Redistributions of source code must retain the above -# copyright notice, this list of conditions and the following -# disclaimer. -# -# 2. Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following -# disclaimer in the documentation and/or other materials -# provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS -# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED -# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# The views and conclusions contained in the software and -# documentation are those of the authors and should not be -# interpreted as representing official policies, either expressed -# or implied, of GRNET S.A. - -from django.core.management.base import BaseCommand -from optparse import make_option - -from synnefo.quotas import Quotaholder -from synnefo.quotas.util import get_db_holdings - - -class Command(BaseCommand): - help = """Reset cyclades.* usage values in Quotaholder""" - output_transaction = True - option_list = BaseCommand.option_list + ( - make_option("--userid", dest="userid", - default=None, - help="Verify quotas only for this user"), - make_option("--dry-run", dest="dry_run", - action='store_true', - default=False), - ) - - def handle(self, *args, **options): - userid = options['userid'] - - users = [userid] if userid else None - # Get info from DB - db_holdings = get_db_holdings(users) - - # Create commissions - qh = Quotaholder.get() - for user, resources in db_holdings.items(): - if not user: - continue - reset_holding = [] - for res, val in resources.items(): - reset_holding.append((user, "cyclades." + res, "1", val, 0, - 0, 0)) - if not options['dry_run']: - try: - qh.reset_holding(context={}, - reset_holding=reset_holding) - except Exception as e: - self.stderr.write("Can not set up holding:%s" % e) - else: - self.stdout.write("Reseting holding: %s\n" % reset_holding) diff --git a/snf-cyclades-app/synnefo/quotas/management/commands/cyclades-usage-verify.py b/snf-cyclades-app/synnefo/quotas/management/commands/cyclades-usage-verify.py index 61cdf100133a790df2a072b7975aedb0d551cfde..55aa82871e6ed6d95aeeea577a97cc52be18fd4c 100644 --- a/snf-cyclades-app/synnefo/quotas/management/commands/cyclades-usage-verify.py +++ b/snf-cyclades-app/synnefo/quotas/management/commands/cyclades-usage-verify.py @@ -34,68 +34,85 @@ from django.core.management.base import BaseCommand from optparse import make_option -from synnefo.quotas import DEFAULT_SOURCE + +from synnefo import quotas from synnefo.quotas.util import (get_db_holdings, get_quotaholder_holdings, transform_quotas) from synnefo.webproject.management.utils import pprint_table +from synnefo.settings import CYCLADES_ASTAKOS_SERVICE_TOKEN as ASTAKOS_TOKEN class Command(BaseCommand): - help = """ - Verify that cyclades.* resource usage. + help = """Reconcile quotas of Astakos with Cyclades DB. - Verify that usage calculated from Cyclades DB agrees with the usage - recorded in the effective quota database (Quotaholder) + Detect unsynchronized quotas between Astakos and Cyclades DB and + synchronize them if specified so. """ - output_transaction = True option_list = BaseCommand.option_list + ( make_option("--userid", dest="userid", default=None, - help="Verify usage only for this user"), + help="Reconcile resources only for this user"), + make_option("--fix", dest="fix", + default=False, + action="store_true", + help="Synchronize Astakos quotas with Cyclades DB."), + make_option("--force", + default=False, + action="store_true", + help="Override Astakos quotas. Force Astakos to impose" + " the Cyclades quota, independently of their value.") ) def handle(self, *args, **options): write = self.stdout.write userid = options['userid'] - users = [userid] if userid else None - # Get info from DB - db_holdings = get_db_holdings(users) - users = db_holdings.keys() + # Get holdings from Cyclades DB + db_holdings = get_db_holdings(userid) + # Get holdings from QuotaHolder qh_holdings = get_quotaholder_holdings(userid) - qh_users = qh_holdings.keys() - if len(qh_users) < len(users): - for u in set(users) - set(qh_users): - write("Unknown entity: %s\n" % u) - users = qh_users + users = set(db_holdings.keys()) + users.update(qh_holdings.keys()) + # Remove 'None' user + users.discard(None) - headers = ("User", "Resource", "Database", "Quotaholder") unsynced = [] for user in users: - db = db_holdings[user] - qh_all = qh_holdings[user] + db = db_holdings.get(user, {}) + qh_all = qh_holdings.get(user, {}) # Assuming only one source - qh = qh_all[DEFAULT_SOURCE] + qh = qh_all.get(quotas.DEFAULT_SOURCE, {}) qh = transform_quotas(qh) - - for resource, (value, value1) in qh.iteritems: - db_value = db.pop(resource, None) - if value != value1: - write("Commission pending for %s" - % str((user, resource))) + for resource in quotas.RESOURCES: + db_value = db.pop(resource, 0) + qh_value, _, qh_pending = qh.pop(resource, (0, 0)) + if qh_pending: + write("Pending commission. User '%s', resource '%s'.\n" % + (user, resource)) continue - if db_value is None: - write("Resource %s exists in QH for %s but not in DB\n" - % (resource, user)) - elif db_value != value: - data = (user, resource, str(db_value), str(value)) + if db_value != qh_value: + data = (user, resource, db_value, qh_value) unsynced.append(data) - for resource, db_value in db.iteritems(): - write("Resource %s exists in DB for %s but not in QH\n" - % (resource, user)) - + headers = ("User", "Resource", "Database", "Quotaholder") if unsynced: pprint_table(self.stderr, unsynced, headers) + if options["fix"]: + qh = quotas.Quotaholder.get() + request = {} + request["force"] = options["force"] + request["auto_accept"] = True + request["provisions"] = map(create_provision, unsynced) + qh.issue_commission(ASTAKOS_TOKEN, request) + else: + write("Everything in sync.\n") + + +def create_provision(provision_info): + user, resource, db_value, qh_value = provision_info + return {"holder": user, + "source": quotas.DEFAULT_SOURCE, + "resource": resource, + "quantity": db_value - qh_value}