Hyper-V: Fixes snapshoting inexistent VM issue

Instance destroy and Instance snapshot are locking operations, meaning
that they can only occur sequentially. This is the result of the commit
6d8903b5d9609305d63aba0067a032a66f5ce3ee

The problem is that the destroy instance can occur before snapshoting,
resulting in a VM NotFound exception being raised while snapshoting, as
the VM no longer exists. In the logs it can be observed that the lock was
being held by "do_terminate_instance", which was then aquired by
"instance_synchronized_snapshot".

Nova ComputeManager expects an InstanceNotFound exception during the
snapshot operation.

Changes exception raised by VMUtils._check_lookup_vm to InstanceNotFound.

Change-Id: I3e4d4f1874c6ee4b410e8bda7412d49f079b5b8f
Closes-Bug: #1486458
This commit is contained in:
Claudiu Belu
2015-09-17 19:56:16 +03:00
parent b39231574a
commit 6e4fdbb122
8 changed files with 43 additions and 9 deletions

View File

@@ -122,7 +122,7 @@ class InstanceEventHandler(object):
"will be ignored."),
instance_name)
return instance_uuid
except exception.NotFound:
except exception.InstanceNotFound:
# The instance has been deleted.
pass

View File

@@ -68,7 +68,7 @@ class LiveMigrationUtils(object):
vms = conn_v2.Msvm_ComputerSystem(ElementName=vm_name)
n = len(vms)
if not n:
raise exception.NotFound(_('VM not found: %s') % vm_name)
raise exception.InstanceNotFound(_('VM not found: %s') % vm_name)
elif n > 1:
raise vmutils.HyperVException(_('Duplicate VM name found: %s')
% vm_name)

View File

@@ -637,7 +637,7 @@ class VMOps(object):
self._set_vm_state(instance,
constants.HYPERV_VM_STATE_DISABLED)
except exception.NotFound:
except exception.InstanceNotFound:
pass
def power_on(self, instance, block_device_info=None, network_info=None):

View File

@@ -194,7 +194,7 @@ class VMUtils(object):
vm = self._lookup_vm(vm_name)
if not vm:
raise exception.NotFound(_('VM not found: %s') % vm_name)
raise exception.InstanceNotFound(_('VM not found: %s') % vm_name)
return vm
def _lookup_vm(self, vm_name):

View File

@@ -139,7 +139,7 @@ class EventHandlerTestCase(test_base.HyperVBaseTestCase):
side_effect = (mock.sentinel.instance_uuid
if not missing_uuid else None, )
else:
side_effect = exception.NotFound
side_effect = exception.InstanceNotFound('fake_instance_uuid')
mock_get_uuid = self._event_handler._vmutils.get_instance_uuid
mock_get_uuid.side_effect = side_effect

View File

@@ -14,14 +14,17 @@
# under the License.
import mock
from nova import exception
from hyperv.nova import livemigrationutils
from hyperv.nova import vmutils
from hyperv.tests import test
class LiveMigrationUtilsTestCase(test.NoDBTestCase):
"""Unit tests for the Hyper-V LiveMigrationUtils class."""
_FAKE_VM_NAME = 'fake_vm_name'
_FAKE_RET_VAL = 0
_RESOURCE_TYPE_VHD = 31
@@ -51,6 +54,30 @@ class LiveMigrationUtilsTestCase(test.NoDBTestCase):
self.liveutils.check_live_migration_config()
self.assertTrue(mock_migr_svc.associators.called)
def test_get_vm(self):
expected_vm = mock.MagicMock()
mock_conn_v2 = mock.MagicMock()
mock_conn_v2.Msvm_ComputerSystem.return_value = [expected_vm]
found_vm = self.liveutils._get_vm(mock_conn_v2, self._FAKE_VM_NAME)
self.assertEqual(expected_vm, found_vm)
def test_get_vm_duplicate(self):
mock_vm = mock.MagicMock()
mock_conn_v2 = mock.MagicMock()
mock_conn_v2.Msvm_ComputerSystem.return_value = [mock_vm, mock_vm]
self.assertRaises(vmutils.HyperVException, self.liveutils._get_vm,
mock_conn_v2, self._FAKE_VM_NAME)
def test_get_vm_not_found(self):
mock_conn_v2 = mock.MagicMock()
mock_conn_v2.Msvm_ComputerSystem.return_value = []
self.assertRaises(exception.InstanceNotFound, self.liveutils._get_vm,
mock_conn_v2, self._FAKE_VM_NAME)
@mock.patch.object(livemigrationutils.LiveMigrationUtils,
'_destroy_planned_vm')
def test_check_existing_planned_vm_found(self, mock_destroy_planned_vm):

View File

@@ -926,7 +926,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
mock_set_vm_state.assert_called_once_with(
mock_instance, constants.HYPERV_VM_STATE_ENABLED)
def _test_power_off(self, timeout):
def _test_power_off(self, timeout, set_state_expected=True):
instance = fake_instance.fake_instance_obj(self.context)
with mock.patch.object(self._vmops, '_set_vm_state') as mock_set_state:
self._vmops.power_off(instance, timeout)
@@ -934,8 +934,9 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
serialops = self._vmops._serial_console_ops
serialops.stop_console_handler.assert_called_once_with(
instance.name)
mock_set_state.assert_called_once_with(
instance, constants.HYPERV_VM_STATE_DISABLED)
if set_state_expected:
mock_set_state.assert_called_once_with(
instance, constants.HYPERV_VM_STATE_DISABLED)
def test_power_off_hard(self):
self._test_power_off(timeout=0)
@@ -945,6 +946,12 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
mock_soft_shutdown.return_value = False
self._test_power_off(timeout=1)
@mock.patch("hyperv.nova.vmops.VMOps._soft_shutdown")
def test_power_off_unexisting_instance(self, mock_soft_shutdown):
mock_soft_shutdown.side_effect = (
exception.InstanceNotFound('fake_instance_uuid'))
self._test_power_off(timeout=1, set_state_expected=False)
@mock.patch("hyperv.nova.vmops.VMOps._set_vm_state")
@mock.patch("hyperv.nova.vmops.VMOps._soft_shutdown")
def test_power_off_soft(self, mock_soft_shutdown, mock_set_state):

View File

@@ -119,7 +119,7 @@ class VMUtilsTestCase(test.NoDBTestCase):
def test_lookup_vm_none(self):
self._vmutils._conn.Msvm_ComputerSystem.return_value = []
self.assertRaises(exception.NotFound,
self.assertRaises(exception.InstanceNotFound,
self._vmutils._lookup_vm_check,
self._FAKE_VM_NAME)