[libvirt] Live migration fails when config_drive_format=iso9660
This patch enables block live migration with read-only config drive. Basically it copies config drive to destination host prior to migration start, so that QEMU will not try to write to read-only device and therefore it will not raise an exception. Co-Authored-By: Bartosz Fic <bartosz.fic@intel.com> Co-Authored-By: Michael Still <mikal@stillhq.com> Change-Id: I9ddaef658e3d9a77e4982275d7df9e56b0a5459f Closes-Bug: #1246201
This commit is contained in:
parent
3f700b5a5a
commit
bc4b9980fa
|
@ -84,7 +84,6 @@ from nova.tests import uuidsentinel as uuids
|
|||
from nova import utils
|
||||
from nova import version
|
||||
from nova.virt import block_device as driver_block_device
|
||||
from nova.virt import configdrive
|
||||
from nova.virt.disk import api as disk
|
||||
from nova.virt import driver
|
||||
from nova.virt import fake
|
||||
|
@ -9426,27 +9425,48 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
|||
pre_migration_result=True)['pre_live_migration_result'],
|
||||
target_ret)
|
||||
|
||||
def test_pre_live_migration_block_with_config_drive_mocked(self):
|
||||
# Creating testdata
|
||||
@mock.patch.object(os, 'mkdir')
|
||||
@mock.patch('nova.virt.libvirt.utils.get_instance_path_at_destination')
|
||||
@mock.patch('nova.virt.libvirt.driver.remotefs.'
|
||||
'RemoteFilesystem.copy_file')
|
||||
@mock.patch('nova.virt.driver.block_device_info_get_mapping')
|
||||
@mock.patch('nova.virt.configdrive.required_by', return_value=True)
|
||||
def test_pre_live_migration_block_with_config_drive_success(
|
||||
self, mock_required_by, block_device_info_get_mapping,
|
||||
mock_copy_file, mock_get_instance_path, mock_mkdir):
|
||||
self.flags(config_drive_format='iso9660')
|
||||
vol = {'block_device_mapping': [
|
||||
{'connection_info': 'dummy', 'mount_device': '/dev/sda'},
|
||||
{'connection_info': 'dummy', 'mount_device': '/dev/sdb'}]}
|
||||
fake_instance_path = os.path.join(cfg.CONF.instances_path,
|
||||
'/fake_instance_uuid')
|
||||
mock_get_instance_path.return_value = fake_instance_path
|
||||
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
|
||||
def fake_true(*args, **kwargs):
|
||||
return True
|
||||
|
||||
self.stubs.Set(configdrive, 'required_by', fake_true)
|
||||
|
||||
instance = objects.Instance(**self.test_instance)
|
||||
c = context.get_admin_context()
|
||||
migrate_data = objects.LibvirtLiveMigrateData()
|
||||
migrate_data.is_shared_instance_path = False
|
||||
migrate_data.is_shared_block_storage = False
|
||||
migrate_data.block_migration = True
|
||||
migrate_data.instance_relative_path = 'foo'
|
||||
src = "%s:%s/disk.config" % (instance.host, fake_instance_path)
|
||||
|
||||
self.assertRaises(exception.NoLiveMigrationForConfigDriveInLibVirt,
|
||||
drvr.pre_live_migration, c, instance, vol, None,
|
||||
None, {'is_shared_instance_path': False,
|
||||
'is_shared_block_storage': False,
|
||||
'block_migration': False,
|
||||
'instance_relative_path': 'foo'})
|
||||
result = drvr.pre_live_migration(
|
||||
self.context, instance, vol, [], None, migrate_data)
|
||||
|
||||
block_device_info_get_mapping.assert_called_once_with(
|
||||
{'block_device_mapping': [
|
||||
{'connection_info': 'dummy', 'mount_device': '/dev/sda'},
|
||||
{'connection_info': 'dummy', 'mount_device': '/dev/sdb'}
|
||||
]}
|
||||
)
|
||||
mock_copy_file.assert_called_once_with(src, fake_instance_path)
|
||||
|
||||
migrate_data.graphics_listen_addrs_vnc = '127.0.0.1'
|
||||
migrate_data.graphics_listen_addrs_spice = '127.0.0.1'
|
||||
migrate_data.serial_listen_addr = '127.0.0.1'
|
||||
self.assertEqual(migrate_data, result)
|
||||
|
||||
@mock.patch('nova.virt.driver.block_device_info_get_mapping',
|
||||
return_value=())
|
||||
|
|
|
@ -6446,22 +6446,6 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
is_shared_instance_path = migrate_data.is_shared_instance_path
|
||||
is_block_migration = migrate_data.block_migration
|
||||
|
||||
if configdrive.required_by(instance):
|
||||
# NOTE(sileht): configdrive is stored into the block storage
|
||||
# kvm is a block device, live migration will work
|
||||
# NOTE(sileht): the configdrive is stored into a shared path
|
||||
# kvm don't need to migrate it, live migration will work
|
||||
# NOTE(dims): Using config drive with iso format does not work
|
||||
# because of a bug in libvirt with read only devices. However
|
||||
# one can use vfat as config_drive_format which works fine.
|
||||
# Please see bug/1246201 for details on the libvirt bug.
|
||||
if (is_shared_block_storage or
|
||||
is_shared_instance_path or
|
||||
CONF.config_drive_format == 'vfat'):
|
||||
pass
|
||||
else:
|
||||
raise exception.NoLiveMigrationForConfigDriveInLibVirt()
|
||||
|
||||
if not is_shared_instance_path:
|
||||
instance_dir = libvirt_utils.get_instance_path_at_destination(
|
||||
instance, migrate_data)
|
||||
|
@ -6498,6 +6482,19 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
self._create_images_and_backing(
|
||||
context, instance, instance_dir, disk_info,
|
||||
fallback_from_host=instance.host)
|
||||
if (configdrive.required_by(instance) and
|
||||
CONF.config_drive_format == 'iso9660'):
|
||||
# NOTE(pkoniszewski): Due to a bug in libvirt iso config
|
||||
# drive needs to be copied to destination prior to
|
||||
# migration when instance path is not shared and block
|
||||
# storage is not shared. Files that are already present
|
||||
# on destination are excluded from a list of files that
|
||||
# need to be copied to destination. If we don't do that
|
||||
# live migration will fail on copying iso config drive to
|
||||
# destination and writing to read-only device.
|
||||
# Please see bug/1246201 for more details.
|
||||
src = "%s:%s/disk.config" % (instance.host, instance_dir)
|
||||
self._remotefs.copy_file(src, instance_dir)
|
||||
|
||||
if not is_block_migration:
|
||||
# NOTE(angdraug): when block storage is shared between source
|
||||
|
|
Loading…
Reference in New Issue