]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Qos SR-IOV: Refactor extension delete to get mac and pci slot
authorMoshe Levi <moshele@mellanox.com>
Tue, 18 Aug 2015 05:48:24 +0000 (08:48 +0300)
committerMoshe Levi <moshele@mellanox.com>
Mon, 7 Sep 2015 06:27:40 +0000 (09:27 +0300)
When calling delete we need the pci slot details to reset the VF rate. The problem
is that when the VM is deleted libvirt return the VF to the hypervisor and eswitch
manager will mark the pci_slot as unassigned so can't know from the mac which pci slot (VF)
to reset. Also newer libvirt version reset the mac when deleteing VM, so than it is
not possible at all.
The solution is to keep pci slot details locally in the agent since upon removal event
you cannot get pci_slot from the neutron server as it is for create/update since port
is already removed from neutron.

This patch pairs the mac and pci_slot for a device (VF) so when calling the extension
port delete api we can have the pci_slot and reset the VF rate.

It is also add a mapping between mac to port_id so we can pass the port_id
when calling the extention port delete api.

Partially-Implements: blueprint ml2-sriov-qos-with-bwlimiting
Closes-Bug: #1492909
Change-Id: Icc3a9599c6d7a4de9c56b452dfab7909c8d0a576

neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py
neutron/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/qos_driver.py
neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.py
neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py
neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/extension_drivers/test_qos_driver.py
neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py
neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_pci_lib.py
neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py

index 12168883e8ba1db4b8fc8ca960b6ec94bd0ed05d..f5f66c9e9ca2c0f3e1b0cd8214d38e017a5209b3 100644 (file)
@@ -125,20 +125,25 @@ class EmbSwitch(object):
         """Get list of VF addresses."""
         return self.pci_slot_map.keys()
 
-    def get_assigned_devices(self):
-        """Get assigned Virtual Functions.
+    def get_assigned_devices_info(self):
+        """Get assigned Virtual Functions mac and pci slot
+        information and populates vf_to_pci_slot mappings
 
-        @return: list of VF mac addresses
+        @return: list of VF pair (mac address, pci slot)
         """
-        vf_list = []
-        assigned_macs = []
-        for vf_index in self.pci_slot_map.values():
+        vf_to_pci_slot_mapping = {}
+        assigned_devices_info = []
+        for pci_slot, vf_index in self.pci_slot_map.items():
             if not PciOsWrapper.is_assigned_vf(self.dev_name, vf_index):
                 continue
-            vf_list.append(vf_index)
-        if vf_list:
-            assigned_macs = self.pci_dev_wrapper.get_assigned_macs(vf_list)
-        return assigned_macs
+            vf_to_pci_slot_mapping[vf_index] = pci_slot
+        if vf_to_pci_slot_mapping:
+            vf_to_mac_mapping = self.pci_dev_wrapper.get_assigned_macs(
+                list(vf_to_pci_slot_mapping.keys()))
+            for vf_index, mac in vf_to_mac_mapping.items():
+                pci_slot = vf_to_pci_slot_mapping[vf_index]
+                assigned_devices_info.append((mac, pci_slot))
+        return assigned_devices_info
 
     def get_device_state(self, pci_slot):
         """Get device state.
@@ -219,8 +224,7 @@ class EmbSwitch(object):
         if vf_index is not None:
             if PciOsWrapper.is_assigned_vf(self.dev_name, vf_index):
                 macs = self.pci_dev_wrapper.get_assigned_macs([vf_index])
-                if macs:
-                    mac = macs[0]
+                mac = macs.get(vf_index)
         return mac
 
 
@@ -247,12 +251,12 @@ class ESwitchManager(object):
             return True
         return False
 
-    def get_assigned_devices(self, phys_net=None):
+    def get_assigned_devices_info(self, phys_net=None):
         """Get all assigned devices.
 
         Get all assigned devices belongs to given embedded switch
         @param phys_net: physical network, if none get all assigned devices
-        @return: set of assigned VFs mac addresses
+        @return: set of assigned VFs (mac address, pci slot) pair
         """
         if phys_net:
             embedded_switch = self.emb_switches_map.get(phys_net, None)
@@ -263,8 +267,8 @@ class ESwitchManager(object):
             eswitch_objects = self.emb_switches_map.values()
         assigned_devices = set()
         for embedded_switch in eswitch_objects:
-            for device_mac in embedded_switch.get_assigned_devices():
-                assigned_devices.add(device_mac)
+            for device in embedded_switch.get_assigned_devices_info():
+                assigned_devices.add(device)
         return assigned_devices
 
     def get_device_state(self, device_mac, pci_slot):
@@ -272,7 +276,7 @@ class ESwitchManager(object):
 
         Get the device state (up/True or down/False)
         @param device_mac: device mac
-        @param pci_slot: VF pci slot
+        @param pci_slot: VF PCI slot
         @return: device state (True/False) None if failed
         """
         embedded_switch = self._get_emb_eswitch(device_mac, pci_slot)
@@ -355,16 +359,27 @@ class ESwitchManager(object):
                 embedded_switch = None
         return embedded_switch
 
-    def get_pci_slot_by_mac(self, device_mac):
-        """Get pci slot by mac.
+    def clear_max_rate(self, pci_slot):
+        """Clear the max rate
 
-        Get pci slot by device mac
-        @param device_mac: device mac
+        Clear the max rate configuration from VF by setting it to 0
+        @param pci_slot: VF PCI slot
         """
-        result = None
-        for pci_slot, embedded_switch in self.pci_slot_map.items():
-            used_device_mac = embedded_switch.get_pci_device(pci_slot)
-            if used_device_mac == device_mac:
-                result = pci_slot
-                break
-        return result
+        #(Note): we don't use the self._get_emb_eswitch here, because when
+        #clearing the VF it may be not assigned. This happens when libvirt
+        #releases the VF back to the hypervisor on delete VM. Therefore we
+        #should just clear the VF max rate according to pci_slot no matter
+        #if VF is assigned or not.
+        embedded_switch = self.pci_slot_map.get(pci_slot)
+        if embedded_switch:
+            #(Note): check the pci_slot is not assigned to some
+            # other port before resetting the max rate.
+            if embedded_switch.get_pci_device(pci_slot) is None:
+                embedded_switch.set_device_max_rate(pci_slot, 0)
+            else:
+                LOG.warning(_LW("VF with PCI slot %(pci_slot)s is already "
+                                "assigned; skipping reset maximum rate"),
+                            {'pci_slot': pci_slot})
+        else:
+            LOG.error(_LE("PCI slot %(pci_slot)s has no mapping to Embedded "
+                          "Switch; skipping"), {'pci_slot': pci_slot})
index 8c30817a1abc0383d2059bc945081913bbcb7772..4822360a717679819fecb25679d7ff59c070cc13 100755 (executable)
@@ -69,8 +69,7 @@ class QosSRIOVAgentDriver(qos.QosAgentDriver):
 
     def _delete_bandwidth_limit(self, port):
         pci_slot = port['profile'].get('pci_slot')
-        device = port['device']
-        self._set_vf_max_rate(device, pci_slot)
+        self.eswitch_mgr.clear_max_rate(pci_slot)
 
     def _set_vf_max_rate(self, device, pci_slot, max_kbps=0):
         if self.eswitch_mgr.device_exists(device, pci_slot):
index 8f984e0aac4b6d8f08108c16d4decdfcded477d5..f57be57bb68d7af6eaef93f59eb15a857d4093f0 100644 (file)
@@ -50,7 +50,7 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper):
         """Get assigned mac addresses for vf list.
 
         @param vf_list: list of vf indexes
-        @return: list of assigned mac addresses
+        @return: dict mapping of vf to mac
         """
         try:
             out = self._as_root([], "link", ("show", self.dev_name))
@@ -58,14 +58,16 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper):
             LOG.exception(_LE("Failed executing ip command"))
             raise exc.IpCommandError(dev_name=self.dev_name,
                                      reason=e)
+        vf_to_mac_mapping = {}
         vf_lines = self._get_vf_link_show(vf_list, out)
-        vf_details_list = []
         if vf_lines:
             for vf_line in vf_lines:
                 vf_details = self._parse_vf_link_show(vf_line)
                 if vf_details:
-                    vf_details_list.append(vf_details)
-        return [details.get("MAC") for details in vf_details_list]
+                    vf_num = vf_details.get('vf')
+                    vf_mac = vf_details.get("MAC")
+                    vf_to_mac_mapping[vf_num] = vf_mac
+        return vf_to_mac_mapping
 
     def get_vf_state(self, vf_index):
         """Get vf state {True/False}
index 13210aa5152aab8ec643d7d9a3400bbdd9d0fd4a..3bd141a954dd5ff9584f9017e8533abcc0115ac1 100644 (file)
@@ -64,8 +64,20 @@ class SriovNicSwitchRpcCallbacks(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
         # Do not store port details, as if they're used for processing
         # notifications there is no guarantee the notifications are
         # processed in the same order as the relevant API requests.
-        self.agent.updated_devices.add(port['mac_address'])
-        LOG.debug("port_update RPC received for port: %s", port['id'])
+        mac = port['mac_address']
+        pci_slot = None
+        if port.get('binding:profile'):
+            pci_slot = port['binding:profile'].get('pci_slot')
+
+        if pci_slot:
+            self.agent.updated_devices.add((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})
+        else:
+            LOG.debug("No PCI Slot for port %(id)s with MAC %(mac)s; "
+                      "skipping", {'id': port['id'], 'mac': mac,
+                                   'pci_slot': pci_slot})
 
 
 class SriovNicSwitchAgent(object):
@@ -87,6 +99,7 @@ class SriovNicSwitchAgent(object):
 
         # Stores port update notifications for processing in the main loop
         self.updated_devices = set()
+        self.mac_to_port_id_mapping = {}
 
         self.context = context.get_admin_context_without_session()
         self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN)
@@ -128,7 +141,7 @@ class SriovNicSwitchAgent(object):
 
     def _report_state(self):
         try:
-            devices = len(self.eswitch_mgr.get_assigned_devices())
+            devices = len(self.eswitch_mgr.get_assigned_devices_info())
             self.agent_state.get('configurations')['devices'] = devices
             self.state_rpc.report_state(self.context,
                                         self.agent_state)
@@ -147,7 +160,7 @@ class SriovNicSwitchAgent(object):
         self.eswitch_mgr.discover_devices(device_mappings, exclude_devices)
 
     def scan_devices(self, registered_devices, updated_devices):
-        curr_devices = self.eswitch_mgr.get_assigned_devices()
+        curr_devices = self.eswitch_mgr.get_assigned_devices_info()
         device_info = {}
         device_info['current'] = curr_devices
         device_info['added'] = curr_devices - registered_devices
@@ -214,14 +227,15 @@ class SriovNicSwitchAgent(object):
         else:
             LOG.info(_LI("No device with MAC %s defined on agent."), device)
 
-    def treat_devices_added_updated(self, devices):
+    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, devices, self.agent_id)
+                self.context, macs_list, self.agent_id)
         except Exception as e:
             LOG.debug("Unable to get port details for devices "
-                      "with MAC address %(devices)s: %(e)s",
-                      {'devices': devices, 'e': e})
+                      "with MAC addresses %(devices)s: %(e)s",
+                      {'devices': macs_list, 'e': e})
             # resync is needed
             return True
 
@@ -232,9 +246,11 @@ class SriovNicSwitchAgent(object):
             if 'port_id' in device_details:
                 LOG.info(_LI("Port %(device)s updated. Details: %(details)s"),
                          {'device': device, 'details': device_details})
+                port_id = device_details['port_id']
+                self.mac_to_port_id_mapping[device] = port_id
                 profile = device_details['profile']
                 spoofcheck = device_details.get('port_security_enabled', True)
-                self.treat_device(device_details['device'],
+                self.treat_device(device,
                                   profile.get('pci_slot'),
                                   device_details['admin_state_up'],
                                   spoofcheck)
@@ -247,31 +263,41 @@ class SriovNicSwitchAgent(object):
     def treat_devices_removed(self, devices):
         resync = False
         for device in devices:
-            LOG.info(_LI("Removing device with mac_address %s"), device)
+            mac, pci_slot = device
+            LOG.info(_LI("Removing device with MAC address %(mac)s and "
+                         "PCI slot %(pci_slot)s"),
+                     {'mac': mac, 'pci_slot': pci_slot})
             try:
-                pci_slot = self.eswitch_mgr.get_pci_slot_by_mac(device)
-                if pci_slot:
+                port_id = self.mac_to_port_id_mapping.get(mac)
+                if port_id:
                     profile = {'pci_slot': pci_slot}
-                    port = {'device': device, 'profile': profile}
+                    port = {'port_id': port_id,
+                            'device': mac,
+                            'profile': profile}
                     self.ext_manager.delete_port(self.context, port)
+                    del self.mac_to_port_id_mapping[mac]
                 else:
-                    LOG.warning(_LW("Failed to find pci slot for device "
-                                    "%(device)s; skipping extension port "
-                                    "cleanup"), device)
-
+                    LOG.warning(_LW("port_id to device with MAC "
+                                 "%s not found"), mac)
                 dev_details = self.plugin_rpc.update_device_down(self.context,
-                                                                 device,
+                                                                 mac,
                                                                  self.agent_id,
                                                                  cfg.CONF.host)
+
             except Exception as e:
-                LOG.debug("Removing port failed for device %(device)s "
-                          "due to %(exc)s", {'device': device, 'exc': 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})
                 resync = True
                 continue
             if dev_details['exists']:
-                LOG.info(_LI("Port %s updated."), device)
+                LOG.info(_LI("Port with MAC %(mac)s and PCI slot "
+                             "%(pci_slot)s updated."),
+                         {'mac': mac, 'pci_slot': pci_slot})
             else:
-                LOG.debug("Device %s not defined on plugin", device)
+                LOG.debug("Device with MAC %(mac)s and PCI slot "
+                          "%(pci_slot)s not defined on plugin",
+                          {'mac': mac, 'pci_slot': pci_slot})
         return resync
 
     def daemon_loop(self):
index 7ccb74507c3369be8f188b9410e84ff899f7ec57..1541a299fec8991ae53d1bd4425289790de43dce 100755 (executable)
@@ -37,7 +37,9 @@ class QosSRIOVAgentDriverTestCase(base.BaseTestCase):
         self.qos_driver.initialize()
         self.qos_driver.eswitch_mgr = mock.Mock()
         self.qos_driver.eswitch_mgr.set_device_max_rate = mock.Mock()
+        self.qos_driver.eswitch_mgr.clear_max_rate = mock.Mock()
         self.max_rate_mock = self.qos_driver.eswitch_mgr.set_device_max_rate
+        self.clear_max_rate_mock = self.qos_driver.eswitch_mgr.clear_max_rate
         self.rule = self._create_bw_limit_rule_obj()
         self.qos_policy = self._create_qos_policy_obj([self.rule])
         self.port = self._create_fake_port()
@@ -78,8 +80,7 @@ class QosSRIOVAgentDriverTestCase(base.BaseTestCase):
 
     def test_delete_rules(self):
         self.qos_driver.delete(self.port, self.qos_policy)
-        self.max_rate_mock.assert_called_once_with(
-            self.ASSIGNED_MAC, self.PCI_SLOT, 0)
+        self.clear_max_rate_mock.assert_called_once_with(self.PCI_SLOT)
 
     def test__set_vf_max_rate_captures_sriov_failure(self):
         self.max_rate_mock.side_effect = exceptions.SriovNicError()
index b3a7d958a870dc508c3bb743cbadd57336a0a57d..248426e9ddd3c6cc74e135fc060e9befa731d060 100644 (file)
@@ -79,12 +79,13 @@ class TestESwitchManagerApi(base.BaseTestCase):
             self.eswitch_mgr = esm.ESwitchManager()
             self.eswitch_mgr.discover_devices(device_mappings, None)
 
-    def test_get_assigned_devices(self):
+    def test_get_assigned_devices_info(self):
         with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
-                        "eswitch_manager.EmbSwitch.get_assigned_devices",
-                        return_value=[self.ASSIGNED_MAC]):
-            result = self.eswitch_mgr.get_assigned_devices()
-            self.assertEqual(set([self.ASSIGNED_MAC]), result)
+                        "eswitch_manager.EmbSwitch.get_assigned_devices_info",
+                        return_value=[(self.ASSIGNED_MAC, self.PCI_SLOT)]):
+            result = self.eswitch_mgr.get_assigned_devices_info()
+            self.assertIn(self.ASSIGNED_MAC, list(result)[0])
+            self.assertIn(self.PCI_SLOT, list(result)[0])
 
     def test_get_device_status_true(self):
         with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
@@ -196,23 +197,28 @@ class TestESwitchManagerApi(base.BaseTestCase):
 
     @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
                 "PciDeviceIPWrapper.get_assigned_macs",
-                return_value=[ASSIGNED_MAC])
+                return_value={})
     @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
-                "eswitch_manager.PciOsWrapper.is_assigned_vf",
-                return_value=True)
-    def test_get_pci_slot_by_existing_mac(self, *args):
-        pci_slot = self.eswitch_mgr.get_pci_slot_by_mac(self.ASSIGNED_MAC)
-        self.assertIsNotNone(pci_slot)
+                "eswitch_manager.EmbSwitch.set_device_max_rate")
+    def test_clear_max_rate_existing_pci_slot(self, max_rate_mock, *args):
+        self.eswitch_mgr.clear_max_rate(self.PCI_SLOT)
+        max_rate_mock.assert_called_once_with(self.PCI_SLOT, 0)
 
     @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
                 "PciDeviceIPWrapper.get_assigned_macs",
-                return_value=[ASSIGNED_MAC])
+                return_value={0: ASSIGNED_MAC})
     @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
-                "eswitch_manager.PciOsWrapper.is_assigned_vf",
-                return_value=True)
-    def test_get_pci_slot_by_not_existing_mac(self, *args):
-        pci_slot = self.eswitch_mgr.get_pci_slot_by_mac(self.WRONG_MAC)
-        self.assertIsNone(pci_slot)
+                "eswitch_manager.EmbSwitch.set_device_max_rate")
+    def test_clear_max_rate_exist_and_assigned_pci(
+            self, max_rate_mock, *args):
+        self.eswitch_mgr.clear_max_rate(self.PCI_SLOT)
+        self.assertFalse(max_rate_mock.called)
+
+    @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
+                "eswitch_manager.EmbSwitch.set_device_max_rate")
+    def test_clear_max_rate_nonexisting_pci_slot(self, max_rate_mock):
+        self.eswitch_mgr.clear_max_rate(self.WRONG_PCI)
+        self.assertFalse(max_rate_mock.called)
 
 
 class TestEmbSwitch(base.BaseTestCase):
@@ -224,6 +230,13 @@ class TestEmbSwitch(base.BaseTestCase):
     SCANNED_DEVICES = [('0000:06:00.1', 0),
                        ('0000:06:00.2', 1),
                        ('0000:06:00.3', 2)]
+    VF_TO_MAC_MAPPING = {0: '00:00:00:00:00:11',
+                         1: '00:00:00:00:00:22',
+                         2: '00:00:00:00:00:33'}
+    EXPECTED_MAC_TO_PCI = {
+        '00:00:00:00:00:11': '0000:06:00.1',
+        '00:00:00:00:00:22': '0000:06:00.2',
+        '00:00:00:00:00:33': '0000:06:00.3'}
 
     def setUp(self):
         super(TestEmbSwitch, self).setUp()
@@ -234,21 +247,44 @@ class TestEmbSwitch(base.BaseTestCase):
             self.emb_switch = esm.EmbSwitch(self.PHYS_NET, self.DEV_NAME,
                                             exclude_devices)
 
-    def test_get_assigned_devices(self):
+    @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.PHYS_NET, self.DEV_NAME, ())
         with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
                         "PciDeviceIPWrapper.get_assigned_macs",
-                        return_value=[self.ASSIGNED_MAC]),\
+                        return_value={0: self.ASSIGNED_MAC}),\
+                mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
+                           "eswitch_manager.PciOsWrapper.is_assigned_vf",
+                           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])
+
+    @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.PHYS_NET, 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",
                            return_value=True):
-            result = self.emb_switch.get_assigned_devices()
-            self.assertEqual([self.ASSIGNED_MAC], result)
+            devices_info = 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)
 
     def test_get_assigned_devices_empty(self):
         with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
                         "eswitch_manager.PciOsWrapper.is_assigned_vf",
                         return_value=False):
-            result = self.emb_switch.get_assigned_devices()
+            result = self.emb_switch.get_assigned_devices_info()
             self.assertFalse(result)
 
     def test_get_device_state_ok(self):
@@ -341,7 +377,7 @@ class TestEmbSwitch(base.BaseTestCase):
     def test_get_pci_device(self):
         with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
                         "PciDeviceIPWrapper.get_assigned_macs",
-                        return_value=[self.ASSIGNED_MAC]),\
+                        return_value={0: self.ASSIGNED_MAC}),\
                 mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
                            "eswitch_manager.PciOsWrapper.is_assigned_vf",
                            return_value=True):
index 5512ea95f6b051931ec38c7640fb7cdd0bdb6668..e8b267e2a3b7a9f65606fc526b60370aaf345b59 100644 (file)
@@ -53,7 +53,8 @@ class TestPciLib(base.BaseTestCase):
                                "_as_root") as mock_as_root:
             mock_as_root.return_value = self.VF_LINK_SHOW
             result = self.pci_wrapper.get_assigned_macs([self.VF_INDEX])
-            self.assertEqual([self.MAC_MAPPING[self.VF_INDEX]], result)
+            self.assertEqual(
+                {self.VF_INDEX: self.MAC_MAPPING[self.VF_INDEX]}, result)
 
     def test_get_assigned_macs_fail(self):
         with mock.patch.object(self.pci_wrapper,
index 8ebc73ce5fb78c6fc4edf2675aff24e41f969e91..b636f71bd731859c14220266f12365a263578504 100644 (file)
 
 import mock
 from oslo_config import cfg
+from oslo_utils import uuidutils
 
 from neutron.plugins.ml2.drivers.mech_sriov.agent.common import config  # noqa
 from neutron.plugins.ml2.drivers.mech_sriov.agent import sriov_nic_agent
 from neutron.tests import base
 
 DEVICE_MAC = '11:22:33:44:55:66'
+PCI_SLOT = "0000:06:00.1"
 
 
 class TestSriovAgent(base.BaseTestCase):
@@ -51,33 +53,30 @@ class TestSriovAgent(base.BaseTestCase):
 
     @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
                 "PciDeviceIPWrapper.get_assigned_macs",
-                return_value=[DEVICE_MAC])
+                return_value=[(DEVICE_MAC, PCI_SLOT)])
     @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
                 "eswitch_manager.PciOsWrapper.is_assigned_vf",
                 return_value=True)
     def test_treat_devices_removed_with_existed_device(self, *args):
         agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0)
-        devices = [DEVICE_MAC]
+        devices = [(DEVICE_MAC, PCI_SLOT)]
         with mock.patch.object(agent.plugin_rpc,
                                "update_device_down") as fn_udd:
             fn_udd.return_value = {'device': DEVICE_MAC,
                                    'exists': True}
-            with mock.patch.object(sriov_nic_agent.LOG,
-                                   'info') as log:
-                resync = agent.treat_devices_removed(devices)
-                self.assertEqual(2, log.call_count)
-                self.assertFalse(resync)
-                self.assertTrue(fn_udd.called)
+            resync = agent.treat_devices_removed(devices)
+            self.assertFalse(resync)
+            self.assertTrue(fn_udd.called)
 
     @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
                 "PciDeviceIPWrapper.get_assigned_macs",
-                return_value=[DEVICE_MAC])
+                return_value=[(DEVICE_MAC, PCI_SLOT)])
     @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
                 "eswitch_manager.PciOsWrapper.is_assigned_vf",
                 return_value=True)
     def test_treat_devices_removed_with_not_existed_device(self, *args):
         agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0)
-        devices = [DEVICE_MAC]
+        devices = [(DEVICE_MAC, PCI_SLOT)]
         with mock.patch.object(agent.plugin_rpc,
                                "update_device_down") as fn_udd:
             fn_udd.return_value = {'device': DEVICE_MAC,
@@ -91,13 +90,13 @@ class TestSriovAgent(base.BaseTestCase):
 
     @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
                 "PciDeviceIPWrapper.get_assigned_macs",
-                return_value=[DEVICE_MAC])
+                return_value=[(DEVICE_MAC, PCI_SLOT)])
     @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
                 "eswitch_manager.PciOsWrapper.is_assigned_vf",
                 return_value=True)
     def test_treat_devices_removed_failed(self, *args):
         agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0)
-        devices = [DEVICE_MAC]
+        devices = [(DEVICE_MAC, PCI_SLOT)]
         with mock.patch.object(agent.plugin_rpc,
                                "update_device_down") as fn_udd:
             fn_udd.side_effect = Exception()
@@ -111,7 +110,8 @@ class TestSriovAgent(base.BaseTestCase):
     def mock_scan_devices(self, expected, mock_current,
                           registered_devices, updated_devices):
         self.agent.eswitch_mgr = mock.Mock()
-        self.agent.eswitch_mgr.get_assigned_devices.return_value = mock_current
+        self.agent.eswitch_mgr.get_assigned_devices_info.return_value = (
+            mock_current)
 
         results = self.agent.scan_devices(registered_devices, updated_devices)
         self.assertEqual(expected, results)
@@ -238,3 +238,38 @@ class TestSriovAgent(base.BaseTestCase):
 
         self.assertFalse(resync_needed)
         self.assertFalse(agent.plugin_rpc.update_device_up.called)
+
+
+class FakeAgent(object):
+    def __init__(self):
+        self.updated_devices = set()
+
+
+class TestSriovNicSwitchRpcCallbacks(base.BaseTestCase):
+
+    def setUp(self):
+        super(TestSriovNicSwitchRpcCallbacks, self).setUp()
+        self.context = object()
+        self.agent = FakeAgent()
+        sg_agent = object()
+        self.sriov_rpc_callback = sriov_nic_agent.SriovNicSwitchRpcCallbacks(
+            self.context, self.agent, sg_agent)
+
+    def _create_fake_port(self):
+        return {'id': uuidutils.generate_uuid(),
+                'binding:profile': {'pci_slot': PCI_SLOT},
+                'mac_address': DEVICE_MAC}
+
+    def test_port_update_with_pci_slot(self):
+        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)
+
+    def test_port_update_without_pci_slot(self):
+        port = self._create_fake_port()
+        port['binding:profile'] = None
+        kwargs = {'context': self.context, 'port': port}
+        self.sriov_rpc_callback.port_update(**kwargs)
+        self.assertEqual(set(), self.agent.updated_devices)