PYSNMP(1) SNMP library for Python PYSNMP(1)

pysnmp - PySNMP Documentation

PySNMP is a cross-platform, pure-Python SNMP engine implementation. It features fully-functional SNMP engine capable to act in Agent/Manager/Proxy roles, talking SNMP v1/v2c/v3 protocol versions over IPv4/IPv6 and other network transports.

Despite its name, SNMP is not really a simple protocol. For instance its third version introduces complex and open-ended security framework, multilingual capabilities, remote configuration and other features. PySNMP implementation closely follows intricate system details and features bringing most possible power and flexibility to its users.

Current PySNMP stable version is 4.4. It runs with Python 2.4 through 3.7 and is recommended for new applications as well as for migration from older, now obsolete, PySNMP releases. All site documentation and examples are written for the 4.4 and later versions in mind. Older materials are still available under the obsolete section.

Besides the libraries, a set of pure-Python command-line tools are shipped along with the system. Those tools mimic the interface and behaviour of popular Net-SNMP snmpget/snmpset/snmpwalk utilities. They may be useful in a cross-platform situations as well as a testing and prototyping instrument for pysnmp users.

PySNMP software is free and open-source. Source code is hosted in a Github repo. The library is being distributed under 2-clause BSD-style license.

PySNMP library development has been initially sponsored by a PSF grant.

You already know something about SNMP and have no courage to dive into this implementation? Try out quick start page!

Once you downloaded and installed PySNMP library on your Linux/Windows/OS X system, you should be able to solve the very basic SNMP task right from your Python prompt - fetch some data from a remote SNMP Agent (you'd need at least version 4.3.0 to run code from this page).

So just cut&paste the following code right into your Python prompt. The code will performs SNMP GET operation for a sysDescr.0 object at a publically available SNMP Command Responder at demo.snmplabs.com:

from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    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]))

Download script.

If everything works as it should you will get:

...
SNMPv2-MIB::sysDescr."0" = SunOS zeus.snmplabs.com 4.1.3_U1 1 sun4m
>>>

on your console.

To send a trivial TRAP message to our hosted Notification Receiver at demo.snmplabs.com , just cut&paste the following code into your interactive Python session:

from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    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)

Download script.

Many ASN.1 MIB files could be downloaded from mibs.snmplabs.com or PySNMP could be configured to download them automatically.

For more sophisticated examples and use cases please refer to examples and library reference pages.

In the early days of networking, when computer networks were research artifacts rather than a critical infrastructure used by almost every second human on Earth, "network management" was practically unknown. Whenever one encountered a network problem, he might run a few pings to locate the source of the problem and then modify system settings, reboot hardware or software, or call a remote colleague to check console at the machine room.

An interesting discussion of the first major "crash" of the ARPAnet in 1980, long before network management tools were available, and the efforts taken to recover from and understand the crash can be read in RFC 789. The astonishment of the engineers taking part in post-mortem investigation could be read between the lines. As the public Internet and private intranets have grown from small networks into a large global infrastructure, the need to more systematically manage the huge number of hardware and software components within these networks has grown more important as well.

SNMP was quickly designed and deployed by a group of university network researchers and users at a time when the need for network management was becoming painfully clear.

SNMP milestones:

  • Research project, successor of SGMP
  • SNMPv1 in 1988: initial revision
  • SNMPv2 in 1993: improvements
  • SNMPv3 in 1999: full redesign
  • SNMPv3: backward compatible
  • SNMPv3: full Internet standard (STD0062)

SNMP was initially thought as an interim solution to fill the need for network management tool while a more theoretically sound system was being developed by the ISO. Anticipating the transition to the new network management system, SNMP designers made SNMP modular. Although that transition never occurred, the modularity of SNMP help it evolving through three major versions and found widespread use and acceptance.

The IETF recognizes SNMP version 3 as defined by RFC 3411 .. RFC 3418 as the current standard version of SNMP. The IETF has designated SNMPv3 a full Internet standard, the highest maturity level for an RFC. In practice, SNMP implementations often support multiple versions: typically SNMPv1, SNMPv2c, and SNMPv3

Considering how old SNMP is you might be wondering why it is still in use and is there a more modern alternative? Apparently, SNMP is still the primary way to do performance and fault management. SNMP is universally supported by all networking hardware manufactures and network management applications.

Perhaps one reason for SNMP being so tenacious is that, considering SNNP's wide deployment, it takes too much effort to migrate to anything else. But the other reason is that no significant drawbacks have been found in SNMP at least in the areas of fault and performance management.

Additionally, SNMP is free and not controlled by any particular vendor. No copyright or licensing fees are required, so anyone can use it or build SNMP products on it.

Despite significant efforts made by technology companies and standards bodies over all these years, no other network monitoring standard was adopted so far. The most prominent open alternative is probably NETCONF (RFC 6241). However it mostly targets configuration management tasks rather than fault or performance monitoring. Additionally, NETCONF is significantly more resource intensive than SNMP is.

It is obviously possible to for everybody to come up with its own ad-hoc management system. That can be done very easily on top of HTTPS/JSON, for example. However that would only work with your application. Also, SSL engine might be heavier on resources.

As for current SNMP deployment, its virtually impossible to estimate how many SNMP-enabled devices run on the modern Internet today. For example, every home router and most of the desktop printers have embedded SNMP agent inside.

Expanding on that, you may found SNMP useful for your home network monitoring. For instance you could easily setup an open source network monitoring application to watch, collect and graph bandwidth utilization of your home Wi-Fi router.

A significant innovation might be coming in the following years. And that is Internet of Things. All those small and low-power gadgets need to be monitored and managed. And that may bring new life to the SNMP technology. Almost three decades ago SNMP was designed for heavily resource-constrained computers of that time. Later on the computers grew in power and resources. But now we are back to building a massive amount of low-power computers for "things" where original lightweight and well-understood SNMP can serve us again!

Contrary to what the name might suggest, SNMP is much more than just a protocol for moving management data. Over time it has grown to be more complex than its initial designers probably planned it.

The network management field has its own specific terminology for various components of a network management architecture, and so we adopt that terminology here. The peculiarity of this terminology is that the word "management" is greatly overused. So bare with it.

There are three principle components of a network management architecture: a managing entity, the managed entity, and a network management protocol. [image]

  • The managing entity is an application running in a centralized network management station (NMS). It is the managing entity that controls the collection, processing, analysis, and/or display of network management information. It is here that actions are initiated to control network behavior and here that the human network administrator interacts with the network devices.
  • A managed entity is typically hardware or software application that resides on a managed network. It enumerates and formalizes some of its properties and states, important for healthy operation, thus making them available to the managing entity. For example, a managed entity could be a host, router, switch, printer, or any other device.
  • The third piece of a network management system is the network management protocol. The protocol runs between the managing entity and the managed entity, allowing the managing entity to query the status of managed entity and make the latter carrying out actions via its agents.

SNMP consists of four parts:

  • Definitions of network management objects known as MIB objects. Management information is represented as a collection of managed objects that together form a virtual information store, known as the Management Information Base (MIB). A MIB object might be a counter, descriptive information such as software version; status information such as whether or not a device is healthy, or protocol-specific information such as a routing path to a destination. MIB objects thus define the management information maintained by a managed node. Related MIB objects are gathered into so-called MIB modules.
  • Data definition language, called SMI (Structure of Management Information) that introduces base data types, allows for creating their subtypes and more complex data structures. MIB objects are expressed in this data definition language.
  • Protocol (SNMP) for conveying information and commands between a managing and managed entities. SNMP is designed around a client-server model. What's interesting that both managing and managed entities contain client and server components.
  • Extensible security framework and system administration capabilities.

The latter features were completely absent in SNMP versions prior to SNMPv3.

SMI introduces eleven base data types used for representing managed objects states. They are either pure ASN.1 types or their specializations. Pure ASN.1 types:

  • INTEGER
  • OCTET STRING
  • OBJECT IDENTIFIER

ASN.1 is a really aged and quite complex set of standards that deals with structuring and serializing data in a portable way.

SNMP-specific subtypes of those base ASN.1 types are:

  • Integer32/Unsigned32 - 32-bit integer
  • Counter32/Counter64 - ever increasing number
  • Gauge32 - positive, non-wrapping 31-bit integer
  • TimeTicks - time since some event
  • IPaddress - IPv4 address
  • Opaque - uninterpreted ASN.1 string

In addition to these scalar types, SNMP defines a way to collect them into ordered arrays. From these arrays 2-d tables could be built.

PySNMP relies on the PyASN1 package for modeling all SNMP types. With PyASN1, instances of ASN.1 types are represented by Python objects that look like either a string or an integer.

We can convert PyASN1 objects into Python types and back. PyASN1 objects can participate in basic arithmetic operations (numbers) or in operations with strings (concatenation, subscription etc). All SNMP base types are immutable like their Python counterparts.

>>> from pyasn1.type.univ import *
>>> Integer(21) * 2
Integer(42)
>>> Integer(-1) + Integer(1)
Integer(0)
>>> int(Integer(42))
42
>>> OctetString('Hello') + ', ' +
>>> OctetString(hexValue='5079534e4d5021')
OctetString('Hello, PySNMP!')

Users of PySNMP library may encounter PyASN1 classes and objects when passing data to or receiving data from PySNMP.

The one data type we will discuss in more detail shortly is the OBJECT IDENTIFIER data type, which is used to name an object. With this system, objects are identified in a hierarchical manner.

OIDs are widly used in computing for identifying objects. This system can be depicted as a tree whose nodes are assigned by different organizations, knowledge domains, types of concepts or objects, concrete instances of objects. From human perspective, an OID is a long sequence of numbers, coding the nodes, separated by dots. [image]

Each 'branch' of this tree has a number and a name, and the complete path from the top of the tree down to the point of interest forms the name of that point. This complete path is the OID, the "identifier of an object" respectively. Nodes near the top of the tree are of an extremely general nature.

Top level MIB object IDs (OIDs) belong to different standard organizations. Vendors define private branches including managed objects for their own products.

At the top of the hierarchy are the International Organization for Standardization (ISO) and the Telecommunication Standardization Sector of the International Telecommunication Union (ITU-T), the two main standards organizations dealing with ASN.1, as well as a brach for joint efforts by these two organizations.

In PyASN1 model, OID looks like an immutable sequence of numbers. Like it is with Python tuples, PyASN1 OID objects can be concatinated or split apart. Subscription operation returns a numeric sub-OID.

>>> from pyasn1.type.univ import *
>>> internetId = ObjectIdentifier((1, 3, 6, 1))
>>> internetId
ObjectIdentifier('1.3.6.1')
>>> internetId[2]
6
>>> [ x for x in internetId ]
[1, 3, 6, 1]
>>> internetId + (2,)
ObjectIdentifier('1.3.6.1.2')
>>> internetId[1:3]
ObjectIdentifier('3.6')
>>> internetId[1]
>>> = 2
...
TypeError: object does not support item assignment

Management Information Base (MIB) can be thought of as a formal description of a collection of relevant managed objects whose values collectively reflect the current "state" of some subsystem at a managed entity. These values may be queried, modified by or reported to a managing entity by sending SNMP messages to the agent that is executing in a managed node.

For example, the typical objects to monitor on a printer are the different cartridge states and maybe the number of printed files, and on a switch the typical objects of interest are the incoming and outgoing traffic as well as the rate of package loss or the number of packets addressed to a broadcast address.

Every managed device keeps a database of values for each of the definitions written in the MIB. So, the available data is actually not dependent on the database, but on the implementation. It is important to realize that MIB files never contain data, they are functionally similar to database schemas rather than data stores.

To organize MIB modules and objects properly, all the manageable features of all products (from each vendor) are arranged in this MIB tree structure. Each MIB module and object is uniquely identified by an Object Identifier.

Both SNMP managed and managing entities could consume MIB information.

Managing entity
  • Looks up OID by MIB object name
  • Casts value to proper type of MIB object
  • Humans read comments left by other humans
Managed entity
Implements MIB objects in code

From human perspective, MIB is a text file written in a subset of ASN.1 language. We maintain a collection of 9000+ MIB modules that you can use for your projects.

PySNMP converts ASN.1 MIB files into Python modules, then SNMP engine loads those modules at runtime on demand. PySNMP MIB modules are universal -- the same module can be consumed by both managed and managing entities.

MIB conversion is performed automatically by PySNMP, but technically, it is handled by PySNMP sister project called PySMI. However you can also perform said conversion by hand with PySMI's mibdump.py tool.

SNMP is designed around a client-server model. Both managing and managed entities contain client and server components. Clients and servers exchange data in a name-value form. Values are strongly typed.

Central to protocol entity is SNMP engine that coordinates workings of all SNMP components. [image]

Two modes of protocol operation are defined:

  • Request-response messages
  • Unsolicited messages

Protocol carries SNMP messages. Besides header information used for protocol operations, management information is transferred in so-called Protocol Data Units (PDU). Seven PDU types are defined in SNMP addressing conceptually different operations to be performed by either managing or managed entities (Manager or Agent repectively).

Manager-to-agent
GetRequest, SetRequest, GetNextRequest, GetBulkRequest, InformRequest
Manager-to-manager
InformRequest, Response
Agent-to-manager
SNMPv2-Trap, Response

The standard (RFC 3413) identifies a few "standard" SNMP applications that are associated with either managing or managed entities. [image]

PySNMP implements all these standard applications (via Native SNMP API) carefully following RFCs and their abstract service interfaces. The backside of this approach is that it's way too detailed and verbose for most SNMP tasks. To make SNMP easy to use, PySNMP introduces High-level SNMP API.

We can look at PySNMP's internal structure from the view point of SNMP protocol evolution. SNMP was evolving for many years from a relatively simple way to structure and retrieve data (SNMPv1/v2c) all the way to extensible and modularized framework that supports strong crypto out-of-the-box (SNMPv3).

In the order from most ancient SNMP services to the most current ones, what follows are different layers of PySNMP APIs:

  • The most basic and low-level is SNMPv1/v2c protocol scope. Here programmer is supposed to build/parse SNMP messages and their payload -- Protocol Data Unit (PDU), handle protocol-level errors, transport issues and so on. Although considered rather complex to deal with, this API probably gives best performance, memory footprint and flexibility, unless MIB access and/or SNMPv3 support is needed.
  • SNMPv3 standards come with abstract service interfaces to SNMP engines and its components. PySNMP implementation adopts this abstract API to a great extent, so it can be used directly. As additional benefit, SNMP RFCs could be referred to API semantics when programming PySNMP at this level. User could implements his own SNMP application using this API.
  • SNMPv3 (RFC 3413) introduced a concept of core "SNMP Applications". PySNMP implements them all (in pysnmp.entity.rfc3413), so user can base his application right on top of one (or multiple) of those SNMP core applications.
  • Finally, to make SNMP more simple for, at least, most frequent tasks, PySNMP comes with a high-level API to core SNMP applications and some of SNMP engine services. This API is known under the name pysnmp.hlapi and should be used whenever possible.

Another view to PySNMP internals could be from the code standpoint: PySNMP consists of a handful of large, self-contained components with well-defined interfaces. The following figure explains PySNMP functional structure. [image]

PySNMP inner components:

  • SNMP Engine is a central, umbrella object that controls the other components of the SNMP system. Typical user application has a single instance of SNMP Engine class possibly shared by many SNMP Applications of all kinds. As the other linked-in components tend to buildup various configuration and housekeeping information in runtime, SNMP Engine object appears to be expensive to configure to a usable state.
  • Transport subsystem is used for sending SNMP messages to and accepting them from network. The I/O subsystem consists of an abstract Dispatcher and one or more abstract Transport classes. Concrete Dispatcher implementation is I/O method-specific, consider BSD sockets for example. Concrete Transport classes are transport domain-specific. SNMP frequently uses UDP Transport but others are also possible. Transport Dispatcher interfaces are mostly used by Message And PDU Dispatcher. However, when using the SNMPv1/v2c-native API (the lowest-level one), these interfaces would be invoked directly.
  • Message and PDU Dispatcher is the locus of SNMP message processing activities. Its main responsibilities include dispatching PDUs from SNMP Applications through various subsystems all the way down to Transport Dispatcher, and passing SNMP messages coming from network up to SNMP Applications. It maintains logical connection with Management Instrumentation Controller which carries out operations on Managed Objects, here for the purpose of LCD access.
  • Message Processing Modules handle message-level protocol operations for present and possibly future versions of SNMP protocol. Most importantly, these include message parsing/building and possibly invoking security services whenever required.
  • Message Security Modules perform message authentication and/or encryption. As of this writing, User-Based (for v3) and Community (for v1/2c) modules are implemented in PySNMP. All Security Modules share standard API used by Message Processing subsystem.
  • Access Control subsystem uses LCD information to authorize remote access to Managed Objects. This is used when running in agent role.
  • A collection of MIB modules and objects that are used by SNMP engine for keeping its configuration and operational statistics. They are collectively called Local Configuration Datastore (LCD).

In most cases user is expected to only deal with the high-level API to all these PySNMP components. However implementing SNMP Agents, Proxies and some non-trivial features of managers require using the Standard Applications API. In those cases general understanding of SNMP operations and SNMP Engine components would be helpful.

In this tutorial we will gradually build and run a few different SNMP command requests and notifications. We will be using PySNMP synchronous high-level API which is the simplest to use.

SNMP engine is a central, umbrella object in PySNMP. All PySNMP operations involve SnmpEngine class instance. PySNMP app can run multiple independent SNMP engines each guided by its own SnmpEngine object.

>>> from pysnmp.hlapi import *
>>>
>>> SnmpEngine()
SnmpEngine(snmpEngineID=OctetString(hexValue='80004fb80567'))

SNMP engine has unique identifier that can be assigned automatically or administratively. This identifier is used in SNMP protocol operations.

We will send SNMP GET command to read a MIB object from SNMP agent. For that purpose we will call synchronous, high-level getCmd() function. Other SNMP commands can be used in a vary similar way by calling corresponding functions.

>>> from pysnmp.hlapi import *
>>> [ x for x in dir() if 'Cmd' in x]
['bulkCmd', 'getCmd', 'nextCmd', 'setCmd']
>>> getCmd
<function getCmd at 0x222b330>

We have a choice of three SNMP protocol versions. To employ SNMP versions 1 or 2c, we pass properly initialized instance of CommunityData class. For the third SNMP version we pass UsmUserData class instance.

SNMP community name, as well as the choice between SNMP v1 and v2c, is conveyed to SNMP LCD via CommunityData object.

>>> from pysnmp.hlapi import *
>>>
>>> CommunityData('public', mpModel=0)  # SNMPv1
CommunityData('public')
>>> CommunityData('public', mpModel=1)  # SNMPv2c
CommunityData('public')

Use of UsmUserData object for LCD configuration implies using SNMPv3. Besides setting up USM user name, UsmUserData object can also carry crypto keys and crypto protocols to SNMP engine LCD.

>>> from pysnmp.hlapi import *
>>>
>>> UsmUserData('testuser', authKey='myauthkey')
UsmUserData(userName='testuser', authKey=<AUTHKEY>)
>>> UsmUserData('testuser', authKey='myauthkey', privKey='myenckey')
UsmUserData(userName='testuser', authKey=<AUTHKEY>, privKey=<PRIVKEY>)

PySNMP supports MD5 and SHA message authentication algorithms, DES, AES128/192/256 and 3DES encryption algoritms.

For sake of simplicity, let's use SNMPv2. Although completely insecure, it's still the most popular SNMP version in use.

>>> from pysnmp.hlapi import *
>>>
>>> g = getCmd(SnmpEngine(), CommunityData('public'),
...

PySNMP supports UDP-over-IPv4 and UDP-over-IPv6 network transports. In this example we will query public SNMP Simulator available over IPv4 on the Internet at demo.snmplabs.com. Transport configuration is passed to SNMP LCD in form of properly initialized UdpTransportTarget or Udp6TransportTarget objects respectively.

>>> from pysnmp.hlapi import *
>>>
>>> g = getCmd(SnmpEngine(),
...            CommunityData('public'),
...            UdpTransportTarget(('demo.snmplabs.com', 161)),
...

SNMP context is a parameter in SNMP (v3) message header that addresses specific collection of MIBs served by SNMP engine at managed entity. SNMP engine could serve many identical MIB objects representing completely different instances of hardware or software being managed. This is where SNMP context could be used.

To indicate SNMP context at high-level API a properly initialized ContextData object should be used. For this example we will use the 'empty' context (default).

>>> from pysnmp.hlapi import *
>>>
>>> g = getCmd(SnmpEngine(),
...            CommunityData('public'),
...            UdpTransportTarget(('demo.snmplabs.com', 161)),
...            ContextData(),
...

Finally, we have to specify the MIB object we want to read. On protocol level, MIB objects are identified by OIDs, but humans tend to address them by name:

$ snmpget -v2c -c public demo.snmplabs.com SNMPv2-MIB::sysDescr.0
SNMPv2-MIB::sysDescr.0 = STRING: SunOS zeus.snmplabs.com
$
$ snmpget -v2c -c public demo.snmplabs.com 1.3.6.1.2.1.1.1.0
SNMPv2-MIB::sysDescr.0 = STRING: SunOS zeus.snmplabs.com

Both object name and OID come from MIB. Name and OID linking is done by high-level SMI construct called OBJECT-TYPE. Here is an example MIB object definition for sysUpTime with OID ...mgmt.mib-2.system.3 and value type TimeTicks.

sysUpTime OBJECT-TYPE
    SYNTAX      TimeTicks
    MAX-ACCESS  read-only
    STATUS      current
    DESCRIPTION
            "The time (in hundredths of a second) since
            the network management portion of the system
            was last re-initialized."
    ::= { system 3 }

In PySNMP we use the ObjectIdentity class that is responsible for MIB objects identification. ObjectIdentity represents ways to address MIB object from human perspective. It needs to consult MIB to enter a fully "resolved" state. ObjectIdentity could be initialized with MIB object name, after a MIB look up it starts behaving like an OID.

>>> from pysnmp.hlapi import *
>>>
>>> x = ObjectIdentity('SNMPv2-MIB', 'system')
>>> # ... calling MIB lookup ...
>>> tuple(x)
(1, 3, 6, 1, 2, 1, 1, 1)
>>> x = ObjectIdentity('iso.org.dod.internet.mgmt.mib-2.system.sysDescr')
>>> # ... calling MIB lookup ...
>>> str(x)
'1.3.6.1.2.1.1.1'

MIB resolution means the service of MIB object name into OID transformation or vice versa.

The ObjectType class instance represents OBJECT-TYPE SMI constuct in PySNMP. ObjectType is a container object that references ObjectIdentity and SNMP type instances. As a Python object it looks like a tuple of (OID, value).

>>> from pysnmp.hlapi import *
>>> x = ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0), 'Linux i386 box'))
>>> # ... calling MIB lookup ...
>>> x[0].prettyPrint()
'SNMPv2-MIB::sysDescr.0'
>>> x[1].prettyPrint()
'Linux i386 box'

The trailing zero is an indication of MIB object instance. Objects described in MIBs are just declarations, they never contain any data. Data is stored in MIB object instances addressed by appending extra information (known as index) to MIB object identifiers.

For scalar MIB objects index is '0' by convention. The ObjectIdentity class takes indices as its initializers.

>>> x = ObjectIdentity('SNMPv2-MIB', 'system', 0)
>>> # ... calling MIB lookup ...
>>> tuple(x)
(1, 3, 6, 1, 2, 1, 1, 1, 0)

We will be reading sysDescr scalar MIB object instance as defined in SNMPv2-MIB module.

>>> from pysnmp.hlapi import *
>>> g = getCmd(SnmpEngine(),
...            CommunityData('public'),
...            UdpTransportTarget(('demo.snmplabs.com', 161)),
...            ContextData(),
...            ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)))

By default PySNMP will search your local filesystem for ASN.1 MIB files you refer to. It can also be configured to automatically download them from remote hosts, as shown in the examples. We maintain a collection of ASN.1 MIB modules that you can use in your SNMP projects.

NOTE:

An "ASN.1 MIB" is a plain-text description of identifiers and types. It is the common format that is distributed by manufacturers to describe their SNMP services, and is the same format used by Perl's Net::SNMP and almost all SNMP tools.

We are finally in a position to send SNMP query and hopefully receive something meaningful in response.

The distinctive feature of synchronous API is that it is built around the idea of Python generator. Any function invocation ends up with a generator object. Iteration over the generator object performs actual SNMP communication. On each iteration SNMP message gets built and sent out, response is awaited, received and parsed.

>>> from pysnmp.hlapi import *
>>>
>>> g = getCmd(SnmpEngine(),
...            CommunityData('public'),
...            UdpTransportTarget(('demo.snmplabs.com', 161)),
...            ContextData(),
...            ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysUpTime', 0)))
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.3.0'), TimeTicks(44430646))])

SNMP defines a concept of table. Tables are used when a single given MIB object may apply to many instances of a property. For example, properties of network interfaces are put into SNMP table. Each instance of a property is addressed by a suffix appended to base MIB object.

Tables are specified in MIBs, their index (or indices) are declared via the INDEX clause. Table index is non-zero integer, or string or any base SNMP type.

At the protocol level all indices take shape of OID parts. For humans to work with indices comfortably, SNMP management applications rely on DISPLAY-HINT clause for automatic indices conversion between their OID and SNMP type-specific, human-friendly representation.

ifEntry OBJECT-TYPE
    SYNTAX      IfEntry
    INDEX   { ifIndex }
::= { ifTable 1 }
ifIndex OBJECT-TYPE
    SYNTAX      InterfaceIndex
::= { ifEntry 1 }
ifDescr OBJECT-TYPE
    SYNTAX      DisplayString (SIZE (0..255))
::= { ifEntry 2 }
InterfaceIndex ::= TEXTUAL-CONVENTION
    DISPLAY-HINT "d"
    SYNTAX       Integer32 (1..2147483647)

In PySNMP parlance:

>>> x = ObjectIdentity('IF-MIB', 'ifDescr', 123)
>>> # ... calling MIB lookup ...
>>> str(x)
'1.3.6.1.2.1.2.2.1.2.123'

Some SNMP tables are indexed by many indices. Each of these indices become parts of OID concatinated to each other and ultimately to MIB object OID.

From semantic standpoint, each index reflects an important and distinct property of a MIB object.

tcpConnectionEntry OBJECT-TYPE
    SYNTAX  TcpConnectionEntry
    INDEX   { tcpConnectionLocalAddressType,
              tcpConnectionLocalAddress,
              tcpConnectionLocalPort,
              tcpConnectionRemAddressType,
              tcpConnectionRemAddress,
              tcpConnectionRemPort }
::= { tcpConnectionTable 1 }
tcpConnectionLocalPort OBJECT-TYPE
    SYNTAX     InetPortNumber
::= { tcpConnectionEntry 3 }

PySNMP's ObjectIdentity class takes any number of indices in human-friendly representation and converts them into full OID:

>>> x = ObjectIdentity('TCP-MIB', 'tcpConnectionState',
...                    'ipv4', '195.218.254.105', 41511,
...                    'ipv4', '194.67.1.250', 993)
>>> # ... calling MIB lookup ...
>>> str(x)
'1.3.6.1.2.1.6.19.1.7.1.4.195.218.254.105.41511.1.4.194.67.1.250.993'

Let's read TCP-MIB::tcpConnectionState object for a TCP connection:

>>> from pysnmp.hlapi import *
>>>
>>> g = getCmd(SnmpEngine(),
...            CommunityData('public'),
...            UdpTransportTarget(('demo.snmplabs.com', 161)),
...            ContextData(),
...            ObjectType(ObjectIdentity('TCP-MIB', 'tcpConnectionState',
...                                      'ipv4', '195.218.254.105', 41511,
...                                      'ipv4', '194.67.1.250', 993)
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity(ObjectName('1.3.6.1.2.1.6.19.1.7.1.4.195.218.254.105.41511.1.4.194.67.1.250.993')), Integer(5))])

SNMP allows you to request a MIB object that is "next" to the given one. That way you can read MIB objects you are not aware about in advance. MIB objects are conceptually sorted by their OIDs. This feature is implemented by the nextCmd() function.

>>> from pysnmp.hlapi import *
>>> g = nextCmd(SnmpEngine(),
...             CommunityData('public'),
...             UdpTransportTarget(('demo.snmplabs.com', 161)),
...             ContextData(),
...             ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr')))
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'), DisplayString('SunOS zeus.snmplabs.com'))])
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.2.0'), ObjectIdentity(ObjectIdentifier('1.3.6.1.4.1.8072.3.2.10')))])

Iteration over the generator object "walk" over SNMP agent's MIB objects.

SNMPv2c introduced significant optimization to the GETNEXT command - the revised version is called GETBULK and is capable to gather and respond a bunch of "next" MIB objects at once. Additional non-repeaters and max-repetitions parameters can be used to influence MIB objects batching.

PySNMP hides this GETBULK optimization at the protocol level, the bulkCmd() function exposes the same generator API as getNext() for convenience.

>>> from pysnmp.hlapi import *
>>>
>>> N, R = 0, 25
>>> g = bulkCmd(SnmpEngine(),
...             CommunityData('public'),
...             UdpTransportTarget(('demo.snmplabs.com', 161)),
...             ContextData(),
...             N, R,
...             ObjectType(ObjectIdentity('1.3.6')))
>>>
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'), DisplayString('SunOS zeus.snmplabs.com'))])
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.2.0'), ObjectIdentifier('1.3.6.1.4.1.20408'))])

Python generators can not only produce data, but it is also possible to send data into running generator object. That feature is used by the high-level API to repeat the same SNMP operation for a new set of MIB objects.

>>> from pysnmp.hlapi import *
>>>
>>> g = nextCmd(SnmpEngine(),
...             CommunityData('public'),
...             UdpTransportTarget(('demo.snmplabs.com', 161)),
...             ContextData(),
...             ObjectType(ObjectIdentity('IF-MIB', 'ifTable')))
>>>
>>> g.send([ObjectType(ObjectIdentity('IF-MIB', 'ifInOctets'))])
(None, 0, 0, [(ObjectType(ObjectIdentity('1.3.6.1.2.1.2.2.1.10.1'), Counter32(284817787))])

You could operate on many unrelated MIB object just by listing them in a single PDU. Response PDU will carry a list of MIB objects and their values in exactly the same order as they were in request message.

>>> from pysnmp.hlapi import *
>>>
>>> g = getCmd(SnmpEngine(),
...            CommunityData('public'),
...            UdpTransportTarget(('demo.snmplabs.com', 161)),
...            ContextData(),
...            ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
...            ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysUpTime', 0))
... )
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'), DisplayString('SunOS zeus.snmplabs.com')), ObjectType(ObjectIdentity('1.3.6.1.2.1.1.3.0'), TimeTicks(44430646))])

Configuration management part of SNMP relies on SNMP SET command. Although its implementation on managed entity's side proved to be somewhat demanding (due to locking and transactional behavior requirements). So vendors tend to leave it out thus rendering managed entity being read-only.

PySNMP supports SET uniformly through setCmd() function.

>>> from pysnmp.hlapi import *
>>>
>>> g = setCmd(SnmpEngine(),
...            CommunityData('public'),
...            UdpTransportTarget(('demo.snmplabs.com', 161)),
...            ContextData(),
...            ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0), 'Linux i386')
... )
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'), DisplayString('Linux i386'))])

Managed entity could send unsolicited messages to the managing entity. That is called notification in SNMP. Notifications help reduce polling, what may become a problem for large networks.

SNMP notifications are enumerated and each has definite semantics. This is done through a special, high-level SMI construct called NOTIFICATION-TYPE. Like OBJECT-TYPE, that defines a MIB object, NOTIFICATION-TYPE has a unique OID, but instead of SNMP value references a sequence of other MIB objects. These MIB objects are specified with the OBJECTS clause and when notification is being sent, their current values are included into the notification message.

linkUp NOTIFICATION-TYPE
    OBJECTS { ifIndex, ifAdminStatus, ifOperStatus }
    STATUS  current
    DESCRIPTION
        "..."
::= { snmpTraps 4 }

To model NOTIFICATION-TYPE construct in PySNMP, we have the NotificationType class that is a container object. It is identified by the ObjectIdentity class and reference a sequence of ObjectType class instances.

From behavior standpoint, NotificationType looks like a sequence of ObjectType class instances.

>>> from pysnmp.hlapi import *
>>>
>>> x = NotificationType(ObjectIdentity('IF-MIB', 'linkUp'))
>>> # ... calling MIB lookup ...
>>> >>> [ str(y) for x in n ]
['SNMPv2-MIB::snmpTrapOID.0 = 1.3.6.1.6.3.1.1.5.3', 'IF-MIB::ifIndex = ', 'IF-MIB::ifAdminStatus = ', 'IF-MIB::ifOperStatus = ']

Sending notification with PySNMP is not much different than sending SNMP command. The difference is in how PDU var-binds are built. There are two different kinds of notifications in SNMP: trap and inform.

With trap, agent-to-manager communication is one-way - no response or acknowledgement is sent.

>>> from pysnmp.hlapi import *
>>>
>>> g = sendNotification(SnmpEngine(),
...                      CommunityData('public'),
...                      UdpTransportTarget(('demo.snmplabs.com', 162)),
...                      ContextData(),
...                      'trap',
...                      NotificationType(ObjectIdentity('IF-MIB', 'linkUp'), instanceIndex=(123,))
... )
>>> next(g)
(None, 0, 0, [])

The inform notification is much like a command. The difference is in PDU format. Informs are used for manager-to-manager communication as well as for agent-to-manager.

>>> from pysnmp.hlapi import *
>>>
>>> g = sendNotification(SnmpEngine(),
...                      CommunityData('public'),
...                      UdpTransportTarget(('demo.snmplabs.com', 162)),
...                      ContextData(),
...                      'inform',
...                      NotificationType(ObjectIdentity('IF-MIB', 'linkUp'), instanceIndex=(123,))
... )
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.3.0'), TimeTicks(0)), ObjectType(ObjectIdentity('1.3.6.1.6.3.1.1.4.1.0'), ObjectIdentity('1.3.6.1.6.3.1.1.5.4')), ObjectType(ObjectName('1.3.6.1.2.1.2.2.1.1.123'), Null('')), ObjectType(ObjectIdentity('1.3.6.1.2.1.2.2.1.7.123'), Null('')), ObjectType(ObjectIdentity('1.3.6.1.2.1.2.2.1.8.123'), Null(''))])

In the latter example you can see MIB objects (ifIndex, ifAdminStatus, ifOperStatus) being automatically expanded from IF-MIB::linkUp notification. To address specific row of SNMP table objects by index, the index part of MIB objects could be passed to NotificationType via instanceIndex parameter.

As you can see, the actual values for expanded MIB objects are NULLs. That's because in these examples our simple scripts do not have access to those MIB objects. We can supply that missing information by passing NotificationType a dictionary-like object that maps MIB object OIDs to current values.

>>> from pysnmp.hlapi import *
>>>
>>> mib = {ObjectIdentifier('1.3.6.1.2.1.2.2.1.1.123'): 123,
...        ObjectIdentifier('1.3.6.1.2.1.2.2.1.7.123'): 'testing',
...        ObjectIdentifier('1.3.6.1.2.1.2.2.1.8.123'): 'up'}
>>>
>>> g = sendNotification(SnmpEngine(),
...                      CommunityData('public'),
...                      UdpTransportTarget(('demo.snmplabs.com', 162)),
...                      ContextData(),
...                      'inform',
...                      NotificationType(ObjectIdentity('IF-MIB', 'linkUp'), instanceIndex=(123,), objects=mib)
... )
>>> next(g)
(None, 0, 0, [ObjectType(ObjectIdentity('1.3.6.1.2.1.1.3.0'), TimeTicks(0)), ObjectType(ObjectIdentity('1.3.6.1.6.3.1.1.4.1.0'), ObjectIdentity('1.3.6.1.6.3.1.1.5.4')), ObjectType(ObjectName('1.3.6.1.2.1.2.2.1.1.123'), InterfaceIndex(123)), ObjectType(ObjectIdentity('1.3.6.1.2.1.2.2.1.7.123'), Integer(3)), ObjectType(ObjectIdentity('1.3.6.1.2.1.2.2.1.8.123'), Integer(1))])

High-volume messaging

When in comes to managing large network, reading MIB objects sequentially introduces latency. By some point the latency becomes intolerable. Solutions to parallelize queries are well known - you could do that by offloading individual operations into multiple processes, or multiple threads of execution or build your application around the asynchronous I/O model.

Compared to other solutions, asynchronous model is most lightweight and scalable. The idea is simple: never wait for I/O - do something else whenever possible. The back side of this is that execution flow becomes non-linear what hurts program analysis by human reader.

PySNMP high-level API is adapted to work with three popular asynchronous I/O frameworks - asyncore, twisted and asyncio. Please, refer to PySNMP library reference and examples for more information on asynchronous API.

Dealing with many SNMP features may quickly overwhelm developers who aim at a quick and trivial task, PySNMP employs a layered architecture approach where the topmost programming API tries to be as simple as possible to allow immediate solutions for most common use cases. It will let you perform SNMP GET/SET/WALK and TRAP/INFORM operations by pasting code snippets from PySNMP documentation and example scripts right into your Python interactive session.

Most of SNMP operations involve packet exchange over network. PySNMP is shipped with a set of bindings to popular asynchronous Python I/O frameworks that let you run PySNMP in parallel with other tasks your application may perform.

Synchronous SNMP

Most simple and strightforward way to use PySNMP is by employing its Synchronous, blocking API. It's also the default API offered by users on pysnmp.hlapi sub-package import.

Command Generator

GET command

SET command

GETNEXT command

GETBULK command

Notification Originator

TRAP/INFORM notification

Transport configuration

The following shortcut classes convey configuration information to SNMP engine's Local Configuration Datastore (RFC 2271#section-3.4.2) as well as to underlying socket API. Once committed to LCD, SNMP engine saves its configuration for the lifetime of SNMP engine object.

Asynchronous: asyncore

The asyncore module is in Python standard library since ancient times. Main loop is built around select dispatcher, user code is invoked through callback callables.

Command Generator

GET command

SET command

GETNEXT command

GETBULK command

Notification Originator

TRAP/INFORM notification

Transport configuration

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. That greately simplifies development and maintanence.

Command Generator

GET command

SET command

GETNEXT command

GETBULK command

Notification Originator

TRAP/INFORM notification

Transport configuration

Asynchronous: trollius

An almost compatible alternative to asyncio for pre-3.3 Python is Trollius module. PySNMP's asyncio bindings automatically work with Trolleus.

Please refer to Trollius examples for more information.

Asynchronous: Twisted

Twisted is one of the earliest and hugely popular asynchronous I/O framework. It introduced a concept of Deferred for representing work-in-progress that is not blocking the rest of I/O operations. PySNMP provides Twisted bindings.

Command Generator

GET command

SET command

GETNEXT command

GETBULK command

Notification Originator

TRAP/INFORM notification

Transport configuration

SNMP Engine is a central, stateful object used by all SNMP v3 substsems. Calls to high-level Applications API also consume SNMP Engine object on input.

Calls to high-level Applications API consume Security Parameters configuration object on input. The shortcut classes described in this section convey configuration information to SNMP engine's Local Configuration Datastore (RFC 2271#section-3.4.2). Once committed to LCD, SNMP engine saves its configuration for the lifetime of SNMP engine object.

Security Parameters object is Security Model specific. The CommunityData class is used for configuring Community-Based Security Model of SNMPv1/SNMPv2c.

The UsmUserData class provides SNMPv3 User-Based Security Model configuration for SNMP v3 systems.

Authentication protocol identifiers

Privacy (encryption) protocol identifiers

Key material types

NOTE:

SNMP authentication and encryption keys must be at least 8 and at most 32 octets long.

Transport configuration is I/O framework specific and is described in respective sections.

SNMP engine may serve several instances of the same MIB within possibly multiple SNMP entities. SNMP context is a tool for unambiguously identifying a collection of MIB variables behind the SNMP engine. See RFC 3411#section-3.3.1 for details.

NOTE:

The SNMP context information is not tied to SNMPv3/USM user, but it is transferred in SNMPv3 message header.

Legacy SNMPv1/v2c protocols do not accommodate the SNMP context information at all.

To fit legacy SNMPv1/SNMPv2c systems into unified SNMPv3 architecture, the mapping procedure is introduced by RFC 2576#section-5.1 which essentially lets you first configure and then supply the missing items (e.g. contextName, contextEngineId and other) to the upper layers of SNMP stack based on SNMPv1/v2c communityName and transport endpoint.

The SNMP context information necessary for this mapping procedure to operate is supplied through the CommunityData object.

SNMP MIB variable is identified by an OBJECT IDENTIFIER (OID) and is accompanied by a value belonging to one of SNMP types (RFC 1902#section-2). This pair is collectively called a variable-binding in SNMP parlance.

The rfc1902 module implements RFC 1902#section-2 MACRO definiitons.

SNMP Notifications are enumerated and imply including certain set of MIB variables. Notification Originator applications refer to MIBs for MIB notifications through NOTIFICATION-TYPE ASN.1 macro. It conveys a set of MIB variables to be gathered and reported in SNMP Notification. The rfc1902 module implements RFC 1902#section-2 macro definiitons.

SNMP represents real-world objects it serves along with their states in form of values. Those values each belong to one of SNMP types (RFC 1902#section-2) which, in turn, are based on ASN.1 data description language. PySNMP types are derived from Python ASN.1 types implementation.

NOTE:

The NULL type actually belongs to the base ASN.1 types. It is not defined in RFC 1902#section-2 as an SNMP type. The Null type is exposed through rfc1902 module just for convenience.

SNMP is not simple (PySNMP implementation takes over 15K lines of Python code), but PySNMP tries to hide the complexities and let you carry out typical SNMP operations in a quick and intuitive way.

PySNMP offers three groups of programming interfaces to deal with SNMP protocol. In the order from most concise to most detailed those APIs follow.

High-level SNMP

The so called high-level API (hlapi) is designed to be simple, concise and suitable for the most frequent operations. For that matter only Command Generator and Notification Originator Applications are currently wrapped into a nearly one-line Python expression.

It comes in several flavours: one synchronous and a bunch of bindings to popular asynchronous I/O frameworks. Those varieties of APIs bring subtile differences, mostly to better match particular I/O framework customs. Unless you have a very specific task, the high-level API might solve your SNMP needs.

Synchronous SNMP

This chapter illustrates various uses of the synchronous high-level programming interface to some of Standard SNMP Applications, as defined in RFC3413.

NOTE:

The following examples involve creating Python iterator, the next() call is used to invoke iterator just once.

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,
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    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]))

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-md5-none', MD5 authentication, no privacy
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for all OIDs in IF-MIB
from pysnmp.hlapi import *
for (errorIndication,
     errorStatus,
     errorIndex,
     varBinds) in nextCmd(SnmpEngine(),
                          UsmUserData('usr-md5-none', 'authkey1'),
                          UdpTransportTarget(('demo.snmplabs.com', 161)),
                          ContextData(),
                          ObjectType(ObjectIdentity('IF-MIB'))):
    if errorIndication:
        print(errorIndication)
        break
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
        break
    else:
        for varBind in varBinds:
            print(' = '.join([x.prettyPrint() for x in varBind]))

More examples on Command Generator API usage follow.

Send SNMP GET request using the following options:

  • 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,

Functionally similar to:

$ snmpget -v1 -c public demo.snmplabs.com SNMPv2-MIB::sysDescr.0
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    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]))

Download script.

Send SNMP GET request using the following options:

  • with SNMPv2c, community 'public'
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for two OIDs in string form

Functionally similar to:

$ snmpget -v2c -c public demo.snmplabs.com 1.3.6.1.2.1.1.1.0 1.3.6.1.2.1.1.6.0
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(SnmpEngine(),
           CommunityData('public'),
           UdpTransportTarget(('demo.snmplabs.com', 161)),
           ContextData(),
           ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0')),
           ObjectType(ObjectIdentity('1.3.6.1.2.1.1.6.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]))

Download script.

Send SNMP GET request using the following options:

  • with SNMPv3, user 'usr-md5-des', MD5 authentication, DES encryption
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for IF-MIB::ifInOctets.1 MIB object

Functionally similar to:

$ snmpget -v3 -l authPriv -u usr-md5-des -A authkey1 -X privkey1 demo.snmplabs.com IF-MIB::ifInOctets.1
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(SnmpEngine(),
           UsmUserData('usr-md5-des', 'authkey1', 'privkey1'),
           UdpTransportTarget(('demo.snmplabs.com', 161)),
           ContextData(),
           ObjectType(ObjectIdentity('IF-MIB', 'ifInOctets', 1)))
)
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]))

Download script.

Send SNMP GET request using the following options:

  • with SNMPv3, user 'usr-md5-none', MD5 authentication, no privacy
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for IF-MIB::ifInOctets.1 MIB object

Functionally similar to:

$ snmpget -v3 -l authNoPriv -u usr-md5-none -A authkey1 demo.snmplabs.com IF-MIB::ifInOctets.1
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(SnmpEngine(),
           UsmUserData('usr-md5-none', 'authkey1'),
           UdpTransportTarget(('demo.snmplabs.com', 161)),
           ContextData(),
           ObjectType(ObjectIdentity('IF-MIB', 'ifInOctets', 1)))
)
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]))

Download script.

Send SNMP GET request using the following options:

  • with SNMPv3, user 'usr-none-none', no authentication, no encryption
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for IF-MIB::ifInOctets.1 MIB object

Functionally similar to:

$ snmpget -v3 -l noAuthNoPriv -u usr-none-none demo.snmplabs.com IF-MIB::ifInOctets.1
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(SnmpEngine(),
           UsmUserData('usr-none-none'),
           UdpTransportTarget(('demo.snmplabs.com', 161)),
           ContextData(),
           ObjectType(ObjectIdentity('IF-MIB', 'ifInOctets', 1)))
)
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]))

Download script.

Send SNMP GET request using the following options:

  • with SNMPv3, user 'usr-sha-aes', SHA authentication, AES128 encryption
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for SNMPv2-MIB::sysDescr.0 MIB object

Available authentication protocols:

1.
usmHMACMD5AuthProtocol
2.
usmHMACSHAAuthProtocol
3.
usmHMAC128SHA224AuthProtocol
4.
usmHMAC192SHA256AuthProtocol
5.
usmHMAC256SHA384AuthProtocol
6.
usmHMAC384SHA512AuthProtocol
7.
usmNoAuthProtocol

Available privacy protocols:

1.
usmDESPrivProtocol
2.
usm3DESEDEPrivProtocol
3.
usmAesCfb128Protocol
4.
usmAesCfb192Protocol
5.
usmAesCfb256Protocol
6.
usmNoPrivProtocol

Functionally similar to:

$ snmpget -v3 -l authPriv -u usr-sha-aes -A authkey1 -X privkey1 -a SHA -x AES demo.snmplabs.com SNMPv2-MIB::sysDescr.0
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(SnmpEngine(),
           UsmUserData('usr-sha-aes', 'authkey1', 'privkey1',
                       authProtocol=usmHMACSHAAuthProtocol,
                       privProtocol=usmAesCfb128Protocol),
           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]))

Download script.

See also: library reference.

Modifying variables

Send SNMP SET request using the following options:

  • with SNMPv2c, community 'public'
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • setting SNMPv2-MIB::sysName.0 to new value (type taken from MIB)

Functionally similar to:

$ snmpset -v2c -c public demo.snmplabs.com SNMPv2-MIB::sysDescr.0 = "new system name"
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    setCmd(SnmpEngine(),
           CommunityData('public'),
           UdpTransportTarget(('demo.snmplabs.com', 161)),
           ContextData(),
           ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysORDescr', 1),
                      'new system name'))
)
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]))

Download script.

Send SNMP SET request using the following options:

  • with SNMPv1, community 'public'
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • setting three var-bindings to new values

Please note, that in this example MIB lookup is only used for the second var-bindins. For the rest, value types are inferred from passed objects.

Functionally similar to:

$ snmpset -v1 -c public demo.snmplabs.com 1.3.6.1.2.1.1.9.1.2.1 o 1.3.6.1.4.1.20408.1.1 1.3.6.1.2.1.1.9.1.2.1 = 1.3.6.1.4.1.20408.1.1 1.3.6.1.2.1.1.9.1.3.1 s "new system name"
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    setCmd(SnmpEngine(),
           CommunityData('public', mpModel=0),
           UdpTransportTarget(('demo.snmplabs.com', 161)),
           ContextData(),
           ObjectType(ObjectIdentity('1.3.6.1.2.1.1.9.1.2.1'),
                      ObjectIdentifier('1.3.6.1.4.1.20408.1.1')),
           ObjectType(ObjectIdentity('1.3.6.1.2.1.1.9.1.2.1'),
                      '1.3.6.1.4.1.20408.1.1'),
           ObjectType(ObjectIdentity('1.3.6.1.2.1.1.9.1.3.1'),
                      OctetString('new system name')))
)
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]))

Download script.

See also: library-reference.

Walking operations

Walk whole MIB

Send a series of SNMP GETNEXT requests using the following options:

  • with SNMPv3, user 'usr-md5-none', MD5 authentication, no privacy
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for all OIDs in IF-MIB

Functionally similar to:

$ snmpwalk -v3 -lauthPriv -u usr-md5-none -A authkey1 -X privkey1 demo.snmplabs.com  IF-MIB::
from pysnmp.hlapi import *
for (errorIndication,
     errorStatus,
     errorIndex,
     varBinds) in nextCmd(SnmpEngine(),
                          UsmUserData('usr-md5-none', 'authkey1'),
                          UdpTransportTarget(('demo.snmplabs.com', 161)),
                          ContextData(),
                          ObjectType(ObjectIdentity('IF-MIB'))):
    if errorIndication:
        print(errorIndication)
        break
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
        break
    else:
        for varBind in varBinds:
            print(' = '.join([x.prettyPrint() for x in varBind]))

Download script.

See also: library reference.

Table operations

Send SNMP GET request using the following options:

  • with SNMPv3, user 'usr-none-none', no authentication, no privacy
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for IF-MIB::ifInOctets.1 and IF-MIB::ifOutOctets.1 MIB object
  • perform response OIDs and values resolution at MIB

Functionally similar to:

$ snmpget -v3 -l authPriv -u usr-md5-des -A authkey1 -X privkey1 demo.snmplabs.com IF-MIB::ifInOctets.1 IF-MIB::ifOutOctets.1
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(SnmpEngine(),
           UsmUserData('usr-none-none'),
           UdpTransportTarget(('demo.snmplabs.com', 161)),
           ContextData(),
           ObjectType(ObjectIdentity('IF-MIB', 'ifInOctets', 1)),
           ObjectType(ObjectIdentity('IF-MIB', 'ifOutOctets', 1)))
)
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]))

Download script.

Send SNMP GET request using the following options:

  • with SNMPv3, user 'usr-sha-aes128', SHA auth, AES128 privacy
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for TCP-MIB::tcpConnLocalAddress."0.0.0.0".22."0.0.0.0".0 MIB object

Functionally similar to:

$ snmpget -v3 -l authPriv -u usr-md5-des -A authkey1 -X privkey1 -a SHA -x AES demo.snmplabs.com TCP-MIB::tcpConnLocalAddress."0.0.0.0".22."0.0.0.0".0
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(SnmpEngine(),
           UsmUserData('usr-sha-aes128', 'authkey1', 'privkey1',
                       authProtocol=usmHMACSHAAuthProtocol,
                       privProtocol=usmAesCfb128Protocol),
           UdpTransportTarget(('demo.snmplabs.com', 161)),
           ContextData(),
           ObjectType(ObjectIdentity('TCP-MIB',
                                     'tcpConnLocalAddress',
                                     '0.0.0.0', 22,
                                     '0.0.0.0', 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]))

Download script.

Fetch scalar and table variables

Send a series of SNMP GETBULK requests using the following options:

  • with SNMPv3 with user 'usr-md5-des', MD5 auth and DES privacy protocols
  • over IPv6/UDP
  • to an Agent at [::1]:161
  • with values non-repeaters = 1, max-repetitions = 25
  • for IP-MIB::ipAdEntAddr and all columns of the IF-MIB::ifEntry table
  • stop when response OIDs leave the scopes of the table

Functionally similar to:

$ snmpbulkwalk -v3 -lauthPriv -u usr-md5-des -A authkey1 -X privkey1 -Cn1, -Cr25 demo.snmplabs.com IP-MIB::ipAdEntAddr IP-MIB::ipAddrEntry
from pysnmp.hlapi import *
for (errorIndication,
     errorStatus,
     errorIndex,
     varBinds) in bulkCmd(SnmpEngine(),
                          UsmUserData('usr-md5-des', 'authkey1', 'privkey1'),
                          Udp6TransportTarget(('::1', 161)),
                          ContextData(),
                          1, 25,
                          ObjectType(ObjectIdentity('IP-MIB', 'ipAdEntAddr')),
                          ObjectType(ObjectIdentity('IP-MIB', 'ipAddrEntry')),
                          lexicographicMode=False):
    if errorIndication:
        print(errorIndication)
        break
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
        break
    else:
        for varBind in varBinds:
            print(' = '.join([x.prettyPrint() for x in varBind]))

Download script.

Send a series of SNMP GETNEXT requests using the following options:

  • with SNMPv1, community 'public'
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for some columns of the IF-MIB::ifEntry table
  • stop when response OIDs leave the scopes of initial OIDs

Functionally similar to:

$ snmpwalk -v1 -c public demo.snmplabs.com IF-MIB::ifDescr IF-MIB::ifType IF-MIB::ifMtu IF-MIB::ifSpeed IF-MIB::ifPhysAddress IF-MIB::ifType
from pysnmp.hlapi import *
for (errorIndication,
     errorStatus,
     errorIndex,
     varBinds) in nextCmd(SnmpEngine(),
                          CommunityData('public', mpModel=0),
                          UdpTransportTarget(('demo.snmplabs.com', 161)),
                          ContextData(),
                          ObjectType(ObjectIdentity('IF-MIB', 'ifDescr')),
                          ObjectType(ObjectIdentity('IF-MIB', 'ifType')),
                          ObjectType(ObjectIdentity('IF-MIB', 'ifMtu')),
                          ObjectType(ObjectIdentity('IF-MIB', 'ifSpeed')),
                          ObjectType(ObjectIdentity('IF-MIB', 'ifPhysAddress')),
                          ObjectType(ObjectIdentity('IF-MIB', 'ifType')),
                          lexicographicMode=False):
    if errorIndication:
        print(errorIndication)
        break
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex)-1][0] or '?'))
        break
    else:
        for varBind in varBinds:
            print(' = '.join([x.prettyPrint() for x in varBind]))

Download script.

See also: library reference.

MIB tweaks

Perform SNMP GETNEXT operation with the following options:

  • with SNMPv2c, community 'public'
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for an OID in string form
  • do not resolve response OIDs and values into human-freidly form

False lookupMib keyword arguments could make pysnmp waiving OIDs and values resolution in response variable-bindings, into human friendly form.

Functionally similar to:

$ snmpwalk -v2c -c public -ObentU demo.snmplabs.com 1.3.6.1.2.1
from pysnmp.hlapi import *
for (errorIndication,
     errorStatus,
     errorIndex,
     varBinds) in nextCmd(SnmpEngine(),
                          CommunityData('public'),
                          UdpTransportTarget(('demo.snmplabs.com', 161)),
                          ContextData(),
                          ObjectType(ObjectIdentity('1.3.6.1.2.1.1')),
                          lookupMib=False):
    if errorIndication:
        print(errorIndication)
        break
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
        break
    else:
        for varBind in varBinds:
            print(' = '.join([x.prettyPrint() for x in varBind]))

Download script.

Send a series of SNMP GETNEXT requests using the following options:

  • with SNMPv3 with user 'usr-md5-des', MD5 auth and DES privacy protocols
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for all OIDs starting from 1.3.6
  • preload all Python MIB modules found in search path

Functionally similar to:

$ snmpwalk -v3 -l authPriv -u usr-md5-des -A authkey1 -X privkey1 -m ALL demo.snmplabs.com:161 1.3.6
from pysnmp.hlapi import *
for (errorIndication,
     errorStatus,
     errorIndex,
     varBinds) in nextCmd(SnmpEngine(),
                          UsmUserData('usr-md5-des', 'authkey1', 'privkey1'),
                          UdpTransportTarget(('demo.snmplabs.com', 161)),
                          ContextData(),
                          ObjectType(ObjectIdentity('1.3.6').loadMibs())):
    if errorIndication:
        print(errorIndication)
        break
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
        break
    else:
        for varBind in varBinds:
            print(' = '.join([x.prettyPrint() for x in varBind]))

Download script.

Send SNMP GET request using the following options:

  • with SNMPv2c, community 'public'
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for IF-MIB::ifInOctets.1 MIB object
  • pass non-default ASN.1 MIB source to MIB compiler

Functionally similar to:

$ snmpget -v2c -c public -M /usr/share/snmp demo.snmplabs.com IF-MIB::ifInOctets.1
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(SnmpEngine(),
           CommunityData('public'),
           UdpTransportTarget(('demo.snmplabs.com', 161)),
           ContextData(),
           ObjectType(ObjectIdentity('IF-MIB', 'ifInOctets', 1).addAsn1MibSource('file:///usr/share/snmp',
                                                                                 'http://mibs.snmplabs.com/asn1/@mib@')))
)
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]))

Download script.

Send a series of SNMP GETBULK requests using the following options:

  • with SNMPv3, user 'usr-none-none', no authentication, no privacy
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for all OIDs within TCP-MIB::tcpConnTable column
  • TCP-MIB Python module will be searched by a user-specified filesystem path (/opt/mib/pysnmp) and in Python package (python_packaged_mibs) which should be in sys.path

Functionally similar to:

$ snmpbulkwalk -v3 -lnoAuthNoPriv -u usr-none-none -Cn0 -Cr50 demo.snmplabs.com  TCP-MIB::tcpConnTable
from pysnmp.hlapi import *
for (errorIndication,
     errorStatus,
     errorIndex,
     varBinds) in bulkCmd(SnmpEngine(),
                          UsmUserData('usr-none-none'),
                          UdpTransportTarget(('demo.snmplabs.com', 161)),
                          ContextData(),
                          0, 50,
                          ObjectType(
                              ObjectIdentity('TCP-MIB', 'tcpConnTable').addMibSource('/opt/mibs/pysnmp').addMibSource('python_packaged_mibs')
                          ),
                          lexicographicMode=False):
    if errorIndication:
        print(errorIndication)
        break
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
        break
    else:
        for varBind in varBinds:
            print(' = '.join([x.prettyPrint() for x in varBind]))

Download script.

See also: library reference.

Transport tweaks

Send SNMP GET request using the following options:

  • with SNMPv2c, community 'public'
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for an OID in string form
  • use custom timeout and request retries values

Transport timing settings (maximum number of request retries and individual request timeout in seconds) can be set on a per-target basis as explained by the code that follows.

Keep in mind that while timeout value can be specified in fractions of a second, default pysnmp timer resolution is quite low (about 0.5 sec) so there's no much point in using timeouts which is not a multiple of 0.5 Internal timer can be programmatically adjusted to finer resolution if needed.

If retries value is set to 0, pysnmp will issue a single request. Even if no response arrives, there will be no retry. Likewise, retries=1 means one initial request plus one retry.

Functionally similar to:

$ snmpget -v1 -c public -t 2 -r 0 demo.snmplabs.com 1.3.6.1.2.1.1.1.0
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(SnmpEngine(),
           CommunityData('public'),
           UdpTransportTarget(
               ('demo.snmplabs.com', 161), timeout=2.0, retries=0
           ),
           ContextData(),
           ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.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]))

Download script.

Send SNMP GET request using the following options:

  • with SNMPv3 with user 'usr-md5-des', MD5 auth and DES privacy protocols
  • over IPv6/UDP
  • to an Agent at [::1]:161
  • for three OIDs in string form

Functionally similar to:

$ snmpget -v3 -l authPriv -u usr-md5-des -A authkey1 -X privkey1 udp6:[::1]:161 1.3.6.1.2.1.1.1.0 1.3.6.1.2.1.1.2.0 1.3.6.1.2.1.1.3.0
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(SnmpEngine(),
           UsmUserData('usr-md5-des', 'authkey1', 'privkey1'),
           Udp6TransportTarget(('::1', 161)),
           ContextData(),
           ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0')),
           ObjectType(ObjectIdentity('1.3.6.1.2.1.1.2.0')),
           ObjectType(ObjectIdentity('1.3.6.1.2.1.1.3.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]))

Download script.

See also: library reference.

Advanced Command Generator

Send a series of SNMP GETBULK requests using the following options:

  • 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 OR maxCalls == 10 request-response interactions occur

Functionally similar to:

$ snmpbulkwalk -v3 -lnoAuthNoPriv -u usr-none-none -Cn0 -Cr50 demo.snmplabs.com  SNMPv2-MIB::system
from pysnmp.hlapi import *
for (errorIndication,
     errorStatus,
     errorIndex,
     varBinds) in bulkCmd(SnmpEngine(),
                          UsmUserData('usr-none-none'),
                          UdpTransportTarget(('demo.snmplabs.com', 161)),
                          ContextData(),
                          0, 50,
                          ObjectType(ObjectIdentity('SNMPv2-MIB', 'system')),
                          maxCalls=10):
    if errorIndication:
        print(errorIndication)
        break
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
        break
    else:
        for varBind in varBinds:
            print(' = '.join([x.prettyPrint() for x in varBind]))

Download script.

Send two SNMP GET requests in a row using the following options:

  • with SNMPv3, user 'usr-md5-none', MD5 authentication, no privacy
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for IF-MIB::ifInOctets.1 and IF-MIB::ifOutOctets.1 MIB objects

Use a queue of MIB objects to query.

The next() call is used to forward Python iterator to the position where it could consume input

Functionally similar to:

$ snmpget -v3 -l authNoPriv -u usr-md5-none -A authkey1 demo.snmplabs.com IF-MIB::ifInOctets.1
from pysnmp.hlapi import *
queue = [[ObjectType(ObjectIdentity('IF-MIB', 'ifInOctets', 1))],
         [ObjectType(ObjectIdentity('IF-MIB', 'ifOutOctets', 1))]]
iter = getCmd(SnmpEngine(),
              UsmUserData('usr-md5-none', 'authkey1'),
              UdpTransportTarget(('demo.snmplabs.com', 161)),
              ContextData())
next(iter)
while queue:
    errorIndication, errorStatus, errorIndex, varBinds = iter.send(queue.pop())
    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]))

Download script.

Send SNMP GET request using the following options:

  • with SNMPv3 with user 'usr-md5-des', MD5 auth and DES privacy protocols
  • use remote SNMP Engine ID 0x80004fb805636c6f75644dab22cc (USM autodiscovery will run)
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • setting SNMPv2-MIB::sysName.0 to new value (type coerced from MIB)

Functionally similar to:

$ snmpset -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 -E 80004fb805636c6f75644dab22cc demo.snmplabs.com SNMPv2-MIB::sysORDescr.1 = "new system name"
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    setCmd(SnmpEngine(),
           UsmUserData('usr-md5-des', 'authkey1', 'privkey1',
                       securityEngineId=OctetString(hexValue='80004fb805636c6f75644dab22cc')),
           UdpTransportTarget(('demo.snmplabs.com', 161)),
           ContextData(),
           ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysName', 0), 'new system name'))
)
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]))

Download script.

Custom ContextEngineId and ContextName

Send SNMP SET request using the following options:

  • with SNMPv3 with user 'usr-md5-none', MD5 auth and no privacy protocols
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • addressing particular set of Managed Objects at remote SNMP Engine by: * contextEngineId 0x80004fb805636c6f75644dab22cc and * contextName 'a172334d7d97871b72241397f713fa12'
  • setting SNMPv2-MIB::sysName.0 to new value (type taken from MIB)

Functionally similar to:

$ snmpset -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 -E 80004fb805636c6f75644dab22cc -n a172334d7d97871b72241397f713fa12 demo.snmplabs.com SNMPv2-MIB::sysORDescr.1 = "new system name"
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    setCmd(SnmpEngine(),
           UsmUserData('usr-md5-none', 'authkey1'),
           UdpTransportTarget(('demo.snmplabs.com', 161)),
           ContextData(contextEngineId=OctetString(hexValue='80004fb805636c6f75644dab22cc'),
                       contextName='da761cfc8c94d3aceef4f60f049105ba'),
           ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysORDescr', 1), 'new system name'))
)
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]))

Download script.

Send SNMP GET request using the following options:

  • with SNMPv3, user 'usr-md5-none', securityName 'myuser' MD5 authentication, no privacy
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for an OID in text form

The securityName parameter can be thought as an alias to userName and allows you to address a USM Table row just as userName does. However securityName can be made human-readable, also it is not an index in usmUserTable, thus duplicate securityName parameters are possible.

from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(SnmpEngine(),
           UsmUserData('usr-md5-none', 'authkey1', securityName='myuser'),
           UdpTransportTarget(('demo.snmplabs.com', 161)),
           ContextData(),
           ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.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]))

Download script.

Send SNMP GET request using the following scenario and options:

  • try to communicate with a SNMPv3 Engine using:
  • a non-existing user
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • if remote SNMP Engine ID is discovered, send SNMP GET request:
  • to the same SNMP Engine ID
  • for an OID in text form
from pysnmp.hlapi import *
snmpEngine = SnmpEngine()
transportTarget = UdpTransportTarget(('demo.snmplabs.com', 161))
#
# To discover remote SNMP EngineID we will tap on SNMP engine inner workings
# by setting up execution point observer setup on INTERNAL class PDU processing
#
observerContext = {}
# Register a callback to be invoked at specified execution point of 
# SNMP Engine and passed local variables at execution point's local scope
snmpEngine.observer.registerObserver(
    lambda e, p, v, c: c.update(securityEngineId=v['securityEngineId']),
    'rfc3412.prepareDataElements:internal',
    cbCtx=observerContext
)
# Send probe SNMP request with invalid credentials
authData = UsmUserData('non-existing-user')
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(snmpEngine, authData, transportTarget, ContextData(),
           ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)))
)
# See if our SNMP engine received REPORT PDU containing securityEngineId
if 'securityEngineId' not in observerContext:
    print("Can't discover peer EngineID, errorIndication: %s" % errorIndication)
    raise Exception()
securityEngineId = observerContext.pop('securityEngineId')
print('Remote securityEngineId = %s' % securityEngineId.prettyPrint())
#
# Query remote SNMP Engine using usmUserTable entry configured for it
#
authData = UsmUserData('usr-md5-none', 'authkey1',
                       securityEngineId=securityEngineId)
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(snmpEngine,
           authData,
           transportTarget,
           ContextData(),
           ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0')))
)
if errorIndication:
    print(errorIndication)
elif errorStatus:
    print('%s at %s' % (errorStatus.prettyPrint(),
                        errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
else:
    for name, val in varBinds:
        print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))

Download script.

Send SNMP GET request using the following options:

  • with SNMPv3, user 'usr-md5-des', MD5 authentication, DES encryption
  • use master auth and privacy keys instead of pass-phrase
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for SNMPv2-MIB::sysDescr.0 MIB object instance

Functionally similar to:

$ snmpget -v3 -l authPriv -u usr-md5-des -3m 0x1dcf59e86553b3afa5d32fd5d61bf0cf -3M 0xec5ab55e93e1d85cb6846d0f23e845e0 demo.snmplabs.com SNMPv2-MIB::sysDescr.0
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(SnmpEngine(),
           UsmUserData('usr-md5-des',
                       authKey=OctetString(
                           hexValue='1dcf59e86553b3afa5d32fd5d61bf0cf'),
                       privKey=OctetString(
                           hexValue='ec5ab55e93e1d85cb6846d0f23e845e0'),
                       authKeyType=usmKeyTypeMaster,
                       privKeyType=usmKeyTypeMaster),
           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]))

Download script.

Send SNMP GET request using the following options:

  • with SNMPv3, user 'usr-md5-des', MD5 authentication, DES encryption
  • use localized auth and privacy keys instead of pass-phrase or master keys
  • configure authoritative SNMP engine ID (0x0000000000 can be used as well)
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for SNMPv2-MIB::sysDescr.0 MIB object instance

Functionally similar to:

$ snmpget -v3 -l authPriv -u usr-md5-des -e 0x80004fb805636c6f75644dab22cc -3k 0x6b99c475259ef7976cf8d028a3381eeb -3K 0x92b5ef98f0a216885e73944e58c07345 demo.snmplabs.com SNMPv2-MIB::sysDescr.0
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(SnmpEngine(),
           UsmUserData('usr-md5-des',
                       authKey=OctetString(
                           hexValue='6b99c475259ef7976cf8d028a3381eeb'),
                       privKey=OctetString(
                           hexValue='92b5ef98f0a216885e73944e58c07345'),
                       authKeyType=usmKeyTypeLocalized,
                       privKeyType=usmKeyTypeLocalized,
                       securityEngineId=OctetString(
                           hexValue='80004fb805636c6f75644dab22cc')),
           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]))

Download script.

Send a bunch of SNMP GET requests simultaneously using the following options:

  • process 5 GET requests in 3 parallel threads
  • with SNMPv1, community 'public' and with SNMPv2c, community 'public' and with SNMPv3, user 'usr-md5-des', MD5 auth and DES privacy
  • over IPv4/UDP and over IPv6/UDP
  • to an Agent at demo.snmplabs.com:161 and to an Agent at [::1]:161
  • for instances of SNMPv2-MIB::sysDescr.0 and SNMPv2-MIB::sysLocation.0 MIB objects
from sys import version_info
from threading import Thread
from pysnmp.hlapi import *
if version_info[0] == 2:
    from Queue import Queue
else:
    from queue import Queue
# List of targets in the following format:
# ( ( authData, transportTarget, varNames ), ... )
targets = (
    # 1-st target (SNMPv1 over IPv4/UDP)
    (CommunityData('public', mpModel=0),
     UdpTransportTarget(('demo.snmplabs.com', 161)),
     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0)))),
    # 2-nd target (SNMPv2c over IPv4/UDP)
    (CommunityData('public'),
     UdpTransportTarget(('demo.snmplabs.com', 161)),
     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0)))),
    # 3-nd target (SNMPv2c over IPv4/UDP) - same community and 
    # different transport address.
    (CommunityData('public'),
     UdpTransportTarget(('localhost', 161)),
     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysContact', 0)),
      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysName', 0)))),
    # 4-nd target (SNMPv3 over IPv4/UDP)
    (UsmUserData('usr-md5-des', 'authkey1', 'privkey1'),
     UdpTransportTarget(('demo.snmplabs.com', 161)),
     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0)))),
    # 5-th target (SNMPv3 over IPv6/UDP)
    (UsmUserData('usr-md5-none', 'authkey1'),
     Udp6TransportTarget(('::1', 161)),
     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0)))),
    # N-th target
    # ...
)
class Worker(Thread):
    def __init__(self, requests, responses):
        Thread.__init__(self)
        self.snmpEngine = SnmpEngine()
        self.requests = requests
        self.responses = responses
        self.setDaemon(True)
        self.start()
    def run(self):
        while True:
            authData, transportTarget, varBinds = self.requests.get()
            self.responses.append(
                next(getCmd(self.snmpEngine,
                     authData, transportTarget, ContextData(), *varBinds))
            )
            if hasattr(self.requests, 'task_done'):  # 2.5+
                self.requests.task_done()
class ThreadPool(object):
    def __init__(self, num_threads):
        self.requests = Queue(num_threads)
        self.responses = []
        for _ in range(num_threads):
            Worker(self.requests, self.responses)
    def addRequest(self, authData, transportTarget, varBinds):
        self.requests.put((authData, transportTarget, varBinds))
    def getResponses(self):
        return self.responses
    def waitCompletion(self):
        if hasattr(self.requests, 'join'):
            self.requests.join()  # 2.5+
        else:
            from time import sleep
            # this is a lame substitute for missing .join()
            # adding an explicit synchronization might be a better solution
            while not self.requests.empty():
                sleep(1)
pool = ThreadPool(3)
# Submit GET requests
for authData, transportTarget, varBinds in targets:
    pool.addRequest(authData, transportTarget, varBinds)
# Wait for responses or errors
pool.waitCompletion()
# Walk through responses
for errorIndication, errorStatus, errorIndex, varBinds in pool.getResponses():
    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]))

Download script.

See also: library reference.

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 pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    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)

More examples on Notification Originator API usage follow.

Common notifications

SNMPv2c TRAP via NOTIFICATION-TYPE

Initialize TRAP message contents from variables specified in NOTIFICATION-TYPE SMI macro.

  • SNMPv2c
  • with community name 'public'
  • over IPv4/UDP
  • send TRAP notification
  • with TRAP ID 'linkUp' specified as a MIB symbol
  • include values for managed objects implicitly added to notification (via NOTIFICATION-TYPE->OBJECTS)

Functionally similar to:

$ snmptrap -v2c -c public demo.snmplabs.com 0 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.2.2.1.1.123 i 123 1.3.6.1.2.1.2.2.1.7.123 i 1 1.3.6.1.2.1.2.2.1.8.123 i 1
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    sendNotification(
        SnmpEngine(),
        CommunityData('public'),
        UdpTransportTarget(('demo.snmplabs.com', 162)),
        ContextData(),
        'trap',
        NotificationType(
            ObjectIdentity('IF-MIB', 'linkUp'),
                           instanceIndex=(123,),
                           objects={('IF-MIB', 'ifIndex'): 123,
                                    ('IF-MIB', 'ifAdminStatus'): 'up',
                                    ('IF-MIB', 'ifOperStatus'): 'up'}
        )
    )
)
if errorIndication:
    print(errorIndication)

Download script.

Send SNMP INFORM notification using the following options:

  • SNMPv3
  • with user 'usr-md5-des', auth: MD5, priv DES
  • over IPv4/UDP
  • send INFORM notification
  • with TRAP ID 'warmStart' specified as a string OID
  • include managed object information 1.3.6.1.2.1.1.5.0 = 'system name'

Functionally similar to:

$ snmpinform -v3 -l authPriv -u usr-md5-des -A authkey1 -X privkey1 demo.snmplabs.com 12345 1.3.6.1.4.1.20408.4.1.1.2 1.3.6.1.2.1.1.1.0 s "my system"
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    sendNotification(
        SnmpEngine(),
        UsmUserData('usr-md5-des', 'authkey1', 'privkey1'),
        UdpTransportTarget(('demo.snmplabs.com', 162)),
        ContextData(),
        'inform',
        NotificationType(
            ObjectIdentity('1.3.6.1.6.3.1.1.5.2')
        ).addVarBinds(
             ObjectType(ObjectIdentity('1.3.6.1.2.1.1.5.0'), 'system name')
        )
    )
)
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]))

Download script.

SNMPv1 TRAP with defaults

Send SNMPv1 TRAP through unified SNMPv3 message processing framework using the following options:

  • 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'

Functionally similar to:

$ snmptrap -v1 -c public demo.snmplabs.com 1.3.6.1.4.1.20408.4.1.1.2 0.0.0.0 1 0 0 1.3.6.1.2.1.1.1.0 s "my system"
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    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)

Download script.

Send SNMP notification using the following options:

  • SNMPv3
  • with authoritative snmpEngineId = 0x8000000001020304 (USM must be configured at the Receiver accordingly)
  • with user 'usr-sha-aes128', auth: SHA, priv: AES128
  • over IPv4/UDP
  • send TRAP notification
  • with TRAP ID 'authenticationFailure' specified as a MIB symbol
  • do not include any additional managed object information

SNMPv3 TRAPs requires pre-sharing the Notification Originator's value of SnmpEngineId with Notification Receiver. To facilitate that we will use static (e.g. not autogenerated) version of snmpEngineId.

Functionally similar to:

$ snmptrap -v3 -e 8000000001020304 -l authPriv -u usr-sha-aes -A authkey1 -X privkey1 -a SHA -x AES demo.snmplabs.com 12345 1.3.6.1.4.1.20408.4.1.1.2 1.3.6.1.2.1.1.1.0 s "my system"
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    sendNotification(
        SnmpEngine(OctetString(hexValue='8000000001020304')),
        UsmUserData('usr-sha-aes128', 'authkey1', 'privkey1',
                    authProtocol=usmHMACSHAAuthProtocol,
                    privProtocol=usmAesCfb128Protocol),
        UdpTransportTarget(('demo.snmplabs.com', 162)),
        ContextData(),
        'trap',
        NotificationType(ObjectIdentity('SNMPv2-MIB', 'authenticationFailure'))
    )
)
if errorIndication:
    print(errorIndication)

Download script.

See also: library reference.

Send SNMPv1 TRAP through unified SNMPv3 message processing framework.

Original v1 TRAP fields are mapped into dedicated variable-bindings, (see RFC2576) for details.

  • SNMPv1
  • with community name 'public'
  • over IPv4/UDP
  • send TRAP notification
  • with Generic Trap #6 (enterpriseSpecific) and Specific Trap 432
  • overriding Uptime value with 12345
  • overriding Agent Address with '127.0.0.1'
  • overriding Enterprise OID with 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'

Functionally similar to:

$ snmptrap -v1 -c public demo.snmplabs.com 1.3.6.1.4.1.20408.4.1.1.2 127.0.0.1 6 432 12345 1.3.6.1.2.1.1.1.0 s "my system"
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    sendNotification(
        SnmpEngine(),
        CommunityData('public', mpModel=0),
        UdpTransportTarget(('demo.snmplabs.com', 162)),
        ContextData(),
        'trap',
        NotificationType(
            ObjectIdentity('1.3.6.1.4.1.20408.4.1.1.2.0.432'),
        ).addVarBinds(
            ('1.3.6.1.2.1.1.3.0', 12345),
            ('1.3.6.1.6.3.18.1.3.0', '127.0.0.1'),
            ('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)

Download script.

SNMPv1 TRAP with defaults

Send SNMPv1 TRAP through unified SNMPv3 message processing framework using the following options:

  • 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'

Functionally similar to:

$ snmptrap -v1 -c public demo.snmplabs.com 1.3.6.1.4.1.20408.4.1.1.2 0.0.0.0 1 0 0 1.3.6.1.2.1.1.1.0 s "my system"
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    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)

Download script.

See also: library reference.

Evaluating NOTIFICATION-TYPE

SNMPv2c TRAP via NOTIFICATION-TYPE

Initialize TRAP message contents from variables specified in NOTIFICATION-TYPE SMI macro.

  • SNMPv2c
  • with community name 'public'
  • over IPv4/UDP
  • send TRAP notification
  • with TRAP ID 'linkUp' specified as a MIB symbol
  • include values for managed objects implicitly added to notification (via NOTIFICATION-TYPE->OBJECTS)

Functionally similar to:

$ snmptrap -v2c -c public demo.snmplabs.com 0 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.2.2.1.1.123 i 123 1.3.6.1.2.1.2.2.1.7.123 i 1 1.3.6.1.2.1.2.2.1.8.123 i 1
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    sendNotification(
        SnmpEngine(),
        CommunityData('public'),
        UdpTransportTarget(('demo.snmplabs.com', 162)),
        ContextData(),
        'trap',
        NotificationType(
            ObjectIdentity('IF-MIB', 'linkUp'),
                           instanceIndex=(123,),
                           objects={('IF-MIB', 'ifIndex'): 123,
                                    ('IF-MIB', 'ifAdminStatus'): 'up',
                                    ('IF-MIB', 'ifOperStatus'): 'up'}
        )
    )
)
if errorIndication:
    print(errorIndication)

Download script.

Send SNMP notification using the following options:

  • SNMPv2c
  • with community name 'public'
  • over IPv4/UDP
  • send INFORM notification
  • with TRAP ID 'coldStart' specified as a MIB symbol
  • include managed object information specified as a MIB symbol

Functionally similar to:

$ snmpinform -v2c -c public demo.snmplabs.com 12345 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.1.0 s "my system"
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    sendNotification(
        SnmpEngine(),
        CommunityData('public'),
        UdpTransportTarget(('demo.snmplabs.com', 162)),
        ContextData(),
        'inform',
        NotificationType(
            ObjectIdentity('SNMPv2-MIB', 'coldStart')
        ).addVarBinds(
            ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysName', 0), 'my system')
        )
    )
)
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]))

Download script.

See also: library reference.

Advanced Notification Originator

Send SNMP notification using the following options:

  • SNMPv3
  • with user 'usr-md5-none', MD5 auth, no priv
  • send INFORM notification
  • in behalf of contextEngineId = SnmpEngineId, contextName 'my-context'
  • over IPv4/UDP
  • with TRAP ID 'warmStart' specified as a string OID

Sending SNMPv3 Notification in behalf of non-default ContextName requires having a collection of Managed Objects registered under the ContextName being used.

Functionally similar to:

$ snmpinform -v3 -l authNoPriv -u usr-md5-none -A authkey1 -n my-context demo.snmplabs.com 12345 0.3.6.1.6.3.1.1.5.2
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    sendNotification(SnmpEngine(),
                     UsmUserData('usr-md5-none', 'authkey1'),
                     UdpTransportTarget(('demo.snmplabs.com', 162)),
                     ContextData(contextName='my-context'),
                     'inform',
                     NotificationType(ObjectIdentity('1.3.6.1.6.3.1.1.5.2')))
)
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]))

Download script.

Send SNMP notification using the following options:

  • SNMPv3
  • with user 'usr-md5-none', MD5 auth, no priv
  • send INFORM notification
  • in behalf of contextEngineId 0x8000000004030201, contextName ''
  • over IPv4/UDP
  • with TRAP ID 'warmStart' specified as a string OID

Sending SNMPv3 Notification in behalf of non-default ContextEngineId requires having a collection of Managed Objects registered under the ContextEngineId being used.

Functionally similar to:

$ snmpinform -v3 -l authNoPriv -u usr-md5-none -A authkey1 -E 0x8000000004030201 demo.snmplabs.com 12345 1.3.6.1.6.3.1.1.5.2
from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    sendNotification(SnmpEngine(),
                     UsmUserData('usr-md5-none', 'authkey1'),
                     UdpTransportTarget(('demo.snmplabs.com', 162)),
                     ContextData(OctetString(hexValue='8000000004030201')),
                     'inform',
                     NotificationType(ObjectIdentity('1.3.6.1.6.3.1.1.5.2')))
)
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]))

Download script.

See also: library reference.

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

Asynchronous: asyncore

With asyncore API your scripts get CPU time on socket events being watched for by select dispatcher. Your code live mostly in isolated functions (or any callable objects).

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,
from pysnmp.hlapi.asyncore import *
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    if errorIndication:
        print(errorIndication)
        return
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBindTable[-1][int(errorIndex) - 1][0] or '?'))
        return
    else:
        for varBind in varBinds:
            print(' = '.join([x.prettyPrint() for x in varBind]))
snmpEngine = SnmpEngine()
getCmd(snmpEngine,
       CommunityData('public'),
       UdpTransportTarget(('demo.snmplabs.com', 161)),
       ContextData(),
       ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
       cbFun=cbFun)
snmpEngine.transportDispatcher.runDispatcher()

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-md5-none', MD5 authentication, no privacy
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for all OIDs in IF-MIB
from pysnmp.hlapi.asyncore import *
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBindTable, cbCtx):
    if errorIndication:
        print(errorIndication)
        return
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBindTable[-1][int(errorIndex) - 1][0] or '?'))
        return
    else:
        for varBindRow in varBindTable:
            for varBind in varBindRow:
                print(' = '.join([x.prettyPrint() for x in varBind]))
    return True  # request lower layers to do GETNEXT and call us back
snmpEngine = SnmpEngine()
nextCmd(snmpEngine,
        UsmUserData('usr-md5-none', 'authkey1'),
        UdpTransportTarget(('demo.snmplabs.com', 161)),
        ContextData(),
        ObjectType(ObjectIdentity('SNMPv2-MIB', 'system')),
        ObjectType(ObjectIdentity('IF-MIB', 'ifTable')),
        cbFun=cbFun)
snmpEngine.transportDispatcher.runDispatcher()

More examples on Command Generator API usage follow.

Various SNMP versions

Send SNMP GET request using the following options:

  • 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,

Functionally similar to:

$ snmpget -v2c -c public demo.snmplabs.com SNMPv2-MIB::sysDescr.0
from pysnmp.hlapi.asyncore import *
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    if errorIndication:
        print(errorIndication)
        return
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBindTable[-1][int(errorIndex) - 1][0] or '?'))
        return
    else:
        for varBind in varBinds:
            print(' = '.join([x.prettyPrint() for x in varBind]))
snmpEngine = SnmpEngine()
getCmd(snmpEngine,
       CommunityData('public'),
       UdpTransportTarget(('demo.snmplabs.com', 161)),
       ContextData(),
       ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
       cbFun=cbFun)
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Walk whole MIB

Send a series of SNMP GETNEXT requests using the following options:

  • with SNMPv3, user 'usr-md5-none', MD5 authentication, no privacy
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for all OIDs in IF-MIB

Functionally similar to:

$ snmpwalk -v3 -lauthNoPriv -u usr-md5-none -A authkey1 -X privkey1 
demo.snmplabs.com  IF-MIB::

from pysnmp.hlapi.asyncore import *
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBindTable, cbCtx):
    if errorIndication:
        print(errorIndication)
        return
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBindTable[-1][int(errorIndex) - 1][0] or '?'))
        return
    else:
        for varBindRow in varBindTable:
            for varBind in varBindRow:
                print(' = '.join([x.prettyPrint() for x in varBind]))
    return True  # request lower layers to do GETNEXT and call us back
snmpEngine = SnmpEngine()
nextCmd(snmpEngine,
        UsmUserData('usr-md5-none', 'authkey1'),
        UdpTransportTarget(('demo.snmplabs.com', 161)),
        ContextData(),
        ObjectType(ObjectIdentity('SNMPv2-MIB', 'system')),
        ObjectType(ObjectIdentity('IF-MIB', 'ifTable')),
        cbFun=cbFun)
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library reference.

Walking operations

Walk whole MIB

Send a series of SNMP GETNEXT requests using the following options:

  • with SNMPv3, user 'usr-md5-none', MD5 authentication, no privacy
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for all OIDs in IF-MIB

Functionally similar to:

$ snmpwalk -v3 -lauthNoPriv -u usr-md5-none -A authkey1 -X privkey1 
demo.snmplabs.com  IF-MIB::

from pysnmp.hlapi.asyncore import *
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBindTable, cbCtx):
    if errorIndication:
        print(errorIndication)
        return
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBindTable[-1][int(errorIndex) - 1][0] or '?'))
        return
    else:
        for varBindRow in varBindTable:
            for varBind in varBindRow:
                print(' = '.join([x.prettyPrint() for x in varBind]))
    return True  # request lower layers to do GETNEXT and call us back
snmpEngine = SnmpEngine()
nextCmd(snmpEngine,
        UsmUserData('usr-md5-none', 'authkey1'),
        UdpTransportTarget(('demo.snmplabs.com', 161)),
        ContextData(),
        ObjectType(ObjectIdentity('SNMPv2-MIB', 'system')),
        ObjectType(ObjectIdentity('IF-MIB', 'ifTable')),
        cbFun=cbFun)
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library reference.

Advanced Command Generator

Walk multiple Agents at once

Iterate over MIBs of multiple SNMP Agents asynchronously using the following options:

  • with SNMPv1, community 'public' and with SNMPv2c, community 'public' and with SNMPv3, user 'usr-md5-des', MD5 auth and DES privacy
  • over IPv4/UDP and over IPv6/UDP
  • to an Agent at demo.snmplabs.com:161 and to an Agent at [::1]:161
  • pull variables till EOM
from pysnmp.hlapi.asyncore import *
# List of targets in the followin format:
# ( ( authData, transportTarget, varNames ), ... )
targets = (
    # 1-st target (SNMPv1 over IPv4/UDP)
    (CommunityData('public', mpModel=0),
     UdpTransportTarget(('demo.snmplabs.com', 161)),
     (ObjectType(ObjectIdentity('1.3.6.1.2.1')),
      ObjectType(ObjectIdentity('1.3.6.1.3.1')))),
    # 2-nd target (SNMPv2c over IPv4/UDP)
    (CommunityData('public'),
     UdpTransportTarget(('demo.snmplabs.com', 161)),
     (ObjectType(ObjectIdentity('1.3.6.1.4.1')),)),
    # 3-nd target (SNMPv3 over IPv4/UDP)
    (UsmUserData('usr-md5-des', 'authkey1', 'privkey1'),
     UdpTransportTarget(('demo.snmplabs.com', 161)),
     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'system')),)),
    # 4-th target (SNMPv3 over IPv6/UDP)
    (UsmUserData('usr-md5-none', 'authkey1'),
     Udp6TransportTarget(('::1', 161)),
     (ObjectType(ObjectIdentity('IF-MIB', 'ifTable')),))
    # N-th target
    # ...
)
# Wait for responses or errors, submit GETNEXT requests for further OIDs
# noinspection PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBindTable, cbCtx):
    (authData, transportTarget) = cbCtx
    print('%s via %s' % (authData, transportTarget))
    if errorIndication:
        print(errorIndication)
        return
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBindTable[-1][int(errorIndex) - 1][0] or '?'))
        return
    else:
        for varBindRow in varBindTable:
            for varBind in varBindRow:
                print(' = '.join([x.prettyPrint() for x in varBind]))
        return True  # continue table retrieval
snmpEngine = SnmpEngine()
# Submit initial GETNEXT requests and wait for responses
for authData, transportTarget, varBinds in targets:
    nextCmd(snmpEngine, authData, transportTarget, ContextData(),
            *varBinds, **dict(cbFun=cbFun, cbCtx=(authData, transportTarget)))
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send multiple SNMP GET requests to multiple peers using multiple independend SNMP engines. Deal with peers asynchronously. SNMP options are:

  • with SNMPv1, community 'public' and with SNMPv2c, community 'public' and with SNMPv3, user 'usr-md5-des', MD5 auth and DES privacy
  • over IPv4/UDP and over IPv6/UDP
  • to an Agent at demo.snmplabs.com:161 and to an Agent at [::1]:161
  • for instances of SNMPv2-MIB::sysDescr.0 and SNMPv2-MIB::sysLocation.0 MIB objects

Within this script we have a single asynchronous TransportDispatcher and a single UDP-based transport serving two independent SNMP engines. We use a single instance of AsyncCommandGenerator with each of SNMP Engines to comunicate GET command request to remote systems.

When we receive a [response] message from remote system we use a custom message router to choose what of the two SNMP engines data packet should be handed over. The selection criteria we employ here is based on peer's UDP port number. Other selection criterias are also possible.

from pysnmp.hlapi.asyncore import *
from pysnmp.carrier.asyncore.dispatch import AsyncoreDispatcher
# List of targets in the following format:
# ( ( authData, transportTarget, varNames ), ... )
targets = (
    # 1-st target (SNMPv1 over IPv4/UDP)
    (CommunityData('public', mpModel=0),
     UdpTransportTarget(('demo.snmplabs.com', 161)),
     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0)))),
    # 2-nd target (SNMPv2c over IPv4/UDP)
    (CommunityData('public'),
     UdpTransportTarget(('demo.snmplabs.com', 1161)),
     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0)))),
    # 3-nd target (SNMPv3 over IPv4/UDP)
    (UsmUserData('usr-md5-des', 'authkey1', 'privkey1'),
     UdpTransportTarget(('demo.snmplabs.com', 2161)),
     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0))))
    # N-th target
    # ...
)
# Wait for responses or errors
# noinspection PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    (snmpEngine, authData, transportTarget) = cbCtx
    print('snmpEngine %s: %s via %s' % (snmpEngine.snmpEngineID.prettyPrint(), authData, transportTarget))
    if errorIndication:
        print(errorIndication)
        return True
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
        return True
    else:
        for varBind in varBinds:
            print(' = '.join([x.prettyPrint() for x in varBind]))
# Instantiate the single transport dispatcher object
transportDispatcher = AsyncoreDispatcher()
# Setup a custom data routing function to select snmpEngine by transportDomain
transportDispatcher.registerRoutingCbFun(
    lambda td, ta, d: ta[1] % 3 and 'A' or 'B'
)
snmpEngineA = SnmpEngine()
snmpEngineA.registerTransportDispatcher(transportDispatcher, 'A')
snmpEngineB = SnmpEngine()
snmpEngineB.registerTransportDispatcher(transportDispatcher, 'B')
for authData, transportTarget, varBinds in targets:
    snmpEngine = transportTarget.getTransportInfo()[1][1] % 3 and \
                 snmpEngineA or snmpEngineB
    getCmd(snmpEngine, authData, transportTarget, ContextData(), *varBinds,
           **dict(cbFun=cbFun, cbCtx=(snmpEngine, authData, transportTarget)))
transportDispatcher.runDispatcher()

Download script.

Multiple concurrent queries

Send a bunch of different SNMP GET requests to different peers all at once, wait for responses asynchronously:

  • with SNMPv1, community 'public' and with SNMPv2c, community 'public' and with SNMPv3, user 'usr-md5-des', MD5 auth and DES privacy
  • over IPv4/UDP and over IPv6/UDP
  • to an Agent at demo.snmplabs.com:161 and to an Agent at [::1]:161
  • for instances of SNMPv2-MIB::sysDescr.0 and SNMPv2-MIB::sysLocation.0 MIB objects
from pysnmp.hlapi.asyncore import *
# List of targets in the followin format:
# ( ( authData, transportTarget, varNames ), ... )
targets = (
    # 1-st target (SNMPv1 over IPv4/UDP)
    (CommunityData('public', mpModel=0),
     UdpTransportTarget(('demo.snmplabs.com', 161)),
     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0)))),
    # 2-nd target (SNMPv2c over IPv4/UDP)
    (CommunityData('public'),
     UdpTransportTarget(('demo.snmplabs.com', 161)),
     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0)))),
    # 3-nd target (SNMPv2c over IPv4/UDP) - same community and 
    # different transport address.
    (CommunityData('public'),
     UdpTransportTarget(('localhost', 161)),
     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysContact', 0)),
      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysName', 0)))),
    # 4-nd target (SNMPv3 over IPv4/UDP)
    (UsmUserData('usr-md5-des', 'authkey1', 'privkey1'),
     UdpTransportTarget(('demo.snmplabs.com', 161)),
     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0)))),
    # 5-th target (SNMPv3 over IPv6/UDP)
    (UsmUserData('usr-md5-none', 'authkey1'),
     Udp6TransportTarget(('::1', 161)),
     (ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)),
      ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0)))),
    # N-th target
    # ...
)
# Wait for responses or errors
# noinspection PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    authData, transportTarget = cbCtx
    print('%s via %s' % (authData, transportTarget))
    if errorIndication:
        print(errorIndication)
        return True
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
        return True
    else:
        for varBind in varBinds:
            print(' = '.join([x.prettyPrint() for x in varBind]))
snmpEngine = SnmpEngine()
# Submit GET requests
for authData, transportTarget, varNames in targets:
    getCmd(snmpEngine, authData, transportTarget, ContextData(), *varNames,
           **dict(cbFun=cbFun, cbCtx=(authData, transportTarget)))
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library reference.

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 pysnmp.hlapi.asyncore import *
snmpEngine = SnmpEngine()
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'))
    )
)
snmpEngine.transportDispatcher.runDispatcher()

More examples on Notification Originator API usage follow.

Common notifications

SNMPv1 TRAP with defaults

Send SNMPv1 TRAP through unified SNMPv3 message processing framework using the following options:

  • 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'

Functionally similar to:

$ snmptrap -v1 -c public demo.snmplabs.com 1.3.6.1.4.1.20408.4.1.1.2 0.0.0.0 1 0 0 1.3.6.1.2.1.1.1.0 s "my system"
from pysnmp.hlapi.asyncore import *
snmpEngine = SnmpEngine()
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'))
    )
)
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library reference.

Advanced Notification Originator

Multiple concurrent queries

Send a bunch of different SNMP Notifications to different peers all at once, wait for responses asynchronously:

  • SNMPv1 and SNMPv2c
  • with community name 'public'
  • over IPv4/UDP
  • send TRAP notification
  • to multiple Managers
  • with TRAP ID 'coldStart' specified as a MIB symbol
  • include managed object information specified as var-bind objects pair
$ snmptrap -v1 -c public demo.snmplabs.com 1.3.6.1.4.1.20408.4.1.1.2 demo.snmplabs.com 6 432 12345 1.3.6.1.2.1.1.1.0 s "my system"
$ snmptrap -v2c -c public demo.snmplabs.com 123 1.3.6.1.6.3.1.1.5.1
from pysnmp.hlapi.asyncore import *
# List of targets in the followin format:
# ( ( authData, transportTarget ), ... )
targets = (
    # 1-st target (SNMPv1 over IPv4/UDP)
    (CommunityData('public', mpModel=0),
     UdpTransportTarget(('demo.snmplabs.com', 162)),
     ContextData()),
    # 2-nd target (SNMPv2c over IPv4/UDP)
    (CommunityData('public'),
     UdpTransportTarget(('demo.snmplabs.com', 162)),
     ContextData()),
)
snmpEngine = SnmpEngine()
for authData, transportTarget, contextData in targets:
    sendNotification(
        snmpEngine,
        authData,
        transportTarget,
        contextData,
        'trap',  # NotifyType
        NotificationType(
            ObjectIdentity('SNMPv2-MIB', 'coldStart')
        ).addVarBinds(
            (ObjectIdentifier('1.3.6.1.2.1.1.1.0'),
             OctetString('my name'))
        )
    )
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Multiple concurrent notifications

Send multiple SNMP notifications at once using the following options:

  • SNMPv2c and SNMPv3
  • with community name 'public' or USM username usr-md5-des
  • over IPv4/UDP
  • send INFORM notification
  • to multiple Managers
  • with TRAP ID 'coldStart' specified as a MIB symbol
  • include managed object information specified as var-bind objects pair
$ snmpinform -v2c -c public demo.snmplabs.com 123 1.3.6.1.6.3.1.1.5.1
$ snmpinform -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 demo.snmplabs.com 123 1.3.6.1.6.3.1.1.5.1
from pysnmp.hlapi.asyncore import *
# List of targets in the followin format:
# ( ( authData, transportTarget ), ... )
targets = (
    # 1-st target (SNMPv2c over IPv4/UDP)
    (CommunityData('public'),
     UdpTransportTarget(('demo.snmplabs.com', 162)),
     ContextData()),
    # 2-nd target (SNMPv3 over IPv4/UDP)
    (UsmUserData('usr-md5-des', 'authkey1', 'privkey1'),
     UdpTransportTarget(('demo.snmplabs.com', 162)),
     ContextData()),
)
# noinspection PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    if errorIndication:
        print('Notification %s not sent: %s' % (sendRequestHandle, errorIndication))
    elif errorStatus:
        print('Notification Receiver returned error for %s: %s @%s' %
              (sendRequestHandle, errorStatus, errorIndex))
    else:
        print('Notification %s delivered:' % sendRequestHandle)
        for name, val in varBinds:
            print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
snmpEngine = SnmpEngine()
for authData, transportTarget, contextData in targets:
    sendPduHandle = sendNotification(
        snmpEngine,
        authData,
        transportTarget,
        contextData,
        'inform',  # NotifyType
        NotificationType(
            ObjectIdentity('SNMPv2-MIB', 'coldStart')
        ).addVarBinds(('1.3.6.1.2.1.1.1.0', 'my name')),
        cbFun=cbFun
    )
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Multiple SNMP Engines

Send SNMP notifications in behalf of multiple independend SNMP engines using the following options:

  • with a single transport dispatcher and two independent SNMP engines
  • SNMPv2c and SNMPv3
  • with community name 'public' or USM username usr-md5-des
  • over IPv4/UDP
  • send IMFORM notification
  • to multiple Managers
  • with TRAP ID 'coldStart' specified as a MIB symbol
  • include managed object information specified as var-bind objects pair

Within this script we have a single asynchronous TransportDispatcher and a single UDP-based transport serving two independent SNMP engines. We use a single instance of AsyncNotificationOriginator with each of SNMP Engines to communicate INFORM notification to remote systems.

When we receive a [response] message from remote system we use a custom message router to choose what of the two SNMP engines data packet should be handed over. The selection criteria we employ here is based on peer's UDP port number. Other selection criterias are also possible.

$ snmpinform -v2c -c public demo.snmplabs.com:1162 123 1.3.6.1.6.3.1.1.5.1
$ snmpinform -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 demo.snmplabs.com 123 1.3.6.1.6.3.1.1.5.1
from pysnmp.hlapi.asyncore import *
from pysnmp.carrier.asyncore.dispatch import AsyncoreDispatcher
# List of targets in the following format:
# ( ( authData, transportTarget ), ... )
targets = (
    # 1-st target (SNMPv2c over IPv4/UDP)
    (CommunityData('public'),
     UdpTransportTarget(('demo.snmplabs.com', 1162)),
     ContextData()),
    # 2-nd target (SNMPv3 over IPv4/UDP)
    (UsmUserData('usr-md5-des', 'authkey1', 'privkey1'),
     UdpTransportTarget(('demo.snmplabs.com', 162)),
     ContextData()),
)
# noinspection PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    snmpEngine = cbCtx
    if errorIndication:
        print('Notification %s for %s not sent: %s' % (sendRequestHandle, snmpEngine.snmpEngineID.prettyPrint(), errorIndication))
    elif errorStatus:
        print('Notification Receiver returned error for request %s, SNMP Engine %s: %s @%s' % (sendRequestHandle, snmpEngine.snmpEngineID.prettyPrint(), errorStatus, errorIndex))
    else:
        print('Notification %s for SNMP Engine %s delivered:' % (sendRequestHandle, snmpEngine.snmpEngineID.prettyPrint()))
        for name, val in varBinds:
            print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
# Instantiate the single transport dispatcher object
transportDispatcher = AsyncoreDispatcher()
# Setup a custom data routing function to select snmpEngine by transportDomain
transportDispatcher.registerRoutingCbFun(
    lambda td, ta, d: ta[1] % 3 and 'A' or 'B'
)
snmpEngineA = SnmpEngine()
snmpEngineA.registerTransportDispatcher(transportDispatcher, 'A')
snmpEngineB = SnmpEngine()
snmpEngineB.registerTransportDispatcher(transportDispatcher, 'B')
for authData, transportTarget, contextData in targets:
    snmpEngine = (transportTarget.getTransportInfo()[1][1] % 3 and
                  snmpEngineA or snmpEngineB)
    sendPduHandle = sendNotification(
        snmpEngine,
        authData,
        transportTarget,
        contextData,
        'inform',  # NotifyType
        NotificationType(
            ObjectIdentity('SNMPv2-MIB', 'coldStart')
        ).addVarBinds(('1.3.6.1.2.1.1.1.0', 'my name')),
        cbFun=cbFun, cbCtx=snmpEngine
    )
transportDispatcher.runDispatcher()

Download script.

See also: library reference.

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

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

Send SNMP GET request using the following options:

  • 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

Functionally similar to:

$ snmpget -v1 -c public demo.snmplabs.com SNMPv2-MIB::sysDescr.0
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())

Download script.

Bulk walk MIB

Send a series of SNMP GETBULK requests using the following options:

  • 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

Functionally similar to:

$ snmpbulkwalk -v3 -lnoAuthNoPriv -u usr-none-none -Cn0 -Cr50 
demo.snmplabs.com  SNMPv2-MIB::system

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'))])
)

Download script.

See also: library reference.

Walking operations

Bulk walk MIB

Send a series of SNMP GETBULK requests using the following options:

  • 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

Functionally similar to:

$ snmpbulkwalk -v3 -lnoAuthNoPriv -u usr-none-none -Cn0 -Cr50 
demo.snmplabs.com  SNMPv2-MIB::system

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'))])
)

Download script.

See also: library reference.

Advanced Command Generator

Concurrent queries

Send multiple SNMP GET requests at once using the following options:

  • with SNMPv2c, community 'public'
  • over IPv4/UDP
  • to multiple Agents at demo.snmplabs.com
  • for instance of SNMPv2-MIB::sysDescr.0 MIB object
  • based on asyncio I/O framework

Functionally similar to:

$ snmpget -v2c -c public demo.snmplabs.com:1161 SNMPv2-MIB::sysDescr.0
$ snmpget -v2c -c public demo.snmplabs.com:2161 SNMPv2-MIB::sysDescr.0
$ snmpget -v2c -c public demo.snmplabs.com:3161 SNMPv2-MIB::sysDescr.0
import asyncio
from pysnmp.hlapi.asyncio import *
@asyncio.coroutine
def getone(snmpEngine, hostname):
    errorIndication, errorStatus, errorIndex, varBinds = yield from getCmd(
        snmpEngine,
        CommunityData('public'),
        UdpTransportTarget(hostname),
        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 = SnmpEngine()
loop = asyncio.get_event_loop()
loop.run_until_complete(
    asyncio.wait([getone(snmpEngine, ('demo.snmplabs.com', 1161)),
                  getone(snmpEngine, ('demo.snmplabs.com', 2161)),
                  getone(snmpEngine, ('demo.snmplabs.com', 3161))])
)

Download script.

Send multiple SNMP GET requests one by one using the following options:

  • with SNMPv2c, community 'public'
  • over IPv4/UDP
  • to multiple Agents at demo.snmplabs.com
  • for instance of SNMPv2-MIB::sysDescr.0 MIB object
  • based on asyncio I/O framework

Functionally similar to:

$ snmpget -v2c -c public demo.snmplabs.com:1161 SNMPv2-MIB::sysDescr.0
$ snmpget -v2c -c public demo.snmplabs.com:2161 SNMPv2-MIB::sysDescr.0
$ snmpget -v2c -c public demo.snmplabs.com:3161 SNMPv2-MIB::sysDescr.0
import asyncio
from pysnmp.hlapi.asyncio import *
@asyncio.coroutine
def getone(snmpEngine, hostname):
    errorIndication, errorStatus, errorIndex, varBinds = yield from getCmd(
        snmpEngine,
        CommunityData('public'),
        UdpTransportTarget(hostname),
        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]))
@asyncio.coroutine
def getall(snmpEngine, hostnames):
    for hostname in hostnames:
        yield from getone(snmpEngine, hostname)
snmpEngine = SnmpEngine()
loop = asyncio.get_event_loop()
loop.run_until_complete(getall(snmpEngine, [('demo.snmplabs.com', 1161),
                                            ('demo.snmplabs.com', 2161),
                                            ('demo.snmplabs.com', 3161)]))

Download script.

See also: library reference.

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

Send SNMPv1 TRAP through unified SNMPv3 message processing framework using the following options:

  • 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'

Functionally similar to:

$ snmptrap -v1 -c public demo.snmplabs.com 1.3.6.1.4.1.20408.4.1.1.2 0.0.0.0 1 0 0 1.3.6.1.2.1.1.1.0 s "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())

Download script.

See also: library reference.

Advanced Notification Originator

Multiple concurrent notifications

Send multiple SNMP notifications at once using the following options:

  • SNMPv2c and SNMPv3
  • with community name 'public'
  • over IPv4/UDP
  • send INFORM notification
  • to multiple Managers
  • with TRAP ID 'coldStart' specified as a MIB symbol
  • include managed object information specified as var-bind objects pair

Here we tag each SNMP-COMMUNITY-MIB::snmpCommunityTable row with the same tag as SNMP-TARGET-MIB::snmpTargetAddrTable row what leads to excessive tables information.

Functionally similar to:

$ snmptrap -v2c -c public demo.snmplabs.com 12345 1.3.6.1.6.3.1.1.5.2
$ snmpinform -v2c -c public demo.snmplabs.com 12345 1.3.6.1.6.3.1.1.5.2
$ snmptrap -v2c -c public demo.snmplabs.com 12345 1.3.6.1.6.3.1.1.5.2
import asyncio
from pysnmp.hlapi.asyncio import *
@asyncio.coroutine
def sendone(snmpEngine, hostname, notifyType):
    (errorIndication,
     errorStatus,
     errorIndex,
     varBinds) = yield from sendNotification(
        snmpEngine,
        CommunityData('public', tag=hostname),
        UdpTransportTarget((hostname, 162), tagList=hostname),
        ContextData(),
        notifyType,
        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)
    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 = SnmpEngine()
loop = asyncio.get_event_loop()
loop.run_until_complete(
    asyncio.wait([sendone(snmpEngine, 'demo.snmplabs.com', 'trap'),
                  sendone(snmpEngine, 'demo.snmplabs.com', 'inform')])
)

Download script.

See also: library reference.

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

Asynchronous: trollius

In order to use asyncio features with older Python (2.6+), you could download and install Trollius module. PySNMP's asyncio bindings will work with Trollius as well.

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 trollius I/O framework
import trollius
from pysnmp.hlapi.asyncio import *
@trollius.coroutine
def run():
    snmpEngine = SnmpEngine()
    (errorIndication,
     errorStatus,
     errorIndex,
     varBinds) = yield trollius.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()
trollius.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 trollius I/O framework
import trollius
from pysnmp.hlapi.asyncio import *
@trollius.coroutine
def run(varBinds):
    snmpEngine = SnmpEngine()
    while True:
        (errorIndication,
         errorStatus,
         errorIndex,
         varBindTable) = yield trollius.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 = trollius.get_event_loop()
loop.run_until_complete(
    run([ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr'))])
)

More examples on Command Generator API usage follow.

Various SNMP versions

Send SNMP GET request using the following options:

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

Functionally similar to:

$ snmpget -v1 -c public demo.snmplabs.com SNMPv2-MIB::sysDescr.0
import trollius
from pysnmp.hlapi.asyncio import *
@trollius.coroutine
def run():
    snmpEngine = SnmpEngine()
    (errorIndication,
     errorStatus,
     errorIndex,
     varBinds) = yield trollius.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()
trollius.get_event_loop().run_until_complete(run())

Download script.

Bulk walk MIB

Send a series of SNMP GETBULK requests using the following options:

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

Functionally similar to:

$ snmpbulkwalk -v3 -lnoAuthNoPriv -u usr-none-none -Cn0 -Cr50 demo.snmplabs.com  SNMPv2-MIB::system
import trollius
from pysnmp.hlapi.asyncio import *
@trollius.coroutine
def run(varBinds):
    snmpEngine = SnmpEngine()
    while True:
        (errorIndication,
         errorStatus,
         errorIndex,
         varBindTable) = yield trollius.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 = trollius.get_event_loop()
loop.run_until_complete(
    run([ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr'))])
)

Download script.

See also: library reference.

Walking operations

Bulk walk MIB

Send a series of SNMP GETBULK requests using the following options:

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

Functionally similar to:

$ snmpbulkwalk -v3 -lnoAuthNoPriv -u usr-none-none -Cn0 -Cr50 demo.snmplabs.com  SNMPv2-MIB::system
import trollius
from pysnmp.hlapi.asyncio import *
@trollius.coroutine
def run(varBinds):
    snmpEngine = SnmpEngine()
    while True:
        (errorIndication,
         errorStatus,
         errorIndex,
         varBindTable) = yield trollius.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 = trollius.get_event_loop()
loop.run_until_complete(
    run([ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr'))])
)

Download script.

See also: library reference.

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'
  • use trollius I/O framework
import trollius
from pysnmp.hlapi.asyncio import *
@trollius.coroutine
def run():
    snmpEngine = SnmpEngine()
    (errorIndication,
     errorStatus,
     errorIndex,
     varBinds) = yield trollius.From(
        sendNotification(
            snmpEngine,
            CommunityData('public'),  # mpModel=0),
            UdpTransportTarget(('demo.snmplabs.com', 162)),
            ContextData(),
            'inform',
            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)
    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()
trollius.get_event_loop().run_until_complete(run())

More examples on Notification Originator API usage follow.

Common notifications

SNMPv1 TRAP with defaults

Send SNMPv1 TRAP through unified SNMPv3 message processing framework using the following options:

  • 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'
  • use trollius I/O framework

Functionally similar to:

$ snmptrap -v1 -c public demo.snmplabs.com 1.3.6.1.4.1.20408.4.1.1.2 0.0.0.0 1 0 0 1.3.6.1.2.1.1.1.0 s "my system"
import trollius
from pysnmp.hlapi.asyncio import *
@trollius.coroutine
def run():
    snmpEngine = SnmpEngine()
    (errorIndication,
     errorStatus,
     errorIndex,
     varBinds) = yield trollius.From(
        sendNotification(
            snmpEngine,
            CommunityData('public'),  # mpModel=0),
            UdpTransportTarget(('demo.snmplabs.com', 162)),
            ContextData(),
            'inform',
            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)
    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()
trollius.get_event_loop().run_until_complete(run())

Download script.

See also: library reference.

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

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

Send SNMP GET request using the following options:

  • 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

Functionally similar to:

$ snmpget -v1 -c public demo.snmplabs.com SNMPv2-MIB::sysDescr.0
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'])

Download script.

Bulk walk MIB

Send a series of SNMP GETBULK requests using the following options:

  • 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

Functionally similar to:

$ snmpbulkwalk -v3 -lnoAuthNoPriv -u usr-none-none -Cn0 -Cr50 demo.snmplabs.com  SNMPv2-MIB::system
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'))])

Download script.

See also: library reference.

Walking operations

Bulk walk MIB

Send a series of SNMP GETBULK requests using the following options:

  • 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

Functionally similar to:

$ snmpbulkwalk -v3 -lnoAuthNoPriv -u usr-none-none -Cn0 -Cr50 demo.snmplabs.com  SNMPv2-MIB::system
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'))])

Download script.

Walk whole MIB

Send a series of SNMP GETNEXT requests using the following options:

  • with SNMPv3, user 'usr-md5-none', MD5 authentication, no privacy
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for all OIDs in IF-MIB
  • based on Twisted I/O framework

Functionally similar to:

$ snmpwalk -v3 -lauthPriv -u usr-md5-none -A authkey1 -X privkey1 demo.snmplabs.com  IF-MIB::
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 getnext(reactor, snmpEngine, *varBindTable[-1])
def failure(errorIndication):
    print(errorIndication)
def getnext(reactor, snmpEngine, varBinds):
    d = nextCmd(snmpEngine,
                UsmUserData('usr-md5-none', 'authkey1'),
                UdpTransportTarget(('demo.snmplabs.com', 161)),
                ContextData(),
                varBinds)
    d.addCallback(success, reactor, snmpEngine).addErrback(failure)
    return d
react(getnext, [SnmpEngine(), ObjectType(ObjectIdentity('SNMPv2-MIB', 'system'))])

Download script.

See also: library reference.

Transport tweaks

Send SNMP GET request using the following options:

  • with SNMPv2c, community 'public'
  • over IPv4/UDP with non-default timeout and no retries
  • to an Agent at demo.snmplabs.com:161
  • for two instances of SNMPv2-MIB::sysDescr.0 MIB object,
  • based on Twisted I/O framework

Functionally similar to:

$ snmpget -v2c -c public -r 0 -t 2 demo.snmplabs.com SNMPv2-MIB::sysDescr.0
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):
    snmpEngine = SnmpEngine()
    d = getCmd(snmpEngine,
               CommunityData('public'),
               UdpTransportTarget((hostname, 161), timeout=2.0, retries=0),
               ContextData(),
               ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0)))
    d.addCallback(success, hostname).addErrback(failure, hostname)
    return d
react(getSysDescr, ['demo.snmplabs.com'])

Download script.

See also: library reference.

Advanced Command Generator

Concurrent queries

Send multiple SNMP GET requests at once using the following options:

  • with SNMPv2c, community 'public'
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for two instances of SNMPv2-MIB::sysDescr.0 and SNMPv2-MIB::sysLocation.0 MIB object,
  • based on Twisted I/O framework

Functionally similar to:

$ snmpget -v2c -c public demo.snmplabs.com SNMPv2-MIB::sysDescr.0
$ snmpget -v2c -c public demo.snmplabs.com SNMPv2-MIB::sysLocation.0
from twisted.internet.defer import DeferredList
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 getSystem(reactor, hostname):
    snmpEngine = SnmpEngine()
    def getScalar(objectType):
        d = getCmd(snmpEngine,
                   CommunityData('public', mpModel=0),
                   UdpTransportTarget((hostname, 161)),
                   ContextData(),
                   objectType)
        d.addCallback(success, hostname).addErrback(failure, hostname)
        return d
    return DeferredList(
        [getScalar(ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))),
         getScalar(ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysLocation', 0)))]
    )
react(getSystem, ['demo.snmplabs.com'])

Download script.

Walk multiple Agents at once

  • with SNMPv3 with user 'usr-md5-none', MD5 auth and no privacy protocols
  • over IPv4/UDP
  • to Agents at demo.snmplabs.com:161 and demo.snmplabs.com:1161
  • for multiple MIB subtrees and tables
  • for whole MIB
  • based on Twisted I/O framework

Functionally similar to:

$ snmpget -v2c -c public demo.snmplabs.com:161 SNMPv2-MIB::system
$ snmpget -v2c -c public demo.snmplabs.comL1161 SNMPv2-MIB::system
from twisted.internet.defer import DeferredList
from twisted.internet.task import react
from pysnmp.hlapi.twisted import *
def success(args, reactor, snmpEngine, hostname):
    (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, hostname, *varBindTable[-1])
def failure(errorIndication):
    print(errorIndication)
def getbulk(reactor, snmpEngine, hostname, varBinds):
    d = bulkCmd(snmpEngine,
                UsmUserData('usr-md5-none', 'authkey1'),
                UdpTransportTarget(hostname),
                ContextData(),
                0, 25,
                varBinds)
    d.addCallback(success, reactor, snmpEngine, hostname).addErrback(failure)
    return d
def getall(reactor, hostnames):
    snmpEngine = SnmpEngine()
    return DeferredList(
        [getbulk(reactor, snmpEngine, hostname,
                 ObjectType(ObjectIdentity('SNMPv2-MIB', 'system')))
         for hostname in hostnames]
    )
react(getall, [(('demo.snmplabs.com', 161), ('demo.snmplabs.com', 1161))])

Download script.

See also: library reference.

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

Send SNMPv1 TRAP through unified SNMPv3 message processing framework using the following options:

  • 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'

Functionally similar to:

$ snmptrap -v1 -c public demo.snmplabs.com 1.3.6.1.4.1.20408.4.1.1.2 0.0.0.0 1 0 0 1.3.6.1.2.1.1.1.0 s "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'])

Download script.

Send SNMPv2c TRAP through unified SNMPv3 message processing framework using the following options:

  • SNMPv2c
  • with community name 'public'
  • over IPv4/UDP
  • send TRAP notification
  • with Generic Trap #1 (warmStart) and Specific Trap 0
  • include managed object information '1.3.6.1.2.1.1.1.0' = 'my system'

Functionally similar to:

$ snmptrap -v2c -c public demo.snmplabs.com 12345 1.3.6.1.6.3.1.1.5.2 1.3.6.1.2.1.1.1.0 s "Hello from Twisted"
from twisted.internet.task import react, defer
from pysnmp.hlapi.twisted import *
@defer.inlineCallbacks
def sendtrap(reactor, snmpEngine, hostname):
    yield sendNotification(
        snmpEngine,
        CommunityData('public', mpModel=0),
        UdpTransportTarget((hostname, 162)),
        ContextData(),
        'trap',
        NotificationType(
            ObjectIdentity('1.3.6.1.6.3.1.1.5.2')
        ).addVarBinds(
            ObjectType(ObjectIdentity('1.3.6.1.2.1.1.1.0'), OctetString('Hello from Twisted'))
        )
    )
# Preserve SnmpEngine instance across [potentially] multiple calls to safe on initialization
snmpEngine = SnmpEngine()
react(sendtrap, [snmpEngine, 'demo.snmplabs.com'])

Download script.

See also: library reference.

Advanced Notification Originator

Multiple concurrent notifications

Send multiple SNMP notifications at once using the following options:

  • SNMPv2c
  • with community name 'public'
  • over IPv4/UDP
  • send TRAP and INFORM notification
  • to multiple Managers
  • with TRAP ID 'coldStart' specified as a MIB symbol
  • include managed object information specified as var-bind objects pair

Here we tag each SNMP-COMMUNITY-MIB::snmpCommunityTable row with the same tag as SNMP-TARGET-MIB::snmpTargetAddrTable row what leads to excessive tables information.

Functionally similar to:

$ snmptrap -v2c -c public demo.snmplabs.com 12345 1.3.6.1.6.3.1.1.5.2
$ snmpinform -v2c -c public demo.snmplabs.com 12345 1.3.6.1.6.3.1.1.5.2
from twisted.internet.defer import DeferredList
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 sendone(reactor, snmpEngine, hostname, notifyType):
    d = sendNotification(
        snmpEngine,
        CommunityData('public', tag=hostname),
        UdpTransportTarget((hostname, 162), tagList=hostname),
        ContextData(),
        notifyType,
        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
def sendall(reactor, destinations):
    snmpEngine = SnmpEngine()
    return DeferredList(
        [sendone(reactor, snmpEngine, hostname, notifyType)
         for hostname, notifyType in destinations]
    )
react(sendall, [[('demo.snmplabs.com', 'trap'),
                 ('demo.snmplabs.com', 'inform')]])

Download script.

See also: library reference.

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

Complete implementation of all official Standard SNMP Applications. It should let you implement any SNMP operation defined in the standard at the cost of working at a somewhat low level.

This API also comes in several transport varieties depending on I/O framework being used.

Asynchronous: asyncore

If you find yourself unable to use particular SNMP feature with the high-level (hlapi) API, your next step would be to use SNMPv3 engine services through one of the Standard SNMP Applications (RFC3413).

There're a large number of SNMPv3 Native API example scripts on this website. Most of them serve a very specific purpose like talking arbitrary SNMP version or handling particular PDU type. That dedication of features serve the purpose of simplifying example code and easing your studies.

Since all these examples are built on top of common PySNMP components like SNMP engine, asyncore-based I/O dispatcher, configuration datastore, you could always combine parts of the examples for getting a new breed of SNMP application best matching your needs.

Various SNMP versions

  • with SNMPv1, community 'public'
  • over IPv4/UDP
  • to an Agent at 104.236.166.95:161
  • for an OID in tuple form

This script performs similar to the following Net-SNMP command:

$ snmpget -v1 -c public -ObentU 104.236.166.95 1.3.6.1.2.1.1.1.0
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
#
# SNMPv1 setup
#
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Specify security settings per SecurityName (SNMPv1 - 0, SNMPv2c - 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 0)
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds'
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    if errorIndication:
        print(errorIndication)
    # SNMPv1 response may contain noSuchName error *and* SNMPv2c exception,
    # so we ignore noSuchName error here
    elif errorStatus and errorStatus != 2:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
    else:
        for oid, val in varBinds:
            print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
# Prepare and send a request message
cmdgen.GetCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    [((1, 3, 6, 1, 2, 1, 1, 1, 0), None)],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send a SNMP SET request * with SNMPv2c with community name 'private' * over IPv4/UDP * to an Agent at 104.236.166.95:161 * for an OID in tuple form and an integer-typed value

This script performs similar to the following Net-SNMP command:

$ snmpset -v2c -c private -ObentU 104.236.166.95:161 1.3.6.1.2.1.1.9.1.4.1 t 123
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
from pysnmp.proto import rfc1902
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
#
# SNMPv2c setup
#
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'private')
# Specify security settings per SecurityName (SNMPv1 - 0, SNMPv2c - 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 1)
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds'
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    if errorIndication:
        print(errorIndication)
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
    else:
        for oid, val in varBinds:
            print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
# Prepare and send a request message
cmdgen.SetCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    [((1, 3, 6, 1, 2, 1, 1, 9, 1, 4, 1), rfc1902.TimeTicks(123))],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send a SNMP GET request * with SNMPv3 with user 'usr-sha-aes', SHA auth and AES128 privacy protocols * over IPv4/UDP * to an Agent at 104.236.166.95:161 * for an OID in tuple form

This script performs similar to the following Net-SNMP command:

$ snmpget -v3 -l authPriv -u usr-sha-aes -a SHA -A authkey1 -x AES -X privkey1 -ObentU 104.236.166.95:161  1.3.6.1.2.1.1.1.0
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
#
# SNMPv3/USM setup
#
# user: usr-sha-aes, auth: SHA, priv AES
config.addV3User(
    snmpEngine, 'usr-sha-aes',
    config.usmHMACSHAAuthProtocol, 'authkey1',
    config.usmAesCfb128Protocol, 'privkey1'
)
config.addTargetParams(snmpEngine, 'my-creds', 'usr-sha-aes', 'authPriv')
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds'
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    if errorIndication:
        print(errorIndication)
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
    else:
        for oid, val in varBinds:
            print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
# Prepare and send a request message
cmdgen.GetCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    [((1, 3, 6, 1, 2, 1, 1, 1, 0), None)],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()
config.delTransport(
    snmpEngine,
    udp.domainName
).closeTransport()

Download script.

Send a SNMP SET request with the following options:

  • with SNMPv3 with user 'usr-sha-none', SHA auth and no privacy protocols
  • over IPv4/UDP
  • to an Agent at 104.236.166.95:161
  • for an OID in tuple form and a string-typed value

This script performs similar to the following Net-SNMP command:

$ snmpset -v3 -l authNoPriv -u usr-sha-none -a SHA -A authkey1 -ObentU 104.236.166.95:161 1.3.6.1.2.1.1.9.1.3.1 s 'my new value'
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
from pysnmp.proto import rfc1902
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
#
# SNMPv3/USM setup
#
# user: usr-sha-none, auth: SHA, priv none
config.addV3User(
    snmpEngine, 'usr-sha-none',
    config.usmHMACSHAAuthProtocol, 'authkey1'
)
config.addTargetParams(snmpEngine, 'my-creds', 'usr-sha-none', 'authNoPriv')
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds'
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    if errorIndication:
        print(errorIndication)
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
    else:
        for oid, val in varBinds:
            print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
# Prepare and send a request message
cmdgen.SetCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    [((1, 3, 6, 1, 2, 1, 1, 9, 1, 3, 1), rfc1902.OctetString('my new value'))],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library reference.

Send SNMP SET request with the following options:

  • with SNMPv1 with community name 'private'
  • over IPv4/UDP
  • to an Agent at 104.236.166.95:161
  • for OIDs in tuple form and an integer and string-typed values

This script performs similar to the following Net-SNMP command:

$ snmpset -v1 -c private -ObentU 104.236.166.95:161 1.3.6.1.2.1.1.9.1.3.1 s 'my value'  1.3.6.1.2.1.1.9.1.4.1 t 123
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
from pysnmp.proto import rfc1902
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
#
# SNMPv1 setup
#
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'private')
# Specify security settings per SecurityName (SNMPv1 - 0, SNMPv2c - 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 0)
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds'
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    if errorIndication:
        print(errorIndication)
    # SNMPv1 response may contain noSuchName error *and* SNMPv2c exception,
    # so we ignore noSuchName error here
    elif errorStatus and errorStatus != 2:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
    else:
        for oid, val in varBinds:
            print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
# Prepare and send a request message
cmdgen.SetCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    [((1, 3, 6, 1, 2, 1, 1, 9, 1, 3, 1), rfc1902.OctetString('my value')),
     ((1, 3, 6, 1, 2, 1, 1, 9, 1, 4, 1), rfc1902.TimeTicks(123))],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library reference.

Walking operations

Bulk walk MIB

Send a series of SNMP GETBULK requests * with SNMPv2c, community 'public' * over IPv4/UDP * to an Agent at 104.236.166.95:161 * with values non-repeaters = 0, max-repetitions = 25 * for two OIDs in tuple form * stop on end-of-mib condition for both OIDs

This script performs similar to the following Net-SNMP command:

$ snmpbulkwalk -v2c -c public -C n0 -C r25 -ObentU 104.236.166.95 1.3.6.1.2.1.1 1.3.6.1.4.1.1
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdgen
from pysnmp.carrier.asyncore.dgram import udp
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
#
# SNMPv2c setup
#
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Specify security settings per SecurityName (SNMPv1 - 0, SNMPv2c - 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 1)
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds'
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequesthandle, errorIndication,
          errorStatus, errorIndex, varBindTable, cbCtx):
    if errorIndication:
        print(errorIndication)
        return  # stop on error
    if errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBindTable[-1][int(errorIndex) - 1][0] or '?'))
        return  # stop on error
    for varBindRow in varBindTable:
        for oid, val in varBindRow:
            print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
    return True  # signal dispatcher to continue walking
# Prepare initial request to be sent
cmdgen.BulkCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    0, 25,  # non-repeaters, max-repetitions
    [((1, 3, 6, 1, 2, 1, 1), None),
     ((1, 3, 6, 1, 4, 1, 1), None)],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send a series of SNMP GETNEXT requests with the following options:

  • with SNMPv1, community 'public'
  • over IPv4/UDP
  • to an Agent at 104.236.166.95:161
  • for two OIDs in tuple form
  • stop on end-of-mib condition for both OIDs

This script performs similar to the following Net-SNMP command:

$ snmpwalk -v1 -c public -ObentU 104.236.166.95 1.3.6.1.2.1.1 1.3.6.1.4.1.1
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
#
# SNMPv1/2c setup
#
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Specify security settings per SecurityName (SNMPv1 - 0, SNMPv2c - 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 0)
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds'
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBindTable, cbCtx):
    if errorIndication:
        print(errorIndication)
        return
    # SNMPv1 response may contain noSuchName error *and* SNMPv2c exception,
    # so we ignore noSuchName error here
    if errorStatus and errorStatus != 2:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBindTable[-1][int(errorIndex) - 1][0] or '?'))
        return  # stop on error
    for varBindRow in varBindTable:
        for oid, val in varBindRow:
            print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
    return 1  # signal dispatcher to continue
# Prepare initial request to be sent
cmdgen.NextCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    [((1, 3, 6, 1, 2, 1, 1), None),
     ((1, 3, 6, 1, 4, 1, 1), None)],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library reference.

Table operations

Fetch scalar and table variables

Send a series of SNMP GETBULK requests with the following options:

  • with SNMPv3 with user 'usr-md5-des', MD5 auth and DES privacy protocols
  • over IPv4/UDP
  • to an Agent at 104.236.166.95:161
  • with values non-repeaters = 1, max-repetitions = 25
  • for two OIDs in tuple form (first OID is non-repeating)
  • stop on end-of-mib condition for both OIDs

This script performs similar to the following Net-SNMP command:

$ snmpbulkwalk -v3 -l authPriv -u usr-md5-des -A authkey1 -X privkey1 -C n1 -C r25 -ObentU 104.236.166.95 1.3.6.1.2.1.1 1.3.6.1.4.1.1
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdgen
from pysnmp.carrier.asyncore.dgram import udp
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
#
# SNMPv3/USM setup
#
# user: usr-md5-des, auth: MD5, priv DES
config.addV3User(
    snmpEngine, 'usr-md5-des',
    config.usmHMACMD5AuthProtocol, 'authkey1',
    config.usmDESPrivProtocol, 'privkey1'
)
config.addTargetParams(snmpEngine, 'my-creds', 'usr-md5-des', 'authPriv')
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds'
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequesthandle, errorIndication,
          errorStatus, errorIndex, varBindTable, cbCtx):
    if errorIndication:
        print(errorIndication)
        return  # stop on error
    if errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBindTable[-1][int(errorIndex) - 1][0] or '?'))
        return  # stop on error
    for varBindRow in varBindTable:
        for oid, val in varBindRow:
            print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
    return True  # signal dispatcher to continue walking
# Prepare initial request to be sent
cmdgen.BulkCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    0, 25,  # non-repeaters, max-repetitions
    (((1, 3, 6, 1, 2, 1, 1), None),
     ((1, 3, 6, 1, 4, 1, 1), None)),
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send a series of SNMP GETNEXT requests * with SNMPv3 with user 'usr-none-none', no auth and no privacy protocols * over IPv4/UDP * to an Agent at 104.236.166.95:161 * for an OID in string form * stop whenever received OID goes out of initial prefix (it may be a table)

This script performs similar to the following Net-SNMP command:

$ snmpwalk -v3 -l noAuthNoPriv -u usr-none-none -ObentU 104.236.166.95:161  1.3.6.1.2.1.1
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
from pysnmp.proto import rfc1902
# Initial OID prefix
initialOID = rfc1902.ObjectName('1.3.6.1.2.1.1')
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
#
# SNMPv3/USM setup
#
# user: usr-none-none, auth: none, priv: none
config.addV3User(
    snmpEngine, 'usr-none-none',
)
config.addTargetParams(snmpEngine, 'my-creds', 'usr-none-none', 'noAuthNoPriv')
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds'
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBindTable, cbCtx):
    if errorIndication:
        print(errorIndication)
        return
    if errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBindTable[-1][int(errorIndex) - 1][0] or '?'))
        return  # stop on error
    for varBindRow in varBindTable:
        for oid, val in varBindRow:
            if initialOID.isPrefixOf(oid):
                print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
            else:
                return False  # signal dispatcher to stop
    return True  # signal dispatcher to continue
# Prepare initial request to be sent
cmdgen.NextCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    [(initialOID, None)],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library reference.

MIB tweaks

Send a series of SNMP GETNEXT requests with the following options:

  • with SNMPv1, community 'public'
  • over IPv4/UDP
  • to an Agent at 104.236.166.95:161
  • for two OIDs in tuple form
  • stop on end-of-mib condition for both OIDs

This script performs similar to the following Net-SNMP command:

$ snmpwalk -v1 -c public -ObentU 104.236.166.95 1.3.6.1.2.1.1 1.3.6.1.4.1.1
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
from pysnmp.smi import compiler, view, rfc1902
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# Attach MIB compiler to SNMP Engine (MIB Builder)
# This call will fail if PySMI is not present on the system
compiler.addMibCompiler(snmpEngine.getMibBuilder())
# ... alternatively, this call will not complain on missing PySMI
# compiler.addMibCompiler(snmpEngine.getMibBuilder(), ifAvailable=True)
# Used for MIB objects resolution
mibViewController = view.MibViewController(snmpEngine.getMibBuilder())
# 
#
# SNMPv1/2c setup
#
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Specify security settings per SecurityName (SNMPv1 - 0, SNMPv2c - 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 1)
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds'
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBindTable, cbCtx):
    if errorIndication:
        print(errorIndication)
        return
    # SNMPv1 response may contain noSuchName error *and* SNMPv2c exception,
    # so we ignore noSuchName error here
    if errorStatus and errorStatus != 2:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBindTable[-1][int(errorIndex) - 1][0] or '?'))
        return  # stop on error
    for varBindRow in varBindTable:
        for varBind in varBindRow:
            print(rfc1902.ObjectType(rfc1902.ObjectIdentity(varBind[0]),
                                     varBind[1]).resolveWithMib(mibViewController).prettyPrint())
    return 1  # signal dispatcher to continue
# Prepare initial request to be sent
cmdgen.NextCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    [rfc1902.ObjectType(rfc1902.ObjectIdentity('iso.org.dod')).resolveWithMib(mibViewController),
     rfc1902.ObjectType(rfc1902.ObjectIdentity('IF-MIB', 'ifMIB')).resolveWithMib(mibViewController)],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library reference.

Transport tweaks

Send a SNMP GET request:

  • with SNMPv2c, community 'public'
  • over IPv4/UDP
  • to an Agent at 104.236.166.95:161
  • wait 3 seconds for response, retry 5 times (plus one initial attempt)
  • for an OID in tuple form

This script performs similar to the following Net-SNMP command:

$ snmpget -v2c -c public -ObentU -r 5 -t 1 104.236.166.95 1.3.6.1.2.1.1.1.0
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
#
# SNMPv2c setup
#
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Specify security settings per SecurityName (SNMPv1 - 0, SNMPv2c - 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 1)
#
# Setup transport endpoint and bind it with security settings yielding
# a target name 
#
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds',
    timeout=300,  # in 1/100 sec
    retryCount=5
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    if errorIndication:
        print(errorIndication)
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
    else:
        for oid, val in varBinds:
            print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
# Prepare and send a request message
cmdgen.GetCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    [((1, 3, 6, 1, 2, 1, 1, 1, 0), None)],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send a series of SNMP GETNEXT requests with the following options:

  • with SNMPv2c, community 'public'
  • over IPv4/UDP
  • to an Agent at 104.236.166.95:161
  • sending packets from primary local interface 0.0.0.0, local port 61024
  • for two OIDs in tuple form
  • stop on end-of-mib condition for both OIDs

This script performs similar to the following Net-SNMP command:

$ snmpwalk -v2c -c public -ObentU 104.236.166.95 1.3.6.1.2.1.1 1.3.6.1.4.1.1
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
#
# SNMPv1/2c setup (if you use SNMPv1 or v2c)
#
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Specify security settings per SecurityName (SNMPv1 - 0, SNMPv2c - 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 0)
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode(('0.0.0.0', 61024))
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds'
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBindTable, cbCtx):
    if errorIndication:
        print(errorIndication)
        return
    if errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBindTable[-1][int(errorIndex) - 1][0] or '?'))
        return  # stop on error
    for varBindRow in varBindTable:
        for oid, val in varBindRow:
            print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
    return 1  # signal dispatcher to continue
# Prepare initial request to be sent
cmdgen.NextCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    [((1, 3, 6, 1, 2, 1, 1), None),
     ((1, 3, 6, 1, 2, 1, 11), None)],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send a SNMP GET request * with SNMPv2c, community 'public' * over IPv4/UDP * to an Agent at 104.236.166.95:161 * from a non-local, spoofed IP 1.2.3.4 (root and Python 3.3+ required) * for an OID in tuple form

This script performs similar to the following Net-SNMP command:

$ snmpget -v2c -c public -ObentU 104.236.166.95 1.3.6.1.2.1.1.1.0

But unlike the above command, this script issues SNMP request from a non-default, non-local IP address.

It is indeed possible to originate SNMP traffic from any valid local IP addresses. It could be a secondary IP interface, for instance. Superuser privileges are only required to send spoofed packets. Alternatively, sending from local interface could also be achieved by binding to it (via openClientMode() parameter).

from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
#
# SNMPv1 setup
#
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Specify security settings per SecurityName (SNMPv1 - 0, SNMPv2c - 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 0)
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# Initialize asyncore-based UDP/IPv4 transport
udpSocketTransport = udp.UdpSocketTransport().openClientMode()
# Use sendmsg()/recvmsg() for socket communication (required for
# IP source spoofing functionality)
udpSocketTransport.enablePktInfo()
# Enable IP source spoofing (requires root privileges)
udpSocketTransport.enableTransparent()
# Register this transport at SNMP Engine
config.addTransport(
    snmpEngine,
    udp.domainName,
    udpSocketTransport
)
# Configure destination IPv4 address as well as source IPv4 address
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds',
    sourceAddress=('1.2.3.4', 0)
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    if errorIndication:
        print(errorIndication)
    # SNMPv1 response may contain noSuchName error *and* SNMPv2c exception,
    # so we ignore noSuchName error here
    elif errorStatus and errorStatus != 2:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
    else:
        for oid, val in varBinds:
            print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
# Prepare and send a request message
cmdgen.GetCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    [((1, 3, 6, 1, 2, 1, 1, 1, 0), None)],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send a series of SNMP GETNEXT requests with the following options:

  • with SNMPv3 with user 'usr-md5-none', MD5 auth and no privacy protocols
  • over IPv6/UDP
  • to an Agent at [::1]:161
  • for two OIDs in tuple form
  • stop on end-of-mib condition for both OIDs

This script performs similar to the following Net-SNMP command:

$ snmpwalk -v3 -l authNoPriv -u usr-md5-none -A authkey1 -ObentU udp6:[::1]:161 1.3.6.1.2.1.1 1.3.6.1.4.1.1
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp6
from pysnmp.entity.rfc3413 import cmdgen
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
#
# SNMPv3/USM setup
#
# user: usr-md5-des, auth: MD5, priv NONE
config.addV3User(
    snmpEngine, 'usr-md5-none',
    config.usmHMACMD5AuthProtocol, 'authkey1'
)
config.addTargetParams(snmpEngine, 'my-creds', 'usr-md5-none', 'authNoPriv')
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv6
config.addTransport(
    snmpEngine,
    udp6.domainName,
    udp6.Udp6SocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp6.domainName, ('::1', 161),
    'my-creds'
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBindTable, cbCtx):
    if errorIndication:
        print(errorIndication)
        return
    if errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBindTable[-1][int(errorIndex) - 1][0] or '?'))
        return  # stop on error
    for varBindRow in varBindTable:
        for oid, val in varBindRow:
            print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
    return True  # signal dispatcher to continue
# Prepare initial request to be sent
cmdgen.NextCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    [((1, 3, 6, 1, 2, 1, 1), None),
     ((1, 3, 6, 1, 4, 1, 1), None)],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library-reference.

Advanced topics

Custom ContextEngineId and ContextName

Send a SNMP GET request with the following options:

  • with SNMPv3 with user 'usr-md5-none', SHA auth and no privacy protocols
  • for MIB instance identified by
  • contextEngineId: 0x80004fb805636c6f75644dab22cc,
  • contextName: da761cfc8c94d3aceef4f60f049105ba
  • over IPv4/UDP
  • to an Agent at 104.236.166.95:161
  • for an OID in tuple form

This script performs similar to the following Net-SNMP command:

$ snmpget -v3 -l authNoPriv -u usr-md5-none -A authkey1 -E 80004fb805636c6f75644dab22cc -n da761cfc8c94d3aceef4f60f049105ba -ObentU 104.236.166.95:161  1.3.6.1.2.1.1.1.0
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
from pysnmp.proto import rfc1902
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
#
# SNMPv3/USM setup
#
# user: usr-md5-none, auth: MD5, priv: NONE
config.addV3User(
    snmpEngine, 'usr-md5-none',
    config.usmHMACMD5AuthProtocol, 'authkey1'
)
config.addTargetParams(snmpEngine, 'my-creds', 'usr-md5-none', 'authNoPriv')
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds'
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    if errorIndication:
        print(errorIndication)
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
    else:
        for oid, val in varBinds:
            print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
# Prepare and send a request message, pass custom ContextEngineId & ContextName
cmdgen.GetCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    # contextEngineId
    rfc1902.OctetString(hexValue='80004fb805636c6f75644dab22cc'),
    # contextName
    rfc1902.OctetString('da761cfc8c94d3aceef4f60f049105ba'),
    [((1, 3, 6, 1, 2, 1, 1, 1, 0), None)],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send SNMP GET request with the following options:

  • with SNMPv3 with user 'usr-sha-aes', SHA auth and AES128 privacy protocols
  • over IPv4/UDP
  • to an Agent at 104.236.166.95:161
  • for an OID in tuple form
  • also registers its own execution observer to snmpEngine

While execution, this script will report some details on request processing as seen by rfc3412.sendPdu() and rfc3412.receiveMessage() abstract interfaces.

This script performs similar to the following Net-SNMP command:

$ snmpget -v3 -l authPriv -u usr-sha-aes -a SHA -A authkey1 -x AES -X privkey1 -ObentU 104.236.166.95:161  1.3.6.1.2.1.1.1.0
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import cmdgen
# Create SNMP engine instance
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' % '@'.join([str(x) for x in variables['transportAddress']]))
    print('* securityModel: %s' % variables['securityModel'])
    print('* securityName: %s' % variables['securityName'])
    print('* securityLevel: %s' % variables['securityLevel'])
    print('* contextEngineId: %s' % (variables['contextEngineId'] and variables['contextEngineId'].prettyPrint() or '<empty>',))
    print('* contextName: %s' % variables['contextName'].prettyPrint())
    print('* PDU: %s' % variables['pdu'].prettyPrint())
snmpEngine.observer.registerObserver(
    requestObserver,
    'rfc3412.sendPdu',
    'rfc3412.receiveMessage:response'
)
#
# SNMPv3/USM setup
#
# user: usr-sha-aes, auth: SHA, priv AES
config.addV3User(
    snmpEngine, 'usr-sha-aes',
    config.usmHMACSHAAuthProtocol, 'authkey1',
    config.usmAesCfb128Protocol, 'privkey1'
)
config.addTargetParams(snmpEngine, 'my-creds', 'usr-sha-aes', 'authPriv')
#
# Setup transport endpoint and bind it with security settings yielding
# a target name
#
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-router',
    udp.domainName, ('104.236.166.95', 161),
    'my-creds'
)
# Error/response receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    if errorIndication:
        print(errorIndication)
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
    else:
        for oid, val in varBinds:
            print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
# Prepare and send a request message
cmdgen.GetCommandGenerator().sendVarBinds(
    snmpEngine,
    'my-router',
    None, '',  # contextEngineId, contextName
    [((1, 3, 6, 1, 2, 1, 1, 1, 0), None)],
    cbFun
)
# Run I/O dispatcher which would send pending queries and process responses
snmpEngine.transportDispatcher.runDispatcher()
snmpEngine.observer.unregisterObserver()

Download script.

See also: library reference.

Command Responder Applications

Various SNMP versions

Multiple SNMP USM users

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 with USM user 'usr-sha-none', auth: SHA, no privacy with USM user 'usr-sha-aes128', auth: SHA, priv AES
  • allow access to SNMPv2-MIB objects (1.3.6.1.2.1)
  • over IPv4/UDP, listening at 127.0.0.1:161

Either of the following Net-SNMP commands will walk this Agent:

$ snmpwalk -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 localhost .1.3.6
$ snmpwalk -v3 -u usr-sha-none -l authNoPriv -a SHA -A authkey1 localhost .1.3.6
$ snmpwalk -v3 -u usr-sha-aes128 -l authPriv -a SHA -A authkey1 -x AES -X privkey1 localhost .1.3.6
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()
# Transport setup
# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpTransport().openServerMode(('127.0.0.1', 1161))
)
# SNMPv3/USM setup
# user: usr-md5-des, auth: MD5, priv DES
config.addV3User(
    snmpEngine, 'usr-md5-des',
    config.usmHMACMD5AuthProtocol, 'authkey1',
    config.usmDESPrivProtocol, 'privkey1'
)
# user: usr-sha-none, auth: SHA, priv NONE
config.addV3User(
    snmpEngine, 'usr-sha-none',
    config.usmHMACSHAAuthProtocol, 'authkey1'
)
# user: usr-sha-none, auth: SHA, priv AES
config.addV3User(
    snmpEngine, 'usr-sha-aes128',
    config.usmHMACSHAAuthProtocol, 'authkey1',
    config.usmAesCfb128Protocol, '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))
config.addVacmUser(snmpEngine, 3, 'usr-sha-none', 'authNoPriv', (1, 3, 6, 1, 2, 1), (1, 3, 6, 1, 2, 1))
config.addVacmUser(snmpEngine, 3, 'usr-sha-aes128', '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.

Multiple SNMP communities

Respond to SNMP GET/SET/GETNEXT queries with the following options:

  • SNMPv1
  • with SNMP community "public" (read access) or "private" (write access)
  • allow access to SNMPv2-MIB objects (1.3.6.1.2.1)
  • over IPv4/UDP, listening at 127.0.0.1:161

Allow read/write access to all objects in the same MIB subtree.

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

$ snmpget -v1 -c public 127.0.0.1 SNMPv2-MIB::sysLocation.0
$ snmpset -v1 -c private 127.0.0.1 SNMPv2-MIB::sysLocation.0 s "far away"
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))
)
# SNMPv1 setup
# SecurityName <-> CommunityName mapping.
# Here we configure two distinct CommunityName's to control read and write
# operations.
config.addV1System(snmpEngine, 'my-read-area', 'public')
config.addV1System(snmpEngine, 'my-write-area', 'private')
# Allow full MIB access for this user / securityModels at VACM
config.addVacmUser(snmpEngine, 1, 'my-read-area', 'noAuthNoPriv', (1, 3, 6, 1, 2, 1))
config.addVacmUser(snmpEngine, 1, 'my-write-area', 'noAuthNoPriv', (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)
# 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.

See also: library reference.

Agent-side MIB implementations

Implementing scalar MIB objects

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

  • SNMPv2c
  • with SNMP community "public"
  • serving custom Managed Object Instance defined within this script
  • allow read access only to the subtree where the custom MIB object resides
  • over IPv4/UDP, listening at 127.0.0.1:161

The following Net-SNMP commands will walk this Agent:

$ snmpwalk -v2c -c public 127.0.0.1 .1.3.6
import sys
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.proto.api import v2c
# Create SNMP engine
snmpEngine = engine.SnmpEngine()
# Transport setup
# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpTransport().openServerMode(('127.0.0.1', 161))
)
# SNMPv2c setup
# SecurityName <-> CommunityName mapping.
config.addV1System(snmpEngine, 'my-area', 'public')
# Allow read MIB access for this user / securityModels at VACM
config.addVacmUser(snmpEngine, 2, 'my-area', 'noAuthNoPriv', (1, 3, 6, 5))
# Create an SNMP context
snmpContext = context.SnmpContext(snmpEngine)
# --- create custom Managed Object Instance ---
mibBuilder = snmpContext.getMibInstrum().getMibBuilder()
MibScalar, MibScalarInstance = mibBuilder.importSymbols(
    'SNMPv2-SMI', 'MibScalar', 'MibScalarInstance'
)
class MyStaticMibScalarInstance(MibScalarInstance):
    # noinspection PyUnusedLocal,PyUnusedLocal
    def getValue(self, name, idx):
        return self.getSyntax().clone(
            'Python %s running on a %s platform' % (sys.version, sys.platform)
        )
mibBuilder.exportSymbols(
    '__MY_MIB', MibScalar((1, 3, 6, 5, 1), v2c.OctetString()),
    MyStaticMibScalarInstance((1, 3, 6, 5, 1), (0,), v2c.OctetString())
)
# --- end of Managed Object Instance initialization ----
# Register SNMP Applications at the SNMP engine for particular SNMP context
cmdrsp.GetCommandResponder(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.

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

  • SNMPv2c
  • with SNMP community "public"
  • define a simple SNMP Table within a newly created EXAMPLE-MIB
  • pre-populate SNMP Table with a single row of values
  • allow read access only to the subtree where example SNMP Table resides
  • over IPv4/UDP, listening at 127.0.0.1:161

The following Net-SNMP commands will populate and walk a table:

$ snmpset -v2c -c public 127.0.0.1 1.3.6.6.1.5.2.97.98.99 s "my value"
$ snmpset -v2c -c public 127.0.0.1 1.3.6.6.1.5.4.97.98.99 i 4
$ snmpwalk -v2c -c public 127.0.0.1 1.3.6

...while the following command will destroy the same row

$ snmpset -v2c -c public 127.0.0.1 1.3.6.6.1.5.4.97.98.99 i 6
$ snmpwalk -v2c -c public 127.0.0.1 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.api import v2c
# Create SNMP engine
snmpEngine = engine.SnmpEngine()
# Transport setup
# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpTransport().openServerMode(('127.0.0.1', 161))
)
# SNMPv2c setup
# SecurityName <-> CommunityName mapping.
config.addV1System(snmpEngine, 'my-area', 'public')
# Allow read MIB access for this user / securityModels at VACM
config.addVacmUser(snmpEngine, 2, 'my-area', 'noAuthNoPriv', (1, 3, 6, 6), (1, 3, 6, 6))
# Create an SNMP context
snmpContext = context.SnmpContext(snmpEngine)
# --- define custom SNMP Table within a newly defined EXAMPLE-MIB ---
mibBuilder = snmpContext.getMibInstrum().getMibBuilder()
(MibTable,
 MibTableRow,
 MibTableColumn,
 MibScalarInstance) = mibBuilder.importSymbols(
    'SNMPv2-SMI',
    'MibTable',
    'MibTableRow',
    'MibTableColumn',
    'MibScalarInstance'
)
RowStatus, = mibBuilder.importSymbols('SNMPv2-TC', 'RowStatus')
mibBuilder.exportSymbols(
    '__EXAMPLE-MIB',
    # table object
    exampleTable=MibTable((1, 3, 6, 6, 1)).setMaxAccess('readcreate'),
    # table row object, also carries references to table indices
    exampleTableEntry=MibTableRow((1, 3, 6, 6, 1, 5)).setMaxAccess('readcreate').setIndexNames((0, '__EXAMPLE-MIB', 'exampleTableColumn1')),
    # table column: string index
    exampleTableColumn1=MibTableColumn((1, 3, 6, 6, 1, 5, 1), v2c.OctetString()).setMaxAccess('readcreate'),
    # table column: string value
    exampleTableColumn2=MibTableColumn((1, 3, 6, 6, 1, 5, 2), v2c.OctetString()).setMaxAccess('readcreate'),
    # table column: integer value with default
    exampleTableColumn3=MibTableColumn((1, 3, 6, 6, 1, 5, 3), v2c.Integer32(123)).setMaxAccess('readcreate'),
    # table column: row status
    exampleTableStatus=MibTableColumn((1, 3, 6, 6, 1, 5, 4), RowStatus('notExists')).setMaxAccess('readcreate')
)
# --- end of custom SNMP table definition, empty table now exists ---
# --- populate custom SNMP table with one row ---
(exampleTableEntry,
 exampleTableColumn2,
 exampleTableColumn3,
 exampleTableStatus) = mibBuilder.importSymbols(
    '__EXAMPLE-MIB',
    'exampleTableEntry',
    'exampleTableColumn2',
    'exampleTableColumn3',
    'exampleTableStatus'
)
rowInstanceId = exampleTableEntry.getInstIdFromIndices('example record one')
mibInstrumentation = snmpContext.getMibInstrum()
mibInstrumentation.writeVars(
    ((exampleTableColumn2.name + rowInstanceId, 'my string value'),
     (exampleTableColumn3.name + rowInstanceId, 123456),
     (exampleTableStatus.name + rowInstanceId, 'createAndGo'))
)
# --- end of SNMP table population ---
# 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.

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

  • SNMPv3
  • with USM username usr-md5-none
  • using alternative set of Managed Objects addressed by contextEngineId: 8000000001020304, contextName: my-context
  • allow access to SNMPv2-MIB objects (1.3.6.1.2.1)
  • over IPv4/UDP, listening at 127.0.0.1:161

Either of the following Net-SNMP commands will walk this Agent:

$ snmpwalk -v3 -u usr-md5-none -l authNoPriv -A authkey1 -E 8000000001020304 -n my-context 127.0.0.1 .1.3.6
$ snmpwalk -v3 -u usr-md5-none -l authNoPriv -A authkey1 -E 8000000001020304 127.0.0.1 .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.smi import instrum, builder
from pysnmp.proto.api import v2c
# Create SNMP engine
snmpEngine = engine.SnmpEngine()
# Transport setup
# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpTransport().openServerMode(('127.0.0.1', 161))
)
# SNMPv3/USM setup
# user: usr-md5-none, auth: MD5, priv NONE
config.addV3User(
    snmpEngine, 'usr-md5-none',
    config.usmHMACMD5AuthProtocol, 'authkey1'
)
# Allow full MIB access for each user at VACM
config.addVacmUser(snmpEngine, 3, 'usr-md5-none', 'authNoPriv', (1, 3, 6, 1, 2, 1), (1, 3, 6, 1, 2, 1))
# Create an SNMP context with ContextEngineId = 8000000001020304
snmpContext = context.SnmpContext(
    snmpEngine, contextEngineId=v2c.OctetString(hexValue='8000000001020304')
)
# Create an [empty] set of Managed Objects (MibBuilder), pass it to
# Management Instrumentation Controller and register at SNMP Context
# under ContextName 'my-context'
snmpContext.registerContextName(
    v2c.OctetString('my-context'),  # Context Name
    instrum.MibInstrumController(builder.MibBuilder())  # Managed Objects
)
# 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.

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

  • SNMPv3
  • with USM username usr-none-none
  • using alternative set of Managed Objects addressed by contextName: my-context
  • 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 send GET request to this Agent:

$ snmpget -v3 -u usr-none-none -l noAuthNoPriv -n my-context -Ir 127.0.0.1 sysDescr.0
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.smi import instrum
from pysnmp.proto.api import v2c
# Create SNMP engine
snmpEngine = engine.SnmpEngine()
# Transport setup
# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpTransport().openServerMode(('127.0.0.1', 161))
)
# SNMPv3/USM setup
# user: usr-none-none, auth: NONE, priv NONE
config.addV3User(
    snmpEngine, 'usr-none-none'
)
# Allow full MIB access for each user at VACM
config.addVacmUser(snmpEngine, 3, 'usr-none-none', 'noAuthNoPriv', (1, 3, 6, 1, 2, 1), (1, 3, 6, 1, 2, 1))
# Create an SNMP context
snmpContext = context.SnmpContext(snmpEngine)
# Very basic Management Instrumentation Controller without
# any Managed Objects attached. It supports only GET's and
# always echos request var-binds in response.
class EchoMibInstrumController(instrum.AbstractMibInstrumController):
    def readVars(self, varBinds, acInfo=(None, None)):
        return [(ov[0], v2c.OctetString('You queried OID %s' % ov[0])) for ov in varBinds]
# Create a custom Management Instrumentation Controller and register at
# SNMP Context under ContextName 'my-context'
snmpContext.registerContextName(
    v2c.OctetString('my-context'),  # Context Name
    EchoMibInstrumController()  # Management Instrumentation
)
# Register GET&SET Applications at the SNMP engine for a custom SNMP context
cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
cmdrsp.SetCommandResponder(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.

See also: library reference.

Transport tweaks

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

  • SNMPv2c
  • with SNMP community "public"
  • allow access to SNMPv2-MIB objects (1.3.6.1.2.1)
  • over IPv4/UDP, listening at 127.0.0.1:161 and over IPv6/UDP, listening at [::1]:161

Either of the following Net-SNMP commands will walk this Agent:

$ snmpwalk -v2c -c public 127.0.0.1 .1.3.6
$ snmpwalk -v2c -c public udp6:[::1] .1.3.6
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.carrier.asyncore.dgram import udp, udp6
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
# Transport setup
# UDP over IPv4 at 127.0.0.1:161
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpTransport().openServerMode(('127.0.0.1', 161))
)
# UDP over IPv6 at [::1]:161
config.addTransport(
    snmpEngine,
    udp6.domainName,
    udp6.Udp6Transport().openServerMode(('::1', 161))
)
# SNMPv2c setup
# SecurityName <-> CommunityName mapping.
config.addV1System(snmpEngine, 'my-area', 'public')
# Allow full MIB access for this user / securityModels at VACM
config.addVacmUser(snmpEngine, 2, 'my-area', 'noAuthNoPriv', (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.

Listen on multiple network interfaces

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

  • SNMPv2c
  • with SNMP community "public"
  • allow access to SNMPv2-MIB objects (1.3.6.1.2.1)
  • over IPv4/UDP, listening at 127.0.0.1:161 and 127.0.0.2:161 interfaces

Either of the following Net-SNMP commands will walk this Agent:

$ snmpwalk -v2c -c public 127.0.0.1 .1.3.6
$ snmpwalk -v2c -c public 127.0.0.2 .1.3.6
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 at 127.0.0.1:161
config.addTransport(
    snmpEngine,
    udp.domainName + (1,),
    udp.UdpTransport().openServerMode(('127.0.0.1', 161))
)
# UDP over IPv4 at 127.0.0.2:161
config.addTransport(
    snmpEngine,
    udp.domainName + (2,),
    udp.UdpTransport().openServerMode(('127.0.0.2', 161))
)
# SNMPv2c setup
# SecurityName <-> CommunityName mapping.
config.addV1System(snmpEngine, 'my-area', 'public')
# Allow full MIB access for this user / securityModels at VACM
config.addVacmUser(snmpEngine, 2, 'my-area', 'noAuthNoPriv', (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.

Listen on all local IPv4 interfaces respond to SNMP GET/SET/GETNEXT/GETBULK queries with the following options:

  • SNMPv3
  • 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 0.0.0.0:161
  • preserve local IP address when responding (Python 3.3+ required)

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

In the situation when UDP responder receives a datagram targeted to a secondary (AKA virtial) IP interface or a non-local IP interface (e.g. routed through policy routing or iptables TPROXY facility), OS stack will by default put primary local IP interface address into the IP source field of the response IP packet. Such datagram may not reach the sender as either the sender itself or a stateful firewall somewhere in between would not be able to match response to original request.

The following script solves this problem by preserving original request destination IP address and put it back into response IP packet's source address field.

To respond from a non-local (e.g. spoofed) IP address, uncomment the .enableTransparent() method call and run this script as root.

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()
# Transport setup
# Initialize asyncore-based UDP/IPv4 transport
udpSocketTransport = udp.UdpSocketTransport().openServerMode(('0.0.0.0', 161))
# Use sendmsg()/recvmsg() for socket communication (used for preserving
# original destination IP address when responding)
udpSocketTransport.enablePktInfo()
# Enable IP source spoofing (requires root privileges)
# udpSocketTransport.enableTransparent()
# Register this transport at SNMP Engine
config.addTransport(
    snmpEngine,
    udp.domainName,
    udpSocketTransport
)
# 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.

See also: library reference.

Advanced topics

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.

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.

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.

Various SNMP versions

SNMPv1 TRAP

Send SNMP notification using the following options:

  • SNMPv1
  • with community name 'public'
  • over IPv4/UDP
  • to a Manager at 104.236.166.95:162
  • send TRAP notification
  • with TRAP ID 'coldStart' specified as an OID
  • include managed objects information:
  • overriding Uptime value with 12345
  • overriding Agent Address with '104.236.166.95'
  • overriding Enterprise OID with 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'

Functionally similar to:

$ snmptrap -v1 -c public demo.snmplabs.com 1.3.6.1.4.1.20408.4.1.1.2 104.236.166.95 6 432 12345 1.3.6.1.2.1.1.1.0 s "my system"
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntforg
from pysnmp.proto.api import v2c
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public', transportTag='all-my-managers')
# Specify security settings per SecurityName (SNMPv1 -> 0)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 0)
# Setup transport endpoint and bind it with security settings yielding
# a target name
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-nms',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Specify what kind of notification should be sent (TRAP or INFORM),
# to what targets (chosen by tag) and what filter should apply to
# the set of targets (selected by tag)
config.addNotificationTarget(
    snmpEngine, 'my-notification', 'my-filter', 'all-my-managers', 'trap'
)
# Allow NOTIFY access to Agent's MIB by this SNMP model (1), securityLevel
# and SecurityName
config.addContext(snmpEngine, '')
config.addVacmUser(snmpEngine, 1, 'my-area', 'noAuthNoPriv', (), (), (1, 3, 6))
# *** SNMP engine configuration is complete by this line ***
# Create Notification Originator App instance. 
ntfOrg = ntforg.NotificationOriginator()
# Build and submit notification message to dispatcher
ntfOrg.sendVarBinds(
    snmpEngine,
    # Notification targets
    'my-notification',  # notification targets
    None, '',  # contextEngineId, contextName
    # var-binds
    [
        # Uptime value with 12345
        (v2c.ObjectIdentifier('1.3.6.1.2.1.1.3.0'),
         v2c.TimeTicks(12345)),
        # trap OID: Generic Trap #6 (enterpriseSpecific)
        #           and Specific Trap 432
        (v2c.ObjectIdentifier('1.3.6.1.6.3.1.1.5.1'),
         v2c.ObjectIdentifier('1.3.6.1.4.1.20408.4.1.1.2.0.432')),
        # Agent Address with '127.0.0.1'
        (v2c.ObjectIdentifier('1.3.6.1.6.3.18.1.3.0'),
         v2c.IpAddress('127.0.0.1')),
        # Enterprise OID with 1.3.6.1.4.1.20408.4.1.1.2
        (v2c.ObjectIdentifier('1.3.6.1.6.3.1.1.4.3.0'),
         v2c.ObjectIdentifier('1.3.6.1.4.1.20408.4.1.1.2')),
        # managed object '1.3.6.1.2.1.1.1.0' = 'my system'
        (v2c.ObjectIdentifier('1.3.6.1.2.1.1.1.0'),
         v2c.OctetString('my system'))
    ]
)
print('Notification is scheduled to be sent')
# Run I/O dispatcher which would send pending message and stop
snmpEngine.transportDispatcher.runDispatcher()

Download script.

SNMPv2c TRAP

Send SNMP TRAP notification using the following options:

  • SNMPv2c
  • with community name 'public'
  • over IPv4/UDP
  • send TRAP notification
  • to a Manager at 104.236.166.95:162
  • with TRAP ID 'coldStart' specified as an OID
  • include managed objects information: 1.3.6.1.2.1.1.1.0 = 'Example Notificator' 1.3.6.1.2.1.1.5.0 = 'Notificator Example'

Functionally similar to:

$ snmptrap -v2c -c public 104.236.166.95 12345 1.3.6.1.4.1.20408.4.1.1.2
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntforg
from pysnmp.proto.api import v2c
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public', transportTag='all-my-managers')
# Specify security settings per SecurityName (SNMPv2c -> 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 1)
# Setup transport endpoint and bind it with security settings yielding
# a target name
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-nms',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Specify what kind of notification should be sent (TRAP or INFORM),
# to what targets (chosen by tag) and what filter should apply to
# the set of targets (selected by tag)
config.addNotificationTarget(
    snmpEngine, 'my-notification', 'my-filter', 'all-my-managers', 'trap'
)
# Allow NOTIFY access to Agent's MIB by this SNMP model (2), securityLevel
# and SecurityName
config.addContext(snmpEngine, '')
config.addVacmUser(snmpEngine, 2, 'my-area', 'noAuthNoPriv', (), (), (1, 3, 6))
# *** SNMP engine configuration is complete by this line ***
# Create Notification Originator App instance. 
ntfOrg = ntforg.NotificationOriginator()
# Error/confirmation receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    print('Notification %s, status - %s' % (
        sendRequestHandle, errorIndication and errorIndication or 'delivered'
    )
          )
# Build and submit notification message to dispatcher
sendRequestHandle = ntfOrg.sendVarBinds(
    snmpEngine,
    # Notification targets
    'my-notification',  # notification targets
    None, '',  # contextEngineId, contextName
    # var-binds
    [
        # SNMPv2-SMI::snmpTrapOID.0 = SNMPv2-MIB::coldStart
        ((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0), v2c.ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 5, 1))),
        # additional var-binds: ( (oid, value), ... )
        ((1, 3, 6, 1, 2, 1, 1, 1, 0), v2c.OctetString('Example Notificator')),
        ((1, 3, 6, 1, 2, 1, 1, 5, 0), v2c.OctetString('Notificator Example'))
    ],
    cbFun
)
print('Notification %s is scheduled to be sent' % sendRequestHandle)
# Run I/O dispatcher which would send pending message and process response
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send SNMP TRAP notification using the following options:

  • SNMPv3
  • with user 'usr-md5-des', auth: MD5, priv DES
  • over IPv4/UDP
  • send TRAP notification
  • to a Manager at 104.236.166.95:162
  • with TRAP ID 'warmStart' specified as an OID
  • include managed object information 1.3.6.1.2.1.1.5.0 = 'system name'

Functionally similar to:

$ snmptrap -v3 -l authPriv -u usr-md5-des -A authkey1 -X privkey1 -e 8000000001020304 demo.snmplabs.com 0 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.1.0 s "my system"
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntforg
from pysnmp.proto.api import v2c
# Create SNMP engine instance with specific (and locally unique)
# SnmpEngineId -- it must also be known to the receiving party
# and configured at its VACM users table.
snmpEngine = engine.SnmpEngine(
    snmpEngineID=v2c.OctetString(hexValue='8000000001020304')
)
# Add USM user
config.addV3User(
    snmpEngine, 'usr-md5-des',
    config.usmHMACMD5AuthProtocol, 'authkey1',
    config.usmDESPrivProtocol, 'privkey1'
)
config.addTargetParams(snmpEngine, 'my-creds', 'usr-md5-des', 'authPriv')
# Setup transport endpoint and bind it with security settings yielding
# a target name
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-nms',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Specify what kind of notification should be sent (TRAP or INFORM),
# to what targets (chosen by tag) and what filter should apply to
# the set of targets (selected by tag)
config.addNotificationTarget(
    snmpEngine, 'my-notification', 'my-filter', 'all-my-managers', 'trap'
)
# Allow NOTIFY access to Agent's MIB by this SNMP model (3), securityLevel
# and SecurityName
config.addContext(snmpEngine, '')
config.addVacmUser(snmpEngine, 3, 'usr-md5-des', 'authPriv', (), (), (1, 3, 6))
# *** SNMP engine configuration is complete by this line ***
# Create Notification Originator App instance. 
ntfOrg = ntforg.NotificationOriginator()
# Build and submit notification message to dispatcher
ntfOrg.sendVarBinds(
    snmpEngine,
    # Notification targets
    'my-notification',  # notification targets
    None, '',  # contextEngineId, contextName
    # var-binds
    [
        # SNMPv2-SMI::snmpTrapOID.0 = SNMPv2-MIB::coldStart
        ((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0), v2c.ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 5, 1))),
        # additional var-binds: ( (oid, value), ... )
        ((1, 3, 6, 1, 2, 1, 1, 5, 0), v2c.OctetString('Notificator Example'))
    ]
)
print('Notification is scheduled to be sent')
# Run I/O dispatcher which would send pending message and process response
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send SNMP INFORM notification using the following options:

  • SNMPv3
  • with user 'usr-md5-none', auth: MD5, priv NONE
  • over IPv4/UDP
  • to a Manager at demo.snmplabs.com:162
  • send INFORM notification
  • with TRAP ID 'warmStart' specified as an OID
  • include managed object information 1.3.6.1.2.1.1.5.0 = 'system name'

Functionally similar to:

$ snmpinform -v3 -l authNoPriv -u usr-md5-none -A authkey1 demo.snmplabs.com  0 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.5.0 = 'system name'
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntforg
from pysnmp.proto.api import v2c
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# Add USM user
config.addV3User(
    snmpEngine, 'usr-md5-none',
    config.usmHMACMD5AuthProtocol, 'authkey1'
)
config.addTargetParams(snmpEngine, 'my-creds', 'usr-md5-none', 'authNoPriv')
# Setup transport endpoint and bind it with security settings yielding
# a target name
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-nms',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Specify what kind of notification should be sent (TRAP or INFORM),
# to what targets (chosen by tag) and what filter should apply to
# the set of targets (selected by tag)
config.addNotificationTarget(
    snmpEngine, 'my-notification', 'my-filter', 'all-my-managers', 'inform'
)
# Allow NOTIFY access to Agent's MIB by this SNMP model (3), securityLevel
# and SecurityName
config.addContext(snmpEngine, '')
config.addVacmUser(snmpEngine, 3, 'usr-md5-none', 'authNoPriv', (), (), (1, 3, 6))
# *** SNMP engine configuration is complete by this line ***
# Create Notification Originator App instance. 
ntfOrg = ntforg.NotificationOriginator()
# Error/confirmation receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    print('Notification %s, status - %s' % (
        sendRequestHandle, errorIndication and errorIndication or 'delivered'
    )
          )
# Build and submit notification message to dispatcher
sendRequestHandle = ntfOrg.sendVarBinds(
    snmpEngine,
    'my-notification',  # notification targets
    None, '',  # contextEngineId, contextName
    # var-binds: SNMPv2-MIB::coldStart, ...
    [((1, 3, 6, 1, 6, 3, 1, 1, 5, 1), v2c.ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 5, 1))),
     ((1, 3, 6, 1, 2, 1, 1, 5, 0), v2c.OctetString('system name'))],
    cbFun
)
print('Notification %s scheduled to be sent' % sendRequestHandle)
# Run I/O dispatcher which would send pending message and process response
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library-reference.

Common notifications

SNMPv1 TRAP

Send SNMP notification using the following options:

  • SNMPv1
  • with community name 'public'
  • over IPv4/UDP
  • to a Manager at 104.236.166.95:162
  • send TRAP notification
  • with TRAP ID 'coldStart' specified as an OID
  • include managed objects information:
  • overriding Uptime value with 12345
  • overriding Agent Address with '104.236.166.95'
  • overriding Enterprise OID with 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'

Functionally similar to:

$ snmptrap -v1 -c public demo.snmplabs.com 1.3.6.1.4.1.20408.4.1.1.2 104.236.166.95 6 432 12345 1.3.6.1.2.1.1.1.0 s "my system"
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntforg
from pysnmp.proto.api import v2c
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public', transportTag='all-my-managers')
# Specify security settings per SecurityName (SNMPv1 -> 0)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 0)
# Setup transport endpoint and bind it with security settings yielding
# a target name
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-nms',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Specify what kind of notification should be sent (TRAP or INFORM),
# to what targets (chosen by tag) and what filter should apply to
# the set of targets (selected by tag)
config.addNotificationTarget(
    snmpEngine, 'my-notification', 'my-filter', 'all-my-managers', 'trap'
)
# Allow NOTIFY access to Agent's MIB by this SNMP model (1), securityLevel
# and SecurityName
config.addContext(snmpEngine, '')
config.addVacmUser(snmpEngine, 1, 'my-area', 'noAuthNoPriv', (), (), (1, 3, 6))
# *** SNMP engine configuration is complete by this line ***
# Create Notification Originator App instance. 
ntfOrg = ntforg.NotificationOriginator()
# Build and submit notification message to dispatcher
ntfOrg.sendVarBinds(
    snmpEngine,
    # Notification targets
    'my-notification',  # notification targets
    None, '',  # contextEngineId, contextName
    # var-binds
    [
        # Uptime value with 12345
        (v2c.ObjectIdentifier('1.3.6.1.2.1.1.3.0'),
         v2c.TimeTicks(12345)),
        # trap OID: Generic Trap #6 (enterpriseSpecific)
        #           and Specific Trap 432
        (v2c.ObjectIdentifier('1.3.6.1.6.3.1.1.5.1'),
         v2c.ObjectIdentifier('1.3.6.1.4.1.20408.4.1.1.2.0.432')),
        # Agent Address with '127.0.0.1'
        (v2c.ObjectIdentifier('1.3.6.1.6.3.18.1.3.0'),
         v2c.IpAddress('127.0.0.1')),
        # Enterprise OID with 1.3.6.1.4.1.20408.4.1.1.2
        (v2c.ObjectIdentifier('1.3.6.1.6.3.1.1.4.3.0'),
         v2c.ObjectIdentifier('1.3.6.1.4.1.20408.4.1.1.2')),
        # managed object '1.3.6.1.2.1.1.1.0' = 'my system'
        (v2c.ObjectIdentifier('1.3.6.1.2.1.1.1.0'),
         v2c.OctetString('my system'))
    ]
)
print('Notification is scheduled to be sent')
# Run I/O dispatcher which would send pending message and stop
snmpEngine.transportDispatcher.runDispatcher()

Download script.

SNMPv2c TRAP

Send SNMP TRAP notification using the following options:

  • SNMPv2c
  • with community name 'public'
  • over IPv4/UDP
  • send TRAP notification
  • to a Manager at 104.236.166.95:162
  • with TRAP ID 'coldStart' specified as an OID
  • include managed objects information: 1.3.6.1.2.1.1.1.0 = 'Example Notificator' 1.3.6.1.2.1.1.5.0 = 'Notificator Example'

Functionally similar to:

$ snmptrap -v2c -c public 104.236.166.95 12345 1.3.6.1.4.1.20408.4.1.1.2
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntforg
from pysnmp.proto.api import v2c
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public', transportTag='all-my-managers')
# Specify security settings per SecurityName (SNMPv2c -> 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 1)
# Setup transport endpoint and bind it with security settings yielding
# a target name
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-nms',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Specify what kind of notification should be sent (TRAP or INFORM),
# to what targets (chosen by tag) and what filter should apply to
# the set of targets (selected by tag)
config.addNotificationTarget(
    snmpEngine, 'my-notification', 'my-filter', 'all-my-managers', 'trap'
)
# Allow NOTIFY access to Agent's MIB by this SNMP model (2), securityLevel
# and SecurityName
config.addContext(snmpEngine, '')
config.addVacmUser(snmpEngine, 2, 'my-area', 'noAuthNoPriv', (), (), (1, 3, 6))
# *** SNMP engine configuration is complete by this line ***
# Create Notification Originator App instance. 
ntfOrg = ntforg.NotificationOriginator()
# Error/confirmation receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    print('Notification %s, status - %s' % (
        sendRequestHandle, errorIndication and errorIndication or 'delivered'
    )
          )
# Build and submit notification message to dispatcher
sendRequestHandle = ntfOrg.sendVarBinds(
    snmpEngine,
    # Notification targets
    'my-notification',  # notification targets
    None, '',  # contextEngineId, contextName
    # var-binds
    [
        # SNMPv2-SMI::snmpTrapOID.0 = SNMPv2-MIB::coldStart
        ((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0), v2c.ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 5, 1))),
        # additional var-binds: ( (oid, value), ... )
        ((1, 3, 6, 1, 2, 1, 1, 1, 0), v2c.OctetString('Example Notificator')),
        ((1, 3, 6, 1, 2, 1, 1, 5, 0), v2c.OctetString('Notificator Example'))
    ],
    cbFun
)
print('Notification %s is scheduled to be sent' % sendRequestHandle)
# Run I/O dispatcher which would send pending message and process response
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send SNMP INFORM notification using the following options:

  • SNMPv2c
  • with community name 'public'
  • over IPv4/UDP
  • send INFORM notification
  • to a Manager at 104.236.166.95:162
  • with TRAP ID 'coldStart' specified as an OID
  • include managed objects information: 1.3.6.1.2.1.1.1.0 = 'Example Notificator' 1.3.6.1.2.1.1.5.0 = 'Notificator Example'

Functionally similar to:

$ snmpinform -v2c -c public 104.236.166.95 12345 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.1.0 s 'Example Notificator' 1.3.6.1.2.1.1.5.0 s 'Notificator Example'
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntforg
from pysnmp.proto.api import v2c
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# SecurityName <-> CommunityName mapping (+ transport binding)
config.addV1System(snmpEngine, 'my-area', 'public',
                   transportTag='all-my-managers')
# Specify security settings per SecurityName (SNMPv2c -> 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 1)
# Setup transport endpoint and bind it with security settings yielding
# a target name
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-nms',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Specify what kind of notification should be sent (TRAP or INFORM),
# to what targets (chosen by tag) and what filter should apply to
# the set of targets (selected by tag)
config.addNotificationTarget(
    snmpEngine, 'my-notification', 'my-filter', 'all-my-managers', 'inform'
)
# Allow NOTIFY access to Agent's MIB by this SNMP model (2), securityLevel
# and SecurityName
config.addContext(snmpEngine, '')
config.addVacmUser(snmpEngine, 2, 'my-area', 'noAuthNoPriv', (), (), (1, 3, 6))
# *** SNMP engine configuration is complete by this line ***
# Create Notification Originator App instance. 
ntfOrg = ntforg.NotificationOriginator()
# Error/confirmation receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    print('Notification %s, status - %s' % (
        sendRequestHandle, errorIndication and errorIndication or 'delivered'
    )
          )
# Build and submit notification message to dispatcher
sendRequestHandle = ntfOrg.sendVarBinds(
    snmpEngine,
    'my-notification',  # notification targets
    None, '',  # contextEngineId, contextName
    # var-binds
    [
        # SNMPv2-SMI::snmpTrapOID.0 = SNMPv2-MIB::coldStart
        ((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0), v2c.ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 5, 1))),
        # additional var-binds: ( (oid, value), ... )
        ((1, 3, 6, 1, 2, 1, 1, 1, 0), v2c.OctetString('Example Notificator')),
        ((1, 3, 6, 1, 2, 1, 1, 5, 0), v2c.OctetString('Notificator Example'))
    ],
    cbFun
)
print('Notification %s scheduled to be sent' % sendRequestHandle)
# Run I/O dispatcher which would send pending message and process response
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library reference.

Evaluating NOTIFICATION-TYPE

SNMP SMI defines notifications as a TRAP or INFORM PDU containing the indication of type (snmpTrapOID) and a set of MIB variables (Managed Objects Instances) fetched from Agent's MIB at the moment of notification.

Consequently, sending specific NOTIFICATION-TYPE implies including certain set of OIDs into PDU. PySNMP offers this facility through NotificationType class.

Send SNMP TRAP notification using the following options:

  • SNMPv2c
  • with community name 'public'
  • over IPv4/UDP
  • send TRAP notification
  • to a Manager at 104.236.166.95:162
  • with TRAP ID IF-MIB::ifLink as MIB symbol

The IF-MIB::ifLink NOTIFICATION-TYPE implies including four other var-binds into the notification message describing the incident occurred. These var-binds are: IF-MIB::ifIndex."x" IF-MIB::ifAdminStatus."x" IF-MIB::ifOperStatus."x" IF-MIB::ifDescr."x"

Where "x" is MIB table index (instance index).

Functionally similar to:

$ snmptrap -v2c -c public 104.236.166.95 0 1.3.6.1.6.3.1.1.5.3 IF-MIB::ifIndex."1" IF-MIB::ifAdminStatus."1" IF-MIB::ifOperStatus."1" IF-MIB::ifDescr."1"
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntforg
from pysnmp.smi import rfc1902, view
#
# Here we fill in some values for Managed Objects Instances (invoked
# later while building TRAP message) by NOTIFICATION-TYPE macro evaluation.
# In real Agent app, these values should already be initialized during
# Agent runtime.
#
instanceIndex = (1,)
objects = {
    ('IF-MIB', 'ifIndex'): instanceIndex[0],
    ('IF-MIB', 'ifAdminStatus'): 'up',
    ('IF-MIB', 'ifOperStatus'): 'down',
    ('IF-MIB', 'ifDescr'): 'eth0'
}
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# MIB view controller is used for MIB lookup purposes
mibViewController = view.MibViewController(snmpEngine.getMibBuilder())
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public', transportTag='all-my-managers')
# Specify security settings per SecurityName (SNMPv2c -> 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 1)
# Setup transport endpoints and bind it with security settings yielding
# a target name:
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-nms-1',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Specify what kind of notification should be sent (TRAP or INFORM),
# to what targets (chosen by tag) and what filter should apply to
# the set of targets (selected by tag)
config.addNotificationTarget(
    snmpEngine, 'my-notification', 'my-filter', 'all-my-managers', 'trap'
)
# Allow NOTIFY access to Agent's MIB by this SNMP model (2), securityLevel
# and SecurityName
config.addContext(snmpEngine, '')
config.addVacmUser(snmpEngine, 2, 'my-area', 'noAuthNoPriv', (), (), (1, 3, 6))
# *** SNMP engine configuration is complete by this line ***
# Create Notification Originator App instance. 
ntfOrg = ntforg.NotificationOriginator()
# Build and submit notification message to dispatcher
ntfOrg.sendVarBinds(
    snmpEngine,
    'my-notification',  # notification targets
    None, '',  # contextEngineId, contextName
    rfc1902.NotificationType(
        rfc1902.ObjectIdentity('IF-MIB', 'linkUp'),
        instanceIndex=instanceIndex,
        objects=objects
    ).resolveWithMib(mibViewController)
)
print('Notification is scheduled to be sent')
# Run I/O dispatcher which would send pending message and process response
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library reference.

SNMPv3 framework is designed to allow Agents sending the same PDU to multiple Managers over different network transports, listening at different network addresses over different SNMP versions/credentials.

The following few examples use this facility.

Send SNMP TRAP notifications to multiple Managers using different security settings:

  • SNMPv2c
  • with community name 'public'
  • over IPv4/UDP
  • send TRAP notification
  • to multiple Managers at 104.236.166.95:162, 104.236.166.95:162
  • with TRAP ID 'coldStart' specified as an OID
  • include managed objects information: 1.3.6.1.2.1.1.1.0 = 'Example Notificator' 1.3.6.1.2.1.1.5.0 = 'Notificator Example'

Functionally similar to:

$ snmptrap -v2c -c public 104.236.166.95 0 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.1.0 s 'Example notification' 1.3.6.1.2.1.1.5.0 s 'Notificator Example'
$ snmptrap -v2c -c public 104.236.166.95 0 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.1.0 s 'Example notification' 1.3.6.1.2.1.1.5.0 s 'Notificator Example'
$ snmptrap -v2c -c public 104.236.166.95 0 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.1.0 s 'Example notification' 1.3.6.1.2.1.1.5.0 s 'Notificator Example'
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntforg
from pysnmp.proto.api import v2c
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public', transportTag='all-my-managers')
# Specify security settings per SecurityName (SNMPv2c -> 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 1)
# Setup transport endpoint and bind it with security settings yielding
# a target name
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
# First target
config.addTargetAddr(
    snmpEngine, 'my-nms-1',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Second target
config.addTargetAddr(
    snmpEngine, 'my-nms-2',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Third target
config.addTargetAddr(
    snmpEngine, 'my-nms-3',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Specify what kind of notification should be sent (TRAP or INFORM),
# to what targets (chosen by tag) and what filter should apply to
# the set of targets (selected by tag)
config.addNotificationTarget(
    snmpEngine, 'my-notification', 'my-filter', 'all-my-managers', 'trap'
)
# Allow NOTIFY access to Agent's MIB by this SNMP model (2), securityLevel
# and SecurityName
config.addContext(snmpEngine, '')
config.addVacmUser(snmpEngine, 2, 'my-area', 'noAuthNoPriv', (), (), (1, 3, 6))
# *** SNMP engine configuration is complete by this line ***
# Create Notification Originator App instance. 
ntfOrg = ntforg.NotificationOriginator()
# Build and submit notification message to dispatcher
ntfOrg.sendVarBinds(
    snmpEngine,
    # Notification targets
    'my-notification',  # notification targets
    None, '',  # contextEngineId, contextName
    # var-binds
    [
        # SNMPv2-SMI::snmpTrapOID.0 = SNMPv2-MIB::coldStart
        ((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0), v2c.ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 5, 1))),
        # additional var-binds: ( (oid, value), ... )
        ((1, 3, 6, 1, 2, 1, 1, 1, 0), v2c.OctetString('Example Notificator')),
        ((1, 3, 6, 1, 2, 1, 1, 5, 0), v2c.OctetString('Notificator Example'))
    ]
)
print('Notifications are scheduled to be sent')
# Run I/O dispatcher which would send pending message and process response
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send SNMP TRAP notifications to multiple Managers using the following options:

  • SNMPv2c
  • with community name 'public'
  • over IPv4/UDP
  • send TRAP notification
  • to multiple Managers at 104.236.166.95:162, 104.236.166.95:162
  • with TRAP ID 'coldStart' specified as an OID
  • include managed objects information: 1.3.6.1.2.1.1.1.0 = 'Example Notificator' 1.3.6.1.2.1.1.5.0 = 'Notificator Example'

Functionally similar to:

$ snmptrap -v2c -c public 104.236.166.95 0 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.1.0 s 'Example notification' 1.3.6.1.2.1.1.5.0 s 'Notificator Example'
$ snmptrap -v2c -c public 104.236.166.95 0 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.1.0 s 'Example notification' 1.3.6.1.2.1.1.5.0 s 'Notificator Example'
$ snmptrap -v2c -c public 104.236.166.95 0 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.1.0 s 'Example notification' 1.3.6.1.2.1.1.5.0 s 'Notificator Example'
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntforg
from pysnmp.proto.api import v2c
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public', transportTag='all-my-managers')
# Specify security settings per SecurityName (SNMPv2c -> 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 1)
# Setup transport endpoint and bind it with security settings yielding
# a target name
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
# First target
config.addTargetAddr(
    snmpEngine, 'my-nms-1',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Second target
config.addTargetAddr(
    snmpEngine, 'my-nms-2',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Third target
config.addTargetAddr(
    snmpEngine, 'my-nms-3',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Specify what kind of notification should be sent (TRAP or INFORM)
# to what targets (chosen by tag) and with what credentials.
config.addNotificationTarget(
    snmpEngine, 'my-notification', 'my-creds', 'all-my-managers', 'trap'
)
# Allow NOTIFY access to Agent's MIB by this SNMP model (2), securityLevel
# and SecurityName
config.addContext(snmpEngine, '')
config.addVacmUser(snmpEngine, 2, 'my-area', 'noAuthNoPriv', (), (), (1, 3, 6))
# *** SNMP engine configuration is complete by this line ***
# Create Notification Originator App instance. 
ntfOrg = ntforg.NotificationOriginator()
# Build and submit notification message to dispatcher
ntfOrg.sendVarBinds(
    snmpEngine,
    # Notification targets
    'my-notification',  # notification targets
    None, '',  # contextEngineId, contextName
    # var-binds
    [
        # SNMPv2-SMI::snmpTrapOID.0 = SNMPv2-MIB::coldStart
        ((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0), v2c.ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 5, 1))),
        # additional var-binds: ( (oid, value), ... )
        ((1, 3, 6, 1, 2, 1, 1, 1, 0), v2c.OctetString('Example Notificator')),
        ((1, 3, 6, 1, 2, 1, 1, 5, 0), v2c.OctetString('Notificator Example'))
    ]
)
print('Notifications are scheduled to be sent')
# Run I/O dispatcher which would send pending message and process response
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send SNMP INFORM notifications to multiple Managers using different security settings:

  • SNMPv2c
  • with community name 'public'
  • AND
  • SNMPv3
  • with user 'usr-md5-none', auth: MD5, priv NONE
  • over IPv4/UDP
  • send INFORM notification
  • to multiple Managers at 104.236.166.95:162, 104.236.166.95:162
  • with TRAP ID 'coldStart' specified as an OID
  • include managed objects information: 1.3.6.1.2.1.1.1.0 = 'Example Notificator'

Functionally similar to:

$ snmpinform -v3 -l authPriv -u usr-md5-none -A authkey1 104.236.166.95 0 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.1.0 s 'Example notification'
$ snmpinform -v2c -c public 104.236.166.95 0 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.1.0 s 'Example notification'
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntforg
from pysnmp.proto.api import v2c
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# SNMPv2c:
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public', transportTag='all-my-managers')
# Specify security settings per SecurityName (SNMPv2c -> 1)
config.addTargetParams(snmpEngine, 'my-creds-1', 'my-area', 'noAuthNoPriv', 1)
# SNMPv3:
config.addV3User(
    snmpEngine, 'usr-md5-none',
    config.usmHMACMD5AuthProtocol, 'authkey1'
)
config.addTargetParams(snmpEngine, 'my-creds-2', 'usr-md5-none', 'authNoPriv')
# Setup transport endpoint and bind it with security settings yielding
# a target name
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
# First target
config.addTargetAddr(
    snmpEngine, 'my-nms-1',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds-1',
    tagList='all-my-managers'
)
# Second target
config.addTargetAddr(
    snmpEngine, 'my-nms-2',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds-2',
    tagList='all-my-managers'
)
# Specify what kind of notification should be sent (TRAP or INFORM),
# to what targets (chosen by tag) and what filter should apply to
# the set of targets (selected by tag)
config.addNotificationTarget(
    snmpEngine, 'my-notification', 'my-filter', 'all-my-managers', 'inform'
)
# Allow NOTIFY access to Agent's MIB by this SNMP model (2&3), securityLevel
# and SecurityName
config.addContext(snmpEngine, '')
config.addVacmUser(snmpEngine, 2, 'my-area', 'noAuthNoPriv', (), (), (1, 3, 6))
config.addVacmUser(snmpEngine, 3, 'usr-md5-none', 'authNoPriv', (), (), (1, 3, 6))
# *** SNMP engine configuration is complete by this line ***
# Create Notification Originator App instance. 
ntfOrg = ntforg.NotificationOriginator()
# Error/confirmation receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    print('Notification %s, status - %s' % (sendRequestHandle,
                                            errorIndication and errorIndication or 'delivered'))
# Build and submit notification message to dispatcher
sendRequestHandle = ntfOrg.sendVarBinds(
    snmpEngine,
    'my-notification',  # notification targets
    None, '',  # contextEngineId, contextName
    # var-binds
    [
        # SNMPv2-SMI::snmpTrapOID.0 = SNMPv2-MIB::coldStart
        ((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0), v2c.ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 5, 1))),
        # additional var-binds: ( (oid, value), ... )
        ((1, 3, 6, 1, 2, 1, 1, 1, 0), v2c.OctetString('Example Notificator'))
    ],
    cbFun
)
print('Notifications %s are scheduled to be sent' % sendRequestHandle)
# Run I/O dispatcher which would send pending message and process response
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library reference.

Transport tweaks

Send SNMP INFORM notifications to multiple Managers over different network protocols:

  • SNMPv2c
  • with community name 'public'
  • over IPv4/UDP and UDP/IPv6
  • send TRAP notification
  • to two Managers through different network transports
  • with TRAP ID 'coldStart' specified as an OID
  • include managed objects information: 1.3.6.1.2.1.1.1.0 = 'Example Notificator' 1.3.6.1.2.1.1.5.0 = 'Notificator Example'

Functionally similar to:

$ snmptrap -v2c -c public udp:104.236.166.95 0 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.1.0 s 'Example notification' 1.3.6.1.2.1.1.5.0 s 'Notificator Example'
$ snmptrap -v2c -c public udp6:[::1] 0 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.1.0 s 'Example notification' 1.3.6.1.2.1.1.5.0 s 'Notificator Example'
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp, udp6
from pysnmp.entity.rfc3413 import ntforg
from pysnmp.proto.api import v2c
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public', transportTag='all-my-managers')
# Specify security settings per SecurityName (SNMPv2c -> 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 1)
# Setup transport endpoints and bind it with security settings yielding
# a target name:
# UDP/IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-nms-1',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# UDP/IPv6
config.addTransport(
    snmpEngine,
    udp6.domainName,
    udp6.Udp6SocketTransport().openClientMode()
)
config.addTargetAddr(
    snmpEngine, 'my-nms-2',
    udp6.domainName, ('::1', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Specify what kind of notification should be sent (TRAP or INFORM),
# to what targets (chosen by tag) and what filter should apply to
# the set of targets (selected by tag)
config.addNotificationTarget(
    snmpEngine, 'my-notification', 'my-filter', 'all-my-managers', 'trap'
)
# Allow NOTIFY access to Agent's MIB by this SNMP model (2), securityLevel
# and SecurityName
config.addContext(snmpEngine, '')
config.addVacmUser(snmpEngine, 2, 'my-area', 'noAuthNoPriv', (), (), (1, 3, 6))
# *** SNMP engine configuration is complete by this line ***
# Create Notification Originator App instance. 
ntfOrg = ntforg.NotificationOriginator()
# Build and submit notification message to dispatcher
ntfOrg.sendVarBinds(
    snmpEngine,
    # Notification targets
    'my-notification',  # notification targets
    None, '',  # contextEngineId, contextName
    # var-binds
    [
        # SNMPv2-SMI::snmpTrapOID.0 = SNMPv2-MIB::coldStart
        ((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0), v2c.ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 5, 1))),
        # additional var-binds: ( (oid, value), ... )
        ((1, 3, 6, 1, 2, 1, 1, 1, 0), v2c.OctetString('Example Notificator')),
        ((1, 3, 6, 1, 2, 1, 1, 5, 0), v2c.OctetString('Notificator Example'))
    ]
)
print('Notification is scheduled to be sent')
# Run I/O dispatcher which would send pending message and process response
snmpEngine.transportDispatcher.runDispatcher()

Download script.

Send SNMP notification using the following options:

  • SNMPv1
  • with community name 'public'
  • over IPv4/UDP
  • to a Manager at 104.236.166.95 UDP port 162
  • from local address 0.0.0.0, UDP port 61024
  • send TRAP notification
  • with TRAP ID 'coldStart' specified as an OID

Functionally similar to:

$ snmptrap -v1 -c public 104.236.166.95 1.3.6.1.6.3.1.1.5.1 0.0.0.0 1 0 0
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntforg
from pysnmp.proto.api import v2c
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public',
                   transportTag='all-my-managers')
# Specify security settings per SecurityName (SNMPv1 -> 0)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 0)
# Setup transport endpoint and bind it with security settings yielding
# a target name. Pay attention to the openClientMode() parameter -- it's
# used to originate packets from particular local IP:port
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode(iface=('0.0.0.0', 61024))
)
config.addTargetAddr(
    snmpEngine, 'my-nms',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds',
    tagList='all-my-managers'
)
# Specify what kind of notification should be sent (TRAP or INFORM),
# to what targets (chosen by tag) and what filter should apply to
# the set of targets (selected by tag)
config.addNotificationTarget(
    snmpEngine, 'my-notification', 'my-filter', 'all-my-managers', 'trap'
)
# Allow NOTIFY access to Agent's MIB by this SNMP model (1), securityLevel
# and SecurityName
config.addContext(snmpEngine, '')
config.addVacmUser(snmpEngine, 1, 'my-area', 'noAuthNoPriv', (), (), (1, 3, 6))
# *** SNMP engine configuration is complete by this line ***
# Create Notification Originator App instance. 
ntfOrg = ntforg.NotificationOriginator()
# Build and submit notification message to dispatcher
ntfOrg.sendVarBinds(
    snmpEngine,
    'my-notification',  # notification targets
    None, '',  # contextEngineId, contextName
    # var-binds
    [
        # SNMPv2-SMI::snmpTrapOID.0 = SNMPv2-MIB::coldStart
        ((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0), v2c.ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 5, 1)))
    ]
)
print('Notification is scheduled to be sent')
# Run I/O dispatcher which would send pending message and stop
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library reference.

Initialize TRAP PDU and pass it over to unified SNMPv3 message processing framework for further treatment.

  • SNMPv2c
  • with community name 'public'
  • over IPv4/UDP
  • send TRAP notification
  • initialize TRAP PDU with the following var-binds: 1.3.6.1.2.1.1.3.0 = 123 1.3.6.1.6.3.1.1.4.1.0 = 1.3.6.1.6.3.1.1.5.1

Functionally similar to:

$ snmptrap -v2c -c public demo.snmplabs.com 0 1.3.6.1.6.3.1.1.5.1
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntforg
from pysnmp.proto.api import v2c
# Create SNMP engine instance
snmpEngine = engine.SnmpEngine()
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Specify security settings per SecurityName (SNMPv2c -> 1)
config.addTargetParams(snmpEngine, 'my-creds', 'my-area', 'noAuthNoPriv', 1)
# Setup transport endpoint and bind it with security settings yielding
# a target name
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpSocketTransport().openClientMode()
)
# Create named target
config.addTargetAddr(
    snmpEngine, 'my-nms',
    udp.domainName, ('104.236.166.95', 162),
    'my-creds'
)
# *** SNMP engine configuration is complete by this line ***
# Create SNMP v2c TRAP PDU with defaults
trapPDU = v2c.TrapPDU()
v2c.apiTrapPDU.setDefaults(trapPDU)
# Set custom var-binds to TRAP PDU
v2c.apiTrapPDU.setVarBinds(
    trapPDU, [
        # sysUpTime
        (v2c.ObjectIdentifier('1.3.6.1.2.1.1.3.0'), v2c.TimeTicks(123)),
        # snmpTrapPDU
        ((1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0), v2c.ObjectIdentifier((1, 3, 6, 1, 6, 3, 1, 1, 5, 1)))
    ]
)
# Create Notification Originator App instance. 
ntfOrg = ntforg.NotificationOriginator()
# Error/confirmation receiver
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, sendRequestHandle, errorIndication,
          errorStatus, errorIndex, varBinds, cbCtx):
    print('Notification %s, status - %s' % (
        sendRequestHandle, errorIndication and errorIndication or 'delivered'
    )
          )
# Build and submit notification message to dispatcher
ntfOrg.sendPdu(
    snmpEngine,
    # Notification targets
    'my-nms',  # target address
    None, '',  # contextEngineId, contextName
    trapPDU,
    cbFun
)
print('Notification is scheduled to be sent')
# Run I/O dispatcher which would send pending message and process response
snmpEngine.transportDispatcher.runDispatcher()

Download script.

See also: library reference.

Notification Receiver Applications

Various SNMP versions

Serving multiple network interfaces

Receive SNMP TRAP/INFORM messages with the following options:

  • SNMPv1/SNMPv2c
  • with SNMP community "public"
  • over IPv4/UDP, listening at 127.0.0.1:162 over IPv4/UDP, listening at 127.0.0.1:2162
  • print received data on stdout

Either of the following Net-SNMP commands will send notifications to this receiver:

$ snmptrap -v2c -c public 127.0.0.1:162 123 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.5.0 s test
$ snmpinform -v2c -c public 127.0.0.1:2162 123 1.3.6.1.6.3.1.1.5.1
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntfrcv
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
# Transport setup
# UDP over IPv4, first listening interface/port
config.addTransport(
    snmpEngine,
    udp.domainName + (1,),
    udp.UdpTransport().openServerMode(('127.0.0.1', 162))
)
# UDP over IPv4, second listening interface/port
config.addTransport(
    snmpEngine,
    udp.domainName + (2,),
    udp.UdpTransport().openServerMode(('127.0.0.1', 2162))
)
# SNMPv1/2c setup
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Callback function for receiving notifications
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
          varBinds, cbCtx):
    print('Notification from ContextEngineId "%s", ContextName "%s"' % (contextEngineId.prettyPrint(),
                                                                        contextName.prettyPrint()))
    for name, val in varBinds:
        print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
# Register SNMP Application at the SNMP engine
ntfrcv.NotificationReceiver(snmpEngine, cbFun)
snmpEngine.transportDispatcher.jobStarted(1)  # this job would never finish
# Run I/O dispatcher which would receive queries and send confirmations
try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.transportDispatcher.closeDispatcher()
    raise

Download script.

Multiple SNMP USM users

Receive SNMP TRAP/INFORM messages with the following options:

  • SNMPv3
  • with USM users:
  • 'usr-md5-des', auth: MD5, priv DES, ContextEngineId: 8000000001020304
  • 'usr-md5-none', auth: MD5, priv NONE, ContextEngineId: 8000000001020304
  • 'usr-sha-aes128', auth: SHA, priv AES, ContextEngineId: 8000000001020304
  • over IPv4/UDP, listening at 127.0.0.1:162
  • print received data on stdout

Either of the following Net-SNMP commands will send notifications to this receiver:

$ snmptrap -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 -e 8000000001020304 127.0.0.1 123 1.3.6.1.6.3.1.1.5.1
$ snmptrap -v3 -u usr-md5-none -l authNoPriv -A authkey1 -e 8000000001020304 127.0.0.1 123 1.3.6.1.6.3.1.1.5.1
$ snmpinform -v3 -u usr-sha-aes128 -l authPriv -a SHA -A authkey1 -x AES -X privkey1 127.0.0.1 123 1.3.6.1.6.3.1.1.5.1
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntfrcv
from pysnmp.proto.api import v2c
# 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', 162))
)
# SNMPv3/USM setup
# user: usr-md5-des, auth: MD5, priv DES
config.addV3User(
    snmpEngine, 'usr-md5-des',
    config.usmHMACMD5AuthProtocol, 'authkey1',
    config.usmDESPrivProtocol, 'privkey1'
)
# user: usr-md5-des, auth: MD5, priv DES, securityEngineId: 8000000001020304
# this USM entry is used for TRAP receiving purposes
config.addV3User(
    snmpEngine, 'usr-md5-des',
    config.usmHMACMD5AuthProtocol, 'authkey1',
    config.usmDESPrivProtocol, 'privkey1',
    securityEngineId=v2c.OctetString(hexValue='8000000001020304')
)
# user: usr-md5-none, auth: MD5, priv NONE
config.addV3User(
    snmpEngine, 'usr-md5-none',
    config.usmHMACMD5AuthProtocol, 'authkey1'
)
# user: usr-md5-none, auth: MD5, priv NONE, securityEngineId: 8000000001020304
# this USM entry is used for TRAP receiving purposes
config.addV3User(
    snmpEngine, 'usr-md5-none',
    config.usmHMACMD5AuthProtocol, 'authkey1',
    securityEngineId=v2c.OctetString(hexValue='8000000001020304')
)
# user: usr-sha-aes128, auth: SHA, priv AES
config.addV3User(
    snmpEngine, 'usr-sha-aes128',
    config.usmHMACSHAAuthProtocol, 'authkey1',
    config.usmAesCfb128Protocol, 'privkey1'
)
# user: usr-sha-aes128, auth: SHA, priv AES, securityEngineId: 8000000001020304
# this USM entry is used for TRAP receiving purposes
config.addV3User(
    snmpEngine, 'usr-sha-aes128',
    config.usmHMACSHAAuthProtocol, 'authkey1',
    config.usmAesCfb128Protocol, 'privkey1',
    securityEngineId=v2c.OctetString(hexValue='8000000001020304')
)
# Callback function for receiving notifications
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
          varBinds, cbCtx):
    print('Notification from ContextEngineId "%s", ContextName "%s"' % (contextEngineId.prettyPrint(),
                                                                        contextName.prettyPrint()))
    for name, val in varBinds:
        print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
# Register SNMP Application at the SNMP engine
ntfrcv.NotificationReceiver(snmpEngine, cbFun)
snmpEngine.transportDispatcher.jobStarted(1)  # this job would never finish
# Run I/O dispatcher which would receive queries and send confirmations
try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.transportDispatcher.closeDispatcher()
    raise

Download script.

See also: library reference.

Transport tweaks

Serving multiple network interfaces

Receive SNMP TRAP/INFORM messages with the following options:

  • SNMPv1/SNMPv2c
  • with SNMP community "public"
  • over IPv4/UDP, listening at 127.0.0.1:162 over IPv4/UDP, listening at 127.0.0.1:2162
  • print received data on stdout

Either of the following Net-SNMP commands will send notifications to this receiver:

$ snmptrap -v2c -c public 127.0.0.1:162 123 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.5.0 s test
$ snmpinform -v2c -c public 127.0.0.1:2162 123 1.3.6.1.6.3.1.1.5.1
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntfrcv
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
# Transport setup
# UDP over IPv4, first listening interface/port
config.addTransport(
    snmpEngine,
    udp.domainName + (1,),
    udp.UdpTransport().openServerMode(('127.0.0.1', 162))
)
# UDP over IPv4, second listening interface/port
config.addTransport(
    snmpEngine,
    udp.domainName + (2,),
    udp.UdpTransport().openServerMode(('127.0.0.1', 2162))
)
# SNMPv1/2c setup
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Callback function for receiving notifications
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
          varBinds, cbCtx):
    print('Notification from ContextEngineId "%s", ContextName "%s"' % (contextEngineId.prettyPrint(),
                                                                        contextName.prettyPrint()))
    for name, val in varBinds:
        print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
# Register SNMP Application at the SNMP engine
ntfrcv.NotificationReceiver(snmpEngine, cbFun)
snmpEngine.transportDispatcher.jobStarted(1)  # this job would never finish
# Run I/O dispatcher which would receive queries and send confirmations
try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.transportDispatcher.closeDispatcher()
    raise

Download script.

Receive SNMP TRAP/INFORM messages with the following options:

  • SNMPv1/SNMPv2c
  • with SNMP community "public"
  • over IPv4/UDP, listening at 127.0.0.1:162 over IPv6/UDP, listening at [::1]:162
  • print received data on stdout

Either of the following Net-SNMP commands will send notifications to this receiver:

$ snmptrap -v1 -c public 127.0.0.1 1.3.6.1.4.1.20408.4.1.1.2 127.0.0.1 1 1 123 1.3.6.1.2.1.1.1.0 s test
$ snmptrap -v2c -c public udp6:[::1]:162 123 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.5.0 s test
$ snmpinform -v2c -c public 127.0.0.1 123 1.3.6.1.6.3.1.1.5.1
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp, udp6
from pysnmp.entity.rfc3413 import ntfrcv
# 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', 162))
)
# UDP over IPv6
config.addTransport(
    snmpEngine,
    udp6.domainName,
    udp6.Udp6Transport().openServerMode(('::1', 162))
)
# SNMPv1/2c setup
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Callback function for receiving notifications
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
          varBinds, cbCtx):
    print('Notification from ContextEngineId "%s", ContextName "%s"' % (contextEngineId.prettyPrint(),
                                                                        contextName.prettyPrint()))
    for name, val in varBinds:
        print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
# Register SNMP Application at the SNMP engine
ntfrcv.NotificationReceiver(snmpEngine, cbFun)
snmpEngine.transportDispatcher.jobStarted(1)  # this job would never finish
# Run I/O dispatcher which would receive queries and send confirmations
try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.transportDispatcher.closeDispatcher()
    raise

Download script.

Receive SNMP TRAP/INFORM messages with the following options:

  • SNMPv1/SNMPv2c
  • with SNMP community "public"
  • over IPv4/UDP, listening at 127.0.0.1:162
  • use observer facility to pull lower-level request details from SNMP engine
  • print received data on stdout

Either of the following Net-SNMP commands will send notifications to this receiver:

$ snmptrap -v2c -c public 127.0.0.1:162 123 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.5.0 s test
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntfrcv
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
# Transport setup
# UDP over IPv4, first listening interface/port
config.addTransport(
    snmpEngine,
    udp.domainName + (1,),
    udp.UdpTransport().openServerMode(('127.0.0.1', 162))
)
# SNMPv1/2c setup
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Callback function for receiving notifications
# noinspection PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
          varBinds, cbCtx):
    # Get an execution context...
    execContext = snmpEngine.observer.getExecutionContext(
        'rfc3412.receiveMessage:request'
    )
    # ... and use inner SNMP engine data to figure out peer address
    print('Notification from %s, ContextEngineId "%s", ContextName "%s"' % ('@'.join([str(x) for x in execContext['transportAddress']]),
                                                                            contextEngineId.prettyPrint(),
                                                                            contextName.prettyPrint()))
    for name, val in varBinds:
        print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
# Register SNMP Application at the SNMP engine
ntfrcv.NotificationReceiver(snmpEngine, cbFun)
snmpEngine.transportDispatcher.jobStarted(1)  # this job would never finish
# Run I/O dispatcher which would receive queries and send confirmations
try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.transportDispatcher.closeDispatcher()
    raise

Download script.

See also: library reference.

Advanced topics

Receive SNMP TRAP/INFORM messages with the following options:

  • SNMPv1/SNMPv2c
  • with SNMP community "public"
  • over IPv4/UDP, listening at 127.0.0.1:162 over IPv6/UDP, listening at [::1]:162
  • registers its own execution observer to snmpEngine
  • print received data on stdout

Either of the following Net-SNMP commands will send notifications to this receiver:

$ snmptrap -v1 -c public 127.0.0.1 1.3.6.1.4.1.20408.4.1.1.2 127.0.0.1 1 1 123 1.3.6.1.2.1.1.1.0 s test
$ snmptrap -v2c -c public udp6:[::1]:162 123 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.5.0 s test
$ snmpinform -v2c -c public 127.0.0.1 123 1.3.6.1.6.3.1.1.5.1
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp, udp6
from pysnmp.entity.rfc3413 import ntfrcv
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
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' % '@'.join([str(x) for x in variables['transportAddress']]))
    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', 162))
)
# UDP over IPv6
config.addTransport(
    snmpEngine,
    udp6.domainName,
    udp6.Udp6Transport().openServerMode(('::1', 162))
)
# SNMPv1/2c setup
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Callback function for receiving notifications
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
          varBinds, cbCtx):
    print('Notification from ContextEngineId "%s", ContextName "%s"' % (contextEngineId.prettyPrint(),
                                                                        contextName.prettyPrint()))
    for name, val in varBinds:
        print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
# Register SNMP Application at the SNMP engine
ntfrcv.NotificationReceiver(snmpEngine, cbFun)
snmpEngine.transportDispatcher.jobStarted(1)  # this job would never finish
# Run I/O dispatcher which would receive queries and send confirmations
try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.observer.unregisterObserver()
    snmpEngine.transportDispatcher.closeDispatcher()
    raise

Download script.

Receive SNMP TRAP/INFORM messages with the following options:

  • SNMPv1/SNMPv2c
  • with any SNMP community matching regexp '.*love.*'
  • over IPv4/UDP, listening at 127.0.0.1:162
  • print received data on stdout

Either of the following Net-SNMP commands will send notifications to this receiver:

$ snmptrap -v1 -c rollover 127.0.0.1 1.3.6.1.4.1.20408.4.1.1.2 127.0.0.1 1 1 123 1.3.6.1.2.1.1.1.0 s test
$ snmpinform -v2c -c glove 127.0.0.1 123 1.3.6.1.6.3.1.1.5.1

The Notification Receiver below taps on v1/v2c SNMP security module to deliver certains values, normally internal to SNMP Engine, up to the context of user application.

This script examines the value of CommunityName, as it came from peer SNMP Engine, and may modify it to match the only locally configured CommunityName 'public'. This effectively makes NotificationReceiver accepting messages with CommunityName's, not explicitly configured to local SNMP Engine.

from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntfrcv
import re
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
# Register a callback to be invoked at specified execution point of
# SNMP Engine and passed local variables at execution point's local scope.
# If at this execution point passed variables are modified, their new
# values will be propagated back and used by SNMP Engine for securityName
# selection.
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def requestObserver(snmpEngine, execpoint, variables, cbCtx):
    if re.match('.*love.*', str(variables['communityName'])):
        print('Rewriting communityName \'%s\' from %s into \'public\'' % (variables['communityName'], ':'.join([str(x) for x in variables['transportInformation'][1]])))
        variables['communityName'] = variables['communityName'].clone('public')
snmpEngine.observer.registerObserver(
    requestObserver,
    'rfc2576.processIncomingMsg:writable'
)
# Transport setup
# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpTransport().openServerMode(('127.0.0.1', 162))
)
# SNMPv1/2c setup
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Callback function for receiving notifications
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
          varBinds, cbCtx):
    print('Notification from ContextEngineId "%s", ContextName "%s"' % (contextEngineId.prettyPrint(),
                                                                        contextName.prettyPrint()))
    for name, val in varBinds:
        print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
# Register SNMP Application at the SNMP engine
ntfrcv.NotificationReceiver(snmpEngine, cbFun)
snmpEngine.transportDispatcher.jobStarted(1)  # this job would never finish
# Run I/O dispatcher which would receive queries and send confirmations
try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.transportDispatcher.closeDispatcher()
    raise

Download script.

See also: library reference.

Act as a local SNMPv1/v2c Agent listening on a UDP/IPv6 transport, relay messages to distant SNMPv1/2c Agent over UDP/IPv4 transport:

  • with local SNMPv2c community 'public'
  • local Agent listening at [::1]:161
  • remote SNMPv2c community 'public'
  • remote Agent listening at 104.236.166.95:161

This script can be queried with the following Net-SNMP command:

$ snmpget -v2c -c public udp6:[::1]:161 sysDescr.0

due to proxy, it is equivalent to

$ snmpget -v2c -c public 104.236.166.95:161 sysDescr.0

Warning: for production operation you would need to modify this script so that it will re-map possible duplicate request-ID values, coming in initial request PDUs from different Managers, into unique values to avoid sending duplicate request-IDs to Agents.

from pysnmp.carrier.asyncore.dgram import udp, udp6
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, cmdgen, context
from pysnmp.proto.api import v2c
from pysnmp import error
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
#
# Transport setup
#
# Agent section
# UDP over IPv6
config.addTransport(
    snmpEngine,
    udp6.domainName,
    udp6.Udp6Transport().openServerMode(('::1', 161))
)
# Manager section
# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName,
    udp.UdpTransport().openClientMode()
)
#
# SNMPv1/2c setup (Agent role)
#
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, '1-my-area', 'public')
#
# SNMPv1/v2c setup (Manager role)
#
# Here we configure securityName lexicographically lesser than '1-my-area'
# to let it match first in snmpCommunityTable on response processing.
#
config.addV1System(snmpEngine, '0-distant-area', 'public', transportTag='remote')
#
# Transport target used by Manager
#
config.addTargetParams(
    snmpEngine, 'distant-agent-auth', '0-distant-area', 'noAuthNoPriv', 1
)
config.addTargetAddr(
    snmpEngine, 'distant-agent',
    udp.domainName, ('104.236.166.95', 161),
    'distant-agent-auth', retryCount=0, tagList='remote'
)
# Default SNMP context
config.addContext(snmpEngine, '')
class CommandResponder(cmdrsp.CommandResponderBase):
    cmdGenMap = {
        v2c.GetRequestPDU.tagSet: cmdgen.GetCommandGenerator(),
        v2c.SetRequestPDU.tagSet: cmdgen.SetCommandGenerator(),
        v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
        v2c.GetBulkRequestPDU.tagSet: cmdgen.BulkCommandGeneratorSingleRun()
    }
    pduTypes = cmdGenMap.keys()  # This app will handle these PDUs
    # SNMP request relay
    def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
                            PDU, acInfo):
        cbCtx = stateReference, PDU
        contextEngineId = None  # address authoritative SNMP Engine
        try:
            self.cmdGenMap[PDU.tagSet].sendPdu(
                snmpEngine, 'distant-agent',
                contextEngineId, contextName,
                PDU,
                self.handleResponsePdu, cbCtx
            )
        except error.PySnmpError:
            self.handleResponsePdu(
                snmpEngine, stateReference, 'error', None, cbCtx
            )
    # SNMP response relay
    # noinspection PyUnusedLocal
    def handleResponsePdu(self, snmpEngine, sendRequestHandle,
                          errorIndication, PDU, cbCtx):
        stateReference, reqPDU = cbCtx
        if errorIndication:
            PDU = v2c.apiPDU.getResponse(reqPDU)
            PDU.setErrorStatus(PDU, 5)
        self.sendPdu(
            snmpEngine, stateReference, PDU
        )
        self.releaseStateInformation(stateReference)
CommandResponder(snmpEngine, context.SnmpContext(snmpEngine))
snmpEngine.transportDispatcher.jobStarted(1)  # this job would never finish
# Run I/O dispatcher which would receive queries and send responses
try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.transportDispatcher.closeDispatcher()
    raise

Download script.

See also: library-reference.

SNMPv2c-to-SNMPv1 conversion

Act as a local SNMPv2c Agent, relay messages to distant SNMPv1 Agent:

  • over IPv4/UDP
  • with local SNMPv2c community public
  • local Agent listening at 127.0.0.1:161
  • remote SNMPv1, community public
  • remote Agent listening at 104.236.166.95:161

This script can be queried with the following Net-SNMP command:

$ snmpbulkwalk -v2c -c public -ObentU 127.0.0.1:161 system

due to proxy, it is equivalent to

$ snmpwalk -v1 -c public 104.236.166.95:161 system

Warning: for production operation you would need to modify this script so that it will re-map possible duplicate request-ID values, coming in initial request PDUs from different Managers, into unique values to avoid sending duplicate request-IDs to Agents.

from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, cmdgen, context
from pysnmp.proto.api import v2c
from pysnmp import error
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
#
# Transport setup
#
# Agent section
# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName + (1,),
    udp.UdpTransport().openServerMode(('127.0.0.1', 161))
)
# Manager section
# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName + (2,),
    udp.UdpTransport().openClientMode()
)
#
# SNMPv2c setup (Agent role)
#
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
#
# SNMPv1 setup (Manager role)
#
# SecurityName <-> CommunityName <-> Transport mapping
config.addV1System(snmpEngine, 'distant-area', 'public', transportTag='distant')
#
# Transport target used by Manager
#
# Specify security settings per SecurityName (SNMPv1 - 0, SNMPv2c - 1)
config.addTargetParams(snmpEngine, 'distant-agent-auth', 'distant-area',
                       'noAuthNoPriv', 0)
config.addTargetAddr(
    snmpEngine, 'distant-agent',
    udp.domainName + (2,), ('104.236.166.95', 161),
    'distant-agent-auth', retryCount=0, tagList='distant'
)
# Default SNMP context
config.addContext(snmpEngine, '')
class CommandResponder(cmdrsp.CommandResponderBase):
    cmdGenMap = {
        v2c.GetRequestPDU.tagSet: cmdgen.GetCommandGenerator(),
        v2c.SetRequestPDU.tagSet: cmdgen.SetCommandGenerator(),
        v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
        v2c.GetBulkRequestPDU.tagSet: cmdgen.BulkCommandGeneratorSingleRun()
    }
    pduTypes = cmdGenMap.keys()  # This app will handle these PDUs
    # SNMP request relay
    def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
                            PDU, acInfo):
        cbCtx = stateReference, PDU
        contextEngineId = None  # address authoritative SNMP Engine
        try:
            self.cmdGenMap[PDU.tagSet].sendPdu(
                snmpEngine, 'distant-agent',
                contextEngineId, contextName,
                PDU,
                self.handleResponsePdu, cbCtx
            )
        except error.PySnmpError:
            self.handleResponsePdu(
                snmpEngine, stateReference, 'error', None, cbCtx
            )
    # SNMP response relay
    # noinspection PyUnusedLocal
    def handleResponsePdu(self, snmpEngine, sendRequestHandle,
                          errorIndication, PDU, cbCtx):
        stateReference, reqPDU = cbCtx
        if errorIndication:
            PDU = v2c.apiPDU.getResponse(reqPDU)
            PDU.setErrorStatus(PDU, 5)
        self.sendPdu(
            snmpEngine, stateReference, PDU
        )
        self.releaseStateInformation(stateReference)
CommandResponder(snmpEngine, context.SnmpContext(snmpEngine))
snmpEngine.transportDispatcher.jobStarted(1)  # this job would never finish
# Run I/O dispatcher which would receive queries and send responses
try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.transportDispatcher.closeDispatcher()
    raise

Download script.

SNMPv2c-to-SNMPv3 conversion

Act as a local SNMPv1/v2c Agent, relay messages to distant SNMPv3 Agent: * over IPv4/UDP * with local SNMPv2c community 'public' * local Agent listening at 127.0.0.1:161 * remote SNMPv3 user usr-md5-none, MD5 auth and no privacy protocols * remote Agent listening at 104.236.166.95:161

This script can be queried with the following Net-SNMP command:

$ snmpget -v2c -c public 127.0.0.1:161 1.3.6.1.2.1.1.1.0

due to proxy, it is equivalent to

$ snmpget -v3 -l authNoPriv -u usr-md5-none -A authkey1 -ObentU 104.236.166.95:161  1.3.6.1.2.1.1.1.0

Warning: for production operation you would need to modify this script so that it will re-map possible duplicate request-ID values, coming in initial request PDUs from different Managers, into unique values to avoid sending duplicate request-IDs to Agents.

from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, cmdgen, context
from pysnmp.proto.api import v2c
from pysnmp import error
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
#
# Transport setup
#
# Agent section
# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName + (1,),
    udp.UdpTransport().openServerMode(('127.0.0.1', 161))
)
# Manager section
# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName + (2,),
    udp.UdpTransport().openClientMode()
)
#
# SNMPv1/2c setup (Agent role)
#
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
#
# SNMPv3/USM setup (Manager role)
#
# user: usr-md5-none, auth: MD5, priv NONE
config.addV3User(
    snmpEngine, 'usr-md5-none', config.usmHMACMD5AuthProtocol, 'authkey1'
)
#
# Transport target used by Manager
#
config.addTargetParams(
    snmpEngine, 'distant-agent-auth', 'usr-md5-none', 'authNoPriv'
)
config.addTargetAddr(
    snmpEngine, 'distant-agent',
    udp.domainName + (2,), ('104.236.166.95', 161),
    'distant-agent-auth', retryCount=0
)
# Default SNMP context
config.addContext(snmpEngine, '')
class CommandResponder(cmdrsp.CommandResponderBase):
    cmdGenMap = {
        v2c.GetRequestPDU.tagSet: cmdgen.GetCommandGenerator(),
        v2c.SetRequestPDU.tagSet: cmdgen.SetCommandGenerator(),
        v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
        v2c.GetBulkRequestPDU.tagSet: cmdgen.BulkCommandGeneratorSingleRun()
    }
    pduTypes = cmdGenMap.keys()  # This app will handle these PDUs
    # SNMP request relay
    def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
                            PDU, acInfo):
        cbCtx = stateReference, PDU
        contextEngineId = None  # address authoritative SNMP Engine
        try:
            self.cmdGenMap[PDU.tagSet].sendPdu(
                snmpEngine, 'distant-agent',
                contextEngineId, contextName,
                PDU,
                self.handleResponsePdu, cbCtx
            )
        except error.PySnmpError:
            self.handleResponsePdu(
                snmpEngine, stateReference, 'error', None, cbCtx
            )
    # SNMP response relay
    # noinspection PyUnusedLocal
    def handleResponsePdu(self, snmpEngine, sendRequestHandle,
                          errorIndication, PDU, cbCtx):
        stateReference, reqPDU = cbCtx
        if errorIndication:
            PDU = v2c.apiPDU.getResponse(reqPDU)
            PDU.setErrorStatus(PDU, 5)
        self.sendPdu(
            snmpEngine, stateReference, PDU
        )
        self.releaseStateInformation(stateReference)
CommandResponder(snmpEngine, context.SnmpContext(snmpEngine))
snmpEngine.transportDispatcher.jobStarted(1)  # this job would never finish
# Run I/O dispatcher which would receive queries and send responses
try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.transportDispatcher.closeDispatcher()
    raise

Download script.

Act as a local SNMPv3 Agent, relay messages to distant SNMPv1/v2c Agent: * over IPv4/UDP * with local SNMPv3 user usr-md5-des, MD5 auth and DES privacy protocols * local Agent listening at 127.0.0.1:161 * remote SNMPv1, community public * remote Agent listening at 104.236.166.95:161

This script can be queried with the following Net-SNMP command:

$ snmpget -v3 -l authPriv -u usr-md5-des -A authkey1 -X privkey1 -ObentU 127.0.0.1:161 1.3.6.1.2.1.1.1.0

due to proxy, it is equivalent to

$ snmpget -v2c -c public 104.236.166.95:161 1.3.6.1.2.1.1.1.0

Warning: for production operation you would need to modify this script so that it will re-map possible duplicate request-ID values, coming in initial request PDUs from different Managers, into unique values to avoid sending duplicate request-IDs to Agents.

from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, cmdgen, context
from pysnmp.proto.api import v2c
from pysnmp import error
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
#
# Transport setup
#
# Agent section
# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName + (1,),
    udp.UdpTransport().openServerMode(('127.0.0.1', 161))
)
# Manager section
# UDP over IPv4
config.addTransport(
    snmpEngine,
    udp.domainName + (2,),
    udp.UdpTransport().openClientMode()
)
#
# SNMPv3/USM setup (Agent role)
#
# user: usr-md5-des, auth: MD5, priv DES
config.addV3User(
    snmpEngine, 'usr-md5-des',
    config.usmHMACMD5AuthProtocol, 'authkey1',
    config.usmDESPrivProtocol, 'privkey1'
)
#
# SNMPv1/2c setup (Manager role)
#
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
#
# Transport target used by Manager
#
# Specify security settings per SecurityName (SNMPv1 - 0, SNMPv2c - 1)
config.addTargetParams(snmpEngine, 'distant-agent-auth', 'my-area',
                       'noAuthNoPriv', 0)
config.addTargetAddr(
    snmpEngine, 'distant-agent',
    udp.domainName + (2,), ('104.236.166.95', 161),
    'distant-agent-auth', retryCount=0
)
# Default SNMP context
config.addContext(snmpEngine, '')
class CommandResponder(cmdrsp.CommandResponderBase):
    cmdGenMap = {
        v2c.GetRequestPDU.tagSet: cmdgen.GetCommandGenerator(),
        v2c.SetRequestPDU.tagSet: cmdgen.SetCommandGenerator(),
        v2c.GetNextRequestPDU.tagSet: cmdgen.NextCommandGeneratorSingleRun(),
        v2c.GetBulkRequestPDU.tagSet: cmdgen.BulkCommandGeneratorSingleRun()
    }
    pduTypes = cmdGenMap.keys()  # This app will handle these PDUs
    # SNMP request relay
    def handleMgmtOperation(self, snmpEngine, stateReference, contextName,
                            PDU, acInfo):
        cbCtx = stateReference, PDU
        contextEngineId = None  # address authoritative SNMP Engine
        try:
            self.cmdGenMap[PDU.tagSet].sendPdu(
                snmpEngine, 'distant-agent',
                contextEngineId, contextName,
                PDU,
                self.handleResponsePdu, cbCtx
            )
        except error.PySnmpError:
            self.handleResponsePdu(
                snmpEngine, stateReference, 'error', None, cbCtx
            )
    # SNMP response relay
    # noinspection PyUnusedLocal
    def handleResponsePdu(self, snmpEngine, sendRequestHandle,
                          errorIndication, PDU, cbCtx):
        stateReference, reqPDU = cbCtx
        if errorIndication:
            PDU = v2c.apiPDU.getResponse(reqPDU)
            PDU.setErrorStatus(PDU, 5)
        self.sendPdu(snmpEngine, stateReference, PDU)
        self.releaseStateInformation(stateReference)
CommandResponder(snmpEngine, context.SnmpContext(snmpEngine))
snmpEngine.transportDispatcher.jobStarted(1)  # this job would never finish
# Run I/O dispatcher which would receive queries and send responses
try:
    snmpEngine.transportDispatcher.runDispatcher()
except:
    snmpEngine.transportDispatcher.closeDispatcher()
    raise

Download script.

See also: library-reference.

For more details on PySNMP programming model and interfaces, please refer to the documentation

Asynchronous: asyncio

Python 3.4 introduced a new module - asyncio (former Tulip, PEP 3156) featuring infrastructure for writing single-threaded concurrent code using coroutines, multiplexing I/O access over sockets and other resources.

PySNMP library was originally built on top of Python's asynchronous I/O library called asyncio. The asyncio module offers similar functionality but uses much more modern and powerful language facilities. Functionally, asyncio can replace asyncio in PySNMP however its use requires understanding the concepts such as coroutines and generators. If your task is to embed SNMP stack into an existing asyncio-based app, using PySNMP's asyncio interfaces greatly simplifies the task.

Older applications (Python 2.6+) can alternatively use Trollius instead of asyncio. Trollius is a backport of asyncio so it supports nearly the same API as asyncio. Full support of both modules is built into pysnmp.

All SNMP-related functionality of Native PySNMP API to Standard SNMP Applications (RFC3413) remains available to asyncio-backed applications.

We do not provide Command Generator and Notification Originator examples, as it is much easier to use high-level interfaces instead.

Command Responder Applications

Various SNMP versions

Multiple SNMP USM users

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

  • SNMPv3
  • 'usr-md5-des', auth: MD5, priv DES or
  • 'usr-sha-none', auth: SHA, no privacy
  • 'usr-sha-aes128', auth: SHA, priv AES
  • allow access to SNMPv2-MIB objects (1.3.6.1.2.1)
  • over IPv4/UDP, listening at 127.0.0.1:161
  • using asyncio network transport (available since Python 3.4)

Either of the following Net-SNMP commands will walk this Agent:

$ snmpwalk -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 localhost .1.3.6
$ snmpwalk -v3 -u usr-sha-none -l authNoPriv -a SHA -A authkey1 localhost .1.3.6
$ snmpwalk -v3 -u usr-sha-aes128 -l authPriv -a SHA -A authkey1 -x AES -X privkey1 localhost .1.3.6

Requires Python 3.4 and later!

from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.carrier.asyncio.dgram import udp
import asyncio
# Get the event loop for this thread
loop = asyncio.get_event_loop()
# 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))
)
# SNMPv3/USM setup
# user: usr-md5-des, auth: MD5, priv DES
config.addV3User(
    snmpEngine, 'usr-md5-des',
    config.usmHMACMD5AuthProtocol, 'authkey1',
    config.usmDESPrivProtocol, 'privkey1'
)
# user: usr-sha-none, auth: SHA, priv NONE
config.addV3User(
    snmpEngine, 'usr-sha-none',
    config.usmHMACSHAAuthProtocol, 'authkey1'
)
# user: usr-sha-none, auth: SHA, priv AES
config.addV3User(
    snmpEngine, 'usr-sha-aes128',
    config.usmHMACSHAAuthProtocol, 'authkey1',
    config.usmAesCfb128Protocol, '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))
config.addVacmUser(snmpEngine, 3, 'usr-sha-none', 'authNoPriv', (1, 3, 6, 1, 2, 1), (1, 3, 6, 1, 2, 1))
config.addVacmUser(snmpEngine, 3, 'usr-sha-aes128', '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)
# Run asyncio main loop
loop.run_forever()

Download script.

See also: library reference.

Notification Receiver Applications

Transport tweaks

Serving multiple network interfaces

Receive SNMP TRAP/INFORM messages with the following options:

  • SNMPv1/SNMPv2c
  • with SNMP community "public"
  • over IPv4/UDP, listening at 127.0.0.1:162 over IPv4/UDP, listening at 127.0.0.1:2162
  • using Asyncio framework for network transport
  • print received data on stdout

Either of the following Net-SNMP commands will send notifications to this receiver:

$ snmptrap -v2c -c public 127.0.0.1:162 123 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.5.0 s test
$ snmpinform -v2c -c public 127.0.0.1:2162 123 1.3.6.1.6.3.1.1.5.1

Requires Python 3.4 and later!

from pysnmp.entity import engine, config
from pysnmp.carrier.asyncio.dgram import udp
from pysnmp.entity.rfc3413 import ntfrcv
import asyncio
# Get the event loop for this thread
loop = asyncio.get_event_loop()
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
# Transport setup
# UDP over IPv4, first listening interface/port
config.addTransport(
    snmpEngine,
    udp.domainName + (1,),
    udp.UdpTransport().openServerMode(('127.0.0.1', 162))
)
# UDP over IPv4, second listening interface/port
config.addTransport(
    snmpEngine,
    udp.domainName + (2,),
    udp.UdpTransport().openServerMode(('127.0.0.1', 2162))
)
# SNMPv1/2c setup
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Callback function for receiving notifications
# noinspection PyUnusedLocal
def cbFun(snmpEngine,
          stateReference,
          contextEngineId, contextName,
          varBinds,
          cbCtx):
    transportDomain, transportAddress = snmpEngine.msgAndPduDsp.getTransportInfo(stateReference)
    print('Notification from %s, SNMP Engine %s, Context %s' % (transportAddress,
                                                                contextEngineId.prettyPrint(),
                                                                contextName.prettyPrint()))
    for name, val in varBinds:
        print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
# Register SNMP Application at the SNMP engine
ntfrcv.NotificationReceiver(snmpEngine, cbFun)
# Run asyncio main loop
loop.run_forever()

Download script.

See also: library reference.

For more details on PySNMP programming model and interfaces, please refer to the documentation

Asynchronous: trollius

Trollius framework offers infrastructure that allows you writing single-threaded, concurrent code using Python coroutines.

Trollius is a backport of asyncio to Python versions older than 3.3. Trollius supports nearly the same API as asyncio. Full support of both asyncio and trollius modules is built into pysnmp.

All SNMP-related functionality of Native PySNMP API to Standard SNMP Applications (RFC3413) remains available to asyncio-backed applications.

We do not provide Command Generator and Notification Originator examples, as it is much easier to use high-level interfaces instead. As for Command Responder and Notification Receiver, those could be use in the same way as with asyncio.

For more details on PySNMP programming model and interfaces, please refer to the documentation

Asynchronous: Twisted

Twisted is event-driven networking engine written in Python. It takes shape of a Python library which is used by many Python applications mostly for network communication purposes.

All SNMP-related functionality of Native API to Standard SNMP Applications remains available to Twisted applications.

We do not provide Command Generator and Notification Originator examples, as it is much easier to use high-level interfaces instead.

Command Responder Applications

Various SNMP versions

Multiple SNMP communities

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

  • SNMPv1
  • with SNMP community "public" (read access) or "private" (write access)
  • allow access to SNMPv2-MIB objects (1.3.6.1.2.1)
  • over IPv4/UDP, listening at 127.0.0.1:161
  • using Twisted fraework for network transport

The following Net-SNMP commands will GET/SET a value at this Agent:

$ snmpget -v1 -c public 127.0.0.1 SNMPv2-MIB::sysLocation.0
$ snmpset -v1 -c private 127.0.0.1 SNMPv2-MIB::sysLocation.0 s "far away"
from twisted.internet import reactor
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.carrier.twisted.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.UdpTwistedTransport().openServerMode(('127.0.0.1', 161))
)
# SNMPv1 setup
# SecurityName <-> CommunityName mapping.
# Here we configure two distinct CommunityName's to control read and write
# operations.
config.addV1System(snmpEngine, 'my-read-area', 'public')
config.addV1System(snmpEngine, 'my-write-area', 'private')
# Allow full MIB access for this user / securityModels at VACM
config.addVacmUser(snmpEngine, 1, 'my-read-area', 'noAuthNoPriv', (1, 3, 6, 1, 2, 1))
config.addVacmUser(snmpEngine, 1, 'my-write-area', 'noAuthNoPriv', (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)
# Run Twisted main loop
reactor.run()

Download script.

Multiple SNMP USM users

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

  • SNMPv3
  • 'usr-md5-des', auth: MD5, priv DES or
  • 'usr-sha-none', auth: SHA, no privacy
  • 'usr-sha-aes128', auth: SHA, priv AES
  • allow access to SNMPv2-MIB objects (1.3.6.1.2.1)
  • over IPv4/UDP, listening at 127.0.0.1:161
  • using Twisted framework for network transport

Either of the following Net-SNMP commands will walk this Agent:

$ snmpwalk -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 localhost .1.3.6
$ snmpwalk -v3 -u usr-sha-none -l authNoPriv -a SHA -A authkey1 localhost .1.3.6
$ snmpwalk -v3 -u usr-sha-aes128 -l authPriv -a SHA -A authkey1 -x AES -X privkey1 localhost .1.3.6
from twisted.internet import reactor
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.carrier.twisted.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.UdpTwistedTransport().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'
)
# user: usr-sha-none, auth: SHA, priv NONE
config.addV3User(
    snmpEngine, 'usr-sha-none',
    config.usmHMACSHAAuthProtocol, 'authkey1'
)
# user: usr-sha-none, auth: SHA, priv AES
config.addV3User(
    snmpEngine, 'usr-sha-aes128',
    config.usmHMACSHAAuthProtocol, 'authkey1',
    config.usmAesCfb128Protocol, '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))
config.addVacmUser(snmpEngine, 3, 'usr-sha-none', 'authNoPriv', (1, 3, 6, 1, 2, 1), (1, 3, 6, 1, 2, 1))
config.addVacmUser(snmpEngine, 3, 'usr-sha-aes128', '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)
# Run Twisted main loop
reactor.run()

Download script.

See also: library reference.

Agent-side MIB implementations

Implementing scalar MIB objects

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

  • SNMPv2c
  • with SNMP community "public"
  • serving custom Managed Object Instance defined within this script
  • allow read access only to the subtree where the custom MIB object resides
  • over IPv4/UDP, listening at 127.0.0.1:161
  • using Twisted fraework for network transport

Either of the following Net-SNMP commands will walk this Agent:

$ snmpwalk -v2c -c public 127.0.0.1 .1.3.6
import sys
from twisted.internet import reactor
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.proto import rfc1902
from pysnmp.carrier.twisted.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.UdpTwistedTransport().openServerMode(('127.0.0.1', 161))
)
# SNMPv2c setup
# SecurityName <-> CommunityName mapping.
config.addV1System(snmpEngine, 'my-area', 'public')
# Allow read MIB access for this user / securityModels at VACM
config.addVacmUser(snmpEngine, 2, 'my-area', 'noAuthNoPriv', (1, 3, 6, 5))
# Create an SNMP context
snmpContext = context.SnmpContext(snmpEngine)
# --- create custom Managed Object Instance ---
mibBuilder = snmpContext.getMibInstrum().getMibBuilder()
MibScalar, MibScalarInstance = mibBuilder.importSymbols(
    'SNMPv2-SMI', 'MibScalar', 'MibScalarInstance'
)
class MyStaticMibScalarInstance(MibScalarInstance):
    # noinspection PyUnusedLocal,PyUnusedLocal
    def getValue(self, name, idx):
        return self.getSyntax().clone(
            'Python %s running on a %s platform' % (sys.version, sys.platform)
        )
mibBuilder.exportSymbols(
    '__MY_MIB', MibScalar((1, 3, 6, 5, 1), rfc1902.OctetString()),
    MyStaticMibScalarInstance((1, 3, 6, 5, 1), (0,), rfc1902.OctetString())
)
# --- end of Managed Object Instance initialization ----
# Register SNMP Applications at the SNMP engine for particular SNMP context
cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
cmdrsp.NextCommandResponder(snmpEngine, snmpContext)
cmdrsp.BulkCommandResponder(snmpEngine, snmpContext)
# Run Twisted main loop
reactor.run()

Download script.

See also: library reference.

Transport tweaks

Listen on multiple network interfaces

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

  • SNMPv2c
  • with SNMP community "public"
  • allow access to SNMPv2-MIB objects (1.3.6.1.2.1)
  • over IPv4/UDP, listening at 127.0.0.1:161 and 127.0.0.2:161 interfaces
  • using Twisted framework for network transport

Either of the following Net-SNMP commands will walk this Agent:

$ snmpwalk -v2c -c public 127.0.0.1 .1.3.6
$ snmpwalk -v2c -c public 127.0.0.2 .1.3.6
from twisted.internet import reactor
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.carrier.twisted.dgram import udp
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
# Transport setup
# UDP over IPv4 at 127.0.0.1:161
config.addTransport(
    snmpEngine,
    udp.domainName + (1,),
    udp.UdpTwistedTransport().openServerMode(('127.0.0.1', 161))
)
# UDP over IPv4 at 127.0.0.2:161
config.addTransport(
    snmpEngine,
    udp.domainName + (2,),
    udp.UdpTwistedTransport().openServerMode(('127.0.0.2', 161))
)
# SNMPv2c setup
# SecurityName <-> CommunityName mapping.
config.addV1System(snmpEngine, 'my-area', 'public')
# Allow full MIB access for this user / securityModels at VACM
config.addVacmUser(snmpEngine, 2, 'my-area', 'noAuthNoPriv', (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)
# Run Twisted main loop
reactor.run()

Download script.

See also: library reference.

Notification Receiver Applications

Various SNMP versions

Multiple SNMP USM users

Receive SNMP TRAP/INFORM messages with the following options:

  • SNMPv3
  • 'usr-md5-des', auth: MD5, priv DES, ContextEngineId: 8000000001020304
  • 'usr-md5-none', auth: MD5, priv NONE, ContextEngineId: 8000000001020304
  • 'usr-sha-aes128', auth: SHA, priv AES, ContextEngineId: 8000000001020304
  • over IPv4/UDP, listening at 127.0.0.1:162
  • using Twisted framework for network transport
  • print received data on stdout

Either of the following Net-SNMP commands will send notifications to this receiver:

$ snmptrap -v3 -u usr-md5-des -l authPriv -A authkey1 -X privkey1 -e 8000000001020304 127.0.0.1 123 1.3.6.1.6.3.1.1.5.1
$ snmptrap -v3 -u usr-md5-none -l authNoPriv -A authkey1 -e 8000000001020304 127.0.0.1 123 1.3.6.1.6.3.1.1.5.1
$ snmpinform -v3 -u usr-sha-aes128 -l authPriv -a SHA -A authkey1 -x AES -X privkey1 127.0.0.1 123 1.3.6.1.6.3.1.1.5.1
from twisted.internet import reactor
from pysnmp.entity import engine, config
from pysnmp.carrier.twisted.dgram import udp
from pysnmp.entity.rfc3413 import ntfrcv
from pysnmp.proto import rfc1902
# 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.UdpTwistedTransport().openServerMode(('127.0.0.1', 162))
)
# -- begin of SNMPv3/USM setup
# user: usr-md5-des, auth: MD5, priv DES
config.addV3User(
    snmpEngine, 'usr-md5-des',
    config.usmHMACMD5AuthProtocol, 'authkey1',
    config.usmDESPrivProtocol, 'privkey1'
)
# user: usr-md5-des, auth: MD5, priv DES, securityEngineId: 8000000001020304
# this USM entry is used for TRAP receiving purposes
config.addV3User(
    snmpEngine, 'usr-md5-des',
    config.usmHMACMD5AuthProtocol, 'authkey1',
    config.usmDESPrivProtocol, 'privkey1',
    securityEngineId=rfc1902.OctetString(hexValue='8000000001020304')
)
# user: usr-md5-none, auth: MD5, priv NONE
config.addV3User(
    snmpEngine, 'usr-md5-none',
    config.usmHMACMD5AuthProtocol, 'authkey1'
)
# user: usr-md5-none, auth: MD5, priv NONE, securityEngineId: 8000000001020304
# this USM entry is used for TRAP receiving purposes
config.addV3User(
    snmpEngine, 'usr-md5-none',
    config.usmHMACMD5AuthProtocol, 'authkey1',
    securityEngineId=rfc1902.OctetString(hexValue='8000000001020304')
)
# user: usr-sha-aes128, auth: SHA, priv AES
config.addV3User(
    snmpEngine, 'usr-sha-aes128',
    config.usmHMACSHAAuthProtocol, 'authkey1',
    config.usmAesCfb128Protocol, 'privkey1'
)
# user: usr-sha-aes128, auth: SHA, priv AES, securityEngineId: 8000000001020304
# this USM entry is used for TRAP receiving purposes
config.addV3User(
    snmpEngine, 'usr-sha-aes128',
    config.usmHMACSHAAuthProtocol, 'authkey1',
    config.usmAesCfb128Protocol, 'privkey1',
    securityEngineId=rfc1902.OctetString(hexValue='8000000001020304')
)
# -- end of SNMPv3/USM setup
# Callback function for receiving notifications
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
          varBinds, cbCtx):
    print('Notification from ContextEngineId "%s", ContextName "%s"' % (contextEngineId.prettyPrint(),
                                                                        contextName.prettyPrint()))
    for name, val in varBinds:
        print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
# Register SNMP Application at the SNMP engine
ntfrcv.NotificationReceiver(snmpEngine, cbFun)
# Run Twisted main loop
reactor.run()

Download script.

See also: library reference.

Transport tweaks

Serving multiple network interfaces

Receive SNMP TRAP/INFORM messages with the following options:

  • SNMPv1/SNMPv2c
  • with SNMP community "public"
  • IPv4/UDP, listening at 127.0.0.1:162
  • IPv4/UDP, listening at 127.0.0.1:2162
  • using Twisted framework for network transport
  • print received data on stdout

Either of the following Net-SNMP commands will send notifications to this receiver:

$ snmptrap -v2c -c public 127.0.0.1:162 123 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.5.0 s test
$ snmpinform -v2c -c public 127.0.0.1:2162 123 1.3.6.1.6.3.1.1.5.1
from twisted.internet import reactor
from pysnmp.entity import engine, config
from pysnmp.carrier.twisted.dgram import udp
from pysnmp.entity.rfc3413 import ntfrcv
# Create SNMP engine with autogenernated engineID and pre-bound
# to socket transport dispatcher
snmpEngine = engine.SnmpEngine()
# Transport setup
# UDP over IPv4, first listening interface/port
config.addTransport(
    snmpEngine,
    udp.domainName + (1,),
    udp.UdpTwistedTransport().openServerMode(('127.0.0.1', 162))
)
# UDP over IPv4, second listening interface/port
config.addTransport(
    snmpEngine,
    udp.domainName + (2,),
    udp.UdpTwistedTransport().openServerMode(('127.0.0.1', 2162))
)
# SNMPv1/2c setup
# SecurityName <-> CommunityName mapping
config.addV1System(snmpEngine, 'my-area', 'public')
# Callback function for receiving notifications
# noinspection PyUnusedLocal,PyUnusedLocal,PyUnusedLocal
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,
          varBinds, cbCtx):
    print('Notification from ContextEngineId "%s", Context "%s"' % (contextEngineId.prettyPrint(),
                                                                    contextName.prettyPrint()))
    for name, val in varBinds:
        print('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
# Register SNMP Application at the SNMP engine
ntfrcv.NotificationReceiver(snmpEngine, cbFun)
# Run Twisted main loop
reactor.run()

Download script.

See also: library reference.

For more details on PySNMP programming model and interfaces, please refer to the documentation

In cases where performance is your top priority and you only need to work with SNMP v1 and v2c systems and you do not mind writing much more code, then there is a low-level API to SNMP v1/v2c PDU and PySNMP I/O engine. There's practically no SNMP engine or SMI infrastructure involved in the operations of these almost wire-level interfaces. Although MIB services can still be used separately.

A packet-level API-based application typically manages both SNMP message building/parsing and network communication via one or more transports. It's fully up to the application to handle failures on message and transport levels.

Perform SNMP GET operation with the following options:

  • with SNMPv1, community 'public'
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for OIDs in tuple form

This script performs similar to the following Net-SNMP command:

$ snmpget -v1 -c public -ObentU demo.snmplabs.com 1.3.6.1.2.1.1.1.0 1.3.6.1.2.1.1.3.0
from pysnmp.carrier.asyncore.dispatch import AsyncoreDispatcher
from pysnmp.carrier.asyncore.dgram import udp, udp6, unix
from pyasn1.codec.ber import encoder, decoder
from pysnmp.proto import api
from time import time
# Protocol version to use
pMod = api.protoModules[api.protoVersion1]
# pMod = api.protoModules[api.protoVersion2c]
# Build PDU
reqPDU = pMod.GetRequestPDU()
pMod.apiPDU.setDefaults(reqPDU)
pMod.apiPDU.setVarBinds(
    reqPDU, (('1.3.6.1.2.1.1.1.0', pMod.Null('')),
             ('1.3.6.1.2.1.1.3.0', pMod.Null('')))
)
# Build message
reqMsg = pMod.Message()
pMod.apiMessage.setDefaults(reqMsg)
pMod.apiMessage.setCommunity(reqMsg, 'public')
pMod.apiMessage.setPDU(reqMsg, reqPDU)
startedAt = time()
def cbTimerFun(timeNow):
    if timeNow - startedAt > 3:
        raise Exception("Request timed out")
# noinspection PyUnusedLocal,PyUnusedLocal
def cbRecvFun(transportDispatcher, transportDomain, transportAddress,
              wholeMsg, reqPDU=reqPDU):
    while wholeMsg:
        rspMsg, wholeMsg = decoder.decode(wholeMsg, asn1Spec=pMod.Message())
        rspPDU = pMod.apiMessage.getPDU(rspMsg)
        # Match response to request
        if pMod.apiPDU.getRequestID(reqPDU) == pMod.apiPDU.getRequestID(rspPDU):
            # Check for SNMP errors reported
            errorStatus = pMod.apiPDU.getErrorStatus(rspPDU)
            if errorStatus:
                print(errorStatus.prettyPrint())
            else:
                for oid, val in pMod.apiPDU.getVarBinds(rspPDU):
                    print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
            transportDispatcher.jobFinished(1)
    return wholeMsg
transportDispatcher = AsyncoreDispatcher()
transportDispatcher.registerRecvCbFun(cbRecvFun)
transportDispatcher.registerTimerCbFun(cbTimerFun)
# UDP/IPv4
transportDispatcher.registerTransport(
    udp.domainName, udp.UdpSocketTransport().openClientMode()
)
# Pass message to dispatcher
transportDispatcher.sendMessage(
    encoder.encode(reqMsg), udp.domainName, ('demo.snmplabs.com', 161)
)
transportDispatcher.jobStarted(1)
## UDP/IPv6 (second copy of the same PDU will be sent)
# transportDispatcher.registerTransport(
#    udp6.domainName, udp6.Udp6SocketTransport().openClientMode()
# )
# Pass message to dispatcher
# transportDispatcher.sendMessage(
#    encoder.encode(reqMsg), udp6.domainName, ('::1', 161)
# )
# transportDispatcher.jobStarted(1)
## Local domain socket
# transportDispatcher.registerTransport(
#    unix.domainName, unix.UnixSocketTransport().openClientMode()
# )
#
# Pass message to dispatcher
# transportDispatcher.sendMessage(
#    encoder.encode(reqMsg), unix.domainName, '/tmp/snmp-agent'
# )
# transportDispatcher.jobStarted(1)
# Dispatcher will finish as job#1 counter reaches zero
transportDispatcher.runDispatcher()
transportDispatcher.closeDispatcher()

Download script.

See also: library reference.

Modifying variables

Perform SNMP SET operation with the following options:

  • with SNMPv2c, community 'public'
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for OIDs in string form and values in form of pyasn1 objects

This script performs similar to the following Net-SNMP command:

$ snmpset -v2c -c public -ObentU demo.snmplabs.com 1.3.6.1.2.1.1.9.1.3.1 s 'New description' 1.3.6.1.2.1.1.9.1.4.1 t 12
from pysnmp.carrier.asyncore.dispatch import AsyncoreDispatcher
from pysnmp.carrier.asyncore.dgram import udp
from pyasn1.codec.ber import encoder, decoder
from pysnmp.proto import api
from time import time
# Protocol version to use
# pMod = api.protoModules[api.protoVersion1]
pMod = api.protoModules[api.protoVersion2c]
# Build PDU
reqPDU = pMod.SetRequestPDU()
pMod.apiPDU.setDefaults(reqPDU)
pMod.apiPDU.setVarBinds(
    reqPDU,
    # A list of Var-Binds to SET
    (('1.3.6.1.2.1.1.9.1.3.1', pMod.OctetString('New system description')),
     ('1.3.6.1.2.1.1.9.1.4.1', pMod.TimeTicks(12)))
)
# Build message
reqMsg = pMod.Message()
pMod.apiMessage.setDefaults(reqMsg)
pMod.apiMessage.setCommunity(reqMsg, 'public')
pMod.apiMessage.setPDU(reqMsg, reqPDU)
startedAt = time()
def cbTimerFun(timeNow):
    if timeNow - startedAt > 3:
        raise Exception("Request timed out")
# noinspection PyUnusedLocal,PyUnusedLocal
def cbRecvFun(transportDispatcher, transportDomain, transportAddress,
              wholeMsg, reqPDU=reqPDU):
    while wholeMsg:
        rspMsg, wholeMsg = decoder.decode(wholeMsg, asn1Spec=pMod.Message())
        rspPDU = pMod.apiMessage.getPDU(rspMsg)
        # Match response to request
        if pMod.apiPDU.getRequestID(reqPDU) == pMod.apiPDU.getRequestID(rspPDU):
            # Check for SNMP errors reported
            errorStatus = pMod.apiPDU.getErrorStatus(rspPDU)
            if errorStatus:
                print(errorStatus.prettyPrint())
            else:
                for oid, val in pMod.apiPDU.getVarBinds(rspPDU):
                    print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
            transportDispatcher.jobFinished(1)
    return wholeMsg
transportDispatcher = AsyncoreDispatcher()
transportDispatcher.registerRecvCbFun(cbRecvFun)
transportDispatcher.registerTimerCbFun(cbTimerFun)
# UDP/IPv4
transportDispatcher.registerTransport(
    udp.domainName, udp.UdpSocketTransport().openClientMode()
)
# Pass message to dispatcher
transportDispatcher.sendMessage(
    encoder.encode(reqMsg), udp.domainName, ('demo.snmplabs.com', 161)
)
transportDispatcher.jobStarted(1)
# Dispatcher will finish as job#1 counter reaches zero
transportDispatcher.runDispatcher()
transportDispatcher.closeDispatcher()

Download script.

See also: library reference.

Perform SNMP GETNEXT operation with the following options:

  • with SNMPv1, community 'public'
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for OID in tuple form

This script performs similar to the following Net-SNMP command:

$ snmpwalk -v1 -c public -ObentU demo.snmplabs.com 1.3.6
from pysnmp.carrier.asyncore.dispatch import AsyncoreDispatcher
from pysnmp.carrier.asyncore.dgram import udp
from pyasn1.codec.ber import encoder, decoder
from pysnmp.proto import api
from time import time
# Protocol version to use
pMod = api.protoModules[api.protoVersion1]
# pMod = api.protoModules[api.protoVersion2c]
# SNMP table header
headVars = [pMod.ObjectIdentifier((1, 3, 6))]
# Build PDU
reqPDU = pMod.GetNextRequestPDU()
pMod.apiPDU.setDefaults(reqPDU)
pMod.apiPDU.setVarBinds(reqPDU, [(x, pMod.null) for x in headVars])
# Build message
reqMsg = pMod.Message()
pMod.apiMessage.setDefaults(reqMsg)
pMod.apiMessage.setCommunity(reqMsg, 'public')
pMod.apiMessage.setPDU(reqMsg, reqPDU)
startedAt = time()
def cbTimerFun(timeNow):
    if timeNow - startedAt > 3:
        raise Exception("Request timed out")
# noinspection PyUnusedLocal
def cbRecvFun(transportDispatcher, transportDomain, transportAddress,
              wholeMsg, reqPDU=reqPDU, headVars=headVars):
    while wholeMsg:
        rspMsg, wholeMsg = decoder.decode(wholeMsg, asn1Spec=pMod.Message())
        rspPDU = pMod.apiMessage.getPDU(rspMsg)
        # Match response to request
        if pMod.apiPDU.getRequestID(reqPDU) == pMod.apiPDU.getRequestID(rspPDU):
            # Check for SNMP errors reported
            errorStatus = pMod.apiPDU.getErrorStatus(rspPDU)
            if errorStatus and errorStatus != 2:
                raise Exception(errorStatus)
            # Format var-binds table
            varBindTable = pMod.apiPDU.getVarBindTable(reqPDU, rspPDU)
            # Report SNMP table
            for tableRow in varBindTable:
                for name, val in tableRow:
                    print('from: %s, %s = %s' % (
                        transportAddress, name.prettyPrint(), val.prettyPrint()
                    )
                          )
            # Stop on EOM
            for oid, val in varBindTable[-1]:
                if not isinstance(val, pMod.Null):
                    break
            else:
                transportDispatcher.jobFinished(1)
            # Generate request for next row
            pMod.apiPDU.setVarBinds(
                reqPDU, [(x, pMod.null) for x, y in varBindTable[-1]]
            )
            pMod.apiPDU.setRequestID(reqPDU, pMod.getNextRequestID())
            transportDispatcher.sendMessage(
                encoder.encode(reqMsg), transportDomain, transportAddress
            )
            global startedAt
            if time() - startedAt > 3:
                raise Exception('Request timed out')
            startedAt = time()
    return wholeMsg
transportDispatcher = AsyncoreDispatcher()
transportDispatcher.registerRecvCbFun(cbRecvFun)
transportDispatcher.registerTimerCbFun(cbTimerFun)
transportDispatcher.registerTransport(
    udp.domainName, udp.UdpSocketTransport().openClientMode()
)
transportDispatcher.sendMessage(
    encoder.encode(reqMsg), udp.domainName, ('demo.snmplabs.com', 161)
)
transportDispatcher.jobStarted(1)
transportDispatcher.runDispatcher()
transportDispatcher.closeDispatcher()

Download script.

Perform SNMP GETBULK operation with the following options:

  • with SNMPv2c, community 'public'
  • over IPv4/UDP
  • to an Agent at demo.snmplabs.com:161
  • for OID in tuple form
  • with non-repeaters=0 and max-repeaters=25

This script performs similar to the following Net-SNMP command:

$ snmpbulkwalk -v2c -c public -ObentU -Cn0 -Cr25 demo.snmplabs.com 1.3.6
from pysnmp.carrier.asyncore.dispatch import AsyncoreDispatcher
from pysnmp.carrier.asyncore.dgram import udp
from pyasn1.codec.ber import encoder, decoder
from pysnmp.proto.api import v2c
from time import time
# SNMP table header
headVars = [v2c.ObjectIdentifier((1, 3, 6))]
# Build PDU
reqPDU = v2c.GetBulkRequestPDU()
v2c.apiBulkPDU.setDefaults(reqPDU)
v2c.apiBulkPDU.setNonRepeaters(reqPDU, 0)
v2c.apiBulkPDU.setMaxRepetitions(reqPDU, 25)
v2c.apiBulkPDU.setVarBinds(reqPDU, [(x, v2c.null) for x in headVars])
# Build message
reqMsg = v2c.Message()
v2c.apiMessage.setDefaults(reqMsg)
v2c.apiMessage.setCommunity(reqMsg, 'public')
v2c.apiMessage.setPDU(reqMsg, reqPDU)
startedAt = time()
def cbTimerFun(timeNow):
    if timeNow - startedAt > 3:
        raise Exception("Request timed out")
# noinspection PyUnusedLocal
def cbRecvFun(transportDispatcher, transportDomain, transportAddress,
              wholeMsg, reqPDU=reqPDU, headVars=headVars):
    while wholeMsg:
        rspMsg, wholeMsg = decoder.decode(wholeMsg, asn1Spec=v2c.Message())
        rspPDU = v2c.apiMessage.getPDU(rspMsg)
        # Match response to request
        if v2c.apiBulkPDU.getRequestID(reqPDU) == v2c.apiBulkPDU.getRequestID(rspPDU):
            # Format var-binds table
            varBindTable = v2c.apiBulkPDU.getVarBindTable(reqPDU, rspPDU)
            # Check for SNMP errors reported
            errorStatus = v2c.apiBulkPDU.getErrorStatus(rspPDU)
            if errorStatus and errorStatus != 2:
                errorIndex = v2c.apiBulkPDU.getErrorIndex(rspPDU)
                print('%s at %s' % (errorStatus.prettyPrint(),
                                    errorIndex and varBindTable[int(errorIndex) - 1] or '?'))
                transportDispatcher.jobFinished(1)
                break
            # Report SNMP table
            for tableRow in varBindTable:
                for name, val in tableRow:
                    print('from: %s, %s = %s' % (
                        transportAddress, name.prettyPrint(), val.prettyPrint()
                    )
                          )
            # Stop on EOM
            for oid, val in varBindTable[-1]:
                if not isinstance(val, v2c.Null):
                    break
            else:
                transportDispatcher.jobFinished(1)
            # Generate request for next row
            v2c.apiBulkPDU.setVarBinds(
                reqPDU, [(x, v2c.null) for x, y in varBindTable[-1]]
            )
            v2c.apiBulkPDU.setRequestID(reqPDU, v2c.getNextRequestID())
            transportDispatcher.sendMessage(
                encoder.encode(reqMsg), transportDomain, transportAddress
            )
            global startedAt
            if time() - startedAt > 3:
                raise Exception('Request timed out')
            startedAt = time()
    return wholeMsg
transportDispatcher = AsyncoreDispatcher()
transportDispatcher.registerRecvCbFun(cbRecvFun)
transportDispatcher.registerTimerCbFun(cbTimerFun)
transportDispatcher.registerTransport(
    udp.domainName, udp.UdpSocketTransport().openClientMode()
)
transportDispatcher.sendMessage(
    encoder.encode(reqMsg), udp.domainName, ('demo.snmplabs.com', 161)
)
transportDispatcher.jobStarted(1)
# Dispatcher will finish as job#1 counter reaches zero
transportDispatcher.runDispatcher()
transportDispatcher.closeDispatcher()

Download script.

See also: library reference.

Transport tweaks

Send SNMP GET request from a non-local IP address:

  • with SNMPv2c, community 'public'
  • over IPv4/UDP
  • to an Agent at 104.236.166.95:161
  • from a non-local, spoofed IP 1.2.3.4 (root and Python 3.3+ required)
  • for OIDs in string form

This script performs similar to the following Net-SNMP command:

$ snmpget -v2c -c public -ObentU 104.236.166.95 1.3.6.1.2.1.1.1.0 1.3.6.1.2.1.1.3.0

But unlike the above command, this script issues SNMP request from a non-default, non-local IP address.

It is indeed possible to originate SNMP traffic from any valid local IP addresses. It could be a secondary IP interface, for instance. Superuser privileges are only required to send spoofed packets. Alternatively, sending from local interface could also be achieved by binding to it (via openClientMode() parameter).

Agent would respond to the IP address you used as a source. So this script could only get a response if that source address is somehow routed to the host this script is running on. Otherwise it just times out.

from pysnmp.carrier.asyncore.dispatch import AsyncoreDispatcher
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.proto import api
from pyasn1.codec.ber import encoder, decoder
from time import time
# Send request message to this address
transportAddress = udp.UdpTransportAddress(('104.236.166.95', 161))
# Send request message from this non-local (!) IP address
transportAddress.setLocalAddress(('1.2.3.4', 0))
# Protocol version to use
# pMod = api.protoModules[api.protoVersion1]
pMod = api.protoModules[api.protoVersion2c]
# Build PDU
reqPDU = pMod.GetRequestPDU()
pMod.apiPDU.setDefaults(reqPDU)
pMod.apiPDU.setVarBinds(
    reqPDU, (('1.3.6.1.2.1.1.1.0', pMod.Null('')),
             ('1.3.6.1.2.1.1.3.0', pMod.Null('')))
)
# Build message
reqMsg = pMod.Message()
pMod.apiMessage.setDefaults(reqMsg)
pMod.apiMessage.setCommunity(reqMsg, 'public')
pMod.apiMessage.setPDU(reqMsg, reqPDU)
startedAt = time()
class StopWaiting(Exception): pass
def cbTimerFun(timeNow):
    if timeNow - startedAt > 3:
        raise StopWaiting()
# noinspection PyUnusedLocal,PyUnusedLocal
def cbRecvFun(transportDispatcher, transportDomain, transportAddress,
              wholeMsg, reqPDU=reqPDU):
    while wholeMsg:
        rspMsg, wholeMsg = decoder.decode(wholeMsg, asn1Spec=pMod.Message())
        rspPDU = pMod.apiMessage.getPDU(rspMsg)
        # Match response to request
        if pMod.apiPDU.getRequestID(reqPDU) == pMod.apiPDU.getRequestID(rspPDU):
            # Check for SNMP errors reported
            errorStatus = pMod.apiPDU.getErrorStatus(rspPDU)
            if errorStatus:
                print(errorStatus.prettyPrint())
            else:
                for oid, val in pMod.apiPDU.getVarBinds(rspPDU):
                    print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
            transportDispatcher.jobFinished(1)
    return wholeMsg
transportDispatcher = AsyncoreDispatcher()
transportDispatcher.registerRecvCbFun(cbRecvFun)
transportDispatcher.registerTimerCbFun(cbTimerFun)
# Initialize UDP/IPv4 transport
udpSocketTransport = udp.UdpSocketTransport().openClientMode()
# Use sendmsg()/recvmsg() for socket communication (required for
# IP source spoofing functionality)
udpSocketTransport.enablePktInfo()
# Enable IP source spoofing (requires root privileges)
udpSocketTransport.enableTransparent()
transportDispatcher.registerTransport(udp.domainName, udpSocketTransport)
# Pass message to dispatcher
transportDispatcher.sendMessage(
    encoder.encode(reqMsg), udp.domainName, transportAddress
)
# We might never receive any response as we sent request with fake source IP
transportDispatcher.jobStarted(1)
# Dispatcher will finish as all jobs counter reaches zero
try:
    transportDispatcher.runDispatcher()
except StopWaiting:
    transportDispatcher.closeDispatcher()
else:
    raise

Download script.

Send SNMP GET request to broadcast address and wait for respons(es):

  • with SNMPv2c, community 'public'
  • over IPv4/UDP
  • to all Agents via broadcast address 255.255.255.255:161
  • for OIDs in tuple form

Here we send out a single SNMP request and wait for potentially many SNMP responses from multiple SNMP Agents listening in local broadcast domain. Since we can't predict the exact number of Agents responding, this script just waits for arbitrary time for collecting all responses. This technology is also known as SNMP-based discovery.

This script performs similar to the following Net-SNMP command:

$ snmpget -v2c -c public -ObentU 255.255.255.255 1.3.6.1.2.1.1.1.0 1.3.6.1.2.1.1.3.0
from pysnmp.carrier.asyncore.dispatch import AsyncoreDispatcher
from pysnmp.carrier.asyncore.dgram import udp
from pyasn1.codec.ber import encoder, decoder
from pysnmp.proto import api
from time import time
# Broadcast manager settings
maxWaitForResponses = 5
maxNumberResponses = 10
# Protocol version to use
# pMod = api.protoModules[api.protoVersion1]
pMod = api.protoModules[api.protoVersion2c]
# Build PDU
reqPDU = pMod.GetRequestPDU()
pMod.apiPDU.setDefaults(reqPDU)
pMod.apiPDU.setVarBinds(
    reqPDU, (('1.3.6.1.2.1.1.1.0', pMod.Null('')),
             ('1.3.6.1.2.1.1.3.0', pMod.Null('')))
)
# Build message
reqMsg = pMod.Message()
pMod.apiMessage.setDefaults(reqMsg)
pMod.apiMessage.setCommunity(reqMsg, 'public')
pMod.apiMessage.setPDU(reqMsg, reqPDU)
startedAt = time()
class StopWaiting(Exception): pass
def cbTimerFun(timeNow):
    if timeNow - startedAt > maxWaitForResponses:
        raise StopWaiting()
# noinspection PyUnusedLocal,PyUnusedLocal
def cbRecvFun(transportDispatcher, transportDomain, transportAddress,
              wholeMsg, reqPDU=reqPDU):
    while wholeMsg:
        rspMsg, wholeMsg = decoder.decode(wholeMsg, asn1Spec=pMod.Message())
        rspPDU = pMod.apiMessage.getPDU(rspMsg)
        # Match response to request
        if pMod.apiPDU.getRequestID(reqPDU) == pMod.apiPDU.getRequestID(rspPDU):
            # Check for SNMP errors reported
            errorStatus = pMod.apiPDU.getErrorStatus(rspPDU)
            if errorStatus:
                print(errorStatus.prettyPrint())
            else:
                for oid, val in pMod.apiPDU.getVarBinds(rspPDU):
                    print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
            transportDispatcher.jobFinished(1)
    return wholeMsg
transportDispatcher = AsyncoreDispatcher()
transportDispatcher.registerRecvCbFun(cbRecvFun)
transportDispatcher.registerTimerCbFun(cbTimerFun)
# UDP/IPv4
udpSocketTransport = udp.UdpSocketTransport().openClientMode().enableBroadcast()
transportDispatcher.registerTransport(udp.domainName, udpSocketTransport)
# Pass message to dispatcher
transportDispatcher.sendMessage(
    encoder.encode(reqMsg), udp.domainName, ('255.255.255.255', 161)
)
# wait for a maximum of 10 responses or time out
transportDispatcher.jobStarted(1, maxNumberResponses)
# Dispatcher will finish as all jobs counter reaches zero
try:
    transportDispatcher.runDispatcher()
except StopWaiting:
    transportDispatcher.closeDispatcher()
else:
    raise

Download script.

See also: library reference.

Agent-side MIB implementations

Implementing scalar MIB objects

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

  • SNMPv1 or SNMPv2c
  • with SNMP community "public"
  • over IPv4/UDP, listening at 127.0.0.1:161
  • over IPv6/UDP, listening at [::1]:161
  • serving two Managed Objects Instances (sysDescr.0 and sysUptime.0)

Either of the following Net-SNMP commands will walk this Agent:

$ snmpwalk -v2c -c public 127.0.0.1 .1.3.6
$ snmpwalk -v2c -c public udp6:[::1] .1.3.6

The Command Receiver below uses two distinct transports for communication with Command Generators - UDP over IPv4 and UDP over IPv6.

from pysnmp.carrier.asyncore.dispatch import AsyncoreDispatcher
from pysnmp.carrier.asyncore.dgram import udp, udp6, unix
from pyasn1.codec.ber import encoder, decoder
from pysnmp.proto import api
import time, bisect
class SysDescr(object):
    name = (1, 3, 6, 1, 2, 1, 1, 1, 0)
    def __eq__(self, other): return self.name == other
    def __ne__(self, other): return self.name != other
    def __lt__(self, other): return self.name < other
    def __le__(self, other): return self.name <= other
    def __gt__(self, other): return self.name > other
    def __ge__(self, other): return self.name >= other
    def __call__(self, protoVer):
        return api.protoModules[protoVer].OctetString(
            'PySNMP example command responder'
        )
class Uptime(object):
    name = (1, 3, 6, 1, 2, 1, 1, 3, 0)
    birthday = time.time()
    def __eq__(self, other): return self.name == other
    def __ne__(self, other): return self.name != other
    def __lt__(self, other): return self.name < other
    def __le__(self, other): return self.name <= other
    def __gt__(self, other): return self.name > other
    def __ge__(self, other): return self.name >= other
    def __call__(self, protoVer):
        return api.protoModules[protoVer].TimeTicks(
            (time.time() - self.birthday) * 100
        )
mibInstr = (
    SysDescr(), Uptime()  # sorted by object name
)
mibInstrIdx = {}
for mibVar in mibInstr:
    mibInstrIdx[mibVar.name] = mibVar
def cbFun(transportDispatcher, transportDomain, transportAddress, wholeMsg):
    while wholeMsg:
        msgVer = api.decodeMessageVersion(wholeMsg)
        if msgVer in api.protoModules:
            pMod = api.protoModules[msgVer]
        else:
            print('Unsupported SNMP version %s' % msgVer)
            return
        reqMsg, wholeMsg = decoder.decode(
            wholeMsg, asn1Spec=pMod.Message(),
        )
        rspMsg = pMod.apiMessage.getResponse(reqMsg)
        rspPDU = pMod.apiMessage.getPDU(rspMsg)
        reqPDU = pMod.apiMessage.getPDU(reqMsg)
        varBinds = []
        pendingErrors = []
        errorIndex = 0
        # GETNEXT PDU
        if reqPDU.isSameTypeWith(pMod.GetNextRequestPDU()):
            # Produce response var-binds
            for oid, val in pMod.apiPDU.getVarBinds(reqPDU):
                errorIndex = errorIndex + 1
                # Search next OID to report
                nextIdx = bisect.bisect(mibInstr, oid)
                if nextIdx == len(mibInstr):
                    # Out of MIB
                    varBinds.append((oid, val))
                    pendingErrors.append(
                        (pMod.apiPDU.setEndOfMibError, errorIndex)
                    )
                else:
                    # Report value if OID is found
                    varBinds.append(
                        (mibInstr[nextIdx].name, mibInstr[nextIdx](msgVer))
                    )
        elif reqPDU.isSameTypeWith(pMod.GetRequestPDU()):
            for oid, val in pMod.apiPDU.getVarBinds(reqPDU):
                if oid in mibInstrIdx:
                    varBinds.append((oid, mibInstrIdx[oid](msgVer)))
                else:
                    # No such instance
                    varBinds.append((oid, val))
                    pendingErrors.append(
                        (pMod.apiPDU.setNoSuchInstanceError, errorIndex)
                    )
                    break
        else:
            # Report unsupported request type
            pMod.apiPDU.setErrorStatus(rspPDU, 'genErr')
        pMod.apiPDU.setVarBinds(rspPDU, varBinds)
        # Commit possible error indices to response PDU
        for f, i in pendingErrors:
            f(rspPDU, i)
        transportDispatcher.sendMessage(
            encoder.encode(rspMsg), transportDomain, transportAddress
        )
    return wholeMsg
transportDispatcher = AsyncoreDispatcher()
transportDispatcher.registerRecvCbFun(cbFun)
# UDP/IPv4
transportDispatcher.registerTransport(
    udp.domainName, udp.UdpSocketTransport().openServerMode(('localhost', 161))
)
# UDP/IPv6
transportDispatcher.registerTransport(
    udp6.domainName, udp6.Udp6SocketTransport().openServerMode(('::1', 161))
)
## Local domain socket
# transportDispatcher.registerTransport(
#    unix.domainName, unix.UnixSocketTransport().openServerMode('/tmp/snmp-agent')
# )
transportDispatcher.jobStarted(1)
try:
    # Dispatcher will never finish as job#1 never reaches zero
    transportDispatcher.runDispatcher()
except:
    transportDispatcher.closeDispatcher()
    raise

Download script.

See also: library reference.

Transport tweaks

The following script sends two SNMP TRAP notification using the following options:

  • with SNMPv1
  • with community name 'public'
  • over IPv4/UDP and IPv6/UDP
  • send TRAP notification
  • to a Manager at demo.snmplabs.com:162 and [::1]
  • with TRAP ID 'coldStart' specified as an OID
  • include managed objects information:
  • with default Uptime value
  • with default Agent Address with '127.0.0.1'
  • overriding Enterprise OID with 1.3.6.1.4.1.20408.4.1.1.2

The following Net-SNMP commands will produce similar SNMP notification:

$ snmptrap -v1 -c public udp:demo.snmplabs.com 1.3.6.1.4.1.20408.4.1.1.2 127.0.0.1 1 0 12345
$ snmptrap -v1 -c public udp6:[::1] 1.3.6.1.4.1.20408.4.1.1.2 127.0.0.1 1 0 12345
from pysnmp.carrier.asyncore.dispatch import AsyncoreDispatcher
from pysnmp.carrier.asyncore.dgram import udp, udp6, unix
from pyasn1.codec.ber import encoder
from pysnmp.proto import api
# Protocol version to use
pMod = api.protoModules[api.protoVersion1]
# pMod = api.protoModules[api.protoVersion2c]
# Build PDU
trapPDU = pMod.TrapPDU()
pMod.apiTrapPDU.setDefaults(trapPDU)
# Traps have quite different semantics across proto versions
if pMod == api.protoModules[api.protoVersion1]:
    pMod.apiTrapPDU.setEnterprise(trapPDU, (1, 3, 6, 1, 1, 2, 3, 4, 1))
    pMod.apiTrapPDU.setGenericTrap(trapPDU, 'coldStart')
# Build message
trapMsg = pMod.Message()
pMod.apiMessage.setDefaults(trapMsg)
pMod.apiMessage.setCommunity(trapMsg, 'public')
pMod.apiMessage.setPDU(trapMsg, trapPDU)
transportDispatcher = AsyncoreDispatcher()
# UDP/IPv4
transportDispatcher.registerTransport(
    udp.domainName, udp.UdpSocketTransport().openClientMode()
)
transportDispatcher.sendMessage(
    encoder.encode(trapMsg), udp.domainName, ('demo.snmplabs.com', 162)
)
# UDP/IPv6
transportDispatcher.registerTransport(
    udp6.domainName, udp6.Udp6SocketTransport().openClientMode()
)
transportDispatcher.sendMessage(
    encoder.encode(trapMsg), udp6.domainName, ('::1', 162)
)
## Local domain socket
# transportDispatcher.registerTransport(
#    unix.domainName, unix.UnixSocketTransport().openClientMode()
# )
# transportDispatcher.sendMessage(
#    encoder.encode(trapMsg), unix.domainName, '/tmp/snmp-manager'
# )
# Dispatcher will finish as all scheduled messages are sent
transportDispatcher.runDispatcher()
transportDispatcher.closeDispatcher()

Download script.

The following script sends SNMP INFORM notification using the following options:

  • with SNMPv2c
  • with community name 'public'
  • over IPv4/UDP and IPv6/UDP
  • send INFORM notification
  • to a Manager at demo.snmplabs.com:162 and [::1]:162
  • with TRAP ID 'coldStart' specified as an OID

The following Net-SNMP command will produce similar SNMP notification:

$ snmpinform -v2c -c public udp:demo.snmplabs.com 0 1.3.6.1.6.3.1.1.5.1
$ snmpinform -v2c -c public udp6:[::1] 0 1.3.6.1.6.3.1.1.5.1
from time import time
from pysnmp.carrier.asynsock.dispatch import AsynsockDispatcher
from pysnmp.carrier.asynsock.dgram import udp, udp6
from pyasn1.codec.ber import encoder, decoder
from pysnmp.proto.api import v2c as pMod
# Build PDU
reqPDU = pMod.InformRequestPDU()
pMod.apiTrapPDU.setDefaults(reqPDU)
# Build message
trapMsg = pMod.Message()
pMod.apiMessage.setDefaults(trapMsg)
pMod.apiMessage.setCommunity(trapMsg, 'public')
pMod.apiMessage.setPDU(trapMsg, reqPDU)
startedAt = time()
def cbTimerFun(timeNow):
    if timeNow - startedAt > 3:
        raise Exception("Request timed out")
# noinspection PyUnusedLocal,PyUnusedLocal
def cbRecvFun(transportDispatcher, transportDomain, transportAddress,
              wholeMsg, reqPDU=reqPDU):
    while wholeMsg:
        rspMsg, wholeMsg = decoder.decode(wholeMsg, asn1Spec=pMod.Message())
        rspPDU = pMod.apiMessage.getPDU(rspMsg)
        # Match response to request
        if pMod.apiPDU.getRequestID(reqPDU) == pMod.apiPDU.getRequestID(rspPDU):
            # Check for SNMP errors reported
            errorStatus = pMod.apiPDU.getErrorStatus(rspPDU)
            if errorStatus:
                print(errorStatus.prettyPrint())
            else:
                print('INFORM message delivered, response var-binds follow')
                for oid, val in pMod.apiPDU.getVarBinds(rspPDU):
                    print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
            transportDispatcher.jobFinished(1)
    return wholeMsg
transportDispatcher = AsynsockDispatcher()
transportDispatcher.registerRecvCbFun(cbRecvFun)
transportDispatcher.registerTimerCbFun(cbTimerFun)
# UDP/IPv4
transportDispatcher.registerTransport(
    udp.domainName, udp.UdpSocketTransport().openClientMode()
)
transportDispatcher.sendMessage(
    encoder.encode(trapMsg), udp.domainName, ('demo.snmplabs.com', 162)
)
transportDispatcher.jobStarted(1)
# UDP/IPv6
# transportDispatcher.registerTransport(
#    udp6.domainName, udp6.Udp6SocketTransport().openClientMode()
# )
# transportDispatcher.sendMessage(
#    encoder.encode(trapMsg), udp6.domainName, ('::1', 162)
# )
# transportDispatcher.jobStarted(1)
# Dispatcher will finish as all scheduled messages are sent
transportDispatcher.runDispatcher()
transportDispatcher.closeDispatcher()

Download script.

See also: library reference.

Transport tweaks

Receive SNMP TRAP messages with the following options:

  • SNMPv1/SNMPv2c
  • with SNMP community "public"
  • over IPv4/UDP, listening at 127.0.0.1:162
  • over IPv6/UDP, listening at [::1]:162
  • print received data on stdout

Either of the following Net-SNMP commands will send notifications to this receiver:

$ snmptrap -v1 -c public 127.0.0.1 1.3.6.1.4.1.20408.4.1.1.2 127.0.0.1 1 1 123 1.3.6.1.2.1.1.1.0 s test
$ snmptrap -v2c -c public udp6:[::1] 123 1.3.6.1.6.3.1.1.5.1 1.3.6.1.2.1.1.5.0 s test

Notification Receiver below uses two different transports for communication with Notification Originators - UDP over IPv4 and UDP over IPv6.

from pysnmp.carrier.asyncore.dispatch import AsyncoreDispatcher
from pysnmp.carrier.asyncore.dgram import udp, udp6, unix
from pyasn1.codec.ber import decoder
from pysnmp.proto import api
# noinspection PyUnusedLocal
def cbFun(transportDispatcher, transportDomain, transportAddress, wholeMsg):
    while wholeMsg:
        msgVer = int(api.decodeMessageVersion(wholeMsg))
        if msgVer in api.protoModules:
            pMod = api.protoModules[msgVer]
        else:
            print('Unsupported SNMP version %s' % msgVer)
            return
        reqMsg, wholeMsg = decoder.decode(
            wholeMsg, asn1Spec=pMod.Message(),
        )
        print('Notification message from %s:%s: ' % (
            transportDomain, transportAddress
        )
              )
        reqPDU = pMod.apiMessage.getPDU(reqMsg)
        if reqPDU.isSameTypeWith(pMod.TrapPDU()):
            if msgVer == api.protoVersion1:
                print('Enterprise: %s' % (pMod.apiTrapPDU.getEnterprise(reqPDU).prettyPrint()))
                print('Agent Address: %s' % (pMod.apiTrapPDU.getAgentAddr(reqPDU).prettyPrint()))
                print('Generic Trap: %s' % (pMod.apiTrapPDU.getGenericTrap(reqPDU).prettyPrint()))
                print('Specific Trap: %s' % (pMod.apiTrapPDU.getSpecificTrap(reqPDU).prettyPrint()))
                print('Uptime: %s' % (pMod.apiTrapPDU.getTimeStamp(reqPDU).prettyPrint()))
                varBinds = pMod.apiTrapPDU.getVarBinds(reqPDU)
            else:
                varBinds = pMod.apiPDU.getVarBinds(reqPDU)
            print('Var-binds:')
            for oid, val in varBinds:
                print('%s = %s' % (oid.prettyPrint(), val.prettyPrint()))
    return wholeMsg
transportDispatcher = AsyncoreDispatcher()
transportDispatcher.registerRecvCbFun(cbFun)
# UDP/IPv4
transportDispatcher.registerTransport(
    udp.domainName, udp.UdpSocketTransport().openServerMode(('localhost', 162))
)
# UDP/IPv6
transportDispatcher.registerTransport(
    udp6.domainName, udp6.Udp6SocketTransport().openServerMode(('::1', 162))
)
## Local domain socket
# transportDispatcher.registerTransport(
#    unix.domainName, unix.UnixSocketTransport().openServerMode('/tmp/snmp-manager')
# )
transportDispatcher.jobStarted(1)
try:
    # Dispatcher will never finish as job#1 never reaches zero
    transportDispatcher.runDispatcher()
except:
    transportDispatcher.closeDispatcher()
    raise

Download script.

See also: library reference.

This script explains how Python application could turn SNMP PDU variable-bindings into MIB objects or the other way around.

The code that configures MIB compiler is similar to what happens inside the pysnmp.hlapi API.

from pysnmp.smi import builder, view, compiler, rfc1902
# Assemble MIB browser
mibBuilder = builder.MibBuilder()
mibViewController = view.MibViewController(mibBuilder)
compiler.addMibCompiler(mibBuilder, sources=['file:///usr/share/snmp/mibs',
                                             'http://mibs.snmplabs.com/asn1/@mib@'])
# Pre-load MIB modules we expect to work with
mibBuilder.loadModules('SNMPv2-MIB', 'SNMP-COMMUNITY-MIB')
# This is what we can get in TRAP PDU
varBinds = [
    ('1.3.6.1.2.1.1.3.0', 12345),
    ('1.3.6.1.6.3.1.1.4.1.0', '1.3.6.1.6.3.1.1.5.2'),
    ('1.3.6.1.6.3.18.1.3.0', '0.0.0.0'),
    ('1.3.6.1.6.3.18.1.4.0', ''),
    ('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', 'my system')
]
# Run var-binds through MIB resolver
# You may want to catch and ignore resolution errors here
varBinds = [rfc1902.ObjectType(rfc1902.ObjectIdentity(x[0]), x[1]).resolveWithMib(mibViewController) for x in varBinds]
for varBind in varBinds:
    print(varBind.prettyPrint())

Download script.

This script explains how Python application (typically pysnmp-based SNMP Manager) could turn SNMP PDU variable-bindings into MIB objects or the other way around.

The code below does not explicitly add MIB compiler - that happens behind the scenes. Examples below try to demo different kinds of MIB objects to work with.

from pysnmp.smi import builder, view, rfc1902, error
# MIB Builder manages pysnmp MIBs
mibBuilder = builder.MibBuilder()
# MIB View Controller implements various queries to loaded MIBs
mibView = view.MibViewController(mibBuilder)
# Obtain MIB object information by MIB object name
mibVar = rfc1902.ObjectIdentity('IF-MIB', 'ifInOctets', 1)
# Optionally attach PySMI MIB compiler to MIB Builder that would
# create pysnmp MIBs on demand from ASN.1 sources downloaded from
# a web site.
try:
    mibVar.addAsn1MibSource('http://mibs.snmplabs.com/asn1/@mib@')
except error.SmiError:
    print('WARNING: not using MIB compiler (PySMI not installed)')
mibVar.resolveWithMib(mibView)
print(mibVar.prettyPrint(), tuple(mibVar), str(mibVar))
# Obtain MIB object information by its [sequence] OID
mibVar = rfc1902.ObjectIdentity(tuple(mibVar)).resolveWithMib(mibView)
print(mibVar.prettyPrint(), tuple(mibVar), str(mibVar))
# Obtain MIB object information by its [string] OID
mibVar = rfc1902.ObjectIdentity(str(mibVar)).resolveWithMib(mibView)
print(mibVar.prettyPrint(), tuple(mibVar), str(mibVar))
# Obtain MIB object information by a mix of OID/label parts
mibVar = rfc1902.ObjectIdentity((1, 3, 6, 1, 2, 'mib-2', 1, 'sysDescr')).resolveWithMib(mibView)
print(mibVar.prettyPrint(), tuple(mibVar), str(mibVar))
# Obtain MIB object information by a label
mibVar = rfc1902.ObjectIdentity('iso.org.dod.internet.mgmt.mib-2.system.sysDescr').resolveWithMib(mibView)
print(mibVar.prettyPrint(), tuple(mibVar), str(mibVar))
# Obtain the first MIB object in given MIB module
mibVar = rfc1902.ObjectIdentity('SNMPv2-MIB').resolveWithMib(mibView)
print(mibVar.prettyPrint(), tuple(mibVar), str(mibVar))
# Obtain the last MIB object in given MIB module
mibVar = rfc1902.ObjectIdentity('SNMPv2-MIB', last=True).resolveWithMib(mibView)
print(mibVar.prettyPrint(), tuple(mibVar), str(mibVar))
# Another way to obtain the first (or last) symbol in MIB module
mibVar = rfc1902.ObjectIdentity('SNMPv2-MIB', '').resolveWithMib(mibView)
print(mibVar.prettyPrint(), tuple(mibVar), str(mibVar))
# Obtain MIB symbol from whatever MIB it is defined at (MIB should be loaded)
mibVar = rfc1902.ObjectIdentity('', 'sysDescr', 0).resolveWithMib(mibView)
print(mibVar.prettyPrint(), tuple(mibVar), str(mibVar))
# Create an OID-value pair (called variable-binding in SNMP)
varBind = rfc1902.ObjectType(
    rfc1902.ObjectIdentity('SNMPv2-MIB', 'sysObjectID', 0), '1.3.6.1'
).resolveWithMib(mibView)
print(varBind[0].prettyPrint(), varBind[1].__class__.__name__, varBind[1].prettyPrint())
# Create just OID
varBind = rfc1902.ObjectType(
    rfc1902.ObjectIdentity('SNMPv2-MIB', 'sysObjectID', 0)
).resolveWithMib(mibView)
print(varBind[0].prettyPrint(), varBind[1].__class__.__name__, varBind[1].prettyPrint())
# Create var-binds from MIB notification object (without OBJECTS clause)
varBinds = rfc1902.NotificationType(
    rfc1902.ObjectIdentity('SNMPv2-MIB', 'coldStart')
).resolveWithMib(mibView)
print(['%s = %s(%s)' % (x[0].prettyPrint(), x[1].__class__.__name__, x[1].prettyPrint()) for x in varBinds])
# Create var-binds from MIB notification object (with OBJECTS clause)
varBinds = rfc1902.NotificationType(
    rfc1902.ObjectIdentity('IF-MIB', 'linkUp'),
    instanceIndex=(1,),
    objects={('IF-MIB', 'ifOperStatus'): 'down'}
).resolveWithMib(mibView)
print(varBinds.prettyPrint())

Download script.

This script explains how Python application (typically SNMP Manager) could load SNMP MIB modules into memory and introspect Managed Objects defined in MIB.

from pysnmp.smi import builder, view, compiler, error
# Create MIB loader/builder
mibBuilder = builder.MibBuilder()
# Optionally attach PySMI MIB compiler (if installed)
#print('Attaching MIB compiler...'),
#compiler.addMibCompiler(mibBuilder, sources=['/usr/share/snmp/mibs'])
#print('done')
# Optionally set an alternative path to compiled MIBs
print('Setting MIB sources...')
mibBuilder.addMibSources(builder.DirMibSource('/opt/pysnmp_mibs'))
print(mibBuilder.getMibSources())
print('done')
print('Loading MIB modules...'),
mibBuilder.loadModules(
    'SNMPv2-MIB', 'SNMP-FRAMEWORK-MIB', 'SNMP-COMMUNITY-MIB', 'IP-MIB'
    )
print('done')
print('Indexing MIB objects...'),
mibView = view.MibViewController(mibBuilder)
print('done')
print('MIB symbol name lookup by OID: '),
oid, label, suffix = mibView.getNodeName((1,3,6,1,2,1,1,1))
print(oid, label, suffix)
print('MIB symbol name lookup by label: '),
oid, label, suffix = mibView.getNodeName((1,3,6,1,2,'mib-2',1,'sysDescr'))
print(oid, label, suffix)
print('MIB symbol name lookup by symbol description: '),
oid, label, suffix = mibView.getNodeName(('sysDescr',))
oid, label, suffix = mibView.getNodeName(('snmpEngineID',), 'SNMP-FRAMEWORK-MIB')
print(oid, label, suffix)
print('MIB object value pretty print: '),
mibNode, = mibBuilder.importSymbols('SNMP-FRAMEWORK-MIB', 'snmpEngineID')
print(mibNode.syntax.prettyPrint())
print('MIB symbol location lookup by name: '),
modName, symName, suffix = mibView.getNodeLocation(('snmpCommunityEntry',))
print(symName, modName)
print('MIB node lookup by location: '),
rowNode, = mibBuilder.importSymbols(modName, symName)
print(rowNode)
print('Conceptual table index value to oid conversion: '),
oid = rowNode.getInstIdFromIndices('router')
print(oid)
print('Conceptual table index oid to value conversion: '),
print(rowNode.getIndicesFromInstId(oid))
print('MIB tree traversal')
oid, label, suffix = mibView.getFirstNodeName()
while 1:
    try:
        modName, nodeDesc, suffix = mibView.getNodeLocation(oid)
        print('%s::%s == %s' % (modName, nodeDesc, oid))
        oid, label, suffix = mibView.getNextNodeName(oid)
    except error.NoSuchObjectError:
        break
print('Modules traversal')
modName = mibView.getFirstModuleName()
while 1:
    if modName: print(modName)
    try:
        modName = mibView.getNextModuleName(modName)
    except error.SmiError:
        break

Download script.

See also: library reference.

This script explains how SNMP Agent application could model real-world data as Managed Objects defined in MIB.

from pysnmp.smi import builder
# MIB Builder is normally pre-created by SNMP engine
mibBuilder = builder.MibBuilder()
#
# This may be done in a stand-alone file and then loaded up
# by SNMP Agent
#
# A base class for a custom Managed Object
MibScalarInstance, = mibBuilder.importSymbols(
    'SNMPv2-SMI', 'MibScalarInstance'
)
# Managed object specification
sysLocation, = mibBuilder.importSymbols('SNMPv2-MIB', 'sysLocation')
# Custom Managed Object
class MySysLocationInstance(MibScalarInstance):
    # noinspection PyUnusedLocal
    def readGet(self, name, *args):
        # Just return a custom value
        return name, self.syntax.clone('The Leaky Cauldron')
sysLocationInstance = MySysLocationInstance(
    sysLocation.name, (0,), sysLocation.syntax
)
# Register Managed Object with a MIB tree
mibBuilder.exportSymbols(
    # '__' prefixed MIB modules take precedence on indexing
    '__MY-LOCATION-MIB', sysLocationInstance=sysLocationInstance
)
if __name__ == '__main__':
    #
    # This is what is done internally by Agent.
    #
    from pysnmp.smi import instrum, exval
    mibInstrum = instrum.MibInstrumController(mibBuilder)
    print('Remote manager read access to MIB instrumentation (table walk)')
    oid, val = (), None
    while 1:
        oid, val = mibInstrum.readNextVars(((oid, val),))[0]
        if exval.endOfMib.isSameTypeWith(val):
            break
        print(oid, val.prettyPrint())

Download script.

This script explains how SNMP Agent application manipulates its MIB possibly triggered by SNMP Manager's commands.

# SNMP agent backend e.g. Agent access to Managed Objects
from pysnmp.smi import builder, instrum, exval
print('Loading MIB modules...'),
mibBuilder = builder.MibBuilder().loadModules(
    'SNMPv2-MIB', 'SNMP-FRAMEWORK-MIB', 'SNMP-COMMUNITY-MIB'
)
print('done')
print('Building MIB tree...'),
mibInstrum = instrum.MibInstrumController(mibBuilder)
print('done')
print('Building table entry index from human-friendly representation...'),
snmpCommunityEntry, = mibBuilder.importSymbols(
    'SNMP-COMMUNITY-MIB', 'snmpCommunityEntry'
)
instanceId = snmpCommunityEntry.getInstIdFromIndices('my-router')
print('done')
print('Create/update SNMP-COMMUNITY-MIB::snmpCommunityEntry table row: ')
varBinds = mibInstrum.writeVars(
    ((snmpCommunityEntry.name + (2,) + instanceId, 'mycomm'),
     (snmpCommunityEntry.name + (3,) + instanceId, 'mynmsname'),
     (snmpCommunityEntry.name + (7,) + instanceId, 'volatile'))
)
for oid, val in varBinds:
    print('%s = %s' % ('.'.join([str(x) for x in oid]), not val.isValue and 'N/A' or val.prettyPrint()))
print('done')
print('Read whole MIB (table walk)')
oid, val = (), None
while True:
    oid, val = mibInstrum.readNextVars(((oid, val),))[0]
    if exval.endOfMib.isSameTypeWith(val):
        break
    print('%s = %s' % ('.'.join([str(x) for x in oid]), not val.isValue and 'N/A' or val.prettyPrint()))
print('done')
print('Unloading MIB modules...'),
mibBuilder.unloadModules()
print('done')

Download script.

See also: library reference.

Before doing cut&paste of the code below into your Python interpreter, make sure to install pysnmp and its dependencies by running pip or easy_install:

# pip pysnmp

There's a public, multilingual SNMP Command Responder and Notification Receiver configured at demo.snmplabs.com to let you run PySNMP examples scripts in a cut&paste fashion. If you wish to use your own SNMP Agent with these scripts, make sure to either configure your local snmpd and/or snmptrapd or use a valid address and SNMP credentials of your SNMP Agent in the examples to let them work.

Should you want to use a MIB to make SNMP operations more human-friendly, you are welcome to search for it and possibly download one from our public MIB repository. Alternatively, you can configure PySNMP to fetch and cache required MIBs from there automatically.

If you find your PySNMP application behaving unexpectedly, try to enable a /more or less verbose/ built-in PySNMP debugging by adding the following snippet of code at the beginning of your application:

from pysnmp import debug
# use specific flags or 'all' for full debugging
debug.setLogger(debug.Debug('dsp', 'msgproc', 'secmod'))

Then run your app and watch stderr. The Debug initializer enables debugging for a particular PySNMP subsystem, 'all' enables full debugging. More specific flags are:

  • io
  • dsp
  • msgproc
  • secmod
  • mibbuild
  • mibview
  • mibinstrum
  • acl
  • proxy
  • app

For more details on PySNMP programming model and interfaces, please refer to library documentation.

Best way is usually to

# pip install pysnmp

If that does not work for you for some reason, you might need to read the following page.

The PySNMP software is provided under terms and conditions of BSD-style license, and can be freely downloaded from PyPI or GitHub (master branch).

Besides official releases, it's advisable to try the cutting-edge development code that could be taken from PySNMP source code repository. It may be less stable in regards to general operation and changes to public interfaces, but it's first to contain fixes to recently discovered bugs.

The best way to obtain PySNMP and dependencies is to run:

$ pip install pysnmp

or

$ easy_install pysnmp

In case you do not have the easy_install command on your system but still would like to use the on-line package installation method, please install setuptools package by downloading and running ez_setup.pz bootstrap:

# wget https://bootstrap.pypa.io/ez_setup.py
# python ez_setup.py

In case you are installing PySNMP on an off-line system, the following packages need to be downloaded and installed for PySNMP to become operational:

  • PyASN1, used for handling ASN.1 objects
  • PySNMP, SNMP engine implementation

Optional, but recommended:

  • PyCryptodomex, used by SNMPv3 crypto features
  • PySMI for automatic MIB download and compilation. That helps visualizing more SNMP objects
  • Ply, parser generator required by PySMI

The installation procedure for all the above packages is as follows (on UNIX-based systems):

$ tar zxf package-X.X.X.tar.gz
$ cd package-X.X.X
# python setup.py install
# cd ..
# rm -rf package-X.X.X

In case of any issues, please open a GitHub issue so we could try to help out.

Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com> All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Here we have an ever-growing list of frequently asked questions regarding PySNMP usage issues. If you got an issue that you think is worth noting here, please open a GitHub issue.

Keep in mind that some the answers below may not be universally applicable to any PySNMP revision.

How do I find out peer transport address or security information within my receiving app (CommandResponder or Notification Receiver)?
SNMP architecture forces you to distinguish communicating entities only on the basis of their community names (SNMPv1/v2c) or ContextEngineId/ContextName pair (SNMPv3).

In other words, if one SNMP Manager should anyhow differ from another, then they should use distinct community names or SNMP contexts. Transport information should never be used for the identification purposes, as in some cases it proves to be unreliable (cases include NAT device or a proxy in the middle, not to mention address spoofing).

As practice reveals, even perfect design does not always cope well with the imperfect world. So we had to pinch a logic hole from the scope of an SNMP app down to transport layer. Now with the getTransportInfo(stateReference) method call you could get peer transport information upon receiving its SNMP message.

# Callback function for receiving notifications
def cbFun(snmpEngine,
          stateReference,
          contextEngineId, contextName,
          varBinds,
          cbCtx):
    transportDomain, transportAddress = snmpEngine.msgAndPduDsp.getTransportInfo(stateReference)

How to instantiate static MIB table at my SNMP Agent?
You need to create MibScalarInstance class instances and register them with your Agent's SNMP engine (mibBuilder, more specifically). Here's an example code for a IP-MIB table:
# SNMP Agent (AKA CommandResponder) is built around SNMP engine object
snmpEngine = engine.SnmpEngine()
# Import table columns
( ipAddressAddrType,
  ipAddressAddr,
  ipAddressIfIndex,
  ipAddressType,
  ipAddressPrefix,
  ipAddressOrigin,
  ipAddressStatus,
  ipAddressCreated,
  ipAddressLastChanged,
  ipAddressRowStatus,
  ipAddressStorageType ) = snmpEngine.msgAndPduDsp.mibInstrumController
.mibBuilder.importSymbols(
  'IP-MIB',
  'ipAddressAddrType',
  'ipAddressAddr',
  'ipAddressIfIndex',
  'ipAddressType',
  'ipAddressPrefix',
  'ipAddressOrigin',
  'ipAddressStatus',
  'ipAddressCreated',
  'ipAddressLastChanged',
  'ipAddressRowStatus',
  'ipAddressStorageType'
)
# Import MibScalarInstance
MibScalarInstance, = snmpEngine.msgAndPduDsp.mibInstrumController.
mibBuilder.importSymbols('SNMPv2-SMI', 'MibScalarInstance')
# Create table columns instances
_ipAddressAddrType = MibScalarInstance(
    ipAddressAddrType.name, (1, 4, 1, 2, 3, 4),
    ipAddressAddrType.syntax.clone(1)
)
_ipAddressAddr = MibScalarInstance(
    ipAddressAddr.name, (1, 4, 1, 2, 3, 4),
    ipAddressAddr.syntax.clone('1.2.3.4')
)
_ipAddressIfIndex = MibScalarInstance(
    ipAddressIfIndex.name, (1, 4, 1, 2, 3, 4),
    ipAddressIfIndex.syntax.clone(1)
)
_ipAddressType = MibScalarInstance(
    ipAddressType.name, (1, 4, 1, 2, 3, 4),
    ipAddressType.syntax.clone(1)
)
_ipAddressPrefix = MibScalarInstance(
    ipAddressPrefix.name, (1, 4, 1, 2, 3, 4),
    ipAddressPrefix.syntax.clone((0,0))
)
_ipAddressOrigin = MibScalarInstance(
    ipAddressOrigin.name, (1, 4, 1, 2, 3, 4),
    ipAddressOrigin.syntax.clone(1)
)
_ipAddressStatus = MibScalarInstance(
    ipAddressStatus.name, (1, 4, 1, 2, 3, 4),
    ipAddressStatus.syntax.clone(1)
)
_ipAddressCreated = MibScalarInstance(
    ipAddressCreated.name, (1, 4, 1, 2, 3, 4),
    ipAddressCreated.syntax.clone(800)
)
_ipAddressLastChanged = MibScalarInstance(
    ipAddressLastChanged.name, (1, 4, 1, 2, 3, 4),
    ipAddressLastChanged.syntax.clone(600)
)
_ipAddressRowStatus = MibScalarInstance(
    ipAddressRowStatus.name, (1, 4, 1, 2, 3, 4),
    ipAddressRowStatus.syntax.clone(1)
)
_ipAddressStorageType = MibScalarInstance(
    ipAddressStorageType.name, (1, 4, 1, 2, 3, 4),
    ipAddressStorageType.syntax
)
# add anonymous column instances
snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.exportSymbols(
    '_IP-MIB',
    _ipAddressAddrType,
    _ipAddressAddr,
    _ipAddressIfIndex,
    _ipAddressType,
    _ipAddressPrefix,
    _ipAddressOrigin,
    _ipAddressStatus,
    _ipAddressCreated,
    _ipAddressLastChanged,
    _ipAddressRowStatus,
    _ipAddressStorageType
    )
# Command responder code would follow...

Keep in mind that the values of this table row will not change by themselves. They basically hold a snapshot of a data set so your application may have to update them somehow. For example, an app could periodically lookup particular MibScalarInstance by OID at mibBuilder and update its "syntax" attribute with a new value.

There are other ways for building MIB tables that represent dynamic Managed Objects.

Some network devices do not respond to PySNMP-based management requests for particular OIDs.
$ pysnmpget -v2c -c public 10.0.0.33 1.3.6.1.2.1.2.2.1.10.3
SNMPv2-SMI::mib-2.2.2.1.10.3 = Counter32: 1519568842
$ snmpget.py -v2c -c public 10.0.0.33 1.3.6.1.2.1.2.2.1.10.4
requestTimedOut
Meanwhile, tcpcump shows request-response sequence:
13:33:30.161843 IP 10.0.0.33.snmp > 10.0.0.1.51094:
GetResponse(31)  interfaces.ifTable.ifEntry.ifInOctets.3=1532504859
13:33:30.161881 IP 10.0.0.33.snmp > 10.0.0.1.51094:
GetResponse(31)  interfaces.ifTable.ifEntry.ifInOctets.3=1532504859
In some cases, particularily when running v1arch PySNMP code, the
following exception may be thrown on response processing:
 Traceback (most recent call last):
 ....
 File "build/bdist.linux-i686/egg/pyasn1/type/base.py", line 64, in
__init__
 File "build/bdist.linux-i686/egg/pyasn1/type/base.py", line 32, in _verifySubtypeSpec
 File "build/bdist.linux-i686/egg/pyasn1/type/constraint.py", line 33, in __call__
 pyasn1.type.error.ValueConstraintError: ConstraintsIntersection(ConstraintsIntersection(), ValueRangeConstraint(0, 4294967295)) failed at: ValueRangeConstraint(0, 4294967295) failed at: -1413698940
This appears to be a [widespread] bug in BER integer encoders. It usually gets noticed on Counter values as they are constrained to be positive while wrong encoding yelds them negative.

Here's broken encoding:

>>> decoder.decode('A\x04\xab\xbc\xaa\x84', asn1Spec=rfc1155.Counter())
Traceback (most recent call last):
...
pyasn1.type.error.ValueConstraintError: ConstraintsIntersection(ConstraintsIntersection(), ValueRangeConstraint(0, 4294967295)) failed at: ValueRangeConstraint(0, 4294967295) failed at: -1413698940

And here's a good one:

>>> decoder.decode('A\x05\x00\xab\xbc\xaa\x84',
>>> asn1Spec=rfc1155.Counter())
(Counter('2881268356'), '')
Notice the third octet -- positive values must have its highest bit set
to zero.
Here's an example hack that converts negated values into their positive
complimentaries for Counter type.
from pysnmp.proto import rfc1155, rfc1902, api
from pyasn1.codec.ber import encoder, decoder
# --- hack Counter type
def counterCloneHack(self, *args):
    if args and args[0] < 0:
        args = (0xffffffff+args[0]-1,) + args[1:]
    return self.__class__(*args)
rfc1155.Counter.clone = counterCloneHack
rfc1902.Counter32.clone = counterCloneHack
Execute this hack before any SNMP message processing occures in your app.
The bad news is that if this BER encoding bug also affects Integer values,
in that case it is theoretically impossible to fix because, unlike Counter,
Integer values may legally be negative so they could not unconditionally be
converted into positives.
Therefore the best solutoin would be to get vendors fixing their
BER encoders.

I need my receiving entity (CommandResponder or Notification Receiver) to listen for SNMP messages on multiple network interfaces. How do I do that with pysnmp?
Simply register multiple network transports with your SNMP engine. Each transport would be bound to an individual local transport endpoint (for instance, IP address & UDP port pair).
 # Security setup would follow
 ...
 # Setup first transport endpoint
 config.addSocketTransport(
     snmpEngine,
     udp.domainName + (1,),
     udp.UdpSocketTransport().openServerMode(('127.0.0.1', 162))
 )
 # Setup second transport endpoint
 config.addSocketTransport(
     snmpEngine,
     udp.domainName + (2,),
     udp.UdpSocketTransport().openServerMode(('192.168.1.1', 162))
 )
 # Receiver callback function implementation and Dispatcher invocation
 # would follow
 ...
Notice extended transport domain specification (udp.domainName) in
the code above. There we register each transport endpoint under distinct
OID, however always within the canonical transport domain OID.

When my PySNMP application prints out fetched values, some of them come out as a garbage on my screan. Here's my code:
 for varBind in varBinds:
   print(' = '.join([ str(x) for x in varBind ])
and the result is:
 1.3.6.1.4.1.161.19.3.2.1.63.0 = 50000
 1.3.6.1.4.1.161.19.3.2.1.4.0 = '\x01\x02\x03\x04'
The IpAddress type seems to be the only one with this problem.
Always use prettyPrint() method for all pyasn1-based objects -- it automatically converts ASN1 types to human-friendly form.
> > > from pysnmp.proto import rfc1902
> > > a = rfc1902.IpAddress('1.2.3.4')
> > > str(a)
'\x01\x02\x03\x04'
> > > a
IpAddress('1.2.3.4')
> > > a.prettyPrint()
'1.2.3.4'
> > > rfc1902.IpAddress.prettyPrint(a)
'1.2.3.4'

See pyasn1 docs for more information on pyasn1 data model.

When fetching data with snmp*.py command-line tools, some values do not print out nicely:
 $ snmpget.py -v2c -c public 127.0.0.1 .1.3.6.1.4.1.14988.1.1.1.2.1.1.0.23.183.34.8.200.3
 SNMPv2-SMI::enterprises.14988.1.1.1.2.1.1.0.23.183.34.8.200.3 =
 OctetString: ˇČ
where Net-SNMP gives nicely formatted human-readable string:
 $ snmpget -v2c -c public 127.0.0.1 .1.3.6.1.4.1.14988.1.1.1.2.1.1.0.23.183.34.8.200.3
 SNMPv2-SMI::enterprises.14988.1.1.1.2.1.1.0.23.183.34.8.200.3 =
 Hex-STRING: 00 17 B7 22 08 C8
What can be done to PySNMP to make it returning HEX data in human-readable?
The difference is that Net-SNMP prints values into hex by-default, whereas pysnmp does not do that. You can force snmpget.py to work similarily with the -OT command line parameter.
$ snmpget.py -OT -v2c -c public 127.0.0.1 .1.3.6.1.4.1.14988.1.1.1.2.1.1.0.23.
183.34.8.200.3
SNMPv2-SMI::enterprises.14988.1.1.1.2.1.1.0.23.183.34.8.200.3 =
OctetString: 00 17 b7 22 08 c8

Another matter is MIB lookup - when snmp*.py tool can use a MIB to figure out what are the display conventions for particular value type, it will reformat the value in a human-readable form.

To let MIB lookup work, please pass appropriate MIB name to snmp*.py tool through command line:

$ snmpwalk.py -m IP-MIB,IF-MIB -v2c -c public 127.0.0.1 .1.3.6.1.4.1

I'm walking a particular Agent with the nextCmd() or bulkCmd() functions. It works for some OIDs, but invariably fails at certain OID with the OID not increasing error. What does it mean and how do I fix that?
The Agent you are talking to seems to be broken. The OID not increasing message means that in the course of fetching OIDs from the Agent, Manager receives an OID that is not greater than those passed in request. Due to the nature of GETNEXT/GETBULK algorithm, passing the same or lesser OID to Manager would result in fetching the same set of OIDs over and over again effectively creating an infinite loop between Manager and Agent so they may never reach the end of MIB. To prevent this the Manager tries to intervene and prevent such loop from happening.

If you have to work with a broken Agent and can terminate the GETNEXT/GETBULK command at some point, you can pass the ignoreNonIncreasingOid=True keyword parameter to the nextCmd() or bulkCmd() to disable OID verification at the Manager side.

for (errorIndication,
     errorStatus,
     errorIndex,
     varBinds) in nextCmd(SnmpEngine(),
                          CommunityData('public'),
                          UdpTransportTarget(('demo.snmplabs.com', 161)),
                          ContextData(),
                          ObjectType(ObjectIdentity('1.3.6')),
                          ignoreNonIncreasingOid=True):
    if errorIndication:
        print(errorIndication)
        break
    elif errorStatus:
        print('%s at %s' % (errorStatus.prettyPrint(),
                            errorIndex and varBinds[int(errorIndex) - 1][0] or '?'))
        break
    else:
        for varBind in varBinds:
            print(' = '.join([x.prettyPrint() for x in varBind])

How to make use of random MIBs at my Manager application?
Starting from PySNMP 4.3.x, plain-text (ASN.1) MIBs can be automatically parsed into PySNMP form by the PySMI tool. PySNMP will call PySMI automatically, parsed PySNMP MIB will be cached in $HOME/.pysnmp/mibs/ (default location).

MIB compiler could be configured to search for plain-text MIBs at multiple local and remote locations. As for remote MIB repos, you are welcome to use our collection of ASN.1 MIB files at http://mibs.snmplabs.com/asn1/ as shown below.

from pysnmp.hlapi import *
errorIndication, errorStatus, errorIndex, varBinds = next(
    getCmd(SnmpEngine(),
           CommunityData('public'),
           UdpTransportTarget(('demo.snmplabs.com', 161)),
           ContextData(),
           ObjectType(ObjectIdentity('IF-MIB', 'ifInOctets', 1).addAsn1MibSource('file:///usr/share/snmp',
                                                                                 'http://mibs.snmplabs.com/asn1/@mib@')))
)
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]))

Download script.

Alternatively, you can invoke the mibdump.py (shipped with PySMI) by hand and this way compile plain-text MIB into PySNMP format. Once the compiled MIBs are stored in a directory, add the directory to your MibBuilder's MibSources.

builder = engine.getMibBuilder()
# Make ./mibs available to all OIDs that are created
# e.g. with "MIB-NAME-MIB::identifier"
builder.addMibSources(builder_module.DirMibSource(
    os.path.join( HERE, 'mibs')
))

I packed my pysnmp-based application with py2exe. When I run my app, it throws a traceback like this:

File "pysnmpentityrfc3413onelinercmdgen.pyc", line 116, in __init__ File "pysnmpentityengine.pyc", line 16, in __init__ File "pysnmpprotorfc3412.pyc", line 16, in __init__ File "pysnmpsmibuilder.pyc", line 143, in __init__ File "pysnmpsmibuilder.pyc", line 35, in init File "pysnmpsmibuilder.pyc", line 80, in _init ImportError: No module named mibs.instances

PySNMP claims itself to be py2exe-friendly. How to make it working?
You have to list pysnmp MIB directories explicitly at your app's setup.py so that py2exe would include them into the binary.
from distutils.core import setup
import sys
options = {}
if "py2exe" in sys.argv:
  import py2exe
  # fix executables
  options['console'] = ['myapp.py']
  # add files not found my modulefinder
  options['options'] = {
    'py2exe': {
      'includes': [
        'pysnmp.smi.mibs.*',
        'pysnmp.smi.mibs.instances.*'
      ]
    }
  }
setup(**options)

My CommandGenerator app reports OIDs and values in form of PyASN1 objects. How do I convert them into human-readable, symbolic names and values?
The most easy to use interface to MIB lookup feature is supported by PySNMP 4.2.3 and later. Just pass the
lookupNames=True, lookupValues=True

parameters to getCmd(), setCmd(), nextCmd(), bulkCmd() methods of oneliner CommandGenerator. Then the OIDs in response variable-binding list will get replaced by similarily looking MibVariable instances, their prettyPrint() methods return MIB symbols instead of OIDs.

Response values will still be PyASN1 objects but some may be replaced by TEXTUAL-CONVENTION decorators what make their prettyPrint() methods returning even more human-friendly output.

>>> from pysnmp.entity.rfc3413.oneliner import cmdgen
>>>
>>> cmdGen = cmdgen.CommandGenerator()
>>>
>>> errorIndication, errorStatus, errorIndex, varBinds = cmdGen.getCmd(
...     cmdgen.CommunityData('public'),
...     cmdgen.UdpTransportTarget(('localhost', 161)),
...     '1.3.6.1.2.1.1.1.0',
...     lookupNames=True, lookupValues=True
... )
>>>
>>> name, value = varBinds[0]
>>> name
MibVariable(ObjectName(1.3.6.1.2.1.1.1.0))
>>> value
DisplayString('Linux saturn 2.6.38.1 Sat Apr 9 23:39:07 CDT 2012 i686')
>>> name.prettyPrint()
'SNMPv2-MIB::sysDescr."0"'
>>> value.prettyPrint()
'Linux cray 2.6.37.6-smp #2 SMP Sat Apr 9 23:39:07 CDT 2011 i686'
>>>

If you are using older PySNMP versions it's strongly recommended to upgrade to the latest one.

Will PySNMP Manager verify the values it sends to and receives from a distant Agent against local MIB constraints?
Yes, it can do that. The Manager will verify the values you pass to SET request against a MIB if:

The values are not already PyASN1 objects but some basic Python types (like integer or string). You tell PySNMP engine to load appropriate MIB where it could lookup the constraints (via the use of MibVariable) So, the following code fragment makes PySNMP engine loading SNMPv2-MIB and verifying that the 'new system name' value satisfies sysName constraints (if any).

errorIndication, errorStatus, errorIndex, varBinds = cmdGen.setCmd(
    cmdgen.CommunityData('public'),
    cmdgen.UdpTransportTarget(('localhost', 161)),
    ( cmdgen.MibVariable('SNMPv2-MIB', 'sysName', 0), 'new system name' )
)

To verify the response values, you should pass at least lookupValues flag to CommandGenerator *cmd() method you use. In the following example PySNMP will make sure that Agent-supplied value for SNMPv2-MIB::sysName Managed Object satisfies MIB constraints (if any).

errorIndication, errorStatus, errorIndex, varBinds = cmdGen.getCmd(
    cmdgen.CommunityData('public'),
    cmdgen.UdpTransportTarget(('localhost', 161)),
    cmdgen.MibVariable('SNMPv2-MIB', 'sysName', 0),
    lookupValues=True
)

In case of constraint violation, a PySNMP exception will be raised.

The nextCmd() and bulkCmd() methods of CommandGenerator app (oneliner version) stop working once returned OIDs went out of scope of request OIDs.

In other words, if I request 1.3.6.1, I would get everything under the 1.3.6.1 prefix, but not 1.3.6.2. Is there any way to make it walking the whole MIB?

Yes, just pass the lexicographicMode=True parameter to CommandGenerator nextCmd() and bulkCmd() methods (introduced in PySNMP 4.2.3+) or set CommandGenerator.lexicographicMode=True option before calling nextCmd() and bulkCmd() methods.
cmdGen = cmdgen.CommandGenerator()
errorIndication, errorStatus, errorIndex, varBindTable = cmdGen.bulkCmd(
    ....,
    ....,
    ....,
    lexicographicMode=True
)

We fanatically document all fixes, changes and new features in changelog. There you could also download the latest unreleased pysnmp tarball containing the latest fixes and improvements.

Fixed broken SNMPv3 msgFlag initialization on authoritative SNMP engine ID discovery. This bug causes secure communication with peer SNMP engines to stall at SNMP engine ID discovery procedure.

  • Added SNMPv3 USM master and localized keys support to LCD configuration
  • Improved initial and runtime USM debugging
  • Fixed a bug in USM configuration which did not allow the same user names to be added under different security names

Reworked VACM access control function. Most important changes include:
  • Added subtree match negation support (vacmViewTreeFamilyType)
  • Added subtree family mask support (vacmViewTreeFamilyMask)
  • Added prefix content name matching support (vacmAccessContextMatch)
  • Added key VACM tables caching for better isAccessAllowed lookup performance

One potential incompatibility may be caused by the addContext() call which now needs to be made explicitly during low-level VACM configuration rather than be a side effect of addVacmAccess() call.

  • Rebased MIB importing code onto importlib because imp is long deprecated
  • Received MIB objects resolution made more forgiving to errors, added optional ignoreErrors parameter to ObjectType.resolveWithMib() to control that behaviour.
  • Fixed asyncore main loop to respect non-default timer resolution
  • Fixed .setTimerResolution() behaviour of abstract main loop dispatcher to update call intervals of the existing periodic dispatcher jobs
  • Fixed var-bindings initialization to prevent pyasn1 encoder failures with newer pyasn1 versions where SequenceOf type looses its default initializer.
  • Fixed crash on uninitialized component serialization left out in SNMP v1 TRAP PDU to SNMPv2/3 TRAP PDU proxy translation routine.

  • Made MIB loader ignoring file and directory access errors
  • Added missing SNMP PDU error classes and their handling in Command Responder
  • Fixed crash on MIB load failure in case of directory access error
  • Fixed socket transparency option (IPV6_TRANSPARENT) to make IPv6 transparent operation functional

Fixed Pythonized MIB load (in the source form) - made sure to turn it into a code object prior to its execution

  • Copyright notice extended to the year 2019
  • Exposed ASN.1 Null type through rfc1902 module for convenience.
  • Use compile() before exec'ing MIB modules to attach filename to the stack frames (ultimately shown in traceback/debugger)
  • Fixed hlapi/v3arch transport target caching to ensure transport targets are different even if just timeout/retries options differ
  • Fixed hlapi LCD configurator to include contextName. Prior to this fix sending SNMPv3 TRAP with non-default contextName would fail.
  • Fixed possible duplicate key occurrence in the OrderedDict following a race condition
  • Fixed undefined name references in inet_pton/inet_ntop substitute routines for IPv6 in TRANSPORT-ADDRESS-MIB.py

  • Improved package build and dependency tracking
  • Fixed missing LICENSE from the tarball distribution
  • Fixed CommandGeneratorLcdConfigurator.unconfigure() to fully clean up internal caches, otherwise repetitive attempts to configure the target would fail.
  • Fix to tolerate possible duplicate enumerations in Bits and Integer SMI types.
  • Fix to tolerate non-initialised entries in SNMP community table. Once a bad entry sneaked into the SNMP community table, all the subsequent SNMP v1/v2c operations failed. The fix ignores incomplete SNMP community table entries in the course of building indices.

  • Added PySnmpError.cause attribute holding parent exception tuple
  • Fixed broken InetAddressType rendering caused by a pyasn1 regression
  • Fixed typo in RFC1158 module
  • Fixed possible infinite loop in GETBULK response PDU builder
  • Fixed memory leak in the config.delContext() VACM management harness
  • Fixed Bits class initialization when enumeration values are given
  • Fixed crash caused by incoming SNMPv3 message requesting SNMPv1/v2c security model
  • Fixed out-of-scope OIDs leaking at the end of SNMP table at hlapi nextCmd and bulkCmd calls when lexicographicMode = False

  • Copyright notice extended to the year 2018
  • Fixed short local key expansion at 3DES key localization implementation.

  • Migrated references from SourceForge
  • Added missing SHA2 support for Blumenthal key localization
  • Fixed named bits handling at rfc1902.Bits
  • Fixed missing SmiError exception class at pysnmp.proto.rfc1155
  • Fixed SNMP v1->v2c PDU proxy -- error-status & error-index fields from v1 PDU get copied over to v2c PDU in addition to the exception sentinels being set

  • The pysnmp version being used gets exposed to the MIB modules via the MibBuilder instance
  • The .setObjects() method of the SMI types now accepts append=False parameter to let the caller adding more than 255 elements over the course of multiple calls
  • Added support for some more missing fields of SMIv2 MACRO types
  • Example scripts rearranged in a way that IPv6 requirement is clearly encoded in the script's name
  • Fixed SNMPv2-SMI.NotificationType to expose .set/getReference() instead of .set/getRevision() which should not be there in the first place
  • Fixed non-implied-OID encoding in SNMP table indices
  • Fixed inconsistent SNMPv3 discovery and retrying algorithm

  • HMAC-SHA-2 Authentication Protocols support added (RFC-7860)
  • The pycryptodome dependency replaced with pycryptodomex as it is recommended by the upstream to avoid unwanted interference with PyCrypto package should it also be installed
  • Sphinx theme changed to Alabaster in the documentation
  • Minor adjustments towards pyasn1 0.4.x compatibility
  • Fixed ObjectIdentifier-into-ObjectIdentity casting at rfc1902.ObjectType MIB resolution harness
  • Fixed NetworkAddress object handling in SNMP table indices
  • Fixed MIB lookup by module:object.indices MIB object with InetAddressIPv{4,6} objects being in the index
  • Fixed non-translated PDU being retries at CommandGenerator what leads to wrong PDU version being sent and even a crash on incompatible PDU/SNMP message combination

  • Refactored partial SNMP message decoding to make it less dependent on unpublished pyasn1 API features.
  • Fix to MibTableRow.setFromName() to keep the input parameter type when it propagates to the return value. Before this fix ObjectIdentity.prettyPrint() may crash when rendering malformed SNMP table indices.
  • Fixed NotificationReceiver to include SNMPv1 TRAP Message community string into SNMPv2c/v3 TRAP PDU
  • Fixed multiple bugs in SNMP table indices rendering, especially the InetAddressIPv6 type which was severely broken.
  • Fixed crashing Bits.prettyPrint() implementation
  • Fixed crashing Bits.clone()/subtype() implementation
  • Fixed leaking exceptions bubbling up from the asyncio and Twisted adapters

  • Deprecated UsmUserData initializaton parameters removed
  • Adapted to pyasn1 API changes introduced by release 0.3.1
  • Fix to a crash happening on inbound SNMP message having non-initialized fields
  • Fix to (persistent SNMP engine ID) file writing on Windows

Security fix to the bug introduced in 4.3.6: msgAuthoritativeEngineTime stopped changing over time and was returning the same timestamp (process start time). This fix makes it growing as it should.

Fixed import error in legacy NotificationOriginator implementation

  • More instrumentation hooks added addressing security failures auditing needs.
  • SNMP table indices correlation implemented within SMI framework. The opaque InetAddress type implemented. INET-ADDRESS-MIB included into the distribution.
  • SNMP table indices resolution logic made more robust against malformed indices.
  • Fixes to lexicographicMode option documentation to make it unambiguous.
  • The ErrorIndication object is now derived from Exception so that it could be raised in exceptions.
  • The errorIndication values produced by various parts of SNMP engine unified to be ErrorIndication instances. This fixes an issue with Twisted.
  • Embedded MIB modules rebuilt with the latest pysmi adding previously missing attributes like status, description etc.
  • Fixed potential SNMP engine crash on handling incoming message at unsupported security level

  • The getNext() and getBulk() calls of Twisted interface. now support ignoreNonIncreasingOid option.
  • TextualConvention is now a new-style class.
  • Fix to accidentally reset error-status when building confirmed class SNMPv1 PDU.
  • Fix to possible infinite recursion in TextualConvention.prettyIn().
  • Fixed crash when attempting to report unsupported request/notification PDU back to sender.

  • Fix to low-level SNMP API example to accommodate changed pyasn1 SEQUENCE supporting iterator protocol.
  • The pyasn1 version dependency bumped (0.2.3), SEQUENCE/SEQUENCE OF API calls adjusted to accommodate changed pyasn1 API (in part of .setComponentBy*() kw flags).
  • Fixed crash on SNMP engine's invalid message counter increment.

  • Switched from now unmaintained PyCrypto to PyCryptodome.
  • Switched to new-style classes.
  • NotificationType now allows additional var-binds specified as MIB objects. A side effect of this change is that additional var-binds can only be added prior to .resolveMibObjects() is run.
  • Non-standard (but apparently used by many vendors) Reeder AES192/256 key localization algorithm implemented and set as default for usmAesCfb192Protocol and usmAesCfb256Protocol identifiers. Original and more standard implementation can still be used with the usmAesBlumenthalCfb192Protocol and usmAesBlumenthalCfb192Protocol IDs respectively.
  • TextualConvention.prettyOut() improved to produce prettier and more SMI-compliant output.
  • TextualConvention.prettyIn() implemented to handle DISPLAY-HINT based value parsing.
  • Fix to NotificationType to make additional var-binds overriding MIB objects implicitly included through NOTIFICATION-TYPE OBJECTS.
  • Fix to SNMP engine boots counter persistence on Python 3.
  • Fix to Pythonized MIBs loading when only .pyc files are present (e.g. py2exe/cx_freeze environments).
  • Fix broken 3DES key localization and encryption procedures.
  • Updated IP address for demo.snmplabs.com in examples.
  • Missing index added to bundled RFC1213::atEntry MIB table.
  • Twisted integration made Python3 compatible.
  • Accommodated ASN.1 SEQUENCE iteration rules change in upcoming pyasn1 version.
  • Author's email changed, copyright extended to 2017.

  • Copyright notice added to non-trivial source code files.
  • SNMP table row consistency check added. This change may break valid SNMP SET operations on tables if RowStatus column is not passed at the very end of var-binds.
  • All SNMP counters now incremented via '+= 1' rather than 'x = x + 1' to simplify their tracking by third-party code.
  • Notification originator examples re-pointed to Notification Receiver at demo.snmplabs.com.
  • Two more execution observer points added: rfc2576.processIncomingMsg and rfc3414.processIncomingMsg to give an insignt on security modules internals.
  • TEXTUAL-CONVENTION's DISPLAY-HINT text formatting reworked for better performance and encoding accurancy of 'a' and 't' formats.
  • WARNING: security fix to USM - extra user entry clone removed on incoming message processing. It made USM accepting SNMPv3 TRAPs from unknown SNMP engine IDs.
  • Fix to snmpInvalidMsgs and snmpUnknownSecurityModels MIB symbols import at SNMPv3 MP model.
  • Fix to NotificationOriginator to cope with unspecified user callable.
  • Fix to OctetString.prettyOut() to pretty-print Python 3 bytes without 'b' qualifier.
  • Fix to better pysmi import errors handling.
  • Fix to missing next() in Python 2.5 at pysnmp.hlapi

  • Added recursive resolution of ObjectIdentifier values at ObjectType by converting it to ObjectIdentity.
  • A bunch of convenience shortcuts to rfc1902.ObjectIdentity added from rfc1902.ObjectType and rfc1902.NotificationType (.addAsn1MibSource(), .addMibSource(), .loadMibs())
  • When pretty printing indices at rfc1902.ObjectType, quote only strings.
  • SNMP overview and PySNMP hlapi tutorial added to documentation.
  • Fix to __doc__ use in setup.py to make -O0 installation mode working.
  • Fix to ObjectIdentity->ObjectIdentifier attributes handover
  • Fixed crash at oneliner compatibility code on EOM response.
  • Fixed crash in hlapi.transport module.
  • Fixed OID resolution issues that roots at node 0 and 2.
  • Fix to MIB builder to fail gracefully on corrupted MIB package encounter.
  • Fix to docs distribution -- now the are Sphinx-buildable out-of-the-box.
  • Source code re-linted

  • Critical error fixed in key localization procedure for AES192/AES256/3DES cyphers. Previous versions might never worked properly in this respect.
  • Initial PySMI integration. Original ASN.1 MIBs could now be parsed, stored at a local pysnmp MIBs repository and loaded into SNMP Engine. Relevant example scripts added. Obsolete libsmi-based scripts removed.
  • Major rewrite of native SNMPv3 CommandGenerator and NotificationOriginator applications towards the following goals:
  • avoid binding to specific SNMP engine instance to promote single SNMP app instance using many SNMP engine instances
  • support two APIs for working with request data: one operates on the whole PDU object while the other on PDU contents
  • keep callback context data in stack rather than in stateful application cache
  • newly introduced sendVarBinds() method offers a more functional and logical signatures.
  • Promote the use of dedicated classes for dealing with OID-value pairs. Instances of those classes resemble OBJECT-IDENTITY, OBJECT-TYPE and NOTIFICATION-TYPE MIB structures.
  • Oneliner API reworked to become more generic: its LCD configuration shortcuts and and var-bindings processing code split off SNMP apps classes to stand-alone objects. The whole API also moved up in package naming hierarchy and becomes 'pysnmp.hlapi.asyncore' (hlapi is apparently an African fish). Old oneliner API remains fully operational at its original location.
  • Synchronous oneliner apps redesigned to offer Python generator-based API along with a more comprehensive set of accepted parameters.
  • Asyncore-based asynchronous apps reworked to become functions.
  • Twisted API moved entirely into high-level domain to be aligned with other high-level APIs. This WILL BREAK backward compatibility for those apps that use Twisted API.
  • Keep backward compatibility for all existing major/documented interfaces
  • Sphinx documentation added to source code and example scripts. Library documentation converted from .html into RsT markup.
  • Execution Observer facility implemented to give app an inside view of SNMP engine inner workings. This is thought to be a generic framework for viewing (and modifying) various internal states of pysnmp engine. Previously introduced non-standard APIs (like getting peer's transport endpoint which is not suggested in RFCs) will be gradually migrated to this new framework.
  • Initial support for the asyncio & Trollius frameworks and coroutines-based SNMP Applications interfaces added. Both IPv4 and IPv6 datagram transports are currently supported.
  • Original asynsock transport and AsyncsockDispatcher renamed into asyncore and AsyncoreDispatcher respectively to provide better hint to fellow devs on the underlying transport being used. Backward compatibility preserved.
  • The asyncore-based transport subsystem extended to support POSIX sendmsg()/recvmsg() based socket communication what could be used, among other things, in the context of a transparent SNMP proxy application. Technically, the following features were brought into pysnmp with this update:
  • Sending SNMP packets from a non-local IP address
  • Receiving IP packets for non-local IP addresses
  • Responding to SNMP requests from exactly the same IP address the query was sent to. This proves to be useful when listening on both primary and secondary IP interfaces.
  • Internal oneliner apps configuration cache moved from respective apps objects to [a singular] snmpEngine "user context" object. That would allow for better cache reuse and allow for a single app working with many snmpEngine instances.
  • Oneliner GETBULK Command Generator now strips possible excessive OIDs off the bottom of returned var-binds table.
  • Constraints assignment shortcut added to some base rfc1902 types (Integer, Integer32, OctetString, Bits). That formally constitutes ASN.1 sub-typing.
  • Built-in debugging is now based on Python logging module.
  • Examples on a single Transport Dispatcher use with multiple SnmpEngine instances applicatons added.
  • Example script on transport timeout & retries manipulation added.
  • Example script explaining incoming message's communityName re-mapping added.
  • Broadcast socket option can now be enabled with the .enableBroadcast() call for any datagram-based transport (namely, UDP and UDP6).
  • AbstractTransportDispatcher's jobStarted() and jobFinished() methods now accept optional 'count' parameter which is a way for an app to indicate how many responses are expected or have been processed in bulk.
  • Example script on SNMP Agents UDP broadcast-based discovery added.
  • Oneliner transport object now supports setLocalAddress() method to force socket binding to specified local interface.
  • New public DgramSocketTransport.getLocalAddress() returns local endpoint address underlying BSD socket is currently bound to.
  • Passing request details to access control callback at CommandResponder reworked towards more robust and simple design with the execution observer facility.
  • All MIBs rebuilt with pysmi.
  • MIB instrumentation example improved to cover table index building facility.
  • Handle the case of null writer at Debug printer.
  • Do not cache snmpEngineId & snmpAdminString at CommandGenerator to let it be reused with many different snmpEngines.
  • TRAP PDU agent address evaluation at proto.api made lazy to improve startup time.
  • Multiple fixes to verify pyasn1 decoder.decode() return to withstand broken SNMP messages or its components.
  • First attempt made to make some of SNMP Engine settings persistent across reboots.
  • Make config.delTransport() returning detached transport object. Asyncio examples now use this facility to explicitly shutdown transport object.
  • Parts of SMIv1 remnant MIBs (RFC1213-MIB, RFC1158-MIB) added to provide complete compatibility with SMIv1. Symbols defined in these MIBs only present in SMIv1 so they can't be substituted with their SMIv2 analogues.
  • MibBuilder.addMibSources() convenience method added.
  • The smi.MibBuilder() will now raise more specific exceptions (MibLoadError, MibNotFoundError) on MIB loading problems rather than more generic SmiError.
  • The oneliner's MibVariable MIB lookup subsystem redesigned for more generality to mimic OBJECT-TYPE macro capabilities related to SNMP PDU handling. The two new classed are ObjectIdentity and ObjectType. The ObjectIdentity class additionally supports just a MIB module name initializer in which case if resolves into either first or last symbol in given MIB. Another option is just a MIB symbol initializer without specifying MIB module. This new subsystem is moved from the scope of oneliner to more common pysnmp.smi.rfc1903 scope to more naturally invoke it from whatever part of pysnmp requires MIB services.
  • MibBuilder now prepends the contents of environment variables it recognizes (PYSNMP_MIB_DIR, PYSNMP_MIB_DIRS, PYSNMP_MIB_PKGS) rather than using them instead of its default core MIBs.
  • Removed RowStatus default value as it may collide with possible subclass constraints.
  • A few additional MIB tree management methods added to MibViewController to better address ordered nature of MIB tree nodes (namely, getFirst*, getLast* family of methods).
  • Wheel distribution format now supported.
  • Fix to authoritative engine side snmpEngineID discovery procedure: respond with notInTimeWindows rather then with unsupportedSecurityLevel at time synchronization phase.
  • Fix to rfc1902.Bits type to make it accepting hex and binary initializers, cope with missing bits identifieirs at prettyPrint().
  • Memory leak fixed in CommandForwarder examples.
  • Fix to BULK CommandGenerator to use the same nonRepeaters OIDs across multiple GETBULK iterations so returned table for nonRepeaters columns would hold the same var-bind.
  • Fix to CommandGenerator to make sendRequestHandle persistent across multiple iterations of GETNEXT/GETBULK queries.
  • Fix to sendNotification() error handling at NotificationOriginator.
  • Fix to preserve possible 'fixed length' setting atrfc1902.OctetString on clone()'ing and subtype()'ing.
  • Fix to rfc1902.OctetString & Bits to base them on OctetString class to make the 'fixed length' property working.
  • Fix to .clone() method of rfc1902.Bits class to make its signature matching the rest of classes. This may broke code which used to pass namedValue parameter positionally rather than binding it by name.
  • Fix to PDU translation service (proto.proxy.rfc2576) to make it initializing errorIndex & errorStatus components of the resulting PDU.
  • Fix to MsgAndPduDispatcher.sendPdu() to clean up request queue on pysnmp-level processing failure.
  • Fix to SNMPv1/v2c message processing subsystem to make it serving unique PDU request-id's in both outgoing and incoming confirmed and response PDU types. Duplicate request-id's in unrelated PDUs may cause cache errors otherwise.
  • Fix to licensing terms of multiple twisted backend modules to make the whole pysnmp package licensed under BSD 2-Clause license. This change has been explicitly permitted by the original modules authors.
  • Fix to asyncore-based transport not to use asyncore's cheap inheritance from socket object what caused warnings.
  • Fix at NotificationOriginator to make is using MibInstrumentationController when expanding Notification OBJECTS into Managed Objects Instances.
  • Missing wrongLength and wrongEncoding SMI errors added.
  • Fix to file descriptor leak at MibBuilder.
  • Fix to rfc2576.v2ToV1() to ignore impossible errorStatus.
  • Fix to rfc2576.v1ToV2() to reset ErrorStatus==noSuchName on proxying.
  • Fix to smi.builder to explicitly fail on any MIB file access error (but ENOENT) and raise IOError uniformly on any directory/egg access failure.
  • Fix to infinite loop at config.delV3User().

  • License updated to vanilla BSD 2-Clause to ease package use (http://opensource.org/licenses/BSD-2-Clause).
  • A dozen of lightweight Twisted-based example scripts replaced more complex example implementations used previously.
  • SNMP Proxy example apps separated into a larger set of more specialized ones.
  • Most of Command Generator examples re-pointed to a live SNMP Agent at demo.snmplabs.com to ease experimentation and adoption.
  • Multithreaded oneliner CommandGenerator example added.
  • Packet-level SNMP API (pysnmp.proto.api) getErrorIndex() method can now be instructed to ignore portentially malformed errorIndex SNMP packet value what sometimes happens with buggy SNMP implementations.
  • Standard SNMP Apps and built-in proxy now ignores malformed errorIndex value.
  • Built-in logging now includes timestamps.
  • Multi-lingual capabilities of all CommandGenerator & NotificationOriginator apps re-worked and improved. For instance it is now it's possible to run getBulk() against a SNMPv1 Agent invoking built-in SNMP Proxy behind the scene.
  • The $PYSNMP_MIB_DIR & $PYSNMP_MIB_DIRS & $PYSNMP_MIB_PKGS path separator made platform-specific.
  • Change to rfc2576.v1tov2() logic: errorStatus = noSuchName is now translated into rfc1905.noSuchObject exception value for all var-bindings at once. Although RFC2576 does not suggest error-status -> v2c exception translation, historically pysnmp used to perform it for a long time so we can't easily stop doing that.
  • Exception re-raising improved at MibInstrumController.flipFlopFsm() and asynsock/twisted dispatchers so that original traceback is preserved.
  • A single instance of transport dispatcher can now serve multiple receivers (identified by IDs) chosen by a public data routing method.
  • SnmpEngine.[un]registerTransportDispatcher() methods now accept optional receiver ID token to be used by transport dispatcher's data router. This allows for multiple SNMP engines registration with a single transport dispatcher.
  • Distribute is gone, switched to setuptools completely.
  • The snmpCommunityTable row selection improved to follow RFC2576, clause 5.2.1.
  • Asyncore-based dispatcher attempts to use poll() whenever available on the platform. It would help handling a really large number (>1024) of file descriptors.
  • AsynCommandGenerator.makeReadVarBinds() generalized into a new makeVarBinds() method which replaces somewhat redundant code at setCmd() and AsynNotificationOriginator.sendNotification().
  • AsynCommandGenerator.uncfgCmdGen() & AsynNotificationOriginator.uncfgCmdGen() methods now accept optional authData parameter to remove specific entries from LCD. This can be useful for modifying security parameters for specific securityName.
  • SNMP credentials management reworked to separate userName from securityName in snmpCommunityEntry and usmUserEntry tables. Changes made to addV1System(), addV3User() functions as well as to their oneliner's wrappers.
  • The contextEngineId parameter of config.addV3User() and auth.UsmUserData() renamed into securityEngineId as it's semantically correct
  • Oneliner UsmUserData() and CommunityData() classes now support clone()'ing to facilitate authentication data management in user applications.
  • Oneliner transport target classes now support the getTransportInfo() method that returns network addresses used on protocol level.
  • Oneliner CommandGenerator.getNext() & .getBulk() methods now support the maxCalls kwarg to limit the maximum number of iterations to perform.
  • The config.addSocketTransport() helper renamed into config.addTransport() and improved by automatically instantiating compatible TransportDispatcher making it dispatcher-agnostic. As an additional bonus, application may not call registerTransportDispatcher() as it would be called by addTransport().
  • The SnmpV3MessageProcessingModel.getPeerEngineInfo() method is implemented to communicate discovered peer SNMP engine information to SNMP apps what can be used for fine usmUserTable configuration.
  • AsynNotificationOriginator.cfgCmdGen() does not take into account securityModel & securityLevel when reducing LCD access via addTrapUser(). This improves LCD consistency on sparse add/del operatons but also does not let you to configure different securityModels per securityname at VACM though the cfgCmdGen() wrapper.
  • MIB builder traceback formatting and reporting improved.
  • SNMP Engine object now has a snmpEngineID attribute exposed.
  • Fix to inet_ntop()/inet_pton() support on Windows at TRANSPORT-ADDRESS-MIB.
  • Fix to usmUserSecurityName table column implementation -- automatic value generation from index value removed.
  • Fix and significant logic rework of snmpCommunityTable to make it working in both Generator and Responder modes and better follow RFC2576 requirements on sequential entries lookup and selection. As a side effect, untagged snmpCommunityTable entries will not match tagged snmpTargetAddrTable entries and vice versa.
  • Fix to Twisted-based NotificationOriginator to make it serving INFORMs again.
  • Fix to rfc2576.v1tov2() logic: errorStatus = noSuchName is now translated into rfc1905.noSuchObject exception value for all var-bindings. Although this is not mentioned in RFC, it looks as a more consistent approach.
  • Fix of rounding error to base I/O dispatcher's next timer call calculation.
  • Explicit twisted dispatcher's timer resolution (of 1 sec) removed to make use of global default of 0.5 sec.
  • Fix to twisted/udp non-default local endpoint binding features. Common socket ('host', port) notation is now supported.
  • Fix to Twisted-based transport to make it closing UDP port / UNIX pipe on shutdown.
  • Fix to Twisted-based dispatcher not to close transport on unregistration at dispatcher as transports can potentially be reused elsewhere.
  • Fix to asyncore-based transport to work only with AsynsockDispatcher's socket map and not to touch default asyncore's one. The latter have caused dispatcher/transport restarting issues.
  • The delV3User() function improved to drop all rows from USM table that were cloned from the target one.
  • Fix to exceptions handling at MsgAndPduDispatcher.sendPdu() to avoid sendPduHandle miss (followed by system crash) on cache expiration run.
  • Break cyclic references at CommandResponder and NotificationReceiver apps through close() method.
  • Fix to octet string typing at 3DES codec (used to throw an exception).
  • Fix to SnmpAdminString, SnmpTagList, SnmpTagValue types to make them supporting UTF-8 initializers.
  • Fix to v1/v2c message processing module which used to refer to a bogus stateReference in some cases what causes SNMP engine crashes.
  • Fix to IPv6 transport to zero ZoneID, FlowID and ScopeID components sometimes coming along with incoming packet.
  • Fix to SNMPv1 MP module to pass stateReference to registered app on unconfirmed notifications reception (to let NotificationReceiver Apps browsing request details). (transport information at the moment) at SNMP engine.
  • Asyncsock sockets now configured with SO_REUSEADDR option to fix possible Windows error 10048.
  • Gracefully handle malformed SnmpEngineID at USM coming from SNMPv3 header.
  • Typos fixed in error-status constants at CommandResponder
  • Missing import added to oneliner auth module.
  • Cosmetic changes to v3arch example scripts.

  • SNMPv3 high-level and native API examples reworked and extended to cover many use cases.
  • The missing functionality of NOTIFICATION-TYPE objects being looked up at local Management Instrumentation and attached to TRAP/INFORM message by Notification Originator is now fully implemented.
  • The missing functionality of passing Response PDU contents of INFORM request is now implemented at Notification Originator app. The return value of NotificationOriginator.sendNotification is now a composite object that includes errorStatus, errorIndex and varBinds.
  • The missing functionality of passing lookupNames & lookupValues params to Notification Originator is now implemented. It may make sense for INFORMs.
  • The missing functionality of passing contextName to oneliner version of NotificationOriginator.sendNotification is now implemented.
  • Oneliner example apps now include cases where non-default SNMP ContextEngineId/ContextName/SecurityEngineId is used.
  • The contextName parameter of SnmpContext.getMibInstrum made optional.
  • AbstractMibInstrumController class added as a base class for all possible kinds of Management Instrumentation controllers.
  • Report package version on debugging code initialization.
  • MibInstrumController.getMibBuilder() added.
  • I/O sockets buffer sizes made configurable, minimum default is now forced to be no less than 2**17 (to fit two huge datagrams).
  • Catch possible exceptions on pyasn1 encoder invocation.
  • VACM modules converted from a function into an object to let it keep state (caches) in the future.
  • Unnecessary MibSource explicit initialization calls removed at MibBuilder.
  • Example configuration for Net-SNMP's snmptrapd added.
  • Cast additionalVarBinds into ObjectIdentifier type at NotificationOriginator.sendNotification()
  • Standard SNMPv3 Apps hardened to catch protocol-related exceptions and report them as errorIndication's.
  • Catch and mute possible failure of getsockname(), that seems to happen on Windows only so far.
  • Memory leak fixed at oneliner cache of already configured targets.
  • Fixes to at AsynNotificationOriginator.sendNotification() to make a) the notificationType param mandatory b)t e varBinds param really optional
  • Fixes to ContextEngineId/ContextName support at the oneliner API: now both items should be passed to request PDU through Cmd() request initiation method, the items of authData object should be used only for LCD configuration.
  • Fix to MibVariable handling of the MIB, <empty-symbol> initializers.
  • Fix to outgoing queue processing order at socket transport. Now it's a FIFO discipline rather than LIFO.
  • Fix to NotificationOriginator's additionalVarBinds parameter - it is not mandatory anymore with the oneliner API. Also additionalVarBinds defaulted value changed from None to () meaning no var-binds.
  • Attempt to convert Windows style EOL into UNIX ones in MIB source modules appeared to be unnecessary and even destructive to modules data in some cases. So the conversion code removed altogether.
  • Fix to isAccessAllowed() error handling at NotificationOriginator. System used to crash on access denied condition.
  • Fix to NotificationOriginator to make it use system uptime and trap OID values from SNMP engine's instrumentation rather then from SNMP context.
  • Fix a couple of bugs at MibTable* logic involved for table instances creation.
  • Fix to Management Instrumentation code to handle cases of non-initialized or not-compliant-to-constraints Managed Objects Instances.
  • Fix to Management Instrumentation code to make table row removal through SNMP working again. Wrong method (instumClone) was probed at terminal MIB nodes values instead of the right one (setValue).

  • SECURITY FIX: USM subsystem did not verify securityLevel of a request to an authoritative SNMP engine against auth/priv protocols configured for the user in question. That allowed unauthenticated/unciphered access to pysnmp-based Agent even if USM user is configured to provide one.
  • Oneliner [Asyn]CommandGenerator now supports optional keyword args lookupNames, lookupValues that enable response OID / value looked up at MIB and reported as a MibVariable container object carrying relevant MIB info.
  • Oneliner [Asyn]CommandGenerator now supports symbolic MIB object names to be passed within a MibVariable container object which would do a deferred MIB lookup for name resolution. This is a new and preferred API which obsoletes the tuple-based one (it is still suppored though).
  • Oneliner CommandGenerator's class attributes lexicographicMode, maxRows and ignoreNonIncreasingOid moved to optional keyword args of nextGen() and bulkGen() methods.
  • IPv6/UDP and Local Domain Socket transport interfaces added to the oneliner API.
  • Mib Instrumentation subsystem re-worked to replace excessive MibNode's smiCreate()/smiWrite()/smiDestroy() methods with MibScalarInstance's getValue()/setValue()
  • MibTree.readTest[Get]Next() reworked to be called uniformely so user could tap on these methods at any level of the MIB tree.
  • MibTableColumn.getNextNodeWithValue() unpublished API method obsoleted and removed for clarity.
  • Hex dumps of binary parts of the protocol added to ease system operations analysis.
  • SnmpEngineId autogeneration does not call DNS resolver but uses local hostname not to depend upon local IP availability and performance.
  • Example apps reworked, additional SNMPv3 auth/priv protocols and transports added.
  • Package version is now available as __init__.__version__ and it is in-sync with distutils.
  • Package meta-information updated.
  • The __init__.py's made non-empty (rumors are that they may be optimized out by package managers).
  • Multiple fixes to UNIX domain socket transport to make it fully operational again.
  • Use sysUpTime value whenever it is included in Notification PDU, otheriwese resort to SNMP engine uptime reading.
  • SNMPv2c Message is now defined in rfc1901.py what matches standard definition.
  • Types defined within SNMPv1/v2c data structures (rfc1157.py/rfc1905.py) moved to module scope to become accessible by wrapper routines (v1.py/v2c.py). This is used for setting strictly typed default values to corresponding SNMP data structures.
  • The obsolete and unpublished MibInstrumController.readVarsFast() method removed for API clarity.
  • MibBuilder now distinguishes case of MIB modules filenames even if underlying OS does not.
  • LCD configuration caching is implemented at pysnmp.entity.rfc3413.config that improves performance of repetitive calls by 10% and might hugely improve NotificationOriginator's performance when working on a large number of targets.
  • A caching maps implemented at rfc2576 subsystem to speed-up communityName to/from securityName resolution. The also makes transport tags processing better compliant to the standard.
  • Community and Transport tags processing changed at the oneliner interface to make the whole mechanism more compliant with the standard. Most importantly, it is now possible to tag authentication and transport information separately.
  • The NoSuchInstanceError exception class is no more inherits from NoSuchObjectError to make class hierarchy closer to SNMP specification which states that these errors are separate and independent.
  • The Next & BulkCommandGenerator's split onto single-run and iterative impementations. The former just process a single interaction and complete while the latter run as many interactions as user callback function indicates to.
  • The pysnmp.entity.rfc3413.mibvar module is now obsolete by pysnmp.entity.rfc3413.oneliner.mibvar featuring basically the same features but within a stateful, dedicated object.
  • Auth & target configuration container classes moved to their separate modules at oneliner API.
  • The notificationType parameter of AsynNotificationOriginator.sendNotification made defaulted to reflect its optional nature.
  • Oneliner UsmUserData, UdpTransportTarget, Udp6TransportTarget instances are not hashable anymore as they are intended to act more like a data structure than object.
  • Built-in debugger now supports negating debugging categories.
  • An async/getgen.py example script added.
  • Fix to MIB data reading routine to make it working with possible Windows end-of-line's.
  • Fix to CommandGenerator's SNMPv3 engine autodiscovery algorithm when retryCount is administratively set to 0.
  • Fix to Notification Originator to make it communicating a single sendPduHandle to an application even when multiple INFORMs are triggered and processed by a single call by way of transport tagging feature.
  • Fix to rfc2576:processIncomingMessage() to take SecurityModel into account when lookup up SecurityName by CommunityName. This allows mixed SNMPv1/v2c communication with the same target.
  • Fix to internal MessageProcessing and SecurityModel timers so they become dependant on system timer resolution.
  • Fix to v1.PDUAPI.setDefaults() method that used to set wrongly typed time-stamp component.
  • Fix to IPv6 address handling to prevent system from crashing whilst running Python3.
  • Fix to SNMPv2 exception objects translation into SNMPv1 PDU and NEXT OIDs calculation.
  • Fix to MibTree class to properly report noSuchObject & noSuchInstance SNMP special values.
  • Fix to libsmi2pysnmp tool to make it working again in Python < 2.7
  • Fix to exception handling at decodeMessageVersion() caller to prevent ASN.1 parsing errors crashing the whole app.
  • Fix to GenericTrap type processing at rfc2576:v1Tov2c() which used to crash the whole SNMP engine.
  • Fix to [possibly uninizilaized] pyasn1 objects printouts at MibInstrumController.__indexMib()
  • Fix to maxSizeResponseScopedPDU calculation at rfc3414/service.py.
  • Dedicated 'withmib' example set is obsolete and removed.
  • Another SNMP proxy example app added (1to3.py).
  • Fix to MIB modules loading code to make it using __import__() properly. This also makes pysnmp working again with Python 3.3rc0.
  • Typo fix to snmpInASNParseErrs MIB instance object.
  • Typo fix to errind.EngineIdMismatch class and its instance.

  • Oneliner CommandGenerator can now limit the number of SNMP table rows returned by nextCmd()/bulkCmd() methods.
  • Oneliner CommunityData configuration object can now be initialized with community name only, security name will be chosen automatically.
  • Oneliner LCD configuration routines reworked towards clarity. The side-effect of this change is that repetitive oneliner call with the same securityName and different configuration options will only honor the first settings. Previous implementation would override older settings.
  • Transport dispatcher now provides its own time expressed in fractions of second. SNMP engine uses this notion of time for handling requests timeout to make packet flow time bound to async I/O core operations rather than to real time.
  • The libsmi2pysnmp tool improved to handle incomplete SMI v1->v2 conversion performed by smidump. The remaining core SMIv1 modules excluded from the core MIB set.
  • The pyasn1 constraint and enumeration objects put into ASN1-* MIB modules what appears to be more in-line with SMI. Existing MIB modules as well as libsmi2pysnmp tool corrected accordingly.
  • SMIv1 MIB modules (including RFC1155 and RFC1213) were moved to pysnmp-mibs as pysnmp is SMIv2-based.
  • The MibBuilder.importSymbols() now takes optional kwargs and push them into MIB modules globals(). This is to facilitate passing user infomation, such as DB connection handler, to MIB module namespace so it could be used by ManagedObjects implementations.
  • When running on Python3, SMI will re-raise exceptions with the original traceback for easier diagnostics.
  • Out of PYTHONPATH MIB paths now supported.
  • Added pyasn1 decoder failures diagnistics in debug mode.
  • Fix to non-MT-safe class attributes at SNMPv3 MP & SEC modules.
  • Fix to ContextName handling in bytes form whilst running Python3. Data mismatch error would return otherwise.
  • Fix to SNMPv3 MP peer engine ID discovery not to learn and use user-specified ContextEngineId.
  • Fix to socket.error processing at Py3 on Windows.
  • Fix to oneliner GETNEXT/GETBULK implementation to properly support ignoreNonIncreasingOIDs option.
  • Fix to setEndOfMibError()/setNoSuchInstanceError() at v1 PDU not to loose errorIndex.
  • Fix to api.v2c.getVarBindTable() to ignore possible non-rectangular GETBULK response tables.
  • Fix to oneliner getnext/getbulk response table rectangulation procedure to gracefully handle an empty column condition.
  • Fix to legacy MibBuilder.getMibPath() to prevent it from missing .egg-based components in path.
  • Fix to oneliner configuration routine that used to implicitly tag SNMPv1/v2c auth and transport LCD rows what resulted in huge delays when processing incoming messages with large number of peers configured.
  • Fix to UDP6 transport handling at rfc2576 security module.
  • Fix to SnmpEngineID value autogeneration (used to fail on Mac).
  • SNMPv2-SMI.ObjectType.__repr__() fixed to do a repr() on its components.
  • All SNMPv2-SMI.MibNode-based objects, once exported to a mibBuilder, will carry an embedded label symbol.
  • Exlicit repr() calls replaced with '%r'
  • Fix to error processing at GETNEXT & GETBULK apps response handlers.
  • Fix to libsmi2pysnmp to make it supporting long (256+) list of function params.
  • Fix to libsmi2pysnmp to support inheritance of MIB types.

  • Support string OIDs at one-liner API.
  • Code quality of libsmi2pysnmp tool improved, MIBs re-built.
  • SNMP-PROXY-MIB & SNMP-USER-BASED-SM-3DES-MIB added
  • v1arch bulkgen.py example added
  • Major overhawl for Python 2.4 -- 3.2 compatibility:
  • get rid of old-style types
  • drop string module usage
  • switch to rich comparation
  • drop explicit long integer type use
  • map()/filter() replaced with list comprehension
  • apply() replaced with var-args
  • dictionary operations made 2K/3K compatible
  • division operator made 2K/3K compatible
  • sorting function now operates on key
  • iterators returned by some funcs in py3k converted to lists
  • exception syntax made 2K/3K compatible
  • tuple function arguments resolved to scalars to become py3k compatible
  • BER octetstream is now of type bytes (Py3k) or still string (Py2k)

Fix to SNMPv1 Trap PDU agentAddress setter shortcut method.

Missing module import fixed in privacy subsystem

  • Oneliner CommandGenerator can now optionally ignore non-increasing OIDs.
  • Default CommandResponder now skips non-compliant (Counter64) values when responding to a v1 Manager.
  • Fix to state information handling at CommandResponder app.
  • Fix to Twisted reactor shutdown condition.
  • Fix to distutils dependencies syntax.

  • Extended Security Options (3DESEDE, AES192, AES256) privacy protocols implemented.
  • The error-indication codes moved from literals to objects for reliability and clarity
  • Fix to v1.TrapPDUAPI.getVarBinds() to address PDU component at the right position.
  • Fix to rfc1902.Bits initialization from named bits sequence.
  • Fix to MIB builder by-extension module filtering code to cope with .pyw files.
  • Internal caches structure improved.
  • Sync versions of oneliner apps split off async implementation for clarity.
  • Randomize initial in various numeric sequences.
  • MsgAndPduDsp expectResponse parameters passing reworked.
  • GetNext/GetBulk response processing logic moved to getNextVarBinds()
  • Changes towards performance improvement:
  • all dict.has_key() & dict.get() invocations replaced with modern syntax (this breaks compatibility with Python 2.1 and older).
  • introduce the MibInstrumControlle.readVarsFast() method (which skips the "testing" phase of MIB value readin) for dealing with internal configuration (LCD).
  • default debug.logger is now just a zero value instead of an object what saves big on frequent calls
  • SNMPv2-SMI columnar indices <-> index values conversion code optimized.
  • pre-compute and re-use some of ASN.1 structures.
  • avoid setting PDU defaults to save on unnecessary initialization.
  • skip ASN.1 types verification where possible.
  • at oneliner Command Generator, avoid looking up pure OID arguments at MIB as it's pointless but takes time.
  • cache MIB columnar objects instance ID <-> symbolic index representation mapping

  • SNMP Proxy example added.
  • End-of-MIB condition detection reworked what caused backward incompatibility at v1arch GETNEXT API. Previous pysnmp versions used value = None in var-binds as returned by getVarBindTable() API method. This version uses rfc1905 exception values (v2c/v3) or pyasn1 Null (v1). Built-in GETNEXT/GETBULK apps now do not require user to track end-of-mib conditions anymore -- this is now done automatically.
  • CommandResponder API now supports async mode of operation.
  • SNMP exception values now exported from rfc1905 module, and made pretty printable.
  • Lexicographic walking mode is now supported at oneliner CommandGenerator.
  • ContextEngineId&ContextName parameters passing implemented at v3arch oneliner API.
  • Multiple instances of the same transport domain now supported.
  • Initial snmpEngineId value generation improved not to accidentally collide within an administrative domain.
  • MibTableColumn instances now build value-to-column-instance map to speedup by-value search.
  • SNMPv2-CONF::AgentCapabilities macro implemented.
  • The libsmi2pysnmp tool handles some more MACROs.
  • Void access control module implemented to let apps disabling [default] VACM.
  • Allow standard SNMP apps to choose access control method to use.
  • Twisted-based CommandResponder example added.
  • Fix/rework of Twisted GETNEXT/BULK CommandGenerator callback API to make it simpler and uniform with other CommandGenerators
  • Fix to SNMPv3 security module to store peer SNMP engine timeline only if taken from an authenticated message. Prior to this fix SNMP engine was not been protected from spoofing.
  • Fix to $SMIPATH initialization at build-pysnmp-mib.
  • Fix to maxSizeResponseScopedPDU calculation.
  • Fix to Next/Bulk CommandGenerators to catch a non-increasing OID error condition (what prevents looping).
  • Fix to Opaque value tagging at rfc1155.Opaque type.
  • Fix to handle (fail gracefully) zero-length user password.
  • Fix to SNMP error propagation at Twisted driver (SF tracker ID #3054505).
  • Fix to Agent-role snmpEngineId discovery procedure that allows authenticated ReportPDU generation.
  • Fix to SNMPv1 PDU handling at CommandResponder & NotificationReceiver apps.
  • Fix to CommandResponder app to skip Counter64 SMI values when responding to SNMPv1 Manager.
  • Fix to protocol translator code (rfc2576) to handle Counter64 type in v2c-to-v1 PDU translation.
  • Fix to non-response var-binds translation in rfc2576.v2ToV1().
  • Fix to wrong exceptions used in pysnmp/entity modules.
  • Fix to noauth/nopriv security module so that it would not crash SNMP engine if called accidentally.
  • Fix to CommandResponder not to return out-of-range errorIndex along with genErr
  • Fix to GETBULK CommandResponder to do a by-column MIB walk, not by-raw.
  • Fix to getVarBindTable() API function logic.
  • Fix to example Manager scripts to use errorIndex when available.
  • Fix to dummy encryptData()/decryptData() API
  • Fix to oneliner GETBULK table collection code to make it stripping uneven rows off table tail.

  • Fix to maxSizeResponseScopedPDU calculation at USM security module: now more precise and robust against screwed values on input.
  • Fix to MIB loading logic that used to load same-name modules at disticts search paths on each loadModules() call.
  • Fix to AsynsockDispatcher.runDispatcher() to make use of optional non-default select() timeout value.
  • AbstractTransportDispatcher now allows user application registering multiple timer callbacks each with dedicated call period.
  • Asynsock mainloop default idle period reduced to 0.5 sec for better timer resolution.
  • Fix to SNMPv1->SNMPv2c error status handling at proxy module. This defect may have caused an infinite loop on a multiple var-bind SNMPv1 GetNext operation.
  • Fix to contextName processing at config.addV1System -- typo rendered passed contextName not committed into LCD.
  • Fix to unknown ContextName exception handling at CommandResponder App.
  • config.addVacmUser() now accepts an optional contextName what makes it usable for configuring multiple contextName-bound bases of Managed Objects to SnmpEngine.
  • MP pending states cache management re-worked so that SNMP engine will now handle an unlimited number of pending request/responses.
  • Fix to SNMP discovery procedure: include ContentName in SNMP discovery messaging.
  • Many fixes to AES crypto code that makes it actually working.
  • Fix to SNMPv2-SMI createUndo operations.
  • Fix to INFORM sending error handling at oneliner.
  • Fix to mismatched response PDU handling at CommandGenerator application.
  • Debug category 'app' (for Application) added to facilitate Standard SNMP Applications debugging.
  • The retryCount semantic of CommandGenerator application changed to include sole retries and do not include initial request. Thus, retryCount=1 will now send up to two packets, not just one as it used to be.
  • Debugging printout now escapes non-printable characters.

  • UDP over IPv6 transport implemented.
  • Fix to MIB tree walking code that used to fail on table columns where indices have identical leading parts.
  • SNMPv1/v2c snmpCommunityTransportTag-based imcoming message filtering implemented (rfc2576).

  • API versioning retired (pysnmp.v4 -> pysnmp).
  • MIB loading mechanics re-designed to allow ZIP import.
  • MIB loader supports code objects (py[co])
  • Installer now uses setuptools for package management whenever available.
  • The libsmi2pysnmp tool improved to build constraints of more than 256 items (Python has a limit on the number of function params).
  • Missing SNMPTrap PDU API implemented at proto.api.v2c, RFC2576 proxy code reworked.
  • Fix to sysUpTime OID at SNMPv2 TRAP PDU.

  • Twisted integration implemented.
  • Attempt to use hashlib whenever available.
  • Fix to oneliner Manager code on < Python 2.4.
  • Let NotificationReceiver and CommandResponder Apps browsing request details (transport information at the moment) at SNMP engine.
  • Fix to config.addV1System() to allow multiple systems to co-exist in LCD.
  • Fix to wrongly succeeding user-parameters-by-community-name searching code in rfc2576 processIncomingMsg() method.
  • Do sanity checking on PYSNMP_MODULE_ID, Groups and Notifications in libsmi2pysnmp (SF bug #2122489).
  • Fix to oneliner Notification Originator that sometimes used to send multiple requests at once.
  • Oneliners LCD names generation code reworked to avoid accidental clashes.
  • Fix and re-work of sysUpTime value management in LCD.
  • Fix to pending inform request data caching in mpmod/rfc2576.py -- previous code led to cache data corruption on multple outstanding requests.
  • In SMI configuration wrapper functions, catch access to non-configured entries and raise an exception.
  • Allow multuple callback timer functions in all transport dispatchers.
  • Fix to libsmi2pysnmp code to preserve more underscored object names and to guess the right type between indistinguishable ObjectGroup & NotificationGroup
  • Fix to MibScalarInstance value setting logic - previous code failed when modifying the same OID multiple times within a single SET operation.
  • Minor usability improvements to tools/build-pysnmp-mib.
  • Made MIB objects unexport feature operational.

  • Internal MIB indexing method __indexMib() unmangled to facilitate setting up mutex there for sharing MIB stuff between threads.
  • Fixed broken IpAddress value handling in SET operation.
  • Broken MibBuilder.unloadModules() method now works.
  • Use getLabel() SMI object method when building MIB tree (in builder.py) in addition to direct attribute access for clearer object protocol.
  • The MIB building tools updated to match significantly improved smidump tool (libsmi version > 0.4.5).
  • Made libsmi2pysnmp tool optionally building MIB text fields into pysnmp MIB code (enabled by default) and MibBuilder conditionally loading them up (disabled by default).
  • SnmpEngine and MsgAndPduDispatcher constructors now optionally take msgAndPduDspr and mibInstrumController class instances respectively to facilitate these objects sharing within a process.
  • Unique integers, for various parts of the system, are now generated by a nextid.py module. This fixes possible issues with duplicate request IDs and handlers.
  • Built-in MIBs re-generated to include text fields.

  • UNSTABLE ALPHA RELEASE.
  • At onliner CommandGenerator, close transport on destruction to prevent socket leak. Implicit async transports registration at default asyncore's socket map has been disabled to avoid side effects.
  • Fix to rfc2576.v1ToV2c() PDU converter to perform noSuchName error code translation.
  • Fixes to Notification PDU conversion code at rfc2576 in part of snmpTrapOID handling.
  • Fix to nonRepeaters object use as sequence slicer (must be int) at cmdrsp.CommandResponderApplication
  • Make AsynsockDispatcher using its own socket map by default for threading safety. This will break asyncore apps that rely on pysnmp sharing the same socket map with them. A solution would be to either set asyncore map to pysnmp (AsynsockDispatcher.setSocketMap()) or pass pysnmp map (AsynsockDispatcher.getSocketMap()) to asyncore.
  • Fix to response timeout roundup bug at CommandGenerator and NotificationOriginator code.
  • Oneline configuration classes made hashable to prevent memory leaks when committing them into CommandGenerator/NotificationOriginator internal repository.
  • Security information is now released properly in all MP modules. This might fix a significant memory leak.
  • Typo fix to rfc3411 confirmed class PDU members.

  • UNSTABLE ALPHA RELEASE.
  • SMI/dispatcher timeout conversion multiplier is actually 100 (1/100 sec) rather than 1/1000. This fix affects timeouts specified through SMI.
  • __repr__() implemented for UdpTransportTarget, CommunityData, UsmUserData in oneliner module.
  • Automatically initialize table index values on table management operations (SF bug ID #1671989).
  • Fix to carrier code: ignore BADFD socket error as it may happen upon FD closure on n-1 select() event.
  • Fix to MibBuilder.setMibPath() to preserve previously loaded modules intact. Otherwise loadModules() called after setMibPath() might fail with 'MIB file not found in search path' exception.
  • Fix to oneliner classes that now invoke unconfiguration methods on destruction. This might have caused memory leaks.
  • Automatically initialize SNMP-USER-BASED-SM-MIB::UsmUserSecurityName columnar object instance on creation, as stated in DESCRIPTION (SF tracker ID #1620392).
  • Fix to USM timeframe arithmetics (SF bug #1649032).
  • VACM shortcuts merged into universal add/delVacmUser() to let notifications and requests to co-exist for the same user.
  • At oneliners, build LCD tables keys from a hashed mix of input parameters to make sure these automatic entries won't interfere or exceed constraints on keys values.
  • Made use of notificationType parameter of the sendNotification method in NotificationOriginator applications. This parameter used to be ignored in the past. Note, that previously used (and ignored) syntax has been changed in an incompatible way.
  • Allow plain Python values in setCmd() and sendNotification() methods in CommandGenerator and NotificationOriginator applications respectively.
  • Multi-target oneliner API-based example script added.
  • Ignore more socket errors in datagram-type async socket code.
  • AES cipher now supported (rfc3826).
  • Fix to messed up tagIDs of noSuchInstance and noSuchObject types.
  • SET Command Responder fixed to obscure access to non-existing variables by returning notWritable error (SF bug #1764839).
  • AsynsockDispatcher.setSocketMap() method added to facilitate pysnmp transport integration into third-party asyncore-based applications.
  • Fix to errorIndex generation at CommandResponder application, the value should be a one-based.

  • UNSTABLE ALPHA RELEASE.
  • Low-level debugging facility implemented.
  • Support UdpTransportTarget timeout and retries parameters in oneliner API.
  • Fix to snmpTrapOID construction at ...proxy.rfc2576.v1ToV2() function.
  • Fix to MibViewController.getNodeName() to take MIB module name into account (SF bug #1505847).
  • Do explicit check for Counter32,Unsigned32,TimeTicks,Counter64 value types in MibTableRow index conversion and in TextualConvention.prettyPrint() methods (SF bug #1506341). Handle Bits in indices as RFC2578 suggests.
  • Apply read-create column status to libsmi2pysnmp-generated code whenever MIB text specifies that (SF bug #1508955).
  • Honor and apply DISPLAY-HINT specification when building TextualConvention class at libsmi2pysnmp.
  • Managed Objects Instances files (smi/mibs/instances/) are now double-underscore prefixed to make them imported explicitly by these prefixed names. They used to be imported as a side-effect of Managed Objects files import what is way too hackerish.
  • The libsmi2pysnmp now supports future libsmi bugfix that would generate "ranges" subtree along with the legacy and ambiguous "range" one.
  • SMI support for fixed-length string indices implemented (SF bug #1584799, #1653908).
  • Managed Object Instances may now have smiRead, smiWrite, smiCreate methods to support specific value mangling. These methods, if present, would be invoked from SNMP [Agent] core instead of conventional clone() method. The reason is to separate trivial value duplication from specific Instance value mangling that may have Agent-specific side effects (such as RowStatus).
  • MIB table row destruction now works (SF bug #1555010).
  • LCD unconfiguration functions for oneliners implemented (SF bug #1635270).
  • unloadModules() and unexportSymbols() implemented at MibBuilder
  • Notification type PDU proxy code fixed to produce symmetrical conversion.
  • Various SNMP engine-internal caches expiration implemented.
  • SMI-level access control now takes effect only if AC object is passed to MIB instrumentation API.
  • LCD management code now uses generic MIB instrumentation features.
  • Fix to oneliner manager code to have individual UdpSocketTransport instance per each SnmpEngine. Multithreaded apps might fail otherwise. (SF bug ID #1586420).
  • Exclude the PYSNMP_MODULE_ID symbol from MIB view index, as it may get resolved into OID label instead of actual MIB object name.
  • Memory leak fixed in indices.OidOrderedDict implementation.
  • Fix to VACM shortcuts to let notifications and requests to co-exist for the same user otherwise.
  • Fix to ...oneliner.cmdgen.UsmUserData to support non-default ciphers.
  • USM now uses local notion of snmpEngineBoots/Time when authoritative and cached estimate otherwise. Also, a security fix applied to to USM time-window verification (SF bug #1649032).
  • Fix to notification objects resolution code at NotificationOriginator.sendNotification()
  • Do not raise securityLevel for USM error reports that lacks user information, as these reports could never be ciphered (SF bug #1624720).
  • Non-default BULK PDU params now actually applied.
  • SnmpEngineID default value generation algorithmic function changed to allow multiple SNMP engines running on the same host.
  • Documentation updated.
  • A handful of minor fixes applied (SourceForge tracker IDs #1537592, #1537600, #1537659, #1548208, #1560939, #1563715, #1575697, #1599220, #1615077, #1615365, #1616579).

  • UNSTABLE ALPHA RELEASE.
  • pysnmpUsmSecretAuthKey and pysnmpUsmSecretPrivKey length increased up to 256 octets. There seems no limit on this in RFC, though.
  • A workaround for probably broken Agents: SNMPv3 Manager code defaults ContextEngineId to SecurityEngineId whenever ContextEngineId is not reported by authoritative SNMP engine on discovery.
  • Use empty PDU in engine-discovery report at mpmod/rfc3412.py.
  • MibBuilder.loadModules() now fails on missing MIB files.
  • MibBuilder.exportSymbols() now accepts unnamed objects (likely Managed Objects Instances)
  • SNMPv2-SMI.MibScalarInstance objects now support create*/destroy* Management Instrumentation methods to pass Columnar Object creation and removal events. MibTableColumn class invoke these methods accordingly.
  • Fix to AsynNotificationOriginator.asyncSendNotification() callback formal parameters
  • Initial VACM configuration implemented according to rfc3415 Appendix 1
  • tools/buildmibs.sh split-up and re-implemented as tools/build-pysnmp-mib and pysnmp-mibs/tools/rebuild-pysnmp-mibs for better usability. These and libsmi2pysnmp scripts made installable.
  • Types/Notifications/Groups exportSymbols() call chunking implemented in tools/libsmi2pysnmp
  • Initial values specified to pyasn1 types to comply with latest pyasn1 API.
  • Documentation improved
  • Minor fixes towards Python 1.5 compatibility

  • UNSTABLE ALPHA RELEASE.
  • Multi-lingual SNMP Trap/Inform Applications completed; examples added
  • SMI model re-designed to make a clear separation between Managed Objects and their specification (AKA Agent and Manager side)
  • SNMP SET Application support completed
  • Minor, though backward incompatible, changes to one-liner API
  • Many bugfixes

  • UNSTABLE ALPHA RELEASE.
  • SHA-based authentication fixed and privacy implemented
  • ...oneliner.cmdgen.UsmUserData constructor now takes authProtocol and privProtocol parameters in a backward incompatible manner.

  • UNSTABLE ALPHA RELEASE.
  • rfc3413 applications API changes (related to callback function behaviour).
  • TransportDispatcher now provides "jobs" interface to clients for better control of dispatcher's execution.
  • Many minor fixes.

  • UNSTABLE ALPHA RELEASE.
  • Top-level application classes renamed into longer, self descripting names for clarity.
  • CommandResponder & NotificationOriginator applications now uses stand-alone SnmpContext for application registration.
  • Many minor fixes (inspired by testing on WinXP)

  • UNSTABLE ALPHA RELEASE.
  • SNMPv3 code first published
  • SNMP engine and applications implemented on library level
  • Major re-design towards SNMPv3-style API.

Adopted to slightly changed asyncore API (as shipped with python 2,4)

Minor bug/typo fixes, mostly in example/ scripts.

  • UNSTABLE EARLY ALPHA RELEASE.
  • Major re-design and re-implementation.
  • Rudimental API versioning implemented to let incompatible package branches to co-exist within the same Python installation.
  • SMI framework designed and implemented. This framework provides 1) various access to MIB data 2) a way to implement custom MIB instrumentation objects. There's also a tool for building SMI classes from libsmi(3) output (smidump -f python).
  • ASN.1 subtyping machinery implemented. Now dynamic ASN.1 instances subtyping and quering becomes available. Previously, this has been done through Python classes inheritance what proved to be a wrong concept.
  • ASN.1 codecs framework re-designed and re-implemented aimed at a more consistent design and better performance. Highlights include abstract codec interface and serialized data caching (at encoder).
  • Asn1Item constraints machinery re-implemented based on Mike C. Fletcher's design and code. Now various constrains are implemented as stand-alone objects serving interested Asn1Object derivatives through some abstract protocol (that's probably the Decorator design pattern).
  • ASN.1 tagging facility re-implemented along the client-server design pattern. Besides this seems to be a more appropriate design, it allows an easier way for dynamic subtyping.

Our development plans and new features we consider for eventual implementation are collected in the following section.

Although PySNMP is already a mature software and it is being used at many places, the ultimate goal of the project is to implement most of the useful features that SNMP standards can offer. What follows is a list of most prominent missing features that PySNMP developers are planning to put their hands on in the future.

1.
Built-in MIB parser. PySNMP uses a data model of its own to work with information contained in MIB files. To convert ASN.1-based MIB texts into Python modules, an off-line, third-party tool is employed. As it turns out, this approach has two major drawback: one is that PySNMP users may need to pre-process MIB texts to use them with their PySNMP-based applications. Another is that LibSMI's Python driver seems to miss some information carried by MIBs. Thus the solution would be to write another MIB parser and code generator which would produce PySNMP compliant Python code right from MIB text files all by itself.

Done: see PySMI project in conjuction with the latest PySNMP codebase.

2.
Reverse MIB index. The variable-bindings received by the system whilst in Manager role could be post-processed using the information kept in MIB files to include human-friendly OIDs names, tables indices and values representation. However, there is currently no provisioning in the PySNMP system for locating and loading up MIB files containing additional information on arbitrary OIDs. So the idea is to maintain an OID-to-MIB index to let PySNMP load relevant MIB automatically on demand.
3.
Stream sockets support. Currently, PySNMP transport subsystem only supports datagram-type network sockets. That covers UDP-over-IPv4 and UDP-over-IPv6. However, SNMP engine can potentially run over stream-oriented protocols what would let it support TCP-over-IPv4, TCP-over-IPv6 and SSL/TSL transports. Neither of these is currently implemented with PySNMP.
4.
AgentX implementation. We anticipate many uses of this. For instance, having AgentX protocol support in pure-Python would let us write AgentX modules in pure-Python and attach them to high-performance Net-SNMP Agent. Or we could build and maintain a fully-featured, stand-alone PySNMP-based Agent so that users would write their own AgentX extensions what would comprise a complete SNMP Agent solution at lesser effort.
5.
A DBMS-based SMI. Currently implemented SMI takes shape of live Python objects that let user hook up his own handler on any existing Managed Object Instance. That's flexible and working approach in many cases, however sometimes, for instance when Management Instrumentation is inherently DBMS-based, it may be more efficient to move the entire SMI/MIB subsystem into a database. PySNMP engine would talk to it through its simple and well defined SMI API.

1.
SNMP Proxy Forwarder. That would be a stand-alone, application-level proxy service supporting all SNMP versions, multiple network transports, Command and Notification SNMP message types. Its anticipated features include extensive configuration facilities, fine-graned access control and logging.

Done: see SNMP Proxy Forwarder.

2.
SNMP Trap Receiver. We see this application as a simple yet flexible SNMP TRAP collector. It would listen on network sockets of different types receiving SNMP TRAP/INFORM notifications over any SNMP version and putting all the details into a database and possibly triggering external events.
3.
Database backend for SNMP Simulator. We have already built a tool for simulating SNMP Agents based on a snapshot of their Management Instrumentation state. Current implementation uses a plain-text file for keeping and possibly managing the snapshot. Many users of the Simulator software requested a value variation feature to be supported so that simulated Agents would look live, not static. We consider this variation and also dependencies features would be best implemented as a relational database application. So we are planning to put some more efforts into the Simulator project as time permits.

Done: since snmpsim-0.2.4

If you need some particular feature - please, open a feature request . Once we see a greater demand in particular area, we would re-arrange our development resources to meet it sooner.

You could greater speed up the development of particular feature by sponsoring it. Please get back to us to discuss details.

Contributions to the PySNMP source code is greatly appreciated as well. We require contributed code to run with Python 2.4 through the latest Python version (which is 3.7 at the time of this writing). Contributed code will be redistributed under the terms of the same license as PySNMP is.

In case of questions or troubles using PySNMP, please open up an issue at GitHub or ask at Stack Overflow .

Starting from PySNMP 4.3.0, we redesigned all documentation and web-site. For previous versions of those please follow these links for old examples and old documentation .

Ilya Etingof <etingof@gmail.com>

2005-2023, Ilya Etingof <etingof@gmail.com>

September 4, 2023 4.4