SR-IOV: Fix macvtap assigned vf check when kernel < 3.13
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
This commit is contained in:
parent
08bd35fa6f
commit
d760416998
@ -26,7 +26,7 @@ class InvalidDeviceError(SriovNicError):
|
|||||||
|
|
||||||
|
|
||||||
class IpCommandError(SriovNicError):
|
class IpCommandError(SriovNicError):
|
||||||
message = _("ip command failed on device %(dev_name)s: %(reason)s")
|
message = _("ip command failed: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
class IpCommandOperationNotSupportedError(SriovNicError):
|
class IpCommandOperationNotSupportedError(SriovNicError):
|
||||||
@ -35,3 +35,7 @@ class IpCommandOperationNotSupportedError(SriovNicError):
|
|||||||
|
|
||||||
class InvalidPciSlotError(SriovNicError):
|
class InvalidPciSlotError(SriovNicError):
|
||||||
message = _("Invalid pci slot %(pci_slot)s")
|
message = _("Invalid pci slot %(pci_slot)s")
|
||||||
|
|
||||||
|
|
||||||
|
class IpCommandDeviceError(SriovNicError):
|
||||||
|
message = _("ip command failed on device %(dev_name)s: %(reason)s")
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import glob
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@ -36,7 +35,6 @@ class PciOsWrapper(object):
|
|||||||
PCI_PATH = "/sys/class/net/%s/device/virtfn%s/net"
|
PCI_PATH = "/sys/class/net/%s/device/virtfn%s/net"
|
||||||
VIRTFN_FORMAT = r"^virtfn(?P<vf_index>\d+)"
|
VIRTFN_FORMAT = r"^virtfn(?P<vf_index>\d+)"
|
||||||
VIRTFN_REG_EX = re.compile(VIRTFN_FORMAT)
|
VIRTFN_REG_EX = re.compile(VIRTFN_FORMAT)
|
||||||
MAC_VTAP_PREFIX = "upper_macvtap*"
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def scan_vf_devices(cls, dev_name):
|
def scan_vf_devices(cls, dev_name):
|
||||||
@ -75,15 +73,25 @@ class PciOsWrapper(object):
|
|||||||
by checking the relevant path in the system:
|
by checking the relevant path in the system:
|
||||||
VF is assigned if:
|
VF is assigned if:
|
||||||
Direct VF: PCI_PATH does not exist.
|
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 dev_name: pf network device name
|
||||||
@param vf_index: vf index
|
@param vf_index: vf index
|
||||||
"""
|
"""
|
||||||
path = cls.PCI_PATH % (dev_name, 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
|
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):
|
class EmbSwitch(object):
|
||||||
|
@ -34,9 +34,11 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper):
|
|||||||
MAC_PATTERN = r"MAC\s+(?P<mac>[a-fA-F0-9:]+),"
|
MAC_PATTERN = r"MAC\s+(?P<mac>[a-fA-F0-9:]+),"
|
||||||
STATE_PATTERN = r"\s+link-state\s+(?P<state>\w+)"
|
STATE_PATTERN = r"\s+link-state\s+(?P<state>\w+)"
|
||||||
ANY_PATTERN = ".*,"
|
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_LINE_FORMAT = VF_PATTERN + MAC_PATTERN + ANY_PATTERN + STATE_PATTERN
|
||||||
VF_DETAILS_REG_EX = re.compile(VF_LINE_FORMAT)
|
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'
|
IP_LINK_OP_NOT_SUPPORTED = 'RTNETLINK answers: Operation not supported'
|
||||||
|
|
||||||
@ -68,7 +70,7 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper):
|
|||||||
raise exc.IpCommandOperationNotSupportedError(
|
raise exc.IpCommandOperationNotSupportedError(
|
||||||
dev_name=self.dev_name)
|
dev_name=self.dev_name)
|
||||||
else:
|
else:
|
||||||
raise exc.IpCommandError(dev_name=self.dev_name,
|
raise exc.IpCommandDeviceError(dev_name=self.dev_name,
|
||||||
reason=str(e))
|
reason=str(e))
|
||||||
|
|
||||||
def get_assigned_macs(self, vf_list):
|
def get_assigned_macs(self, vf_list):
|
||||||
@ -81,7 +83,7 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper):
|
|||||||
out = self._as_root([], "link", ("show", self.dev_name))
|
out = self._as_root([], "link", ("show", self.dev_name))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception(_LE("Failed executing ip command"))
|
LOG.exception(_LE("Failed executing ip command"))
|
||||||
raise exc.IpCommandError(dev_name=self.dev_name,
|
raise exc.IpCommandDeviceError(dev_name=self.dev_name,
|
||||||
reason=e)
|
reason=e)
|
||||||
vf_to_mac_mapping = {}
|
vf_to_mac_mapping = {}
|
||||||
vf_lines = self._get_vf_link_show(vf_list, out)
|
vf_lines = self._get_vf_link_show(vf_list, out)
|
||||||
@ -104,7 +106,7 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper):
|
|||||||
out = self._as_root([], "link", ("show", self.dev_name))
|
out = self._as_root([], "link", ("show", self.dev_name))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.exception(_LE("Failed executing ip command"))
|
LOG.exception(_LE("Failed executing ip command"))
|
||||||
raise exc.IpCommandError(dev_name=self.dev_name,
|
raise exc.IpCommandDeviceError(dev_name=self.dev_name,
|
||||||
reason=e)
|
reason=e)
|
||||||
vf_lines = self._get_vf_link_show([vf_index], out)
|
vf_lines = self._get_vf_link_show([vf_index], out)
|
||||||
if vf_lines:
|
if vf_lines:
|
||||||
@ -181,3 +183,26 @@ class PciDeviceIPWrapper(ip_lib.IPWrapper):
|
|||||||
"for %(device)s"),
|
"for %(device)s"),
|
||||||
{'line': vf_line, 'device': self.dev_name})
|
{'line': vf_line, 'device': self.dev_name})
|
||||||
return vf_details
|
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
|
||||||
|
@ -19,7 +19,6 @@ import os
|
|||||||
import mock
|
import mock
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
|
|
||||||
from neutron.plugins.ml2.drivers.mech_sriov.agent.common \
|
from neutron.plugins.ml2.drivers.mech_sriov.agent.common \
|
||||||
import exceptions as exc
|
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 eswitch_manager as esm
|
||||||
@ -457,31 +456,30 @@ class TestPciOsWrapper(base.BaseTestCase):
|
|||||||
esm.PciOsWrapper.scan_vf_devices,
|
esm.PciOsWrapper.scan_vf_devices,
|
||||||
self.DEV_NAME)
|
self.DEV_NAME)
|
||||||
|
|
||||||
def _mock_assign_vf(self, dir_exists):
|
@mock.patch("os.listdir", side_effect=OSError())
|
||||||
with mock.patch("os.path.isdir",
|
def test_is_assigned_vf_true(self, *args):
|
||||||
return_value=dir_exists):
|
self.assertTrue(esm.PciOsWrapper.is_assigned_vf(
|
||||||
result = esm.PciOsWrapper.is_assigned_vf(self.DEV_NAME,
|
self.DEV_NAME, self.VF_INDEX))
|
||||||
self.VF_INDEX)
|
|
||||||
self.assertEqual(not dir_exists, result)
|
|
||||||
|
|
||||||
def test_is_assigned_vf_true(self):
|
@mock.patch("os.listdir", return_value=[DEV_NAME, "eth1"])
|
||||||
self._mock_assign_vf(True)
|
@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_false(self):
|
@mock.patch("os.listdir", return_value=["eth0", "eth1"])
|
||||||
self._mock_assign_vf(False)
|
@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 _mock_assign_vf_macvtap(self, macvtap_exists):
|
@mock.patch("os.listdir", side_effect=OSError())
|
||||||
def _glob(file_path):
|
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
|
||||||
return ["upper_macvtap0"] if macvtap_exists else []
|
"PciDeviceIPWrapper.is_macvtap_assigned")
|
||||||
|
def test_is_assigned_vf_macvtap_failure(
|
||||||
with mock.patch("os.path.isdir", return_value=True),\
|
self, mock_is_macvtap_assigned, *args):
|
||||||
mock.patch("glob.glob", side_effect=_glob):
|
esm.PciOsWrapper.is_assigned_vf(self.DEV_NAME, self.VF_INDEX)
|
||||||
result = esm.PciOsWrapper.is_assigned_vf(self.DEV_NAME,
|
self.assertFalse(mock_is_macvtap_assigned.called)
|
||||||
self.VF_INDEX)
|
|
||||||
self.assertEqual(macvtap_exists, result)
|
|
||||||
|
|
||||||
def test_is_assigned_vf_macvtap_true(self):
|
|
||||||
self._mock_assign_vf_macvtap(True)
|
|
||||||
|
|
||||||
def test_is_assigned_vf_macvtap_false(self):
|
|
||||||
self._mock_assign_vf_macvtap(False)
|
|
||||||
|
@ -37,6 +37,12 @@ class TestPciLib(base.BaseTestCase):
|
|||||||
' checking off, link-state enable')
|
' checking off, link-state enable')
|
||||||
VF_LINK_SHOW = '\n'.join((PF_LINK_SHOW, PF_MAC, VF_0_LINK_SHOW,
|
VF_LINK_SHOW = '\n'.join((PF_LINK_SHOW, PF_MAC, VF_0_LINK_SHOW,
|
||||||
VF_1_LINK_SHOW, VF_2_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 = {
|
MAC_MAPPING = {
|
||||||
0: "fa:16:3e:b4:81:ac",
|
0: "fa:16:3e:b4:81:ac",
|
||||||
@ -60,7 +66,7 @@ class TestPciLib(base.BaseTestCase):
|
|||||||
with mock.patch.object(self.pci_wrapper,
|
with mock.patch.object(self.pci_wrapper,
|
||||||
"_as_root") as mock_as_root:
|
"_as_root") as mock_as_root:
|
||||||
mock_as_root.side_effect = Exception()
|
mock_as_root.side_effect = Exception()
|
||||||
self.assertRaises(exc.IpCommandError,
|
self.assertRaises(exc.IpCommandDeviceError,
|
||||||
self.pci_wrapper.get_assigned_macs,
|
self.pci_wrapper.get_assigned_macs,
|
||||||
[self.VF_INDEX])
|
[self.VF_INDEX])
|
||||||
|
|
||||||
@ -82,7 +88,7 @@ class TestPciLib(base.BaseTestCase):
|
|||||||
with mock.patch.object(self.pci_wrapper,
|
with mock.patch.object(self.pci_wrapper,
|
||||||
"_as_root") as mock_as_root:
|
"_as_root") as mock_as_root:
|
||||||
mock_as_root.side_effect = Exception()
|
mock_as_root.side_effect = Exception()
|
||||||
self.assertRaises(exc.IpCommandError,
|
self.assertRaises(exc.IpCommandDeviceError,
|
||||||
self.pci_wrapper.get_vf_state,
|
self.pci_wrapper.get_vf_state,
|
||||||
self.VF_INDEX)
|
self.VF_INDEX)
|
||||||
|
|
||||||
@ -96,7 +102,7 @@ class TestPciLib(base.BaseTestCase):
|
|||||||
with mock.patch.object(self.pci_wrapper,
|
with mock.patch.object(self.pci_wrapper,
|
||||||
"_as_root") as mock_as_root:
|
"_as_root") as mock_as_root:
|
||||||
mock_as_root.side_effect = Exception()
|
mock_as_root.side_effect = Exception()
|
||||||
self.assertRaises(exc.IpCommandError,
|
self.assertRaises(exc.IpCommandDeviceError,
|
||||||
self.pci_wrapper.set_vf_state,
|
self.pci_wrapper.set_vf_state,
|
||||||
self.VF_INDEX,
|
self.VF_INDEX,
|
||||||
True)
|
True)
|
||||||
@ -111,7 +117,7 @@ class TestPciLib(base.BaseTestCase):
|
|||||||
with mock.patch.object(self.pci_wrapper,
|
with mock.patch.object(self.pci_wrapper,
|
||||||
"_execute") as mock_exec:
|
"_execute") as mock_exec:
|
||||||
mock_exec.side_effect = Exception()
|
mock_exec.side_effect = Exception()
|
||||||
self.assertRaises(exc.IpCommandError,
|
self.assertRaises(exc.IpCommandDeviceError,
|
||||||
self.pci_wrapper.set_vf_spoofcheck,
|
self.pci_wrapper.set_vf_spoofcheck,
|
||||||
self.VF_INDEX,
|
self.VF_INDEX,
|
||||||
True)
|
True)
|
||||||
@ -128,7 +134,7 @@ class TestPciLib(base.BaseTestCase):
|
|||||||
with mock.patch.object(self.pci_wrapper,
|
with mock.patch.object(self.pci_wrapper,
|
||||||
"_execute") as mock_exec:
|
"_execute") as mock_exec:
|
||||||
mock_exec.side_effect = Exception()
|
mock_exec.side_effect = Exception()
|
||||||
self.assertRaises(exc.IpCommandError,
|
self.assertRaises(exc.IpCommandDeviceError,
|
||||||
self.pci_wrapper.set_vf_max_rate,
|
self.pci_wrapper.set_vf_max_rate,
|
||||||
self.VF_INDEX,
|
self.VF_INDEX,
|
||||||
1000)
|
1000)
|
||||||
@ -142,3 +148,25 @@ class TestPciLib(base.BaseTestCase):
|
|||||||
self.pci_wrapper.set_vf_state,
|
self.pci_wrapper.set_vf_state,
|
||||||
self.VF_INDEX,
|
self.VF_INDEX,
|
||||||
state=True)
|
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')
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- Fix SR-IOV agent macvtap assigned VF check when linux kernel < 3.13
|
Loading…
Reference in New Issue
Block a user