diff --git a/CLAUDE.md b/CLAUDE.md index fdf8301..ee46429 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,12 +12,15 @@ is the engineering counterpart. ## 1. What this app does -Electron desktop client for two bulk operations against **Ciena -MCMS 6.2** PON Manager: +Electron desktop client for operating a **Ciena MCMS 6.2** PON Manager +fleet. Top-level **tabs** (Dashboard / ONU / OLT) switch between: -1. **Bulk ONU firmware upgrades** (the original purpose). -2. **Bulk OLT PON flooding-mode changes** (private↔auto on NNI services — - see §14). +- **Dashboard** — read-only network telemetry (see §15). +- **ONU** — two sub-tabs: an **Info / stats** page (per-ONU optical/FEC plus + the CPE router's DHCPv4/v6 lease health, §17) and the **Fleet & upgrade** + flow (the original purpose: bulk ONU firmware upgrades + verify). +- **OLT** — bulk OLT NNI-service edits: PON flooding mode (private↔auto) and + DHCPv4/v6 filter (pass↔umt), see §14. Reference deployment: GNXS / Tibit MicroPlug ONUs running Interos Everest (EV051xxR / EV052xxR firmware family) behind Tibit OLTs. @@ -116,12 +119,16 @@ stream progress events back to the renderer. | `api:listFleet` | ONU-CFG + ONU-STATE + OLT-STATE merged | — | | `api:fetchStates` | per-ONU state fallback if bulk failed | `states:progress` | | `api:listOnuConfigs` / `api:getOnuConfig` | direct CFG access | — | +| `api:getOnuCpe` | CPE (router) DHCP lease health + OUI vendor | — | +| `api:getOnuDetail` | rich ONU detail: firmware/UNI/alarms/OLT/switch/traffic/CPE | — | +| `api:getOltDetail` | OLT detail: traffic/temp/ONU-counts/laser/alarms/switch | — | | `api:listOnuFirmware` / `api:uploadFirmware` | firmware inventory | — | | `api:planUpgrade` | dry-run plan for selected ONUs | — | | `api:executePerOnu` | Procedure 7 — per-ONU PUTs | `upgrade:progress` | | `api:executeBulkTask` | Procedure 8 — single AUTO-TASK-CFG | — | | `api:getTaskConfig` / `api:getUpgradeStatus` | poll helpers | — | | `api:deleteOnus` | bulk delete (CFG + best-effort STATE) | `delete:progress` | +| `api:dashboardStats` | network telemetry aggregates (Dashboard tab) | — | | `api:listOlts` | OLT-CFG list + per-OLT NNI flood-mode summary | — | | `api:executeFloodChange` | bulk PON flooding-mode flip (GET→plan→PUT) | `olt-flood:progress` | | `api:fetchFecHealth` | pre-flight FEC pre/post + optical | `fec:progress` | @@ -136,8 +143,10 @@ stream progress events back to the renderer. ### Fleet view -- Login with email / password, accept-self-signed-TLS toggle (essential - for lab MCMS installs). +- Login with email / password. TLS to MCMS is always verified + (`rejectUnauthorized: true`); there is no accept-self-signed option — + MCMS must present a valid certificate. The web server can prefill the host + field from `PFW_MCMS_URL`. - Bulk fleet load: `listAllOnuConfigs` + `listAllOnuStates` + `listAllOltStates`, all paginated via the `next` cursor at page size 100 (see §5). @@ -350,8 +359,12 @@ state_collection.STATS["OLT-PON"]["RX Post-FEC BER"] → The pre-flight fetch (`api:fetchFecHealth`) and the verify fetch (`api:fetchOnuSnapshot`) both call `getOnuState(id)` and extract via -`extractOnuHealth`. The time-series stats endpoint is **not used** -anywhere — earlier code that hit it has been removed. +`extractOnuHealth`. For *current* FEC/optical, prefer ONU-STATE. + +The time-series endpoint **is** used, but only for the ONU detail page's +traffic sparkline (`getOnuStatsSeries` → `api:getOnuDetail`, §17): it pulls +the last ~hour of samples for the down/up rate history. Don't use it for the +live FEC pill — that stays on ONU-STATE. `extractOnuHealth` accepts both shapes: the bare state doc (`{STATS: ...}`) and the UI-helper wrapped form @@ -653,48 +666,54 @@ Key procedure citations: --- -## 14. OLT PON flooding mode (bulk private↔auto) +## 14. OLT NNI-service edits (bulk flood mode + DHCP filter) Second bulk feature, added after the firmware upgrader. Lets an operator -flip the PON flooding mode of OLT NNI services across many OLTs at once. +edit OLT NNI services across many OLTs at once. Two concerns, applied +together in one GET→edit→PUT per OLT: -### The mutation rule (the whole feature in one fact) +### The two mutation rules -On Tibit OLTs, a service's flooding mode is encoded by the **presence or -absence of one key** on its `OLT-CFG["NNI Networks"][i]` entry: +Both live on each `OLT-CFG["NNI Networks"][i]` entry, both confirmed +empirically from operator paste-backs of `s0.c76.c0`: -- **`PON FLOOD ID` key present** (any value, including `0`) → **private** -- **`PON FLOOD ID` key absent** → **auto** +1. **Flooding mode** — the **presence/absence of the `PON FLOOD ID` key**: + present (any value, incl. `0`) → **private**; absent → **auto**. So + private→auto is `delete entry['PON FLOOD ID']`; auto→private is + `entry['PON FLOOD ID'] = 0`. +2. **DHCP filter mode** — a **value** under the nested `Filter` object: + `entry.Filter.DHCPv4` / `entry.Filter.DHCPv6`, e.g. `"pass"` ↔ `"umt"` + (siblings `EAPOL`/`PPPoE`/`NDP` are left alone). Value replacement, not + key presence. **Replace-only-when-present**: an entry with no `Filter` + (or no such key) is skipped, never invented. -So private→auto is `delete entry['PON FLOOD ID']`; auto→private is -`entry['PON FLOOD ID'] = 0`. Everything else on the entry (`TAG MATCH`, -`Learning Limit`, …) and the rest of the OLT-CFG is preserved. This was -confirmed empirically by diffing two paste-backs of the same OLT -(`24:65:e1:cc:af:1e`, NNI `s0.c76.c0`) in private vs auto — the *only* -difference was that key. - -The default action ships as: TAG MATCH = `s0.c76.c0`, mode `private` → -`auto`. Both directions are wired so a change can be rolled back from the -same UI. +Everything else on the entry and the rest of the OLT-CFG is preserved. +Default actions: TAG MATCH `s0.c76.c0`, flood `private→auto`, DHCPv4/v6 +`pass→umt`. All directions are wired, so changes roll back from the same UI. ### Data flow ``` listOlts ─► McmsClient.listAllOltConfigs ─► summarizeOltNniNetworks (pure) -executeFloodChange ─► per-OLT: getOltConfig ─► planFloodChange (pure, no-mutate) - ─► putOltConfig (full-doc PUT) +executeFloodChange ─► per-OLT: getOltConfig ─► planNniEdit (pure, no-mutate) + (channel name kept for wire compat) ─► putOltConfig (full-doc PUT) ``` -- **Pure helpers** live at the bottom of `src/mcms-api.js`: - `floodModeOfNni`, `summarizeOltNniNetworks`, `tagPatternToRegex`, - `planFloodChange`. `planFloodChange` clones the `NNI Networks` array and - never mutates its input — it returns `{ newDoc, changes, unchanged }`. +- **Pure helpers** at the bottom of `src/mcms-api.js`: `floodModeOfNni`, + `dhcpModeOfNni`, `summarizeOltNniNetworks`, `tagPatternToRegex`, + `planNniEdit`. `planNniEdit(oltCfg, { tagPattern, flood, dhcpv4, dhcpv6 })` + clones the `NNI Networks` array (and any edited `Filter`) and never mutates + its input. Returns `{ newDoc, changes, unchanged }`, where a change is + `{ index, tagMatch, edits: [{ type, from, to }] }` — `type` is `'flood'`, + `'DHCPv4'`, or `'DHCPv6'`, so one entry can carry several edits. +- Each concern is optional (`flood`/`dhcpv4`/`dhcpv6` may be `null`); the UI + has three independent action dropdowns. - **TAG MATCH pattern** supports exact strings, `*`/`""` (match all), and `*` globs (e.g. `s0.c76.*`). `tagPatternToRegex` escapes regex metachars then turns `*` into `.*`. -- **`fromMode` guard**: only entries currently in `fromMode` are flipped; - entries already in the target mode are recorded in `unchanged` (the - per-OLT `noop` case), so re-running is idempotent. +- **`from` guards**: only entries currently on the `from` mode/value are + flipped; entries already at target are left in `unchanged` (the per-OLT + `noop` case), so re-running is idempotent. - **Concurrency 3** on the write path (vs 5 for ONU delete, 8 for state fetch) — OLT writes carry far more blast radius. - **PUT-as-replace**, same gotcha as ONU-CFG (§5.7): the handler GETs the @@ -721,4 +740,197 @@ entries pass through untouched. --- +## 15. Tabbed navigation & Dashboard + +### Tabs + +The topbar holds a `