Enhance exc_wrapper to handle internal exceptions

The idea was to reuse the existing exception handling decorator
in quark/utils.py to use the same decorator to handle exceptions
in internal methods. The enhanced decorator accepts an arguement
to specify if we are handling exceptions in an internal method.

Existing exception handling for api methods do not change.
This commit adds the enhanced wrapper and relevant unit tests, in
addition to adding the wrapper to internal calls in ports.py

Change-Id: I2aa2e3bdd06ff8f839f65e462b885c34b024c157
Implements: blueprint enhance-exc-wrapper
This commit is contained in:
Sourav Banerjee 2017-11-09 21:14:17 +05:30
parent 43c8359db5
commit d8ed2b6b2b
3 changed files with 82 additions and 15 deletions

View File

@ -130,6 +130,7 @@ def split_and_validate_requested_subnets(context, net_id, segment_id,
return ip_addresses, subnets
@utils.exc_wrapper(internal=True)
def create_port(context, port):
"""Create a port
@ -352,6 +353,7 @@ def create_port(context, port):
return v._make_port_dict(new_port)
@utils.exc_wrapper(internal=True)
def update_port(context, id, port):
"""Update values of a port.
@ -503,6 +505,7 @@ def update_port(context, id, port):
return v._make_port_dict(port_db)
@utils.exc_wrapper(internal=True)
def get_port(context, id, fields=None):
"""Retrieve a port.
@ -524,6 +527,7 @@ def get_port(context, id, fields=None):
return v._make_port_dict(results)
@utils.exc_wrapper(internal=True)
def get_ports(context, limit=None, sorts=['id'], marker=None,
page_reverse=False, filters=None, fields=None):
"""Retrieve a list of ports.
@ -570,6 +574,7 @@ def get_ports(context, limit=None, sorts=['id'], marker=None,
return v._make_ports_list(ports, fields)
@utils.exc_wrapper(internal=True)
def get_ports_count(context, filters=None):
"""Return the number of ports.
@ -592,6 +597,7 @@ def get_ports_count(context, filters=None):
return db_api.port_count_all(context, join_security_groups=True, **filters)
@utils.exc_wrapper(internal=True)
def delete_port(context, id):
"""Delete a port.
@ -634,6 +640,7 @@ def _diag_port(context, port, fields):
return p
@utils.exc_wrapper(internal=True)
def diagnose_port(context, id, fields):
if not context.is_admin:
raise n_exc.NotAuthorized()

View File

@ -53,6 +53,22 @@ class TestExcWrapper(test_base.TestBase):
def raise_generic_exception(self):
raise Exception()
@utils.exc_wrapper(internal=True)
def raise_generic_exception_internal(self):
raise n_exc.NeutronException()
@utils.exc_wrapper(internal=False)
def raise_generic_exception_internal_false(self):
raise Exception()
@utils.exc_wrapper(internal=True)
def raise_specific_exception_internal(self):
raise n_exc.Conflict()
@utils.exc_wrapper(internal=True)
def no_raise_internal(self):
return ""
@utils.exc_wrapper
def no_raise(self):
return ""
@ -73,5 +89,20 @@ class TestExcWrapper(test_base.TestBase):
with self.assertRaises(webob.exc.HTTPInternalServerError):
self.raise_generic_exception()
def test_generic_exception_internal(self):
with self.assertRaises(n_exc.NeutronException):
self.raise_generic_exception_internal()
def test_generic_exception_internal_false(self):
with self.assertRaises(webob.exc.HTTPInternalServerError):
self.raise_generic_exception_internal_false()
def test_specific_exception_internal(self):
with self.assertRaises(n_exc.Conflict):
self.raise_specific_exception_internal()
def test_no_raise(self):
self.assertEqual("", self.no_raise())
def test_no_raise_internal(self):
self.assertEqual("", self.no_raise_internal())

View File

@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
import cProfile as profiler
import gc
@ -25,6 +24,7 @@ except Exception:
# Don't want to force pstats into the venv if it's not always used
pass
from functools import wraps
from neutron_lib import constants as attributes
from neutron_lib import exceptions as n_exc
from oslo_log import log as logging
@ -79,22 +79,51 @@ def live_profile(fn):
return _wrapped
def exc_wrapper(func):
def opt_args_decorator(func):
"""A decorator to be used on another decorator
This is done to allow separate handling on the basis of argument values
"""
@wraps(func)
def wrapped_dec(*args, **kwargs):
if len(args) == 1 and len(kwargs) == 0 and callable(args[0]):
# actual decorated function
return func(args[0])
else:
# decorator arguments
return lambda realf: func(realf, *args, **kwargs)
return wrapped_dec
@opt_args_decorator
def exc_wrapper(func, internal=False):
@wraps(func)
def wrapped(*args, **kwargs):
e = None
try:
return func(*args, **kwargs)
except n_exc.NotFound as e:
raise webob.exc.HTTPNotFound(e)
except n_exc.Conflict as e:
raise webob.exc.HTTPConflict(e)
except n_exc.BadRequest as e:
raise webob.exc.HTTPBadRequest(e)
except Exception as e:
raise webob.exc.HTTPInternalServerError(e)
finally:
if not e:
LOG.exception(str(e))
if internal:
try:
return func(*args, **kwargs)
except Exception as e:
# Re-raise the exception to make it pass through
raise e
finally:
if not e:
LOG.exception(str(e))
else:
try:
return func(*args, **kwargs)
except n_exc.NotFound as e:
raise webob.exc.HTTPNotFound(e)
except n_exc.Conflict as e:
raise webob.exc.HTTPConflict(e)
except n_exc.BadRequest as e:
raise webob.exc.HTTPBadRequest(e)
except Exception as e:
raise webob.exc.HTTPInternalServerError(e)
finally:
if e:
LOG.exception(str(e))
return wrapped