Move the port inspector onto device pages
Lint / JS (eslint) (push) Successful in 1m14s
Lint / Python (flake8) (push) Successful in 2m12s
Lint / Notify on failure (push) Skipped
Lint / Deploy (push) Skipped
Security / Python Security (bandit) (push) Successful in 1m3s
Test / Python Tests (pytest) (push) Successful in 1m11s
Lint / Python (flake8) (pull_request) Successful in 1m1s
Lint / JS (eslint) (pull_request) Successful in 10s
Lint / Notify on failure (pull_request) Skipped
Lint / Deploy (pull_request) Skipped
Security / Python Security (bandit) (pull_request) Successful in 56s
Test / Python Tests (pytest) (pull_request) Successful in 1m5s

Replace the all-switches Inspector page with a per-device front panel,
port table and detail panel. /inspector now redirects to the dashboard,
and /device/<name>#port-<n> links directly to a port.

Restore neighbor names after UniFi's LLDP change: read local_port_idx
and resolve neighbors by MAC using Prometheus NIC data, UniFi devices
and the wired client table. This also brings back Run Diagnostics on
server ports, which depends on a named neighbor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-05 10:11:11 -05:00
co-authored by Claude Opus 5.5
parent e32499b7f6
commit bdcfb76c25
13 changed files with 484 additions and 226 deletions
+29 -25
View File
@@ -29,7 +29,7 @@ A Proxmox/Ceph cluster lives or dies by its network, and network problems are sn
**Device pages**: search by hostname, IP, MAC, model or interface from the dashboard, then open one page per host, switch, router, PDU or modem. Each interface or port is one row with its state, speed, traffic and neighbour. Expanding a row shows duplex, carrier flaps, error and drop rates, PoE, LLDP, and **SFP optical readings** (temperature, TX/RX power) with colour-coded thresholds.
**Network inspector**: chassis diagrams for each UniFi switch. Click a port for its stats, LLDP neighbour and a side-by-side switch/server path check.
**Port inspector**: each switch and router page has a front-panel diagram. Click a port for its stats, LLDP neighbour and a side-by-side switch/server path check.
![Inspector](docs/img/inspector-port.png)
@@ -124,8 +124,9 @@ interface and port names, and LLDP neighbors.
- **WAN** (routers): connection state, speed, latency, availability
- **Connected to** (hosts): switch ports that see this host over LLDP
- **Outlets** (PDUs): per-outlet state, power, and auto power-cycle setting
- **Port inspector** (switches, routers): see below
- **Interfaces / Ports**: one row each with state, speed, RX/TX rate, and neighbor, filterable.
Expanding a row shows duplex, auto-negotiation, carrier changes, error and drop rates,
Expanding a host's row shows duplex, auto-negotiation, carrier changes, error and drop rates,
traffic bars, PoE, LLDP detail, and the **SFP / optical panel** (vendor/PN, temperature, voltage,
bias, TX/RX power, RX−TX delta)
- **WAN / Fiber** (modem): the same panel as the dashboard
@@ -134,9 +135,9 @@ These replaced the Link Debug page; `/links` now redirects to the dashboard. Hos
sees but `config.json` doesn't list still get a page, with their state shown as unknown because
they aren't alerted on.
### Inspector (`/inspector`)
Visual switch chassis diagrams. Each switch is rendered model-accurately using
layout config in the template (`SWITCH_LAYOUTS`).
### Port inspector (on switch and router device pages)
A model-accurate front panel, drawn from the layout config in `static/port-inspector.js`
(`SWITCH_LAYOUTS`). `/inspector` now redirects to the dashboard. Link to a port with `/device/<name>#port-<n>`.
**Port block colours:**
| Colour | State |
@@ -147,7 +148,7 @@ layout config in the template (`SWITCH_LAYOUTS`).
| Grey | Down |
| White outline | Currently selected |
**Clicking a port** opens the right-side detail panel showing:
**Clicking a port**, on the panel or in the port table, opens the detail panel showing:
- Link stats (status, speed, duplex, auto-neg, media type)
- PoE (class, max wattage, current draw, mode)
- Traffic (TX/RX rates)
@@ -157,10 +158,15 @@ layout config in the template (`SWITCH_LAYOUTS`).
two-column comparison of the switch port stats vs. the server NIC stats,
including SFP DOM data if the server side has an SFP module
**LLDP path debug requirements:**
1. Server must run `lldpd`: `apt install lldpd && systemctl enable --now lldpd`
2. `lldpd` hostname must match the key in `data.hosts` (set via `config.json → hosts`)
3. Switch has LLDP enabled (UniFi default: on)
**Port neighbors**: UniFi's LLDP data identifies a neighbor only by MAC, so Gandalf names it by matching that MAC
against host NICs (`node_network_info` from node_exporter) and UniFi devices, then falling back to management IPs.
Servers that don't send LLDP are found through UniFi's wired client table. Uplink ports are skipped there, and
neighbors that can't be matched show their MAC. Bonded NICs share a MAC, so only one member's port may be identified.
**Path check and diagnostics requirements:**
1. The server's node_exporter is scraped, so its NIC MACs are in Prometheus
2. The Pulse worker can SSH to the server
3. Optional: `lldpd` on the server lets diagnostics confirm the cabling from the server's side
**Supported switch models** (set `SWITCH_LAYOUTS` keys to your UniFi model codes):
@@ -468,16 +474,15 @@ after `ldap.cache_ttl` seconds or can be removed from `ldap.cache_dir`.
- Confirm `sshpass` + SSH access from the Pulse worker to each Proxmox host
- Only interfaces with physical SFP modules return DOM data (`ethtool -m`)
### Inspector: path debug section not appearing
- Requires LLDP: run `apt install lldpd && systemctl enable --now lldpd` on each server
- The LLDP `system_name` broadcast by `lldpd` must match the hostname in `config.json → hosts[].name`
- Override: `echo 'configure system hostname large1' > /etc/lldpd.d/hostname.conf && systemctl restart lldpd`
- Allow up to 2 poll cycles (240s) after installing lldpd for LLDP table to populate
### Port inspector: path check or Run Diagnostics not appearing
- These need the port's neighbor to be a host with link stats. A port showing only a MAC couldn't be matched
- Check that the server's node_exporter is scraped and reports `node_network_info` for that NIC
- Hosts missing from `config.json` are named by IP, so their ports may not match what you expect
### Inspector: switch chassis shows as flat list (no layout)
- The switch's `model` field from UniFi doesn't match any key in `SWITCH_LAYOUTS` in `inspector.html`
- Check the UniFi API: the model appears in the `link_stats` API response under `unifi_switches.<name>.model`
- Add the model key to `SWITCH_LAYOUTS` in `inspector.html` with the correct row/SFP layout
### Port inspector: front panel shows as a flat list (no layout)
- The switch's `model` field from UniFi doesn't match any key in `SWITCH_LAYOUTS` in `port-inspector.js`
- The model is shown on the device page
- Add the model key to `SWITCH_LAYOUTS` in `port-inspector.js` with the correct row/SFP layout
### Baseline re-initializing on every restart
- `interface_baseline` is stored in the `monitor_state` DB table; survives restarts
@@ -519,13 +524,13 @@ gandalf/
├── static/
│ ├── style.css # Terminal aesthetic CSS (CRT scanlines, green-on-black)
│ ├── app.js # Dashboard JS (auto-refresh, events, suppress modal, search, modem panel)
│ └── device.js # Device page rendering
│ ├── device.js # Device page rendering
│ └── port-inspector.js # Switch front panel, port panel, path check, diagnostics
└── templates/
├── base.html # Shared layout (header, nav, footer)
├── index.html # Dashboard page
├── device.html # Per-device page
├── _modem_panel.html # WAN / Fiber panel, shared by the dashboard and modem page
├── inspector.html # Visual switch inspector + LLDP path debug
└── suppressions.html # Suppression management page
```
@@ -540,11 +545,10 @@ gandalf/
4. Restart monitor: `systemctl restart gandalf-monitor`
5. For SFP DOM / ethtool: ensure the host is SSH-accessible from the Pulse worker
### Adding a New Switch Layout (Inspector)
### Adding a New Switch Layout
Find the UniFi model code for the switch (it appears in the `/api/links` JSON response
under `unifi_switches.<switch_name>.model`), then add to `SWITCH_LAYOUTS` in
`templates/inspector.html`:
Find the UniFi model code for the switch (shown on its device page), then add to `SWITCH_LAYOUTS` in
`static/port-inspector.js`:
```javascript
'MYNEWMODEL': {