Skip to content

Commit c21cbf2

Browse files
mgagnemriedem
authored andcommitted
Fix rebuild of baremetal instance when vm_state is ERROR
Nova allows rebuild of instance when vm_state is ERROR. [1] The vm_state is restored to ACTIVE only after a successful build. This means rebuilding a baremetal instance using the Ironic driver is impossible because wait_for_active fails if vm_state=ERROR is found. This is a regression introduced in a previous change which added the ability to delete an instance in spawning state. [2] This present change will skip the abort installation logic if task_state is REBUILD_SPAWNING while preserving the previous logic. [1] https://bugs.launchpad.net/nova/+bug/1183946 [2] https://bugs.launchpad.net/nova/+bug/1455000 Change-Id: I857ad7264f1a7ef1263d8a9d4eca491d6c8dce0f Closes-bug: #1735009 (cherry picked from commit 1819718)
1 parent 7cc6527 commit c21cbf2

File tree

2 files changed

+23
-3
lines changed

2 files changed

+23
-3
lines changed

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,23 @@ def test__wait_for_active_done(self, fake_validate, fake_refresh):
201201
fake_validate.assert_called_once_with(instance)
202202
fake_refresh.assert_called_once_with()
203203

204+
@mock.patch.object(objects.Instance, 'refresh')
205+
@mock.patch.object(ironic_driver.IronicDriver,
206+
'_validate_instance_and_node')
207+
def test__wait_for_active_from_error(self, fake_validate, fake_refresh):
208+
instance = fake_instance.fake_instance_obj(self.ctx,
209+
uuid=uuidutils.generate_uuid(),
210+
vm_state=vm_states.ERROR,
211+
task_state=task_states.REBUILD_SPAWNING)
212+
node = ironic_utils.get_test_node(
213+
provision_state=ironic_states.ACTIVE)
214+
215+
fake_validate.return_value = node
216+
self.assertRaises(loopingcall.LoopingCallDone,
217+
self.driver._wait_for_active, instance)
218+
fake_validate.assert_called_once_with(instance)
219+
fake_refresh.assert_called_once_with()
220+
204221
@mock.patch.object(objects.Instance, 'refresh')
205222
@mock.patch.object(ironic_driver.IronicDriver,
206223
'_validate_instance_and_node')
@@ -236,7 +253,8 @@ def test__wait_for_active_abort_deleted(self):
236253
self._wait_for_active_abort({'vm_state': vm_states.DELETED})
237254

238255
def test__wait_for_active_abort_error(self):
239-
self._wait_for_active_abort({'vm_state': vm_states.ERROR})
256+
self._wait_for_active_abort({'task_state': task_states.SPAWNING,
257+
'vm_state': vm_states.ERROR})
240258

241259
@mock.patch.object(ironic_driver.IronicDriver,
242260
'_validate_instance_and_node')

nova/virt/ironic/driver.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,8 +485,10 @@ def _cleanup_deploy(self, node, instance, network_info=None,
485485
def _wait_for_active(self, instance):
486486
"""Wait for the node to be marked as ACTIVE in Ironic."""
487487
instance.refresh()
488-
if (instance.task_state == task_states.DELETING or
489-
instance.vm_state in (vm_states.ERROR, vm_states.DELETED)):
488+
# Ignore REBUILD_SPAWNING when rebuilding from ERROR state.
489+
if (instance.task_state != task_states.REBUILD_SPAWNING and
490+
(instance.task_state == task_states.DELETING or
491+
instance.vm_state in (vm_states.ERROR, vm_states.DELETED))):
490492
raise exception.InstanceDeployFailure(
491493
_("Instance %s provisioning was aborted") % instance.uuid)
492494

0 commit comments

Comments
 (0)