SNMPv2c client — general usage guide¶
Asynchronous SNMPv2c GET, WALK, and SET with fast OID resolution and practical utilities.
When to use - You need to script CM telemetry polling without running the FastAPI service. - You want a reference implementation for typed SNMP GET/WALK/SET calls that mirrors PyPNM’s internals.
Prerequisites - Python 3.10+ and
pip install pypnm-docsis(or clone/install this repo). - Optional: compiled MIB map (see MIB compiling) so symbolic OIDs resolve instantly.
Initialization¶
Instantiate an SNMPv2c client:
import asyncio
from pypnm.lib.inet import Inet
from pypnm.snmp.snmp_v2c import Snmp_v2c # adjust path to your project
async def main():
snmp = Snmp_v2c(host=Inet("192.168.0.100"), community="public") # default port 161
try:
rows = await snmp.get("sysName.0")
print([Snmp_v2c.get_result_value(vb) for vb in rows])
finally:
snmp.close()
asyncio.run(main())
Basic operations¶
SNMP GET¶
Accepts numeric OIDs, symbolic names (resolved via compiled map), or tuple segments:
# Symbolic + instance
rows = await snmp.get("sysDescr.0")
# Numeric OID
rows = await snmp.get("1.3.6.1.2.1.1.1.0")
# Tuple form (base + suffix)
rows = await snmp.get(("1.3.6.1.2.1.1.5", "0"))
value = Snmp_v2c.get_result_value(rows[0]) if rows else None
SNMP WALK¶
Traverse a subtree; returns None if empty:
entries = await snmp.walk("ifDescr") # 1.3.6.1.2.1.2.2.1.2
pairs = Snmp_v2c.snmp_get_result_last_idx_force_value_type(entries, str) if entries else []
idx_to_name = dict(pairs) # {2: "GigabitEthernet0/1", ...}
SNMP SET¶
Explicit pysnmp type is required:
from pysnmp.proto.rfc1902 import OctetString, Integer32
# Strings
ack = await snmp.set("sysLocation.0", "MDF-Rack-A1", OctetString)
# Integers
ack = await snmp.set("snmpSetSerialNo.0", 42, Integer32)
MIB compilation¶
Use the precompiled symbol→OID map to avoid runtime MIB parsing:
from pypnm.snmp.compiled_oids import COMPILED_OIDS
sysdescr_oid = f"{COMPILED_OIDS['sysDescr']}.0"
rows = await snmp.get(sysdescr_oid)
print([Snmp_v2c.get_result_value(vb) for vb in rows])
You can also pass symbolic names directly (e.g., "sysDescr.0"); the client resolves them internally.
Utility methods¶
| Method | Input | Output | Description |
|---|---|---|---|
get_result_value(vb) |
ObjectType/tuple |
string \| None |
Human-readable conversion; pretty-prints OctetString. |
snmp_get_result_value(rows) |
List[ObjectType] |
List[string] |
String values for each varbind. |
snmp_get_result_bytes(rows) |
List[ObjectType] |
List[bytes] |
Raw bytes via asOctets() when available. |
snmp_get_result_last_idx_value(rows) |
List[ObjectType] |
List[(int, string)] |
(last_index, value) pairs per row. |
snmp_get_result_last_idx_force_value_type(rows, T) |
rows, int/str |
List[(int, T)] |
Same as above with value cast. |
get_oid_index(oid) |
string |
int \| None |
Trailing numeric index from an OID. |
extract_last_oid_index(rows) |
rows | List[int] |
Batch extract trailing indices. |
extract_oid_indices(rows, num_indices=1) |
rows, int |
List[List[int]] |
Last num_indices per row (for composite indexes). |
parse_snmp_datetime(bytes) |
bytes |
string |
SNMP DateAndTime → ISO-8601 string. |
truth_value(v) |
int \| str |
bool |
SNMP TruthValue (1=true, 2=false). |
ticks_to_duration(ticks) |
int |
string |
sysUpTime hundredths → duration. |
get_inet_address_type(ip) |
string |
InetAddressType |
IPV4 or IPV6. |
Additional usage patterns¶
Map interface index → name¶
rows = await snmp.walk("ifDescr")
idx_to_name = dict(Snmp_v2c.snmp_get_result_last_idx_force_value_type(rows, str)) if rows else {}
Extract multiple index components (composite indexes)¶
rows = await snmp.walk("1.3.6.1.2.1.10.7.2.1") # example table
idx_pairs = Snmp_v2c.extract_oid_indices(rows, num_indices=2) if rows else []
# [[33, 1], [33, 2], [34, 1], ...]
Force integer values from a table¶
rows = await snmp.walk("ifSpeed")
speed_pairs = Snmp_v2c.snmp_get_result_last_idx_force_value_type(rows, int) if rows else []
# [(2, 1000000000), (3, 100000000), ...]
Handle OctetString as bytes (for example, MAC-like values)¶
rows = await snmp.get("ifPhysAddress.2")
raw = Snmp_v2c.snmp_get_result_bytes(rows)[0]
mac = ":".join(f"{b:02x}" for b in raw)
Symbolic OID with instance suffix¶
name = Snmp_v2c.get_result_value((await snmp.get("sysName.0"))[0])
descr = Snmp_v2c.get_result_value((await snmp.get("sysDescr.0"))[0])
Parse DateAndTime to ISO-8601¶
rows = await snmp.get("hrSystemDate.0")
ts = Snmp_v2c.parse_snmp_datetime(Snmp_v2c.snmp_get_result_bytes(rows)[0])
Convert sysUpTime ticks to human duration¶
ticks = int(Snmp_v2c.get_result_value((await snmp.get("sysUpTime.0"))[0]))
print(Snmp_v2c.ticks_to_duration(ticks))
IPv6 targets¶
snmp_v6 = Snmp_v2c(host=Inet("2001:db8::100"), community="public")
rows = await snmp_v6.get("sysObjectID.0")
snmp_v6.close()
Batch GETs with asyncio.gather¶
import asyncio
oids = ["sysName.0", "sysLocation.0", "sysContact.0", "sysObjectID.0"]
results = await asyncio.gather(*[snmp.get(oid) for oid in oids], return_exceptions=True)
values = []
for res in results:
if isinstance(res, Exception) or not res:
values.append(None)
else:
values.append(Snmp_v2c.get_result_value(res[0]))
print(dict(zip(oids, values)))
General, non-DOCSIS examples¶
Simple, device-agnostic snippets that you can lift into scripts or notebooks.
1) Read system info (name, description, uptime)¶
async def read_system_info():
snmp = Snmp_v2c(Inet("192.168.0.100"), community="public")
try:
name = Snmp_v2c.get_result_value((await snmp.get("sysName.0"))[0])
descr = Snmp_v2c.get_result_value((await snmp.get("sysDescr.0"))[0])
ut = Snmp_v2c.get_result_value((await snmp.get("sysUpTime.0"))[0])
uptime = Snmp_v2c.ticks_to_duration(int(ut))
print({"sysName": name, "sysDescr": descr, "sysUpTime": uptime})
finally:
snmp.close()
2) Walk interface names¶
async def map_ifindex_to_name():
snmp = Snmp_v2c(Inet("192.168.0.100"), community="public")
try:
rows = await snmp.walk("ifDescr")
mapping = dict(Snmp_v2c.snmp_get_result_last_idx_force_value_type(rows, str)) if rows else {}
print(mapping)
finally:
snmp.close()
3) Get interface speeds as integers¶
async def interface_speeds():
snmp = Snmp_v2c(Inet("192.168.0.100"), community="public")
try:
rows = await snmp.walk("ifSpeed")
speeds = Snmp_v2c.snmp_get_result_last_idx_force_value_type(rows, int) if rows else []
print(speeds) # [(2, 1_000_000_000), ...]
finally:
snmp.close()
4) Set sysLocation/sysContact¶
from pysnmp.proto.rfc1902 import OctetString
async def set_location_and_contact():
snmp = Snmp_v2c(Inet("192.168.0.100"), community="private")
try:
await snmp.set("sysLocation.0", "MDF-Rack-A1", OctetString)
await snmp.set("sysContact.0", "noc@example.com", OctetString)
finally:
snmp.close()
5) Parse hrSystemDate.0 DateAndTime¶
async def parse_hr_system_date():
snmp = Snmp_v2c(Inet("192.168.0.100"), community="public")
try:
rows = await snmp.get("hrSystemDate.0")
raw = Snmp_v2c.snmp_get_result_bytes(rows)[0]
print(Snmp_v2c.parse_snmp_datetime(raw))
finally:
snmp.close()
6) Use compiled OIDs directly¶
from pypnm.snmp.compiled_oids import COMPILED_OIDS
async def compiled_oid_lookup():
snmp = Snmp_v2c(Inet("192.168.0.100"), community="public")
try:
sysdescr_oid = f"{COMPILED_OIDS['sysDescr']}.0"
val = Snmp_v2c.get_result_value((await snmp.get(sysdescr_oid))[0])
print(val)
finally:
snmp.close()
7) Minimal robust GET wrapper¶
async def robust_get(oid: str):
snmp = Snmp_v2c(Inet("192.168.0.100"), community="public")
try:
try:
rows = await snmp.get(oid)
return Snmp_v2c.get_result_value(rows[0]) if rows else None
except (RuntimeError, ValueError):
return None
finally:
snmp.close()
# Example
# print(asyncio.run(robust_get("sysObjectID.0")))