Table of Contents
Cisco Netmiko Scripting with Examples: 15 Practical Scripts
The complete original topic set, updated with readable code, downloads, lab checks and clear paths into live training.
Ask About a Free DemoAll 15 Netmiko examples, modernized
Work from connection basics through backups, multi-device collection and controlled configuration examples. Every example is a complete Python file with prompted credentials, connection cleanup and common error handling. Each has a specific validation step.
What is Cisco Netmiko?
Netmiko is a Python library for device-aware SSH command execution across supported network platforms. These examples target Cisco IOS/IOS-XE; VLAN and switch-status commands need a supporting switch image. It does not replace network design knowledge or verification.
Install and run the examples
python -m venv .venv
# Windows PowerShell
.\.venv\Scripts\Activate.ps1
# macOS / Linux (use this instead of the Windows line)
source .venv/bin/activate
python -m pip install netmiko
python -c "import netmiko; print(netmiko.__version__)"Run only the activation line for your operating system. Download an example, replace lab-specific values and execute it using the virtual environment’s Python. Credentials are requested interactively. An optional enable secret is supported by single-device examples; use appropriate AAA privileges. The multi-device example assumes show-command privileges without an enable transition.
python 01_basic_connection_script.pyChoose an emulator and confirm SSH
EVE-NG, GNS3, PNETLab, Cisco Modeling Labs or an authorized physical lab may be suitable when the chosen appliance supports the required CLI and SSH connectivity. Obtain images and licenses through authorized channels. Check platform requirements, nested virtualization and the management bridge before installing. Packet-level simulation alone is not a substitute for a reachable SSH endpoint.
Choose a script
| # | Example | Mode |
|---|---|---|
| 1 | Basic Connection Script | READ ONLY |
| 2 | Running a Single Command | READ ONLY |
| 3 | Sending Configuration Commands | LAB CHANGE |
| 4 | Running Multiple Commands Sequentially | READ ONLY |
| 5 | Backup Device Configuration | READ ONLY |
| 6 | VLAN Configuration | LAB CHANGE |
| 7 | Looping Through Multiple Devices | READ ONLY |
| 8 | Interface Status Check | READ ONLY |
| 9 | Applying ACLs | LAB CHANGE |
| 10 | BGP Configuration | LAB CHANGE |
| 11 | Checking Device Uptime | READ ONLY |
| 12 | Loopback Interface Configuration | LAB CHANGE |
| 13 | Monitoring CPU Usage | READ ONLY |
| 14 | NTP Server Configuration | LAB CHANGE |
| 15 | DHCP Pool Configuration | LAB CHANGE |
1. Basic Connection Script
READ ONLYShow the detected CLI prompt and verify the account privilege.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
print("Prompt:", connection.find_prompt())
print("Enable mode:", connection.check_enable_mode())
def main():
host = input("Authorized lab management IP: ").strip()
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
secret = getpass("Enable secret (Enter if not needed): ")
device = device_for(host, username, password)
if secret:
device["secret"] = secret
try:
with ConnectHandler(**device) as connection:
if secret:
connection.enable()
task(connection, host)
except NetmikoAuthenticationException:
print("Authentication failed: check credentials, AAA and enable privileges.")
except NetmikoTimeoutException:
print("Connection failed: check address, routing, SSH port and device state.")
except ReadTimeout:
print("Command read timed out: inspect device prompt and command completion.")
except (OSError, ValueError) as exc:
print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")
if __name__ == "__main__":
main()Verify the result
A device prompt is printed; check the expected hostname and privilege.
2. Running a Single Command
READ ONLYCollect interface addressing and operational state.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
print(read(connection, "show ip interface brief"))
def main():
host = input("Authorized lab management IP: ").strip()
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
secret = getpass("Enable secret (Enter if not needed): ")
device = device_for(host, username, password)
if secret:
device["secret"] = secret
try:
with ConnectHandler(**device) as connection:
if secret:
connection.enable()
task(connection, host)
except NetmikoAuthenticationException:
print("Authentication failed: check credentials, AAA and enable privileges.")
except NetmikoTimeoutException:
print("Connection failed: check address, routing, SSH port and device state.")
except ReadTimeout:
print("Command read timed out: inspect device prompt and command completion.")
except (OSError, ValueError) as exc:
print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")
if __name__ == "__main__":
main()Verify the result
An interface table appears; compare IP addresses and up/down state with your lab.
3. Sending Configuration Commands
LAB CHANGEPractice a description update on an explicitly chosen lab interface.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
interface = input("Lab interface (example GigabitEthernet1): ").strip()
if not interface or any(c.isspace() for c in interface):
raise ValueError("Enter one interface identifier, with no spaces")
print("BEFORE:", read(connection, f"show running-config interface {interface}"))
if apply_lab(connection, [f"interface {interface}", "description NJ-LAB-DESCRIPTION"]):
print("AFTER:", read(connection, f"show running-config interface {interface}"))
def main():
host = input("Authorized lab management IP: ").strip()
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
secret = getpass("Enable secret (Enter if not needed): ")
device = device_for(host, username, password)
if secret:
device["secret"] = secret
try:
with ConnectHandler(**device) as connection:
if secret:
connection.enable()
task(connection, host)
except NetmikoAuthenticationException:
print("Authentication failed: check credentials, AAA and enable privileges.")
except NetmikoTimeoutException:
print("Connection failed: check address, routing, SSH port and device state.")
except ReadTimeout:
print("Command read timed out: inspect device prompt and command completion.")
except (OSError, ValueError) as exc:
print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")
if __name__ == "__main__":
main()Verify the result
Description changes without altering addressing or shutting an interface. Restore the previous description, or remove it only if none existed.
4. Running Multiple Commands Sequentially
READ ONLYLabel each show-command result so failures are easier to locate.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
commands = ["show version", "show ip route", "show ip interface brief"]
for command in commands:
print(f"\n===== {host}: {command} =====")
print(read(connection, command))
def main():
host = input("Authorized lab management IP: ").strip()
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
secret = getpass("Enable secret (Enter if not needed): ")
device = device_for(host, username, password)
if secret:
device["secret"] = secret
try:
with ConnectHandler(**device) as connection:
if secret:
connection.enable()
task(connection, host)
except NetmikoAuthenticationException:
print("Authentication failed: check credentials, AAA and enable privileges.")
except NetmikoTimeoutException:
print("Connection failed: check address, routing, SSH port and device state.")
except ReadTimeout:
print("Command read timed out: inspect device prompt and command completion.")
except (OSError, ValueError) as exc:
print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")
if __name__ == "__main__":
main()Verify the result
Three labeled outputs appear in order. This is sequential execution, not concurrency.
5. Backup Device Configuration
READ ONLYCreate a unique timestamped running-configuration backup.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
output = read(connection, "show running-config")
if not output.strip() or "% Invalid input" in output:
raise ValueError("No usable running configuration returned")
folder = Path("backups")
folder.mkdir(exist_ok=True)
stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%S%fZ")
safe_host = "".join(c if c.isalnum() or c in ".-_" else "_" for c in host)
path = folder / f"{safe_host}_{stamp}.txt"
path.write_text(output, encoding="utf-8")
print(f"Saved {path}; inspect content and restrict access.")
def main():
host = input("Authorized lab management IP: ").strip()
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
secret = getpass("Enable secret (Enter if not needed): ")
device = device_for(host, username, password)
if secret:
device["secret"] = secret
try:
with ConnectHandler(**device) as connection:
if secret:
connection.enable()
task(connection, host)
except NetmikoAuthenticationException:
print("Authentication failed: check credentials, AAA and enable privileges.")
except NetmikoTimeoutException:
print("Connection failed: check address, routing, SSH port and device state.")
except ReadTimeout:
print("Command read timed out: inspect device prompt and command completion.")
except (OSError, ValueError) as exc:
print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")
if __name__ == "__main__":
main()Verify the result
A new backup file is created. Inspect its content and keep secrets out of public repositories.
6. VLAN Configuration
LAB CHANGECreate two clearly named practice VLANs on an isolated switch.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
print("BEFORE:", read(connection, "show vlan brief"))
print("VLANs 310 and 320 must be unused practice VLANs; check VTP mode and switch support.")
commands = ["vlan 310", "name NJ_LAB_310", "exit", "vlan 320", "name NJ_LAB_320", "exit"]
if apply_lab(connection, commands):
print("AFTER:", read(connection, "show vlan brief"))
def main():
host = input("Authorized lab management IP: ").strip()
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
secret = getpass("Enable secret (Enter if not needed): ")
device = device_for(host, username, password)
if secret:
device["secret"] = secret
try:
with ConnectHandler(**device) as connection:
if secret:
connection.enable()
task(connection, host)
except NetmikoAuthenticationException:
print("Authentication failed: check credentials, AAA and enable privileges.")
except NetmikoTimeoutException:
print("Connection failed: check address, routing, SSH port and device state.")
except ReadTimeout:
print("Command read timed out: inspect device prompt and command completion.")
except (OSError, ValueError) as exc:
print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")
if __name__ == "__main__":
main()Verify the result
VLANs 310/320 appear on a supported switch. No access ports are assigned. Remove only objects newly created for this exercise after verifying they are unused.
7. Looping Through Multiple Devices
READ ONLYCollect interfaces from a device list while isolating expected failures.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
# This example overrides main() below with a multi-device runner.
print(read(connection, "show ip interface brief"))
def main():
hosts = [x.strip() for x in input("Lab IPs, comma-separated: ").split(",") if x.strip()]
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
for host in hosts:
try:
with ConnectHandler(**device_for(host, username, password)) as connection:
print(f"\n===== {host} =====")
task(connection, host)
except NetmikoAuthenticationException:
print(f"{host}: authentication failed")
except NetmikoTimeoutException:
print(f"{host}: connection failed")
except ReadTimeout:
print(f"{host}: command read timed out")
except (OSError, ValueError) as exc:
print(f"{host}: failed: {exc}")
if __name__ == "__main__":
main()Verify the result
Each host is labeled; a failed host does not stop later hosts. Shared credentials are used only for this small lab example.
8. Interface Status Check
READ ONLYCollect switch status or router interface state without assuming identical CLI support.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
kind = input("Device role: switch or router: ").strip().lower()
if kind not in {"switch", "router"}:
raise ValueError("Choose switch or router")
command = "show interfaces status" if kind == "switch" else "show ip interface brief"
print(read(connection, command))
def main():
host = input("Authorized lab management IP: ").strip()
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
secret = getpass("Enable secret (Enter if not needed): ")
device = device_for(host, username, password)
if secret:
device["secret"] = secret
try:
with ConnectHandler(**device) as connection:
if secret:
connection.enable()
task(connection, host)
except NetmikoAuthenticationException:
print("Authentication failed: check credentials, AAA and enable privileges.")
except NetmikoTimeoutException:
print("Connection failed: check address, routing, SSH port and device state.")
except ReadTimeout:
print("Command read timed out: inspect device prompt and command completion.")
except (OSError, ValueError) as exc:
print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")
if __name__ == "__main__":
main()Verify the result
A switch port-status table or router interface table appears. The script reports state; it does not infer a complete network health verdict.
9. Applying ACLs
LAB CHANGECreate a named lab ACL and inspect it without attaching it to an interface.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
name = "NJ_LAB_ACL"
print("BEFORE:", read(connection, f"show ip access-lists {name}"))
print("Use a new, unused ACL name; abort if this object already exists or is referenced.")
commands = [f"ip access-list extended {name}", "remark ISOLATED LAB EXAMPLE", "permit icmp host 192.0.2.10 host 192.0.2.11", "deny ip any any log", "exit"]
if apply_lab(connection, commands):
print("AFTER:", read(connection, f"show ip access-lists {name}"))
def main():
host = input("Authorized lab management IP: ").strip()
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
secret = getpass("Enable secret (Enter if not needed): ")
device = device_for(host, username, password)
if secret:
device["secret"] = secret
try:
with ConnectHandler(**device) as connection:
if secret:
connection.enable()
task(connection, host)
except NetmikoAuthenticationException:
print("Authentication failed: check credentials, AAA and enable privileges.")
except NetmikoTimeoutException:
print("Connection failed: check address, routing, SSH port and device state.")
except ReadTimeout:
print("Command read timed out: inspect device prompt and command completion.")
except (OSError, ValueError) as exc:
print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")
if __name__ == "__main__":
main()Verify the result
The ACL exists but is not enforced until attached. Do not apply it to management traffic. If newly created and unreferenced, cleanup is no ip access-list extended NJ_LAB_ACL.
10. BGP Configuration
LAB CHANGEConfigure a lab peer and verify session state separately from configuration acceptance.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
print("BEFORE BGP CONFIG:", read(connection, "show running-config | section router bgp"))
print("BEFORE STATE:", read(connection, "show ip bgp summary"))
print("Prerequisites: fresh isolated R1, no conflicting BGP ASN; peer 198.51.100.2 reachable on transit, peer ASN 65002 configured there.")
commands = ["router bgp 65001", "neighbor 198.51.100.2 remote-as 65002", "exit"]
if apply_lab(connection, commands):
print("AFTER:", read(connection, "show ip bgp summary"))
def main():
host = input("Authorized lab management IP: ").strip()
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
secret = getpass("Enable secret (Enter if not needed): ")
device = device_for(host, username, password)
if secret:
device["secret"] = secret
try:
with ConnectHandler(**device) as connection:
if secret:
connection.enable()
task(connection, host)
except NetmikoAuthenticationException:
print("Authentication failed: check credentials, AAA and enable privileges.")
except NetmikoTimeoutException:
print("Connection failed: check address, routing, SSH port and device state.")
except ReadTimeout:
print("Command read timed out: inspect device prompt and command completion.")
except (OSError, ValueError) as exc:
print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")
if __name__ == "__main__":
main()Verify the result
An established peer shows a numeric received-prefix count, possibly 0. Idle/Active is not success; check peer reachability, ASN, TCP/179 and address-family requirements. Restore captured BGP baseline afterward.
11. Checking Device Uptime
READ ONLYRead uptime without depending on a fragile numeric parser.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
print(read(connection, "show version | include uptime"))
def main():
host = input("Authorized lab management IP: ").strip()
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
secret = getpass("Enable secret (Enter if not needed): ")
device = device_for(host, username, password)
if secret:
device["secret"] = secret
try:
with ConnectHandler(**device) as connection:
if secret:
connection.enable()
task(connection, host)
except NetmikoAuthenticationException:
print("Authentication failed: check credentials, AAA and enable privileges.")
except NetmikoTimeoutException:
print("Connection failed: check address, routing, SSH port and device state.")
except ReadTimeout:
print("Command read timed out: inspect device prompt and command completion.")
except (OSError, ValueError) as exc:
print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")
if __name__ == "__main__":
main()Verify the result
A platform-specific uptime line appears. Empty output means inspect full show version and adjust the filter.
12. Loopback Interface Configuration
LAB CHANGECreate a dedicated lab loopback with documented addressing.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
print("BEFORE:", read(connection, "show running-config interface Loopback100"))
print("Abort if Loopback100 or 203.0.113.100 is already in use.")
commands = ["interface Loopback100", "description NJ_AUTOMATION_LAB", "ip address 203.0.113.100 255.255.255.255", "no shutdown"]
if apply_lab(connection, commands):
print("AFTER:", read(connection, "show ip interface brief | include Loopback100"))
def main():
host = input("Authorized lab management IP: ").strip()
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
secret = getpass("Enable secret (Enter if not needed): ")
device = device_for(host, username, password)
if secret:
device["secret"] = secret
try:
with ConnectHandler(**device) as connection:
if secret:
connection.enable()
task(connection, host)
except NetmikoAuthenticationException:
print("Authentication failed: check credentials, AAA and enable privileges.")
except NetmikoTimeoutException:
print("Connection failed: check address, routing, SSH port and device state.")
except ReadTimeout:
print("Command read timed out: inspect device prompt and command completion.")
except (OSError, ValueError) as exc:
print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")
if __name__ == "__main__":
main()Verify the result
Loopback100 appears with its configured address. If newly created only for this exercise, cleanup is no interface Loopback100; otherwise restore its original configuration.
13. Monitoring CPU Usage
READ ONLYTake several timestamped CPU snapshots; output syntax varies by platform.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
from time import sleep
for sample in range(3):
print(datetime.now(timezone.utc).isoformat(), "sample", sample + 1)
print(read(connection, "show processes cpu"))
if sample < 2:
sleep(5)
def main():
host = input("Authorized lab management IP: ").strip()
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
secret = getpass("Enable secret (Enter if not needed): ")
device = device_for(host, username, password)
if secret:
device["secret"] = secret
try:
with ConnectHandler(**device) as connection:
if secret:
connection.enable()
task(connection, host)
except NetmikoAuthenticationException:
print("Authentication failed: check credentials, AAA and enable privileges.")
except NetmikoTimeoutException:
print("Connection failed: check address, routing, SSH port and device state.")
except ReadTimeout:
print("Command read timed out: inspect device prompt and command completion.")
except (OSError, ValueError) as exc:
print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")
if __name__ == "__main__":
main()Verify the result
Three snapshots appear. This is periodic sampling, not streaming telemetry or historical monitoring. Connection cleanup is managed by the context manager.
14. NTP Server Configuration
LAB CHANGEAdd a reachable lab time source and inspect synchronization state.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
print("BEFORE:", read(connection, "show running-config | include ^ntp"))
print("Lab NTP server 192.0.2.20 must be reachable and serving NTP. Do not overwrite existing time policies.")
if apply_lab(connection, ["ntp server 192.0.2.20"]):
print(read(connection, "show ntp associations"))
print(read(connection, "show ntp status"))
def main():
host = input("Authorized lab management IP: ").strip()
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
secret = getpass("Enable secret (Enter if not needed): ")
device = device_for(host, username, password)
if secret:
device["secret"] = secret
try:
with ConnectHandler(**device) as connection:
if secret:
connection.enable()
task(connection, host)
except NetmikoAuthenticationException:
print("Authentication failed: check credentials, AAA and enable privileges.")
except NetmikoTimeoutException:
print("Connection failed: check address, routing, SSH port and device state.")
except ReadTimeout:
print("Command read timed out: inspect device prompt and command completion.")
except (OSError, ValueError) as exc:
print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")
if __name__ == "__main__":
main()Verify the result
A configured association does not prove synchronized time. Allow protocol convergence and check status, reachability and authentication requirements. Remove only the lab-added server afterward.
15. DHCP Pool Configuration
LAB CHANGEBuild an isolated router DHCP pool with excluded addresses and verify service state.
from datetime import datetime, timezone
from getpass import getpass
from pathlib import Path
import json
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout
ERROR_PATTERN = r"% (?:Invalid input|Incomplete command|Ambiguous command|Error|Authorization failed)"
def device_for(host, username, password):
return dict(device_type="cisco_ios", host=host, username=username,
password=password, conn_timeout=10, auth_timeout=15,
banner_timeout=20)
def read(connection, command):
return connection.send_command(command, read_timeout=60)
def apply_lab(connection, commands):
# Review the existing object before calling this helper.
print("PROPOSED LAB COMMANDS:")
print("\n".join(commands))
if input("Type APPLY LAB to send these commands: ").strip() != "APPLY LAB":
print("Cancelled. No configuration sent.")
return False
print(connection.send_config_set(commands, error_pattern=ERROR_PATTERN))
print("Not saved to startup configuration. Verify and restore your captured baseline after practice.")
return True
def task(connection, host):
print("BEFORE DHCP:", read(connection, "show running-config | section ip dhcp"))
print("Use a dedicated lab LAN on 203.0.113.0/24 with gateway .1. No existing overlapping pool or live clients may use it. Check service dhcp is enabled.")
commands = ["ip dhcp excluded-address 203.0.113.1 203.0.113.20", "ip dhcp pool NJ_LAB_POOL", "network 203.0.113.0 255.255.255.0", "default-router 203.0.113.1", "exit"]
if apply_lab(connection, commands):
print(read(connection, "show ip dhcp pool NJ_LAB_POOL"))
print(read(connection, "show ip dhcp binding"))
def main():
host = input("Authorized lab management IP: ").strip()
username = input("SSH username: ").strip()
password = getpass("SSH password: ")
secret = getpass("Enable secret (Enter if not needed): ")
device = device_for(host, username, password)
if secret:
device["secret"] = secret
try:
with ConnectHandler(**device) as connection:
if secret:
connection.enable()
task(connection, host)
except NetmikoAuthenticationException:
print("Authentication failed: check credentials, AAA and enable privileges.")
except NetmikoTimeoutException:
print("Connection failed: check address, routing, SSH port and device state.")
except ReadTimeout:
print("Command read timed out: inspect device prompt and command completion.")
except (OSError, ValueError) as exc:
print(f"Operation failed: {exc}. If a change started, inspect for partial configuration.")
if __name__ == "__main__":
main()Verify the result
Bindings appear only after a lab client requests an address and gateway/L2 connectivity works. DNS is intentionally omitted; add a valid lab resolver if needed. Restore the pool and exclusions to their baseline after practice.
Troubleshoot by failure stage
| Failure | Investigation |
|---|---|
| Connection timeout | Check routing, management address, SSH port and device state. |
| Authentication error | Check login credentials, AAA and enable requirements. |
| ReadTimeout | Check command completion and prompt detection before increasing read_timeout. |
| Configuration ValueError | Inspect returned output and partial changes. The error pattern covers common IOS errors, not every platform response. |
| Configuration accepted, service down | Verify operational state: BGP peer, NTP synchronization or DHCP client behavior. |
| Backup file exists | Inspect content and secure it; a written file is not proof of a valid complete backup. |
Frequently asked questions
What are the prerequisites?
Networking fundamentals, manual SSH and enough Python to follow functions, loops and exceptions. Run installation in a virtual environment.
Can Netmiko manage non-Cisco devices?
Yes, supported platforms use specific device_type values. Replace commands and validation for the actual platform; these 15 examples are Cisco-focused.
Are these production-ready scripts?
They are complete lab examples, not a production change platform. Production use needs reviewed inventory, host-key policy, a secret store, access controls, testing, recovery and change approval.
How are SSH connections closed?
Each script uses the ConnectHandler context manager, which handles disconnection when leaving the block, including normal exception paths.
What are alternatives or complementary tools?
Ansible can orchestrate repeatable tasks, NAPALM provides supported network APIs, and pyATS/Genie supports network-state parsing and validation.
Does the website execute the scripts?
No. Copy/download controls run in the browser. Python must run in your own authorized lab environment.
Continue from scripts to a validated workflow
Keep the same lab and inventory as you progress. Collect state, identify expected changes, deploy a reviewed configuration and rerun verification. Preserve the results so you can explain what succeeded and what still needs investigation.
Technical references: Netmiko documentation · Python virtual environments · EVE-NG platform requirements
Back to the main course page

![[Day #89 PyATS Series] Building Python REST API Wrappers Around pyATS Jobs Using pyATS for Cisco [Python for Network Engineer]](https://networkjourney.com/wp-content/uploads/2025/09/Day-89-PyATS-Series-Building-Python-REST-API-Wrappers-Around-pyATS-Jobs-Using-pyATS-for-Cisco-Python-for-Network-Engineer-470x274.png)
