[PATCH v5] dts: report dut/NIC info during DTS run
Koushik Bhargav Nimoji
knimoji at iol.unh.edu
Thu Jun 25 17:47:12 CEST 2026
This patch gathers NIC info during a DTS run and writes it to an output
json file. This allows the json file to be used when reporting results
on the DTS results dashboard.
Signed-off-by: Koushik Bhargav Nimoji <knimoji at iol.unh.edu>
---
v2:
*Resolved merge conflicts
v3:
*Fixed an issue with retrieving
the NIC's hardware version
v4:
*Moved nic info gathering step before the nics get
binded to their respective drivers
*Condensed some areas of code in order to make them
more readable
*Removed redundant None checks and added some where
required
*Fixed LshwOutput class to better reflect the lshw
command output
v5:
*Changed variable names for code readability
---
dts/framework/test_run.py | 10 +++
dts/framework/testbed_model/linux_session.py | 68 ++++++++++++++++++++
dts/framework/testbed_model/os_session.py | 11 ++++
3 files changed, 89 insertions(+)
diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py
index 94dc6023a7..fea1b52e44 100644
--- a/dts/framework/test_run.py
+++ b/dts/framework/test_run.py
@@ -98,6 +98,7 @@
"InternalError" -> "exit":ew
"""
+import json
import random
from collections import deque
from collections.abc import Iterable
@@ -347,6 +348,14 @@ def next(self) -> State | None:
test_run.ctx.dpdk.setup()
test_run.ctx.topology.setup()
+ testrun_nic_info: list[dict[str, str]] = (
+ self.test_run.ctx.sut_node.main_session.get_nic_info()
+ )
+ with open(f"{SETTINGS.output_dir}/dut_info.json", "w") as file:
+ json.dump(testrun_nic_info, file, indent=3)
+
+ self.logger.info(f"DUT NIC info written to: {SETTINGS.output_dir}/dut_info.json")
+
if test_run.config.use_virtual_functions:
test_run.ctx.topology.instantiate_vf_ports()
if test_run.ctx.sut_node.cryptodevs and test_run.config.crypto:
@@ -370,6 +379,7 @@ def next(self) -> State | None:
test_run.supported_capabilities = get_supported_capabilities(
test_run.ctx.sut_node, test_run.ctx.topology, test_run.required_capabilities
)
+
return TestRunExecution(test_run, self.result)
def on_error(self, ex: BaseException) -> State | None:
diff --git a/dts/framework/testbed_model/linux_session.py b/dts/framework/testbed_model/linux_session.py
index 3a6e97974b..b8836effbe 100644
--- a/dts/framework/testbed_model/linux_session.py
+++ b/dts/framework/testbed_model/linux_session.py
@@ -38,6 +38,8 @@ class LshwConfigurationOutput(TypedDict):
driver: str
#:
link: str
+ #:
+ firmware: str
class LshwOutput(TypedDict):
@@ -61,6 +63,12 @@ class LshwOutput(TypedDict):
...
"""
+ #:
+ vendor: NotRequired[str]
+ #:
+ product: NotRequired[str]
+ #:
+ version: NotRequired[str]
#:
businfo: str
#:
@@ -197,6 +205,66 @@ def unbind_ports(self, ports: list[Port]):
if self._lshw_net_info:
del self._lshw_net_info
+ def get_nic_info(self) -> list[dict[str, str]]:
+ """Overrides :meth`~.os_session.OSSession.get_nic_info`.
+
+ Raises:
+ ConfigurationError: If the NIC info could not be found.
+ """
+ port_data = {
+ port.get("businfo"): port for port in self._lshw_net_info if port.get("businfo")
+ }
+
+ all_nic_info: list[dict[str, str]] = []
+ for port in self._config.ports:
+ pci_addr = port.pci
+
+ lshw_result = self.send_command(
+ f"sudo lshw -c network -businfo | grep '{pci_addr}' | cut -d'@' -f1"
+ )
+ if lshw_result.return_code != 0 and lshw_result.stdout == "":
+ raise ConfigurationError(f"Unable to get bus type for port {pci_addr}.")
+ bus_type = lshw_result.stdout
+
+ bus_info = f"{bus_type}@{pci_addr}"
+ nic_port: LshwOutput | None = port_data[bus_info]
+ if nic_port is None:
+ raise ConfigurationError(f"Port {pci_addr} could not be found on the node.")
+
+ config: LshwConfigurationOutput | None = nic_port["configuration"]
+ if config is None:
+ raise ConfigurationError(
+ f"Configuration info for port {pci_addr} could not be found on the node."
+ )
+
+ if "logicalname" not in nic_port:
+ raise ConfigurationError(
+ f"Logical name for port {pci_addr} could not be found on the node."
+ )
+
+ ethtool_result = self.send_command(
+ f"ethtool {nic_port['logicalname']} | grep 'Speed:' | awk '{{print $2}}'"
+ )
+ if ethtool_result.return_code == 0 and ethtool_result.stdout:
+ nic_speed = ethtool_result.stdout
+ else:
+ self._logger.error(f"Unable to get speed for NIC: {pci_addr}")
+ nic_speed = None
+
+ dut_json = {
+ "make": nic_port["vendor"] if "vendor" in nic_port else "Unknown",
+ "model": nic_port["product"] if "product" in nic_port else "Unknown",
+ "hardware version": nic_port["version"] if "version" in nic_port else "Unknown",
+ "firmware version": config["firmware"] if "firmware" in config else "Unknown",
+ "deviceBusType": bus_type,
+ "deviceId": nic_port["serial"] if "serial" in nic_port else "Unknown",
+ "pmd": config["driver"] if "driver" in config else "Unknown",
+ "speed": nic_speed or "Unknown",
+ }
+ all_nic_info.append(dut_json)
+
+ return all_nic_info
+
def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
"""Overrides :meth:`~.os_session.OSSession.bind_ports_to_driver`.
diff --git a/dts/framework/testbed_model/os_session.py b/dts/framework/testbed_model/os_session.py
index f2dc9b20a9..f88427a53d 100644
--- a/dts/framework/testbed_model/os_session.py
+++ b/dts/framework/testbed_model/os_session.py
@@ -581,6 +581,17 @@ def unbind_ports(self, ports: list[Port]) -> None:
ports: The list of ports to unbind.
"""
+ @abstractmethod
+ def get_nic_info(self) -> list[dict[str, str]]:
+ """Get NIC information.
+
+ Returns:
+ NIC info as a list of dictionaries.
+
+ Raises:
+ ConfigurationError: If the NIC info could not be found.
+ """
+
@abstractmethod
def bind_ports_to_driver(self, ports: list[Port], driver_name: str) -> None:
"""Bind `ports` to the given `driver_name`.
--
2.54.0
More information about the dev
mailing list