#!/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 import random import time import signal # Debug switch and logfile location. Set debug to 1 to enable debug logging debug = '1' f = open('/var/log/persist_test.log', 'a') # our base OID baseOID = '.1.3.6.1.4.1.8072.9999.9999' # Cache ttl in seconds. we'll also update a time stamp each time we get a # request, then compare new time stamps with the old one to determine cache age. ttl = 15 last_req = 0 # OID data will be stored in this dict. The dict shall contain two dicts: # types - the SNMP data type. e.g. {"1.1":"integer", "1.2":"string"} # values - the object values. e.g. {"1.1":"42", "1.2":"the answer"} # The checkCache function produces the data into the global namespace. oidData = {} # sortedOids is a list containing sorted OIDs, for use by GETNEXT sortedOids = [] def readStdin () : '''Read commands from snmpd from standard input.''' line = sys.stdin.readline() '''Examine input, call subroutines''' if 'PING' in line : logData('in: '+line) playPingPong() return if 'getnext' in line : target = readTarget(line) doGetNext(target) return if 'get' in line : target = readTarget(line) doGet(target) return # anything else and we bail logData('- exiting\n') sys.exit() def playPingPong () : '''Perform the snmpd secret handshake''' logData('out: '+'PONG\n') print 'PONG' def doGet(target) : '''Process a "get" request''' global oidData, baseOID # Check the cache, refresh global data if necessary checkCache() if target in oidData["values"] : type = oidData["types"][target] val = oidData["values"][target] sendData(type, val, target) else : logData('out: '+'NONE\n') print 'NONE' def doGetNext(target) : '''Process a "getnext" request.''' global oidData, sortedOids, baseOID checkCache() sentData = '' for n in sortedOids : logData('- getnext is working '+n+'\n'+'- '+target+' < '+n+'\n') if target < n : type = oidData["types"][n] val = oidData["values"][n] sendData(type, val, n) sentData = 'yep' return if not sentData : logData('out: '+'NONE\n') print 'NONE' return def readTarget(line) : global baseOID logData('in: '+line) # next line will be a target OID target = sys.stdin.readline() logData('in: '+target) # return a relative version of the target OID ext = target.replace('%s' % baseOID, '') target = ext.strip() logData('- relative target: |'+target+'|\n') return target def checkCache () : '''Check cache and populate globals as needed''' global last_req, oidData, sortedOids, run now = int(time.time()) if ( now - last_req ) > ttl : # cache is expired, get new data logData('- refreshing OID cache '+str(last_req)+'\n') last_req = int(time.time()) # These could be external calls, but for now, just some random numbers... values = {".1.1":random.randint(1,1000), ".1.2": random.randint(1,1000)} types = {".1.1":"integer", ".1.2":"integer"} oidData["values"] = values oidData["types"] = types # Also maintain a sorted version of just the OIDs, for GETNEXT sortedOids = values.keys() sortedOids.sort() logData('- sorted OIDs to: '+repr(sortedOids)+'\n') def shutDown(sig, frame) : '''We may get a SIGPIPE if stdin is going away.''' global run logData('- caught signal '+repr(sig)+'\n') # This will terminate the 'while' loop that keeps us running run = '' def sendData(type, val, target) : '''Write results to stdout''' global baseOID logData('out: '+baseOID+target+'\n') logData('out: '+type+'\n') logData('out: '+str(val)+'\n') print baseOID+target print type print val def logData (data) : '''Optional debug logging''' global debug if debug : f.write(str(data)) f.flush() # install signal handlers for graceful shutdown signal.signal(signal.SIGTERM, shutDown) signal.signal(signal.SIGPIPE, shutDown) run = "thanks" # loop logData('- starting up\n') while run : if not sys.stdin.closed : readStdin()