Nornir for Network Engineers
nornir_netmiko: Wrapping Netmiko as a Task Plugin
What you'll learn
- That nornir_netmiko is a plugin wrapping the Netmiko already learned, not a new tool
- The real signatures of netmiko_send_command and netmiko_send_config
- How a real, unreachable device's failure surfaces through a Nornir task
- Where this fits: Option A for this module's capstone
Listen to a guided explanation of this lesson.
Spoken narration isn't supported in this browser.
The same Netmiko, a different caller
Not a new tool
ConnectHandler, send_command, send_config_set, and Netmiko's own exceptions were fully taught in Netmiko Fundamentals (Phase 2.7) — none of that is repeated here. nornir_netmiko is a plugin: it calls the exact Netmiko you already know, from inside a Nornir task, so a task function doesn't need to manage ConnectHandler itself.
- nr.run(task=netmiko_send_command, ...)a Nornir task
- nornir_netmikothe plugin
- Netmiko's ConnectHandler / send_commandexactly what Phase 2.7 taught
- A real device (or none)
The real, verified signatures
def netmiko_send_command(
task: Task,
command_string: str,
use_timing: bool = False,
enable: bool = False,
**kwargs: Any,
) -> Result:
"""Execute Netmiko's send_command (or send_command_timing)."""Verified directly by inspecting the installed package, not assumed from memory. command_string is the show command; **kwargs pass through to Netmiko's own send_command() call.
from nornir_netmiko import netmiko_send_command
agg = nr.run(task=netmiko_send_command, command_string="show version")No ConnectHandler, no manual connection management — nr.run() handles calling the task once per host in the (filtered or full) inventory.
Real failure behavior, verified against an unreachable target
one = nr.filter(name="SW1")
# one inherits the username/password already injected onto
# nr.inventory.defaults via Lesson 2's os.environ pattern -- filtering
# never requires re-injecting credentials, verified directly.
result = one.run(task=netmiko_send_command, command_string="show version")
print("agg.failed:", result.failed)
for hostname, multi_result in result.items():
print(hostname, "failed:", multi_result.failed)
print(hostname, "exception:", repr(multi_result[0].exception))Run against SW1's inventory IP (10.10.10.1), which nothing is listening on in this environment -- a genuine, reproducible failure, not a hypothetical. Credentials still come from Lesson 2's environment-variable injection, never hardcoded here.
agg.failed: True
SW1 failed: True
SW1 exception: NetmikoTimeoutException('TCP connection to device failed.\n\nCommon causes of this problem are:\n1. Incorrect hostname or IP address.\n2. Wrong TCP port.\n3. Intermediate firewall blocking access.\n\nDevice settings: cisco_ios 10.10.10.1:22\n\n')Real, verified output -- the exact NetmikoTimeoutException message already verified in Phase 2.7, now surfaced as multi_result[0].exception through a Nornir task instead of a bare try/except block.
A real, useful extra: netmiko_send_config's built-in dry_run
def netmiko_send_config(
task: Task,
config_commands: list[str] | None = None,
config_file: str | None = None,
enable: bool = True,
dry_run: bool | None = None,
**kwargs: Any,
) -> Result:Verified directly. dry_run is a real, built-in parameter -- the same safety concept Reliable Automation taught by hand (Phase 2.8), now offered by the plugin itself rather than something you implement yourself.
Key takeaways
- nornir_netmiko wraps the exact Netmiko already taught in Phase 2.7 — ConnectHandler, send_command, and Netmiko's exceptions are not re-taught here.
- netmiko_send_command(task, command_string, ...) and netmiko_send_config(task, config_commands, ..., dry_run=...) — real, verified signatures from the installed package, not assumed.
- A real, unreachable target's NetmikoTimeoutException was verified directly, ending up on multi_result[0].exception — the same exception Phase 2.7 verified, now surfaced through Nornir's own result model.
- netmiko_send_config's built-in dry_run parameter is the same safety principle Reliable Automation taught by hand, now offered by the plugin itself.