|
1 | | -## gosnmp-python |
| 1 | +# gosnmp-python |
2 | 2 |
|
3 | | -The purpose of this module is to provide a Python interface to the Golang [gosnmp](https://github.com/soniah/gosnmp) module. |
| 3 | +This library binds [our fork](https://github.com/ftpsolutions/gosnmp) of [gosnmp](https://github.com/gosnmp/gosnmp) for use in Python3 |
| 4 | +using [gopy](https://github.com/go-python/gopy). |
4 | 5 |
|
5 | | -It was made very easy with the help of the Golang [gopy](https://github.com/go-python/gopy) module. |
| 6 | +## Versions |
6 | 7 |
|
7 | | -#### Versions |
| 8 | +All versions 1.0.0 and up support Python 3 only! If you need Python 2 support, check out the following: |
8 | 9 |
|
9 | | -This version (0.91) is the last version to support Python 2; all versions after this have been subject to a refactor and support Python 3 |
10 | | -only. |
| 10 | +- [https://github.com/ftpsolutions/gosnmp-python/tree/v0.91](https://github.com/ftpsolutions/gosnmp-python/tree/v0.91) |
| 11 | +- https://pypi.org/project/gosnmp-python/0.91/ |
11 | 12 |
|
12 | | -#### Limitations |
| 13 | +## Concept |
13 | 14 |
|
14 | | -* Python command needs to be prefixed with GODEBUG=cgocheck=0 (or have that in the environment) |
15 | | -* I've not implemented walk (as I didn't need it for my use-case, I just use get_next with Python) |
16 | | -* Seems to have some odd memory problems with PyPy (via CFFI); lots of locks and stuff to try and alleviate that |
| 15 | +In the early days `gopy` was fairly limited in it's ability to track object allocation across the border of Go and Python. |
17 | 16 |
|
18 | | -#### Prerequisites |
| 17 | +As a result, our implementation is fairly naive- we only pass primitive types from Go to Python (nothing that comes by reference). |
19 | 18 |
|
20 | | -* Go 1.13 |
21 | | -* Python 2.7+ |
22 | | -* pip |
23 | | -* virtualenvwrapper |
24 | | -* pkgconfig/pkg-config |
| 19 | +Session are managed entirely on the Go side and identified with an integer- here are a few function signatures to demonstrate: |
25 | 20 |
|
26 | | -#### Installation (from PyPI) |
| 21 | +- `NewRPCSessionV2c(hostname string, port int, community string, timeout, retries int) uint64` |
| 22 | +- `RPCConnect(sessionID uint64) error` |
| 23 | +- `RPCGet(sessionID uint64, oid string) (string, error)` |
| 24 | +- `RPCClose(sessionID uint64) error` |
27 | 25 |
|
28 | | -* ```python -m pip install gosnmp-python``` |
| 26 | +The functions that return complex data do so in a special JSON-based format- at this point `gopy` does it's magic and those functions are |
| 27 | +made available to Python. |
29 | 28 |
|
30 | | -#### Installation (for prod) |
| 29 | +We then have `RPCSession` abstraction on the Python side that pulls things together in a class for convenience (saving you need the to keep |
| 30 | +track of the identifiers and handling deserialisation). |
31 | 31 |
|
32 | | -* ```python setup.py install``` |
| 32 | +## Weird gotchas |
33 | 33 |
|
34 | | -#### Making a python wheel install file (for distribution) |
| 34 | +We're building for Python3 and we use a `python-config` script for Python3 however we're using a `python.pc` file from Python2. |
35 | 35 |
|
36 | | -* ```python setup.py bdist_wheel``` |
| 36 | +I dunno why this has to be this way, but it's the only way I can get the C Python API GIL lock/unlock calls to be available to |
| 37 | +Go (`C.PyEval_SaveThread()` and `C.PyEval_RestoreThread(tState)`). |
37 | 38 |
|
38 | | -#### Setup (for dev) |
| 39 | +So if you're wondering why Python2 still comes into it here and there- that's why. Doesn't seem to cause any problems. |
39 | 40 |
|
40 | | -Ensure both go and pkg-config are installed. |
| 41 | +## Prerequisites |
41 | 42 |
|
42 | | -* ```mkvirtualenvwrapper -p (/path/to/pypy) gosnmp-python``` |
43 | | -* ```pip install -r requirements-dev.txt``` |
44 | | -* ```./build.sh``` |
45 | | -* ```GODEBUG=cgocheck=0 py.test -v``` |
| 43 | +- MacOS |
| 44 | +- CPython3.8+ |
| 45 | +- virtualenv |
| 46 | + - `pip install virtualenv` |
| 47 | +- pkgconfig |
| 48 | + - `brew install pkg-config` |
| 49 | +- Docker |
46 | 50 |
|
47 | | -#### What's worth knowing if I want to further the development? |
48 | | - |
49 | | -* gopy doesn't like Go interfaces; so make sure you don't have any public (exported) interfaces |
50 | | - * this includes a struct with a public property that may eventually lead to an interface |
51 | | - |
52 | | -#### Example Go RPCSession usage (simple session ID, calls return JSON) |
53 | | - |
54 | | -There's no real reason why you'd want to do this (just use gosnmp on it's own)- it's more for reference: |
| 51 | +## Install (from PyPI) |
55 | 52 |
|
56 | 53 | ``` |
57 | | -package main |
58 | | -
|
59 | | -import ( |
60 | | - "gosnmp_python" |
61 | | - "fmt" |
62 | | -) |
63 | | -
|
64 | | -func main() { |
65 | | -
|
66 | | - sessionID := gosnmp_python.NewRPCSessionV2c( |
67 | | - "1.2.3.4", |
68 | | - 161, |
69 | | - "public", |
70 | | - 5, |
71 | | - 1, |
72 | | - ) |
73 | | -
|
74 | | - err := gosnmp_python.RPCConnect(sessionID) |
75 | | - if err != nil { |
76 | | - panic(err) |
77 | | - } |
78 | | -
|
79 | | - jsonResult, err := gosnmp_python.RPCGet(sessionID, ".1.3.6.1.2.1.1.5.0") |
80 | | - if err != nil { |
81 | | - panic(err) |
82 | | - } |
83 | | -
|
84 | | - fmt.Println(jsonResult) |
85 | | -
|
86 | | - err = gosnmp_python.RPCClose(sessionID) |
87 | | - if err != nil { |
88 | | - panic(err) |
89 | | - } |
90 | | -
|
91 | | -} |
| 54 | +python -m pip install gosnmp-python |
92 | 55 | ``` |
93 | 56 |
|
94 | | -#### Example Python usage (uses RPCSession underneath because of memory leaks between Go and Python with structs) |
95 | | - |
96 | | -To create an SNMPv2 session in Python do the following: |
| 57 | +## Setup |
97 | 58 |
|
98 | 59 | ``` |
99 | | -from gosnmp_python import create_snmpv2c_session |
100 | | -
|
101 | | -session = create_snmpv2c_session( |
102 | | - hostname='1.2.3.4', |
103 | | - community='public', |
104 | | -) |
105 | | -
|
106 | | -session.connect() |
107 | | -
|
108 | | -print session.get('.1.3.6.1.2.1.1.5.0') |
109 | | -
|
110 | | -session.close() |
| 60 | +virtualenv -p $(which python3) venv |
| 61 | +source venv/bin/activate |
| 62 | +./fix_venv.sh |
| 63 | +pip install -r requirements-dev.txt |
111 | 64 | ``` |
112 | 65 |
|
113 | | -Which returns an object like this: |
| 66 | +## Build |
114 | 67 |
|
115 | 68 | ``` |
116 | | -SNMPVariable( |
117 | | - oid='.1.3.6.1.2.1.1.5', |
118 | | - oid_index=0, |
119 | | - snmp_type=u'bytearray', |
120 | | - value='hostname.domain.com.au' |
121 | | -) |
| 69 | +source venv/bin/activate |
| 70 | +./native_build.sh |
122 | 71 | ``` |
123 | 72 |
|
124 | | -Some of this may feel a bit like [easysnmp](https://github.com/kamakazikamikaze/easysnmp); that's intentional, I was originally using that |
125 | | -but I think its got some underlying thread-safety issues on the C side (particularly to do with SNMPv3). |
| 73 | +If you're deep in the grind and want to iterate faster, you can invoke: |
126 | 74 |
|
127 | | -No offence to the guys that contribute to that project- it's served me very well. |
128 | | - |
129 | | -To use the test build container... |
130 | | - |
131 | | - ./test.sh |
132 | | - |
133 | | -To shell into the test container to have a look around... |
| 75 | +``` |
| 76 | +./native_build.sh fast |
| 77 | +``` |
134 | 78 |
|
135 | | - ./test.sh bash |
| 79 | +This skips the setup (assuming you've already done that). |
0 commit comments