Add bulk OLT PON flooding-mode tool; restore README; docs
Adds a second bulk workflow alongside the firmware upgrader: changing PON flooding mode (private<->auto) on OLT NNI services across many OLTs. Flooding mode is encoded by the presence of the "PON FLOOD ID" key on each OLT-CFG["NNI Networks"] entry (present = private, absent = auto). private->auto deletes the key; auto->private sets it to 0. The rest of the OLT-CFG is preserved (PUT-as-replace, same as ONU-CFG). - src/mcms-api.js: listAllOltConfigs / getOltConfig / putOltConfig, plus pure helpers floodModeOfNni, summarizeOltNniNetworks, planFloodChange (planFloodChange clones and never mutates its input) - main.js: api:listOlts + api:executeFloodChange IPC handlers (concurrency 3, GET->plan->PUT, streams olt-flood:progress) - preload.js: expose both channels + onFloodProgress - renderer: OLT inspector view (#view-olts), two-step APPLY confirm, auto-saved pon-olt-flood-*.csv result report - Restore README.md (lost to a filesystem error) and document the new feature in README.md and CLAUDE.md (new section 14) Verified: npm run check passes; pure helpers unit-tested for the no-mutation, idempotency, and rollback invariants. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
0eb9b55717
commit
80fd7e8d37
7 changed files with 612 additions and 7 deletions
91
main.js
91
main.js
|
|
@ -8,7 +8,10 @@
|
|||
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { McmsClient, McmsApiError, extractOnuHealth } = require('./src/mcms-api');
|
||||
const {
|
||||
McmsClient, McmsApiError, extractOnuHealth,
|
||||
summarizeOltNniNetworks, planFloodChange,
|
||||
} = require('./src/mcms-api');
|
||||
const { planUpgrade, activeBankPtr, inactiveBank } = require('./src/bank-strategy');
|
||||
|
||||
/** @type {McmsClient | null} */
|
||||
|
|
@ -617,3 +620,89 @@ ipcMain.handle('api:deleteOnus', async (event, { onuIds, concurrency = 5 }) => {
|
|||
return { ok: false, error: toWireError(err) };
|
||||
}
|
||||
});
|
||||
|
||||
// ---- OLT PON flooding mode -------------------------------------------
|
||||
|
||||
/**
|
||||
* Bulk-load every OLT-CFG, summarize each one's NNI Networks against
|
||||
* the given TAG MATCH pattern, and return a flat row per OLT.
|
||||
*/
|
||||
ipcMain.handle('api:listOlts', async (_ev, opts) => {
|
||||
try {
|
||||
const c = requireClient();
|
||||
const tagPattern = (opts && opts.tagPattern) || '';
|
||||
const cfgs = await c.listAllOltConfigs();
|
||||
const rows = cfgs.map((cfg) => {
|
||||
const summary = summarizeOltNniNetworks(cfg, tagPattern);
|
||||
const counts = { private: 0, auto: 0, unknown: 0 };
|
||||
for (const s of summary) counts[s.mode] = (counts[s.mode] || 0) + 1;
|
||||
return {
|
||||
_id: cfg._id,
|
||||
name: cfg?.OLT?.Name || '',
|
||||
location: cfg?.OLT?.Location || '',
|
||||
ponMode: cfg?.OLT?.['PON Mode'] || '',
|
||||
activeFwVersion: cfg?.OLT?.['FW Bank Versions']?.[cfg?.OLT?.['FW Bank Ptr']] || '',
|
||||
matchingNni: summary,
|
||||
counts,
|
||||
};
|
||||
});
|
||||
return { ok: true, data: rows };
|
||||
} catch (err) {
|
||||
return { ok: false, error: toWireError(err) };
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Apply a planned flooding mode change to a list of OLT IDs. For each
|
||||
* OLT we GET the current CFG (to avoid stomping concurrent edits),
|
||||
* run planFloodChange, and PUT the result back. Streams progress.
|
||||
*
|
||||
* Concurrency is kept low (default 3) — OLT writes carry a much larger
|
||||
* blast radius than ONU reads, so we deliberately stay below the ONU
|
||||
* delete (5) and state-fetch (8) limits.
|
||||
*/
|
||||
ipcMain.handle('api:executeFloodChange', async (event, opts) => {
|
||||
try {
|
||||
const c = requireClient();
|
||||
const {
|
||||
oltIds, tagPattern, fromMode = 'private',
|
||||
targetMode = 'auto', ponFloodIdValue = 0,
|
||||
concurrency = 3,
|
||||
} = opts || {};
|
||||
const queue = [...oltIds];
|
||||
const results = [];
|
||||
let done = 0;
|
||||
const total = oltIds.length;
|
||||
|
||||
async function worker() {
|
||||
for (;;) {
|
||||
const id = queue.shift();
|
||||
if (id === undefined) return;
|
||||
let entry = { oltId: id, ok: false };
|
||||
try {
|
||||
const cfg = await c.getOltConfig(id);
|
||||
if (!cfg) throw new Error('OLT not found');
|
||||
const plan = planFloodChange(cfg, {
|
||||
tagPattern, fromMode, targetMode, ponFloodIdValue,
|
||||
});
|
||||
if (plan.changes.length === 0) {
|
||||
entry = { oltId: id, ok: true, noop: true, changes: [], skipped: plan.unchanged };
|
||||
} else {
|
||||
await c.putOltConfig(id, plan.newDoc);
|
||||
entry = { oltId: id, ok: true, changes: plan.changes, skipped: plan.unchanged };
|
||||
}
|
||||
} catch (e) {
|
||||
entry = { oltId: id, ok: false, error: toWireError(e) };
|
||||
}
|
||||
results.push(entry);
|
||||
done += 1;
|
||||
event.sender.send('olt-flood:progress', { ...entry, done, total });
|
||||
}
|
||||
}
|
||||
const workers = Array.from({ length: Math.min(concurrency, total) }, () => worker());
|
||||
await Promise.all(workers);
|
||||
return { ok: true, data: results };
|
||||
} catch (err) {
|
||||
return { ok: false, error: toWireError(err) };
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue