The BMC serial number (e.g. MXE3000048LHA03C) is embedded in the IOM hostname shown in the serial login prompt. It also doubles as the root password, so no manual entry is needed. - If at login prompt: extract serial from 'hostname login:' line - If already logged in: run 'hostname' on the shell and extract serial - Falls back to prompt_password() if extraction fails either way _login_serial_console() now returns (success, password) instead of bool. Callers in workflow_serial.py and workflow_check.py updated accordingly. CLAUDE.md updated to document the behaviour and remove it from planned features. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
218 lines
7.7 KiB
Python
218 lines
7.7 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 (dhcp_enabled, ip, gateway, netmask, origin) from a
|
|
Redfish EthernetInterfaces/1 response dict.
|
|
"""
|
|
dhcp_enabled = (
|
|
data.get("DHCPv4", {}).get("DHCPEnabled", False) or
|
|
data.get("DHCPv6", {}).get("DHCPEnabled", False)
|
|
)
|
|
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 = "--"
|
|
|
|
origin = (
|
|
data.get("IPv4Addresses", [{}])[0].get("AddressOrigin", "Unknown")
|
|
if data.get("IPv4Addresses")
|
|
else ("DHCP" if dhcp_enabled else "Static")
|
|
)
|
|
return dhcp_enabled, ip, gateway, netmask, origin
|
|
|
|
|
|
def _print_results(net_rows: list, fw_rows: list):
|
|
rule("Network Settings")
|
|
draw_table(
|
|
["IOM", "Mode", "Origin", "IP Address", "Gateway", "Subnet Mask"],
|
|
net_rows,
|
|
[5, 10, 8, 16, 16, 16],
|
|
)
|
|
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 IOM Status")
|
|
info("Querying network settings and firmware versions via serial console...")
|
|
print()
|
|
|
|
net_rows = []
|
|
fw_rows = []
|
|
|
|
for iom in ("IOM1", "IOM2"):
|
|
# ── Network settings ───────────────────────────────────────────────────
|
|
net_ok, net_data = _serial_redfish_request(
|
|
ser, password, "GET",
|
|
f"/redfish/v1/Managers/{iom}/EthernetInterfaces/1",
|
|
)
|
|
if net_ok and isinstance(net_data, dict):
|
|
dhcp, ip, gw, nm, origin = _parse_network_data(net_data)
|
|
mode = _c(C.CYN, "DHCP") if dhcp else _c(C.GRN, "Static")
|
|
net_rows.append([iom, mode, origin, ip, gw, nm])
|
|
else:
|
|
net_rows.append([iom, _c(C.RED, "No response"), "--", "--", "--", "--"])
|
|
error(f"{iom} network query failed: {net_data}")
|
|
|
|
# ── IOM firmware version ───────────────────────────────────────────────
|
|
iom_ok, iom_data = _serial_redfish_request(
|
|
ser, password, "GET",
|
|
f"/redfish/v1/Managers/{iom}",
|
|
)
|
|
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",
|
|
f"/redfish/v1/Chassis/{iom}/NetworkAdapters/1",
|
|
)
|
|
fab_ver = (
|
|
(fab_data.get("Oem", {})
|
|
.get("Version", {})
|
|
.get("ActiveFirmwareVersion", "Unknown"))
|
|
if (fab_ok and isinstance(fab_data, dict))
|
|
else _c(C.RED, "Unreachable")
|
|
)
|
|
|
|
fw_rows.append([iom, iom_ver, fab_ver])
|
|
|
|
_print_results(net_rows, fw_rows)
|
|
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 = []
|
|
fw_rows = []
|
|
|
|
for iom, ip in iom_list:
|
|
# ── Network settings ───────────────────────────────────────────────────
|
|
net_ok, net_data = _redfish_request(
|
|
password, "GET",
|
|
f"/redfish/v1/Managers/{iom}/EthernetInterfaces/1",
|
|
host=ip,
|
|
)
|
|
if net_ok and isinstance(net_data, dict):
|
|
dhcp, ip_addr, gw, nm, origin = _parse_network_data(net_data)
|
|
mode = _c(C.CYN, "DHCP") if dhcp else _c(C.GRN, "Static")
|
|
net_rows.append([iom, mode, origin, ip_addr, gw, nm])
|
|
else:
|
|
net_rows.append([iom, _c(C.RED, "No response"), "--", "--", "--", "--"])
|
|
error(f"{iom} network query failed: {net_data}")
|
|
|
|
# ── Firmware versions (reuse shared redfish helpers) ───────────────────
|
|
iom_ver = _get_iom_fw_version(password, ip, iom)
|
|
fab_ver = _get_fabric_fw_version(password, ip, iom)
|
|
fw_rows.append([iom, iom_ver, fab_ver])
|
|
|
|
_print_results(net_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
|