diff --git a/nova/scheduler/filter_scheduler.py b/nova/scheduler/filter_scheduler.py index ec33b2d2f6f7..e67dbeef0d6a 100644 --- a/nova/scheduler/filter_scheduler.py +++ b/nova/scheduler/filter_scheduler.py @@ -321,10 +321,14 @@ class FilterScheduler(driver.Scheduler): def _get_all_host_states(self, context, spec_obj, provider_summaries): """Template method, so a subclass can implement caching.""" - # NOTE(jaypipes): None is treated differently from an empty dict. We - # pass None when we want to grab all compute nodes (for instance, when - # using the caching scheduler. We pass an empty dict when the Placement - # API found no providers that match the requested constraints. + # NOTE(jaypipes): provider_summaries being None is treated differently + # from an empty dict. provider_summaries is None when we want to grab + # all compute nodes, for instance when using the caching scheduler. + # The provider_summaries variable will be an empty dict when the + # Placement API found no providers that match the requested + # constraints, which in turn makes compute_uuids an empty list and + # get_host_states_by_uuids will return an empty tuple also, which will + # eventually result in a NoValidHost error. compute_uuids = None if provider_summaries is not None: compute_uuids = list(provider_summaries.keys()) diff --git a/nova/scheduler/host_manager.py b/nova/scheduler/host_manager.py index 52ca3f09141e..974ff0bde2e9 100644 --- a/nova/scheduler/host_manager.py +++ b/nova/scheduler/host_manager.py @@ -596,6 +596,14 @@ class HostManager(object): def _get_computes_for_cells(self, context, cells, compute_uuids=None): """Get a tuple of compute node and service information. + :param context: request context + :param cells: list of CellMapping objects + :param compute_uuids: list of ComputeNode UUIDs. If this is None, all + compute nodes from each specified cell will be returned, otherwise + only the ComputeNode objects with a UUID in the list of UUIDs in + any given cell is returned. If this is an empty list, the returned + compute_nodes tuple item will be an empty dict. + Returns a tuple (compute_nodes, services) where: - compute_nodes is cell-uuid keyed dict of compute node lists - services is a dict of services indexed by hostname diff --git a/nova/tests/unit/scheduler/test_filter_scheduler.py b/nova/tests/unit/scheduler/test_filter_scheduler.py index 681dcf71c370..cfc5bb06e95d 100644 --- a/nova/tests/unit/scheduler/test_filter_scheduler.py +++ b/nova/tests/unit/scheduler/test_filter_scheduler.py @@ -599,3 +599,29 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase): mock.call(self.context, 'scheduler.select_destinations.end', dict(request_spec=expected))] self.assertEqual(expected, mock_info.call_args_list) + + def test_get_all_host_states_provider_summaries_is_none(self): + """Tests that HostManager.get_host_states_by_uuids is called with + compute_uuids being None when the incoming provider_summaries is None. + """ + with mock.patch.object(self.driver.host_manager, + 'get_host_states_by_uuids') as get_host_states: + self.driver._get_all_host_states( + mock.sentinel.ctxt, mock.sentinel.spec_obj, None) + # Make sure get_host_states_by_uuids was called with + # compute_uuids being None. + get_host_states.assert_called_once_with( + mock.sentinel.ctxt, None, mock.sentinel.spec_obj) + + def test_get_all_host_states_provider_summaries_is_empty(self): + """Tests that HostManager.get_host_states_by_uuids is called with + compute_uuids being [] when the incoming provider_summaries is {}. + """ + with mock.patch.object(self.driver.host_manager, + 'get_host_states_by_uuids') as get_host_states: + self.driver._get_all_host_states( + mock.sentinel.ctxt, mock.sentinel.spec_obj, {}) + # Make sure get_host_states_by_uuids was called with + # compute_uuids being []. + get_host_states.assert_called_once_with( + mock.sentinel.ctxt, [], mock.sentinel.spec_obj)