Asynchronous: asyncio
*********************

The "asyncio" module first appeared in standard library since Python
3.3 (in provisional basis). Its main design feature is that it makes
asynchronous code looking like synchronous one thus eliminating
"callback hell".

With *asyncio* built-in facilities, you could run many SNMP queries in
parallel and/or sequentially, interleave SNMP queries with I/O
operations with other systems. See asyncio resources repository for
other *asyncio*-compatible modules.

In most examples approximate analogues of well known Net-SNMP snmp*
tools command line options are shown. That may help those readers who,
by chance are familiar with Net-SNMP tools, better understanding what
example code doe

Here's a quick example on a simple SNMP GET by high-level API:

   * with SNMPv1, community 'public'

   * over IPv4/UDP

   * to an Agent at demo.snmplabs.com:161

   * for an instance of SNMPv2-MIB::sysDescr.0 MIB object

   * Based on asyncio I/O framework

   import asyncio
   from pysnmp.hlapi.asyncio import *


   @asyncio.coroutine
   def run():
       snmpEngine = SnmpEngine()
       errorIndication, errorStatus, errorIndex, varBinds = yield from getCmd(
           snmpEngine,
           CommunityData('public', mpModel=0),
           UdpTransportTarget(('demo.snmplabs.com', 161)),
           ContextData(),
           ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))
       )

       if errorIndication:
           print(errorIndication)
       elif errorStatus:
           print('%s at %s' % (
               errorStatus.prettyPrint(),
               errorIndex and varBinds[int(errorIndex) - 1][0] or '?'
           )
                 )
       else:
           for varBind in varBinds:
               print(' = '.join([x.prettyPrint() for x in varBind]))

       snmpEngine.transportDispatcher.closeDispatcher()


   asyncio.get_event_loop().run_until_complete(run())

To make use of SNMPv3 and USM, the following code performs a series of
SNMP GETNEXT operations effectively fetching a table of SNMP variables
from SNMP Agent:

* with SNMPv3, user 'usr-none-none', no authentication, no privacy

* over IPv4/UDP

* to an Agent at demo.snmplabs.com:161

* for all OIDs past SNMPv2-MIB::system

* run till end-of-mib condition is reported by Agent

* based on asyncio I/O framework

   import asyncio
   from pysnmp.hlapi.asyncio import *


   @asyncio.coroutine
   def run(varBinds):
       snmpEngine = SnmpEngine()
       while True:
           (errorIndication,
            errorStatus,
            errorIndex,
            varBindTable) = yield from bulkCmd(
               snmpEngine,
               UsmUserData('usr-none-none'),
               UdpTransportTarget(('demo.snmplabs.com', 161)),
               ContextData(),
               0, 50,
               *varBinds)

           if errorIndication:
               print(errorIndication)
               break
           elif errorStatus:
               print('%s at %s' % (
                   errorStatus.prettyPrint(),
                   errorIndex and varBinds[int(errorIndex) - 1][0] or '?'
               )
                     )
           else:
               for varBindRow in varBindTable:
                   for varBind in varBindRow:
                       print(' = '.join([x.prettyPrint() for x in varBind]))

           varBinds = varBindTable[-1]
           if isEndOfMib(varBinds):
               break

       snmpEngine.transportDispatcher.closeDispatcher()


   loop = asyncio.get_event_loop()
   loop.run_until_complete(
       run([ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr'))])
   )

More examples on Command Generator API usage follow.

* Various SNMP versions

  * SNMPv1

  * Bulk walk MIB

* Walking operations

  * Bulk walk MIB

* Advanced Command Generator

  * Concurrent queries

  * Sequential queries

Sending SNMP TRAP's and INFORM's is as easy with PySNMP library. The
following code sends SNMP TRAP:

* SNMPv1

* with community name 'public'

* over IPv4/UDP

* send TRAP notification

* with Generic Trap #1 (warmStart) and Specific Trap 0

* with default Uptime

* with default Agent Address

* with Enterprise OID 1.3.6.1.4.1.20408.4.1.1.2

* include managed object information '1.3.6.1.2.1.1.1.0' = 'my
  system'

   import asyncio
   from pysnmp.hlapi.asyncio import *


   @asyncio.coroutine
   def run():
       snmpEngine = SnmpEngine()
       errorIndication, errorStatus, errorIndex, varBinds = yield from sendNotification(
           snmpEngine,
           CommunityData('public', mpModel=0),
           UdpTransportTarget(('demo.snmplabs.com', 162)),
           ContextData(),
           'trap',
           NotificationType(
               ObjectIdentity('1.3.6.1.6.3.1.1.5.2')
           ).addVarBinds(
               ('1.3.6.1.6.3.1.1.4.3.0', '1.3.6.1.4.1.20408.4.1.1.2'),
               ('1.3.6.1.2.1.1.1.0', OctetString('my system'))
           )
       )

       if errorIndication:
           print(errorIndication)

       snmpEngine.transportDispatcher.closeDispatcher()


   asyncio.get_event_loop().run_until_complete(run())

More examples on Notification Originator API usage follow.

* Common notifications

  * SNMPv1 TRAP with defaults

* Advanced Notification Originator

  * Multiple concurrent notifications

More sophisticated or less popular SNMP operations can still be
performed with PySNMP through its Native API to Standard SNMP
Applications.
