Senior network engineering — AI-native network operations tooling.
A Flask dashboard on a live multivendor CLOS-EVPN fabric — Arista cEOS, Nokia SR Linux, and FRRouting — that shows the honest truth about NAPALM’s multivendor promise, node by node and getter by getter. Plus a secure Command Console running 2,361 curated operational commands against real lab gear.
This site describes the shipped Python lab: 19 nodes, 3 vendors (cEOS · SR Linux · FRR). Junos is a core NAPALM driver in DRIVER_MAP — it is not in the containerlab topology. A cRPD leaf, gNMI subscribe, and a napalm-frr stub are the next increment, documented below as plan, not as deployed. The live coverage matrix is the Flask dashboard after you clone and run the lab — it is not a route on this static Pages site.
NAPALM’s pitch is a single Python API that talks to any vendor — call get_bgp_neighbors() and you get the same normalized data structure whether the box runs Arista EOS, Cisco IOS-XR, or Juniper Junos. It’s a genuinely good abstraction, and for the vendors with first-class drivers it largely delivers. But the moment you build a real multivendor fabric, the promise frays in ways the marketing never mentions — and the gaps are exactly where you find out the hard way, usually mid-automation, against production.
This lab makes those gaps visible and reproducible instead of surprising. It runs a live multivendor CLOS-EVPN fabric (3 spine + 6 leaf, mixing Arista cEOS, Nokia SR Linux, and FRRouting) plus a 10-router 3-Tier FRR network, and shows you — node by node, getter by getter — what NAPALM actually returns versus what it claims to.
The eos driver ships in core NAPALM and talks eAPI over HTTPS. The standard getters work. This is what “NAPALM works” looks like. Official EOS still has method-level holes (no get_ipv6_neighbors_table, no get_ntp_peers).
No core driver — coverage comes from the separate napalm-srl package over JSON-RPC. Real, but a different package on a different cadence. get_bgp_neighbors is a known parse gap. Labeled community-sourced, not pretended equal.
No NAPALM driver exists. The lab doesn’t fake one — FRR is collected via docker exec … vtysh -c ", mapped into NAPALM-shaped dicts and tagged method: exec, napalm_supported: false. You see the seam.
Core junos driver over NETCONF/SSH. In DRIVER_MAP. Official matrix: no get_arp_table. Not in the 19-node topology. A cRPD leaf is the next CLOS increment — first-class is not every getter, and it is not deployed here.
On Docker Desktop for Mac, the host has no route to container management IPs — you can’t open an eAPI/JSON-RPC session from your laptop to 172.20.20.x the way native Linux Docker allows. NAPALM needs that L3 reachability, so on a Mac it simply can’t connect. The fix is a sidecar pattern: a napalm-runner container attached to the lab’s management networks runs the real NAPALM drivers from inside the fabric, and the host dispatches collection to it over docker exec (argv list, never a shell). Because every collection path — runner for eos/srl, vtysh for FRR — is docker exec-based, the exact same code runs identically on macOS and Linux. No VPN, no route add, no host-networking hacks, no “works on my Linux box” caveat.
Run real getters against real (containerized) multivendor devices instead of reading driver docs and guessing what the output looks like.
Decide whether NAPALM can underpin an automation platform across an Arista/Nokia/FRR estate — with the coverage matrix before writing thousands of lines against an abstraction that’s thinner than advertised on half the fleet.
A reproducible, disposable fabric to write and test collection/audit code against — known topology, known BGP/EVPN state, zero risk to production.
A demonstrable running artifact: a live multivendor fabric, a real NAPALM coverage matrix, and a security-conscious command console — concrete evidence of containerlab fluency, not a slide deck.
Every benefit below is computed from real collection against a live fabric — no marketing-grade “supports all vendors.”
Per-node, per-getter pass/fail computed from real collection — napalm_native vs exec_fallback vs unreachable, broken out by driver. You see exactly where the abstraction holds and where it doesn’t.
A curated catalog of 2,361 single-line multivendor commands, executed live against lab nodes with the right CLI wrapper per vendor: Cli for cEOS, vtysh for FRR, sr_cli for SR Linux. 634 live-runnable on this fabric; Cisco and Juniper rows are reference-only.
Arista cEOS, Nokia SR Linux, and FRRouting in one CLOS-EVPN fabric (3+6) plus a 10-router 3-Tier FRR network. True multivendor, not single-vendor cosplay — the mix is where tooling actually breaks.
A napalm-runner container runs the real drivers from inside the fabric, dispatched over docker exec. Sidesteps the Docker-Desktop routing wall — identical behavior on macOS and Linux.
Hostnames validated against a fabric allowlist; commands run as an argv list (no shell, no injection); a read-only guard permits only show/display/get/ping-class verbs; pipe filters are an allowlist; newlines and control chars rejected.
A fixed containerlab topology with known state means repeatable results run-to-run. 76 hermetic pytest tests cover the collection backend, the security guard, and the catalog loader. GitHub Actions runs them on every push and pull request.
No papered-over seams. Each vendor is collected the way it actually has to be, and labeled honestly in the UI.
| Vendor / NOS | NAPALM tier | What you get |
|---|---|---|
| Arista cEOS | Full · shipped | Core NAPALM driver. Standard getters work. Falls back to Cli -c "show version | json" if eAPI is down. |
| Nokia SR Linux | Partial · shipped | Community package, separate maintenance cadence. get_bgp_neighbors is a known parse gap. Labeled community-sourced — not pretended equal. |
| FRRouting | Exec fallback · shipped | No NAPALM driver. Collected via vtysh, mapped into NAPALM-shaped dicts. Tagged napalm_supported:false; LLDP / environment marked unsupported with reason. |
| Juniper Junos | Mapped · not shipped | Core driver in DRIVER_MAP. Official NAPALM matrix: no get_arp_table. Not part of the 19-node topology. A cRPD leaf is plan, not inventory. |
Same intent, different CLI. SR Linux has no show running-config — it uses info. A Cisco/Arista command on an SRL node returns an honest device parse error, not a tool bug. Universal intents v1 (version, BGP, OSPF, interfaces, counters, routes, memory, CPU) verified live: 24/24 across the three shipped vendors.
| Task | Arista cEOS | Nokia SR Linux |
|---|---|---|
| Running config | show running-config | info from running |
| BGP summary | show ip bgp summary | show network-instance default protocols bgp neighbor |
| Routing table | show ip route | show …default route-table ipv4-unicast summary |
| Live state | show … | info from state … |
The console ships publicly, so it defaults safe. Full policy in SECURITY.md.
docker exec is invoked with an argv list. There is no shell to inject into. Newlines, control characters, and metacharacters are rejected before exec.
Every target is validated against NODE_INDEX. Arbitrary container names cannot reach the runtime. Unknown fabrics return 400 on the matrix API.
Positive allowlist of operational verbs (show / display / get / ping / info). Pipe filters are display-only; redirect, append, and | bash are writes and are rejected. LAB_CONSOLE_READONLY=1 kills write mode for any exposed deploy.
These are designed and documented. They are not in the shipped 19-node topology. Claiming them as live would violate the honesty contract above.
pytest on every push and pull request (Python 3.12). The README badge now points at the real workflow, not a static shield. JUnit from the live test platform remains optional — it needs a fabric.
A suite whose selector matches zero hosts must ERROR, not export tests="0" JUnit that CI treats as green. Canonical trigger: evpn_bgp × fabric=dcn. Draft: gesh75/napalm-live-lab#2.
v1 is 8 intents, 24/24 on the three shipped vendors. v2 names the CLOS as an EVPN fabric: running_config (info from running on SRL), lldp (honest FRR miss), arp, evpn, ntp.
JSON-RPC poll is how napalm-srl talks. SRL’s native ops plane is gNMI. Subscribe on interface stats, BGP session-state, system, NTP, route table — beside the matrix, not instead of it.
The exec fallback already produces NAPALM-shaped dicts. Extract that mapping as a package. Keep napalm_supported: false until getters complete a live round-trip. Do not pretend FRR is a core driver.
Add one Juniper cRPD leaf so the fabric is core + community + exec + core. Official Junos still has no get_arp_table. Putting a first-class driver next to FRR does not make ARP appear. Not deployed in this repo yet.
NAPALM Live Lab is a Flask dashboard that runs a live, multivendor containerlab fabric — Arista cEOS, Nokia SR Linux, and FRRouting across a 9-node CLOS-EVPN spine/leaf plus a 10-router 3-Tier network — and shows you the truth about NAPALM’s multivendor promise: full coverage on cEOS, partial community coverage on SR Linux, and no driver at all on FRR (collected honestly via vtysh and labeled as such). Junos is a core driver in the map and is not in the topology. Real NAPALM runs inside a sidecar container so the whole thing behaves identically on a macOS laptop and on Linux, with no host-to-container routing hacks. On top of the live coverage matrix it adds a Command Console that runs 2,000+ curated multivendor operational commands behind a read-only, allowlist-guarded, shell-injection-proof execution layer — so you can learn NAPALM, evaluate its real multivendor coverage, and develop lab-driven automation against true multivendor gear without a single piece of physical hardware.