Search code examples
snmppysnmp

Use Pysnmp to update SNMP Table dynamically from agent side


I had been searching a way to use pysnmp to dynamically update a SNMP table during SNMP Agent running. But so far no luck...

The table had been already defined in MIB file(see below), but it seems I need to overwrite its "readGet()" method in order to return correct data from current system status.

According to the instructions I'm able to build a static table with pre-defined value before SNMP Agent startup, and after SNMP Agent startup:

# Register an imaginary never-ending job to keep I/O dispatcher running forever
self.snmpEngine.transportDispatcher.jobStarted(1)
# Run I/O dispatcher which would receive queries and send responses
try:
    self.snmpEngine.transportDispatcher.runDispatcher()
except:
    self.snmpEngine.transportDispatcher.closeDispatcher()
    raise

it is able to return my expected value.

But for my system, it will dynamically generate many alarm information, and those information need to be updated into SNMP's MIB Table, which allow other SNMP Manager to send "get/getNext" to fetch alarm information from my system.

So I would like to know

  • is there a way to do this in pysnmp?
  • or I have to always update my alarm table statically? and then restart the SNMP Agent after each update action?
  • or I have to initialize my alarm table with all possible 2147483647 instances during SNMP Agent startup? and use "name[-1]" to get index/row number from each query?

if so, how about getNext query? does it will ignore those empty table row? or it always return next one, even it is a dummy one?

  • or is there a better way to fetch the "index/row" number from the query message?

@Ilya Etingof, the expert of pysnmp. Could you help me with it if you have time?

Br, -Dapeng Jiao

1.

MIB file definition of that alarm table (some sensitive info are removed)

alarmTable = MibTable((1, 3, 6, *, *, *, *, *, *, *, *, *, , 3))
alarmEntry = MibTableRow((1, 3, 6, *, *, *, *, *, *, *, *, *, , 3, 1)).setIndexNames((0, "MY-MIB", "alarmIndex"))
alarmIndex = MibTableColumn((1, 3, 6, *, *, *, *, *, *, *, *, *, , 3, 1, 1), Integer32().subtype(subtypeSpec=ValueRangeConstraint(1, 2147483647))).setMaxAccess("readonly")
alarmId = MibTableColumn((1, 3, 6, *, *, *, *, *, *, *, *, *, , 3, 1, 2), Integer32().subtype(subtypeSpec=ValueRangeConstraint(1, 2147483647))).setMaxAccess("readonly")
alarmName = MibTableColumn((1, 3, 6, *, *, *, *, *, *, *, *, *, , 3, 1, 3), DisplayString().subtype(subtypeSpec=ValueSizeConstraint(0, 255))).setMaxAccess("readonly")
alarmSeverity = MibTableColumn((1, 3, 6, *, *, *, *, *, *, *, *, *, , 3, 1, 4), AlarmSeverity()).setMaxAccess("readonly")
alarmTime = MibTableColumn((1, 3, 6, *, *, *, *, *, *, *, *, *, , 3, 1, 5), DateAndTime()).setMaxAccess("readonly")
alarmType = MibTableColumn((1, 3, 6, *, *, *, *, *, *, *, *, *, , 3, 1, 6), AlarmType()).setMaxAccess("readonly")
alarmSource = MibTableColumn((1, 3, 6, *, *, *, *, *, *, *, *, *, , 3, 1, 7), DisplayString().subtype(subtypeSpec=ValueSizeConstraint(0, 255))).setMaxAccess( "readonly")
alarmCategory = MibTableColumn((1, 3, 6, *, *, *, *, *, *, *, *, *, , 3, 1, 8), DisplayString().subtype(subtypeSpec=ValueSizeConstraint(0, 255))).setMaxAccess("readonly")
alarmProbableCause = MibTableColumn((1, 3, 6, *, *, *, *, *, *, *, *, *, , 3, 1, 9), ProbableCause()).setMaxAccess("readonly")
alarmComparable = MibTableColumn((1, 3, 6, *, *, *, *, *, *, *, *, *, , 3, 1, 10), DisplayString().subtype(subtypeSpec=ValueSizeConstraint(0, 255))).setMaxAccess("readonly")
alarmAdditionalText = MibTableColumn((1, 3, 6, *, *, *, *, *, *, *, *, *, , 3, 1, 11), DisplayString().subtype(subtypeSpec=ValueSizeConstraint(0, 255))).setMaxAccess("readonly")

Solution

  • It is possible to maintain dynamic SNMP table with pysnmp. There are many approaches to this problem:

    1. Periodically update your table (via a table callback or dedicated thread) by calling mibInstrumentation.writeVars in this example script. The backsides includes delays in serving new data and queries if they come at the moment of running update. But no much coding required.

    2. Extend MibTableColumn class and implement its readGet/readGetNext methods to look into your data when being called and return OID/value pair. The complication here is that to handle GETNEXT query you need to maintain some sort of consistent ordering of OIDs and search for next greater than given.

    3. Ditch the whole pysnmp's SMI infrastructure and implement your own MIB controller on top of whatever data source you have to read data from. You would need to implement readGet (easy) and likely readGetNext (more complicated as stable OIDs sort is required) methods. This way you would relief yourself from learning the details of rather generic and complicated pysnmp SMI implementation and focus on your minimal requirements.

    To answer your other questions:

    • It is not a good idea to restart a service just to update the date it is serving.
    • You may update it dynamically (timer callback or thread) unless you really have so many (2^31) rows in your SNMP table. If they are more than a few hundreds the only way is to keep all the alarm data where it is now, read it when query comes and build SNMP response from it.
    • The knowledge of what part of OID belongs to SNMP table index belongs to MibTableRow and MibTableColumn classes. So if you extend them you could figure that out. There are helper methods for that purpose though.