NAPALM: Getters, Config Replace and Rollback¶
NAPALM (Network Automation and Programmability Abstraction Layer with Multivendor support) gives you one Python API across several network operating systems. You get normalised getters that return the same data structure whatever the vendor, and a configuration workflow with candidate configs, diffs, commit and rollback. This article covers the getters, the difference between merge and replace, how compare_config and rollback behave, and when to pick NAPALM over Netmiko.
What NAPALM is¶
NAPALM is a library of drivers. Each driver speaks to its platform in whatever way works best: the IOS driver uses SSH through Netmiko, the Junos driver uses NETCONF, the EOS driver uses eAPI, and the NX-OS driver can use NX-API. You write against one interface and the driver handles the transport.
graph LR
S["Your script"] --> N["NAPALM API"]
N --> I["ios driver<br/>SSH via Netmiko"]
N --> J["junos driver<br/>NETCONF"]
N --> E["eos driver<br/>eAPI"]
N --> X["nxos driver<br/>NX-API or SSH"]
Install it into a virtual environment:
Which drivers ship in the core package and which are community packages has changed between releases, so check the project documentation for your version. For Cisco IOS and IOS XE the driver name is ios.
Connecting¶
import os
from napalm import get_network_driver
driver = get_network_driver("ios")
device = driver(
hostname="192.0.2.10",
username=os.environ["NET_USER"],
password=os.environ["NET_PASS"],
optional_args={"secret": os.environ.get("NET_ENABLE", "")},
)
device.open()
try:
facts = device.get_facts()
print(facts["hostname"], facts["os_version"], facts["uptime"])
finally:
device.close()
optional_args carries driver-specific settings such as the enable secret, a non-default port, or dest_file_system for config replace on IOS. The driver also works as a context manager: with driver(...) as device:.
Getters¶
Getters return plain dictionaries and lists with a documented structure. The same getter gives the same keys on every supported platform, which is the whole point.
interfaces = device.get_interfaces()
for name, data in interfaces.items():
print(name, data["is_up"], data["is_enabled"], data["speed"])
arp = device.get_arp_table()
bgp = device.get_bgp_neighbors()
lldp = device.get_lldp_neighbors()
Commonly used getters include:
| Getter | Returns |
|---|---|
get_facts |
Hostname, vendor, model, OS version, serial, uptime, interface list |
get_interfaces |
Per-interface state, speed, MTU, MAC, description |
get_interfaces_ip |
IPv4 and IPv6 addresses per interface |
get_arp_table |
ARP entries |
get_mac_address_table |
Learned MAC addresses |
get_lldp_neighbors |
Neighbours by local port |
get_bgp_neighbors |
Peers per VRF with state and prefix counts |
get_environment |
Fans, power, temperature, CPU, memory |
get_config |
Running, startup or candidate config |
Not every getter is implemented on every driver. Calling an unimplemented getter raises NotImplementedError, so wrap calls when you work across platforms. The support matrix in the NAPALM documentation lists what each driver covers.
For anything without a getter, device.cli(["show ip route summary"]) returns raw text for each command. That is Netmiko-level work, so you lose the portability.
Merge versus replace¶
NAPALM loads configuration into a candidate and only applies it when you call commit_config(). There are two ways to load:
- Merge (
load_merge_candidate): the candidate is a snippet. It is applied on top of the running config, like typing the lines in config mode. Lines you do not mention stay as they are. Removal needs explicitnocommands. - Replace (
load_replace_candidate): the candidate is the complete intended config. The device ends up matching the file exactly. Anything on the device that is not in the file is removed.
# Merge: add a snippet
device.load_merge_candidate(config="""
interface Loopback100
description NAPALM test
ip address 10.255.100.1 255.255.255.255
""")
# Replace: converge the whole device to a file
device.load_replace_candidate(filename="golden/sw1.cfg")
Replace is the stronger model because the file in Git becomes the truth and drift is removed on every run. It is also the riskier one. A replace file that is missing the management interface, the AAA lines or the SSH settings can cut you off. Always generate replace files from a complete, reviewed source, and test them on a lab device first.
Warning
On IOS and IOS XE the replace workflow relies on configure replace. The driver copies the candidate file to the device (it uses SCP, so the device needs ip scp server enable and an account allowed to use it) and runs configure replace against it. Check the driver documentation for the exact requirements and the dest_file_system option. Whether your platform supports configure replace cleanly depends on the release, so lab it before depending on it.
compare_config¶
After loading a candidate, ask for the diff before you commit anything.
device.load_merge_candidate(filename="changes/vlan-update.cfg")
diff = device.compare_config()
if diff:
print(diff)
else:
print("No changes needed")
device.discard_config()
An empty string means the candidate would change nothing. That is how you get idempotent behaviour: run the script every hour and it only acts when the device has drifted.
The diff format depends on the driver. Junos returns its own diff format, and IOS shows lines to add and remove. Treat the diff as something to display and log, not as data to parse across vendors.
Commit and rollback¶
device.load_replace_candidate(filename="golden/sw1.cfg")
diff = device.compare_config()
print(diff)
answer = input("Commit? [y/N] ")
if answer.lower() == "y":
device.commit_config()
else:
device.discard_config()
commit_config()applies the candidate.discard_config()throws the candidate away without touching the device.rollback()returns the device to the configuration it had before the last commit.
device.commit_config()
# post-change validation
if not device.get_bgp_neighbors()["global"]["peers"]:
device.rollback()
The mechanism differs per platform. On IOS the driver keeps a rollback copy of the config on the device's file system before it commits, and rollback() replaces the running config with that copy. On Junos the platform's own commit history does the work. Do not assume a rollback undoes anything other than configuration: it does not reset BGP sessions that flapped or bring back traffic that was dropped in between.
Some drivers support commit_config(revert_in=<seconds>), a commit-confirmed style safety net where the device reverts by itself unless you confirm. Check the driver support table before relying on it, and note that it is not implemented everywhere.
Tip
Run validation getters after every commit. A change is only done when get_interfaces and get_bgp_neighbors look the way you expect, not when commit_config() returned.
Putting it together¶
A small drift-correction script for a set of switches, using replace and showing the diff:
import os, sys
from napalm import get_network_driver
def converge(host, golden_path, apply=False):
driver = get_network_driver("ios")
with driver(hostname=host,
username=os.environ["NET_USER"],
password=os.environ["NET_PASS"]) as dev:
dev.load_replace_candidate(filename=golden_path)
diff = dev.compare_config()
if not diff:
print(f"{host}: in sync")
dev.discard_config()
return
print(f"{host}: drift detected\n{diff}")
if apply:
dev.commit_config()
else:
dev.discard_config()
converge(sys.argv[1], f"golden/{sys.argv[1]}.cfg", apply="--apply" in sys.argv)
Without --apply it is a read-only drift report, which is a safe first step to put in front of a team.
NAPALM or Netmiko¶
| Question | Netmiko | NAPALM |
|---|---|---|
| Works on anything with an SSH CLI | Yes | Only platforms with a driver |
| Normalised, vendor-neutral data | No (parse it yourself) | Yes, through getters |
| Diff before applying | You build it | Built in (compare_config) |
| Rollback | You build it | Built in, platform dependent |
| Run arbitrary commands | Yes, easily | Possible with cli(), but not the focus |
| Learning curve | Small | Small to moderate |
| Config style | Line-by-line pushes | Merge or full replace |
Use Netmiko when you need to reach odd platforms, run one-off commands, or do interactive things such as upgrades with confirmation prompts. Use NAPALM when you manage a mixed-vendor estate, want a consistent data model for audits, or want a diff-and-commit workflow without writing it yourself. Many teams use both: NAPALM for the routine config lifecycle, Netmiko for the odd commands NAPALM does not model.
Neither tool decides what the right configuration is. Pair them with templates and a source of truth, and put the output in Git as described in Git and CI/CD for network configs.
References¶
Unlock the full question banks and cram sheets
A free account opens every question bank, the timed practice exams, the printable cram sheets, all the simulations and the lab files. Without one you see a 5-question sample of each.
No card needed. Study tips by email are optional and off unless you tick the box. Your address is never sold or shared. Privacy policy ยท Terms
Signed in as . Your account
You're on the free account, so lessons after the first module, question banks, exams and cram sheets show a sample. See Track Pass and All-Access
Also from NetManager Systems: NetManager, network monitoring for multi-vendor networks. Try the NetManager, NetDiag, PCAP Analyzer and OpsDash demos: every new account gets a free 7-day read-only login to each.