使用pySNMP编写SNMP代理(适用于OpenNMS)

2024-03-15

我正在尝试编写一个 python SNMP 代理,我可以将其嵌入到我的 python 应用程序中,以便可以通过 OpenNMS 远程监控该应用程序。 OpenNMS 期望 Agent 实现HOST-RESOURCES-MIB查询两个字段hrSWRunNameand hrSWRunStatus.

我以 pysnmp 示例作为代码的基础,并根据需要对其进行了编辑。结果代码如下所示:

import logging

from pysnmp import debug
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.proto.api import v2c
from pysnmp.smi import builder, instrum, exval


# debug.setLogger(debug.Debug('all'))

formatting = '[%(asctime)s-%(levelname)s]-(%(module)s) %(message)s'
logging.basicConfig(level=logging.DEBUG, format=formatting, )

logging.info("Starting....")

# Create SNMP engine
snmpEngine = engine.SnmpEngine()

# Transport setup

# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpTransport().openServerMode(('localhost', 12345))
)

# SNMPv2c setup

# SecurityName <-> CommunityName mapping.
config.addV1System(snmpEngine, 'my-area', 'public')

# Allow read MIB access for this user / securityModels at VACM
config.addVacmUser(snmpEngine,
                   2,
                   'my-area',
                   'noAuthNoPriv',
                   (1, 3, 6, 1, 2, 1),
                   (1, 3, 6, 1, 2, 1))


# Create an SNMP context
snmpContext = context.SnmpContext(snmpEngine)

logging.debug('Loading HOST-RESOURCES-MIB module...'),
mibBuilder = builder.MibBuilder().loadModules('HOST-RESOURCES-MIB')
logging.debug('done')

logging.debug('Building MIB tree...'),
mibInstrum = instrum.MibInstrumController(mibBuilder)
logging.debug('done')

logging.debug('Building table entry index from human-friendly representation...')
# see http://www.oidview.com/mibs/0/HOST-RESOURCES-MIB.html
hostRunTable, = mibBuilder.importSymbols('HOST-RESOURCES-MIB', 'hrSWRunEntry')
instanceId = hostRunTable.getInstIdFromIndices(1)
logging.debug('done')


# The following shows the OID name mapping
#
# hrSWRunTable          1.3.6.1.2.1.25.4.2          <TABLE>
# hrSWRunEntry          1.3.6.1.2.1.25.4.2.1        <SEQUENCE>
# hrSWRunIndex          1.3.6.1.2.1.25.4.2.1.1      <Integer32>
# hrSWRunName           1.3.6.1.2.1.25.4.2.1.2      <InternationalDisplayString> 64 Char
# hrSWRunID             1.3.6.1.2.1.25.4.2.1.3      <ProductID>
# hrSWRunPath           1.3.6.1.2.1.25.4.2.1.4      <InternationalDisplayString> 128 octets
# hrSWRunParameters     1.3.6.1.2.1.25.4.2.1.5      <InternationalDisplayString> 128 octets
# hrSWRunType           1.3.6.1.2.1.25.4.2.1.6      <INTEGER>
# hrSWRunStatus         1.3.6.1.2.1.25.4.2.1.7      <INTEGER>  <<===== This is the key variable used by Opennms


# http://docs.opennms.org/opennms/releases/18.0.1/guide-admin/guide-admin.html#_hostresourceswrunmonitor)


logging.debug('Create/update HOST-RESOURCES-MIB::hrSWRunTable table row:')
varBinds = mibInstrum.writeVars((
                                 (hostRunTable.name + (1,) + instanceId, 1),
                                 (hostRunTable.name + (2,) + instanceId, 'AppName'), # <=== Must match OpenNMS service-name variable
                                 (hostRunTable.name + (3,) + instanceId,  {0,0}), #  
                                 (hostRunTable.name + (4,) + instanceId, 'All is well'),
                                 (hostRunTable.name + (5,) + instanceId, 'If this was not the case it would say so here'),
                                 (hostRunTable.name + (6,) + instanceId, 4),# Values are ==> unknown(1), operatingSystem(2), deviceDriver(3), application(4)     
                                 (hostRunTable.name + (7,) + instanceId, 1) #<<=== This is the status number OpenNMS looks at Values are ==> running(1), runnable(2), notRunnable(3), invalid(4)
                                 ))

for oid, val in varBinds:
    print('%s = %s' % ('.'.join([str(x) for x in oid]), val.prettyPrint()))
logging.debug('done')

logging.debug('Read whole MIB (table walk)')
oid, val = (), None
while True:
    oid, val = mibInstrum.readNextVars(((oid, val),))[0]
    if exval.endOfMib.isSameTypeWith(val):
        break
    print('%s = %s' % ('.'.join([str(x) for x in oid]), val.prettyPrint()))
logging.debug('done')

# logging.debug('Unloading MIB modules...'),
# mibBuilder.unloadModules()
# logging.debug('done')


# --- end of  table population ---

# Register SNMP Applications at the SNMP engine for particular SNMP context
cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
cmdrsp.SetCommandResponder(snmpEngine, snmpContext)
cmdrsp.NextCommandResponder(snmpEngine, snmpContext)
cmdrsp.BulkCommandResponder(snmpEngine, snmpContext)

# Register an imaginary never-ending job to keep I/O dispatcher running forever
snmpEngine.transportDispatcher.jobStarted(1)

# Run I/O dispatcher which would receive queries and send responses
try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.transportDispatcher.closeDispatcher()
    raise

代码运行时不会产生错误。 varBinds 和 MIB Table walk 显示了我认为我应该期待的内容:

[2016-12-29 16:42:49,323-INFO]-(SNMPAgent) Starting....
[2016-12-29 16:42:49,470-DEBUG]-(SNMPAgent) Loading HOST-RESOURCES-MIB module...
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) done
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) Building MIB tree...
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) done
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) Building table entry index from human-friendly representation...
[2016-12-29 16:42:49,631-DEBUG]-(SNMPAgent) done
[2016-12-29 16:42:49,632-DEBUG]-(SNMPAgent) Create/update HOST-RESOURCES-MIB::hrSWRunTable table row:
1.3.6.1.2.1.25.4.2.1.1.1 = 1
[2016-12-29 16:42:49,651-DEBUG]-(SNMPAgent) done
1.3.6.1.2.1.25.4.2.1.2.1 = TradeLoader
1.3.6.1.2.1.25.4.2.1.3.1 = 0
1.3.6.1.2.1.25.4.2.1.4.1 = All is well
1.3.6.1.2.1.25.4.2.1.5.1 = If this was not the case it would say so here
1.3.6.1.2.1.25.4.2.1.6.1 = 'application'
1.3.6.1.2.1.25.4.2.1.7.1 = 'running'
[2016-12-29 16:42:49,651-DEBUG]-(SNMPAgent) Read whole MIB (table walk)
1.3.6.1.2.1.25.4.2.1.1.1 = 1
1.3.6.1.2.1.25.4.2.1.2.1 = TradeLoader
1.3.6.1.2.1.25.4.2.1.3.1 = 0
1.3.6.1.2.1.25.4.2.1.4.1 = All is well
1.3.6.1.2.1.25.4.2.1.5.1 = If this was not the case it would say so here
1.3.6.1.2.1.25.4.2.1.6.1 = 'application'
1.3.6.1.2.1.25.4.2.1.7.1 = 'running'
1.3.6.1.2.1.25.5.1.1.1.1 = <no value>
1.3.6.1.2.1.25.5.1.1.2.1 = <no value>
[2016-12-29 16:42:53,490-DEBUG]-(SNMPAgent) done

最后,调度程序启动。

问题是当我尝试查询代理时什么也没有发生。我没有得到任何回应。我查看了我的代码,一个明显的事实是我没有将 snmpEngine 显式链接到我创建的 MIB。我应该这样做吗?

任何见解都会受到极大的欢迎,因为我正在努力了解目前该去哪里。


我想我只会发布我的问题的答案,因为我花了很长时间才弄清楚如何做我需要的事情。希望其他人会发现这很有用。以下代码允许我填充 pysnmp 已知的任何 MIB,然后使 MIB 作为 V2 SNMP 代理可供网络使用。

import logging

from pysnmp import debug
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.proto.api import v2c
from pysnmp.smi import builder, instrum, exval

# Uncomment this to turn pysnmp debugging on
#debug.setLogger(debug.Debug('all'))

formatting = '[%(asctime)s-%(levelname)s]-(%(module)s) %(message)s'
logging.basicConfig(level=logging.DEBUG, format=formatting, )

logging.info("Starting....")

# Create SNMP engine
snmpEngine = engine.SnmpEngine()

# Transport setup

# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpTransport().openServerMode(('0.0.0.0', 12345))
)

# SNMPv2c setup

# SecurityName <-> CommunityName mapping.
config.addV1System(snmpEngine, 'my-area', 'public')

# Allow read MIB access for this user / securityModels at VACM
# Limit access to just the custom MIB. Widen if need be
config.addVacmUser(snmpEngine,
                   2,
                   'my-area',
                   'noAuthNoPriv',
                   (1, 3, 6, 1, 2, 1, 25, 4),
                   (1, 3, 6, 1, 2, 1, 25, 4))


# Create an SNMP context and ensure the custom MIB is loaded
# Your system must have this MIB installed otherwise pysnmp 
# can't load it! 
snmpContext = context.SnmpContext(snmpEngine)
logging.debug('Loading HOST-RESOURCES-MIB module...'),
mibBuilder = snmpContext.getMibInstrum().getMibBuilder()
mibBuilder.loadModules('HOST-RESOURCES-MIB')
mibInstrum = snmpContext.getMibInstrum()
logging.debug('done')

logging.debug('Building table entry index from human-friendly representation...')
# see http://www.oidview.com/mibs/0/HOST-RESOURCES-MIB.html
hostRunTable, = mibBuilder.importSymbols('HOST-RESOURCES-MIB', 'hrSWRunEntry')
instanceId = hostRunTable.getInstIdFromIndices(1)
logging.debug('done')


# The following shows the OID name mapping
#
# hrSWRunTable          1.3.6.1.2.1.25.4.2          <TABLE>
# hrSWRunEntry          1.3.6.1.2.1.25.4.2.1        <SEQUENCE>
# hrSWRunIndex          1.3.6.1.2.1.25.4.2.1.1      <Integer32>
# hrSWRunName           1.3.6.1.2.1.25.4.2.1.2      <InternationalDisplayString> 64 Char
# hrSWRunID             1.3.6.1.2.1.25.4.2.1.3      <ProductID>
# hrSWRunPath           1.3.6.1.2.1.25.4.2.1.4      <InternationalDisplayString> 128 octets
# hrSWRunParameters     1.3.6.1.2.1.25.4.2.1.5      <InternationalDisplayString> 128 octets
# hrSWRunType           1.3.6.1.2.1.25.4.2.1.6      <INTEGER>
# hrSWRunStatus         1.3.6.1.2.1.25.4.2.1.7      <INTEGER>  <<===== This is the key variable used by Opennms

# We are going to use OpenNMS as the SNMP manager. OpenNMS will poll this agent to check on its status. The manual 
# states:
#
# "This monitor tests the running state of one or more processes. It does this using SNMP and by inspecting the 
# hrSwRunTable of the HOST-RESOURCES-MIB. The test is done by matching a given process as hrSWRunName against
# the numeric value of the hrSWRunStatus". hrSWRunName is matched against the process name defined in the OpenNMS
# config file under the heading "service-name". hrSWRunStatus is set to whatever your desired status is. OpenNMS 
# will compare this value against the config file variable run-level. If hrSWRunStatus > run-level the process
# will be marked as having problems. for the complete page see: 
# http://docs.opennms.org/opennms/releases/18.0.1/guide-admin/guide-admin.html#_hostresourceswrunmonitor)


# I have made up an enterprise MIB for us. The number is moot as it's not going to go anywhere but the code needs 
# something valid.
# The enterprise MIB I have chosen is
enterpriseMib = (1, 3, 6, 1, 4, 1, 50000, 0)

logging.debug('Create/update HOST-RESOURCES-MIB::hrSWRunTable table row:')
varBinds = mibInstrum.writeVars((
                                 (hostRunTable.name + (1,) + instanceId, 1),
                                 (hostRunTable.name + (2,) + instanceId, 'TradeLoader'), # <=== Must match OpenNMS service-name variable
                                 (hostRunTable.name + (3,) + instanceId,  enterpriseMib), #  
                                 (hostRunTable.name + (4,) + instanceId, 'All is well'),
                                 (hostRunTable.name + (5,) + instanceId, 'If this was not the case it would say so here'),
                                 (hostRunTable.name + (6,) + instanceId, 4),# Values are ==> unknown(1), operatingSystem(2), deviceDriver(3), application(4)     
                                 (hostRunTable.name + (7,) + instanceId, 1) #<<=== This is the status number OpenNMS looks at Values are ==> running(1), runnable(2), notRunnable(3), invalid(4)
                                 ))

# --- end of  table population ---
logging.debug('Confirm that the data has been set by reading whole MIB (table walk)')
oid, val = (), None
while True:
    oid, val = mibInstrum.readNextVars(((oid, val),))[0]
    if exval.endOfMib.isSameTypeWith(val):
        break
    print('%s = %s' % ('.'.join([str(x) for x in oid]), val.prettyPrint()))
logging.debug('done')

# Register SNMP Applications at the SNMP engine for particular SNMP context
cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
cmdrsp.SetCommandResponder(snmpEngine, snmpContext)
cmdrsp.NextCommandResponder(snmpEngine, snmpContext)
cmdrsp.BulkCommandResponder(snmpEngine, snmpContext)

# Register an imaginary never-ending job to keep I/O dispatcher running forever
snmpEngine.transportDispatcher.jobStarted(1)

# Run I/O dispatcher which would receive queries and send responses
try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.transportDispatcher.closeDispatcher()
    raise

记录器消息显示

[2017-01-09 16:30:15,401-INFO]-(SNMPAgent) Starting.... [2017-01-09 16:30:15,490-DEBUG]-(SNMPAgent) Loading HOST-RESOURCES-MIB module... [2017-01-09 16:30:15,513-DEBUG]-(SNMPAgent) done [2017-01-09 16:30:15,513-DEBUG]-(SNMPAgent) Building table entry index from human-friendly representation... [2017-01-09 16:30:15,515-DEBUG]-(SNMPAgent) done [2017-01-09 16:30:15,515-DEBUG]-(SNMPAgent) Create/update HOST-RESOURCES-MIB::hrSWRunTable table row: [2017-01-09 16:30:15,536-DEBUG]-(SNMPAgent) Confirm that the data has been set by reading whole MIB (table walk)
1.3.6.1.2.1.25.4.2.1.1.1 = 1
1.3.6.1.2.1.25.4.2.1.2.1 = TradeLoader
1.3.6.1.2.1.25.4.2.1.3.1 = 1.3.6.1.4.1.50000.0
1.3.6.1.2.1.25.4.2.1.4.1 = All is well
1.3.6.1.2.1.25.4.2.1.5.1 = If this was not the case it would say so here
1.3.6.1.2.1.25.4.2.1.6.1 = 'application'
1.3.6.1.2.1.25.4.2.1.7.1 = 'running'
1.3.6.1.2.1.25.5.1.1.1.1 = <no value>
1.3.6.1.2.1.25.5.1.1.2.1 = <no value>
1.3.6.1.6.3.10.2.1.1.0 = 0x80004fb805049c06c8
1.3.6.1.6.3.10.2.1.2.0 = 2
1.3.6.1.6.3.10.2.1.3.0 = 0
1.3.6.1.6.3.10.2.1.4.0 = 65507
1.3.6.1.6.3.16.1.1.1.1.0 = 
1.3.6.1.6.3.16.1.2.1.1.2.7.109.121.45.97.114.101.97 = 2
1.3.6.1.6.3.16.1.2.1.2.2.7.109.121.45.97.114.101.97 = my-area
1.3.6.1.6.3.16.1.2.1.3.2.7.109.121.45.97.114.101.97 = v-1203634843-2
1.3.6.1.6.3.16.1.2.1.4.2.7.109.121.45.97.114.101.97 = 'nonVolatile'
1.3.6.1.6.3.16.1.2.1.5.2.7.109.121.45.97.114.101.97 = 'active'
1.3.6.1.6.3.16.1.4.1.1.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = 
1.3.6.1.6.3.16.1.4.1.2.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = 2
1.3.6.1.6.3.16.1.4.1.3.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = 'noAuthNoPriv'
1.3.6.1.6.3.16.1.4.1.4.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = 'exact'
1.3.6.1.6.3.16.1.4.1.5.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = rv-1203634843-2
1.3.6.1.6.3.16.1.4.1.6.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = wv-1203634843-2
1.3.6.1.6.3.16.1.4.1.7.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = nv-1203634843-2
1.3.6.1.6.3.16.1.4.1.8.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = 'nonVolatile'
1.3.6.1.6.3.16.1.4.1.9.14.118.45.49.50.48.51.54.51.52.56.52.51.45.50.0.2.1 = 'active'
1.3.6.1.6.3.16.1.5.2.1.1.15.114.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4
= rv-1203634843-2
1.3.6.1.6.3.16.1.5.2.1.1.15.119.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4
= wv-1203634843-2
1.3.6.1.6.3.16.1.5.2.1.2.15.114.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4
= 1.3.6.1.2.1.25.4
1.3.6.1.6.3.16.1.5.2.1.2.15.119.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4
= 1.3.6.1.2.1.25.4
1.3.6.1.6.3.16.1.5.2.1.3.15.114.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4
= 
1.3.6.1.6.3.16.1.5.2.1.3.15.119.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4
= 
1.3.6.1.6.3.16.1.5.2.1.4.15.114.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4
= 'included'
1.3.6.1.6.3.16.1.5.2.1.4.15.119.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4
= 'included'
1.3.6.1.6.3.16.1.5.2.1.5.15.114.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4
= 'nonVolatile'
1.3.6.1.6.3.16.1.5.2.1.5.15.119.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4
= 'nonVolatile'
1.3.6.1.6.3.16.1.5.2.1.6.15.114.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4
= 'active'
1.3.6.1.6.3.16.1.5.2.1.6.15.119.118.45.49.50.48.51.54.51.52.56.52.51.45.50.11.1.3.6.1.2.1.25.4
= 'active'
1.3.6.1.6.3.18.1.1.1.1.109.121.45.97.114.101.97 = my-area
1.3.6.1.6.3.18.1.1.1.2.109.121.45.97.114.101.97 = public
1.3.6.1.6.3.18.1.1.1.3.109.121.45.97.114.101.97 = my-area
1.3.6.1.6.3.18.1.1.1.4.109.121.45.97.114.101.97 = 0x80004fb805049c06c8
1.3.6.1.6.3.18.1.1.1.5.109.121.45.97.114.101.97 = 
1.3.6.1.6.3.18.1.1.1.6.109.121.45.97.114.101.97 = 
1.3.6.1.6.3.18.1.1.1.7.109.121.45.97.114.101.97 = 'nonVolatile'
1.3.6.1.6.3.18.1.1.1.8.109.121.45.97.114.101.97 = 'active' [2017-01-09 16:30:15,683-DEBUG]-(SNMPAgent) done

并且可以查询代理显示得到的结果如下:

snmpwalk -v 2c -c public -n my-context 0.0.0.0:12345 1.3.6

HOST-RESOURCES-MIB::hrSWRunIndex.1 = INTEGER: 1
HOST-RESOURCES-MIB::hrSWRunName.1 = STRING: "TradeLoader"
HOST-RESOURCES-MIB::hrSWRunID.1 = OID: SNMPv2-SMI::enterprises.50000.0
HOST-RESOURCES-MIB::hrSWRunPath.1 = STRING: "All is well"
HOST-RESOURCES-MIB::hrSWRunParameters.1 = STRING: "If this was not the case it would say so here"
HOST-RESOURCES-MIB::hrSWRunType.1 = INTEGER: application(4)
HOST-RESOURCES-MIB::hrSWRunStatus.1 = INTEGER: running(1)
HOST-RESOURCES-MIB::hrSWRunStatus.1 = No more variables left in this MIB View (It is past the end of the MIB tree)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用pySNMP编写SNMP代理(适用于OpenNMS) 的相关文章

随机推荐