Skip to content

Commit

Permalink
Add new configuration option to turn auto converge on/off
Browse files Browse the repository at this point in the history
Since QEMU 1.6 and Libvirt 1.2.3 it is possible to use auto
converge during live migrations. This option helps to live migrate
instances with memory-intensive workload by throttling down CPU
on guest machine.

Partially-implements: blueprint auto-live-migration-completion
Change-Id: I967bad47b67586ea2838e219eed1c6dce91ce153
  • Loading branch information
pkoniszewski committed Jun 28, 2016
1 parent 3d83c46 commit 0c0f600
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 0 deletions.
18 changes: 18 additions & 0 deletions nova/conf/libvirt.py
Expand Up @@ -171,6 +171,24 @@
When using post-copy mode, if the source and destination hosts loose network
connectivity, the VM being live-migrated will need to be rebooted. For more
details, please see the Administration guide.
Related options:
* live_migration_permit_auto_converge
"""),
cfg.BoolOpt('live_migration_permit_auto_converge',
default=False,
help="""
This option allows nova to start live migration with auto converge on.
Auto converge throttles down CPU if a progress of on-going live migration
is slow. Auto converge will only be used if this flag is set to True and
post copy is not permitted or post copy is unavailable due to the version
of libvirt and QEMU in use. Auto converge requires libvirt>=1.2.3 and
QEMU>=1.6.0.
Related options:
* live_migration_permit_post_copy
"""),
cfg.StrOpt('snapshot_image_format',
choices=('raw', 'qcow2', 'vmdk', 'vdi'),
Expand Down
1 change: 1 addition & 0 deletions nova/tests/unit/virt/libvirt/fakelibvirt.py
Expand Up @@ -94,6 +94,7 @@ def _reset():
VIR_MIGRATE_PERSIST_DEST = 8
VIR_MIGRATE_UNDEFINE_SOURCE = 16
VIR_MIGRATE_NON_SHARED_INC = 128
VIR_MIGRATE_AUTO_CONVERGE = 8192
VIR_MIGRATE_POSTCOPY = 32768

VIR_NODE_CPU_STATS_ALL_CPUS = -1
Expand Down
132 changes: 132 additions & 0 deletions nova/tests/unit/virt/libvirt/test_driver.py
Expand Up @@ -1206,9 +1206,120 @@ def test_live_migration_permit_postcopy_true(self, host):
libvirt_driver.libvirt.VIR_MIGRATE_NON_SHARED_INC |
libvirt_driver.libvirt.VIR_MIGRATE_POSTCOPY))

@mock.patch.object(host.Host, 'has_min_version', return_value=True)
def test_live_migration_permit_auto_converge_true(self, host):
self.flags(live_migration_permit_auto_converge=True, group='libvirt')
self._do_test_parse_migration_flags(
lm_config=('VIR_MIGRATE_PERSIST_DEST, '
'VIR_MIGRATE_PEER2PEER, '
'VIR_MIGRATE_LIVE, '
'VIR_MIGRATE_TUNNELLED'),
bm_config=('VIR_MIGRATE_PERSIST_DEST, '
'VIR_MIGRATE_PEER2PEER, '
'VIR_MIGRATE_LIVE, '
'VIR_MIGRATE_TUNNELLED, '
'VIR_MIGRATE_NON_SHARED_INC'),
lm_expected=(libvirt_driver.libvirt.VIR_MIGRATE_UNDEFINE_SOURCE |
libvirt_driver.libvirt.VIR_MIGRATE_PEER2PEER |
libvirt_driver.libvirt.VIR_MIGRATE_LIVE |
libvirt_driver.libvirt.VIR_MIGRATE_TUNNELLED |
libvirt_driver.libvirt.VIR_MIGRATE_AUTO_CONVERGE),
bm_expected=(libvirt_driver.libvirt.VIR_MIGRATE_UNDEFINE_SOURCE |
libvirt_driver.libvirt.VIR_MIGRATE_PEER2PEER |
libvirt_driver.libvirt.VIR_MIGRATE_LIVE |
libvirt_driver.libvirt.VIR_MIGRATE_TUNNELLED |
libvirt_driver.libvirt.VIR_MIGRATE_NON_SHARED_INC |
libvirt_driver.libvirt.VIR_MIGRATE_AUTO_CONVERGE))

@mock.patch.object(host.Host, 'has_min_version', return_value=True)
def test_live_migration_permit_auto_converge_and_post_copy_true(self,
host):
self.flags(live_migration_permit_auto_converge=True, group='libvirt')
self.flags(live_migration_permit_post_copy=True, group='libvirt')
self._do_test_parse_migration_flags(
lm_config=('VIR_MIGRATE_PERSIST_DEST, '
'VIR_MIGRATE_PEER2PEER, '
'VIR_MIGRATE_LIVE, '
'VIR_MIGRATE_TUNNELLED'),
bm_config=('VIR_MIGRATE_PERSIST_DEST, '
'VIR_MIGRATE_PEER2PEER, '
'VIR_MIGRATE_LIVE, '
'VIR_MIGRATE_TUNNELLED, '
'VIR_MIGRATE_NON_SHARED_INC'),
lm_expected=(libvirt_driver.libvirt.VIR_MIGRATE_UNDEFINE_SOURCE |
libvirt_driver.libvirt.VIR_MIGRATE_PEER2PEER |
libvirt_driver.libvirt.VIR_MIGRATE_LIVE |
libvirt_driver.libvirt.VIR_MIGRATE_TUNNELLED |
libvirt_driver.libvirt.VIR_MIGRATE_POSTCOPY),
bm_expected=(libvirt_driver.libvirt.VIR_MIGRATE_UNDEFINE_SOURCE |
libvirt_driver.libvirt.VIR_MIGRATE_PEER2PEER |
libvirt_driver.libvirt.VIR_MIGRATE_LIVE |
libvirt_driver.libvirt.VIR_MIGRATE_TUNNELLED |
libvirt_driver.libvirt.VIR_MIGRATE_NON_SHARED_INC |
libvirt_driver.libvirt.VIR_MIGRATE_POSTCOPY))

@mock.patch.object(host.Host, 'has_min_version')
def test_live_migration_auto_converge_and_post_copy_true_old_libvirt(
self, mock_host):
self.flags(live_migration_permit_auto_converge=True, group='libvirt')
self.flags(live_migration_permit_post_copy=True, group='libvirt')

def fake_has_min_version(lv_ver=None, hv_ver=None, hv_type=None):
if (lv_ver == libvirt_driver.MIN_LIBVIRT_POSTCOPY_VERSION and
hv_ver == libvirt_driver.MIN_QEMU_POSTCOPY_VERSION):
return False
return True
mock_host.side_effect = fake_has_min_version

self._do_test_parse_migration_flags(
lm_config=('VIR_MIGRATE_PERSIST_DEST, '
'VIR_MIGRATE_PEER2PEER, '
'VIR_MIGRATE_LIVE, '
'VIR_MIGRATE_TUNNELLED'),
bm_config=('VIR_MIGRATE_PERSIST_DEST, '
'VIR_MIGRATE_PEER2PEER, '
'VIR_MIGRATE_LIVE, '
'VIR_MIGRATE_TUNNELLED, '
'VIR_MIGRATE_NON_SHARED_INC'),
lm_expected=(libvirt_driver.libvirt.VIR_MIGRATE_UNDEFINE_SOURCE |
libvirt_driver.libvirt.VIR_MIGRATE_PEER2PEER |
libvirt_driver.libvirt.VIR_MIGRATE_LIVE |
libvirt_driver.libvirt.VIR_MIGRATE_TUNNELLED |
libvirt_driver.libvirt.VIR_MIGRATE_AUTO_CONVERGE),
bm_expected=(libvirt_driver.libvirt.VIR_MIGRATE_UNDEFINE_SOURCE |
libvirt_driver.libvirt.VIR_MIGRATE_PEER2PEER |
libvirt_driver.libvirt.VIR_MIGRATE_LIVE |
libvirt_driver.libvirt.VIR_MIGRATE_TUNNELLED |
libvirt_driver.libvirt.VIR_MIGRATE_NON_SHARED_INC |
libvirt_driver.libvirt.VIR_MIGRATE_AUTO_CONVERGE))

@mock.patch.object(host.Host, 'has_min_version', return_value=False)
def test_live_migration_permit_postcopy_true_old_libvirt(self, host):
self.flags(live_migration_permit_post_copy=True, group='libvirt')

self._do_test_parse_migration_flags(
lm_config=('VIR_MIGRATE_PERSIST_DEST, '
'VIR_MIGRATE_PEER2PEER, '
'VIR_MIGRATE_LIVE, '
'VIR_MIGRATE_TUNNELLED'),
bm_config=('VIR_MIGRATE_PERSIST_DEST, '
'VIR_MIGRATE_PEER2PEER, '
'VIR_MIGRATE_LIVE, '
'VIR_MIGRATE_TUNNELLED, '
'VIR_MIGRATE_NON_SHARED_INC'),
lm_expected=(libvirt_driver.libvirt.VIR_MIGRATE_UNDEFINE_SOURCE |
libvirt_driver.libvirt.VIR_MIGRATE_PEER2PEER |
libvirt_driver.libvirt.VIR_MIGRATE_LIVE |
libvirt_driver.libvirt.VIR_MIGRATE_TUNNELLED),
bm_expected=(libvirt_driver.libvirt.VIR_MIGRATE_UNDEFINE_SOURCE |
libvirt_driver.libvirt.VIR_MIGRATE_PEER2PEER |
libvirt_driver.libvirt.VIR_MIGRATE_LIVE |
libvirt_driver.libvirt.VIR_MIGRATE_TUNNELLED |
libvirt_driver.libvirt.VIR_MIGRATE_NON_SHARED_INC))

@mock.patch.object(host.Host, 'has_min_version', return_value=False)
def test_live_migration_permit_auto_converge_true_old_libvirt(self, host):
self.flags(live_migration_permit_auto_converge=True, group='libvirt')
self._do_test_parse_migration_flags(
lm_config=('VIR_MIGRATE_PERSIST_DEST, '
'VIR_MIGRATE_PEER2PEER, '
Expand Down Expand Up @@ -1250,6 +1361,27 @@ def test_live_migration_permit_postcopy_false(self):
libvirt_driver.libvirt.VIR_MIGRATE_TUNNELLED |
libvirt_driver.libvirt.VIR_MIGRATE_NON_SHARED_INC))

def test_live_migration_permit_autoconverge_false(self):
self._do_test_parse_migration_flags(
lm_config=('VIR_MIGRATE_PERSIST_DEST, '
'VIR_MIGRATE_PEER2PEER, '
'VIR_MIGRATE_LIVE, '
'VIR_MIGRATE_TUNNELLED'),
bm_config=('VIR_MIGRATE_PERSIST_DEST, '
'VIR_MIGRATE_PEER2PEER, '
'VIR_MIGRATE_LIVE, '
'VIR_MIGRATE_TUNNELLED, '
'VIR_MIGRATE_NON_SHARED_INC'),
lm_expected=(libvirt_driver.libvirt.VIR_MIGRATE_UNDEFINE_SOURCE |
libvirt_driver.libvirt.VIR_MIGRATE_PEER2PEER |
libvirt_driver.libvirt.VIR_MIGRATE_LIVE |
libvirt_driver.libvirt.VIR_MIGRATE_TUNNELLED),
bm_expected=(libvirt_driver.libvirt.VIR_MIGRATE_UNDEFINE_SOURCE |
libvirt_driver.libvirt.VIR_MIGRATE_PEER2PEER |
libvirt_driver.libvirt.VIR_MIGRATE_LIVE |
libvirt_driver.libvirt.VIR_MIGRATE_TUNNELLED |
libvirt_driver.libvirt.VIR_MIGRATE_NON_SHARED_INC))

@mock.patch('nova.utils.get_image_from_system_metadata')
@mock.patch.object(host.Host,
'has_min_version', return_value=True)
Expand Down
28 changes: 28 additions & 0 deletions nova/virt/libvirt/driver.py
Expand Up @@ -272,6 +272,10 @@ def repr_method(self):
MIN_LIBVIRT_KVM_PPC64_VERSION = (1, 2, 12)
MIN_QEMU_PPC64_VERSION = (2, 1, 0)

# Auto converge support
MIN_LIBVIRT_AUTO_CONVERGE_VERSION = (1, 2, 3)
MIN_QEMU_AUTO_CONVERGE = (1, 6, 0)

# Names of the types that do not get compressed during migration
NO_COMPRESSION_TYPES = ('qcow2',)

Expand Down Expand Up @@ -611,6 +615,25 @@ def _handle_live_migration_post_copy(self, migration_flags,
migration_flags &= ~libvirt.VIR_MIGRATE_POSTCOPY
return migration_flags

def _handle_live_migration_auto_converge(self, migration_flags,
config_name):
if self._host.has_min_version(lv_ver=MIN_LIBVIRT_AUTO_CONVERGE_VERSION,
hv_ver=MIN_QEMU_AUTO_CONVERGE):
if (self._is_post_copy_available() and
(migration_flags & libvirt.VIR_MIGRATE_POSTCOPY) != 0):
migration_flags &= ~libvirt.VIR_MIGRATE_AUTO_CONVERGE
LOG.info(_LI('The live_migration_permit_post_copy is set to '
'True and post copy live migration is available '
'so auto-converge will not be in use.'))
elif not CONF.libvirt.live_migration_permit_auto_converge:
migration_flags &= ~libvirt.VIR_MIGRATE_AUTO_CONVERGE
else:
migration_flags |= libvirt.VIR_MIGRATE_AUTO_CONVERGE
elif CONF.libvirt.live_migration_permit_auto_converge:
LOG.info(_LI('The live_migration_permit_auto_converge is set '
'to True, but it is not supported.'))
return migration_flags

def _parse_migration_flags(self):
def str2sum(str_val):
logical_sum = 0
Expand Down Expand Up @@ -647,6 +670,11 @@ def str2sum(str_val):
block_migration_flags = self._handle_live_migration_post_copy(
block_migration_flags, block_config_name)

live_migration_flags = self._handle_live_migration_auto_converge(
live_migration_flags, live_config_name)
block_migration_flags = self._handle_live_migration_auto_converge(
block_migration_flags, block_config_name)

self._live_migration_flags = live_migration_flags
self._block_migration_flags = block_migration_flags

Expand Down
@@ -0,0 +1,7 @@
---
features:
- New configuration option live_migration_permit_auto_converge
has been added to allow hypervisor to throttle down CPU of an
instance during live migration in case of a slow progress due
to high ratio of dirty pages. Requires libvirt>=1.2.3 and
QEMU>=1.6.0.

0 comments on commit 0c0f600

Please sign in to comment.