Skip to content

Commit 1ed3f8f

Browse files
markgoddardmriedem
authored andcommitted
[ironic] Don't remove instance info twice in destroy
During teardown, at the end of the ironic virt driver's destroy method, we call _cleanup_deploy, which since https://review.opendev.org/#/c/563722/ removes instance_info from the ironic node. Given that we're destroying the node, the instance_info will have been cleared from the node anyway, so we don't need to do this. Further, it can cause tear down to fail if automated cleaning is enabled in ironic. This happens because ironic prevents updates to nodes that are undergoing a state transition, as is the case during cleaning. The virt driver will retry this request by default every 2 seconds with 60 attempts. Typically this is not long enough for cleaning to complete, so tear down fails with the following error: Conflict: Node a00696d5-32ba-475e-9528-59bf11cffea6 can not be updated while a state transition is in progress. (HTTP 409) This change skips the instance info update in _cleanup_deploy in the case of tear down. Change-Id: Iea337f73c231db2cb9d9f639b92475daaede6793 Closes-Bug: #1815799 (cherry picked from commit 060e42b) (cherry picked from commit 95ace7c)
1 parent c19d602 commit 1ed3f8f

File tree

2 files changed

+34
-6
lines changed

2 files changed

+34
-6
lines changed

nova/tests/unit/virt/ironic/test_driver.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1809,7 +1809,8 @@ def fake_set_provision_state(*_):
18091809

18101810
mock_node.get_by_instance_uuid.assert_called_with(
18111811
instance.uuid, fields=ironic_driver._NODE_FIELDS)
1812-
mock_cleanup_deploy.assert_called_with(node, instance, network_info)
1812+
mock_cleanup_deploy.assert_called_with(node, instance, network_info,
1813+
remove_instance_info=False)
18131814

18141815
# For states that makes sense check if set_provision_state has
18151816
# been called
@@ -1842,7 +1843,8 @@ def test_destroy_trigger_undeploy_fail(self, mock_clean, fake_validate,
18421843
mock_sps.side_effect = exception.NovaException()
18431844
self.assertRaises(exception.NovaException, self.driver.destroy,
18441845
self.ctx, instance, None, None)
1845-
mock_clean.assert_called_once_with(node, instance, None)
1846+
mock_clean.assert_called_once_with(node, instance, None,
1847+
remove_instance_info=False)
18461848

18471849
@mock.patch.object(FAKE_CLIENT.node, 'update')
18481850
@mock.patch.object(ironic_driver.IronicDriver,
@@ -1861,7 +1863,8 @@ def test_destroy_trigger_remove_info_fail(self, mock_clean, fake_validate,
18611863
mock_update.side_effect = SystemError('unexpected error')
18621864
self.assertRaises(SystemError, self.driver.destroy,
18631865
self.ctx, instance, None, None)
1864-
mock_clean.assert_called_once_with(node, instance, None)
1866+
mock_clean.assert_called_once_with(node, instance, None,
1867+
remove_instance_info=False)
18651868

18661869
@mock.patch.object(FAKE_CLIENT.node, 'set_provision_state')
18671870
@mock.patch.object(ironic_driver.IronicDriver,
@@ -2692,6 +2695,24 @@ def test__cleanup_deploy(self, mock_call, mock_vol, mock_unvif,
26922695
mock_call.has_calls(
26932696
[mock.call('node.update', node.uuid, expected_patch)])
26942697

2698+
@mock.patch.object(ironic_driver.IronicDriver, '_stop_firewall')
2699+
@mock.patch.object(ironic_driver.IronicDriver, '_unplug_vifs')
2700+
@mock.patch.object(ironic_driver.IronicDriver,
2701+
'_cleanup_volume_target_info')
2702+
@mock.patch.object(cw.IronicClientWrapper, 'call')
2703+
def test__cleanup_deploy_no_remove_ii(self, mock_call, mock_vol,
2704+
mock_unvif, mock_stop_fw):
2705+
# TODO(TheJulia): This REALLY should be updated to cover all of the
2706+
# calls that take place.
2707+
node = ironic_utils.get_test_node(driver='fake')
2708+
instance = fake_instance.fake_instance_obj(self.ctx,
2709+
node=node.uuid)
2710+
self.driver._cleanup_deploy(node, instance, remove_instance_info=False)
2711+
mock_vol.assert_called_once_with(instance)
2712+
mock_unvif.assert_called_once_with(node, instance, None)
2713+
mock_stop_fw.assert_called_once_with(instance, None)
2714+
self.assertFalse(mock_call.called)
2715+
26952716

26962717
class IronicDriverSyncTestCase(IronicDriverTestCase):
26972718

nova/virt/ironic/driver.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,11 +471,13 @@ def _cleanup_volume_target_info(self, instance):
471471
'reason': e},
472472
instance=instance)
473473

474-
def _cleanup_deploy(self, node, instance, network_info=None):
474+
def _cleanup_deploy(self, node, instance, network_info=None,
475+
remove_instance_info=True):
475476
self._cleanup_volume_target_info(instance)
476477
self._unplug_vifs(node, instance, network_info)
477478
self._stop_firewall(instance, network_info)
478-
self._remove_instance_info_from_node(node, instance)
479+
if remove_instance_info:
480+
self._remove_instance_info_from_node(node, instance)
479481

480482
def _wait_for_active(self, instance):
481483
"""Wait for the node to be marked as ACTIVE in Ironic."""
@@ -1264,7 +1266,12 @@ def destroy(self, context, instance, network_info,
12641266
# removed from ironic node.
12651267
self._remove_instance_info_from_node(node, instance)
12661268
finally:
1267-
self._cleanup_deploy(node, instance, network_info)
1269+
# NOTE(mgoddard): We don't need to remove instance info at this
1270+
# point since we will have already done it. The destroy will only
1271+
# succeed if this method returns without error, so we will end up
1272+
# removing the instance info eventually.
1273+
self._cleanup_deploy(node, instance, network_info,
1274+
remove_instance_info=False)
12681275

12691276
LOG.info('Successfully unprovisioned Ironic node %s',
12701277
node.uuid, instance=instance)

0 commit comments

Comments
 (0)