I'm trying to write a python SNMP agent that I can embed within my python application so that the application can be monitored remotely by OpenNMS. OpenNMS expects the Agent to implement the HOST-RESOURCES-MIB
querying two fields hrSWRunName
and hrSWRunStatus
.
I took a pysnmp example as the basis of my code and edited it as I believed necessary. The resulting code looks like this:
import logging
from pysnmp import debug
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.proto.api import v2c
from pysnmp.smi import builder, instrum, exval
# debug.setLogger(debug.Debug('all'))
formatting = '[%(asctime)s-%(levelname)s]-(%(module)s) %(message)s'
logging.basicConfig(level=logging.DEBUG, format=formatting, )
logging.info("Starting....")
# Create SNMP engine
snmpEngine = engine.SnmpEngine()
# Transport setup
# UDP over IPv4
config.addTransport(
snmpEngine,
udp.domainName,
udp.UdpTransport().openServerMode(('localhost', 12345))
)
# SNMPv2c setup
# SecurityName <-> CommunityName mapping.
config.addV1System(snmpEngine, 'my-area', 'public')
# Allow read MIB access for this user / securityModels at VACM
config.addVacmUser(snmpEngine,
2,
'my-area',
'noAuthNoPriv',
(1, 3, 6, 1, 2, 1),
(1, 3, 6, 1, 2, 1))
# Create an SNMP context
snmpContext = context.SnmpContext(snmpEngine)
logging.debug('Loading HOST-RESOURCES-MIB module...'),
mibBuilder = builder.MibBuilder().loadModules('HOST-RESOURCES-MIB')
logging.debug('done')
logging.debug('Building MIB tree...'),
mibInstrum = instrum.MibInstrumController(mibBuilder)
logging.debug('done')
logging.debug('Building table entry index from human-friendly representation...')
# see http://www.oidview.com/mibs/0/HOST-RESOURCES-MIB.html
hostRunTable, = mibBuilder.importSymbols('HOST-RESOURCES-MIB', 'hrSWRunEntry')
instanceId = hostRunTable.getInstIdFromIndices(1)
logging.debug('done')
# The following shows the OID name mapping
#
# hrSWRunTable 1.3.6.1.2.1.25.4.2 <TABLE>
# hrSWRunEntry 1.3.6.1.2.1.25.4.2.1 <SEQUENCE>
# hrSWRunIndex 1.3.6.1.2.1.25.4.2.1.1 <Integer32>
# hrSWRunName 1.3.6.1.2.1.25.4.2.1.2 <InternationalDisplayString> 64 Char
# hrSWRunID 1.3.6.1.2.1.25.4.2.1.3 <ProductID>
# hrSWRunPath 1.3.6.1.2.1.25.4.2.1.4 <InternationalDisplayString> 128 octets
# hrSWRunParameters 1.3.6.1.2.1.25.4.2.1.5 <InternationalDisplayString> 128 octets
# hrSWRunType 1.3.6.1.2.1.25.4.2.1.6 <INTEGER>
# hrSWRunStatus 1.3.6.1.2.1.25.4.2.1.7 <INTEGER> <<===== This is the key variable used by Opennms
# http://docs.opennms.org/opennms/releases/18.0.1/guide-admin/guide-admin.html#_hostresourceswrunmonitor)
logging.debug('Create/update HOST-RESOURCES-MIB::hrSWRunTable table row:')
varBinds = mibInstrum.writeVars((
(hostRunTable.name + (1,) + instanceId, 1),
(hostRunTable.name + (2,) + instanceId, 'AppName'), # <=== Must match OpenNMS service-name variable
(hostRunTable.name + (3,) + instanceId, {0,0}), #
(hostRunTable.name + (4,) + instanceId, 'All is well'),
(hostRunTable.name + (5,) + instanceId, 'If this was not the case it would say so here'),
(hostRunTable.name + (6,) + instanceId, 4),# Values are ==> unknown(1), operatingSystem(2), deviceDriver(3), application(4)
(hostRunTable.name + (7,) + instanceId, 1) #<<=== This is the status number OpenNMS looks at Values are ==> running(1), runnable(2), notRunnable(3), invalid(4)
))
for oid, val in varBinds:
print('%s = %s' % ('.'.join([str(x) for x in oid]), val.prettyPrint()))
logging.debug('done')
logging.debug('Read whole MIB (table walk)')
oid, val = (), None
while True:
oid, val = mibInstrum.readNextVars(((oid, val),))[0]
if exval.endOfMib.isSameTypeWith(val):
break
print('%s = %s' % ('.'.join([str(x) for x in oid]), val.prettyPrint()))
logging.debug('done')
# logging.debug('Unloading MIB modules...'),
# mibBuilder.unloadModules()
# logging.debug('done')
# --- end of table population ---
# Register SNMP Applications at the SNMP engine for particular SNMP context
cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
cmdrsp.SetCommandResponder(snmpEngine, snmpContext)
cmdrsp.NextCommandResponder(snmpEngine, snmpContext)
cmdrsp.BulkCommandResponder(snmpEngine, snmpContext)
# Register an imaginary never-ending job to keep I/O dispatcher running forever
snmpEngine.transportDispatcher.jobStarted(1)
# Run I/O dispatcher which would receive queries and send responses
try:
snmpEngine.transportDispatcher.runDispatcher()
except:
snmpEngine.transportDispatcher.closeDispatcher()
raise
The code runs without producing errors. varBinds and MIB Table walk show what I think I should expect:
[2016-12-29 16:42:49,323-INFO]-(SNMPAgent) Starting....
[2016-12-29 16:42:49,470-DEBUG]-(SNMPAgent) Loading HOST-RESOURCES-MIB module...
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) done
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) Building MIB tree...
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) done
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) Building table entry index from human-friendly representation...
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) done
[2016-12-29 16:42:49,632-DEBUG]-(SNMPAgent) Create/update HOST-RESOURCES-MIB::hrSWRunTable table row:
1.3.6.1.2.1.25.4.2.1.1.1 = 1
[2016-12-29 16:42:49,651-DEBUG]-(SNMPAgent) done
1.3.6.1.2.1.25.4.2.1.2.1 = TradeLoader
1.3.6.1.2.1.25.4.2.1.3.1 = 0
1.3.6.1.2.1.25.4.2.1.4.1 = All is well
1.3.6.1.2.1.25.4.2.1.5.1 = If this was not the case it would say so here
1.3.6.1.2.1.25.4.2.1.6.1 = 'application'
1.3.6.1.2.1.25.4.2.1.7.1 = 'running'
[2016-12-29 16:42:49,651-DEBUG]-(SNMPAgent) Read whole MIB (table walk)
1.3.6.1.2.1.25.4.2.1.1.1 = 1
1.3.6.1.2.1.25.4.2.1.2.1 = TradeLoader
1.3.6.1.2.1.25.4.2.1.3.1 = 0
1.3.6.1.2.1.25.4.2.1.4.1 = All is well
1.3.6.1.2.1.25.4.2.1.5.1 = If this was not the case it would say so here
1.3.6.1.2.1.25.4.2.1.6.1 = 'application'
1.3.6.1.2.1.25.4.2.1.7.1 = 'running'
1.3.6.1.2.1.25.5.1.1.1.1 = <no value>
1.3.6.1.2.1.25.5.1.1.2.1 = <no value>
[2016-12-29 16:42:53,490-DEBUG]-(SNMPAgent) done
Finally, the dispatcher is started.
The problem is that when I try and query the agent nothing happens. I get no response. I have looked at my code and one obvious thing about it is the fact that I do not explicitly link the snmpEngine to my created MIB. Should I do this?
Any insight would be greatly received as I'm struggling to understand where to go at the moment.
I thought I would just post an answer to my question as it took me so long to figure out how to do what I need. Hopefully someone else will find this useful. The following code allows me to populate any MIB known to pysnmp and then make the MIB available to the network as an V2 SNMP Agent.
The logger messages show
And the agent can be queried to show get the results as follows: