Fix soft deleting vm fails after "nova resize" vm

Problem description:
When trying to soft-delete an instance that is just resized but the resize
action is not yet confirmed, the soft-delete call will first attempt to
confirm_resize the instance, then an error will occur since confirm_resize
method does not expect a SOFT_DELETING task_state currently.
https://github.com/openstack/nova/blob/8e052c7/nova/compute/manager.py#L3911

Fix steps:
1 Add SOFT_DELETING to the expected_task_state of confirm_resize method.
2 Since confirm_resize method sets instance.task_state to None, set
  instance.task_state to back to SOFT_DELETING after confirm_resize is
  executed, so the rest workflow should finish as normal situations.

Co-Authored-By: Chen <dstbtgagt@foxmail.com>

Change-Id: Ia4592adc93960625148ffa6e9f7d1cfa0c6046aa
Closes-Bug: #1712480
This commit is contained in:
Neha Alhat 2017-09-18 17:57:44 +05:30 committed by Matt Riedemann
parent d78055df0e
commit 018522f4d0
4 changed files with 38 additions and 1 deletions

View File

@ -1895,6 +1895,18 @@ class API(base.Base):
if instance.vm_state == vm_states.RESIZED:
self._confirm_resize_on_deleting(context, instance)
# NOTE(neha_alhat): After confirm resize vm_state will become
# 'active' and task_state will be set to 'None'. But for soft
# deleting a vm, the _do_soft_delete callback requires
# task_state in 'SOFT_DELETING' status. So, we need to set
# task_state as 'SOFT_DELETING' again for soft_delete case.
# After confirm resize and before saving the task_state to
# "SOFT_DELETING", during the short window, user can submit
# soft delete vm request again and system will accept and
# process it without any errors.
if delete_type == 'soft_delete':
instance.task_state = instance_attrs['task_state']
instance.save()
is_local_delete = True
try:

View File

@ -3767,7 +3767,8 @@ class ComputeManager(manager.Manager):
instance.vm_state = vm_state
instance.task_state = None
instance.save(expected_task_state=[None, task_states.DELETING])
instance.save(expected_task_state=[None, task_states.DELETING,
task_states.SOFT_DELETING])
self._notify_about_instance_usage(
context, instance, "resize.confirm.end",

View File

@ -1037,6 +1037,13 @@ class _ComputeAPIUnitTestMixIn(object):
mock_confirm = self.useFixture(
fixtures.MockPatchObject(rpcapi, 'confirm_resize')).mock
def _reset_task_state(context, instance, migration, src_host,
cast=False):
inst.update({'task_state': None})
# After confirm resize action, instance task_state is reset to None
mock_confirm.side_effect = _reset_task_state
is_shelved = inst.vm_state in (vm_states.SHELVED,
vm_states.SHELVED_OFFLOADED)
if is_shelved:
@ -1079,6 +1086,17 @@ class _ComputeAPIUnitTestMixIn(object):
expected_record_calls.append(
mock.call(self.context, inst,
instance_actions.CONFIRM_RESIZE))
# After confirm resize action, instance task_state
# is reset to None, so is the expected value. But
# for soft delete, task_state will be again reset
# back to soft-deleting in the code to avoid status
# checking failure.
updates['task_state'] = None
if delete_type == 'soft_delete':
expected_save_calls.append(mock.call())
updates['task_state'] = 'soft-deleting'
if inst.host is not None:
mock_elevated.return_value = self.context
expected_elevated_calls.append(mock.call())
@ -1222,6 +1240,9 @@ class _ComputeAPIUnitTestMixIn(object):
def test_delete_soft_with_down_host(self):
self._test_delete('soft_delete', host='down-host')
def test_delete_soft_in_resized(self):
self._test_delete('soft_delete', vm_state=vm_states.RESIZED)
def test_delete_soft(self):
self._test_delete('soft_delete')

View File

@ -6551,6 +6551,9 @@ class ComputeManagerMigrationTestCase(test.NoDBTestCase):
self.migration,
self.instance.old_flavor,
self.migration.source_node)
mock_save.assert_called_with(expected_task_state=
[None, task_states.DELETING,
task_states.SOFT_DELETING])
do_confirm_resize()