From 0be8cbc302aa77b312e031f6a7a419e83afd6e3b Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Fri, 13 Jul 2018 15:42:03 -0400 Subject: [PATCH] Handle HostMappingNotFound when deleting a compute service If the compute service we're deleting has never been discovered (mapped to a cell using discover_hosts) or the host mapping was already deleted, we need to handle the HostMappingNotFound error when deleting a compute service so that we (1) don't 500 out of the API and (2) continue to delete the compute service. Change-Id: I0d7644db3537a67b94e75972b3c4fce25a623763 Closes-Bug: #1780727 (cherry picked from commit ee6e0c1e7eb861e56915c5b6c60a214b75240366) --- nova/api/openstack/compute/services.py | 10 ++++-- .../api/openstack/compute/test_services.py | 33 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/nova/api/openstack/compute/services.py b/nova/api/openstack/compute/services.py index c4aade2d51ae..98247b88829a 100644 --- a/nova/api/openstack/compute/services.py +++ b/nova/api/openstack/compute/services.py @@ -240,8 +240,14 @@ class ServiceController(wsgi.Controller): self.placementclient.delete_resource_provider( context, service.compute_node, cascade=True) # remove the host_mapping of this host. - hm = objects.HostMapping.get_by_host(context, service.host) - hm.destroy() + try: + hm = objects.HostMapping.get_by_host(context, service.host) + hm.destroy() + except exception.HostMappingNotFound: + # It's possible to startup a nova-compute service and then + # delete it (maybe it was accidental?) before mapping it to + # a cell using discover_hosts, so we just ignore this. + pass self.host_api.service_delete(context, id) except exception.ServiceNotFound: diff --git a/nova/tests/unit/api/openstack/compute/test_services.py b/nova/tests/unit/api/openstack/compute/test_services.py index fb36081876ae..4b0c17db8913 100644 --- a/nova/tests/unit/api/openstack/compute/test_services.py +++ b/nova/tests/unit/api/openstack/compute/test_services.py @@ -704,6 +704,39 @@ class ServicesTestV21(test.TestCase): self.controller.delete, self.req, 1234) self.assertTrue(host_api.service_delete.called) + @mock.patch('nova.objects.InstanceList.get_uuids_by_host', + return_value=objects.InstanceList()) + @mock.patch('nova.objects.HostMapping.get_by_host', + side_effect=exception.HostMappingNotFound(name='host1')) + def test_compute_service_delete_host_mapping_not_found( + self, get_instances, get_hm): + """Tests that we are still able to successfully delete a nova-compute + service even if the HostMapping is not found. + """ + @mock.patch.object(self.controller.host_api, 'service_get_by_id', + return_value=objects.Service( + host='host1', binary='nova-compute', + compute_node=objects.ComputeNode())) + @mock.patch.object(self.controller.aggregate_api, + 'get_aggregates_by_host', + return_value=objects.AggregateList()) + @mock.patch.object(self.controller.placementclient, + 'delete_resource_provider') + @mock.patch.object(self.controller.host_api, 'service_delete') + def _test(service_delete, delete_resource_provider, + get_aggregates_by_host, service_get_by_id): + self.controller.delete(self.req, 2) + ctxt = self.req.environ['nova.context'] + service_get_by_id.assert_called_once_with(ctxt, 2) + get_instances.assert_called_once_with(ctxt, 'host1') + get_aggregates_by_host.assert_called_once_with(ctxt, 'host1') + delete_resource_provider.assert_called_once_with( + ctxt, service_get_by_id.return_value.compute_node, + cascade=True) + get_hm.assert_called_once_with(ctxt, 'host1') + service_delete.assert_called_once_with(ctxt, 2) + _test() + # This test is just to verify that the servicegroup API gets used when # calling the API @mock.patch.object(db_driver.DbDriver, 'is_up', side_effect=KeyError)