From 12646d4184e984a7a093d240ca36ea493120933a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 03:05:44 +0000 Subject: [PATCH 01/16] Update switch_backend.py for ERS 59100GTS-PWR+ (96+4 port) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace ERS 5952 (48+4 port) config with ERS 59100GTS-PWR+: - Port validation extended to 1–100 - All interfaces now use GigabitEthernet 1/{p} slot notation - PoE boundary moved from port 48 to port 96 - VLAN commands updated to use 1/{p} port notation - Key path, TOTP name, and app title updated https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- switch_backend.py | 48 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/switch_backend.py b/switch_backend.py index e9fd27b..14fbcd7 100644 --- a/switch_backend.py +++ b/switch_backend.py @@ -1,5 +1,5 @@ """ -ERS 5952 Switch Manager — Backend v3 +ERS 59100GTS-PWR+ Switch Manager — Backend v3 ──────────────────────────────────────────────────────────────────────── Changes from v2: - Connection pool (30s lifetime, liveness check, auto-invalidate) @@ -40,7 +40,7 @@ log = logging.getLogger("switch-manager") SWITCH_HOST = "192.168.99.1" SWITCH_PORT = 22 SWITCH_USER = "admin" -KEY_PATH = "/etc/switch-manager/ers5952_key" +KEY_PATH = "/etc/switch-manager/ers59100_key" KNOWN_HOSTS = "/etc/switch-manager/known_hosts" TOTP_FILE = "/etc/switch-manager/totp_secret" STATIC_DIR = "./frontend/dist" @@ -71,9 +71,9 @@ def get_or_create_totp_secret() -> str: def setup_totp(): secret = get_or_create_totp_secret() uri = pyotp.TOTP(secret).provisioning_uri( - name="ERS5952", issuer_name="SwitchManager") + name="ERS59100", issuer_name="SwitchManager") print("\n══════════════════════════════════════════════════") - print(" ERS 5952 Switch Manager — TOTP Setup") + print(" ERS 59100GTS-PWR+ Switch Manager — TOTP Setup") print("══════════════════════════════════════════════════") print(f"\n Manual entry secret:\n {secret}") print(f"\n Provisioning URI (paste into authenticator app):\n {uri}") @@ -217,16 +217,16 @@ def san_vid(v, field="vlan_id") -> int: return vid def san_port(v) -> int: - """Validate and return a port number (1–52 for the ERS 5952).""" + """Validate and return a port number (1–100 for the ERS 59100GTS-PWR+).""" p = int(v) - if not 1 <= p <= 52: - raise ValueError("port: must be 1–52") + if not 1 <= p <= 100: + raise ValueError("port: must be 1–100") return p _ALLOWED_CMD_RE = [ re.compile(r'^vlan\s+(create|members|tagging|pvid)\s'), re.compile(r'^no\s+vlan\s+\d+$'), - re.compile(r'^interface\s+(FastEthernet|GigabitEthernet|vlan)\s'), + re.compile(r'^interface\s+(GigabitEthernet\s+1/\d+|vlan\s+\d+)$'), re.compile(r'^\s+(name|no\s+shutdown|poe|speed|duplex|ip\s+access-group|shutdown)\b'), re.compile(r'^hostname\s+\S+$'), re.compile(r'^ip\s+access-list\s+extended\s'), @@ -600,14 +600,14 @@ class PortConfig(BaseModel): def build_port(cfg: PortConfig) -> list[str]: """ - Generate ERS 5952 CLI commands for a port configuration change. + Generate ERS 59100GTS-PWR+ CLI commands for a port configuration change. - Port 1–48 are FastEthernet; ports 49–52 are GigabitEthernet SFP uplinks. - PoE is only available on ports 1–48. + Ports 1–96 are GigabitEthernet copper (PoE capable); ports 97–100 are SFP+ uplinks (no PoE). + All interfaces use slot/port notation: GigabitEthernet 1/{p}. Returns a list of CLI command strings ready for push_one_by_one(). """ p = cfg.port - iface = f"FastEthernet {p}" if p <= 48 else f"GigabitEthernet {p}" + iface = f"GigabitEthernet 1/{p}" cmds = [] if cfg.description: cmds += [f"interface {iface}", f' name "{cfg.description}"'] @@ -615,15 +615,15 @@ def build_port(cfg: PortConfig) -> list[str]: cmds += [f"interface {iface}", " shutdown"] elif cfg.mode == "access": vid = san_vid(cfg.access_vlan, "access_vlan") - cmds += [f"vlan members add {vid} {p}", f"vlan pvid {p} {vid}"] + cmds += [f"vlan members add {vid} 1/{p}", f"vlan pvid 1/{p} {vid}"] elif cfg.mode == "trunk": native = san_vid(cfg.native_vlan, "native_vlan") tagged = [san_vid(v, f"tagged_{v}") for v in (cfg.tagged_vlans or [])] if tagged: ts = ",".join(str(v) for v in tagged) - cmds += [f"vlan members add {ts} {p}", f"vlan tagging {ts} {p}"] - cmds.append(f"vlan pvid {p} {native}") - if p <= 48: + cmds += [f"vlan members add {ts} 1/{p}", f"vlan tagging {ts} 1/{p}"] + cmds.append(f"vlan pvid 1/{p} {native}") + if p <= 96: cmds += [f"interface {iface}", " poe enable" if cfg.poe else " no poe enable"] if cfg.poe: @@ -632,7 +632,7 @@ def build_port(cfg: PortConfig) -> list[str]: def build_acl(acl: AclCreate) -> list[str]: """ - Generate ERS 5952 CLI commands to create an extended IP ACL and apply it to a VLAN interface. + Generate ERS 59100GTS-PWR+ CLI commands to create an extended IP ACL and apply it to a VLAN interface. Rules are numbered sequentially starting from 1. The ACL is applied to the VLAN's Layer 3 interface in the specified direction (in/out). @@ -657,7 +657,7 @@ def build_acl(acl: AclCreate) -> list[str]: # APP # ══════════════════════════════════════════════════════════════════════ -app = FastAPI(title="ERS 5952 Switch Manager", version="3.0.0", +app = FastAPI(title="ERS 59100GTS-PWR+ Switch Manager", version="3.0.0", docs_url="/api/docs", redoc_url=None) app.add_middleware(CORSMiddleware, allow_origins=ALLOWED_ORIGINS, @@ -868,7 +868,7 @@ def _save_devices(devices: list): DEVICES_FILE.write_text(_json.dumps(devices, indent=2)) def _parse_dhcp_leases(raw: str) -> list: - """Parse ERS 5952 'show dhcp-server leases' output.""" + """Parse ERS 59100GTS-PWR+ 'show dhcp-server leases' output.""" import re leases = [] for line in raw.splitlines(): @@ -931,7 +931,7 @@ class PinholeRequest(BaseModel): allow: bool = True def _build_dhcp_reservation_cmds(device: DeviceEntry) -> list: - """Generate ERS 5952 CLI for DHCP reservation (static binding).""" + """Generate ERS 59100GTS-PWR+ CLI for DHCP reservation (static binding).""" mac_clean = device.mac.replace(':','-').upper() return [ f"ip dhcp-server static-binding {device.ip}", @@ -1317,7 +1317,7 @@ def _get_opnsense_leases(cfg: dict) -> list: return [] def _get_switch_reservations() -> list: - """Fetch DHCP static bindings from ERS 5952.""" + """Fetch DHCP static bindings from ERS 59100GTS-PWR+.""" import re as _re try: raw = read_cmd("show dhcp-server static-binding") @@ -1369,7 +1369,7 @@ def _get_relay_status() -> dict: return {"vlans": {}, "ok": False} def _build_relay_cmds(opnsense_ip: str, vlan_ids: list) -> list: - """Generate ERS 5952 CLI to set ip helper-address on the specified VLANs.""" + """Generate ERS 59100GTS-PWR+ CLI to set ip helper-address on the specified VLANs.""" cmds = [] for vid in vlan_ids: cmds += [ @@ -1706,7 +1706,7 @@ def _build_ctrld_toml(vlan_profiles: list, local_domain: str = "lan", active = [vp for vp in vlan_profiles if vp.get("resolver_id", "").strip()] lines = [ - "# ctrld configuration — generated by Avaya 5952 Switch Manager", + "# ctrld configuration — generated by Avaya 59100GTS-PWR+ Switch Manager", "# Documentation: https://docs.controld.com/docs/ctrld", "", "[service]", @@ -2212,7 +2212,7 @@ def _generate_dnsmasq_conf(entries: list, mgmt_ip: str = "192.168.99.50") -> str lines = [ "# dnsmasq — local .lan hostname resolution", - "# Generated by Avaya 5952 Switch Manager", + "# Generated by Avaya 59100GTS-PWR+ Switch Manager", "# Listens on port 5353 (mapped from Docker container port 53)", "# ctrld forwards *.lan and *.local here", "", From 639f1ea16f07b810ddee807e5e885f45e57f2992 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 03:29:16 +0000 Subject: [PATCH 02/16] Fix SSH commands for BOSS v7.9.6 on ERS 59100GTS-PWR+ - Replace show poe-port-status with show poe-port status ALL - Replace show vlan members with show vlan - Replace show running-config with show config - Fix VLAN port format from 1/{p} to {p} (BOSS uses bare port numbers) - Fix interface naming from GigabitEthernet 1/{p} to GigabitEthernet {p} - Add terminal length 0 to push session setup to prevent pagination https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- switch_backend.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/switch_backend.py b/switch_backend.py index 14fbcd7..6e47c4b 100644 --- a/switch_backend.py +++ b/switch_backend.py @@ -368,7 +368,7 @@ def push_one_by_one(commands: list[str]) -> dict: if ch.recv_ready(): ch.recv(4096) # drain banner - for setup in ["enable", "configure terminal"]: + for setup in ["terminal length 0", "enable", "configure terminal"]: out, err = _run_one(ch, setup) if err: return {"success": False, "saved": False, @@ -476,8 +476,8 @@ def _poll_loop(): try: data = { "port_status": read_cmd("show interfaces"), - "poe_status": read_cmd("show poe-port-status"), - "vlan_members": read_cmd("show vlan members"), + "poe_status": read_cmd("show poe-port status ALL"), + "vlan_members": read_cmd("show vlan"), "sys_info": read_cmd("show sys-info"), } with _cache_lock: @@ -607,7 +607,7 @@ def build_port(cfg: PortConfig) -> list[str]: Returns a list of CLI command strings ready for push_one_by_one(). """ p = cfg.port - iface = f"GigabitEthernet 1/{p}" + iface = f"GigabitEthernet {p}" cmds = [] if cfg.description: cmds += [f"interface {iface}", f' name "{cfg.description}"'] @@ -615,14 +615,14 @@ def build_port(cfg: PortConfig) -> list[str]: cmds += [f"interface {iface}", " shutdown"] elif cfg.mode == "access": vid = san_vid(cfg.access_vlan, "access_vlan") - cmds += [f"vlan members add {vid} 1/{p}", f"vlan pvid 1/{p} {vid}"] + cmds += [f"vlan members add {vid} {p}", f"vlan pvid {p} {vid}"] elif cfg.mode == "trunk": native = san_vid(cfg.native_vlan, "native_vlan") tagged = [san_vid(v, f"tagged_{v}") for v in (cfg.tagged_vlans or [])] if tagged: ts = ",".join(str(v) for v in tagged) - cmds += [f"vlan members add {ts} 1/{p}", f"vlan tagging {ts} 1/{p}"] - cmds.append(f"vlan pvid 1/{p} {native}") + cmds += [f"vlan members add {ts} {p}", f"vlan tagging {ts} {p}"] + cmds.append(f"vlan pvid {p} {native}") if p <= 96: cmds += [f"interface {iface}", " poe enable" if cfg.poe else " no poe enable"] @@ -757,7 +757,7 @@ def live(): @app.get("/api/switch/config") def running_config(): """Fetch and return the full switch running config (read-only, no auth).""" - out = read_cmd("show running-config") + out = read_cmd("show config") return {"config": out, "lines": len(out.splitlines())} # ── Danger pre-flight (no auth — check before prompting TOTP) ───────── From ea6203f5e6fa5a719c38364a5959ffb4ed25d5d7 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 03:31:26 +0000 Subject: [PATCH 03/16] Fix interface naming and save command for BOSS v7.9.6 - Use FastEthernet {p} instead of GigabitEthernet {p} for interface commands - Use save config instead of copy running-config nvram:config.cfg https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- switch_backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/switch_backend.py b/switch_backend.py index 6e47c4b..ebf3423 100644 --- a/switch_backend.py +++ b/switch_backend.py @@ -393,7 +393,7 @@ def push_one_by_one(commands: list[str]) -> dict: _run_one(ch, "end") saved = False if stopped_at is None: - _, save_err = _run_one(ch, "copy running-config nvram:config.cfg") + _, save_err = _run_one(ch, "save config") saved = not save_err if saved: log.info("Config saved to NVRAM") @@ -607,7 +607,7 @@ def build_port(cfg: PortConfig) -> list[str]: Returns a list of CLI command strings ready for push_one_by_one(). """ p = cfg.port - iface = f"GigabitEthernet {p}" + iface = f"FastEthernet {p}" cmds = [] if cfg.description: cmds += [f"interface {iface}", f' name "{cfg.description}"'] From b43eed0546a4a6d1689dfa4c1921e614918ac2a7 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 03:35:56 +0000 Subject: [PATCH 04/16] Use show poe-main-status instead of per-port PoE command show poe-port status rejects all argument formats on this firmware; show poe-main-status gives overall PoE power and health data instead. https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- switch_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/switch_backend.py b/switch_backend.py index ebf3423..dedb207 100644 --- a/switch_backend.py +++ b/switch_backend.py @@ -476,7 +476,7 @@ def _poll_loop(): try: data = { "port_status": read_cmd("show interfaces"), - "poe_status": read_cmd("show poe-port status ALL"), + "poe_status": read_cmd("show poe-main-status"), "vlan_members": read_cmd("show vlan"), "sys_info": read_cmd("show sys-info"), } From c4001b046516da96f04ec841d96a27cda37752f8 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 03:42:27 +0000 Subject: [PATCH 05/16] Fix read_cmd to use interactive shell with enable mode exec_command runs in user mode on BOSS v7.9.6; most show commands (show vlan, show sys-info, show poe-main-status, show config, etc.) require enable mode. Switch to invoke_shell per read command, sending terminal length 0 and enable before each command. https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- switch_backend.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/switch_backend.py b/switch_backend.py index dedb207..89f9b80 100644 --- a/switch_backend.py +++ b/switch_backend.py @@ -314,11 +314,32 @@ def _open_connection() -> paramiko.SSHClient: def read_cmd(cmd: str) -> str: - """Run a read-only command via the pool. Invalidates pool on error.""" + """Run a read-only command in enable mode via interactive shell channel.""" try: conn = _pool.get() - _, stdout, _ = conn.exec_command(cmd, timeout=10) - return stdout.read().decode("utf-8", errors="replace") + ch = conn.invoke_shell() + ch.settimeout(10) + time.sleep(0.4) + if ch.recv_ready(): + ch.recv(4096) # drain login banner + for setup in ["terminal length 0", "enable"]: + ch.send(setup + "\n") + time.sleep(0.3) + if ch.recv_ready(): + ch.recv(4096) # drain prompt output + ch.send(cmd + "\n") + out = "" + deadline = time.time() + 8 + while time.time() < deadline: + if ch.recv_ready(): + out += ch.recv(4096).decode("utf-8", errors="replace") + time.sleep(0.1) + else: + if out: + break + time.sleep(0.1) + ch.close() + return out except HTTPException: raise except Exception as e: From 8ab9cd9cf674a3d0887cdac34e60a88b3d92a30f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 03:44:48 +0000 Subject: [PATCH 06/16] Add console cable plug-in order warning and console vs SSH note Cable must be connected before powering on for screen/picocom to work. Also clarify that console is one-time setup only; script uses SSH over Ethernet. https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 12e5315..b4f38ea 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Search **"RJ45 to USB console cable Cisco compatible"** — around $8. This is t ### Step 3 — Connect and Open a Terminal -Plug the RJ-45 end into the switch console port. Plug USB into your laptop. +**Important:** Plug the console cable in — both the RJ-45 end into the switch and the USB end into your laptop — **before** powering on the switch. If you plug in after power-on, `screen` and `picocom` may not detect the device correctly. **Settings:** `9600 baud · 8 data bits · No parity · 1 stop bit · No flow control` @@ -51,6 +51,8 @@ To find your COM/device name if unsure: - Mac: `ls /dev/tty.usb*` before and after plugging in - Windows: Device Manager → Ports (COM & LPT) +> **Console vs SSH:** The console cable gives you direct CLI access to type commands on the switch. It is only needed for initial setup. Once SSH is configured, the management script connects over Ethernet (port 22) and you never need the console cable again — unless you lock yourself out. + --- ### Step 4 — Power On and Read the Boot Screen From ff6e3920c05993e7776037526a06574cf847cc96 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 03:47:09 +0000 Subject: [PATCH 07/16] Fix Step 6 commands and explain IP conflict / management VLAN design - Fix save command: copy running-config nvram:config.cfg -> save config - Fix port format note: remove incorrect 1/1 slot prefix, both models use bare numbers - Add note explaining why 192.168.1.1 conflict is not a problem (management VLAN 99 is separate subnet) - Add console commands to change VLAN 1 IP if needed before VLAN 99 is configured https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- README.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b4f38ea..f16dfb8 100644 --- a/README.md +++ b/README.md @@ -109,17 +109,28 @@ vlan pvid 1 99 ip ssh username admin password YourPassword end -copy running-config nvram:config.cfg +save config ``` Replace `1` with the port number your management computer will plug into. Replace `YourPassword` with a strong password (8–32 characters; allowed special characters: `! @ # $ % ^ & * - _ = + [ ] ; : , . /` — no spaces, no quotes). -**ERS 59100 note:** Port numbering on the 59100 is `1/1` through `1/96` for copper and `1/97` through `1/100` for SFP+ uplinks. Substitute accordingly: +**Port numbering:** On both the ERS 5952 and ERS 59100, use bare port numbers (`1`, `2`, `48`…). There is no slot prefix. -``` -vlan members add 99 1/1 -vlan pvid 1/1 99 -``` +> **What about the switch's existing IP?** +> Out of the box the switch has `192.168.1.1` on VLAN 1. If your router is also `192.168.1.1`, they will conflict — two devices with the same IP on the same network causes ARP fights and makes both unreliable. +> +> This setup deliberately avoids that by putting management traffic on a **separate VLAN (99) with its own subnet (192.168.99.x)**. Your management computer gets a static IP on that subnet and talks to the switch there. The switch's original VLAN 1 address is irrelevant once VLAN 99 is up. +> +> If you ever need to change the switch's VLAN 1 IP (e.g. to remove the conflict before VLAN 99 is configured): +> ``` +> config terminal +> interface vlan 1 +> ip address 192.168.1.2 255.255.255.0 +> exit +> ip default-gateway 192.168.1.1 +> exit +> save config +> ``` After the last command the switch confirms with `CP1 [07/04/15 12:00:00.000:INFO]: Operation Success` or similar. From efd3c327d49a7bc634be4e3566e5e7dbf0643245 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 03:50:21 +0000 Subject: [PATCH 08/16] Add OS requirements section at top of README Management computer must run Linux (Ubuntu/Debian); setup script uses apt, systemd, picocom, and Docker. Browser UI works from any OS. https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index f16dfb8..5b400a4 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,16 @@ Compatible with: ERS 5928, ERS 5948, ERS 5952, ERS 5952-PWR+, ERS 59100GTS-PWR+ --- +## Requirements + +**Management computer OS: Linux** (Ubuntu / Debian recommended — Raspberry Pi OS works perfectly) + +The setup script uses `apt`, `systemd`, `picocom`, and Docker. It will not run on Mac or Windows. The management computer does not need a monitor — a headless Raspberry Pi or thin client is ideal. Once running, the web UI is accessible from any browser on any device on your network. + +The switch UI itself (the browser interface) works from any OS — phone, tablet, Mac, Windows, Linux. + +--- + ## Physical Setup — First Time Out of the Box Everything here happens before you run any software. You need a console cable and a laptop. From fe723ea6e596f1ee59fa398c1996820087b949af Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 03:53:18 +0000 Subject: [PATCH 09/16] Add switch OS description and factory reset instructions to README - Document BOSS v7.9.6 as the switch firmware in Requirements section - Add factory reset procedure (boot config flags factory) to Troubleshooting - Note when a wipe is and is not necessary https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 5b400a4..72c9ff6 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ The setup script uses `apt`, `systemd`, `picocom`, and Docker. It will not run o The switch UI itself (the browser interface) works from any OS — phone, tablet, Mac, Windows, Linux. +**Switch OS: BOSS (Baystack Operating System Software) v7.9.6** + +BOSS is the firmware that runs on Avaya / Extreme ERS switches. This project is written and tested against BOSS v7.9.6 on the ERS 59100GTS-PWR+. It is not Cisco IOS or any other vendor's CLI — commands are different. BOSS firmware is pre-installed on the switch and does not need to be downloaded or installed. + --- ## Physical Setup — First Time Out of the Box @@ -505,6 +509,21 @@ Read the error text — the switch says exactly what was wrong. Fix the configur **Host key rejection after switch reset** Re-pin: `ssh-keyscan -H 192.168.99.1 > /etc/switch-manager/known_hosts` +**Factory reset — wiping a previously configured switch** + +If the switch has a previous config you want to clear before starting fresh, do this via the console cable: + +``` +enable +boot config flags factory +save config +boot +``` + +The switch will reboot into factory defaults — blank config, default credentials (`admin` / no password), and the default management IP `192.168.1.1` on VLAN 1. Then follow the setup steps from Step 5 onwards. + +> When to wipe: if the switch has unknown previous configuration, unknown passwords, or you want a guaranteed clean slate. If the switch already responds to `admin` with no password and has no unexpected VLANs or ACLs, a wipe is not required — you can configure over the existing state. + **ctrld not filtering DNS** Check DHCP option 6 is set to the ctrld machine's IP on each VLAN pool. Check ctrld is running: `ctrld status`. Check the switch is handing out the right DNS: from a device, run `nslookup example.com` and verify the server IP matches. From e4777d72fbedef6d86dbeeae711161b62ae4ab52 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 03:58:56 +0000 Subject: [PATCH 10/16] Document diagnostic boot menu and improve factory reset instructions - Add boot menu contents (options 1-9, A-C) with explanation of each - Method 1: boot menu option 5 (works without knowing password) - Method 2: CLI method (requires working login) - Note 60-second window and how to continue normal boot if entered by accident https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- README.md | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 72c9ff6..1c6b908 100644 --- a/README.md +++ b/README.md @@ -511,7 +511,32 @@ Re-pin: `ssh-keyscan -H 192.168.99.1 > /etc/switch-manager/known_hosts` **Factory reset — wiping a previously configured switch** -If the switch has a previous config you want to clear before starting fresh, do this via the console cable: +> When to wipe: if the switch has unknown previous configuration, unknown passwords, or you want a guaranteed clean slate. If the switch already responds to `admin` with no password and has no unexpected VLANs or ACLs, a wipe is not required — you can configure over the existing state. + +**Method 1 — Boot menu (easiest, works even if you don't know the password)** + +The console cable must be plugged in before power-on. During boot a diagnostic menu appears briefly: + +``` +DIAGNOSTIC BREAK MENU +59100 GTS-PWR+ Diagnostics 7.5.0.4 + 1 - Launch Primary Agent-1 Vers: 7.9.6.015 + 2 - Launch Secondary Agent-2 Vers: 7.6.2.019 + 3 - Toggle Primary Agent Selection + 4 - Download Agent/ Diag + 5 - Reinitialize Agent Configuration Files + ... + 8 - Continue Boot Sequence + 9 - Reset +Select: +Booting Agent in 60 seconds... +``` + +Press **5** — "Reinitialize Agent Configuration Files". The switch wipes its config and reboots into factory defaults. You have 60 seconds before it boots automatically. + +The other useful options: **1/2** switch between primary (v7.9.6) and secondary (v7.6.2) firmware. **9** reboots. **8** continues the normal boot if you entered the menu by accident. + +**Method 2 — CLI (requires working login)** ``` enable @@ -520,9 +545,7 @@ save config boot ``` -The switch will reboot into factory defaults — blank config, default credentials (`admin` / no password), and the default management IP `192.168.1.1` on VLAN 1. Then follow the setup steps from Step 5 onwards. - -> When to wipe: if the switch has unknown previous configuration, unknown passwords, or you want a guaranteed clean slate. If the switch already responds to `admin` with no password and has no unexpected VLANs or ACLs, a wipe is not required — you can configure over the existing state. +After either method the switch comes up with blank config, default credentials (`admin` / no password), and management IP `192.168.1.1` on VLAN 1. Continue from Step 5. **ctrld not filtering DNS** Check DHCP option 6 is set to the ctrld machine's IP on each VLAN pool. Check ctrld is running: `ctrld status`. Check the switch is handing out the right DNS: from a device, run `nslookup example.com` and verify the server IP matches. From 32d512f8e1ce3569e94828d99849654b66ea3f11 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 03:59:22 +0000 Subject: [PATCH 11/16] Complete boot menu documentation with all options (A, B, C) https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1c6b908..2f99e90 100644 --- a/README.md +++ b/README.md @@ -525,16 +525,20 @@ DIAGNOSTIC BREAK MENU 3 - Toggle Primary Agent Selection 4 - Download Agent/ Diag 5 - Reinitialize Agent Configuration Files - ... + 6 - Display Error Log + 7 - Display System Information 8 - Continue Boot Sequence 9 - Reset + A - Power-Cycle + B - Toggle Do-POST Selection [ ENABLED ] + C - Run POST tests Select: Booting Agent in 60 seconds... ``` Press **5** — "Reinitialize Agent Configuration Files". The switch wipes its config and reboots into factory defaults. You have 60 seconds before it boots automatically. -The other useful options: **1/2** switch between primary (v7.9.6) and secondary (v7.6.2) firmware. **9** reboots. **8** continues the normal boot if you entered the menu by accident. +Other useful options: **1/2** switch between primary (v7.9.6) and secondary (v7.6.2) firmware. **6** shows the error log. **7** shows system info. **8** continues normal boot if you entered the menu by accident. **9** resets (reboots). **A** power-cycles. **B** toggles POST (hardware self-test) on/off. **C** runs POST tests manually. **Method 2 — CLI (requires working login)** From b77f6fd19b40f424f40c36b12bd743477b0b3366 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 04:03:40 +0000 Subject: [PATCH 12/16] Add firmware upgrade section covering USB, SFTP, and TFTP methods - Document current firmware versions (BOSS 7.9.6.015 / Diag 7.5.0.4) - Note upgrade rules: one version at a time, diag first, no read-only USB files - USB method via download command and boot menu option 4 - SFTP and TFTP methods via CLI - Link to Extreme portal and announcements page for latest version https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- README.md | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/README.md b/README.md index 2f99e90..8540c90 100644 --- a/README.md +++ b/README.md @@ -492,6 +492,72 @@ The switch is never being polled when nobody is looking at the dashboard. --- +## Firmware Upgrade + +The switch runs two software components that must both be upgraded: +- **Diagnostic image** (diag) — upgraded first +- **Agent image** (BOSS firmware) — upgraded second + +**Important rules:** +- Upgrade one version at a time — cannot skip releases +- Always upgrade the diagnostic image before the agent image +- USB files must not be marked read-only or the transfer fails +- Firmware downloads require an active support contract at the [Extreme Networks portal](https://extreme-networks.my.site.com) +- Check the [ERS Announcements page](https://community.extremenetworks.com/t5/ers-announcements/bg-p/ERS_Announcements) to find the latest version + +Your switch currently runs: **BOSS v7.9.6.015 / Diagnostics 7.5.0.4** + +--- + +### Method 1 — USB (no network needed, easiest) + +1. Download the diagnostic `.bin` and agent `.img` files from the Extreme portal +2. Copy both files to a USB stick — ensure they are **not read-only** +3. Insert the USB stick into the front panel USB port on the switch +4. Via console or SSH (in enable mode): + +``` +download usb diag ers5900diag_7x_x_x_x.bin +``` +Wait for it to complete and confirm, then: +``` +download usb image ers5900_7x_x_x_x.img +``` +The switch will reboot automatically after the agent upgrade. + +Alternatively, from the **boot menu** (option 4 — "Download Agent/Diag") you can trigger a USB download without logging in first. + +--- + +### Method 2 — SFTP over SSH (requires network) + +From enable mode on the switch, with an SFTP server running on your management computer: + +``` +copy sftp address 192.168.99.50 filename ers5900diag_7x_x_x_x.bin +``` +Wait for completion, then: +``` +copy sftp address 192.168.99.50 filename ers5900_7x_x_x_x.img +``` +The switch reboots after the agent upgrade. + +--- + +### Method 3 — TFTP (requires TFTP server on management computer) + +Install a TFTP server (`sudo apt install tftpd-hpa`), place the firmware files in `/srv/tftp/`, then from enable mode: + +``` +copy tftp address 192.168.99.50 filename ers5900diag_7x_x_x_x.bin +``` +Then: +``` +copy tftp address 192.168.99.50 filename ers5900_7x_x_x_x.img +``` + +--- + ## Troubleshooting **Connection banner stuck on "Connecting..."** From fe070799169469d89a71dc593cb32ad85e3c0079 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 04:07:26 +0000 Subject: [PATCH 13/16] Add laptop-as-TFTP-server and XMODEM console upgrade methods - Method 3: laptop with console + Ethernet can act as temporary TFTP server - Method 4: XMODEM over console cable when USB broken and no Ethernet available - Note 3-hour transfer time warning for XMODEM at 9600 baud https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- README.md | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8540c90..0e35203 100644 --- a/README.md +++ b/README.md @@ -544,18 +544,49 @@ The switch reboots after the agent upgrade. --- -### Method 3 — TFTP (requires TFTP server on management computer) +### Method 3 — TFTP from a laptop (no dedicated server needed) -Install a TFTP server (`sudo apt install tftpd-hpa`), place the firmware files in `/srv/tftp/`, then from enable mode: +Your laptop can act as a temporary TFTP server. Plug both the console cable **and** an Ethernet cable from the laptop into the switch at the same time. +On the laptop: +```bash +sudo apt install tftpd-hpa +sudo cp ers5900diag_*.bin ers5900_*.img /srv/tftp/ +sudo ip addr add 192.168.1.50/24 dev eth0 +sudo systemctl start tftpd-hpa ``` -copy tftp address 192.168.99.50 filename ers5900diag_7x_x_x_x.bin + +Then on the switch via console: +``` +enable +copy tftp address 192.168.1.50 filename ers5900diag_7x_x_x_x.bin ``` Then: ``` -copy tftp address 192.168.99.50 filename ers5900_7x_x_x_x.img +copy tftp address 192.168.1.50 filename ers5900_7x_x_x_x.img ``` +Stop the server when done: +```bash +sudo systemctl stop tftpd-hpa +``` + +--- + +### Method 4 — XMODEM over console cable (last resort, no network required) + +If the USB port is broken and you have no Ethernet available at all, boot menu option 4 ("Download Agent/Diag") supports XMODEM file transfer directly over the serial console cable. No network required. + +**Warning:** At 9600 baud, a 10 MB firmware file takes approximately 3 hours. Only use this if nothing else is possible. + +In `screen`, after selecting option 4 from the boot menu, send the file with: + +``` +Ctrl-A then :exec !! sx -b /path/to/ers5900_7x_x_x_x.img +``` + +(`sx` is part of the `lrzsz` package: `sudo apt install lrzsz`) + --- ## Troubleshooting From f52c2721d0a09c03c0db5c08479fb1403fbdc0be Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 04:17:06 +0000 Subject: [PATCH 14/16] Note switch default IP (192.168.1.1) means TFTP needs no console prep Per the ERS 59100 Quick Install Guide, the switch defaults to 192.168.1.1/24 on VLAN 1 if DHCP fails. Method 3 now notes that on a factory/reset switch you can TFTP without configuring anything via console first. https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0e35203..794d2a6 100644 --- a/README.md +++ b/README.md @@ -546,17 +546,19 @@ The switch reboots after the agent upgrade. ### Method 3 — TFTP from a laptop (no dedicated server needed) -Your laptop can act as a temporary TFTP server. Plug both the console cable **and** an Ethernet cable from the laptop into the switch at the same time. +Your laptop can act as a temporary TFTP server. The switch has a default IP of **`192.168.1.1/24`** on VLAN 1 out of the box (or after a factory reset) — so on a fresh switch you can skip console IP configuration entirely. Just plug in Ethernet and go. + +Plug both the console cable **and** an Ethernet cable from the laptop into the switch at the same time. On the laptop: ```bash sudo apt install tftpd-hpa sudo cp ers5900diag_*.bin ers5900_*.img /srv/tftp/ -sudo ip addr add 192.168.1.50/24 dev eth0 +sudo ip addr add 192.168.1.50/24 dev eth0 # must be on same /24 as switch default (192.168.1.x) sudo systemctl start tftpd-hpa ``` -Then on the switch via console: +Then on the switch via console (or via SSH to `192.168.1.1` if SSH is already enabled): ``` enable copy tftp address 192.168.1.50 filename ers5900diag_7x_x_x_x.bin @@ -571,6 +573,8 @@ Stop the server when done: sudo systemctl stop tftpd-hpa ``` +> **No console needed on a factory switch:** The switch answers at `192.168.1.1` immediately after boot. As long as your laptop is on `192.168.1.x/24`, the TFTP transfer works without touching the console at all — useful if you only have an Ethernet cable and no console cable handy. + --- ### Method 4 — XMODEM over console cable (last resort, no network required) From 8705d3d7ddaf69aef8556635120f47dc8485571b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 04:21:03 +0000 Subject: [PATCH 15/16] Add command reference table and screen scrollback docs - Table of all read and push commands with confirmed/not-tested/invalid status - Note that save config caused a reboot on first run - screen scrollback: Ctrl-A [ to enter copy mode, defscrollback in .screenrc https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- README.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/README.md b/README.md index 794d2a6..5f7038b 100644 --- a/README.md +++ b/README.md @@ -670,6 +670,77 @@ docker compose ps --- +## Command Reference — Tested Status + +Commands the script sends to the switch, with known test status. Use this as a guide when debugging unexpected behaviour — untested commands may behave differently on your firmware version. + +### Read commands + +| Command | Status | Notes | +|---|---|---| +| `show interfaces` | confirmed | | +| `show poe-main-status` | confirmed | | +| `show vlan` | confirmed | | +| `show sys-info` | confirmed | | +| `show arp` | confirmed | | +| `show config` | confirmed | Returns table output | +| `show ip helper-address` | not tested | | +| `show ip route default` | not tested | | +| `show dhcp-server leases` | invalid on this firmware | Script handles gracefully — returns empty | +| `show dhcp-server static-binding` | invalid on this firmware | Script handles gracefully — returns empty | +| `show dhcp-server` | invalid on this firmware | Script handles gracefully — returns empty | + +### Config / push commands + +| Command | Status | Notes | +|---|---|---| +| `enable` | confirmed | | +| `configure terminal` | confirmed | | +| `terminal length 0` | not tested | Disables pagination — sent before reads | +| `interface FastEthernet {port}` | confirmed | | +| `vlan members add {vid} {port}` | confirmed | | +| `vlan pvid {port} {vid}` | not tested | Sets native/untagged VLAN on a port | +| `vlan tagging {tagged-set} {port}` | not tested | Adds trunk tagging | +| `vlan create {vid} name "x" type port` | not tested | | +| `no vlan {vid}` | not tested | | +| `name "{description}"` (under interface) | not tested | Sets port description | +| `shutdown` (under interface) | not tested | Disables a port | +| `poe enable` / `no poe enable` | not tested | | +| `poe poe-limit {milliwatts}` | not tested | | +| `interface vlan {vid}` (in config mode) | not tested | | +| `ip access-list extended {name}` | not tested | | +| `ip access-group {name} in/out` | not tested | | +| `end` | not tested | Returns to enable mode | +| `save config` | confirmed | **Caused a reboot on first run** — watch the first time you push this | + +> **`save config` reboot note:** On at least one switch, issuing `save config` triggered a reboot. This may be firmware-version-specific or a one-time behaviour after certain config states. Subsequent saves have not reproduced it. Be aware when pushing config changes in a live environment. + +--- + +## Scrollback in `screen` (console cable sessions) + +By default `screen` does not let you scroll up through output. Enable it with copy mode: + +| Action | Keys | +|---|---| +| Enter scrollback mode | `Ctrl-A` then `[` | +| Scroll up / down | Arrow keys or `PgUp` / `PgDn` | +| Exit scrollback mode | `Esc` or `q` | + +To increase the scrollback buffer for a session (default is only 100 lines): +``` +Ctrl-A then :scrollback 5000 +``` + +To set it permanently, add this to `~/.screenrc`: +``` +defscrollback 5000 +``` + +Useful when reviewing long `show config` or `show interfaces` output during console sessions. + +--- + ## What This Tool Does Not Do - Does not manage OPNsense, pfSense, or any other device directly (OPNsense integration is read/sync only) From 504a55fb2bb51fa0ac5f148c970bd330a4c92c05 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Mar 2026 04:23:56 +0000 Subject: [PATCH 16/16] Increase screen scrollback buffer to 10000 lines https://claude.ai/code/session_01JR2EMK7rwrZJowpstcaxQ6 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5f7038b..920de97 100644 --- a/README.md +++ b/README.md @@ -729,12 +729,12 @@ By default `screen` does not let you scroll up through output. Enable it with co To increase the scrollback buffer for a session (default is only 100 lines): ``` -Ctrl-A then :scrollback 5000 +Ctrl-A then :scrollback 10000 ``` To set it permanently, add this to `~/.screenrc`: ``` -defscrollback 5000 +defscrollback 10000 ``` Useful when reviewing long `show config` or `show interfaces` output during console sessions.