SNMP via gevent
Asynchronous SNMP via gevent.
Main characteristics as bullet point list:
The API does no oid translation for you.
An oid must be given as a tuple of integers.
For example system.sysDescr.0
must be convertet to (1, 3, 6, 1, 2, 1, 1, 1, 0)
The output is always a dictionary containing the varbind list from the response pdu. This varbind list is converted to a dictionary where the keys are the oids (as tuples) and the values the corresponding value from the response pdu.
from async_session import AsyncSession
config = {
'peername': '127.0.0.1',
'version': '2c',
'community': 'public',
'retries': 5,
'timeout': 3
}
session = AsyncSession(config)
session.open_session()
Converts a oid string of digits into a tuple of integers.
Example:
ret = async_session.oid_str_to_tuple("1.3.6.1.2.1.1.1.0")
assert ret == (1, 3, 6, 1, 2, 1, 1, 1, 0)
Converts a tuple of integers to a string with digits and dots.
Example:
ret = async_session.oid_tuple_to_str((1, 3, 6, 1, 2, 1, 1, 1, 0))
assert ret == "1.3.6.1.2.1.1.1.0"
Checks if an oid is within another one.
First parameter is the root
. Second one is checked if its a subtree of the root.
Both parameters must be tuple of integers.
ret = async_session.is_in_subtree(
(1, 3, 6, 1, 4, 1, 4491, 2, 1, 20, 1, 4, 1, 4),
(1, 3, 6, 1, 4, 1, 4491, 2, 1, 20, 1, 4, 1, 4, 3165, 50798601)
)
assert ret == True
ret = async_session.is_in_subtree(
(1, 3, 6, 1, 4, 1, 4491, 2, 1, 20, 1, 4, 1, 4),
(1, 3, 6, 1, 4, 1, 4491, 2, 1, 20, 1, 4, 1, 5, 1)
)
assert ret == False
Example:
oids = [
(1, 3, 6, 1, 2, 1, 1, 1, 0),
(1, 3, 6, 1, 2, 1, 1, 3, 0),
]
result = session.get(oids)
print 'system description', result[(1, 3, 6, 1, 2, 1, 1, 1, 0)]
print 'system uptime', result[(1, 3, 6, 1, 2, 1, 1, 3, 0)]
Example:
oid = (1, 3, 6, 1, 2, 1, 1, 1, 0)
result = session.get_next(oid)
if result:
next_oid, next_value = result.items()[0]
print 'next oid is', next_oid
print 'value for this id', next_value
Example:
oids = [
(1, 3, 6, 1, 2, 1, 1, 1),
(1, 3, 6, 1, 2, 1, 1, 3),
(1, 3, 6, 1, 2, 1, 2, 2, 1, 2)
]
# Get
# - system description
# - system uptime
# - The first 5 interface names
result = session.get_bulk(oids, nonrepeaters=2, maxrepetitions=5)
print 'system description', result.pop((1, 3, 6, 1, 2, 1, 1, 1, 0))
print 'system uptime', result.pop((1, 3, 6, 1, 2, 1, 1, 3, 0))
for oid, ifname in result.items():
print 'oid', oid
print 'interface name', ifname
This walk uses only snmp-getnext
to traverse the tree.
Example:
root_id = (1, 3, 6, 1, 2, 1, 1)
result = session.walk(root_id)
for oid in sorted(result):
print 'oid', oid, result[oid]
getbulk
The main difference to walk
is the usage of the snmp-getbulk
operation to traverse the tree.
Example:
root_id = (1, 3, 6, 1, 2, 1, 1)
result = session.walk_with_get_bulk(root_id, maxrepetitions=10)
for oid in sorted(result):
print 'oid', oid, result[oid]
Example:
to_set = {
# Set a new location.
(1, 3, 6, 1, 2, 1, 1, 1, 0): ('new location', 's'),
# Also integers have to be given as strings.
# The following disable IP forwarding on the first interface.
(1, 3, 6, 1, 2, 1, 4, 1, 0): ("2", "i")
}
result = session.set_oids(to_set)
The APIs mentioned above may raise the following exceptions.
This exception is raised if there is no SNMP-Response within the configured time constraints.
This exception is raised if something with the response is wrong. It has the following attributes:
code
: Which is the error status from the response PDUindex
: Which is the error index from the response PDUmessage
: String representation of the errortry:
session.set_oids(to_set)
except SNMPResponseError as error:
print error.code
print error.index
print error.message
The following methods have an optional py_flags
parameter to control how
the response is parsed.
This parameter is a python dictionary which may contains the following flags:
Each entry in the varbind list of a SNMP response contains type
and value
.
Per default the API takes automatically care to convert the value into the
corresponding python object. However it is also possible to get the type
for each entry. Is get_var_type
given and set to True
the value for the returned
dictionary will be a tuple. Where the first element is the type
and the second
element the value
as a python object.
For example:
for oid, (asn_type, asn_value) for session.walk(oid, py_flags={'get_var_type': True}).items():
print oid, asn_type, asn_value
If given and set to True
each single varbind of type
SNMP_ENDOFMIBVIEW
will be encode to the special object
async_session.END_OF_MIB
. If this flag is not set 'end of mib view'
is converted to None
.
If given and set to True
each single varbind of type
SNMP_NOSUCHOBJECT:
will be encode to the special object
async_session.NO_SUCH_OBJECT
. If this flag is not set 'no such object'
is converted to None
.
If given and set to True
each single varbind of type
SNMP_NOSUCHINSTANCE
will be encode to the special object
async_session.NO_SUCH_INSTANCE
. If this flag is not set 'no such instance'
is converted to None
.
If given and set to True
a collections.OrderedDict
is returned.
It preserves the order of the incoming varbinds.
If given and set to True
the result is return as a list of (oid, value)
tuples.
If given and set to True
each result value is converted to a string.
This string is generated by the NET-SNMP function family print_value
.
These functions take the definition of an OID from its corresponding MIB to
format the value accordingly. NET-SNMP offers some options to control the
format behaviour. See toggle_netsnmp_format_options
.
The result of the SNMP-API changes then to a tuple where the raw value is at
position one and the generated string at position two.
Note: The MIB's must be loaded, otherwise the format does not work. To do so call the following at process startup:
async_session.init_snmplib()
Use this call to clone an existing session.
open_session
has been already called.Example:
with sess.clone_session(community='private') as priv_sess:
print priv_sess.set_oids(oids)
Control the format of the values if the flag get_netsnmp_string=True
is
used. Since the NET-SNMP functions for formatting are used, the corresponding
format options are also supported.
To get a list of all supported options see:
snmpget -h
For example to enable 'quick view' and 'numeric oids use:
async_session.toggle_netsnmp_format_options('nq')
Keep in mind:
This function toggles the option. For example:
# Enables 'quick view'
async_session.toggle_netsnmp_format_options('q')
# Disables 'quick view'
async_session.toggle_netsnmp_format_options('q')
Furthermore these options are global for the entire process. So they affect all AsyncSession() at the same time. Hence the recommendation is to set them once at process startup.
Why the patch ? gevent_snmp works by replacing the calls to 'select()' inside libnetsnmp. So if libnetsnmp would call 'select()', gevent.socket.wait_read is called insted. Per default libnetsnmp does not allow to replace/override the calls to select. The patch to libnetsnmp just adds another function where the select could be replaced via a callback. The advantage of this patch is, that we can still use the synchronous 'high level' API of libnetsnmp. This synchronous 'high levell' is way more easier to use than the asynchronous 'low level' API. For example we get retry and timeout handling for free, because the synchronous 'high level' API implements them. Whereas the asynchronous 'low level' API does not support retry/timeout.
Have a look at netsnmp_patch.diff, its fairly easy and not intrusive at all.