diff --git a/src/statd/python/cli_pretty/cli_pretty.py b/src/statd/python/cli_pretty/cli_pretty.py index f9d3c37a..7ba241d1 100755 --- a/src/statd/python/cli_pretty/cli_pretty.py +++ b/src/statd/python/cli_pretty/cli_pretty.py @@ -1631,7 +1631,7 @@ def show_hardware(json): components = get_json_data({}, json, "ietf-hardware:hardware", "component") - # Separate components by type + motherboard = [c for c in components if c.get("class") == "iana-hardware:chassis"] usb_ports = [c for c in components if c.get("class") == "infix-hardware:usb"] sensors = [c for c in components if c.get("class") == "iana-hardware:sensor"] @@ -1640,9 +1640,20 @@ def show_hardware(json): # Display full-width inverted heading print(Decore.invert(f"{'HARDWARE COMPONENTS':<{width}}")) - print() - # USB Ports section + if motherboard: + board = motherboard[0] # Should only be one + Decore.title("Board Information", width) + + if board.get("model-name"): + print(f"Model : {board['model-name']}") + if board.get("mfg-name"): + print(f"Manufacturer : {board['mfg-name']}") + if board.get("serial-num"): + print(f"Serial Number : {board['serial-num']}") + if board.get("hardware-rev"): + print(f"Hardware Revision : {board['hardware-rev']}") + if usb_ports: Decore.title("USB Ports", width) hdr = (f"{'NAME':<{PadUsbPort.name}}" @@ -1655,9 +1666,7 @@ def show_hardware(json): for component in usb_ports: port = USBport(component) port.print() - print() - # Sensors section if sensors: Decore.title("Sensors", width) hdr = (f"{'NAME':<{PadSensor.name}}" diff --git a/src/statd/python/yanger/ietf_hardware.py b/src/statd/python/yanger/ietf_hardware.py index 5e7f3281..202cf5f1 100644 --- a/src/statd/python/yanger/ietf_hardware.py +++ b/src/statd/python/yanger/ietf_hardware.py @@ -52,30 +52,63 @@ def vpd_components(systemjson): def usb_port_components(systemjson): usb_ports = systemjson.get("usb-ports", []) - ports=[] - names=[] + ports = [] for usb_port in usb_ports: - port={} + port = {} if usb_port.get("path"): - if usb_port["name"] in names: - continue + # Path now points to the USB device directory, not the attribute + base_path = usb_port["path"] + authorized_default_path = os.path.join(base_path, "authorized_default") - path = usb_port["path"] - if os.path.basename(path) == "authorized_default": - if HOST.exists(path): - names.append(usb_port["name"]) - data = int(HOST.read(path)) - enabled = "unlocked" if data == 1 else "locked" - port["state"] = {} - port["state"]["admin-state"] = enabled - port["name"] = usb_port["name"] - port["class"] = "infix-hardware:usb" - port["state"]["oper-state"] = "enabled" - ports.append(port) + if HOST.exists(authorized_default_path): + data = int(HOST.read(authorized_default_path)) + enabled = "unlocked" if data == 1 else "locked" + port["state"] = {} + port["state"]["admin-state"] = enabled + port["name"] = usb_port["name"] + port["class"] = "infix-hardware:usb" + port["state"]["oper-state"] = "enabled" + ports.append(port) return ports +def motherboard_component(systemjson): + """ + Create a mainboard/chassis component from system.json data. + This provides a standard ietf-hardware representation of the main board. + """ + component = { + "name": "mainboard", + "class": "iana-hardware:chassis", + } + + # Add manufacturer if available (from VPD or defaults) + if systemjson.get("vendor"): + component["mfg-name"] = systemjson["vendor"] + + # Add model name (from device tree or VPD) + if systemjson.get("product-name"): + component["model-name"] = systemjson["product-name"] + + # Add serial number if available (from VPD) + if systemjson.get("serial-number"): + component["serial-num"] = systemjson["serial-number"] + + # Add part number as hardware revision if available + if systemjson.get("part-number"): + component["hardware-rev"] = systemjson["part-number"] + + # Set state - admin-state is "unknown" since chassis cannot be + # administratively controlled (locked/unlocked) + component["state"] = { + "admin-state": "unknown", + "oper-state": "enabled" + } + + return [component] + + def thermal_sensor_components(): """ Discover thermal zones and create sensor components. @@ -139,6 +172,7 @@ def operational(): return { "ietf-hardware:hardware": { "component": + motherboard_component(systemjson) + vpd_components(systemjson) + usb_port_components(systemjson) + thermal_sensor_components() +