Handle @safe_connect returns None side effect in _ensure_resource_provider

Change I0c4ca6a81f213277fe7219cb905a805712f81e36 added more error
handling to the _ensure_resource_provider flow but didn't account
for @safe_connect returning None when calling _create_resource_provider
in the case that nova-compute is started before placement is running.
If that happens, we fail with a TypeError during the nova-compute
startup because we put None in the resource provider cache and then
later blindly try to use it because the compute node resource provider
uuid is in the cache, but mapped to None.

This adds the None check back in _ensure_resource_provider and if
None is returned from _create_resource_provider we raise the same
exception that _create_resource_provider would raise if it couldn't
create the provider.

Conflicts:

nova/scheduler/client/report.py: entire method conflicted as
provider_tree changes have not been backported.

nova/tests/unit/scheduler/client/test_report.py - didn't conflict but
referenced non-existant methods. Re-implemented based on
test_ensure_resource_provider_create_fail changing mocked
create_resource_provider to return None instead of raising an exception.

Change-Id: If9e1581db9c1ae14340b787d03c815d243d5a50c
Closes-Bug: #1767139
(cherry picked from commit 80a0019893)
(cherry picked from commit f95a10b26e)
This commit is contained in:
Matt Riedemann 2018-05-03 11:21:47 -04:00 committed by Oliver Walsh
parent efa26e8dc2
commit d75b995d03
2 changed files with 33 additions and 0 deletions

View File

@ -514,6 +514,14 @@ class SchedulerReportClient(object):
rp = self._get_resource_provider(uuid)
if rp is None:
rp = self._create_resource_provider(uuid, name or uuid)
# If @safe_connect can't establish a connection to the placement
# service, like if placement isn't running or nova-compute is
# mis-configured for authentication, we'll get None back and need
# to treat it like we couldn't create the provider (because we
# couldn't).
if rp is None:
raise exception.ResourceProviderCreationFailed(
name=name or uuid)
msg = "Grabbing aggregate associations for resource provider %s"
LOG.debug(msg, uuid)

View File

@ -1040,6 +1040,31 @@ class TestProviderOperations(SchedulerReportClientTestCase):
self.assertEqual({}, self.client._resource_providers)
self.assertEqual({}, self.client._provider_aggregate_map)
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'_create_resource_provider', return_value=None)
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'_get_provider_aggregates')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'_get_resource_provider')
def test_ensure_resource_provider_create_no_placement(self, get_rp_mock,
get_agg_mock, create_rp_mock):
# No resource provider exists in the client's cache, and
# @safe_connect on _create_resource_provider returns None because
# Placement isn't running yet. Ensure we don't populate the resource
# provider cache.
get_rp_mock.return_value = None
self.assertRaises(
exception.ResourceProviderCreationFailed,
self.client._ensure_resource_provider, uuids.compute_node)
get_rp_mock.assert_called_once_with(uuids.compute_node)
create_rp_mock.assert_called_once_with(uuids.compute_node,
uuids.compute_node)
self.assertFalse(get_agg_mock.called)
self.assertEqual({}, self.client._resource_providers)
self.assertEqual({}, self.client._provider_aggregate_map)
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'
'_create_resource_provider')
@mock.patch('nova.scheduler.client.report.SchedulerReportClient.'