Repository navigation
Conversation
The report commands each build the same ssh command line and call it once per value they need. The hardware reports open one session per device, pipe a collector script to the remote python3, and run many sessions from worker threads, where a password prompt would block a thread for good. run_remote() runs the ssh binary with the options the report commands already use, adds BatchMode=yes, passes text on stdin and bounds the call with a timeout. Tests: the command line, stdin and timeout handling, and that the timeout exception reaches the caller. Part of #2876 and #2877. Assisted-by: Claude:claude-fable-5-2 Signed-off-by: Christian Berendt <berendt@osism.tech>
convert_netbox_interface_to_sonic() reads the interfaces of a device from the thread-local interface cache and fetches them from NetBox on a miss, one request per device. A caller that already loaded the interfaces of many devices with a single query had no way to hand them over. set_device_interfaces() stores such a list, so that later lookups for that device are served from the cache. Tests: a seeded device is served without a NetBox request, the list is copied, and a second call replaces the entry. Part of #2876 and #2877. Assisted-by: Claude:claude-fable-5-2 Signed-off-by: Christian Berendt <berendt@osism.tech>
osism.utils.hardware will hold what the live hardware reports share: device selection, collection over SSH, parsing, interface mapping and rendering. It starts with two helpers used throughout. normalize_mac() brings the spellings found in sysfs, ethtool, LLDP and NetBox into one form and rejects the all-zero address. natural_key() sorts interface names by the value of their numbers, Ethernet2 before Ethernet10. Part of #2876 and #2877. Assisted-by: Claude:claude-fable-5-2 Signed-off-by: Christian Berendt <berendt@osism.tech>
The existing report commands take their hosts from the Ansible inventory, which knows the nodes but not the SONiC switches, and can only be narrowed with an Ansible pattern. select_targets() queries NetBox with the filters given on the command line (site, role, rack, tag and so on, plus a generic KEY=VALUE filter), classifies every device as node or switch, and applies shell-style name patterns. A switch has to match NETBOX_FILTER_CONDUCTOR_SONIC, because those are the switches the operator key is deployed to; others are skipped with a warning unless --include-unmanaged is given. A node is a device whose hostname is in the inventory. Without NetBox the inventory is the only source and NetBox filters are rejected. resolve_addresses() then sets the SSH address and user. A switch is reached at the management IP of its config context, its OOB IP or its primary IP, so it does not need a synced config context. Targets carry the NetBox device as plain data. The collection can then run in threads without sharing the NetBox session. Tests: query building, selection with and without NetBox, the handling of unmanaged switches, address resolution, and the inventory and NetBox failure paths. Part of #2876 and #2877. Assisted-by: Claude:claude-fable-5-2 Signed-off-by: Christian Berendt <berendt@osism.tech>
The hardware reports need many values per device from many devices. One ssh call per value, device after device, as the existing report commands do it, does not scale to that. collect() opens one session per device from a thread pool. The session pipes a stdlib-only collector to python3 on the device, and the collector prints one JSON document of raw command output, sysfs values or Redis dumps. A device that times out, refuses the login or returns garbage is recorded as failed and does not hold up the rest. dump_raw() writes the documents to files, which is how parser fixtures are captured from real devices. The collectors gather and do not parse. This first version covers what both reports need: on Linux the physical ports from sysfs, link state, the permanent MAC and the LLDP neighbours; on SONiC the PORT tables of CONFIG_DB and APPL_DB, the device metadata and the management port. sonic-db-dump is tried as the login user and again with sudo, because the access to Redis differs between releases and was not verified on a switch. Tests: failure handling, parallelism and its limit, the collectors against a fake sysfs tree and a fake command runner. Part of #2876 and #2877. Assisted-by: Claude:claude-fable-5-2 Signed-off-by: Christian Berendt <berendt@osism.tech>
7 tasks
The collectors return raw output. These parsers turn it into the records the reports work with. parse_linux and parse_sonic produce one port record per physical port with name, alias, altnames, admin and oper state, speed, MTU and MAC addresses. A bond member keeps its permanent MAC, since its address is the MAC of the bond while NetBox documents the one burned into the NIC. A port on a USB device, such as the host interface of a BMC, has no PCI address. parse_lldp normalises lldpctl JSON into neighbour records and keeps several neighbours per port. The fixtures are collector documents in the format --dump-raw writes. linux-connectx4.json and sonic-enterprise.json were captured from a node and from a switch running Enterprise SONiC 4.5.2, anonymized and reduced to a selection of ports. linux-mellanox.json and sonic-synthetic.json are synthetic: a node with a bond, an optical module and an empty cage, which were not captured, and the switch it is cabled to. The README next to them says which is which. Tests: the parsers against the fixtures and against malformed input. Part of #2876 and #2877. Assisted-by: Claude:claude-fable-5-2 Signed-off-by: Christian Berendt <berendt@osism.tech>
A port is called ens1f0np0 or Ethernet0 on the device and something else in NetBox. Both reports have to bring the two together, and have to show how they did it, so that a wrong match is visible. fetch_interfaces() loads the interfaces of all devices with a few chunked queries and converts the records to plain data at once, including the far end of the cable path. Reading a missing attribute from a pynetbox record would fetch that record again, once per interface. Switch ports are matched through the HWSKU port config, then by alias, then by name. The translation is the one the config generator uses; the interface cache is seeded from the bulk fetch so that it does not query NetBox per switch. The management port is matched against the mgmt_only interfaces. Node ports are matched by permanent MAC, name, label and finally by their LLDP neighbour. A neighbour is compared by the names it gives its port: the port ID and the port description. RouterOS announces a port that is member of a bridge as bridge/ether9; the name of the port alone is one of its names as well. Tests: conversion of API data including real pynetbox records, the translation against the port config files of the repository, and every mapping method with its precedence. Part of #2876 and #2877. Assisted-by: Claude:claude-fable-5-2 Signed-off-by: Christian Berendt <berendt@osism.tech>
The report commands print a psql table and nothing else. The hardware reports are also meant to be read by scripts and spreadsheets. render provides the --format, --column, --long and --output options and renders flat rows as table or CSV with a selectable set of columns. Table cells are not parsed as numbers, which would turn a serial number such as 1E5 into 100000.0. JSON is rendered from a document the command builds, because it is nested per device. Tests: column selection, flattening, natural sorting, the three formats, and that writing to a file leaves stdout empty. Part of #2876 and #2877. Assisted-by: Claude:claude-fable-5-2 Signed-off-by: Christian Berendt <berendt@osism.tech>
berendt
force-pushed
the
implement/issue-2876-hardware-report-groundwork
branch
from
November 13, 2026 13:08
2442c8e to
2e9df2f
Compare
berendt
marked this pull request as ready for review
November 13, 2026 13:14
|
Sorry @berendt, your pull request is larger than the review limit of 150,000 diff characters |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request adds
osism/utils/hardware/, the groundwork shared byosism report transceiver(#2876) and the cabling check inosism report lldp(#2877). It adds no command and changes no existing one.It is the first of a stack of three. The two commands follow in their own pull requests on top of this branch.
Commits
Add run_remote helper for non-interactive SSH commands:osism/utils/ssh.pygetsrun_remote(), the ssh call the report commands already build, withBatchMode=yes, stdin and a timeout.Let the SONiC interface cache take interfaces fetched in bulk:InterfaceCache.set_device_interfaces(), so thatconvert_netbox_interface_to_sonic()can work on interfaces loaded with one query instead of fetching them per switch.Add the hardware report package with MAC and sort helpers:normalize_mac()andnatural_key().Add device selection for the hardware reports: NetBox filters, name patterns,--type,--limit, classification into nodes and SONiC switches, the handling of switches outsideNETBOX_FILTER_CONDUCTOR_SONIC, the inventory fallback without NetBox, and address resolution.Add parallel SSH collection with Linux and SONiC collectors: one SSH session per device from a thread pool. Each session pipes a stdlib-only collector topython3 -and gets one JSON document of raw data back.dump_raw()writes those documents to files.Add parsers for collected port and LLDP data: port records for nodes and switches, and neighbour records fromlldpctl -f json.Add matching of live ports to NetBox interfaces: bulk fetch of interfaces as plain data including the far end of the cable path, and the mapping methodsport-config,alias,namefor switches andmac,name,label,lldpfor nodes.Add table, CSV and JSON rendering for hardware reports:--format,--column,--long,--output.Verification
The unit tests of this revision run in CI; they were not run locally. Every commit of the stack was checked with
black --checkandflake8, and all test modules import at every commit.mypy --ignore-missing-importsreports nothing in the new modules.The parsers were run against the complete captures before the fixtures were derived from them, and the expectations of the new tests were taken from those runs.
Checked against captures
The collectors ran on three nodes and two SONiC switches of a deployment (#2884): nodes with ConnectX-4 Lx, BCM57414, X710, 82599ES and I350 NICs on Ubuntu 24.04, and two Accton AS7726-32X running Enterprise SONiC 4.4.1 and 4.5.2.
python3 -over SSH.sonic-db-dumpworks as the login user on both SONiC releases. The login user is a member of theredisgroup there.Eth1/1/1), lanes and index on the switches, altnames and permanent MAC addresses on the nodes.lldpdon a node announces the MAC address of the port and its name. A RouterOS switch announces a bridge port asbridge/ether9; the matching compares it by the name of the port.linux-connectx4.jsonandsonic-enterprise.jsonundertests/unit/utils/hardware/fixtures/are captures, anonymized and reduced to a selection of ports. The README next to them lists the origin of every file.Not verified
linux-mellanox.jsononly.sonic-db-dumpwithsudo -nwas not needed on the captured switches and has not run on a device.Part of #2876 and #2877.
Assisted-by: Claude:claude-fable-5-2