From 722b9b57e1ff91200636db45e515ca52a769736c Mon Sep 17 00:00:00 2001 From: Rodolfo Alonso Hernandez Date: Wed, 11 Aug 2021 09:13:55 +0000 Subject: [PATCH] Do not fail if the agent load is not bumped When a new network and its first subnet is created, the DHCP agent bumps the "load" parameter to reflect the number of networks handled. This "load" parameter is modified when: - As commented, when the first subnet of a network is created. The "load" value is bumped. - When periodically the DHCP agent sends the status, informing about the current number of networks handled. If during the subnet creation this "load" value is not updated, it will be in the next periodic update of the agent. This "load" value is used by the scheduler to equally distribute the objects to be managed by any agent type (DHCP agents manage networks). The bug refers to DHCP but is valid for any other agent. Conflicts: neutron/common/utils.py neutron/scheduler/base_resource_filter.py Change-Id: Ief402048d99d40b64d81fcf58eb2e39b1ba7ebbb Closes-Bug: #1939432 (cherry picked from commit 668b1cc652f076e555ef1fc1289684367159186a) (cherry picked from commit 816aca60b90b89038863d6974b5a9e0ee8983424) (cherry picked from commit 1eb6b8926a7a5ad442c5af6057c042999e2645f1) (cherry picked from commit f315f85a7b0ead30877e19988db4e4f80fc960e9) --- neutron/common/utils.py | 22 ++++++++++++++++++++++ neutron/scheduler/base_resource_filter.py | 4 ++++ neutron/tests/unit/common/test_utils.py | 22 ++++++++++++++++++++++ 3 files changed, 48 insertions(+) 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)