]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
SR-IOV: Fix macvtap assigned vf check when kernel < 3.13
authorMoshe Levi <moshele@mellanox.com>
Tue, 8 Dec 2015 11:11:33 +0000 (13:11 +0200)
committerIhar Hrachyshka <ihrachys@redhat.com>
Thu, 7 Jan 2016 08:38:39 +0000 (09:38 +0100)
when creating macvtap passthrough to SR-IOV VF in Kernel >= 3.13 an
upper_macvtap symbolic link is created. For Kernel < 3.13 the only
way to know it is by parsing the ip link show output and look for
macvtap[0-9]+@<vf ifname>.

This patch used the ip link show command to detects
macvtap assigned vf so that detection of macvtap assigned vf
will work on all kernels

Closes-Bug: #1523083
Change-Id: Icbc8d6af5c00d1453095e04dd779210dc7244c7f

neutron/plugins/ml2/drivers/mech_sriov/agent/common/exceptions.py
neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py
neutron/plugins/ml2/drivers/mech_sriov/agent/pci_lib.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
releasenotes/notes/macvtap_assigned_vf_check-f4d07660ffd82a24.yaml [new file with mode: 0644]

index fbf8a5492ca63af78fae6bd023e72f0ad966a795..2c960748f46aeb8b8aab624a14de4b2e08fc925d 100644 (file)
@@ -26,7 +26,7 @@ class InvalidDeviceError(SriovNicError):
 
 
 class IpCommandError(SriovNicError):
-    message = _("ip command failed on device %(dev_name)s: %(reason)s")
+    message = _("ip command failed: %(reason)s")
 
 
 class IpCommandOperationNotSupportedError(SriovNicError):
@@ -35,3 +35,7 @@ class IpCommandOperationNotSupportedError(SriovNicError):
 
 class InvalidPciSlotError(SriovNicError):
     message = _("Invalid pci slot %(pci_slot)s")
+
+
+class IpCommandDeviceError(SriovNicError):
+    message = _("ip command failed on device %(dev_name)s: %(reason)s")
index dc4c0e72f01ef268d869773b2851d8dd882cb9bb..ea0fac3d0be17ad4397ffdcf574b1bb5760f3073 100644 (file)
@@ -13,7 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import glob
 import os
 import re
 
@@ -36,7 +35,6 @@ class PciOsWrapper(object):
     PCI_PATH = "/sys/class/net/%s/device/virtfn%s/net"
     VIRTFN_FORMAT = r"^virtfn(?P<vf_index>\d+)"
     VIRTFN_REG_EX = re.compile(VIRTFN_FORMAT)
-    MAC_VTAP_PREFIX = "upper_macvtap*"
 
     @classmethod
     def scan_vf_devices(cls, dev_name):
@@ -75,15 +73,25 @@ class PciOsWrapper(object):
         by checking the relevant path in the system:
         VF is assigned if:
             Direct VF: PCI_PATH does not exist.
-            Macvtap VF: upper_macvtap path exists.
+            Macvtap VF: macvtap@<vf interface> interface exists in ip link show
         @param dev_name: pf network device name
         @param vf_index: vf index
         """
         path = cls.PCI_PATH % (dev_name, vf_index)
-        if not os.path.isdir(path):
+
+        try:
+            ifname_list = os.listdir(path)
+        except OSError:
+            # PCI_PATH does not exist means that the DIRECT VF assigend
             return True
-        upper_macvtap_path = os.path.join(path, "*", cls.MAC_VTAP_PREFIX)
-        return bool(glob.glob(upper_macvtap_path))
+
+        # Note(moshele) kernel < 3.13 doesn't create symbolic link
+        # for macvtap interface. Therefore we workaround it
+        # by parsing ip link show and checking if macvtap interface exists
+        for ifname in ifname_list:
+            if pci_lib.PciDeviceIPWrapper.is_macvtap_assigned(ifname):
+                return True
+        return False
 
 
 class EmbSwitch(object):
index e5403e2e4bce4ac10ce74b61b37418f5c9c28e24..307418207dcea33c8bf9f45fd4dea696c6c590b6 100644 (file)
@@ -34,9 +34,11 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper):
     MAC_PATTERN = r"MAC\s+(?P<mac>[a-fA-F0-9:]+),"
     STATE_PATTERN = r"\s+link-state\s+(?P<state>\w+)"
     ANY_PATTERN = ".*,"
+    MACVTAP_PATTERN = r".*macvtap[0-9]+@(?P<vf_interface>[a-zA-Z0-9]+):"
 
     VF_LINE_FORMAT = VF_PATTERN + MAC_PATTERN + ANY_PATTERN + STATE_PATTERN
     VF_DETAILS_REG_EX = re.compile(VF_LINE_FORMAT)
+    MACVTAP_REG_EX = re.compile(MACVTAP_PATTERN)
 
     IP_LINK_OP_NOT_SUPPORTED = 'RTNETLINK answers: Operation not supported'
 
@@ -68,8 +70,8 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper):
                 raise exc.IpCommandOperationNotSupportedError(
                     dev_name=self.dev_name)
             else:
-                raise exc.IpCommandError(dev_name=self.dev_name,
-                                         reason=str(e))
+                raise exc.IpCommandDeviceError(dev_name=self.dev_name,
+                                               reason=str(e))
 
     def get_assigned_macs(self, vf_list):
         """Get assigned mac addresses for vf list.
@@ -81,8 +83,8 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper):
             out = self._as_root([], "link", ("show", self.dev_name))
         except Exception as e:
             LOG.exception(_LE("Failed executing ip command"))
-            raise exc.IpCommandError(dev_name=self.dev_name,
-                                     reason=e)
+            raise exc.IpCommandDeviceError(dev_name=self.dev_name,
+                                           reason=e)
         vf_to_mac_mapping = {}
         vf_lines = self._get_vf_link_show(vf_list, out)
         if vf_lines:
@@ -104,8 +106,8 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper):
             out = self._as_root([], "link", ("show", self.dev_name))
         except Exception as e:
             LOG.exception(_LE("Failed executing ip command"))
-            raise exc.IpCommandError(dev_name=self.dev_name,
-                                     reason=e)
+            raise exc.IpCommandDeviceError(dev_name=self.dev_name,
+                                           reason=e)
         vf_lines = self._get_vf_link_show([vf_index], out)
         if vf_lines:
             vf_details = self._parse_vf_link_show(vf_lines[0])
@@ -181,3 +183,26 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper):
                             "for %(device)s"),
                         {'line': vf_line, 'device': self.dev_name})
         return vf_details
+
+    @classmethod
+    def is_macvtap_assigned(cls, ifname):
+        """Check if vf has macvtap interface assigned
+
+        Parses the output of ip link show command and checks
+        if macvtap[0-9]+@<vf interface> regex matches the
+        output.
+        @param ifname: vf interface name
+        @return: True on match otherwise False
+        """
+        try:
+            out = cls._execute([], "link", ("show", ), run_as_root=True)
+        except Exception as e:
+            LOG.error(_LE("Failed executing ip command: %s"), e)
+            raise exc.IpCommandError(reason=e)
+
+        for line in out.splitlines():
+            pattern_match = cls.MACVTAP_REG_EX.match(line)
+            if pattern_match:
+                if ifname == pattern_match.group('vf_interface'):
+                    return True
+        return False
index 248426e9ddd3c6cc74e135fc060e9befa731d060..bdaf9f3aa9ef8e681ad0888b537be13ea5dbdc5e 100644 (file)
@@ -19,7 +19,6 @@ import os
 import mock
 import testtools
 
-
 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
@@ -457,31 +456,30 @@ class TestPciOsWrapper(base.BaseTestCase):
                               esm.PciOsWrapper.scan_vf_devices,
                               self.DEV_NAME)
 
-    def _mock_assign_vf(self, dir_exists):
-        with mock.patch("os.path.isdir",
-                        return_value=dir_exists):
-            result = esm.PciOsWrapper.is_assigned_vf(self.DEV_NAME,
-                                                     self.VF_INDEX)
-            self.assertEqual(not dir_exists, result)
-
-    def test_is_assigned_vf_true(self):
-        self._mock_assign_vf(True)
-
-    def test_is_assigned_vf_false(self):
-        self._mock_assign_vf(False)
-
-    def _mock_assign_vf_macvtap(self, macvtap_exists):
-        def _glob(file_path):
-            return ["upper_macvtap0"] if macvtap_exists else []
+    @mock.patch("os.listdir", side_effect=OSError())
+    def test_is_assigned_vf_true(self, *args):
+        self.assertTrue(esm.PciOsWrapper.is_assigned_vf(
+            self.DEV_NAME, self.VF_INDEX))
 
-        with mock.patch("os.path.isdir", return_value=True),\
-                mock.patch("glob.glob", side_effect=_glob):
-            result = esm.PciOsWrapper.is_assigned_vf(self.DEV_NAME,
-                                                     self.VF_INDEX)
-            self.assertEqual(macvtap_exists, result)
+    @mock.patch("os.listdir", return_value=[DEV_NAME, "eth1"])
+    @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
+                "PciDeviceIPWrapper.is_macvtap_assigned", return_value=False)
+    def test_is_assigned_vf_false(self, *args):
+        self.assertFalse(esm.PciOsWrapper.is_assigned_vf(
+            self.DEV_NAME, self.VF_INDEX))
 
-    def test_is_assigned_vf_macvtap_true(self):
-        self._mock_assign_vf_macvtap(True)
+    @mock.patch("os.listdir", return_value=["eth0", "eth1"])
+    @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
+                "PciDeviceIPWrapper.is_macvtap_assigned", return_value=True)
+    def test_is_assigned_vf_macvtap(
+        self, mock_is_macvtap_assigned, *args):
+        esm.PciOsWrapper.is_assigned_vf(self.DEV_NAME, self.VF_INDEX)
+        mock_is_macvtap_assigned.called_with(self.VF_INDEX, "eth0")
 
-    def test_is_assigned_vf_macvtap_false(self):
-        self._mock_assign_vf_macvtap(False)
+    @mock.patch("os.listdir", side_effect=OSError())
+    @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
+                "PciDeviceIPWrapper.is_macvtap_assigned")
+    def test_is_assigned_vf_macvtap_failure(
+        self, mock_is_macvtap_assigned, *args):
+        esm.PciOsWrapper.is_assigned_vf(self.DEV_NAME, self.VF_INDEX)
+        self.assertFalse(mock_is_macvtap_assigned.called)
index d6e385be0bbd3ac07523921ac17c31f29184a466..c6b892e77c5152f21c0ac2c1cbd662ea11a480ca 100644 (file)
@@ -37,6 +37,12 @@ class TestPciLib(base.BaseTestCase):
                       ' checking off, link-state enable')
     VF_LINK_SHOW = '\n'.join((PF_LINK_SHOW, PF_MAC, VF_0_LINK_SHOW,
                               VF_1_LINK_SHOW, VF_2_LINK_SHOW))
+    MACVTAP_LINK_SHOW = ('63: macvtap1@enp129s0f1: <BROADCAST,MULTICAST> mtu '
+                         '1500 qdisc  noop state DOWN mode DEFAULT group '
+                         'default qlen 500 link/ether 4a:9b:6d:de:65:b5 brd '
+                         'ff:ff:ff:ff:ff:ff')
+
+    IP_LINK_SHOW_WITH_MACVTAP = '\n'.join((VF_LINK_SHOW, MACVTAP_LINK_SHOW))
 
     MAC_MAPPING = {
         0: "fa:16:3e:b4:81:ac",
@@ -60,7 +66,7 @@ class TestPciLib(base.BaseTestCase):
         with mock.patch.object(self.pci_wrapper,
                                "_as_root") as mock_as_root:
             mock_as_root.side_effect = Exception()
-            self.assertRaises(exc.IpCommandError,
+            self.assertRaises(exc.IpCommandDeviceError,
                               self.pci_wrapper.get_assigned_macs,
                               [self.VF_INDEX])
 
@@ -82,7 +88,7 @@ class TestPciLib(base.BaseTestCase):
         with mock.patch.object(self.pci_wrapper,
                                "_as_root") as mock_as_root:
             mock_as_root.side_effect = Exception()
-            self.assertRaises(exc.IpCommandError,
+            self.assertRaises(exc.IpCommandDeviceError,
                               self.pci_wrapper.get_vf_state,
                               self.VF_INDEX)
 
@@ -96,7 +102,7 @@ class TestPciLib(base.BaseTestCase):
         with mock.patch.object(self.pci_wrapper,
                                "_as_root") as mock_as_root:
             mock_as_root.side_effect = Exception()
-            self.assertRaises(exc.IpCommandError,
+            self.assertRaises(exc.IpCommandDeviceError,
                               self.pci_wrapper.set_vf_state,
                               self.VF_INDEX,
                               True)
@@ -111,7 +117,7 @@ class TestPciLib(base.BaseTestCase):
         with mock.patch.object(self.pci_wrapper,
                                "_execute") as mock_exec:
             mock_exec.side_effect = Exception()
-            self.assertRaises(exc.IpCommandError,
+            self.assertRaises(exc.IpCommandDeviceError,
                               self.pci_wrapper.set_vf_spoofcheck,
                               self.VF_INDEX,
                               True)
@@ -128,7 +134,7 @@ class TestPciLib(base.BaseTestCase):
         with mock.patch.object(self.pci_wrapper,
                                "_execute") as mock_exec:
             mock_exec.side_effect = Exception()
-            self.assertRaises(exc.IpCommandError,
+            self.assertRaises(exc.IpCommandDeviceError,
                               self.pci_wrapper.set_vf_max_rate,
                               self.VF_INDEX,
                               1000)
@@ -142,3 +148,25 @@ class TestPciLib(base.BaseTestCase):
                               self.pci_wrapper.set_vf_state,
                               self.VF_INDEX,
                               state=True)
+
+    def test_is_macvtap_assigned(self):
+        with mock.patch.object(pci_lib.PciDeviceIPWrapper,
+                               "_execute") as mock_exec:
+            mock_exec.return_value = self.IP_LINK_SHOW_WITH_MACVTAP
+            self.assertTrue(
+                pci_lib.PciDeviceIPWrapper.is_macvtap_assigned('enp129s0f1'))
+
+    def test_is_macvtap_assigned_not_assigned(self):
+        with mock.patch.object(pci_lib.PciDeviceIPWrapper,
+                               "_execute") as mock_exec:
+            mock_exec.return_value = self.IP_LINK_SHOW_WITH_MACVTAP
+            self.assertFalse(
+                pci_lib.PciDeviceIPWrapper.is_macvtap_assigned('enp129s0f2'))
+
+    def test_is_macvtap_assigned_failed(self):
+        with mock.patch.object(pci_lib.PciDeviceIPWrapper,
+                               "_execute") as mock_exec:
+            mock_exec.side_effect = Exception()
+            self.assertRaises(exc.IpCommandError,
+                              pci_lib.PciDeviceIPWrapper.is_macvtap_assigned,
+                              'enp129s0f3')
diff --git a/releasenotes/notes/macvtap_assigned_vf_check-f4d07660ffd82a24.yaml b/releasenotes/notes/macvtap_assigned_vf_check-f4d07660ffd82a24.yaml
new file mode 100644 (file)
index 0000000..c74a571
--- /dev/null
@@ -0,0 +1,3 @@
+---
+fixes:
+  - Fix SR-IOV agent macvtap assigned VF check when linux kernel < 3.13