Quick answer: PySNMP and Netmiko are the two Python libraries most network engineers reach for when they start monitoring devices with code. PySNMP polls SNMP counters β interface traffic, CPU, memory, uptime β without logging in, while Netmiko opens an SSH session and runs show commands exactly as you would by hand, then hands the output back to Python. Together they let a single script check hundreds of routers, switches and firewalls in minutes.
This guide explains when to use each library, how to install them, and walks through working code for polling interface statistics over SNMP, collecting CLI output over SSH, and turning raw text into structured data you can alert on. You will also see the common pitfalls and how the two tools fit next to modern APIs such as RESTCONF and gNMI.
Why monitor the network with Python at all?
Commercial monitoring tools are excellent at dashboards but slow to customise. When you need something specific β “alert me if any uplink in the Pune branch negotiates below 1 Gbps”, or “export every core switch config to Git nightly” β a 50-line Python script beats a vendor feature request. Python also combines data from SNMP, CLI and REST APIs in one report.
PySNMP versus Netmiko: which one when?
| PySNMP | Netmiko | |
|---|---|---|
| Protocol | SNMP v1/v2c/v3 over UDP 161 | SSH (or Telnet) to the device CLI |
| Best for | Numeric counters: traffic, errors, CPU, memory, uptime | Anything a show command returns; pushing configuration |
| Data format | Structured OIDs and values | Raw text (parse with TextFSM or regex) |
| Login required | No β community string or SNMPv3 user | Yes β username/password or SSH key |
| Device load | Very light, scales to thousands of polls | Heavier; one SSH session per device |
| Can change config? | Rarely used for writes | Yes, via send_config_set() |
The rule of thumb: use SNMP for frequent, lightweight polling of numbers; use Netmiko when you need text that SNMP does not expose or when you must change something.
Installing the libraries
Use Python 3.9 or newer and install both with pip install "pysnmp>=7" netmiko. The actively maintained PySNMP release is version 7, which uses asyncio; Netmiko 4 is synchronous and built on Paramiko.
Make sure SNMP is enabled on the device (snmp-server community public RO on Cisco IOS for a lab; SNMPv3 with authentication and privacy in production) and that SSH is reachable with a user that has at least read privileges.
Hands-on 1: polling interface statistics with PySNMP
SNMP exposes data as OIDs (object identifiers). The IF-MIB standard describes interfaces: ifDescr is the name, ifOperStatus is up/down, and ifHCInOctets/ifHCOutOctets are 64-bit byte counters. PySNMP can load the MIB and translate names for you. The example below walks the interface table and prints status and byte counts.
import asyncio
from pysnmp.hlapi.v3arch.asyncio import (
SnmpEngine, CommunityData, UdpTransportTarget, ContextData,
ObjectType, ObjectIdentity, walk_cmd,
)
HOST = "192.168.1.1"
COMMUNITY = "public" # lab only; use SNMPv3 UsmUserData in production
async def walk(oid_name: str) -> dict[int, str]:
"""Walk one IF-MIB column and return {ifIndex: value}."""
engine = SnmpEngine()
target = await UdpTransportTarget.create((HOST, 161), timeout=2, retries=1)
results = {}
async for err_ind, err_status, err_index, var_binds in walk_cmd(
engine,
CommunityData(COMMUNITY, mpModel=1), # mpModel=1 -> SNMPv2c
target,
ContextData(),
ObjectType(ObjectIdentity("IF-MIB", oid_name)),
lexicographicMode=False, # stop at end of this column
):
if err_ind:
raise RuntimeError(err_ind)
if err_status:
raise RuntimeError(f"{err_status.prettyPrint()} at {err_index}")
for name, value in var_binds:
if_index = int(name.getOid()[-1])
results[if_index] = value.prettyPrint()
return results
async def main():
names, status, in_octets, out_octets = await asyncio.gather(
walk("ifDescr"), walk("ifOperStatus"),
walk("ifHCInOctets"), walk("ifHCOutOctets"),
)
print(f'{"Interface":<24}{"Status":<8}{"In (MB)":>12}{"Out (MB)":>12}')
for idx, name in sorted(names.items()):
print(f'{name:<24}{status.get(idx, "?"):<8}'
f'{int(in_octets.get(idx, 0)) / 1e6:>12.1f}'
f'{int(out_octets.get(idx, 0)) / 1e6:>12.1f}')
asyncio.run(main())
Because the four walks run concurrently with asyncio.gather, the whole poll finishes in roughly the time of one. To calculate bandwidth, poll twice a fixed interval apart and divide the counter difference by the seconds elapsed; always use the 64-bit HC counters so a 10 Gbps link does not wrap between polls.
Hands-on 2: collecting CLI output with Netmiko
Netmiko supports well over a hundred platforms (cisco_ios, cisco_nxos, arista_eos, juniper_junos, huawei, fortinet and more). The key feature for monitoring is use_textfsm=True, which runs the output through community TextFSM templates and gives you a list of dictionaries instead of text.
import os
from netmiko import ConnectHandler
from netmiko.exceptions import NetmikoTimeoutException, NetmikoAuthenticationException
devices = [
{"device_type": "cisco_ios", "host": "192.168.1.1"},
{"device_type": "cisco_ios", "host": "192.168.1.2"},
]
creds = {"username": os.environ["NET_USER"], "password": os.environ["NET_PASS"]}
for dev in devices:
try:
with ConnectHandler(**dev, **creds, conn_timeout=10) as conn:
hostname = conn.find_prompt().strip("#>")
interfaces = conn.send_command("show ip interface brief", use_textfsm=True)
down = [i["interface"] for i in interfaces
if i["status"] == "down" and i["proto"] == "down"]
print(f"{hostname}: {len(interfaces)} interfaces, {len(down)} down -> {down}")
# Back up the running configuration
running = conn.send_command("show running-config")
with open(f"backups/{hostname}.cfg", "w") as fh:
fh.write(running)
except NetmikoAuthenticationException:
print(f'{dev["host"]}: authentication failed')
except NetmikoTimeoutException:
print(f'{dev["host"]}: unreachable')
The with statement guarantees the SSH session is closed even if something fails halfway. Credentials come from environment variables so the script can be committed to Git safely.
Monitoring only matters if someone finds out. Store the previous counters in a JSON file, compare on each run, and when an error count rises post a message with requests.post(webhook_url, json={"text": message}) β Slack, Teams and Google Chat all accept that format. Schedule the script with cron every five minutes and you have a working alerting system. If you would rather watch live packets than counters, Analyzing Network Traffic with Scapy covers the packet-level side.
Seven monitoring mistakes to avoid
- Using SNMPv1 or v2c in production. Community strings travel in clear text. Use SNMPv3 with
UsmUserData(authentication plus privacy). - Reading 32-bit counters on fast links.
ifInOctetswraps every few seconds at 10 Gbps. Always useifHCInOctets. - Polling too aggressively. Hundreds of SNMP walks per second can spike device CPU. Poll what you need, at a sensible interval.
- Parsing CLI text with ad-hoc regex. Use
use_textfsm=Trueor Genie parsers; they survive output format changes. - Hard-coding passwords. Environment variables, a vault, or SSH keys β never literals in the script.
- Ignoring exceptions. One unreachable device should log an error and move on, not crash the loop for the other 199.
- Opening a new SSH session per command. Collect everything you need inside a single
ConnectHandlerblock.
Frequently asked questions
Is SNMP obsolete now that streaming telemetry exists?
Not yet. gNMI and model-driven telemetry are superior where supported, but SNMP works on virtually every device ever shipped, including printers, UPSs and legacy switches. Most real networks need both for years to come.
Can Netmiko change configuration, not just read it?
Yes. conn.send_config_set(["interface Gi0/1", "description Uplink"]) enters config mode, applies the lines and exits. Combine it with conn.save_config() to write memory.
How do I monitor hundreds of devices quickly with Netmiko?
Run connections in parallel with concurrent.futures.ThreadPoolExecutor, or move to Nornir, which wraps Netmiko with inventory management and built-in threading.
What is the difference between PySNMP and the older pysnmp 4.x code online?
Many tutorials show the synchronous getCmd/bulkCmd API from version 4. PySNMP 6 and 7 switched to asyncio with snake-case names such as get_cmd and walk_cmd. Check your installed version with pip show pysnmp before copying examples.
Key takeaways
- PySNMP polls structured counters cheaply; Netmiko gives you full CLI access over SSH. Use each for what it does best.
- Use SNMPv3 and 64-bit counters, and parse CLI output with TextFSM rather than regex.
- Handle exceptions per device, keep credentials in the environment, and reuse sessions.
- A few dozen lines of Python plus a webhook is a complete, customisable alerting pipeline.
Want structured, mentor-led training that takes you from Python basics to automation and data projects? Explore the Techknowledgehub Python & Data Science course, with live classes, hands-on labs and placement assistance. For free walkthroughs, subscribe to our YouTube channel.



