Collect bus and driver for interfaces

It's useful to have pci bus address/driver collected, the operator can
use the information to configure portgroup in a consistent way.

Change-Id: I432bca881ad881bae6d5e67c9b6fb52fe55b4e1e
This commit is contained in:
Kaifeng Wang
2025-02-01 14:35:16 +08:00
parent b612bde7fa
commit 96bf1ef012
5 changed files with 104 additions and 3 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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,

View File

@@ -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)

View File

@@ -0,0 +1,4 @@
---
features:
- |
Adds support to collect pci address and driver information for interfaces.