ironic: Cleanup instance information when spawn fails

Instance information such as an instance_uuid set to an ironic node by
_add_driver_fields() is not cleared when spawning is aborted by an
exception raised before ironic starts deployment. Then, ironic node
stays AVAILABLE state with instance_uuid set. This information is not
cleared even if the instance is deleted. The ironic node cannot be
removed nor deployed again becuase instance_uuid remains.

This patch adds a method to remove the information. This method is
called if ironic doesn't need unprovisioning when an instance is
destroyed.

Change-Id: Idf5191aa1c990552ca2340856d5d5b6ac03f7539
Closes-Bug: 1596922
(cherry picked from commit 0e24e9e2ec)
This commit is contained in:
Hironori Shiina 2016-07-10 15:32:58 +09:00 committed by Dmitry Tantsur
parent cc2105fc4b
commit 20ba099205
2 changed files with 37 additions and 1 deletions

View File

@ -957,6 +957,24 @@ class IronicDriverTestCase(test.NoDBTestCase):
self.driver._add_driver_fields,
node, instance, image_meta, flavor)
def _test_remove_driver_fields(self, mock_update):
node = ironic_utils.get_test_node(driver='fake')
instance = fake_instance.fake_instance_obj(self.ctx,
node=node.uuid)
self.driver._remove_driver_fields(node, instance)
expected_patch = [{'path': '/instance_info', 'op': 'remove'},
{'path': '/instance_uuid', 'op': 'remove'}]
mock_update.assert_called_once_with(node.uuid, expected_patch)
@mock.patch.object(FAKE_CLIENT.node, 'update')
def test_remove_driver_fields(self, mock_update):
self._test_remove_driver_fields(mock_update)
@mock.patch.object(FAKE_CLIENT.node, 'update')
def test_remove_driver_fields_fail(self, mock_update):
mock_update.side_effect = ironic_exception.BadRequest()
self._test_remove_driver_fields(mock_update)
@mock.patch.object(configdrive, 'required_by')
@mock.patch.object(FAKE_CLIENT, 'node')
def test_spawn_node_driver_validation_fail(self, mock_node,
@ -1156,8 +1174,10 @@ class IronicDriverTestCase(test.NoDBTestCase):
self.assertEqual('/dev/sda1', instance.default_ephemeral_device)
@mock.patch.object(FAKE_CLIENT, 'node')
@mock.patch.object(ironic_driver.IronicDriver, '_remove_driver_fields')
@mock.patch.object(ironic_driver.IronicDriver, '_cleanup_deploy')
def _test_destroy(self, state, mock_cleanup_deploy, mock_node):
def _test_destroy(self, state, mock_cleanup_deploy,
mock_remove_driver_fields, mock_node):
node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
network_info = 'foo'
@ -1181,8 +1201,10 @@ class IronicDriverTestCase(test.NoDBTestCase):
if state in ironic_driver._UNPROVISION_STATES:
mock_node.set_provision_state.assert_called_once_with(
node_uuid, 'deleted')
self.assertFalse(mock_remove_driver_fields.called)
else:
self.assertFalse(mock_node.set_provision_state.called)
mock_remove_driver_fields.assert_called_once_with(node, instance)
def test_destroy(self):
for state in ironic_states.PROVISION_STATE_LIST:

View File

@ -377,6 +377,18 @@ class IronicDriver(virt_driver.ComputeDriver):
LOG.error(msg)
raise exception.InstanceDeployFailure(msg)
def _remove_driver_fields(self, node, instance):
patch = [{'path': '/instance_info', 'op': 'remove'},
{'path': '/instance_uuid', 'op': 'remove'}]
try:
self.ironicclient.call('node.update', node.uuid, patch)
except ironic.exc.BadRequest as e:
LOG.warning(_LW("Failed to remove deploy parameters from node "
"%(node)s when unprovisioning the instance "
"%(instance)s: %(reason)s"),
{'node': node.uuid, 'instance': instance.uuid,
'reason': six.text_type(e)})
def _cleanup_deploy(self, node, instance, network_info):
self._unplug_vifs(node, instance, network_info)
self._stop_firewall(instance, network_info)
@ -868,6 +880,8 @@ class IronicDriver(virt_driver.ComputeDriver):
if node.provision_state in _UNPROVISION_STATES:
self._unprovision(instance, node)
else:
self._remove_driver_fields(node, instance)
self._cleanup_deploy(node, instance, network_info)
LOG.info(_LI('Successfully unprovisioned Ironic node %s'),