Featured image

Table of Contents Link to heading

Introduction Link to heading

In today’s fast-paced networking landscape, automation is no longer optional—it’s essential. Managing network devices manually is inefficient and error-prone, but Python-based automation frameworks like Netmiko make it easy to interact with network devices programmatically. In this blog, we’ll explore how to use the Netmiko module to automate configuration and verification tasks on Cisco devices.

What is Netmiko? Link to heading

Netmiko is a Python library that simplifies SSH connections to network devices. Built on Paramiko, it provides high-level functionality tailored for network engineers, making device interactions seamless. It supports multiple vendors, including Cisco, Juniper, Arista, and more.

Why Netmiko? Link to heading

Netmiko simplifies SSH automation in networking for several reasons:

  • Vendor Support: Works with Cisco, Juniper, Arista, HP, and more.
  • Simplified SSH Handling: Abstracts low-level SSH complexities.
  • Efficiency: Bulk configuration across multiple devices with minimal effort.
  • Error Handling: Provides built-in timeout mechanisms.

Setting Up Netmiko Link to heading

Before diving into automation, ensure you have Netmiko installed. You can install it using pip:

pip install netmiko

You’ll also need access to networking devices and valid credentials.

Automating Network Tasks with Netmiko Link to heading

The script you’ve crafted demonstrates a practical use case: configuring a Cisco router and verifying the settings. Let’s break it down.

Define Device Parameters Link to heading

We define three Cisco devices with their IP addresses, credentials, and device types:

from netmiko import ConnectHandler

r1 = {
    "device_type": "cisco_ios",
    "ip": "2001:DB8:CAFE:1::1",
    "username": "admin",
    "password": "admin",
}

r2 = {
    "device_type": "cisco_ios",
    "ip": "10.254.3.1",
    "username": "admin",
    "password": "admin",
}

r3 = {
    "device_type": "cisco_ios",
    "ip": "10.254.3.2",
    "username": "admin",
    "password": "admin",
}

Establish SSH Connections Link to heading

Using ConnectHandler, we initiate SSH sessions with all three routers:

connection_r1 = ConnectHandler(**r1)
connection_r2 = ConnectHandler(**r2)
connection_r3 = ConnectHandler(**r3)

Apply Configuration Commands Link to heading

We send commands to configure the loopback interface and BGP routing on R3:

config_commands = [
    "interface Loopback1",
    "ip address 10.3.254.1 255.255.255.0",
    "ipv6 address 2001:DB8:CAFE:3254::1/64",
    "exit",
    "router bgp 65510",
    "address-family ipv4",
    "network 10.3.254.0 mask 255.255.255.0",
    "address-family ipv6",
    "network 2001:DB8:CAFE:3254::/64",
    "exit-address-family",
    "end",
]

output = connection_r3.send_config_set(config_commands)

Verification Link to heading

We check the configurations on all routers by running show commands:

show_commands = [
    "show bgp ipv4 unicast 10.3.254.0/24",
    "show bgp ipv6 unicast 2001:DB8:CAFE:3254::1/64",
    "show ip route 10.3.254.1",
    "show ipv6 route 2001:DB8:CAFE:3254::1",
]

for connection in (connection_r1, connection_r2, connection_r3):
    for command in show_commands:
        output += f"{command}\n{connection.send_command(command)}\n\n"

Closing Connections Link to heading

Once done, we close all SSH connections:

connection_r1.disconnect()
connection_r2.disconnect()
connection_r3.disconnect()

To review results:

print(output)

Common Real-World Netmiko Issues Link to heading

Authentication Failures Link to heading

  • Issue: Incorrect credentials or unsupported authentication methods.
  • Example: If a device rejects SSH login, Netmiko may throw a NetmikoAuthenticationException.
  • Fix: Double-check the username/password and ensure SSH is enabled on the device.

Connection Timeouts Link to heading

  • Issue: Netmiko fails to establish an SSH session.
  • Example: NetmikoTimeoutException: TCP connection to device failed.
  • Fix: Verify the device is reachable, check firewall rules, and increase the timeout value.

Unsupported Device Types Link to heading

  • Issue: Netmiko doesn’t recognise the specified device_type.
  • Example: ValueError: Unsupported 'device_type' currently supported platforms are....
  • Fix: Ensure the correct device_type is used from Netmiko’s supported list.

Command Execution Errors Link to heading

  • Issue: Commands fail due to syntax errors or interactive prompts.
  • Example: Some commands require confirmation (e.g., write memory).
  • Fix: Use send_command_timing() instead of send_command() to handle interactive prompts.

Output Parsing Issues Link to heading

  • Issue: Unexpected output formatting or missing data.
  • Example: show command output may be truncated or misformatted.
  • Fix: Use strip_prompt=False in send_command() to retain full responses.

Session Hanging or Not Closing Link to heading

  • Issue: SSH sessions remain open, causing resource exhaustion.
  • Example: Netmiko script runs but doesn’t release connections.
  • Fix: Always call connection.disconnect() after execution.

Troubleshooting Tips Link to heading

Handling Authentication Failures Link to heading

If you encounter login issues, ensure:

  • The username and password are correct.
  • The device supports SSH (some may require enabling SSH manually).
  • You handle authentication errors gracefully using NetmikoAuthenticationException.

Resolving Connection Timeouts Link to heading

If Netmiko fails to connect:

  • Verify the IP address and port (default is 22).
  • Check if the device is reachable (ping it first).
  • Increase the timeout value using timeout=60 in your connection parameters.

Managing Command Execution Errors Link to heading

If commands fail:

  • Ensure the command syntax matches the device’s CLI.
  • Use send_command_timing() instead of send_command() for commands requiring interactive input.
  • Implement exception handling for unexpected errors.

Debugging Output Issues Link to heading

If output is missing or incorrect:

  • Print the raw output using print(connection.find_prompt()).
  • Use strip_prompt=False in send_command() to retain the full response.
  • Log outputs for debugging.

Closing Connections Properly Link to heading

Always disconnect after execution:

connection.disconnect()

This prevents lingering SSH sessions that may cause issues.

Using Logging for Debugging Link to heading

Enable logging to track errors:

import logging
logging.basicConfig(level=logging.DEBUG)

This helps identify where the script fails.

Conclusion Link to heading

Automating network tasks using Netmiko enhances efficiency, minimises errors, and accelerates operational workflows. Whether configuring routers, gathering information, or implementing changes across multiple devices, Netmiko makes automation seamless for network engineers.

So why manually configure devices when Python can do it for you? 🚀


Click to expand the complete code we have discussed
#!/usr/bin/python
# -*- coding: utf-8 -*-
from netmiko import ConnectHandler

# Define device parameters
r1 = {
    "device_type": "cisco_ios",
    "ip": "2001:DB8:CAFE:1::1",
    "username": "admin",
    "password": "admin",
}

r2 = {
    "device_type": "cisco_ios",
    "ip": "10.254.3.1",
    "username": "admin",
    "password": "admin",
}

r3 = {
    "device_type": "cisco_ios",
    "ip": "10.254.3.2",
    "username": "admin",
    "password": "admin",
}

# Establish SSH connections to the all routers
connection_r1 = ConnectHandler(**r1)
connection_r2 = ConnectHandler(**r2)
connection_r3 = ConnectHandler(**r3)

# Define the configuration commands to be implemented in R3
config_commands = [
    "interface Loopback1",
    "ip address 10.3.254.1 255.255.255.0",
    "ipv6 address 2001:DB8:CAFE:3254::1/64",
    "exit",
    "router bgp 65510",
    "address-family ipv4",
    "network 10.3.254.0 mask 255.255.255.0",
    "address-family ipv6",
    "network 2001:DB8:CAFE:3254::/64",
    "exit-address-family",
    "end",
]

# Send the configuration commands to R3
output = connection_r3.send_config_set(config_commands)

# Verify the configuration from all routers
show_commands = [
    "show bgp ipv4 unicast 10.3.254.0/24",
    "show bgp ipv6 unicast 2001:DB8:CAFE:3254::1/64",
    "show ip route 10.3.254.1",
    "show ipv6 route 2001:DB8:CAFE:3254::1",
]

for connection in (connection_r1, connection_r2, connection_r3):
    for command in show_commands:
        output += command + "\n"
        output += connection.send_command(command) + "\n"
    output += "\n"

    # Close the connections
    connection.disconnect()

# Print the output
print(output)