diff --git a/neutron/common/utils.py b/neutron/common/utils.py index bff10bc40a2..4bed9b0e14d 100644 --- a/neutron/common/utils.py +++ b/neutron/common/utils.py @@ -994,3 +994,25 @@ class SingletonDecorator(object): if self._instance is None: self._instance = self._klass(*args, **kwargs) return self._instance + + +def skip_exceptions(exceptions): + """Decorator to catch and hide any provided exception in the argument""" + + # NOTE(ralonsoh): could be rehomed to neutron-lib. + if not isinstance(exceptions, list): + exceptions = [exceptions] + + def decorator(function): + @functools.wraps(function) + def wrapper(*args, **kwargs): + try: + return function(*args, **kwargs) + except Exception as exc: + with excutils.save_and_reraise_exception() as ctx: + if issubclass(type(exc), tuple(exceptions)): + LOG.info('Skipped exception %s when calling method %s', + ctx.value.__repr__(), function.__repr__()) + ctx.reraise = False + return wrapper + return decorator diff --git a/neutron/scheduler/base_resource_filter.py b/neutron/scheduler/base_resource_filter.py index 17f13bdafe6..9b46c9241c9 100644 --- a/neutron/scheduler/base_resource_filter.py +++ b/neutron/scheduler/base_resource_filter.py @@ -16,8 +16,11 @@ import abc from neutron_lib.db import api as db_api +from oslo_db import exception as db_exc import six +from neutron.common import utils as n_utils + @six.add_metaclass(abc.ABCMeta) class BaseResourceFilter(object): @@ -26,6 +29,7 @@ class BaseResourceFilter(object): def filter_agents(self, plugin, context, resource): """Return the agents that can host the resource.""" + @n_utils.skip_exceptions(db_exc.DBError) def bind(self, context, agents, resource_id): """Bind the resource to the agents.""" with db_api.CONTEXT_WRITER.using(context): diff --git a/neutron/tests/unit/common/test_utils.py b/neutron/tests/unit/common/test_utils.py index 93b94913c08..e0a90c680aa 100644 --- a/neutron/tests/unit/common/test_utils.py +++ b/neutron/tests/unit/common/test_utils.py @@ -642,3 +642,25 @@ class SingletonDecoratorTestCase(base.BaseTestCase): instance_2 = _TestSingletonClass() self.assertEqual(instance_1.__hash__(), instance_2.__hash__()) self.assertEqual('value1', instance_2.variable) + + +class SkipDecoratorTestCase(base.BaseTestCase): + + def test_skip_exception(self): + @utils.skip_exceptions(AttributeError) + def raise_attribute_error_single_exception(): + raise AttributeError() + + @utils.skip_exceptions([AttributeError, IndexError]) + def raise_attribute_error_exception_list(): + raise AttributeError() + + raise_attribute_error_single_exception() + raise_attribute_error_exception_list() + + def test_skip_exception_fail(self): + @utils.skip_exceptions(IndexError) + def raise_attribute_error(): + raise AttributeError() + + self.assertRaises(AttributeError, raise_attribute_error)