“Error: OID not increasing” when declaring SNMP tables with python-agentx

At work, I’m currently working with python-agentx to implement an AgentX-based SNMP agent. Testing around with snmpwalk, I got the following error:

$ snmpwalk -Of -v2c -c public localhost DFS-HW-MIB::cpus
.iso.org.dod.internet.private.enterprises.dfs.systemhaus.dfsHwMIB.cpus.cpusNumber = INTEGER: 2
.iso.org.dod.internet.private.enterprises.dfs.systemhaus.dfsHwMIB.cpus.cpusTable.cpuEntry.0 = INTEGER: 0
.iso.org.dod.internet.private.enterprises.dfs.systemhaus.dfsHwMIB.cpus.cpusTable.cpuEntry.cpuIndex.1 = INTEGER: 0
.iso.org.dod.internet.private.enterprises.dfs.systemhaus.dfsHwMIB.cpus.cpusTable.cpuEntry.cpuIndex.2 = INTEGER: 1
.iso.org.dod.internet.private.enterprises.dfs.systemhaus.dfsHwMIB.cpus.cpusTable.cpuEntry.cpuStatus.1 = INTEGER: OK(1)
.iso.org.dod.internet.private.enterprises.dfs.systemhaus.dfsHwMIB.cpus.cpusTable.cpuEntry.cpuStatus.2 = INTEGER: OK(1)
.iso.org.dod.internet.private.enterprises.dfs.systemhaus.dfsHwMIB.cpus.cpusTable.cpuEntry.cpuNumCores.1 = INTEGER: 2
Error: OID not increasing: .iso.org.dod.internet.private.enterprises.dfs.systemhaus.dfsHwMIB.cpus.cpusTable.cpuEntry.cpuStatus.2
>= .iso.org.dod.internet.private.enterprises.dfs.systemhaus.dfsHwMIB.cpus.cpusTable.cpuEntry.cpuNumCores.1


While this error can be surpressed with snmpwalk’s -Cc switch, this is not a real solution as one can not always influence the behaviour of third party SNMP applications. So where does the error come from?

With python-agentx, you declare a table like this:

	axd.Table(
		"cpuEntry",
		{
			"cpuIndex"	: [ 0, 1 ],
			"cpuDesc"	: [ "Boar 6000", "Boar 6000" ],
			"cpuNumCores"	: [ 2, 2 ],
			"cpuTemp"	: [ 45, 47 ],
			"cpuStatus"	: [ 1, 1 ]
		}
	)

This gives you rows named cpuEntry.X with columns cpuIndex.X, cpuDesc.X, cpuNumCores.X, cpuTemp.X and cpuStatus.X. Naturally, your associated MIB must contain mappings to numerical OIDs for each element.

Now section 7.2.3.2 of RFC 2741 speaks about a “lexicographically ordered list of variable names”, which is what snmpwalk checks for. Basically this means, that if cpuIndex translates to 1.1, then cpuDesc must be 1.2, cpuNumCores must be 1.3 etc. — cpuIndex being 1.4 and cpuDesc being 1.3 would be invalid if traversal using the SNMP “Get Next” command occurs the way it was defined above.

And that’s exactly the point: if using a Python dictionary (that’s what the {} imply), there is no predictable order. Traversing the dictionary with a for x in... loop such as used by python-agentx will lead to snmpwalk getting elements returned with arbitrarily “jumping” OIDs, as can be seen above.

In order to avoid this error, dictionaries would have to maintain the declaration order. While there is such a class, OrderedDict, it is not part of standard Python 2.x, so I went for a different solution using a list of tuples:

	axd.Table(
		"cpuEntry",
		[
			("cpuIndex",	[ 0, 1 ]),
			("cpuDesc",	[ "Boar 6000", "Boar 6000" ]),
			("cpuNumCores",	[ 2, 2 ]),
			("cpuTemp",	[ 45, 47 ]),
			("cpuStatus",	[ 1, 1 ])
		]
	)

Lists do maintain declaration order, so with this simple concept we can avoid a OrderedDict dependency with a simple two-linge change to the python-agentx module.

The patch can be found at Sourceforge Bug report #3518403.

Leave a comment