On Wed, Jun 24, 2026 at 5:33 PM Koushik Bhargav Nimoji <[email protected]> wrote:
> 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 <[email protected]> > --- > 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 > --- > dts/framework/test_run.py | 8 +++ > dts/framework/testbed_model/linux_session.py | 68 ++++++++++++++++++++ > dts/framework/testbed_model/os_session.py | 11 ++++ > 3 files changed, 87 insertions(+) > > diff --git a/dts/framework/test_run.py b/dts/framework/test_run.py > index 94dc6023a7..c92fe90f2e 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,12 @@ def next(self) -> State | None: > test_run.ctx.dpdk.setup() > test_run.ctx.topology.setup() > > + used_nic_info: list[dict[str, str]] = > self.test_run.ctx.sut_node.main_session.get_nic_info() > drop "used" for nic_info or change to testrun_nic_info? > + with open(f"{SETTINGS.output_dir}/dut_info.json", "w") as file: > + json.dump(used_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 +377,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..9e9146c372 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 > + > + command_result = self.send_command( > rename to lshw_result please. > + f"sudo lshw -c network -businfo | grep '{pci_addr}' | cut > -d'@' -f1" > + ) > + if command_result.return_code != 0 and command_result.stdout > == "": > + raise ConfigurationError(f"Unable to get bus type for > port {pci_addr}.") > + bus_type = command_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." > + ) > + > + command_result = self.send_command( > ethtool_result > + f"ethtool {nic_port['logicalname']} | grep 'Speed:' | awk > '{{print $2}}'" > + ) + if command_result.return_code == 0 and command_result.stdout: > + nic_speed = command_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 > + > What is the intended behavior for cryptodev tests? I realize the ports list will be empty and we will not enter the initial loop, but is this intended? Do we want to gether cryptodev info too? > def bind_ports_to_driver(self, ports: list[Port], driver_name: str) > -> None: > > Reviewed-by: Patrick Robb <[email protected]>

