Skip to content

Commit

Permalink
Get list of disks to copy early to avoid multiple DB hits
Browse files Browse the repository at this point in the history
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
  • Loading branch information
pkoniszewski committed Jan 5, 2016
1 parent a237fc8 commit f0d5fc6
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 31 deletions.
36 changes: 17 additions & 19 deletions nova/tests/unit/virt/libvirt/test_driver.py
Expand Up @@ -6870,20 +6870,14 @@ def test_live_migration_copy_disk_paths(self, mock_xml):
"_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(), "<domain/>", 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(), "<domain/>", False)
guest = libvirt_guest.Guest(dom)
instance = objects.Instance(**self.test_instance)

def fake_stat(path):
Expand All @@ -6902,15 +6896,14 @@ def st_size(self):
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
Expand Down Expand Up @@ -6978,7 +6971,8 @@ def fake_time():
False,
migrate_data,
dom,
finish_event)
finish_event,
[])

if expect_result == self.EXPECT_SUCCESS:
self.assertFalse(fake_recover_method.called,
Expand Down Expand Up @@ -7239,14 +7233,18 @@ def test_live_migration_downtime_steps(self):
@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(),
"<domain><name>demo</name></domain>", 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

Expand All @@ -7257,7 +7255,7 @@ def fake_recover():
pass

drvr._live_migration(self.context, instance, "fakehost",
fake_post, fake_recover, False,
fake_post, fake_recover, True,
migrate_data)

class AnyEventletEvent(object):
Expand All @@ -7266,12 +7264,12 @@ def __eq__(self, other):

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)
Expand Down
25 changes: 13 additions & 12 deletions nova/virt/libvirt/driver.py
Expand Up @@ -5828,12 +5828,12 @@ def _live_migration_copy_disk_paths(self, guest):
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
Expand All @@ -5849,12 +5849,8 @@ def _live_migration_data_gb(self, instance, guest, block_migration):
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)
Expand All @@ -5872,9 +5868,9 @@ def _live_migration_data_gb(self, instance, guest, block_migration):
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)
Expand Down Expand Up @@ -6087,6 +6083,11 @@ def _live_migration(self, context, instance, dest, post_method,

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.
Expand Down Expand Up @@ -6114,7 +6115,7 @@ def thread_finished(thread, event):
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)
Expand Down

0 comments on commit f0d5fc6

Please sign in to comment.