From b1af09112a7a71af1bdbc036818f79ecccb9dc27 Mon Sep 17 00:00:00 2001 From: scott Date: Tue, 3 Mar 2026 20:52:47 -0500 Subject: [PATCH] Add es24n-conf.sh --- es24n-conf.sh | 399 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 399 insertions(+) create mode 100644 es24n-conf.sh diff --git a/es24n-conf.sh b/es24n-conf.sh new file mode 100644 index 0000000..86b8d6f --- /dev/null +++ b/es24n-conf.sh @@ -0,0 +1,399 @@ +#!/usr/bin/env bash +# ============================================================================= +# ES24N IOM Network Configuration Script +# TrueNAS ES24N Expansion Shelf — Serial Configuration Tool +# Based on ES24N Product Service Guide v.26011 +# ============================================================================= + +# ── Colours & formatting ────────────────────────────────────────────────────── +RED='\033[0;31m' +GRN='\033[0;32m' +YEL='\033[1;33m' +CYN='\033[0;36m' +BLD='\033[1m' +DIM='\033[2m' +RST='\033[0m' + +# ── Helpers ─────────────────────────────────────────────────────────────────── +banner() { + clear + echo -e "${CYN}${BLD}" + echo " ╔══════════════════════════════════════════════════════════╗" + echo " ║ TrueNAS ES24N IOM Configuration Tool ║" + echo " ║ Serial Network Setup • v1.0 ║" + echo " ╚══════════════════════════════════════════════════════════╝" + echo -e "${RST}" +} + +info() { echo -e " ${CYN}[INFO]${RST} $*"; } +ok() { echo -e " ${GRN}[ OK ]${RST} $*"; } +warn() { echo -e " ${YEL}[WARN]${RST} $*"; } +error() { echo -e " ${RED}[ERR ]${RST} $*"; } +step() { echo -e "\n ${BLD}${YEL}──▶ $*${RST}"; } +divider() { echo -e " ${DIM}──────────────────────────────────────────────────────────${RST}"; } + +# ── Input validators ────────────────────────────────────────────────────────── +validate_ip() { + local ip="$1" + local re='^([0-9]{1,3}\.){3}[0-9]{1,3}$' + if [[ ! $ip =~ $re ]]; then return 1; fi + IFS='.' read -ra oct <<< "$ip" + for o in "${oct[@]}"; do + (( o > 255 )) && return 1 + done + return 0 +} + +prompt_ip() { + local label="$1" + local var_name="$2" + local ip + while true; do + read -rp " ${label}: " ip + if validate_ip "$ip"; then + eval "$var_name='$ip'" + return 0 + fi + warn "Invalid IP address format. Please try again." + done +} + +prompt_password() { + local var_name="$1" + local pass + while true; do + read -rsp " Admin password (BMC/chassis serial, e.g. MXE3000043CHA007): " pass + echo + if [[ -n "$pass" ]]; then + eval "$var_name='$pass'" + return 0 + fi + warn "Password cannot be empty." + done +} + +# ── Step 1: Connect cable & detect USB device ───────────────────────────────── +detect_serial_device() { + step "Serial Cable & Device Detection" + divider + echo + echo -e " ${BLD}Action required:${RST}" + echo " 1. Connect the serial cable from the ES24N IOM1 port" + echo " to the active F-Series controller USB port." + echo " 2. Press ${BLD}[Enter]${RST} once the cable is connected." + echo + read -rp " Press [Enter] when the serial cable is connected... " + echo + + info "Scanning for USB serial devices (/dev/ttyUSB* and /dev/ttyACM*)..." + sleep 1 + + mapfile -t devices < <(ls /dev/ttyUSB* /dev/ttyACM* 2>/dev/null) + + if [[ ${#devices[@]} -eq 0 ]]; then + error "No USB serial device detected." + echo + echo " Troubleshooting:" + echo " • Make sure the serial cable is fully seated." + echo " • Try a different USB port on the controller." + echo " • Verify the ES24N is powered on." + echo + read -rp " Retry detection? [y/N]: " retry + [[ "${retry,,}" == "y" ]] && detect_serial_device && return + return 1 + fi + + if [[ ${#devices[@]} -eq 1 ]]; then + SERIAL_DEV="${devices[0]}" + ok "Device found: ${BLD}${SERIAL_DEV}${RST}" + else + echo + echo " Multiple devices detected:" + local i=1 + for dev in "${devices[@]}"; do + echo " ${i}) ${dev}" + (( i++ )) + done + echo + while true; do + read -rp " Select device number [1-${#devices[@]}]: " sel + if [[ "$sel" =~ ^[0-9]+$ ]] && (( sel >= 1 && sel <= ${#devices[@]} )); then + SERIAL_DEV="${devices[$((sel-1))]}" + ok "Selected: ${BLD}${SERIAL_DEV}${RST}" + break + fi + warn "Invalid selection. Please enter a number between 1 and ${#devices[@]}." + done + fi + + # Fix permissions so the current user can access the port + info "Setting group permission on ${SERIAL_DEV}..." + if sudo chown ":wheel" "${SERIAL_DEV}" 2>/dev/null || \ + sudo chmod a+rw "${SERIAL_DEV}" 2>/dev/null; then + ok "Permissions updated." + else + warn "Could not update permissions automatically. You may need to run as root." + fi + echo + return 0 +} + +# ── Step 2: Open serial connection ──────────────────────────────────────────── +open_serial_connection() { + local dev="$1" + step "Opening Serial Connection → ${dev}" + divider + + # Check that a terminal tool is available + if command -v screen &>/dev/null; then + SERIAL_TOOL="screen" + elif command -v minicom &>/dev/null; then + SERIAL_TOOL="minicom" + elif command -v picocom &>/dev/null; then + SERIAL_TOOL="picocom" + else + error "No serial terminal found (screen / minicom / picocom)." + error "Install one of them and re-run this script." + return 1 + fi + + info "Using ${BLD}${SERIAL_TOOL}${RST} at 115200 8N1" + echo + echo -e " ${YEL}${BLD}NOTE:${RST} A serial session will now open." + echo " • Log into the ES24N IOM shell when the prompt appears." + echo " • Press Enter once or twice if you do not see a login prompt." + echo " • Exit the terminal session and return here when done:" + case "$SERIAL_TOOL" in + screen) echo " screen exit: Ctrl-A then K (then 'y' to confirm)" ;; + minicom) echo " minicom exit: Ctrl-A then X" ;; + picocom) echo " picocom exit: Ctrl-A then Ctrl-X" ;; + esac + echo + read -rp " Press [Enter] to launch the serial terminal... " + echo + + case "$SERIAL_TOOL" in + screen) screen "${dev}" 115200 ;; + minicom) minicom -D "${dev}" -b 115200 ;; + picocom) picocom -b 115200 "${dev}" ;; + esac + + ok "Serial session closed. Returning to configuration menu." + echo +} + +# ── Step 3: Collect network config from user ────────────────────────────────── +collect_network_config() { + step "IOM Network Configuration" + divider + echo + echo " How should the IOMs be configured?" + echo " 1) Static IP addresses" + echo " 2) DHCP" + echo + while true; do + read -rp " Select option [1-2]: " choice + case "$choice" in + 1) IP_MODE="static"; break ;; + 2) IP_MODE="dhcp"; break ;; + *) warn "Please enter 1 or 2." ;; + esac + done + + echo + prompt_password ADMIN_PASS + + if [[ "$IP_MODE" == "static" ]]; then + echo + info "Enter static network details for IOM1:" + prompt_ip " IOM1 IP address " IOM1_IP + prompt_ip " IOM1 Gateway " IOM1_GW + prompt_ip " IOM1 Subnet Mask " IOM1_NM + + echo + info "Enter static network details for IOM2:" + prompt_ip " IOM2 IP address " IOM2_IP + + read -rp " IOM2 Gateway (same as IOM1? [Y/n]): " same_gw + if [[ "${same_gw,,}" != "n" ]]; then + IOM2_GW="$IOM1_GW"; IOM2_NM="$IOM1_NM" + else + prompt_ip " IOM2 Gateway " IOM2_GW + prompt_ip " IOM2 Subnet Mask " IOM2_NM + fi + fi +} + +# ── Step 4: Apply configuration via Redfish over the serial loopback ────────── +apply_configuration() { + step "Applying Configuration" + divider + echo + + local BASE="https://127.0.0.1/redfish/v1/Managers" + + if [[ "$IP_MODE" == "dhcp" ]]; then + info "Setting IOM1 to DHCP..." + curl -sk -u "Admin:${ADMIN_PASS}" -X PATCH \ + "${BASE}/IOM1/EthernetInterfaces/1" \ + -H "Content-Type: application/json" \ + -d '{"DHCPv4": {"DHCPEnabled": true}}' \ + -o /tmp/es24n_iom1.json + show_result /tmp/es24n_iom1.json "IOM1" + + info "Setting IOM2 to DHCP..." + curl -sk -u "Admin:${ADMIN_PASS}" -X PATCH \ + "${BASE}/IOM2/EthernetInterfaces/1" \ + -H "Content-Type: application/json" \ + -d '{"DHCPv4": {"DHCPEnabled": true}}' \ + -o /tmp/es24n_iom2.json + show_result /tmp/es24n_iom2.json "IOM2" + + else + info "Applying static IP to IOM1 (${IOM1_IP})..." + curl -sk -u "Admin:${ADMIN_PASS}" -X PATCH \ + "${BASE}/IOM1/EthernetInterfaces/1" \ + -H "Content-Type: application/json" \ + -d "{\"DHCPv4\": {\"DHCPEnabled\": false}, \ + \"IPv4StaticAddresses\": [{\"Address\": \"${IOM1_IP}\", \ + \"Gateway\": \"${IOM1_GW}\", \"SubnetMask\": \"${IOM1_NM}\"}]}" \ + -o /tmp/es24n_iom1.json + show_result /tmp/es24n_iom1.json "IOM1" + + info "Applying static IP to IOM2 (${IOM2_IP})..." + curl -sk -u "Admin:${ADMIN_PASS}" -X PATCH \ + "${BASE}/IOM2/EthernetInterfaces/1" \ + -H "Content-Type: application/json" \ + -d "{\"DHCPv4\": {\"DHCPEnabled\": false}, \ + \"IPv4StaticAddresses\": [{\"Address\": \"${IOM2_IP}\", \ + \"Gateway\": \"${IOM2_GW}\", \"SubnetMask\": \"${IOM2_NM}\"}]}" \ + -o /tmp/es24n_iom2.json + show_result /tmp/es24n_iom2.json "IOM2" + fi + + echo + info "Configuration applied. Verify settings in TrueNAS → System Settings → Enclosure." +} + +# parse curl response and print a friendly summary +show_result() { + local file="$1" label="$2" + if [[ -f "$file" ]]; then + local http_err + http_err=$(python3 -c "import json,sys; d=json.load(open('$file')); \ + print(d.get('error',{}).get('message',''))" 2>/dev/null) + if [[ -z "$http_err" ]]; then + ok "${label}: Configuration accepted." + else + error "${label}: ${http_err}" + fi + else + warn "${label}: No response received (curl may have failed)." + fi +} + +# ── Step 5: Print a config summary ──────────────────────────────────────────── +print_summary() { + echo + divider + echo -e " ${BLD}Configuration Summary${RST}" + divider + echo " Mode : ${IP_MODE^^}" + if [[ "$IP_MODE" == "static" ]]; then + echo " IOM1 : ${IOM1_IP} GW ${IOM1_GW} NM ${IOM1_NM}" + echo " IOM2 : ${IOM2_IP} GW ${IOM2_GW} NM ${IOM2_NM}" + fi + echo " Device : ${SERIAL_DEV}" + divider + echo + warn "IMPORTANT: Per the ES24N Service Guide, remove the serial cable ONLY" + warn "after verifying each expander is configured in TrueNAS with matching" + warn "drives in the expected slots (System Settings → Enclosure)." + echo +} + +# ── Step 6: Close serial connection (prompt) ────────────────────────────────── +close_serial_connection() { + step "Close Serial Connection" + echo + echo " When you have verified the configuration in TrueNAS, disconnect" + echo " the serial cable from the IOM1 port." + echo + read -rp " Press [Enter] to confirm the serial cable has been disconnected... " + ok "Serial connection closed." +} + +# ── Main loop ───────────────────────────────────────────────────────────────── +main() { + banner + + echo -e " ${DIM}This tool guides you through ES24N IOM network configuration" + echo -e " over a serial connection. Based on TrueNAS ES24N Service Guide v.26011.${RST}" + echo + + while true; do + banner + + step "Main Menu" + divider + echo + echo " 1) Configure a new ES24N shelf" + echo " 2) Exit" + echo + read -rp " Select [1-2]: " main_choice + + case "$main_choice" in + 1) # ── Full configuration flow ────────────────────────────────── + banner + + # 1. Cable & device + detect_serial_device || { error "Aborting shelf configuration."; sleep 2; continue; } + + # 2. Open serial terminal so user can verify/log in + open_serial_connection "${SERIAL_DEV}" + + # 3. Collect network settings + collect_network_config + + # 4. Apply via Redfish (runs locally on the IOM over 127.0.0.1) + echo + echo -e " ${YEL}${BLD}Ready to apply network configuration via Redfish API.${RST}" + echo " Ensure you are still logged into the IOM serial session" + echo " or that the Redfish API is reachable over the loopback." + echo + read -rp " Apply configuration now? [Y/n]: " apply_now + if [[ "${apply_now,,}" != "n" ]]; then + apply_configuration + fi + + # 5. Summary + print_summary + + # 6. Close serial + close_serial_connection + + # 7. Another shelf? + echo + divider + read -rp " Configure another ES24N shelf? [y/N]: " another + [[ "${another,,}" != "y" ]] && break + ;; + + 2) # ── Exit ──────────────────────────────────────────────────── + echo + ok "Exiting ES24N IOM Configuration Tool. Goodbye." + echo + exit 0 + ;; + + *) warn "Invalid selection. Please enter 1 or 2." ; sleep 1 ;; + esac + done + + echo + ok "All shelves configured. Exiting." + echo + exit 0 +} + +main "$@" \ No newline at end of file