text: # -*- coding: utf-8 -*-
  #
  # Copyright © 2013 Unitedstack Inc.
  #
  # 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.
  
  """A middleware that turns exceptions into parsable string.
  
  Inspired by Cinder's faultwrapper.
  """
  import six
  
  import sys
  import traceback
  
  from oslo_config import cfg
  import webob
  
  from heat.common import exception
  from heat.common import serializers
  from heat.common import wsgi
  
  
  class Fault(object):
  
      def __init__(self, error):
          self.error = error
  
      @webob.dec.wsgify(RequestClass=wsgi.Request)
      def __call__(self, req):
          if req.content_type == 'application/xml':
              serializer = serializers.XMLResponseSerializer()
          else:
              serializer = serializers.JSONResponseSerializer()
          resp = webob.Response(request=req)
          default_webob_exc = webob.exc.HTTPInternalServerError()
          resp.status_code = self.error.get('code', default_webob_exc.code)
          serializer.default(resp, self.error)
          return resp
  
  
  class FaultWrapper(wsgi.Middleware):
      """Replace error body with something the client can parse."""
  
      error_map = {
          'AttributeError': webob.exc.HTTPBadRequest,
          'ActionInProgress': webob.exc.HTTPConflict,
          'ValueError': webob.exc.HTTPBadRequest,
          'EntityNotFound': webob.exc.HTTPNotFound,
          'NotFound': webob.exc.HTTPNotFound,
          'ResourceActionNotSupported': webob.exc.HTTPBadRequest,
          'InvalidGlobalResource': webob.exc.HTTPInternalServerError,
          'ResourceNotAvailable': webob.exc.HTTPNotFound,
          'PhysicalResourceNameAmbiguity': webob.exc.HTTPBadRequest,
          'InvalidTenant': webob.exc.HTTPForbidden,
          'Forbidden': webob.exc.HTTPForbidden,
          'StackExists': webob.exc.HTTPConflict,
          'StackValidationFailed': webob.exc.HTTPBadRequest,
          'InvalidSchemaError': webob.exc.HTTPBadRequest,
          'InvalidTemplateReference': webob.exc.HTTPBadRequest,
          'InvalidTemplateVersion': webob.exc.HTTPBadRequest,
          'InvalidTemplateSection': webob.exc.HTTPBadRequest,
          'UnknownUserParameter': webob.exc.HTTPBadRequest,
          'RevertFailed': webob.exc.HTTPInternalServerError,
          'StopActionFailed': webob.exc.HTTPInternalServerError,
          'EventSendFailed': webob.exc.HTTPInternalServerError,
          'ServerBuildFailed': webob.exc.HTTPInternalServerError,
          'NotSupported': webob.exc.HTTPBadRequest,
          'MissingCredentialError': webob.exc.HTTPBadRequest,
          'UserParameterMissing': webob.exc.HTTPBadRequest,
          'RequestLimitExceeded': webob.exc.HTTPBadRequest,
          'InvalidTemplateParameter': webob.exc.HTTPBadRequest,
          'Invalid': webob.exc.HTTPBadRequest,
          'ResourcePropertyConflict': webob.exc.HTTPBadRequest,
          'PropertyUnspecifiedError': webob.exc.HTTPBadRequest,
          'ObjectFieldInvalid': webob.exc.HTTPBadRequest,
          'ReadOnlyFieldError': webob.exc.HTTPBadRequest,
          'ObjectActionError': webob.exc.HTTPBadRequest,
          'IncompatibleObjectVersion': webob.exc.HTTPBadRequest,
          'OrphanedObjectError': webob.exc.HTTPBadRequest,
          'UnsupportedObjectError': webob.exc.HTTPBadRequest,
          'ResourceTypeUnavailable': webob.exc.HTTPBadRequest,
          'InvalidBreakPointHook': webob.exc.HTTPBadRequest,
      }
  
      def _map_exception_to_error(self, class_exception):
          if class_exception == Exception:
              return webob.exc.HTTPInternalServerError
  
          if class_exception.__name__ not in self.error_map:
              return self._map_exception_to_error(class_exception.__base__)
  
          return self.error_map[class_exception.__name__]
  
      def _error(self, ex):
  
          trace = None
          traceback_marker = 'Traceback (most recent call last)'
          webob_exc = None
          if isinstance(ex, exception.HTTPExceptionDisguise):
              # An HTTP exception was disguised so it could make it here
              # let's remove the disguise and set the original HTTP exception
              if cfg.CONF.debug:
                  trace = ''.join(traceback.format_tb(ex.tb))
              ex = ex.exc
              webob_exc = ex
  
          ex_type = ex.__class__.__name__
  
          is_remote = ex_type.endswith('_Remote')
          if is_remote:
              ex_type = ex_type[:-len('_Remote')]
  
          full_message = six.text_type(ex)
          if '\n' in full_message and is_remote:
              message, msg_trace = full_message.split('\n', 1)
          elif traceback_marker in full_message:
              message, msg_trace = full_message.split(traceback_marker, 1)
              message = message.rstrip('\n')
              msg_trace = traceback_marker + msg_trace
          else:
              msg_trace = 'None\n'
              if sys.exc_info() != (None, None, None):
                  msg_trace = traceback.format_exc()
              message = full_message
  
          if isinstance(ex, exception.HeatException):
              message = ex.message
  
          if cfg.CONF.debug and not trace:
              trace = msg_trace
  
          if not webob_exc:
              webob_exc = self._map_exception_to_error(ex.__class__)
  
          error = {
              'code': webob_exc.code,
              'title': webob_exc.title,
              'explanation': webob_exc.explanation,
              'error': {
                  'message': message,
                  'type': ex_type,
                  'traceback': trace,
              }
          }
  
          return error
  
      def process_request(self, req):
          try:
              return req.get_response(self.application)
          except Exception as exc:
              return req.get_response(Fault(self._error(exc)))
  