2017-01-12 111 views
0

我正在嘗試編寫一個SNMP代理,可以用來監視我的python進程。爲此,我編寫了一個使用pysnmp實現SNMP代理的類。在多線程環境中使用pysnmp的SNMP代理Timeout

這個代理的核心部分工作(即我可以使用snmpwalk來詢問代理和返回的數據是正確的)。爲了讓我更新代理MIB值,我在它自己的線程中運行了dispatcher()。我的問題是,當使用snmpwalk與代理通信時,我會得到超時(snmpwalk正確地遍歷MIB,然後超時)。

有沒有人有洞察我做錯了什麼?

代理代碼如下:

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.smi import exval 

import threading 

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

class SNMPAgent(object): 

    def _main(self): 

     logging.debug("Creating SNMP Agent....") 

     self._snmpEngine = engine.SnmpEngine() 

     config.addTransport(
      self._snmpEngine, 
      udp.domainName, 
      udp.UdpTransport().openServerMode((self._agentHost, self._agentPort)) 
     ) 

     config.addV1System(self._snmpEngine, 'my-area', self._communityName) 

     config.addVacmUser(self._snmpEngine, 
          2, 
          'my-area', 
          'noAuthNoPriv', 
          (1, 3, 6), 
          (1, 3, 6)) 

     snmpContext = context.SnmpContext(self._snmpEngine) 
     mibBuilder = snmpContext.getMibInstrum().getMibBuilder() 
     mibBuilder.loadModules('HOST-RESOURCES-MIB') 
     self._mibInstrum = snmpContext.getMibInstrum() 

     self._hostRunTable, = mibBuilder.importSymbols('HOST-RESOURCES-MIB', 'hrSWRunEntry') 
     self._instanceId = self._hostRunTable.getInstIdFromIndices(1) 


     # 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 

     self._setVars() 

     cmdrsp.GetCommandResponder(self._snmpEngine, snmpContext) 
     cmdrsp.SetCommandResponder(self._snmpEngine, snmpContext) 
     cmdrsp.NextCommandResponder(self._snmpEngine, snmpContext) 
     cmdrsp.BulkCommandResponder(self._snmpEngine, snmpContext) 

    def runAgent(self): 
     ''' 
     Run the Agent 
     ''' 
     t = threading.Thread(target=self._runAgentFunc, args =()) 
     t.daemon = True 
     t.start() 



    def _runAgentFunc(self): 

     try: 
      self._snmpEngine.transportDispatcher.jobStarted(1) 
      self._snmpEngine.transportDispatcher.runDispatcher() 
     except: 
      self._snmpEngine.transportDispatcher.closeDispatcher() 
      raise 

    def updateAgentStatus(self, runStatus, text1, text2): 

     self._mibDict = {"hrSWRunIndex" : 1, 
         "hrSWRunName" : self._name, 
         "hrSWRunID" : self._enterpriseMIB, 
         "hrSWRunPath" : text1[:128] if text1 is not None else '',  
         "hrSWRunParameters" : text2[:128] if text1 is not None else '', 
         "hrSWRunType" : 4, 
         "hrSWRunStatus" : 1 
         } 
     self._setVars() 
    def _setVars(self): 

     varBinds = self._mibInstrum.writeVars((
      (self._hostRunTable.name + (1,) + self._instanceId, self._mibDict["hrSWRunIndex"]), 
      (self._hostRunTable.name + (2,) + self._instanceId, self._mibDict["hrSWRunName"]), # <=== Must match OpenNMS service-name variable 
      (self._hostRunTable.name + (3,) + self._instanceId, self._mibDict["hrSWRunID" ]), # 
      (self._hostRunTable.name + (4,) + self._instanceId, self._mibDict["hrSWRunPath"]), 
      (self._hostRunTable.name + (5,) + self._instanceId, self._mibDict["hrSWRunParameters"]), 
      (self._hostRunTable.name + (6,) + self._instanceId, self._mibDict["hrSWRunType"]), # Values are ==> unknown(1), operatingSystem(2), deviceDriver(3), application(4)  
      (self._hostRunTable.name + (7,) + self._instanceId, self._mibDict["hrSWRunStatus"]) #<<=== This is the status number OpenNMS looks at Values are ==> running(1), runnable(2), notRunnable(3), invalid(4) 
      )) 

    def __init__(self, name, host, port, community, text1='Service up', text2=''): 
     ''' 
     #======================================================================= 
     # Constructor 
     # name  -- the (process) name the agent should publish (must match 
     #    the openNMS name 
     # host  -- the host name or ip the agent will run on 
     # port  -- the port the snmp agent will listen on 
     # community -- the community name the agent will use (usually 'public') 
     # text1  -- the first status text string published (128 char max) 
     # text2  -- the second status text string published (128 char max) 
     #======================================================================= 
     ''' 
     self._name = name 
     self._agentHost = host 
     self._agentPort = port 
     self._communityName = community 

     self._enterpriseMIB = (1, 3, 6, 1, 4, 1, 50000, 0) # Made up for now 
     self._mibInstrum = None 
     self._snmpEngine = None 
     self._dataChanged = False 

     self._mibDict = {"hrSWRunIndex" : 1, 
         "hrSWRunName" : self._name, 
         "hrSWRunID" : self._enterpriseMIB, 
         "hrSWRunPath" : text1[:128] if text1 is not None else '',  
         "hrSWRunParameters" : text2[:128] if text1 is not None else '', 
         "hrSWRunType" : 4, 
         "hrSWRunStatus" : 1 
         } 

     self._main() 

,我把這個代碼如下(這只是測試,我可以改變狀態):

from SNMPAgent import SNMPAgent 
from time import sleep 

agent = SNMPAgent("test", "127.0.0.1", 12345, "public", "This is my test message 1", "This is my test message 2") 

    agent.runAgent() 
    sleep(10) # Wait for it to start 

    while True: 
     agent.updateAgentStatus(3, "Oh no", "Something is wrong!") 
     sleep(30) 
     agent.updateAgentStatus(2, "Whew", "Everything is fixed") 
     sleep(30) 

走代理MIB我使用:

snmpwalk -v 2c -c public -n my-context 127.0.0.1:12345 1.3.6.1.2.1.25.4.2 

這顯示數據更新,但在步行結束時MIB代理超時:

HOST-RESOURCES-MIB::hrSWRunIndex.1 = INTEGER: 1 
HOST-RESOURCES-MIB::hrSWRunName.1 = STRING: "test" 
HOST-RESOURCES-MIB::hrSWRunID.1 = OID: SNMPv2-SMI::enterprises.50000.0 
HOST-RESOURCES-MIB::hrSWRunPath.1 = STRING: "Whew" 
HOST-RESOURCES-MIB::hrSWRunParameters.1 = STRING: "Everything is fixed" 
HOST-RESOURCES-MIB::hrSWRunType.1 = INTEGER: application(4) 
HOST-RESOURCES-MIB::hrSWRunStatus.1 = INTEGER: running(1) 
Timeout: No Response from 127.0.0.1:12345 

回答

1

啓用pysnmp調試揭示了造成特定OID未設置的整數值序列化錯誤:

[2017-01-13 02:05:18,387-DEBUG]-(debug) generateResponseMsg: Message: 
version='version-2c' 
community=public 
data=PDUs: 
    response=ResponsePDU: 
    request-id=1950819527 
    error-status='noError' 
    error-index=0 
    variable-bindings=VarBindList: 
    VarBind: 
    name=1.3.6.1.2.1.25.5.1.1.1.1 
    =_BindValue: 
     value=ObjectSyntax: 
     simple=SimpleSyntax: 
     integer-value=<no value> 

2017-01-13 02:05:18,387 pysnmp: generateResponseMsg: serialization failure: Uninitialized ASN.1 value ("__eq__" attribute looked up) 
[2017-01-13 02:05:18,387-DEBUG]-(debug) generateResponseMsg: serialization failure: Uninitialized ASN.1 value ("__eq__" attribute looked up) 
2017-01-13 02:05:18,388 pysnmp: StatusInformation: {'errorIndication': <pysnmp.proto.errind.SerializationError object at 0x10162f828>} 
[2017-01-13 02:05:18,388-DEBUG]-(debug) StatusInformation: {'errorIndication': <pysnmp.proto.errind.SerializationError object at 0x10162f828>} 

你或許應該確保你所有非默認的SNMP表列設置的值。

作爲一個方面說明,您似乎在沒有明確同步的情況下從主代理線程和SNMP代理線程管理您的MIB。這可能會導致競爭條件...

+0

這似乎已修復它。我添加了1.3.6.1.2.1.25.5.1.1.1.1我發佈的數據列表。你能解釋一下我應該如何使用鎖更新MIB數據嗎?我看不到任何由pysnmp提供的功能來鎖定MIB,而我更新它。我似乎擁有的oply選項是使用jobFinished()來停止Dispatcher,然後重新啓動它。那是我應該做的嗎? –

+1

可能會將新數據推送到main和pysnmp線程之間共享的[mutex protected] dict。然後在pysnmp線程中有一個定時器回調函數,從共享字典中提取數據並將其推送到MIB中。笑話像[這](https://github.com/etingof/pysnmp/blob/master/examples/v1arch/asyncore/manager/cmdgen/broadcast-agent-discovery.py#L84) –

+0

謝謝。我有一個閱讀 –