Multi-vendor Automation
Capstone: One Workflow, Three Vendors
What you'll learn
- Combining driver selection, batched getters, and failure isolation into one script
- Why this demonstrates something neither Nornir nor NAPALM alone could show as clearly
- Reading a real, verified end-to-end run and its JSON report
- What's next: Nornir + NAPALM stay the pattern for any future vendor-aware module
Listen to a guided explanation of this lesson.
Spoken narration isn't supported in this browser.
What "one workflow, three vendors" actually means
The one thing this capstone must show
Not another VLAN check, and not a second single-vendor NAPALM compliance report (Phase 3.5 already built that). This capstone's entire point: the SAME validation function, unmodified, correctly evaluates a Cisco-like host, an Arista-like host, and a Juniper-like host -- because the getter's shape is guaranteed and the driver was selected automatically from inventory, not because the code branches per vendor.
- 4-host inventorySW1/SW2/SW3 mock-driven "vendors", SW4 unreachable
- nr.run(task=napalm_get, getters=[...])Lesson 3 -- one call, every host
- get_network_driver(host.platform)Lesson 2 -- automatic, per host
- normalized get_facts() / get_interfaces()same shape, different real values
- one desired-state check, applied identically
- compliance classification -> JSON report
The full script
import json
import os
from nornir import InitNornir
from nornir_napalm.plugins.tasks import napalm_get
EXPECTED_DESCRIPTION = "mgmt-uplink"
nr = InitNornir(
inventory={
"plugin": "SimpleInventory",
"options": {
"host_file": "inventory/hosts.yaml",
"group_file": "inventory/groups.yaml",
"defaults_file": "inventory/defaults.yaml",
},
},
)
nr.inventory.defaults.username = os.environ.get("NAPALM_USERNAME", "")
nr.inventory.defaults.password = os.environ.get("NAPALM_PASSWORD", "")
agg = nr.run(task=napalm_get, getters=["get_facts", "get_interfaces"])
report = {"compliant": [], "non_compliant": [], "unreachable": []}
for hostname, multi_result in agg.items():
r = multi_result[0]
vendor_label = nr.inventory.hosts[hostname]["vendor_label"]
if r.failed:
report["unreachable"].append({
"host": hostname, "vendor_label": vendor_label, "detail": str(r.exception),
})
continue
facts = r.result["get_facts"]
interfaces = r.result["get_interfaces"]
# Find the management interface WITHOUT hardcoding a per-vendor name --
# Cisco calls it GigabitEthernet0/1, Arista Ethernet1, Juniper ge-0/0/0.
# facts["interface_list"][0] works identically for all three.
mgmt_interface = facts["interface_list"][0]
description = interfaces[mgmt_interface]["description"]
if description == EXPECTED_DESCRIPTION:
report["compliant"].append({
"host": hostname, "vendor_label": vendor_label,
"vendor": facts["vendor"], "interface": mgmt_interface,
})
else:
report["non_compliant"].append({
"host": hostname, "vendor_label": vendor_label,
"vendor": facts["vendor"], "interface": mgmt_interface,
"detail": f"description mismatch: expected '{EXPECTED_DESCRIPTION}', got '{description}'",
})
print(f"Checked: {len(agg)} devices")
print(f"compliant: {len(report['compliant'])}")
print(f"non-compliant: {len(report['non_compliant'])}")
print(f"unreachable: {len(report['unreachable'])}")
with open("multivendor_report.json", "w") as f:
json.dump(report, f, indent=2)
print("Report written to multivendor_report.json")The complete, real, verified capstone script. Note there is no if vendor == 'cisco' anywhere -- facts['interface_list'][0] finds the management interface identically regardless of naming convention.
Real, verified end-to-end output
Checked: 4 devices
compliant: 2
non-compliant: 1
unreachable: 1
Report written to multivendor_report.jsonReal, verified output from an actual run of the script above.
{
"compliant": [
{ "host": "SW1", "vendor_label": "Cisco-like", "vendor": "Cisco", "interface": "GigabitEthernet0/1" },
{ "host": "SW2", "vendor_label": "Arista-like", "vendor": "Arista", "interface": "Ethernet1" }
],
"non_compliant": [
{ "host": "SW3", "vendor_label": "Juniper-like", "vendor": "Juniper", "interface": "ge-0/0/0",
"detail": "description mismatch: expected 'mgmt-uplink', got 'uplink'" }
],
"unreachable": [
{ "host": "SW4", "vendor_label": "Cisco-like", "detail": "You told me to do this" }
]
}Real, verified file content -- grep-confirmed to contain no credential of any kind. Three different vendor labels, three different real interface names, one validation function.
Option A vs. Option B, and what B never means here
Never claim what wasn't tested
Option A -- pointing this exact script at real ios/eos/junos drivers against real, responding devices of each vendor -- was never executed. No multi-vendor lab was available in this authoring environment. The 'Cisco-like'/'Arista-like'/'Juniper-like' labels describe fixture content designed to resemble typical output, not verified real-device behavior. Only the mechanism (driver selection, batched getters, per-device failure isolation) is Category A -- genuinely executed. Do not read this capstone as "multi-vendor support verified."
What's next
The pattern, not a new one
Nornir + NAPALM (via nornir_napalm where vendor-neutral state matters, or nornir_netmiko where a single vendor's CLI is enough) is now the reusable pattern this curriculum expects going forward. NetBox / Source of Truth, the next planned module, is about modeling that same platform-aware inventory as external data rather than static YAML files -- not a new orchestration or normalization mechanism.
Key takeaways
- This capstone combines every piece of the module: automatic driver selection from platform, batched napalm_get across a mixed fleet, and per-device failure isolation -- all verified together in one real, runnable script.
- The validation function never hardcodes a vendor-specific interface name -- facts['interface_list'][0] works identically across all three fixture-driven 'vendors', the concrete payoff of normalization.
- Real, verified run: 2 compliant, 1 non-compliant, 1 unreachable across the 4-device fleet -- written to multivendor_report.json with zero credentials on disk.
- Only the integration mechanism is Category A here -- vendor-specific real-device behavior was never tested and is never claimed to be.
- Next: NetBox / Source of Truth -- modeling this same platform-aware inventory as external data, not a new orchestration mechanism.