From dcde535f957e2eb4b8bfddd48d6f19eef05ad4f9 Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Tue, 18 Jul 2017 20:08:57 -0400 Subject: [PATCH] Handle None returned from get_allocation_candidates due to connect failure The get_allocation_candidates method is decorated with the safe_connect decorator that handles any failures trying to connect to the Placement service. If keystoneauth raises an exception, safe_connect will log it and return None. The select_destinations() method in the SchedulerManager needs to handle the None case so it doesn't assume a tuple is coming back which would result in a TypeError. Change-Id: Iffd72f51f25a9e874eaacf374d80794675236ac1 Closes-Bug: #1705141 --- nova/scheduler/manager.py | 5 ++++- nova/tests/unit/scheduler/test_scheduler.py | 22 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py index b38d12c18a20..98116b92f92a 100644 --- a/nova/scheduler/manager.py +++ b/nova/scheduler/manager.py @@ -111,7 +111,10 @@ class SchedulerManager(manager.Manager): alloc_reqs, p_sums = None, None if self.driver.USES_ALLOCATION_CANDIDATES: res = self.placement_client.get_allocation_candidates(resources) - alloc_reqs, p_sums = res + # We have to handle the case that we failed to connect to the + # Placement service and the safe_connect decorator on + # get_allocation_candidates returns None. + alloc_reqs, p_sums = res if res is not None else (None, None) if not alloc_reqs: LOG.debug("Got no allocation candidates from the Placement " "API. This may be a temporary occurrence as compute " diff --git a/nova/tests/unit/scheduler/test_scheduler.py b/nova/tests/unit/scheduler/test_scheduler.py index 340cb6ba8df4..07dfe1772d08 100644 --- a/nova/tests/unit/scheduler/test_scheduler.py +++ b/nova/tests/unit/scheduler/test_scheduler.py @@ -127,6 +127,28 @@ class SchedulerManagerTestCase(test.NoDBTestCase): [fake_spec.instance_uuid], None) mock_get_ac.assert_called_once_with(mock_rfrs.return_value) + @mock.patch('nova.scheduler.utils.resources_from_request_spec') + @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' + 'get_allocation_candidates', return_value=None) + def test_select_destination_placement_connect_fails( + self, mock_get_ac, mock_rfrs): + """Tests that we will pass None for the provider_summaries parameter to + the scheduler driver select_destinations() method when the scheduler + report client's get_allocation_candidates() returns None, which it + would if the connection to Placement failed and the safe_connect + decorator returns None. + """ + fake_spec = objects.RequestSpec() + fake_spec.instance_uuid = uuids.instance + with mock.patch.object(self.manager.driver, + 'select_destinations') as select_destinations: + self.manager.select_destinations( + self.context, spec_obj=fake_spec, + instance_uuids=[fake_spec.instance_uuid]) + select_destinations.assert_called_once_with( + self.context, fake_spec, [fake_spec.instance_uuid], None) + mock_get_ac.assert_called_once_with(mock_rfrs.return_value) + @mock.patch('nova.scheduler.utils.resources_from_request_spec') @mock.patch('nova.scheduler.client.report.SchedulerReportClient.' 'get_allocation_candidates')