From cf2cfe81dbfecf24a4b9aae70ff46585c319203c Mon Sep 17 00:00:00 2001
From: int32bit <krystism@gmail.com>
Date: Thu, 27 Dec 2018 21:39:19 +0800
Subject: [PATCH] Skip to remove resource provider if compute node not found

As user may change compute service's hostname or compute driver,
this service may have no hypervisor node and the service of compute_node
value may be None. For this case, We just ignore this and let the delete
workflow go on.

Closes-Bug: 1809693

Change-Id: Ia0777e86c556fad763269ef53886f311710c22bb
---
 nova/api/openstack/compute/services.py             | 13 +++++++--
 .../unit/api/openstack/compute/test_services.py    | 32 ++++++++++++++++++++++
 2 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/nova/api/openstack/compute/services.py b/nova/api/openstack/compute/services.py
index ada8dd15af..f9d673a692 100644
--- a/nova/api/openstack/compute/services.py
+++ b/nova/api/openstack/compute/services.py
@@ -243,8 +243,17 @@ class ServiceController(wsgi.Controller):
                 compute_nodes = objects.ComputeNodeList.get_all_by_host(
                     context, service.host)
                 for compute_node in compute_nodes:
-                    self.placementclient.delete_resource_provider(
-                        context, compute_node, cascade=True)
+                    try:
+                        self.placementclient.delete_resource_provider(
+                            context, compute_node, cascade=True)
+                    except exception.ComputeHostNotFound:
+                        # As user may change compute service's hostname or
+                        # compute driver, this service may have no hypervisor
+                        # node and the service of compute_node value may be
+                        # None. For this case, we just ignore this and let the
+                        # delete workflow go on.
+                        pass
+
                 # remove the host_mapping of this host.
                 try:
                     hm = objects.HostMapping.get_by_host(context, service.host)
diff --git a/nova/tests/unit/api/openstack/compute/test_services.py b/nova/tests/unit/api/openstack/compute/test_services.py
index dff73fdfec..7f8f2312af 100644
--- a/nova/tests/unit/api/openstack/compute/test_services.py
+++ b/nova/tests/unit/api/openstack/compute/test_services.py
@@ -640,6 +640,38 @@ class ServicesTestV21(test.TestCase):
             service_delete.assert_called_once_with(ctxt, 2)
         _test()
 
+    @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'))
+    @mock.patch('nova.objects.Service.destroy')
+    def test_compute_service_delete_compute_node_not_found(
+            self, service_delete, get_instances, get_hm):
+        """Tests that we are still able to successfully delete a nova-compute
+        service even if the ComputeHost 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',
+            side_effect=exception.ComputeHostNotFound(host='host1'))
+        def _test(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')
+            get_hm.assert_called_once_with(ctxt, 'host1')
+            service_delete.assert_called_once_with()
+        _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)
-- 
2.16.4

