summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2012-07-13 23:32:51 +0000
committerGerrit Code Review <review@openstack.org>2012-07-13 23:32:51 +0000
commit58c92d66e7220574038cedd3079d64649ced2106 (patch)
treede6e7ba63faf9681e7d4c611c29422b27d6ab374
parentf4a778f31f647b065b945311920148366d4ead53 (diff)
parente9d21589d39355ffc126e360cc2ba7311e014edb (diff)
downloadnova-58c92d66e7220574038cedd3079d64649ced2106.tar.gz
nova-58c92d66e7220574038cedd3079d64649ced2106.tar.xz
nova-58c92d66e7220574038cedd3079d64649ced2106.zip
Merge "Expose over-quota exceptions via native API."
-rw-r--r--nova/api/openstack/__init__.py30
-rw-r--r--nova/exception.py2
-rw-r--r--nova/tests/api/openstack/compute/test_api.py24
3 files changed, 47 insertions, 9 deletions
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index afeed0399..3372d9b5e 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -25,6 +25,7 @@ import webob.dec
import webob.exc
from nova.api.openstack import wsgi
+from nova import exception
from nova.openstack.common import log as logging
from nova import wsgi as base_wsgi
@@ -35,20 +36,31 @@ LOG = logging.getLogger(__name__)
class FaultWrapper(base_wsgi.Middleware):
"""Calls down the middleware stack, making exceptions into faults."""
+ def _error(self, inner, req, safe=False):
+ LOG.exception(_("Caught error: %s"), unicode(inner))
+ msg_dict = dict(url=req.url, status=500)
+ LOG.info(_("%(url)s returned with HTTP %(status)d") % msg_dict)
+ outer = webob.exc.HTTPInternalServerError()
+ # NOTE(johannes): We leave the explanation empty here on
+ # purpose. It could possibly have sensitive information
+ # that should not be returned back to the user. See
+ # bugs 868360 and 874472
+ # NOTE(eglynn): However, it would be over-conservative and
+ # inconsistent with the EC2 API to hide every exception,
+ # including those that are safe to expose, see bug 1021373
+ if safe:
+ outer.explanation = '%s: %s' % (inner.__class__.__name__,
+ unicode(inner))
+ return wsgi.Fault(outer)
+
@webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, req):
try:
return req.get_response(self.application)
+ except exception.NovaException as ex:
+ return self._error(ex, req, ex.safe)
except Exception as ex:
- LOG.exception(_("Caught error: %s"), unicode(ex))
- msg_dict = dict(url=req.url, status=500)
- LOG.info(_("%(url)s returned with HTTP %(status)d") % msg_dict)
- exc = webob.exc.HTTPInternalServerError()
- # NOTE(johannes): We leave the explanation empty here on
- # purpose. It could possibly have sensitive information
- # that should not be returned back to the user. See
- # bugs 868360 and 874472
- return wsgi.Fault(exc)
+ return self._error(ex, req)
class APIMapper(routes.Mapper):
diff --git a/nova/exception.py b/nova/exception.py
index 8d0a20922..ac4f44abf 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -129,6 +129,7 @@ class NovaException(Exception):
"""
message = _("An unknown exception occurred.")
+ safe = False
def __init__(self, message=None, **kwargs):
self.kwargs = kwargs
@@ -994,6 +995,7 @@ class WillNotSchedule(NovaException):
class QuotaError(NovaException):
message = _("Quota exceeded") + ": code=%(code)s"
+ safe = True
class TooManyInstances(QuotaError):
diff --git a/nova/tests/api/openstack/compute/test_api.py b/nova/tests/api/openstack/compute/test_api.py
index 434befdb9..a1dd01be3 100644
--- a/nova/tests/api/openstack/compute/test_api.py
+++ b/nova/tests/api/openstack/compute/test_api.py
@@ -23,6 +23,7 @@ import webob.exc
from nova.api import openstack as openstack_api
from nova.api.openstack import wsgi
import nova.context
+from nova import exception
from nova.openstack.common import jsonutils
from nova import test
from nova.tests.api.openstack import fakes
@@ -120,6 +121,29 @@ class APITest(test.TestCase):
self.assertTrue('<computeFault' in resp.body, resp.body)
self.assertEqual(resp.status_int, 500, resp.body)
+ def _do_test_exception_safety_reflected_in_faults(self, expose):
+ class ExceptionWithSafety(exception.NovaException):
+ safe = expose
+
+ @webob.dec.wsgify
+ def fail(req):
+ raise ExceptionWithSafety('some explanation')
+
+ api = self._wsgi_app(fail)
+ resp = webob.Request.blank('/').get_response(api)
+ self.assertTrue('{"computeFault' in resp.body, resp.body)
+ expected = ('ExceptionWithSafety: some explanation' if expose else
+ 'The server has either erred or is incapable '
+ 'of performing the requested operation.')
+ self.assertTrue(expected in resp.body, resp.body)
+ self.assertEqual(resp.status_int, 500, resp.body)
+
+ def test_safe_exceptions_are_described_in_faults(self):
+ self._do_test_exception_safety_reflected_in_faults(True)
+
+ def test_unsafe_exceptions_are_not_described_in_faults(self):
+ self._do_test_exception_safety_reflected_in_faults(False)
+
def test_request_id_in_response(self):
req = webob.Request.blank('/')
req.method = 'GET'