diff --git a/astakosclient/astakosclient/__init__.py b/astakosclient/astakosclient/__init__.py index 7146b2c24e746ec74ab533ed97b9005cd7731cf7..161be1103d56561fa7b0e29baba11430345e3d57 100644 --- a/astakosclient/astakosclient/__init__.py +++ b/astakosclient/astakosclient/__init__.py @@ -30,7 +30,8 @@ except ImportError: import json from astakosclient.utils import \ - retry_dec, scheme_to_class, parse_request, check_input, join_urls + retry_dec, scheme_to_class, parse_request, check_input, join_urls, \ + render_overlimit_exception from astakosclient.errors import \ AstakosClientException, Unauthorized, BadRequest, NotFound, Forbidden, \ NoUserName, NoUUID, BadValue, QuotaLimit, InvalidResponse, NoEndpoints, \ @@ -301,7 +302,8 @@ class AstakosClient(object): elif status == 404: raise NotFound(message, data) elif status < 200 or status >= 300: - raise AstakosClientException(message, data, status) + raise AstakosClientException( + message=message, status=status, response=data) try: if data: @@ -311,7 +313,7 @@ class AstakosClient(object): except Exception as err: msg = "Cannot parse response \"%r\" with simplejson: %s" self.logger.error(msg % (data, str(err))) - raise InvalidResponse(str(err), data) + raise InvalidResponse(message=str(err), response=data) # ---------------------------------- # do a POST to ``API_USERCATALOGS`` (or ``API_SERVICE_USERCATALOGS``) @@ -328,7 +330,7 @@ class AstakosClient(object): msg = "_uuid_catalog request returned %r. No uuid_catalog found" \ % data self.logger.error(msg) - raise AstakosClientException(msg) + raise AstakosClientException(message=msg, response=data) def get_usernames(self, uuids): """Return a uuid_catalog dictionary for the given uuids @@ -379,7 +381,7 @@ class AstakosClient(object): msg = "_displayname_catalog request returned %r. " \ "No displayname_catalog found" % data self.logger.error(msg) - raise AstakosClientException(msg) + raise AstakosClientException(message=msg, response=data) def get_uuids(self, display_names): """Return a displayname_catalog for the given names @@ -601,7 +603,17 @@ class AstakosClient(object): method="POST") except AstakosClientException as err: if err.status == 413: - raise QuotaLimit(err.message, err.details) + try: + msg, details = render_overlimit_exception( + err.response, self.logger) + except Exception as perr: + self.logger.error("issue_commission request returned '413'" + " but response '%r' could not be parsed:" + " %s", err.response, str(perr)) + msg, details = err.message, "" + raise QuotaLimit(message=msg, + details=details, + response=err.response) else: raise @@ -611,7 +623,7 @@ class AstakosClient(object): msg = "issue_commission_core request returned %r. " + \ "No serial found" % response self.logger.error(msg) - raise AstakosClientException(msg) + raise AstakosClientException(message=msg, response=response) def _mk_user_provision(self, holder, source, resource, quantity): holder = "user:" + holder diff --git a/astakosclient/astakosclient/errors.py b/astakosclient/astakosclient/errors.py index 0a63cd9056cf49b94e5169fed1e3eb1ac67fbeae..fdfd9fc8b41d3355b5e3a4ccc7f355f0a61d07b3 100644 --- a/astakosclient/astakosclient/errors.py +++ b/astakosclient/astakosclient/errors.py @@ -20,10 +20,12 @@ Astakos Client Exceptions class AstakosClientException(Exception): """Base AstakosClientException Class""" - def __init__(self, message='', details='', status=500, errobject=None): + def __init__(self, message='', details='', status=500, + response=None, errobject=None): self.message = message self.details = details self.errobject = errobject + self.response = response if not hasattr(self, 'status'): self.status = status super(AstakosClientException, @@ -46,8 +48,6 @@ class BadValue(AstakosClientException): class InvalidResponse(AstakosClientException): """Return simplejson parse Exception as AstakosClient one""" - def __init__(self, message, details): - super(InvalidResponse, self).__init__(message, details) class BadRequest(AstakosClientException): diff --git a/astakosclient/astakosclient/utils.py b/astakosclient/astakosclient/utils.py index 6da60c020acddd900762aa8dff7c83771097dbeb..52fc5d82458fe3123bb37bc0542a9f2cdd0c672a 100644 --- a/astakosclient/astakosclient/utils.py +++ b/astakosclient/astakosclient/utils.py @@ -102,3 +102,35 @@ def check_input(function_name, logger, **kwargs): def join_urls(url_a, url_b): """Join_urls from synnefo.lib""" return url_a.rstrip("/") + "/" + url_b.lstrip("/") + + +def render_overlimit_exception(response, logger): + """Render a human readable message for QuotaLimit Exception""" + resource_name = { + "cyclades.disk": "Disk", + "cyclades.vm": "Virtual Machine", + "cyclades.cpu": "CPU", + "cyclades.ram": "RAM", + "cyclades.floating_ip": "Floating IP address", + "cyclades.network.private": "Private Network", + "pithos.diskspace": "Storage space", + "astakos.pending_app": "Pending Applications" + } + response = json.loads(response) + data = response['overLimit']['data'] + usage = data["usage"] + limit = data["limit"] + available = limit - usage + provision = data['provision'] + requested = provision['quantity'] + resource = provision['resource'] + try: + resource = resource_name[resource] + except KeyError: + logger.error("Unknown resource name '%s'", resource) + + msg = "Resource Limit Exceeded for your account." + details = "Limit for resource '%s' exceeded for your account."\ + " Available: %s, Requested: %s"\ + % (resource, available, requested) + return msg, details diff --git a/snf-cyclades-app/synnefo/quotas/__init__.py b/snf-cyclades-app/synnefo/quotas/__init__.py index 7c3c361e2d7c3b9e95be0bed67395231af30a4c3..6b780c1ee2e4abfda1f1f666ecdbbf2c140f3f0d 100644 --- a/snf-cyclades-app/synnefo/quotas/__init__.py +++ b/snf-cyclades-app/synnefo/quotas/__init__.py @@ -13,7 +13,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -from django.utils import simplejson as json from synnefo.db import transaction from snf_django.lib.api import faults @@ -67,7 +66,7 @@ class AstakosClientExceptionHandler(object): def __enter__(self): pass - def check_notFound(self): + def check_not_found(self): if not self.user or not self.projects: return try: @@ -90,10 +89,9 @@ class AstakosClientExceptionHandler(object): if not isinstance(value, errors.AstakosClientException): return False # reraise if exc_type is errors.QuotaLimit: - msg, details = render_overlimit_exception(value) - raise faults.OverLimit(msg, details=details) + raise faults.OverLimit(value.message, details=value.details) if exc_type is errors.NotFound: - self.check_notFound() + self.check_not_found() log.exception("Unexpected error %s" % value.message) raise faults.InternalServerError("Unexpected error") @@ -251,33 +249,6 @@ def get_quotaholder_pending(): return pending_serials -def render_overlimit_exception(e): - resource_name = {"vm": "Virtual Machine", - "cpu": "CPU", - "ram": "RAM", - "network.private": "Private Network", - "floating_ip": "Floating IP address"} - details = json.loads(e.details) - data = details['overLimit']['data'] - usage = data["usage"] - limit = data["limit"] - available = limit - usage - provision = data['provision'] - requested = provision['quantity'] - resource = provision['resource'] - res = resource.replace("cyclades.", "", 1) - try: - resource = resource_name[res] - except KeyError: - resource = res - - msg = "Resource Limit Exceeded for your account." - details = "Limit for resource '%s' exceeded for your account."\ - " Available: %s, Requested: %s"\ - % (resource, available, requested) - return msg, details - - @transaction.commit_on_success def issue_and_accept_commission(resource, action="BUILD", action_fields=None): """Issue and accept a commission to Quotaholder. @@ -393,7 +364,7 @@ def get_commission_info(resource, action, action_fields=None): get_volume_size_delta(action, db_volume, info) return resources else: - #["CONNECT", "DISCONNECT", "SET_FIREWALL_PROFILE"]: + # ["CONNECT", "DISCONNECT", "SET_FIREWALL_PROFILE"]: return None elif isinstance(resource, Network): resources = {(project, "cyclades.network.private"): 1}