From c63f4af000af191a9a70b6b62dbda40a5c9b59e5 Mon Sep 17 00:00:00 2001 From: Nikola Dipanov Date: Thu, 10 Oct 2013 16:00:26 +0200 Subject: [PATCH] Libvirt: disallow live-mig for volume-backed with local disk This patch makes libvirt raise an error if a live migration was requested without shared storage for a volume-backed instance, if that instance has any local disks. The reason is that without shared storage, local disks will be re-created on the destination node which can result in loss of data. Change-Id: Ic96dabf6020e957309280862b325792faf44b1f5 Closes-bug: 1236356 (cherry picked from commit cf89e78a1b921adee5b1943600315b0637fdefdc) --- nova/tests/virt/libvirt/test_libvirt.py | 31 +++++++++++++++++++++++++ nova/virt/libvirt/driver.py | 4 +++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/nova/tests/virt/libvirt/test_libvirt.py b/nova/tests/virt/libvirt/test_libvirt.py index 227a4fcf6c45..fc4a7c963038 100644 --- a/nova/tests/virt/libvirt/test_libvirt.py +++ b/nova/tests/virt/libvirt/test_libvirt.py @@ -2861,6 +2861,8 @@ class LibvirtConnTestCase(test.TestCase): self.mox.StubOutWithMock(conn, "_check_shared_storage_test_file") conn._check_shared_storage_test_file("file").AndReturn(False) + self.mox.StubOutWithMock(conn, "get_instance_disk_info") + conn.get_instance_disk_info(instance_ref['name']).AndReturn('[]') self.mox.StubOutWithMock(conn, "_assert_dest_node_has_enough_disk") conn._assert_dest_node_has_enough_disk( @@ -2881,12 +2883,32 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) self.mox.StubOutWithMock(conn, "_check_shared_storage_test_file") conn._check_shared_storage_test_file("file").AndReturn(False) + self.mox.StubOutWithMock(conn, "get_instance_disk_info") + conn.get_instance_disk_info(instance_ref['name']).AndReturn('[]') self.mox.ReplayAll() ret = conn.check_can_live_migrate_source(self.context, instance_ref, dest_check_data) self.assertTrue(type(ret) == dict) self.assertTrue('is_shared_storage' in ret) + def test_check_can_live_migrate_source_vol_backed_w_disk_raises(self): + instance_ref = db.instance_create(self.context, self.test_instance) + dest_check_data = {"filename": "file", + "block_migration": False, + "disk_over_commit": False, + "disk_available_mb": 1024, + "is_volume_backed": True} + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + self.mox.StubOutWithMock(conn, "_check_shared_storage_test_file") + conn._check_shared_storage_test_file("file").AndReturn(False) + self.mox.StubOutWithMock(conn, "get_instance_disk_info") + conn.get_instance_disk_info(instance_ref['name']).AndReturn( + '[{"fake_disk_attr": "fake_disk_val"}]') + self.mox.ReplayAll() + self.assertRaises(exception.InvalidSharedStorage, + conn.check_can_live_migrate_source, self.context, + instance_ref, dest_check_data) + def test_check_can_live_migrate_source_vol_backed_fails(self): instance_ref = db.instance_create(self.context, self.test_instance) dest_check_data = {"filename": "file", @@ -2897,6 +2919,9 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) self.mox.StubOutWithMock(conn, "_check_shared_storage_test_file") conn._check_shared_storage_test_file("file").AndReturn(False) + self.mox.StubOutWithMock(conn, "get_instance_disk_info") + conn.get_instance_disk_info(instance_ref['name']).AndReturn( + '[{"fake_disk_attr": "fake_disk_val"}]') self.mox.ReplayAll() self.assertRaises(exception.InvalidSharedStorage, conn.check_can_live_migrate_source, self.context, @@ -2912,6 +2937,8 @@ class LibvirtConnTestCase(test.TestCase): self.mox.StubOutWithMock(conn, "_check_shared_storage_test_file") conn._check_shared_storage_test_file("file").AndReturn(True) + self.mox.StubOutWithMock(conn, "get_instance_disk_info") + conn.get_instance_disk_info(instance_ref['name']).AndReturn('[]') self.mox.ReplayAll() self.assertRaises(exception.InvalidLocalStorage, @@ -2928,6 +2955,8 @@ class LibvirtConnTestCase(test.TestCase): self.mox.StubOutWithMock(conn, "_check_shared_storage_test_file") conn._check_shared_storage_test_file("file").AndReturn(False) + self.mox.StubOutWithMock(conn, "get_instance_disk_info") + conn.get_instance_disk_info(instance_ref['name']).AndReturn('[]') self.mox.ReplayAll() self.assertRaises(exception.InvalidSharedStorage, @@ -2944,6 +2973,8 @@ class LibvirtConnTestCase(test.TestCase): self.mox.StubOutWithMock(conn, "get_instance_disk_info") conn.get_instance_disk_info(instance_ref["name"]).AndReturn( '[{"virt_disk_size":2}]') + conn.get_instance_disk_info(instance_ref["name"]).AndReturn( + '[{"virt_disk_size":2}]') dest_check_data = {"filename": "file", "disk_available_mb": 0, diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index eba247fd1a61..e1a515c5bde7 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -3812,6 +3812,8 @@ class LibvirtDriver(driver.ComputeDriver): filename = dest_check_data["filename"] block_migration = dest_check_data["block_migration"] is_volume_backed = dest_check_data.get('is_volume_backed', False) + has_local_disks = bool( + jsonutils.loads(self.get_instance_disk_info(instance['name']))) shared = self._check_shared_storage_test_file(filename) @@ -3824,7 +3826,7 @@ class LibvirtDriver(driver.ComputeDriver): dest_check_data['disk_available_mb'], dest_check_data['disk_over_commit']) - elif not shared and not is_volume_backed: + elif not shared and (not is_volume_backed or has_local_disks): reason = _("Live migration can not be used " "without shared storage.") raise exception.InvalidSharedStorage(reason=reason, path=source)