Back to Learn

NAPALM โ€” vendor-neutral network automation

One consistent Python API across Cisco IOS/NX-OS, Arista EOS and Juniper JunOS. Pull structured facts with getters, and merge or replace config with safe diffs, atomic commit and rollback.

Python
Multi-vendor
Cisco IOS / NX-OS
Arista EOS
Juniper JunOS
Config diff
get_driver() opens a uniform connection per platform
compare_config() shows the diff before you commit
commit_config() is atomic with rollback support

Setup, key commands & run examples

Step 0 โ€” install
Setup

Install NAPALM into a virtualenv. It bundles the core drivers for EOS, JunOS, IOS, IOS-XR and NX-OS.

Step 0 โ€” install
bash
python3 -m venv .venv
source .venv/bin/activate
pip install napalm
facts.py โ€” structured getters
get_driver

Open a driver for the platform and call getters. Every getter returns normalised, vendor-independent dictionaries.

facts.py โ€” structured getters
python
import napalm

driver = napalm.get_network_driver("ios")
device = driver(
    hostname="10.10.1.1",
    username="admin",
    password="secret",
)

device.open()
print(device.get_facts())
print(device.get_interfaces_ip())
device.close()
diff.py โ€” preview a change
compare_config

load_merge_candidate stages config without applying it. compare_config returns the diff so you can review before committing.

diff.py โ€” preview a change
python
with driver("10.10.1.1", "admin", "secret") as device:
    device.load_merge_candidate(filename="loopback.cfg")
    print(device.compare_config())
    # discard if it doesn't look right
    device.discard_config()
commit.py โ€” apply & rollback
commit_config

commit_config applies the staged candidate atomically. If something breaks, rollback() restores the previous running config.

commit.py โ€” apply & rollback
python
with driver("10.10.1.1", "admin", "secret") as device:
    device.load_replace_candidate(filename="full_config.cfg")
    if device.compare_config():
        device.commit_config()
    # later, if needed:
    # device.rollback()

Best practices

  • Always diff before commit

    Run compare_config() and review the output (or gate it in CI) so no change is applied blind.

  • Prefer merge over replace early on

    load_merge_candidate is additive and safer; move to load_replace_candidate once you trust your full config templates.

  • Use context managers

    with driver(...) guarantees sessions close even on error, avoiding stale locked candidate configs.

  • Normalise, don't screen-scrape

    Lean on getters (get_facts, get_bgp_neighbors) for stable structured data instead of parsing raw CLI yourself.

Hands-on exercises

Guided practice tasks โ€” copy the starter snippet, run it against a lab device, and extend it to meet the goal.

Exercise 1
Build a fleet facts report

Goal: Loop over three hosts, call get_facts() on each, and print hostname, model and OS version in a table.

exercise_1
python
import napalm

driver = napalm.get_network_driver("ios")
hosts = ["10.10.1.1", "10.10.1.2", "10.10.1.3"]

for host in hosts:
    with driver(host, "admin", "secret") as dev:
        f = dev.get_facts()
        # TODO: print f["hostname"], f["model"], f["os_version"]
        ...
Exercise 2
Safe loopback change with diff gate

Goal: Stage a loopback config, print compare_config(), and only commit when the diff is non-empty.

exercise_2
python
with driver("10.10.1.1", "admin", "secret") as dev:
    dev.load_merge_candidate(config="interface Loopback42\n description napt-lab\n")
    diff = dev.compare_config()
    print(diff)
    # TODO: if diff: dev.commit_config() else dev.discard_config()
Exercise 3
Validate BGP neighbours

Goal: Use get_bgp_neighbors() and assert every neighbour in the default VRF is 'up'.

exercise_3
python
with driver("10.10.1.1", "admin", "secret") as dev:
    bgp = dev.get_bgp_neighbors()
    peers = bgp["global"]["peers"]
    # TODO: for ip, data in peers.items(): assert data["is_up"], ip

Curated videos & guides

Checklist โ€” pitfalls, gotchas & verification

Common pitfalls

  • โ€ขForgetting to close the session โ€” use a context manager so locked candidate configs are released.
  • โ€ขUsing load_replace_candidate before you trust your full templates can wipe unmanaged config.
  • โ€ขAssuming all getters exist on every platform โ€” support varies per driver.

Gotchas to watch

  • โ€ขcompare_config() returns an empty string when there is no change โ€” check truthiness, not None.
  • โ€ขSome platforms need optional_args (e.g. transport, secret) for enable mode.
  • โ€ขcommit_config() behaviour differs: JunOS is truly atomic, IOS emulates it.

Verify it works

  • โ€ขRun get_facts() first to confirm the connection and driver are correct.
  • โ€ขDiff every change with compare_config() before commit.
  • โ€ขAfter commit, re-read the relevant getter (e.g. get_interfaces_ip) to confirm the new state.

NAPALM shines as a config and state abstraction layer. Pair it with Nornir to run the same getters and commits across a whole inventory in parallel.