Commit 3a4db0d3 authored by Ilias Tsitsimpis's avatar Ilias Tsitsimpis

astakosclient: Human readable details message

In case of QuotaLimit exception, fill 'details' with a human readable
message and put the actual JSON response in the 'response' variable.
Synnefo UI shows the 'details' field of an exception to the user so it
has to be human readable.
parent 4b6f82fb
...@@ -30,7 +30,8 @@ except ImportError: ...@@ -30,7 +30,8 @@ except ImportError:
import json import json
from astakosclient.utils import \ 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 \ from astakosclient.errors import \
AstakosClientException, Unauthorized, BadRequest, NotFound, Forbidden, \ AstakosClientException, Unauthorized, BadRequest, NotFound, Forbidden, \
NoUserName, NoUUID, BadValue, QuotaLimit, InvalidResponse, NoEndpoints, \ NoUserName, NoUUID, BadValue, QuotaLimit, InvalidResponse, NoEndpoints, \
...@@ -301,7 +302,8 @@ class AstakosClient(object): ...@@ -301,7 +302,8 @@ class AstakosClient(object):
elif status == 404: elif status == 404:
raise NotFound(message, data) raise NotFound(message, data)
elif status < 200 or status >= 300: elif status < 200 or status >= 300:
raise AstakosClientException(message, data, status) raise AstakosClientException(
message=message, status=status, response=data)
try: try:
if data: if data:
...@@ -311,7 +313,7 @@ class AstakosClient(object): ...@@ -311,7 +313,7 @@ class AstakosClient(object):
except Exception as err: except Exception as err:
msg = "Cannot parse response \"%r\" with simplejson: %s" msg = "Cannot parse response \"%r\" with simplejson: %s"
self.logger.error(msg % (data, str(err))) 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``) # do a POST to ``API_USERCATALOGS`` (or ``API_SERVICE_USERCATALOGS``)
...@@ -328,7 +330,7 @@ class AstakosClient(object): ...@@ -328,7 +330,7 @@ class AstakosClient(object):
msg = "_uuid_catalog request returned %r. No uuid_catalog found" \ msg = "_uuid_catalog request returned %r. No uuid_catalog found" \
% data % data
self.logger.error(msg) self.logger.error(msg)
raise AstakosClientException(msg) raise AstakosClientException(message=msg, response=data)
def get_usernames(self, uuids): def get_usernames(self, uuids):
"""Return a uuid_catalog dictionary for the given uuids """Return a uuid_catalog dictionary for the given uuids
...@@ -379,7 +381,7 @@ class AstakosClient(object): ...@@ -379,7 +381,7 @@ class AstakosClient(object):
msg = "_displayname_catalog request returned %r. " \ msg = "_displayname_catalog request returned %r. " \
"No displayname_catalog found" % data "No displayname_catalog found" % data
self.logger.error(msg) self.logger.error(msg)
raise AstakosClientException(msg) raise AstakosClientException(message=msg, response=data)
def get_uuids(self, display_names): def get_uuids(self, display_names):
"""Return a displayname_catalog for the given names """Return a displayname_catalog for the given names
...@@ -601,7 +603,17 @@ class AstakosClient(object): ...@@ -601,7 +603,17 @@ class AstakosClient(object):
method="POST") method="POST")
except AstakosClientException as err: except AstakosClientException as err:
if err.status == 413: 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: else:
raise raise
...@@ -611,7 +623,7 @@ class AstakosClient(object): ...@@ -611,7 +623,7 @@ class AstakosClient(object):
msg = "issue_commission_core request returned %r. " + \ msg = "issue_commission_core request returned %r. " + \
"No serial found" % response "No serial found" % response
self.logger.error(msg) self.logger.error(msg)
raise AstakosClientException(msg) raise AstakosClientException(message=msg, response=response)
def _mk_user_provision(self, holder, source, resource, quantity): def _mk_user_provision(self, holder, source, resource, quantity):
holder = "user:" + holder holder = "user:" + holder
......
...@@ -20,10 +20,12 @@ Astakos Client Exceptions ...@@ -20,10 +20,12 @@ Astakos Client Exceptions
class AstakosClientException(Exception): class AstakosClientException(Exception):
"""Base AstakosClientException Class""" """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.message = message
self.details = details self.details = details
self.errobject = errobject self.errobject = errobject
self.response = response
if not hasattr(self, 'status'): if not hasattr(self, 'status'):
self.status = status self.status = status
super(AstakosClientException, super(AstakosClientException,
...@@ -46,8 +48,6 @@ class BadValue(AstakosClientException): ...@@ -46,8 +48,6 @@ class BadValue(AstakosClientException):
class InvalidResponse(AstakosClientException): class InvalidResponse(AstakosClientException):
"""Return simplejson parse Exception as AstakosClient one""" """Return simplejson parse Exception as AstakosClient one"""
def __init__(self, message, details):
super(InvalidResponse, self).__init__(message, details)
class BadRequest(AstakosClientException): class BadRequest(AstakosClientException):
......
...@@ -102,3 +102,35 @@ def check_input(function_name, logger, **kwargs): ...@@ -102,3 +102,35 @@ def check_input(function_name, logger, **kwargs):
def join_urls(url_a, url_b): def join_urls(url_a, url_b):
"""Join_urls from synnefo.lib""" """Join_urls from synnefo.lib"""
return url_a.rstrip("/") + "/" + url_b.lstrip("/") 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
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # 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 synnefo.db import transaction
from snf_django.lib.api import faults from snf_django.lib.api import faults
...@@ -67,7 +66,7 @@ class AstakosClientExceptionHandler(object): ...@@ -67,7 +66,7 @@ class AstakosClientExceptionHandler(object):
def __enter__(self): def __enter__(self):
pass pass
def check_notFound(self): def check_not_found(self):
if not self.user or not self.projects: if not self.user or not self.projects:
return return
try: try:
...@@ -90,10 +89,9 @@ class AstakosClientExceptionHandler(object): ...@@ -90,10 +89,9 @@ class AstakosClientExceptionHandler(object):
if not isinstance(value, errors.AstakosClientException): if not isinstance(value, errors.AstakosClientException):
return False # reraise return False # reraise
if exc_type is errors.QuotaLimit: if exc_type is errors.QuotaLimit:
msg, details = render_overlimit_exception(value) raise faults.OverLimit(value.message, details=value.details)
raise faults.OverLimit(msg, details=details)
if exc_type is errors.NotFound: if exc_type is errors.NotFound:
self.check_notFound() self.check_not_found()
log.exception("Unexpected error %s" % value.message) log.exception("Unexpected error %s" % value.message)
raise faults.InternalServerError("Unexpected error") raise faults.InternalServerError("Unexpected error")
...@@ -251,33 +249,6 @@ def get_quotaholder_pending(): ...@@ -251,33 +249,6 @@ def get_quotaholder_pending():
return pending_serials 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 @transaction.commit_on_success
def issue_and_accept_commission(resource, action="BUILD", action_fields=None): def issue_and_accept_commission(resource, action="BUILD", action_fields=None):
"""Issue and accept a commission to Quotaholder. """Issue and accept a commission to Quotaholder.
...@@ -393,7 +364,7 @@ def get_commission_info(resource, action, action_fields=None): ...@@ -393,7 +364,7 @@ def get_commission_info(resource, action, action_fields=None):
get_volume_size_delta(action, db_volume, info) get_volume_size_delta(action, db_volume, info)
return resources return resources
else: else:
#["CONNECT", "DISCONNECT", "SET_FIREWALL_PROFILE"]: # ["CONNECT", "DISCONNECT", "SET_FIREWALL_PROFILE"]:
return None return None
elif isinstance(resource, Network): elif isinstance(resource, Network):
resources = {(project, "cyclades.network.private"): 1} resources = {(project, "cyclades.network.private"): 1}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment