Lesson 2 of 6

Netmiko for Device Interaction

Objective

In this lesson you will use Netmiko to connect to Cisco IOS devices over SSH, send both EXEC and configuration commands, and parse the returned output in Python. This matters in production because automation reduces human error and accelerates repetitive tasks — for example, applying standard banners, enabling console logging behavior, auditing interface state across many devices, and backing up running-configs. In the real world you’ll use scripts like these from an operations workstation to perform mass changes, gather inventory, or perform scheduled backups.

Quick Recap

Topology used in Lesson 1 is reused here. This lesson does not add new devices — it connects from an Admin PC to the routers using their loopback addresses shown below.

ASCII topology (exact IPs on every interface):

                 +-----------------+
                 |     Admin PC    |
                 | (runs Netmiko)  |
                 +--------+--------+
                          |
                          | SSH (to loopbacks)
                          |
    +---------------------+---------------------+
    |                     |                     |
+---+---+             +---+---+             +---+---+
|  R1   |             |  R2   |             |  R3   |
|Loop0  |             |Loop0  |             |Loop0  |
|172.25.1.1|          |172.25.1.2|          |172.25.1.3|
+-------+--+          +-------+--+          +-------+--+
                                                
                     +---+---+
                     |  R4   |
                     |Loop0  |
                     |172.25.1.4|
                     +--------+

Device table

DeviceLoopback InterfaceIP Address
R1Loopback10000172.25.1.1
R2Loopback10000172.25.1.2
R3Loopback10000172.25.1.3
R4Loopback10000172.25.1.4

Credentials used in these examples (from the lab reference)

  • Username: khawar
  • Password: cisco

Key Concepts

  • Netmiko ConnectHandler — a Python helper that opens an SSH session and provides send_command and send_config_set methods. Under the hood it manages the SSH transport and handles prompts so your script can be synchronous and deterministic.
  • send_command vs send_config_set — send_command executes a single EXEC-mode command and returns the text output. send_config_set enters configuration mode, applies a list of configuration lines, exits config mode, and returns the combined output. In production, use send_config_set for multi-line config changes to ensure atomic application.
  • Parsing output — scripts commonly extract structured data from show command output (for example, parse the hostname from show run | i host). Think of the device returning plain text; your script must locate the tokens you need (split, regex, or text parsing).
  • Idempotence & safety — adding the same banner repeatedly can be harmless, but configuration scripts in real networks should check state before making changes and use a versioned backup (we demonstrate backups).
  • SSH session behavior — when Netmiko opens an SSH session, the device still enforces exec-timeouts and console behavior; commands like line con 0 + logg sync affect local console interaction, but you configure them via the network session.

Step-by-step configuration

Each step below presents the exact script/commands from the lab reference, explains what they do and why they matter, and shows a verification command with the expected output.

Step 1: Configure a banner and console logging on a single device

What we are doing: Connect to R1 (172.25.1.1) using Netmiko and send configuration commands to set a message-of-the-day banner and configure the console line. This automates a standard administrative banner and a console setting that improves console output readability.

from netmiko import ConnectHandler

ABC = {
    'device_type': 'cisco_ios',
    'host':   '172.25.1.1',
    'username': 'khawar',
    'password': 'cisco',
}
MYSSH = ConnectHandler(**ABC)

config_commands = [ 'banner motd #Authorized NHPREP.LIVE Users Only#',
                    'line con 0',
                    ' logg sync'
                    ]
output = MYSSH.send_config_set(config_commands)
output = MYSSH.send_command('show runn | inc banner')
print(output)

What just happened:

  • ConnectHandler(**ABC) opened an SSH connection to R1. Netmiko handled prompt negotiation and session setup.
  • send_config_set(config_commands) entered global configuration mode, applied the three lines (the banner and console configuration), then exited to exec mode. On the device, banner motd ... sets the login banner. line con 0 enters console-line context and logg sync (as provided in the reference) configures the line behavior.
  • send_command('show runn | inc banner') retrieved the running configuration lines that include the word "banner" — this verifies the banner was applied.

Real-world note: Standardizing banners is common for legal notices and for automation you keep such changes in a central script so all devices receive the same corporate banner.

Verify:

show runn | inc banner
banner motd #Authorized NHPREP.LIVE Users Only#

Step 2: Interactive script — choose host and credentials at runtime

What we are doing: Make the script interactive so the operator can type the target host, username, and password at runtime. This avoids hard-coding credentials and allows the same script to initialize multiple different devices.

from netmiko import ConnectHandler

HOST = input("Enter Hostname: ")
user = input("Enter your SSH username: ")
PASS = input("Enter your SSH password: ")
ABC = {
    'device_type': 'cisco_ios',
    'host':   HOST,
    'username': user,
    'password': PASS,
    'port' : 22,          # optional, defaults to 22
    'secret': 'cisco',     # optional, defaults to ''
}
myconnect = ConnectHandler(**ABC)

config_commands = [ 'banner motd #Authorized NHPREP.LIVE Users Only#',
                    'line con 0',
                    ' logg sync',
                    ' no exec -timeout' ]
output = myconnect.send_config_set(config_commands)
output = myconnect.send_command('show runn  | inc banner ')
print(output)

What just happened:

  • The script prompts the operator for HOST, user, and PASS, allowing flexibility.
  • The ConnectHandler dictionary is created dynamically and used to open an SSH connection to the supplied host.
  • The config_commands list includes the banner and console changes, plus no exec -timeout (as in the reference) which attempts to modify exec timeout behavior for the line context. The send_config_set applies them in one configuration session.

Real-world note: Interactive scripts are convenient for ad-hoc tasks, but in production you typically use credential stores or orchestration systems to avoid embedding credentials in scripts or prompting users.

Verify:

show runn  | inc banner
banner motd #Authorized NHPREP.LIVE Users Only#

Step 3: Retrieve interface status from multiple routers

What we are doing: Read a list of device IP addresses from devices.txt, open an SSH session to each router in turn, run show ip interface brief, and print the output. This demonstrates gathering inventory and interface status across devices.

Content of devices.txt (exact lines):

172.25.1.1
172.25.1.2
172.25.1.3
172.25.1.4

Python script:

from netmiko import ConnectHandler

with open('devices.txt') as routers:
    for IP in routers:
        IP = IP.strip()
        Router = {
            'device_type': 'cisco_ios',
            'ip': IP,
            'username': 'khawar',
            'password': 'cisco'
        }

        net_connect = ConnectHandler(**Router)

        print('Connecting to ' + IP)
        print(' -' * 79)
        output = net_connect.send_command('sh ip int brief')
        print(output)
        print()
        print(' -' * 79)

net_connect.disconnect()

What just happened:

  • The file devices.txt is opened and iterated line-by-line. Each line is a loopback IP for R1–R4.
  • For each IP, ConnectHandler establishes an SSH connection and send_command('sh ip int brief') retrieves a concise listing of interfaces and their states.
  • The script prints the full show ip interface brief output to the console for operator inspection.

Real-world note: Collecting interface status across many devices is used for automated monitoring, change validation, and scheduled checks before and after maintenance.

Verify (example output for R1 — full, not abbreviated):

sh ip int brief
Interface              IP-Address      OK? Method Status                Protocol
Loopback10000          172.25.1.1      YES manual up                    up
GigabitEthernet0/0     unassigned      YES unset  administratively down down
GigabitEthernet0/1     unassigned      YES unset  administratively down down

Step 4: Backup running-config from a single router and save locally

What we are doing: Connect to R1, retrieve the hostname from the running-config, fetch the entire running-config, and write it to a file named after the device. This creates a local backup copy of the router configuration.

from netmiko import ConnectHandler

ROUTER = {
    'device_type': 'cisco_ios',
    'ip': '172.25.1.1',
    'username': 'khawar',
    'password': 'cisco'
}

net_connect = ConnectHandler(**ROUTER)

hostname = net_connect.send_command('show run | i host')
hostname.split(" ")
hostname,device = hostname.split(" ")
print ("Backing up " + device)

filename = device + '.txt'

showrun = net_connect.send_command('show run')
log_file = open(filename, "w")
log_file.write(showrun)
log_file.write(" \n")

net_connect.disconnect()

What just happened:

  • show run | i host returns the hostname line from the running-config (for example hostname R1). The script splits that to extract the device name.
  • show run obtains the full running configuration text and the script writes it to a local file named R1.txt.
  • This provides a point-in-time backup for configuration management and change rollback procedures.

Real-world note: Automated backups are essential before planned changes. In production, backups are usually timestamped and stored off-host (e.g., central server or version control).

Verify:

show run | i host
hostname R1

Also confirm that R1.txt exists in your working folder and contains the router's running-config.

Step 5: Backup running-configs from multiple routers using devices.txt

What we are doing: Reuse devices.txt to iterate over routers, obtain each device hostname, and write each running-config to a device-specific backup file. This scales the single-router backup process to many devices.

from netmiko import ConnectHandler

with open('devices.txt') as routers:
    for IP in routers:
        IP = IP.strip()
        Router = {
            'device_type': 'cisco_ios',
            'ip': IP,
            'username': 'khawar',
            'password': 'cisco'
        }

        net_connect = ConnectHandler(**Router)

        hostname = net_connect.send_command('show run | i host')
        hostname.split(" ")
        hostname,device = hostname.split(" ")
        print ("Backing up " + device)

        filename = device + ' -Backup .txt'

        showrun = net_connect.send_command('show run')
        log_file = open(filename, "w")
        log_file.write(showrun)
        log_file.write(" \n")

net_connect.disconnect()

What just happened:

  • For each IP in devices.txt, the script connects, extracts the hostname from the running-config, and writes the full running-config to a file named, for example, R1 -Backup .txt.
  • This creates per-device backups that can be collected centrally or archived.

Real-world note: When backing up many devices, include timestamps and use a secure transfer or repository; disk space and naming conventions matter for long-term retention.

Verify:

show run | i host
hostname R2

And check that files R1 -Backup .txt, R2 -Backup .txt, etc. exist in your script folder.

Verification Checklist

  • Check 1: Banner applied on R1 — run show runn | inc banner and confirm the banner text is present.
  • Check 2: Interface status retrieved — run the scripts that call sh ip int brief and confirm Loopback10000 entries for 172.25.1.1–172.25.1.4 appear and show up if configured.
  • Check 3: Backups created — confirm R1.txt and R# -Backup .txt files exist and contain the running-config text for each device.

Common Mistakes

SymptomCauseFix
Script errors out with authentication failureWrong username/password or leading/trailing whitespace in devices.txtVerify credentials are khawar / cisco and strip whitespace from IP lines (IP = IP.strip())
No banner appears after running scriptsend_config_set did not reach the device because of session or privilege issuesEnsure ConnectHandler connected successfully; check that the user has privilege to enter config mode and that SSH is established
`show runninc banner` returns nothingTypo in the show command or the banner was set with different delimiters
Backups are empty or file not createdScript failed before writing file or file path is wrongCheck earlier print/debug output; ensure show run returned data and the script has write permissions in the working folder

Key Takeaways

  • Netmiko's ConnectHandler abstracts SSH session management; use send_command for show-style queries and send_config_set for configuration changes.
  • Always verify after automation: run show commands (show run | i host, show runn | inc banner, sh ip int brief) to confirm the intended changes or collected data.
  • For multi-device operations, keep a canonical device list (devices.txt) and strip whitespace from each line — this prevents connection errors.
  • In production, turn single-run interactive scripts into controlled automation with credential management, error handling, logging, and timestamped backups.

Tip: Build small, testable scripts (set banner on one device, test, then scale to multiple devices). Keep outputs and backups so you can audit changes later.