Introduction
Nornir is a Python framework for network automation. Unlike Ansible, Nornir is pure Python — you write Python code (not YAML playbooks) to automate network devices. This gives you the full power of Python for complex logic, error handling, and integration with other systems.
Why Nornir?
- Pure Python — no domain-specific language to learn
- Multi-threaded by default — runs tasks on all devices simultaneously
- Pluggable — supports Netmiko, NAPALM, Scrapli connections
- Easy to integrate with databases, APIs, monitoring systems
- Better for complex automation logic than Ansible YAML
Installation
BASH
pip install nornir nornir-utils nornir-netmiko nornir-napalmInventory Setup
YAML
# inventory/hosts.yaml
core-switch-01:
hostname: 192.168.1.1
platform: ios
groups:
- cisco_ios
edge-router-01:
hostname: 192.168.1.254
platform: ios
groups:
- cisco_ios
mikrotik-01:
hostname: 192.168.1.2
platform: routeros
groups:
- mikrotik
# inventory/groups.yaml
cisco_ios:
username: admin
password: cisco123
connection_options:
netmiko:
extras:
device_type: cisco_ios
timeout: 30
mikrotik:
username: admin
password: mikrotik123
connection_options:
netmiko:
extras:
device_type: mikrotik_routerosBasic Nornir Script
PYTHON
from nornir import InitNornir
from nornir_netmiko.tasks import netmiko_send_command
from nornir_utils.plugins.functions import print_result
# Initialize Nornir
nr = InitNornir(
runner={"plugin": "threaded", "options": {"num_workers": 20}},
inventory={
"plugin": "SimpleInventory",
"options": {
"host_file": "inventory/hosts.yaml",
"group_file": "inventory/groups.yaml",
},
},
)
# Run command on ALL devices simultaneously
result = nr.run(
task=netmiko_send_command,
command_string="show version"
)
# Print results
print_result(result)Filtering Devices
PYTHON
from nornir.core.filter import F
# Filter by group
cisco_devices = nr.filter(F(groups__contains="cisco_ios"))
# Filter by hostname pattern
core_switches = nr.filter(F(hostname__startswith="192.168.1.1"))
# Filter by custom data attribute
# hosts.yaml: data: role: core
core_devices = nr.filter(F(data__role="core"))Running Configuration Changes
PYTHON
from nornir_netmiko.tasks import netmiko_send_config
def configure_ntp(task):
'''Configure NTP servers on Cisco IOS devices.'''
ntp_config = [
"ntp server 192.168.1.100 prefer",
"ntp server 192.168.1.101",
]
result = task.run(
task=netmiko_send_config,
config_commands=ntp_config
)
# Save config after change
task.run(
task=netmiko_send_command,
command_string="write memory"
)
return result
# Run on all Cisco devices
result = nr.filter(F(groups__contains="cisco_ios")).run(task=configure_ntp)
print_result(result)Error Handling
PYTHON
result = nr.run(task=configure_ntp)
# Check for failures
if result.failed:
print("Failed devices:")
for hostname, task_result in result.failed_hosts.items():
print(f" {hostname}: {task_result[0].exception}")
# Print only results that have issues
for hostname, task_result in result.items():
if task_result.failed:
print(f"FAILED: {hostname}")
else:
print(f"OK: {hostname}")Collecting Data and Building Reports
PYTHON
import json
from nornir_netmiko.tasks import netmiko_send_command
def collect_interfaces(task):
result = task.run(
task=netmiko_send_command,
command_string="show interfaces status"
)
# Parse output (or use ntc-templates for structured output)
return result
# Collect from all devices
result = nr.run(task=collect_interfaces)
# Build inventory report
inventory_data = {}
for hostname, task_result in result.items():
inventory_data[hostname] = task_result[0].result
with open("interface_report.json", "w") as f:
json.dump(inventory_data, f, indent=2)Using NAPALM for Structured Data
PYTHON
from nornir_napalm.plugins.tasks import napalm_get
# Get structured interface data (no manual parsing!)
result = nr.run(
task=napalm_get,
getters=["interfaces", "bgp_neighbors", "arp_table"]
)
for hostname, task_result in result.items():
interfaces = task_result[0].result["interfaces"]
for iface_name, iface_data in interfaces.items():
if not iface_data["is_up"]:
print(f"{hostname}: {iface_name} is DOWN")Integrating with Netbox (Source of Truth)
PYTHON
import pynetbox
from nornir import InitNornir
# Pull inventory from Netbox instead of YAML files
nb = pynetbox.api("http://netbox.company.com", token="your-token")
# Build Nornir hosts from Netbox
hosts = {}
for device in nb.dcim.devices.filter(status="active"):
hosts[device.name] = {
"hostname": str(device.primary_ip.address).split("/")[0],
"platform": device.platform.slug,
"data": {
"site": device.site.name,
"role": device.device_role.name,
}
}Summary
- Nornir is pure Python — use it when you need complex logic
- Multi-threaded by default — fast on large networks
- Use Netmiko for SSH connections, NAPALM for structured data
- Always handle failures — not all devices will respond the same way
- Combine with Netbox for a proper source of truth for your inventory
