libvirt: Fail when live block migrating instance with volumes
This raises an exception when attempting to live block migrate (nova live-migration --block-migrate) an instance with attached volumes. libvirt copies these volumes from themselves to themselves. At a minimum, this is horribly slow and de-sparses a sparse volume; at worst, this could cause massive data corruption. Closes-Bug: 1398999 Change-Id: Ibcd423976bb9fea46e3e1cb23cc8e5cd944d8fc2
This commit is contained in:
parent
9b27095a00
commit
d667b6a63e
|
@ -440,7 +440,7 @@ class FakeNodeDevice(object):
|
||||||
return self.xml
|
return self.xml
|
||||||
|
|
||||||
|
|
||||||
class LibvirtConnTestCase(test.NoDBTestCase):
|
class LibvirtConnTestCase(test.TestCase):
|
||||||
|
|
||||||
REQUIRES_LOCKING = True
|
REQUIRES_LOCKING = True
|
||||||
|
|
||||||
|
@ -6881,7 +6881,9 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||||
drvr.plug_vifs(mox.IsA(instance), nw_info)
|
drvr.plug_vifs(mox.IsA(instance), nw_info)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
result = drvr.pre_live_migration(c, instance, vol, nw_info, None)
|
result = drvr.pre_live_migration(
|
||||||
|
c, instance, vol, nw_info, None,
|
||||||
|
migrate_data={"block_migration": False})
|
||||||
|
|
||||||
target_ret = {
|
target_ret = {
|
||||||
'graphics_listen_addrs': {'spice': '127.0.0.1', 'vnc': '127.0.0.1'},
|
'graphics_listen_addrs': {'spice': '127.0.0.1', 'vnc': '127.0.0.1'},
|
||||||
|
@ -7080,6 +7082,32 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||||
self.assertTrue(create_image_mock.called)
|
self.assertTrue(create_image_mock.called)
|
||||||
self.assertIsInstance(res, dict)
|
self.assertIsInstance(res, dict)
|
||||||
|
|
||||||
|
def test_pre_live_migration_block_migrate_fails(self):
|
||||||
|
bdms = [{
|
||||||
|
'connection_info': {
|
||||||
|
'serial': '12345',
|
||||||
|
u'data': {
|
||||||
|
'device_path':
|
||||||
|
u'/dev/disk/by-path/ip-1.2.3.4:3260-iqn.abc.12345.t-lun-X'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'mount_device': '/dev/sda'}]
|
||||||
|
|
||||||
|
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||||
|
instance = objects.Instance(**self.test_instance)
|
||||||
|
|
||||||
|
with contextlib.nested(
|
||||||
|
mock.patch.object(drvr, '_create_images_and_backing'),
|
||||||
|
mock.patch.object(drvr, 'ensure_filtering_rules_for_instance'),
|
||||||
|
mock.patch.object(drvr, 'plug_vifs'),
|
||||||
|
mock.patch.object(drvr, '_connect_volume'),
|
||||||
|
mock.patch.object(driver, 'block_device_info_get_mapping',
|
||||||
|
return_value=bdms)):
|
||||||
|
self.assertRaises(exception.MigrationError,
|
||||||
|
drvr.pre_live_migration,
|
||||||
|
self.context, instance, block_device_info=None,
|
||||||
|
network_info=[], disk_info={}, migrate_data={})
|
||||||
|
|
||||||
def test_get_instance_disk_info_works_correctly(self):
|
def test_get_instance_disk_info_works_correctly(self):
|
||||||
# Test data
|
# Test data
|
||||||
instance = objects.Instance(**self.test_instance)
|
instance = objects.Instance(**self.test_instance)
|
||||||
|
|
|
@ -5442,6 +5442,18 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
CONF.libvirt.virt_type, image_meta, vol)
|
CONF.libvirt.virt_type, image_meta, vol)
|
||||||
self._connect_volume(connection_info, disk_info)
|
self._connect_volume(connection_info, disk_info)
|
||||||
|
|
||||||
|
if is_block_migration and len(block_device_mapping):
|
||||||
|
# NOTE(stpierre): if this instance has mapped volumes,
|
||||||
|
# we can't do a block migration, since that will
|
||||||
|
# result in volumes being copied from themselves to
|
||||||
|
# themselves, which is a recipe for disaster.
|
||||||
|
LOG.error(
|
||||||
|
_LE('Cannot block migrate instance %s with mapped volumes') %
|
||||||
|
instance.uuid)
|
||||||
|
raise exception.MigrationError(
|
||||||
|
_('Cannot block migrate instance %s with mapped volumes') %
|
||||||
|
instance.uuid)
|
||||||
|
|
||||||
# We call plug_vifs before the compute manager calls
|
# We call plug_vifs before the compute manager calls
|
||||||
# ensure_filtering_rules_for_instance, to ensure bridge is set up
|
# ensure_filtering_rules_for_instance, to ensure bridge is set up
|
||||||
# Retry operation is necessary because continuously request comes,
|
# Retry operation is necessary because continuously request comes,
|
||||||
|
|
Loading…
Reference in New Issue