snimpy-0.8.8/ 0000755 0000764 0000144 00000000000 12622134562 013507 5 ustar bernat users 0000000 0000000 snimpy-0.8.8/snimpy.egg-info/ 0000755 0000764 0000144 00000000000 12622134562 016520 5 ustar bernat users 0000000 0000000 snimpy-0.8.8/snimpy.egg-info/requires.txt 0000644 0000764 0000144 00000000045 12622134561 021116 0 ustar bernat users 0000000 0000000 cffi >= 1.0.0
pysnmp >= 4
setuptools
snimpy-0.8.8/snimpy.egg-info/dependency_links.txt 0000644 0000764 0000144 00000000001 12622134561 022565 0 ustar bernat users 0000000 0000000
snimpy-0.8.8/snimpy.egg-info/SOURCES.txt 0000644 0000764 0000144 00000002625 12622134562 020411 0 ustar bernat users 0000000 0000000 AUTHORS.rst
CONTRIBUTING.rst
HISTORY.rst
MANIFEST.in
README.rst
setup.py
version.txt
docs/api.rst
docs/conf.py
docs/contributing.rst
docs/history.rst
docs/index.rst
docs/installation.rst
docs/license.rst
docs/usage.rst
docs/_static/snimpy.svg
docs/_themes/.gitignore
docs/_themes/LICENSE
docs/_themes/README
docs/_themes/flask_theme_support.py
docs/_themes/flask/layout.html
docs/_themes/flask/relations.html
docs/_themes/flask/theme.conf
docs/_themes/flask/static/flasky.css_t
docs/_themes/flask_small/layout.html
docs/_themes/flask_small/theme.conf
docs/_themes/flask_small/static/flasky.css_t
examples/add-vlan.py
examples/disable-port-ingress-filtering.py
examples/enable-lldp.py
examples/get-serial.py
examples/list-interfaces.py
examples/list-routes.py
examples/rename-vlan.py
examples/set-syslog-ntp.py
examples/vlan-and-interfaces.py
man/snimpy.1
snimpy/__init__.py
snimpy/__main__.py
snimpy/_version.py
snimpy/basictypes.py
snimpy/config.py
snimpy/main.py
snimpy/manager.py
snimpy/mib.py
snimpy/smi_build.py
snimpy/snmp.py
snimpy.egg-info/PKG-INFO
snimpy.egg-info/SOURCES.txt
snimpy.egg-info/dependency_links.txt
snimpy.egg-info/entry_points.txt
snimpy.egg-info/not-zip-safe
snimpy.egg-info/requires.txt
snimpy.egg-info/top_level.txt
tests/SNIMPY-INVALID-MIB.mib
tests/SNIMPY-MIB.mib
tests/agent.py
tests/test_basictypes.py
tests/test_conf.py
tests/test_main.py
tests/test_manager.py
tests/test_mib.py
tests/test_snmp.py snimpy-0.8.8/snimpy.egg-info/top_level.txt 0000644 0000764 0000144 00000000007 12622134561 021246 0 ustar bernat users 0000000 0000000 snimpy
snimpy-0.8.8/snimpy.egg-info/entry_points.txt 0000644 0000764 0000144 00000000061 12622134561 022012 0 ustar bernat users 0000000 0000000 [console_scripts]
snimpy = snimpy.main:interact
snimpy-0.8.8/snimpy.egg-info/PKG-INFO 0000644 0000764 0000144 00000015576 12622134561 017632 0 ustar bernat users 0000000 0000000 Metadata-Version: 1.1
Name: snimpy
Version: 0.8.8
Summary: interactive SNMP tool
Home-page: https://github.com/vincentbernat/snimpy
Author: Vincent Bernat
Author-email: bernat@luffy.cx
License: UNKNOWN
Description: ===============================
snimpy
===============================
.. image:: https://badge.fury.io/py/snimpy.png
:target: http://badge.fury.io/py/snimpy
.. image:: https://travis-ci.org/vincentbernat/snimpy.png?branch=master
:target: https://travis-ci.org/vincentbernat/snimpy
.. image:: https://pypip.in/d/snimpy/badge.png
:target: https://crate.io/packages/snimpy?version=latest
.. image:: https://coveralls.io/repos/vincentbernat/snimpy/badge.png
:target: https://coveralls.io/r/vincentbernat/snimpy
Interactive SNMP tool.
*Snimpy* is a Python-based tool providing a simple interface to build
SNMP query. Here is a very simplistic example that allows us to
display the routing table of a given host::
load("IP-FORWARD-MIB")
m=M("localhost", "public", 2)
routes = m.ipCidrRouteNextHop
for x in routes:
net, netmask, tos, src = x
print("%15s/%-15s via %-15s src %-15s" % (net, netmask, routes[x], src))
You can either use *Snimpy* interactively throught its console
(derived from Python own console or from IPython_ if available) or
write *Snimpy* scripts which are just Python scripts with some global
variables available.
.. _IPython: http://ipython.org
* Free software: ISC license
* Documentation: http://snimpy.rtfd.org.
*Snimpy* requires libsmi_ to work correctly. See the documentation for
more information.
Features
--------
*Snimpy* is aimed at being the more Pythonic possible. You should forget
that you are doing SNMP requests. *Snimpy* will rely on MIB to hide SNMP
details. Here are some "features":
* MIB parser based on libsmi (through CFFI)
* SNMP requests are handled by PySNMP (SNMPv1, SNMPv2 and SNMPv3
support)
* scalars are just attributes of your session object
* columns are like a Python dictionary and made available as an
attribute
* getting an attribute is like issuing a GET method
* setting an attribute is like issuing a SET method
* iterating over a table is like using GETNEXT
* when something goes wrong, you get an exception
History
-------
0.8.8 (2015-11-15)
++++++++++++++++++
* Fix thread-safety problem introduced in 0.8.6. This also undo any
improvement advertised in 0.8.6 when using multiple
threads. However, performance should be kept when using a single
thread.
0.8.7 (2015-11-14)
++++++++++++++++++
* Ability to specify a module name when querying a manager.
* Compatibility with PySNMP 4.3
* Array-like interface for OIDs.
* Ability to restrict lookups to a specific MIB: m['IF-MIB'].ifDescr.
* Fix multithread support with SNMPv3 (with a performance impact).
0.8.6 (2015-06-24)
++++++++++++++++++
* Major speed improvement.
* Major memory usage improvement.
0.8.5 (2015-04-04)
++++++++++++++++++
* Ability to set SMI search path (with ``mib.path()``)
* Fix documentation build on *Read the Doc*.
* Add a loose mode to manager to loosen type coercion.
0.8.4 (2015-02-10)
++++++++++++++++++
* More CFFI workarounds, including cross-compilation support.
* Ability to override a node type.
* Automatic workaround for "SNMP too big" error message.
0.8.3 (2014-08-18)
++++++++++++++++++
* IPv6 support.
0.8.2 (2014-06-08)
++++++++++++++++++
* Minor bugfixes.
0.8.1 (2013-10-25)
++++++++++++++++++
* Workaround a problem with CFFI extension installation.
0.8.0 (2013-10-19)
++++++++++++++++++++
* Python 3.3 support. Pypy support.
* PEP8 compliant.
* Sphinx documentation.
* Octet strings with a display hint are now treated differently than
plain octet strings (unicode). Notably, they can now be set using
the displayed format (for example, for MAC addresses).
0.7.0 (2013-09-23)
++++++++++++++++++
* Major rewrite.
* SNMP support is now provided through PySNMP_.
* MIB parsing is still done with libsmi_ but through CFFI instead of a
C module.
* More unittests. Many bugfixes.
.. _PySNMP: http://pysnmp.sourceforge.net/
.. _libsmi: http://www.ibr.cs.tu-bs.de/projects/libsmi/
0.6.4 (2013-03-21)
++++++++++++++++++
* GETBULK support.
* MacAddress SMI type support.
0.6.3 (2012-04-13)
++++++++++++++++++
* Support for IPython 0.12.
* Minor bugfixes.
0.6.2 (2012-01-19)
++++++++++++++++++
* Ability to return None instead of getting an exception.
0.6.1 (2012-01-14)
++++++++++++++++++
* Thread safety and efficiency.
0.6 (2012-01-10)
++++++++++++++++++
* SNMPv3 support
0.5.1 (2011-08-07)
++++++++++++++++++
* Compatibility with IPython 0.11.
* Custom timeouts and retries.
0.5 (2010-02-03)
++++++++++++++++++
* Check conformity of loaded modules.
* Many bugfixes.
0.4 (2009-06-06)
++++++++++++++++++
* Allow to cache requests.
0.3 (2008-11-23)
++++++++++++++++++
* Provide a manual page.
* Use a context manager to group SET requests.
0.2.1 (2008-09-28)
++++++++++++++++++
* First release on PyPI.
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: ISC License (ISCL)
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: System :: Networking
Classifier: Topic :: Utilities
Classifier: Topic :: System :: Monitoring
snimpy-0.8.8/snimpy.egg-info/not-zip-safe 0000644 0000764 0000144 00000000001 12621664265 020755 0 ustar bernat users 0000000 0000000
snimpy-0.8.8/docs/ 0000755 0000764 0000144 00000000000 12622134562 014437 5 ustar bernat users 0000000 0000000 snimpy-0.8.8/docs/contributing.rst 0000644 0000764 0000144 00000000040 12376564336 017706 0 ustar bernat users 0000000 0000000 .. include:: ../CONTRIBUTING.rst snimpy-0.8.8/docs/usage.rst 0000644 0000764 0000144 00000013357 12600052762 016303 0 ustar bernat users 0000000 0000000 ========
Usage
========
Invocation
----------
There are three ways to use *Snimpy*:
1. Interactively through a console.
2. As a script interpreter.
3. As a regular Python module.
Interactive use
+++++++++++++++
*Snimpy* can be invoked with either `snimpy` or `python -m
snimpy`. Without any other arhument, the interactive console is
spawned. Otherwise, the given script is executed and the remaining
arguments are served as arguments for the script.
When running interactively, you get a classic Python
environment. There are two additional objects available:
* The `load()` method that takes a MIB name or a path to a
filename. The MIB will be loaded into memory and made available in
all SNMP managers::
load("IF-MIB")
load("/usr/share/mibs/ietf/IF-MIB")
* The `M` class which is used to instantiate a manager (a SNMP
client)::
m = M()
m = M(host="localhost", community="private", version=2)
m = M("localhost", "private", 2)
m = M(community="private")
m = M(version=3,
secname="readonly",
authprotocol="MD5", authpassword="authpass",
privprotocol="AES", privpassword="privpass")
A manager instance contains all the scalars and the columns in MIB
loaded with the `load()` method. There is no table, node or other
entities. For a scalar, getting and setting a value is a simple as::
print(m.sysDescr)
m.sysName = "newhostname"
For a column, you get a dictionary-like interface::
for index in m.ifDescr:
print(repr(m.ifDescr[index]))
m.ifAdminStatus[3] = "down"
If you want to group several write into a single request, you can do
it with `with` keyword::
with M("localhost", "private") as m:
m.sysName = "toto"
m.ifAdminStatus[20] = "down"
There is also a caching mechanism which is disabled by default::
import time
m = M("localhost", cache=True)
print(m.sysUpTime)
time.sleep(1)
print(m.sysUpTime)
time.sleep(1)
print(m.sysUpTime)
time.sleep(10)
print(m.sysUpTime)
You can also specify the number of seconds data should be cached::
m = M("localhost", cache=20)
It's also possible to set a custom timeout and a custom value for the
number of retries. For example, to wait 2.5 seconds before timeout
occurs and retry 10 times, you can use::
m = M("localhost", timeout=2.5, retries=10)
*Snimpy* will stop on any error with an exception. This allows you to
not check the result at each step. Your script can't go awry. If this
behaviour does not suit you, it is possible to suppress exceptions
when querying inexistant objects. Instead of an exception, you'll get
`None`::
m = M("localhost", none=True)
If for some reason, you need to specify the module you want to use to
lookup a node, you can do that using the following syntax::
print(m['SNMPv2-MIB'].sysDescr)
print(m['IF-MIB'].ifNumber)
Script interpreter
++++++++++++++++++
*Snimpy* can be run as a script interpreter. There are two ways to do
this. The first one is to invoke *Snimpy* and provide a script name as
well as any argument you want to pass to the script::
$ snimpy example-script.py arg1 arg2
$ python -m snimpy example-script.py arg1 arg2
The second one is to use *Snimpy* as a shebang_ interpreter. For
example, here is a simple script::
#!/usr/bin/env snimpy
load("IF-MIB")
m = M("localhost")
print(m.ifDescr[0])
The script can be invoked as any shell script.
.. _shebang: http://en.wikipedia.org/wiki/Shebang_(Unix)
Inside the script, you can use any valid Python code. You also get the
`load()` method and the `M` class available, like for the interactive
use.
Regular Python module
+++++++++++++++++++++
*Snimpy* can also be imported as a regular Python module::
from snimpy.manager import Manager as M
from snimpy.manager import load
load("IF-MIB")
m = M("localhost")
print(m.ifDescr[0])
About "major SMI errors"
------------------------
If you get an exception like `RAPID-CITY contains major SMI errors
(check with smilint -s -l1)`, this means that there are some grave
errors in this MIB which may lead to segfaults if the MIB is used as
is. Usually, this means that some identifier are unknown. Use `smilint
-s -l1 YOUR-MIB` to see what the problem is and try to solve all
problems reported by lines beginning by `[1]`.
For example::
$ smilint -s -l1 rapid_city.mib
rapid_city.mib:30: [1] failed to locate MIB module `IGMP-MIB'
rapid_city.mib:32: [1] failed to locate MIB module `DVMRP-MIB'
rapid_city.mib:34: [1] failed to locate MIB module `IGMP-MIB'
rapid_city.mib:27842: [1] unknown object identifier label `igmpInterfaceIfIndex'
rapid_city.mib:27843: [1] unknown object identifier label `igmpInterfaceQuerier'
rapid_city.mib:27876: [1] unknown object identifier label `dvmrpInterfaceIfIndex'
rapid_city.mib:27877: [1] unknown object identifier label `dvmrpInterfaceOperState'
rapid_city.mib:27894: [1] unknown object identifier label `dvmrpNeighborIfIndex'
rapid_city.mib:27895: [1] unknown object identifier label `dvmrpNeighborAddress'
rapid_city.mib:32858: [1] unknown object identifier label `igmpCacheAddress'
rapid_city.mib:32858: [1] unknown object identifier label `igmpCacheIfIndex'
To solve the problem here, load `IGMP-MIB` and `DVMRP-MIB` before
loading `rapid_city.mib`. `IGMP-MIB` should be pretty easy to
find. For `DVMRP-MIB`, try Google.
Download it and use `smistrip` to get the MIB. You can check that the
problem is solved with this command::
$ smilint -p ../cisco/IGMP-MIB.my -p ./DVMRP-MIB -s -l1 rapid_city.mib
You will get a lot of errors in `IGMP-MIB` and `DVMRP-MIB` but no line
with `[1]`: everything should be fine. To load `rapid_city.mib`, you
need to do this::
load("../cisco/IGMP-MIB.my")
load("./DVMRP-MIB")
load("rapid_city.mib")
snimpy-0.8.8/docs/index.rst 0000644 0000764 0000144 00000004513 12600052762 016300 0 ustar bernat users 0000000 0000000 Snimpy: interactive SNMP tool
====================================================
*Snimpy* is a Python-based tool providing a simple interface to build
SNMP query. Here is a very simplistic example that allows us to
display the routing table of a given host::
load("IP-FORWARD-MIB")
m=M("localhost", "public", 2)
routes = m.ipCidrRouteNextHop
for x in routes:
net, netmask, tos, src = x
print("%15s/%-15s via %-15s src %-15s" % (net, netmask, routes[x], src))
You can either use *Snimpy* interactively throught its console
(derived from Python own console or from IPython_ if available) or
write *Snimpy* scripts which are just Python scripts with some global
variables available.
.. _IPython: http://ipython.org
Why another tool?
-----------------
There are a lot of SNMP tools available but most of them have
important drawback when you need to reliably automatize operations.
`snmpget`, `snmpset` and `snmpwalk` are difficult to use in
scripts. Errors are printed on standard output and there is no easy
way to tell if the command was successful or not. Moreover, results
can be multiline (a long HexString for example). At least,
automatisation is done through the shell and OID or bit manipulation
are quite difficult.
Net-SNMP provides officiel bindings for Perl and
Python. Unfortunately, the integration is quite poor. You don't have
an easy way to load and browse MIBs and error handling is
inexistant. For example, the Python bindings will return None for a
non-existant OID. Having to check for this on each request is quite
cumbersome.
For Python, there are other bindings. For example, pysnmp_ provides a
pure Python implementation. However, MIBs need to be
compiled. Moreover, the exposed interface is still low-level. Sending
a simple SNMP GET can either take 10 lines or one line wrapped into 10
lines.
.. _pysnmp: http://pysnmp.sourceforge.net/
The two main points of *Snimpy* are:
* very high-level interface relying on MIBs
* raise exceptions when something goes wrong
Meantime, another Pythonic SNMP library based on Net-SNMP has been
released: `Easy SNMP`_. Its interface is a less Pythonic than *Snimpy*
but it doesn't need MIBs to work.
.. _Easy SNMP: https://github.com/fgimian/easysnmp
Contents
---------
.. toctree::
:maxdepth: 1
installation
usage
api
contributing
license
history
snimpy-0.8.8/docs/license.rst 0000644 0000764 0000144 00000001745 12376564336 016636 0 ustar bernat users 0000000 0000000 ========
License
========
*Snimpy* is licensed under the ISC license. It basically means: do
whatever you want with it as long as the copyright sticks around, the
conditions are not modified and the disclaimer is present.
.. include:: ../AUTHORS.rst
ISC License
-----------
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
snimpy-0.8.8/docs/_themes/ 0000755 0000764 0000144 00000000000 12622134562 016063 5 ustar bernat users 0000000 0000000 snimpy-0.8.8/docs/_themes/flask_theme_support.py 0000644 0000764 0000144 00000011413 12272767420 022521 0 ustar bernat users 0000000 0000000 # flasky extensions. flasky pygments style based on tango style
from pygments.style import Style
from pygments.token import Keyword, Name, Comment, String, Error, \
Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
class FlaskyStyle(Style):
background_color = "#f8f8f8"
default_style = ""
styles = {
# No corresponding class for the following:
#Text: "", # class: ''
Whitespace: "underline #f8f8f8", # class: 'w'
Error: "#a40000 border:#ef2929", # class: 'err'
Other: "#000000", # class 'x'
Comment: "italic #8f5902", # class: 'c'
Comment.Preproc: "noitalic", # class: 'cp'
Keyword: "bold #004461", # class: 'k'
Keyword.Constant: "bold #004461", # class: 'kc'
Keyword.Declaration: "bold #004461", # class: 'kd'
Keyword.Namespace: "bold #004461", # class: 'kn'
Keyword.Pseudo: "bold #004461", # class: 'kp'
Keyword.Reserved: "bold #004461", # class: 'kr'
Keyword.Type: "bold #004461", # class: 'kt'
Operator: "#582800", # class: 'o'
Operator.Word: "bold #004461", # class: 'ow' - like keywords
Punctuation: "bold #000000", # class: 'p'
# because special names such as Name.Class, Name.Function, etc.
# are not recognized as such later in the parsing, we choose them
# to look the same as ordinary variables.
Name: "#000000", # class: 'n'
Name.Attribute: "#c4a000", # class: 'na' - to be revised
Name.Builtin: "#004461", # class: 'nb'
Name.Builtin.Pseudo: "#3465a4", # class: 'bp'
Name.Class: "#000000", # class: 'nc' - to be revised
Name.Constant: "#000000", # class: 'no' - to be revised
Name.Decorator: "#888", # class: 'nd' - to be revised
Name.Entity: "#ce5c00", # class: 'ni'
Name.Exception: "bold #cc0000", # class: 'ne'
Name.Function: "#000000", # class: 'nf'
Name.Property: "#000000", # class: 'py'
Name.Label: "#f57900", # class: 'nl'
Name.Namespace: "#000000", # class: 'nn' - to be revised
Name.Other: "#000000", # class: 'nx'
Name.Tag: "bold #004461", # class: 'nt' - like a keyword
Name.Variable: "#000000", # class: 'nv' - to be revised
Name.Variable.Class: "#000000", # class: 'vc' - to be revised
Name.Variable.Global: "#000000", # class: 'vg' - to be revised
Name.Variable.Instance: "#000000", # class: 'vi' - to be revised
Number: "#990000", # class: 'm'
Literal: "#000000", # class: 'l'
Literal.Date: "#000000", # class: 'ld'
String: "#4e9a06", # class: 's'
String.Backtick: "#4e9a06", # class: 'sb'
String.Char: "#4e9a06", # class: 'sc'
String.Doc: "italic #8f5902", # class: 'sd' - like a comment
String.Double: "#4e9a06", # class: 's2'
String.Escape: "#4e9a06", # class: 'se'
String.Heredoc: "#4e9a06", # class: 'sh'
String.Interpol: "#4e9a06", # class: 'si'
String.Other: "#4e9a06", # class: 'sx'
String.Regex: "#4e9a06", # class: 'sr'
String.Single: "#4e9a06", # class: 's1'
String.Symbol: "#4e9a06", # class: 'ss'
Generic: "#000000", # class: 'g'
Generic.Deleted: "#a40000", # class: 'gd'
Generic.Emph: "italic #000000", # class: 'ge'
Generic.Error: "#ef2929", # class: 'gr'
Generic.Heading: "bold #000080", # class: 'gh'
Generic.Inserted: "#00A000", # class: 'gi'
Generic.Output: "#888", # class: 'go'
Generic.Prompt: "#745334", # class: 'gp'
Generic.Strong: "bold #000000", # class: 'gs'
Generic.Subheading: "bold #800080", # class: 'gu'
Generic.Traceback: "bold #a40000", # class: 'gt'
}
snimpy-0.8.8/docs/_themes/flask/ 0000755 0000764 0000144 00000000000 12622134562 017163 5 ustar bernat users 0000000 0000000 snimpy-0.8.8/docs/_themes/flask/static/ 0000755 0000764 0000144 00000000000 12622134562 020452 5 ustar bernat users 0000000 0000000 snimpy-0.8.8/docs/_themes/flask/static/flasky.css_t 0000644 0000764 0000144 00000021546 12272767474 023027 0 ustar bernat users 0000000 0000000 /*
* flasky.css_t
* ~~~~~~~~~~~~
*
* :copyright: Copyright 2010 by Armin Ronacher.
* :license: Flask Design License, see LICENSE for details.
*/
{% set page_width = '940px' %}
{% set sidebar_width = '220px' %}
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'Georgia', serif;
font-size: 17px;
background-color: white;
color: #000;
margin: 0;
padding: 0;
}
div.document {
width: {{ page_width }};
margin: 30px auto 0 auto;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 {{ sidebar_width }};
}
div.sphinxsidebar {
width: {{ sidebar_width }};
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 0 30px;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
width: {{ page_width }};
margin: 20px auto 30px auto;
font-size: 14px;
color: #888;
text-align: right;
}
div.footer a {
color: #888;
}
div.related {
display: none;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
border-bottom: 1px dotted #999;
}
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
div.sphinxsidebar {
font-size: 14px;
line-height: 1.5;
}
div.sphinxsidebarwrapper {
padding: 18px 10px;
}
div.sphinxsidebarwrapper p.logo {
padding: 0 0 20px 0;
margin: 0;
text-align: center;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: 'Garamond', 'Georgia', serif;
color: #444;
font-size: 24px;
font-weight: normal;
margin: 0 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 20px;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p.logo a,
div.sphinxsidebar h3 a,
div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
color: #000;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: 'Georgia', serif;
font-size: 1em;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
{% if theme_index_logo %}
div.indexwrapper h1 {
text-indent: -999999px;
background: url({{ theme_index_logo }}) no-repeat center center;
height: {{ theme_index_logo_height }};
}
{% endif %}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #ddd;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition tt.xref, div.admonition a tt {
border-bottom: 1px solid #fafafa;
}
dd div.admonition {
margin-left: -60px;
padding-left: 60px;
}
div.admonition p.admonition-title {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight {
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.9em;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
background: #fdfdfd;
font-size: 0.9em;
}
table.footnote + table.footnote {
margin-top: -15px;
border-top: none;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td.label {
width: 0px;
padding: 0.3em 0 0.3em 0.5em;
}
table.footnote td {
padding: 0.3em 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
blockquote {
margin: 0 0 0 30px;
padding: 0;
}
ul, ol {
margin: 10px 0 10px 30px;
padding: 0;
}
pre {
background: #eee;
padding: 7px 30px;
margin: 15px -30px;
line-height: 1.3em;
}
dl pre, blockquote pre, li pre {
margin-left: -60px;
padding-left: 60px;
}
dl dl pre {
margin-left: -90px;
padding-left: 90px;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #FBFBFB;
border-bottom: 1px solid white;
}
a.reference {
text-decoration: none;
border-bottom: 1px dotted #004B6B;
}
a.reference:hover {
border-bottom: 1px solid #6D4100;
}
a.footnote-reference {
text-decoration: none;
font-size: 0.7em;
vertical-align: top;
border-bottom: 1px dotted #004B6B;
}
a.footnote-reference:hover {
border-bottom: 1px solid #6D4100;
}
a:hover tt {
background: #EEE;
}
@media screen and (max-width: 870px) {
div.sphinxsidebar {
display: none;
}
div.document {
width: 100%;
}
div.documentwrapper {
margin-left: 0;
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
}
div.bodywrapper {
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
margin-left: 0;
}
ul {
margin-left: 0;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.bodywrapper {
margin: 0;
}
.footer {
width: auto;
}
.github {
display: none;
}
}
@media screen and (max-width: 875px) {
body {
margin: 0;
padding: 20px 30px;
}
div.documentwrapper {
float: none;
background: white;
}
div.sphinxsidebar {
display: block;
float: none;
width: 102.5%;
margin: 50px -30px -20px -30px;
padding: 10px 20px;
background: #333;
color: white;
}
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
div.sphinxsidebar h3 a {
color: white;
}
div.sphinxsidebar a {
color: #aaa;
}
div.sphinxsidebar p.logo {
display: none;
}
div.document {
width: 100%;
margin: 0;
}
div.related {
display: block;
margin: 0;
padding: 10px 0 20px 0;
}
div.related ul,
div.related ul li {
margin: 0;
padding: 0;
}
div.footer {
display: none;
}
div.bodywrapper {
margin: 0;
}
div.body {
min-height: 0;
padding: 0;
}
.rtd_doc_footer {
display: none;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.footer {
width: auto;
}
.github {
display: none;
}
}
/* scrollbars */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-button:start:decrement,
::-webkit-scrollbar-button:end:increment {
display: block;
height: 10px;
}
::-webkit-scrollbar-button:vertical:increment {
background-color: #fff;
}
::-webkit-scrollbar-track-piece {
background-color: #eee;
-webkit-border-radius: 3px;
}
::-webkit-scrollbar-thumb:vertical {
height: 50px;
background-color: #ccc;
-webkit-border-radius: 3px;
}
::-webkit-scrollbar-thumb:horizontal {
width: 50px;
background-color: #ccc;
-webkit-border-radius: 3px;
}
/* misc. */
.revsys-inline {
display: none!important;
} snimpy-0.8.8/docs/_themes/flask/relations.html 0000644 0000764 0000144 00000001116 12272767472 022065 0 ustar bernat users 0000000 0000000
Related Topics
snimpy-0.8.8/docs/_themes/flask/theme.conf 0000644 0000764 0000144 00000000244 12272767472 021151 0 ustar bernat users 0000000 0000000 [theme]
inherit = basic
stylesheet = flasky.css
pygments_style = flask_theme_support.FlaskyStyle
[options]
index_logo = ''
index_logo_height = 120px
touch_icon =
snimpy-0.8.8/docs/_themes/flask/layout.html 0000644 0000764 0000144 00000001265 12272767472 021407 0 ustar bernat users 0000000 0000000 {%- extends "basic/layout.html" %}
{%- block extrahead %}
{{ super() }}
{% if theme_touch_icon %}
{% endif %}
{% endblock %}
{%- block relbar2 %}{% endblock %}
{% block header %}
{{ super() }}
{% if pagename == 'index' %}
{% endif %}
{% endblock %}
{%- block footer %}
{% if pagename == 'index' %}
{% endif %}
{%- endblock %}
snimpy-0.8.8/docs/_themes/LICENSE 0000644 0000764 0000144 00000003375 12272767420 017106 0 ustar bernat users 0000000 0000000 Copyright (c) 2010 by Armin Ronacher.
Some rights reserved.
Redistribution and use in source and binary forms of the theme, 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.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
We kindly ask you to only use these themes in an unmodified manner just
for Flask and Flask-related products, not for unrelated projects. If you
like the visual style and want to use it for your own projects, please
consider making some larger changes to the themes (such as changing
font faces, sizes, colors or margins).
THIS THEME 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 OWNER 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 THEME, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
snimpy-0.8.8/docs/_themes/flask_small/ 0000755 0000764 0000144 00000000000 12622134562 020353 5 ustar bernat users 0000000 0000000 snimpy-0.8.8/docs/_themes/flask_small/static/ 0000755 0000764 0000144 00000000000 12622134562 021642 5 ustar bernat users 0000000 0000000 snimpy-0.8.8/docs/_themes/flask_small/static/flasky.css_t 0000644 0000764 0000144 00000011001 12272767474 024200 0 ustar bernat users 0000000 0000000 /*
* flasky.css_t
* ~~~~~~~~~~~~
*
* Sphinx stylesheet -- flasky theme based on nature theme.
*
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'Georgia', serif;
font-size: 17px;
color: #000;
background: white;
margin: 0;
padding: 0;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 40px auto 0 auto;
width: 700px;
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 30px 30px;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
text-align: right;
color: #888;
padding: 10px;
font-size: 14px;
width: 650px;
margin: 0 auto 40px auto;
}
div.footer a {
color: #888;
text-decoration: underline;
}
div.related {
line-height: 32px;
color: #888;
}
div.related ul {
padding: 0 0 0 10px;
}
div.related a {
color: #444;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body {
padding-bottom: 40px; /* saved for footer */
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
}
{% if theme_index_logo %}
div.indexwrapper h1 {
text-indent: -999999px;
background: url({{ theme_index_logo }}) no-repeat center center;
height: {{ theme_index_logo_height }};
}
{% endif %}
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: white;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition p.admonition-title {
font-family: 'Garamond', 'Georgia', serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight{
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
div.warning {
background-color: #ffe4e4;
border: 1px solid #f66;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.85em;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td {
padding: 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
pre {
padding: 0;
margin: 15px -30px;
padding: 8px;
line-height: 1.3em;
padding: 7px 30px;
background: #eee;
border-radius: 2px;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
}
dl pre {
margin-left: -60px;
padding-left: 60px;
}
tt {
background-color: #ecf0f3;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #FBFBFB;
}
a:hover tt {
background: #EEE;
}
snimpy-0.8.8/docs/_themes/flask_small/theme.conf 0000644 0000764 0000144 00000000270 12272767472 022340 0 ustar bernat users 0000000 0000000 [theme]
inherit = basic
stylesheet = flasky.css
nosidebar = true
pygments_style = flask_theme_support.FlaskyStyle
[options]
index_logo = ''
index_logo_height = 120px
github_fork = ''
snimpy-0.8.8/docs/_themes/flask_small/layout.html 0000644 0000764 0000144 00000001253 12272767472 022574 0 ustar bernat users 0000000 0000000 {% extends "basic/layout.html" %}
{% block header %}
{{ super() }}
{% if pagename == 'index' %}
{% endif %}
{% endblock %}
{% block footer %}
{% if pagename == 'index' %}
{% endif %}
{% endblock %}
{# do not display relbars #}
{% block relbar1 %}{% endblock %}
{% block relbar2 %}
{% if theme_github_fork %}
{% endif %}
{% endblock %}
{% block sidebar1 %}{% endblock %}
{% block sidebar2 %}{% endblock %}
snimpy-0.8.8/docs/_themes/README 0000644 0000764 0000144 00000002105 12272767420 016747 0 ustar bernat users 0000000 0000000 Flask Sphinx Styles
===================
This repository contains sphinx styles for Flask and Flask related
projects. To use this style in your Sphinx documentation, follow
this guide:
1. put this folder as _themes into your docs folder. Alternatively
you can also use git submodules to check out the contents there.
2. add this to your conf.py:
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'flask'
The following themes exist:
- 'flask' - the standard flask documentation theme for large
projects
- 'flask_small' - small one-page theme. Intended to be used by
very small addon libraries for flask.
The following options exist for the flask_small theme:
[options]
index_logo = '' filename of a picture in _static
to be used as replacement for the
h1 in the index.rst file.
index_logo_height = 120px height of the index logo
github_fork = '' repository name on github for the
"fork me" badge
snimpy-0.8.8/docs/_themes/.gitignore 0000644 0000764 0000144 00000000026 12272767420 020057 0 ustar bernat users 0000000 0000000 *.pyc
*.pyo
.DS_Store
snimpy-0.8.8/docs/conf.py 0000644 0000764 0000144 00000002430 12600052762 015732 0 ustar bernat users 0000000 0000000 #!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
rtd = os.environ.get('READTHEDOCS', None) == 'True'
cwd = os.getcwd()
project_root = os.path.dirname(cwd)
sys.path.insert(0, project_root)
# -- Don't try to load CFFI (doesn't work on RTD) -----------------------------
if rtd:
from mock import Mock
sys.modules['cffi'] = Mock()
sys.modules['cffi.verifier'] = Mock()
import snimpy
# -- General configuration ----------------------------------------------------
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
templates_path = ['_templates']
source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = u'Snimpy'
copyright = u'2015, Vincent Bernat'
version = snimpy.__version__
release = snimpy.__version__
exclude_patterns = ['_build']
pygments_style = 'sphinx'
# -- Options for HTML output --------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'flask'
html_static_path = ['_static']
html_use_modindex = False
html_theme_options = {
"index_logo": "snimpy.svg",
"index_logo_height": "200px"
}
htmlhelp_basename = 'snimpydoc'
snimpy-0.8.8/docs/history.rst 0000644 0000764 0000144 00000000033 12376564336 016702 0 ustar bernat users 0000000 0000000 .. include:: ../HISTORY.rst snimpy-0.8.8/docs/api.rst 0000644 0000764 0000144 00000001322 12600052762 015735 0 ustar bernat users 0000000 0000000 ==============
API reference
==============
While *Snimpy* is targeted at being used interactively or through
simple scripts, you can also use it from your Python program.
It provides a high-level interface as well as lower-level
ones. However, the effort is only put in th :mod:`manager` module and
other modules are considered as internal details.
:mod:`manager` module
----------------------
.. automodule:: snimpy.manager
:members: Manager, load
:mod:`mib` module
------------------
.. automodule:: snimpy.mib
:members:
:mod:`snmp` module
-------------------
.. automodule:: snimpy.snmp
:members:
:mod:`basictypes` module
-------------------------
.. automodule:: snimpy.basictypes
:members:
snimpy-0.8.8/docs/installation.rst 0000644 0000764 0000144 00000001714 12376564336 017711 0 ustar bernat users 0000000 0000000 ============
Installation
============
At the command line::
$ easy_install snimpy
Or, if you have virtualenvwrapper installed::
$ mkvirtualenv snimpy
$ pip install snimpy
*Snimpy* requires libsmi_, a library to access SMI MIB
information. You need to install both the library and the development
headers. If *Snimpy* complains to not find ``smi.h``, you can help by
specifying where this file is located by exporting the appropriate
environment variable::
$ export C_INCLUDE_PATH=/opt/local/include
On Debian/Ubuntu, you can install libsmi with::
$ sudo apt-get install libsmi2-dev
On RedHat and similar, you can use::
$ sudo yum install libsmi-devel
On OS X, if you are using homebrew_, you can use::
$ brew install libsmi
.. _libsmi: http://www.ibr.cs.tu-bs.de/projects/libsmi/
.. _homebrew: http://brew.sh
On Debian and Ubuntu, *Snimpy* is also available as a package you can
install with::
$ sudo apt-get install snimpy
snimpy-0.8.8/docs/_static/ 0000755 0000764 0000144 00000000000 12622134562 016065 5 ustar bernat users 0000000 0000000 snimpy-0.8.8/docs/_static/snimpy.svg 0000644 0000764 0000144 00000027443 12376564336 020153 0 ustar bernat users 0000000 0000000
image/svg+xml
snimpy-0.8.8/HISTORY.rst 0000644 0000764 0000144 00000005317 12622132460 015403 0 ustar bernat users 0000000 0000000 .. :changelog:
History
-------
0.8.8 (2015-11-15)
++++++++++++++++++
* Fix thread-safety problem introduced in 0.8.6. This also undo any
improvement advertised in 0.8.6 when using multiple
threads. However, performance should be kept when using a single
thread.
0.8.7 (2015-11-14)
++++++++++++++++++
* Ability to specify a module name when querying a manager.
* Compatibility with PySNMP 4.3
* Array-like interface for OIDs.
* Ability to restrict lookups to a specific MIB: m['IF-MIB'].ifDescr.
* Fix multithread support with SNMPv3 (with a performance impact).
0.8.6 (2015-06-24)
++++++++++++++++++
* Major speed improvement.
* Major memory usage improvement.
0.8.5 (2015-04-04)
++++++++++++++++++
* Ability to set SMI search path (with ``mib.path()``)
* Fix documentation build on *Read the Doc*.
* Add a loose mode to manager to loosen type coercion.
0.8.4 (2015-02-10)
++++++++++++++++++
* More CFFI workarounds, including cross-compilation support.
* Ability to override a node type.
* Automatic workaround for "SNMP too big" error message.
0.8.3 (2014-08-18)
++++++++++++++++++
* IPv6 support.
0.8.2 (2014-06-08)
++++++++++++++++++
* Minor bugfixes.
0.8.1 (2013-10-25)
++++++++++++++++++
* Workaround a problem with CFFI extension installation.
0.8.0 (2013-10-19)
++++++++++++++++++++
* Python 3.3 support. Pypy support.
* PEP8 compliant.
* Sphinx documentation.
* Octet strings with a display hint are now treated differently than
plain octet strings (unicode). Notably, they can now be set using
the displayed format (for example, for MAC addresses).
0.7.0 (2013-09-23)
++++++++++++++++++
* Major rewrite.
* SNMP support is now provided through PySNMP_.
* MIB parsing is still done with libsmi_ but through CFFI instead of a
C module.
* More unittests. Many bugfixes.
.. _PySNMP: http://pysnmp.sourceforge.net/
.. _libsmi: http://www.ibr.cs.tu-bs.de/projects/libsmi/
0.6.4 (2013-03-21)
++++++++++++++++++
* GETBULK support.
* MacAddress SMI type support.
0.6.3 (2012-04-13)
++++++++++++++++++
* Support for IPython 0.12.
* Minor bugfixes.
0.6.2 (2012-01-19)
++++++++++++++++++
* Ability to return None instead of getting an exception.
0.6.1 (2012-01-14)
++++++++++++++++++
* Thread safety and efficiency.
0.6 (2012-01-10)
++++++++++++++++++
* SNMPv3 support
0.5.1 (2011-08-07)
++++++++++++++++++
* Compatibility with IPython 0.11.
* Custom timeouts and retries.
0.5 (2010-02-03)
++++++++++++++++++
* Check conformity of loaded modules.
* Many bugfixes.
0.4 (2009-06-06)
++++++++++++++++++
* Allow to cache requests.
0.3 (2008-11-23)
++++++++++++++++++
* Provide a manual page.
* Use a context manager to group SET requests.
0.2.1 (2008-09-28)
++++++++++++++++++
* First release on PyPI.
snimpy-0.8.8/setup.cfg 0000644 0000764 0000144 00000000073 12622134562 015330 0 ustar bernat users 0000000 0000000 [egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
snimpy-0.8.8/setup.py 0000644 0000764 0000144 00000004720 12621664030 015221 0 ustar bernat users 0000000 0000000 import os
import sys
from setuptools import setup
from setuptools.command.test import test
import snimpy
rtd = os.environ.get('READTHEDOCS', None) == 'True'
class SnimpyTestCommand(test):
def run_tests(self, *args, **kwds):
# Ensure we keep a reference to multiprocessing and pysnmp to
# avoid errors at the end of the test
import multiprocessing
import pysnmp
SnimpyTestCommand.multiprocessing = multiprocessing
SnimpyTestCommand.pysnmp = pysnmp
return test.run_tests(self, *args, **kwds)
if __name__ == "__main__":
readme = open('README.rst').read()
history = open('HISTORY.rst').read().replace('.. :changelog:', '')
setup(name="snimpy",
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Console',
'Intended Audience :: System Administrators',
'License :: OSI Approved :: ISC License (ISCL)',
'Operating System :: POSIX',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 3',
'Topic :: System :: Networking',
'Topic :: Utilities',
'Topic :: System :: Monitoring'
],
url='https://github.com/vincentbernat/snimpy',
description=snimpy.__doc__,
long_description=readme + '\n\n' + history,
author=snimpy.__author__,
author_email=snimpy.__email__,
packages=["snimpy"],
entry_points={
'console_scripts': [
'snimpy = snimpy.main:interact',
],
},
data_files=[('share/man/man1', ['man/snimpy.1'])],
zip_safe=False,
cffi_modules=["snimpy/smi_build.py:ffi"],
install_requires=["cffi >= 1.0.0", "pysnmp >= 4", "setuptools"],
setup_requires=["cffi >= 1.0.0", "vcversioner"],
tests_require=list(filter(None, ["cffi >= 1.0.0",
"pysnmp >= 4",
"nose",
"mock",
sys.version_info < (2, 7) and
"unittest2"])),
test_suite="nose.collector",
cmdclass={
"test": SnimpyTestCommand
},
pbr=False,
vcversioner={
'version_module_paths': ['snimpy/_version.py'],
},
)
snimpy-0.8.8/man/ 0000755 0000764 0000144 00000000000 12622134562 014262 5 ustar bernat users 0000000 0000000 snimpy-0.8.8/man/snimpy.1 0000644 0000764 0000144 00000001767 12326014751 015674 0 ustar bernat users 0000000 0000000 .TH SNIMPY 1 "Oct 4, 2008"
.SH NAME
snimpy \- interactive SNMP tool with Python
.SH SYNOPSIS
.B snimpy
.RI [ options ]
.SH DESCRIPTION
This manual page documents briefly the
.B snimpy
command.
.PP
\fBsnimpy\fP is a Python-based tool providing a simple interface to build SNMP queries. This interface aims at being the most Pythonic possible: you grab scalars using attributes and columns are like dictionaries.
.PP
\fBsnimpy\fP can be used either interactively through its console
(derived from Python own console or from IPython if available) or by
writing \fBsnimpy\fP scripts which are just Python scripts with some global
variables available.
.SH OPTIONS
\fBsnimpy\fP does not take any option. If you launch it without any
argument, you will get the interactive console. Otherwise, the first
argument is the name of a script to be executed and the remaining
arguments are the arguments for this script.
.SH SEE ALSO
.nf
https://github.com/vincentbernat/snimpy
.fi
.SH AUTHOR
Vincent Bernat
snimpy-0.8.8/MANIFEST.in 0000644 0000764 0000144 00000000665 12600052762 015251 0 ustar bernat users 0000000 0000000 recursive-include examples *.py
recursive-include tests *.py SNIMPY-MIB.mib SNIMPY-INVALID-MIB.mib
# Documentation
include man/snimpy.1
include AUTHORS.rst
include CONTRIBUTING.rst
include HISTORY.rst
include README.rst
include version.txt
recursive-include docs *.rst *.py
recursive-include docs/_themes *
recursive-include docs/_static *
# Remove CFFI files
global-exclude __pycache__/*
# Remove git directories
global-exclude .git
snimpy-0.8.8/PKG-INFO 0000644 0000764 0000144 00000015576 12622134562 014622 0 ustar bernat users 0000000 0000000 Metadata-Version: 1.1
Name: snimpy
Version: 0.8.8
Summary: interactive SNMP tool
Home-page: https://github.com/vincentbernat/snimpy
Author: Vincent Bernat
Author-email: bernat@luffy.cx
License: UNKNOWN
Description: ===============================
snimpy
===============================
.. image:: https://badge.fury.io/py/snimpy.png
:target: http://badge.fury.io/py/snimpy
.. image:: https://travis-ci.org/vincentbernat/snimpy.png?branch=master
:target: https://travis-ci.org/vincentbernat/snimpy
.. image:: https://pypip.in/d/snimpy/badge.png
:target: https://crate.io/packages/snimpy?version=latest
.. image:: https://coveralls.io/repos/vincentbernat/snimpy/badge.png
:target: https://coveralls.io/r/vincentbernat/snimpy
Interactive SNMP tool.
*Snimpy* is a Python-based tool providing a simple interface to build
SNMP query. Here is a very simplistic example that allows us to
display the routing table of a given host::
load("IP-FORWARD-MIB")
m=M("localhost", "public", 2)
routes = m.ipCidrRouteNextHop
for x in routes:
net, netmask, tos, src = x
print("%15s/%-15s via %-15s src %-15s" % (net, netmask, routes[x], src))
You can either use *Snimpy* interactively throught its console
(derived from Python own console or from IPython_ if available) or
write *Snimpy* scripts which are just Python scripts with some global
variables available.
.. _IPython: http://ipython.org
* Free software: ISC license
* Documentation: http://snimpy.rtfd.org.
*Snimpy* requires libsmi_ to work correctly. See the documentation for
more information.
Features
--------
*Snimpy* is aimed at being the more Pythonic possible. You should forget
that you are doing SNMP requests. *Snimpy* will rely on MIB to hide SNMP
details. Here are some "features":
* MIB parser based on libsmi (through CFFI)
* SNMP requests are handled by PySNMP (SNMPv1, SNMPv2 and SNMPv3
support)
* scalars are just attributes of your session object
* columns are like a Python dictionary and made available as an
attribute
* getting an attribute is like issuing a GET method
* setting an attribute is like issuing a SET method
* iterating over a table is like using GETNEXT
* when something goes wrong, you get an exception
History
-------
0.8.8 (2015-11-15)
++++++++++++++++++
* Fix thread-safety problem introduced in 0.8.6. This also undo any
improvement advertised in 0.8.6 when using multiple
threads. However, performance should be kept when using a single
thread.
0.8.7 (2015-11-14)
++++++++++++++++++
* Ability to specify a module name when querying a manager.
* Compatibility with PySNMP 4.3
* Array-like interface for OIDs.
* Ability to restrict lookups to a specific MIB: m['IF-MIB'].ifDescr.
* Fix multithread support with SNMPv3 (with a performance impact).
0.8.6 (2015-06-24)
++++++++++++++++++
* Major speed improvement.
* Major memory usage improvement.
0.8.5 (2015-04-04)
++++++++++++++++++
* Ability to set SMI search path (with ``mib.path()``)
* Fix documentation build on *Read the Doc*.
* Add a loose mode to manager to loosen type coercion.
0.8.4 (2015-02-10)
++++++++++++++++++
* More CFFI workarounds, including cross-compilation support.
* Ability to override a node type.
* Automatic workaround for "SNMP too big" error message.
0.8.3 (2014-08-18)
++++++++++++++++++
* IPv6 support.
0.8.2 (2014-06-08)
++++++++++++++++++
* Minor bugfixes.
0.8.1 (2013-10-25)
++++++++++++++++++
* Workaround a problem with CFFI extension installation.
0.8.0 (2013-10-19)
++++++++++++++++++++
* Python 3.3 support. Pypy support.
* PEP8 compliant.
* Sphinx documentation.
* Octet strings with a display hint are now treated differently than
plain octet strings (unicode). Notably, they can now be set using
the displayed format (for example, for MAC addresses).
0.7.0 (2013-09-23)
++++++++++++++++++
* Major rewrite.
* SNMP support is now provided through PySNMP_.
* MIB parsing is still done with libsmi_ but through CFFI instead of a
C module.
* More unittests. Many bugfixes.
.. _PySNMP: http://pysnmp.sourceforge.net/
.. _libsmi: http://www.ibr.cs.tu-bs.de/projects/libsmi/
0.6.4 (2013-03-21)
++++++++++++++++++
* GETBULK support.
* MacAddress SMI type support.
0.6.3 (2012-04-13)
++++++++++++++++++
* Support for IPython 0.12.
* Minor bugfixes.
0.6.2 (2012-01-19)
++++++++++++++++++
* Ability to return None instead of getting an exception.
0.6.1 (2012-01-14)
++++++++++++++++++
* Thread safety and efficiency.
0.6 (2012-01-10)
++++++++++++++++++
* SNMPv3 support
0.5.1 (2011-08-07)
++++++++++++++++++
* Compatibility with IPython 0.11.
* Custom timeouts and retries.
0.5 (2010-02-03)
++++++++++++++++++
* Check conformity of loaded modules.
* Many bugfixes.
0.4 (2009-06-06)
++++++++++++++++++
* Allow to cache requests.
0.3 (2008-11-23)
++++++++++++++++++
* Provide a manual page.
* Use a context manager to group SET requests.
0.2.1 (2008-09-28)
++++++++++++++++++
* First release on PyPI.
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: ISC License (ISCL)
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: System :: Networking
Classifier: Topic :: Utilities
Classifier: Topic :: System :: Monitoring
snimpy-0.8.8/CONTRIBUTING.rst 0000644 0000764 0000144 00000006121 12376564336 016164 0 ustar bernat users 0000000 0000000 ============
Contributing
============
Contributions are welcome, and they are greatly appreciated! Every
little bit helps, and credit will always be given.
You can contribute in many ways:
Types of Contributions
----------------------
Report Bugs
~~~~~~~~~~~
Report bugs at https://github.com/vincentbernat/snimpy/issues.
If you are reporting a bug, please include:
* Your operating system name and version.
* Any details about your local setup that might be helpful in troubleshooting.
* Detailed steps to reproduce the bug.
Fix Bugs
~~~~~~~~
Look through the GitHub issues for bugs. Anything tagged with "bug"
is open to whoever wants to implement it.
Implement Features
~~~~~~~~~~~~~~~~~~
Look through the GitHub issues for features. Anything tagged with "feature"
is open to whoever wants to implement it.
Write Documentation
~~~~~~~~~~~~~~~~~~~
Snimpy could always use more documentation, whether as part of the
official Snimpy docs, in docstrings, or even on the web in blog posts,
articles, and such.
Submit Feedback
~~~~~~~~~~~~~~~
The best way to send feedback is to file an issue at https://github.com/vincentbernat/snimpy/issues.
If you are proposing a feature:
* Explain in detail how it would work.
* Keep the scope as narrow as possible, to make it easier to implement.
* Remember that this is a volunteer-driven project, and that contributions
are welcome :)
Get Started!
------------
Ready to contribute? Here's how to set up `snimpy` for local development.
1. Fork the `snimpy` repo on GitHub.
2. Clone your fork locally::
$ git clone git@github.com:your_name_here/snimpy.git
3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development::
$ mkvirtualenv snimpy
$ cd snimpy/
$ python setup.py develop
4. Create a branch for local development::
$ git checkout -b name-of-your-bugfix-or-feature
Now you can make your changes locally.
5. When you're done making changes, check that your changes pass flake8 and the
tests, including testing other Python versions with tox::
$ flake8 snimpy tests
$ python setup.py test
$ tox
To get flake8 and tox, just pip install them into your virtualenv.
6. Commit your changes and push your branch to GitHub::
$ git add .
$ git commit -m "Your detailed description of your changes."
$ git push origin name-of-your-bugfix-or-feature
7. Submit a pull request through the GitHub website.
Pull Request Guidelines
-----------------------
Before you submit a pull request, check that it meets these guidelines:
1. The pull request should include tests.
2. If the pull request adds functionality, the docs should be updated. Put
your new functionality into a function with a docstring, and add the
feature to the list in README.rst.
3. The pull request should work for Python 2.6, 2.7, 3.3 and 3.4, and for PyPy. Check
https://travis-ci.org/vincentbernat/snimpy/pull_requests
and make sure that the tests pass for all supported Python versions.
Tips
----
To run a subset of tests::
$ python -m nose tests/test_snmp.py
snimpy-0.8.8/version.txt 0000644 0000764 0000144 00000000025 12622134561 015731 0 ustar bernat users 0000000 0000000 0.8.8-0-g761a70444eac snimpy-0.8.8/AUTHORS.rst 0000644 0000764 0000144 00000000204 12376564336 015376 0 ustar bernat users 0000000 0000000 Development Lead
----------------
* Vincent Bernat
Contributors
------------
* Jakub Wroniecki
* Julian Taylor
snimpy-0.8.8/tests/ 0000755 0000764 0000144 00000000000 12622134562 014651 5 ustar bernat users 0000000 0000000 snimpy-0.8.8/tests/SNIMPY-MIB.mib 0000644 0000764 0000144 00000026257 12600052762 016777 0 ustar bernat users 0000000 0000000 SNIMPY-MIB DEFINITIONS ::= BEGIN
IMPORTS
MODULE-IDENTITY, OBJECT-TYPE,
IpAddress, Integer32, Gauge32,
TimeTicks, Counter64,
Counter32, mib-2 FROM SNMPv2-SMI
DisplayString, TEXTUAL-CONVENTION,
PhysAddress, TruthValue FROM SNMPv2-TC
InetAddressType, InetAddress,
InetAddressIPv4, InetAddressIPv6 FROM INET-ADDRESS-MIB
IANAifType FROM IANAifType-MIB;
snimpy MODULE-IDENTITY
LAST-UPDATED "200809160000Z"
ORGANIZATION
"snimpy
https://github.com/vincentbernat/snimpy"
CONTACT-INFO
"Lorem ipsum, etc, etc."
DESCRIPTION
"This is a test MIB module for snimpy."
REVISION "200809160000Z"
DESCRIPTION "Last revision"
::= { mib-2 45121 }
OddInteger ::= TEXTUAL-CONVENTION
DISPLAY-HINT "d-2"
STATUS current
DESCRIPTION
"Testing fmt"
SYNTAX INTEGER (6..18 | 20..23 | 27 | 28..1336)
UnicodeString ::= TEXTUAL-CONVENTION
DISPLAY-HINT "255t"
STATUS current
DESCRIPTION
"Testing fmt"
SYNTAX OCTET STRING (SIZE(0..255))
snimpyScalars OBJECT IDENTIFIER ::= { snimpy 1 }
snimpyTables OBJECT IDENTIFIER ::= { snimpy 2 }
snimpyIpAddress OBJECT-TYPE
SYNTAX IpAddress
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"An IP address"
::= { snimpyScalars 1 }
snimpyString OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"An string to display"
::= { snimpyScalars 2 }
snimpyInteger OBJECT-TYPE
SYNTAX OddInteger
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"An integer"
::= { snimpyScalars 3 }
snimpyEnum OBJECT-TYPE
SYNTAX INTEGER {
up(1),
down(2),
testing(3)
}
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"An enumeration"
::= { snimpyScalars 4 }
snimpyObjectId OBJECT-TYPE
SYNTAX OBJECT IDENTIFIER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"An oid"
::= { snimpyScalars 5 }
snimpyBoolean OBJECT-TYPE
SYNTAX TruthValue
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"A boolean"
::= { snimpyScalars 6 }
snimpyCounter OBJECT-TYPE
SYNTAX Counter32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"A 32 bits counter"
::= { snimpyScalars 7 }
snimpyGauge OBJECT-TYPE
SYNTAX Gauge32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"A 32 bits gauge"
::= { snimpyScalars 8 }
snimpyTimeticks OBJECT-TYPE
SYNTAX TimeTicks
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"A timetick"
::= { snimpyScalars 9 }
snimpyCounter64 OBJECT-TYPE
SYNTAX Counter64
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"A 64-bit counter"
::= { snimpyScalars 10 }
snimpyBits OBJECT-TYPE
SYNTAX BITS {
first(0),
second(1),
third(2),
last(7)
}
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"A bit field"
::= { snimpyScalars 11 }
snimpyNotImplemented OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"An string to display (not implemented)"
::= { snimpyScalars 12 }
snimpyOctetString OBJECT-TYPE
SYNTAX OCTET STRING
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"An string to display"
::= { snimpyScalars 13 }
snimpyUnicodeString OBJECT-TYPE
SYNTAX UnicodeString
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"An unicode string to display"
::= { snimpyScalars 14 }
snimpyMacAddress OBJECT-TYPE
SYNTAX PhysAddress
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"A MAC address"
::= { snimpyScalars 15 }
snimpyMacAddressInvalid OBJECT-TYPE
SYNTAX DisplayString
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"A MAC address with invalid syntax"
::= { snimpyScalars 16 }
-- A simple table
snimpySimpleTable OBJECT-TYPE
SYNTAX SEQUENCE OF SnimpySimpleEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A table"
::= { snimpyTables 1 }
snimpySimpleEntry OBJECT-TYPE
SYNTAX SnimpySimpleEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Entry for our simple table"
INDEX { snimpySimpleIndex }
::= { snimpySimpleTable 1 }
SnimpySimpleEntry ::=
SEQUENCE {
snimpySimpleIndex Integer32,
snimpySimpleDescr DisplayString,
snimpySimpleType IANAifType,
snimpySimplePhys PhysAddress
}
snimpySimpleIndex OBJECT-TYPE
SYNTAX Integer32 (1..30)
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Index for snimpy simple table"
::= { snimpySimpleEntry 1 }
snimpySimpleDescr OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Blah blah"
::= { snimpySimpleEntry 2 }
snimpySimpleType OBJECT-TYPE
SYNTAX IANAifType
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Blah blah"
::= { snimpySimpleEntry 3 }
snimpySimplePhys OBJECT-TYPE
SYNTAX PhysAddress
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Blah blah"
::= { snimpySimpleEntry 4 }
-- A more complex table
snimpyComplexTable OBJECT-TYPE
SYNTAX SEQUENCE OF SnimpyComplexEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A more complex table"
::= { snimpyTables 2 }
snimpyComplexEntry OBJECT-TYPE
SYNTAX SnimpyComplexEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Entry for our complex table"
INDEX { snimpyComplexFirstIP, snimpyComplexSecondIP }
::= { snimpyComplexTable 1 }
SnimpyComplexEntry ::=
SEQUENCE {
snimpyComplexFirstIP IpAddress,
snimpyComplexSecondIP IpAddress,
snimpyComplexState INTEGER
}
snimpyComplexFirstIP OBJECT-TYPE
SYNTAX IpAddress
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"First IP address for index"
::= { snimpyComplexEntry 1 }
snimpyComplexSecondIP OBJECT-TYPE
SYNTAX IpAddress
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Second IP address for index"
::= { snimpyComplexEntry 2 }
snimpyComplexState OBJECT-TYPE
SYNTAX INTEGER {
up(1),
down(2),
testing(3)
}
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"State for our both IP"
::= { snimpyComplexEntry 3 }
-- A table with complex indexes
snimpyIndexTable OBJECT-TYPE
SYNTAX SEQUENCE OF SnimpyIndexEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A table with complex indexes"
::= { snimpyTables 3 }
snimpyIndexEntry OBJECT-TYPE
SYNTAX SnimpyIndexEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Entry for our indexed table"
INDEX { snimpyIndexVarLen, snimpyIndexOidVarLen,
snimpyIndexFixedLen, IMPLIED snimpyIndexImplied }
::= { snimpyIndexTable 1 }
SnimpyIndexEntry ::=
SEQUENCE {
snimpyIndexVarLen DisplayString,
snimpyIndexIntIndex Integer32,
snimpyIndexOidVarLen OBJECT IDENTIFIER,
snimpyIndexFixedLen DisplayString,
snimpyIndexImplied DisplayString,
snimpyIndexInt Integer32
}
snimpyIndexVarLen OBJECT-TYPE
SYNTAX DisplayString (SIZE (1..10))
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Variable length index"
::= { snimpyIndexEntry 1 }
snimpyIndexIntIndex OBJECT-TYPE
SYNTAX Integer32
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Integer index"
::= { snimpyIndexEntry 2 }
snimpyIndexOidVarLen OBJECT-TYPE
SYNTAX OBJECT IDENTIFIER
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"OID as index"
::= { snimpyIndexEntry 3 }
snimpyIndexFixedLen OBJECT-TYPE
SYNTAX DisplayString (SIZE (6))
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Fixed length index"
::= { snimpyIndexEntry 4 }
snimpyIndexImplied OBJECT-TYPE
SYNTAX DisplayString (SIZE (1..30))
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Variable length index, implied"
::= { snimpyIndexEntry 5 }
snimpyIndexInt OBJECT-TYPE
SYNTAX Integer32
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"An integer of fixed size"
::= { snimpyIndexEntry 6 }
-- A table indexed using InetAddresses
snimpyInetAddressTable OBJECT-TYPE
SYNTAX SEQUENCE OF SnimpyInetAddressEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A InetAddress table"
::= { snimpyTables 4 }
snimpyInetAddressEntry OBJECT-TYPE
SYNTAX SnimpyInetAddressEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Entry for our complex table"
INDEX { snimpyInetAddressType, snimpyInetAddress }
::= { snimpyInetAddressTable 1 }
SnimpyInetAddressEntry ::=
SEQUENCE {
snimpyInetAddressType InetAddressType,
snimpyInetAddress InetAddress,
snimpyInetAddressState INTEGER
}
snimpyInetAddressType OBJECT-TYPE
SYNTAX InetAddressType
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Address type identifier for snimpyInetAddress"
::= { snimpyInetAddressEntry 1 }
snimpyInetAddress OBJECT-TYPE
SYNTAX InetAddress
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Type dependent InetAddress"
::= { snimpyInetAddressEntry 2 }
snimpyInetAddressState OBJECT-TYPE
SYNTAX INTEGER {
up(1),
down(2),
testing(3)
}
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"State for the IP"
::= { snimpyInetAddressEntry 3 }
-- A table that may contain invalid values
snimpyInvalidTable OBJECT-TYPE
SYNTAX SEQUENCE OF SnimpyInvalidEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"A table"
::= { snimpyTables 5 }
snimpyInvalidEntry OBJECT-TYPE
SYNTAX SnimpyInvalidEntry
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Entry for our invalid table"
INDEX { snimpyInvalidIndex }
::= { snimpyInvalidTable 1 }
SnimpyInvalidEntry ::=
SEQUENCE {
snimpyInvalidIndex Integer32,
snimpyInvalidDescr DisplayString
}
snimpyInvalidIndex OBJECT-TYPE
SYNTAX Integer32 (1..30)
MAX-ACCESS not-accessible
STATUS current
DESCRIPTION
"Index for snimpy invalid table"
::= { snimpyInvalidEntry 1 }
snimpyInvalidDescr OBJECT-TYPE
SYNTAX DisplayString (SIZE (0..255))
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"Blah blah"
::= { snimpyInvalidEntry 2 }
END
snimpy-0.8.8/tests/agent.py 0000644 0000764 0000144 00000032441 12622132734 016324 0 ustar bernat users 0000000 0000000 from multiprocessing import Process, Queue
import random
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import cmdrsp, context
from pysnmp.carrier.asynsock.dgram import udp, udp6
from pysnmp.proto.api import v2c
class TestAgent(object):
next_port = [random.randint(22000, 32000)]
"""Agent for testing purpose"""
def __init__(self, ipv6=False, community='public',
authpass='authpass', privpass='privpass'):
q = Queue()
self.ipv6 = ipv6
self.community = community
self.authpass = authpass
self.privpass = privpass
self.next_port[0] += 1
self._process = Process(target=self._setup,
args=(q, self.next_port[0]))
self._process.start()
self.port = q.get()
def terminate(self):
self._process.terminate()
def _setup(self, q, port):
"""Setup a new agent in a separate process.
The port the agent is listening too will be returned using the
provided queue.
"""
snmpEngine = engine.SnmpEngine()
if self.ipv6:
config.addSocketTransport(
snmpEngine,
udp6.domainName,
udp6.Udp6Transport().openServerMode(('::1', port)))
else:
config.addSocketTransport(
snmpEngine,
udp.domainName,
udp.UdpTransport().openServerMode(('127.0.0.1', port)))
# Community is public and MIB is writable
config.addV1System(snmpEngine, 'read-write', self.community)
config.addVacmUser(snmpEngine, 1, 'read-write', 'noAuthNoPriv',
(1, 3, 6), (1, 3, 6))
config.addVacmUser(snmpEngine, 2, 'read-write', 'noAuthNoPriv',
(1, 3, 6), (1, 3, 6))
config.addV3User(
snmpEngine, 'read-write',
config.usmHMACMD5AuthProtocol, self.authpass,
config.usmAesCfb128Protocol, self.privpass)
config.addVacmUser(snmpEngine, 3, 'read-write', 'authPriv',
(1, 3, 6), (1, 3, 6))
# Build MIB
def stringToOid(string):
return [ord(x) for x in string]
def flatten(*args):
result = []
for el in args:
if isinstance(el, (list, tuple)):
for sub in el:
result.append(sub)
else:
result.append(el)
return tuple(result)
snmpContext = context.SnmpContext(snmpEngine)
mibBuilder = snmpContext.getMibInstrum().getMibBuilder()
(MibTable, MibTableRow, MibTableColumn,
MibScalar, MibScalarInstance) = mibBuilder.importSymbols(
'SNMPv2-SMI',
'MibTable', 'MibTableRow', 'MibTableColumn',
'MibScalar', 'MibScalarInstance')
class RandomMibScalarInstance(MibScalarInstance):
previous_value = 0
def getValue(self, name, idx):
self.previous_value += random.randint(1, 2000)
return self.getSyntax().clone(self.previous_value)
mibBuilder.exportSymbols(
'__MY_SNMPv2_MIB',
# SNMPv2-MIB::sysDescr
MibScalar((1, 3, 6, 1, 2, 1, 1, 1), v2c.OctetString()),
MibScalarInstance((1, 3, 6, 1, 2, 1, 1, 1), (0,),
v2c.OctetString(
"Snimpy Test Agent {}".format(
self.community))))
mibBuilder.exportSymbols(
'__MY_IF_MIB',
# IF-MIB::ifNumber
MibScalar((1, 3, 6, 1, 2, 1, 2, 1), v2c.Integer()),
MibScalarInstance((1, 3, 6, 1, 2, 1, 2, 1), (0,), v2c.Integer(3)),
# IF-MIB::ifTable
MibTable((1, 3, 6, 1, 2, 1, 2, 2)),
MibTableRow((1, 3, 6, 1, 2, 1, 2, 2, 1)).setIndexNames(
(0, '__MY_IF_MIB', 'ifIndex')),
# IF-MIB::ifIndex
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 2, 2, 1, 1), (1,), v2c.Integer(1)),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 2, 2, 1, 1), (2,), v2c.Integer(2)),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 2, 2, 1, 1), (3,), v2c.Integer(3)),
# IF-MIB::ifDescr
MibTableColumn((1, 3, 6, 1, 2, 1, 2, 2, 1, 2), v2c.OctetString()),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 2, 2, 1, 2), (1,), v2c.OctetString("lo")),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 2, 2, 1, 2), (2,), v2c.OctetString("eth0")),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 2, 2, 1, 2), (3,), v2c.OctetString("eth1")),
# IF-MIB::ifType
MibTableColumn((1, 3, 6, 1, 2, 1, 2, 2, 1, 3), v2c.Integer()),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 2, 2, 1, 3), (1,), v2c.Integer(24)),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 2, 2, 1, 3), (2,), v2c.Integer(6)),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 2, 2, 1, 3), (3,), v2c.Integer(6)),
# IF-MIB::ifInOctets
MibTableColumn((1, 3, 6, 1, 2, 1, 2, 2, 1, 10), v2c.Integer()),
RandomMibScalarInstance(
(1, 3, 6, 1, 2, 1, 2, 2, 1, 10), (1,), v2c.Gauge32()),
RandomMibScalarInstance(
(1, 3, 6, 1, 2, 1, 2, 2, 1, 10), (2,), v2c.Gauge32()),
RandomMibScalarInstance(
(1, 3, 6, 1, 2, 1, 2, 2, 1, 10), (3,), v2c.Gauge32()),
# IF-MIB::ifIndex
ifIndex=MibTableColumn((1, 3, 6, 1, 2, 1, 2, 2, 1, 1),
v2c.Integer()))
mibBuilder.exportSymbols(
'__MY_SNIMPY-MIB',
# SNIMPY-MIB::snimpyIpAddress
MibScalar((1, 3, 6, 1, 2, 1, 45121, 1, 1),
v2c.OctetString()).setMaxAccess("readwrite"),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 45121, 1, 1), (0,),
v2c.OctetString("AAAA")),
# SNIMPY-MIB::snimpyString
MibScalar((1, 3, 6, 1, 2, 1, 45121, 1, 2),
v2c.OctetString()).setMaxAccess("readwrite"),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 45121, 1, 2), (0,), v2c.OctetString("bye")),
# SNIMPY-MIB::snimpyInteger
MibScalar((1, 3, 6, 1, 2, 1, 45121, 1, 3),
v2c.Integer()).setMaxAccess("readwrite"),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 45121, 1, 3), (0,), v2c.Integer(19)),
# SNIMPY-MIB::snimpyEnum
MibScalar((1, 3, 6, 1, 2, 1, 45121, 1, 4),
v2c.Integer()).setMaxAccess("readwrite"),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 45121, 1, 4), (0,), v2c.Integer(2)),
# SNIMPY-MIB::snimpyObjectId
MibScalar((1, 3, 6, 1, 2, 1, 45121, 1, 5),
v2c.ObjectIdentifier()).setMaxAccess("readwrite"),
MibScalarInstance((1, 3, 6, 1, 2, 1, 45121, 1, 5), (
0,), v2c.ObjectIdentifier((1, 3, 6, 4454, 0, 0))),
# SNIMPY-MIB::snimpyBoolean
MibScalar((1, 3, 6, 1, 2, 1, 45121, 1, 6),
v2c.Integer()).setMaxAccess("readwrite"),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 45121, 1, 6), (0,), v2c.Integer(1)),
# SNIMPY-MIB::snimpyCounter
MibScalar((1, 3, 6, 1, 2, 1, 45121, 1, 7),
v2c.Counter32()).setMaxAccess("readwrite"),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 45121, 1, 7), (0,), v2c.Counter32(47)),
# SNIMPY-MIB::snimpyGauge
MibScalar((1, 3, 6, 1, 2, 1, 45121, 1, 8),
v2c.Gauge32()).setMaxAccess("readwrite"),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 45121, 1, 8), (0,), v2c.Gauge32(18)),
# SNIMPY-MIB::snimpyTimeticks
MibScalar((1, 3, 6, 1, 2, 1, 45121, 1, 9),
v2c.TimeTicks()).setMaxAccess("readwrite"),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 45121, 1, 9), (0,),
v2c.TimeTicks(12111100)),
# SNIMPY-MIB::snimpyCounter64
MibScalar((1, 3, 6, 1, 2, 1, 45121, 1, 10),
v2c.Counter64()).setMaxAccess("readwrite"),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 45121, 1, 10), (0,),
v2c.Counter64(2 ** 48 + 3)),
# SNIMPY-MIB::snimpyBits
MibScalar((1, 3, 6, 1, 2, 1, 45121, 1, 11),
v2c.OctetString()).setMaxAccess("readwrite"),
MibScalarInstance(
(1, 3, 6, 1, 2, 1, 45121, 1, 11), (0,),
v2c.OctetString(b"\xa0")),
# SNIMPY-MIB::snimpyMacAddress
MibScalar((1, 3, 6, 1, 2, 1, 45121, 1, 15),
v2c.OctetString()).setMaxAccess("readwrite"),
MibScalarInstance((1, 3, 6, 1, 2, 1, 45121, 1, 15), (
0,), v2c.OctetString(b"\x11\x12\x13\x14\x15\x16")),
# SNIMPY-MIB::snimpyMacAddressInvalid
MibScalar((1, 3, 6, 1, 2, 1, 45121, 1, 16),
v2c.OctetString()).setMaxAccess("readwrite"),
MibScalarInstance((1, 3, 6, 1, 2, 1, 45121, 1, 16), (
0,), v2c.OctetString(b"\xf1\x12\x13\x14\x15\x16")),
# SNIMPY-MIB::snimpyIndexTable
MibTable((1, 3, 6, 1, 2, 1, 45121, 2, 3)),
MibTableRow(
(1, 3, 6, 1, 2, 1, 45121, 2, 3, 1)).setIndexNames(
(0, "__MY_SNIMPY-MIB", "snimpyIndexVarLen"),
(0, "__MY_SNIMPY-MIB", "snimpyIndexOidVarLen"),
(0, "__MY_SNIMPY-MIB", "snimpyIndexFixedLen"),
(1, "__MY_SNIMPY-MIB", "snimpyIndexImplied")),
# SNIMPY-MIB::snimpyIndexInt
MibScalarInstance((1, 3, 6, 1, 2, 1, 45121, 2, 3, 1, 6),
flatten(4, stringToOid('row1'),
3, 1, 2, 3,
stringToOid('alpha5'),
stringToOid('end of row1')),
v2c.Integer(4571)),
MibScalarInstance((1, 3, 6, 1, 2, 1, 45121, 2, 3, 1, 6),
flatten(4, stringToOid('row2'),
4, 1, 0, 2, 3,
stringToOid('beta32'),
stringToOid('end of row2')),
v2c.Integer(78741)),
MibScalarInstance((1, 3, 6, 1, 2, 1, 45121, 2, 3, 1, 6),
flatten(4, stringToOid('row3'),
4, 120, 1, 2, 3,
stringToOid('gamma7'),
stringToOid('end of row3')),
v2c.Integer(4110)),
# SNIMPY-MIB::snimpyInvalidTable
MibTable((1, 3, 6, 1, 2, 1, 45121, 2, 5)),
MibTableRow(
(1, 3, 6, 1, 2, 1, 45121, 2, 5, 1)).setIndexNames(
(0, "__MY_SNIMPY-MIB", "snimpyInvalidIndex")),
# SNIMPY-MIB::snimpyInvalidDescr
MibScalarInstance((1, 3, 6, 1, 2, 1, 45121, 2, 5, 1, 2),
(1,),
v2c.OctetString(b"Hello")),
MibScalarInstance((1, 3, 6, 1, 2, 1, 45121, 2, 5, 1, 2),
(2,),
v2c.OctetString(b"\xf1\x12\x13\x14\x15\x16")),
# Indexes
snimpyIndexVarLen=MibTableColumn(
(1, 3, 6, 1, 2, 1, 45121, 2, 3, 1, 1),
v2c.OctetString(
)).setMaxAccess("noaccess"),
snimpyIndexIntIndex=MibTableColumn(
(1, 3, 6, 1, 2, 1, 45121, 2, 3, 1, 2),
v2c.Integer(
)).setMaxAccess(
"noaccess"),
snimpyIndexOidVarLen=MibTableColumn(
(1, 3, 6, 1, 2, 1, 45121, 2, 3, 1, 3),
v2c.ObjectIdentifier(
)).setMaxAccess(
"noaccess"),
snimpyIndexFixedLen=MibTableColumn(
(1, 3, 6, 1, 2, 1, 45121, 2, 3, 1, 4),
v2c.OctetString(
).setFixedLength(
6)).setMaxAccess(
"noaccess"),
snimpyIndexImplied=MibTableColumn(
(1, 3, 6, 1, 2, 1, 45121, 2, 3, 1, 5),
v2c.OctetString(
)).setMaxAccess("noaccess"),
snimpyIndexInt=MibTableColumn(
(1, 3, 6, 1, 2, 1, 45121, 2, 3, 1, 6),
v2c.Integer()).setMaxAccess("readwrite"),
snimpyInvalidIndex=MibTableColumn(
(1, 3, 6, 1, 2, 1, 45121, 2, 5, 1, 1),
v2c.Integer()).setMaxAccess("noaccess"),
snimpyInvalidDescr=MibTableColumn(
(1, 3, 6, 1, 2, 1, 45121, 2, 5, 1, 2),
v2c.OctetString()).setMaxAccess("readwrite")
)
# Start agent
cmdrsp.GetCommandResponder(snmpEngine, snmpContext)
cmdrsp.SetCommandResponder(snmpEngine, snmpContext)
cmdrsp.NextCommandResponder(snmpEngine, snmpContext)
cmdrsp.BulkCommandResponder(snmpEngine, snmpContext)
q.put(port)
snmpEngine.transportDispatcher.jobStarted(1)
snmpEngine.transportDispatcher.runDispatcher()
snimpy-0.8.8/tests/test_basictypes.py 0000644 0000764 0000144 00000062231 12620437116 020433 0 ustar bernat users 0000000 0000000 import unittest
import os
import re
import socket
import mock
from datetime import timedelta
from snimpy import mib, basictypes
from pysnmp.proto import rfc1902
class TestBasicTypes(unittest.TestCase):
def setUp(self):
mib.load(os.path.join(os.path.dirname(os.path.abspath(__file__)),
"SNIMPY-MIB.mib"))
def tearDown(self):
mib.reset()
def testInteger(self):
"""Test integer basic type"""
a = basictypes.build("SNIMPY-MIB", "snimpyInteger", 18)
self.assert_(isinstance(a, basictypes.Integer))
self.assertEqual(a, 18)
self.assertEqual(a + 10, 28)
a = basictypes.build("SNIMPY-MIB", "snimpyInteger", 4)
self.assertEqual(a, 4)
self.assertEqual(a * 4, 16)
a = basictypes.build("SNIMPY-MIB", "snimpyInteger", 5)
self.assertEqual(a, 5)
self.assert_(a < 6)
# self.assert_(a > 4.6) # type coercion does not work
self.assert_(a > 4)
self.assertRaises(TypeError,
basictypes.build, ("SNIMPY-MIB",
"snimpyInteger", [1, 2, 3]))
def testString(self):
"""Test string basic type"""
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"hello")
self.assert_(isinstance(a, basictypes.String))
self.assertEqual(a, "hello")
self.assertEqual(a + " john", "hello john")
self.assertEqual(a * 2, "hellohello")
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"hello john")
self.assert_("john" in a)
self.assert_("steve" not in a)
self.assertEqual(a[1], 'e')
self.assertEqual(a[1:4], 'ell')
self.assertEqual(len(a), 10)
def testStringFromBytes(self):
"""Test string basic type when built from bytes"""
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"hello")
self.assert_(isinstance(a, basictypes.String))
self.assertEqual(a, "hello")
self.assertEqual(a + " john", "hello john")
self.assertEqual(a * 2, "hellohello")
def testStringEncoding(self):
"""Test we can create an UTF-8 encoded string"""
a = basictypes.build("SNIMPY-MIB", "snimpyString", u"hello")
self.assertEqual(a, u"hello")
self.assertEqual(a, "hello")
a = basictypes.build(
"SNIMPY-MIB",
"snimpyUnicodeString",
u"\U0001F60E Hello")
self.assertEqual(a, u"\U0001F60E Hello")
a = basictypes.build(
"SNIMPY-MIB",
"snimpyUnicodeString",
b'\xf0\x9f\x98\x8e Hello')
self.assertEqual(a, u"\U0001F60E Hello")
self.assertRaises(UnicodeError,
basictypes.build, "SNIMPY-MIB", "snimpyString",
b'\xf0\x9f\x98\x8e Hello')
def testOctetString(self):
"""Test octet string basic type"""
a = basictypes.build("SNIMPY-MIB", "snimpyOctetString", b"hello\x41")
self.assert_(isinstance(a, basictypes.OctetString))
self.assertEqual(a, b"hello\x41")
self.assertEqual(len(a), 6)
def testIpAddress(self):
"""Test IP address basic type"""
a = basictypes.build(
"SNIMPY-MIB",
"snimpyIpAddress",
socket.inet_aton("10.0.4.5"))
self.assert_(isinstance(a, basictypes.IpAddress))
self.assertEqual(a, "10.0.4.5")
self.assertEqual(a, "10.00.4.05")
self.assertEqual(a, [10, 0, 4, 5])
self.assertEqual(a[2], 4)
self.assert_(a < "10.1.2.4")
self.assert_(a > "10.0.0.1")
a = basictypes.build("SNIMPY-MIB", "snimpyIpAddress", [1, 2, 3, 5])
self.assertEqual(a, "1.2.3.5")
a = basictypes.build("SNIMPY-MIB", "snimpyIpAddress", "10.0.4.5")
self.assertEqual(a, "10.0.4.5")
self.assertEqual(a, [10, 0, 4, 5])
a = basictypes.build("SNIMPY-MIB", "snimpyIpAddress", b"1001")
self.assertEqual(a, [49, 48, 48, 49])
a = basictypes.build("SNIMPY-MIB", "snimpyIpAddress", b"0101")
self.assertEqual(a, [48, 49, 48, 49])
a = basictypes.build("SNIMPY-MIB", "snimpyIpAddress", "100")
self.assertEqual(a, [0, 0, 0, 100])
def testIncorrectIpAddress(self):
"""Test inappropriate IP addresses"""
self.assertRaises(ValueError,
basictypes.build,
"SNIMPY-MIB", "snimpyIpAddress", "999.5.6.4")
self.assertRaises(ValueError,
basictypes.build,
"SNIMPY-MIB", "snimpyIpAddress", "AAA")
self.assertRaises(ValueError,
basictypes.build,
"SNIMPY-MIB", "snimpyIpAddress", "AAACC")
def testEnum(self):
"""Test enum basic type"""
a = basictypes.build("SNIMPY-MIB", "snimpyEnum", 1)
self.assert_(isinstance(a, basictypes.Enum))
self.assertEqual(a, 1)
self.assertEqual(a, "up")
a = basictypes.build("SNIMPY-MIB", "snimpyEnum", "down")
self.assertEqual(a, "down")
self.assert_(a != "up")
self.assertEqual(a, 2)
self.assertEqual(str(a), "down(2)")
self.assertRaises(ValueError,
basictypes.build,
"SNIMPY-MIB", "snimpyEnum", "unknown")
self.assertEqual(str(a), "down(2)")
a = basictypes.build("SNIMPY-MIB", "snimpyEnum", 54)
self.assertEqual(a, 54)
def testOid(self):
"""Test OID basic type"""
a = basictypes.build("SNIMPY-MIB", "snimpyObjectId",
mib.get("SNIMPY-MIB", "snimpyInteger"))
self.assert_(isinstance(a, basictypes.Oid))
self.assertEqual(a, mib.get("SNIMPY-MIB", "snimpyInteger"))
self.assertEqual(a, mib.get("SNIMPY-MIB", "snimpyInteger").oid)
# Suboid
self.assert_((list(mib.get("SNIMPY-MIB",
"snimpyInteger").oid) + [2, 3]) in a)
self.assert_((list(mib.get("SNIMPY-MIB",
"snimpyInteger").oid)[:-1] +
[29, 3]) not in a)
# Ability to extract a component
self.assertEqual(a[0], 1)
self.assertEqual(a[1], 3)
self.assertEqual(a[-3], 45121)
self.assertEqual(a[-1], 3)
self.assertEqual(a[:3], (1, 3, 6))
# Also accepts list
a = basictypes.build("SNIMPY-MIB", "snimpyObjectId",
(1, 2, 3, 4))
self.assertEqual(a, (1, 2, 3, 4))
self.assert_((1, 2, 3, 4, 5) in a)
self.assert_((3, 4, 5, 6) not in a)
def testBoolean(self):
"""Test boolean basic type"""
a = basictypes.build("SNIMPY-MIB", "snimpyBoolean", True)
self.assert_(isinstance(a, basictypes.Boolean))
self.assertEqual(a, True)
self.assert_(a)
self.assert_(not(not(a)))
self.assertEqual(not(a), False)
a = basictypes.build("SNIMPY-MIB", "snimpyBoolean", "false")
self.assertEqual(a, False)
b = basictypes.build("SNIMPY-MIB", "snimpyBoolean", True)
self.assertEqual(a or b, True)
self.assertEqual(a and b, False)
def testTimeticks(self):
"""Test timeticks basic type"""
a = basictypes.build("SNIMPY-MIB", "snimpyTimeticks", 676544)
self.assert_(isinstance(a, basictypes.Timeticks))
# We can compare to int but otherwise, this is a timedelta
self.assertEqual(a, 676544)
self.assertEqual(str(a), '1:52:45.440000')
self.assertEqual(a, timedelta(0, 6765, 440000))
a = basictypes.build("SNIMPY-MIB", "snimpyTimeticks",
timedelta(1, 3))
self.assertEqual(str(a), '1 day, 0:00:03')
self.assertEqual(a, (3 + 3600 * 24) * 100)
self.assert_(a != (3 + 3600 * 24) * 100 + 1)
self.assert_(a < timedelta(1, 4))
self.assert_(a > timedelta(1, 1))
self.assert_(a > 654)
self.assert_(a >= 654)
self.assert_(a < (3 + 3600 * 24) * 100 + 2)
self.assertEqual(a,
basictypes.build("SNIMPY-MIB", "snimpyTimeticks",
timedelta(1, 3)))
self.assert_(a < basictypes.build("SNIMPY-MIB", "snimpyTimeticks",
timedelta(100, 30)))
def testBits(self):
"""Test bit basic type"""
a = basictypes.build("SNIMPY-MIB", "snimpyBits", [1, 2])
self.assert_(isinstance(a, basictypes.Bits))
self.assertEqual(a, [2, 1])
self.assertEqual(a, (1, 2))
self.assertEqual(a, set([1, 2]))
self.assertEqual(a, ["second", "third"])
self.assertEqual(a, set(["second", "third"]))
self.assertEqual(a, ["second", 2])
self.assert_(a != ["second"])
self.assertFalse(a == ["second"])
self.assertFalse(a != ["second", 2])
a |= "last"
a |= ["last", "second"]
self.assertEqual(a, ["second", "last", "third"])
self.assertEqual(str(a), "second(1), third(2), last(7)")
a -= 1
a -= 1
self.assertEqual(a, ["last", "third"])
self.assertEqual(a & "last", True)
self.assertEqual(a & "second", False)
self.assertEqual(a & ["last", 2], True)
self.assertEqual(a & set(["last", 2]), True)
self.assertEqual(a & ["last", 0], True)
self.assertEqual(a & ["second", 0], False)
a = basictypes.build("SNIMPY-MIB", "snimpyBits",
set(["first", "second"]))
self.assertEqual(a, [0, 1])
a = basictypes.build("SNIMPY-MIB", "snimpyBits", [])
self.assertEqual(a, [])
self.assertEqual(str(a), "")
def testInexistentBits(self):
"""Check we cannot set inexistent bits"""
a = basictypes.build("SNIMPY-MIB", "snimpyBits", [1, 2])
self.assert_(a & 1)
def nope(a):
a |= 3
self.assertRaises(ValueError, nope, a)
def testStringAsBits(self):
"""Test using bit specific operator with string"""
a = basictypes.build(
"SNIMPY-MIB",
"snimpyOctetString",
b"\x17\x00\x01")
self.assert_(isinstance(a, basictypes.OctetString))
b = [7, 6, 5, 3, 23]
for i in range(30):
if i in b:
self.assert_(a & i)
else:
self.assert_(not(a & i))
self.assert_(a & [5, 7])
self.assert_(not(a & [5, 9]))
a |= [2, 10]
a -= 22
a -= [23, 22]
self.assert_(a & [2, 10])
self.assert_(not(a & 23))
self.assertEqual(a, b"\x37\x20\x00")
a |= 31
self.assertEqual(a, b"\x37\x20\x00\x01")
def testPacking(self):
"""Test pack() function"""
self.assertEqual(basictypes.build("SNIMPY-MIB",
"snimpyString",
"Hello world").pack(),
rfc1902.OctetString("Hello world"))
self.assertEqual(basictypes.build("SNIMPY-MIB",
"snimpyInteger",
18).pack(),
rfc1902.Integer(18))
self.assertEqual(basictypes.build("SNIMPY-MIB",
"snimpyInteger",
1804).pack(),
rfc1902.Integer(1804))
self.assertEqual(basictypes.build("SNIMPY-MIB",
"snimpyEnum",
"testing").pack(),
rfc1902.Integer(3))
self.assertEqual(basictypes.build("SNIMPY-MIB",
"snimpyIpAddress",
"10.11.12.13").pack(),
rfc1902.IpAddress("10.11.12.13"))
self.assertEqual(basictypes.build("SNIMPY-MIB",
"snimpyObjectId",
(1, 2, 3, 4)).pack(),
rfc1902.univ.ObjectIdentifier((1, 2, 3, 4)))
self.assertEqual(basictypes.build("SNIMPY-MIB",
"snimpyTimeticks",
timedelta(3, 2)).pack(),
rfc1902.TimeTicks(3 * 3600 * 24 * 100 + 2 * 100))
self.assertEqual(basictypes.build("SNIMPY-MIB",
"snimpyBits",
[1, 7]).pack(),
rfc1902.Bits(b"\x41"))
def testOidConversion(self):
"""Test conversion to/from OID."""
tt = {("snimpySimpleIndex", 47): (47,),
("snimpyComplexFirstIP", "10.14.15.4"): (10, 14, 15, 4),
("snimpyComplexSecondIP", (14, 15, 16, 17)): (14, 15, 16, 17),
("snimpyIndexOidVarLen", (47, 48, 49)): (3, 47, 48, 49),
("snimpyIndexVarLen", "hello1"): tuple([len("hello1")] +
[ord(a)
for a in "hello1"]),
("snimpyIndexFixedLen", "hello2"): tuple(ord(a)
for a in "hello2"),
("snimpyIndexImplied", "hello3"): tuple(ord(a)
for a in "hello3"),
}
for t, v in tt:
oid = basictypes.build("SNIMPY-MIB",
t,
v).toOid()
self.assertEqual(oid,
tt[t, v])
# Test double conversion
self.assertEqual(mib.get("SNIMPY-MIB", t).type.fromOid(
mib.get("SNIMPY-MIB", t), oid),
(len(tt[t, v]), v))
def testTooLargeOid(self):
"""Handle the special case of octet string as OID with too large octets.
See: https://github.com/vincentbernat/snimpy/pull/14
"""
self.assertEqual(mib.get("SNIMPY-MIB",
"snimpyIndexImplied").type.fromOid(
mib.get("SNIMPY-MIB",
"snimpyIndexImplied"),
(104, 0xff00 | 101, 108, 108, 111)),
(5, basictypes.build("SNIMPY-MIB",
"snimpyIndexImplied",
"hello")))
def testOidGreedy(self):
"""Test greediness of fromOid."""
tt = {
"snimpyIndexVarLen":
((5, 104, 101, 108, 108, 111, 111, 111, 111), (6, "hello")),
"snimpyIndexFixedLen":
((104, 101, 108, 108, 111, 49, 49, 111), (6, "hello1")),
"snimpyIndexImplied":
((104, 101, 108, 108, 111, 50), (6, "hello2")),
"snimpyComplexFirstIP":
((15, 15, 16, 100, 23, 74, 87), (4, "15.15.16.100")),
"snimpySimpleIndex": ((17, 19, 20), (1, 17)),
"snimpyIndexOidVarLen": ((3, 247, 145, 475568, 475, 263),
(4, (247, 145, 475568))),
}
for t in tt:
self.assertEqual(mib.get("SNIMPY-MIB", t).type.fromOid(
mib.get("SNIMPY-MIB", t), tt[t][0]),
tt[t][1])
# Test if too short
tt = {"snimpyComplexFirstIP": (17, 19, 20),
"snimpyIndexFixedLen": (104, 101, 108),
"snimpyIndexVarLen": (6, 102, 103, 104, 105),
"snimpyIndexOidVarLen": (3, 247, 145),
}
for t in tt:
self.assertRaises(ValueError,
mib.get("SNIMPY-MIB", t).type.fromOid,
mib.get("SNIMPY-MIB", t), tt[t])
def testDisplay(self):
"""Test string transformation"""
self.assertEqual(str(basictypes.build("SNIMPY-MIB",
"snimpyInteger",
18)), "0.18")
self.assertEqual(str(basictypes.build("SNIMPY-MIB",
"snimpyInteger",
8)), "0.08")
self.assertEqual(str(basictypes.build("SNIMPY-MIB",
"snimpyInteger",
288)), "2.88")
self.assertEqual(str(basictypes.build("SNIMPY-MIB",
"snimpyInteger",
28801)), "288.01")
self.assertEqual(str(basictypes.build("SNIMPY-MIB",
"snimpyString",
"test")), "test")
self.assertEqual(str(basictypes.build("SNIMPY-MIB",
"snimpyOctetString",
b"test")), str(b"test"))
self.assertEqual(str(basictypes.build("SNIMPY-MIB",
"snimpyOctetString",
b"tes\x05")), str(b"tes\x05"))
def testDisplayFormat(self):
"""Test display some with some formats"""
with mock.patch("snimpy.mib.Node.fmt",
new_callable=mock.PropertyMock) as e:
e.return_value = "255a"
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"test")
self.assertEqual(str(a), "test")
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"")
self.assertEqual(str(a), "")
e.return_value = "1x:"
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"test")
self.assertEqual(str(a), "74:65:73:74")
e.return_value = "2a:"
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"test")
self.assertEqual(str(a), "te:st")
e.return_value = "3a:"
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"test")
self.assertEqual(str(a), "tes:t")
e.return_value = "4a"
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"test")
self.assertEqual(str(a), "test")
e.return_value = "2o+1a"
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"test")
self.assertEqual(str(a), "072145+st")
e.return_value = "*2a:+255a"
a = basictypes.build("SNIMPY-MIB", "snimpyString",
b"\x03testtest...")
self.assertEqual(str(a), "te:st:te+st...")
e.return_value = "2a1x:"
a = basictypes.build("SNIMPY-MIB", "snimpyString",
b"aatest")
self.assertEqual(str(a), "aa74:65:73:74")
e.return_value = "*2a+1a:-*3a?="
a = basictypes.build("SNIMPY-MIB", "snimpyString",
b"\x04testtestZ\x02testes\x03testestes")
self.assertEqual(str(a), "te+st+te+st+Z-tes?tes=tes?tes?tes")
def testInputFormat(self):
"""Test we can input a string with a given format"""
with mock.patch("snimpy.mib.Node.fmt",
new_callable=mock.PropertyMock) as e:
e.return_value = "255a"
a = basictypes.build("SNIMPY-MIB", "snimpyString", u"test")
self.assertEqual(a.pack(), b"test")
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"")
self.assertEqual(a.pack(), b"")
e.return_value = "1x:"
a = basictypes.build("SNIMPY-MIB", "snimpyString", u"74:65:73:74")
self.assertEqual(a.pack(), b"test")
a = basictypes.build("SNIMPY-MIB", "snimpyString", u"74:6:73:4")
self.assertEqual(a.pack(), b"t\x06s\x04")
e.return_value = "2a:"
a = basictypes.build("SNIMPY-MIB", "snimpyString", u"te:st")
self.assertEqual(a.pack(), b"test")
e.return_value = "3a:"
a = basictypes.build("SNIMPY-MIB", "snimpyString", u"tes:t")
self.assertEqual(a.pack(), b"test")
e.return_value = "4a"
a = basictypes.build("SNIMPY-MIB", "snimpyString", u"test")
self.assertEqual(a.pack(), b"test")
e.return_value = "2o+1a"
a = basictypes.build("SNIMPY-MIB", "snimpyString", u"072145+st")
self.assertEqual(a.pack(), b"test")
e.return_value = "*2a:+255a"
a = basictypes.build(
"SNIMPY-MIB",
"snimpyString",
u"te:st:te+st...")
self.assertEqual(a.pack(), b"\x03testtest...")
e.return_value = "2a1x:"
a = basictypes.build("SNIMPY-MIB", "snimpyString",
u"aa74:65:73:74")
self.assertEqual(a.pack(), b"aatest")
e.return_value = "*2a+@1a:-*3a?="
a = basictypes.build("SNIMPY-MIB", "snimpyString",
u"te+st+te+st@Z-tes?tes=tes?tes?tes")
self.assertEqual(a.pack(), b"\x04testtestZ\x02testes\x03testestes")
e.return_value = "3a"
a = basictypes.build("SNIMPY-MIB", "snimpyString",
u"a\n\r")
self.assertEqual(a.pack(), b"a\n\r")
def testRepr(self):
"""Test representation"""
self.assertEqual(repr(basictypes.build("SNIMPY-MIB",
"snimpyInteger",
18)), "")
self.assertEqual(repr(basictypes.build("SNIMPY-MIB",
"snimpyObjectId",
(1, 3, 6, 1, 4, 1,
45, 3, 52, 1))),
"")
self.assertEqual(repr(basictypes.build("SNIMPY-MIB",
"snimpyIpAddress",
"124.24.14.3")),
"")
self.assertEqual(repr(basictypes.build("SNIMPY-MIB",
"snimpyString",
"45754dfgf")),
"")
self.assertEqual(repr(basictypes.build("SNIMPY-MIB",
"snimpyEnum",
2)),
"")
self.assertEqual(repr(basictypes.build("SNIMPY-MIB",
"snimpyBoolean",
False)),
"")
self.assertEqual(repr(basictypes.build("SNIMPY-MIB",
"snimpyCounter",
4547)),
"")
self.assertEqual(repr(basictypes.build("SNIMPY-MIB",
"snimpyBits",
["first", "second"])),
"")
def testEqualityWithDisplay(self):
"""Test we can check for equality with displayed form"""
a = basictypes.build("SNIMPY-MIB", "snimpyString", "test")
self.assertEqual(a, "test")
with mock.patch("snimpy.mib.Node.fmt",
new_callable=mock.PropertyMock) as e:
e.return_value = "255a"
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"test")
self.assertEqual(a, "test")
e.return_value = "1x:"
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"test")
self.assertEqual(a, "74:65:73:74")
e.return_value = "2a:"
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"test")
self.assertEqual(a, "te:st")
e.return_value = "3a:"
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"test")
self.assertEqual(a, "tes:t")
e.return_value = "4a"
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"test")
self.assertEqual(a, "test")
e.return_value = "2o+1a"
a = basictypes.build("SNIMPY-MIB", "snimpyString", b"test")
self.assertEqual(a, "072145+st")
self.assertNotEqual(a, "072145+sta")
self.assertFalse(a != "072145+st")
e.return_value = "*2a:+255a"
a = basictypes.build("SNIMPY-MIB",
"snimpyString",
b"\x03testtest...")
self.assertEqual(a, "te:st:te+st...")
def testEqualityUnicode(self):
"""Test that equality works for both unicode and bytes"""
a = basictypes.build("SNIMPY-MIB", "snimpyString", "test")
self.assertEqual(a, "test")
a = basictypes.build("SNIMPY-MIB", "snimpyString", "test")
self.assertEqual(a, u"test")
def testLikeAString(self):
"""Test String is like str"""
a = basictypes.build("SNIMPY-MIB",
"snimpyString",
"4521dgf")
self.assert_(a.startswith("4521"))
self.assertEqual(a.upper(), "4521DGF")
self.assert_(re.match("[0-9]+[defg]+", a))
snimpy-0.8.8/tests/test_conf.py 0000644 0000764 0000144 00000002345 12376564336 017227 0 ustar bernat users 0000000 0000000 import unittest
import os
import tempfile
from snimpy.config import Conf
class TestConf(unittest.TestCase):
"""Test configuration loading"""
def test_default_configuration(self):
"""Check we can load the default configuration"""
conf = Conf()
loaded = conf.load()
self.assertEqual(conf, loaded)
self.assertEqual(conf.mibs, [])
self.assertEqual(conf.ipython, True)
self.assertEqual(conf.prompt, "\033[1m[snimpy]>\033[0m ")
def test_inexistent_configuration(self):
conf = Conf().load("dontexist")
self.assertEqual(conf.mibs, [])
self.assertEqual(conf.ipython, True)
def test_loading_custom_configuration(self):
conffile = tempfile.NamedTemporaryFile(delete=False)
try:
conffile.write("""
mibs = [ "IF-MIB", "LLDP-MIB" ]
ipython = False
unknown = "hey!"
""".encode("ascii"))
conffile.close()
conf = Conf().load(conffile.name)
self.assertEqual(conf.mibs, ["IF-MIB", "LLDP-MIB"])
self.assertEqual(conf.unknown, "hey!")
self.assertEqual(conf.ipython, False)
self.assertEqual(conf.ipythonprofile, None)
finally:
os.unlink(conffile.name)
snimpy-0.8.8/tests/test_manager.py 0000644 0000764 0000144 00000035672 12622122776 017715 0 ustar bernat users 0000000 0000000 import sys
if sys.version_info < (2, 7):
import unittest2 as unittest
else:
import unittest
import os
import time
from datetime import timedelta
from snimpy.manager import load, Manager, snmp
import agent
class TestManager(unittest.TestCase):
@classmethod
def setUpClass(cls):
load('IF-MIB')
load('SNMPv2-MIB')
load(os.path.join(os.path.dirname(os.path.abspath(__file__)),
"SNIMPY-MIB.mib"))
cls.agent = agent.TestAgent()
@classmethod
def tearDownClass(cls):
cls.agent.terminate()
def setUp(self):
self.manager = Manager(host="127.0.0.1:{0}".format(self.agent.port),
community="public",
version=2)
self.session = self.manager._session
class TestManagerGet(TestManager):
"""Test getting stuff from manager"""
def testGetScalar(self):
"""Retrieve some simple scalar values"""
self.assertEqual(self.manager.sysDescr, "Snimpy Test Agent public")
self.assertEqual(self.manager.ifNumber, 3)
def scalarGetAndCheck(self, name, value):
self.assertEqual(getattr(self.manager, name),
value)
def testScalar_IpAddress(self):
"""Retrieve IpAdress as a scalar"""
self.scalarGetAndCheck("snimpyIpAddress", "65.65.65.65")
def testScalar_String(self):
"""Retrieve a String as a scalar"""
self.scalarGetAndCheck("snimpyString", "bye")
def testScalar_Integer(self):
"""Retrieve an Integer as a scalar"""
self.scalarGetAndCheck("snimpyInteger", 19)
def testScalar_Enum(self):
"""Retrieve an Enum as a scalar"""
self.scalarGetAndCheck("snimpyEnum", "down")
def testScalar_ObjectId(self):
"""Retrieve an ObjectId as a scalar"""
self.scalarGetAndCheck("snimpyObjectId", (1, 3, 6, 4454, 0, 0))
def testScalar_Boolean(self):
"""Retrieve a Boolean as a scalar"""
self.scalarGetAndCheck("snimpyBoolean", True)
def testScalar_Counter(self):
"""Retrieve a Counter as a scalar"""
self.scalarGetAndCheck("snimpyCounter", 47)
self.scalarGetAndCheck("snimpyCounter64", 2 ** 48 + 3)
def testScalar_Gauge(self):
"""Retrieve a Gauge as a scalar"""
self.scalarGetAndCheck("snimpyGauge", 18)
def testScalar_Timeticks(self):
"""Retrieve a TimeTicks as a scalar"""
self.scalarGetAndCheck(
"snimpyTimeticks",
timedelta(days=1,
hours=9,
minutes=38,
seconds=31))
def testScalar_Bits(self):
"""Retrieve Bits as a scalar"""
self.scalarGetAndCheck("snimpyBits", ["first", "third"])
def testScalar_MacAddress(self):
"""Retrieve MacAddress as a scalar"""
self.scalarGetAndCheck("snimpyMacAddress", "11:12:13:14:15:16")
def testWalkIfTable(self):
"""Test we can walk IF-MIB::ifTable"""
results = [(idx, self.manager.ifDescr[idx], self.manager.ifType[idx])
for idx in self.manager.ifIndex]
self.assertEqual(results,
[(1, "lo", 24),
(2, "eth0", 6),
(3, "eth1", 6)])
def testWalkIfTableWithoutBulk(self):
"""Walk IF-MIB::ifTable without GETBULK"""
self.session.bulk = False
self.testWalkIfTable()
def testWalkComplexIndexes(self):
"""Test if we can walk a table with complex indexes"""
results = [(idx, self.manager.snimpyIndexInt[idx])
for idx in self.manager.snimpyIndexInt]
self.assertEqual(results,
[(("row1", (1, 2, 3),
"alpha5", "end of row1"), 4571),
(("row2", (1, 0, 2, 3),
"beta32", "end of row2"), 78741),
(("row3", (120, 1, 2, 3),
"gamma7", "end of row3"), 4110)])
def testGetInexistentStuff(self):
"""Try to access stuff that does not exist on the agent"""
self.assertRaises(snmp.SNMPNoSuchObject,
getattr, self.manager, "snimpyNotImplemented")
self.assertRaises(snmp.SNMPNoSuchObject,
self.manager.ifName.__getitem__, 47)
self.assertRaises(snmp.SNMPNoSuchInstance,
self.manager.ifDescr.__getitem__, 47)
def testAccessInexistentStuff(self):
"""Try to access stuff that don't exist in MIB"""
self.assertRaises(AttributeError,
getattr, self.manager, "iDoNotExist")
def testAccessIncorrectIndex(self):
"""Try to access with incorrect indexes"""
self.assertRaises(ValueError,
self.manager.ifDescr.__getitem__, (47, 18))
self.assertRaises(ValueError,
self.manager.ifDescr.__getitem__, "nothing")
def testGetChangingStuff(self):
"""Get stuff with varying values"""
initial = self.manager.ifInOctets[2]
current = self.manager.ifInOctets[2]
self.assertGreater(current, initial)
class TestManagerRestrictModule(TestManager):
"""Test when we restrict modules to be used by a manager"""
def testGetSpecificModule(self):
"""Get a scalar from a specific module only"""
self.assertEqual(self.manager['IF-MIB'].ifNumber, 3)
self.assertEqual(self.manager['SNMPv2-MIB'].sysDescr,
"Snimpy Test Agent public")
def testGetInexistentModule(self):
"""Get a scalar from a non loaded module"""
self.assertRaises(KeyError, lambda: self.manager['IF-MIB2'])
def testGetInexistentScalarFromModule(self):
"""Get a non-existent scalar from a specific module"""
self.assertRaises(AttributeError,
lambda: self.manager['IF-MIB'].sysDescr)
class TestManagerSet(TestManager):
"""Test setting stuff from manager"""
def testSetScalar(self):
"""Try to set a simple value"""
self.manager.snimpyString = "hello"
self.assertEqual(self.manager.snimpyString, "hello")
def scalarSetAndCheck(self, name, value):
setattr(self.manager, name, value)
self.assertEqual(getattr(self.manager, name),
value)
def testScalar_IpAddress(self):
"""Retrieve IpAdress as a scalar"""
self.scalarSetAndCheck("snimpyIpAddress", "165.255.65.65")
def testScalar_String(self):
"""Retrieve a String as a scalar"""
self.scalarSetAndCheck("snimpyString", "awesome !!!")
def testScalar_Integer(self):
"""Retrieve an Integer as a scalar"""
self.scalarSetAndCheck("snimpyInteger", 1900)
def testScalar_Enum(self):
"""Retrieve an Enum as a scalar"""
self.scalarSetAndCheck("snimpyEnum", "up")
def testScalar_ObjectId(self):
"""Retrieve an ObjectId as a scalar"""
self.scalarSetAndCheck("snimpyObjectId", (1, 3, 6, 4454, 19, 47))
def testScalar_Boolean(self):
"""Retrieve a Boolean as a scalar"""
self.scalarSetAndCheck("snimpyBoolean", False)
def testScalar_Counter(self):
"""Retrieve a Counter as a scalar"""
self.scalarSetAndCheck("snimpyCounter", 4700)
self.scalarSetAndCheck("snimpyCounter64", 2 ** 48 + 3 - 18)
def testScalar_Gauge(self):
"""Retrieve a Gauge as a scalar"""
self.scalarSetAndCheck("snimpyGauge", 180014)
def testScalar_Timeticks(self):
"""Retrieve a TimeTicks as a scalar"""
self.scalarSetAndCheck(
"snimpyTimeticks",
timedelta(days=1,
hours=17,
minutes=38,
seconds=31))
def testScalar_Bits(self):
"""Retrieve Bits as a scalar"""
self.scalarSetAndCheck("snimpyBits", ["first", "second"])
def testScalar_MacAddress(self):
"""Retrieve MAC address as a scala"""
self.scalarSetAndCheck("snimpyMacAddress", "a0:b0:c0:d0:e:ff")
def testNonScalarSet(self):
"""Check we can set a non-scalar value"""
idx = ("row2", (1, 0, 2, 3), "beta32", "end of row2")
self.manager.snimpyIndexInt[idx] = 1041
self.assertEqual(self.manager.snimpyIndexInt[idx], 1041)
def testSetWithContext(self):
"""Set several values atomically (inside a context)"""
with self.manager as m:
m.snimpyString = "Noooooo!"
m.snimpyInteger = 42
self.assertEqual(m.snimpyString, "Noooooo!")
self.assertEqual(m.snimpyInteger, 42)
def testSetWithContextAndAbort(self):
"""Check if writing several values atomically can be aborted"""
try:
with self.manager as m:
m.snimpyString = "Abort sir!"
m.snimpyInteger = 37
raise RuntimeError("Abort now!")
except RuntimeError as e:
self.assertEqual(str(e), "Abort now!")
self.assertNotEqual(m.snimpyString, "Abort sir!")
self.assertNotEqual(m.snimpyInteger, 37)
def testSetInexistentStuff(self):
"""Try to access stuff that does not exist on the agent"""
self.assertRaises(snmp.SNMPNotWritable,
setattr, self.manager, "snimpyNotImplemented",
"Hello")
self.assertRaises(snmp.SNMPNotWritable,
self.manager.ifName.__setitem__, 47, "Wouh")
self.assertRaises(snmp.SNMPNotWritable,
self.manager.ifDescr.__setitem__, 47, "Noooo")
def testAccessInexistentStuff(self):
"""Try to access stuff that don't exist in MIB"""
self.assertRaises(AttributeError,
setattr, self.manager, "iDoNotExist", 47)
def testAccessIncorrectIndex(self):
"""Try to access with incorrect indexes"""
self.assertRaises(ValueError,
self.manager.ifDescr.__setitem__, (47, 18), "Nooo")
self.assertRaises(ValueError,
self.manager.ifDescr.__setitem__,
"nothing", "Neither")
class TestManagerWithNone(TestManagerGet):
"""Test a manager answering None for inexistent stuff"""
def setUp(self):
self.manager = Manager(host="127.0.0.1:{0}".format(self.agent.port),
community="public",
version=2, none=True)
self.session = self.manager._session._session
def testGetInexistentStuff(self):
"""Try to access stuff that does not exist on the agent"""
self.assertEqual(self.manager.snimpyNotImplemented, None)
self.assertEqual(self.manager.ifName[47], None)
self.assertEqual(self.manager.ifDescr[47], None)
class TestCachingManager(TestManagerGet):
"""Test if caching manager works like regular manager"""
def setUp(self):
self.manager = Manager(host="127.0.0.1:{0}".format(self.agent.port),
community="public",
version=2, cache=1)
self.session = self.manager._session._session
def testGetChangingStuff(self):
"""Get stuff with varying values"""
initial = self.manager.ifInOctets[2]
current = self.manager.ifInOctets[2]
self.assertEqual(current, initial)
def testCacheFlush(self):
"""Test cache timeout is working as expected"""
first1 = self.manager.ifInOctets[1]
second1 = self.manager.ifInOctets[2]
third1 = self.manager.ifInOctets[3]
time.sleep(0.5)
second2 = self.manager.ifInOctets[2]
third2 = self.manager.ifInOctets[3]
self.assertEqual(second1, second2) # timeout not reached
self.assertEqual(third1, third2) # timeout not reached
time.sleep(1)
first2 = self.manager.ifInOctets[1]
self.assertGreater(first2, first1) # timeout was reached
class TestCachingManagerWithModificatons(TestManager):
"""Test if caching manager works with modifications"""
def setUp(self):
self.manager = Manager(host="127.0.0.1:{0}".format(self.agent.port),
community="public",
version=2, cache=1)
self.session = self.manager._session._session
def testCacheScalar(self):
"""Check that a scalar value is kept in cache"""
original = self.manager.snimpyString
self.manager.snimpyString = "Nooooo"
self.assertEqual(self.manager.snimpyString, original)
def testCacheNonScalar(self):
"""Check we can cache a non-scalar value"""
idx = ("row2", (1, 0, 2, 3), "beta32", "end of row2")
original = self.manager.snimpyIndexInt[idx]
self.manager.snimpyIndexInt[idx] = 1041
self.assertEqual(self.manager.snimpyIndexInt[idx], original)
def testCacheExpire(self):
"""Check the cache can expire"""
self.manager.snimpyString = "Yeesss"
time.sleep(1)
self.assertEqual(self.manager.snimpyString, "Yeesss")
class TestManagerInvalidValues(TestManager):
"""Test when the agent is returning invalid values"""
def testInvalidValue(self):
"""Check if an invalid value raises an exception"""
self.assertRaises(ValueError,
getattr, self.manager,
"snimpyMacAddressInvalid")
def testInvalidValueInTable(self):
"""Check if an invalid value in a table raises an exception"""
self.assertRaises(ValueError,
self.manager.snimpyInvalidDescr.__getitem__,
2)
def testInvalidValueWhileIterating(self):
"""Check if an invalid value while walking raises an exception"""
self.assertRaises(ValueError,
list,
self.manager.snimpyInvalidDescr.iteritems())
class TestManagerLoose(TestManager):
"""Test when the agent is returning invalid values with loose mode"""
def setUp(self):
self.manager = Manager(host="127.0.0.1:{0}".format(self.agent.port),
community="public",
version=2, loose=True)
self.session = self.manager._session
def testInvalidValue(self):
"""Check if an invalid value is correctly returned"""
self.assertEqual(self.manager.snimpyMacAddressInvalid,
b"\xf1\x12\x13\x14\x15\x16")
def testInvalidValueInTable(self):
"""Check if an invalid value in a table is correctly returned"""
self.assertEqual(self.manager.snimpyInvalidDescr[1],
"Hello")
self.assertEqual(self.manager.snimpyInvalidDescr[2],
b"\xf1\x12\x13\x14\x15\x16")
def testInvalidValueWhileIterating(self):
"""Check if an invalid value while walking works"""
self.assertEqual(list(self.manager.snimpyInvalidDescr.iteritems()),
[(1, "Hello"),
(2, b"\xf1\x12\x13\x14\x15\x16")])
snimpy-0.8.8/tests/SNIMPY-INVALID-MIB.mib 0000644 0000764 0000144 00000001160 12326014751 020046 0 ustar bernat users 0000000 0000000 SNIMPY-INVALID-MIB DEFINITIONS ::= BEGIN
IMPORTS
inexistentNode FROM INEXISTENT-SNIMPY-MIB ;
invalidSnimpy MODULE-IDENTITY
LAST-UPDATED "200809160000Z"
ORGANIZATION
"snimpy
https://github.com/vincentbernat/snimpy"
CONTACT-INFO
"Lorem ipsum, etc, etc."
DESCRIPTION
"This is a test MIB module for snimpy."
REVISION "200809160000Z"
DESCRIPTION "Last revision"
::= { mib-2 45122 }
invalidSnimpyNode OBJECT-TYPE
SYNTAX INTEGER
MAX-ACCESS read-only
STATUS current
DESCRIPTION
"An integer"
::= { inexistentNode 1 }
END
snimpy-0.8.8/tests/test_main.py 0000644 0000764 0000144 00000002275 12600052762 017211 0 ustar bernat users 0000000 0000000 import sys
if sys.version_info < (2, 7):
import unittest2 as unittest
else:
import unittest
import os
import tempfile
import code # nopep8
import mock
import platform
from snimpy.main import interact
from multiprocessing import Process
import agent
class TestMain(unittest.TestCase):
"""Test the main shell"""
@classmethod
def setUpClass(cls):
cls.agent = agent.TestAgent()
@classmethod
def tearDownClass(cls):
cls.agent.terminate()
@unittest.skipIf(platform.python_implementation() == "PyPy",
"setupterm seems unreliable with Pypy")
def test_loadfile(self):
script = tempfile.NamedTemporaryFile(delete=False)
try:
script.write("""
load("IF-MIB")
m = M(host="127.0.0.1:{0}",
community="public",
version=2)
assert(m.ifDescr[1] == "lo")
""".format(self.agent.port).encode("ascii"))
script.close()
with mock.patch("code.InteractiveInterpreter.write"):
p = Process(target=interact, args=((script.name,),))
p.start()
p.join()
self.assertEqual(p.exitcode, 0)
finally:
os.unlink(script.name)
snimpy-0.8.8/tests/test_mib.py 0000644 0000764 0000144 00000042205 12613477065 017044 0 ustar bernat users 0000000 0000000 import unittest
import os
import sys
from snimpy import mib, basictypes
PYTHON3 = sys.version_info >= (3, 0)
if PYTHON3:
unicode = str
class TestMibSnimpy(unittest.TestCase):
def setUp(self):
mib.load(os.path.join(os.path.dirname(os.path.abspath(__file__)),
"SNIMPY-MIB.mib"))
self.nodes = ["snimpy",
"snimpyScalars",
"snimpyTables"]
self.nodes.sort()
self.tables = ["snimpyComplexTable",
"snimpyInetAddressTable",
"snimpySimpleTable",
"snimpyIndexTable",
"snimpyInvalidTable"]
self.tables.sort()
self.columns = ["snimpyComplexFirstIP",
"snimpyComplexSecondIP",
"snimpySimpleIndex",
"snimpyComplexState",
"snimpyInetAddressType",
"snimpyInetAddress",
"snimpyInetAddressState",
"snimpySimpleDescr",
"snimpySimplePhys",
"snimpySimpleType",
"snimpyIndexVarLen",
"snimpyIndexIntIndex",
"snimpyIndexOidVarLen",
"snimpyIndexFixedLen",
"snimpyIndexImplied",
"snimpyIndexInt",
"snimpyInvalidIndex",
"snimpyInvalidDescr"
]
self.columns.sort()
self.scalars = ["snimpyIpAddress",
"snimpyString",
"snimpyInteger",
"snimpyEnum",
"snimpyObjectId",
"snimpyBoolean",
"snimpyCounter",
"snimpyGauge",
"snimpyTimeticks",
"snimpyCounter64",
"snimpyBits",
"snimpyNotImplemented",
"snimpyOctetString",
"snimpyUnicodeString",
"snimpyMacAddress",
"snimpyMacAddressInvalid"]
self.scalars.sort()
self.expected_modules = [u"SNMPv2-SMI",
u"SNMPv2-TC",
u"SNIMPY-MIB",
u"INET-ADDRESS-MIB",
u"IANAifType-MIB"]
def tearDown(self):
mib.reset()
def testGetNodes(self):
"""Test that we can get all nodes"""
nodes = mib.getNodes('SNIMPY-MIB')
snodes = sorted([str(a) for a in nodes])
self.assertEqual(self.nodes,
snodes)
for n in nodes:
self.assert_(isinstance(n, mib.Node))
def testGetTables(self):
"""Test that we can get all tables"""
tables = mib.getTables('SNIMPY-MIB')
stables = sorted([str(a) for a in tables])
self.assertEqual(self.tables,
stables)
for n in tables:
self.assert_(isinstance(n, mib.Table))
def testGetColumns(self):
"""Test that we can get all columns"""
columns = mib.getColumns('SNIMPY-MIB')
scolumns = sorted([str(a) for a in columns])
self.assertEqual(self.columns,
scolumns)
for n in columns:
self.assert_(isinstance(n, mib.Column))
def testGetScalars(self):
"""Test that we can get all scalars"""
scalars = mib.getScalars('SNIMPY-MIB')
sscalars = sorted([str(a) for a in scalars])
self.assertEqual(self.scalars, sscalars)
for n in scalars:
self.assert_(isinstance(n, mib.Scalar))
def testGet(self):
"""Test that we can get all named attributes"""
for i in self.scalars:
self.assertEqual(str(mib.get('SNIMPY-MIB', i)), i)
self.assert_(isinstance(mib.get('SNIMPY-MIB', i), mib.Scalar))
for i in self.tables:
self.assertEqual(str(mib.get('SNIMPY-MIB', i)), i)
self.assert_(isinstance(mib.get('SNIMPY-MIB', i), mib.Table))
for i in self.columns:
self.assertEqual(str(mib.get('SNIMPY-MIB', i)), i)
self.assert_(isinstance(mib.get('SNIMPY-MIB', i), mib.Column))
for i in self.nodes:
self.assertEqual(str(mib.get('SNIMPY-MIB', i)), i)
self.assert_(isinstance(mib.get('SNIMPY-MIB', i), mib.Node))
def testGetByOid(self):
"""Test that we can get all named attributes by OID."""
for i in self.scalars:
nodebyname = mib.get('SNIMPY-MIB', i)
self.assertEqual(str(mib.getByOid(nodebyname.oid)), i)
self.assert_(isinstance(mib.getByOid(nodebyname.oid), mib.Scalar))
for i in self.tables:
nodebyname = mib.get('SNIMPY-MIB', i)
self.assertEqual(str(mib.getByOid(nodebyname.oid)), i)
self.assert_(isinstance(mib.getByOid(nodebyname.oid), mib.Table))
for i in self.columns:
nodebyname = mib.get('SNIMPY-MIB', i)
self.assertEqual(str(mib.getByOid(nodebyname.oid)), i)
self.assert_(isinstance(mib.getByOid(nodebyname.oid), mib.Column))
for i in self.nodes:
nodebyname = mib.get('SNIMPY-MIB', i)
self.assertEqual(str(mib.getByOid(nodebyname.oid)), i)
self.assert_(isinstance(mib.getByOid(nodebyname.oid), mib.Node))
def testGetByOid_UnknownOid(self):
"""Test that unknown OIDs raise an exception."""
self.assertRaises(mib.SMIException, mib.getByOid, (255,))
def testGetType(self):
"""Test that _getType properly identifies known and unknown types."""
self.assertEqual(b"PhysAddress",
mib.ffi.string(mib._getType("PhysAddress").name))
self.assertEqual(b"InetAddress",
mib.ffi.string(mib._getType(b"InetAddress").name))
self.assertEqual(None, mib._getType("SomeUnknownType.kjgf"))
self.assertEqual(None, mib._getType("snimpySimpleTable"))
def testTableColumnRelation(self):
"""Test that we can get the column from the table and vice-versa"""
for i in self.tables:
table = mib.get('SNIMPY-MIB', i)
for r in table.columns:
self.assert_(isinstance(r, mib.Column))
self.assertEqual(str(r.table), str(i))
self.assert_(str(r).startswith(str(i).replace("Table", "")))
columns = sorted([str(rr)
for rr in self.columns
if str(rr).startswith(str(i).replace("Table",
""))])
tcolumns = [str(rr) for rr in table.columns]
tcolumns.sort()
self.assertEqual(columns, tcolumns)
for r in self.columns:
column = mib.get('SNIMPY-MIB', r)
table = column.table
self.assert_(isinstance(table, mib.Table))
prefix = str(table).replace("Table", "")
self.assertEqual(prefix, str(r)[:len(prefix)])
def testTypes(self):
"""Test that we get the correct types"""
tt = {"snimpyIpAddress": basictypes.IpAddress,
"snimpyString": basictypes.OctetString,
"snimpyOctetString": basictypes.OctetString,
"snimpyUnicodeString": basictypes.OctetString,
"snimpyMacAddress": basictypes.OctetString,
"snimpyInteger": basictypes.Integer,
"snimpyEnum": basictypes.Enum,
"snimpyObjectId": basictypes.Oid,
"snimpyBoolean": basictypes.Boolean,
"snimpyCounter": basictypes.Unsigned32,
"snimpyGauge": basictypes.Unsigned32,
"snimpyTimeticks": basictypes.Timeticks,
"snimpyCounter64": basictypes.Unsigned64,
"snimpyBits": basictypes.Bits,
"snimpySimpleIndex": basictypes.Integer,
"snimpyComplexFirstIP": basictypes.IpAddress,
"snimpyComplexSecondIP": basictypes.IpAddress,
"snimpyComplexState": basictypes.Enum}
for t in tt:
self.assertEqual(mib.get('SNIMPY-MIB', t).type, tt[t])
# Also check we get an exception when no type available
def call():
mib.get('SNIMPY-MIB', 'snimpySimpleTable').type
self.assertRaises(mib.SMIException, call)
def testRanges(self):
tt = {"snimpyIpAddress": 4,
"snimpyString": (0, 255),
"snimpyOctetString": None,
"snimpyInteger": [(6, 18), (20, 23), (27, 1336)],
"snimpyEnum": None,
"snimpyObjectId": None,
"snimpyBoolean": None,
"snimpyCounter": (0, 4294967295),
"snimpyGauge": (0, 4294967295),
"snimpyTimeticks": (0, 4294967295),
"snimpyCounter64": (0, 18446744073709551615),
"snimpyBits": None,
"snimpySimpleIndex": (1, 30),
"snimpyComplexFirstIP": 4,
"snimpyComplexSecondIP": 4,
"snimpyComplexState": None
}
for t in tt:
self.assertEqual(mib.get('SNIMPY-MIB', t).ranges, tt[t])
def testEnums(self):
"""Test that we got the enum values correctly"""
self.assertEqual(mib.get('SNIMPY-MIB', "snimpyInteger").enum, None)
self.assertEqual(mib.get("SNIMPY-MIB", "snimpyEnum").enum,
{1: "up",
2: "down",
3: "testing"})
self.assertEqual(mib.get("SNIMPY-MIB", "snimpyBits").enum,
{0: "first",
1: "second",
2: "third",
7: "last"})
def testIndexes(self):
"""Test that we can retrieve correctly the index of tables"""
self.assertEqual(
[str(i) for i in mib.get("SNIMPY-MIB", "snimpySimpleTable").index],
["snimpySimpleIndex"])
self.assertEqual(
[str(i)
for i in mib.get("SNIMPY-MIB", "snimpyComplexTable").index],
["snimpyComplexFirstIP", "snimpyComplexSecondIP"])
self.assertEqual(
[str(i)
for i in mib.get("SNIMPY-MIB", "snimpyInetAddressTable").index],
["snimpyInetAddressType", "snimpyInetAddress"])
def testImplied(self):
"""Check that we can get implied attribute for a given table"""
self.assertEqual(
mib.get("SNIMPY-MIB",
'snimpySimpleTable').implied,
False)
self.assertEqual(
mib.get("SNIMPY-MIB",
'snimpyComplexTable').implied,
False)
self.assertEqual(
mib.get("SNIMPY-MIB",
'snimpyIndexTable').implied,
True)
def testOid(self):
"""Test that objects are rooted at the correct OID"""
oids = {"snimpy": (1, 3, 6, 1, 2, 1, 45121),
"snimpyScalars": (1, 3, 6, 1, 2, 1, 45121, 1),
"snimpyString": (1, 3, 6, 1, 2, 1, 45121, 1, 2),
"snimpyInteger": (1, 3, 6, 1, 2, 1, 45121, 1, 3),
"snimpyBits": (1, 3, 6, 1, 2, 1, 45121, 1, 11),
"snimpyTables": (1, 3, 6, 1, 2, 1, 45121, 2),
"snimpySimpleTable": (1, 3, 6, 1, 2, 1, 45121, 2, 1),
"snimpySimplePhys": (1, 3, 6, 1, 2, 1, 45121, 2, 1, 1, 4),
"snimpyComplexTable": (1, 3, 6, 1, 2, 1, 45121, 2, 2),
"snimpyComplexState": (1, 3, 6, 1, 2, 1, 45121, 2, 2, 1, 3),
}
for o in oids:
self.assertEqual(mib.get('SNIMPY-MIB', o).oid, oids[o])
def testLoadedMibNames(self):
"""Check that only expected modules were loaded."""
for module in self.expected_modules:
self.assertTrue(module in list(mib.loadedMibNames()))
def testLoadInexistantModule(self):
"""Check that we get an exception when loading an inexistant module"""
self.assertRaises(mib.SMIException, mib.load, "idontexist.gfdgfdg")
def testLoadInvalidModule(self):
"""Check that an obviously invalid module cannot be loaded"""
path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"SNIMPY-INVALID-MIB.mib")
self.assertRaises(mib.SMIException, mib.load, path)
self.assertRaises(mib.SMIException, mib.getNodes, "SNIMPY-INVALID-MIB")
self.assertRaises(mib.SMIException, mib.get,
"SNIMPY-INVALID-MIB", "invalidSnimpyNode")
def testAccesInexistantModule(self):
"""Check that we get an exception when querying inexistant module"""
self.assertRaises(mib.SMIException, mib.getNodes, "idontexist.kjgf")
self.assertRaises(mib.SMIException, mib.getScalars, "idontexist.kjgf")
self.assertRaises(mib.SMIException, mib.getTables, "idontexist.kjgf")
self.assertRaises(mib.SMIException, mib.getColumns, "idontexist.kjgf")
def testFmt(self):
"""Check that we get FMT from types"""
self.assertEqual(mib.get("SNIMPY-MIB", 'snimpySimplePhys').fmt, "1x:")
self.assertEqual(mib.get("SNIMPY-MIB", 'snimpyInteger').fmt, "d-2")
def testTypeOverrides(self):
"""Check that we can override a type"""
table = mib.get("SNIMPY-MIB", "snimpyInetAddressTable")
addrtype_attr = table.index[0]
addr_attr = table.index[1]
# Try overriding to IPv4 with a byte string name.
addrtype = addrtype_attr.type(addrtype_attr, "ipv4")
self.assertEqual(addrtype, "ipv4")
addr_attr.typeName = b"InetAddressIPv4"
ipv4 = u"127.0.0.1"
ipv4_oid = (4, 127, 0, 0, 1)
addr = addr_attr.type(addr_attr, ipv4)
self.assertEqual(str(addr), ipv4)
self.assertEqual(addr.toOid(), ipv4_oid)
addr_len, addr = addr_attr.type.fromOid(addr_attr, ipv4_oid)
self.assertEqual(addr_len, ipv4_oid[0] + 1)
self.assertEqual(str(addr), ipv4)
self.assertEqual(addr.toOid(), ipv4_oid)
# Try both IPv6 and non-bytes name.
addrtype = addrtype_attr.type(addrtype_attr, "ipv6")
self.assertEqual(addrtype, "ipv6")
addr_attr.typeName = u"InetAddressIPv6"
# Snimpy does not use leading zeroes.
ipv6 = u'102:304:506:708:90a:b0c:d0e:f01'
ipv6_oid = (16, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x01)
addr = addr_attr.type(addr_attr, ipv6)
self.assertEqual(str(addr), ipv6)
self.assertEqual(addr.toOid(), ipv6_oid)
addr_len, addr = addr_attr.type.fromOid(addr_attr, ipv6_oid)
self.assertEqual(addr_len, ipv6_oid[0] + 1)
self.assertEqual(str(addr), ipv6)
self.assertEqual(addr.toOid(), ipv6_oid)
# Try a type from a different module (chosen because snmpwalk
# prints IPv6 addresses incorrectly).
ipv6_1xformat = u'1:2:3:4:5:6:7:8:9:a:b:c:d:e:f:1'
addr_attr.typeName = "PhysAddress"
addr = addr_attr.type(addr_attr, ipv6_1xformat)
self.assertEqual(str(addr), ipv6_1xformat)
self.assertEqual(addr.toOid(), ipv6_oid)
# Try overriding back to the default.
del addr_attr.typeName
addr_len, addr = addr_attr.type.fromOid(addr_attr, ipv4_oid)
self.assertEqual(bytes(addr), b"\x7f\0\0\1")
def testTypeOverrides_Errors(self):
table = mib.get("SNIMPY-MIB", "snimpyInetAddressTable")
attr = table.index[1]
# Value with the wrong type.
self.assertRaises(AttributeError, setattr, attr, "typeName", None)
# Unknown type.
self.assertRaises(mib.SMIException, setattr, attr, "typeName",
"SomeUnknownType.kjgf")
# Incompatible basetype.
self.assertRaises(mib.SMIException, setattr, attr, "typeName",
"InetAddressType")
# Parse error.
attr.typeName = "InetAddressIPv4"
self.assertRaises(ValueError, attr.type, attr, u"01:02:03:04")
def testTypeName(self):
"""Check that we can get the current declared type name"""
table = mib.get("SNIMPY-MIB", "snimpyInetAddressTable")
attr = table.index[1]
self.assertEqual(attr.typeName, b"InetAddress")
attr.typeName = b"InetAddressIPv4"
self.assertEqual(attr.typeName, b"InetAddressIPv4")
attr.typeName = b"InetAddressIPv6"
self.assertEqual(attr.typeName, b"InetAddressIPv6")
attr = mib.get("SNIMPY-MIB", "snimpySimpleIndex")
self.assertEqual(attr.typeName, b"Integer32")
class TestSmi(unittest.TestCase):
def testGetPath(self):
"""Test we can get default SMI path"""
current = mib.path()
self.assertTrue(type(current), unicode)
self.assertNotEqual(mib.path(), u"")
def testSetPath(self):
"""Test we can set path to some value"""
original = mib.path()
current = original + ":/some/other/directory"
try:
mib.path(current)
self.assertEqual(mib.path(), current)
finally:
mib.path(original)
snimpy-0.8.8/tests/test_snmp.py 0000644 0000764 0000144 00000035156 12622134340 017243 0 ustar bernat users 0000000 0000000 import unittest
import os
import threading
import multiprocessing
from datetime import timedelta
from snimpy import basictypes, snmp, mib
import agent
class TestSnmpRetriesTimeout(unittest.TestCase):
"""Live modification of retry and timeout values for a session"""
def setUp(self):
self.session = snmp.Session(host="localhost",
community="public",
version=2)
def testGetRetries(self):
"""Get default retries value"""
self.assertEqual(self.session.retries, 5)
def testGetTimeout(self):
"""Get default timeout value"""
self.assertEqual(self.session.timeout, 1000000)
def testSetRetries(self):
"""Try to set a new retry value"""
self.session.retries = 2
self.assertEqual(self.session.retries, 2)
self.session.retries = 0
self.assertEqual(self.session.retries, 0)
def testSetTimeout(self):
"""Try to set a new timeout value"""
self.session.timeout = 500000
self.assertEqual(self.session.timeout, 500000)
def testErrors(self):
"""Try invalid values for timeout and retries"""
self.assertRaises(ValueError, setattr, self.session, "timeout", 0)
self.assertRaises(ValueError, setattr, self.session, "timeout", -30)
self.assertRaises(ValueError, setattr, self.session, "retries", -5)
class TestSnmpSession(unittest.TestCase):
"""Test for session creation using SNMPv1/v2c/v3"""
def testSnmpV1(self):
"""Check initialization of SNMPv1 session"""
snmp.Session(host="localhost",
community="public",
version=1)
def testSnmpV2(self):
"""Check initialization of SNMPv2 session"""
snmp.Session(host="localhost",
community="public",
version=2)
def testSnmpV3(self):
"""Check initialization of SNMPv3 session"""
snmp.Session(host="localhost",
version=3,
secname="readonly",
authprotocol="MD5", authpassword="authpass",
privprotocol="AES", privpassword="privpass")
def testSnmpV3Protocols(self):
"""Check accepted auth and privacy protocols"""
for auth in ["MD5", "SHA"]:
for priv in ["AES", "AES128", "DES"]:
snmp.Session(host="localhost",
version=3,
secname="readonly",
authprotocol=auth, authpassword="authpass",
privprotocol=priv, privpassword="privpass")
self.assertRaises(ValueError,
snmp.Session,
host="localhost",
version=3,
secname="readonly",
authprotocol="NOEXIST", authpassword="authpass",
privprotocol="AES", privpassword="privpass")
self.assertRaises(ValueError,
snmp.Session,
host="localhost",
version=3,
secname="readonly",
authprotocol="MD5", authpassword="authpass",
privprotocol="NOEXIST", privpassword="privpass")
def testRepresentation(self):
"""Test session representation"""
s = snmp.Session(host="localhost",
community="public",
version=1)
self.assertEqual(repr(s), "Session(host=localhost,version=1)")
def testSnmpV3SecLevels(self):
"""Check accepted security levels"""
auth = "MD5"
priv = "DES"
snmp.Session(host="localhost",
version=3,
secname="readonly",
authprotocol=auth, authpassword="authpass",
privprotocol=priv, privpassword="privpass")
snmp.Session(host="localhost",
version=3,
secname="readonly",
authprotocol=None,
privprotocol=None)
snmp.Session(host="localhost",
version=3,
secname="readonly",
authprotocol=auth, authpassword="authpass",
privprotocol=None)
class TestSnmp1(unittest.TestCase):
"""
Test communication with an agent with SNMPv1.
"""
version = 1
@classmethod
def addAgent(cls, community, auth, priv):
a = agent.TestAgent(community=community,
authpass=auth,
privpass=priv)
cls.agents.append(a)
return a
@classmethod
def setUpClass(cls):
mib.load('IF-MIB')
mib.load('SNMPv2-MIB')
cls.agents = []
cls.agent = cls.addAgent('public',
'public-authpass', 'public-privpass')
def setUp(self):
params = self.setUpSession(self.agent, 'public')
self.session = snmp.Session(**params)
def setUpSession(self, agent, password):
return dict(host="127.0.0.1:{0}".format(agent.port),
community=password,
version=self.version)
@classmethod
def tearDownClass(cls):
for a in cls.agents:
a.terminate()
def testGetString(self):
"""Get a string value"""
ooid = mib.get('SNMPv2-MIB', 'sysDescr').oid + (0,)
oid, a = self.session.get(ooid)[0]
self.assertEqual(oid, ooid)
self.assertEqual(a, b"Snimpy Test Agent public")
def testGetInteger(self):
"""Get an integer value"""
oid, a = self.session.get(mib.get('IF-MIB', 'ifNumber').oid + (0,))[0]
self.assert_(a > 1) # At least lo and another interface
def testGetEnum(self):
"""Get an enum value"""
oid, a = self.session.get(mib.get('IF-MIB', 'ifType').oid + (1,))[0]
self.assertEqual(a, 24) # This is software loopback
b = basictypes.build('IF-MIB', 'ifType', a)
self.assertEqual(b, "softwareLoopback")
def testGetMacAddress(self):
"""Get a MAC address"""
mib.load(os.path.join(os.path.dirname(os.path.abspath(__file__)),
"SNIMPY-MIB.mib"))
oid, a = self.session.get((1, 3, 6, 1, 2, 1, 45121, 1, 15, 0))[0]
self.assertEqual(a, b"\x11\x12\x13\x14\x15\x16")
b = basictypes.build('SNIMPY-MIB', 'snimpyMacAddress', a)
self.assertEqual(b, "11:12:13:14:15:16")
def testInexistant(self):
"""Get an inexistant value"""
try:
self.session.get((1, 2, 3))
self.assertFalse("we should have got an exception")
except snmp.SNMPException as ex:
self.assert_(isinstance(ex, snmp.SNMPNoSuchName) or
isinstance(ex, snmp.SNMPNoSuchObject))
def testSetIpAddress(self):
"""Set IpAddress."""
self.setAndCheck('snimpyIpAddress', '10.14.12.12')
def testSetString(self):
"""Set String."""
self.setAndCheck('snimpyString', 'hello')
def testSetInteger(self):
"""Set Integer."""
self.setAndCheck('snimpyInteger', 1574512)
def testSetEnum(self):
"""Set Enum."""
self.setAndCheck('snimpyEnum', 'testing')
def testSetObjectId(self):
"""Set ObjectId."""
self.setAndCheck('snimpyObjectId', (1, 2, 3, 4, 5, 6))
def testSetCounter(self):
"""Set Counter."""
self.setAndCheck('snimpyCounter', 545424)
def testSetGauge(self):
"""Set Gauge."""
self.setAndCheck('snimpyGauge', 4857544)
def testSetBoolean(self):
"""Set Boolean."""
self.setAndCheck('snimpyBoolean', True)
def testSetTimeticks(self):
"""Set Timeticks."""
self.setAndCheck('snimpyTimeticks', timedelta(3, 18))
def testSetBits(self):
"""Set Bits."""
self.setAndCheck('snimpyBits', ["third", "last"])
def testSetMacAddress(self):
"""Set a MAC address."""
self.setAndCheck('snimpyMacAddress', u"a0:b0:c0:d0:e:ff")
oid, a = self.session.get((1, 3, 6, 1, 2, 1, 45121, 1, 15, 0))[0]
# This is software loopback
self.assertEqual(a, b"\xa0\xb0\xc0\xd0\x0e\xff")
def setAndCheck(self, oid, value):
"""Set and check a value"""
mib.load(os.path.join(os.path.dirname(os.path.abspath(__file__)),
"SNIMPY-MIB.mib"))
ooid = mib.get('SNIMPY-MIB', oid).oid + (0,)
self.session.set(ooid,
basictypes.build('SNIMPY-MIB', oid, value))
self.assertEqual(
basictypes.build('SNIMPY-MIB', oid, self.session.get(ooid)[0][1]),
basictypes.build('SNIMPY-MIB', oid, value))
def testMultipleGet(self):
"""Get multiple values at once"""
ooid1 = mib.get('SNMPv2-MIB', 'sysDescr').oid + (0,)
ooid2 = mib.get('IF-MIB', 'ifNumber').oid + (0,)
ooid3 = mib.get('IF-MIB', 'ifType').oid + (1,)
(oid1, a1), (oid2, a2), (oid3, a3) = self.session.get(
ooid1, ooid2, ooid3)
self.assertEqual(oid1, ooid1)
self.assertEqual(oid2, ooid2)
self.assertEqual(oid3, ooid3)
self.assertEqual(a1, b"Snimpy Test Agent public")
self.assert_(a2 > 1)
b = basictypes.build('IF-MIB', 'ifType', a3)
self.assertEqual(b, "softwareLoopback")
def testBulk(self):
"""Try to set bulk to different values"""
self.session.bulk = 32
self.assertEqual(self.session.bulk, 32)
self.assertRaises(ValueError,
setattr,
self.session,
"bulk",
0)
self.assertRaises(ValueError,
setattr,
self.session,
"bulk",
-10)
def testWalk(self):
"""Check if we can walk"""
ooid = mib.get("IF-MIB", "ifDescr").oid
results = self.session.walk(ooid)
self.assertEqual(results,
((ooid + (1,), b"lo"),
(ooid + (2,), b"eth0"),
(ooid + (3,), b"eth1")))
def testSeveralSessions(self):
"""Test with two sessions"""
agent2 = self.addAgent('private',
'private-authpass', 'private-privpass')
params = self.setUpSession(agent2, 'private')
session2 = snmp.Session(**params)
ooid = mib.get('SNMPv2-MIB', 'sysDescr').oid + (0,)
oid1, a1 = self.session.get(ooid)[0]
oid2, a2 = session2.get(ooid)[0]
self.assertEqual(oid1, ooid)
self.assertEqual(oid2, ooid)
self.assertEqual(a1, b"Snimpy Test Agent public")
self.assertEqual(a2, b"Snimpy Test Agent private")
def testMultipleThreads(self):
"""Test with multiple sessions in different threads."""
count = 20
agents = []
for i in range(count):
agents.append(self.addAgent('community{}'.format(i),
'community{}-authpass'.format(i),
'community{}-privpass'.format(i)))
ooid = mib.get('SNMPv2-MIB', 'sysDescr').oid + (0,)
threads = []
successes = []
failures = []
lock = multiprocessing.Lock()
# Start one thread
def run(i):
params = self.setUpSession(agents[i], 'community{}'.format(i))
session = snmp.Session(**params)
session.timeout = 10 * 1000 * 1000
oid, a = session.get(ooid)[0]
exp = ("Snimpy Test Agent community{}".format(i)).encode('ascii')
with lock:
if oid == ooid and \
a == exp:
successes.append("community{}".format(i))
else:
failures.append("community{}".format(i))
for i in range(count):
threads.append(threading.Thread(target=run, args=(i,)))
for i in range(count):
threads[i].start()
for i in range(count):
threads[i].join()
self.assertEqual(failures, [])
self.assertEqual(sorted(successes),
sorted(["community{}".format(i)
for i in range(count)]))
class TestSnmp2(TestSnmp1):
"""Test communication with an agent with SNMPv2."""
version = 2
def testSetCounter64(self):
"""Set Counter64."""
self.setAndCheck('snimpyCounter64', 2 ** 47 + 1)
def testWalk(self):
"""Check if we can walk"""
ooid = mib.get("IF-MIB", "ifDescr").oid
self.session.bulk = 4
results = self.session.walk(ooid)
self.assertEqual(results[:3],
((ooid + (1,), b"lo"),
(ooid + (2,), b"eth0"),
(ooid + (3,), b"eth1")))
self.session.bulk = 2
results = self.session.walk(ooid)
self.assertEqual(results[:2],
((ooid + (1,), b"lo"),
(ooid + (2,), b"eth0")))
class TestSnmp3(TestSnmp2):
"""Test communicaton with an agent with SNMPv3."""
version = 3
def setUpSession(self, agent, password):
return dict(host="127.0.0.1:{0}".format(agent.port),
version=3,
secname="read-write",
authprotocol="MD5",
authpassword="{0}-authpass".format(password),
privprotocol="AES",
privpassword="{0}-privpass".format(password))
class TestSnmpTransports(unittest.TestCase):
"""Test communication using IPv6."""
ipv6 = True
@classmethod
def setUpClass(cls):
mib.load('IF-MIB')
mib.load('SNMPv2-MIB')
def _test(self, ipv6, host):
m = agent.TestAgent(ipv6)
session = snmp.Session(
host="{0}:{1}".format(host, m.port),
community="public",
version=2)
try:
ooid = mib.get('SNMPv2-MIB', 'sysDescr').oid + (0,)
oid, a = session.get(ooid)[0]
self.assertEqual(a, b"Snimpy Test Agent public")
finally:
m.terminate()
def testIpv4(self):
"""Test IPv4 transport"""
self._test(False, "127.0.0.1")
def testIpv4WithDNS(self):
"""Test IPv4 transport with name resolution"""
self._test(False, "localhost")
def testIpv6(self):
"""Test IPv6 transport"""
self._test(True, "[::1]")
def testIpv6WithDNS(self):
"""Test IPv6 transport with name resolution"""
self._test(True, "ip6-localhost")
snimpy-0.8.8/examples/ 0000755 0000764 0000144 00000000000 12622134562 015325 5 ustar bernat users 0000000 0000000 snimpy-0.8.8/examples/vlan-and-interfaces.py 0000644 0000764 0000144 00000001162 12272767417 021534 0 ustar bernat users 0000000 0000000 #!/usr/bin/snimpy
"""
On Nortel switches, list vlan on all active ports
"""
import os
import sys
load("SNMPv2-MIB")
load("IF-MIB")
load(os.path.expanduser("~/.snmp/mibs/RAPID-CITY-MIB"))
load(os.path.expanduser("~/.snmp/mibs/RC-VLAN-MIB"))
s = M(host=sys.argv[1], community=sys.argv[2])
vlans = {}
for interface in s.ifIndex:
if s.ifOperStatus[interface] == "up":
vlans[int(interface)] = []
for vlan in s.rcVlanId:
for interface in vlans:
if s.rcVlanStaticMembers[vlan] & interface:
vlans[interface].append("%s(%s)" % (vlan, s.rcVlanName[vlan]))
import pprint
pprint.pprint(vlans)
snimpy-0.8.8/examples/disable-port-ingress-filtering.py 0000644 0000764 0000144 00000001072 12376564336 023731 0 ustar bernat users 0000000 0000000 #!/usr/bin/snimpy
"""Disable port ingress filtering on Nortel switch (also known as
filter-unregistered-frames)."""
from __future__ import print_function
import sys
load("SNMPv2-MIB")
load("Q-BRIDGE-MIB")
s = M(host=sys.argv[1], community=sys.argv[2])
if "Ethernet Routing Switch 55" not in s.sysDescr:
print("Not a 5510")
sys.exit(1)
for id in s.dot1qPortIngressFiltering:
if s.dot1qPortIngressFiltering[id]:
print("Filtering on port %d of %s is not disabled, disable it." % (id, sys.argv[1]))
s.dot1qPortIngressFiltering[id] = False
snimpy-0.8.8/examples/enable-lldp.py 0000644 0000764 0000144 00000003430 12376564336 020072 0 ustar bernat users 0000000 0000000 #!/usr/bin/snimpy
"""Enable LLDP.
Generic procedure but we restrict ourself to Nortel 55x0.
"""
from __future__ import print_function
import sys
import os
load("SNMPv2-MIB")
for l in ["LLDP", "LLDP-EXT-DOT3", "LLDP-EXT-DOT1"]:
load(os.path.expanduser("~/.snmp/mibs/%s-MIB" % l))
s = M(host=sys.argv[1], community=sys.argv[2])
try:
type = s.sysDescr
except snmp.SNMPException:
print("Cannot process %s: bad community?" % sys.argv[1])
sys.exit(1)
if not type.startswith("Ethernet Routing Switch 55") and \
not type.startswith("Ethernet Switch 425"):
print("Not a 55x0: %s" % type)
sys.exit(1)
print("Processing %s..." % sys.argv[1])
try:
for oid in s.lldpConfigManAddrPortsTxEnable:
if oid[0] == "ipV4":
s.lldpConfigManAddrPortsTxEnable[oid] = "\xff"*10
except snmp.SNMPNoSuchObject:
print("No LLDP for this switch")
sys.exit(2)
dot3 = True
for port in s.lldpPortConfigAdminStatus:
s.lldpPortConfigAdminStatus[port] = "txAndRx"
s.lldpPortConfigTLVsTxEnable[port] = ["portDesc",
"sysName",
"sysDesc",
"sysCap" ]
# Dot3
try:
if dot3:
s.lldpXdot3PortConfigTLVsTxEnable[port] = ["macPhyConfigStatus",
"powerViaMDI",
"linkAggregation",
"maxFrameSize"]
except snmp.SNMPException:
print("No Dot3")
dot3 = False
# Dot1
try:
for port,vlan in s.lldpXdot1ConfigVlanNameTxEnable:
s.lldpXdot1ConfigVlanNameTxEnable[port, vlan] = True
except snmp.SNMPException:
print("No Dot1")
print("Success!")
snimpy-0.8.8/examples/add-vlan.py 0000644 0000764 0000144 00000002363 12376564336 017405 0 ustar bernat users 0000000 0000000 #!/usr/bin/snimpy
"""
On Nortel switches, create a new VLAN and tag it on "TagAll" ports.
"""
from __future__ import print_function
import os
import sys
load("SNMPv2-MIB")
load(os.path.expanduser("~/.snmp/mibs/RAPID-CITY-MIB"))
load(os.path.expanduser("~/.snmp/mibs/RC-VLAN-MIB"))
vlanNumber = int(sys.argv[3])
vlanName = sys.argv[4]
s = M(host=sys.argv[1], community=sys.argv[2])
# Create the VLAN
if vlanNumber not in s.rcVlanId:
print("VLAN %d will be created with name %s on %s" % (vlanNumber, vlanName, sys.argv[1]))
with s:
s.rcVlanRowStatus[vlanNumber] = "createAndGo"
s.rcVlanName[vlanNumber] = vlanName
s.rcVlanType[vlanNumber] = "byPort"
else:
print("VLAN %d already exists on %s" % (vlanNumber, sys.argv[1]))
# Just set the name
if s.rcVlanName[vlanNumber] != vlanName:
s.rcVlanName[vlanNumber] = vlanName
# Which ports are tagall ?
tagged = [port for port in s.rcVlanPortPerformTagging
if s.rcVlanPortPerformTagging[port] ]
if len(tagged) != 2 and len(tagged) != 3:
print("%s does not have exactly two or three tagged ports (%r)" % (sys.argv[1], tagged))
sys.exit(1)
print("VLAN %d will be tagged on ports %s" % (vlanNumber, tagged))
s.rcVlanStaticMembers[vlanNumber] |= tagged
snimpy-0.8.8/examples/rename-vlan.py 0000644 0000764 0000144 00000001160 12376564336 020116 0 ustar bernat users 0000000 0000000 #!/usr/bin/snimpy
from __future__ import print_function
import os
import sys
load("SNMPv2-MIB")
load(os.path.expanduser("~/.snmp/mibs/RAPID-CITY-MIB"))
load(os.path.expanduser("~/.snmp/mibs/RC-VLAN-MIB"))
vlanNumber = int(sys.argv[3])
newName = sys.argv[4]
s = M(host=sys.argv[1], community=sys.argv[2])
try:
cur = s.rcVlanName[vlanNumber]
except snmp.SNMPException:
print("%s is not a Nortel switch or does not have VLAN %d" % (sys.argv[1], vlanNumber))
sys.exit(1)
if cur != newName:
s.rcVlanName[vlanNumber] = newName
print("Setting VLAN %d of %s as %s: done." % (vlanNumber, sys.argv[1], newName))
snimpy-0.8.8/examples/set-syslog-ntp.py 0000755 0000764 0000144 00000004247 12376564336 020635 0 ustar bernat users 0000000 0000000 #!/usr/bin/snimpy
"""
Set NTP or syslog server on various switches
Usage:
./set-syslog-ntp.py [syslog|ntp] host community first [...]
"""
from __future__ import print_function
import os
import sys
load("SNMPv2-MIB")
host = sys.argv[2]
targets = sys.argv[4:]
operation = sys.argv[1]
try:
s = M(host=host, community=sys.argv[3])
sid = str(s.sysObjectID)
except snmp.SNMPException, e:
print("%s: %s" % (host, e))
sys.exit(1)
if sid.startswith("1.3.6.1.4.1.45.3."):
# Nortel
print("%s is Nortel 55xx" % host)
load(os.path.expanduser("~/.snmp/mibs/SYNOPTICS-ROOT-MIB"))
load(os.path.expanduser("~/.snmp/mibs/S5-ROOT-MIB"))
if operation == "ntp":
load(os.path.expanduser("~/.snmp/mibs/S5-AGENT-MIB"))
s.s5AgSntpPrimaryServerAddress = targets[0]
if len(targets) > 1:
s.s5AgSntpSecondaryServerAddress = targets[1]
else:
s.s5AgSntpSecondaryServerAddress = "0.0.0.0"
s.s5AgSntpState = "unicast"
s.s5AgSntpManualSyncRequest = "requestSync"
elif operation == "syslog":
load(os.path.expanduser("~/.snmp/mibs/BN-LOG-MESSAGE-MIB"))
s.bnLogMsgRemoteSyslogAddress = targets[0]
s.bnLogMsgRemoteSyslogSaveTargets = "msgTypeInformational"
s.bnLogMsgRemoteSyslogEnabled = True
elif sid.startswith("1.3.6.1.4.1.1872."):
print("%s is Alteon" % host)
load(os.path.expanduser("~/.snmp/mibs/ALTEON-ROOT-MIB"))
if operation == "ntp":
s.agNewCfgNTPServer = targets[0]
if len(targets) > 1:
s.agNewCfgNTPSecServer = targets[1]
else:
s.agNewCfgNTPSecServer = "0.0.0.0"
s.agNewCfgNTPService = "enabled"
elif operation == "syslog":
s.agNewCfgSyslogHost = targets[0]
s.agNewCfgSyslogFac = "local2"
if len(targets) > 1:
s.agNewCfgSyslog2Host = targets[1]
s.agNewCfgSyslog2Fac = "local2"
else:
s.agNewCfgSyslog2Host = "0.0.0.0"
if s.agApplyPending == "applyNeeded":
if s.agApplyConfig == "complete":
s.agApplyConfig = "idle"
s.agApplyConfig = "apply"
else:
print("%s is unknown (%s)" % (host, s.sysDescr))
sys.exit(1)
snimpy-0.8.8/examples/list-routes.py 0000644 0000764 0000144 00000001212 12376564336 020201 0 ustar bernat users 0000000 0000000 #!/usr/bin/snimpy
from __future__ import print_function
from socket import inet_ntoa
load("IP-FORWARD-MIB")
m=M()
print("Using IP-FORWARD-MIB::ipCidrRouteTable...")
routes = m.ipCidrRouteNextHop
for x in routes:
net, netmask, tos, src = x
print("%15s/%-15s via %-15s src %-15s" % (net, netmask, routes[x], src))
print
print("Using IP-FORWARD-MIB::inetCidrRouteTable...")
routes = m.inetCidrRouteIfIndex
for x in routes:
dsttype, dst, prefix, oid, nhtype, nh = x
if dsttype != "ipv4" or nhtype != "ipv4":
print("Non-IPv4 route")
continue
print("%15s/%-2d via %-15s" % (inet_ntoa(dst), prefix, inet_ntoa(nh)))
snimpy-0.8.8/examples/get-serial.py 0000644 0000764 0000144 00000001630 12376564336 017747 0 ustar bernat users 0000000 0000000 #!/usr/bin/snimpy
"""
Get serial number of a given equipment using ENTITY-MIB
"""
from __future__ import print_function
import sys
load("ENTITY-MIB")
host=sys.argv[1]
s = M(host=host, community=sys.argv[2])
# Locate parent of all other elements
print("[-] %s: Search for parent element" % host)
parent = None
for i in s.entPhysicalContainedIn:
if s.entPhysicalContainedIn[i] == 0:
parent = i
break
if parent is None:
print("[!] %s: Unable to find parent" % host)
sys.exit(1)
print("[+] %s: %s" % (host, s.entPhysicalDescr[parent]))
print("[+] %s: HW %s, FW %s, SW %s" % (host,
s.entPhysicalHardwareRev[parent],
s.entPhysicalFirmwareRev[parent],
s.entPhysicalSoftwareRev[parent]))
print("[+] %s: SN %s" % (host,
s.entPhysicalSerialNum[parent]))
snimpy-0.8.8/examples/list-interfaces.py 0000644 0000764 0000144 00000000231 12376564336 021003 0 ustar bernat users 0000000 0000000 #!/usr/bin/snimpy
from __future__ import print_function
load("IF-MIB")
m=M()
for i in m.ifDescr:
print("Interface %3d: %s" % (i, m.ifDescr[i]))
snimpy-0.8.8/snimpy/ 0000755 0000764 0000144 00000000000 12622134562 015026 5 ustar bernat users 0000000 0000000 snimpy-0.8.8/snimpy/config.py 0000644 0000764 0000144 00000003350 12376564336 016662 0 ustar bernat users 0000000 0000000 #
# snimpy -- Interactive SNMP tool
#
# Copyright (C) Vincent Bernat
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
import os.path
import imp
class Conf:
prompt = "\033[1m[snimpy]>\033[0m "
histfile = "~/.snimpy_history" # Not used with IPython
userconf = "~/.snimpy.conf"
ipython = True
ipythonprofile = None # Set for example to "snimpy"
mibs = []
def load(self, userconf=None):
if userconf is None:
userconf = self.userconf
try:
conffile = open(os.path.expanduser(userconf))
except (OSError, IOError):
pass
else:
try:
confuser = imp.load_module("confuser", conffile,
os.path.expanduser(userconf),
("conf", 'r', imp.PY_SOURCE))
for k in confuser.__dict__:
if not k.startswith("__"):
setattr(self, k, confuser.__dict__[k])
finally:
conffile.close()
return self
snimpy-0.8.8/snimpy/_version.py 0000644 0000764 0000144 00000000204 12622134561 017217 0 ustar bernat users 0000000 0000000
# This file is automatically generated by setup.py.
__version__ = '0.8.8'
__sha__ = 'g761a70444eac'
__revision__ = 'g761a70444eac'
snimpy-0.8.8/snimpy/basictypes.py 0000644 0000764 0000144 00000074025 12620437116 017555 0 ustar bernat users 0000000 0000000 #
# snimpy -- Interactive SNMP tool
#
# Copyright (C) Vincent Bernat
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
"""
This module is aimed at providing Pythonic representation of
various SNMP types. Each SMIv2 type is mapped to a corresponding class
which tries to mimic a basic type from Python. For example, display
strings are like Python string while SMIv2 integers are just like
Python integers. This module is some kind of a hack and its use
outside of *Snimpy* seems convoluted.
"""
import sys
import struct
import socket
import re
from datetime import timedelta
from pysnmp.proto import rfc1902
from snimpy import mib
PYTHON3 = sys.version_info >= (3, 0)
if PYTHON3:
def ord2(x):
return x
def chr2(x):
return bytes([x & 0xff])
unicode = str
long = int
else:
ord2 = ord
def chr2(x):
return chr(x & 0xff)
def ordering_with_cmp(cls):
ops = {'__lt__': lambda self, other: self.__cmp__(other) < 0,
'__gt__': lambda self, other: self.__cmp__(other) > 0,
'__le__': lambda self, other: self.__cmp__(other) <= 0,
'__ge__': lambda self, other: self.__cmp__(other) >= 0,
'__eq__': lambda self, other: self.__cmp__(other) == 0,
'__ne__': lambda self, other: self.__cmp__(other) != 0}
for opname, opfunc in ops.items():
opfunc.__name__ = opname
opfunc.__doc__ = getattr(int, opname).__doc__
setattr(cls, opname, opfunc)
return cls
class Type(object):
"""Base class for all types."""
def __new__(cls, entity, value, raw=True):
"""Create a new typed value.
:param entity: A :class:`mib.Node` instance
:param value: The value to set
:param raw: Whetever the raw value is provided (as opposed to
a user-supplied value). This parameter is important when
the provided input is ambiguous, for example when it is an
array of bytes.
:type raw: bool
:return: an instance of the new typed value
"""
if entity.type != cls:
raise ValueError("MIB node is {0}. We are {1}".format(entity.type,
cls))
if cls == OctetString and entity.fmt is not None:
# Promotion of OctetString to String if we have unicode stuff
if isinstance(value, (String, unicode)) or not raw:
cls = String
if not isinstance(value, Type):
value = cls._internal(entity, value)
else:
value = cls._internal(entity, value._value)
if issubclass(cls, unicode):
self = unicode.__new__(cls, value)
elif issubclass(cls, bytes):
self = bytes.__new__(cls, value)
elif issubclass(cls, long):
self = long.__new__(cls, value)
else:
self = object.__new__(cls)
self._value = value
self.entity = entity
if cls == OctetString and entity.fmt is not None:
# A display-hint propose to use only ascii and UTF-8
# chars. We promote an OCTET-STRING to a DisplayString if
# we have a format. This means we won't be able to access
# individual bytes in this format, only the full displayed
# version.
value = String._internal(entity, self)
self = unicode.__new__(String, value)
self._value = value
self.entity = entity
if isinstance(self, String):
# Ensure that strings follow their format, if it is applied.
# This is safer and simpler than toOid, as it does not do
# additional validation.
self._toBytes()
return self
@classmethod
def _internal(cls, entity, value):
"""Get internal value for a given value."""
raise NotImplementedError # pragma: no cover
def pack(self):
"""Prepare the instance to be sent on the wire."""
raise NotImplementedError # pragma: no cover
def toOid(self):
"""Convert to an OID.
If this function is implemented, then class function
:meth:`fromOid` should also be implemented as the "invert"
function of this one.
This function only works if the entity is used as an index!
Otherwise, it should raises NotImplementedError.
:return: An OID that can be used as index
"""
raise NotImplementedError # pragma: no cover
@classmethod
def fromOid(cls, entity, oid):
"""Create instance from an OID.
This is the sister function of :meth:`toOid`.
:param oid: The OID to use to create an instance
:param entity: The MIB entity we want to instantiate
:return: A couple `(l, v)` with `l` the number of suboids
needed to create the instance and `v` the instance created from
the OID
"""
raise NotImplementedError # pragma: no cover
@classmethod
def _fixedOrImplied(cls, entity):
"""Determine if the given entity is fixed-len or implied.
This function is an helper that is used for String and
Oid. When converting a variable-length type to an OID, we need
to prefix it by its len or not depending of what the MIB say.
:param entity: entity to check
:return: "fixed" if it is fixed-len, "implied" if implied var-len,
`False` otherwise
"""
if entity.ranges and not isinstance(entity.ranges, (tuple, list)):
# Fixed length
return "fixed"
# We have a variable-len string/oid. We need to know if it is implied.
try:
table = entity.table
except:
raise NotImplementedError(
"{0} is not an index of a table".format(entity))
indexes = [str(a) for a in table.index]
if str(entity) not in indexes:
raise NotImplementedError(
"{0} is not an index of a table".format(entity))
if str(entity) != indexes[-1] or not table.implied:
# This index is not implied
return False
return "implied"
def __str__(self):
return str(self._value)
def __repr__(self):
try:
return '<{0}: {1}>'.format(self.__class__.__name__,
str(self))
except:
return '<{0} ????>'.format(self.__class__.__name__)
@ordering_with_cmp
class IpAddress(Type):
"""Class representing an IP address/"""
@classmethod
def _internal(cls, entity, value):
if isinstance(value, (list, tuple)):
value = ".".join([str(a) for a in value])
elif isinstance(value, bytes):
try:
value = socket.inet_ntoa(value)
except:
pass
try:
value = socket.inet_ntoa(socket.inet_aton(value))
except:
raise ValueError("{0!r} is not a valid IP".format(value))
return [int(a) for a in value.split(".")]
def pack(self):
return (
rfc1902.IpAddress(
str(".".join(["{0:d}".format(x) for x in self._value])))
)
def toOid(self):
return tuple(self._value)
@classmethod
def fromOid(cls, entity, oid):
if len(oid) < 4:
raise ValueError(
"{0!r} is too short for an IP address".format(oid))
return (4, cls(entity, oid[:4]))
def __str__(self):
return ".".join([str(a) for a in self._value])
def __cmp__(self, other):
if not isinstance(other, IpAddress):
try:
other = IpAddress(self.entity, other)
except:
raise NotImplementedError # pragma: no cover
if self._value == other._value:
return 0
if self._value < other._value:
return -1
return 1
def __getitem__(self, nb):
return self._value[nb]
class StringOrOctetString(Type):
def toOid(self):
# To convert properly to OID, we need to know if it is a
# fixed-len string, an implied string or a variable-len
# string.
b = self._toBytes()
if self._fixedOrImplied(self.entity):
return tuple(ord2(a) for a in b)
return tuple([len(b)] + [ord2(a) for a in b])
def _toBytes(self):
raise NotImplementedError
@classmethod
def fromOid(cls, entity, oid):
type = cls._fixedOrImplied(entity)
if type == "implied":
# Eat everything
return (len(oid), cls(entity, b"".join([chr2(x) for x in oid])))
if type == "fixed":
l = entity.ranges
if len(oid) < l:
raise ValueError(
"{0} is too short for wanted fixed "
"string (need at least {1:d})".format(oid, l))
return (l, cls(entity, b"".join([chr2(x) for x in oid[:l]])))
# This is var-len
if not oid:
raise ValueError("empty OID while waiting for var-len string")
l = oid[0]
if len(oid) < l + 1:
raise ValueError(
"{0} is too short for variable-len "
"string (need at least {1:d})".format(oid, l))
return (
(l + 1, cls(entity, b"".join([chr2(x) for x in oid[1:(l + 1)]])))
)
def pack(self):
return rfc1902.OctetString(self._toBytes())
class OctetString(StringOrOctetString, bytes):
"""Class for a generic octet string. This class should be compared to
:class:`String` which is used to represent a display string. This
class is usually used to store raw bytes, like a bitmask of
VLANs.
"""
@classmethod
def _internal(cls, entity, value):
# Internally, we are using bytes
if isinstance(value, bytes):
return value
if isinstance(value, unicode):
return value.encode("ascii")
return bytes(value)
def _toBytes(self):
return self._value
def __ior__(self, value):
nvalue = [ord2(u) for u in self._value]
if not isinstance(value, (tuple, list)):
value = [value]
for v in value:
if not isinstance(v, (int, long)):
raise NotImplementedError(
"on string, bit-operation are limited to integers")
if len(nvalue) < (v >> 3) + 1:
nvalue.extend([0] * ((v >> 3) + 1 - len(self._value)))
nvalue[v >> 3] |= 1 << (7 - v % 8)
return self.__class__(self.entity, b"".join([chr2(i) for i in nvalue]))
def __isub__(self, value):
nvalue = [ord2(u) for u in self._value]
if not isinstance(value, (tuple, list)):
value = [value]
for v in value:
if not isinstance(v, int) and not isinstance(v, long):
raise NotImplementedError(
"on string, bit-operation are limited to integers")
if len(nvalue) < (v >> 3) + 1:
continue
nvalue[v >> 3] &= ~(1 << (7 - v % 8))
return self.__class__(self.entity, b"".join([chr2(i) for i in nvalue]))
return self
def __and__(self, value):
nvalue = [ord2(u) for u in self._value]
if not isinstance(value, (tuple, list)):
value = [value]
for v in value:
if not isinstance(v, (int, long)):
raise NotImplementedError(
"on string, bit-operation are limited to integers")
if len(nvalue) < (v >> 3) + 1:
return False
if not(nvalue[v >> 3] & (1 << (7 - v % 8))):
return False
return True
class String(StringOrOctetString, unicode):
"""Class for a display string. Such a string is an unicode string and
it is therefore expected that only printable characters are
used. This is usually the case if the corresponding MIB node comes
with a format string.
With such an instance, the user is expected to be able to provide
a formatted. For example, a MAC address could be written
`00:11:22:33:44:55`.
"""
@classmethod
def _parseOctetFormat(cls, fmt, j):
# repeater
if fmt[j] == "*":
dorepeat = True
j += 1
else:
dorepeat = False
# length
length = ""
while fmt[j].isdigit():
length += fmt[j]
j += 1
length = int(length)
# format
format = fmt[j]
j += 1
# seperator
if j < len(fmt) and \
fmt[j] != "*" and not fmt[j].isdigit():
sep = fmt[j]
j += 1
else:
sep = ""
# terminator
if j < len(fmt) and \
fmt[j] != "*" and not fmt[j].isdigit():
term = fmt[j]
j += 1
else:
term = ""
return (j, dorepeat, length, format, sep, term)
@classmethod
def _fromBytes(cls, value, fmt):
i = 0 # Position in value
j = 0 # Position in fmt
result = ""
term = None
sep = None
while i < len(value):
if j < len(fmt):
j, dorepeat, length, format, sep, term = cls._parseOctetFormat(
fmt, j)
# building
if dorepeat:
repeat = ord2(value[i])
i += 1
else:
repeat = 1
for r in range(repeat):
bb = value[i:i + length]
i += length
if format in ['o', 'x', 'd']:
if length > 4:
raise ValueError(
"don't know how to handle integers "
"more than 4 bytes long")
bb = b"\x00" * (4 - length) + bb
number = struct.unpack(b"!l", bb)[0]
if format == "o":
# In Python2, oct() is 01242, while it is 0o1242 in
# Python3
result += "".join(oct(number).partition("o")[0:3:2])
elif format == "x":
result += hex(number)[2:]
else: # format == "d":
result += str(number)
elif format == "a":
result += bb.decode("ascii")
elif format == "t":
result += bb.decode("utf-8")
else:
raise ValueError("{0!r} cannot be represented with "
"the given display string ({1})".format(
bb, fmt))
result += sep
if sep and term:
result = result[:-1]
result += term
if term or sep:
result = result[:-1]
return result
def _toBytes(self):
# We need to reverse what was done by `_fromBytes`. This is
# not an exact science. In most case, this is easy because a
# separator is used but sometimes, this is not. We do some
# black magic that will fail.
i = 0
j = 0
fmt = self.entity.fmt
bb = b""
while i < len(self._value):
if j < len(fmt):
parsed = self._parseOctetFormat(fmt, j)
j, dorepeat, length, format, sep, term = parsed
if format == "o":
fmatch = "(?P[0-7]{{1,{0}}})".format(
int(length * 2.66667) + 1)
elif format == "x":
fmatch = "(?P[0-9A-Fa-f]{{1,{0}}})".format(length * 2)
elif format == "d":
fmatch = "(?P[0-9]{{1,{0}}})".format(
int(length * 2.4083) + 1)
elif format == "a":
fmatch = "(?P(?:.|\n){{1,{0}}})".format(length)
elif format == "t":
fmatch = "(?P(?:.|\n){{1,{0}}})".format(length)
else:
raise ValueError("{0!r} cannot be parsed due to an "
"incorrect format ({1})".format(
self._value, fmt))
repeats = []
while True:
mo = re.match(fmatch, self._value[i:])
if not mo:
raise ValueError("{0!r} cannot be parsed because it "
"does not match format {1} at "
"index {2}".format(self._value, fmt, i))
if format in ["o", "x", "d"]:
if format == "o":
r = int(mo.group("o"), 8)
elif format == "x":
r = int(mo.group("x"), 16)
else:
r = int(mo.group("d"))
result = struct.pack(b"!l", r)[-length:]
else:
result = mo.group(1).encode("utf-8")
i += len(mo.group(1))
if dorepeat:
repeats.append(result)
if i < len(self._value):
# Approximate...
if sep and self._value[i] == sep:
i += 1
elif term and self._value[i] == term:
i += 1
break
else:
break
else:
break
if dorepeat:
bb += chr2(len(repeats))
bb += b"".join(repeats)
else:
bb += result
if i < len(self._value) and (sep and self._value[i] == sep or
term and self._value[i] == term):
i += 1
return bb
@classmethod
def _internal(cls, entity, value):
# Internally, we use the displayed string. We have a special
# case if the value is an OctetString to do the conversion.
if isinstance(value, OctetString):
return cls._fromBytes(value._value, entity.fmt)
if PYTHON3 and isinstance(value, bytes):
return value.decode("utf-8")
return unicode(value)
def __str__(self):
return self._value
class Integer(Type, long):
"""Class for any integer."""
@classmethod
def _internal(cls, entity, value):
return long(value)
def pack(self):
if self._value >= (1 << 64):
raise OverflowError("too large to be packed")
if self._value >= (1 << 32):
return rfc1902.Counter64(self._value)
if self._value >= 0:
return rfc1902.Integer(self._value)
if self._value >= -(1 << 31):
return rfc1902.Integer(self._value)
raise OverflowError("too small to be packed")
def toOid(self):
return (self._value,)
@classmethod
def fromOid(cls, entity, oid):
if len(oid) < 1:
raise ValueError("{0} is too short for an integer".format(oid))
return (1, cls(entity, oid[0]))
def __str__(self):
if self.entity.fmt:
if self.entity.fmt[0] == "x":
return hex(self._value)
if self.entity.fmt[0] == "o":
return oct(self._value)
if self.entity.fmt[0] == "b":
if self._value == 0:
return "0"
if self._value > 0:
v = self._value
r = ""
while v > 0:
r = str(v % 2) + r
v = v >> 1
return r
elif self.entity.fmt[0] == "d" and \
len(self.entity.fmt) > 2 and \
self.entity.fmt[1] == "-":
dec = int(self.entity.fmt[2:])
result = str(self._value)
if len(result) < dec + 1:
result = "0" * (dec + 1 - len(result)) + result
return "{0}.{1}".format(result[:-2], result[-2:])
return str(self._value)
class Unsigned32(Integer):
"""Class to represent an unsigned 32bits integer."""
def pack(self):
if self._value >= (1 << 32):
raise OverflowError("too large to be packed")
if self._value < 0:
raise OverflowError("too small to be packed")
return rfc1902.Unsigned32(self._value)
class Unsigned64(Integer):
"""Class to represent an unsigned 64bits integer."""
def pack(self):
if self._value >= (1 << 64):
raise OverflowError("too large to be packed")
if self._value < 0:
raise OverflowError("too small to be packed")
return rfc1902.Counter64(self._value)
class Enum(Integer):
"""Class for an enumeration. An enumaration is an integer but labels
are attached to some values for a more user-friendly display."""
@classmethod
def _internal(cls, entity, value):
if value in entity.enum:
return value
for (k, v) in entity.enum.items():
if (v == value):
return k
try:
return long(value)
except:
raise ValueError("{0!r} is not a valid "
"value for {1}".format(value,
entity))
def pack(self):
return rfc1902.Integer(self._value)
@classmethod
def fromOid(cls, entity, oid):
if len(oid) < 1:
raise ValueError(
"{0!r} is too short for an enumeration".format(oid))
return (1, cls(entity, oid[0]))
def __eq__(self, other):
if not isinstance(other, self.__class__):
try:
other = self.__class__(self.entity, other)
except:
raise NotImplementedError # pragma: no cover
return self._value == other._value
def __ne__(self, other):
return not(self.__eq__(other))
def __str__(self):
if self._value in self.entity.enum:
return (
"{0}({1:d})".format(self.entity.enum[self._value], self._value)
)
else:
return str(self._value)
@ordering_with_cmp
class Oid(Type):
"""Class to represent and OID."""
@classmethod
def _internal(cls, entity, value):
if isinstance(value, (list, tuple)):
return tuple([int(v) for v in value])
elif isinstance(value, str):
return tuple([ord2(i) for i in value.split(".") if i])
elif isinstance(value, mib.Node):
return tuple(value.oid)
else:
raise TypeError(
"don't know how to convert {0!r} to OID".format(value))
def pack(self):
return rfc1902.univ.ObjectIdentifier(self._value)
def toOid(self):
if self._fixedOrImplied(self.entity):
return self._value
return tuple([len(self._value)] + list(self._value))
@classmethod
def fromOid(cls, entity, oid):
if cls._fixedOrImplied(entity) == "fixed":
# A fixed OID? We don't like this. Provide a real example.
raise ValueError(
"{0!r} seems to be a fixed-len OID index. Odd.".format(entity))
if not cls._fixedOrImplied(entity):
# This index is not implied. We need the len
if len(oid) < 1:
raise ValueError(
"{0!r} is too short for a not "
"implied index".format(entity))
l = oid[0]
if len(oid) < l + 1:
raise ValueError(
"{0!r} has an incorrect size "
"(needs at least {1:d})".format(oid, l))
return (l + 1, cls(entity, oid[1:(l + 1)]))
else:
# Eat everything
return (len(oid), cls(entity, oid))
def __str__(self):
return ".".join([str(x) for x in self._value])
def __cmp__(self, other):
if not isinstance(other, Oid):
other = Oid(self.entity, other)
if tuple(self._value) == tuple(other._value):
return 0
if self._value > other._value:
return 1
return -1
def __getitem__(self, index):
return self._value[index]
def __contains__(self, item):
"""Test if item is a sub-oid of this OID"""
if not isinstance(item, Oid):
item = Oid(self.entity, item)
return tuple(item._value[:len(self._value)]) == \
tuple(self._value[:len(self._value)])
class Boolean(Enum):
"""Class for a boolean."""
@classmethod
def _internal(cls, entity, value):
if isinstance(value, bool):
if value:
return Enum._internal(entity, "true")
else:
return Enum._internal(entity, "false")
else:
return Enum._internal(entity, value)
def __nonzero__(self):
if self._value == 1:
return True
else:
return False
def __bool__(self):
return self.__nonzero__()
@ordering_with_cmp
class Timeticks(Type):
"""Class for timeticks."""
@classmethod
def _internal(cls, entity, value):
if isinstance(value, (int, long)):
# Value in centiseconds
return timedelta(0, value / 100.)
elif isinstance(value, timedelta):
return value
else:
raise TypeError(
"dunno how to handle {0!r} ({1})".format(value, type(value)))
def __int__(self):
return self._value.days * 3600 * 24 * 100 + \
self._value.seconds * 100 + \
self._value.microseconds // 10000
def toOid(self):
return (int(self),)
@classmethod
def fromOid(cls, entity, oid):
if len(oid) < 1:
raise ValueError("{0!r} is too short for a timetick".format(oid))
return (1, cls(entity, oid[0]))
def pack(self):
return rfc1902.TimeTicks(int(self))
def __str__(self):
return str(self._value)
def __cmp__(self, other):
if isinstance(other, Timeticks):
other = other._value
elif isinstance(other, (int, long)):
other = timedelta(0, other / 100.)
elif not isinstance(other, timedelta):
raise NotImplementedError(
"only compare to int or "
"timedelta, not {0}".format(type(other)))
if self._value == other:
return 0
if self._value < other:
return -1
return 1
class Bits(Type):
"""Class for bits."""
@classmethod
def _internal(cls, entity, value):
bits = set()
tryalternate = False
if isinstance(value, bytes):
for i, x in enumerate(value):
if ord2(x) == 0:
continue
for j in range(8):
if ord2(x) & (1 << (7 - j)):
if j not in entity.enum:
tryalternate = True
break
bits.add(j)
if tryalternate:
break
if not tryalternate:
return bits
else:
bits = set()
elif not isinstance(value, (tuple, list, set, frozenset)):
value = set([value])
for v in value:
found = False
if v in entity.enum:
bits.add(v)
found = True
else:
for (k, t) in entity.enum.items():
if (t == v):
bits.add(k)
found = True
break
if not found:
raise ValueError("{0!r} is not a valid bit value".format(v))
return bits
def pack(self):
string = []
for b in self._value:
if len(string) < (b >> 4) + 1:
string.extend([0] * ((b >> 4) - len(string) + 1))
string[b >> 416] |= 1 << (7 - b % 16)
return rfc1902.Bits(b"".join([chr2(x) for x in string]))
def __eq__(self, other):
if isinstance(other, str):
other = [other]
if not isinstance(other, Bits):
other = Bits(self.entity, other)
return self._value == other._value
def __ne__(self, other):
return not self.__eq__(other)
def __str__(self):
result = []
for b in sorted(self._value):
result.append("{0}({1:d})".format(self.entity.enum[b], b))
return ", ".join(result)
def __and__(self, other):
if isinstance(other, str):
other = [other]
if not isinstance(other, Bits):
other = Bits(self.entity, other)
return len(self._value & other._value) > 0
def __ior__(self, other):
if isinstance(other, str):
other = [other]
if not isinstance(other, Bits):
other = Bits(self.entity, other)
self._value |= other._value
return self
def __isub__(self, other):
if isinstance(other, str):
other = [other]
if not isinstance(other, Bits):
other = Bits(self.entity, other)
self._value -= other._value
return self
def build(mibname, node, value):
"""Build a new basic type with the given value.
:param mibname: The MIB to use to locate the entity.
:param node: The node that will be attached to this type.
:param value: The initial value to set for the type.
:return: A :class:`Type` instance
"""
m = mib.get(mibname, node)
t = m.type(m, value)
return t
snimpy-0.8.8/snimpy/manager.py 0000644 0000764 0000144 00000040375 12600052762 017020 0 ustar bernat users 0000000 0000000 #
# snimpy -- Interactive SNMP tool
#
# Copyright (C) Vincent Bernat
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
"""This module is the high-level interface to *Snimpy*. It exposes
:class:`Manager` class to instantiate a new manager (which is an SNMP
client). This is the preferred interface for *Snimpy*.
Here is a simple example of use of this module::
>>> load("IF-MIB")
>>> m = Manager("localhost")
>>> m.ifDescr[1]
"""
import inspect
from time import time
from collections import MutableMapping
from snimpy import snmp, mib, basictypes
class DelegatedSession(object):
"""General class for SNMP session for delegation"""
def __init__(self, session):
self._session = session
def __getattr__(self, attr):
return getattr(self._session, attr)
def __setattribute__(self, attr, value):
return setattr(self._session, attr, value)
class DelayedSetSession(DelegatedSession):
"""SNMP session that is able to delay SET requests.
This is an adapter. The constructor takes the original (not
delayed) session.
"""
def __init__(self, session):
DelegatedSession.__init__(self, session)
self.setters = []
def set(self, *args):
self.setters.extend(args)
def commit(self):
if self.setters:
self._session.set(*self.setters)
class NoneSession(DelegatedSession):
"""SNMP session that will return None on unsucessful GET requests.
In a normal session, a GET request returning `No such instance`
error will trigger an exception. This session will catch such an
error and return None instead.
"""
def get(self, *args):
try:
return self._session.get(*args)
except (snmp.SNMPNoSuchName,
snmp.SNMPNoSuchObject,
snmp.SNMPNoSuchInstance):
if len(args) > 1:
# We can't handle this case yet because we don't know
# which value is unavailable.
raise
return ((args[0], None),)
class CachedSession(DelegatedSession):
"""SNMP session using a cache.
This is an adapter. The constructor takes the original session.
"""
def __init__(self, session, timeout=5):
DelegatedSession.__init__(self, session)
self.cache = {} # contains (operation, oid) -> [time, result] entries
self.timeout = timeout
self.count = 0
def getorwalk(self, op, *args):
self.count += 1
if (op, args) in self.cache:
t, v = self.cache[op, args]
if time() - t < self.timeout:
return v
value = getattr(self._session, op)(*args)
self.cache[op, args] = [time(), value]
if op == "walk":
# also cache all the get requests we got for free
for oid, get_value in value:
self.count += 1
self.cache["get", (oid, )] = [time(), ((oid, get_value), )]
self.flush()
return value
def get(self, *args):
return self.getorwalk("get", *args)
def walk(self, *args):
assert(len(args) == 1) # we should ony walk one oid at a time
return self.getorwalk("walk", *args)
def flush(self):
keys = list(self.cache.keys())
for k in keys:
if time() - self.cache[k][0] > self.timeout:
del self.cache[k]
self.count = 0
def MibRestrictedManager(original, mibs):
"""Copy an existing manager but restrict its view to the given set of
MIBs.
"""
clone = Manager(**original._constructor_args)
clone._loaded = mibs
return clone
class Manager(object):
"""SNMP manager. An instance of this class will represent an SNMP
manager (client).
When a MIB is loaded with :func:`load`, scalars and row names from
it will be made available as an instance attribute. For a scalar,
reading the corresponding attribute will get its value while
setting it will allow to modify it:
>>> load("SNMPv2-MIB")
>>> m = Manager("localhost", "private")
>>> m.sysContact
>>> m.sysContact = "Brian Jones"
>>> m.sysContact
For a row name, the provided interface is like a Python
dictionary. Requesting an item using its index will retrieve the
value from the agent (the server)::
>>> load("IF-MIB")
>>> m = Manager("localhost", "private")
>>> m.ifDescr[1]
>>> m.ifName[1] = "Loopback interface"
Also, it is possible to iterate on a row name to get all available
values for index::
>>> load("IF-MIB")
>>> m = Manager("localhost", "private")
>>> for idx in m.ifDescr:
... print(m.ifDescr[idx])
A context manager is also provided. Any modification issued inside
the context will be delayed until the end of the context and then
grouped into a single SNMP PDU to be executed atomically::
>>> load("IF-MIB")
>>> m = Manager("localhost", "private")
>>> with m:
... m.ifName[1] = "Loopback interface"
... m.ifName[2] = "First interface"
Any error will be turned into an exception::
>>> load("IF-MIB")
>>> m = Manager("localhost", "private")
>>> m.ifDescr[999]
Traceback (most recent call last):
...
SNMPNoSuchName: There is no such variable name in this MIB.
"""
# do we want this object to be populated with all nodes?
_complete = False
def __init__(self,
host="localhost",
community="public", version=2,
cache=False, none=False,
timeout=None, retries=None,
loose=False, bulk=40,
# SNMPv3
secname=None,
authprotocol=None, authpassword=None,
privprotocol=None, privpassword=None):
"""Create a new SNMP manager. Some of the parameters are explained in
:meth:`snmp.Session.__init__`.
:param host: The hostname or IP address of the agent to
connect to. Optionally, the port can be specified
separated with a double colon.
:type host: str
:param community: The community to transmit to the agent for
authorization purpose. This parameter is ignored if the
specified version is 3.
:type community: str
:param version: The SNMP version to use to talk with the
agent. Possible values are `1`, `2` (community-based) or
`3`.
:type version: int
:param cache: Should caching be enabled? This can be either a
boolean or an integer to specify the cache timeout in
seconds. If `True`, the default timeout is 5 seconds.
:type cache: bool or int
:param none: Should `None` be returned when the agent does not
know the requested OID? If `True`, `None` will be returned
when requesting an inexisting scalar or column.
:type none: bool
:param timeout: Use the specified value in seconds as timeout.
:type timeout: int
:param retries: How many times the request should be retried?
:type retries: int
:param loose: Enable loose typing. When type coercion fails
(for example when a MIB declare an element to be an ASCII
string while it is not), just return the raw result
instead of an exception. This mode should be enabled with
caution. Patching the MIB is a better idea.
:type loose: bool
:param bulk: Max-repetition to use to speed up MIB walking
with `GETBULK`. Set to `0` to disable.
:type bulk: int
"""
if host is None:
host = Manager._host
self._host = host
self._session = snmp.Session(host, community, version,
secname,
authprotocol, authpassword,
privprotocol, privpassword,
bulk=bulk)
if timeout is not None:
self._session.timeout = int(timeout * 1000000)
if retries is not None:
self._session.retries = retries
if cache:
if cache is True:
self._session = CachedSession(self._session)
else:
self._session = CachedSession(self._session, cache)
if none:
self._session = NoneSession(self._session)
self._loose = loose
self._loaded = loaded
# To be able to clone, we save the arguments provided to the
# constructor in a generic way
frame = inspect.currentframe()
args, _, _, values = inspect.getargvalues(frame)
self._constructor_args = dict((a, values[a])
for a in args
if a != 'self')
def _locate(self, attribute):
for m in self._loaded:
try:
a = mib.get(m, attribute)
return (m, a)
except mib.SMIException:
pass
raise AttributeError("{0} is not an attribute".format(attribute))
def __getattribute__(self, attribute):
if attribute.startswith("_"):
return object.__getattribute__(self, attribute)
m, a = self._locate(attribute)
if isinstance(a, mib.Scalar):
oid, result = self._session.get(a.oid + (0,))[0]
if result is not None:
try:
return a.type(a, result)
except ValueError:
if self._loose:
return result
raise
return None
elif isinstance(a, mib.Column):
return ProxyColumn(self._session, a, self._loose)
raise NotImplementedError
def __setattr__(self, attribute, value):
if attribute.startswith("_"):
return object.__setattr__(self, attribute, value)
m, a = self._locate(attribute)
if not isinstance(value, basictypes.Type):
value = a.type(a, value, raw=False)
if isinstance(a, mib.Scalar):
self._session.set(a.oid + (0,), value)
return
raise AttributeError("{0} is not writable".format(attribute))
def __getitem__(self, modulename):
modulename = modulename.encode('ascii')
for m in loaded:
if modulename == m:
return MibRestrictedManager(self, [m])
raise KeyError("{0} is not a loaded module".format(modulename))
def __repr__(self):
return "".format(self._host)
def __enter__(self):
"""In a context, we group all "set" into a single request"""
self._osession = self._session
self._session = DelayedSetSession(self._session)
return self
def __exit__(self, type, value, traceback):
"""When we exit, we should execute all "set" requests"""
try:
if type is None:
self._session.commit()
finally:
self._session = self._osession
del self._osession
class Proxy:
def __repr__(self):
return "<{0} for {1}>".format(self.__class__.__name__,
repr(self.proxy)[1:-1])
class ProxyColumn(Proxy, MutableMapping):
"""Proxy for column access"""
def __init__(self, session, column, loose):
self.proxy = column
self.session = session
self._loose = loose
def _op(self, op, index, *args):
if not isinstance(index, tuple):
index = (index,)
indextype = self.proxy.table.index
if len(indextype) != len(index):
raise ValueError(
"{0} column uses the following "
"indexes: {1!r}".format(self.proxy, indextype))
oidindex = []
for i, ind in enumerate(index):
# Cast to the correct type since we need "toOid()"
ind = indextype[i].type(indextype[i], ind, raw=False)
oidindex.extend(ind.toOid())
result = getattr(
self.session,
op)(self.proxy.oid + tuple(oidindex),
*args)
if op != "set":
oid, result = result[0]
if result is not None:
try:
return self.proxy.type(self.proxy, result)
except ValueError:
if self._loose:
return result
raise
return None
def __getitem__(self, index):
return self._op("get", index)
def __setitem__(self, index, value):
if not isinstance(value, basictypes.Type):
value = self.proxy.type(self.proxy, value, raw=False)
self._op("set", index, value)
def __delitem__(self, index):
raise NotImplementedError("cannot suppress a column")
def keys(self):
return [k for k in self]
def has_key(self, object):
try:
self._op("get", object)
except:
return False
return True
def __iter__(self):
for k, _ in self.iteritems():
yield k
def __len__(self):
len(list(self.iteritems()))
def iteritems(self):
count = 0
oid = self.proxy.oid
indexes = self.proxy.table.index
for noid, result in self.session.walk(oid):
if noid <= oid:
noid = None
break
oid = noid
if not((len(oid) >= len(self.proxy.oid) and
oid[:len(self.proxy.oid)] ==
self.proxy.oid[:len(self.proxy.oid)])):
noid = None
break
# oid should be turned into index
index = oid[len(self.proxy.oid):]
target = []
for x in indexes:
l, o = x.type.fromOid(x, tuple(index))
target.append(x.type(x, o))
index = index[l:]
count = count + 1
if result is not None:
try:
result = self.proxy.type(self.proxy, result)
except ValueError:
if not self._loose:
raise
if len(target) == 1:
# Should work most of the time
yield target[0], result
else:
yield tuple(target), result
if count == 0:
# We did not find any element. Is it because the column is
# empty or because the column does not exist. We do a SNMP
# GET to know. If we get a "No such instance" exception,
# this means the column is empty. If we get "No such
# object", this means the column does not exist. We cannot
# make such a distinction with SNMPv1.
try:
self.session.get(self.proxy.oid)
except snmp.SNMPNoSuchInstance:
# OK, the set of result is really empty
raise StopIteration
except snmp.SNMPNoSuchName:
# SNMPv1, we don't know
pass
except snmp.SNMPNoSuchObject:
# The result is empty because the column is unknown
raise
raise StopIteration
loaded = []
def load(mibname):
"""Load a MIB in memory.
:param mibname: MIB name or filename
:type mibname: str
"""
m = mib.load(mibname)
if m not in loaded:
loaded.append(m)
if Manager._complete:
for o in mib.getScalars(m) + \
mib.getColumns(m):
setattr(Manager, str(o), 1)
snimpy-0.8.8/snimpy/mib.py 0000644 0000764 0000144 00000052464 12613477065 016172 0 ustar bernat users 0000000 0000000 #
# snimpy -- Interactive SNMP tool
#
# Copyright (C) Vincent Bernat
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
"""This module is a low-level interface to manipulate and
extract information from MIB files. It is a CFFI_ wrapper around
libsmi_. You may find convenient to use it in other projects but the
wrapper is merely here to serve *Snimpy* use and is therefore
incomplete.
.. _libsmi: http://www.ibr.cs.tu-bs.de/projects/libsmi/
.. _CFFI: http://cffi.readthedocs.org/
"""
try:
from snimpy._smi import lib as _smi
from snimpy._smi import ffi
except ImportError:
from snimpy.smi_build import ffi, get_lib
_smi = get_lib()
class SMIException(Exception):
"""SMI related exception. Any exception thrown in this module is
inherited from this one."""
class Node(object):
"""MIB node. An instance of this class represents a MIB node. It
can be specialized by other classes, like :class:`Scalar`,
:class:`Table`, :class:`Column`, :class:`Node`.
"""
def __init__(self, node):
"""Create a new MIB node.
:param node: libsmi node supporting this node.
"""
self.node = node
self._override_type = None
@property
def type(self):
"""Get the basic type associated with this node.
:return: The class from :mod:`basictypes` module which can
represent the node. When retrieving a valid value for
this node, the returned class can be instanciated to get
an appropriate representation.
"""
from snimpy import basictypes
if self._override_type:
t = self._override_type
else:
t = _smi.smiGetNodeType(self.node)
if t == ffi.NULL:
raise SMIException("unable to retrieve type of node")
target = {
_smi.SMI_BASETYPE_INTEGER32: basictypes.Integer,
_smi.SMI_BASETYPE_INTEGER64: basictypes.Integer,
_smi.SMI_BASETYPE_UNSIGNED32: {b"TimeTicks": basictypes.Timeticks,
None: basictypes.Unsigned32},
_smi.SMI_BASETYPE_UNSIGNED64: basictypes.Unsigned64,
_smi.SMI_BASETYPE_OCTETSTRING: {b"IpAddress": basictypes.IpAddress,
None: basictypes.OctetString},
_smi.SMI_BASETYPE_OBJECTIDENTIFIER: basictypes.Oid,
_smi.SMI_BASETYPE_ENUM: {b"TruthValue": basictypes.Boolean,
None: basictypes.Enum},
_smi.SMI_BASETYPE_BITS: basictypes.Bits
}.get(t.basetype, None)
if isinstance(target, dict):
tt = _smi.smiGetParentType(t)
target = target.get((t.name != ffi.NULL and ffi.string(t.name)) or
(tt.name != ffi.NULL and ffi.string(
tt.name)) or None,
target.get(None, None))
if target is None:
raise SMIException("unable to retrieve type of node")
return target
@property
def typeName(self):
"""Retrieves the name of the the node's current declared type
(not basic type).
:return: A string representing the current declared type,
suitable for assignment to type.setter.
"""
if self._override_type:
t = self._override_type
else:
t = _smi.smiGetNodeType(self.node)
# This occurs when the type is "implied".
if t.name == ffi.NULL:
t = _smi.smiGetParentType(t)
if t is None or t == ffi.NULL:
raise SMIException("unable to retrieve the declared type "
"of the node '{}'".format(self.node.name))
return ffi.string(t.name)
@typeName.setter
def typeName(self, type_name):
"""Override the node's type to type_name, found using _getType.
The new type must resolve to the same basictype.
:param type_name: string name of the type.
"""
current_override = self._override_type
declared_type = _smi.smiGetNodeType(self.node)
declared_basetype = self.type
new_type = _getType(type_name)
if not new_type:
raise SMIException("no type named {0} in any loaded module".format(
type_name))
# Easiest way to find the new basetype is to set the override
# and ask.
self._override_type = new_type
new_basetype = self.type
if declared_basetype != new_basetype:
self._override_type = current_override
raise SMIException("override type {1} not compatible with "
"basetype of {0}".format(
ffi.string(declared_type.name),
ffi.string(new_type.name)))
@typeName.deleter
def typeName(self):
"""Clears the type override."""
self._override_type = None
@property
def fmt(self):
"""Get node format. The node format is a string to use to display
a user-friendly version of the node. This is can be used for
both octet strings or integers (to make them appear as decimal
numbers).
:return: The node format as a string or None if there is no
format available.
"""
if self._override_type:
t = self._override_type
else:
t = _smi.smiGetNodeType(self.node)
tt = _smi.smiGetParentType(t)
f = (t != ffi.NULL and t.format != ffi.NULL and ffi.string(t.format) or
tt != ffi.NULL and tt.format != ffi.NULL and
ffi.string(tt.format)) or None
if f is None:
return None
return f.decode("ascii")
@property
def oid(self):
"""Get OID for the current node. The OID can then be used to request
the node from an SNMP agent.
:return: OID as a tuple
"""
return tuple([self.node.oid[i] for i in range(self.node.oidlen)])
@property
def ranges(self):
"""Get node ranges. An node can be restricted by a set of
ranges. For example, an integer can only be provided between
two values. For strings, the restriction is on the length of
the string.
The returned value can be `None` if no restriction on range
exists for the current node, a single value if the range is
fixed or a list of tuples or fixed values otherwise.
:return: The valid range for this node.
"""
t = _smi.smiGetNodeType(self.node)
if t == ffi.NULL:
return None
ranges = []
range = _smi.smiGetFirstRange(t)
while range != ffi.NULL:
m1 = self._convert(range.minValue)
m2 = self._convert(range.maxValue)
if m1 == m2:
ranges.append(m1)
else:
ranges.append((m1, m2))
range = _smi.smiGetNextRange(range)
if len(ranges) == 0:
return None
if len(ranges) == 1:
return ranges[0]
return ranges
@property
def enum(self):
"""Get possible enum values. When the node can only take a discrete
number of values, those values are defined in the MIB and can
be retrieved through this property.
:return: The dictionary of possible values keyed by the integer value.
"""
t = _smi.smiGetNodeType(self.node)
if t == ffi.NULL or t.basetype not in (_smi.SMI_BASETYPE_ENUM,
_smi.SMI_BASETYPE_BITS):
return None
result = {}
element = _smi.smiGetFirstNamedNumber(t)
while element != ffi.NULL:
result[self._convert(element.value)] = ffi.string(
element.name).decode("ascii")
element = _smi.smiGetNextNamedNumber(element)
return result
def __str__(self):
return ffi.string(self.node.name).decode("ascii")
def __repr__(self):
r = _smi.smiRenderNode(self.node, _smi.SMI_RENDER_ALL)
if r == ffi.NULL:
return "".format(
self.__class__.__name__, hex(id(self)))
r = ffi.gc(r, _smi.free)
module = _smi.smiGetNodeModule(self.node)
if module == ffi.NULL:
raise SMIException("unable to get module for {0}".format(
self.node.name))
return "<{0} {1} from '{2}'>".format(self.__class__.__name__,
ffi.string(r),
ffi.string(module.name))
def _convert(self, value):
attr = {_smi.SMI_BASETYPE_INTEGER32: "integer32",
_smi.SMI_BASETYPE_UNSIGNED32: "unsigned32",
_smi.SMI_BASETYPE_INTEGER64: "integer64",
_smi.SMI_BASETYPE_UNSIGNED64: "unsigned64"}.get(value.basetype,
None)
if attr is None:
raise SMIException("unexpected type found in range")
return getattr(value.value, attr)
class Scalar(Node):
"""MIB scalar node. This class represents a scalar value in the
MIB. A scalar value is a value not contained in a table.
"""
class Table(Node):
"""MIB table node. This class represents a table. A table is an
ordered collection of objects consisting of zero or more
rows. Each object in the table is identified using an index. An
index can be a single value or a list of values.
"""
@property
def columns(self):
"""Get table columns. The columns are the different kind of objects
that can be retrieved in a table.
:return: list of table columns (:class:`Column` instances)
"""
child = _smi.smiGetFirstChildNode(self.node)
if child == ffi.NULL:
return []
if child.nodekind != _smi.SMI_NODEKIND_ROW:
raise SMIException("child {0} of {1} is not a row".format(
ffi.string(child.name),
ffi.string(self.node.name)))
columns = []
child = _smi.smiGetFirstChildNode(child)
while child != ffi.NULL:
if child.nodekind != _smi.SMI_NODEKIND_COLUMN:
raise SMIException("child {0} of {1} is not a column".format(
ffi.string(child.name),
ffi.string(self.node.name)))
columns.append(Column(child))
child = _smi.smiGetNextChildNode(child)
return columns
@property
def _row(self):
"""Get the table row.
:return: row object (as an opaque object)
"""
child = _smi.smiGetFirstChildNode(self.node)
if child != ffi.NULL and child.indexkind == _smi.SMI_INDEX_AUGMENT:
child = _smi.smiGetRelatedNode(child)
if child == ffi.NULL:
raise SMIException("AUGMENT index for {0} but "
"unable to retrieve it".format(
ffi.string(self.node.name)))
if child == ffi.NULL:
raise SMIException("{0} does not have a row".format(
ffi.string(self.node.name)))
if child.nodekind != _smi.SMI_NODEKIND_ROW:
raise SMIException("child {0} of {1} is not a row".format(
ffi.string(child.name),
ffi.string(self.node.name)))
if child.indexkind != _smi.SMI_INDEX_INDEX:
raise SMIException("child {0} of {1} has an unhandled "
"kind of index".format(
ffi.string(child.name),
ffi.string(self.node.name)))
return child
@property
def implied(self):
"""Is the last index implied? An implied index is an index whose size
is not fixed but who is not prefixed by its size because this
is the last index of a table.
:return: `True` if and only if the last index is implied.
"""
child = self._row
if child.implied:
return True
return False
@property
def index(self):
"""Get indexes for a table. The indexes are used to locate a precise
row in a table. They are a subset of the table columns.
:return: The list of indexes (as :class:`Column` instances) of
the table.
"""
child = self._row
lindex = []
element = _smi.smiGetFirstElement(child)
while element != ffi.NULL:
nelement = _smi.smiGetElementNode(element)
if nelement == ffi.NULL:
raise SMIException("cannot get index "
"associated with {0}".format(
ffi.string(self.node.name)))
if nelement.nodekind != _smi.SMI_NODEKIND_COLUMN:
raise SMIException("index {0} for {1} is "
"not a column".format(
ffi.string(nelement.name),
ffi.string(self.node.name)))
lindex.append(Column(nelement))
element = _smi.smiGetNextElement(element)
return lindex
class Column(Node):
"""MIB column node. This class represent a column of a table."""
@property
def table(self):
"""Get table associated with this column.
:return: The :class:`Table` instance associated to this
column.
"""
parent = _smi.smiGetParentNode(self.node)
if parent == ffi.NULL:
raise SMIException("unable to get parent of {0}".format(
ffi.string(self.node.name)))
if parent.nodekind != _smi.SMI_NODEKIND_ROW:
raise SMIException("parent {0} of {1} is not a row".format(
ffi.string(parent.name),
ffi.string(self.node.name)))
parent = _smi.smiGetParentNode(parent)
if parent == ffi.NULL:
raise SMIException("unable to get parent of {0}".format(
ffi.string(self.node.name)))
if parent.nodekind != _smi.SMI_NODEKIND_TABLE:
raise SMIException("parent {0} of {1} is not a table".format(
ffi.string(parent.name),
ffi.string(self.node.name)))
t = Table(parent)
return t
_lastError = None
@ffi.callback("void(char *, int, int, char *, char*)")
def _logError(path, line, severity, msg, tag):
global _lastError
if path != ffi.NULL and msg != ffi.NULL:
_lastError = "{0}:{1}: {2}".format(ffi.string(path), line,
ffi.string(msg))
else:
_lastError = None
def reset():
"""Reset libsmi to its initial state."""
_smi.smiExit()
if _smi.smiInit(b"snimpy") < 0:
raise SMIException("unable to init libsmi")
_smi.smiSetErrorLevel(1)
_smi.smiSetErrorHandler(_logError)
try:
_smi.smiSetFlags(_smi.SMI_FLAG_ERRORS | _smi.SMI_FLAG_RECURSIVE)
except TypeError:
pass # We are being mocked
def path(path=None):
"""Set or get a search path to libsmi.
When no path is provided, return the current path,
unmodified. Otherwise, set the path to the specified value.
:param path: The string to be used to change the search path or
`None`
"""
if path is None:
# Get the path
path = _smi.smiGetPath()
if path == ffi.NULL:
raise SMIException("unable to get current libsmi path")
path = ffi.gc(path, _smi.free)
result = ffi.string(path)
return result.decode("utf8")
# Set the path
if not isinstance(path, bytes):
path = path.encode("utf8")
if _smi.smiSetPath(path) < 0:
raise SMIException("unable to set the path {0}".format(path))
def _get_module(name):
"""Get the SMI module from its name.
:param name: The name of the module
:return: The SMI module or `None` if not found (not loaded)
"""
if not isinstance(name, bytes):
name = name.encode("ascii")
m = _smi.smiGetModule(name)
if m == ffi.NULL:
return None
if m.conformance and m.conformance <= 1:
return None
return m
def _kind2object(kind):
return {
_smi.SMI_NODEKIND_NODE: Node,
_smi.SMI_NODEKIND_SCALAR: Scalar,
_smi.SMI_NODEKIND_TABLE: Table,
_smi.SMI_NODEKIND_COLUMN: Column
}.get(kind, Node)
def get(mib, name):
"""Get a node by its name.
:param mib: The MIB name to query
:param name: The object name to get from the MIB
:return: the requested MIB node (:class:`Node`)
"""
if not isinstance(mib, bytes):
mib = mib.encode("ascii")
module = _get_module(mib)
if module is None:
raise SMIException("no module named {0}".format(mib))
node = _smi.smiGetNode(module, name.encode("ascii"))
if node == ffi.NULL:
raise SMIException("in {0}, no node named {1}".format(
mib, name))
pnode = _kind2object(node.nodekind)
return pnode(node)
def getByOid(oid):
"""Get a node by its OID.
:param oid: The OID as a tuple
:return: The requested MIB node (:class:`Node`)
"""
node = _smi.smiGetNodeByOID(len(oid), oid)
if node == ffi.NULL:
raise SMIException("no node for {0}".format(
".".join([str(o) for o in oid])))
pnode = _kind2object(node.nodekind)
return pnode(node)
def _getType(type_name):
"""Searches for a smi type through all loaded modules.
:param type_name: The name of the type to search for.
:return: The requested type (:class:`smi.SmiType`), if found, or None.
"""
if not isinstance(type_name, bytes):
type_name = type_name.encode("ascii")
for module in _loadedModules():
new_type = _smi.smiGetType(module, type_name)
if new_type != ffi.NULL:
return new_type
return None
def _get_kind(mib, kind):
"""Get nodes of a given kind from a MIB.
:param mib: The MIB name to search objects for
:param kind: The SMI kind of object
:return: The list of matched MIB nodes for the MIB
"""
if not isinstance(mib, bytes):
mib = mib.encode("ascii")
module = _get_module(mib)
if module is None:
raise SMIException("no module named {0}".format(mib))
lnode = []
node = _smi.smiGetFirstNode(module, kind)
while node != ffi.NULL:
lnode.append(_kind2object(kind)(node))
node = _smi.smiGetNextNode(node, kind)
return lnode
def getNodes(mib):
"""Return all nodes from a given MIB.
:param mib: The MIB name
:return: The list of all MIB nodes for the MIB
:rtype: list of :class:`Node` instances
"""
return _get_kind(mib, _smi.SMI_NODEKIND_NODE)
def getScalars(mib):
"""Return all scalars from a given MIB.
:param mib: The MIB name
:return: The list of all scalars for the MIB
:rtype: list of :class:`Scalar` instances
"""
return _get_kind(mib, _smi.SMI_NODEKIND_SCALAR)
def getTables(mib):
"""Return all tables from a given MIB.
:param mib: The MIB name
:return: The list of all tables for the MIB
:rtype: list of :class:`Table` instances
"""
return _get_kind(mib, _smi.SMI_NODEKIND_TABLE)
def getColumns(mib):
"""Return all columns from a givem MIB.
:param mib: The MIB name
:return: The list of all columns for the MIB
:rtype: list of :class:`Column` instances
"""
return _get_kind(mib, _smi.SMI_NODEKIND_COLUMN)
def load(mib):
"""Load a MIB into the library.
:param mib: The MIB to load, either a filename or a MIB name.
:return: The MIB name that has been loaded.
:except SMIException: The requested MIB cannot be loaded.
"""
if not isinstance(mib, bytes):
mib = mib.encode("ascii")
modulename = _smi.smiLoadModule(mib)
if modulename == ffi.NULL:
raise SMIException("unable to find {0} (check the path)".format(mib))
modulename = ffi.string(modulename)
if not _get_module(modulename.decode("ascii")):
details = "check with smilint -s -l1"
if _lastError is not None:
details = "{0}: {1}".format(_lastError,
details)
raise SMIException(
"{0} contains major SMI error ({1})".format(mib, details))
return modulename
def _loadedModules():
"""Generates the list of loaded modules.
:yield: The :class:`smi.SmiModule` of all currently loaded modules.
"""
module = _smi.smiGetFirstModule()
while module != ffi.NULL:
yield module
module = _smi.smiGetNextModule(module)
def loadedMibNames():
"""Generates the list of loaded MIB names.
:yield: The names of all currently loaded MIBs.
"""
for module in _loadedModules():
yield ffi.string(module.name).decode('utf-8')
reset()
snimpy-0.8.8/snimpy/snmp.py 0000644 0000764 0000144 00000034661 12622127607 016371 0 ustar bernat users 0000000 0000000 #
# snimpy -- Interactive SNMP tool
#
# Copyright (C) Vincent Bernat
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
"""
This module is a low-level interface to build SNMP requests, send
them and receive answers. It is built on top of pysnmp_ but the
exposed interface is far simpler. It is also far less complete and
there is an important dependency to the :mod:`basictypes` module for
type coercing.
.. _pysnmp: http://pysnmp.sourceforge.net/
"""
import re
import socket
import inspect
import threading
from pysnmp.entity.rfc3413.oneliner import cmdgen
from pysnmp.proto import rfc1902, rfc1905
from pysnmp.smi import error
class SNMPException(Exception):
"""SNMP related base exception. All SNMP exceptions are inherited from
this one. The inherited exceptions are named after the name of the
corresponding SNMP error.
"""
class SNMPTooBig(SNMPException):
pass
class SNMPNoSuchName(SNMPException):
pass
class SNMPBadValue(SNMPException):
pass
class SNMPReadOnly(SNMPException):
pass
# Dynamically build remaining (v2) exceptions
for name, obj in inspect.getmembers(error):
if name.endswith("Error") and \
inspect.isclass(obj) and \
issubclass(obj, error.MibOperationError) and \
obj != error.MibOperationError:
name = str("SNMP{0}".format(name[:-5]))
globals()[name] = type(name, (SNMPException,), {})
del name
del obj
class Session(object):
"""SNMP session. An instance of this object will represent an SNMP
session. From such an instance, one can get information from the
associated agent."""
_tls = threading.local()
def __init__(self, host,
community="public", version=2,
secname=None,
authprotocol=None,
authpassword=None,
privprotocol=None,
privpassword=None,
bulk=40):
"""Create a new SNMP session.
:param host: The hostname or IP address of the agent to
connect to. Optionally, the port can be specified
separated with a double colon.
:type host: str
:param community: The community to transmit to the agent for
authorization purpose. This parameter is ignored if the
specified version is 3.
:type community: str
:param version: The SNMP version to use to talk with the
agent. Possible values are `1`, `2` (community-based) or
`3`.
:type version: int
:param secname: Security name to use for SNMPv3 only.
:type secname: str
:param authprotocol: Authorization protocol to use for
SNMPv3. This can be `None` or either the string `SHA` or
`MD5`.
:type authprotocol: None or str
:param authpassword: Authorization password if authorization
protocol is not `None`.
:type authpassword: str
:param privprotocol: Privacy protocol to use for SNMPv3. This
can be `None` or either the string `AES`, `AES128`,
`AES192`, `AES256` or `3DES`.
:type privprotocol: None or str
:param privpassword: Privacy password if privacy protocol is
not `None`.
:type privpassword: str
:param bulk: Max repetition value for `GETBULK` requests. Set
to `0` to disable.
:type bulk: int
"""
self._host = host
self._version = version
if version == 3:
self._cmdgen = cmdgen.CommandGenerator()
else:
if not hasattr(self._tls, "cmdgen"):
self._tls.cmdgen = cmdgen.CommandGenerator()
self._cmdgen = self._tls.cmdgen
# Put authentication stuff in self._auth
if version in [1, 2]:
self._auth = cmdgen.CommunityData(
community, community, version - 1)
elif version == 3:
if secname is None:
secname = community
try:
authprotocol = {
None: cmdgen.usmNoAuthProtocol,
"MD5": cmdgen.usmHMACMD5AuthProtocol,
"SHA": cmdgen.usmHMACSHAAuthProtocol,
"SHA1": cmdgen.usmHMACSHAAuthProtocol
}[authprotocol]
except KeyError:
raise ValueError("{0} is not an acceptable authentication "
"protocol".format(authprotocol))
try:
privprotocol = {
None: cmdgen.usmNoPrivProtocol,
"DES": cmdgen.usmDESPrivProtocol,
"3DES": cmdgen.usm3DESEDEPrivProtocol,
"AES": cmdgen.usmAesCfb128Protocol,
"AES128": cmdgen.usmAesCfb128Protocol,
"AES192": cmdgen.usmAesCfb192Protocol,
"AES256": cmdgen.usmAesCfb256Protocol,
}[privprotocol]
except KeyError:
raise ValueError("{0} is not an acceptable privacy "
"protocol".format(privprotocol))
self._auth = cmdgen.UsmUserData(secname,
authpassword,
privpassword,
authprotocol,
privprotocol)
else:
raise ValueError("unsupported SNMP version {0}".format(version))
# Put transport stuff into self._transport
mo = re.match(r'^(?:'
r'\[(?P[\d:]+)\]|'
r'(?P[\d\.]+)|'
r'(?P.*?))'
r'(?::(?P\d+))?$',
host)
if mo.group("port"):
port = int(mo.group("port"))
else:
port = 161
if mo.group("ipv6"):
self._transport = cmdgen.Udp6TransportTarget((mo.group("ipv6"),
port))
elif mo.group("ipv4"):
self._transport = cmdgen.UdpTransportTarget((mo.group("ipv4"),
port))
else:
results = socket.getaddrinfo(mo.group("any"),
port,
0,
socket.SOCK_DGRAM,
socket.IPPROTO_UDP)
# We should try to connect to each result to determine if
# the given family is available. However, we cannot do
# that over UDP. Let's implement a safe choice. If we have
# an IPv4 address, use that. If not, use IPv6. If we want
# to add an option to force IPv6, it is a good place.
if [x for x in results if x[0] == socket.AF_INET]:
self._transport = cmdgen.UdpTransportTarget((mo.group("any"),
port))
else:
self._transport = cmdgen.Udp6TransportTarget((mo.group("any"),
port))
# Bulk stuff
self.bulk = bulk
def _check_exception(self, value):
"""Check if the given ASN1 value is an exception"""
if isinstance(value, rfc1905.NoSuchObject):
raise SNMPNoSuchObject("No such object was found") # nopep8
if isinstance(value, rfc1905.NoSuchInstance):
raise SNMPNoSuchInstance("No such instance exists") # nopep8
if isinstance(value, rfc1905.EndOfMibView):
raise SNMPEndOfMibView("End of MIB was reached") # nopep8
def _convert(self, value):
"""Convert a PySNMP value to some native Python type"""
for cl, fn in {rfc1902.Integer: int,
rfc1902.Integer32: int,
rfc1902.OctetString: bytes,
rfc1902.IpAddress: value.prettyOut,
rfc1902.Counter32: int,
rfc1902.Counter64: int,
rfc1902.Gauge32: int,
rfc1902.Unsigned32: int,
rfc1902.TimeTicks: int,
rfc1902.Bits: str,
rfc1902.Opaque: str,
rfc1902.univ.ObjectIdentifier: tuple}.items():
if isinstance(value, cl):
return fn(value)
self._check_exception(value)
raise NotImplementedError("unable to convert {0}".format(repr(value)))
def _op(self, cmd, *oids):
"""Apply an SNMP operation"""
errorIndication, errorStatus, errorIndex, varBinds = cmd(
self._auth, self._transport, *oids)
if errorIndication:
self._check_exception(errorIndication)
raise SNMPException(str(errorIndication))
if errorStatus:
# We try to find a builtin exception with the same message
exc = str(errorStatus.prettyPrint())
exc = re.sub(r'\W+', '', exc)
exc = "SNMP{0}".format(exc[0].upper() + exc[1:])
if str(exc) in globals():
raise globals()[exc]
raise SNMPException(errorStatus.prettyPrint())
if cmd in [self._cmdgen.getCmd, self._cmdgen.setCmd]:
results = [(tuple(name), val) for name, val in varBinds]
else:
results = [(tuple(name), val)
for row in varBinds for name, val in row]
if len(results) > 0 and isinstance(results[-1][1],
rfc1905.EndOfMibView):
results = results[:-1]
if len(results) == 0:
if cmd not in [self._cmdgen.nextCmd, self._cmdgen.bulkCmd]:
raise SNMPException("empty answer")
# This seems to be filtered
raise SNMPEndOfMibView("no more stuff after this OID") # nopep8
return tuple([(oid, self._convert(val)) for oid, val in results])
def get(self, *oids):
"""Retrieve an OID value using GET.
:param oids: a list of OID to retrieve. An OID is a tuple.
:return: a list of tuples with the retrieved OID and the raw value.
"""
return self._op(self._cmdgen.getCmd, *oids)
def walk(self, *oids):
"""Retrieve OIDs values using GETBULK or GETNEXT. The method is called
"walk" but this is either a GETBULK or a GETNEXT. The later is
only used for SNMPv1 or if bulk has been disabled using
:meth:`bulk` property.
:param oids: a list of OID to retrieve. An OID is a tuple.
:return: a list of tuples with the retrieved OID and the raw value.
"""
if self._version == 1 or not self.bulk:
return self._op(self._cmdgen.nextCmd, *oids)
args = [0, self.bulk] + list(oids)
try:
return self._op(self._cmdgen.bulkCmd, *args)
except SNMPTooBig:
# Let's try to ask for less values. We will never increase
# bulk again. We cannot increase it just after the walk
# because we may end up requesting everything twice (or
# more).
nbulk = self.bulk / 2 or False
if nbulk != self.bulk:
self.bulk = nbulk
return self.walk(*oids)
raise
def set(self, *args):
"""Set an OID value using SET. This function takes an odd number of
arguments. They are working by pair. The first member is an
OID and the second one is :class:`basictypes.Type` instace
whose `pack()` method will be used to transform into the
appropriate form.
:return: a list of tuples with the retrieved OID and the raw value.
"""
if len(args) % 2 != 0:
raise ValueError("expect an even number of arguments for SET")
varbinds = zip(*[args[0::2], [v.pack() for v in args[1::2]]])
return self._op(self._cmdgen.setCmd, *varbinds)
def __repr__(self):
return "{0}(host={1},version={2})".format(
self.__class__.__name__,
self._host,
self._version)
@property
def timeout(self):
"""Get timeout value for the current session.
:return: Timeout value in microseconds.
"""
return self._transport.timeout * 1000000
@timeout.setter
def timeout(self, value):
"""Set timeout value for the current session.
:param value: Timeout value in microseconds.
"""
value = int(value)
if value <= 0:
raise ValueError("timeout is a positive integer")
self._transport.timeout = value / 1000000.
@property
def retries(self):
"""Get number of times a request is retried.
:return: Number of retries for each request.
"""
return self._transport.retries
@retries.setter
def retries(self, value):
"""Set number of times a request is retried.
:param value: Number of retries for each request.
"""
value = int(value)
if value < 0:
raise ValueError("retries is a non-negative integer")
self._transport.retries = value
@property
def bulk(self):
"""Get bulk settings.
:return: `False` if bulk is disabled or a non-negative integer
for the number of repetitions.
"""
return self._bulk
@bulk.setter
def bulk(self, value):
"""Set bulk settings.
:param value: `False` to disable bulk or a non-negative
integer for the number of allowed repetitions.
"""
if value is False:
self._bulk = False
return
value = int(value)
if value <= 0:
raise ValueError("{0} is not an appropriate value "
"for max repeater parameter".format(
value))
self._bulk = value
snimpy-0.8.8/snimpy/__main__.py 0000644 0000764 0000144 00000001611 12376564336 017133 0 ustar bernat users 0000000 0000000 #
# snimpy -- Interactive SNMP tool
#
# Copyright (C) Vincent Bernat
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
if __name__ == "__main__": # pragma: no cover
from snimpy import main
main.interact()
snimpy-0.8.8/snimpy/main.py 0000644 0000764 0000144 00000011366 12621670211 016326 0 ustar bernat users 0000000 0000000 #
# snimpy -- Interactive SNMP tool
#
# Copyright (C) Vincent Bernat
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# We are using readline module of Python. Depending on the Python
# distribution, this module may be linked to GNU Readline which is
# GPLv2 licensed.
"""Provide an interactive shell for snimpy.
The main method is C{interact()}. It will either use IPython if
available or just plain python Shell otherwise. It will also try to
use readline if available.
For IPython, there is some configuration stuff to use a special
profile. This is the recommended way to use it. It allows a separate
history.
"""
import sys
import os
import atexit
import code
from datetime import timedelta
import snimpy
from snimpy import manager
from snimpy.config import Conf
def interact(argv=sys.argv): # pragma: no cover
conf = Conf().load()
banner = "\033[1mSnimpy\033[0m ({0}) -- {1}.\n".format(
snimpy.__version__, snimpy.__doc__)
banner += " load -> load an additional MIB\n"
banner += " M -> manager object"
local = {"conf": conf,
"M": manager.Manager,
"load": manager.load,
"timedelta": timedelta,
"snmp": manager.snmp
}
if len(argv) <= 1:
manager.Manager._complete = True
for ms in conf.mibs:
manager.load(ms)
globals().update(local)
if len(argv) > 1:
argv = argv[1:]
exec(compile(open(argv[0]).read(), argv[0], 'exec')) in local
return
try:
try:
try:
# ipython >= 1.0
from IPython.terminal.embed import \
InteractiveShellEmbed
except ImportError:
# ipython >= 0.11
from IPython.frontend.terminal.embed import \
InteractiveShellEmbed
try:
# ipython >= 4
from traitlets.config.loader import Config
except ImportError:
# ipython >= 0.11
from IPython.config.loader import Config
cfg = Config()
try:
# >= 0.12
cfg.PromptManager.in_template = "Snimpy [\\#]> "
cfg.PromptManager.out_template = "Snimpy [\\#]: "
except ImportError:
# 0.11
cfg.InteractiveShellEmbed.prompt_in1 = "Snimpy [\\#]> "
cfg.InteractiveShellEmbed.prompt_out = "Snimpy [\\#]: "
if conf.ipythonprofile:
cfg.InteractiveShellEmbed.profile = conf.ipythonprofile
shell = InteractiveShellEmbed(
config=cfg,
banner1=banner,
user_ns=local)
# Not interested by traceback in this module
shell.InteractiveTB.tb_offset += 1
except ImportError:
# ipython < 0.11
from IPython.Shell import IPShellEmbed
argv = ["-prompt_in1", "Snimpy [\\#]> ",
"-prompt_out", "Snimpy [\\#]: "]
if conf.ipythonprofile:
argv += ["-profile", conf.ipythonprofile]
shell = IPShellEmbed(argv=argv,
banner=banner, user_ns=local)
# Not interested by traceback in this module
shell.IP.InteractiveTB.tb_offset += 1
except ImportError:
shell = None
if shell and conf.ipython:
shell()
else:
try:
import rlcompleter
import readline
except ImportError:
readline = None
if readline:
if conf.histfile:
try:
readline.read_history_file(
os.path.expanduser(conf.histfile))
except IOError:
pass
atexit.register(lambda: readline.write_history_file(
os.path.expanduser(conf.histfile)))
readline.set_completer(rlcompleter.Completer(local).complete)
readline.parse_and_bind("tab: menu-complete")
sys.ps1 = conf.prompt
code.interact(banner=banner, local=local)
snimpy-0.8.8/snimpy/smi_build.py 0000644 0000764 0000144 00000011561 12600052762 017350 0 ustar bernat users 0000000 0000000 #
# snimpy -- Interactive SNMP tool
#
# Copyright (C) 2015 Vincent Bernat
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
"""This module just exports libsmi through CFFI_.
.. _CFFI: http://cffi.readthedocs.org/
"""
from cffi import FFI
_CDEF = """
typedef char *SmiIdentifier;
typedef unsigned long SmiUnsigned32;
typedef long SmiInteger32;
typedef unsigned long long SmiUnsigned64;
typedef long long SmiInteger64;
typedef unsigned int SmiSubid;
typedef float SmiFloat32;
typedef double SmiFloat64;
typedef long double SmiFloat128;
typedef enum SmiBasetype {
SMI_BASETYPE_INTEGER32,
SMI_BASETYPE_OCTETSTRING,
SMI_BASETYPE_OBJECTIDENTIFIER,
SMI_BASETYPE_UNSIGNED32,
SMI_BASETYPE_INTEGER64,
SMI_BASETYPE_UNSIGNED64,
SMI_BASETYPE_ENUM,
SMI_BASETYPE_BITS,
...
} SmiBasetype;
typedef struct SmiType {
SmiIdentifier name;
SmiBasetype basetype;
char *format;
...;
} SmiType;
typedef enum SmiIndexkind {
SMI_INDEX_INDEX,
SMI_INDEX_AUGMENT,
...
} SmiIndexkind;
typedef unsigned int SmiNodekind;
#define SMI_NODEKIND_NODE ...
#define SMI_NODEKIND_SCALAR ...
#define SMI_NODEKIND_TABLE ...
#define SMI_NODEKIND_ROW ...
#define SMI_NODEKIND_COLUMN ...
typedef struct SmiNode {
SmiIdentifier name;
unsigned int oidlen;
SmiSubid *oid;
char *format;
SmiIndexkind indexkind;
int implied;
SmiNodekind nodekind;
...;
} SmiNode;
typedef struct SmiValue {
SmiBasetype basetype;
union {
SmiUnsigned64 unsigned64;
SmiInteger64 integer64;
SmiUnsigned32 unsigned32;
SmiInteger32 integer32;
SmiFloat32 float32;
SmiFloat64 float64;
SmiFloat128 float128;
SmiSubid *oid;
char *ptr;
} value;
...;
} SmiValue;
typedef struct SmiRange {
SmiValue minValue;
SmiValue maxValue;
} SmiRange;
typedef struct SmiModule {
SmiIdentifier name;
int conformance;
...;
} SmiModule;
typedef struct SmiElement {
...;
} SmiElement;
typedef struct SmiNamedNumber {
SmiIdentifier name;
SmiValue value;
} SmiNamedNumber;
typedef void (SmiErrorHandler) (char *path, int line,
int severity, char *msg, char *tag);
int smiInit(const char *);
void smiExit(void);
void smiSetErrorLevel(int);
void smiSetErrorHandler(SmiErrorHandler *);
void smiSetFlags(int);
int smiSetPath(const char *);
char *smiGetPath(void);
char *smiLoadModule(const char *);
SmiModule *smiGetFirstModule(void);
SmiModule *smiGetNextModule(SmiModule *);
SmiModule *smiGetModule(const char *);
SmiModule *smiGetNodeModule(SmiNode *);
SmiType *smiGetNodeType(SmiNode *);
SmiType *smiGetParentType(SmiType *);
SmiType *smiGetType(SmiModule *, char *);
SmiModule *smiGetTypeModule(SmiType *);
char *smiRenderNode(SmiNode *, int);
SmiElement *smiGetFirstElement(SmiNode *);
SmiElement *smiGetNextElement(SmiElement *);
SmiNode *smiGetElementNode(SmiElement *);
SmiRange *smiGetFirstRange(SmiType *);
SmiRange *smiGetNextRange(SmiRange *);
SmiNode *smiGetNode(SmiModule *, const char *);
SmiNode *smiGetNodeByOID(unsigned int oidlen, SmiSubid *);
SmiNode *smiGetFirstNode(SmiModule *, SmiNodekind);
SmiNode *smiGetNextNode(SmiNode *, SmiNodekind);
SmiNode *smiGetParentNode(SmiNode *);
SmiNode *smiGetRelatedNode(SmiNode *);
SmiNode *smiGetFirstChildNode(SmiNode *);
SmiNode *smiGetNextChildNode(SmiNode *);
SmiNamedNumber *smiGetFirstNamedNumber(SmiType *);
SmiNamedNumber *smiGetNextNamedNumber(SmiNamedNumber *);
void free(void *);
#define SMI_FLAG_ERRORS ...
#define SMI_FLAG_RECURSIVE ...
#define SMI_RENDER_ALL ...
"""
_SOURCE = """
#include
"""
ffi = FFI()
ffi.cdef(_CDEF)
if hasattr(ffi, 'set_source'):
ffi.set_source("snimpy._smi", _SOURCE,
libraries=["smi"])
def get_lib():
return ffi.verify(_SOURCE, libraries=["smi"])
if __name__ == "__main__":
ffi.compile()
snimpy-0.8.8/snimpy/__init__.py 0000644 0000764 0000144 00000000305 12600052762 017132 0 ustar bernat users 0000000 0000000 """interactive SNMP tool"""
__author__ = 'Vincent Bernat'
__email__ = 'bernat@luffy.cx'
try:
from snimpy._version import __version__ # nopep8
except ImportError:
__version__ = '0.0~dev'
snimpy-0.8.8/README.rst 0000644 0000764 0000144 00000004001 12600052762 015166 0 ustar bernat users 0000000 0000000 ===============================
snimpy
===============================
.. image:: https://badge.fury.io/py/snimpy.png
:target: http://badge.fury.io/py/snimpy
.. image:: https://travis-ci.org/vincentbernat/snimpy.png?branch=master
:target: https://travis-ci.org/vincentbernat/snimpy
.. image:: https://pypip.in/d/snimpy/badge.png
:target: https://crate.io/packages/snimpy?version=latest
.. image:: https://coveralls.io/repos/vincentbernat/snimpy/badge.png
:target: https://coveralls.io/r/vincentbernat/snimpy
Interactive SNMP tool.
*Snimpy* is a Python-based tool providing a simple interface to build
SNMP query. Here is a very simplistic example that allows us to
display the routing table of a given host::
load("IP-FORWARD-MIB")
m=M("localhost", "public", 2)
routes = m.ipCidrRouteNextHop
for x in routes:
net, netmask, tos, src = x
print("%15s/%-15s via %-15s src %-15s" % (net, netmask, routes[x], src))
You can either use *Snimpy* interactively throught its console
(derived from Python own console or from IPython_ if available) or
write *Snimpy* scripts which are just Python scripts with some global
variables available.
.. _IPython: http://ipython.org
* Free software: ISC license
* Documentation: http://snimpy.rtfd.org.
*Snimpy* requires libsmi_ to work correctly. See the documentation for
more information.
Features
--------
*Snimpy* is aimed at being the more Pythonic possible. You should forget
that you are doing SNMP requests. *Snimpy* will rely on MIB to hide SNMP
details. Here are some "features":
* MIB parser based on libsmi (through CFFI)
* SNMP requests are handled by PySNMP (SNMPv1, SNMPv2 and SNMPv3
support)
* scalars are just attributes of your session object
* columns are like a Python dictionary and made available as an
attribute
* getting an attribute is like issuing a GET method
* setting an attribute is like issuing a SET method
* iterating over a table is like using GETNEXT
* when something goes wrong, you get an exception