Python Network Automation Dry Runs with Nornir, Scrapli, and Netmiko
Python network automation often starts as a script and slowly becomes a change platform. Before that happens, it needs tests, dry runs, and guardrails.
The netdevops_cicd images include Nornir, Scrapli, Netmiko, NAPALM, ncclient, Paramiko, pytest, and common validation tooling. That makes them useful for testing Python-first network automation without turning every pipeline into an Ansible job.
Why Dry Runs Matter
A dry run should answer three questions:
- Which devices would be touched?
- Which commands or API calls would be sent?
- What validation must pass before a real change is allowed?
This does not require connecting to production devices on every pull request. A good pipeline can test inventory selection, task construction, templates, and parsing offline.
Example Nornir Inventory
Write Tasks That Can Be Tested
Keep command selection separate from transport:
def commands_for_device(host):
if host.platform == "arista_eos":
return ["show version", "show interfaces status"]
if host.platform == "cisco_ios":
return ["show version", "show ip interface brief"]
raise ValueError(f"Unsupported platform: {host.platform}")
Now test it without a device:
from types import SimpleNamespace
def test_arista_commands():
host = SimpleNamespace(platform="arista_eos")
commands = commands_for_device(host)
assert "show version" in commands
assert "show interfaces status" in commands
Nornir Dry Run Task
from nornir import InitNornir
from nornir_utils.plugins.functions import print_result
def dry_run_task(task):
commands = commands_for_device(task.host)
task.host["planned_commands"] = commands
return commands
nr = InitNornir(config_file="nornir.yml")
result = nr.run(task=dry_run_task)
print_result(result)
This proves inventory selection and command planning without opening a network session.
Real Collection with Scrapli
When you do need a real collection job, use CI secrets and keep it explicit:
from nornir_scrapli.tasks import send_command
def collect_version(task):
return task.run(task=send_command, command="show version")
GitHub Actions example:
jobs:
dry-run:
runs-on: ubuntu-latest
container:
image: bsmeding/netdevops_cicd_ubuntu:latest
env:
NORNIR_USERNAME: ${{ secrets.LAB_USERNAME }}
NORNIR_PASSWORD: ${{ secrets.LAB_PASSWORD }}
steps:
- uses: actions/checkout@v5
- run: pytest tests/ -vv
- run: python automation/dry_run.py
Netmiko for Simple Jobs
Netmiko is still useful for straightforward CLI collection:
from netmiko import ConnectHandler
device = {
"device_type": "cisco_ios",
"host": "192.0.2.10",
"username": "admin",
"password": "password",
}
with ConnectHandler(**device) as conn:
output = conn.send_command("show ip interface brief")
print(output)
Keep this type of direct connection in smoke tests or scheduled jobs, not every pull request.
NAPALM for State Checks
NAPALM is useful when you want normalized getters:
def test_facts_have_hostname(napalm_device):
facts = napalm_device.get_facts()
assert facts["hostname"]
Practical Tips
- Make dry-run tests run on every pull request.
- Make live device tests manual, scheduled, or environment-protected.
- Keep inventory filters visible in CI logs.
- Store collected output as artifacts when debugging.
- Prefer read-only commands in CI unless approvals are in place.
Summary
Nornir, Scrapli, Netmiko, and NAPALM are powerful tools for Python network automation. In CI/CD, their best use is often not immediate configuration changes, but dry runs, inventory checks, read-only validation, and controlled smoke tests.