Files
es24n-conf/modules/workflow_check.py
scott 6912680ed4 Fix fabric firmware version path — add VikingEnterpriseSolutions key
The Redfish response nests the version under
Oem.VikingEnterpriseSolutions.Version.ActiveFirmwareVersion, not
Oem.Version.ActiveFirmwareVersion. Fixed in both redfish.py
(_get_fabric_fw_version, used by network check and firmware workflow)
and workflow_check.py (serial inline parsing).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 11:28:07 -04:00

235 lines
8.5 KiB
Python

"""
workflow_check.py — System check workflow for ES24N IOM controllers.
Queries current network settings and firmware versions from one or both IOMs
via either a serial connection (for IOMs not yet on the network) or a direct
network connection (for IOMs already reachable via their management IP).
No changes are made — this is a read-only diagnostic workflow.
"""
import time
from redfish import _redfish_request, _get_iom_fw_version, _get_fabric_fw_version
from workflow_serial import (
detect_serial_device,
open_serial_connection,
close_serial_connection,
_login_serial_console,
_serial_redfish_request,
)
from ui import (
_c, C,
banner, rule, draw_table,
info, ok, warn, error,
prompt, prompt_ip, prompt_password,
)
# ── Shared helpers ─────────────────────────────────────────────────────────────
def _parse_network_data(data: dict) -> tuple:
"""
Extract network details from a Redfish EthernetInterfaces/1 response dict.
Returns (dhcp_enabled, ip, gateway, netmask, mac, hostname, link_status).
"""
dhcp_enabled = (
data.get("DHCPv4", {}).get("DHCPEnabled", False) or
data.get("DHCPv6", {}).get("DHCPEnabled", False)
)
# Prefer IPv4StaticAddresses; fall back to IPv4Addresses
addrs = data.get("IPv4StaticAddresses") or data.get("IPv4Addresses", [])
if addrs:
addr = addrs[0]
ip = addr.get("Address", "--")
gateway = addr.get("Gateway", "--")
netmask = addr.get("SubnetMask", "--")
else:
ip = gateway = netmask = "--"
mac = data.get("MACAddress", "--")
hostname = data.get("HostName", "--")
link_status = data.get("LinkStatus", "--")
return dhcp_enabled, ip, gateway, netmask, mac, hostname, link_status
def _print_results(net_rows: list, iface_rows: list, fw_rows: list):
"""
Display network settings, interface identity, and firmware versions.
net_rows: [IOM, Mode, IP Address, Gateway, Subnet Mask, Link Status]
iface_rows: [IOM, MAC Address, Hostname]
fw_rows: [IOM, IOM Firmware, Fabric Firmware]
"""
rule("Network Settings")
draw_table(
["IOM", "Mode", "IP Address", "Gateway", "Subnet Mask", "Link"],
net_rows,
[5, 8, 15, 15, 15, 8],
)
print()
rule("Interface Details")
draw_table(
["IOM", "MAC Address", "Hostname"],
iface_rows,
[5, 19, 42],
)
print()
rule("Firmware Versions")
draw_table(
["IOM", "IOM Firmware", "Fabric Firmware"],
fw_rows,
[5, 32, 24],
)
print()
# ── Serial check ───────────────────────────────────────────────────────────────
def _check_via_serial():
banner()
rule("System Check -- Serial Connection")
device = detect_serial_device()
if not device:
error("Could not detect a serial device. Returning to main menu.")
time.sleep(2)
return
ser = open_serial_connection(device)
if not ser:
error("Could not open serial port. Returning to main menu.")
time.sleep(2)
return
print()
logged_in, password = _login_serial_console(ser)
if not logged_in:
error("Could not log in to IOM console. Returning to main menu.")
close_serial_connection(ser, device)
time.sleep(2)
return
rule("Querying IOM1 Status")
info("Querying IOM1 network settings and firmware versions via serial console...")
info("Note: only IOM1 is reachable over the serial connection.")
print()
# ── Network settings ───────────────────────────────────────────────────────
net_ok, net_data = _serial_redfish_request(
ser, password, "GET",
"/redfish/v1/Managers/IOM1/EthernetInterfaces/1",
)
if net_ok and isinstance(net_data, dict):
dhcp, ip, gw, nm, mac, hostname, link = _parse_network_data(net_data)
mode = _c(C.CYN, "DHCP") if dhcp else _c(C.GRN, "Static")
net_rows = [["IOM1", mode, ip, gw, nm, link]]
iface_rows = [["IOM1", mac, hostname]]
else:
net_rows = [["IOM1", _c(C.RED, "No response"), "--", "--", "--", "--"]]
iface_rows = [["IOM1", "--", "--"]]
error(f"IOM1 network query failed: {net_data}")
# ── IOM firmware version ───────────────────────────────────────────────────
iom_ok, iom_data = _serial_redfish_request(
ser, password, "GET",
"/redfish/v1/Managers/IOM1",
)
iom_ver = (
iom_data.get("FirmwareVersion", "Unknown")
if (iom_ok and isinstance(iom_data, dict))
else _c(C.RED, "Unreachable")
)
# ── Fabric card firmware version ───────────────────────────────────────────
fab_ok, fab_data = _serial_redfish_request(
ser, password, "GET",
"/redfish/v1/Chassis/IOM1/NetworkAdapters/1",
)
fab_ver = (
(fab_data.get("Oem", {})
.get("VikingEnterpriseSolutions", {})
.get("Version", {})
.get("ActiveFirmwareVersion", "Unknown"))
if (fab_ok and isinstance(fab_data, dict))
else _c(C.RED, "Unreachable")
)
_print_results(net_rows, iface_rows, [["IOM1", iom_ver, fab_ver]])
close_serial_connection(ser, device)
# ── Network check ──────────────────────────────────────────────────────────────
def _check_via_network():
banner()
rule("System Check -- Network Connection")
print()
password = prompt_password()
print()
ip = prompt_ip(" IOM IP address (either IOM1 or IOM2)")
iom_list = [("IOM1", ip), ("IOM2", ip)]
rule("Querying IOM Status")
info("Querying network settings and firmware versions over the network...")
print()
net_rows = []
iface_rows = []
fw_rows = []
for iom, host in iom_list:
# ── Network settings ───────────────────────────────────────────────────
net_ok, net_data = _redfish_request(
password, "GET",
f"/redfish/v1/Managers/{iom}/EthernetInterfaces/1",
host=host,
)
if net_ok and isinstance(net_data, dict):
dhcp, ip_addr, gw, nm, mac, hostname, link = _parse_network_data(net_data)
mode = _c(C.CYN, "DHCP") if dhcp else _c(C.GRN, "Static")
net_rows.append([iom, mode, ip_addr, gw, nm, link])
iface_rows.append([iom, mac, hostname])
else:
net_rows.append([iom, _c(C.RED, "No response"), "--", "--", "--", "--"])
iface_rows.append([iom, "--", "--"])
error(f"{iom} network query failed: {net_data}")
# ── Firmware versions (reuse shared redfish helpers) ───────────────────
iom_ver = _get_iom_fw_version(password, host, iom)
fab_ver = _get_fabric_fw_version(password, host, iom)
fw_rows.append([iom, iom_ver, fab_ver])
_print_results(net_rows, iface_rows, fw_rows)
# ── Top-level entry point ──────────────────────────────────────────────────────
def system_check_workflow():
banner()
rule("System Check")
print(" How do you want to connect to the IOM(s)?")
print()
print(f" {_c(C.BOLD, '1')} Serial connection (IOM not yet on the network)")
print(f" {_c(C.BOLD, '2')} Network connection (IOM reachable via management IP)")
print(f" {_c(C.BOLD, '3')} Cancel")
print()
while True:
choice = prompt("Select [1/2/3]")
if choice in ("1", "2", "3"):
break
warn("Please enter 1, 2, or 3.")
if choice == "1":
_check_via_serial()
elif choice == "2":
_check_via_network()
# choice == "3" returns to main menu