diff --git a/nova/tests/virt/libvirt/test_driver.py b/nova/tests/virt/libvirt/test_driver.py index 7bae5d52d668..70ee45d37c9f 100644 --- a/nova/tests/virt/libvirt/test_driver.py +++ b/nova/tests/virt/libvirt/test_driver.py @@ -5624,6 +5624,61 @@ class LibvirtConnTestCase(test.TestCase): db.instance_destroy(self.context, instance_ref['uuid']) + @mock.patch.object(libvirt, 'VIR_DOMAIN_XML_MIGRATABLE', 8675, create=True) + def test_live_migration_raises_unsupported_config_exception(self): + # Tests that when migrateToURI2 fails with VIR_ERR_CONFIG_UNSUPPORTED, + # migrateToURI is used instead. + + # Preparing data + instance_ref = fake_instance.fake_instance_obj( + self.context, **self.test_instance) + + # Preparing mocks + vdmock = self.mox.CreateMock(libvirt.virDomain) + self.mox.StubOutWithMock(vdmock, 'migrateToURI2') + self.mox.StubOutWithMock(vdmock, 'migrateToURI') + _bandwidth = CONF.libvirt.live_migration_bandwidth + vdmock.XMLDesc(libvirt.VIR_DOMAIN_XML_MIGRATABLE).AndReturn( + FakeVirtDomain().XMLDesc(0)) + unsupported_config_error = libvirt.libvirtError('ERR') + unsupported_config_error.err = (libvirt.VIR_ERR_CONFIG_UNSUPPORTED,) + # This is the first error we hit but since the error code is + # VIR_ERR_CONFIG_UNSUPPORTED we'll try migrateToURI. + vdmock.migrateToURI2(CONF.libvirt.live_migration_uri % 'dest', None, + mox.IgnoreArg(), mox.IgnoreArg(), None, + _bandwidth).AndRaise(unsupported_config_error) + # This is the second and final error that will actually kill the run, + # we use TestingException to make sure it's not the same libvirtError + # above. + vdmock.migrateToURI(CONF.libvirt.live_migration_uri % 'dest', + mox.IgnoreArg(), None, + _bandwidth).AndRaise(test.TestingException('oops')) + + def fake_lookup(instance_name): + if instance_name == instance_ref.name: + return vdmock + + self.create_fake_libvirt_mock(lookupByName=fake_lookup) + + def fake_recover_method(context, instance, dest, block_migration): + pass + + graphics_listen_addrs = {'vnc': '0.0.0.0', 'spice': '127.0.0.1'} + migrate_data = {'pre_live_migration_result': + {'graphics_listen_addrs': graphics_listen_addrs}} + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + + self.mox.StubOutWithMock( + conn, '_check_graphics_addresses_can_live_migrate') + conn._check_graphics_addresses_can_live_migrate(graphics_listen_addrs) + self.mox.ReplayAll() + + # start test + self.assertRaises(test.TestingException, conn._live_migration, + self.context, instance_ref, 'dest', post_method=None, + recover_method=fake_recover_method, + migrate_data=migrate_data) + def test_rollback_live_migration_at_destination(self): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) with mock.patch.object(conn, "destroy") as mock_destroy: diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 43123eeab25c..90b9d49a8de2 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -5353,13 +5353,40 @@ class LibvirtDriver(driver.ComputeDriver): old_xml_str = dom.XMLDesc(migratable_flag) new_xml_str = self._correct_listen_addr(old_xml_str, listen_addrs) - - dom.migrateToURI2(CONF.libvirt.live_migration_uri % dest, - None, - new_xml_str, - logical_sum, - None, - CONF.libvirt.live_migration_bandwidth) + try: + dom.migrateToURI2(CONF.libvirt.live_migration_uri % dest, + None, + new_xml_str, + logical_sum, + None, + CONF.libvirt.live_migration_bandwidth) + except libvirt.libvirtError as ex: + # NOTE(mriedem): There is a bug in older versions of + # libvirt where the VIR_DOMAIN_XML_MIGRATABLE flag causes + # virDomainDefCheckABIStability to not compare the source + # and target domain xml's correctly for the CPU model. + # We try to handle that error here and attempt the legacy + # migrateToURI path, which could fail if the console + # addresses are not correct, but in that case we have the + # _check_graphics_addresses_can_live_migrate check in place + # to catch it. + # TODO(mriedem): Remove this workaround when + # Red Hat BZ #1141838 is closed. + error_code = ex.get_error_code() + if error_code == libvirt.VIR_ERR_CONFIG_UNSUPPORTED: + LOG.warn(_LW('An error occurred trying to live ' + 'migrate. Falling back to legacy live ' + 'migrate flow. Error: %s'), ex, + instance=instance) + self._check_graphics_addresses_can_live_migrate( + listen_addrs) + dom.migrateToURI( + CONF.libvirt.live_migration_uri % dest, + logical_sum, + None, + CONF.libvirt.live_migration_bandwidth) + else: + raise except Exception as e: with excutils.save_and_reraise_exception():