# Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """Join base exception handling. Based on exception.py from cinder project """ from oslo_log import log as logging import six import webob.exc from webob.util import status_generic_reasons from webob.util import status_reasons LOG = logging.getLogger(__name__) class ConvertedException(webob.exc.WSGIHTTPException): def __init__(self, code=500, title="", explanation=""): self.code = code # There is a strict rule about constructing status line for HTTP: # '...Status-Line, consisting of the protocol version followed by a # numeric status code and its associated textual phrase, with each # element separated by SP characters' # (http://www.faqs.org/rfcs/rfc2616.html) # 'code' and 'title' can not be empty because they correspond # to numeric status code and its associated text if title: self.title = title else: try: self.title = status_reasons[self.code] except KeyError: generic_code = self.code // 100 self.title = status_generic_reasons[generic_code] self.explanation = explanation super(ConvertedException, self).__init__() class JoinException(Exception): """Base Join Exception To correctly use this class, inherit from it and define a 'message' property. That message will get printf'd with the keyword arguments provided to the constructor. """ message = "An unknown exception occurred." code = 500 headers = {} safe = False def __init__(self, message=None, **kwargs): self.kwargs = kwargs self.kwargs['message'] = message if 'code' not in self.kwargs: try: self.kwargs['code'] = self.code except AttributeError: pass for k, v in self.kwargs.items(): if isinstance(v, Exception): self.kwargs[k] = six.text_type(v) if self._should_format(): try: message = self.message % kwargs except Exception: # pylint: disable=broad-except # kwargs doesn't match a variable in the message # log the issue and the kwargs LOG.exception('Exception in string format operation') for name, value in kwargs.items(): LOG.error("%(name)s: %(value)s", {'name': name, 'value': value}) message = self.message elif isinstance(message, Exception): message = six.text_type(message) # NOTE(luisg): We put the actual message in 'msg' so that we can access # it, because if we try to access the message via 'message' it will be # overshadowed by the class' message attribute self.msg = message super(JoinException, self).__init__(message) def _should_format(self): return self.kwargs['message'] is None or '%(message)' in self.message def __unicode__(self): return six.text_type(self.msg) class NotAuthorized(JoinException): message = "Not authorized." code = 403 class Invalid(JoinException): message = "Unacceptable parameters." code = 400 class InvalidInput(Invalid): message = "Invalid input received: %(reason)s" class InvalidContentType(Invalid): message = "Invalid content type %(content_type)s." class MalformedRequestBody(JoinException): message = "Malformed message body: %(reason)s" class GlanceConnectionFailed(JoinException): message = "Connection to glance failed: %(reason)s" class ImageNotAuthorized(JoinException): message = "Not authorized for image %(image_id)s." class NotFound(JoinException): message = "Resource could not be found." code = 404 safe = True class ImageNotFound(NotFound): message = "Image %(image_id)s could not be found." class PolicyNotAuthorized(NotAuthorized): message = "Policy doesn't allow %(action)s to be performed." class NotificationVersionMismatch(JoinException): message = ("Provided notification version " "%(provided_maj)s.%(provided_min)s did not match expected " "%(expected_maj)s.%(expected_min)s for %(type)s") class IPAConnectionError(JoinException): message = "Unable to connect to IPA after %(tries) tries"