NAPALM for Network Engineers
Capstone: A Multi-Vendor-Ready State & Compliance Report
What you'll learn
- Combining drivers, getters, and desired-state validation into one script
- Where a real device (Option A) fits versus the mock-driver path (Option B)
- Reading a real, verified end-to-end run and its JSON report
- What's next: Multi-vendor Automation, where Nornir + NAPALM integrate
Listen to a guided explanation of this lesson.
Spoken narration isn't supported in this browser.
Why this capstone isn't another VLAN check
A deliberate scenario change
Every capstone since Reliable Automation has validated VLAN compliance. This one doesn't -- not because VLANs stopped mattering, but because repeating that scenario again wouldn't demonstrate anything NAPALM specifically adds. This capstone validates facts, interface state, and management IP addressing -- exactly the structured getters this module actually taught.
- SW1-SW5 inventorythe same fleet since Phase 2.6
- get_network_driver("mock")Lesson 2 + 6
- get_facts() / get_interfaces() / get_interfaces_ip()Lesson 3
- desired-state validationhostname, interface description, management IP
- compliance classification -> JSON report
The full script
import json
import os
from napalm import get_network_driver
from napalm.base.exceptions import ConnectionException, ConnectAuthError
MGMT_INTERFACE = "GigabitEthernet0/1"
EXPECTED_DESCRIPTION = "mgmt-uplink"
EXPECTED_IPS = {
"SW1": "10.10.10.1", "SW2": "10.10.10.2", "SW3": "10.10.10.3",
"SW4": "10.10.10.4", "SW5": "10.10.10.5",
}
FLEET = ["SW1", "SW2", "SW3", "SW4", "SW5"]
FAIL_ON_OPEN = {"SW4"} # Option B: scripted "unreachable" via Lesson 6's fail_on_open
def check_device(hostname):
driver_cls = get_network_driver("mock")
optional_args = {"path": os.path.join("fixtures", hostname.lower())}
if hostname in FAIL_ON_OPEN:
optional_args["fail_on_open"] = True
driver = driver_cls(
hostname=hostname,
username=os.environ.get("NAPALM_USERNAME", ""),
password=os.environ.get("NAPALM_PASSWORD", ""),
optional_args=optional_args,
)
try:
driver.open()
except ConnectionException as e:
return {"host": hostname, "status": "unreachable", "detail": str(e)}
try:
facts = driver.get_facts()
interfaces = driver.get_interfaces()
ips = driver.get_interfaces_ip()
except ConnectAuthError as e:
driver.close()
return {"host": hostname, "status": "auth-failed", "detail": str(e)}
driver.close()
issues = []
if facts["hostname"] != hostname:
issues.append(f"hostname mismatch: expected {hostname}, got {facts['hostname']}")
iface = interfaces.get(MGMT_INTERFACE)
if iface is None:
issues.append(f"{MGMT_INTERFACE} missing from get_interfaces()")
elif iface["description"] != EXPECTED_DESCRIPTION:
issues.append(
f"{MGMT_INTERFACE} description mismatch: expected "
f"'{EXPECTED_DESCRIPTION}', got '{iface['description']}'"
)
iface_ips = ips.get(MGMT_INTERFACE, {}).get("ipv4", {})
expected_ip = EXPECTED_IPS[hostname]
if expected_ip not in iface_ips:
issues.append(f"{MGMT_INTERFACE} IP mismatch: expected {expected_ip}, got {list(iface_ips.keys())}")
if issues:
return {"host": hostname, "status": "non-compliant", "detail": issues}
return {"host": hostname, "status": "compliant", "detail": "facts/interfaces/IP all match desired state"}
report = {"compliant": [], "non_compliant": [], "unreachable": [], "auth_failed": []}
key_by_status = {"compliant": "compliant", "non-compliant": "non_compliant",
"unreachable": "unreachable", "auth-failed": "auth_failed"}
for hostname in FLEET:
result = check_device(hostname)
report[key_by_status[result["status"]]].append(result)
print(f"Checked: {len(FLEET)} devices")
print(f"compliant: {len(report['compliant'])}")
print(f"non-compliant: {len(report['non_compliant'])}")
print(f"unreachable: {len(report['unreachable'])}")
print(f"auth-failed: {len(report['auth_failed'])}")
with open("napalm_report.json", "w") as f:
json.dump(report, f, indent=2)
print("Report written to napalm_report.json")The complete, real, verified capstone script. Reuses the SW1-SW5 fleet's IP scheme (10.10.10.1-5) from every prior module, and Lesson 6's fail_on_open/exception-fixture mechanisms for the failure scenarios.
Real, verified end-to-end output
Checked: 5 devices
compliant: 1
non-compliant: 2
unreachable: 1
auth-failed: 1
Report written to napalm_report.jsonReal, verified output from an actual run of the script above.
{
"compliant": [
{ "host": "SW1", "status": "compliant", "detail": "facts/interfaces/IP all match desired state" }
],
"non_compliant": [
{ "host": "SW2", "status": "non-compliant",
"detail": ["GigabitEthernet0/1 description mismatch: expected 'mgmt-uplink', got 'uplink'"] },
{ "host": "SW3", "status": "non-compliant",
"detail": ["GigabitEthernet0/1 IP mismatch: expected 10.10.10.3, got ['10.10.10.99']"] }
],
"unreachable": [
{ "host": "SW4", "status": "unreachable", "detail": "You told me to do this" }
],
"auth_failed": [
{ "host": "SW5", "status": "auth-failed", "detail": "SW5 rejected the credentials" }
]
}Real, verified file content -- grep-confirmed to contain no credential of any kind. Note SW2 and SW3 fail for two genuinely different reasons -- one from get_interfaces(), one from get_interfaces_ip() -- not the same check twice.
Option A vs. Option B
Same pattern as every prior capstone
Option A: swap check_device()'s driver_cls from get_network_driver("mock") to get_network_driver("ios") and point optional_args at a real device instead of a fixture path -- everything else (the desired-state checks, the report structure) stays identical. Never executed here -- no real device was available in this authoring environment, consistent with every module since Phase 2.6. Option B (used above): NAPALM's own mock driver, Lesson 6's real, verified mechanism -- not a hand-rolled simulation.
What's next
One vendor's driver so far
Every driver actually exercised in this module was IOS-family (real execution) or the vendor-neutral mock driver. Multi-vendor Automation, the next planned module, is where Nornir's orchestration (Phase 3.4) and NAPALM's normalization (this module) combine through nornir_napalm -- running the exact getters this module taught, across many devices at once, the way Nornir already runs Netmiko tasks. Not implemented here.
Key takeaways
- This capstone combines every piece of the module: driver instantiation, get_facts()/get_interfaces()/get_interfaces_ip(), and the mock-driver testing strategy -- all verified together in one real, runnable script.
- Deliberately not another VLAN check -- this capstone validates facts, interface state, and management IP addressing, exactly what NAPALM's normalized getters actually add.
- Real, verified run: 1 compliant, 2 non-compliant (for two different reasons), 1 unreachable, 1 auth-failed across the 5-device fleet -- written to napalm_report.json with zero credentials on disk.
- Option A (a real driver against a real device) and Option B (the mock driver, used here) differ only in driver_cls and optional_args -- the surrounding workflow is identical.
- Next: Multi-vendor Automation, where Nornir + NAPALM combine through nornir_napalm -- not implemented in this module.