Advanced topics
***************


Specific SNMP Engine ID
=======================

Listen and respond to SNMP GET/SET/GETNEXT/GETBULK queries with the
following options:

* SNMPv3

* with SNMP EngineID: 8000000004030201

* with USM user 'usr-md5-des', auth: MD5, priv DES

* allow access to SNMPv2-MIB objects (1.3.6.1.2.1)

* over IPv4/UDP, listening at 127.0.0.1:161

The following Net-SNMP command will walk this Agent:

   $ snmpwalk -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 -e 8000000004030201 localhost .1.3.6

   from pysnmp.entity import engine, config
   from pysnmp.entity.rfc3413 import cmdrsp, context
   from pysnmp.carrier.asyncore.dgram import udp
   from pysnmp.proto import rfc1902

   # Create SNMP engine
   snmpEngine = engine.SnmpEngine(rfc1902.OctetString(hexValue='8000000004030201'))

   # Transport setup

   # UDP over IPv4
   config.addTransport(
       snmpEngine,
       udp.domainName,
       udp.UdpTransport().openServerMode(('127.0.0.1', 161))
   )

   # SNMPv3/USM setup

   # user: usr-md5-des, auth: MD5, priv DES
   config.addV3User(
       snmpEngine, 'usr-md5-des',
       config.usmHMACMD5AuthProtocol, 'authkey1',
       config.usmDESPrivProtocol, 'privkey1'
   )

   # Allow full MIB access for each user at VACM
   config.addVacmUser(snmpEngine, 3, 'usr-md5-des', 'authPriv', (1, 3, 6, 1, 2, 1), (1, 3, 6, 1, 2, 1))

   # Get default SNMP context this SNMP engine serves
   snmpContext = context.SnmpContext(snmpEngine)

   # Register SNMP Applications at the SNMP engine for particular SNMP context
   cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
   cmdrsp.SetCommandResponder(snmpEngine, snmpContext)
   cmdrsp.NextCommandResponder(snmpEngine, snmpContext)
   cmdrsp.BulkCommandResponder(snmpEngine, snmpContext)

   # Register an imaginary never-ending job to keep I/O dispatcher running forever
   snmpEngine.transportDispatcher.jobStarted(1)

   # Run I/O dispatcher which would receive queries and send responses
   try:
       snmpEngine.transportDispatcher.runDispatcher()
   except:
       snmpEngine.transportDispatcher.closeDispatcher()
       raise

"Download" script.


Observe SNMP engine operations
==============================

Listen and respond to SNMP GET/SET/GETNEXT/GETBULK queries with the
following options:

* SNMPv3

* with USM user 'usr-md5-des', auth: MD5, priv DES or

* allow access to SNMPv2-MIB objects (1.3.6.1.2.1)

* over IPv4/UDP, listening at 127.0.0.1:161

* registers its own execution observer to snmpEngine

The following Net-SNMP command will walk this Agent:

   $ snmpwalk -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 localhost .1.3.6

This script will report some details on request processing as seen by
rfc3412.receiveMessage() and rfc3412.returnResponsePdu() abstract
interfaces.

   from pysnmp.entity import engine, config
   from pysnmp.entity.rfc3413 import cmdrsp, context
   from pysnmp.carrier.asyncore.dgram import udp

   # Create SNMP engine
   snmpEngine = engine.SnmpEngine()


   # Execution point observer setup

   # Register a callback to be invoked at specified execution point of 
   # SNMP Engine and passed local variables at code point's local scope
   # noinspection PyUnusedLocal,PyUnusedLocal
   def requestObserver(snmpEngine, execpoint, variables, cbCtx):
       print('Execution point: %s' % execpoint)
       print('* transportDomain: %s' % '.'.join([str(x) for x in variables['transportDomain']]))
       print('* transportAddress: %s (local %s)' % ('@'.join([str(x) for x in variables['transportAddress']]), '@'.join([str(x) for x in variables['transportAddress'].getLocalAddress()])))
       print('* securityModel: %s' % variables['securityModel'])
       print('* securityName: %s' % variables['securityName'])
       print('* securityLevel: %s' % variables['securityLevel'])
       print('* contextEngineId: %s' % variables['contextEngineId'].prettyPrint())
       print('* contextName: %s' % variables['contextName'].prettyPrint())
       print('* PDU: %s' % variables['pdu'].prettyPrint())


   snmpEngine.observer.registerObserver(
       requestObserver,
       'rfc3412.receiveMessage:request',
       'rfc3412.returnResponsePdu'
   )

   # Transport setup

   # UDP over IPv4
   config.addTransport(
       snmpEngine,
       udp.domainName,
       udp.UdpTransport().openServerMode(('127.0.0.1', 161))
   )

   # SNMPv3/USM setup

   # user: usr-md5-des, auth: MD5, priv DES
   config.addV3User(
       snmpEngine, 'usr-md5-des',
       config.usmHMACMD5AuthProtocol, 'authkey1',
       config.usmDESPrivProtocol, 'privkey1'
   )

   # Allow full MIB access for each user at VACM
   config.addVacmUser(snmpEngine, 3, 'usr-md5-des', 'authPriv', (1, 3, 6, 1, 2, 1), (1, 3, 6, 1, 2, 1))

   # Get default SNMP context this SNMP engine serves
   snmpContext = context.SnmpContext(snmpEngine)

   # Register SNMP Applications at the SNMP engine for particular SNMP context
   cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
   cmdrsp.SetCommandResponder(snmpEngine, snmpContext)
   cmdrsp.NextCommandResponder(snmpEngine, snmpContext)
   cmdrsp.BulkCommandResponder(snmpEngine, snmpContext)

   # Register an imaginary never-ending job to keep I/O dispatcher running forever
   snmpEngine.transportDispatcher.jobStarted(1)

   # Run I/O dispatcher which would receive queries and send responses
   try:
       snmpEngine.transportDispatcher.runDispatcher()
   except:
       snmpEngine.observer.unregisterObserver()
       snmpEngine.transportDispatcher.closeDispatcher()
       raise

"Download" script.


Multiple SNMP Engines
=====================

Run multiple SNMP Engines each with a complete Command Responder. Bind
each SNMP Engine to a dedicated network transport endpoint:

* IPv4/UDP, listening at 127.0.0.1:161

* IPv4/UDP, listening at 127.0.0.2:161

Each Command Responder will respond to SNMP GET/SET/GETNEXT/GETBULK
queries with the following options:

* SNMPv3

* with USM user 'usr-md5-des', auth: MD5, priv DES

* allow read access to SNMPv2-MIB objects (1.3.6)

* allow write access to SNMPv2-MIB objects (1.3.6.1.2.1)

The following Net-SNMP commands will walk the first and the second
Agent respectively:

   $ snmpwalk -Ob -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 127.0.0.1 usmUserEntry
   $ snmpwalk -Ob -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 127.0.0.2 usmUserEntry

Notice differently configured snmpEngineId's in usmUserEntry columns.

   from pysnmp.entity import engine, config
   from pysnmp.entity.rfc3413 import cmdrsp, context
   from pysnmp.proto import rfc1902
   from pysnmp.carrier.asyncore.dispatch import AsyncoreDispatcher
   from pysnmp.carrier.asyncore.dgram import udp

   # Configuration parameters for each of SNMP Engines
   snmpEngineInfo = (
       ('0102030405060708', udp.domainName + (0,), ('127.0.0.1', 161)),
       ('0807060504030201', udp.domainName + (1,), ('127.0.0.2', 161))
   )

   # Instantiate the single transport dispatcher object
   transportDispatcher = AsyncoreDispatcher()

   # Setup a custom data routing function to select snmpEngine by transportDomain
   transportDispatcher.registerRoutingCbFun(lambda td, t, d: td)

   # Instantiate and configure SNMP Engines 
   for snmpEngineId, transportDomain, transportAddress in snmpEngineInfo:
       # Create SNMP engine with specific engineID
       snmpEngine = engine.SnmpEngine(rfc1902.OctetString(hexValue=snmpEngineId))

       # Register SNMP Engine object with transport dispatcher. Request incoming
       # data from specific transport endpoint to be funneled to this SNMP Engine.
       snmpEngine.registerTransportDispatcher(transportDispatcher, transportDomain)

       # Transport setup

       # UDP over IPv4 
       config.addTransport(
           snmpEngine,
           transportDomain,
           udp.UdpTransport().openServerMode(transportAddress)
       )

       # SNMPv3/USM setup

       # user: usr-md5-des, auth: MD5, priv DES
       config.addV3User(
           snmpEngine, 'usr-md5-des',
           config.usmHMACMD5AuthProtocol, 'authkey1',
           config.usmDESPrivProtocol, 'privkey1'
       )

       # Allow full MIB access for this user / securityModels at VACM
       config.addVacmUser(snmpEngine, 3, 'usr-md5-des', 'authPriv', (1, 3, 6), (1, 3, 6, 1, 2, 1))

       # Get default SNMP context this SNMP engine serves
       snmpContext = context.SnmpContext(snmpEngine)

       # Register SNMP Applications at the SNMP engine for particular SNMP context
       cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
       cmdrsp.SetCommandResponder(snmpEngine, snmpContext)
       cmdrsp.NextCommandResponder(snmpEngine, snmpContext)
       cmdrsp.BulkCommandResponder(snmpEngine, snmpContext)

   # Register an imaginary never-ending job to keep I/O dispatcher running forever
   transportDispatcher.jobStarted(1)

   # Run I/O dispatcher which would receive queries and send responses
   try:
       transportDispatcher.runDispatcher()
   except:
       transportDispatcher.closeDispatcher()
       raise

"Download" script.


Detailed VACM configuration
===========================

Serves MIB subtrees under different conditions:

* Respond to SNMPv2c commands

* with SNMP community "public"

* over IPv4/UDP, listening at 127.0.0.1:161

* Serve MIB under non-default contextName *abcd*

* Allow access to *SNMPv2-MIB::system* subtree

* Although deny access to *SNMPv2-MIB::sysUpTime* by a bit mask

* Use partial context name matching (*a*)

This example demonstrates detailed VACM configuration performed via
low-level VACM calls: *addContext*, *addVacmGroup*, *addVacmAccess*
and *addVacmView*. Each function populates one of the tables defined
in *SNMP-VIEW-BASED-ACM-MIB* and used strictly as described in the
above mentioned MIB.

The following Net-SNMP's commands will GET a value at this Agent:

   $ snmpget -v2c -c public 127.0.0.1 SNMPv2-MIB::sysLocation.0

However this command will fail:

   $ snmpget -v2c -c public 127.0.0.1 SNMPv2-MIB::sysUpTime.0

This command will not reveal *SNMPv2-MIB::sysUpTime.0* among other
objects:

   $ snmpwalk -v2c -c public 127.0.0.1 SNMPv2-MIB::system

   from pysnmp.entity import engine, config
   from pysnmp.entity.rfc3413 import cmdrsp, context
   from pysnmp.carrier.asyncore.dgram import udp

   # Create SNMP engine with autogenernated engineID and pre-bound
   # to socket transport dispatcher
   snmpEngine = engine.SnmpEngine()

   # Transport setup

   # UDP over IPv4
   config.addTransport(
       snmpEngine,
       udp.domainName,
       udp.UdpTransport().openServerMode(('127.0.0.1', 161))
   )

   # Register default MIB instrumentation controller with a new SNMP context

   contextName = 'abcd'

   snmpContext = context.SnmpContext(snmpEngine)

   snmpContext.registerContextName(
       contextName, snmpEngine.msgAndPduDsp.mibInstrumController)

   # Add new SNMP community name, map it to a new security name and
   # SNMP context

   securityName = 'my-area'
   communityName = 'public'

   config.addV1System(
       snmpEngine, securityName, communityName,
       contextEngineId=snmpContext.contextEngineId,
       contextName=contextName)

   # VACM configuration settings

   securityModel = 2  # SNMPv2c
   securityLevel = 1  # noAuthNoPriv

   vacmGroup = 'my-group'
   readViewName = 'my-read-view'

   # We will match by context name prefix
   contextPrefix = contextName[:1]

   # Populate SNMP-VIEW-BASED-ACM-MIB::vacmContextTable
   config.addContext(snmpEngine, contextName)

   # Populate SNMP-VIEW-BASED-ACM-MIB::vacmSecurityToGroupTable
   config.addVacmGroup(
       snmpEngine, vacmGroup, securityModel, securityName)

   # Populate SNMP-VIEW-BASED-ACM-MIB::vacmAccessTable
   config.addVacmAccess(
       snmpEngine, vacmGroup, contextPrefix, securityModel, securityLevel,
       'prefix', readViewName, '', '')

   # Populate SNMP-VIEW-BASED-ACM-MIB::vacmViewTreeFamilyTable

   # Allow the whole system subtree
   config.addVacmView(
       snmpEngine, readViewName, 'included', '1.3.6.1.2.1.1.1', '1.1.1.1.1.1.1.0')

   # ...but exclude one sub-branch (just one scalar OID)
   config.addVacmView(
       snmpEngine, readViewName, 'excluded', '1.3.6.1.2.1.1.3', '1.1.1.1.1.1.1.1')

   # Register SNMP Applications at the SNMP engine for particular SNMP context
   cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
   cmdrsp.SetCommandResponder(snmpEngine, snmpContext)
   cmdrsp.NextCommandResponder(snmpEngine, snmpContext)

   # Register an imaginary never-ending job to keep I/O dispatcher running forever
   snmpEngine.transportDispatcher.jobStarted(1)

   # Run I/O dispatcher which would receive queries and send responses
   try:
       snmpEngine.transportDispatcher.runDispatcher()

   except Exception:
       snmpEngine.transportDispatcher.closeDispatcher()
       raise

"Download" script.

See also: library reference.
