diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py index 8e9cf9c49aaf..39cbf428e541 100644 --- a/nova/conductor/manager.py +++ b/nova/conductor/manager.py @@ -20,6 +20,7 @@ import functools import sys from oslo_config import cfg +from oslo_db import exception as db_exc from oslo_log import log as logging import oslo_messaging as messaging from oslo_serialization import jsonutils @@ -77,7 +78,12 @@ def targets_cell(fn): except exception.InstanceMappingNotFound: LOG.error('InstanceMapping not found, unable to target cell', instance=instance) - im = None + except db_exc.CantStartEngineError: + # Check to see if we can ignore API DB connection failures + # because we might already be in the cell conductor. + with excutils.save_and_reraise_exception() as err_ctxt: + if CONF.api_database.connection is None: + err_ctxt.reraise = False else: LOG.debug('Targeting cell %(cell)s for conductor method %(meth)s', {'cell': im.cell_mapping.identity, diff --git a/nova/tests/unit/conductor/test_conductor.py b/nova/tests/unit/conductor/test_conductor.py index 896f6ec3247b..e19d0b19da9a 100644 --- a/nova/tests/unit/conductor/test_conductor.py +++ b/nova/tests/unit/conductor/test_conductor.py @@ -18,6 +18,7 @@ import copy import mock +from oslo_db import exception as db_exc import oslo_messaging as messaging from oslo_serialization import jsonutils from oslo_utils.fixture import uuidsentinel as uuids @@ -3315,6 +3316,41 @@ class ConductorTaskRPCAPITestCase(_BaseTaskTestCase, test(None, ctxt, inst)) mock_im.assert_called_once_with(ctxt, inst.uuid) + @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid', + side_effect=db_exc.CantStartEngineError) + def test_targets_cell_no_api_db_conn_noop(self, mock_im): + """Tests that targets_cell noops on CantStartEngineError if the API + DB is not configured because we assume we're in the cell conductor. + """ + self.flags(connection=None, group='api_database') + + @conductor_manager.targets_cell + def _test(self, context, instance): + return mock.sentinel.iransofaraway + + ctxt = mock.MagicMock() + inst = mock.MagicMock() + self.assertEqual(mock.sentinel.iransofaraway, + _test(None, ctxt, inst)) + mock_im.assert_called_once_with(ctxt, inst.uuid) + + @mock.patch.object(objects.InstanceMapping, 'get_by_instance_uuid', + side_effect=db_exc.CantStartEngineError) + def test_targets_cell_no_api_db_conn_reraise(self, mock_im): + """Tests that targets_cell reraises CantStartEngineError if the + API DB is configured. + """ + self.flags(connection='mysql://dbhost?nova_api', group='api_database') + + @conductor_manager.targets_cell + def _test(self, context, instance): + return mock.sentinel.iransofaraway + + ctxt = mock.MagicMock() + inst = mock.MagicMock() + self.assertRaises(db_exc.CantStartEngineError, _test, None, ctxt, inst) + mock_im.assert_called_once_with(ctxt, inst.uuid) + def test_schedule_and_build_instances_with_tags(self): build_request = fake_build_request.fake_req_obj(self.context)