diff --git a/nova/pci/utils.py b/nova/pci/utils.py index e10a303156..b11ce064b7 100644 --- a/nova/pci/utils.py +++ b/nova/pci/utils.py @@ -23,6 +23,7 @@ from oslo_log import log as logging import six from nova import exception +from nova.i18n import _LW LOG = logging.getLogger(__name__) @@ -105,16 +106,23 @@ def is_physical_function(domain, bus, slot, function): return False +def _get_sysfs_netdev_path(pci_addr, pf_interface): + """Get the sysfs path based on the PCI address of the device. + + Assumes a networking device - will not check for the existence of the path. + """ + if pf_interface: + return "/sys/bus/pci/devices/%s/physfn/net" % (pci_addr) + return "/sys/bus/pci/devices/%s/net" % (pci_addr) + + def get_ifname_by_pci_address(pci_addr, pf_interface=False): """Get the interface name based on a VF's pci address The returned interface name is either the parent PF's or that of the VF itself based on the argument of pf_interface. """ - if pf_interface: - dev_path = "/sys/bus/pci/devices/%s/physfn/net" % (pci_addr) - else: - dev_path = "/sys/bus/pci/devices/%s/net" % (pci_addr) + dev_path = _get_sysfs_netdev_path(pci_addr, pf_interface) try: dev_info = os.listdir(dev_path) return dev_info.pop() @@ -122,6 +130,27 @@ def get_ifname_by_pci_address(pci_addr, pf_interface=False): raise exception.PciDeviceNotFoundById(id=pci_addr) +def get_mac_by_pci_address(pci_addr, pf_interface=False): + """Get the MAC address of the nic based on it's PCI address + + Raises PciDeviceNotFoundById in case the pci device is not a NIC + """ + dev_path = _get_sysfs_netdev_path(pci_addr, pf_interface) + if_name = get_ifname_by_pci_address(pci_addr, pf_interface) + addr_file = os.path.join(dev_path, if_name, 'address') + + try: + with open(addr_file) as f: + mac = next(f).strip() + return mac + except (IOError, StopIteration) as e: + LOG.warning(_LW("Could not find the expected sysfs file for " + "determining the MAC address of the PCI device " + "%(addr)s. May not be a NIC. Error: %(e)s"), + {'addr': pci_addr, 'e': e}) + raise exception.PciDeviceNotFoundById(id=pci_addr) + + def get_vf_num_by_pci_address(pci_addr): """Get the VF number based on a VF's pci address diff --git a/nova/tests/unit/pci/test_utils.py b/nova/tests/unit/pci/test_utils.py index 109f47eb4c..2bded05933 100644 --- a/nova/tests/unit/pci/test_utils.py +++ b/nova/tests/unit/pci/test_utils.py @@ -17,6 +17,7 @@ import glob import os +import fixtures import mock from six.moves import builtins @@ -149,6 +150,60 @@ class GetIfnameByPciAddressTestCase(test.NoDBTestCase): ) +class GetMacByPciAddressTestCase(test.NoDBTestCase): + def setUp(self): + super(GetMacByPciAddressTestCase, self).setUp() + self.pci_address = '0000:07:00.1' + self.if_name = 'enp7s0f1' + self.tmpdir = self.useFixture(fixtures.TempDir()) + self.fake_file = os.path.join(self.tmpdir.path, "address") + with open(self.fake_file, "w") as f: + f.write("a0:36:9f:72:00:00\n") + + @mock.patch.object(os, 'listdir') + @mock.patch.object(os.path, 'join') + def test_get_mac(self, mock_join, mock_listdir): + mock_listdir.return_value = [self.if_name] + mock_join.return_value = self.fake_file + mac = utils.get_mac_by_pci_address(self.pci_address) + mock_join.assert_called_once_with( + "/sys/bus/pci/devices/%s/net" % self.pci_address, self.if_name, + "address") + self.assertEqual("a0:36:9f:72:00:00", mac) + + @mock.patch.object(os, 'listdir') + @mock.patch.object(os.path, 'join') + def test_get_mac_fails(self, mock_join, mock_listdir): + os.unlink(self.fake_file) + mock_listdir.return_value = [self.if_name] + mock_join.return_value = self.fake_file + self.assertRaises( + exception.PciDeviceNotFoundById, + utils.get_mac_by_pci_address, self.pci_address) + + @mock.patch.object(os, 'listdir') + @mock.patch.object(os.path, 'join') + def test_get_mac_fails_empty(self, mock_join, mock_listdir): + with open(self.fake_file, "w") as f: + f.truncate(0) + mock_listdir.return_value = [self.if_name] + mock_join.return_value = self.fake_file + self.assertRaises( + exception.PciDeviceNotFoundById, + utils.get_mac_by_pci_address, self.pci_address) + + @mock.patch.object(os, 'listdir') + @mock.patch.object(os.path, 'join') + def test_get_physical_function_mac(self, mock_join, mock_listdir): + mock_listdir.return_value = [self.if_name] + mock_join.return_value = self.fake_file + mac = utils.get_mac_by_pci_address(self.pci_address, pf_interface=True) + mock_join.assert_called_once_with( + "/sys/bus/pci/devices/%s/physfn/net" % self.pci_address, + self.if_name, "address") + self.assertEqual("a0:36:9f:72:00:00", mac) + + class GetVfNumByPciAddressTestCase(test.NoDBTestCase): def setUp(self):