Alessandro Pilotti 10 лет назад
Родитель
Сommit
05acbbc0bf
4 измененных файлов с 90 добавлено и 2 удалено
  1. 0 0
      coriolis/api/middleware/auth.py
  2. 72 0
      coriolis/api/middleware/fault.py
  3. 13 0
      coriolis/utils.py
  4. 5 2
      etc/coriolis/api-paste.ini

+ 0 - 0
coriolis/api/auth.py → coriolis/api/middleware/auth.py


+ 72 - 0
coriolis/api/middleware/fault.py

@@ -0,0 +1,72 @@
+# 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.
+
+from oslo_log import log as logging
+import six
+import webob.dec
+import webob.exc
+
+from coriolis.api import wsgi
+from coriolis import exception
+from coriolis.i18n import _, _LE, _LI
+from coriolis import utils
+
+
+LOG = logging.getLogger(__name__)
+
+
+class FaultWrapper(wsgi.Middleware):
+    """Calls down the middleware stack, making exceptions into faults."""
+
+    _status_to_type = {}
+
+    @staticmethod
+    def status_to_type(status):
+        if not FaultWrapper._status_to_type:
+            for clazz in utils.walk_class_hierarchy(webob.exc.HTTPError):
+                FaultWrapper._status_to_type[clazz.code] = clazz
+        return FaultWrapper._status_to_type.get(
+            status, webob.exc.HTTPInternalServerError)()
+
+    def _error(self, inner, req):
+        LOG.exception(_LE("Caught error: %(type)s %(error)s"),
+                      {'type': type(inner),
+                       'error': inner})
+        safe = getattr(inner, 'safe', False)
+        headers = getattr(inner, 'headers', None)
+        status = getattr(inner, 'code', 500)
+        if status is None:
+            status = 500
+
+        msg_dict = dict(url=req.url, status=status)
+        LOG.info(_LI("%(url)s returned with HTTP %(status)d"), msg_dict)
+        outer = self.status_to_type(status)
+        if headers:
+            outer.headers = headers
+        if safe:
+            msg = (inner.msg if isinstance(inner, exception.CoriolisException)
+                   else six.text_type(inner))
+            params = {'exception': inner.__class__.__name__,
+                      'explanation': msg}
+            outer.explanation = _('%(exception)s: %(explanation)s') % params
+        return wsgi.Fault(outer)
+
+    @webob.dec.wsgify(RequestClass=wsgi.Request)
+    def __call__(self, req):
+        try:
+            return req.get_response(self.application)
+        except Exception as ex:
+            return self._error(ex, req)

+ 13 - 0
coriolis/utils.py

@@ -188,3 +188,16 @@ def get_hostname():
 
 def get_exception_details():
     return traceback.format_exc()
+
+
+def walk_class_hierarchy(clazz, encountered=None):
+    """Walk class hierarchy, yielding most derived classes first."""
+    if not encountered:
+        encountered = []
+    for subclass in clazz.__subclasses__():
+        if subclass not in encountered:
+            encountered.append(subclass)
+            # drill down to leaves first
+            for subsubclass in walk_class_hierarchy(subclass, encountered):
+                yield subsubclass
+            yield subclass

+ 5 - 2
etc/coriolis/api-paste.ini

@@ -3,7 +3,7 @@ use = call:coriolis.api:root_app_factory
 /v1: coriolis-api-v1
 
 [pipeline:coriolis-api-v1]
-pipeline = request_id authtoken keystonecontext apiv1
+pipeline = request_id faultwrap authtoken keystonecontext apiv1
 
 [app:apiv1]
 paste.app_factory = coriolis.api.v1.router:APIRouter.factory
@@ -12,8 +12,11 @@ paste.app_factory = coriolis.api.v1.router:APIRouter.factory
 [filter:authtoken]
 paste.filter_factory = keystonemiddleware.auth_token:filter_factory
 
+[filter:faultwrap]
+paste.filter_factory = coriolis.api.middleware.fault:FaultWrapper.factory
+
 [filter:keystonecontext]
-paste.filter_factory = coriolis.api.auth:CoriolisKeystoneContext.factory
+paste.filter_factory = coriolis.api.middleware.auth:CoriolisKeystoneContext.factory
 
 [filter:request_id]
 paste.filter_factory = oslo_middleware.request_id:RequestId.factory