From f0d5fc61916f41214da580097a09136e4ed2c99a Mon Sep 17 00:00:00 2001 From: Pawel Koniszewski Date: Fri, 11 Dec 2015 03:28:50 +0100 Subject: [PATCH] Get list of disks to copy early to avoid multiple DB hits To support selective block migration we need to read block devices from nova block device mappings instead of libvirt block info. It means that in current implementation we would call _live_migration_copy_disk_paths two times - from live_migration_operations and from live_migration_monitor. To avoid that this change gets disk paths early and pass them as and additional paremeter to live migration monitor. Change-Id: Ic894cfc7374ba06b436b2a76a5984012d1dba3a5 Related-bug: #1398999 --- nova/tests/unit/virt/libvirt/test_driver.py | 36 ++++++++++----------- nova/virt/libvirt/driver.py | 25 +++++++------- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index 86dd43fb59cb..5e53598231cf 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -6870,20 +6870,14 @@ class LibvirtConnTestCase(test.NoDBTestCase): "_live_migration_copy_disk_paths") def test_live_migration_data_gb_plain(self, mock_paths): drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) - dom = fakelibvirt.Domain(drvr._get_connection(), "", False) - guest = libvirt_guest.Guest(dom) instance = objects.Instance(**self.test_instance) - data_gb = drvr._live_migration_data_gb(instance, guest, False) + data_gb = drvr._live_migration_data_gb(instance, []) self.assertEqual(2, data_gb) self.assertEqual(0, mock_paths.call_count) - @mock.patch.object(libvirt_driver.LibvirtDriver, - "_live_migration_copy_disk_paths") - def test_live_migration_data_gb_block(self, mock_paths): + def test_live_migration_data_gb_block(self): drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) - dom = fakelibvirt.Domain(drvr._get_connection(), "", False) - guest = libvirt_guest.Guest(dom) instance = objects.Instance(**self.test_instance) def fake_stat(path): @@ -6902,15 +6896,14 @@ class LibvirtConnTestCase(test.NoDBTestCase): else: raise Exception("Should not be reached") - mock_paths.return_value = ["/var/lib/nova/instance/123/disk.root", - "/dev/mapper/somevol"] + disk_paths = ["/var/lib/nova/instance/123/disk.root", + "/dev/mapper/somevol"] with mock.patch.object(os, "stat") as mock_stat: mock_stat.side_effect = fake_stat - data_gb = drvr._live_migration_data_gb(instance, guest, True) + data_gb = drvr._live_migration_data_gb(instance, disk_paths) # Expecting 2 GB for RAM, plus 10 GB for disk.root # and 1.5 GB rounded to 2 GB for somevol, so 14 GB self.assertEqual(14, data_gb) - self.assertEqual(1, mock_paths.call_count) EXPECT_SUCCESS = 1 EXPECT_FAILURE = 2 @@ -6978,7 +6971,8 @@ class LibvirtConnTestCase(test.NoDBTestCase): False, migrate_data, dom, - finish_event) + finish_event, + []) if expect_result == self.EXPECT_SUCCESS: self.assertFalse(fake_recover_method.called, @@ -7239,14 +7233,18 @@ class LibvirtConnTestCase(test.NoDBTestCase): @mock.patch.object(libvirt_driver.LibvirtDriver, "_live_migration_monitor") @mock.patch.object(host.Host, "get_guest") @mock.patch.object(fakelibvirt.Connection, "_mark_running") - def test_live_migration_main(self, mock_running, mock_guest, - mock_monitor, mock_thread): + @mock.patch.object(libvirt_driver.LibvirtDriver, + "_live_migration_copy_disk_paths") + def test_live_migration_main(self, mock_copy_disk_path, mock_running, + mock_guest, mock_monitor, mock_thread): drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) instance = objects.Instance(**self.test_instance) dom = fakelibvirt.Domain(drvr._get_connection(), "demo", True) guest = libvirt_guest.Guest(dom) migrate_data = {} + disk_paths = ['/dev/vda', '/dev/vdb'] + mock_copy_disk_path.return_value = disk_paths mock_guest.return_value = guest @@ -7257,7 +7255,7 @@ class LibvirtConnTestCase(test.NoDBTestCase): pass drvr._live_migration(self.context, instance, "fakehost", - fake_post, fake_recover, False, + fake_post, fake_recover, True, migrate_data) class AnyEventletEvent(object): @@ -7266,12 +7264,12 @@ class LibvirtConnTestCase(test.NoDBTestCase): mock_thread.assert_called_once_with( drvr._live_migration_operation, - self.context, instance, "fakehost", False, + self.context, instance, "fakehost", True, migrate_data, dom) mock_monitor.assert_called_once_with( self.context, instance, guest, "fakehost", - fake_post, fake_recover, False, - migrate_data, dom, AnyEventletEvent()) + fake_post, fake_recover, True, + migrate_data, dom, AnyEventletEvent(), disk_paths) def _do_test_create_images_and_backing(self, disk_type): drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 797c4846c9e1..4a782e591958 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -5828,12 +5828,12 @@ class LibvirtDriver(driver.ComputeDriver): disks.append(dev.source_path) return disks - def _live_migration_data_gb(self, instance, guest, block_migration): + def _live_migration_data_gb(self, instance, disk_paths): '''Calculate total amount of data to be transferred :param instance: the nova.objects.Instance being migrated - :param guest: the Guest being migrated - :param block_migration: true if block migration is requested + :param disk_paths: list of disk paths that are being migrated + with instance Calculates the total amount of data that needs to be transferred during the live migration. The actual @@ -5849,12 +5849,8 @@ class LibvirtDriver(driver.ComputeDriver): if ram_gb < 2: ram_gb = 2 - if not block_migration: - return ram_gb - - paths = self._live_migration_copy_disk_paths(guest) disk_gb = 0 - for path in paths: + for path in disk_paths: try: size = os.stat(path).st_size size_gb = (size / units.Gi) @@ -5872,9 +5868,9 @@ class LibvirtDriver(driver.ComputeDriver): def _live_migration_monitor(self, context, instance, guest, dest, post_method, recover_method, block_migration, - migrate_data, dom, finish_event): - data_gb = self._live_migration_data_gb(instance, guest, - block_migration) + migrate_data, dom, finish_event, + disk_paths): + data_gb = self._live_migration_data_gb(instance, disk_paths) downtime_steps = list(self._migration_downtime_steps(data_gb)) completion_timeout = int( CONF.libvirt.live_migration_completion_timeout * data_gb) @@ -6087,6 +6083,11 @@ class LibvirtDriver(driver.ComputeDriver): guest = self._host.get_guest(instance) + disk_paths = [] + if block_migration: + disk_paths = self._live_migration_copy_disk_paths( + context, instance, guest) + # TODO(sahid): We are converting all calls from a # virDomain object to use nova.virt.libvirt.Guest. # We should be able to remove dom at the end. @@ -6114,7 +6115,7 @@ class LibvirtDriver(driver.ComputeDriver): self._live_migration_monitor(context, instance, guest, dest, post_method, recover_method, block_migration, migrate_data, - dom, finish_event) + dom, finish_event, disk_paths) except Exception as ex: LOG.warn(_LW("Error monitoring migration: %(ex)s"), {"ex": ex}, instance=instance, exc_info=True)