""" 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 # Prompt for which IOM the serial cable is connected to print() print(" Which IOM is the serial cable connected to?") print(f" {_c(C.BOLD, '1')} IOM1") print(f" {_c(C.BOLD, '2')} IOM2") print() while True: iom_choice = prompt("Select [1/2]") if iom_choice in ("1", "2"): break warn("Please enter 1 or 2.") iom = "IOM1" if iom_choice == "1" else "IOM2" rule(f"Querying {iom} Status") info(f"Querying {iom} network settings and firmware versions via serial console...") print() # ── 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, mac, hostname, link = _parse_network_data(net_data) mode = _c(C.CYN, "DHCP") if dhcp else _c(C.GRN, "Static") net_rows = [[iom, mode, ip, gw, nm, link]] iface_rows = [[iom, mac, hostname]] else: net_rows = [[iom, _c(C.RED, "No response"), "--", "--", "--", "--"]] iface_rows = [[iom, "--", "--"]] 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("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, [[iom, 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) prompt("Press Enter to return to main menu") # ── 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