An OVH/Cacti-style live network weathermap for MikroTik RouterOS devices. It polls your routers over the REST API, auto-discovers the topology from LLDP/MNDP neighbor data, computes live per-link utilization from interface counters, and serves an auto-refreshing HTML dashboard with color-coded links (green → yellow → red).
- Physical-topology auto-discovery — links are matched physical port ↔ physical port by MAC, requiring bidirectional LLDP/MNDP agreement so flooded/indirect neighbors don't create phantom links (robust even with duplicate identities). Logical interfaces (bridges, VLANs/trunks) are ignored.
- Bond aggregation — member links (e.g.
bonding1over 2× SFP+) collapse into a single link with the summed bond speed (2×10G = 20G). - Accurate capacities via
/interface/ethernet/monitornegotiated rate (the configuredspeedis blank on auto-neg SFP+ ports). - Live link colors & widths scaled by
max(rx,tx) / capacity. - D3 force-directed layout with zoom/pan, draggable/pinnable nodes, and a link TTL cache so transient neighbor-refresh gaps don't make nodes flicker.
- Hover tooltips with exact rx/tx, ports, capacity, and router model.
--dumpCLI to debug topology resolution.- Multiple routers from a single YAML file. Pure stdlib HTTP server +
requests/PyYAML. No database.
pip install -r requirements.txt
# Preview with simulated data first (no router needed):
python3 weathermap.py --mock # open http://127.0.0.1:8088
# Then wire up your real devices:
cp routers.example.yaml routers.yaml
$EDITOR routers.yaml
python3 weathermap.py # http://127.0.0.1:8088Bind to all interfaces / change port / poll faster:
python3 weathermap.py --host 0.0.0.0 --port 8088 --interval 3Debug how links are being resolved (interfaces, neighbors, mutual matches, bonds, and the final link list):
python3 weathermap.py --dump --config routers.yaml# Build & run with docker compose (mounts ./routers.yaml into the container)
docker compose up -d # http://127.0.0.1:8088
# Or plain docker:
docker build -t mikrotik-weathermap .
docker run -d --name weathermap -p 8088:8088 \
-v "$PWD/routers.yaml:/config/routers.yaml:ro" \
mikrotik-weathermap
# Preview with simulated data, no config needed:
docker run --rm -p 8088:8088 mikrotik-weathermap --mock-
Enable the REST service (it rides on the
www-ssl/wwwservice):/ip/service/enable www-ssl # https, recommended # or for plain http: /ip/service/enable www -
Create a read-only user for the weathermap:
/user/add name=weathermap group=read password=SECRET -
Make sure neighbor discovery is on (it is by default) so topology can be auto-discovered:
/ip/neighbor/discovery-settings/set discover-interface-list=all
Then add each device to routers.yaml. Use scheme: http / port: 80 if you
only enabled the plain www service.
The server reads rx-byte / tx-byte from /interface on every poll and
divides the byte delta by the elapsed time to get bits/sec. Capacity comes from
the negotiated rate via /interface/ethernet/monitor, then the configured
speed, then default_capacity_bps. Utilization = max(rx,tx) / cap. Bonded
links use the summed bond speed as the denominator.
| File | Purpose |
|---|---|
weathermap.py |
Poller + HTTP/JSON server |
index.html |
Front-end (SVG weathermap, force layout) |
routers.example.yaml |
Copy to routers.yaml and edit |
requirements.txt |
Python deps |
verify_tls: falseis the default because home routers use self-signed certs. Point it at a real cert and settrueif you have one.- Inter-router links require mutual MAC visibility; a port that only sees flooded (indirect) neighbors falls back to a stub rather than a false link.
- The front-end loads D3 from a CDN; the viewing machine needs internet access.
- To run headless without Docker, drop a systemd unit calling
weathermap.py --host 0.0.0.0.
