import pecan
from neutron.common import exceptions as n_exc
+from neutron.newapi import hooks
from neutron.newapi import startup
CONF = cfg.CONF
}
pecan_config = pecan.configuration.conf_from_dict(config)
- app_hooks = []
+ app_hooks = [
+ hooks.ExceptionTranslationHook(),
+ ]
app = pecan.make_app(
pecan_config.app.root,
def index(self):
if pecan.request.method != 'GET':
pecan.abort(405)
+ return self.get()
+
+ def get(self):
return {'message': 'GET'}
@when(index, method='PUT')
--- /dev/null
+# Copyright (c) 2015 Mirantis, Inc.
+# 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 neutron.newapi.hooks import translation
+
+
+ExceptionTranslationHook = translation.ExceptionTranslationHook
--- /dev/null
+# Copyright (c) 2015 Mirantis, Inc.
+# 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
+from pecan import hooks
+import webob.exc
+
+from neutron.api.v2 import base as v2base
+
+
+LOG = logging.getLogger(__name__)
+
+
+class ExceptionTranslationHook(hooks.PecanHook):
+ def on_error(self, state, e):
+ # if it's already an http error, just return to let it go through
+ if isinstance(e, webob.exc.WSGIHTTPException):
+ return
+ for exc_class, to_class in v2base.FAULT_MAP.items():
+ if isinstance(e, exc_class):
+ raise to_class(e.message)
+ # leaked unexpected exception, convert to boring old 500 error and
+ # hide message from user in case it contained sensitive details
+ LOG.exception(_("An unexpected exception was caught: %s") % e)
+ raise webob.exc.HTTPInternalServerError(
+ _("An unexpected internal error occured."))
import os
+import mock
from oslo_config import cfg
from oslo_utils import uuidutils
from pecan import set_config
cfg.CONF.set_override('auth_strategy', 'badvalue')
with testtools.ExpectedException(n_exc.InvalidConfigurationOption):
load_test_app(os.path.join(os.path.dirname(__file__), 'config.py'))
+
+
+class TestExceptionTranslationHook(PecanFunctionalTest):
+
+ def test_neutron_nonfound_to_webob_exception(self):
+ # this endpoint raises a Neutron notfound exception. make sure it gets
+ # translated into a 404 error
+ with mock.patch(
+ 'neutron.newapi.controllers.root.GeneralController.get',
+ side_effect=n_exc.NotFound()):
+ response = self.app.get('/v2.0/ports.json', expect_errors=True)
+ self.assertEqual(response.status_int, 404)
+
+ def test_unexpected_exception(self):
+ with mock.patch(
+ 'neutron.newapi.controllers.root.GeneralController.get',
+ side_effect=ValueError('secretpassword')):
+ response = self.app.get('/v2.0/ports.json', expect_errors=True)
+ self.assertNotIn(response.body, 'secretpassword')
+ self.assertEqual(response.status_int, 500)