NAPALM — One API, Many Vendors
20 min In ProgressLearning Objectives
- Explain what 'multivendor abstraction' actually buys you
- Use load_merge_candidate() / compare_config() to preview a change before applying it
- Decide between commit() and rollback() based on a diff
Why NAPALM
Cisco, Juniper, and Arista each speak their own CLI dialect. NAPALM hides those differences behind one shared Python API, so the same driver code works no matter the vendor underneath. Its standout safety feature is compare_config() — it shows you the diff of a proposed change before anything is actually applied.
The Real Driver (Lab / Real-Device Code)
Notice the review-before-commit pattern: load, compare, then decide.
from napalm import get_network_driver
class napalm_driver:
def __init__(self, vendor, hostname, username, password):
driver = get_network_driver(vendor)
self.device = driver(hostname, username, password)
def connect(self):
self.device.open()
def load_and_merge(self, config_file):
self.device.load_merge_candidate(filename=config_file)
return self.device.compare_config()
def commit(self):
self.device.commit_config()
def rollback(self):
self.device.discard_config()
def disconnect(self):
self.device.close()
napalm = napalm_driver('ios', '192.168.1.1', 'admin', 'secret')
napalm.connect()
diff = napalm.load_and_merge('snippets/ntp.cfg')
print(diff)
if 'ntp server' in diff:
napalm.commit()
else:
napalm.rollback()
napalm.disconnect()
Practice Safely — Simulated Multivendor Diff/Commit
The same driver class wraps two completely different simulated vendors below. That's the entire point of NAPALM: one class, any vendor underneath.
class simulated_napalm_device:
def __init__(self, vendor, hostname):
self.vendor = vendor
self.hostname = hostname
self._pending_diff = ''
def open(self):
pass
def load_merge_candidate(self, config_text):
self._pending_diff = config_text
def compare_config(self):
return self._pending_diff
def commit_config(self):
result = f'{self.hostname} ({self.vendor}): COMMITTED -> {self._pending_diff.strip()}'
self._pending_diff = ''
return result
def discard_config(self):
result = f'{self.hostname} ({self.vendor}): DISCARDED -> {self._pending_diff.strip()}'
self._pending_diff = ''
return result
def close(self):
pass
class napalm_style_driver:
def __init__(self, device):
self.device = device
def connect(self):
self.device.open()
def load_and_merge(self, config_text):
self.device.load_merge_candidate(config_text)
return self.device.compare_config()
def commit(self):
return self.device.commit_config()
def rollback(self):
return self.device.discard_config()
def disconnect(self):
self.device.close()
fleet = [
napalm_style_driver(simulated_napalm_device('ios', 'edge-router-1')),
napalm_style_driver(simulated_napalm_device('junos', 'core-switch-1')),
]
for driver in fleet:
driver.connect()
diff = driver.load_and_merge('+ ntp server 10.0.0.1')
print(diff)
if 'ntp server' in diff:
print(driver.commit())
else:
print(driver.rollback())
driver.disconnect()
📝 Exercise: Trigger a Real Rollback
Add a third simulated device whose proposed change is unrelated to NTP (e.g. an interface description change). Confirm the driver correctly rolls it back instead of committing, and print a final count of committed vs rolled-back devices.