Net SNMP

From Wikifications
Revision as of 16:30, 29 June 2007 by Dre (Talk | contribs)

Jump to: navigation, search

In investigating how to dynamically produce data that I could plumb via snmp, it became quickly apparent that the snmpd shipped on Intel macs in 10.4.* is pretty well busted. It only supports a very small subset of the functionality provided by the net-snmp daemon in general (i.e. on other platforms). This can be observed by running snmpd with the -H switch. Notable omissions include 'exec' and 'extend', for example, which are typically used to easily fork off arbitrary sub processes tasked with collecting data for the targeted OID or range of OIDs.

It was quickly obvious that we'd have to use the macports snmpd. That version does have support for more functionality than the one shipped with Mac OS X, but it's *missing* exec and extend! But wait! What's this about 'pass' and 'pass_persist'? They are similar to 'exec' except that pass and pass_persist allow the script to dynamically define the OID hierarchy. This is different from exec; with exec you call out the specific OID to script mappings in the config file. With pass_persist, the script takes control of an entire 'tree', and is responsible for responding for any OIDs under that tree.

To use pass_persist, we need a script that can speak the net-snmp pass_persist protocol, which is described in the snmpd.conf(5) man page.

       pass_persist [-p priority] MIBOID PROG
              will also pass control of the subtree rooted at  MIBOID  to  the
              specified  PROG  command.  However this command will continue to
              run after the initial request has been answered,  so  subsequent
              requests can be processed without the startup overheads.

              Upon  initialization, PROG will be passed the string "PING\n" on
              stdin, and should respond by printing "PONG\n" to stdout.

              For GET and GETNEXT requests, PROG will be passed two  lines  on
              stdin,  the  command (get or getnext) and the requested OID.  It
              should respond by printing three lines to stdout - the  OID  for
              the  result  varbind, the TYPE and the VALUE itself - exactly as
              for the pass directive above.  If the command cannot  return  an
              appropriate  varbind,  it  should print print "NONE\n" to stdout
              (but continue running).

A sample script might look something like this - note, this is just proof of concept (does not implement getnext, for example):

#!/usr/bin/python -u
'''This is a python backend for snmp's "pass_persist" function. It is critical
that the python interpreter be invoked with unbuffered STDIN and STDOUT by use
of the -u switch in the shebang line.'''

import sys
from select import select

def readStdin () :
  '''Read from standard input. Use "select" to wait for new data'''
  (rr, wr, er) = select([sys.stdin], [], [])
  for fd in rr:
    line = fd.readline()
    processInput(line)

def processInput (line) :
  '''Examine input, call subroutines'''
  if 'PING' in line :
    playPingPong()
  if 'get' in line :
    target = sys.stdin.readline()
    doGet(target)

def playPingPong () :
  '''Perform the snmpd secret handshake'''
  print 'PONG'

def doGet(target) :
  '''Process a "get" request'''
  print target,
  print 'integer'
  print '42'

# loop
while 1 : readStdin()

Next, we add an entry in the snmpd config file, associating an SNMP OID tree with our script:

pass_persist .1.3.6.1.4.1.2021.255 /path/to/persist_test.py

Now let's start snmpd in a debug mode, asking for details about the pass_persist backend:

snmpd -c /etc/snmpd.conf --logTimeStamp=true -f -Lf /tmp/snmpd.log \
-Ducd-snmp/pass_persist,ucd-snmp/pass,snmpd,output,helper:debug

Tail that log file in one window, and make an snmp request in another.

snmpget -v 1 -c <community> localhost .1.3.6.1.4.1.2021.255.2

In the snmpd debug log, we see:

2007-06-29 16:06:09 ucd-snmp/pass_persist: open_persist_pipe(1,'/path/to/persist_test.py')
2007-06-29 16:06:09 ucd-snmp/pass_persist: persistpass-sending:
get
.1.3.6.1.4.1.2021.255.2

... and then a response to our snmp query:

UCD-SNMP-MIB::ucdavis.255.2 = INTEGER: 42

Sweet!