Python pysnmp中的异步IO代码未按预期响应

Python pysnmp中的异步IO代码未按预期响应,python,python-asyncio,net-snmp,pysnmp,Python,Python Asyncio,Net Snmp,Pysnmp,我是pysnmp库的新手。我尝试了snmplabs文档中提供的示例代码,并进行了如下所示的某些修改 import asyncio from pysnmp.hlapi.asyncio import * @asyncio.coroutine def run(host,oid): errorIndication, errorStatus, errorIndex, varBinds = yield from getCmd( SnmpEngine(), Commun

我是pysnmp库的新手。我尝试了snmplabs文档中提供的示例代码,并进行了如下所示的某些修改

import asyncio
from pysnmp.hlapi.asyncio import *
@asyncio.coroutine
def run(host,oid):
    errorIndication, errorStatus, errorIndex, varBinds = yield from getCmd(
        SnmpEngine(),
        CommunityData('public'),
        UdpTransportTarget((host, 161)),
        ContextData(),
        ObjectType(ObjectIdentity(oid))
    )
    print(errorIndication, errorStatus, errorIndex, varBinds)


asyncio.get_event_loop().run_until_complete(run('demo.snmplabs.com','1.3.6.1.2.1.1.1.0'))
print("asynch_1")
asyncio.get_event_loop().run_until_complete(run('198.155.104.8','1.3.6.1.2.1.1.1.0'))
print("asynch_2")
asyncio.get_event_loop().run_until_complete(run('snmp.live.gambitcommunications.com','1.3.6.1.2.1.1.1.0'))
print("asynch_3")

在上面的示例中,我尝试为不同的代理查询get命令。其中“198.155.104.8”是不存在的虚拟代理ip。 我希望这场比赛的结果是一样的

None 0 0 [ObjectType(ObjectIdentity(<ObjectName value object at 0x7fdaa071e400 tagSet <TagSet object at 0x7fdaa4760828 tags 0:0:6> payload [1.3.6.1.2.1.1.1.0]>), <DisplayString value object at 0x7fda9fcf8c88 tagSet <TagSet object at 0x7fdaa4760400 tags 0:0:4> subtypeSpec <ConstraintsIntersection object at 0x7fdaa085e7b8 consts <ValueSizeConstraint object at 0x7fdaa4710f28 consts 0, 65535>, <ValueSizeConstraint object at 0x7fdaa07a1fd0 consts 0, 255>, <ValueSizeConstraint object at 0x7fdaa085e780 consts 0, 255>> encoding iso-8859-1 payload [Linux zeus 4.8.6...11 CDT 2016 i686]>)]
asynch_1

None 0 0 [ObjectType(ObjectIdentity(<ObjectName value object at 0x7fda9fba2da0 tagSet <TagSet object at 0x7fdaa4760828 tags 0:0:6> payload [1.3.6.1.2.1.1.1.0]>), <DisplayString value object at 0x7fda9fbaa828 tagSet <TagSet object at 0x7fdaa4760400 tags 0:0:4> subtypeSpec <ConstraintsIntersection object at 0x7fda9fac1c88 consts <ValueSizeConstraint object at 0x7fdaa4710f28 consts 0, 65535>, <ValueSizeConstraint object at 0x7fda9f9e5cf8 consts 0, 255>, <ValueSizeConstraint object at 0x7fdaa36e4048 consts 0, 255>> encoding iso-8859-1 payload [Cisco Internetwo...5:14 by kellythw]>)]
asynch_3

No SNMP response received before timeout 0 0 []
asynch_2
None 0[ObjectType(ObjectIdentity(),)]
异步_1
无0[ObjectType(ObjectIdentity(),)]
异步3
在超时0[]之前未收到SNMP响应
异步2
由于没有代理引用“198.155.104.8”,因此代码不应在第二个请求中等待,而应打印第三个请求

但我得到的输出如下所示


None 0 0 [ObjectType(ObjectIdentity(<ObjectName value object at 0x7fdaa071e400 tagSet <TagSet object at 0x7fdaa4760828 tags 0:0:6> payload [1.3.6.1.2.1.1.1.0]>), <DisplayString value object at 0x7fda9fcf8c88 tagSet <TagSet object at 0x7fdaa4760400 tags 0:0:4> subtypeSpec <ConstraintsIntersection object at 0x7fdaa085e7b8 consts <ValueSizeConstraint object at 0x7fdaa4710f28 consts 0, 65535>, <ValueSizeConstraint object at 0x7fdaa07a1fd0 consts 0, 255>, <ValueSizeConstraint object at 0x7fdaa085e780 consts 0, 255>> encoding iso-8859-1 payload [Linux zeus 4.8.6...11 CDT 2016 i686]>)]
asynch_1
No SNMP response received before timeout 0 0 []
asynch_2
None 0 0 [ObjectType(ObjectIdentity(<ObjectName value object at 0x7fda9fba2da0 tagSet <TagSet object at 0x7fdaa4760828 tags 0:0:6> payload [1.3.6.1.2.1.1.1.0]>), <DisplayString value object at 0x7fda9fbaa828 tagSet <TagSet object at 0x7fdaa4760400 tags 0:0:4> subtypeSpec <ConstraintsIntersection object at 0x7fda9fac1c88 consts <ValueSizeConstraint object at 0x7fdaa4710f28 consts 0, 65535>, <ValueSizeConstraint object at 0x7fda9f9e5cf8 consts 0, 255>, <ValueSizeConstraint object at 0x7fdaa36e4048 consts 0, 255>> encoding iso-8859-1 payload [Cisco Internetwo...5:14 by kellythw]>)]
asynch_3


无0[ObjectType(ObjectIdentity(),)]
异步_1
在超时0[]之前未收到SNMP响应
异步2
无0[ObjectType(ObjectIdentity(),)]
异步3
因为我不熟悉snmp。我无法理解使用异步IO代码一次查询多个代理的解决方案

请帮助理解这个问题。以及纠正我的代码,如果一个错误的方式写它

任何帮助都是值得的

提前感谢

将阐明为什么
运行直到完成()
会阻止代码执行

简言之,您必须为循环创建一个任务(例程)列表,然后以异步方式运行它

使用(Python 3.5+)重构后的代码将如下所示:

import asyncio
from pysnmp.hlapi.asyncio import *

async def run(host,oid):
    errorIndication, errorStatus, errorIndex, varBinds = await getCmd(
        SnmpEngine(),
        CommunityData('public'),
        UdpTransportTarget((host, 161)),
        ContextData(),
        ObjectType(ObjectIdentity(oid))
    )
    print(errorIndication, errorStatus, errorIndex, varBinds)

async def main():
    tasks = []
    tasks.append(run('demo.snmplabs.com','1.3.6.1.2.1.1.1.0'))
    tasks.append(run('198.155.104.8','1.3.6.1.2.1.1.1.0'))
    tasks.append(run('snmp.live.gambitcommunications.com','1.3.6.1.2.1.1.1.0'))
    results = await asyncio.gather(*tasks)

if __name__ == '__main__':
    asyncio.run(main())
请看一看

附带说明:最好在脚本/线程中保留一个可重用的SnmpEngine对象。初始化这个对象的代价很高,因为它包含各种缓存,所以重新创建它会大大降低pysnmp的速度。(c)etingof

这一建议大大提高了性能(大约快3倍)。 下面是代码的另一个版本,包含额外的错误处理和返回可读结果

import asyncio
import pysnmp.hlapi.asyncio as snmp

async def get(host,oid):
    result = []
    try:
        snmp_engine = snmp.SnmpEngine()
        response = await snmp.getCmd(snmp_engine,
                                     snmp.CommunityData('public'),
                                     snmp.UdpTransportTarget((host, 161)),
                                     snmp.ContextData(),
                                     snmp.ObjectType(snmp.ObjectIdentity(oid)))

        errorIndication, errorStatus, errorIndex, varBinds = response

        if errorIndication:                
            print(f'{host}: errorIndication: {errorIndication}')
        elif errorStatus:
            print('{}: {} at {}'.format(host, errorStatus.prettyPrint(), errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
        else:
            for varBind in varBinds:
                result.append([x.prettyPrint() for x in varBind])

        snmp_engine.transportDispatcher.closeDispatcher()

    except Exception as err:
        print (f'Error at SNMP get() due to {err}')

    finally:
        print(f'Get {host}, {oid}: {result}')
        return result

async def main():
    tasks = []
    tasks.append(get('demo.snmplabs.com','1.3.6.1.2.1.1.1.0'))
    tasks.append(get('198.155.104.8','1.3.6.1.2.1.1.1.0'))
    tasks.append(get('snmp.live.gambitcommunications.com','1.3.6.1.2.1.1.1.0'))
    results = await asyncio.gather(*tasks)
    print(f'main() results: {results}')

if __name__ == '__main__':
    asyncio.run(main())
有关asyncio的更多了解,请参阅以下教程:

最好的祝愿。

将阐明为什么
运行直到完成()
会阻止代码执行

简言之,您必须为循环创建一个任务(例程)列表,然后以异步方式运行它

使用(Python 3.5+)重构后的代码将如下所示:

import asyncio
from pysnmp.hlapi.asyncio import *

async def run(host,oid):
    errorIndication, errorStatus, errorIndex, varBinds = await getCmd(
        SnmpEngine(),
        CommunityData('public'),
        UdpTransportTarget((host, 161)),
        ContextData(),
        ObjectType(ObjectIdentity(oid))
    )
    print(errorIndication, errorStatus, errorIndex, varBinds)

async def main():
    tasks = []
    tasks.append(run('demo.snmplabs.com','1.3.6.1.2.1.1.1.0'))
    tasks.append(run('198.155.104.8','1.3.6.1.2.1.1.1.0'))
    tasks.append(run('snmp.live.gambitcommunications.com','1.3.6.1.2.1.1.1.0'))
    results = await asyncio.gather(*tasks)

if __name__ == '__main__':
    asyncio.run(main())
请看一看

附带说明:最好在脚本/线程中保留一个可重用的SnmpEngine对象。初始化这个对象的代价很高,因为它包含各种缓存,所以重新创建它会大大降低pysnmp的速度。(c)etingof

这一建议大大提高了性能(大约快3倍)。 下面是代码的另一个版本,包含额外的错误处理和返回可读结果

import asyncio
import pysnmp.hlapi.asyncio as snmp

async def get(host,oid):
    result = []
    try:
        snmp_engine = snmp.SnmpEngine()
        response = await snmp.getCmd(snmp_engine,
                                     snmp.CommunityData('public'),
                                     snmp.UdpTransportTarget((host, 161)),
                                     snmp.ContextData(),
                                     snmp.ObjectType(snmp.ObjectIdentity(oid)))

        errorIndication, errorStatus, errorIndex, varBinds = response

        if errorIndication:                
            print(f'{host}: errorIndication: {errorIndication}')
        elif errorStatus:
            print('{}: {} at {}'.format(host, errorStatus.prettyPrint(), errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
        else:
            for varBind in varBinds:
                result.append([x.prettyPrint() for x in varBind])

        snmp_engine.transportDispatcher.closeDispatcher()

    except Exception as err:
        print (f'Error at SNMP get() due to {err}')

    finally:
        print(f'Get {host}, {oid}: {result}')
        return result

async def main():
    tasks = []
    tasks.append(get('demo.snmplabs.com','1.3.6.1.2.1.1.1.0'))
    tasks.append(get('198.155.104.8','1.3.6.1.2.1.1.1.0'))
    tasks.append(get('snmp.live.gambitcommunications.com','1.3.6.1.2.1.1.1.0'))
    results = await asyncio.gather(*tasks)
    print(f'main() results: {results}')

if __name__ == '__main__':
    asyncio.run(main())
有关asyncio的更多了解,请参阅以下教程:


最美好的祝愿。

@Ilya Etingof你能帮我弄清楚我要去哪里吗wrong@Ilya你能帮我弄清楚我哪里出了问题吗