NAPALM – Network Automation with Python
1. What is NAPALM?
NAPALM stands for Network Automation and Programmability Abstraction Layer with Multivendor support. It is an open-source Python library that provides a unified API to interact with network devices from multiple vendors — Cisco IOS, Cisco IOS-XE, Cisco NX-OS, Arista EOS, Juniper JunOS, and more — using the same Python code regardless of the underlying vendor or operating system.
Without NAPALM, a network engineer would need to write completely separate automation scripts for each vendor — one script for Cisco, another for Arista, another for Juniper. NAPALM eliminates this duplication by providing a single consistent set of methods that work across all supported platforms.
| NAPALM Property | Value |
|---|---|
| Full name | Network Automation and Programmability Abstraction Layer with Multivendor support |
| Type | Open-source Python library |
| Python version | Python 3.7+ (recommended: 3.8 or higher) |
| Install | pip install napalm |
| GitHub | github.com/napalm-automation/napalm |
| Key capability | Read facts, retrieve operational data, push configurations, validate device state — across multiple vendors with one API |
| Integrations | Ansible (napalm collection), Nornir, Salt, Netbox |
| Relation to other tools | Complements Netmiko (SSH commands) and RESTCONF/NETCONF (API-based) — NAPALM often uses Netmiko internally for IOS devices |
Think of NAPALM as a universal remote control for network devices. Just as a universal remote uses one set of buttons to control TVs from Sony, Samsung, and LG, NAPALM uses one set of Python methods to manage routers and switches from Cisco, Arista, and Juniper.
Related pages: Network Automation Overview | Python for Networking | Python Scripts | Netmiko | Ansible Overview | NETCONF & RESTCONF | REST API Overview | SSH Overview
2. Why Use NAPALM? The Multi-Vendor Problem
Enterprise networks rarely run a single vendor. A typical network might have Cisco IOS routers, Arista switches in the data centre, and Juniper firewalls at the perimeter. Managing all of these with vendor-specific scripts creates a maintenance nightmare. NAPALM solves this with a driver model — each vendor has a driver that translates NAPALM's generic methods into vendor-specific commands or API calls behind the scenes.
| Without NAPALM | With NAPALM |
|---|---|
| Separate script per vendor | One script works across all vendors |
| Learn vendor-specific show command syntax | Call get_facts() on any device |
| Parse unstructured CLI text output manually | Receive structured Python dictionaries |
| Config push method differs per platform | Same load_merge_candidate() or load_replace_candidate() call |
| No built-in diff before applying changes | Built-in compare_config() shows diff before commit |
| No rollback mechanism | Built-in discard_config() and rollback() |
| Validation requires custom scripts | Built-in compliance_report() validates device state |
3. Supported Drivers (Platforms)
NAPALM uses drivers to communicate with different platforms. Each driver knows how to connect to its target OS and translate NAPALM method calls into the correct vendor commands or API calls. You specify the driver when opening a connection.
| Driver Name | Platform | Connection Method | Notes |
|---|---|---|---|
ios |
Cisco IOS / IOS-XE | SSH via Netmiko | Most widely used driver; covers classic IOS routers and Cat9k switches |
iosxr |
Cisco IOS-XR | SSH via Netmiko | Service-provider routers (ASR9k, NCS) |
nxos |
Cisco NX-OS | NX-API (HTTP/JSON) | Nexus data-centre switches; requires NX-API enabled |
nxos_ssh |
Cisco NX-OS | SSH via Netmiko | Fallback driver for Nexus when NX-API is unavailable |
eos |
Arista EOS | eAPI (HTTPS/JSON) | Requires management api http-commands on the device |
junos |
Juniper JunOS | NETCONF via ncclient | Routers and switches (SRX, MX, QFX); requires NETCONF enabled |
NAPALM Driver Architecture:
Your Python Script
|
| napalm.get_network_driver('ios')
↓
┌─────────────────────────────────────────┐
│ NAPALM Abstraction Layer │
│ get_facts() / get_interfaces() / │
│ load_merge_candidate() / commit() ... │
└───────────┬────────────┬────────────────┘
| |
┌────────▼───┐ ┌────▼──────┐ ┌─────────────┐
│ IOS driver │ │ EOS driver│ │ JunOS driver │
│ (Netmiko) │ │ (eAPI) │ │ (NETCONF) │
└────────┬───┘ └────┬──────┘ └──────┬───────┘
| | |
Cisco IOS Arista EOS Juniper JunOS
Related pages: NETCONF & RESTCONF | REST API Overview | JSON, XML & YANG
4. Installation and Opening a Connection
NAPALM is installed with pip. Once installed, every interaction follows the same three-step pattern: get the driver → open the connection → call methods → close.
# ── Step 1: Install NAPALM ───────────────────────────────────────────────
pip install napalm
# ── Step 2: Import and select the driver ─────────────────────────────────
import napalm
driver = napalm.get_network_driver('ios') # returns the IOS driver class
# ── Step 3: Create a device object ───────────────────────────────────────
device = driver(
hostname='192.168.1.1',
username='admin',
password='cisco123',
optional_args={
'port': 22, # SSH port (default: 22)
'secret': 'enable_secret', # enable secret if required
'global_delay_factor': 2 # slow devices need more time
}
)
# ── Step 4: Open the connection ───────────────────────────────────────────
device.open()
# ── Step 5: Do your work (call getters, push config, etc.) ───────────────
# ... (see next sections)
# ── Step 6: Always close the connection ──────────────────────────────────
device.close()
Using a Context Manager (Recommended)
Using Python's with statement ensures the connection is always closed cleanly — even if an exception occurs — and is the recommended pattern for production scripts.
import napalm
driver = napalm.get_network_driver('ios')
with driver(hostname='192.168.1.1', username='admin', password='cisco123') as device:
facts = device.get_facts()
print(facts)
# Connection is automatically closed when the 'with' block exits
password=os.environ.get('DEVICE_PASSWORD').5. NAPALM Getters — Reading Device State
Getters are NAPALM methods that retrieve operational data from a device and return it as a structured Python dictionary — no manual CLI parsing required. Every getter returns the same dictionary structure regardless of which vendor driver is being used.
| Getter Method | What It Returns | Typical Use Case |
|---|---|---|
get_facts() | Hostname, vendor, model, OS version, serial number, uptime, interface list | Device inventory; asset management |
get_interfaces() | Interface name, enabled state, speed, MAC address, MTU, description | Interface audit; change verification |
get_interfaces_ip() | IP addresses and prefix lengths per interface | IP address management; audit |
get_interfaces_counters() | TX/RX packets, errors, discards per interface | Performance monitoring; error detection |
get_bgp_neighbors() | BGP peer state, AS numbers, received/sent prefixes | BGP health checks; peering audits |
get_arp_table() | ARP entries: IP, MAC, interface, age | Troubleshooting; security audits |
get_mac_address_table() | MAC table: MAC, VLAN, interface, type | Port security audit; device location |
get_lldp_neighbors() | LLDP neighbour hostname and port per local interface | Topology discovery; documentation |
get_ntp_servers() | Configured NTP server addresses | NTP compliance check |
get_route_to() | Best route(s) to a specific destination prefix | Routing table verification |
get_snmp_information() | SNMP chassis ID, contact, location, community strings | SNMP compliance audit |
get_config() | Running, startup, or candidate configuration as a string | Config backup; change management |
Getter Examples — Real Python Code
import napalm
driver = napalm.get_network_driver('ios')
with driver(hostname='10.0.0.1', username='admin', password='cisco123') as device:
# ── get_facts() ───────────────────────────────────────────────────────
facts = device.get_facts()
print(facts)
# Output (dictionary):
# {
# 'hostname': 'R1',
# 'fqdn': 'R1.lab.local',
# 'vendor': 'Cisco',
# 'model': 'CSR1000V',
# 'os_version': 'Version 16.9.3',
# 'serial_number': 'ABC123DEF456',
# 'uptime': 86400,
# 'interface_list': ['GigabitEthernet1', 'GigabitEthernet2', 'Loopback0']
# }
# ── get_interfaces_ip() ───────────────────────────────────────────────
ip_info = device.get_interfaces_ip()
print(ip_info)
# Output:
# {
# 'GigabitEthernet1': {
# 'ipv4': {'192.168.1.1': {'prefix_length': 24}}
# },
# 'Loopback0': {
# 'ipv4': {'10.0.0.1': {'prefix_length': 32}}
# }
# }
# ── get_bgp_neighbors() ───────────────────────────────────────────────
bgp = device.get_bgp_neighbors()
for peer_ip, peer_data in bgp['global']['peers'].items():
state = peer_data['description']
is_up = peer_data['is_up']
print(f"Peer {peer_ip} — Up: {is_up}")
Related pages: Python Scripts for Networking | show ip interface brief | show ip route | ARP Table | BGP Overview
6. Configuration Management — Replace vs Merge
NAPALM provides two methods for pushing configuration changes to a device. Understanding the difference between replace and merge is critical — choosing the wrong one can cause unintended config changes or even outages.
| Method | How It Works | When to Use | Risk Level |
|---|---|---|---|
| load_merge_candidate() | Adds or updates only the specified configuration lines. Existing config not mentioned in the candidate is left untouched. | Adding new interfaces, BGP neighbours, ACL entries, or NTP servers without touching anything else | Low — existing config is preserved |
| load_replace_candidate() | Replaces the entire running configuration with the candidate. Any line in the running config that is not in the candidate is removed. | Full configuration lifecycle management; golden config enforcement; when you want the device to exactly match a source-of-truth file | High — removes any config not in the candidate file |
Merge (safe, additive): Replace (destructive, absolute):
Running config: A, B, C Running config: A, B, C
Candidate: D, E Candidate: A, D, E
↓ ↓
Result: A, B, C, D, E Result: A, D, E
(B and C are REMOVED)
Config Push Workflow — Always Diff Before Commit
import napalm
driver = napalm.get_network_driver('ios')
with driver(hostname='10.0.0.1', username='admin', password='cisco123') as device:
# ── Load a merge candidate from a file ────────────────────────────────
device.load_merge_candidate(filename='ntp_config.txt')
# Contents of ntp_config.txt:
# ntp server 10.10.10.1
# ntp server 10.10.10.2
# ── Always compare BEFORE committing ─────────────────────────────────
diff = device.compare_config()
if diff:
print("Pending changes:")
print(diff)
# Output shows unified diff — lines starting with + are additions,
# lines starting with - are removals (like 'git diff')
# ── Commit only if the diff looks correct ─────────────────────────
confirm = input("Commit these changes? (yes/no): ")
if confirm.lower() == 'yes':
device.commit_config()
print("Configuration committed successfully.")
else:
device.discard_config() # Roll back the candidate — nothing applied
print("Changes discarded.")
else:
print("No changes detected — device already has this configuration.")
device.discard_config()
import napalm
driver = napalm.get_network_driver('ios')
with driver(hostname='10.0.0.1', username='admin', password='cisco123') as device:
# ── Load a REPLACE candidate (full config file) ───────────────────────
device.load_replace_candidate(filename='golden_config_R1.txt')
diff = device.compare_config()
print("Replace diff (lines to be REMOVED start with -, ADDED with +):")
print(diff)
# ── Rollback if something goes wrong post-commit ──────────────────────
device.commit_config()
# If the device becomes unreachable after commit:
# device.rollback() # Restores the previous running config
commit_config() is atomic — the entire change applies or nothing does. On Cisco IOS, config is applied line by line, so partial application is possible if an error occurs mid-way. Always test on non-production devices first.7. Compliance Validation with NAPALM
NAPALM's validation feature lets you define the expected state of a device in a YAML file and then automatically compare it against the device's actual state. It returns a compliance report — ideal for post-change verification, audits, and continuous compliance checking in a CI/CD pipeline.
Validation File (YAML)
# validation.yml — defines expected device state
---
- get_facts:
hostname: R1
vendor: Cisco
os_version:
_mode: contains
_value: "16.9"
- get_interfaces:
GigabitEthernet1:
is_up: true
is_enabled: true
GigabitEthernet2:
is_up: true
- get_bgp_neighbors:
global:
peers:
10.0.0.2:
is_up: true
remote_as: 65002
Running the Compliance Check
import napalm
driver = napalm.get_network_driver('ios')
with driver(hostname='10.0.0.1', username='admin', password='cisco123') as device:
# Run compliance report against the validation YAML file
report = device.compliance_report('validation.yml')
# Print results
import json
print(json.dumps(report, indent=2))
# Check overall pass/fail
if report['complies']:
print("✅ Device is COMPLIANT with the validation policy.")
else:
print("❌ Device is NON-COMPLIANT — review the report above.")
# Example output structure:
# {
# "complies": false,
# "get_facts": {
# "complies": true,
# "skipped": []
# },
# "get_bgp_neighbors": {
# "complies": false,
# "skipped": [],
# "extra": [],
# "missing": ["10.0.0.2"]
# }
# }
Related pages: Network Baseline | JSON, XML & YANG | Python Scripts
8. Multi-Device Automation — Inventory Loop
The real power of NAPALM comes when you run it against multiple devices at once. By combining NAPALM with a simple Python inventory list (or an external inventory tool like Nornir or Ansible), you can collect facts, audit configs, or push changes across your entire network in seconds.
Example — Collect Facts from All Devices
import napalm
import json
# Define your device inventory
inventory = [
{'host': '10.0.0.1', 'driver': 'ios', 'username': 'admin', 'password': 'cisco123'},
{'host': '10.0.0.2', 'driver': 'eos', 'username': 'admin', 'password': 'arista123'},
{'host': '10.0.0.3', 'driver': 'junos', 'username': 'admin', 'password': 'juniper123'},
]
results = {}
for device_info in inventory:
driver_class = napalm.get_network_driver(device_info['driver'])
try:
with driver_class(
hostname=device_info['host'],
username=device_info['username'],
password=device_info['password']
) as device:
facts = device.get_facts()
results[device_info['host']] = {
'hostname': facts['hostname'],
'vendor': facts['vendor'],
'model': facts['model'],
'version': facts['os_version'],
'uptime': facts['uptime'],
'status': 'success'
}
except Exception as e:
results[device_info['host']] = {'status': 'failed', 'error': str(e)}
# Print structured results
print(json.dumps(results, indent=2))
Example — Push NTP Config to All IOS Devices
import napalm
ios_devices = [
{'host': '10.0.0.1', 'username': 'admin', 'password': 'cisco123'},
{'host': '10.0.0.2', 'username': 'admin', 'password': 'cisco123'},
]
ntp_config = """
ntp server 10.10.10.1 prefer
ntp server 10.10.10.2
"""
driver = napalm.get_network_driver('ios')
for dev in ios_devices:
with driver(hostname=dev['host'], username=dev['username'], password=dev['password']) as device:
device.load_merge_candidate(config=ntp_config)
diff = device.compare_config()
if diff:
device.commit_config()
print(f"[{dev['host']}] ✅ NTP config applied.")
else:
device.discard_config()
print(f"[{dev['host']}] ℹ️ Already configured — no changes needed.")
Related pages: Ansible Overview | Postman & Ansible | NTP Overview | NTP Sync | NAPALM Multi-Vendor Lab
9. NAPALM vs Other Network Automation Tools
| Tool | What It Does | Best For | Multi-Vendor? |
|---|---|---|---|
| NAPALM | Unified Python API — getters, config push, validation | Structured data retrieval; config lifecycle management; compliance checks | ✅ Yes — core purpose |
| Netmiko | SSH library — sends CLI commands and returns raw text output | Running arbitrary show commands; one-off CLI tasks; platforms NAPALM doesn't support | ✅ Yes (40+ platforms) |
| Ansible | Agentless automation framework — YAML playbooks, large task orchestration | Large-scale deployments; config management pipelines; CI/CD integration | ✅ Yes (via napalm collection and vendor modules) |
| NETCONF / ncclient | XML-based network management protocol over SSH | Fine-grained config manipulation on NETCONF-capable devices | ✅ Yes (IETF standard) |
| RESTCONF | REST API for YANG-modelled device data over HTTPS | Programmatic config/state access on modern IOS-XE, NX-OS, EOS platforms | ✅ Yes (IETF standard) |
| Nornir | Pure-Python automation framework — often used as an Ansible alternative with NAPALM plugins | Python-native orchestration; parallel task execution across large inventories | ✅ Yes (via NAPALM/Netmiko plugins) |
Nornir (orchestration + parallelism) + NAPALM (structured data + config management) + Netmiko (fallback for CLI-only tasks) + Netbox (source of truth for inventory).
Related pages: Network Automation Overview | Ansible Overview | NETCONF & RESTCONF | REST API Overview | Northbound & Southbound APIs | Controller-Based Networking
10. Common NAPALM Errors and Fixes
| Error | Cause | Fix |
|---|---|---|
ConnectionException |
Wrong hostname/IP, SSH not enabled, firewall blocking port 22 | Verify SSH is enabled (ip ssh version 2); check reachability with
ping; confirm port in optional_args |
AuthenticationException |
Wrong username or password; AAA misconfiguration on device | Test credentials manually with SSH; verify AAA/TACACS+ config on the device |
MergeConfigException |
Syntax error in the candidate config file; IOS rejected one or more lines | Validate config syntax locally; check the error message for the specific rejected line |
ReplaceConfigException |
Platform does not support atomic config replace; or the candidate is missing critical lines (e.g. no management interface) | Ensure the golden config includes all necessary lines including management access; test on a lab device first |
Getter returns empty dict {} |
Feature not configured on the device (e.g. get_bgp_neighbors() on a non-BGP device) |
Confirm the feature is configured and active; check show ip bgp summary on the device manually |
NX-OS ConnectionException |
NX-API not enabled on Nexus switch | Run feature nxapi on the switch, or switch to the nxos_ssh driver |
Arista EOS ConnectionException |
eAPI not enabled | Run management api http-commands + no shutdown on the switch |
Related pages: SSH | SSH vs Telnet Security | AAA Overview | Debug Commands | Troubleshooting Methodology
11. Key Points Summary
| Topic | Key Facts |
|---|---|
| What is NAPALM | Open-source Python library providing a unified multi-vendor API for network device management |
| Install | pip install napalm — Python 3.7+ required |
| Drivers | ios, iosxr, nxos, nxos_ssh, eos, junos — each translates NAPALM calls into vendor-specific commands or API calls |
| Getters | Methods that return structured Python dicts: get_facts(), get_interfaces(), get_bgp_neighbors(), get_arp_table(), etc. |
| Merge | load_merge_candidate() — additive; existing config not in candidate is preserved |
| Replace | load_replace_candidate() — absolute; config not in candidate is removed. Use with caution. |
| Safe change workflow | Load candidate → compare_config() to review diff → commit_config() or discard_config() |
| Rollback | rollback() restores the previous config; discard_config() cancels an uncommitted candidate |
| Validation | compliance_report() compares device state against a YAML validation file — returns pass/fail per getter |
| Context manager | Use with driver(...) as device: — guarantees connection is closed even if an exception occurs |
| vs Netmiko | Netmiko sends CLI commands and returns raw text; NAPALM returns structured dicts and handles config management |
| vs Ansible | Ansible is an orchestration framework; NAPALM is a Python library — they are often used together |