Redesign topology diagram with dual-homed bus layout and improve inspector chassis
- Replace flat topology with tiered bus-bar layout: Internet → UDM-Pro → SVG fork → USW-Agg + Pro 24 PoE → dual-homed servers - Show 10G VLAN90 (Ceph) bus from USW-Agg and 1G DHCP management bus from Pro 24 PoE per host - Add per-host drop wires (solid 10G + dashed 1G) with correct rack positions - Mark large1 as off-rack (dashed border), ZimaBoards as off-rack mon-01/mon-02 - Add topology legend, inter-switch 10G ISL indicator - Add recently resolved events section (last 24h) to dashboard - Add last_seen column and relative timestamps to events table - Add stale data banner when monitoring data >15 min old - Improve inspector chassis with port speed labels, LLDP neighbor info, mounting ears, chassis legend - Add duplex/speed mismatch warnings and carrier changes to path debug panel - Bump updateTopology() to handle both topo-v2-status-* and topo-status-* classes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -84,16 +84,46 @@ function portBlockState(d) {
|
||||
return 'up';
|
||||
}
|
||||
|
||||
// ── Speed label helper ───────────────────────────────────────────────────
|
||||
function portSpeedLabel(port) {
|
||||
if (!port || !port.up) return '';
|
||||
const spd = port.speed; // speed in Mbps from UniFi API
|
||||
if (!spd) return '';
|
||||
if (spd >= 10000) return '10G';
|
||||
if (spd >= 1000) return '1G';
|
||||
if (spd >= 100) return '100M';
|
||||
return spd + 'M';
|
||||
}
|
||||
|
||||
// ── Render a single port block element ──────────────────────────────────
|
||||
function portBlockHtml(idx, port, swName, sfpBlock) {
|
||||
const state = portBlockState(port);
|
||||
const label = sfpBlock ? 'SFP' : idx;
|
||||
const title = port ? escHtml(port.name) : `Port ${idx}`;
|
||||
const sfpCls = sfpBlock ? ' sfp-block' : '';
|
||||
const state = portBlockState(port);
|
||||
const numLabel = sfpBlock ? 'SFP' : idx;
|
||||
const title = port ? escHtml(port.name) : `Port ${idx}`;
|
||||
const sfpCls = sfpBlock ? ' sfp-block' : '';
|
||||
const speedTxt = portSpeedLabel(port);
|
||||
// LLDP neighbor: first 6 chars of hostname
|
||||
const lldpName = (port && port.lldp_table && port.lldp_table.length)
|
||||
? escHtml((port.lldp_table[0].chassis_id_subtype === 'local'
|
||||
? port.lldp_table[0].chassis_id
|
||||
: port.lldp_table[0].system_name || port.lldp_table[0].chassis_id || '').slice(0, 6))
|
||||
: '';
|
||||
const lldpHtml = lldpName ? `<span class="port-lldp">${lldpName}</span>` : '';
|
||||
const speedHtml = speedTxt ? `<span class="port-speed">${speedTxt}</span>` : '';
|
||||
return `<div class="switch-port-block ${state}${sfpCls}"
|
||||
data-switch="${escHtml(swName)}" data-port-idx="${idx}"
|
||||
title="${title}"
|
||||
onclick="selectPort(this)">${label}</div>`;
|
||||
onclick="selectPort(this)"><span class="port-num">${numLabel}</span>${speedHtml}${lldpHtml}</div>`;
|
||||
}
|
||||
|
||||
// ── Chassis legend HTML ──────────────────────────────────────────────────
|
||||
function chassisLegendHtml() {
|
||||
return `<div class="chassis-legend">
|
||||
<div class="chassis-legend-item"><span class="chassis-legend-swatch cls-down"></span>down</div>
|
||||
<div class="chassis-legend-item"><span class="chassis-legend-swatch cls-up"></span>up</div>
|
||||
<div class="chassis-legend-item"><span class="chassis-legend-swatch cls-poe"></span>poe active</div>
|
||||
<div class="chassis-legend-item"><span class="chassis-legend-swatch cls-uplink"></span>uplink</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// ── Render one switch chassis ────────────────────────────────────────────
|
||||
@@ -107,26 +137,38 @@ function renderChassis(swName, sw) {
|
||||
const downCount = totCount - upCount;
|
||||
const meta = [model, `${upCount}/${totCount} up`, downCount ? `${downCount} down` : ''].filter(Boolean).join(' · ');
|
||||
|
||||
// Is this a US24PRO? Used to add group-separator class
|
||||
const isUs24Pro = (model === 'US24PRO');
|
||||
|
||||
let chassisHtml = '';
|
||||
|
||||
if (layout) {
|
||||
const sfpPortSet = new Set(layout.sfp_ports || []);
|
||||
const sfpPortSet = new Set(layout.sfp_ports || []);
|
||||
const sfpSectionSet = new Set(layout.sfp_section || []);
|
||||
|
||||
// Main port rows
|
||||
chassisHtml += '<div class="chassis-rows">';
|
||||
for (const row of layout.rows) {
|
||||
chassisHtml += '<div class="chassis-row">';
|
||||
const rowCls = isUs24Pro ? ' us24pro-row' : '';
|
||||
chassisHtml += `<div class="chassis-row${rowCls}">`;
|
||||
for (const idx of row) {
|
||||
const port = portMap[idx];
|
||||
const isSfp = sfpPortSet.has(idx);
|
||||
const sfpCls = isSfp ? ' sfp-port' : '';
|
||||
const state = portBlockState(port);
|
||||
const title = port ? escHtml(port.name) : `Port ${idx}`;
|
||||
const speedTxt = portSpeedLabel(port);
|
||||
const lldpName = (port && port.lldp_table && port.lldp_table.length)
|
||||
? escHtml((port.lldp_table[0].chassis_id_subtype === 'local'
|
||||
? port.lldp_table[0].chassis_id
|
||||
: port.lldp_table[0].system_name || port.lldp_table[0].chassis_id || '').slice(0, 6))
|
||||
: '';
|
||||
const speedHtml = speedTxt ? `<span class="port-speed">${speedTxt}</span>` : '';
|
||||
const lldpHtml = lldpName ? `<span class="port-lldp">${lldpName}</span>` : '';
|
||||
chassisHtml += `<div class="switch-port-block ${state}${sfpCls}"
|
||||
data-switch="${escHtml(swName)}" data-port-idx="${idx}"
|
||||
title="${title}"
|
||||
onclick="selectPort(this)">${idx}</div>`;
|
||||
onclick="selectPort(this)"><span class="port-num">${idx}</span>${speedHtml}${lldpHtml}</div>`;
|
||||
}
|
||||
chassisHtml += '</div>';
|
||||
}
|
||||
@@ -158,7 +200,12 @@ function renderChassis(swName, sw) {
|
||||
${sw.ip ? `<span class="chassis-ip">${escHtml(sw.ip)}</span>` : ''}
|
||||
<span class="chassis-meta">${escHtml(meta)}</span>
|
||||
</div>
|
||||
<div class="chassis-body">${chassisHtml}</div>
|
||||
<div class="chassis-body">
|
||||
<div class="chassis-ear-l"></div>
|
||||
<div class="chassis-ear-r"></div>
|
||||
${chassisHtml}
|
||||
</div>
|
||||
${chassisLegendHtml()}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user