diff --git a/nova/tests/unit/virt/xenapi/test_vif.py b/nova/tests/unit/virt/xenapi/test_vif.py index 917a44cf21c0..e2472138037e 100644 --- a/nova/tests/unit/virt/xenapi/test_vif.py +++ b/nova/tests/unit/virt/xenapi/test_vif.py @@ -227,13 +227,9 @@ class XenAPIOpenVswitchDriverTestCase(XenVIFDriverTestBase): instance = {'name': "fake_instance"} vm_ref = "fake_vm_ref" - mock_network_get_VIFs = self.mock_patch_object( - self._session.network, 'get_VIFs', return_val=None) self.ovs_driver.unplug(instance, fake_vif, vm_ref) self.assertTrue(mock_super_unplug.called) - self.assertTrue(mock_find_network_with_name_label.called) - self.assertTrue(mock_network_get_VIFs.called) self.assertTrue(mock_delete_network_bridge.called) @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_delete_linux_bridge') @@ -241,30 +237,200 @@ class XenAPIOpenVswitchDriverTestCase(XenVIFDriverTestBase): @mock.patch.object(os_xenapi.client.host_network, 'ovs_del_br') @mock.patch.object(os_xenapi.client.host_network, 'ovs_del_port') @mock.patch.object(network_utils, 'find_network_with_name_label') - def test_delete_network_and_bridge(self, mock_find_network, + @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_get_network_by_vif') + def test_delete_network_and_bridge(self, mock_get_network, + mock_find_network, mock_ovs_del_port, mock_ovs_del_br, mock_delete_linux_port, mock_delete_linux_bridge): - mock_find_network.return_value = 'fake_network' + # Delete network and bridge + mock_get_network.return_value = 'fake_network' instance = {'name': 'fake_instance'} - vif = {'id': 'fake_vif'} self._session.network = mock.Mock() - self.ovs_driver.delete_network_and_bridge(instance, vif) + self._session.network.get_VIFs.return_value = None + self.ovs_driver.delete_network_and_bridge(instance, 'fake_vif_id') self._session.network.get_bridge.assert_called_once_with( 'fake_network') self._session.network.destroy.assert_called_once_with('fake_network') - self.assertTrue(mock_find_network.called) self.assertEqual(mock_ovs_del_port.call_count, 2) self.assertEqual(mock_delete_linux_port.call_count, 2) self.assertTrue(mock_delete_linux_bridge.called) self.assertTrue(mock_ovs_del_br.called) + @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_delete_linux_bridge') + @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_delete_linux_port') + @mock.patch.object(os_xenapi.client.host_network, 'ovs_del_br') + @mock.patch.object(os_xenapi.client.host_network, 'ovs_del_port') + @mock.patch.object(network_utils, 'find_network_with_name_label') + @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_get_network_by_vif') + def test_delete_network_and_bridge_with_remote_vif_on( + self, + mock_get_network, + mock_find_network, + mock_ovs_del_port, + mock_ovs_del_br, + mock_delete_linux_port, + mock_delete_linux_bridge): + # If still has vifs attached to the network on remote hosts, delete + # network function would not be called, while the bridge would + # be deleted + mock_get_network.return_value = 'fake_network' + instance = {'name': 'fake_instance'} + fake_local_host_ref = 'fake_host_ref' + fake_vif_id = 'fake_vif_id' + expected_qbr_name = 'qbr' + fake_vif_id + self._session.host_ref = fake_local_host_ref + self.mock_patch_object( + self._session.network, 'get_VIFs', + return_val=['fake_vif']) + self.mock_patch_object( + self._session.VIF, 'get_all_records_where', + return_val={'rec': 'fake_rec'}) + self.mock_patch_object( + self._session.VIF, 'get_VM', + return_val='fake_vm_ref') + self.mock_patch_object( + self._session.network, 'get_bridge', + return_val='fake_bridge') + # The host ref which the remain vif resident on doesn't match the local + # host + self.mock_patch_object( + self._session.VM, 'get_resident_on', + return_val='fake_host_ref_remote') + + self.ovs_driver.delete_network_and_bridge(instance, fake_vif_id) + self._session.network.get_bridge.assert_called_once_with( + 'fake_network') + self._session.network.destroy.assert_not_called() + self.assertEqual(2, mock_ovs_del_port.call_count) + self.assertEqual(2, mock_delete_linux_port.call_count) + mock_delete_linux_bridge.assert_called_once_with(expected_qbr_name) + mock_ovs_del_br.assert_called_once_with(self._session, 'fake_bridge') + + @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_delete_linux_bridge') + @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_delete_linux_port') + @mock.patch.object(os_xenapi.client.host_network, 'ovs_del_br') + @mock.patch.object(os_xenapi.client.host_network, 'ovs_del_port') + @mock.patch.object(network_utils, 'find_network_with_name_label') + @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_get_network_by_vif') + def test_delete_network_and_bridge_abort( + self, + mock_get_network, + mock_find_network, + mock_ovs_del_port, + mock_ovs_del_br, + mock_delete_linux_port, + mock_delete_linux_bridge): + # If still has vifs attached to the network on local hosts, all the + # operations would be abort + mock_get_network.return_value = 'fake_network' + instance = {'name': 'fake_instance'} + fake_local_host_ref = 'fake_host_ref' + self._session.host_ref = fake_local_host_ref + self.mock_patch_object( + self._session.network, 'get_VIFs', + return_val=['fake_vif']) + self.mock_patch_object( + self._session.VIF, 'get_all_records_where', + return_val={'rec': 'fake_rec'}) + self.mock_patch_object( + self._session.VIF, 'get_VM', + return_val='fake_vm_ref') + # The host ref which the remain vif resident on match the local host + self.mock_patch_object( + self._session.VM, 'get_resident_on', + return_val=fake_local_host_ref) + + self.ovs_driver.delete_network_and_bridge(instance, 'fake_vif_id') + self._session.network.get_bridge.assert_called_once_with( + 'fake_network') + self._session.network.destroy.assert_not_called() + mock_ovs_del_port.assert_not_called() + mock_delete_linux_port.assert_not_called() + mock_delete_linux_bridge.assert_not_called() + mock_ovs_del_br.assert_not_called() + + @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_delete_linux_bridge') + @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_delete_linux_port') + @mock.patch.object(os_xenapi.client.host_network, 'ovs_del_br') + @mock.patch.object(os_xenapi.client.host_network, 'ovs_del_port') + @mock.patch.object(network_utils, 'find_network_with_name_label') + @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_get_network_by_vif') + @mock.patch.object(vif.XenAPIOpenVswitchDriver, + '_get_patch_port_pair_names') + def test_delete_network_and_bridge_del_port_exc(self, mock_get_port_name, + mock_get_network, + mock_find_network, + mock_ovs_del_port, + mock_ovs_del_br, + mock_delete_linux_port, + mock_delete_linux_bridge): + # Get an exception when deleting the patch port pair + mock_get_network.return_value = 'fake_network' + instance = {'name': 'fake_instance'} + self._session.network = mock.Mock() + self._session.network.get_VIFs.return_value = None + self._session.network.get_bridge.return_value = 'fake_bridge' + mock_get_port_name.return_value = ['fake_port', 'fake_tap'] + mock_ovs_del_port.side_effect = test.TestingException + self.assertRaises(exception.VirtualInterfaceUnplugException, + self.ovs_driver.delete_network_and_bridge, instance, + 'fake_vif_id') + self._session.network.get_bridge.assert_called_once_with( + 'fake_network') + self._session.network.destroy.assert_called_once_with('fake_network') + mock_ovs_del_port.assert_called_once_with(self._session, + 'fake_bridge', + 'fake_port') + mock_delete_linux_port.assert_not_called() + mock_delete_linux_bridge.assert_not_called() + mock_ovs_del_br.assert_not_called() + + @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_delete_linux_bridge') + @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_delete_linux_port') + @mock.patch.object(os_xenapi.client.host_network, 'ovs_del_br') + @mock.patch.object(os_xenapi.client.host_network, 'ovs_del_port') + @mock.patch.object(network_utils, 'find_network_with_name_label') + @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_get_network_by_vif') + @mock.patch.object(vif.XenAPIOpenVswitchDriver, + '_get_patch_port_pair_names') + def test_delete_network_and_bridge_del_br_exc(self, mock_get_port_name, + mock_get_network, + mock_find_network, + mock_ovs_del_port, + mock_ovs_del_br, + mock_delete_linux_port, + mock_delete_linux_bridge): + # Get an exception when deleting the bridge and the patch ports + # existing on this bridge + mock_get_network.return_value = 'fake_network' + instance = {'name': 'fake_instance'} + self._session.network = mock.Mock() + self._session.network.get_VIFs.return_value = None + self._session.network.get_bridge.return_value = 'fake_bridge' + mock_get_port_name.return_value = ['fake_port', 'fake_tap'] + mock_ovs_del_br.side_effect = test.TestingException + self.assertRaises(exception.VirtualInterfaceUnplugException, + self.ovs_driver.delete_network_and_bridge, instance, + 'fake_vif_id') + self._session.network.get_bridge.assert_called_once_with( + 'fake_network') + self._session.network.destroy.assert_called_once_with('fake_network') + mock_ovs_del_port.assert_called_once_with(self._session, + 'fake_bridge', + 'fake_port') + mock_delete_linux_port.assert_not_called() + mock_delete_linux_bridge.assert_not_called() + mock_ovs_del_br.assert_called_once_with(self._session, 'fake_bridge') + @mock.patch.object(os_xenapi.client.host_network, 'ovs_del_port') @mock.patch.object(network_utils, 'find_network_with_name_label', return_value='fake_network') - def test_delete_network_and_bridge_destroy_exception(self, - mock_find_network, - mock_ovs_del_port): + def test_delete_network_and_bridge_destroy_network_exception( + self, + mock_find_network, + mock_ovs_del_port): + # Get an exception when destroying the network instance = {'name': "fake_instance"} self.mock_patch_object( self._session.network, 'get_VIFs', return_val=None) @@ -276,9 +442,8 @@ class XenAPIOpenVswitchDriverTestCase(XenVIFDriverTestBase): self.assertRaises(exception.VirtualInterfaceUnplugException, self.ovs_driver.delete_network_and_bridge, instance, - fake_vif) + 'fake_vif_id') self.assertTrue(mock_find_network.called) - self.assertTrue(mock_ovs_del_port.called) @mock.patch.object(vif.XenAPIOpenVswitchDriver, '_device_exists') @mock.patch.object(os_xenapi.client.host_network, 'brctl_add_if') diff --git a/nova/tests/unit/virt/xenapi/test_vmops.py b/nova/tests/unit/virt/xenapi/test_vmops.py index fe4a187fbb0e..dc9f6b5e2a9a 100644 --- a/nova/tests/unit/virt/xenapi/test_vmops.py +++ b/nova/tests/unit/virt/xenapi/test_vmops.py @@ -1746,12 +1746,15 @@ class LiveMigrateHelperTestCase(VMOpsTestBase): "_generate_vdi_map") as mock_gen_vdi_map, \ mock.patch.object(self.vmops._session, 'call_xenapi') as mock_call_xenapi, \ + mock.patch.object(vm_utils, 'host_in_this_pool' + ) as mock_host_in_this_pool, \ mock.patch.object(self.vmops, "_generate_vif_network_map") as mock_vif_map: mock_call_xenapi.side_effect = side_effect mock_gen_vdi_map.side_effect = [ {"vdi": "sr_ref"}, {"vdi": "sr_ref_2"}] mock_vif_map.return_value = {"vif_ref1": "dest_net_ref"} + mock_host_in_this_pool.return_value = False self.vmops._call_live_migrate_command(command_name, vm_ref, migrate_data) @@ -1924,7 +1927,7 @@ class LiveMigrateHelperTestCase(VMOpsTestBase): def test_delete_networks_and_bridges(self): self.vmops.vif_driver = mock.Mock() - network_info = ['fake_vif'] + network_info = [{'id': 'fake_vif'}] self.vmops._delete_networks_and_bridges('fake_instance', network_info) self.vmops.vif_driver.delete_network_and_bridge.\ assert_called_once_with('fake_instance', 'fake_vif') diff --git a/nova/tests/unit/virt/xenapi/test_xenapi.py b/nova/tests/unit/virt/xenapi/test_xenapi.py index c597ba8b6a1d..ee0841cd2523 100644 --- a/nova/tests/unit/virt/xenapi/test_xenapi.py +++ b/nova/tests/unit/virt/xenapi/test_xenapi.py @@ -3502,20 +3502,26 @@ class XenAPILiveMigrateTestCase(stubs.XenAPITestBaseNoDB): self.conn = xenapi_conn.XenAPIDriver(fake.FakeVirtAPI(), False) self.stubs.Set(vm_utils, "safe_find_sr", lambda _x: "asdf") - - expected = {'block_migration': True, - 'is_volume_backed': False, - 'migrate_data': { - 'migrate_send_data': {'value': 'fake_migrate_data'}, - 'destination_sr_ref': 'asdf' + with mock.patch.object(self.conn._vmops._session, "host_ref") as \ + fake_host_ref, mock.patch.object( + self.conn._vmops, '_get_network_ref') as \ + fake_get_network_ref: + fake_host_ref.return_value = 'fake_host_ref' + fake_get_network_ref.return_value = 'fake_network_ref' + expected = {'block_migration': True, + 'is_volume_backed': False, + 'migrate_data': { + 'migrate_send_data': {'value': + 'fake_migrate_data'}, + 'destination_sr_ref': 'asdf'} } - } - result = self.conn.check_can_live_migrate_destination(self.context, - {'host': 'host'}, - {}, {}, - True, False) - result.is_volume_backed = False - self.assertEqual(expected, result.to_legacy_dict()) + result = self.conn.check_can_live_migrate_destination( + self.context, + {'host': 'host'}, + {}, {}, + True, False) + result.is_volume_backed = False + self.assertEqual(expected, result.to_legacy_dict()) def test_check_live_migrate_destination_verifies_ip(self): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) @@ -3659,17 +3665,21 @@ class XenAPILiveMigrateTestCase(stubs.XenAPITestBaseNoDB): def test_check_can_live_migrate_works(self, mock_get_by_host): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) self.conn = xenapi_conn.XenAPIDriver(fake.FakeVirtAPI(), False) - - metadata = {'host': 'test_host_uuid'} - aggregate = objects.Aggregate(metadata=metadata) - aggregate_list = objects.AggregateList(objects=[aggregate]) - mock_get_by_host.return_value = aggregate_list - - instance = objects.Instance(host='host') - self.conn.check_can_live_migrate_destination( - self.context, instance, None, None) - mock_get_by_host.assert_called_once_with( - self.context, CONF.host, key='hypervisor_pool') + with mock.patch.object(self.conn._vmops._session, "host_ref") as \ + fake_host_ref, \ + mock.patch.object(self.conn._vmops, + '_get_network_ref') as fake_get_network_ref: + fake_host_ref.return_value = 'fake_host_ref' + fake_get_network_ref.return_value = 'fake_network_ref' + metadata = {'host': 'test_host_uuid'} + aggregate = objects.Aggregate(metadata=metadata) + aggregate_list = objects.AggregateList(objects=[aggregate]) + mock_get_by_host.return_value = aggregate_list + instance = objects.Instance(host='host') + self.conn.check_can_live_migrate_destination( + self.context, instance, None, None) + mock_get_by_host.assert_called_once_with( + self.context, CONF.host, key='hypervisor_pool') @mock.patch.object(objects.AggregateList, 'get_by_host') def test_check_can_live_migrate_fails(self, mock_get_by_host): diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index a0f9784ba489..969eff8b22e1 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -355,7 +355,8 @@ def _create_local_pif(host_ref): 'IP': '10.1.1.1', 'IPv6': '', 'uuid': '', - 'management': 'true'}) + 'management': 'true', + 'host': 'fake_host_ref'}) _db_content['PIF'][pif_ref]['uuid'] = pif_ref return pif_ref diff --git a/nova/virt/xenapi/vif.py b/nova/virt/xenapi/vif.py index b8c6c0cec7bb..24d45083aec3 100644 --- a/nova/virt/xenapi/vif.py +++ b/nova/virt/xenapi/vif.py @@ -127,7 +127,7 @@ class XenVIFDriver(object): def create_vif_interim_network(self, vif): pass - def delete_network_and_bridge(self, instance, vif): + def delete_network_and_bridge(self, instance, vif_id): pass @@ -283,69 +283,76 @@ class XenAPIOpenVswitchDriver(XenVIFDriver): return vif_ref def unplug(self, instance, vif, vm_ref): - """unplug vif: + super(XenAPIOpenVswitchDriver, self).unplug(instance, vif, vm_ref) + self.delete_network_and_bridge(instance, vif['id']) + + def delete_network_and_bridge(self, instance, vif_id): + """Delete network and bridge: 1. delete the patch port pair between the integration bridge and the qbr linux bridge(if exist) and the interim network. 2. destroy the interim network 3. delete the OVS bridge service for the interim network 4. delete linux bridge qbr and related ports if exist """ - super(XenAPIOpenVswitchDriver, self).unplug(instance, vif, vm_ref) - net_name = self.get_vif_interim_net_name(vif['id']) - network = network_utils.find_network_with_name_label( - self._session, net_name) - if network is None: + network = self._get_network_by_vif(vif_id) + if not network: return vifs = self._session.network.get_VIFs(network) - if vifs: - # only remove the interim network when it's empty. - # for resize/migrate on local host, vifs on both of the - # source and target VM will be connected to the same - # interim network. - return - self.delete_network_and_bridge(instance, vif) - - def delete_network_and_bridge(self, instance, vif): - net_name = self.get_vif_interim_net_name(vif['id']) - network = network_utils.find_network_with_name_label( - self._session, net_name) - if network is None: - LOG.debug("Didn't find network by name %s", net_name, - instance=instance) - return - LOG.debug('destroying patch port pair for vif: vif_id=%(vif_id)s', - {'vif_id': vif['id']}) bridge_name = self._session.network.get_bridge(network) - patch_port1, tap_name = self._get_patch_port_pair_names(vif['id']) + if vifs: + # Still has vifs attached to this network + for remain_vif in vifs: + # if remain vifs are on the local server, give up all the + # operations. If remain vifs are on the remote hosts, keep + # the network and delete the bridge + if self._get_host_by_vif(remain_vif) == self._session.host_ref: + return + else: + # No vif left, delete the network + try: + self._session.network.destroy(network) + except Exception as e: + LOG.warning("Failed to destroy network for vif (id=%(if)s), " + "exception:%(exception)s", + {'if': vif_id, 'exception': e}, instance=instance) + raise exception.VirtualInterfaceUnplugException( + reason=_("Failed to destroy network")) + # Two cases: + # 1) No vif left, just delete the bridge + # 2) For resize/intra-pool migrate, vifs on both of the + # source and target VM will be connected to the same + # interim network. If the VM is resident on a remote host, + # linux bridge on current host will be deleted. + self.delete_bridge(instance, vif_id, bridge_name) + + def delete_bridge(self, instance, vif_id, bridge_name): + LOG.debug('destroying patch port pair for vif id: vif_id=%(vif_id)s', + {'vif_id': vif_id}) + patch_port1, tap_name = self._get_patch_port_pair_names(vif_id) try: # delete the patch port pair host_network.ovs_del_port(self._session, bridge_name, patch_port1) except Exception as e: - LOG.warning("Failed to delete patch port pair for vif %(if)s," + LOG.warning("Failed to delete patch port pair for vif id %(if)s," " exception:%(exception)s", - {'if': vif, 'exception': e}, instance=instance) + {'if': vif_id, 'exception': e}, instance=instance) raise exception.VirtualInterfaceUnplugException( reason=_("Failed to delete patch port pair")) - LOG.debug('destroying network: network=%(network)s,' - 'bridge=%(br)s', - {'network': network, 'br': bridge_name}) + LOG.debug('destroying bridge: bridge=%(br)s', {'br': bridge_name}) try: - self._session.network.destroy(network) # delete bridge if it still exists. - # As there is patch port existing on this bridge when destroying - # the VM vif (which happens when shutdown the VM), the bridge - # won't be destroyed automatically by XAPI. So let's destroy it - # at here. + # As there are patch ports existing on this bridge when + # destroying won't be destroyed automatically by XAPI, let's + # destroy it at here. host_network.ovs_del_br(self._session, bridge_name) - - qbr_name = self._get_qbr_name(vif['id']) - qvb_name, qvo_name = self._get_veth_pair_names(vif['id']) + qbr_name = self._get_qbr_name(vif_id) + qvb_name, qvo_name = self._get_veth_pair_names(vif_id) if self._device_exists(qbr_name): # delete tap port, qvb port and qbr LOG.debug( - "destroy linux bridge %(qbr)s when unplug vif %(vif)s", - {'qbr': qbr_name, 'vif': vif['id']}) + "destroy linux bridge %(qbr)s when unplug vif id" + " %(vif_id)s", {'qbr': qbr_name, 'vif_id': vif_id}) self._delete_linux_port(qbr_name, tap_name) self._delete_linux_port(qbr_name, qvb_name) self._delete_linux_bridge(qbr_name) @@ -353,12 +360,35 @@ class XenAPIOpenVswitchDriver(XenVIFDriver): CONF.xenserver.ovs_integration_bridge, qvo_name) except Exception as e: - LOG.warning("Failed to delete bridge for vif %(if)s, " + LOG.warning("Failed to delete bridge for vif id %(if)s, " "exception:%(exception)s", - {'if': vif, 'exception': e}, instance=instance) + {'if': vif_id, 'exception': e}, instance=instance) raise exception.VirtualInterfaceUnplugException( reason=_("Failed to delete bridge")) + def _get_network_by_vif(self, vif_id): + net_name = self.get_vif_interim_net_name(vif_id) + network = network_utils.find_network_with_name_label( + self._session, net_name) + if network is None: + LOG.debug("Failed to find network for vif id %(if)s", + {'if': vif_id}) + return + return network + + def _get_host_by_vif(self, vif_id): + network = self._get_network_by_vif(vif_id) + if not network: + return + vif_info = self._session.VIF.get_all_records_where( + 'field "network" = "%s"' % network) + if not vif_info or len(vif_info) != 1: + raise exception.NovaException( + "Couldn't find vif id information in network %s" + % network) + vm_ref = self._session.VIF.get_VM(list(vif_info.keys())[0]) + return self._session.VM.get_resident_on(vm_ref) + def hot_plug(self, vif, instance, vm_ref, vif_ref): # hot plug vif only when VM's power state is running LOG.debug("Hot plug vif, vif: %s", vif, instance=instance) @@ -491,9 +521,13 @@ class XenAPIOpenVswitchDriver(XenVIFDriver): def create_vif_interim_network(self, vif): net_name = self.get_vif_interim_net_name(vif['id']) + # In a pooled environment, make the network to be shared to ensure it + # can also be used in the target host while live migration. It will + # make no change if the environment is not pooled. network_rec = {'name_label': net_name, - 'name_description': "interim network for vif", - 'other_config': {}} + 'name_description': "interim network for vif[%s]" + % vif['id'], + 'other_config': {'assume_network_is_shared': 'true'}} network_ref = network_utils.find_network_with_name_label( self._session, net_name) if network_ref: diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 58f28ffe8432..19787bc12ee0 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -814,9 +814,14 @@ def _find_cached_images(session, sr_ref): def _find_cached_image(session, image_id, sr_ref): """Returns the vdi-ref of the cached image.""" name_label = _get_image_vdi_label(image_id) - recs = session.call_xenapi("VDI.get_all_records_where", - 'field "name__label"="%s"' - % name_label) + # For not pooled hosts, only name_lable is enough to get a cached image. + # When in a xapi pool, each host may have a cached image using the + # same name while xapi api will search all of them. Add SR to the filter + # to ensure only one image returns. + expr = ('field "name__label"="%(name_label)s" and field "SR" = "%(SR)s"' + % {'name_label': name_label, 'SR': sr_ref}) + recs = session.call_xenapi("VDI.get_all_records_where", expr) + number_found = len(recs) if number_found > 0: if number_found > 1: @@ -2618,3 +2623,8 @@ def set_other_config_pci(session, vm_ref, params): other_config = session.call_xenapi("VM.get_other_config", vm_ref) other_config['pci'] = params session.call_xenapi("VM.set_other_config", vm_ref, other_config) + + +def host_in_this_pool(session, host_ref): + rec_dict = session.host.get_all_records() + return host_ref in rec_dict.keys() diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 27fe23abc7fb..88855caa71ab 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -2249,7 +2249,8 @@ class VMOps(object): # This is the one associated with the pif marked management. From cli: # uuid=`xe pif-list --minimal management=true` # xe pif-param-get param-name=network-uuid uuid=$uuid - expr = 'field "management" = "true"' + expr = ('field "management" = "true" and field "host" = "%s"' % + self._session.host_ref) pifs = self._session.call_xenapi('PIF.get_all_records_where', expr) if len(pifs) != 1: @@ -2492,12 +2493,15 @@ class VMOps(object): self._generate_vdi_map( sr_uuid_map[sr_uuid], vm_ref, sr_ref)) vif_map = {} - vif_uuid_map = None - if 'vif_uuid_map' in migrate_data: - vif_uuid_map = migrate_data.vif_uuid_map - if vif_uuid_map: - vif_map = self._generate_vif_network_map(vm_ref, vif_uuid_map) - LOG.debug("Generated vif_map for live migration: %s", vif_map) + # For block migration, need to pass vif map to the destination hosts. + if not vm_utils.host_in_this_pool(self._session, + migrate_send_data.get('host')): + vif_uuid_map = None + if 'vif_uuid_map' in migrate_data: + vif_uuid_map = migrate_data.vif_uuid_map + if vif_uuid_map: + vif_map = self._generate_vif_network_map(vm_ref, vif_uuid_map) + LOG.debug("Generated vif_map for live migration: %s", vif_map) options = {} self._session.call_xenapi(command_name, vm_ref, migrate_send_data, True, @@ -2641,7 +2645,7 @@ class VMOps(object): # Unplug VIFs and delete networks for vif in network_info: try: - self.vif_driver.delete_network_and_bridge(instance, vif) + self.vif_driver.delete_network_and_bridge(instance, vif['id']) except Exception: LOG.exception(_('Failed to delete networks and bridges with ' 'VIF %s'), vif['id'], instance=instance)