Fix live migration with shared storage

At the moment, when live migration is requested, we attempt to
copy the instance dvd disks and log files to the remote location,
other instance files being transparently copied by Hyper-V.

The issue is that we don't check whether shared storage is being
used, in which case this will fail.

This change adds this check.

Closes-Bug: #1565895

Change-Id: Ib646c90c830a1cd0e5401d14c6d400226b034f73
This commit is contained in:
Lucian Petrut 2016-05-27 13:37:22 +03:00
parent 820f5f8ff4
commit ba2b0bbda5
4 changed files with 53 additions and 8 deletions

View File

@ -51,13 +51,16 @@ class LiveMigrationOps(object):
instance_name = instance_ref["name"]
try:
self._vmops.copy_vm_dvd_disks(instance_name, dest)
# We must make sure that the console log workers are stopped,
# otherwise we won't be able to delete / move VM log files.
self._serial_console_ops.stop_console_handler(instance_name)
self._pathutils.copy_vm_console_logs(instance_name, dest)
shared_storage = (
self._pathutils.check_remote_instances_dir_shared(dest))
if not shared_storage:
self._vmops.copy_vm_dvd_disks(instance_name, dest)
self._pathutils.copy_vm_console_logs(instance_name, dest)
self._livemigrutils.live_migrate_vm(instance_name,
dest)
except Exception:

View File

@ -202,3 +202,11 @@ class PathUtils(pathutils.PathUtils):
"is local, thus it will not be handled as a "
"loopback share.")
LOG.exception(err_msg)
def check_remote_instances_dir_shared(self, dest):
# Checks if the instances dir from a remote host points
# to the same storage location as the local instances dir.
local_inst_dir = self.get_instances_dir()
remote_inst_dir = self.get_instances_dir(dest)
return self.check_dirs_shared_storage(local_inst_dir,
remote_inst_dir)

View File

@ -37,14 +37,19 @@ class LiveMigrationOpsTestCase(test_base.HyperVBaseTestCase):
@mock.patch('hyperv.nova.serialconsoleops.SerialConsoleOps.'
'stop_console_handler')
@mock.patch('hyperv.nova.vmops.VMOps.copy_vm_dvd_disks')
def _test_live_migration(self, mock_get_vm_dvd_paths,
mock_stop_console_handler, side_effect):
def _test_live_migration(self, mock_copy_dvd_disks,
mock_stop_console_handler, side_effect=None,
shared_storage=False):
mock_instance = fake_instance.fake_instance_obj(self.context)
mock_post = mock.MagicMock()
mock_recover = mock.MagicMock()
fake_dest = mock.sentinel.DESTINATION
self._livemigrops._livemigrutils.live_migrate_vm.side_effect = [
side_effect]
mock_check_shared_storage = (
self._livemigrops._pathutils.check_remote_instances_dir_shared)
mock_check_shared_storage.return_value = shared_storage
if side_effect is os_win_exc.HyperVException:
self.assertRaises(os_win_exc.HyperVException,
self._livemigrops.live_migration,
@ -61,9 +66,18 @@ class LiveMigrationOpsTestCase(test_base.HyperVBaseTestCase):
mock_stop_console_handler.assert_called_once_with(
mock_instance.name)
mock_check_shared_storage.assert_called_once_with(fake_dest)
mock_copy_logs = self._livemigrops._pathutils.copy_vm_console_logs
mock_copy_logs.assert_called_once_with(mock_instance.name,
fake_dest)
if not shared_storage:
mock_copy_logs.assert_called_once_with(mock_instance.name,
fake_dest)
mock_copy_dvd_disks.assert_called_once_with(
mock_instance.name, fake_dest)
else:
self.assertFalse(mock_copy_logs.called)
self.assertFalse(mock_copy_dvd_disks.called)
mock_live_migr = self._livemigrops._livemigrutils.live_migrate_vm
mock_live_migr.assert_called_once_with(mock_instance.name,
fake_dest)
@ -71,7 +85,10 @@ class LiveMigrationOpsTestCase(test_base.HyperVBaseTestCase):
fake_dest, False)
def test_live_migration(self):
self._test_live_migration(side_effect=None)
self._test_live_migration()
def test_live_migration_shared_storage(self):
self._test_live_migration(shared_storage=True)
def test_live_migration_exception(self):
self._test_live_migration(side_effect=os_win_exc.HyperVException)

View File

@ -207,3 +207,20 @@ class PathUtilsTestCase(test_base.HyperVBaseTestCase):
local_share_path, share_address)
else:
self.assertFalse(mock_check_dirs_shared_storage.called)
@mock.patch.object(pathutils.PathUtils, 'check_dirs_shared_storage')
@mock.patch.object(pathutils.PathUtils, 'get_instances_dir')
def test_check_remote_instances_shared(self, mock_get_instances_dir,
mock_check_dirs_shared_storage):
mock_get_instances_dir.side_effect = [mock.sentinel.local_inst_dir,
mock.sentinel.remote_inst_dir]
shared_storage = self._pathutils.check_remote_instances_dir_shared(
mock.sentinel.dest)
self.assertEqual(mock_check_dirs_shared_storage.return_value,
shared_storage)
mock_get_instances_dir.assert_has_calls(
[mock.call(), mock.call(mock.sentinel.dest)])
mock_check_dirs_shared_storage.assert_called_once_with(
mock.sentinel.local_inst_dir, mock.sentinel.remote_inst_dir)