# Asynchronous: Twisted

Twisted is quite old and widly used I/O framework. With Twisted, your code will mostly live in isolated functions, but unlike as it is with callback-based design, with Twisted work-in-progress is represented by a `Deferred` class instance effectively carrying state and context of running operation. Your callback functions will be attached to these Deferred objects and invoked as Deferred is done.

Based on Twisted infrastructure, individual asynchronous functions could be chained to run sequentially or in parallel.

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 two instances of SNMPv2-MIB::sysDescr.0 MIB object,

- based on Twisted I/O framework

    from twisted.internet.task import react
    from pysnmp.hlapi.twisted import *

    def success(args, hostname):
        (errorStatus, errorIndex, varBinds) = args

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

    def failure(errorIndication, hostname):
        print('%s failure: %s' % (hostname, errorIndication))

    # noinspection PyUnusedLocal
    def getSysDescr(reactor, hostname):
        d = getCmd(SnmpEngine(),
                   CommunityData('public', mpModel=0),
                   UdpTransportTarget((hostname, 161)),
                   ContextData(),
                   ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)))

        d.addCallback(success, hostname).addErrback(failure, hostname)

        return d

    react(getSysDescr, ['demo.snmplabs.com'])

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 Twisted I/O framework

    from twisted.internet.task import react
    from pysnmp.hlapi.twisted import *

    def success(args, reactor, snmpEngine):
        (errorStatus, errorIndex, varBindTable) = args

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

            if not isEndOfMib(varBindTable[-1]):
                return getbulk(reactor, snmpEngine, *varBindTable[-1])

    def failure(errorIndication):
        print(errorIndication)

    def getbulk(reactor, snmpEngine, varBinds):
        d = bulkCmd(snmpEngine,
                    UsmUserData('usr-none-none'),
                    UdpTransportTarget(('demo.snmplabs.com', 161)),
                    ContextData(),
                    0, 50,
                    varBinds)
        d.addCallback(success, reactor, snmpEngine).addErrback(failure)
        return d

    react(getbulk, [SnmpEngine(), ObjectType(ObjectIdentity('SNMPv2-MIB', 'system'))])

More examples on Command Generator API usage follow.

- Various SNMP versions
  - SNMPv1
  - Bulk walk MIB
- Walking operations
  - Bulk walk MIB
  - Walk whole MIB
- Transport tweaks
  - SNMPv2c
- Advanced Command Generator
  - Concurrent queries
  - Walk multiple Agents at once

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’

    from twisted.internet.task import react
    from pysnmp.hlapi.twisted import *

    def success(args, hostname):
        (errorStatus, errorIndex, varBinds) = args

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

    def failure(errorIndication, hostname):
        print('%s failure: %s' % (hostname, errorIndication))

    # noinspection PyUnusedLocal
    def run(reactor, hostname):
        d = sendNotification(
            SnmpEngine(),
            CommunityData('public', mpModel=0),
            UdpTransportTarget((hostname, 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'))
            )
        )
        d.addCallback(success, hostname).addErrback(failure, hostname)
        return d

    react(run, ['demo.snmplabs.com'])

More examples on Notification Originator API usage follow.

- Common notifications
  - SNMPv1 TRAP with defaults
  - SNMPv2c TRAP via Twisted inline callbacks
- 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.