#!/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()
