Simple SNMP Poller with Python

Just want to share how to implement simple SNMP Poller with pySNMP, this code using Oracle Database to save MIB dimension, save pool result and others.

OID Dimension Table definition:

CREATE TABLE "NMS"."DIM_OID" 
   (	"OID" VARCHAR2(150 BYTE), 
	"ALIAS" VARCHAR2(150 BYTE), 
	"DESCRIPTION" VARCHAR2(150 BYTE), 
	 CONSTRAINT "DIM_OID_UK1" UNIQUE ("OID")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "NMS"  ENABLE
   ) PCTFREE 0 PCTUSED 40 INITRANS 1 MAXTRANS 255 COMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "NMS" ;

Server Parameter Table definition :

  CREATE TABLE "NMS"."PAR_SERVER" 
   (	"ID" NUMBER(9,0), 
	"HOSTNAME" VARCHAR2(50 BYTE), 
	"DESCRIPTION" VARCHAR2(150 BYTE), 
	"IP_ADDRESS" VARCHAR2(15 BYTE), 
	"RO_COMMUNITY" VARCHAR2(50 BYTE), 
	"RW_COMMUNITY" VARCHAR2(50 BYTE), 
	"AUTH_USER" VARCHAR2(50 BYTE), 
	"AUTH_PASS" VARCHAR2(50 BYTE), 
	"STATUS" VARCHAR2(4 BYTE), 
	"SNMP_VERSION" VARCHAR2(3 BYTE), 
	 CONSTRAINT "PAR_SERVER_UK1" UNIQUE ("ID")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "NMS"  ENABLE
   ) PCTFREE 0 PCTUSED 40 INITRANS 1 MAXTRANS 255 COMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "NMS" ;

OID Server To Be Check Table definition :

  CREATE TABLE "NMS"."DIM_SERVER_CHECK" 
   (	"HOSTNAME" VARCHAR2(50 BYTE), 
	"OID" VARCHAR2(150 BYTE), 
	 CONSTRAINT "DIM_SERVER_CHECK_UK1" UNIQUE ("HOSTNAME", "OID")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "NMS"  ENABLE
   ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "NMS" ;

Reporting Table definition:

  CREATE TABLE "NMS"."RPT_STATISTIC" 
   (	"ID" NUMBER(9,0), 
	"HOSTNAME" VARCHAR2(50 BYTE), 
	"OID" VARCHAR2(150 BYTE), 
	"VALUE" VARCHAR2(200 BYTE), 
	"QUERY_TIME" DATE
   ) PCTFREE 0 PCTUSED 40 INITRANS 1 MAXTRANS 255 COMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "NMS" ;
 
  CREATE INDEX "NMS"."RPT_STATISTIC_INDEX1" ON "NMS"."RPT_STATISTIC" ("HOSTNAME", "OID", "QUERY_TIME") 
  PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "NMS" ;
 
  CREATE OR REPLACE TRIGGER "NMS"."TRG_RPT_STATISTIC" 
 BEFORE INSERT ON RPT_STATISTIC
 FOR EACH ROW
BEGIN
  :NEW.QUERY_TIME := SYSDATE;
END;


/
ALTER TRIGGER "NMS"."TRG_RPT_STATISTIC" ENABLE;

Poller class definition:


import sys, os
import cx_Oracle

sys.path.append(os.environ['NMS_LIB'])
import parameters, logger, GetPoller

## [ Class Definition ]
class ServerInformation:
    server = None

    def __init__(self):
        self.server = {"hostname":"", "ip_address":"", "ro_community":"", "rw_community":"", "auth_user":"", "auth_password":""}


class Poller:
    connection = None
    getPoller = GetPoller.GetPoller()

    def __init__(self):
        None

    def createOracleConnection(self, config):
        try:
            self.connection = cx_Oracle.connect(config['username'] + '/' + config['password'] + '@' + config['sid'])
            return self.connection
        except Exception, errmsg:
            logger.write(errmsg)
            exit(1)
    
    def getTargetHost(self):
        try:
            result = []
            
            q_host = "SELECT hostname, ip_address, ro_community, rw_community, auth_user, auth_pass FROM PAR_SERVER WHERE STATUS = 'UP'"
            stmt = self.connection.cursor()
            stmt.execute(q_host)
            ##fetching all host information
            rs = stmt.fetchall()
            for res in rs:
                ##print res
                serverInformation = ServerInformation()
                serverInformation.server["hostname"] = str(res[0])
                serverInformation.server["ip_address"] = res[1]
                serverInformation.server["ro_community"] = res[2]
                serverInformation.server["rw_community"] = res[3]
                serverInformation.server["auth_user"] = res[4]
                serverInformation.server["auth_password"] = res[4]
                result.append(serverInformation)
            stmt.close()
    
            ##print result
            return result
        except Exception, errmsg:
            logger.write(errmsg)
            exit(1)

    def getConnection(self):
        return self.connection

    def insertStatistic(self, info, param):
        try:
            q_insert = "INSERT INTO RPT_STATISTIC (ID, HOSTNAME, OID, VALUE) VALUES (SEQ_RPT_STAT.nextval, :p_hostname, :p_oid, :p_value)"
            binSel = {"p_hostname":info.server["hostname"], "p_oid":param["oid"], "p_value":param["value"]}

            stmt = self.getConnection().cursor()
            stmt.execute(q_insert, binSel)
            self.getConnection().commit()
            stmt.close()
        except Exception, errmsg:
            logger.write(errmsg)

    def getOidList(self):
        try:
            result = []
            
            
            q_host = "SELECT oid, alias FROM DIM_OID"
            stmt = self.connection.cursor()
            stmt.execute(q_host)
            ##fetching all host information
            rs = stmt.fetchall()
            for res in rs:
                info = {"oid":"", "alias":""}
                info["oid"] = str(res[0])
                info["alias"] = res[1]
                result.append(info)
            stmt.close()
    
            return result
        except Exception, errmsg:
            logger.write(errmsg)
            exit(1)

GET Poller command class definition:


import os, sys
from pysnmp.entity.rfc3413.oneliner import cmdgen

sys.path.append(os.environ['NMS_LIB'])
import logger
##[ Global Variable Definition ]


## [ Class Definition ]
class ServerInformation:
    server = None

    def __init__(self):
        self.server = {"hostname":"", "ip_address":"", "ro_community":"", "rw_community":"", "auth_user":"", "auth_password":""}


class GetPoller:
    commandGenerator = cmdgen.CommandGenerator()
    serverInformation = ServerInformation()
    udpTransport = None
    comdata = None
    oid = None
    
    def __init__(self):
        None

    def initialize(self, serverInformation):
        self.serverInformation = serverInformation
        ##print 'setup connectio to SNMP agent.'
        self.udpTransport = cmdgen.UdpTransportTarget((self.serverInformation.server["ip_address"], 161))
        self.comdata = cmdgen.CommunityData(self.serverInformation.server["auth_user"], self.serverInformation.server["ro_community"], 0)
        ##print 'done.'

    def get(self, oid):
        self.oid = self.createOID(oid.split("."))
        errorIndication, errorStatus, errorIndex, varBinds = self.commandGenerator.getCmd(self.comdata, self.udpTransport, self.oid)
        for oid, val in varBinds:
            return val.prettyPrint() 

    def createOID(self, tokens):
        result = ()
        for token in tokens:
            result += (int(token),)
        return result

Main program of poller:


import sys, os
import socket
import threading
import time

sys.path.append(os.environ['NMS_LIB'])
import parameters, logger, GetPoller, Poller

##[ Global Variable Definition ]
par = parameters.Parameter()
confFileName = os.environ['NMS_HOME'] + os.path.sep + 'config' + os.path.sep + 'connections.cfg'
dbConf = par.getParams(confFileName)


pollerConf= os.environ['NMS_HOME'] + os.path.sep + 'config' + os.path.sep + 'poller.cfg'
pollerConfig = par.getParams(pollerConf)

## [ Class Definition ]
class Starter(threading.Thread):
    def __init__(self, lock):
        self.lock= lock
        threading.Thread.__init__ ( self )
        
    def run(self):
        poller = Poller.Poller()
        ##setup oracle connection
        print 'setup oracle connection....'
        conn = poller.createOracleConnection(dbConf)
        
        print 'initialize target host to query....'
        serverList = poller.getTargetHost()
        
        print 'initialize oid target host to be query'
        oidList = poller.getOidList()
        
        print 'polling query to agent....'
        
        for server in serverList:
            ##print server.server
            poller.getPoller.initialize(server)
            for info in oidList:
                ##print  info["oid"]
                param = {"oid":"", "value":""}
                param["oid"] = info["oid"]
                param["value"] = poller.getPoller.get(info["oid"])
        
                if param["value"] != '':
                    poller.insertStatistic(server, param)
        
        print 'process complete.....'
        conn.close()


class ClientThread(threading.Thread):
    # Override Thread's __init__ method to accept the parameters needed:
    def __init__ ( self, channel, details, lock ):
        self.channel = channel
        self.details = details
        self.lock = lock

        threading.Thread.__init__ ( self )

    def run ( self ):
        flag = True
        while flag:
            command = self.channel.recv ( 1024 )
            command = command.strip()
            
            print 'Received connection:', self.details [ 0 ]
            print command

            if command == "shutdown":
                self.channel.send("SNMP Poller agent will be shutdown.....\nPlease close your connection.....\n")
                os.remove(self.lock)
            elif command == "close":
                flag = False
            elif command == "help":
                ##print help command
                self.channel.send("___________________________________________________\n")
                self.channel.send("""SNMP Poller agent version 0.1\nHelp:\nshutdown\t: Used to shutdown SNMP Poller agent.\nclose\t\t: Used to close connection session.\n""")
                self.channel.send("___________________________________________________\n")
            else:
                self.channel.send("Unknow command, type help to see detail.\n")

        self.channel.close()



class Server(threading.Thread):
    def __init__(self, lock):
        self.lock = lock
        self.flag = True
        threading.Thread.__init__(self)
        ##create lock on server
        lock = open(self.lock,"w")
        lock.close()

    def run(self):
        ## Set up the server:
        
        server = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
        server.bind ( ( '', int(pollerConfig["port"]) ) )
        server.listen ( int(pollerConfig["max_connection"]) )

        while self.flag == True:
            channel, details = server.accept()
            ClientThread ( channel, details , self.lock).start()
            self.flag = os.path.exists(self.lock)
            if flag == False:
                break

        server.close()


##[ Procedure or Function Definition ]



##[ Main Program ]

##create lock on server
lockFilename = os.environ['NMS_HOME'] + os.path.sep + 'config' + os.path.sep + "SNMPpoller.lock"
lock = open(lockFilename, "w")
lock.close()

##setup socket server
Server(lockFilename).start()

##running poller
flag = True
while flag == True:
    time.sleep(int(pollerConfig["sleep"]))
    Starter(lockFilename).start()
    flag = os.path.exists(lockFilename)
    if flag == False:
        break


sys.exit()
##[ End Program ]

This poller program using some of configuration, configuration for connection to database and poller server configuration.
Sample connections.cfg :
username=nms
password=nms
sid=oratunggul

Sample poller.cfg :
sleep=300
max_connection=1
port=2728

Hopefully this will be useful..


About this entry