If you work with network devices (Cisco, Juniper, Aruba, Fortinet, etc.) and you want simple, reliable SSH automation, Netmiko is one of the best Python libraries to start with.

To install it use pip install netmiko.

Netmiko uses a device dictionary and ConnectHandler() to create an SSH connection.

We should specify the device type in this example cisco_ios, then IP or Host, then user/pass, if we have enable password we should provide it too. Then we send the deice dictionary to ConnectHndler function, the ** before device means we send each key:value separately to the function and the function treat them separately. At the end we should disconnect the connection, otherwise it will stay open, but with context manager we don’t need to disconnect the connection.

from netmiko import ConnectHandler

device = {
    "device_type": "cisco_ios",
    "host": "10.10.10.1",
    "username": "admin",
    "password": "Cisco123!",
    "secret": "Enable123!",   # enable password (optional, but common)
}

conn = ConnectHandler(**device)
print(conn.find_prompt())
conn.disconnect()

# Using the context manager to disconnect automatically
with ConnectHandler(**device) as net_connect:
    print(net_connect.find_prompt())

Some devices need enable mode to run privileged commands or configuration.

from netmiko import ConnectHandler

conn = ConnectHandler(
    device_type="cisco_ios",
    host="10.10.10.1",
    username="admin",
    password="Cisco123!",
    secret="Enable123!"
)

conn.enable()
output = conn.send_command("show ip int brief")
print(output)

conn.exit_enable()
conn.disconnect()

Use send_command() for sending command to the device.

output = conn.send_command("show version")
print(output)

The easiest way to push multiple config lines is to use a list: We need here to use send_config_set().

cfg = [
    "interface loopback22",
    "ip address 22.22.22.22 255.255.255.255",
    "description CREATED_BY_NETMIKO",
]

output = conn.send_config_set(cfg)
print(output)

output>>
config term
Enter configuration commands, one per line.  End with CNTL/Z.
Router(config)#interface loopback22
Router(config-if)#ip address 22.22.22.22 255.255.255.255
Router(config-if)#description CREATED_BY_NETMIKO
Router(config-if)#end
Router#


We can do show running-config and then save it into a txt file:


running = conn.send_command("show running-config")
outfile = OUT_DIR / f"{ip}_running-config.txt"
outfile.write_text(running, encoding="utf-8")

Netmiko can parse many commands using TextFSM templates (NTC Templates).

from netmiko import ConnectHandler
from pprint import pprint

conn = ConnectHandler(
    device_type="cisco_ios",
    host="10.10.10.1",
    username="admin",
    password="Cisco123!"
)

data = conn.send_command("show ip int brief", use_textfsm=True)
pprint(data)

conn.disconnect()

Output:
[
    {
        'interface': 'GigabitEthernet0/0',
        'ip_address': '10.10.10.1',
        'status': 'up',
        'protocol': 'up'
    },
    {
        'interface': 'GigabitEthernet0/1',
        'ip_address': 'unassigned',
        'status': 'administratively down',
        'protocol': 'down'
    },
    {
        'interface': 'Loopback0',
        'ip_address': '1.1.1.1',
        'status': 'up',
        'protocol': 'up'
    }
]

delay_factor tells Netmiko to wait longer before reading the device’s response.

output = net_connect.send_command_timing("copy running-config startup-config")

if "Destination filename" in output:
    output += net_connect.send_command_timing("\n", delay_factor=2)

We can run the same function on the same device at the same time.

import concurrent.futures

with concurrent.futures.ThreadPoolExecutor() as executor:  #Automatically starts the thread pool and cleans up threads when finished
    executor.map(execute_command, (R1, R2, R3)) #Calls the commands in Parallel

We can pull the config from a file:

output = connection.send_config_from_file('config.txt')
print(output)

Text file:
interface loopback33
 ip address 33.33.33.33 255.255.255.255
 description CONFIG_FROM_FILE

output:
config term
Enter configuration commands, one per line.  End with CNTL/Z.
Router(config)#interface loopback33
Router(config-if)#ip address 33.33.33.33 255.255.255.255
Router(config-if)#description CONFIG_FROM_FILE
Router(config-if)#end
Router#

We can save the log to a file, Using session_log in Netmiko saves the entire SSH interaction to a file, making debugging and auditing much easier.

device = {
    'device_type': 'cisco_ios',
    'ip': '192.168.31.99',
    'username': 'admin',
    'password': 'cisco',
    'secret': 'cisco',
    'session_log': 'my_session.txt',  # Save session logs into a file
}

my_session.txt

Router#
Router#show ip interface brief
Interface              IP-Address      OK? Method Status                Protocol
GigabitEthernet0/0     192.168.31.99    YES manual up                    up
Router#configure terminal
Router(config)#interface loopback10
Router(config-if)#description TEST
Router(config-if)#end
Router#

The Python logging module captures Netmiko’s internal debug information, making it ideal for troubleshooting and production automation:

Logs everything, including: DEBUG, INFO, WARNING, ERROR, CRITICAL

import logging

logging.basicConfig(filename='test.log', level=logging.DEBUG)
logger = logging.getLogger("netmiko")


text.log file:

DEBUG:netmiko:Establishing SSH connection to 192.168.31.99
DEBUG:netmiko:Authentication successful
DEBUG:netmiko:Sending command: show ip interface brief
DEBUG:netmiko:Received output (length=312)

Posted in

Leave a comment