diff --git a/doc/source/support-matrix.ini b/doc/source/support-matrix.ini index 105d6369a6..360e6d8693 100644 --- a/doc/source/support-matrix.ini +++ b/doc/source/support-matrix.ini @@ -313,8 +313,8 @@ driver-impl-vmware=missing driver-notes-vmware=https://bugs.launchpad.net/nova/+bug/1192192 driver-impl-hyperv=complete driver-impl-ironic=missing -driver-impl-libvirt-vz-vm=missing -driver-impl-libvirt-vz-ct=missing +driver-impl-libvirt-vz-vm=complete +driver-impl-libvirt-vz-ct=complete [operation.force-live-migration-to-complete] title=Force live migration to complete diff --git a/nova/tests/unit/virt/libvirt/test_driver.py b/nova/tests/unit/virt/libvirt/test_driver.py index dd0651a4ab..a010b2dcb5 100644 --- a/nova/tests/unit/virt/libvirt/test_driver.py +++ b/nova/tests/unit/virt/libvirt/test_driver.py @@ -7638,6 +7638,60 @@ class LibvirtConnTestCase(test.NoDBTestCase): self.context, instance_ref, 'dest', False, migrate_data, guest, []) + def test_live_migration_parallels_no_new_xml(self): + self.flags(virt_type='parallels', group='libvirt') + self.flags(enabled=False, group='vnc') + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI()) + instance_dict = dict(self.test_instance) + instance_dict.update({'host': 'fake', + 'power_state': power_state.RUNNING, + 'vm_state': vm_states.ACTIVE}) + instance = objects.Instance(**instance_dict) + migrate_data = objects.LibvirtLiveMigrateData( + block_migration=False) + dom_mock = mock.MagicMock() + guest = libvirt_guest.Guest(dom_mock) + drvr._live_migration_operation(self.context, instance, 'dest', + False, migrate_data, guest, []) + # when new xml is not passed we fall back to migrateToURI + dom_mock.migrateToURI.assert_called_once_with( + drvr._live_migration_uri('dest'), + flags=0, bandwidth=0) + + @mock.patch.object(utils, 'spawn') + @mock.patch.object(host.Host, 'get_guest') + @mock.patch.object(fakelibvirt.Connection, '_mark_running') + @mock.patch.object(libvirt_driver.LibvirtDriver, + '_live_migration_monitor') + @mock.patch.object(libvirt_driver.LibvirtDriver, + '_live_migration_copy_disk_paths') + def test_live_migration_parallels_no_migrate_disks(self, + mock_copy_disk_paths, + mock_monitor, + mock_running, + mock_guest, + mock_thread): + self.flags(virt_type='parallels', group='libvirt') + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI()) + instance_dict = dict(self.test_instance) + instance_dict.update({'host': 'fake', + 'power_state': power_state.RUNNING, + 'vm_state': vm_states.ACTIVE}) + instance = objects.Instance(**instance_dict) + migrate_data = objects.LibvirtLiveMigrateData( + block_migration=True) + dom = fakelibvirt.Domain(drvr._get_connection(), '', True) + guest = libvirt_guest.Guest(dom) + mock_guest.return_value = guest + drvr._live_migration(self.context, instance, 'dest', + lambda: None, lambda: None, True, + migrate_data) + self.assertFalse(mock_copy_disk_paths.called) + mock_thread.assert_called_once_with( + drvr._live_migration_operation, + self.context, instance, 'dest', True, + migrate_data, guest, []) + def test_live_migration_update_volume_xml(self): self.compute = manager.ComputeManager() instance_dict = dict(self.test_instance) @@ -7798,9 +7852,9 @@ class LibvirtConnTestCase(test.NoDBTestCase): ('xen', 'xenmigr://%s/system'), ('kvm', 'qemu+tcp://%s/system'), ('qemu', 'qemu+tcp://%s/system'), + ('parallels', 'parallels+tcp://%s/system'), # anything else will return None ('lxc', None), - ('parallels', None), ) dest = 'destination' for hyperv, uri in hypervisor_uri_map: @@ -9420,6 +9474,17 @@ class LibvirtConnTestCase(test.NoDBTestCase): # Assert that we did nothing self.assertEqual({}, fake_backend.created_disks) + @mock.patch.object(libvirt_driver.LibvirtDriver, + '_fetch_instance_kernel_ramdisk') + def test_create_images_and_backing_parallels(self, mock_fetch): + self.flags(virt_type='parallels', group='libvirt') + instance = objects.Instance(**self.test_instance) + instance.vm_mode = fields.VMMode.EXE + drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI()) + drvr._create_images_and_backing(self.context, instance, + '/fake/instance/dir', None) + self.assertFalse(mock_fetch.called) + def _generate_target_ret(self, target_connect_addr=None): target_ret = { 'graphics_listen_addrs': {'spice': '127.0.0.1', 'vnc': '127.0.0.1'}, diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 6add73117e..ffcef901eb 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -681,12 +681,11 @@ class LibvirtDriver(driver.ComputeDriver): @staticmethod def _live_migration_uri(dest): - # Only Xen and QEMU support live migration, see - # https://libvirt.org/migration.html#scenarios for reference uris = { 'kvm': 'qemu+tcp://%s/system', 'qemu': 'qemu+tcp://%s/system', 'xen': 'xenmigr://%s/system', + 'parallels': 'parallels+tcp://%s/system', } virt_type = CONF.libvirt.virt_type uri = CONF.libvirt.live_migration_uri or uris.get(virt_type) @@ -6036,11 +6035,13 @@ class LibvirtDriver(driver.ComputeDriver): migrate_uri = self._migrate_uri(dest) params = None - new_xml_str = libvirt_migrate.get_updated_guest_xml( - # TODO(sahid): It's not a really good idea to pass - # the method _get_volume_config and we should to find - # a way to avoid this in future. - guest, migrate_data, self._get_volume_config) + new_xml_str = None + if CONF.libvirt.virt_type != "parallels": + new_xml_str = libvirt_migrate.get_updated_guest_xml( + # TODO(sahid): It's not a really good idea to pass + # the method _get_volume_config and we should to find + # a way to avoid this in future. + guest, migrate_data, self._get_volume_config) if self._host.has_min_version( MIN_LIBVIRT_BLOCK_LM_WITH_VOLUMES_VERSION): params = { @@ -6483,7 +6484,8 @@ class LibvirtDriver(driver.ComputeDriver): disk_paths = [] device_names = [] - if migrate_data.block_migration: + if (migrate_data.block_migration and + CONF.libvirt.virt_type != "parallels"): disk_paths, device_names = self._live_migration_copy_disk_paths( context, instance, guest) @@ -6792,6 +6794,12 @@ class LibvirtDriver(driver.ComputeDriver): not available. """ + + # Virtuozzo containers don't use backing file + if (CONF.libvirt.virt_type == "parallels" and + instance.vm_mode == fields.VMMode.EXE): + return + if not disk_info: disk_info = [] diff --git a/releasenotes/notes/live-migration-vz-3236af37a522e411.yaml b/releasenotes/notes/live-migration-vz-3236af37a522e411.yaml new file mode 100644 index 0000000000..398d63392b --- /dev/null +++ b/releasenotes/notes/live-migration-vz-3236af37a522e411.yaml @@ -0,0 +1,4 @@ +--- +features: + - Live migration is supported for both Virtuozzo containers + and virtual machines when using virt_type=parallels.