diff --git a/neutron/agent/rpc.py b/neutron/agent/rpc.py index 990aef9d209..a5e75a69b4b 100644 --- a/neutron/agent/rpc.py +++ b/neutron/agent/rpc.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import collections from datetime import datetime import itertools @@ -38,6 +39,7 @@ from neutron import objects LOG = logging.getLogger(__name__) BINDING_DEACTIVATE = 'binding_deactivate' +DeviceInfo = collections.namedtuple('DeviceInfo', 'mac pci_slot') def create_consumers(endpoints, prefix, topic_details, start_listening=True): @@ -118,19 +120,26 @@ class PluginApi(object): 1.7 - Support get_ports_by_vnic_type_and_host 1.8 - Rename agent_restarted to refresh_tunnels in update_device_list to reflect its expanded purpose + 1.9 - Support for device definition as DeviceInfo(mac, pci_info) for: + - get_device_details + - get_devices_details_list (indirectly, calls get_device_details) + - update_device_down + - update_device_up + - update_device_list (indirectly, called from update_device_down + and update_device_up) ''' def __init__(self, topic): - target = oslo_messaging.Target(topic=topic, version='1.0') + target = oslo_messaging.Target(topic=topic, version='1.9') self.client = lib_rpc.get_client(target) def get_device_details(self, context, device, agent_id, host=None): - cctxt = self.client.prepare() + cctxt = self.client.prepare(version='1.9') return cctxt.call(context, 'get_device_details', device=device, agent_id=agent_id, host=host) def get_devices_details_list(self, context, devices, agent_id, host=None): - cctxt = self.client.prepare(version='1.3') + cctxt = self.client.prepare(version='1.9') return cctxt.call(context, 'get_devices_details_list', devices=devices, agent_id=agent_id, host=host) @@ -155,18 +164,18 @@ class PluginApi(object): agent_id=agent_id, host=host) def update_device_down(self, context, device, agent_id, host=None): - cctxt = self.client.prepare() + cctxt = self.client.prepare(version='1.9') return cctxt.call(context, 'update_device_down', device=device, agent_id=agent_id, host=host) def update_device_up(self, context, device, agent_id, host=None): - cctxt = self.client.prepare() + cctxt = self.client.prepare(version='1.9') return cctxt.call(context, 'update_device_up', device=device, agent_id=agent_id, host=host) def update_device_list(self, context, devices_up, devices_down, agent_id, host, refresh_tunnels=False): - cctxt = self.client.prepare(version='1.8') + cctxt = self.client.prepare(version='1.9') ret_devices_up = [] failed_devices_up = [] diff --git a/neutron/objects/ports.py b/neutron/objects/ports.py index b8885518200..2466f96ffb4 100644 --- a/neutron/objects/ports.py +++ b/neutron/objects/ports.py @@ -14,6 +14,7 @@ import netaddr from neutron_lib import constants +from neutron_lib.db import api as db_api from neutron_lib.objects import common_types from neutron_lib.utils import net as net_utils from oslo_log import log as logging @@ -688,3 +689,19 @@ class Port(base.NeutronDbObject): return context.session.query(models_v2.Port).filter( models_v2.IPAllocation.port_id == models_v2.Port.id).filter( models_v2.IPAllocation.subnet_id == subnet_id).all() + + @classmethod + def get_port_from_mac_and_pci_slot(cls, context, device_mac, + pci_slot=None): + with db_api.CONTEXT_READER.using(context): + ports = cls.get_objects(context, mac_address=device_mac) + + if not ports: + return + elif not pci_slot: + return ports.pop() + else: + for port in ports: + for _binding in port.bindings: + if _binding.get('profile', {}).get('pci_slot') == pci_slot: + return port diff --git a/neutron/plugins/ml2/db.py b/neutron/plugins/ml2/db.py index 0e4925b3b1c..2abe76ea8b4 100644 --- a/neutron/plugins/ml2/db.py +++ b/neutron/plugins/ml2/db.py @@ -143,13 +143,6 @@ def get_port(context, port_id): return -@db_api.CONTEXT_READER -def get_port_from_device_mac(context, device_mac): - LOG.debug("get_port_from_device_mac() called for mac %s", device_mac) - ports = port_obj.Port.get_objects(context, mac_address=device_mac) - return ports.pop() if ports else None - - def get_ports_and_sgs(context, port_ids): """Get ports from database with security group info.""" diff --git a/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py b/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py index 98f1fbc0e08..cb9e942503a 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py @@ -21,6 +21,7 @@ from neutron_lib.utils import helpers from oslo_log import log as logging from neutron._i18n import _ +from neutron.agent import rpc as agent_rpc from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ import exceptions as exc from neutron.plugins.ml2.drivers.mech_sriov.agent import pci_lib @@ -185,7 +186,8 @@ class EmbSwitch(object): for pci_slot, vf_index in self.pci_slot_map.items(): mac = self.get_pci_device(pci_slot) if mac: - assigned_devices_info.append((mac, pci_slot)) + assigned_devices_info.append( + agent_rpc.DeviceInfo(mac, pci_slot)) return assigned_devices_info def get_device_state(self, pci_slot): diff --git a/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py b/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py index cd38b3c5d34..87979e8e98c 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py @@ -88,12 +88,9 @@ class SriovNicSwitchRpcCallbacks(sg_rpc.SecurityGroupAgentRpcCallbackMixin): # notifications there is no guarantee the notifications are # processed in the same order as the relevant API requests. mac = port['mac_address'] - pci_slot = None - if port.get(portbindings.PROFILE): - pci_slot = port[portbindings.PROFILE].get('pci_slot') - + pci_slot = port.get(portbindings.PROFILE, {}).get('pci_slot') if pci_slot: - self.agent.updated_devices.add((mac, pci_slot)) + self.agent.updated_devices.add(agent_rpc.DeviceInfo(mac, pci_slot)) LOG.debug("port_update RPC received for port: %(id)s with MAC " "%(mac)s and PCI slot %(pci_slot)s slot", {'id': port['id'], 'mac': mac, 'pci_slot': pci_slot}) @@ -277,97 +274,98 @@ class SriovNicSwitchAgent(object): # If one of the above operations fails => resync with plugin return (resync_a | resync_b) - def treat_device(self, device, pci_slot, admin_state_up, spoofcheck=True, + def treat_device(self, device_info, admin_state_up, spoofcheck=True, propagate_uplink_state=False): - if self.eswitch_mgr.device_exists(device, pci_slot): + if self.eswitch_mgr.device_exists(device_info.mac, + device_info.pci_slot): try: - self.eswitch_mgr.set_device_spoofcheck(device, pci_slot, - spoofcheck) + self.eswitch_mgr.set_device_spoofcheck( + device_info.mac, device_info.pci_slot, spoofcheck) except Exception: LOG.warning("Failed to set spoofcheck for device %s", - device) + device_info) LOG.info("Device %(device)s spoofcheck %(spoofcheck)s", - {"device": device, "spoofcheck": spoofcheck}) + {"device": device_info, "spoofcheck": spoofcheck}) try: - self.eswitch_mgr.set_device_state(device, pci_slot, - admin_state_up, - propagate_uplink_state) + self.eswitch_mgr.set_device_state( + device_info.mac, device_info.pci_slot, admin_state_up, + propagate_uplink_state) except priv_ip_lib.InterfaceOperationNotSupported: - LOG.warning("Device %s does not support state change", device) + LOG.warning("Device %s does not support state change", + device_info) except pyroute2.NetlinkError: - LOG.warning("Failed to set device %s state", device) + LOG.warning("Failed to set device %s state", device_info) return False else: - LOG.info("No device with MAC %s defined on agent.", device) + LOG.info("No device %s defined on agent.", device_info) return False return True - def _update_network_ports(self, network_id, port_id, mac_pci_slot): - self._clean_network_ports(mac_pci_slot) + def _update_network_ports(self, network_id, port_id, device): + self._clean_network_ports(device) self.network_ports[network_id].append({ "port_id": port_id, - "device": mac_pci_slot}) + "device": device}) - def _clean_network_ports(self, mac_pci_slot): - for netid, ports_list in self.network_ports.items(): - for port_data in ports_list: - if mac_pci_slot == port_data['device']: + def _clean_network_ports(self, device_to_clean): + for netid, ports_list in dict(self.network_ports).items(): + for port_data in list(ports_list): + if device_to_clean == port_data['device']: ports_list.remove(port_data) - if ports_list == []: + if not ports_list: self.network_ports.pop(netid) return port_data['port_id'] def treat_devices_added_updated(self, devices_info): try: - macs_list = set([device_info[0] for device_info in devices_info]) - devices_details_list = self.plugin_rpc.get_devices_details_list( - self.context, macs_list, self.agent_id, self.conf.host) + rpc_devices_details = self.plugin_rpc.get_devices_details_list( + self.context, devices_info, self.agent_id, self.conf.host) except Exception as e: LOG.debug("Unable to get port details for devices " "with MAC addresses %(devices)s: %(e)s", - {'devices': macs_list, 'e': e}) + {'devices': devices_info, 'e': e}) # resync is needed return True devices_up = set() devices_down = set() resync = False - for device_details in devices_details_list: - device = device_details['device'] - LOG.debug("Port with MAC address %s is added", device) + for device_details in rpc_devices_details: + mac_address = device_details['device'] + LOG.debug("Port with MAC address %s is added", mac_address) if 'port_id' in device_details: LOG.info("Port %(device)s updated. Details: %(details)s", - {'device': device, 'details': device_details}) + {'device': mac_address, 'details': device_details}) port_id = device_details['port_id'] profile = device_details['profile'] + device_info = agent_rpc.DeviceInfo(mac_address, + profile.get('pci_slot')) spoofcheck = device_details.get('port_security_enabled', True) if self.treat_device( - device, - profile.get('pci_slot'), + device_info, device_details['admin_state_up'], spoofcheck, device_details['propagate_uplink_status']): if device_details['admin_state_up']: - devices_up.add(device) + devices_up.add(device_info) else: - devices_down.add(device) + devices_down.add(device_info) else: resync = True self._update_network_ports(device_details['network_id'], - port_id, - (device, profile.get('pci_slot'))) + port_id, device_info) self.ext_manager.handle_port(self.context, device_details) elif n_constants.NO_ACTIVE_BINDING in device_details: # Port was added but its binding in this agent # hasn't been activated yet. It will be treated as # added when binding is activated LOG.info("Device with MAC %s has no active binding in host", - device) + mac_address) else: LOG.info("Device with MAC %s not defined on plugin", - device) + mac_address) self.plugin_rpc.update_device_list(self.context, devices_up, devices_down, @@ -378,39 +376,32 @@ class SriovNicSwitchAgent(object): def treat_devices_removed(self, devices): resync = False for device in devices: - mac, pci_slot = device LOG.info("Removing device with MAC address %(mac)s and " "PCI slot %(pci_slot)s", - {'mac': mac, 'pci_slot': pci_slot}) + {'mac': device.mac, 'pci_slot': device.pci_slot}) try: port_id = self._clean_network_ports(device) if port_id: port = {'port_id': port_id, - 'device': mac, - 'profile': {'pci_slot': pci_slot}} + 'device': device.mac, + 'profile': {'pci_slot': device.pci_slot}} self.ext_manager.delete_port(self.context, port) else: - LOG.warning("port_id to device with MAC " - "%s not found", mac) + LOG.warning("port_id to device %s not found", device) dev_details = self.plugin_rpc.update_device_down(self.context, - mac, + device, self.agent_id, cfg.CONF.host) except Exception as e: - LOG.debug("Removing port failed for device with MAC address " - "%(mac)s and PCI slot %(pci_slot)s due to %(exc)s", - {'mac': mac, 'pci_slot': pci_slot, 'exc': e}) + LOG.debug("Removing port failed for device %(device)s due to " + "%(exc)s", {'device': device, 'exc': e}) resync = True continue if dev_details['exists']: - LOG.info("Port with MAC %(mac)s and PCI slot " - "%(pci_slot)s updated.", - {'mac': mac, 'pci_slot': pci_slot}) + LOG.info("Port from device %s updated", device) else: - LOG.debug("Device with MAC %(mac)s and PCI slot " - "%(pci_slot)s not defined on plugin", - {'mac': mac, 'pci_slot': pci_slot}) + LOG.debug("Device %s not defined on plugin", device) return resync def process_activated_bindings(self, device_info, activated_bindings_copy): diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index 76e29072269..3a3feb60e94 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -2251,7 +2251,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, return ports @staticmethod - def _device_to_port_id(context, device): + def _device_to_port_id(context, device, pci_slot=None): # REVISIT(rkukura): Consider calling into MechanismDrivers to # process device names, or having MechanismDrivers supply list # of device prefixes to strip. @@ -2261,7 +2261,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, # REVISIT(irenab): Consider calling into bound MD to # handle the get_device_details RPC if not uuidutils.is_uuid_like(device): - port = db.get_port_from_device_mac(context, device) + port = ports_obj.Port.get_port_from_mac_and_pci_slot( + context, device, pci_slot=pci_slot) if port: return port.id return device diff --git a/neutron/plugins/ml2/rpc.py b/neutron/plugins/ml2/rpc.py index c0356366f8f..6d99b9245f6 100644 --- a/neutron/plugins/ml2/rpc.py +++ b/neutron/plugins/ml2/rpc.py @@ -57,8 +57,14 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin): # 1.7 Support get_ports_by_vnic_type_and_host # 1.8 Rename agent_restarted to refresh_tunnels in # update_device_list to reflect its expanded purpose - - target = oslo_messaging.Target(version='1.8') + # 1.9 Support for device definition as DeviceInfo(mac, pci_info) for: + # - get_device_details + # - get_devices_details_list (indirectly, calls get_device_details) + # - update_device_down + # - update_device_up + # - update_device_list (indirectly, called from update_device_down + # and update_device_up) + target = oslo_messaging.Target(version='1.9') def __init__(self, notifier, type_manager): self.setup_tunnel_callback_mixin(notifier, type_manager) @@ -78,6 +84,14 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin): kwargs.get('host'), kwargs.get('device') or kwargs.get('network')) + @staticmethod + def _device_to_mac_pci_slot(device): + """This method will keep backwards compatibility with agents < 1.9""" + # NOTE(ralonsoh): this method can be removed in Z release. + if isinstance(device, list): # RPC loads from agent_rpc.DeviceInfo + return device[0], device[1] + return device, None + def get_device_details(self, rpc_context, **kwargs): """Agent requests device details.""" agent_id, host, device = self._get_request_details(kwargs) @@ -90,7 +104,9 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin): {'device': device, 'agent_id': agent_id, 'host': host}) plugin = directory.get_plugin() - port_id = plugin._device_to_port_id(rpc_context, device) + mac_or_port_id, pci_slot = self._device_to_mac_pci_slot(device) + port_id = plugin._device_to_port_id(rpc_context, mac_or_port_id, + pci_slot=pci_slot) port_context = plugin.get_bound_port_context(rpc_context, port_id, host, @@ -98,8 +114,8 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin): if not port_context: LOG.debug("Device %(device)s requested by agent " "%(agent_id)s not found in database", - {'device': device, 'agent_id': agent_id}) - return {'device': device} + {'device': mac_or_port_id, 'agent_id': agent_id}) + return {'device': mac_or_port_id} port = port_context.current # caching information about networks for future use @@ -108,7 +124,7 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin): cached_networks[port['network_id']] = ( port_context.network.current) result = self._get_device_details(rpc_context, agent_id=agent_id, - host=host, device=device, + host=host, device=mac_or_port_id, port_context=port_context) if 'network_id' in result: # success so we update status @@ -244,13 +260,15 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin): "%(agent_id)s", {'device': device, 'agent_id': agent_id}) plugin = directory.get_plugin() - port_id = plugin._device_to_port_id(rpc_context, device) + mac_or_device, pci_slot = self._device_to_mac_pci_slot(device) + port_id = plugin._device_to_port_id(rpc_context, mac_or_device, + pci_slot=pci_slot) port_exists = True if (host and not plugin.port_bound_to_host(rpc_context, port_id, host)): LOG.debug("Device %(device)s not bound to the" " agent host %(host)s", - {'device': device, 'host': host}) + {'device': mac_or_device, 'host': host}) else: try: port_exists = bool(plugin.update_port_status( @@ -259,12 +277,12 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin): port_exists = False LOG.debug("delete_port and update_device_down are being " "executed concurrently. Ignoring StaleDataError.") - return {'device': device, + return {'device': mac_or_device, 'exists': port_exists} self.notify_l2pop_port_wiring(port_id, rpc_context, n_const.PORT_STATUS_DOWN, host) - return {'device': device, + return {'device': mac_or_device, 'exists': port_exists} @profiler.trace("rpc") @@ -278,12 +296,14 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin): LOG.debug("Device %(device)s up at agent %(agent_id)s", {'device': device, 'agent_id': agent_id}) plugin = directory.get_plugin() - port_id = plugin._device_to_port_id(rpc_context, device) + mac_or_device, pci_slot = self._device_to_mac_pci_slot(device) + port_id = plugin._device_to_port_id(rpc_context, mac_or_device, + pci_slot=pci_slot) port = plugin.port_bound_to_host(rpc_context, port_id, host) if host and not port: LOG.debug("Device %(device)s not bound to the" " agent host %(host)s", - {'device': device, 'host': host}) + {'device': mac_or_device, 'host': host}) # this might mean that a VM is in the process of live migration # and vif was plugged on the destination compute node; # need to notify nova explicitly diff --git a/neutron/tests/unit/objects/test_ports.py b/neutron/tests/unit/objects/test_ports.py index fa3c832ebb6..77cd1688a2b 100644 --- a/neutron/tests/unit/objects/test_ports.py +++ b/neutron/tests/unit/objects/test_ports.py @@ -13,6 +13,7 @@ from unittest import mock import netaddr +from neutron_lib.api.definitions import portbindings from neutron_lib import constants from neutron_lib.tests import tools from oslo_utils import uuidutils @@ -609,3 +610,34 @@ class PortDbObjectTestCase(obj_test_base.BaseDbObjectTestCase, (0, 1), (len(dhcp_ports), count), ) + + def test_get_port_from_mac_and_pci_slot_no_ports(self): + self.assertIsNone( + ports.Port.get_port_from_mac_and_pci_slot(self.context, + 'ca:fe:ca:fe:ca:fe')) + + def test_get_port_from_mac_and_pci_slot_no_pci_slot(self): + obj = self._make_object(self.obj_fields[0]) + obj.create() + mac_address = obj.mac_address + port = ports.Port.get_port_from_mac_and_pci_slot(self.context, + mac_address) + self.assertEqual(obj.id, port.id) + + def test_get_port_from_mac_and_pci_slot(self): + obj = self._make_object(self.obj_fields[0]) + obj.create() + mac_address = obj.mac_address + pci_slot = '0000:04:00.1' + port = ports.Port.get_port_from_mac_and_pci_slot( + self.context, mac_address, pci_slot=pci_slot) + self.assertIsNone(port) + + port_binding = ports.PortBinding( + self.context, port_id=obj.id, host='any_host', + vif_type=portbindings.VIF_TYPE_OTHER, + vnic_type=portbindings.VNIC_DIRECT, profile={'pci_slot': pci_slot}) + port_binding.create() + port = ports.Port.get_port_from_mac_and_pci_slot( + self.context, mac_address, pci_slot=pci_slot) + self.assertEqual(obj.id, port.id) diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py index 3799c6c8e75..83d1ef7e02a 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py @@ -16,9 +16,11 @@ import os from unittest import mock +from neutron.agent import rpc as agent_rpc from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ import exceptions as exc from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm +from neutron.plugins.ml2.drivers.mech_sriov.agent import pci_lib from neutron.tests import base @@ -429,51 +431,38 @@ class TestEmbSwitch(base.BaseTestCase): return_value=self.SCANNED_DEVICES): self.emb_switch = esm.EmbSwitch(self.DEV_NAME, exclude_devices) - @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." - "eswitch_manager.PciOsWrapper.scan_vf_devices", - return_value=[(PCI_SLOT, 0)]) - def test_get_assigned_devices_info(self, *args): - emb_switch = esm.EmbSwitch(self.DEV_NAME, ()) - with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." - "PciDeviceIPWrapper.get_assigned_macs", - return_value={0: self.ASSIGNED_MAC}),\ - mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." - "eswitch_manager.PciOsWrapper.pf_device_exists", - return_value=True), \ - mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." - "eswitch_manager.PciOsWrapper." - "is_assigned_vf_direct", return_value=True): - result = emb_switch.get_assigned_devices_info() - self.assertIn(self.ASSIGNED_MAC, list(result)[0]) - self.assertIn(self.PCI_SLOT, list(result)[0]) + def test_get_assigned_devices_info(self): + with mock.patch.object(pci_lib.PciDeviceIPWrapper, 'get_assigned_macs', + return_value={0: self.ASSIGNED_MAC}), \ + mock.patch.object(esm.PciOsWrapper, 'pf_device_exists', + return_value=True), \ + mock.patch.object(esm.PciOsWrapper, 'is_assigned_vf_direct', + return_value=True): + result = self.emb_switch.get_assigned_devices_info() + device_info = agent_rpc.DeviceInfo(self.ASSIGNED_MAC, + self.PCI_SLOT) + self.assertEqual(1, len(result)) + self.assertEqual(device_info, result[0]) - @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." - "eswitch_manager.PciOsWrapper.scan_vf_devices", - return_value=SCANNED_DEVICES) - def test_get_assigned_devices_info_multiple_slots(self, *args): - emb_switch = esm.EmbSwitch(self.DEV_NAME, ()) - with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." - "PciDeviceIPWrapper.get_assigned_macs", - return_value=self.VF_TO_MAC_MAPPING),\ - mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." - "eswitch_manager.PciOsWrapper." - "is_assigned_vf_direct", return_value=True): - devices_info = emb_switch.get_assigned_devices_info() + def test_get_assigned_devices_info_multiple_slots(self): + with mock.patch.object(pci_lib.PciDeviceIPWrapper, 'get_assigned_macs', + return_value=self.VF_TO_MAC_MAPPING), \ + mock.patch.object(esm.PciOsWrapper, 'pf_device_exists', + return_value=True), \ + mock.patch.object(esm.PciOsWrapper, 'is_assigned_vf_direct', + return_value=True): + devices_info = self.emb_switch.get_assigned_devices_info() for device_info in devices_info: - mac = device_info[0] - pci_slot = device_info[1] - self.assertEqual( - self.EXPECTED_MAC_TO_PCI[mac], pci_slot) + self.assertEqual(self.EXPECTED_MAC_TO_PCI[device_info.mac], + device_info.pci_slot) def test_get_assigned_devices_empty(self): - with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." - "eswitch_manager.PciOsWrapper.is_assigned_vf_direct", - return_value=False), \ - mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent." - "eswitch_manager.PciOsWrapper." - "is_assigned_vf_macvtap", return_value=False): + with mock.patch.object(esm.PciOsWrapper, 'is_assigned_vf_direct', + return_value=False), \ + mock.patch.object(esm.PciOsWrapper, 'is_assigned_vf_macvtap', + return_value=False): result = self.emb_switch.get_assigned_devices_info() - self.assertFalse(result) + self.assertEqual([], result) def test_get_device_state_ok(self): with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib." diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py index 3934bba3c75..ede87aa439b 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import copy from unittest import mock from neutron_lib.api.definitions import portbindings @@ -30,6 +31,30 @@ from neutron.tests import base DEVICE_MAC = '11:22:33:44:55:66' PCI_SLOT = "0000:06:00.1" +DEV1 = agent_rpc.DeviceInfo('mac1', 'pci_slot1') +DEV2 = agent_rpc.DeviceInfo('mac2', 'pci_slot2') +DEV3 = agent_rpc.DeviceInfo('mac3', 'pci_slot3') +DEV4 = agent_rpc.DeviceInfo('mac4', 'pci_slot4') +RPC_DEV1 = {'device': DEV1.mac, + 'port_id': 'port123', + 'network_id': 'net123', + 'admin_state_up': True, + 'propagate_uplink_status': False, + 'network_type': 'vlan', + 'segmentation_id': 100, + 'profile': {'pci_slot': DEV1.pci_slot}, + 'physical_network': 'physnet1', + 'port_security_enabled': False} +RPC_DEV2 = {'device': DEV2.mac, + 'port_id': 'port321', + 'network_id': 'net123', + 'admin_state_up': True, + 'propagate_uplink_status': False, + 'network_type': 'vlan', + 'segmentation_id': 100, + 'profile': {'pci_slot': DEV2.pci_slot}, + 'physical_network': 'physnet1', + 'port_security_enabled': False} class TestSriovAgent(base.BaseTestCase): @@ -78,7 +103,7 @@ class TestSriovAgent(base.BaseTestCase): def test_treat_devices_removed_with_existed_device(self, *args): agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0, {}, {}, {}) - devices = [(DEVICE_MAC, PCI_SLOT)] + devices = {agent_rpc.DeviceInfo(DEVICE_MAC, PCI_SLOT)} with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd: fn_udd.return_value = {'device': DEVICE_MAC, @@ -89,7 +114,7 @@ class TestSriovAgent(base.BaseTestCase): def test_treat_devices_removed_with_not_existed_device(self, *args): agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0, {}, {}, {}) - devices = [(DEVICE_MAC, PCI_SLOT)] + devices = {agent_rpc.DeviceInfo(DEVICE_MAC, PCI_SLOT)} with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd: fn_udd.return_value = {'device': DEVICE_MAC, @@ -103,7 +128,7 @@ class TestSriovAgent(base.BaseTestCase): def test_treat_devices_removed_failed(self, *args): agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0, {}, {}, {}) - devices = [(DEVICE_MAC, PCI_SLOT)] + devices = {agent_rpc.DeviceInfo(DEVICE_MAC, PCI_SLOT)} with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd: fn_udd.side_effect = Exception() @@ -134,51 +159,51 @@ class TestSriovAgent(base.BaseTestCase): self.mock_scan_devices(expected, mock_current, registered, updated) def test_scan_devices_no_changes(self): - registered = set(['1', '2']) + registered = {DEV1, DEV2} updated = set() - mock_current = set(['1', '2']) - expected = {'current': set(['1', '2']), + mock_current = {DEV1, DEV2} + expected = {'current': {DEV1, DEV2}, 'updated': set(), 'added': set(), 'removed': set()} self.mock_scan_devices(expected, mock_current, registered, updated) def test_scan_devices_new_and_removed(self): - registered = set(['1', '2']) + registered = {DEV1, DEV2} updated = set() - mock_current = set(['2', '3']) - expected = {'current': set(['2', '3']), + mock_current = {DEV2, DEV3} + expected = {'current': {DEV2, DEV3}, 'updated': set(), - 'added': set(['3']), - 'removed': set(['1'])} + 'added': {DEV3}, + 'removed': {DEV1}} self.mock_scan_devices(expected, mock_current, registered, updated) def test_scan_devices_updated_and_removed(self): - registered = set(['1', '2']) - # '1' is in removed and updated tuple - updated = set(['1']) - mock_current = set(['2', '3']) - expected = {'current': set(['2', '3']), + registered = {DEV1, DEV2} + # 'DEV1' is in removed and updated tuple + updated = {DEV1} + mock_current = {DEV2, DEV3} + expected = {'current': {DEV2, DEV3}, 'updated': set(), - 'added': set(['3']), - 'removed': set(['1'])} + 'added': {DEV3}, + 'removed': {DEV1}} self.mock_scan_devices(expected, mock_current, registered, updated) def test_scan_devices_new_updates(self): - registered = set(['1']) - updated = set(['2']) - mock_current = set(['1', '2']) - expected = {'current': set(['1', '2']), - 'updated': set(['2']), - 'added': set(['2']), + registered = {DEV1} + updated = {DEV2} + mock_current = {DEV1, DEV2} + expected = {'current': {DEV1, DEV2}, + 'updated': {DEV2}, + 'added': {DEV2}, 'removed': set()} self.mock_scan_devices(expected, mock_current, registered, updated) def test_scan_devices_updated_missing(self): - registered = set(['1']) - updated = set(['2']) - mock_current = set(['1']) - expected = {'current': set(['1']), + registered = {DEV1} + updated = {DEV2} + mock_current = {DEV1} + expected = {'current': {DEV1}, 'updated': set(), 'added': set(), 'removed': set()} @@ -187,9 +212,9 @@ class TestSriovAgent(base.BaseTestCase): def test_process_network_devices(self): agent = self.agent device_info = {'current': set(), - 'added': set(['mac3', 'mac4']), - 'updated': set(['mac2', 'mac3']), - 'removed': set(['mac1'])} + 'added': {DEV3, DEV4}, + 'updated': {DEV2, DEV3}, + 'removed': {DEV1}} agent.sg_agent.prepare_devices_filter = mock.Mock() agent.sg_agent.refresh_firewall = mock.Mock() agent.treat_devices_added_updated = mock.Mock(return_value=False) @@ -197,226 +222,115 @@ class TestSriovAgent(base.BaseTestCase): agent.process_network_devices(device_info) - agent.sg_agent.prepare_devices_filter.assert_called_with( - set(['mac3', 'mac4'])) + agent.sg_agent.prepare_devices_filter.assert_called_with({DEV3, DEV4}) self.assertTrue(agent.sg_agent.refresh_firewall.called) - agent.treat_devices_added_updated.assert_called_with(set(['mac2', - 'mac3', - 'mac4'])) - agent.treat_devices_removed.assert_called_with(set(['mac1'])) + agent.treat_devices_added_updated.assert_called_with( + {DEV2, DEV3, DEV4}) + agent.treat_devices_removed.assert_called_with({DEV1}) def test_treat_devices_added_updated_sends_host(self): agent = self.agent host = 'host1' cfg.CONF.set_override('host', host) agent.plugin_rpc = mock.Mock() - MAC = 'aa:bb:cc:dd:ee:ff' - device_details = {'device': MAC, - 'port_id': 'port123', - 'network_id': 'net123', - 'admin_state_up': True, - 'propagate_uplink_status': True, - 'network_type': 'vlan', - 'segmentation_id': 100, - 'profile': {'pci_slot': '1:2:3.0'}, - 'physical_network': 'physnet1', - 'port_security_enabled': False} - agent.plugin_rpc.get_devices_details_list.return_value = ( - [device_details]) - agent.treat_devices_added_updated([[MAC]]) + agent.plugin_rpc.get_devices_details_list.return_value = [RPC_DEV1] + devices = {agent_rpc.DeviceInfo(DEV1.mac, DEV1.pci_slot)} + agent.treat_devices_added_updated(devices) agent.plugin_rpc.get_devices_details_list.assert_called_once_with( - mock.ANY, set([MAC]), mock.ANY, host) + agent.context, devices, agent.agent_id, host) def test_treat_devices_added_updated_and_removed(self): agent = self.agent - MAC1 = 'aa:bb:cc:dd:ee:ff' - SLOT1 = '1:2:3.0' - MAC2 = 'aa:bb:cc:dd:ee:fe' - SLOT2 = '1:3:3.0' - mac_pci_slot_device1 = (MAC1, SLOT1) - mac_pci_slot_device2 = (MAC2, SLOT2) - mock_device1_details = {'device': MAC1, - 'port_id': 'port123', - 'network_id': 'net123', - 'admin_state_up': True, - 'propagate_uplink_status': False, - 'network_type': 'vlan', - 'segmentation_id': 100, - 'profile': {'pci_slot': SLOT1}, - 'physical_network': 'physnet1', - 'port_security_enabled': False} - mock_device2_details = {'device': MAC2, - 'port_id': 'port124', - 'network_id': 'net123', - 'admin_state_up': True, - 'propagate_uplink_status': False, - 'network_type': 'vlan', - 'segmentation_id': 100, - 'profile': {'pci_slot': SLOT2}, - 'physical_network': 'physnet1', - 'port_security_enabled': False} agent.plugin_rpc = mock.Mock() - agent.plugin_rpc.get_devices_details_list.return_value = ( - [mock_device1_details]) - agent.treat_devices_added_updated(set([MAC1])) - self.assertEqual({'net123': [{'port_id': 'port123', - 'device': mac_pci_slot_device1}]}, + agent.plugin_rpc.get_devices_details_list.return_value = [RPC_DEV1] + agent.treat_devices_added_updated({DEV1}) + self.assertEqual({'net123': [{'port_id': 'port123', 'device': DEV1}]}, agent.network_ports) - agent.plugin_rpc.get_devices_details_list.return_value = ( - [mock_device2_details]) + agent.plugin_rpc.get_devices_details_list.return_value = [RPC_DEV2] # add the second device and check the network_ports dict - agent.treat_devices_added_updated(set([MAC2])) - self.assertEqual( - {'net123': [{'port_id': 'port123', - 'device': mac_pci_slot_device1}, {'port_id': 'port124', - 'device': mac_pci_slot_device2}]}, - agent.network_ports) - with mock.patch.object(agent.plugin_rpc, - "update_device_down"): - agent.treat_devices_removed([mac_pci_slot_device2]) + agent.treat_devices_added_updated({DEV2}) + self.assertEqual({'net123': [{'port_id': 'port123', 'device': DEV1}, + {'port_id': 'port321', 'device': DEV2}]}, + agent.network_ports) + with mock.patch.object(agent.plugin_rpc, "update_device_down"): + agent.treat_devices_removed({DEV2}) # remove the second device and check the network_ports dict - self.assertEqual({'net123': [{'port_id': 'port123', - 'device': mac_pci_slot_device1}]}, + self.assertEqual({'net123': [{'port_id': 'port123', 'device': DEV1}]}, agent.network_ports) def test_treat_devices_added_updated_admin_state_up_true(self): agent = self.agent - mock_details = {'device': 'aa:bb:cc:dd:ee:ff', - 'port_id': 'port123', - 'network_id': 'net123', - 'admin_state_up': True, - 'propagate_uplink_status': False, - 'network_type': 'vlan', - 'segmentation_id': 100, - 'profile': {'pci_slot': '1:2:3.0'}, - 'physical_network': 'physnet1', - 'port_security_enabled': False} agent.plugin_rpc = mock.Mock() - agent.plugin_rpc.get_devices_details_list.return_value = [mock_details] + agent.plugin_rpc.get_devices_details_list.return_value = [RPC_DEV1] agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = True agent.set_device_state = mock.Mock() agent.set_device_spoofcheck = mock.Mock() - resync_needed = agent.treat_devices_added_updated( - set(['aa:bb:cc:dd:ee:ff'])) - + devices = {DEV1} + resync_needed = agent.treat_devices_added_updated(devices) self.assertFalse(resync_needed) - agent.eswitch_mgr.device_exists.assert_called_with('aa:bb:cc:dd:ee:ff', - '1:2:3.0') + agent.eswitch_mgr.device_exists.assert_called_with(DEV1.mac, + DEV1.pci_slot) agent.eswitch_mgr.set_device_state.assert_called_with( - 'aa:bb:cc:dd:ee:ff', - '1:2:3.0', - True, False) + DEV1.mac, DEV1.pci_slot, True, False) agent.eswitch_mgr.set_device_spoofcheck.assert_called_with( - 'aa:bb:cc:dd:ee:ff', - '1:2:3.0', - False) + DEV1.mac, DEV1.pci_slot, False) agent.plugin_rpc.update_device_list.assert_called_once_with( - mock.ANY, - set(['aa:bb:cc:dd:ee:ff']), - set(), - mock.ANY, - mock.ANY) + agent.context, devices, set(), agent.agent_id, agent.conf.host) def test_treat_devices_added_updated_multiple_admin_state_up_true(self): agent = self.agent - mock_details = [{'device': 'aa:bb:cc:dd:ee:ff', - 'port_id': 'port123', - 'network_id': 'net123', - 'admin_state_up': True, - 'propagate_uplink_status': False, - 'network_type': 'vlan', - 'segmentation_id': 100, - 'profile': {'pci_slot': '1:2:3.0'}, - 'physical_network': 'physnet1', - 'port_security_enabled': False}, - {'device': '11:22:33:44:55:66', - 'port_id': 'port321', - 'network_id': 'net123', - 'admin_state_up': True, - 'propagate_uplink_status': False, - 'network_type': 'vlan', - 'segmentation_id': 100, - 'profile': {'pci_slot': '1:2:3.0'}, - 'physical_network': 'physnet1', - 'port_security_enabled': False}] + mock_details = [RPC_DEV1, RPC_DEV2] agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = mock_details agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = True agent.set_device_state = mock.Mock() agent.set_device_spoofcheck = mock.Mock() - resync_needed = agent.treat_devices_added_updated( - set(['aa:bb:cc:dd:ee:ff', - '11:22:33:44:55:66'])) + devices = {DEV1, DEV2} + resync_needed = agent.treat_devices_added_updated(devices) self.assertFalse(resync_needed) - calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0'), - mock.call('11:22:33:44:55:66', '1:2:3.0')] + calls = [mock.call(DEV1.mac, DEV1.pci_slot), + mock.call(DEV2.mac, DEV2.pci_slot)] agent.eswitch_mgr.device_exists.assert_has_calls(calls, any_order=True) - calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0', True, False), - mock.call('11:22:33:44:55:66', '1:2:3.0', True, False)] + calls = [mock.call(DEV1.mac, DEV1.pci_slot, True, False), + mock.call(DEV2.mac, DEV2.pci_slot, True, False)] agent.eswitch_mgr.set_device_state.assert_has_calls(calls, any_order=True) - calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0', False), - mock.call('11:22:33:44:55:66', '1:2:3.0', False)] - agent.eswitch_mgr.set_device_spoofcheck.assert_has_calls(calls, - any_order=True) + calls = [mock.call(DEV1.mac, DEV1.pci_slot, False), + mock.call(DEV2.mac, DEV2.pci_slot, False)] + agent.eswitch_mgr.set_device_spoofcheck.assert_has_calls( + calls, any_order=True) agent.plugin_rpc.update_device_list.assert_called_once_with( - mock.ANY, - set(['aa:bb:cc:dd:ee:ff', '11:22:33:44:55:66']), - set(), - mock.ANY, - mock.ANY) + agent.context, devices, set(), agent.agent_id, agent.conf.host) def test_treat_devices_added_updated_multiple_admin_states(self): agent = self.agent - mock_details = [{'device': 'aa:bb:cc:dd:ee:ff', - 'port_id': 'port123', - 'network_id': 'net123', - 'admin_state_up': True, - 'propagate_uplink_status': False, - 'network_type': 'vlan', - 'segmentation_id': 100, - 'profile': {'pci_slot': '1:2:3.0'}, - 'physical_network': 'physnet1', - 'port_security_enabled': False}, - {'device': '11:22:33:44:55:66', - 'port_id': 'port321', - 'network_id': 'net123', - 'admin_state_up': False, - 'propagate_uplink_status': False, - 'network_type': 'vlan', - 'segmentation_id': 100, - 'profile': {'pci_slot': '1:2:3.0'}, - 'physical_network': 'physnet1', - 'port_security_enabled': False}] + rpc_dev2 = copy.deepcopy(RPC_DEV2) + rpc_dev2['admin_state_up'] = False + mock_details = [RPC_DEV1, rpc_dev2] agent.plugin_rpc = mock.Mock() agent.plugin_rpc.get_devices_details_list.return_value = mock_details agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = True agent.set_device_state = mock.Mock() agent.set_device_spoofcheck = mock.Mock() - resync_needed = agent.treat_devices_added_updated( - set(['aa:bb:cc:dd:ee:ff', - '11:22:33:44:55:66'])) + devices = {DEV1, DEV2} + resync_needed = agent.treat_devices_added_updated(devices) self.assertFalse(resync_needed) - calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0'), - mock.call('11:22:33:44:55:66', '1:2:3.0')] + calls = [mock.call(DEV1.mac, DEV1.pci_slot), + mock.call(DEV2.mac, DEV2.pci_slot)] agent.eswitch_mgr.device_exists.assert_has_calls(calls, any_order=True) - calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0', True, False), - mock.call('11:22:33:44:55:66', '1:2:3.0', False, False)] + calls = [mock.call(DEV1.mac, DEV1.pci_slot, True, False), + mock.call(DEV2.mac, DEV2.pci_slot, False, False)] agent.eswitch_mgr.set_device_state.assert_has_calls(calls, any_order=True) - calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0', False), - mock.call('11:22:33:44:55:66', '1:2:3.0', False)] - agent.eswitch_mgr.set_device_spoofcheck.assert_has_calls(calls, - any_order=True) + calls = [mock.call(DEV1.mac, DEV1.pci_slot, False), + mock.call(DEV2.mac, DEV2.pci_slot, False)] + agent.eswitch_mgr.set_device_spoofcheck.assert_has_calls( + calls, any_order=True) agent.plugin_rpc.update_device_list.assert_called_once_with( - mock.ANY, - set(['aa:bb:cc:dd:ee:ff']), - set(['11:22:33:44:55:66']), - mock.ANY, - mock.ANY) + agent.context, {DEV1}, {DEV2}, agent.agent_id, agent.conf.host) def test_treat_device_ip_link_state_not_supported(self): agent = self.agent @@ -426,8 +340,7 @@ class TestSriovAgent(base.BaseTestCase): agent.eswitch_mgr.set_device_state.side_effect = ( priv_ip_lib.InterfaceOperationNotSupported()) - self.assertTrue(agent.treat_device('aa:bb:cc:dd:ee:ff', '1:2:3:0', - admin_state_up=True)) + self.assertTrue(agent.treat_device(DEV1, admin_state_up=True)) def test_treat_device_set_device_state_exception(self): agent = self.agent @@ -437,8 +350,7 @@ class TestSriovAgent(base.BaseTestCase): agent.eswitch_mgr.set_device_state.side_effect = ( pyroute2.NetlinkError(22)) - self.assertFalse(agent.treat_device('aa:bb:cc:dd:ee:ff', '1:2:3:0', - admin_state_up=True)) + self.assertFalse(agent.treat_device(DEV1, admin_state_up=True)) def test_treat_device_no_device_found(self): agent = self.agent @@ -446,53 +358,32 @@ class TestSriovAgent(base.BaseTestCase): agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = False - self.assertFalse(agent.treat_device('aa:bb:cc:dd:ee:ff', '1:2:3:0', - admin_state_up=True)) + self.assertFalse(agent.treat_device(DEV1, admin_state_up=True)) def test_treat_devices_added_updated_admin_state_up_false(self): agent = self.agent - mock_details = {'device': 'aa:bb:cc:dd:ee:ff', - 'port_id': 'port123', - 'network_id': 'net123', - 'admin_state_up': False, - 'propagate_uplink_status': False, - 'network_type': 'vlan', - 'segmentation_id': 100, - 'profile': {'pci_slot': '1:2:3.0'}, - 'physical_network': 'physnet1'} agent.plugin_rpc = mock.Mock() - agent.plugin_rpc.get_devices_details_list.return_value = [mock_details] + rpc_dev1 = copy.deepcopy(RPC_DEV1) + rpc_dev1['admin_state_up'] = False + agent.plugin_rpc.get_devices_details_list.return_value = [rpc_dev1] agent.remove_port_binding = mock.Mock() agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = True - resync_needed = agent.treat_devices_added_updated( - set(['aa:bb:cc:dd:ee:ff'])) + devices = {DEV1} + resync_needed = agent.treat_devices_added_updated(devices) self.assertFalse(resync_needed) agent.plugin_rpc.update_device_list.assert_called_once_with( - mock.ANY, - set(), - set(['aa:bb:cc:dd:ee:ff']), - mock.ANY, - mock.ANY) + agent.context, set(), devices, agent.agent_id, agent.conf.host) def test_treat_devices_added_updated_no_device_found(self): agent = self.agent - mock_details = {'device': 'aa:bb:cc:dd:ee:ff', - 'port_id': 'port123', - 'network_id': 'net123', - 'admin_state_up': True, - 'network_type': 'vlan', - 'segmentation_id': 100, - 'profile': {'pci_slot': '1:2:3.0'}, - 'propagate_uplink_status': False, - 'physical_network': 'physnet1'} agent.plugin_rpc = mock.Mock() - agent.plugin_rpc.get_devices_details_list.return_value = [mock_details] + agent.plugin_rpc.get_devices_details_list.return_value = [RPC_DEV1] agent.remove_port_binding = mock.Mock() agent.eswitch_mgr = mock.Mock() agent.eswitch_mgr.device_exists.return_value = False - resync_needed = agent.treat_devices_added_updated( - set(['aa:bb:cc:dd:ee:ff'])) + devices = {DEV1} + resync_needed = agent.treat_devices_added_updated(devices) self.assertTrue(resync_needed) self.assertFalse(agent.plugin_rpc.update_device_up.called) @@ -502,27 +393,20 @@ class TestSriovAgent(base.BaseTestCase): port_id1 = 'port_id1' port_id2 = 'port_id2' - mac_slot_1 = ('mac1', 'slot1') - mac_slot_2 = ('mac2', 'slot2') - self.agent.network_ports[network_id1] = [{'port_id': port_id1, - 'device': mac_slot_1}, {'port_id': port_id2, 'device': mac_slot_2}] + self.agent._update_network_ports(network_id1, port_id1, DEV1) + self.agent._update_network_ports(network_id1, port_id2, DEV2) - self.agent._update_network_ports(network_id2, port_id1, mac_slot_1) + self.agent._update_network_ports(network_id2, port_id1, DEV1) + expected = {network_id1: [{'port_id': port_id2, 'device': DEV2}], + network_id2: [{'port_id': port_id1, 'device': DEV1}]} + self.assertEqual(expected, dict(self.agent.network_ports)) - self.assertEqual({network_id1: [{'port_id': port_id2, - 'device': mac_slot_2}], network_id2: [ - {'port_id': port_id1, 'device': mac_slot_1}]}, - self.agent.network_ports) + self.assertEqual(port_id1, self.agent._clean_network_ports(DEV1)) + expected = {network_id1: [{'port_id': port_id2, 'device': DEV2}]} + self.assertEqual(expected, self.agent.network_ports) - cleaned_port_id = self.agent._clean_network_ports(mac_slot_1) - self.assertEqual(cleaned_port_id, port_id1) - - self.assertEqual({network_id1: [{'port_id': port_id2, - 'device': mac_slot_2}]}, - self.agent.network_ports) - - cleaned_port_id = self.agent._clean_network_ports(mac_slot_2) + self.assertEqual(port_id2, self.agent._clean_network_ports(DEV2)) self.assertEqual({}, self.agent.network_ports) def test_configurations_has_rp_bandwidth(self): @@ -558,17 +442,12 @@ class TestSriovAgent(base.BaseTestCase): rp_inv_defaults[inv_key]) def test_process_activated_bindings(self): - # Create several devices which are pairs of (, ) - dev_a = ('fa:16:3e:f8:ae:af', "0000:01:00.0") - dev_b = ('fa:16:3e:f8:ae:b0', "0000:02:00.0") - dev_c = ('fa:16:3e:f8:ae:b1', "0000:03:00.0") - # Create device_info fake_device_info = { - 'current': set([dev_a, dev_b]), - 'added': set([dev_c]), + 'current': {DEV1, DEV2}, + 'added': {DEV3}, 'removed': set(), 'updated': set()} - fake_activated_bindings = set([dev_a]) + fake_activated_bindings = {DEV1} self.agent.process_activated_bindings(fake_device_info, fake_activated_bindings) self.assertLessEqual(fake_activated_bindings, @@ -592,6 +471,7 @@ class TestSriovNicSwitchRpcCallbacks(base.BaseTestCase): sg_agent = object() self.sriov_rpc_callback = sriov_nic_agent.SriovNicSwitchRpcCallbacks( self.context, self.agent, sg_agent) + self.device_info = agent_rpc.DeviceInfo(DEVICE_MAC, PCI_SLOT) def _create_fake_port(self): return {'id': uuidutils.generate_uuid(), @@ -606,8 +486,7 @@ class TestSriovNicSwitchRpcCallbacks(base.BaseTestCase): port = self._create_fake_port() kwargs = {'context': self.context, 'port': port} self.sriov_rpc_callback.port_update(**kwargs) - self.assertEqual(set([(DEVICE_MAC, PCI_SLOT)]), - self.agent.updated_devices) + self.assertEqual({self.device_info}, self.agent.updated_devices) def test_port_update_with_vnic_physical_direct(self): port = self._create_fake_port() @@ -618,7 +497,7 @@ class TestSriovNicSwitchRpcCallbacks(base.BaseTestCase): def test_port_update_without_pci_slot(self): port = self._create_fake_port() - port[portbindings.PROFILE] = None + port[portbindings.PROFILE] = {} kwargs = {'context': self.context, 'port': port} self.sriov_rpc_callback.port_update(**kwargs) self.assertEqual(set(), self.agent.updated_devices) diff --git a/neutron/tests/unit/plugins/ml2/test_db.py b/neutron/tests/unit/plugins/ml2/test_db.py index 07ada232e69..fbdd649d147 100644 --- a/neutron/tests/unit/plugins/ml2/test_db.py +++ b/neutron/tests/unit/plugins/ml2/test_db.py @@ -280,16 +280,6 @@ class Ml2DBTestCase(testlib_api.SqlTestCase): port = ml2_db.get_port(self.ctx, port_id) self.assertIsNone(port) - def test_get_port_from_device_mac(self): - network_id = uuidutils.generate_uuid() - port_id = uuidutils.generate_uuid() - self._setup_neutron_network(network_id) - port = self._setup_neutron_port(network_id, port_id) - - observed_port = ml2_db.get_port_from_device_mac(self.ctx, - port['mac_address']) - self.assertEqual(port_id, observed_port.id) - def test_generating_multiple_mac_addresses(self): mac_regex = "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$" diff --git a/neutron/tests/unit/plugins/ml2/test_rpc.py b/neutron/tests/unit/plugins/ml2/test_rpc.py index 4e0facca7b8..d70d072d0f4 100644 --- a/neutron/tests/unit/plugins/ml2/test_rpc.py +++ b/neutron/tests/unit/plugins/ml2/test_rpc.py @@ -436,7 +436,8 @@ class RpcApiTestCase(base.BaseTestCase): 'get_device_details', rpc_method='call', device='fake_device', agent_id='fake_agent_id', - host='fake_host') + host='fake_host', + version='1.9') def test_devices_details_list(self): rpcapi = agent_rpc.PluginApi(topics.PLUGIN) @@ -444,7 +445,7 @@ class RpcApiTestCase(base.BaseTestCase): 'get_devices_details_list', rpc_method='call', devices=['fake_device1', 'fake_device2'], agent_id='fake_agent_id', host='fake_host', - version='1.3') + version='1.9') def test_update_device_down(self): rpcapi = agent_rpc.PluginApi(topics.PLUGIN) @@ -452,7 +453,8 @@ class RpcApiTestCase(base.BaseTestCase): 'update_device_down', rpc_method='call', device='fake_device', agent_id='fake_agent_id', - host='fake_host') + host='fake_host', + version='1.9') def test_tunnel_sync(self): rpcapi = agent_rpc.PluginApi(topics.PLUGIN) @@ -469,7 +471,8 @@ class RpcApiTestCase(base.BaseTestCase): 'update_device_up', rpc_method='call', device='fake_device', agent_id='fake_agent_id', - host='fake_host') + host='fake_host', + version='1.9') def test_update_device_list(self): rpcapi = agent_rpc.PluginApi(topics.PLUGIN) @@ -480,7 +483,7 @@ class RpcApiTestCase(base.BaseTestCase): agent_id='fake_agent_id', host='fake_host', refresh_tunnels=False, - version='1.8') + version='1.9') def test_get_devices_details_list_and_failed_devices(self): rpcapi = agent_rpc.PluginApi(topics.PLUGIN) diff --git a/releasenotes/notes/sriov-agent-duplicated-mac-ports-a861b0ff800c3172.yaml b/releasenotes/notes/sriov-agent-duplicated-mac-ports-a861b0ff800c3172.yaml new file mode 100644 index 00000000000..3722f03a853 --- /dev/null +++ b/releasenotes/notes/sriov-agent-duplicated-mac-ports-a861b0ff800c3172.yaml @@ -0,0 +1,20 @@ +--- +features: + - | + SR-IOV agent now can handle ports from different networks with the same + MAC addresses. This feature implies an upgrade in the agent and the server + RPC version (see ``neutron.plugins.ml2.rpc.RpcCallbacks`` version 1.9). + Some agent RPC methods have been updated to pass not only the device MAC + address but the PCI slot too. In case of having more than one port with + the same MAC address, the PCI slot will discriminate the requested port. +upgrade: + - | + Both the server and the agent RPC versions have been bumped to 1.9; to + provide a smooth upgrade transition, the `Upgrade Procedure + `_ should + be followed, upgrading first the servers and then the agents. + The agent RPC methods returned values are not modified to keep + compatibility with other agents (Linux Bridge, Open vSwitch). The RPC + server side is capable of attending calls from agent API < 1.9, in order to + provide backwards compatibility. If the device PCI slot is not provided, + the behavior will be the previous one.