From 98834ab9f745d53dd3bf40e486e4b8f14f1fd47e Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Wed, 3 Dec 2014 16:16:34 -0600 Subject: [PATCH] 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. (cherry picked from commit d667b6a63e80b2f8d6311c2cf224ba32628eed84) Closes-Bug: 1398999 Change-Id: Ibcd423976bb9fea46e3e1cb23cc8e5cd944d8fc2 --- nova/tests/virt/libvirt/test_driver.py | 30 +++++++++++++++++++++++++- nova/virt/libvirt/driver.py | 12 +++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/nova/tests/virt/libvirt/test_driver.py b/nova/tests/virt/libvirt/test_driver.py index c4d7e52559df..c9e03e45b524 100644 --- a/nova/tests/virt/libvirt/test_driver.py +++ b/nova/tests/virt/libvirt/test_driver.py @@ -5993,7 +5993,9 @@ class LibvirtConnTestCase(test.TestCase): conn.plug_vifs(mox.IsA(inst_ref), nw_info) self.mox.ReplayAll() - result = conn.pre_live_migration(c, inst_ref, vol, nw_info, None) + result = conn.pre_live_migration( + c, inst_ref, vol, nw_info, None, + migrate_data={"block_migration": False}) target_res = {'graphics_listen_addrs': {'spice': '127.0.0.1', 'vnc': '127.0.0.1'}} @@ -6189,6 +6191,32 @@ class LibvirtConnTestCase(test.TestCase): self.assertTrue(create_image_mock.called) 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 = db.instance_create(self.context, 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): # Test data instance_ref = db.instance_create(self.context, self.test_instance) diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 8a321a639b2d..cf3a93759834 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -5577,6 +5577,18 @@ class LibvirtDriver(driver.ComputeDriver): CONF.libvirt.virt_type, vol) 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 # ensure_filtering_rules_for_instance, to ensure bridge is set up # Retry operation is necessary because continuously request comes,