1"""
2Multiple SNMP engines
3+++++++++++++++++++++
4
5Send multiple SNMP GET requests to multiple peers using multiple
6independend SNMP engines. Deal with peers asynchronously. SNMP options
7are:
8
9* with SNMPv1, community 'public' and
10  with SNMPv2c, community 'public' and
11  with SNMPv3, user 'usr-md5-des', MD5 auth and DES privacy
12* over IPv4/UDP and
13  over IPv6/UDP
14* to an Agent at demo.snmplabs.com:161 and
15  to an Agent at [::1]:161
16* for instances of SNMPv2-MIB::sysDescr.0 and
17  SNMPv2-MIB::sysLocation.0 MIB objects
18
19Within this script we have a single asynchronous TransportDispatcher
20and a single UDP-based transport serving two independent SNMP engines.
21We use a single instance of AsyncCommandGenerator with each of
22SNMP Engines to comunicate GET command request to remote systems.
23
24When we receive a [response] message from remote system we use
25a custom message router to choose what of the two SNMP engines
26data packet should be handed over. The selection criteria we
27employ here is based on peer's UDP port number. Other selection
28criterias are also possible.
29
30"""#
31from pysnmp.hlapi.asyncore import *
32from pysnmp.carrier.asyncore.dispatch import AsyncoreDispatcher
33
34# List of targets in the following format:
35# ( ( authData, transportTarget, varNames ), ... )
36targets = (
37    # 1-st target (SNMPv1 over IPv4/UDP)
38    (CommunityData('public', mpModel=0),
39     UdpTransportTarget(('demo.snmplabs.com', 161)),
40     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
41      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0)))),
42    # 2-nd target (SNMPv2c over IPv4/UDP)
43    (CommunityData('public'),
44     UdpTransportTarget(('demo.snmplabs.com', 1161)),
45     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
46      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0)))),
47    # 3-nd target (SNMPv3 over IPv4/UDP)
48    (UsmUserData('usr-md5-des', 'authkey1', 'privkey1'),
49     UdpTransportTarget(('demo.snmplabs.com', 2161)),
50     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
51      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0))))
52    # N-th target
53    # ...
54)
55
56
57# Wait for responses or errors
58# noinspection PyUnusedLocal,PyUnusedLocal
59def cbFun(snmpEngine, sendRequestHandle, errorIndication,
60          errorStatus, errorIndex, varBinds, cbCtx):
61    (snmpEngine, authData, transportTarget) = cbCtx
62    print('snmpEngine %s: %s via %s' % (snmpEngine.snmpEngineID.prettyPrint(), authData, transportTarget))
63    if errorIndication:
64        print(errorIndication)
65        return True
66    elif errorStatus:
67        print('%s at %s' % (errorStatus.prettyPrint(),
68                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
69        return True
70    else:
71        for varBind in varBinds:
72            print(' = '.join([x.prettyPrint() for x in varBind]))
73
74
75# Instantiate the single transport dispatcher object
76transportDispatcher = AsyncoreDispatcher()
77
78# Setup a custom data routing function to select snmpEngine by transportDomain
79transportDispatcher.registerRoutingCbFun(
80    lambda td, ta, d: ta[1] % 3 and 'A' or 'B'
81)
82
83snmpEngineA = SnmpEngine()
84snmpEngineA.registerTransportDispatcher(transportDispatcher, 'A')
85
86snmpEngineB = SnmpEngine()
87snmpEngineB.registerTransportDispatcher(transportDispatcher, 'B')
88
89for authData, transportTarget, varBinds in targets:
90    snmpEngine = transportTarget.getTransportInfo()[1][1] % 3 and \
91                 snmpEngineA or snmpEngineB
92    getCmd(snmpEngine, authData, transportTarget, ContextData(), *varBinds,
93           **dict(cbFun=cbFun, cbCtx=(snmpEngine, authData, transportTarget)))
94
95transportDispatcher.runDispatcher()
96