NAPALM — One API, Many Vendors

20 min In Progress

Learning 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.

Lab-only: needs `pip install napalm` plus the matching vendor driver and a reachable device. Running it here raises ModuleNotFoundError — expected.
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.

code.py
Output

            
← Back to Chapter