Всем привет!
Есть агент snmp, написанный на python на основе библиотеки netsnmpagent. Со скалярными переменными всё работает, проблема с таблицами.
Сам агент
#!/usr/bin/env python36
import sys, os, signal, time
import optparse, threading, subprocess
sys.path.insert(0, os.path.dirname(os.getcwd()))
import netsnmpagent
prgname = sys.argv[0]
parser = optparse.OptionParser()
parser.add_option(
"-i",
"--interval",
dest="interval",
help="Set interval in seconds between data updates",
default=30
)
parser.add_option(
"-m",
"--mastersocket",
dest="mastersocket",
help="Sets the transport specification for the master agent's AgentX socket",
default="/var/run/agentx/master"
)
parser.add_option(
"-p",
"--persistencedir",
dest="persistencedir",
help="Sets the path to the persistence directory",
default="/var/lib/net-snmp"
)
(options, args) = parser.parse_args()
headerlogged = 0
def LogMsg(msg):
""" Writes a formatted log message with a timestamp to stdout. """
global headerlogged
if headerlogged == 0:
print("{0:<8} {1:<90} {2}".format(
"Time",
"MainThread",
"UpdateSNMPObjsThread"
))
print("{0:-^120}".format("-"))
headerlogged = 1
threadname = threading.currentThread().name
funcname = sys._getframe(1).f_code.co_name
if funcname == "<module>":
funcname = "Main code path"
elif funcname == "LogNetSnmpMsg":
funcname = "net-snmp code"
else:
funcname = "{0}()".format(funcname)
if threadname == "MainThread":
logmsg = "{0} {1:<112.112}".format(
time.strftime("%T", time.localtime(time.time())),
"{0}: {1}".format(funcname, msg)
)
else:
logmsg = "{0} {1:>112.112}".format(
time.strftime("%T", time.localtime(time.time())),
"{0}: {1}".format(funcname, msg)
)
print(logmsg)
def LogNetSnmpMsg(priority, msg):
""" Log handler for log messages generated by net-snmp code. """
LogMsg("[{0}] {1}.".format(priority, msg))
# Create an instance of the netsnmpAgent class
try:
agent = netsnmpagent.netsnmpAgent(
AgentName = "ThreadingAgent",
MasterSocket = options.mastersocket,
PersistenceDir = options.persistencedir,
MIBFiles = ["/usr/share/snmp/mibs/roschat-m0-mib.mib"],
LogHandler = LogNetSnmpMsg
)
except netsnmpagent.netsnmpAgentException as e:
print("{0}: {1}".format(prgname, e))
sys.exit(1)
from params import Params
params = Params(agent) #здесь вызывается другой скрипт, в котором формируется таблица
params.register()
def UpdateSNMPObjs():
""" Function that does the actual data update. """
global threadingString
LogMsg("Beginning data update.")
data = ""
params.update()
LogMsg("Data update done, exiting thread.")
def UpdateSNMPObjsAsync():
""" Starts UpdateSNMPObjs() in a separate thread. """
if threading.active_count() == 1:
LogMsg("Creating thread for UpdateSNMPObjs().")
t = threading.Thread(target=UpdateSNMPObjs, name="UpdateSNMPObjsThread")
t.daemon = True
t.start()
else:
LogMsg("Data update still active, data update interval too low?")
try:
agent.start()
except netsnmpagent.netsnmpAgentException as e:
LogMsg("{0}: {1}".format(prgname, e))
sys.exit(1)
LogMsg("Doing initial call to UpdateSNMPObjsAsync().")
UpdateSNMPObjsAsync()
def TermHandler(signum, frame):
global loop
loop = False
signal.signal(signal.SIGINT, TermHandler)
signal.signal(signal.SIGTERM, TermHandler)
def AlarmHandler(signum, frame):
global loop, timer_triggered
LogMsg("Got triggered by SIGALRM.")
if loop:
timer_triggered = True
UpdateSNMPObjsAsync()
signal.signal(signal.SIGALRM, AlarmHandler)
signal.setitimer(signal.ITIMER_REAL, float(options.interval))
msg = "Installing SIGALRM handler triggered every {0} seconds."
msg = msg.format(options.interval)
LogMsg(msg)
signal.signal(signal.SIGALRM, AlarmHandler)
signal.setitimer(signal.ITIMER_REAL, float(options.interval))
LogMsg("Now serving SNMP requests, press ^C to terminate.")
loop = True
while loop:
timer_triggered = False
res = agent.check_and_process()
if res == -1 and not timer_triggered and loop:
loop = False
LogMsg("Error {0} in SNMP packet processing!".format(res))
elif loop and timer_triggered:
LogMsg("net-snmp's check_and_process() returned due to SIGALRM (res={0}), doing another loop.".format(res))
elif loop:
LogMsg("net-snmp's check_and_process() returned (res={0}), doing another loop.".format(res))
LogMsg("Terminating.")
agent.shutdown()
Скрипт params.py, в котором формируется таблица
import sys,os
from dbusclient import Dbusclient
import yaml
import json
if __name__ == '__main__':
sys.exit(0)
config = yaml.safe_load(open('/etc/roschat-snmp/conf.d/default.yml'))['snmp']
class Params:
def __init__(self, agent):
self.agent = agent
self.dbus = Dbusclient(config['object'], config['path'], config['iface'])
self.state = None
def register(self):
mib = "ROSCHAT-M0-MIB::roschat-m0."
self.publicNetwork = self.agent.Integer32(
oidstr = mib+"publicNetwork",
# BoolType (1 - no, 2 - yes)
initval = 1
)
self.domainCertificate = self.agent.Integer32(
oidstr = mib+"domainCertificate",
initval = 1
)
self.domainCertificateTime = self.agent.TimeTicks(
oidstr = mib+"domainCertificateTime"
)
self.userLicense = self.agent.Integer32(
oidstr = mib+"userLicense",
initval = 1
)
self.userLicenseLimit = self.agent.Integer32(
oidstr = mib+"userLicenseLimit",
initval = 0
)
self.userLicenseUsed = self.agent.Integer32(
oidstr = mib+"userLicenseUsed",
initval = 0
)
self.userLicenseTime = self.agent.TimeTicks(
oidstr = mib+"userLicenseTime"
)
self.pushService = self.agent.Integer32(
oidstr = mib+"pushService",
initval = 1
)
self.pushServiceApple = self.agent.Integer32(
oidstr = mib+"pushServiceApple",
initval = 1
)
self.pushServiceGoogle = self.agent.Integer32(
oidstr = mib+"pushServiceGoogle",
initval = 1
)
self.finishElement = self.agent.Integer32(
oidstr = mib+"finishElement",
initval = 0
)
#здесь формируются столбцы таблицы
self.serviceTable = self.agent.Table(
oidstr = mib+"serviceTable",
indexes = [
self.agent.Integer32()
],
columns = [
(1, self.agent.Integer32(), True),
(2, self.agent.DisplayString(""), True),
(3, self.agent.Integer32(), True),
(4, self.agent.TimeTicks(), True)
],
counterobj = self.agent.Unsigned32(
#oidstr = "ROSCHAT-M0-MIB::serviceTable.serviceEntry"
oidstr = "ROSCHAT-M0-MIB::serviceEntry"
),
extendable = True
)
# TODO: add providerTable
def updateServiceTable(self, prev_services_state = [{"name": "", "running": False}]):
print('UPDate service table')
serviceState = 0
serviceStateTime = 0
self.serviceTable.clear()
for i, service in enumerate(self.state['services']):
serviceName = service['name'].replace('.service', '')
if service['running']:
serviceState = 3
serviceStateTime = service['uptime']
else:
serviceState = 1
serviceStateTime = service['downtime']
for prevservice in prev_services_state:
if service['name'] == prevservice['name'] and not service['running'] == prevservice['running']:
print('Change service state')
if service['running']:
self.sendTrap(14, "serviceTable", "s", serviceName)
else:
self.sendTrap(13, "serviceTable", "s", serviceName)
#Здесь формируются строки таблицы
row = self.serviceTable.addRow([self.agent.Integer32(i)])
row.setRowCell(1, self.agent.Integer32(i))
row.setRowCell(2, self.agent.DisplayString(serviceName))
row.setRowCell(3, self.agent.Integer32(serviceState))
row.setRowCell(4, self.agent.TimeTicks(serviceStateTime))
#return self.agent.serviceTable.value()
def valueServiceTable(self):
return self.agent.serviceTable.value()
def sendTrap(self, trapcode=0, oid="", valuetype="s", value=""):
config = json.loads(open("/opt/roschat-ms/settings/snmp/snmpConfig.json", "r").read())
os.system("snmptrap -v 1 -c public "+config['snmp_trap_host']+":"+config['snmp_trap_port']+" ROSCHAT-M0-MIB::roschat-m0 '' 6 "+str(trapcode)+" '0' ROSCHAT-M0-MIB::roschat-m0."+oid+" "+valuetype+" "+str(value))
def update(self, data=None):
# TODO: оптимизировать, убрать лишние if, etc
prev_state = self.state
if prev_state == None:
prev_state = {
"network": {"ok": False},
"pushStatus": {"status": False},
"certs": {"errors": []},
"services": [{"name": "", "running": False}]
}
self.state = self.dbus.GetState()
self.updateServiceTable(prev_state['services']).value()
if self.state['network']['ok']:
publicNetwork = 2 # True
if not self.state['network']['ok'] == prev_state['network']['ok']:
self.sendTrap(2, "publicNetwork", "i", publicNetwork)
else:
publicNetwork = 1 # False
if not self.state['network']['ok'] == prev_state['network']['ok']:
self.sendTrap(1, "publicNetwork", "i", publicNetwork)
self.publicNetwork.update(publicNetwork)
if 'certs' in self.state:
domainCertificate = 2 # True
if 'errors' in self.state['certs']:
if len(self.state['certs']['errors']) > 0:
domainCertificate = 1 #False
if not self.state['certs']['errors'] == prev_state['certs']['errors']:
self.sendTrap(3, "domainCertificate", "i", domainCertificate)
else:
if not self.state['certs']['errors'] == prev_state['certs']['errors']:
self.sendTrap(4, "domainCertificate", "i", domainCertificate)
self.domainCertificate.update(domainCertificate)
if 'pushStatus' in self.state and 'status' in self.state['pushStatus']:
pushService = 2 # True
if not self.state['pushStatus']['status'] == prev_state['pushStatus']['status']:
self.sendTrap(8, "pushService", "i", pushService)
else:
pushService = 1 # False
if self.state['pushStatus'] and not self.state['pushStatus']['status'] == prev_state['pushStatus']['status']:
self.sendTrap(7, "pushService", "i", pushService)
self.pushService.update(pushService)
self.domainCertificateTime.update(2) # TODO: разобраться с типом данных
self.userLicense.update(2) # TODO: Что это?
self.userLicenseLimit.update(203) # TODO: И это тоже что?
Код не мой, поэтому не удивляйтесь комментариям в конце скрипта
MIB файл
ROSCHAT-M0-MIB DEFINITIONS ::= BEGIN
IMPORTS
OBJECT-TYPE
FROM RFC1155-SMI
roschat-m
FROM ROSCHAT-M-MIB
TRAP-TYPE
FROM RFC-1215;
roschat-m0 OBJECT IDENTIFIER ::= { roschat-m 1 }
DisplayString ::= OCTET STRING
DateTimeString ::= DisplayString (SIZE (0..20))
StateType ::= INTEGER {
crush(1),
warning(2),
ok(3),
unknown(4)
}
BoolType ::= INTEGER {
no(1),
yes(2)
}
-- MIB-II (same prefix as MIB-I)
-- *********************************************************************
-- **************** SCALAR OBJECTS *************************************
-- *********************************************************************
publicNetwork OBJECT-TYPE
SYNTAX BoolType
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Status access to public network"
::= { roschat-m0 1 }
domainCertificate OBJECT-TYPE
SYNTAX BoolType
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Validating domain certificate"
::= { roschat-m0 2 }
domainCertificateTime OBJECT-TYPE
SYNTAX TimeTicks
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Time limit of domain certificate"
::= { roschat-m0 3 }
userLicense OBJECT-TYPE
SYNTAX BoolType
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Validating license"
::= { roschat-m0 4 }
userLicenseLimit OBJECT-TYPE
SYNTAX INTEGER
ACCESS read-only
STATUS mandatory
DESCRIPTION
"User limit of license"
::= { roschat-m0 5 }
userLicenseUsed OBJECT-TYPE
SYNTAX INTEGER
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Used user of license"
::= { roschat-m0 6 }
userLicenseTime OBJECT-TYPE
SYNTAX TimeTicks
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Time limit of license"
::= { roschat-m0 7 }
pushService OBJECT-TYPE
SYNTAX BoolType
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Status access to push service"
::= { roschat-m0 8 }
pushServiceApple OBJECT-TYPE
SYNTAX BoolType
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Status access to push service Apple (APNS)"
::= { roschat-m0 9 }
pushServiceGoogle OBJECT-TYPE
SYNTAX BoolType
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Status access to push service Google (GCM)"
::= { roschat-m0 10 }
finishElement OBJECT-TYPE
SYNTAX INTEGER
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Terminated element"
::= { roschat-m0 32767 }
-- *********************************************************************
-- **************** TABLE OBJECTS **************************************
-- *********************************************************************
-- *********************************************************************
-- Service Table
-- *********************************************************************
serviceTable OBJECT-TYPE
SYNTAX SEQUENCE OF ServiceEntry
ACCESS not-accessible
STATUS mandatory
DESCRIPTION
"Service table"
::= { roschat-m0 11 }
serviceEntry OBJECT-TYPE
SYNTAX ServiceEntry
ACCESS not-accessible
STATUS mandatory
INDEX { ServiceNum }
::= { serviceTable 1 }
ServiceEntry ::= SEQUENCE {
serviceNum INTEGER,
serviceName DisplayString,
serviceState StateType,
serviceStateTime DateTimeString
}
serviceNum OBJECT-TYPE
SYNTAX INTEGER
ACCESS read-only
STATUS mandatory
DESCRIPTION
"# Service"
::= { serviceEntry 1 }
serviceName OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Service name"
::= { serviceEntry 2 }
serviceState OBJECT-TYPE
SYNTAX StateType
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Service state"
::= { serviceEntry 3 }
serviceStateTime OBJECT-TYPE
SYNTAX DateTimeString
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Service setted time"
::= { serviceEntry 4 }
-- *********************************************************************
-- Provider Table
-- *********************************************************************
providerTable OBJECT-TYPE
SYNTAX SEQUENCE OF ProviderEntry
ACCESS not-accessible
STATUS mandatory
DESCRIPTION
"Provider table"
::= { roschat-m0 12 }
providerEntry OBJECT-TYPE
SYNTAX ProviderEntry
ACCESS not-accessible
STATUS mandatory
INDEX { providerNum }
::= { providerTable 1 }
ProviderEntry ::= SEQUENCE {
providerNum INTEGER,
providerName DisplayString,
providerDescription DisplayString,
providerAdress DisplayString,
providerState StateType,
}
providerNum OBJECT-TYPE
SYNTAX INTEGER
ACCESS read-only
STATUS mandatory
DESCRIPTION
"# Provider"
::= { providerEntry 1 }
providerName OBJECT-TYPE
SYNTAX DisplayString (SIZE (1..255))
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Provider name"
::= { providerEntry 2 }
providerDescription OBJECT-TYPE
SYNTAX DisplayString
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Provider description"
::= { providerEntry 3 }
providerAdress OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..32))
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Provider adress"
::= { providerEntry 4 }
providerState OBJECT-TYPE
SYNTAX StateType
ACCESS read-only
STATUS mandatory
DESCRIPTION
"Provider state"
::= { providerEntry 5 }
-- *********************************************************************
-- **************** TRAPS **********************************************
-- *********************************************************************
agentOffTrap TRAP-TYPE
ENTERPRISE roschat-m0
DESCRIPTION
"Agent Off"
::= 1
agentOnTrap TRAP-TYPE
ENTERPRISE roschat-m0
DESCRIPTION
"Agent On"
::= 2
publicNetworkNo TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { publicNetwork }
DESCRIPTION
"Denied access to public network"
::= 3
publicNetworkYes TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { publicNetwork }
DESCRIPTION
"Access to public network is present"
::= 4
domainCertificateInvalid TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { domainCertificate }
DESCRIPTION
"Invalide domain certificate"
::= 5
domainCertificateValid TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { domainCertificate }
DESCRIPTION
"Valide domain certificate"
::= 6
userLicenseInvalid TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { userLicense }
DESCRIPTION
"Invalide user license"
::= 7
domainCertificateValid TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { userLicense }
DESCRIPTION
"Valide user license"
::= 8
pushServiceNo TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { pushService }
DESCRIPTION
"Denied access to push service"
::= 9
pushServiceYes TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { pushService }
DESCRIPTION
"Denied access to push service"
::= 10
pushServiceAplleNo TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { pushServiceApple }
DESCRIPTION
"Denied access to push service Aplle"
::= 11
pushServiceAplleYes TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { pushServiceApple }
DESCRIPTION
"Denied access to push service Aplle"
::= 12
pushServiceGoogleNo TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { pushServiceGoogle }
DESCRIPTION
"Denied access to push service Google"
::= 13
pushServiceGoogleYes TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { pushServiceGoogle }
DESCRIPTION
"Denied access to push service Google"
::= 14
serviceStateCrush TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { serviceState }
DESCRIPTION
"Service state is Crush"
::= 15
serviceStateOk TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { serviceState }
DESCRIPTION
"Service state is Ok"
::= 16
providerStateCrush TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { providerState }
DESCRIPTION
"Provider state is Crush"
::= 17
providerStateOk TRAP-TYPE
ENTERPRISE roschat-m0
VARIABLES { providerState }
DESCRIPTION
"Provider state is Ok"
::= 18
END
При запросе таблицы всё отображается корректно вроде бы
snmptable -v 1 -c public localhost ROSCHAT-M0-MIB::serviceTable
SNMP table: ROSCHAT-M0-MIB::serviceTable
serviceNum serviceName serviceState serviceStateTime
0 "kamailio" ok 29:20:04:34.51
1 "rtpengine" ok 32:12:46:35.84
2 "roschat-ms" ok 130:5:18:26.04
3 "wlan" ok 9:11:53:49.05
4 "turnserver" ok 283:13:26:41.34
5 "roschat-db" ok 283:13:21:41.17
6 "roschat-snmp" ok 0:3:12:37.02
7 "gw-booster" crush 52:5:49:14.66
Дерево таблицы тоже выстраивается
snmpwalk -v 2c -c public localhost ROSCHAT-M0-MIB::serviceTable
ROSCHAT-M0-MIB::serviceEntry.0 = Gauge32: 8
ROSCHAT-M0-MIB::serviceNum.0 = INTEGER: 0
ROSCHAT-M0-MIB::serviceNum.1 = INTEGER: 1
ROSCHAT-M0-MIB::serviceNum.2 = INTEGER: 2
ROSCHAT-M0-MIB::serviceNum.3 = INTEGER: 3
ROSCHAT-M0-MIB::serviceNum.4 = INTEGER: 4
ROSCHAT-M0-MIB::serviceNum.5 = INTEGER: 5
ROSCHAT-M0-MIB::serviceNum.6 = INTEGER: 6
ROSCHAT-M0-MIB::serviceNum.7 = INTEGER: 7
ROSCHAT-M0-MIB::serviceName.0 = STRING: "kamailio"
ROSCHAT-M0-MIB::serviceName.1 = STRING: "rtpengine"
ROSCHAT-M0-MIB::serviceName.2 = STRING: "roschat-ms"
ROSCHAT-M0-MIB::serviceName.3 = STRING: "wlan"
ROSCHAT-M0-MIB::serviceName.4 = STRING: "turnserver"
ROSCHAT-M0-MIB::serviceName.5 = STRING: "roschat-db"
ROSCHAT-M0-MIB::serviceName.6 = STRING: "roschat-snmp"
ROSCHAT-M0-MIB::serviceName.7 = STRING: "gw-booster"
ROSCHAT-M0-MIB::serviceState.0 = INTEGER: ok(3)
ROSCHAT-M0-MIB::serviceState.1 = INTEGER: ok(3)
ROSCHAT-M0-MIB::serviceState.2 = INTEGER: ok(3)
ROSCHAT-M0-MIB::serviceState.3 = INTEGER: ok(3)
ROSCHAT-M0-MIB::serviceState.4 = INTEGER: ok(3)
ROSCHAT-M0-MIB::serviceState.5 = INTEGER: ok(3)
ROSCHAT-M0-MIB::serviceState.6 = INTEGER: ok(3)
ROSCHAT-M0-MIB::serviceState.7 = INTEGER: crush(1)
ROSCHAT-M0-MIB::serviceStateTime.0 = Timeticks: (257848363) 29 days, 20:14:43.63
ROSCHAT-M0-MIB::serviceStateTime.1 = Timeticks: (281140496) 32 days, 12:56:44.96
ROSCHAT-M0-MIB::serviceStateTime.2 = Timeticks: (1125171516) 130 days, 5:28:35.16
ROSCHAT-M0-MIB::serviceStateTime.3 = Timeticks: (82103817) 9 days, 12:03:58.17
ROSCHAT-M0-MIB::serviceStateTime.4 = Timeticks: (2450021046) 283 days, 13:36:50.46
ROSCHAT-M0-MIB::serviceStateTime.5 = Timeticks: (2449991029) 283 days, 13:31:50.29
ROSCHAT-M0-MIB::serviceStateTime.6 = Timeticks: (59773) 0:09:57.73
ROSCHAT-M0-MIB::serviceStateTime.7 = Timeticks: (451436378) 52 days, 5:59:23.78
Но вот, когда запрашиваю конкретный OID, получаю ошибку
snmpget -v 1 -c public localhost ROSCHAT-M0-MIB::serviceState.2
Error in packet
Reason: (noSuchName) There is no such variable name in this MIB.
Failed object: ROSCHAT-M0-MIB::serviceState.2
Подскажите, как формировать таблицу, чтобы получить доступ к OID таблице?