код asyncio в pysnmp не отвечает, как ожидалось

Я новичок в библиотеке 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

Поскольку нет агента, ссылающегося на «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

Так как я новичок в snmp. Я не могу решить, как использовать асинхронный код для одновременного запроса нескольких агентов.

Помогите пожалуйста разобраться в проблеме. А также исправить мой код, если я написал его неправильно.

Любая помощь будет ощутима.

заранее спасибо


person s_kifayath    schedule 01.08.2019    source источник
comment
@Илья Этингоф, не могли бы вы помочь мне понять, где я ошибаюсь?   -  person s_kifayath    schedule 01.08.2019


Ответы (1)


Этот ответ пояснит, почему run_until_complete() блокирует ваш выполнение кода.

Короче говоря, вам нужно создать список задач (курутин) для цикла, а затем запустить его асинхронно, собранным целиком.

С современным синтаксисом async/await (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())

Взгляните на этот пример Ильи Этингоф @ github

В качестве примечания: лучше сохранить один повторно используемый объект SnmpEngine в вашем скрипте/потоке. Этот объект дорого инициализируется, он содержит различные кеши, поэтому его повторное создание сильно замедляет работу pysnmp. (с) этингоф

этот совет значительно повышает производительность (примерно в 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 см. руководства ниже:

С наилучшими пожеланиями.

person Ilya Gruntal    schedule 17.08.2019