So, I'm trying to build an SNMP polling service to get some inventory data off my network devices. I'm able to connect to the devices using either the netsnmp or easysnmp modules.
The issue comes along when I try to change which MIBs to use for querying some of the more enterprise-specific stuff like the "chStackUnitTable" of a Force10 network device.
Since I can't say which mib to load prior to querying the device's sysObjectId.0 oid, I have to query the device first, then tell the net-snmp bindings (which both netsnmp and easysnmp rely on) to look in a specific directory, by setting the os.environ['MIBDIRS']
variable.
The problem seems to be that the net-snmp bindings ignore changes to the MIBDIRS environment variable after the first method call using those bindings.
Examples
Not working but the order I want
Example using a Force10 S3048-ON switch:
import os
import netsnmp
mib_dir_root = "/opt/project/var/lib/snmp/mibs"
session_options = {'DestHost': "10.0.0.254", 'Version': 2, 'Community': "public"}
s = netsnmp.Session(**session_options)
vl = netsnmp.VarList(netsnmp.Varbind('sysObjectID', 0))
_r = s.get(vl)
obj_id = vl[0].val
print('{:s}.{:s}: {:s}'.format(vl.tag, vl.iid, vl.val))
# output: "sysObjectID.0: .1.3.6.1.4.1.6027.1.3.23"
# We can now determine which MIB to use to get the interesting stuff (serial number,
# service tag, etc) by inspecting the obj_id. In this case we know we want to query
# the chStackUnitTable of the F10-S-SERIES-CHASSIS-MIB mib.
# Let's add the MIB directory to our MIBDIRS environment variable
mib_dir = os.path.join(mib_dir_root, 'Force10')
os.environ['MIBDIRS'] = "+{:s}".format(mib_dir))
# We also have the annoyance here of having another mib (F10-M-SERIES-CHASSIS-MIB)
# that has the same OID name of 'chStackUnitTable' at a different numeric OID. So we
# need to specify the MIB explicitly
mib = 'F10-S-SERIES-CHASSIS-MIB'
oid = 'chStackUnitTable'
vl = netsnmp.VarList(netsnmp.Varbind('{:s}:{:s}'.format(mib, oid)))
s.walk(vl)
# output:
# MIB search path: /home/username/.snmp/mibs;/usr/share/snmp/mibs
# Cannot find module (F10-S-SERIES-CHASSIS-MIB): At line 1 in (none)
# snmp_build: unknown failure
Working but bad
However, if I add the MIBDIRS environment variable prior to calling netsnmp bindings, it works:
import os
import netsnmp
mib_dir_root = "/opt/project/var/lib/snmp/mibs"
mib_dirs = ['Force10', 'Cisco', 'Dell']
mib_dirs = [os.path.join(mib_dir_root, d) for d in mib_dirs if os.path.isdir(os.path.join(mib_dir_root, d))]
os.environ['MIBDIRS'] = "+{:s}".format(";".join(mib_dirs))
print(os.environ['MIBDIRS'])
# output:
# +/opt/project/var/lib/snmp/mibs/Force10;/opt/project/var/lib/snmp/mibs/Cisco;/opt/project/var/lib/snmp/mibs/Dell;
session_options = {'DestHost': "10.0.0.254", 'Version': 2, 'Community': "public"}
s = netsnmp.Session(**session_options)
vl = netsnmp.VarList(netsnmp.Varbind('sysObjectID', 0))
_r = s.get(vl)
obj_id = vl[0].val
print('{:s}.{:s}: {:s}'.format(vl.tag, vl.iid, vl.val))
# output: "sysObjectID.0: .1.3.6.1.4.1.6027.1.3.23"
mib = 'F10-S-SERIES-CHASSIS-MIB'
oid = 'chStackUnitTable'
vl = netsnmp.VarList(netsnmp.Varbind('{:s}:{:s}'.format(mib, oid)))
_r = s.walk(vl)
cols = ['chStackUnitSerialNumber', 'chStackUnitModelID', 'chStackUnitCodeVersion', 'chStackUnitServiceTag']
for v in vl:
if v.tag in cols:
print('{:s}.{:s}: {:s}'.format(v.tag, v.iid, v.val))
# output:
# chStackUnitModelID.1: S3048-ON-01-FE-52T
# chStackUnitCodeVersion.1: 9.8(0.0P2)
# chStackUnitSerialNumber.1: NA
# chStackUnitServiceTag.1: <REDACTED>
The problem I have with this solution is scalability. I plan on supported a number of different devices and will require a MIB directory for each manufacturer. This means the MIBDIRS and the MIB search path will become quite un-wieldy. Not to mention that the net-snmp bindings will probably flake-out at some stage since it has to search through potentially thousands of MIB files.
Is there a way to clear out the bindings after the first snmp queries are done, set the MIBDIRS variable, then re-import the netsnmp module? I've tried using reload(netsnmp)
but that doesn't seem to work.
Desired code-like-text
Ideally, something like this:
...
sess.get(object_id)
# determine which mib dir to point to
os.environ['MIBDIRS'] = "+" + "path_to_mib_dir"
# magic reloading of netsnmp
sess = netsnmp.Session(**session_options)
varlist = netsnmp.VarList(netsnmp.Varbind(mib + ":" + table_oid))
sess.walk(varlist)
...
# Profit!!!