diff --git a/ironic_python_agent/hardware.py b/ironic_python_agent/hardware.py index a03e44d5b..98738b4a8 100644 --- a/ironic_python_agent/hardware.py +++ b/ironic_python_agent/hardware.py @@ -822,11 +822,13 @@ class NetworkInterface(encoding.SerializableComparable): serializable_fields = ('name', 'mac_address', 'ipv4_address', 'ipv6_address', 'has_carrier', 'lldp', 'vendor', 'product', 'client_id', - 'biosdevname', 'speed_mbps') + 'biosdevname', 'speed_mbps', 'pci_address', + 'driver') def __init__(self, name, mac_addr, ipv4_address=None, ipv6_address=None, has_carrier=True, lldp=None, vendor=None, product=None, - client_id=None, biosdevname=None, speed_mbps=None): + client_id=None, biosdevname=None, speed_mbps=None, + pci_address=None, driver=None): self.name = name self.mac_address = mac_addr self.ipv4_address = ipv4_address @@ -837,6 +839,8 @@ class NetworkInterface(encoding.SerializableComparable): self.product = product self.biosdevname = biosdevname self.speed_mbps = speed_mbps + self.pci_address = pci_address + self.driver = driver # client_id is used for InfiniBand only. we calculate the DHCP # client identifier Option to allow DHCP to work over InfiniBand. # see https://tools.ietf.org/html/rfc4390 @@ -1469,7 +1473,10 @@ class GenericHardwareManager(HardwareManager): vendor=_get_device_info(interface_name, 'net', 'vendor'), product=_get_device_info(interface_name, 'net', 'device'), biosdevname=self.get_bios_given_nic_name(interface_name), - speed_mbps=self._get_network_speed(interface_name)) + speed_mbps=self._get_network_speed(interface_name), + pci_address=netutils.get_interface_pci_address(interface_name), + driver=netutils.get_interface_driver(interface_name) + ) def get_ipv4_addr(self, interface_id): return netutils.get_ipv4_addr(interface_id) diff --git a/ironic_python_agent/netutils.py b/ironic_python_agent/netutils.py index 2f39bbaf7..8f401855b 100644 --- a/ironic_python_agent/netutils.py +++ b/ironic_python_agent/netutils.py @@ -280,6 +280,24 @@ def interface_has_carrier(interface_name): return False +def get_interface_pci_address(interface_name): + path = '/sys/class/net/{}/device'.format(interface_name) + try: + return os.path.basename(os.readlink(path)) + except FileNotFoundError: + LOG.debug('No bus address found for interface %s', + interface_name) + + +def get_interface_driver(interface_name): + path = '/sys/class/net/{}/device/driver'.format(interface_name) + try: + return os.path.basename(os.readlink(path)) + except FileNotFoundError: + LOG.debug('No driver found for interface %s', + interface_name) + + def wrap_ipv6(ip): if netutils.is_valid_ipv6(ip): return "[%s]" % ip diff --git a/ironic_python_agent/tests/unit/test_hardware.py b/ironic_python_agent/tests/unit/test_hardware.py index c7e037254..f110e0a81 100644 --- a/ironic_python_agent/tests/unit/test_hardware.py +++ b/ironic_python_agent/tests/unit/test_hardware.py @@ -6597,6 +6597,54 @@ class TestListNetworkInterfaces(base.IronicAgentTest): self.assertTrue(interfaces[0].has_carrier) self.assertEqual('', interfaces[0].biosdevname) + @mock.patch.object(netutils, 'get_interface_driver', autospec=True) + @mock.patch.object(netutils, 'get_interface_pci_address', autospec=True) + def test_list_network_interfaces_with_pci_address(self, + mock_get_pci, + mock_get_driver, + mock_has_carrier, + mocked_execute, + mocked_open, + mocked_exists, + mocked_listdir, + mocked_net_if_addrs, + mockedget_managers, + mocked_lshw): + mocked_listdir.return_value = ['lo', 'eth0'] + mocked_exists.side_effect = [False, False, True] + mocked_open.return_value.__enter__ = lambda s: s + mocked_open.return_value.__exit__ = mock.Mock() + read_mock = mocked_open.return_value.read + read_mock.side_effect = ['1'] + mocked_net_if_addrs.return_value = { + 'lo': [ + FakeAddr(socket.AF_INET, '127.0.0.1'), + FakeAddr(socket.AF_INET6, '::1'), + FakeAddr(socket.AF_PACKET, '00:00:00:00:00:00') + ], + 'eth0': [ + FakeAddr(socket.AF_INET, '192.168.1.2'), + FakeAddr(socket.AF_INET6, 'fd00::101'), + FakeAddr(socket.AF_PACKET, '00:0c:29:8c:11:b1') + ] + } + mocked_execute.return_value = ('em0\n', '') + mock_has_carrier.return_value = True + mock_get_pci.return_value = '0000:02:00.0' + mock_get_driver.return_value = 'e1000e' + interfaces = self.hardware.list_network_interfaces() + self.assertEqual(1, len(interfaces)) + self.assertEqual('eth0', interfaces[0].name) + self.assertEqual('00:0c:29:8c:11:b1', interfaces[0].mac_address) + self.assertEqual('192.168.1.2', interfaces[0].ipv4_address) + self.assertEqual('fd00::101', interfaces[0].ipv6_address) + self.assertIsNone(interfaces[0].lldp) + self.assertTrue(interfaces[0].has_carrier) + self.assertEqual('em0', interfaces[0].biosdevname) + self.assertIsNone(interfaces[0].speed_mbps) + self.assertEqual('0000:02:00.0', interfaces[0].pci_address) + self.assertEqual('e1000e', interfaces[0].driver) + def test_list_network_vlan_interfaces(self, mock_has_carrier, mocked_execute, diff --git a/ironic_python_agent/tests/unit/test_netutils.py b/ironic_python_agent/tests/unit/test_netutils.py index 21bf3a4ab..255cbecb5 100644 --- a/ironic_python_agent/tests/unit/test_netutils.py +++ b/ironic_python_agent/tests/unit/test_netutils.py @@ -394,3 +394,27 @@ class TestNetutils(base.IronicAgentTest): def test_wrap_ipv6_with_ipv4(self): res = netutils.wrap_ipv6('1.2.3.4') self.assertEqual('1.2.3.4', res) + + @mock.patch('os.readlink', autospec=True) + def test_get_interface_pci_address(self, mock_read): + mock_read.return_value = '../../../0000:02:00.0' + addr = netutils.get_interface_pci_address('ens160') + self.assertEqual('0000:02:00.0', addr) + + @mock.patch('os.readlink', autospec=True) + def test_get_interface_pci_address_notfound(self, mock_read): + mock_read.side_effect = FileNotFoundError + addr = netutils.get_interface_pci_address('ens160') + self.assertIsNone(addr) + + @mock.patch('os.readlink', autospec=True) + def test_get_interface_driver(self, mock_read): + mock_read.return_value = '../../../../bus/pci/drivers/e1000e' + addr = netutils.get_interface_driver('ens160') + self.assertEqual('e1000e', addr) + + @mock.patch('os.readlink', autospec=True) + def test_get_interface_driver_notfound(self, mock_read): + mock_read.side_effect = FileNotFoundError + driver = netutils.get_interface_driver('ens160') + self.assertIsNone(driver) diff --git a/releasenotes/notes/report-intf-bus-and-driver-63ed0277b372c1d1.yaml b/releasenotes/notes/report-intf-bus-and-driver-63ed0277b372c1d1.yaml new file mode 100644 index 000000000..ead108a5f --- /dev/null +++ b/releasenotes/notes/report-intf-bus-and-driver-63ed0277b372c1d1.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Adds support to collect pci address and driver information for interfaces.