I'm having difficulty implementing a custom MIB in a PySNMP agent.
I've started with its docs site, created my own MIB file, used build-pysnmp-mib
to make a Python module and successfully imported the symbol.
I can't see where to go next. I need to somehow mount the imported symbol on the list of served MIBs and provide an implementation. (It's currently a MIB with one read-only INTEGER property.)
The MIB file passes smilint
without warnings, but I've had to manually add a missing MibScalar
import to the generated module.
MIB:
TRS-MIB DEFINITIONS ::= BEGIN
internet OBJECT IDENTIFIER ::= { iso(1) org(3) dod(6) 1 }
enterprises OBJECT IDENTIFIER ::= { internet private(4) 1 }
thorcom OBJECT IDENTIFIER ::= { enterprises 27817 }
trs OBJECT IDENTIFIER ::= { thorcom 2 }
trsEntry OBJECT IDENTIFIER ::= { trs 1 }
trsDeliveryTime OBJECT-TYPE
SYNTAX Integer32
ACCESS not-accessible
STATUS mandatory
DESCRIPTION "Average message delivery time in milliseconds."
::= { trsEntry 1 }
END
Code:
#!/usr/bin/env python
# Command Responder
from pysnmp.entity import engine, config
from pysnmp.carrier.asynsock.dgram import udp
#from pysnmp.carrier.asynsock.dgram import udp6
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.proto.rfc1902 import OctetString
from pysnmp.smi import builder
from pysnmp import debug
debug.setLogger(debug.Debug('all'))
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
# Setup UDP over IPv4 transport endpoint
config.addSocketTransport(
snmpEngine,
udp.domainName,
udp.UdpSocketTransport().openServerMode(('127.0.0.1', 161))
)
# Start of new code
mibBuilder = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder
mibSources = mibBuilder.getMibSources() + (
builder.DirMibSource('.'),
)
mibBuilder.setMibSources(*mibSources)
# Create and put on-line my managed object
deliveryTime, = mibBuilder.importSymbols('TRS-MIB', 'trsDeliveryTime')
Integer32, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMPv2-SMI', 'Integer32')
MibScalarInstance, = mibBuilder.importSymbols('SNMPv2-SMI', 'MibScalarInstance')
class MyDeliveryTime(Integer32):
def readGet(self, name, val, idx, (acFun, acCtx)):
return name, self.syntax.clone(42)
deliveryTimeInstance = MibScalarInstance(
deliveryTime.name, (0,), deliveryTime.syntax
)
mibBuilder.exportSymbols('TRS-MIB', deliveryTimeInstance=deliveryTimeInstance) # creating MIB
# End of new code
# v1/2 setup
config.addV1System(snmpEngine, 'test-agent', 'public')
# v3 setup
config.addV3User(
snmpEngine, 'test-user',
config.usmHMACMD5AuthProtocol, 'authkey1',
config.usmDESPrivProtocol, 'privkey1'
)
# VACM setup
config.addContext(snmpEngine, '')
config.addRwUser(snmpEngine, 1, 'test-agent', 'noAuthNoPriv', (1,3,6)) # v1
config.addRwUser(snmpEngine, 2, 'test-agent', 'noAuthNoPriv', (1,3,6)) # v2c
config.addRwUser(snmpEngine, 3, 'test-user', 'authPriv', (1,3,6)) # v3
# SNMP context
snmpContext = context.SnmpContext(snmpEngine)
# Apps registration
cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
cmdrsp.SetCommandResponder(snmpEngine, snmpContext)
cmdrsp.NextCommandResponder(snmpEngine, snmpContext)
cmdrsp.BulkCommandResponder(snmpEngine, snmpContext)
snmpEngine.transportDispatcher.jobStarted(1) # this job would never finish
snmpEngine.transportDispatcher.runDispatcher()
Generated and amended TRS-MIB.py:
# PySNMP SMI module. Autogenerated from smidump -f python TRS-MIB
# by libsmi2pysnmp-0.1.1 at Fri Aug 31 13:56:45 2012,
# Python version (2, 6, 6, 'final', 0)
# Imported just in case new ASN.1 types would be created
from pyasn1.type import constraint, namedval
# Imports
( Integer, ObjectIdentifier, OctetString, ) = mibBuilder.importSymbols("ASN1", "Integer", "ObjectIdentifier", "OctetString")
( Bits, Integer32, MibIdentifier, MibScalar, TimeTicks, ) = mibBuilder.importSymbols("SNMPv2-SMI", "Bits", "Integer32", "MibIdentifier", "MibScalar", "TimeTicks")
# Objects
internet = MibIdentifier((1, 3, 6, 1))
enterprises = MibIdentifier((1, 3, 6, 1, 4, 1))
thorcom = MibIdentifier((1, 3, 6, 1, 4, 1, 27817))
trs = MibIdentifier((1, 3, 6, 1, 4, 1, 27817, 2))
trsEntry = MibIdentifier((1, 3, 6, 1, 4, 1, 27817, 2, 1))
trsDeliveryTime = MibScalar((1, 3, 6, 1, 4, 1, 27817, 2, 1, 1), Integer32()).setMaxAccess("noaccess")
if mibBuilder.loadTexts: trsDeliveryTime.setDescription("Average message delivery time in milliseconds.")
# Augmentions
# Exports
# Objects
mibBuilder.exportSymbols("TRS-MIB", internet=internet, enterprises=enterprises, thorcom=thorcom, trs=trs, trsEntry=trsEntry, trsDeliveryTime=trsDeliveryTime)
Update:
I now have one error left:
$ snmpget -v2c -c public localhost .1.3.6.1.4.1.27817.2.1.1
Error in packet
Reason: noAccess
Failed object: iso.3.6.1.4.1.27817.2.1.1
The debug is:
DBG: handle_read: transportAddress ('127.0.0.1', 48191) incomingMessage '0,\x02\x01\x01\x04\x06public\xa0\x1f\x02\x04>9\xc4\xa0\x02\x01\x00\x02\x01\x000\x110\x0f\x06\x0b+\x06\x01\x04\x01\x81\xd9)\x02\x01\x01\x05\x00'
DBG: receiveMessage: msgVersion 1, msg decoded
DBG: prepareDataElements: Message:
version='version-2'
community=public
data=PDUs:
get-request=GetRequestPDU:
request-id=1043973280
error-status='noError'
error-index=0
variable-bindings=VarBindList:
VarBind:
name=1.3.6.1.4.1.27817.2.1.1
=_BindValue:
unSpecified=
DBG: value index rebuilt at (1, 3, 6, 1, 6, 3, 18, 1, 1, 1, 2), 1 entries
DBG: processIncomingMsg: looked up securityName MibScalarInstance((1, 3, 6, 1, 6, 3, 18, 1, 1, 1, 3, 116, 101, 115, 116, 45, 97, 103, 101, 110, 116), test-agent) contextEngineId MibScalarInstance((1, 3, 6, 1, 6, 3, 18, 1, 1, 1, 4, 116, 101, 115, 116, 45, 97, 103, 101, 110, 116), �O�c�@��) contextName MibScalarInstance((1, 3, 6, 1, 6, 3, 18, 1, 1, 1, 5, 116, 101, 115, 116, 45, 97, 103, 101, 110, 116), ) by communityName MibScalarInstance((1, 3, 6, 1, 6, 3, 18, 1, 1, 1, 2, 116, 101, 115, 116, 45, 97, 103, 101, 110, 116), public)
DBG: processIncomingMsg: generated maxSizeResponseScopedPDU 65379 securityStateReference 12831470
DBG: prepareDataElements: SM returned securityEngineID SnmpEngineID(hexValue='8004fb857f00163de40e2b7') securityName test-agent
DBG: prepareDataElements: cached by new stateReference 2662033
DBG: receiveMessage: MP succeded
DBG: receiveMessage: PDU GetRequestPDU:
request-id=1043973280
error-status='noError'
error-index=0
variable-bindings=VarBindList:
VarBind:
name=1.3.6.1.4.1.27817.2.1.1
=_BindValue:
unSpecified=
DBG: receiveMessage: pduType TagSet(Tag(tagClass=128, tagFormat=32, tagId=0))
DBG: processPdu: stateReference 2662033, varBinds [(ObjectName(1.3.6.1.4.1.27817.2.1.1), Null(''))]
DBG: getMibInstrum: contextName "", mibInstum <pysnmp.smi.instrum.MibInstrumController instance at 0x7fcbfe3d5e60>
DBG: flipFlopFsm: inputNameVals [(ObjectName(1.3.6.1.4.1.27817.2.1.1), Null(''))]
DBG: flipFlopFsm: state start status ok -> fsmState readTest
DBG: flipFlopFsm: fun <bound method MibTree.readTest of MibTree((1,), None)> failed NoAccessError({'name': (1, 3, 6, 1, 4, 1, 27817, 2, 1, 1), 'idx': 0}) for 1.3.6.1.4.1.27817.2.1.1=Null('')
DBG: flipFlopFsm: state readTest status err -> fsmState stop
DBG: sendRsp: stateReference 2662033, errorStatus noAccess, errorIndex 1, varBinds [(ObjectName(1.3.6.1.4.1.27817.2.1.1), Null(''))]
DBG: returnResponsePdu: PDU ResponsePDU:
request-id=1043973280
error-status='noAccess'
error-index=1
variable-bindings=VarBindList:
VarBind:
name=1.3.6.1.4.1.27817.2.1.1
=_BindValue:
unSpecified=
DBG: prepareResponseMessage: cache read msgID 1043973280 transportDomain (1, 3, 6, 1, 6, 1, 1) transportAddress ('127.0.0.1', 48191) by stateReference 2662033
DBG: prepareResponseMessage: using contextEngineId SnmpEngineID(hexValue='8004fb857f00163de40e2b7') contextName
DBG: generateResponseMsg: recovered community public by securityStateReference 12831470
DBG: generateResponseMsg: Message:
version='version-2'
community=public
data=PDUs:
response=ResponsePDU:
request-id=1043973280
error-status='noAccess'
error-index=1
variable-bindings=VarBindList:
VarBind:
name=1.3.6.1.4.1.27817.2.1.1
=_BindValue:
unSpecified=
DBG: returnResponsePdu: MP suceeded
DBG: receiveMessage: processPdu succeeded
DBG: handle_write: transportAddress ('127.0.0.1', 48191) outgoingMessage '0,\x02\x01\x01\x04\x06public\xa2\x1f\x02\x04>9\xc4\xa0\x02\x01\x06\x02\x01\x010\x110\x0f\x06\x0b+\x06\x01\x04\x01\x81\xd9)\x02\x01\x01\x05\x00'
As for implementing a Managed Object Instance, you have two choices:
Load and subclass the MibScalarInstance class, then override its readGet() method to make it returning your live value. Then instantiate your new class (make sure to pass it appropriate OID that identifies it) and pass it to exportSymbols(), so its OID will get registered at pysnmp Agent.
Load the Integer32 class, subclass it and override its "clone()" method to make it returning your live value. Then load the MibScalarInstance class, instantiate it passing appropriate OID and the instance of your Integer32 subclass, then pass MibScalarInstance object to exportSymbols(), so its OID will get registered at pysnmp Agent.
It may make sense to keep all your code in your own MIB module. Take a look at pysnmp/smi/mibs/instances/*.py to get an idea.
From within your Agent app, invoke mibBuilder.loadModules('TRC-MIB') to load your MIB module into Agent.
In your code you seem to somehow combine the above two approaches: MyDeliveryTime.readGet() will not work, however MyDeliveryTime.clone() or deliveryTimeInstance.readGet() will.