As we have discussed earlier, Net-SNMP is installed as an agent on most *nix machines. There is a default set of information that an agent can return, but it is also possible to extend an agent on a machine. It is reasonably straightforward to write an agent that collects just about anything and then returns the results via the SNMP protocol.
The EXAMPLE.conf file is one of the best sources for information on extending Net-SNMP, and it is included with Net-SNMP. Doing a man on snmpd.conf is also useful for more verbose information that documents the API. Both of these would be good sources of information to reference if you are interested in further study on extending native agents.
For a Python programmer, extending Net-SNMP is one of the most exciting aspects of working with SNMP, as it allows a developer to write code to monitor whatever they see fit, and to additionally have the agent internally respond to conditions that you have assigned to it.
Net-SNMP offers quite a few ways to extend its agent, but to get started we are going to write a Hello World program that we will query from snmp. The first step is to create a very simple snmpd.conf file that executes our Hello World program in Python. Example 7-4 shows what that looks like on a Red Hat machine.
Example 7-4. SNMP configuration file with Hello World syslocation "O'Reilly" syscontact [email protected] rocommunity public exec helloworld /usr/bin/python -c "print 'hello world from Python'"
Next we need to tell snmpd to re-read the configuration file. We can do that three different ways. On Red Hat you can use:
service snmpd reload or you can also do:
00:00:30 /usr/sbin/snmpd -Lsd -Lf /dev/null -p /var/run/snmpd.pid -a Then you can send it: kill -HUP 12345
Finally, the snmpset command can assign an integer (1) to UCD-SNMP-MIB::versionUpdateConfig.0, which will tell snmpd to reread the configuration file.
Now that we have modified the snmpd.conf file and told snmpd to reread the configuration file, we can go ahead and query our machine by using either the snmpwalk command-line tool or the Net-SNMP binding with IPython. Here is what it looks like from the snmpwalk command:
[[email protected]][H:4904][J:0]> snmpwalk -v 1 -c public localhost .188.8.131.52.4.1.2021.8 UCD-SNMP-MIB::extIndex.1 = INTEGER: 1 UCD-SNMP-MIB::extNames.1 = STRING: helloworld UCD-SNMP-MIB::extCommand.1 = STRING: /usr/bin/python
-c "print 'hello world from Python'" UCD-SNMP-MIB::extResult.1 = INTEGER: 0
UCD-SNMP-MIB::extOutput.1 = STRING: hello world from Python UCD-SNMP-MIB::extErrFix.1 = INTEGER: noError(0) UCD-SNMP-MIB::extErrFixCmd.1 = STRING:
This query bears some explanation, as the observant reader may wonder where we got it. 184.108.40.206.4.1.2021.8 from. This OID is the ucdavis.extTable. When you create an extension to snmpd.conf, it will assign it to this OID. Things get slightly more complicated if you would like to query a custom OID that you create. The proper way to do this is to fill out a request with iana.org and to get an enterprise number. You can then use that number to create custom queries to an agent. The main reason for this is to keep a uniform namespace that avoids collisions with other future vendor numbers you may run into.
Getting output from one-liners isn't really Python's strength, and it is kind of silly. Here is an example of a script that parses the total number of Firefox hits in an Apache log and then returns the number for a custom enterprise number. Let's start backward this time and see what it looks like when we query it:
snmpwalk -v 2c -c public localhost .220.127.116.11.4.1.2021.28664.100 UCD-SNMP-MIB::ucdavis.28618.104.22.168 = INTEGER: 1 UCD-SNMP-MIB::ucdavis.28622.214.171.124 = STRING: "FirefoxHits" UCD-SNMP-MIB::ucdavis.286126.96.36.199 = STRING:
"/usr/bin/python /opt/local/snmp_scripts/agent_ext_logs.py" UCD-SNMP-MIB::ucdavis.286188.8.131.52 = INTEGER: 0 UCD-SNMP-MIB::ucdavis.286184.108.40.206 = STRING: "Total number of Firefox Browser Hits: 15702" UCD-SNMP-MIB::ucdavis.286220.127.116.11 = INTEGER: 0 UCD-SNMP-MIB::ucdavis.28618.104.22.168 = ""
If you look at the value of 100.101.1, you will see the output of a script that uses a generator pipeline to parse an Apache log and look for all Firefox hits in the log. It then sums them and returns the output via SNMP. Example 7-5 is the script that gets run when we query this OID.
Example 7-5. Generator pipeline to look for total firefox hits in Apache logfile import re
Returns Hit Count for Firefox
pat = re.compile(pattern) for line in lines:
if pat.search(line): yield line def increment(lines): num = 0
for line in lines:
num += 1 return num wwwlog = open("/home/noahgift/logs/noahgift.com-combined-log") column = (line.rsplit(None,1) for line in wwwlog) match = grep(column) count = increment(match)
print "Total Number of Firefox Hits: %s" % count
In order for our query to work in the first place, we needed to tell snmpd.conf about this script, and here is what that section looks like:
syslocation "O'Reilly" syscontact [email protected] rocommunity public exec helloworld /usr/bin/python -c "print 'hello world from Python'" exec .22.214.171.124.4.1.2021.28664.100 FirefoxHits /usr/bin/python /opt/local/snmp_scripts/agent_ext_logs.py
The magic portion is the last line, in which .126.96.36.199.4.1.2021 is the ucdavis enterprise number, 28664 our enterprise number, and 100 is some contrived value that we decided we wanted to use. It is really important to follow best practices and use your our enterprise number if you plan on extending SNMP. The main reason is that you will avoid causing havoc if you decide to use a range already occupied by someone else and then make changes via snmpset.
We would like to close with the fact that this is one of the more exciting topics in the book, and SNMP is still a very untouched playground. There are many things that customizing Net-SNMP can be useful for, and if you are careful to use SNMP v3, you can do some incredible things that are most easily accomplished through the SNMP protocol; and that ssh or sockets would be the most natural choice.
Was this article helpful?