Improve OVS Representor Lookup
This patch set addresses some of the concerns outlined in: https://bugs.launchpad.net/os-vif/+bug/1704485 * Determining the PF switchdev from the VF PCI address: /sys/bus/pci/devices/<PCI_ADDR>/physfn/net/* is scanned and the _last_ netdev is assumed to be the PF switchdev netdev. It's more general to assume that there might be multiple netdevs, and one would be the desired switchdev. This change iterates through all the netdevs and pick the first with a valid phys_switch_id. (A sysfs file that returns a non-null ID.) * Note: in stead of returning the _last_ netdev in the physfn directory, the behaviour now returns the first netdev that has a valid switchdev id. * Added test cases to verify the above behaviour. Change-Id: Ia8d4609746df38c7072e535fb92b00b903d05042 Partial-Bug: #1704485 Signed-off-by: Jan Gutter <jan.gutter@netronome.com>
This commit is contained in:
parent
4a8e07f6c5
commit
c9facf2dd3
|
@ -293,18 +293,41 @@ def _get_sysfs_netdev_path(pci_addr, pf_interface):
|
|||
return "/sys/bus/pci/devices/%s/net" % (pci_addr)
|
||||
|
||||
|
||||
def get_ifname_by_pci_address(pci_addr, pf_interface=False):
|
||||
def _is_switchdev(netdev):
|
||||
"""Returns True if a netdev has a readable phys_switch_id"""
|
||||
try:
|
||||
sw_id_file = "/sys/class/net/%s/phys_switch_id" % netdev
|
||||
with open(sw_id_file, 'r') as fd:
|
||||
phys_switch_id = fd.readline().rstrip()
|
||||
if phys_switch_id != "" and phys_switch_id is not None:
|
||||
return True
|
||||
except (OSError, IOError):
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
def get_ifname_by_pci_address(pci_addr, pf_interface=False, switchdev=False):
|
||||
"""Get the interface name based on a VF's pci address
|
||||
|
||||
:param pci_addr: the PCI address of the VF
|
||||
:param pf_interface: if True, look for the netdev of the parent PF
|
||||
:param switchdev: if True, ensure that phys_switch_id is valid
|
||||
|
||||
:returns: netdev interface name
|
||||
|
||||
The returned interface name is either the parent PF or that of the VF
|
||||
itself based on the argument of pf_interface.
|
||||
"""
|
||||
dev_path = _get_sysfs_netdev_path(pci_addr, pf_interface)
|
||||
# make the if statement later more readable
|
||||
ignore_switchdev = not switchdev
|
||||
try:
|
||||
dev_info = os.listdir(dev_path)
|
||||
return dev_info.pop()
|
||||
for netdev in os.listdir(dev_path):
|
||||
if ignore_switchdev or _is_switchdev(netdev):
|
||||
return netdev
|
||||
except Exception:
|
||||
raise exception.PciDeviceNotFoundById(id=pci_addr)
|
||||
raise exception.PciDeviceNotFoundById(id=pci_addr)
|
||||
|
||||
|
||||
def get_vf_num_by_pci_address(pci_addr):
|
||||
|
|
|
@ -160,7 +160,7 @@ class OvsPlugin(plugin.PluginBase):
|
|||
vif.network.bridge, constants.OVS_DATAPATH_SYSTEM)
|
||||
pci_slot = vif.dev_address
|
||||
pf_ifname = linux_net.get_ifname_by_pci_address(
|
||||
pci_slot, pf_interface=True)
|
||||
pci_slot, pf_interface=True, switchdev=True)
|
||||
vf_num = linux_net.get_vf_num_by_pci_address(pci_slot)
|
||||
representor = linux_net.get_representor_port(pf_ifname, vf_num)
|
||||
linux_net.set_interface_state(representor, 'up')
|
||||
|
@ -221,7 +221,7 @@ class OvsPlugin(plugin.PluginBase):
|
|||
"""Remove port from OVS."""
|
||||
pci_slot = vif.dev_address
|
||||
pf_ifname = linux_net.get_ifname_by_pci_address(pci_slot,
|
||||
pf_interface=True)
|
||||
pf_interface=True, switchdev=True)
|
||||
vf_num = linux_net.get_vf_num_by_pci_address(pci_slot)
|
||||
representor = linux_net.get_representor_port(pf_ifname, vf_num)
|
||||
linux_net.delete_ovs_vif_port(vif.network.bridge, representor)
|
||||
|
|
|
@ -291,6 +291,49 @@ class LinuxNetTest(testtools.TestCase):
|
|||
mock_vsctl.assert_called_with(args, timeout=timeout)
|
||||
self.assertTrue(result)
|
||||
|
||||
@mock.patch('six.moves.builtins.open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_is_switchdev_ioerror(self, mock_isfile, mock_open):
|
||||
mock_isfile.side_effect = [True]
|
||||
mock_open.return_value.__enter__ = lambda s: s
|
||||
readline_mock = mock_open.return_value.readline
|
||||
readline_mock.side_effect = (
|
||||
[IOError()])
|
||||
test_switchdev = linux_net._is_switchdev('pf_ifname')
|
||||
self.assertEqual(test_switchdev, False)
|
||||
|
||||
@mock.patch('six.moves.builtins.open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_is_switchdev_empty(self, mock_isfile, mock_open):
|
||||
mock_isfile.side_effect = [True]
|
||||
mock_open.return_value.__enter__ = lambda s: s
|
||||
readline_mock = mock_open.return_value.readline
|
||||
readline_mock.side_effect = (
|
||||
[''])
|
||||
open_calls = (
|
||||
[mock.call('/sys/class/net/pf_ifname/phys_switch_id', 'r'),
|
||||
mock.call().readline(),
|
||||
mock.call().__exit__(None, None, None)])
|
||||
test_switchdev = linux_net._is_switchdev('pf_ifname')
|
||||
mock_open.assert_has_calls(open_calls)
|
||||
self.assertEqual(test_switchdev, False)
|
||||
|
||||
@mock.patch('six.moves.builtins.open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
def test_is_switchdev_positive(self, mock_isfile, mock_open):
|
||||
mock_isfile.side_effect = [True]
|
||||
mock_open.return_value.__enter__ = lambda s: s
|
||||
readline_mock = mock_open.return_value.readline
|
||||
readline_mock.side_effect = (
|
||||
['pf_sw_id'])
|
||||
open_calls = (
|
||||
[mock.call('/sys/class/net/pf_ifname/phys_switch_id', 'r'),
|
||||
mock.call().readline(),
|
||||
mock.call().__exit__(None, None, None)])
|
||||
test_switchdev = linux_net._is_switchdev('pf_ifname')
|
||||
mock_open.assert_has_calls(open_calls)
|
||||
self.assertEqual(test_switchdev, True)
|
||||
|
||||
@mock.patch('six.moves.builtins.open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
@mock.patch.object(os, 'listdir')
|
||||
|
@ -394,18 +437,34 @@ class LinuxNetTest(testtools.TestCase):
|
|||
linux_net.get_representor_port,
|
||||
'pf_ifname', '3')
|
||||
|
||||
@mock.patch('six.moves.builtins.open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
@mock.patch.object(os, 'listdir')
|
||||
def test_physical_function_inferface_name(self, mock_listdir):
|
||||
def test_physical_function_inferface_name(
|
||||
self, mock_listdir, mock_isfile, mock_open):
|
||||
mock_listdir.return_value = ['foo', 'bar']
|
||||
mock_isfile.side_effect = [True, True]
|
||||
mock_open.return_value.__enter__ = lambda s: s
|
||||
readline_mock = mock_open.return_value.readline
|
||||
readline_mock.side_effect = (
|
||||
['', 'valid_switch'])
|
||||
ifname = linux_net.get_ifname_by_pci_address(
|
||||
'0000:00:00.1', pf_interface=True)
|
||||
self.assertEqual(ifname, 'bar')
|
||||
'0000:00:00.1', pf_interface=True, switchdev=False)
|
||||
self.assertEqual(ifname, 'foo')
|
||||
|
||||
@mock.patch('six.moves.builtins.open')
|
||||
@mock.patch.object(os.path, 'isfile')
|
||||
@mock.patch.object(os, 'listdir')
|
||||
def test_virtual_function_inferface_name(self, mock_listdir):
|
||||
def test_physical_function_inferface_name_with_switchdev(
|
||||
self, mock_listdir, mock_isfile, mock_open):
|
||||
mock_listdir.return_value = ['foo', 'bar']
|
||||
mock_isfile.side_effect = [True, True]
|
||||
mock_open.return_value.__enter__ = lambda s: s
|
||||
readline_mock = mock_open.return_value.readline
|
||||
readline_mock.side_effect = (
|
||||
['', 'valid_switch'])
|
||||
ifname = linux_net.get_ifname_by_pci_address(
|
||||
'0000:00:00.1', pf_interface=False)
|
||||
'0000:00:00.1', pf_interface=True, switchdev=True)
|
||||
self.assertEqual(ifname, 'bar')
|
||||
|
||||
@mock.patch.object(os, 'listdir')
|
||||
|
|
|
@ -345,7 +345,8 @@ class PluginTest(testtools.TestCase):
|
|||
'ensure_ovs_bridge': [mock.call('br0',
|
||||
constants.OVS_DATAPATH_SYSTEM)],
|
||||
'get_ifname_by_pci_address': [mock.call('0002:24:12.3',
|
||||
pf_interface=True)],
|
||||
pf_interface=True,
|
||||
switchdev=True)],
|
||||
'get_vf_num_by_pci_address': [mock.call('0002:24:12.3')],
|
||||
'get_representor_port': [mock.call('eth0', '2')],
|
||||
'set_interface_state': [mock.call('eth0_2', 'up')],
|
||||
|
@ -379,7 +380,8 @@ class PluginTest(testtools.TestCase):
|
|||
calls = {
|
||||
|
||||
'get_ifname_by_pci_address': [mock.call('0002:24:12.3',
|
||||
pf_interface=True)],
|
||||
pf_interface=True,
|
||||
switchdev=True)],
|
||||
'get_vf_num_by_pci_address': [mock.call('0002:24:12.3')],
|
||||
'get_representor_port': [mock.call('eth0', '2')],
|
||||
'set_interface_state': [mock.call('eth0_2', 'down')],
|
||||
|
|
Loading…
Reference in New Issue