Skip to content

Commit 95ace7c

Browse files
markgoddardjuliakreger
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)
1 parent 9b21d10 commit 95ace7c

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
@@ -1666,7 +1666,8 @@ def fake_set_provision_state(*_):
16661666

16671667
mock_node.get_by_instance_uuid.assert_called_with(
16681668
instance.uuid, fields=ironic_driver._NODE_FIELDS)
1669-
mock_cleanup_deploy.assert_called_with(node, instance, network_info)
1669+
mock_cleanup_deploy.assert_called_with(node, instance, network_info,
1670+
remove_instance_info=False)
16701671

16711672
# For states that makes sense check if set_provision_state has
16721673
# been called
@@ -1699,7 +1700,8 @@ def test_destroy_trigger_undeploy_fail(self, mock_clean, fake_validate,
16991700
mock_sps.side_effect = exception.NovaException()
17001701
self.assertRaises(exception.NovaException, self.driver.destroy,
17011702
self.ctx, instance, None, None)
1702-
mock_clean.assert_called_once_with(node, instance, None)
1703+
mock_clean.assert_called_once_with(node, instance, None,
1704+
remove_instance_info=False)
17031705

17041706
@mock.patch.object(FAKE_CLIENT.node, 'update')
17051707
@mock.patch.object(ironic_driver.IronicDriver,
@@ -1718,7 +1720,8 @@ def test_destroy_trigger_remove_info_fail(self, mock_clean, fake_validate,
17181720
mock_update.side_effect = SystemError('unexpected error')
17191721
self.assertRaises(SystemError, self.driver.destroy,
17201722
self.ctx, instance, None, None)
1721-
mock_clean.assert_called_once_with(node, instance, None)
1723+
mock_clean.assert_called_once_with(node, instance, None,
1724+
remove_instance_info=False)
17221725

17231726
@mock.patch.object(FAKE_CLIENT.node, 'set_provision_state')
17241727
@mock.patch.object(ironic_driver.IronicDriver,
@@ -2549,6 +2552,24 @@ def test__cleanup_deploy(self, mock_call, mock_vol, mock_unvif,
25492552
mock_call.has_calls(
25502553
[mock.call('node.update', node.uuid, expected_patch)])
25512554

2555+
@mock.patch.object(ironic_driver.IronicDriver, '_stop_firewall')
2556+
@mock.patch.object(ironic_driver.IronicDriver, '_unplug_vifs')
2557+
@mock.patch.object(ironic_driver.IronicDriver,
2558+
'_cleanup_volume_target_info')
2559+
@mock.patch.object(cw.IronicClientWrapper, 'call')
2560+
def test__cleanup_deploy_no_remove_ii(self, mock_call, mock_vol,
2561+
mock_unvif, mock_stop_fw):
2562+
# TODO(TheJulia): This REALLY should be updated to cover all of the
2563+
# calls that take place.
2564+
node = ironic_utils.get_test_node(driver='fake')
2565+
instance = fake_instance.fake_instance_obj(self.ctx,
2566+
node=node.uuid)
2567+
self.driver._cleanup_deploy(node, instance, remove_instance_info=False)
2568+
mock_vol.assert_called_once_with(instance)
2569+
mock_unvif.assert_called_once_with(node, instance, None)
2570+
mock_stop_fw.assert_called_once_with(instance, None)
2571+
self.assertFalse(mock_call.called)
2572+
25522573

25532574
class IronicDriverSyncTestCase(IronicDriverTestCase):
25542575

nova/virt/ironic/driver.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -474,11 +474,13 @@ def _cleanup_volume_target_info(self, instance):
474474
'reason': e},
475475
instance=instance)
476476

477-
def _cleanup_deploy(self, node, instance, network_info=None):
477+
def _cleanup_deploy(self, node, instance, network_info=None,
478+
remove_instance_info=True):
478479
self._cleanup_volume_target_info(instance)
479480
self._unplug_vifs(node, instance, network_info)
480481
self._stop_firewall(instance, network_info)
481-
self._remove_instance_info_from_node(node, instance)
482+
if remove_instance_info:
483+
self._remove_instance_info_from_node(node, instance)
482484

483485
def _wait_for_active(self, instance):
484486
"""Wait for the node to be marked as ACTIVE in Ironic."""
@@ -1324,7 +1326,12 @@ def destroy(self, context, instance, network_info,
13241326
# removed from ironic node.
13251327
self._remove_instance_info_from_node(node, instance)
13261328
finally:
1327-
self._cleanup_deploy(node, instance, network_info)
1329+
# NOTE(mgoddard): We don't need to remove instance info at this
1330+
# point since we will have already done it. The destroy will only
1331+
# succeed if this method returns without error, so we will end up
1332+
# removing the instance info eventually.
1333+
self._cleanup_deploy(node, instance, network_info,
1334+
remove_instance_info=False)
13281335

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

0 commit comments

Comments
 (0)