Building Custom SNMP Agent Extensions with Python and ezsnmp¶
Note
Published: October 27, 2026
Authors: Written by Gemini 3.8 Flash, Co-Authored & Reviewed by Carlos Santos (@carlkidcrypto)
Estimated Reading Time: 5 minutes
Tags: Python • Snmp • Snmpd • Agentx • Subagent • Ezsnmp • Sysadmin
Extending the SNMP Agent: Beyond Client Polling¶
While most network engineers interact with SNMP purely as client consumers
(running GET, WALK, or SET queries against network gear), systems
engineers and software architects often face the reverse challenge:
How do you expose custom application metrics, GPU telemetry, or service health to an existing enterprise Network Management System (NMS)?
Enterprise monitoring platforms (such as LibreNMS, Zabbix, SolarWinds, or PRTG) are deeply entrenched in data center infrastructure. Rather than deploying custom REST scrapers across isolated security enclaves, exposing application metrics over standard SNMP allows centralized tools to monitor custom Python services out of the box.
Writing an entire SNMP daemon from scratch in Python is complex and error-prone. The standard, production-proven solution is the AgentX Protocol (RFC 2741) and Net-SNMP subagent architecture.
In this guide, we explore how to extend Net-SNMP using Python subagents and how ezsnmp provides the ideal, high-speed client harness to validate and benchmark your custom MIB branches.
The Architecture of AgentX Subagents¶
The Agent Extensibility (AgentX) protocol decouples core daemon operations from metric generation:
Master Agent (``snmpd``): Runs as a system-level service, binding to standard UDP port 161. It manages client access control, SNMPv3 USM authentication, and standard RFC MIBs (system, interfaces, CPU).
Local IPC Socket: The master agent creates a local Unix domain socket (typically at
/var/agentx/master).Python Subagent: Connects to the master agent over the local IPC socket and registers a specific Private Enterprise Number (PEN) OID subtree (e.g.
.1.3.6.1.4.1.99999).
When an external NMS polls an OID within the registered subtree, snmpd
routes the request over AgentX to the Python subagent, which computes the value
and returns it to the client.
Configuring the Net-SNMP Master Agent¶
To enable AgentX master mode in Net-SNMP, edit /etc/snmp/snmpd.conf:
# Enable AgentX master agent mode
master agentx
# Bind AgentX to a secure local Unix domain socket
agentXSocket /var/agentx/master
agentXPerms 0770 0770 root snmp
Restart the master daemon to apply changes:
sudo systemctl restart snmpd
Implementing a Lightweight Subagent in Python¶
While full AgentX implementations exist, Net-SNMP also supports the lightweight
pass_persist protocol—a high-performance IPC mechanism where snmpd
maintains a persistent bi-directional pipe to a long-running Python script.
1. The Python Metric Provider¶
Create a persistent provider script (/opt/custom_snmp_provider.py) that
serves custom application metrics:
import sys
import time
BASE_OID = ".1.3.6.1.4.1.99999.1"
# Define custom metrics under our enterprise branch
METRICS = {
# Active worker jobs
f"{BASE_OID}.1.0": ("integer", lambda: 42),
# Current queue depth
f"{BASE_OID}.2.0": ("gauge", lambda: int(time.time()) % 100),
# Service health string
f"{BASE_OID}.3.0": ("string", lambda: "Service-Online-OK"),
}
def oid_to_tuple(s: str) -> tuple[int, ...]:
"""Convert a dotted OID string into an integer tuple for numeric comparison."""
return tuple(int(p) for p in s.strip(".").split(".") if p)
def handle_pass_persist():
while True:
line = sys.stdin.readline()
if not line:
break
cmd = line.strip()
if cmd == "PING":
print("PONG", flush=True)
elif cmd == "set":
# pass_persist SET provides: OID, then TYPE and VALUE lines
_target_oid = sys.stdin.readline()
_val_type_and_val = sys.stdin.readline()
# Explicitly reject writes for this read-only metric provider
print("not-writable", flush=True)
elif cmd == "get":
oid = sys.stdin.readline().strip()
if oid in METRICS:
val_type, val_fn = METRICS[oid]
print(oid)
print(val_type)
print(str(val_fn()), flush=True)
else:
print("NONE", flush=True)
elif cmd == "getnext":
oid = sys.stdin.readline().strip()
target_key = oid_to_tuple(oid)
sorted_oids = sorted(METRICS.keys(), key=oid_to_tuple)
match = next((o for o in sorted_oids if oid_to_tuple(o) > target_key), None)
if match:
val_type, val_fn = METRICS[match]
print(match)
print(val_type)
print(str(val_fn()), flush=True)
else:
print("NONE", flush=True)
if __name__ == "__main__":
handle_pass_persist()
2. Registering with snmpd¶
Add the persistent script to /etc/snmp/snmpd.conf:
# Pass persistent registration for our enterprise subtree (single physical line)
pass_persist .1.3.6.1.4.1.99999.1 /usr/bin/python3 /opt/custom_snmp_provider.py
Reload snmpd:
sudo systemctl reload snmpd
Validating and Benchmarking with ezsnmp¶
Once your custom agent extension is registered, test and benchmark its
responsiveness using ezsnmp:
from ezsnmp import Session
from ezsnmp.exceptions import GenericError
def test_custom_agent_extension():
session = Session(
hostname="localhost",
community="public",
version=2,
timeout=1,
retries=1
)
try:
# 1. Query individual scalar metrics
jobs = session.get(".1.3.6.1.4.1.99999.1.1.0")[0]
status = session.get(".1.3.6.1.4.1.99999.1.3.0")[0]
print(f"Active Worker Jobs : {jobs.value} ({jobs.type})")
print(f"Service Health : {status.value} ({status.type})")
# 2. Walk the entire custom enterprise branch
print("\nWalking custom enterprise subtree:")
tree_vars = session.walk(".1.3.6.1.4.1.99999.1")
for var in tree_vars:
print(f" {var.oid}.{var.index} = {var.value}")
except GenericError as exc:
print(f"SNMP validation query failed: {exc}")
if __name__ == "__main__":
test_custom_agent_extension()
Production Best Practices¶
Non-Blocking Metric Computation: In custom subagents, avoid blocking I/O (like slow database queries) inside the SNMP request handler. Compute metrics in a background worker thread and store results in an in-memory dictionary to ensure SNMP responses return in under 10 milliseconds.
Keep Private Enterprise Numbers (PEN) Clean: Request an official, free PEN from the Internet Assigned Numbers Authority (IANA) for enterprise production rather than squatting on experimental ranges.
Automate Regression Tests: Run automated CI test suites using
ezsnmpagainst your custom agent scripts to verify OID ordering and data types before deploying to production servers.
Migration Notes: Easy SNMP vs ezsnmp¶
Subagent Stress Testing: Testing high-frequency subagents requires running thousands of client queries in tight loops. Legacy
easysnmpsuffered from socket exhaustion and thread synchronization deadlocks during stress testing.Concurrency Architecture in ezsnmp: While
ezsnmp’s modernized C++17 architecture ensures deterministic connection lifecycle management, Net-SNMP itself maintains process-global C state protected by internal mutexes. For high-throughput subagent stress testing, isolate workers usingconcurrent.futures.ProcessPoolExecutorrather than unbounded thread pools to prevent global mutex contention.
Useful Links & Community¶
Source Repository: carlkidcrypto/ezsnmp on GitHub
PyPI Distribution: ezsnmp Package on PyPI
Documentation: ezsnmp Online Documentation
Blog & Updates: Carlos Santos Blog Updates
Warning
AI-generated content may contain misinformation or mistakes. A human does review this, but we are only human.