带异步的 PySNMP 仪器控制器

PySNMP Instrumentation Controller with asyncio

我需要编写简单的 SNMP 响应程序,其中部分工作是通过 asyncio.subprocess 模块调用一些外部脚本。现在我正在试验 pysnmp,但是从示例中稍微修改后的代码不起作用:

import asyncio                                                                                                                                     

from pysnmp.entity import engine, config                                                                                                           
from pysnmp.entity.rfc3413 import cmdrsp, context                                                                                                  
from pysnmp.carrier.asyncio.dgram import udp                                                                                                       
from pysnmp.smi import instrum                                                                                                                     
from pysnmp.proto.api import v2c                                                                                                                   

snmpEngine = engine.SnmpEngine()                                                                                                                   

config.addTransport(                                                                                                                               
    snmpEngine,                                                                                                                                    
    udp.domainName,                                                                                                                                
    udp.UdpTransport().openServerMode(('0.0.0.0', 161))                                                                                            
)                                                                                                                                                  

# config.addV1System(snmpEngine, 'my-area', 'public')                                                                                              
config.addV1System(snmpEngine, 'public', 'public', contextName='my-context')                                                                       
config.addV1System(snmpEngine, 'private', 'private', contextName='my-context')                                                                     

# Allow full MIB access for each user at VACM                                                                                                      
config.addVacmUser(snmpEngine, 2, "public", 'noAuthNoPriv', (1, 3, 6, 1, 2, 1), (1, 3, 6, 1, 2, 1))                                                
config.addVacmUser(snmpEngine, 2, "private", 'noAuthNoPriv', (1, 3, 6, 1, 2, 1), (1, 3, 6, 1, 2, 1))                                               

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

# Very basic Management Instrumentation Controller without                                                                                         
# any Managed Objects attached. It supports only GET's and                                                                                         
# always echos request var-binds in response.                                                                                                      
class EchoMibInstrumController(instrum.AbstractMibInstrumController):                                                                              
    @asyncio.coroutine                                                                                                                             
    def readVars(self, varBinds, acInfo=(None, None)):                                                                                             
        yield from asyncio.sleep(1)                                                                                                                
        return [(ov[0], v2c.OctetString('You queried OID %s' % ov[0])) for ov in varBinds]                                                         

# Create a custom Management Instrumentation Controller and register at                                                                            
# SNMP Context under ContextName 'my-context'                                                                                                      
snmpContext.registerContextName(                                                                                                                   
    v2c.OctetString('my-context'),  # Context Name                                                                                                 
    EchoMibInstrumController()  # Management Instrumentation                                                                                       
)                                                                                                                                                  

# Register GET&SET Applications at the SNMP engine for a custom SNMP context                                                                       
cmdrsp.GetCommandResponder(snmpEngine, snmpContext)                                                                                                
cmdrsp.SetCommandResponder(snmpEngine, snmpContext)                                                                                                

loop = asyncio.get_event_loop()                                                                                                                    
loop.run_forever()

当尝试使用以下命令进行查询时 snmpget -v 2c -c public 127.0.0.1 .1.3.6.1.2.1.1.1.0 脚本失败并出现以下回溯。

Exception in callback AbstractTransportDispatcher._cbFun(<pysnmp.carri...x7f4c7540d518>, ('127.0.0.1', 44209), b'0)\x02\x01\...1\x00\x05\x00') 
handle: <Handle AbstractTransportDispatcher._cbFun(<pysnmp.carri...x7f4c7540d518>, ('127.0.0.1', 44209), b'0)\x02\x01\...1\x00\x05\x00')>      
Traceback (most recent call last):                                                                                                             
  File "/usr/lib/python3.4/asyncio/events.py", line 120, in _run                                                                               
    self._callback(*self._args)                                                                                                                
  File "/opt/profiset2/lib/python3.4/site-packages/pysnmp/carrier/base.py", line 70, in _cbFun                                                 
    self, transportDomain, transportAddress, incomingMessage                                                                                   
  File "/opt/profiset2/lib/python3.4/site-packages/pysnmp/entity/engine.py", line 154, in __receiveMessageCbFun                                
    self, transportDomain, transportAddress, wholeMsg                                                                                          
  File "/opt/profiset2/lib/python3.4/site-packages/pysnmp/proto/rfc3412.py", line 421, in receiveMessage                                       
    PDU, maxSizeResponseScopedPDU, stateReference)                                                                                             
  File "/opt/profiset2/lib/python3.4/site-packages/pysnmp/entity/rfc3413/cmdrsp.py", line 140, in processPdu                                   
    (self.__verifyAccess, snmpEngine))                                                                                                         
  File "/opt/profiset2/lib/python3.4/site-packages/pysnmp/entity/rfc3413/cmdrsp.py", line 247, in handleMgmtOperation                          
    mgmtFun(v2c.apiPDU.getVarBinds(PDU), (acFun, acCtx)))                                                                                      
  File "/opt/profiset2/lib/python3.4/site-packages/pysnmp/entity/rfc3413/cmdrsp.py", line 46, in sendVarBinds                                  
    v2c.apiPDU.setVarBinds(PDU, varBinds)                                                                                                      
  File "/opt/profiset2/lib/python3.4/site-packages/pysnmp/proto/api/v1.py", line 135, in setVarBinds                                           
    varBindList.getComponentByPosition(idx), varBind                                                                                           
  File "/opt/profiset2/lib/python3.4/site-packages/pysnmp/proto/api/v1.py", line 38, in setOIDVal                                              
    oid, val = oidVal[0], oidVal[1]                                                                                                            
TypeError: 'Future' object does not support indexing

可能是我在做一些愚蠢的事情,我承认我对 SNMP 了解不多,那么请指点我正确的方向。

发生这种情况是因为 CommandResponder <-> MIB 控制器交互是 currently synchronous。换句话说,你不能 yield from MIB 控制器,你只能 return 来自它。它不能是 coroutine。删除这两件事后,您的代码可能会正常工作。

mgmtFun(在我上面链接的代码中)可以 return Future 触发其余代码的方式重构 pysnmp 代码绝对是可行的(例如sendVarBinds) 一旦 Future 完成。这将使您的 MIB 控制器异步。

这就是你需要的?然后考虑针对 pysnmp 的 PR 或 FR。