pax_global_header00006660000000000000000000000064142645567030014526gustar00rootroot0000000000000052 comment=20b9b89a34f41c03cad898d0154e34f6eb061369 easysnmp-0.2.6/000077500000000000000000000000001426455670300133725ustar00rootroot00000000000000easysnmp-0.2.6/.github/000077500000000000000000000000001426455670300147325ustar00rootroot00000000000000easysnmp-0.2.6/.github/ISSUE_TEMPLATE/000077500000000000000000000000001426455670300171155ustar00rootroot00000000000000easysnmp-0.2.6/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000013671426455670300216160ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: "[BUG]" labels: bug assignees: '' --- **EasySNMP release version OR commit number** ex. 0.2.5 OR ecf5b3f **Operating System and Version** - OS: [e.g. macOS, Ubuntu, RHEL, CentOS, Fedora, etc.] - Version [e.g. 22] **Net-SNMP Library Version** - 5.x.x **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Additional context** Add any other context about the problem here. easysnmp-0.2.6/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000011451426455670300226430ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: "[FEATURE]" labels: enhancement assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. easysnmp-0.2.6/.github/ISSUE_TEMPLATE/question-to-the-developers.md000066400000000000000000000002441426455670300246520ustar00rootroot00000000000000--- name: Question to the developers about: General question(s) about the project title: "[Question]" labels: question assignees: '' --- **What's on your mind?** easysnmp-0.2.6/.github/workflows/000077500000000000000000000000001426455670300167675ustar00rootroot00000000000000easysnmp-0.2.6/.github/workflows/build.yml000066400000000000000000000052411426455670300206130ustar00rootroot00000000000000name: build on: release: types: [published] push: branches: - 'actions' - 'actions-test' jobs: linux-wheels: runs-on: ubuntu-latest steps: - name: Set up Python uses: actions/setup-python@v2 - name: Checkout repo uses: actions/checkout@v3 - name: Create manylinux wheels uses: RalfG/python-wheels-manylinux-build@v0.4.2 with: system-packages: 'snmpd libsnmp-dev libperl-dev' - name: Install local dependencies run: sudo apt install -y snmpd libsnmp-dev libperl-dev - name: Create source dist run: sudo chown -R $USER . && python setup.py sdist - name: Remove linux_x86_64 run: rm dist/*linux_x86_64.whl - name: Upload artifacts uses: actions/upload-artifact@v2 with: name: wheels path: dist/* macos-wheels: runs-on: macos-latest steps: - name: Set up Python uses: actions/setup-python@v2 - name: Checkout repo uses: actions/checkout@v2 - name: Install dependencies run: | brew install easysnmp/netsnmp-easysnmp/net-snmp echo 'export PATH="/usr/local/opt/net-snmp/bin:$PATH"' >> /Users/runner/.bash_profile export PATH="/usr/local/opt/net-snmp/bin:$PATH" echo 'export PATH="/usr/local/opt/net-snmp/sbin:$PATH"' >> /Users/runner/.bash_profile export PATH="/usr/local/opt/net-snmp/sbin:$PATH" - name: Install cibuildwheel run: python -m pip install cibuildwheel - name: Build wheels env: CIBW_BEFORE_BUILD_MACOS: python setup.py build run: python -m cibuildwheel --output-dir dist - name: Upload artifacts uses: actions/upload-artifact@v2 with: name: wheels path: dist/* publish-wheels: runs-on: ubuntu-latest needs: [linux-wheels, macos-wheels] if: ${{ always() }} steps: - name: Download wheel artifacts id: download uses: actions/download-artifact@v3 with: path: ./dist name: wheels - name: Publish to PyPi uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ github.event_name == 'release' && secrets.PYPI_API_TOKEN || secrets.TEST_PYPI_API_TOKEN }} verbose: true repository_url: ${{ github.event_name == 'release' && 'https://upload.pypi.org/legacy/' || 'https://test.pypi.org/legacy/' }} skip_existing: true - name: Add to release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') with: files: dist/* easysnmp-0.2.6/.github/workflows/tests.yml000066400000000000000000000166431426455670300206660ustar00rootroot00000000000000name: tests on: push: branches-ignore: - 'master' - 'actions' pull_request: jobs: check-source-changes: runs-on: ubuntu-latest outputs: run_job: ${{ steps.changed-files.outputs.any_changed }} steps: - name: Checkout changes uses: actions/checkout@v3 - name: Check for changes in source code id: changed-files uses: tj-actions/changed-files@v18.7 with: files: | easysnmp/*.py easysnmp/*.c easysnmp/*.h setup.py setup.cfg setup-py2.7.cfg .github/workflows/*.yml build-and-test: runs-on: ${{ matrix.os }} needs: check-source-changes if: needs.check-source-changes.outputs.run_job == 'true' strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] python-version: ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10"] steps: - name: Set up dependencies run: | if [ "$RUNNER_OS" == "Linux" ] then sudo apt-get update sudo apt-get install -y snmpd libsnmp-dev libperl-dev snmp-mibs-downloader valgrind sudo systemctl stop snmpd sudo download-mibs elif [ "$RUNNER_OS" == "macOS" ] then brew install easysnmp/netsnmp-easysnmp/net-snmp echo 'export PATH="/usr/local/opt/net-snmp/bin:$PATH"' >> /Users/runner/.bash_profile export PATH="/usr/local/opt/net-snmp/bin:$PATH" echo 'export PATH="/usr/local/opt/net-snmp/sbin:$PATH"' >> /Users/runner/.bash_profile export PATH="/usr/local/opt/net-snmp/sbin:$PATH" else echo "$RUNNER_OS not currently supported" exit 1 fi mkdir -m 0755 ~/.snmp echo 'mibs +ALL' > ~/.snmp/snmp.conf shell: bash - name: Checkout repo uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install pip dependencies run: | python -m pip install --upgrade pip if [[ "${{ matrix.python-version }}" =~ "2.7" ]] then pip install pytest wheel six else pip install flake8 pytest pytest-flake8 pytest-cov wheel fi if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Build source run: | python setup.py build pip install -e . - name: Start SNMP daemon run: | if [ "$RUNNER_OS" == "Linux" ] then mibdir="-M +/var/lib/snmp/mibs" SNMPD=$(which snmpd) elif [ "$RUNNER_OS" == "macOS" ] then mibdir="" SNMPD=/usr/local/opt/net-snmp/sbin/snmpd else mibdir="" SNMPD=$(which.exe snmpd) fi $SNMPD -C -c tests/snmpd.conf -r -Le $mibdir -m ALL - name: Lint with flake8 uses: py-actions/flake8@v2 continue-on-error: true - name: Run tests run: | if [[ "${{ matrix.python-version }}" =~ "2.7" ]] then PYTEST_ARGS=( '-c' './setup-py2.7.cfg' ) fi if [[ "${{ matrix.python-version }}" =~ "3.10" ]] && [ "$RUNNER_OS" == "Linux" ] then wget https://raw.githubusercontent.com/python/cpython/main/Misc/valgrind-python.supp VALGRIND=( 'valgrind' '--tool=memcheck' '--leak-check=full' '--show-leak-kinds=definite,indirect,possible' '--suppressions=./valgrind-python.supp' '--log-file=./valgrind.out' ) echo 'PYTHONMALLOC=malloc' >> $GITHUB_ENV fi ${VALGRIND[@]} python -m pytest ${PYTEST_ARGS[@]} --junitxml=test-results_${{ matrix.os }}_${{ matrix.python-version }}.xml | tee ./test-outputs_${{ matrix.os }}_${{ matrix.python-version }}.log - name: Upload test results uses: actions/upload-artifact@v2 with: name: pytest-results path: | test-results_*.xml test-outputs_*.log - name: Upload valgrind report uses: actions/upload-artifact@v2 if: ${{ matrix.python-version == '3.10' && matrix.os == 'ubuntu-latest' }} with: name: valgrind-report path: ./valgrind.out - name: Generate wheel run: python setup.py bdist_wheel - name: Upload wheel uses: actions/upload-artifact@v2 with: name: wheels path: dist/*.whl comment-coverage: runs-on: ubuntu-latest needs: build-and-test steps: - name: Download pytest artifacts id: download uses: actions/download-artifact@v3 with: path: ./pytest-results name: pytest-results - name: Create multi-file output listing run: | echo 'pytest_multiple_files<> $GITHUB_ENV export test_xml=($(ls -d ${{ steps.download.outputs.download-path }}/*.xml | sort )) export test_log=($(ls -d ${{ steps.download.outputs.download-path }}/*.log | sort )) for i in "${!test_log[@]}" do echo "$(echo ${test_log[$i]} | cut -d_ -f2) - $(echo ${test_log[$i]%.*} | cut -d_ -f3), ${test_log[$i]}, ${test_xml[$i]}" >> $GITHUB_ENV done echo 'EOF' >> $GITHUB_ENV - name: Pytest coverage comment uses: MishaKav/pytest-coverage-comment@main with: title: Pytest Coverage Report hide-badge: true hide-report: true create-new-comment: false hide-comment: false report-only-changed-files: false multiple-files: | ${{ env.pytest_multiple_files }} comment-valgrind: runs-on: ubuntu-latest needs: build-and-test steps: - name: Download valgrind artifact id: download uses: actions/download-artifact@v3 with: path: ./valgrind-report name: valgrind-report - name: Parse valgrind report run: | python - << "EOF" from re import split with open("${{ steps.download.outputs.download-path }}/valgrind.out") as f: data = f.read() blocks = split(r'==[0-9]+==\s\n', data) snmp = list(filter(lambda b: 'snmp' in b, blocks[1:])) with open('./valgrind-stripped.log', 'w') as f: f.write('```sh\n') f.writelines(filter(lambda b: all(filt not in b for filt in ('invalid file descriptor', 'alternative log')), snmp)) f.write(blocks[-2]) f.write('```\n') EOF - id: get-comment-body run: | body="$(cat ./valgrind-stripped.log)" body="${body//'%'/'%25'}" body="${body//$'\n'/'%0A'}" body="${body//$'\r'/'%0D'}" echo "::set-output name=body::$body" - name: Create comment for PR uses: peter-evans/create-or-update-comment@v1 if: github.event_name == 'pull_request' with: issue-number: ${{ github.event.pull_request.number }} body: ${{ steps.get-comment-body.outputs.body }} - name: Create comment for Push uses: peter-evans/commit-comment@v1 if: github.event_name == 'push' with: body: ${{ steps.get-comment-body.outputs.body }} easysnmp-0.2.6/.gitignore000066400000000000000000000005561426455670300153700ustar00rootroot00000000000000# Python byte-compiled, optimized and DLL files *.py[cod] # Cache .cache # Compiled libraries *.so # Nosetests and coverage information .coverage nosetests.xml coverage.xml # Distribution & packaging build/ develop-eggs/ .eggs/ dist/ *.egg-info/ *.egg # Documentation builds docs/_build/ # vim stuff .ropeproject # Local development dev/ .vscode */*.log *.logeasysnmp-0.2.6/CHANGELOG.rst000066400000000000000000000174611426455670300154240ustar00rootroot00000000000000Changelog --------- `0.2.6 `_ (2022-07-16) +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Cached SNMP v3 credentials for a session are removed when the session configuration is updated or session is free'd (`3826d0a `_) - New method for (re)creating the underlying Net-SNMP interface object (`e75aab `_) - Developer requirements updated and are now using `Black `_ for Python formatting `0.2.6a1 `_ (2022-04-17) +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - CI/CD builds and publishes wheels and source code to PyPI `0.2.6a `_ (2022-04-17) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Finally fixed underlying memory leaks (`PR#142 `_) - Proper handling of errors caused within Python that prevented exception raises (`PR#129 `_) - Added modern SNMPv3 privacy/auth algorithms (`PR#143 `_, `94ad10 `_) - CI/CD moved to Github Actions (`PR#137 `_) `0.2.5 `_ (2017-06-14) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Refactored code to use CObjects/PyCapsules for better memory management. Fixed truncation of data returned. (`#30 `_, `PR#48 `_) - Implemented ``bulkwalk`` functionality (`PR#48 `_) - Added support for Net-SNMP installed via ``brew`` on OS X (`PR#48 `_) - Allow ``snmp_type`` parameter in ``set(...)`` to support snmpset(1) type specifiers. (`#28 `_, `PR#29 `_) - Support Net-SNMP 5.6.x to add support for OSX. (`#12 `_, `4e121e9 `_) - Remove printf debug statements when making SNMPv1 fixes from interface.c. (`320df28 `_) `Full Source Code Changelog `_ `0.2.4 `_ (2015-07-09) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Ensured that the simple bitarray header is correctly referenced. `Full Source Code Changelog `_ `0.2.3 `_ (2015-06-30) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Introduce this changelog. - Removed dead Python 2.5.x code. (`PR#22 `_) - Fix SNMPv1 when using ``retry_no_such=True`` to ensure the response values return correctly to the corresponding supplied requested OIDs. (`PR#18 `_) - Allow OIDs to be specified without a leading dot. (`#15 `_) - By default do not throw exception if a non-existent OID is fetched, and introduce ``abort_on_nonexistent`` to allow user-configurable action. (`49ea15ec `_) - Fix C interface to not tread on existing logging configuration to instead import logging module and configure a NullHandler instead. (`PR#13 `_) - Modify ``snmpd`` to run on ``localhost:11161`` to avoid requiring root privilege and not clash with a local running instance of snmpd. (`5604a4bb `_) `Full Source Code Changelog `_ `0.2.2 `_ (2015-06-03) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Cosmetic fixes to the codebase. - Fixes for Python 3.x/unicode support. `Full Source Code Changelog `_ `0.2.1 `_ (2015-06-02) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Various fixes to the C interface including better exception handling and removal of remnant debug code. - Use pytest-sugar for bling-bling test output. (`71c567f9 `_) `Full Source Code Changelog `_ `0.2 `_ (2015-06-02) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Introduced support for Python 3.x. `Full Source Code Changelog `_ `0.1.1 `_ (2015-06-02) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Added PyPI documentation. (`da16cd74 `_) - Quelch stderr messages generated from the internal Net-SNMP library. (`15fce1ea `_) - Improved coverage of testsuite. - Use of fixtures and parametization in testsuite to target specific versions of SNMP (v1/v2/v3). (`427a9dfd `_) `Full Source Code Changelog `_ `0.1 `_ (2015-05-30) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - Raise Python exceptions in the C interface where necessary. - Bug fixes to the C interface when specifying context engine session ID and the value returned when requesting an invalid OID. (`PR#6 `_) - Implement python logging in the C interface and rewrite existing fprintf diagnostics to use the logging interface. (`PR#4 `_) - Unit tests rewritten in pytest and travis-ci integration. (`PR#2 `_, `b2018587 `_) - Implement ``compat_netsnmp_memdup()`` to fix C interface to compile against Net-SNMP 5.7.2. (`PR#2 `_) - Import and overhaul of the original Net-SNMP Python bindings: - Wrote a README that provides an overview and quickstart of the project - Sphinx generated documentation which is also hosted on readthedocs. - Conform to PEP8 using ``flake8`` - Coverage support via coveralls.io (integrated into travis) - A simple pythonic interface which resembles the use of the Net-SNMP CLI client utilities. - Python package uploaded to PyPI `Full Source Code Changelog `_ easysnmp-0.2.6/LICENSE000066400000000000000000000062271426455670300144060ustar00rootroot00000000000000Various copyrights apply to this package, listed in various separate parts below. Please make sure that you read all the parts. ---- Part 1: Sparta, Inc (BSD) ----- Copyright (c) 2003-2010, Sparta, Inc All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Sparta, Inc nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---- Part 2: ScienceLogic, LLC (BSD) ---- Copyright (c) 2006, ScienceLogic, LLC All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of ScienceLogic, LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. easysnmp-0.2.6/MANIFEST.in000066400000000000000000000000521426455670300151250ustar00rootroot00000000000000include README.rst LICENSE graft easysnmp easysnmp-0.2.6/README.rst000066400000000000000000000133031426455670300150610ustar00rootroot00000000000000Easy SNMP ========= |Python Code Style| |Build Status| |Discussions| |License| .. |Python Code Style| image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black .. |Build Status| image:: https://img.shields.io/github/workflow/status/easysnmp/easysnmp/build :target: https://github.com/easysnmp/easysnmp/actions .. |License| image:: https://img.shields.io/badge/license-BSD-blue.svg :target: https://github.com/kamakazikamikaze/easysnmp/blob/master/LICENSE .. |Discussions| image:: https://img.shields.io/github/discussions/easysnmp/easysnmp :alt: Join the Discussions! :target: https://github.com/easysnmp/easysnmp .. image:: https://raw.githubusercontent.com/easysnmp/easysnmp/master/images/easysnmp-logo.png :alt: Easy SNMP Logo Artwork courtesy of `Open Clip Art Library `_ Introduction ------------ Easy SNMP is a fork of `Net-SNMP Python Bindings `_ that attempts to bring a more Pythonic interface to the library. Check out the `Net-SNMP website `_ for more information about SNMP. This module provides a full-featured SNMP client API and supports all dialects of the SNMP protocol. Why Another Library? -------------------- - The `original Net-SNMP Python library `_ is a great starting point but is quite un-Pythonic and lacks proper unit tests and documentation. - `PySNMP `_ is entirely written in Python and therefore has a huge performance hit. In some brief tests, I estimate that both the Net-SNMP Python bindings and Easy SNMP are more than 4 times faster than PySNMP. Further to this, PySNMP has an even less Pythonic interface than the official Net-SNMP bindings. - Many other libraries like `Snimpy `_ are sadly based on PySNMP, so they also suffer performance penalty. Quick Start ----------- There are primarily two ways you can use the Easy SNMP library: 1. By using a Session object which is most suitable when you want to request multiple pieces of SNMP data from a source: .. code:: python from easysnmp import Session # Create an SNMP session to be used for all our requests session = Session(hostname='localhost', community='public', version=2) # You may retrieve an individual OID using an SNMP GET location = session.get('sysLocation.0') # You may also specify the OID as a tuple (name, index) # Note: the index is specified as a string as it can be of other types than # just a regular integer contact = session.get(('sysContact', '0')) # And of course, you may use the numeric OID too description = session.get('.1.3.6.1.2.1.1.1.0') # Set a variable using an SNMP SET session.set('sysLocation.0', 'The SNMP Lab') # Perform an SNMP walk system_items = session.walk('system') # Each returned item can be used normally as its related type (str or int) # but also has several extended attributes with SNMP-specific information for item in system_items: print '{oid}.{oid_index} {snmp_type} = {value}'.format( oid=item.oid, oid_index=item.oid_index, snmp_type=item.snmp_type, value=item.value ) 2. By using Easy SNMP via its simple interface which is intended for one-off operations (where you wish to specify all details in the request): .. code:: python from easysnmp import snmp_get, snmp_set, snmp_walk # Grab a single piece of information using an SNMP GET snmp_get('sysDescr.0', hostname='localhost', community='public', version=1) # Perform an SNMP SET to update data snmp_set( 'sysLocation.0', 'My Cool Place', hostname='localhost', community='public', version=1 ) # Perform an SNMP walk snmp_walk('system', hostname='localhost', community='public', version=1) Documentation ------------- Please check out the `Easy SNMP documentation at Read the Docs `_. This includes installation instructions for various operating systems. You may generate the documentation as follows: .. code:: bash # Install Sphinx pip install sphinx # You may optionally export the READTHEDOCS environment variable to build docs # on systems where you haven't built the C interface export READTHEDOCS=1 # Build the documentation into static HTML pages cd docs make html Acknowledgments --------------- I'd like to say thanks to the following folks who have made this project possible: - **Giovanni Marzot**: the original author - **ScienceLogic, LLC**: sponsored the initial development of this module - **Wes Hardaker and the net-snmp-coders**: for their hard work and dedication - **fgimian and nnathan**: the original contributors to this codebase Running Tests ------------- Tests use `Pytest `_. You can run them with the following: .. code:: bash git clone https://github.com/easysnmp/easysnmp.git cd easysnmp pip install pytest pytest License ------- Easy SNMP is released under the **BSD** license. Please see the `LICENSE `_ file for more details. Copyright --------- The original version of this library is copyright (c) 2006 G. S. Marzot. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Net-SNMP itself. Copyright (c) 2006 SPARTA, Inc. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Net-SNMP itself. easysnmp-0.2.6/dev-requirements-py2.txt000066400000000000000000000005611426455670300201440ustar00rootroot00000000000000atomicwrites==1.4.0 attrs==21.4.0 backports.functools-lru-cache==1.6.4 configparser==4.0.2 contextlib2==0.6.0.post1 funcsigs==1.0.2 importlib-metadata==2.1.3 more-itertools==5.0.0 packaging==20.9 pathlib2==2.3.7.post1 pkg-resources==0.0.0 pluggy==0.13.1 py==1.11.0 pyparsing==2.4.7 pytest==4.6.11 scandir==1.10.0 six==1.16.0 typing==3.10.0.0 wcwidth==0.2.5 zipp==1.2.0 easysnmp-0.2.6/dev-requirements.txt000066400000000000000000000005001426455670300174250ustar00rootroot00000000000000attrs==21.4.0 black==22.3.0 click==8.1.2 coverage==6.3.2 flake8==4.0.1 iniconfig==1.1.1 mccabe==0.6.1 mypy-extensions==0.4.3 packaging==21.3 pathspec==0.9.0 platformdirs==2.5.2 pluggy==1.0.0 py==1.11.0 pycodestyle==2.8.0 pyflakes==2.4.0 pyparsing==3.0.8 pytest==7.1.2 pytest-cov==3.0.0 pytest-flake8==1.1.1 tomli==2.0.1 easysnmp-0.2.6/docs/000077500000000000000000000000001426455670300143225ustar00rootroot00000000000000easysnmp-0.2.6/docs/Makefile000066400000000000000000000163711426455670300157720ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/EasySNMP.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/EasySNMP.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/EasySNMP" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/EasySNMP" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." easysnmp-0.2.6/docs/_static/000077500000000000000000000000001426455670300157505ustar00rootroot00000000000000easysnmp-0.2.6/docs/_static/easysnmp.png000066400000000000000000000325171426455670300203250ustar00rootroot00000000000000PNG  IHDR sBIT|d pHYs  dtEXtSoftwarewww.inkscape.org< tEXtTitleNetworkLBtEXtAuthorPrinterKillerGr~tEXtDescriptionNetwork Ѳ!tEXtCreation Time2011-08-09T15:31:22/EtEXtSourcehttps://openclipart.org/detail/154453/network-by-printerkillerXtEXtCopyrightCC0 Public Domain Dedication http://creativecommons.org/publicdomain/zero/1.0/ IDATxy|>3YIX(TUqL@"H&Pm-$1P_[k I*.dBXQ}-Kšm$L&3dO&޹ϙ̝{ u C df㞽nוl hL|ѣ6rhpDD%$N?m2DO 0PR=be_}7`AO Mӯ*8e/XzZ\x !@ KЭAf`xF~>E nKz3R;%KgPaɍ7P}d)Mn+ݍ>ƜzOӽXLO=Qs.֮ q?qvFRCS I֦$K( Ripy~w}A&^vc#]O@?98ذk{ Ϡ; cauXjH ?1݀R@hmoRAq,jbP"Sހƀ$nJ:= oF޽㛑,%2x nZzKtJ8Ly*C75)s4$%'9?8ӷ^ܑpQ9֒ɬcvk#_KV*5RR0r`*I yy(rf'ymZ8jk>vE,zIC;/`PČE@q@7V.}/9 z@dfxjN1 @Z?<ٹ;=, x"3N >Dͯ "k! ͝dT ?ŠrwȀVn[$}fq d5o}/n얧 bhPlRI3V^}1M:P=m~Z.l\@3x?X>TeZݳ_pSS>a^6YYhph!Dִ=l} &L \*Qlv6̄lcskߛgؗMPWR!{x --W_z\f@ +rk 9@C9AiRJD)`2AC[-iw"`$ 5Rg,*;C)\ +b&1Ր&͏D\VXƯVB[iĔg_6Ft4mRf6M%ߪ~aИa \\;s>W|C iM/T,U43@x)c}ZM`B~aԧ| ׃0>ho8t[sOaK t)Lm/f{1}^mƁ\!ֱ5i\%z&%  }{PCQb,i+uxEOJ^]'Yʚ[c}_}>ܺ7wko{?da_sjXcx=+U%seYK`Jܠz{ϭ>NRn]uJb*pFPoUoxkw'[7ZH| Bc+`viEK[ڸjrVQl#eW_)oZY򥳲p)ZKG$q) 40ʛJs펛 47gEM h-C7gJADZ8;tKdE$OCջMSub;nduV>L܂ jHlXIF<l™[Tvg!.޺OC8H))ZQ`MO՞ hki#[Qm:  >v<ѵZ]1:*zR@v@ 1Jz<- QmgVSNJԞbN99cG f|YY)>@k(]aZWz><ꯑ|sЅYRi&Dːb cH%yHrRrr%fSRg?kII#f> ݽwuiY;T>$(Ij*.Ro.=-qm߿#`&u'xڹZ_6c0NLNJERhjvAs]_FOYSR|K s3~fx& Äkơ{x =$ A?ub.{g= <^/\f75d3Al~TwgԩˉC8/BRn&x 3*x[?1 ҄mx,r -ڷQi6g3vG] Мsǃv9nKvo>lx,ZmS90#gQ.^Ubowrk'N~94H|Ajefim͵Rx$~T=̩[;jiVXfW;̟DT<{U~Ws\/T)"j}1 @Fi@JJ<Xfn/?W!({Ѿw{\9<;{zŔg_V`fꝟF76Xȡi5%@hr|R)bF$-߸R8f K6]]]Y5A~Z׀b6{8%v},Gb38W*>y^9kdiZ," 1 ^_׀iJ(V4S]3e^lc\>a~Kn|bN/-A{pcfe_vQ[ j|oVl\J:@Y4XNj~'t% ?wB ``EC 6`cBv\nj:"ud;W19f\/(CBѠXm2s .*}5z;.;]3hWK aa#eo6  է#rbڵ1OZU ?Ϟ= /_S2A8s a`fwԁj!P;> Zn*ez&K<76 Mø)+{o}xb62hʹEYXڠ`/Գ573uW5Q3SfWCRZIT(擔{5(S`{>^pCG׷h0jWRާk$;L7Ÿ@gb Hp$GV @B00&h._qk Oy.sNrzhMi+\ u7k_3L ߲s`[sj8 $s'̙`AީSxo8#GHhRIҼc˶!@' ؎8ŭ5ZSGY"ale1`dpGLv(j#UI5Qݝl> zm)~l|o˿7jšTzЬ^"8>Ϡvz뀷 "b @ M[C5O,7ט H(H#S&nԵ#" r}9#v^Ȩ;< LaAb4rT FT@US#իJg/1萋H ;꺝 i (b sc nЏ61쳌,^pM.񼳢5 MO>P%+Gglm0Jg"/~8Z")iɗu6xu4v]b!) c h0dݜU π.f'x}"\A[,_4M=5 3]M! E{ 1v'G eLBayEeE%T"DuO[D,@m16I^K}D$^@+3ѥe}+gwwYȲF>KD4{ 7UWhT>S!|;^/$TWkw,OȆG;48궶);"TU9DaVn D`(hJC6ckTNׂDw:ؙV=Lue۷%5 a6ge_#;[&祙0{Vn@aGhdg+K Ybh>Š ۏOSp*Gs0@?jpsy1CJLH#S$Qpĉ\kh_WF4ιEl&&if  BADi49 ! >e 6%I{~@ߟޗh4R _:HmUOrwc)lŵ @ٔlI2#-#àCFlۼqzaY YY?nvo) h"d r|bh9g1htKttp2%QʽuCmv~ԧ;+KisW aNW ZOv}H ,b[AoLɢy "rd08sh{cIxYQ<%"Ha_6AA=o #!Zvohv7V,NDxֲ"D0̧#Iu]2I 3VwQ"Wl A ?>a o1hr b~뺇@ݱ+(;.ZMdA!;'g#^K"vZ|'i/AĄDDFI%%T$Z(ysѓdMf[=<OXk"O,5 %&(c6X0@z4*Zu܇,  LJ"q(u2soQ+71^ cZl9]{^ 7<{ާQmY}%PN)4 d4. 6<`  M@`%4AR>}IHf~[b/Al9+KWw0.;[A]EDs=YcMU[Dn$wqxHR R׶͝:곭Lr$ e0q`1s4-4m|A-\6HR7._ugeiW)l߶`|—`$x/3G+K:dkXT>ohqhXhH+8]'ס$N=VX`P k*KWc[O6@ r[aMҿeP@6D܊F[ hli8=sլ{[߄&t0K6u'&gcڵz|6S)l*P sHAVn sr@țo8fvs=@7 !K3mve )H¼!}_֒VM~pKMe9[tm 0Q)01nwV>P}st]O~*8c@lY}QSY{hCf"( Ɯꪒ{Znyge dX#d *w+~ mA1['G@p@E͊CfIDATKYx"ۏ; ΪϹE~[7#lėDM~no( ; YMcC->;^cxz,H 8\BjD :&t>Zk~9t/M?rf@A۶H( SPQ+ڵs%Ov3:o_/O"( mpu*ɄǠftYBF~td8[NsGm #tώWP (@fV> h D^/21)$Q{Œ.)W ͤ14\oKZ!! {(+L_JKG9zİ XK 5%cGcUX/B<- Û|GNbY3Nya(l_ HO>T==x YÇah~0 wf{ڹB&η-rLPdC>),R)R=po%=h:p0\>M!?LwФN'Ub>HUK|bFV=mVr§&'q/zZވƈP}l𸔻z"+,W:f.8^g}cOWoǷj/ #&\n|ICx׈?@pEn&O1J i)+ҲVorJү 8F}r9(f:er;WQCNKݭ<{6 ӄkmQJ)7C;_4Sa|{~y7-?3-d4d}k_\VGHgELԵ:+Jn/gxoß"i93)3<]#U!| T-SN[IZHHJChFCHO[ǘi<^3zg0{8TMgR!?U" %D}e)hMi%lYvaLUفRwtY"T\KD0;tueS6{5@Ĉs-+^h=f`,__|wJ%c@K2( VG%o~ܢ`a%-zD>m~x|4T^YVs2 LS”ӄaJx f×@4aEje f,Ƅ BSM"-Zv&zJ(MH?lEw^ 5RЅMEeΨ-5 |[0֟R)( M")9) fJT_-: y#Xjo"R"~M͏D_Kr$pG %t rZ}- m`G߲fR҇"%-#bdS7nc] U $T,P\to ^>DN' ̱/헂 8\0ߘ":*n=}犡XA)  "!d =5P,kRNӿ ؜ի'ȿT%g~q,^UrtQvVb'Grw٤Ss ifK0430p۫[̞}gVi~˕-t[ڥ sY^!<ʒ=YVfw,pVSkIZdehZ"(!--^~RE 2%Bz-o7](HJ)É._-;gW]z'{ju\0*!jmU֖?Qrţ.nFvTl0tEue.} 撂Z,s.`hR&n FL/â/ĕ]u q*r5^߂BDЄhDB)o@5 d>͑[n(~ n™vMuu+լ;"Iݶϝ|ROCf5v\E3-;mzh5O=/L)s8,v 4iSeqN a+b@ .T&1#&.wqx %[l[?X K, 03R ٶ S 0Z{sRe-^e+r8ħLk*K~傅G{294B gܮx43cDS&^-{ #SnP|h̘*]EW O৘qT /c֐;W_E"_E3 t-tk#<怂PcxAuEiF-,D+"|BۥxӮ,:zEq7$"p=Q9}#nq(6=gc!alwVy f9fAoǦp$szbD_4OuUl 2D( ?i0xO<1\;?#gi]yŎK>f vJ$K_?Е2BQnΊ?< 9çu^l^ao<#|=k&UK7d'#䮸nnQi6{#,ݯԱDhm#SU%w];7|W;jڊʮ&ȉeLf/P 4X%uyg'}R"RHC .Ĺz$/'JzRC /$1G^pY.@D+gE铹 MH&`>b{2,Ln[h ;]t\gQD giQEΕ%;+OWҫݴ~ẻy_LpoXt9 DX"EEgF[$"\Ɍ|r|'dq+GVkrzn%fG V<!K r[RdRb_`yKMP$=A/(w Ί\`+rN45&DϜo1pZ]UuVub.B- #$zρ5&A^qf/o%_XlXǀǘXG@$W=V 4Ε8ZLO25}6J[;k Xnf<ė.X{cR~2<%Vp^d\45VkP@ p_9Csig0_, Vrf W QEۍ? _>cϯOp_MOeMp="0~>A<1` : A L]`p(H햚w*wbAX k_ b߭ܡu]QG`\0CQUk'Ov_x3g PXe䟅? pX_`+t+F_XqJsHZUN܁0@UW%.R8%ũq<&cը"[`+*;Lj 6^l/ןpm;'Z3ԯ?cY:M0OGx 1}˚xcW0FbNGkz){Gw$+=l?pBW _x&y8>V}5%w7nr8% Cn,mBly/<Gļ] I}je @leׁ(rNJ|+m@x@vaSwzO[*3,"Ȋ b,Iےrgvea~_'Iſ֣ʒ?{΀V˳ 1D(3G2kÞpLZfFtXSYRA3LXh&%Ƨ!p0Nљ0BzAybir@q%Ow%MUX>"g7|Kt 34MUi qf1R+t=k2mݙ;ܖ1^~?'_TW %A|!֮WTu taƿFh,X֝u"3 `^DŽk|ܫ0ax$tA厑 lMh3{ >c `||clZYeJ>x=rIENDB`easysnmp-0.2.6/docs/_static/easysnmp.svg000066400000000000000000000660561426455670300203450ustar00rootroot00000000000000 image/svg+xml Openclipart Network 2011-08-09T15:31:22 Network https://openclipart.org/detail/154453/network-by-printerkiller PrinterKiller Network web internet connect www easysnmp-0.2.6/docs/conf.py000066400000000000000000000032241426455670300156220ustar00rootroot00000000000000import sys import os # Include the parent path so that the package may be imported by autodoc sys.path.insert(0, os.path.abspath(os.path.pardir)) # Enable the appropriate Sphinx extensions extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The master toctree document. master_doc = 'index' # General information about the project. project = 'Easy SNMP' copyright = '2015-2022, Fotis Gimian, Kent Coble' author = 'Fotis Gimian, Kent Coble' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The theme to use for HTML and HTML Help pages. html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the Alabaster # documentation at https://github.com/bitprophet/alabaster. html_theme_options = { 'github_user': 'kamakazikamikaze', 'github_repo': 'easysnmp', 'github_banner': True, 'logo': 'easysnmp.svg', 'logo_name': True, 'font_family': "'Hiragino Mincho Pro', Georgia, serif" } # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Custom sidebar templates, maps document names to template names. html_sidebars = { '**': [ 'about.html', 'localtoc.html', 'relations.html', 'searchbox.html', ] } easysnmp-0.2.6/docs/easy_api.rst000066400000000000000000000004051426455670300166450ustar00rootroot00000000000000Easy API -------- .. currentmodule:: easysnmp .. autofunction:: snmp_get .. autofunction:: snmp_set .. autofunction:: snmp_set_multiple .. autofunction:: snmp_get_next .. autofunction:: snmp_get_bulk .. autofunction:: snmp_walk .. autofunction:: snmp_bulkwalkeasysnmp-0.2.6/docs/exceptions.rst000066400000000000000000000005111426455670300172320ustar00rootroot00000000000000Exceptions ---------- .. currentmodule:: easysnmp .. autoclass:: EasySNMPError .. autoclass:: EasySNMPConnectionError .. autoclass:: EasySNMPTimeoutError .. autoclass:: EasySNMPUnknownObjectIDError .. autoclass:: EasySNMPNoSuchObjectError .. autoclass:: EasySNMPNoSuchInstanceError .. autoclass:: EasySNMPUndeterminedTypeError easysnmp-0.2.6/docs/index.rst000066400000000000000000000113041426455670300161620ustar00rootroot00000000000000Welcome to Easy SNMP ==================== *A blazingly fast and Pythonic SNMP library based on the official Net-SNMP bindings* Introduction ------------ This is a fork of the official `Net-SNMP Python Bindings`_ but attempts to bring a more Pythonic interface to the library. Check out the `Net-SNMP website`_ for more information about SNMP. This module provides a full featured SNMP client API supporting all dialects of the SNMP protocol. .. _Net-SNMP Python Bindings: http://net-snmp.sourceforge.net/wiki/index.php/Python_Bindings .. _Net-SNMP website: http://www.net-snmp.org/ Why Another Library? -------------------- * The `original Net-SNMP Python library`_ is a great starting point but is quite un-Pythonic and lacks proper unit tests and documentation. * `PySNMP`_ is written in pure Python and therefore has a huge performance hit. In some brief tests, I estimate that both the Net-SNMP Python bindings and Easy SNMP are more than 4 times faster. Further to this, PySNMP has an even less Pythonic interface than the official Net-SNMP bindings. * Many other libraries like `Snimpy`_ are sadly based on PySNMP and so they suffer the same performance penalty. .. _original Net-SNMP Python library: http://net-snmp.sourceforge.net/wiki/index.php/Python_Bindings .. _PySNMP: http://pysnmp.sourceforge.net/ .. _Snimpy: https://snimpy.readthedocs.org/en/latest/ Installation ------------ EasySNMP has been tested and is supported on systems running Net-SNMP 5.7.x and newer. All non-EOL versions of Python 3 are fully supported, with 2.7 and recent EOL versions of Python 3 receiving partial support. If your OS ships with a supported version of Net-SNMP, then you can install it without compiling it via your package manager: On RHEL / CentOS systems: .. code-block:: bash sudo yum install net-snmp-devel On Debian / Ubuntu systems: .. code-block:: bash sudo apt-get install libsnmp-dev snmp-mibs-downloader On macOS systems: .. code-block:: bash brew install net-snmp If your OS doesn't ship with Net-SNMP 5.7.x or newer, please follow instructions provided on the `Net-SNMP install page `_ to build and install Net-SNMP on your system. You'll also need to ensure that you have the following packages installed so that Easy SNMP installs correctly: On RHEL / CentOS systems: .. code-block:: bash sudo yum install gcc python-devel On Debian / Ubuntu systems: .. code-block:: bash sudo apt-get install gcc python-dev On macOS systems: .. code-block:: bash brew install gcc Install Easy SNMP via pip as follows: .. code-block:: bash pip install easysnmp Quick Start ----------- There are primarily two ways you can use the Easy SNMP library. The first is with the use of a Session object which is most suitable when you are planning on requesting multiple pieces of SNMP data from a source. .. code-block:: python from easysnmp import Session # Create an SNMP session to be used for all our requests session = Session(hostname='localhost', community='public', version=2) # You may retrieve an individual OID using an SNMP GET location = session.get('sysLocation.0') # You may also specify the OID as a tuple (name, index) # Note: the index is specified as a string as it can be of other types than # just a regular integer contact = session.get(('sysContact', '0')) # And of course, you may use the numeric OID too description = session.get('.1.3.6.1.2.1.1.1.0') # Set a variable using an SNMP SET session.set('sysLocation.0', 'The SNMP Lab') # Perform an SNMP walk system_items = session.walk('system') # Each returned item can be used normally as its related type (str or int) # but also has several extended attributes with SNMP-specific information for item in system_items: print '{oid}.{oid_index} {snmp_type} = {value}'.format( oid=item.oid, oid_index=item.oid_index, snmp_type=item.snmp_type, value=item.value ) You may also use Easy SNMP via its simple interface which is intended for one-off operations where you wish to specify all details in the request: .. code-block:: python from easysnmp import snmp_get, snmp_set, snmp_walk # Grab a single piece of information using an SNMP GET snmp_get('sysDescr.0', hostname='localhost', community='public', version=1) # Perform an SNMP SET to update data snmp_set( 'sysLocation.0', 'My Cool Place', hostname='localhost', community='public', version=1 ) # Perform an SNMP walk snmp_walk('system', hostname='localhost', community='public', version=1) API --- .. toctree:: :maxdepth: 2 session_api easy_api exceptions easysnmp-0.2.6/docs/session_api.rst000066400000000000000000000002451426455670300173710ustar00rootroot00000000000000Session API ----------- .. currentmodule:: easysnmp .. autoclass:: Session :members: get, set, set_multiple, get_next, get_bulk, walk, bulkwalk, update_session easysnmp-0.2.6/easysnmp/000077500000000000000000000000001426455670300152315ustar00rootroot00000000000000easysnmp-0.2.6/easysnmp/__init__.py000066400000000000000000000007351426455670300173470ustar00rootroot00000000000000from .easy import ( # noqa snmp_get, snmp_set, snmp_set_multiple, snmp_get_next, snmp_get_bulk, snmp_walk, snmp_bulkwalk, ) from .exceptions import ( # noqa EasySNMPError, EasySNMPConnectionError, EasySNMPTimeoutError, EasySNMPUnknownObjectIDError, EasySNMPNoSuchObjectError, EasySNMPNoSuchInstanceError, EasySNMPUndeterminedTypeError, ) from .session import Session # noqa from .variables import SNMPVariable # noqa easysnmp-0.2.6/easysnmp/compat.py000066400000000000000000000012701426455670300170660ustar00rootroot00000000000000import logging import sys PY3 = sys.version_info[0] == 3 if PY3: text_type = str def ub(s): return s def urepr(s): return repr(s) else: text_type = unicode def ub(s): return s.decode("latin-1") def urepr(s): if isinstance(s, unicode): return repr(s)[1:] else: return repr(s) class NullHandler(logging.Handler): """ This handler does nothing. It's intended to be used to avoid the "No handlers could be found for logger XXX" one-off warning. """ def handle(self, record): pass def emit(self, record): pass def createLock(self): self.lock = None easysnmp-0.2.6/easysnmp/easy.py000066400000000000000000000136671426455670300165610ustar00rootroot00000000000000from __future__ import unicode_literals, absolute_import from .session import Session def snmp_get(oids, **session_kargs): """ Perform an SNMP GET operation to retrieve a particular piece of information. :param oids: you may pass in a list of OIDs or single item; each item may be a string representing the entire OID (e.g. 'sysDescr.0') or may be a tuple containing the name as its first item and index as its second (e.g. ('sysDescr', 0)) :param session_kargs: keyword arguments which will be sent used when constructing the session for this operation; all parameters in the Session class are supported """ session = Session(**session_kargs) return session.get(oids) def snmp_set(oid, value, type=None, **session_kargs): """ Perform an SNMP SET operation to update a particular piece of information. :param oid: the OID that you wish to set which may be a string representing the entire OID (e.g. 'sysDescr.0') or may be a tuple containing the name as its first item and index as its second (e.g. ('sysDescr', 0)) :param value: the value to set the OID to :param snmp_type: if a numeric OID is used and the object is not in the parsed MIB, a type must be explicitly supplied :param session_kargs: keyword arguments which will be sent used when constructing the session for this operation; all parameters in the Session class are supported """ session = Session(**session_kargs) return session.set(oid, value, type) def snmp_set_multiple(oid_values, **session_kargs): """ Perform multiple SNMP SET operations to update various pieces of information at the same time. :param oid_values: a list of tuples whereby each tuple contains a (oid, value) or an (oid, value, snmp_type) :param session_kargs: keyword arguments which will be sent used when constructing the session for this operation; all parameters in the Session class are supported """ session = Session(**session_kargs) return session.set_multiple(oid_values) def snmp_get_next(oids, **session_kargs): """ Uses an SNMP GETNEXT operation to retrieve the next variable after the chosen item. :param oids: you may pass in a list of OIDs or single item; each item may be a string representing the entire OID (e.g. 'sysDescr.0') or may be a tuple containing the name as its first item and index as its second (e.g. ('sysDescr', 0)) :param session_kargs: keyword arguments which will be sent used when constructing the session for this operation; all parameters in the Session class are supported """ session = Session(**session_kargs) return session.get_next(oids) def snmp_get_bulk(oids, non_repeaters=0, max_repetitions=10, **session_kargs): """ Performs a bulk SNMP GET operation to retrieve multiple pieces of information in a single packet. :param oids: you may pass in a list of OIDs or single item; each item may be a string representing the entire OID (e.g. 'sysDescr.0') or may be a tuple containing the name as its first item and index as its second (e.g. ('sysDescr', 0)) :param non_repeaters: the number of objects that are only expected to return a single GETNEXT instance, not multiple instances :param max_repetitions: the number of objects that should be returned for all the repeating OIDs :param session_kargs: keyword arguments which will be sent used when constructing the session for this operation; all parameters in the Session class are supported """ session = Session(**session_kargs) return session.get_bulk(oids, non_repeaters, max_repetitions) def snmp_walk(oids=".1.3.6.1.2.1", **session_kargs): """ Uses SNMP GETNEXT operation to automatically retrieve multiple pieces of information in an OID for you. :param oids: you may pass in a single item (multiple values currently experimental) which may be a string representing the entire OID (e.g. 'sysDescr.0') or may be a tuple containing the name as its first item and index as its second (e.g. ('sysDescr', 0)) :param session_kargs: keyword arguments which will be sent used when constructing the session for this operation; all parameters in the Session class are supported """ session = Session(**session_kargs) return session.walk(oids) def snmp_bulkwalk( oids=".1.3.6.1.2.1", non_repeaters=0, max_repetitions=10, **session_kargs ): """ Uses SNMP GETBULK operation using the prepared session to automatically retrieve multiple pieces of information in an OID :param oids: you may pass in a single item * string representing the entire OID (e.g. 'sysDescr.0') * tuple (name, index) (e.g. ('sysDescr', 0)) * list of OIDs :param non_repeaters: the number of objects that are only expected to return a single GETNEXT instance, not multiple instances :param max_repetitions: the number of objects that should be returned for all the repeating OIDs :return: a list of SNMPVariable objects containing the values that were retrieved via SNMP """ session = Session(**session_kargs) return session.bulkwalk(oids, non_repeaters, max_repetitions) easysnmp-0.2.6/easysnmp/exceptions.py000066400000000000000000000022661426455670300177720ustar00rootroot00000000000000from __future__ import unicode_literals class EasySNMPError(Exception): """The base Easy SNMP exception which covers all exceptions raised.""" pass class EasySNMPConnectionError(EasySNMPError): """Indicates a problem connecting to the remote host.""" pass class EasySNMPTimeoutError(EasySNMPConnectionError): """Raised when an SNMP request times out.""" pass class EasySNMPUnknownObjectIDError(EasySNMPError): """Raised when a nonexistent OID is requested.""" pass class EasySNMPNoSuchNameError(EasySNMPError): """ Raised when an OID is requested which may be an invalid object name or invalid instance (only applies to SNMPv1). """ pass class EasySNMPNoSuchObjectError(EasySNMPError): """ Raised when an OID is requested which may have some form of existence but is an invalid object name. """ pass class EasySNMPNoSuchInstanceError(EasySNMPError): """ Raised when a particular OID index requested from Net-SNMP doesn't exist. """ pass class EasySNMPUndeterminedTypeError(EasySNMPError): """ Raised when the type cannot be determined when setting the value of an OID. """ pass easysnmp-0.2.6/easysnmp/helpers.py000066400000000000000000000021251426455670300172450ustar00rootroot00000000000000from __future__ import unicode_literals import re # This regular expression is used to extract the index from an OID OID_INDEX_RE = re.compile( r"""( \.?\d+(?:\.\d+)* # numeric OID | # or (?:\w+(?:[-:]*\w+)+) # regular OID | # or (?:\.?iso(?:\.\w+[-:]*\w+)+) # fully qualified OID ) \.?(.*) # OID index """, re.VERBOSE, ) def normalize_oid(oid, oid_index=None): """ Ensures that the index is set correctly given an OID definition. :param oid: the OID to normalize :param oid_index: the OID index to normalize """ # Determine the OID index from the OID if not specified if oid_index is None and oid is not None: # We attempt to extract the index from an OID (e.g. sysDescr.0 # or .iso.org.dod.internet.mgmt.mib-2.system.sysContact.0) match = OID_INDEX_RE.match(oid) if match: oid, oid_index = match.group(1, 2) return oid, oid_index easysnmp-0.2.6/easysnmp/interface.c000066400000000000000000004125611426455670300173460ustar00rootroot00000000000000/* This is the main interface for the EasySNMP python module. */ #include /* * Old versions of Python use CObject API instead of Capsules. * Both are similar, so we just use a bit of preprocessor magic * to provide backwards compatibility. */ #if ( \ (PY_VERSION_HEX < 0x02070000) || \ ((PY_VERSION_HEX >= 0x03000000) && \ (PY_VERSION_HEX < 0x03010000))) #define USE_DEPRECATED_COBJECT_API #define PyCapsule_New(pointer, name, destructor) \ (PyCObject_FromVoidPtr(pointer, destructor)) #define PyCapsule_GetPointer(capsule, name) \ (PyCObject_AsVoidPtr(capsule)) #endif /* PY_VERSION_HEX */ #include #include #include #include #include #include #include #include #include #include #include #ifdef I_SYS_TIME #include #endif #include #include #include #include #ifdef HAVE_REGEX_H #include #endif /* include bitarray data structure for v1 queries */ #include "simple_bitarray.h" #include "interface.h" /****************************************************************************** * * PyObjects used in the 'interface.c' file are listed below * ******************************************************************************/ static PyObject *easysnmp_import = NULL; static PyObject *easysnmp_exceptions_import = NULL; static PyObject *easysnmp_compat_import = NULL; static PyObject *logging_import = NULL; static PyObject *PyLogger = NULL; static PyObject *EasySNMPError = NULL; static PyObject *EasySNMPConnectionError = NULL; static PyObject *EasySNMPTimeoutError = NULL; static PyObject *EasySNMPNoSuchNameError = NULL; static PyObject *EasySNMPUnknownObjectIDError = NULL; static PyObject *EasySNMPNoSuchObjectError = NULL; static PyObject *EasySNMPUndeterminedTypeError = NULL; /* * Ripped wholesale from library/tools.h from Net-SNMP 5.7.3 * to remain compatible with versions 5.7.2 and earlier. */ static void *compat_netsnmp_memdup(const void *from, size_t size) { void *to = NULL; if (from) { to = malloc(size); if (to) { memcpy(to, from, size); } } return to; } /* * Match the user's algorithm of choice to the proper flags and length. * * @param int is_auth: 1 or greater indicates we are looking for hashing algos. * 0 or lower indicates we are looking for encryption (privacy) algos. * * @param char algo: User's input string. Exact matches only. * @param char output: Pointer to assign the matched algorithm to. * @param size_t len: Pointer to assign the algorithm output size to. * @returns int: 0 on success, non-zero if no match found */ static int __match_algo(int is_auth, char *algo, oid **output, size_t *len) { int found = -1; *output = NULL; *len = 0; if (is_auth >= 1) // Authethentication. Hashes, i.e. MD5, SHA, etc. { #ifndef NETSNMP_DISABLE_MD5 if (strcmp(algo, "MD5") == 0) { *output = usmHMACMD5AuthProtocol; *len = sizeof(usmHMACMD5AuthProtocol) / sizeof(oid); found = 0; } else if (strcmp(algo, "SHA") == 0 || #else if (strcmp(algo, "SHA") == 0 || #endif // NETSNMP_DISABLE_MD5 strcmp(algo, "SHA1") == 0 || strcmp(algo, "SHA-1") == 0) { *output = usmHMACSHA1AuthProtocol; *len = sizeof(usmHMACSHA1AuthProtocol) / sizeof(oid); found = 0; } #ifdef HAVE_EVP_SHA224 else if (strcmp(algo, "SHA224") == 0 || strcmp(algo, "SHA-224") == 0) { *output = usmHMAC128SHA224AuthProtocol; *len = sizeof(usmHMAC128SHA224AuthProtocol) / sizeof(oid); found = 0; } else if (strcmp(algo, "SHA256") == 0 || strcmp(algo, "SHA-256") == 0) { *output = usmHMAC192SHA256AuthProtocol; *len = sizeof(usmHMAC192SHA256AuthProtocol) / sizeof(oid); found = 0; } #endif // HAVE_EVP_SHA224 #ifdef HAVE_EVP_SHA384 else if (strcmp(algo, "SHA384") == 0 || strcmp(algo, "SHA-384") == 0) { *output = usmHMAC256SHA384AuthProtocol; *len = sizeof(usmHMAC256SHA384AuthProtocol) / sizeof(oid); found = 0; } else if (strcmp(algo, "SHA512") == 0 || strcmp(algo, "SHA-512") == 0) { *output = usmHMAC384SHA512AuthProtocol; *len = sizeof(usmHMAC384SHA512AuthProtocol) / sizeof(oid); found = 0; } #endif // HAVE_EVP_SHA384 else if (strcmp(algo, "DEFAULT") == 0) { *output = (oid *)get_default_authtype(len); found = 0; } } else // Privacy. Encryption, i.e. DES, AES, etc. { #ifndef NETSNMP_DISABLE_DES if (strcmp(algo, "DES") == 0) { *output = usmDESPrivProtocol; *len = sizeof(usmDESPrivProtocol) / sizeof(oid); found = 0; } #endif // NETSNMP_DISABLE_DES #ifdef HAVE_AES #ifndef NETSNMP_DISABLE_DES else if (strcmp(algo, "AES128") == 0 || #else if (strcmp(algo, "AES128") == 0 || #endif // NETSNMP_DISABLE_AES strcmp(algo, "AES-128") == 0 || strcmp(algo, "AES") == 0) { *output = usmAESPrivProtocol; *len = sizeof(usmAESPrivProtocol) / sizeof(oid); found = 0; } #endif // HAVE_AES #ifdef NETSNMP_DRAFT_BLUMENTHAL_AES_04 #if defined(HAVE_AES) || !defined(NETSNMP_DISABLE_DES) else if (strcmp(algo, "AES192") == 0 || #else if (strcmp(algo, "AES192") == 0 || #endif // HAVE_AES | NETSNMP_DISABLE_DES strcmp(algo, "AES-192") == 0) { *output = usmAES192PrivProtocol; *len = sizeof(usmAES192PrivProtocol) / sizeof(oid); found = 0; } else if (strcmp(algo, "AES256") == 0 || strcmp(algo, "AES-256") == 0) { *output = usmAES256PrivProtocol; *len = sizeof(usmAES256PrivProtocol) / sizeof(oid); found = 0; } else if (strcmp(algo, "AES192C") == 0 || strcmp(algo, "AES-192-C") == 0 || strcmp(algo, "AES-192C") == 0 || strcmp(algo, "AES192-C") == 0) { *output = usmAES192CiscoPrivProtocol; *len = sizeof(usmAES192CiscoPrivProtocol) / sizeof(oid); found = 0; } else if (strcmp(algo, "AES256C") == 0 || strcmp(algo, "AES-256-C") == 0 || strcmp(algo, "AES-256C") == 0 || strcmp(algo, "AES256-C") == 0) { *output = usmAES256CiscoPrivProtocol; *len = sizeof(usmAES256CiscoPrivProtocol) / sizeof(oid); found = 0; } #endif // NETSNMP_DRAFT_BLUMENTHAL_AES_04 #if !defined(NETSNMP_DISABLE_DES) || defined(HAVE_AES) || defined(NETSNMP_DRAFT_BLUMENTHAL_AES_04) else if (strcmp(algo, "DEFAULT") == 0) #else if (strcmp(algo, "DEFAULT") == 0) #endif { *output = (oid *)get_default_privtype(len); found = 0; } } return found; } void __libraries_init() { static int have_inited = 0; if (have_inited) { return; } have_inited = 1; snmp_set_quick_print(1); /* completely disable logging otherwise it will default to stderr */ netsnmp_register_loghandler(NETSNMP_LOGHANDLER_NONE, 0); init_snmp(APPNAME); netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_BREAKDOWN_OIDS, 1); netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_SUFFIX_ONLY, 1); netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_SUFFIX); } void __libraries_free() { snmp_shutdown(APPNAME); } static int __is_numeric_oid(char *oidstr) { if (!oidstr) { return 0; } for (; *oidstr; oidstr++) { if (isalpha((int)*oidstr)) { return 0; } } return 1; } static int __is_leaf(struct tree *tp) { char buf[MAX_TYPE_NAME_LEN]; return (tp && (__get_type_str(tp->type, buf, 0) || (tp->parent && __get_type_str(tp->parent->type, buf, 0)))); } static int __translate_appl_type(char *typestr) { if (typestr == NULL || *typestr == '\0') { return TYPE_UNKNOWN; } /* * looking at a one-char string, so use snmpset(1) * type specification that allows for single characters * (note: 'd' for decimal string and 'x' for hex is missing) */ if (typestr[1] == '\0') { switch (typestr[0]) { case 'i': return TYPE_INTEGER; case 'u': return TYPE_UNSIGNED32; case 's': return TYPE_OCTETSTR; case 'n': return TYPE_NULL; case 'o': return TYPE_OBJID; case 't': return TYPE_TIMETICKS; case 'a': return TYPE_IPADDR; case 'b': return TYPE_BITSTRING; default: return TYPE_UNKNOWN; } } if (!strncasecmp(typestr, "INTEGER32", 8)) { return TYPE_INTEGER32; } if (!strncasecmp(typestr, "INTEGER", 3)) { return TYPE_INTEGER; } if (!strncasecmp(typestr, "UNSIGNED32", 3)) { return TYPE_UNSIGNED32; } if (!strcasecmp(typestr, "COUNTER")) /* check all in case counter64 */ { return TYPE_COUNTER; } if (!strncasecmp(typestr, "GAUGE", 3)) { return TYPE_GAUGE; } if (!strncasecmp(typestr, "IPADDR", 3)) { return TYPE_IPADDR; } if (!strncasecmp(typestr, "OCTETSTR", 3)) { return TYPE_OCTETSTR; } if (!strncasecmp(typestr, "TICKS", 3)) { return TYPE_TIMETICKS; } if (!strncasecmp(typestr, "OPAQUE", 3)) { return TYPE_OPAQUE; } if (!strncasecmp(typestr, "OBJECTID", 3)) { return TYPE_OBJID; } if (!strncasecmp(typestr, "NETADDR", 3)) { return TYPE_NETADDR; } if (!strncasecmp(typestr, "COUNTER64", 3)) { return TYPE_COUNTER64; } if (!strncasecmp(typestr, "NULL", 3)) { return TYPE_NULL; } if (!strncasecmp(typestr, "BITS", 3)) { return TYPE_BITSTRING; } if (!strncasecmp(typestr, "ENDOFMIBVIEW", 3)) { return SNMP_ENDOFMIBVIEW; } if (!strncasecmp(typestr, "NOSUCHOBJECT", 7)) { return SNMP_NOSUCHOBJECT; } if (!strncasecmp(typestr, "NOSUCHINSTANCE", 7)) { return SNMP_NOSUCHINSTANCE; } if (!strncasecmp(typestr, "UINTEGER", 3)) { return (TYPE_UINTEGER); /* historic - should not show up */ /* but it does? */ } if (!strncasecmp(typestr, "NOTIF", 3)) { return TYPE_NOTIFTYPE; } if (!strncasecmp(typestr, "TRAP", 4)) { return TYPE_TRAPTYPE; } return TYPE_UNKNOWN; } static int __translate_asn_type(int type) { switch (type) { case ASN_INTEGER: return TYPE_INTEGER; case ASN_OCTET_STR: return TYPE_OCTETSTR; case ASN_OPAQUE: return TYPE_OPAQUE; case ASN_OBJECT_ID: return TYPE_OBJID; case ASN_TIMETICKS: return TYPE_TIMETICKS; case ASN_GAUGE: return TYPE_GAUGE; case ASN_COUNTER: return TYPE_COUNTER; case ASN_IPADDRESS: return TYPE_IPADDR; case ASN_BIT_STR: return TYPE_BITSTRING; case ASN_NULL: return TYPE_NULL; /* no translation for these exception type values */ case SNMP_ENDOFMIBVIEW: case SNMP_NOSUCHOBJECT: case SNMP_NOSUCHINSTANCE: return type; case ASN_UINTEGER: return TYPE_UINTEGER; case ASN_COUNTER64: return TYPE_COUNTER64; default: py_log_msg(ERROR, "translate_asn_type: unhandled asn type (%d)", type); return TYPE_OTHER; } } #define USE_BASIC (0) #define USE_ENUMS (1) #define USE_SPRINT_VALUE (2) static int __snprint_value(char *buf, size_t buf_len, netsnmp_variable_list *var, struct tree *tp, int type, int flag) { int len = 0; u_char *ip; struct enum_list *ep; buf[0] = '\0'; if (flag == USE_SPRINT_VALUE) { snprint_value(buf, buf_len, var->name, var->name_length, var); len = STRLEN(buf); } else { switch (var->type) { case ASN_INTEGER: if (flag == USE_ENUMS) { for (ep = tp->enums; ep; ep = ep->next) { if (ep->value == *var->val.integer) { strlcpy(buf, ep->label, buf_len); len = STRLEN(buf); break; } } } if (!len) { snprintf(buf, buf_len, "%ld", *var->val.integer); len = STRLEN(buf); } break; case ASN_GAUGE: case ASN_COUNTER: case ASN_TIMETICKS: case ASN_UINTEGER: snprintf(buf, buf_len, "%lu", (unsigned long)*var->val.integer); len = STRLEN(buf); break; case ASN_OCTET_STR: case ASN_OPAQUE: len = var->val_len; if (len > buf_len) { len = buf_len; } memcpy(buf, (char *)var->val.string, len); break; case ASN_IPADDRESS: ip = (u_char *)var->val.string; snprintf(buf, buf_len, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); len = STRLEN(buf); break; case ASN_NULL: break; case ASN_OBJECT_ID: __sprint_num_objid(buf, (oid *)(var->val.objid), var->val_len / sizeof(oid)); len = STRLEN(buf); break; case SNMP_ENDOFMIBVIEW: snprintf(buf, buf_len, "%s", "ENDOFMIBVIEW"); len = STRLEN(buf); break; case SNMP_NOSUCHOBJECT: snprintf(buf, buf_len, "%s", "NOSUCHOBJECT"); len = STRLEN(buf); break; case SNMP_NOSUCHINSTANCE: snprintf(buf, buf_len, "%s", "NOSUCHINSTANCE"); len = STRLEN(buf); break; case ASN_COUNTER64: #ifdef OPAQUE_SPECIAL_TYPES case ASN_OPAQUE_COUNTER64: case ASN_OPAQUE_U64: #endif printU64(buf, (struct counter64 *)var->val.counter64); len = STRLEN(buf); break; #ifdef OPAQUE_SPECIAL_TYPES case ASN_OPAQUE_I64: printI64(buf, (struct counter64 *)var->val.counter64); len = STRLEN(buf); break; #endif case ASN_BIT_STR: snprint_bitstring(buf, buf_len, var, NULL, NULL, NULL); len = STRLEN(buf); break; #ifdef OPAQUE_SPECIAL_TYPES case ASN_OPAQUE_FLOAT: if (var->val.floatVal) { snprintf(buf, buf_len, "%f", *var->val.floatVal); } break; case ASN_OPAQUE_DOUBLE: if (var->val.doubleVal) { snprintf(buf, buf_len, "%f", *var->val.doubleVal); } break; #endif case ASN_NSAP: default: py_log_msg(ERROR, "snprint_value: asn type not handled %d", var->type); } } return len; } static int __sprint_num_objid(char *buf, oid *objid, int len) { int i; buf[0] = '\0'; for (i = 0; i < len; i++) { sprintf(buf, ".%lu", *objid++); buf += STRLEN(buf); } return SUCCESS; } static int __scan_num_objid(char *buf, oid *objid, size_t *len) { char *cp; *len = 0; if (*buf == '.') { buf++; } cp = buf; while (*buf) { if (*buf++ == '.') { sscanf(cp, "%lu", objid++); /* *objid++ = atoi(cp); */ (*len)++; cp = buf; } else { if (isalpha((int)*buf)) { return FAILURE; } } } sscanf(cp, "%lu", objid++); /* *objid++ = atoi(cp); */ (*len)++; return SUCCESS; } static int __get_type_str(int type, char *str, int log_error) { switch (type) { case TYPE_OBJID: strcpy(str, "OBJECTID"); break; case TYPE_OCTETSTR: strcpy(str, "OCTETSTR"); break; case TYPE_INTEGER: strcpy(str, "INTEGER"); break; case TYPE_INTEGER32: strcpy(str, "INTEGER32"); break; case TYPE_UNSIGNED32: strcpy(str, "UNSIGNED32"); break; case TYPE_NETADDR: strcpy(str, "NETADDR"); break; case TYPE_IPADDR: strcpy(str, "IPADDR"); break; case TYPE_COUNTER: strcpy(str, "COUNTER"); break; case TYPE_GAUGE: strcpy(str, "GAUGE"); break; case TYPE_TIMETICKS: strcpy(str, "TICKS"); break; case TYPE_OPAQUE: strcpy(str, "OPAQUE"); break; case TYPE_COUNTER64: strcpy(str, "COUNTER64"); break; case TYPE_NULL: strcpy(str, "NULL"); break; case SNMP_ENDOFMIBVIEW: strcpy(str, "ENDOFMIBVIEW"); break; case SNMP_NOSUCHOBJECT: strcpy(str, "NOSUCHOBJECT"); break; case SNMP_NOSUCHINSTANCE: strcpy(str, "NOSUCHINSTANCE"); break; case TYPE_UINTEGER: strcpy(str, "UINTEGER"); /* historic - should not show up */ /* but it does? */ break; case TYPE_NOTIFTYPE: strcpy(str, "NOTIF"); break; case TYPE_BITSTRING: strcpy(str, "BITS"); break; case TYPE_TRAPTYPE: strcpy(str, "TRAP"); break; case TYPE_OTHER: /* not sure if this is a valid leaf type?? */ case TYPE_NSAPADDRESS: default: /* unsupported types for now */ strcpy(str, ""); if (log_error) { py_log_msg(ERROR, "unspported type found: %d", type); } return FAILURE; } return SUCCESS; } /* does a destructive disection of .... returning and in seperate strings (note: will destructively alter input string, 'name') */ static int __get_label_iid(char *name, char **last_label, char **iid, int flag) { char *lcp; char *icp; int len = STRLEN(name); int found_label = 0; *last_label = *iid = NULL; if (len == 0) { return FAILURE; } /* Handle case where numeric oid's have been requested. The input 'name' ** in this case should be a numeric OID -- return failure if not. */ if ((flag & USE_NUMERIC_OIDS)) { if (!__is_numeric_oid(name)) { return FAILURE; } /* Walk backward through the string, looking for first two '.' chars */ lcp = &(name[len]); icp = NULL; while (lcp > name) { if (*lcp == '.') { /* If this is the first occurence of '.', note it in icp. ** Otherwise, this must be the second occurrence, so break ** out of the loop. */ if (icp == NULL) { icp = lcp; } else { break; } } lcp--; } /* Make sure we found at least a label and index. */ if (!icp) { return FAILURE; } /* Push forward past leading '.' chars and separate the strings. */ lcp++; *icp++ = '\0'; *last_label = (flag & USE_LONG_NAMES) ? name : lcp; *iid = icp; return SUCCESS; } lcp = icp = &(name[len]); while (lcp > name) { if (*lcp == '.') { if (found_label) { lcp++; break; } else { icp = lcp; } } if (!found_label && isalpha((int)*lcp)) { found_label = 1; } lcp--; } if (!found_label || (!isdigit((int)*(icp + 1)) && (flag & FAIL_ON_NULL_IID))) { return FAILURE; } if (flag & NON_LEAF_NAME) /* dont know where to start instance id */ { /* put the whole thing in label */ icp = &(name[len]); flag |= USE_LONG_NAMES; /* special hack in case no mib loaded - object identifiers will * start with .iso......, in which case it is preferable * to make the label entirely numeric (i.e., convert "iso" => "1") */ if (*lcp == '.' && lcp == name) { if (!strncmp(".ccitt.", lcp, 7)) { name += 2; *name = '.'; *(name + 1) = '0'; } else if (!strncmp(".iso.", lcp, 5)) { name += 2; *name = '.'; *(name + 1) = '1'; } else if (!strncmp(".joint-iso-ccitt.", lcp, 17)) { name += 2; *name = '.'; *(name + 1) = '2'; } } } else if (*icp) { *(icp++) = '\0'; } *last_label = (flag & USE_LONG_NAMES ? name : lcp); *iid = icp; return SUCCESS; } /* Convert a tag (string) to an OID array */ /* Tag can be either a symbolic name, or an OID string */ static struct tree *__tag2oid(char *tag, char *iid, oid *oid_arr, int *oid_arr_len, int *type, int best_guess) { struct tree *tp = NULL; struct tree *rtp = NULL; oid newname[MAX_OID_LEN], *op; size_t newname_len = 0; if (type) { *type = TYPE_UNKNOWN; } if (oid_arr_len) { *oid_arr_len = 0; } if (!tag) { goto done; } /*********************************************************/ /* best_guess = 0 - same as no switches (read_objid) */ /* if multiple parts, or uses find_node */ /* if a single leaf */ /* best_guess = 1 - same as -Ib (get_wild_node) */ /* best_guess = 2 - same as -IR (get_node) */ /*********************************************************/ /* numeric scalar (1,2) */ /* single symbolic (1,2) */ /* single regex (1) */ /* partial full symbolic (2) */ /* full symbolic (2) */ /* module::single symbolic (2) */ /* module::partial full symbolic (2) */ if (best_guess == 1 || best_guess == 2) { /* make sure it's not a numeric tag */ if (!__scan_num_objid(tag, newname, &newname_len)) { newname_len = MAX_OID_LEN; if (best_guess == 2) /* Random search -IR */ { if (get_node(tag, newname, &newname_len)) { rtp = tp = get_tree(newname, newname_len, get_tree_head()); } } else if (best_guess == 1) /* Regex search -Ib */ { clear_tree_flags(get_tree_head()); if (get_wild_node(tag, newname, &newname_len)) { rtp = tp = get_tree(newname, newname_len, get_tree_head()); } } } else { rtp = tp = get_tree(newname, newname_len, get_tree_head()); } if (type) { *type = (tp ? tp->type : TYPE_UNKNOWN); } if ((oid_arr == NULL) || (oid_arr_len == NULL)) { return rtp; } memcpy(oid_arr, (char *)newname, newname_len * sizeof(oid)); *oid_arr_len = newname_len; } /* if best_guess is off and multi part tag or module::tag */ /* numeric scalar */ /* module::single symbolic */ /* module::partial full symbolic */ /* FULL symbolic OID */ else if (strchr(tag, '.') || strchr(tag, ':')) { /* make sure it's not a numeric tag */ if (!__scan_num_objid(tag, newname, &newname_len)) { newname_len = MAX_OID_LEN; if (read_objid(tag, newname, &newname_len)) /* long name */ { rtp = tp = get_tree(newname, newname_len, get_tree_head()); } else { /* failed to parse the OID */ newname_len = 0; } } else { rtp = tp = get_tree(newname, newname_len, get_tree_head()); } if (type) { *type = (tp ? tp->type : TYPE_UNKNOWN); } if ((oid_arr == NULL) || (oid_arr_len == NULL)) { return rtp; } memcpy(oid_arr, (char *)newname, newname_len * sizeof(oid)); *oid_arr_len = newname_len; } /* else best_guess is off and it is a single leaf */ /* single symbolic */ else { rtp = tp = find_node(tag, get_tree_head()); if (tp) { if (type) { *type = tp->type; } if ((oid_arr == NULL) || (oid_arr_len == NULL)) { return rtp; } /* code taken from get_node in snmp_client.c */ for (op = newname + MAX_OID_LEN - 1; op >= newname; op--) { *op = tp->subid; tp = tp->parent; if (tp == NULL) { break; } } *oid_arr_len = newname + MAX_OID_LEN - op; memcpy(oid_arr, op, *oid_arr_len * sizeof(oid)); } else { return rtp; /* HACK: otherwise, concat_oid_str confuses things */ } } done: if (iid && *iid && oid_arr_len) { __concat_oid_str(oid_arr, oid_arr_len, iid); } return rtp; } /* function: __concat_oid_str * * This function converts a dotted-decimal string, soid_str, to an array * of oid types and concatenates them on doid_arr begining at the index * specified by doid_arr_len. * * returns : SUCCESS, FAILURE */ static int __concat_oid_str(oid *doid_arr, int *doid_arr_len, char *soid_str) { char *soid_buf; char *cp; char *st; if (!soid_str || !*soid_str) { return SUCCESS; /* successfully added nothing */ } if (*soid_str == '.') { soid_str++; } soid_buf = strdup(soid_str); if (!soid_buf) { return FAILURE; } cp = strtok_r(soid_buf, ".", &st); while (cp) { sscanf(cp, "%lu", &(doid_arr[(*doid_arr_len)++])); /* doid_arr[(*doid_arr_len)++] = atoi(cp); */ cp = strtok_r(NULL, ".", &st); } free(soid_buf); return SUCCESS; } /* add a varbind to PDU */ static int __add_var_val_str(netsnmp_pdu *pdu, oid *name, int name_length, char *val, int len, int type) { netsnmp_variable_list *vars; oid oidbuf[MAX_OID_LEN]; int ret = SUCCESS; if (pdu->variables == NULL) { vars = (netsnmp_variable_list *)calloc(1, sizeof(netsnmp_variable_list)); pdu->variables = vars; } else { /* make a copy of PDU variables */ for (vars = pdu->variables; vars->next_variable; vars = vars->next_variable) { /* EXIT */; } vars->next_variable = (netsnmp_variable_list *)calloc(1, sizeof(netsnmp_variable_list)); vars = vars->next_variable; } vars->next_variable = NULL; vars->name = snmp_duplicate_objid(name, name_length); vars->name_length = name_length; switch (type) { case TYPE_INTEGER: case TYPE_INTEGER32: vars->type = ASN_INTEGER; vars->val.integer = malloc(sizeof(long)); if (val) { *(vars->val.integer) = strtol(val, NULL, 0); } else { ret = FAILURE; *(vars->val.integer) = 0; } vars->val_len = sizeof(long); break; case TYPE_GAUGE: case TYPE_UNSIGNED32: vars->type = ASN_GAUGE; goto UINT; case TYPE_COUNTER: vars->type = ASN_COUNTER; goto UINT; case TYPE_TIMETICKS: vars->type = ASN_TIMETICKS; goto UINT; case TYPE_UINTEGER: vars->type = ASN_UINTEGER; UINT: vars->val.integer = malloc(sizeof(long)); if (val) { sscanf(val, "%lu", vars->val.integer); } else { ret = FAILURE; *(vars->val.integer) = 0; } vars->val_len = sizeof(long); break; case TYPE_OCTETSTR: vars->type = ASN_OCTET_STR; goto OCT; case TYPE_BITSTRING: vars->type = ASN_OCTET_STR; goto OCT; case TYPE_OPAQUE: vars->type = ASN_OCTET_STR; OCT: vars->val.string = malloc(len); vars->val_len = len; if (val && len) { memcpy((char *)vars->val.string, val, len); } else { ret = FAILURE; vars->val.string = (u_char *)strdup(""); vars->val_len = 0; } break; case TYPE_IPADDR: vars->type = ASN_IPADDRESS; { in_addr_t addr; if (val) { addr = inet_addr(val); } else { ret = FAILURE; addr = 0; } vars->val.integer = compat_netsnmp_memdup(&addr, sizeof(addr)); vars->val_len = sizeof(addr); } break; case TYPE_OBJID: vars->type = ASN_OBJECT_ID; vars->val_len = MAX_OID_LEN; /* if (read_objid(val, oidbuf, &(vars->val_len))) { */ /* tp = __tag2oid(val, NULL, oidbuf, &(vars->val_len), NULL, 0); */ if (!val || !snmp_parse_oid(val, oidbuf, &vars->val_len)) { vars->val.objid = NULL; ret = FAILURE; } else { vars->val.objid = snmp_duplicate_objid(oidbuf, vars->val_len); vars->val_len *= sizeof(oid); } break; default: vars->type = ASN_NULL; vars->val_len = 0; vars->val.string = NULL; ret = FAILURE; } return ret; } /* takes ss and pdu as input and updates the 'response' argument */ /* the input 'pdu' argument will be freed */ static int __send_sync_pdu(netsnmp_session *ss, netsnmp_pdu *pdu, netsnmp_pdu **response, int retry_nosuch, char *err_str, int *err_num, int *err_ind, bitarray *invalid_oids) { int status = 0; long command = pdu->command; char *tmp_err_str; size_t retry_num = 0; /* Note: SNMP uses 1-based indexing with OIDs, so 0 is unused */ unsigned long last_errindex = 0; *err_num = 0; *err_ind = 0; *response = NULL; tmp_err_str = NULL; memset(err_str, '\0', STR_BUF_SIZE); if (ss == NULL) { *err_num = 0; *err_ind = SNMPERR_BAD_SESSION; status = SNMPERR_BAD_SESSION; strlcpy(err_str, snmp_api_errstring(*err_ind), STR_BUF_SIZE); goto done; } retry: Py_BEGIN_ALLOW_THREADS status = snmp_sess_synch_response(ss, pdu, response); Py_END_ALLOW_THREADS if ((*response == NULL) && (status == STAT_SUCCESS)) { status = STAT_ERROR; } switch (status) { case STAT_SUCCESS: status = (*response)->errstat; switch (status) { case SNMP_ERR_NOERROR: break; case SNMP_ERR_NOSUCHNAME: /* * if retry_nosuch is set, then remove the offending * OID which returns with NoSuchName, until none exist. */ if (retry_nosuch) { /* * When using retry, we expect the agent to behave * in two ways: * * (1) provide error index in descending order (easy case) * (2) provide error index in ascending order (hard case) * * The reason (2) is hard, is because everytime an OID * is elided in the request PDU, we need to compensate. * * It is possible that the agent may perform pathologically * in which case we provide no guarantees whatsoever. */ if (!last_errindex) { /* we haven't seen an errindex yet */ bitarray_set_bit(invalid_oids, (*response)->errindex - 1); } else if (last_errindex > (*response)->errindex) { /* case (1) where error index is in descending order */ bitarray_set_bit(invalid_oids, (*response)->errindex - 1); } else { /* case (2) where error index is in ascending order */ bitarray_set_bit(invalid_oids, (*response)->errindex - 1 + retry_num); } /* finally we update the last_errindex for the next retry */ last_errindex = (*response)->errindex; /* * fix the GET REQUEST message using snmp_fix_pdu * which elidse variable which return NOSUCHNAME error, * until there is either a successful response * (which indicates SNMP_ERR_NOERROR) or returns NULL * likely indicating no more remaining variables. */ pdu = snmp_fix_pdu(*response, command); /* * The condition when pdu==NULL will happen when * there are no OIDs left to retry. */ if (!pdu) { status = STAT_SUCCESS; goto done; } if (*response) { snmp_free_pdu(*response); *response = NULL; } retry_num++; goto retry; } else /* !retry_nosuch */ { PyErr_SetString(EasySNMPNoSuchNameError, "no such name error encountered"); } break; /* Pv1, SNMPsec, Pv2p, v2c, v2u, v2*, and SNMPv3 PDUs */ case SNMP_ERR_TOOBIG: case SNMP_ERR_BADVALUE: case SNMP_ERR_READONLY: case SNMP_ERR_GENERR: /* in SNMPv2p, SNMPv2c, SNMPv2u, SNMPv2*, and SNMPv3 PDUs */ case SNMP_ERR_NOACCESS: case SNMP_ERR_WRONGTYPE: case SNMP_ERR_WRONGLENGTH: case SNMP_ERR_WRONGENCODING: case SNMP_ERR_WRONGVALUE: case SNMP_ERR_NOCREATION: case SNMP_ERR_INCONSISTENTVALUE: case SNMP_ERR_RESOURCEUNAVAILABLE: case SNMP_ERR_COMMITFAILED: case SNMP_ERR_UNDOFAILED: case SNMP_ERR_AUTHORIZATIONERROR: case SNMP_ERR_NOTWRITABLE: /* in SNMPv2c, SNMPv2u, SNMPv2*, and SNMPv3 PDUs */ case SNMP_ERR_INCONSISTENTNAME: default: strlcpy(err_str, (char *)snmp_errstring((*response)->errstat), STR_BUF_SIZE); *err_num = (int)(*response)->errstat; *err_ind = (*response)->errindex; py_log_msg(DEBUG, "sync PDU: %s", err_str); PyErr_SetString(EasySNMPError, err_str); break; } break; case STAT_TIMEOUT: case STAT_ERROR: snmp_sess_error(ss, err_num, err_ind, &tmp_err_str); strlcpy(err_str, tmp_err_str, STR_BUF_SIZE); py_log_msg(DEBUG, "sync PDU: %s", err_str); // SNMP v3 doesn't quite raise timeouts correctly, so we correct it if (strncmp(err_str, "Timeout", 7) == 0) { PyErr_SetString(EasySNMPTimeoutError, "timed out while connecting to remote host"); } else { PyErr_SetString(EasySNMPError, tmp_err_str); } break; default: strcat(err_str, "send_sync_pdu: unknown status"); *err_num = ss->s_snmp_errno; py_log_msg(DEBUG, "sync PDU: %s", err_str); break; } done: if (tmp_err_str) { free(tmp_err_str); } return status; } /* * Clears v3 user credentials from the local cache */ static void __remove_user_from_cache(struct session_list *ss) { struct usmUser *actUser = usm_get_userList(); while (actUser != NULL) { struct usmUser *dummy = actUser; if ( strcmp((const char *)dummy->secName, (const char *)ss->session->securityName) == 0 && strcmp((const char *)dummy->engineID, (const char *)ss->session->contextEngineID) == 0) { usm_remove_user(actUser); actUser->next = NULL; actUser->prev = NULL; usm_free_user(actUser); break; } actUser = dummy->next; } } static PyObject *py_netsnmp_construct_varbind(void) { return PyObject_CallMethod(easysnmp_import, "SNMPVariable", NULL); } /* * Fetches a PyObject's attribute and converts it to Str/Bytes. * A successful return will set `attr_bytes` to a new PyObject. * This will need to be DECREF'd by the caller when access to * the converted value is no longer needed. */ static int py_netsnmp_attr_string(PyObject *obj, char *attr_name, char **val, Py_ssize_t *len, PyObject **attr_bytes) { *val = NULL; if (obj && attr_name && PyObject_HasAttrString(obj, attr_name)) { PyObject *attr = PyObject_GetAttrString(obj, attr_name); if (attr) { int retval; #if PY_MAJOR_VERSION >= 3 // Encode the provided attribute using latin-1 into bytes and // retrieve its value and length *attr_bytes = PyUnicode_AsEncodedString(attr, "latin-1", "surrogateescape"); if (!attr_bytes) { Py_DECREF(attr); return -1; } retval = PyBytes_AsStringAndSize(*attr_bytes, val, len); #else retval = PyString_AsStringAndSize(attr, val, len); #endif Py_DECREF(attr); return retval; } } return -1; } static long long py_netsnmp_attr_long(PyObject *obj, char *attr_name) { long long val = -1; if (obj && attr_name && PyObject_HasAttrString(obj, attr_name)) { PyObject *attr = PyObject_GetAttrString(obj, attr_name); if (attr) { val = PyLong_AsLong(attr); Py_DECREF(attr); } } return val; } static void *py_netsnmp_attr_void_ptr(PyObject *obj, char *attr_name) { void *val = NULL; if (obj && attr_name && PyObject_HasAttrString(obj, attr_name)) { PyObject *attr = PyObject_GetAttrString(obj, attr_name); if (attr) { val = PyLong_AsVoidPtr(attr); Py_DECREF(attr); } } return val; } static int py_netsnmp_attr_set_string(PyObject *obj, char *attr_name, char *val, size_t len) { int ret = -1; if (obj && attr_name) { PyObject *val_obj = PyUnicode_Decode(val, len, "latin-1", "surrogateescape"); if (!val_obj) { return -1; } ret = PyObject_SetAttrString(obj, attr_name, val_obj); Py_DECREF(val_obj); } return ret; } /** * Update python session object error attributes. * * Copy the error info which may have been returned from __send_sync_pdu(...) * into the python object. This will allow the python code to determine if * an error occured during an snmp operation. * * Currently there are 3 attributes we care about * * error_number - Copy of the value of netsnmp_session.s_errno. This is the * system errno that was generated during our last call into the net-snmp * library. * * error_index - Copy of the value of netsmp_session.s_snmp_errno. These error * numbers are separate from the system errno's and describe SNMP errors. * * error_string - A string describing the error_index that was returned during * our last operation. * * @param[in] session The python object that represents our current Session * @param[in|out] err_str A string describing err_ind * @param[in|out] err_num The system errno currently stored by our session * @param[in|out] err_ind The snmp errno currently stored by our session */ static void __py_netsnmp_update_session_errors(PyObject *session, char *err_str, int err_num, int err_ind) { PyObject *tmp_for_conversion; PyObject *type, *value, *traceback; PyErr_Fetch(&type, &value, &traceback); py_netsnmp_attr_set_string(session, "error_string", err_str, STRLEN(err_str)); tmp_for_conversion = PyLong_FromLong(err_num); if (!tmp_for_conversion) { goto done; /* nothing better to do? */ } PyObject_SetAttrString(session, "error_number", tmp_for_conversion); Py_DECREF(tmp_for_conversion); tmp_for_conversion = PyLong_FromLong(err_ind); if (!tmp_for_conversion) { goto done; /* nothing better to do? */ } PyObject_SetAttrString(session, "error_index", tmp_for_conversion); Py_DECREF(tmp_for_conversion); done: PyErr_Restore(type, value, traceback); return; } /* * Returns a new reference to a python capsule object containing * a newly allocated session_capsule_ctx. * * This function will raise an exception on failure. */ static PyObject *create_session_capsule(SnmpSession *session) { void *handle = NULL; struct session_capsule_ctx *ctx = NULL; PyObject *capsule = NULL; /* create a long lived handle from throwaway session object */ if (!(handle = snmp_sess_open(session))) { PyErr_SetString(EasySNMPConnectionError, "couldn't create SNMP handle"); goto done; } /* * We have to free these here because we allocated the memory and `snmp_sess_open` * does not use our instance. In fact, it creates a carbon copy. The call is as follows: * snmp_sess_open -> _sess_open -> snmp_sess_add -> snmp_sess_add_ex -> snmp_sess_copy -> _sess_copy */ if (!(ctx = malloc(sizeof *ctx))) { PyErr_SetString(PyExc_RuntimeError, "could not malloc() session_capsule_ctx"); goto done; } /* * Create a capsule containing the ctx pointer with an "anonymous" name, * which is automatically destroyed by delete_session_capsule() when * no more references to the object are held. */ if (!(capsule = PyCapsule_New(ctx, NULL, delete_session_capsule))) { PyErr_SetString(PyExc_RuntimeError, "failed to create Python Capsule object"); goto done; } free(session->securityEngineID); free(session->contextEngineID); /* init session context variables */ ctx->handle = handle; ctx->invalid_oids = (bitarray *)ctx->invalid_oids_buf; bitarray_buf_init(ctx->invalid_oids, sizeof(ctx->invalid_oids_buf)); return (capsule); done: if (handle) { snmp_sess_close(handle); } if (ctx) { free(ctx); } free(session->securityEngineID); free(session->contextEngineID); Py_XDECREF(capsule); return NULL; } static void *get_session_handle_from_capsule(PyObject *session_capsule) { if (!session_capsule) { PyErr_SetString(PyExc_RuntimeError, "NULL arg calling get_session_handle_from_capsule"); return NULL; } /* raises exception on failure. */ return PyCapsule_GetPointer(session_capsule, NULL); } #ifdef USE_DEPRECATED_COBJECT_API /* The CObject API calls destructor with stored pointer */ static void delete_session_capsule(void *session_ptr) { struct session_capsule_ctx *ctx = session_ptr; if (ctx) { // clear_user_list(); // Too dangerous, may disrupt other valid sessions __remove_user_from_cache((struct session_list *)ctx->handle); snmp_sess_close(ctx->handle); free(ctx); } } #else /* Automatically called when Python reclaims session_capsule object. */ static void delete_session_capsule(PyObject *session_capsule) { /* PyCapsule_GetPointer will raise an exception if it fails. */ struct session_capsule_ctx *ctx = PyCapsule_GetPointer(session_capsule, NULL); if (ctx) { // clear_user_list(); // Too dangerous, may disrupt other valid sessions __remove_user_from_cache((struct session_list *)ctx->handle); snmp_sess_close(ctx->handle); free(ctx); } } #endif /* USE_DEPRECATED_COBJECT_API */ static PyObject *netsnmp_create_session(PyObject *self, PyObject *args) { int version; char *community; char *peer; int lport; int retries; int timeout; SnmpSession session = {0}; if (!PyArg_ParseTuple(args, "issiii", &version, &community, &peer, &lport, &retries, &timeout)) { goto done; } snmp_sess_init(&session); session.version = -1; #ifndef DISABLE_SNMPV1 if (version == 1) { session.version = SNMP_VERSION_1; } #endif #ifndef DISABLE_SNMPV2C if (version == 2) { session.version = SNMP_VERSION_2c; } #endif if (version == 3) { session.version = SNMP_VERSION_3; } if (session.version == -1) { PyErr_Format(PyExc_ValueError, "unsupported SNMP version (%d)", version); goto done; } session.community_len = STRLEN((char *)community); session.community = (u_char *)community; session.peername = peer; session.local_port = lport; session.retries = retries; /* 5 */ session.timeout = timeout; /* 1000000L */ session.authenticator = NULL; return create_session_capsule(&session); done: return NULL; } static PyObject *netsnmp_create_session_v3(PyObject *self, PyObject *args) { int version; char *peer; int lport; int retries; int timeout; char *sec_name; int sec_level; char *sec_eng_id; char *context_eng_id; char *context; char *auth_proto; char *auth_pass; char *priv_proto; char *priv_pass; int eng_boots; int eng_time; SnmpSession session = {0}; if (!PyArg_ParseTuple(args, "isiiisisssssssii", &version, &peer, &lport, &retries, &timeout, &sec_name, &sec_level, &sec_eng_id, &context_eng_id, &context, &auth_proto, &auth_pass, &priv_proto, &priv_pass, &eng_boots, &eng_time)) { return NULL; } snmp_sess_init(&session); if (version == 3) { session.version = SNMP_VERSION_3; } else { PyErr_Format(PyExc_ValueError, "unsupported SNMP version (%d)", version); goto done; } session.peername = peer; session.retries = retries; /* 5 */ session.timeout = timeout; /* 1000000L */ session.authenticator = NULL; session.contextNameLen = STRLEN(context); session.contextName = context; session.securityNameLen = STRLEN(sec_name); session.securityName = sec_name; session.securityLevel = sec_level; session.securityModel = USM_SEC_MODEL_NUMBER; session.securityEngineIDLen = hex_to_binary2((unsigned char *)sec_eng_id, STRLEN(sec_eng_id), (char **)&session.securityEngineID); session.contextEngineIDLen = hex_to_binary2((unsigned char *)context_eng_id, STRLEN(context_eng_id), (char **)&session.contextEngineID); session.engineBoots = eng_boots; session.engineTime = eng_time; if (__match_algo(1, auth_proto, &session.securityAuthProto, &session.securityAuthProtoLen) != 0) { PyErr_Format(PyExc_ValueError, "unsupported authentication protocol (%s)", auth_proto); goto done; } if (session.securityLevel >= SNMP_SEC_LEVEL_AUTHNOPRIV) { if (STRLEN(auth_pass) > 0) { session.securityAuthKeyLen = USM_AUTH_KU_LEN; if (generate_Ku(session.securityAuthProto, session.securityAuthProtoLen, (u_char *)auth_pass, STRLEN(auth_pass), session.securityAuthKey, &session.securityAuthKeyLen) != SNMPERR_SUCCESS) { PyErr_SetString(EasySNMPConnectionError, "error generating Ku from authentication " "password"); goto done; } } } if (__match_algo(0, priv_proto, &session.securityPrivProto, &session.securityPrivProtoLen) != 0) { PyErr_Format(PyExc_ValueError, "unsupported privacy protocol (%s)", priv_proto); goto done; } if (session.securityLevel >= SNMP_SEC_LEVEL_AUTHPRIV) { session.securityPrivKeyLen = USM_PRIV_KU_LEN; if (generate_Ku(session.securityAuthProto, session.securityAuthProtoLen, (u_char *)priv_pass, STRLEN(priv_pass), session.securityPrivKey, &session.securityPrivKeyLen) != SNMPERR_SUCCESS) { PyErr_SetString(EasySNMPConnectionError, "couldn't gen Ku from priv pass phrase"); goto done; } } return create_session_capsule(&session); done: SAFE_FREE(session.securityEngineID); SAFE_FREE(session.contextEngineID); return NULL; } static PyObject *netsnmp_create_session_tunneled(PyObject *self, PyObject *args) { int version; char *peer; int lport; int retries; int timeout; char *sec_name; int sec_level; char *context_eng_id; char *context; char *our_identity; char *their_identity; char *their_hostname; char *trust_cert; SnmpSession session = {0}; if (!PyArg_ParseTuple(args, "isiiisissssss", &version, &peer, &lport, &retries, &timeout, &sec_name, &sec_level, &context_eng_id, &context, &our_identity, &their_identity, &their_hostname, &trust_cert)) { goto done; } if (version != 3) { PyErr_SetString(PyExc_ValueError, "you must use SNMP version 3 as it's the only " "version that supports tunneling"); goto done; } snmp_sess_init(&session); session.peername = peer; session.retries = retries; /* 5 */ session.timeout = timeout; /* 1000000L */ session.contextNameLen = STRLEN(context); session.contextName = context; session.securityNameLen = STRLEN(sec_name); session.securityName = sec_name; session.securityLevel = sec_level; session.securityModel = NETSNMP_TSM_SECURITY_MODEL; /* create the transport configuration store */ if (!session.transport_configuration) { netsnmp_container_init_list(); session.transport_configuration = netsnmp_container_find("transport_configuration:fifo"); if (!session.transport_configuration) { py_log_msg(ERROR, "failed to initialize the transport " "configuration container"); goto done; } session.transport_configuration->compare = (netsnmp_container_compare *)netsnmp_transport_config_compare; } if (our_identity && our_identity[0] != '\0') CONTAINER_INSERT(session.transport_configuration, netsnmp_transport_create_config("localCert", our_identity)); if (their_identity && their_identity[0] != '\0') CONTAINER_INSERT(session.transport_configuration, netsnmp_transport_create_config("peerCert", their_identity)); if (their_hostname && their_hostname[0] != '\0') CONTAINER_INSERT(session.transport_configuration, netsnmp_transport_create_config("their_hostname", their_hostname)); if (trust_cert && trust_cert[0] != '\0') CONTAINER_INSERT(session.transport_configuration, netsnmp_transport_create_config("trust_cert", trust_cert)); return create_session_capsule(&session); done: return NULL; } static PyObject *netsnmp_get(PyObject *self, PyObject *args) { PyObject *session = NULL; PyObject *varlist = NULL; PyObject *varbind = NULL; PyObject *varlist_iter = NULL; PyObject *err_bytes = NULL; PyObject *tag_bytes = NULL; PyObject *iid_bytes = NULL; int varlist_len = 0; int varlist_ind; /* variables associated for session_ctx (can be condensed into a macro) */ PyObject *sess_ptr = NULL; struct session_capsule_ctx *session_ctx = NULL; netsnmp_session *ss = NULL; oid *oid_arr = NULL; int oid_arr_len = 0; u_char *str_buf = NULL; u_char *str_bufp = NULL; char *err_str = NULL; bitarray *invalid_oids = NULL; netsnmp_pdu *pdu = NULL; netsnmp_pdu *response = NULL; netsnmp_variable_list *vars = NULL; struct tree *tp = NULL; int len; int type; char type_str[MAX_TYPE_NAME_LEN]; int status; int buf_over = 0; char *tag = NULL; char *iid = NULL; int getlabel_flag = NO_FLAGS; int sprintval_flag = USE_BASIC; int old_format; int best_guess; int retry_nosuch; int err_ind; int err_num; char *tmpstr = NULL; Py_ssize_t tmplen; int error = 0; unsigned long snmp_version = 0; if (!args) { const char *err_msg = "netsnmp_get: missing arguments"; PyErr_SetString(PyExc_ValueError, err_msg); error = 1; goto done; } if (!PyArg_ParseTuple(args, "OO", &session, &varlist)) { goto done; } sess_ptr = PyObject_GetAttrString(session, "sess_ptr"); session_ctx = get_session_handle_from_capsule(sess_ptr); if (!session_ctx) { goto done; } ss = session_ctx->handle; invalid_oids = session_ctx->invalid_oids; oid_arr = session_ctx->oid_arr; str_buf = session_ctx->buf; str_bufp = str_buf; err_str = session_ctx->err_str; snmp_version = py_netsnmp_attr_long(session, "version"); if (py_netsnmp_attr_string(session, "error_string", &tmpstr, &tmplen, &err_bytes) < 0) { goto done; } if (py_netsnmp_attr_long(session, "use_long_names")) { getlabel_flag |= USE_LONG_NAMES; } if (py_netsnmp_attr_long(session, "use_numeric")) { getlabel_flag |= USE_NUMERIC_OIDS; } if (py_netsnmp_attr_long(session, "use_enums")) { sprintval_flag = USE_ENUMS; } if (py_netsnmp_attr_long(session, "use_sprint_value")) { sprintval_flag = USE_SPRINT_VALUE; } best_guess = py_netsnmp_attr_long(session, "best_guess"); retry_nosuch = py_netsnmp_attr_long(session, "retry_no_such"); pdu = snmp_pdu_create(SNMP_MSG_GET); if (!varlist) { const char *err_msg = "unexpected error: varlist == null"; PyErr_SetString(PyExc_RuntimeError, err_msg); error = 1; goto done; } varlist_iter = PyObject_GetIter(varlist); while (varlist_iter && (varbind = PyIter_Next(varlist_iter))) { if (py_netsnmp_attr_string(varbind, "oid", &tag, NULL, &tag_bytes) < 0 || py_netsnmp_attr_string(varbind, "oid_index", &iid, NULL, &iid_bytes) < 0) { oid_arr_len = 0; } else { tp = __tag2oid(tag, iid, oid_arr, &oid_arr_len, NULL, best_guess); } if (oid_arr_len) { snmp_add_null_var(pdu, oid_arr, oid_arr_len); varlist_len++; } else { PyErr_Format(EasySNMPUnknownObjectIDError, "unknown object id (%s)", (tag ? tag : "")); error = 1; snmp_free_pdu(pdu); Py_DECREF(varbind); Py_DECREF(varlist_iter); Py_XDECREF(tag_bytes); Py_XDECREF(iid_bytes); goto done; } /* release reference when done */ Py_DECREF(varbind); Py_XDECREF(tag_bytes); tag_bytes = NULL; Py_XDECREF(iid_bytes); iid_bytes = NULL; } Py_XDECREF(varlist_iter); if (PyErr_Occurred()) { error = 1; snmp_free_pdu(pdu); goto done; } if (snmp_version == 1) { bitarray_clear_bits(invalid_oids, (size_t)varlist_len); } status = __send_sync_pdu(ss, pdu, &response, retry_nosuch, err_str, &err_num, &err_ind, invalid_oids); __py_netsnmp_update_session_errors(session, err_str, err_num, err_ind); if (status != 0) { error = 1; goto done; } /* * Set up for numeric or full OID's, if necessary. Save the old * output format so that it can be restored when we finish -- this * is a library-wide global, and has to be set/restored for each * session. */ old_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT); if (py_netsnmp_attr_long(session, "use_long_names")) { getlabel_flag |= USE_LONG_NAMES; netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_FULL); } /* * Setting use_numeric forces use_long_names on so check for * use_numeric after use_long_names (above) to make sure the final * outcome of NETSNMP_DS_LIB_OID_OUTPUT_FORMAT is * NETSNMP_OID_OUTPUT_NUMERIC */ if (py_netsnmp_attr_long(session, "use_numeric")) { getlabel_flag |= USE_LONG_NAMES; getlabel_flag |= USE_NUMERIC_OIDS; netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_NUMERIC); } /* * In SNMPv1 we go through the response variables only if we know * the varlist_ind is not set in the invalid_oids bit array. * For bits that are set, we fix the input varbind so that it * indicates NOSUCHNAME. For bits that are not set, we fill in the * corresponding varbind and advance. * * In SNMPv2/v3 we simply fill the response variables against the * original input Varbind list. */ vars = (response ? response->variables : NULL); for (varlist_ind = 0; varlist_ind < varlist_len; varlist_ind++) { int no_such_name = 0; if (snmp_version == 1) { if (!vars) { /* if no more variables in response then remaining varbinds are invalid. */ no_such_name = 1; } else if (bitarray_test_bit(invalid_oids, varlist_ind)) { /* oid is invalid */ no_such_name = 1; } } else if (!vars) { /* * sanity check for snmp v2/v3: no more varbinds to inspect; * this should throw an exception. */ py_log_msg(DEBUG, "netsnmp_get: response had less vars compared to varlist"); break; } varbind = PySequence_GetItem(varlist, varlist_ind); if (no_such_name) { if (!PyObject_HasAttrString(varbind, "oid")) { py_log_msg(DEBUG, "netsnmp_get: bad varbind (%d)", varlist_ind); // Py_XDECREF(varbind); Double DECREF? } py_netsnmp_attr_set_string(varbind, "snmp_type", "NOSUCHNAME", strlen("NOSUCHNAME")); py_netsnmp_attr_set_string(varbind, "value", "NOSUCHNAME", strlen("NOSUCHNAME")); Py_DECREF(varbind); } else if (PyObject_HasAttrString(varbind, "oid")) { size_t str_buf_len = sizeof(session_ctx->buf); size_t out_len = 0; *str_buf = '.'; *(str_buf + 1) = '\0'; out_len = 0; tp = netsnmp_sprint_realloc_objid_tree(&str_bufp, &str_buf_len, &out_len, 0, &buf_over, vars->name, vars->name_length); py_log_msg(DEBUG, "netsnmp_get: str_bufp: %s:%lu:%lu", str_bufp, str_buf_len, out_len); /* clamp value */ str_buf[sizeof(session_ctx->buf) - 1] = '\0'; type = __translate_asn_type(vars->type); if (__is_leaf(tp)) { getlabel_flag &= ~NON_LEAF_NAME; py_log_msg(DEBUG, "netsnmp_get: is_leaf: %d", tp->type); } else { getlabel_flag |= NON_LEAF_NAME; py_log_msg(DEBUG, "netsnmp_get: !is_leaf: %d", tp->type); } py_log_msg(DEBUG, "netsnmp_get: str_buf: %s", str_buf); __get_label_iid((char *)str_buf, &tag, &iid, getlabel_flag); py_netsnmp_attr_set_string(varbind, "oid", tag, STRLEN(tag)); py_netsnmp_attr_set_string(varbind, "oid_index", iid, STRLEN(iid)); __get_type_str(type, type_str, 1); py_netsnmp_attr_set_string(varbind, "snmp_type", type_str, strlen(type_str)); len = __snprint_value((char *)str_buf, sizeof(session_ctx->buf), vars, tp, type, sprintval_flag); str_buf[len] = '\0'; py_netsnmp_attr_set_string(varbind, "value", (char *)str_buf, len); Py_DECREF(varbind); } else { py_log_msg(DEBUG, "netsnmp_get: bad varbind (%d)", varlist_ind); Py_XDECREF(varbind); } /* * in v1 this will only advance if the varbind index is valid; * in v2/v3 no_such_name is always set to 0. */ if (!no_such_name) { vars = vars->next_variable; } } /* Reset the library's behavior for numeric/symbolic OID's. */ netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, old_format); done: Py_XDECREF(sess_ptr); Py_XDECREF(err_bytes); if (response) { snmp_free_pdu(response); response = NULL; } if (error) { return NULL; } Py_RETURN_NONE; } static PyObject *netsnmp_getnext(PyObject *self, PyObject *args) { PyObject *session = NULL; PyObject *sess_ptr = NULL; PyObject *varlist; PyObject *varbind; PyObject *err_bytes = NULL; PyObject *tag_bytes = NULL; PyObject *iid_bytes = NULL; int varlist_len = 0; int varlist_ind; struct session_capsule_ctx *session_ctx = NULL; netsnmp_session *ss; netsnmp_pdu *pdu = NULL; netsnmp_pdu *response = NULL; netsnmp_variable_list *vars; struct tree *tp; int len; oid *oid_arr; int oid_arr_len = MAX_OID_LEN; int type; char type_str[MAX_TYPE_NAME_LEN]; int status; u_char str_buf[STR_BUF_SIZE]; u_char *str_bufp = str_buf; size_t str_buf_len = sizeof(str_buf); size_t out_len = 0; int buf_over = 0; char *tag; char *iid = NULL; int getlabel_flag = NO_FLAGS; int sprintval_flag = USE_BASIC; int old_format; int best_guess; int retry_nosuch; int err_ind; int err_num; char err_str[STR_BUF_SIZE]; char *tmpstr; Py_ssize_t tmplen; int error = 0; unsigned long snmp_version = 0; BITARRAY_DECLARE(snmpv1_invalid_oids, DEFAULT_NUM_BAD_OIDS); bitarray *invalid_oids = snmpv1_invalid_oids; oid_arr = calloc(MAX_OID_LEN, sizeof(oid)); if (oid_arr && args) { if (!PyArg_ParseTuple(args, "OO", &session, &varlist)) { goto done; } sess_ptr = PyObject_GetAttrString(session, "sess_ptr"); session_ctx = get_session_handle_from_capsule(sess_ptr); if (!session_ctx) { goto done; } ss = session_ctx->handle; snmp_version = py_netsnmp_attr_long(session, "version"); if (py_netsnmp_attr_string(session, "error_string", &tmpstr, &tmplen, &err_bytes) < 0) { goto done; } memcpy(&err_str, tmpstr, tmplen); err_num = py_netsnmp_attr_long(session, "error_number"); err_ind = py_netsnmp_attr_long(session, "error_index"); if (py_netsnmp_attr_long(session, "use_long_names")) { getlabel_flag |= USE_LONG_NAMES; } if (py_netsnmp_attr_long(session, "use_numeric")) { getlabel_flag |= USE_NUMERIC_OIDS; } if (py_netsnmp_attr_long(session, "use_enums")) { sprintval_flag = USE_ENUMS; } if (py_netsnmp_attr_long(session, "use_sprint_value")) { sprintval_flag = USE_SPRINT_VALUE; } best_guess = py_netsnmp_attr_long(session, "best_guess"); retry_nosuch = py_netsnmp_attr_long(session, "retry_no_such"); pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); if (varlist) { PyObject *varlist_iter = PyObject_GetIter(varlist); while (varlist_iter && (varbind = PyIter_Next(varlist_iter))) { if (py_netsnmp_attr_string(varbind, "oid", &tag, NULL, &tag_bytes) < 0 || py_netsnmp_attr_string(varbind, "oid_index", &iid, NULL, &iid_bytes) < 0) { oid_arr_len = 0; } else { tp = __tag2oid(tag, iid, oid_arr, &oid_arr_len, NULL, best_guess); } py_log_msg(DEBUG, "netsnmp_getnext: filling request: %s:%s:%d:%d", tag, iid, oid_arr_len, best_guess); if (oid_arr_len) { snmp_add_null_var(pdu, oid_arr, oid_arr_len); varlist_len++; } else { PyErr_Format(EasySNMPUnknownObjectIDError, "unknown object id (%s)", (tag ? tag : "")); error = 1; snmp_free_pdu(pdu); Py_DECREF(varbind); Py_DECREF(varlist_iter); Py_XDECREF(tag_bytes); Py_XDECREF(iid_bytes); goto done; } /* release reference when done */ Py_DECREF(varbind); Py_XDECREF(tag_bytes); tag_bytes = NULL; Py_XDECREF(iid_bytes); iid_bytes = NULL; } Py_DECREF(varlist_iter); if (PyErr_Occurred()) { error = 1; snmp_free_pdu(pdu); goto done; } } /* if we cannot represent the number of bad oids, we will need to resize. */ if (snmp_version == 1 && DEFAULT_NUM_BAD_OIDS < varlist_len) { invalid_oids = bitarray_calloc(varlist_len); if (!invalid_oids) { error = 1; snmp_free_pdu(pdu); const char *err_msg = "failed to call bitarray_calloc"; PyErr_SetString(PyExc_RuntimeError, err_msg); goto done; } } status = __send_sync_pdu(ss, pdu, &response, retry_nosuch, err_str, &err_num, &err_ind, invalid_oids); __py_netsnmp_update_session_errors(session, err_str, err_num, err_ind); if (status != 0) { error = 1; goto done; } /* ** Set up for numeric or full OID's, if necessary. Save the old ** output format so that it can be restored when we finish -- this ** is a library-wide global, and has to be set/restored for each ** session. */ old_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT); if (py_netsnmp_attr_long(session, "use_long_names")) { getlabel_flag |= USE_LONG_NAMES; netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_FULL); } /* Setting use_numeric forces use_long_names on so check for use_numeric after use_long_names (above) to make sure the final outcome of NETSNMP_DS_LIB_OID_OUTPUT_FORMAT is NETSNMP_OID_OUTPUT_NUMERIC */ if (py_netsnmp_attr_long(session, "use_numeric")) { getlabel_flag |= USE_LONG_NAMES; getlabel_flag |= USE_NUMERIC_OIDS; netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_NUMERIC); } /* * In SNMPv1 we go through the response variables only if we know * the varlist_ind is not set in the invalid_oids bit array. * For bits that are set, we fix the input varbind so that it * indicates NOSUCHNAME. For bits that are not set, we fill in the * corresponding varbind and advance. * * In SNMPv2/v3 we simply fill the response variables against the * original input Varbind list. */ vars = (response ? response->variables : NULL); for (varlist_ind = 0; varlist_ind < varlist_len; varlist_ind++) { int no_such_name = 0; if (snmp_version == 1) { /* check if oid is invalid */ if (bitarray_test_bit(invalid_oids, varlist_ind)) { no_such_name = 1; } } else if (!vars) { /* * no more varbinds to inspect * (this will only happen if no response is received from SNMP. */ break; } varbind = PySequence_GetItem(varlist, varlist_ind); if (!no_such_name && PyObject_HasAttrString(varbind, "oid")) { *str_buf = '.'; *(str_buf + 1) = '\0'; out_len = 0; tp = netsnmp_sprint_realloc_objid_tree(&str_bufp, &str_buf_len, &out_len, 0, &buf_over, vars->name, vars->name_length); str_buf[sizeof(str_buf) - 1] = '\0'; type = __translate_asn_type(vars->type); if (__is_leaf(tp)) { getlabel_flag &= ~NON_LEAF_NAME; py_log_msg(DEBUG, "netsnmp_getnext: is_leaf: %d", tp->type); } else { getlabel_flag |= NON_LEAF_NAME; py_log_msg(DEBUG, "netsnmp_getnext: !is_leaf: %d", tp->type); } py_log_msg(DEBUG, "netsnmp_getnext: str_buf: %s", str_buf); __get_label_iid((char *)str_buf, &tag, &iid, getlabel_flag); py_log_msg(DEBUG, "netsnmp_getnext: filling response: %s:%s", tag, iid); py_netsnmp_attr_set_string(varbind, "oid", tag, STRLEN(tag)); py_netsnmp_attr_set_string(varbind, "oid_index", iid, STRLEN(iid)); __get_type_str(type, type_str, 1); py_netsnmp_attr_set_string(varbind, "snmp_type", type_str, strlen(type_str)); len = __snprint_value((char *)str_buf, sizeof(str_buf), vars, tp, type, sprintval_flag); str_buf[len] = '\0'; py_netsnmp_attr_set_string(varbind, "value", (char *)str_buf, len); } else if (no_such_name) { if (!PyObject_HasAttrString(varbind, "oid")) { py_log_msg(DEBUG, "netsnmp_get: bad varbind (%d)", varlist_ind); // Py_XDECREF(varbind); /* Double decref? */ } py_netsnmp_attr_set_string(varbind, "snmp_type", "NOSUCHNAME", strlen("NOSUCHNAME")); py_netsnmp_attr_set_string(varbind, "value", "NOSUCHNAME", strlen("NOSUCHNAME")); } else { py_log_msg(DEBUG, "netsnmp_getnext: bad varbind (%d)", varlist_ind); } Py_XDECREF(varbind); /* * in v1 this will only advance if the varbind index is valid; * in v2/v3 no_such_name is always set to 0. */ if (!no_such_name) { vars = vars->next_variable; } } /* Reset the library's behavior for numeric/symbolic OID's. */ netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, old_format); } done: Py_XDECREF(sess_ptr); Py_XDECREF(err_bytes); /* the pointers will be equal if we didn't allocate additional space */ if (invalid_oids != snmpv1_invalid_oids) { bitarray_free(invalid_oids); } SAFE_FREE(oid_arr); if (response) { snmp_free_pdu(response); response = NULL; } if (error) { return NULL; } Py_RETURN_NONE; } static PyObject *netsnmp_walk(PyObject *self, PyObject *args) { PyObject *session = NULL; PyObject *sess_ptr = NULL; PyObject *varlist = NULL; PyObject *varlist_iter; PyObject *varbind; PyObject *varbinds = NULL; PyObject *err_bytes = NULL; PyObject *tag_bytes = NULL; PyObject *iid_bytes = NULL; int varlist_len = 0; int varlist_ind; struct session_capsule_ctx *session_ctx = NULL; netsnmp_session *ss; netsnmp_pdu *pdu = NULL; netsnmp_pdu *response = NULL; /* ** Variable `oldvars` does not appear to fufill any immediate purpose and ** causes segfaults in some cases, especially when running against OID '.1' ** For legacy reasons, however, we will only leave it commented out, should ** we find that it was only partially implemented or actually served a purpose ** somewhere in the Net-SNMP library */ netsnmp_variable_list *vars; //, *oldvars; struct tree *tp; int len; oid **oid_arr = NULL; int *oid_arr_len = NULL; oid **oid_arr_broken_check = NULL; int *oid_arr_broken_check_len = NULL; int type; char type_str[MAX_TYPE_NAME_LEN]; int status; u_char str_buf[STR_BUF_SIZE]; u_char *str_bufp = str_buf; size_t str_buf_len = sizeof(str_buf); size_t out_len = 0; int buf_over = 0; char *tag; char *iid = NULL; int getlabel_flag = NO_FLAGS; int sprintval_flag = USE_BASIC; int old_format; int best_guess; int retry_nosuch; int err_ind; int err_num; char err_str[STR_BUF_SIZE]; int notdone = 1; char *tmpstr; Py_ssize_t tmplen; int error = 0; bitarray *invalid_oids = NULL; if (args) { if (!PyArg_ParseTuple(args, "OO", &session, &varlist)) { goto done; } if (!varlist) { goto done; } if ((varbinds = PyObject_GetAttrString(varlist, "varbinds")) == NULL) { goto done; } sess_ptr = PyObject_GetAttrString(session, "sess_ptr"); session_ctx = get_session_handle_from_capsule(sess_ptr); if (!session_ctx) { goto done; } ss = session_ctx->handle; invalid_oids = session_ctx->invalid_oids; if (py_netsnmp_attr_string(session, "error_string", &tmpstr, &tmplen, &err_bytes) < 0) { goto done; } memcpy(&err_str, tmpstr, tmplen); err_num = py_netsnmp_attr_long(session, "error_number"); err_ind = py_netsnmp_attr_long(session, "error_index"); if (py_netsnmp_attr_long(session, "use_long_names")) { getlabel_flag |= USE_LONG_NAMES; } if (py_netsnmp_attr_long(session, "use_numeric")) { getlabel_flag |= USE_NUMERIC_OIDS; } if (py_netsnmp_attr_long(session, "use_enums")) { sprintval_flag = USE_ENUMS; } if (py_netsnmp_attr_long(session, "use_sprint_value")) { sprintval_flag = USE_SPRINT_VALUE; } best_guess = py_netsnmp_attr_long(session, "best_guess"); retry_nosuch = py_netsnmp_attr_long(session, "retry_no_such"); pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); /* we need an initial count for memory allocation */ varlist_iter = PyObject_GetIter(varlist); varlist_len = 0; while (varlist_iter && (varbind = PyIter_Next(varlist_iter))) { varlist_len++; Py_DECREF(varbind); } Py_XDECREF(varlist_iter); oid_arr_len = calloc(varlist_len, sizeof(int)); oid_arr_broken_check_len = calloc(varlist_len, sizeof(int)); oid_arr = calloc(varlist_len, sizeof(oid *)); oid_arr_broken_check = calloc(varlist_len, sizeof(oid *)); for (varlist_ind = 0; varlist_ind < varlist_len; varlist_ind++) { oid_arr[varlist_ind] = calloc(MAX_OID_LEN, sizeof(oid)); oid_arr_broken_check[varlist_ind] = calloc(MAX_OID_LEN, sizeof(oid)); oid_arr_len[varlist_ind] = MAX_OID_LEN; oid_arr_broken_check_len[varlist_ind] = MAX_OID_LEN; } /* get the initial starting oids */ varlist_iter = PyObject_GetIter(varlist); varlist_ind = 0; while (varlist_iter && (varbind = PyIter_Next(varlist_iter))) { if (py_netsnmp_attr_string(varbind, "oid", &tag, NULL, &tag_bytes) < 0 || py_netsnmp_attr_string(varbind, "oid_index", &iid, NULL, &iid_bytes) < 0) { oid_arr_len[varlist_ind] = 0; } else { tp = __tag2oid(tag, iid, oid_arr[varlist_ind], &oid_arr_len[varlist_ind], NULL, best_guess); } if (oid_arr_len[varlist_ind]) { py_log_msg(DEBUG, "netsnmp_walk: filling request: %s:%s:%d:%d", tag, iid, oid_arr_len[varlist_ind], best_guess); snmp_add_null_var(pdu, oid_arr[varlist_ind], oid_arr_len[varlist_ind]); } else { PyErr_Format(EasySNMPUnknownObjectIDError, "unknown object id (%s)", (tag ? tag : "")); error = 1; snmp_free_pdu(pdu); pdu = NULL; Py_DECREF(varlist_iter); Py_DECREF(varbind); Py_XDECREF(tag_bytes); Py_XDECREF(iid_bytes); goto done; } /* release reference when done */ Py_DECREF(varbind); Py_XDECREF(tag_bytes); tag_bytes = NULL; Py_XDECREF(iid_bytes); iid_bytes = NULL; varlist_ind++; } Py_XDECREF(varlist_iter); if (PyErr_Occurred()) { error = 1; snmp_free_pdu(pdu); pdu = NULL; goto done; } /* ** Set up for numeric or full OID's, if necessary. Save the old ** output format so that it can be restored when we finish -- this ** is a library-wide global, and has to be set/restored for each ** session. */ old_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT); if (py_netsnmp_attr_long(session, "use_long_names")) { getlabel_flag |= USE_LONG_NAMES; netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_FULL); } /* ** Setting use_numeric forces use_long_names on so check for ** use_numeric after use_long_names (above) to make sure the final ** outcome of NETSNMP_DS_LIB_OID_OUTPUT_FORMAT is ** NETSNMP_OID_OUTPUT_NUMERIC */ if (py_netsnmp_attr_long(session, "use_numeric")) { getlabel_flag |= USE_LONG_NAMES; getlabel_flag |= USE_NUMERIC_OIDS; netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_NUMERIC); } /* delete the existing varbinds that we'll replace */ PySequence_DelSlice(varbinds, 0, PySequence_Length(varbinds)); if (PyErr_Occurred()) { error = 1; snmp_free_pdu(pdu); pdu = NULL; goto done; } /* save the starting OID */ for (vars = pdu->variables, varlist_ind = 0; vars != NULL; vars = vars->next_variable, varlist_ind++) { oid_arr_broken_check_len[varlist_ind] = vars->name_length; memcpy(oid_arr_broken_check[varlist_ind], vars->name, vars->name_length * sizeof(oid)); } while (notdone) { status = __send_sync_pdu(ss, pdu, &response, retry_nosuch, err_str, &err_num, &err_ind, invalid_oids); __py_netsnmp_update_session_errors(session, err_str, err_num, err_ind); if (status != 0) { error = 1; /* * pdu is released in __send_sync_pdu if an error occurs during * the snmp_sess_synch_response function. It does not, however, * appear to be set to NULL afterwards. */ pdu = NULL; if (response) { snmp_free_pdu(response); response = NULL; } goto done; } if (!response || !response->variables || status != STAT_SUCCESS || response->errstat != SNMP_ERR_NOERROR) { notdone = 0; } else { pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); for (vars = (response ? response->variables : NULL), varlist_ind = 0; vars && (varlist_ind < varlist_len); vars = vars->next_variable, varlist_ind++) { if ((vars->name_length < oid_arr_len[varlist_ind]) || (memcmp(oid_arr[varlist_ind], vars->name, oid_arr_len[varlist_ind] * sizeof(oid)) != 0)) { notdone = 0; break; } if ((vars->type == SNMP_ENDOFMIBVIEW) || (vars->type == SNMP_NOSUCHOBJECT) || (vars->type == SNMP_NOSUCHINSTANCE)) { notdone = 0; break; } if (snmp_oid_compare(vars->name, vars->name_length, oid_arr_broken_check[varlist_ind], oid_arr_broken_check_len[varlist_ind]) <= 0) { /* The agent responded with an illegal response as the returning OID was lexograghically less then or equal to the requested OID... We need to give up here because an infinite loop will result otherwise. XXX: this really should be an option to continue like the -Cc option to the snmpwalk application. */ notdone = 0; break; } varbind = py_netsnmp_construct_varbind(); if (PyObject_HasAttrString(varbind, "oid")) { str_buf[0] = '.'; str_buf[1] = '\0'; out_len = 0; tp = netsnmp_sprint_realloc_objid_tree(&str_bufp, &str_buf_len, &out_len, 0, &buf_over, vars->name, vars->name_length); str_buf[sizeof(str_buf) - 1] = '\0'; type = __translate_asn_type(vars->type); if (__is_leaf(tp)) { getlabel_flag &= ~NON_LEAF_NAME; py_log_msg(DEBUG, "netsnmp_walk: is_leaf: %d", tp->type); } else { getlabel_flag |= NON_LEAF_NAME; py_log_msg(DEBUG, "netsnmp_walk: !is_leaf: %d", tp->type); } py_log_msg(DEBUG, "netsnmp_walk: str_buf: %s", str_buf); __get_label_iid((char *)str_buf, &tag, &iid, getlabel_flag); py_log_msg(DEBUG, "netsnmp_walk: filling response: %s:%s", tag, iid); py_netsnmp_attr_set_string(varbind, "oid", tag, STRLEN(tag)); py_netsnmp_attr_set_string(varbind, "oid_index", iid, STRLEN(iid)); __get_type_str(type, type_str, 1); py_netsnmp_attr_set_string(varbind, "snmp_type", type_str, strlen(type_str)); len = __snprint_value((char *)str_buf, sizeof(str_buf), vars, tp, type, sprintval_flag); str_buf[len] = '\0'; py_netsnmp_attr_set_string(varbind, "value", (char *)str_buf, len); /* push the varbind onto the return varbinds */ PyList_Append(varbinds, varbind); } else { py_log_msg(DEBUG, "netsnmp_walk: bad varbind (%d)", varlist_ind); } Py_XDECREF(varbind); memcpy(oid_arr_broken_check[varlist_ind], vars->name, sizeof(oid) * vars->name_length); oid_arr_broken_check_len[varlist_ind] = vars->name_length; snmp_add_null_var(pdu, vars->name, vars->name_length); } } if (response) { snmp_free_pdu(response); response = NULL; } } /* Reset the library's behavior for numeric/symbolic OIDs. */ netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, old_format); if (PyErr_Occurred()) { error = 1; } } done: Py_XDECREF(sess_ptr); Py_XDECREF(varbinds); Py_XDECREF(err_bytes); SAFE_FREE(oid_arr_len); SAFE_FREE(oid_arr_broken_check_len); for (varlist_ind = 0; varlist_ind < varlist_len; varlist_ind++) { SAFE_FREE(oid_arr[varlist_ind]); SAFE_FREE(oid_arr_broken_check[varlist_ind]); } SAFE_FREE(oid_arr); SAFE_FREE(oid_arr_broken_check); if (response) { snmp_free_pdu(response); response = NULL; } if (pdu) { snmp_free_pdu(pdu); response = NULL; } if (error) { return NULL; } Py_RETURN_NONE; } static PyObject *netsnmp_getbulk(PyObject *self, PyObject *args) { int nonrepeaters; int maxrepetitions; PyObject *session = NULL; PyObject *sess_ptr = NULL; PyObject *varlist; PyObject *varbinds = NULL; PyObject *varbind; PyObject *varbinds_iter; PyObject *err_bytes = NULL; PyObject *tag_bytes = NULL; PyObject *iid_bytes = NULL; int varbind_ind; struct session_capsule_ctx *session_ctx = NULL; netsnmp_session *ss; netsnmp_pdu *pdu = NULL; netsnmp_pdu *response = NULL; netsnmp_variable_list *vars; struct tree *tp; int len; oid *oid_arr; int oid_arr_len = MAX_OID_LEN; int type; char type_str[MAX_TYPE_NAME_LEN]; int status; u_char str_buf[STR_BUF_SIZE]; u_char *str_bufp = str_buf; size_t str_buf_len = sizeof(str_buf); size_t out_len = 0; int buf_over = 0; char *tag; char *iid; int getlabel_flag = NO_FLAGS; int sprintval_flag = USE_BASIC; int old_format; int best_guess; int retry_nosuch; int err_ind; int err_num; char err_str[STR_BUF_SIZE]; char *tmpstr; Py_ssize_t tmplen; int error = 0; oid_arr = calloc(MAX_OID_LEN, sizeof(oid)); if (oid_arr && args) { if (!PyArg_ParseTuple(args, "OiiO", &session, &nonrepeaters, &maxrepetitions, &varlist)) { goto done; } if (varlist && (varbinds = PyObject_GetAttrString(varlist, "varbinds"))) { sess_ptr = PyObject_GetAttrString(session, "sess_ptr"); session_ctx = get_session_handle_from_capsule(sess_ptr); if (!session_ctx) { goto done; } ss = session_ctx->handle; if (py_netsnmp_attr_string(session, "error_string", &tmpstr, &tmplen, &err_bytes) < 0) { goto done; } memcpy(&err_str, tmpstr, tmplen); err_num = py_netsnmp_attr_long(session, "error_number"); err_ind = py_netsnmp_attr_long(session, "error_index"); if (py_netsnmp_attr_long(session, "use_long_names")) { getlabel_flag |= USE_LONG_NAMES; } if (py_netsnmp_attr_long(session, "use_numeric")) { getlabel_flag |= USE_NUMERIC_OIDS; } if (py_netsnmp_attr_long(session, "use_enums")) { sprintval_flag = USE_ENUMS; } if (py_netsnmp_attr_long(session, "use_sprint_value")) { sprintval_flag = USE_SPRINT_VALUE; } best_guess = py_netsnmp_attr_long(session, "best_guess"); retry_nosuch = py_netsnmp_attr_long(session, "retry_no_such"); pdu = snmp_pdu_create(SNMP_MSG_GETBULK); pdu->errstat = nonrepeaters; pdu->errindex = maxrepetitions; varbinds_iter = PyObject_GetIter(varbinds); while (varbinds_iter && (varbind = PyIter_Next(varbinds_iter))) { if (py_netsnmp_attr_string(varbind, "oid", &tag, NULL, &tag_bytes) < 0 || py_netsnmp_attr_string(varbind, "oid_index", &iid, NULL, &iid_bytes) < 0) { oid_arr_len = 0; } else { tp = __tag2oid(tag, iid, oid_arr, &oid_arr_len, NULL, best_guess); } if (oid_arr_len) { snmp_add_null_var(pdu, oid_arr, oid_arr_len); } else { PyErr_Format(EasySNMPUnknownObjectIDError, "unknown object id (%s)", (tag ? tag : "")); error = 1; snmp_free_pdu(pdu); Py_DECREF(varbind); Py_DECREF(varbinds_iter); Py_XDECREF(tag_bytes); Py_XDECREF(iid_bytes); goto done; } /* release reference when done */ Py_DECREF(varbind); Py_XDECREF(tag_bytes); tag_bytes = NULL; Py_XDECREF(iid_bytes); iid_bytes = NULL; } Py_XDECREF(varbinds_iter); if (PyErr_Occurred()) { error = 1; snmp_free_pdu(pdu); pdu = NULL; goto done; } status = __send_sync_pdu(ss, pdu, &response, retry_nosuch, err_str, &err_num, &err_ind, NULL); __py_netsnmp_update_session_errors(session, err_str, err_num, err_ind); if (status != 0) { error = 1; if (response) { snmp_free_pdu(response); response = NULL; } goto done; } /* * Set up for numeric or full OID's, if necessary. Save the old * output format so that it can be restored when we finish -- this * is a library-wide global, and has to be set/restored for each * session. */ old_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT); if (py_netsnmp_attr_long(session, "use_long_names")) { getlabel_flag |= USE_LONG_NAMES; netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_FULL); } /* * Setting use_numeric forces use_long_names on so check for * use_numeric after use_long_names (above) to make sure the final * outcome of NETSNMP_DS_LIB_OID_OUTPUT_FORMAT is * NETSNMP_OID_OUTPUT_NUMERIC */ if (py_netsnmp_attr_long(session, "use_numeric")) { getlabel_flag |= USE_LONG_NAMES; getlabel_flag |= USE_NUMERIC_OIDS; netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_NUMERIC); } if (response && response->variables) { /* clear varlist to receive response varbinds*/ PySequence_DelSlice(varbinds, 0, PySequence_Length(varbinds)); if (PyErr_Occurred()) { error = 1; snmp_free_pdu(pdu); pdu = NULL; if (response) { snmp_free_pdu(response); response = NULL; } goto done; } for (vars = response->variables, varbind_ind = 0; vars; vars = vars->next_variable, varbind_ind++) { varbind = py_netsnmp_construct_varbind(); if (PyObject_HasAttrString(varbind, "oid")) { *str_buf = '.'; *(str_buf + 1) = '\0'; out_len = 0; buf_over = 0; str_bufp = str_buf; tp = netsnmp_sprint_realloc_objid_tree(&str_bufp, &str_buf_len, &out_len, 0, &buf_over, vars->name, vars->name_length); str_buf[sizeof(str_buf) - 1] = '\0'; type = __translate_asn_type(vars->type); if (__is_leaf(tp)) { getlabel_flag &= ~NON_LEAF_NAME; py_log_msg(DEBUG, "netsnmp_getbulk: is_leaf: %d", tp->type); } else { getlabel_flag |= NON_LEAF_NAME; py_log_msg(DEBUG, "netsnmp_getbulk: !is_leaf: %d", tp->type); } py_log_msg(DEBUG, "netsnmp_getbulk: str_buf: %s", str_buf); __get_label_iid((char *)str_buf, &tag, &iid, getlabel_flag); py_netsnmp_attr_set_string(varbind, "oid", tag, STRLEN(tag)); py_netsnmp_attr_set_string(varbind, "oid_index", iid, STRLEN(iid)); __get_type_str(type, type_str, 1); py_netsnmp_attr_set_string(varbind, "snmp_type", type_str, strlen(type_str)); len = __snprint_value((char *)str_buf, sizeof(str_buf), vars, tp, type, sprintval_flag); str_buf[len] = '\0'; py_netsnmp_attr_set_string(varbind, "value", (char *)str_buf, len); /* push varbind onto varbinds */ PyList_Append(varbinds, varbind); } else { PyObject *none = Py_BuildValue(""); /* new ref */ /* not sure why making vabind failed - should not happen */ PyList_Append(varbinds, none); /* increments ref */ /* Return None for this variable. */ Py_DECREF(none); } Py_XDECREF(varbind); } } /* Reset the library's behavior for numeric/symbolic OID's. */ netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, old_format); if (response) { snmp_free_pdu(response); response = NULL; } // Py_DECREF(varbinds); } if (PyErr_Occurred()) { error = 1; } } done: Py_XDECREF(varbinds); Py_XDECREF(sess_ptr); Py_XDECREF(err_bytes); SAFE_FREE(oid_arr); if (error) { return NULL; } Py_RETURN_NONE; } static PyObject *netsnmp_bulkwalk(PyObject *self, PyObject *args) { PyObject *session = NULL; PyObject *sess_ptr = NULL; PyObject *varlist = NULL; PyObject *varlist_iter = NULL; PyObject *varbind = NULL; PyObject *varbinds = NULL; PyObject *err_bytes = NULL; PyObject *tag_bytes = NULL; PyObject *iid_bytes = NULL; int varlist_len = 0; int varlist_ind; struct session_capsule_ctx *session_ctx = NULL; netsnmp_session *ss = NULL; netsnmp_pdu *pdu = NULL; netsnmp_pdu *response = NULL; netsnmp_variable_list *vars = NULL; struct tree *tp = NULL; int len; oid **oid_arr = NULL; int *oid_arr_len = NULL; // char **initial_oid_str_arr = NULL; char **oid_str_arr = NULL; char **oid_idx_str_arr = NULL; int type; char type_str[MAX_TYPE_NAME_LEN]; int status; u_char str_buf[STR_BUF_SIZE]; u_char *str_bufp = str_buf; size_t str_buf_len = sizeof(str_buf); size_t out_len = 0; int buf_over = 0; int getlabel_flag = NO_FLAGS; int sprintval_flag = USE_BASIC; int old_format; int best_guess; int retry_nosuch; int err_ind; int err_num; char err_str[STR_BUF_SIZE]; int notdone = 1; char *tmpstr = NULL; Py_ssize_t tmplen; int error = 0; int nonrepeaters; int maxrepetitions; py_log_msg(DEBUG, "netsnmp_bulkwalk: Starting"); if (args) { if (!PyArg_ParseTuple(args, "OiiO", &session, &nonrepeaters, &maxrepetitions, &varlist)) { goto done; } py_log_msg(DEBUG, "netsnmp_bulkwalk: nonreps (%d) max_reps (%d)", nonrepeaters, maxrepetitions); if (!varlist) { goto done; } if ((varbinds = PyObject_GetAttrString(varlist, "varbinds")) == NULL) { goto done; } sess_ptr = PyObject_GetAttrString(session, "sess_ptr"); session_ctx = get_session_handle_from_capsule(sess_ptr); if (!session_ctx) { goto done; } ss = session_ctx->handle; if (py_netsnmp_attr_string(session, "error_string", &tmpstr, &tmplen, &err_bytes) < 0) { goto done; } memcpy(&err_str, tmpstr, tmplen); err_num = py_netsnmp_attr_long(session, "error_number"); err_ind = py_netsnmp_attr_long(session, "error_index"); if (py_netsnmp_attr_long(session, "use_long_names")) { getlabel_flag |= USE_LONG_NAMES; } if (py_netsnmp_attr_long(session, "use_numeric")) { getlabel_flag |= USE_NUMERIC_OIDS; } if (py_netsnmp_attr_long(session, "use_enums")) { sprintval_flag = USE_ENUMS; } if (py_netsnmp_attr_long(session, "use_sprint_value")) { sprintval_flag = USE_SPRINT_VALUE; } best_guess = py_netsnmp_attr_long(session, "best_guess"); retry_nosuch = py_netsnmp_attr_long(session, "retry_no_such"); /* we need an initial count for memory allocation */ varlist_iter = PyObject_GetIter(varlist); varlist_len = 0; while (varlist_iter && (varbind = PyIter_Next(varlist_iter))) { varlist_len++; Py_DECREF(varbind); } Py_XDECREF(varlist_iter); oid_arr_len = calloc(varlist_len, sizeof(int)); oid_arr = calloc(varlist_len, sizeof(oid *)); // initial_oid_str_arr = calloc(varlist_len, sizeof(char *)); oid_str_arr = calloc(varlist_len, sizeof(char *)); oid_idx_str_arr = calloc(varlist_len, sizeof(char *)); for (varlist_ind = 0; varlist_ind < varlist_len; varlist_ind++) { oid_arr[varlist_ind] = calloc(MAX_OID_LEN, sizeof(oid)); oid_arr_len[varlist_ind] = MAX_OID_LEN; } /* get the initial oids */ py_log_msg(DEBUG, "netsnmp_bulkwalk: Getting initial oids"); varlist_ind = 0; varlist_iter = PyObject_GetIter(varlist); while (varlist_iter && (varbind = PyIter_Next(varlist_iter))) { if (py_netsnmp_attr_string(varbind, "oid", &oid_str_arr[varlist_ind], NULL, &tag_bytes) >= 0 && py_netsnmp_attr_string(varbind, "oid_index", &oid_idx_str_arr[varlist_ind], NULL, &iid_bytes) >= 0) { // initial_oid_str_arr[varlist_ind] = oid_str_arr[varlist_ind]; py_log_msg(DEBUG, "netsnmp_bulkwalk: Initial oid(%s) oid_idx(%s)", oid_str_arr[varlist_ind], oid_idx_str_arr[varlist_ind]); // Get oid array len tp = __tag2oid(oid_str_arr[varlist_ind], oid_idx_str_arr[varlist_ind], oid_arr[varlist_ind], &oid_arr_len[varlist_ind], NULL, best_guess); } else { oid_arr_len[varlist_ind] = 0; } if (!oid_arr_len[varlist_ind]) { PyErr_Format(EasySNMPUnknownObjectIDError, "unknown object id (%s)", (oid_str_arr[varlist_ind] ? oid_str_arr[varlist_ind] : "")); error = 1; Py_DECREF(varbind); Py_DECREF(varlist_iter); Py_XDECREF(tag_bytes); Py_XDECREF(iid_bytes); goto done; } Py_DECREF(varbind); Py_XDECREF(tag_bytes); tag_bytes = NULL; Py_XDECREF(iid_bytes); iid_bytes = NULL; varlist_ind++; } Py_XDECREF(varlist_iter); if (PyErr_Occurred()) { error = 1; goto done; } /* * Set up for numeric or full OID's, if necessary. Save the old * output format so that it can be restored when we finish -- this * is a library-wide global, and has to be set/restored for each * session. */ old_format = netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT); if (py_netsnmp_attr_long(session, "use_long_names")) { getlabel_flag |= USE_LONG_NAMES; netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_FULL); } /* * Setting use_numeric forces use_long_names on so check for * use_numeric after use_long_names (above) to make sure the final * outcome of NETSNMP_DS_LIB_OID_OUTPUT_FORMAT is * NETSNMP_OID_OUTPUT_NUMERIC */ if (py_netsnmp_attr_long(session, "use_numeric")) { getlabel_flag |= USE_LONG_NAMES; getlabel_flag |= USE_NUMERIC_OIDS; netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, NETSNMP_OID_OUTPUT_NUMERIC); } /* delete the existing varbinds that we'll replace */ PySequence_DelSlice(varbinds, 0, PySequence_Length(varbinds)); if (PyErr_Occurred()) { error = 1; goto done; } py_log_msg(DEBUG, "netsnmp_bulkwalk: Starting bulk walk request"); for (varlist_ind = 0; varlist_ind < varlist_len; varlist_ind++) { pdu = snmp_pdu_create(SNMP_MSG_GETBULK); pdu->non_repeaters = nonrepeaters; pdu->max_repetitions = maxrepetitions; snmp_add_null_var(pdu, oid_arr[varlist_ind], oid_arr_len[varlist_ind]); notdone = 1; while (notdone) { py_log_msg(DEBUG, "netsnmp_bulkwalk: Sending pdu req"); status = __send_sync_pdu(ss, pdu, &response, retry_nosuch, err_str, &err_num, &err_ind, NULL); __py_netsnmp_update_session_errors(session, err_str, err_num, err_ind); if (status != 0) { error = 1; if (response) { snmp_free_pdu(response); response = NULL; } goto done; } if (!response || !response->variables || status != STAT_SUCCESS || response->errstat != SNMP_ERR_NOERROR) { notdone = 0; } else { vars = (response ? response->variables : NULL); while (vars) { if ((vars->name_length < oid_arr_len[varlist_ind]) || (memcmp(oid_arr[varlist_ind], vars->name, oid_arr_len[varlist_ind] * sizeof(oid)) != 0)) { py_log_msg(DEBUG, "netsnmp_bulkwalk: encountered end condition " "(next subtree iteration out of scope)"); notdone = 0; break; } if ((vars->type == SNMP_ENDOFMIBVIEW) || (vars->type == SNMP_NOSUCHOBJECT) || (vars->type == SNMP_NOSUCHINSTANCE)) { py_log_msg(DEBUG, "netsnmp_bulkwalk: encountered end condition " "(ENDOFMIBVIEW, NOSUCHOBJECT, NO SUCH " "INSTANCE)"); notdone = 0; break; } varbind = py_netsnmp_construct_varbind(); if (PyObject_HasAttrString(varbind, "oid")) { str_buf[0] = '.'; str_buf[1] = '\0'; out_len = 0; tp = netsnmp_sprint_realloc_objid_tree(&str_bufp, &str_buf_len, &out_len, 0, &buf_over, vars->name, vars->name_length); str_buf[sizeof(str_buf) - 1] = '\0'; type = __translate_asn_type(vars->type); if (__is_leaf(tp)) { getlabel_flag &= ~NON_LEAF_NAME; py_log_msg(DEBUG, "netsnmp_bulkwalk: is_leaf: %d", tp->type); } else { getlabel_flag |= NON_LEAF_NAME; py_log_msg(DEBUG, "netsnmp_bulkwalk: !is_leaf: %d", tp->type); } py_log_msg(DEBUG, "netsnmp_bulkwalk: str_buf: %s", str_buf); /* * Part of adopted code. SNMPVariable does not have * a root_oid attribute. Maybe this could be added * in a future update; will not implement now in * case it breaks someone else's code */ // py_netsnmp_attr_set_string(varbind, "root_oid", // initial_oid_str_arr[varlist_ind], // STRLEN(initial_oid_str_arr[varlist_ind])); __get_label_iid((char *)str_buf, &oid_str_arr[varlist_ind], &oid_idx_str_arr[varlist_ind], getlabel_flag); py_log_msg(DEBUG, "netsnmp_bulkwalk: filling response: %s:%s", oid_str_arr[varlist_ind], oid_idx_str_arr[varlist_ind]); py_netsnmp_attr_set_string(varbind, "oid", oid_str_arr[varlist_ind], STRLEN(oid_str_arr[varlist_ind])); py_netsnmp_attr_set_string(varbind, "oid_index", oid_idx_str_arr[varlist_ind], STRLEN(oid_idx_str_arr[varlist_ind])); __get_type_str(type, type_str, 1); py_netsnmp_attr_set_string(varbind, "snmp_type", type_str, strlen(type_str)); len = __snprint_value((char *)str_buf, sizeof(str_buf), vars, tp, type, sprintval_flag); str_buf[len] = '\0'; py_netsnmp_attr_set_string(varbind, "value", (char *)str_buf, len); /* push the varbind onto the return varbinds */ PyList_Append(varbinds, varbind); } else { py_log_msg(DEBUG, "netsnmp_bulkwalk: bad varbind (%d)", varlist_ind); } Py_XDECREF(varbind); // Create next request if we've reached the end if (vars->next_variable == NULL) { pdu = snmp_pdu_create(SNMP_MSG_GETBULK); pdu->non_repeaters = nonrepeaters; pdu->max_repetitions = maxrepetitions; snmp_add_null_var(pdu, vars->name, vars->name_length); } // Move on to next vars = vars->next_variable; } py_log_msg(DEBUG, "netsnmp_bulkwalk: Finished reading all " "variables for req"); } if (response) { snmp_free_pdu(response); response = NULL; } } } py_log_msg(DEBUG, "netsnmp_bulkwalk: Ending bulk walk request"); /* Reset the library's behavior for numeric/symbolic OID's. */ netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OID_OUTPUT_FORMAT, old_format); if (PyErr_Occurred()) { error = 1; } } done: py_log_msg(DEBUG, "netsnmp_bulkwalk: Starting cleanup"); Py_XDECREF(varbinds); Py_XDECREF(sess_ptr); Py_XDECREF(err_bytes); // SAFE_FREE(initial_oid_str_arr); SAFE_FREE(oid_arr_len); for (varlist_ind = 0; varlist_ind < varlist_len; varlist_ind++) { SAFE_FREE(oid_arr[varlist_ind]); } SAFE_FREE(oid_arr); SAFE_FREE(oid_str_arr); SAFE_FREE(oid_idx_str_arr); py_log_msg(DEBUG, "netsnmp_bulkwalk: End cleanup"); if (error) { return NULL; } Py_RETURN_NONE; } static PyObject *netsnmp_set(PyObject *self, PyObject *args) { PyObject *session = NULL; PyObject *sess_ptr = NULL; PyObject *varlist = NULL; PyObject *varbind = NULL; PyObject *ret = NULL; PyObject *err_bytes = NULL; PyObject *tag_bytes = NULL; PyObject *iid_bytes = NULL; PyObject *type_bytes = NULL; PyObject *value_bytes = NULL; struct session_capsule_ctx *session_ctx = NULL; netsnmp_session *ss = NULL; netsnmp_pdu *pdu = NULL; netsnmp_pdu *response = NULL; struct tree *tp = NULL; char *tag = NULL; char *iid = NULL; char *val = NULL; char *type_str; int len; oid *oid_arr = calloc(MAX_OID_LEN, sizeof(oid)); int oid_arr_len = MAX_OID_LEN; int type; u_char tmp_val_str[STR_BUF_SIZE]; int use_enums; struct enum_list *ep = NULL; int best_guess; int status; int err_ind; int err_num; char err_str[STR_BUF_SIZE]; char *tmpstr = NULL; Py_ssize_t tmplen; int error = 0; if (oid_arr && args) { if (!PyArg_ParseTuple(args, "OO", &session, &varlist)) { goto done; } sess_ptr = PyObject_GetAttrString(session, "sess_ptr"); session_ctx = get_session_handle_from_capsule(sess_ptr); if (!session_ctx) { goto done; } ss = session_ctx->handle; /* PyObject_SetAttrString(); */ if (py_netsnmp_attr_string(session, "error_string", &tmpstr, &tmplen, &err_bytes) < 0) { goto done; } use_enums = py_netsnmp_attr_long(session, "use_enums"); best_guess = py_netsnmp_attr_long(session, "best_guess"); pdu = snmp_pdu_create(SNMP_MSG_SET); if (varlist) { PyObject *varlist_iter = PyObject_GetIter(varlist); while (varlist_iter && (varbind = PyIter_Next(varlist_iter))) { if (py_netsnmp_attr_string(varbind, "oid", &tag, NULL, &tag_bytes) < 0 || py_netsnmp_attr_string(varbind, "oid_index", &iid, NULL, &iid_bytes) < 0) { oid_arr_len = 0; } else { tp = __tag2oid(tag, iid, oid_arr, &oid_arr_len, &type, best_guess); } if (oid_arr_len == 0) { PyErr_Format(EasySNMPUnknownObjectIDError, "unknown object id (%s)", (tag ? tag : "")); error = 1; snmp_free_pdu(pdu); pdu = NULL; Py_DECREF(varbind); Py_DECREF(varlist_iter); Py_XDECREF(tag_bytes); Py_XDECREF(iid_bytes); goto done; } if (type == TYPE_UNKNOWN) { if (py_netsnmp_attr_string(varbind, "snmp_type", &type_str, NULL, &type_bytes) < 0) { /** * NoneType error returned if this is not included. * Not sure why original author did not raise an error * here before. Keep an eye out for edge cases! */ PyErr_SetString(EasySNMPUndeterminedTypeError, "a type could not be determine for " "the object"); error = 1; snmp_free_pdu(pdu); pdu = NULL; Py_DECREF(varbind); Py_DECREF(varlist_iter); Py_XDECREF(tag_bytes); Py_XDECREF(iid_bytes); Py_XDECREF(type_bytes); goto done; } type = __translate_appl_type(type_str); if (type == TYPE_UNKNOWN) { PyErr_SetString(EasySNMPUndeterminedTypeError, "a type could not be determine for " "the object"); error = 1; snmp_free_pdu(pdu); pdu = NULL; Py_DECREF(varbind); Py_DECREF(varlist_iter); Py_XDECREF(tag_bytes); Py_XDECREF(iid_bytes); Py_XDECREF(type_bytes); goto done; } Py_XDECREF(type_bytes); } if (py_netsnmp_attr_string(varbind, "value", &val, &tmplen, &value_bytes) < 0) { snmp_free_pdu(pdu); pdu = NULL; Py_DECREF(varbind); Py_DECREF(varlist_iter); Py_XDECREF(tag_bytes); Py_XDECREF(iid_bytes); Py_XDECREF(type_bytes); Py_XDECREF(value_bytes); goto done; } memset(tmp_val_str, 0, sizeof(tmp_val_str)); if (tmplen >= sizeof(tmp_val_str)) { tmplen = sizeof(tmp_val_str) - 1; } memcpy(tmp_val_str, val, tmplen); if (type == TYPE_INTEGER && use_enums && tp && tp->enums) { for (ep = tp->enums; ep; ep = ep->next) { if (val && !strcmp(ep->label, val)) { snprintf((char *)tmp_val_str, sizeof(tmp_val_str), "%d", ep->value); break; } } } len = (int)tmplen; status = __add_var_val_str(pdu, oid_arr, oid_arr_len, (char *)tmp_val_str, len, type); if (status == FAILURE) { py_log_msg(ERROR, "set: adding variable/value to PDU"); } /* release reference when done */ Py_DECREF(varbind); Py_XDECREF(value_bytes); Py_XDECREF(tag_bytes); tag_bytes = NULL; Py_XDECREF(iid_bytes); iid_bytes = NULL; } Py_DECREF(varlist_iter); if (PyErr_Occurred()) { error = 1; snmp_free_pdu(pdu); pdu = NULL; goto done; } } status = __send_sync_pdu(ss, pdu, &response, NO_RETRY_NOSUCH, err_str, &err_num, &err_ind, NULL); __py_netsnmp_update_session_errors(session, err_str, err_num, err_ind); if (response) { snmp_free_pdu(response); response = NULL; } if (status != 0) { error = 1; goto done; } if (status == STAT_SUCCESS) { ret = Py_BuildValue("i", 1); /* success, return True */ } else { ret = Py_BuildValue("i", 0); /* fail, return False */ } } done: Py_XDECREF(sess_ptr); Py_XDECREF(err_bytes); SAFE_FREE(oid_arr); if (error) { return NULL; } return (ret ? ret : Py_BuildValue("")); } /** * Get a logger object from the logging module. */ static PyObject *py_get_logger(char *logger_name) { PyObject *logger = NULL; PyObject *null_handler = NULL; logger = PyObject_CallMethod(logging_import, "getLogger", "s", logger_name); if (logger == NULL) { const char *err_msg = "failed to call logging.getLogger"; PyErr_SetString(PyExc_RuntimeError, err_msg); goto done; } /* * Since this is a library module, a handler needs to be configured when * logging; otherwise a warning is emitted to stderr. * * https://docs.python.org/3.4/howto/logging.html#library-config recommends: * >>> logging.getLogger('foo').addHandler(logging.NullHandler()) * * However NullHandler doesn't come with python <2.6 and <3.1, so we need * to improvise by using an identical copy in easysnmp.compat. */ null_handler = PyObject_CallMethod(easysnmp_compat_import, "NullHandler", NULL); if (null_handler == NULL) { const char *err_msg = "failed to call easysnmp.compat.NullHandler()"; PyErr_SetString(PyExc_RuntimeError, err_msg); goto done; } if (PyObject_CallMethod(logger, "addHandler", "O", null_handler) == NULL) { const char *err_msg = "failed to call logger.addHandler(NullHandler())"; PyErr_SetString(PyExc_RuntimeError, err_msg); goto done; } /* we don't need the null_handler around anymore. */ Py_DECREF(null_handler); return logger; done: Py_XDECREF(logger); Py_XDECREF(null_handler); return NULL; } static void py_log_msg(int log_level, char *printf_fmt, ...) { PyObject *log_msg = NULL, *pval; PyObject *type, *value, *traceback; va_list fmt_args; PyErr_Fetch(&type, &value, &traceback); va_start(fmt_args, printf_fmt); log_msg = PyUnicode_FromFormatV(printf_fmt, fmt_args); va_end(fmt_args); if (log_msg == NULL) { /* fail silently. */ return; } /* call function depending on loglevel */ switch (log_level) { case INFO: pval = PyObject_CallMethod(PyLogger, "info", "O", log_msg); break; case WARNING: pval = PyObject_CallMethod(PyLogger, "warn", "O", log_msg); break; case ERROR: pval = PyObject_CallMethod(PyLogger, "error", "O", log_msg); break; case DEBUG: pval = PyObject_CallMethod(PyLogger, "debug", "O", log_msg); break; case EXCEPTION: pval = PyObject_CallMethod(PyLogger, "exception", "O", log_msg); break; default: break; } PyErr_Restore(type, value, traceback); Py_DECREF(log_msg); Py_XDECREF(pval); } /* * Array of defined methods when initialising the module, * each entry must contain the following: * * (char *) ml_name: name of method * (PyCFunction) ml_meth: pointer to the C implementation * (int) ml_flags: flag bit indicating how call should be * (char *) ml_doc: points to contents of method docstring * * See: https://docs.python.org/2/c-api/structures.html for more info. * */ static PyMethodDef interface_methods[] = { {"session", netsnmp_create_session, METH_VARARGS, "create a netsnmp session."}, {"session_v3", netsnmp_create_session_v3, METH_VARARGS, "create a netsnmp session."}, {"session_tunneled", netsnmp_create_session_tunneled, METH_VARARGS, "create a tunneled netsnmp session over tls, dtls or ssh."}, {"get", netsnmp_get, METH_VARARGS, "perform an SNMP GET operation."}, {"getnext", netsnmp_getnext, METH_VARARGS, "perform an SNMP GETNEXT operation."}, {"getbulk", netsnmp_getbulk, METH_VARARGS, "perform an SNMP GETBULK operation."}, {"set", netsnmp_set, METH_VARARGS, "perform an SNMP SET operation."}, {"walk", netsnmp_walk, METH_VARARGS, "perform an SNMP WALK operation."}, {"bulkwalk", netsnmp_bulkwalk, METH_VARARGS, "perform an SNMP BULKWALK operation."}, {NULL, NULL, 0, NULL} /* Sentinel */ }; /* entry point when importing the module */ #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "interface", NULL, -1, interface_methods, NULL, NULL, NULL, __libraries_free}; PyMODINIT_FUNC PyInit_interface(void) { /* Initialise the module */ PyObject *interface_module = PyModule_Create(&moduledef); #else PyMODINIT_FUNC initinterface(void) { /* Initialise the module */ PyObject *interface_module = Py_InitModule("interface", interface_methods); #endif if (interface_module == NULL) { goto done; } /* * Perform global imports: * * import logging * import easysnmp * import easysnmp.exceptions * import easysnmp.compat * */ logging_import = PyImport_ImportModule("logging"); if (logging_import == NULL) { const char *err_msg = "failed to import 'logging'"; PyErr_SetString(PyExc_ImportError, err_msg); goto done; } easysnmp_import = PyImport_ImportModule("easysnmp"); if (easysnmp_import == NULL) { const char *err_msg = "failed to import 'easysnmp'"; PyErr_SetString(PyExc_ImportError, err_msg); goto done; } easysnmp_exceptions_import = PyImport_ImportModule("easysnmp.exceptions"); if (easysnmp_exceptions_import == NULL) { const char *err_msg = "failed to import 'easysnmp.exceptions'"; PyErr_SetString(PyExc_ImportError, err_msg); goto done; } easysnmp_compat_import = PyImport_ImportModule("easysnmp.compat"); if (easysnmp_compat_import == NULL) { const char *err_msg = "failed to import 'easysnmp.compat'"; PyErr_SetString(PyExc_ImportError, err_msg); goto done; } EasySNMPError = PyObject_GetAttrString(easysnmp_exceptions_import, "EasySNMPError"); EasySNMPConnectionError = PyObject_GetAttrString(easysnmp_exceptions_import, "EasySNMPConnectionError"); EasySNMPTimeoutError = PyObject_GetAttrString(easysnmp_exceptions_import, "EasySNMPTimeoutError"); EasySNMPNoSuchNameError = PyObject_GetAttrString(easysnmp_exceptions_import, "EasySNMPNoSuchNameError"); EasySNMPUnknownObjectIDError = PyObject_GetAttrString(easysnmp_exceptions_import, "EasySNMPUnknownObjectIDError"); EasySNMPNoSuchObjectError = PyObject_GetAttrString(easysnmp_exceptions_import, "EasySNMPNoSuchObjectError"); EasySNMPUndeterminedTypeError = PyObject_GetAttrString(easysnmp_exceptions_import, "EasySNMPUndeterminedTypeError"); /* Initialise logging (note: automatically has refcount 1) */ PyLogger = py_get_logger("easysnmp.interface"); if (PyLogger == NULL) { goto done; } /* initialise the netsnmp library */ __libraries_init(); py_log_msg(DEBUG, "initialised easysnmp.interface"); #if PY_MAJOR_VERSION >= 3 return interface_module; #else return; #endif done: Py_XDECREF(interface_module); Py_XDECREF(logging_import); Py_XDECREF(easysnmp_import); Py_XDECREF(easysnmp_exceptions_import); Py_XDECREF(easysnmp_compat_import); Py_XDECREF(EasySNMPError); Py_XDECREF(EasySNMPConnectionError); Py_XDECREF(EasySNMPTimeoutError); Py_XDECREF(EasySNMPNoSuchNameError); Py_XDECREF(EasySNMPUnknownObjectIDError); Py_XDECREF(EasySNMPNoSuchObjectError); Py_XDECREF(EasySNMPUndeterminedTypeError); Py_XDECREF(PyLogger); #if PY_MAJOR_VERSION >= 3 return NULL; #else return; #endif } easysnmp-0.2.6/easysnmp/interface.h000066400000000000000000000112401426455670300173400ustar00rootroot00000000000000/* This is the header file to the 'interface.c' file. Will include things like * function definitions, enums, etc. */ /****************************************************************************** * * Defines for the 'interface.c' file are listed below * ******************************************************************************/ #define APPNAME "python" /* * In snmpv1 when using retry_nosuch we need to track the * index of each bad OID in the responses using a bitarray; * DEFAULT_NUM_BAD_OIDS is a tradeoff to avoid allocating * heavily on the heap; if we need to track more, we can * just malloc on the heap. */ #define DEFAULT_NUM_BAD_OIDS (sizeof(bitarray) * 8 * 3) #define STRLEN(x) ((x) ? strlen((x)) : 0) #define SUCCESS (1) #define FAILURE (0) #define VARBIND_TAG_F (0) #define VARBIND_IID_F (1) #define VARBIND_VAL_F (2) #define VARBIND_TYPE_F (3) #define TYPE_UNKNOWN (0) #define MAX_TYPE_NAME_LEN (32) #define STR_BUF_SIZE ((MAX_TYPE_NAME_LEN) * (MAX_OID_LEN)) #define MAX_VALUE_SIZE (65536) #define MAX_INVALID_OIDS (MAX_VALUE_SIZE / MIN_OID_LEN) #define ENG_ID_BUF_SIZE (32) #define NO_RETRY_NOSUCH (0) #define USE_NUMERIC_OIDS (0x08) #define NON_LEAF_NAME (0x04) #define USE_LONG_NAMES (0x02) #define FAIL_ON_NULL_IID (0x01) #define NO_FLAGS (0x00) #define SAFE_FREE(x) \ do \ { \ if (x != NULL) \ { \ free(x); \ } \ } while (0) /****************************************************************************** * * Data structures used in the 'interface.c' file are listed below * ******************************************************************************/ typedef netsnmp_session SnmpSession; /* * This structure is attached to the easysnmp.Session * object as a Python Capsule (or CObject). * * This allows a one time allocation of large buffers * without resorting to (unnecessary) allocation on the * stack, but also remains thread safe; as long as only * one Session object is restricted to each thread. * * This is allocated in create_session_capsule() * and later (automatically via garbage collection) destroyed * delete_session_capsule(). */ struct session_capsule_ctx { /* * Technically this should be a (void *), but this probably * won't ever change in Net-SNMP. */ netsnmp_session *handle; /* buf is reusable and stores OID values and names */ u_char buf[MAX_VALUE_SIZE]; /* err_str is used to fetch the error message from net-snmp libs */ char err_str[STR_BUF_SIZE]; /* used by netsnmp_{get,getnext,set}. */ oid oid_arr[MAX_OID_LEN]; /* * invalid_oids is a bitarray for maintaining invalid OIDS when performing * SNMPv1 requests. * * Note: prior to use, the number of bits required should be cleared. */ unsigned char invalid_oids_buf[MAX_INVALID_OIDS / CHAR_BIT]; bitarray *invalid_oids; }; enum { INFO, WARNING, ERROR, DEBUG, EXCEPTION }; /****************************************************************************** * * Function definitions for the 'interface.c' file are listed below * ******************************************************************************/ static PyObject *create_session_capsule(SnmpSession *ss); static void *get_session_handle_from_capsule(PyObject *session_capsule); #ifdef USE_DEPRECATED_COBJECT_API static void delete_session_capsule(void *session_ptr); #else static void delete_session_capsule(PyObject *session_capsule); #endif static int __is_numeric_oid(char *oidstr); static int __is_leaf(struct tree *tp); static int __translate_appl_type(char *typestr); static int __translate_asn_type(int type); static int __snprint_value(char *buf, size_t buf_len, netsnmp_variable_list *var, struct tree *tp, int type, int flag); static int __sprint_num_objid(char *buf, oid *objid, int len); static int __scan_num_objid(char *buf, oid *objid, size_t *len); static int __get_type_str(int type, char *str, int log_error); static int __get_label_iid(char *name, char **last_label, char **iid, int flag); static struct tree *__tag2oid(char *tag, char *iid, oid *oid_arr, int *oid_arr_len, int *type, int best_guess); static int __concat_oid_str(oid *doid_arr, int *doid_arr_len, char *soid_str); static int __add_var_val_str(netsnmp_pdu *pdu, oid *name, int name_length, char *val, int len, int type); static void py_log_msg(int log_level, char *printf_fmt, ...); static int __match_algo(int is_auth, char *algo, oid **output, size_t *len); static void __remove_user_from_cache(struct session_list *ss); easysnmp-0.2.6/easysnmp/session.py000066400000000000000000000544131426455670300172750ustar00rootroot00000000000000from __future__ import unicode_literals, absolute_import import os import re from warnings import warn # Don't attempt to import the C interface if building docs on RTD if not os.environ.get("READTHEDOCS", False): # noqa from . import interface from .exceptions import ( EasySNMPError, EasySNMPNoSuchObjectError, EasySNMPNoSuchInstanceError, ) from .variables import SNMPVariable, SNMPVariableList # Mapping between security level strings and their associated integer values. # Here we provide camelCase naming as per the original spec but also more # Pythonic variations for those who wish to use them. SECURITY_LEVEL_MAPPING = { "noAuthNoPriv": 1, "authNoPriv": 2, "authPriv": 3, "no_auth_or_privacy": 1, "auth_without_privacy": 2, "auth_with_privacy": 3, } def build_varlist(oids): """ Prepare the variable binding list which will be used by the C interface. :param oids: an individual or list of strings or tuples representing one or more OIDs :return: a tuple containing where the first item is a list of SNMPVariable objects or an individual SNMPVariable and a boolean indicating whether or not the first tuple item is a list or single item """ if isinstance(oids, list): is_list = True else: is_list = False oids = [oids] varlist = SNMPVariableList() for oid in oids: # OIDs specified as a tuple (e.g. ('sysContact', 0)) if isinstance(oid, tuple): oid, oid_index = oid varlist.append(SNMPVariable(oid, oid_index)) # OID . is specified (which we convert to iso) elif oid == ".": varlist.append(SNMPVariable("iso")) # OIDs specified as a string (e.g. 'sysContact.0') else: varlist.append(SNMPVariable(oid)) return varlist, is_list def validate_results(varlist): """ Validates a list of SNMPVariable objects and raises any appropriate exceptions where necessary. :param varlist: a variable list containing SNMPVariable objects to be processed """ for variable in varlist: # Create a printable variable string for the error varstr = variable.oid if variable.oid_index: varstr += " with index {0}".format(variable.oid_index) if variable.snmp_type == "NOSUCHOBJECT": raise EasySNMPNoSuchObjectError( "no such object {0} could be found".format(varstr) ) if variable.snmp_type == "NOSUCHINSTANCE": raise EasySNMPNoSuchInstanceError( "no such instance {0} could be found".format(varstr) ) class Session(object): """ A Net-SNMP session which may be setup once and then used to query and manipulate SNMP data. .. note:: This class transparently uses ``interface`` to create a session instance from the Net-SNMP library. Most variable values are not synchronized between the ``Session`` and ``interface``. If you intend to make changes to the ``Session`` instead of creating a new one, you must manually call the :py:meth:`.update_session` method :param hostname: hostname or IP address of SNMP agent :param version: the SNMP version to use; 1, 2 (equivalent to 2c) or 3 :param community: SNMP community string (used for both R/W) (v1 & v2) :param timeout: seconds before retry :param retries: retries before failure :param remote_port: allow remote UDP port to be overridden (this will communicate on port 161 at its default setting) :param local_port: allow overriding of the local SNMP port :param security_level: security level (no_auth_or_privacy, auth_without_privacy or auth_with_privacy) (v3) :param security_username: security name (v3) :param privacy_protocol: privacy protocol (v3) :param privacy_password: privacy passphrase (v3) :param auth_protocol: authentication protocol (MD5 or SHA) (v3) :param auth_password: authentication passphrase (v3) :param context_engine_id: context engine ID, will be probed if not supplied (v3) :param security_engine_id: security engine ID, will be probed if not supplied (v3) :param context: context name (v3) :param engine_boots: the number of times the SNMP engine has re-booted/re-initialized since SNMP engine ID was last configured (v3) :param engine_time: the number of seconds since the engine_boots counter was last incremented (v3) :param our_identity: the fingerprint or file name for the local X.509 certificate to use for our identity (run net-snmp-cert to create and manage certificates) (v3 TLS / DTLS) :param their_identity: the fingerprint or file name for the local X.509 certificate to use for their identity (v3 TLS / DTLS) :param their_hostname: their hostname to expect; either their_hostname or a trusted certificate plus a hostname is needed to validate the server is the proper server (v3 TLS / DTLS) :param trust_cert: a trusted certificate to use for validating certificates; typically this would be a CA certificate (v3 TLS / DTLS) :param use_long_names: set to True to have for getnext methods generated preferring longer Mib name convention (e.g., system.sysDescr vs just sysDescr) :param use_numeric: set to True to have returned by the get methods untranslated (i.e. dotted-decimal). Setting the use_long_names value for the session is highly recommended :param use_sprint_value: set to True to have return values for get and getnext methods formatted with the libraries sprint_value function. This will result in certain data types being returned in non-canonical format Note: values returned with this option set may not be appropriate for set operations :param use_enums: set to True to have integer return values converted to enumeration identifiers if possible, these values will also be acceptable when supplied to set operations :param best_guess: this setting controls how oids are parsed; setting to 0 causes a regular lookup. setting to 1 causes a regular expression match (defined as -Ib in snmpcmd); setting to 2 causes a random access lookup (defined as -IR in snmpcmd). :param retry_no_such: if enabled NOSUCH errors in get pdus will be repaired, removing the SNMP variable in error, and resent; undef will be returned for all NOSUCH SNMP variables, when set to False this feature is disabled and the entire get request will fail on any NOSUCH error (applies to v1 only) :param abort_on_nonexistent: raise an exception if no object or no instance is found for the given oid and oid index """ def __init__( self, hostname="localhost", version=3, community="public", timeout=1, retries=3, remote_port=0, local_port=0, security_level="no_auth_or_privacy", security_username="initial", privacy_protocol="DEFAULT", privacy_password="", auth_protocol="DEFAULT", auth_password="", context_engine_id="", security_engine_id="", context="", engine_boots=0, engine_time=0, our_identity="", their_identity="", their_hostname="", trust_cert="", use_long_names=False, use_numeric=False, use_sprint_value=False, use_enums=False, best_guess=0, retry_no_such=False, abort_on_nonexistent=False, ): # Validate and extract the remote port if ":" in hostname: if remote_port: raise ValueError( "a remote port was specified yet the hostname appears " "to have a port defined too" ) else: hostname, remote_port = hostname.split(":") remote_port = int(remote_port) self.hostname = hostname self.version = version self.community = community self.timeout = timeout self.retries = retries self.local_port = local_port self.remote_port = remote_port self.security_level = security_level self.security_username = security_username self.privacy_protocol = privacy_protocol self.privacy_password = privacy_password self.auth_protocol = auth_protocol self.auth_password = auth_password self.context_engine_id = context_engine_id self.security_engine_id = security_engine_id self.context = context self.engine_boots = engine_boots self.engine_time = engine_time self.our_identity = our_identity self.their_identity = their_identity self.their_hostname = their_hostname self.trust_cert = trust_cert self.use_long_names = use_long_names self.use_numeric = use_numeric self.use_sprint_value = use_sprint_value self.use_enums = use_enums self.best_guess = best_guess self.retry_no_such = retry_no_such self.abort_on_nonexistent = abort_on_nonexistent # The following variables are required for internal use as they are # passed to the C interface #: internal field used to cache a created session structure self.sess_ptr = None #: read-only, holds the error message assoc. w/ last request self.error_string = "" #: read-only, holds the snmp_err or status of last request self.error_number = 0 #: read-only, holds the snmp_err_index when appropriate self.error_index = 0 # Check for transports that may be tunneled self.tunneled = re.match("^(tls|dtls|ssh)", self.hostname) # Create interface instance self.update_session() @property def connect_hostname(self): if self.remote_port: return "{0}:{1}".format(self.hostname, self.remote_port) else: return self.hostname @property def timeout_microseconds(self): # Calculate our timeout in microseconds return int(self.timeout * 1000000) def get(self, oids): """ Perform an SNMP GET operation using the prepared session to retrieve a particular piece of information. :param oids: you may pass in a list of OIDs or single item; each item may be a string representing the entire OID (e.g. 'sysDescr.0') or may be a tuple containing the name as its first item and index as its second (e.g. ('sysDescr', 0)) :return: an SNMPVariable object containing the value that was retrieved or a list of objects when you send in a list of OIDs """ # Build our variable bindings for the C interface varlist, is_list = build_varlist(oids) # Perform the SNMP GET operation interface.get(self, varlist) # Validate the variable list returned if self.abort_on_nonexistent: validate_results(varlist) # Return a list or single item depending on what was passed in return list(varlist) if is_list else varlist[0] def set(self, oid, value, snmp_type=None): """ Perform an SNMP SET operation using the prepared session. :param oid: the OID that you wish to set which may be a string representing the entire OID (e.g. 'sysDescr.0') or may be a tuple containing the name as its first item and index as its second (e.g. ('sysDescr', 0)) :param value: the value to set the OID to :param snmp_type: if a numeric OID is used and the object is not in the parsed MIB, a type must be explicitly supplied :return: a boolean indicating the success of the operation """ varlist = SNMPVariableList() # OIDs specified as a tuple (e.g. ('sysContact', 0)) if isinstance(oid, tuple): oid, oid_index = oid varlist.append(SNMPVariable(oid, oid_index, value, snmp_type)) # OIDs specefied as a string (e.g. 'sysContact.0') else: varlist.append(SNMPVariable(oid, value=value, snmp_type=snmp_type)) # Perform the set operation and return whether or not it worked success = interface.set(self, varlist) return bool(success) def set_multiple(self, oid_values): """ Perform an SNMP SET operation on multiple OIDs with multiple values using the prepared session. :param oid_values: a list of tuples whereby each tuple contains a (oid, value) or an (oid, value, snmp_type) :return: a list of SNMPVariable objects containing the values that were retrieved via SNMP """ varlist = SNMPVariableList() for oid_value in oid_values: if len(oid_value) == 2: oid, value = oid_value snmp_type = None # TODO: Determine the best way to test this else: oid, value, snmp_type = oid_value # OIDs specified as a tuple (e.g. ('sysContact', 0)) if isinstance(oid, tuple): oid, oid_index = oid varlist.append(SNMPVariable(oid, oid_index, value, snmp_type)) # OIDs specefied as a string (e.g. 'sysContact.0') else: varlist.append(SNMPVariable(oid, value=value, snmp_type=snmp_type)) # Perform the set operation and return whether or not it worked success = interface.set(self, varlist) return bool(success) def get_next(self, oids): """ Uses an SNMP GETNEXT operation using the prepared session to retrieve the next variable after the chosen item. :param oids: you may pass in a list of OIDs or single item; each item may be a string representing the entire OID (e.g. 'sysDescr.0') or may be a tuple containing the name as its first item and index as its second (e.g. ('sysDescr', 0)) :return: an SNMPVariable object containing the value that was retrieved or a list of objects when you send in a list of OIDs """ # Build our variable bindings for the C interface varlist, is_list = build_varlist(oids) # Perform the SNMP GET operation interface.getnext(self, varlist) # Validate the variable list returned if self.abort_on_nonexistent: validate_results(varlist) # Return a list or single item depending on what was passed in return list(varlist) if is_list else varlist[0] def get_bulk(self, oids, non_repeaters=0, max_repetitions=10): """ Performs a bulk SNMP GET operation using the prepared session to retrieve multiple pieces of information in a single packet. :param oids: you may pass in a list of OIDs or single item; each item may be a string representing the entire OID (e.g. 'sysDescr.0') or may be a tuple containing the name as its first item and index as its second (e.g. ('sysDescr', 0)) :param non_repeaters: the number of objects that are only expected to return a single GETNEXT instance, not multiple instances :param max_repetitions: the number of objects that should be returned for all the repeating OIDs :return: a list of SNMPVariable objects containing the values that were retrieved via SNMP """ if self.version == 1: raise EasySNMPError( "you cannot perform a bulk GET operation for SNMP version 1" ) # Build our variable bindings for the C interface varlist, _ = build_varlist(oids) interface.getbulk(self, non_repeaters, max_repetitions, varlist) # Validate the variable list returned if self.abort_on_nonexistent: validate_results(varlist) # Return a list of variables return varlist def walk(self, oids=".1.3.6.1.2.1"): """ Uses SNMP GETNEXT operation using the prepared session to automatically retrieve multiple pieces of information in an OID. :param oids: you may pass in a single item (multiple values currently experimental) which may be a string representing the entire OID (e.g. 'sysDescr.0') or may be a tuple containing the name as its first item and index as its second (e.g. ('sysDescr', 0)) :return: a list of SNMPVariable objects containing the values that were retrieved via SNMP """ # Build our variable bindings for the C interface varlist, _ = build_varlist(oids) # Perform the SNMP walk using GETNEXT operations interface.walk(self, varlist) # Validate the variable list returned if self.abort_on_nonexistent: validate_results(varlist) # Return a list of variables return list(varlist) def bulkwalk(self, oids=".1.3.6.1.2.1", non_repeaters=0, max_repetitions=10): """ Uses SNMP GETBULK operation using the prepared session to automatically retrieve multiple pieces of information in an OID :param oids: you may pass in a single item (multiple values currently experimental) which may be a string representing the entire OID (e.g. 'sysDescr.0') or may be a tuple containing the name as its first item and index as its second (e.g. ('sysDescr', 0)) :return: a list of SNMPVariable objects containing the values that were retrieved via SNMP """ if self.version == 1: raise EasySNMPError("BULKWALK is not available for SNMP version 1") # Build our variable bindings for the C interface varlist, _ = build_varlist(oids) # Perform the SNMP walk using GETNEXT operations interface.bulkwalk(self, non_repeaters, max_repetitions, varlist) # Validate the variable list returned if self.abort_on_nonexistent: validate_results(varlist) # Return a list of variables return varlist def update_session(self, **kwargs): """ (Re)creates the underlying Net-SNMP session object. While it is recommended to create a new ``Session`` instance instead, this method has been added for your convenience in case you really need it (we've mis-typed the community string before in our interactive sessions and totally understand your pain). Keywords passed to the method will be assigned to the instance if they match existing attribute names. A warning will be emitted if something is passed that does not match anything that already exists. .. code-block:: python :caption: Example usage s = Session(version=2, community='readonly', hostname='localhost') # Whoops, wrong hostname and community string. Let's change that s.update_session(community='readwrite', hostname'remotehost') # Actually I need to use version 1 s.version = 1 s.update_session() """ for keyword, value in kwargs.items(): if keyword in self.__dict__: self.__setattr__(keyword, value) else: warn('Keyword argument "{}" is not an attribute'.format(keyword)) # Tunneled if self.tunneled: # TODO: Determine the best way to test this self.sess_ptr = interface.session_tunneled( self.version, self.connect_hostname, self.local_port, self.retries, self.timeout_microseconds, self.security_username, SECURITY_LEVEL_MAPPING[self.security_level], self.context_engine_id, self.context, self.our_identity, self.their_identity, self.their_hostname, self.trust_cert, ) # SNMP v3 elif self.version == 3: self.sess_ptr = interface.session_v3( self.version, self.connect_hostname, self.local_port, self.retries, self.timeout_microseconds, self.security_username, SECURITY_LEVEL_MAPPING[self.security_level], self.security_engine_id, self.context_engine_id, self.context, self.auth_protocol, self.auth_password, self.privacy_protocol, self.privacy_password, self.engine_boots, self.engine_time, ) # SNMP v1 & v2 else: self.sess_ptr = interface.session( self.version, self.community, self.connect_hostname, self.local_port, self.retries, self.timeout_microseconds, ) easysnmp-0.2.6/easysnmp/simple_bitarray.h000066400000000000000000000137221426455670300205750ustar00rootroot00000000000000#ifndef SIMPLE_BITARRAY_H #define SIMPLE_BITARRAY_H #include #include #include #if (__STDC_VERSION__ < 199901L) #define inline #endif /* * No weird architectures; we expect the following: * - a byte to be 8 bits (i.e. CHAR_BIT == 8) * - integral types do not have padding or reserved bits * e.g. if sizeof(unsigned int) == 4 then UINT_MAX == 4294967295 */ typedef unsigned int bitarray; /* * This is not guaranteed to be portable since an integral type size * dosn't necessarily define the range. See warning above about * "no weird architectures." */ #define BITARRAY_LIMB_BITS (sizeof(bitarray) * CHAR_BIT) /* macro to find the number of limbs to store num-bits */ #define BITARRAY_NUM_BITS_TO_LIMBS(num_bits) \ (((num_bits) + BITARRAY_LIMB_BITS - 1) / BITARRAY_LIMB_BITS) /* macro to find the number of limbs to store num-bits */ #define BITARRAY_NUM_BITS_TO_BUF_SIZE(num_bits) \ ((BITARRAY_NUM_BITS_TO_LIMBS(num_bits) + 1) * sizeof(bitarray)) /* fetch the limb containing bit position n */ #define BITARRAY_LIMB(bitarray, nbit) \ ((bitarray)[1 + ((nbit) / BITARRAY_LIMB_BITS)]) /* build mask to select bit in limb */ #define BITARRAY_LIMBBIT(nbit) \ ((1UL << ((nbit) % BITARRAY_LIMB_BITS))) /* * To declare a bitarray with automatic storage: * { * BITARRAY_DECLARE(x, 1024); // 'x' will now be set to bitarray * } */ #define BITARRAY_DECLARE(name, nbits) \ bitarray (name)[1 + BITARRAY_NUM_BITS_TO_LIMBS((nbits))] = { nbits } static inline size_t bitarray_num_limbs(bitarray *ba) { return BITARRAY_NUM_BITS_TO_LIMBS(ba[0]); } static inline size_t bitarray_num_bits(bitarray *ba) { return ba[0]; } static inline void bitarray_set_bit(bitarray *ba, size_t n) { BITARRAY_LIMB(ba, n) |= BITARRAY_LIMBBIT(n); } static inline void bitarray_clear_bit(bitarray *ba, size_t n) { BITARRAY_LIMB(ba, n) &= ~BITARRAY_LIMBBIT(n); } static inline void bitarray_change_bit(bitarray *ba, size_t n) { BITARRAY_LIMB(ba, n) ^= BITARRAY_LIMBBIT(n); } static inline int bitarray_test_bit(const bitarray *ba, size_t n) { return !!(BITARRAY_LIMB(ba, n) & BITARRAY_LIMBBIT(n)); } static inline void bitarray_zero(bitarray *ba) { size_t nbytes = sizeof(bitarray) * bitarray_num_limbs(ba); memset(&ba[1], 0, nbytes); } /* clear bits at position 0 to (nbits-1) */ static inline void bitarray_clear_bits(bitarray *ba, size_t nbits) { if (ba[0] >= nbits) { /* clear the entire bitarray */ bitarray_zero(ba); } else { size_t nbytes; /* * cases: * - (1) nbits align on byte boundary * - (2) nbits does not align on byte boundary, * manually clear remaining bits */ if (nbits % CHAR_BIT == 0) { nbytes = nbits * CHAR_BIT; } else { size_t remaining_bits = nbits % CHAR_BIT; size_t i; /* clear bits in the partial byte first */ for (i = nbits; i > (nbits - remaining_bits); i--) { bitarray_clear_bit(ba, i - 1); } } memset(&ba[1], 0, nbytes); } } /* * Allocation functions */ static inline bitarray *bitarray_alloc(size_t nbits) { bitarray *ba = NULL; size_t nlimbs = 1 + BITARRAY_NUM_BITS_TO_LIMBS(nbits); ba = malloc(sizeof(bitarray) * nlimbs); if (ba) { ba[0] = nbits; } return ba; } static inline bitarray *bitarray_calloc(size_t nbits) { bitarray *ba = NULL; size_t nlimbs = 1 + BITARRAY_NUM_BITS_TO_LIMBS(nbits); ba = calloc(sizeof(bitarray), nlimbs); if (ba) { ba[0] = nbits; } return ba; } static inline void bitarray_free(bitarray *ba) { if (ba) { free(ba); } } /* * Take in a raw buffer and corresponding size and return a pointer to * a newly initialised bitarray struct. * * buf_size is required to be minimum 2*sizeof(bitarray) in size. * * e.g. * unsigned char p[1024]; // bitarray will be slightly <8192 bits. * bitarray *ba = bitarray_buf_init(p, sizeof(p)); */ static inline bitarray *bitarray_buf_init(void *buf, size_t buf_size) { bitarray *ba = buf; size_t nlimbs; size_t nbits; if (!ba) { return NULL; } /* the buf needs to have the size of at least 1 limb */ if (buf_size < sizeof(bitarray)) { return NULL; } /* * using the free available space (after allocating the initial limb) * to determine how many limbs are available. */ nlimbs = (buf_size - sizeof(bitarray)) / sizeof(bitarray); if (nlimbs < 1) { nbits = 0; } else { nbits = nlimbs * sizeof(bitarray) * CHAR_BIT; } /* reserve a limb for storing number of bits */ ba[0] = nbits; bitarray_zero(ba); return (ba); } static inline void bitarray_print_base16(bitarray *ba) { bitarray i; size_t num_limbs = bitarray_num_limbs(ba); printf("DEBUG numbits=%lu\n", (unsigned long) ba[0]); printf("DEBUG sizeof(limb)=%lu\n", sizeof(ba[0])); printf("DEBUG num_limbs=%lu\n", num_limbs); for (i = 0; i <= num_limbs; i++) { unsigned char c; unsigned int j; size_t limb_size = sizeof(ba); for (j = 0; j < limb_size; j++) { /* mask the byte we want to print in hex */ unsigned long mask = (0xFFUL) << (j * CHAR_BIT); c = (unsigned char) ((ba[i] & mask) >> (j * CHAR_BIT)); printf("%02x", c); } printf(" "); } printf("\n"); } /* ignore unused function warnings */ static void wno_unused_function_simple_bitarray_h(void) { (void) bitarray_num_bits(NULL); (void) bitarray_change_bit(NULL, 0); (void) bitarray_clear_bit(NULL, 0); (void) bitarray_clear_bits(NULL, 0); (void) bitarray_alloc(0); (void) bitarray_calloc(0); (void) bitarray_free(NULL); return; } #endifeasysnmp-0.2.6/easysnmp/utils.py000066400000000000000000000023431426455670300167450ustar00rootroot00000000000000from __future__ import unicode_literals, absolute_import import string from .compat import ub, text_type def strip_non_printable(value): """ Removes any non-printable characters and adds an indicator to the string when binary characters are fonud. :param value: the value that you wish to strip """ if value is None: return None # Filter all non-printable characters # (note that we must use join to account for the fact that Python 3 # returns a generator) printable_value = "".join(filter(lambda c: c in string.printable, value)) if printable_value != value: if printable_value: printable_value += " " printable_value += "(contains binary)" return printable_value def tostr(value): """ Converts any variable to a string or returns None if the variable contained None to begin with; this function currently supports None, unicode strings, byte strings and numbers. :param value: the value you wish to convert to a string """ if value is None: return None elif isinstance(value, text_type): return value elif isinstance(value, (int, float)): return str(value) else: return ub(value) easysnmp-0.2.6/easysnmp/variables.py000066400000000000000000000031541426455670300175560ustar00rootroot00000000000000from __future__ import unicode_literals, absolute_import from .compat import urepr from .helpers import normalize_oid from .utils import strip_non_printable, tostr class SNMPVariable(object): """ An SNMP variable binding which is used to represent a piece of information being retreived via SNMP. :param oid: the OID being manipulated :param oid_index: the index of the OID :param value: the OID value :param snmp_type: the snmp_type of data contained in val (please see http://www.net-snmp.org/wiki/index.php/TUT:snmpset#Data_Types for further information); in the case that an object or instance is not found, the type will be set to NOSUCHOBJECT and NOSUCHINSTANCE respectively """ def __init__(self, oid=None, oid_index=None, value=None, snmp_type=None): self.oid, self.oid_index = normalize_oid(oid, oid_index) self.value = value self.snmp_type = snmp_type def __repr__(self): printable_value = strip_non_printable(self.value) return "<{0} value={1} (oid={2}, oid_index={3}, snmp_type={4})>".format( self.__class__.__name__, urepr(printable_value), urepr(self.oid), urepr(self.oid_index), urepr(self.snmp_type), ) def __setattr__(self, name, value): self.__dict__[name] = tostr(value) class SNMPVariableList(list): """ An slight variation of a list which is used internally by the Net-SNMP C interface. """ @property def varbinds(self): return self easysnmp-0.2.6/images/000077500000000000000000000000001426455670300146375ustar00rootroot00000000000000easysnmp-0.2.6/images/easysnmp-logo.png000066400000000000000000000443161426455670300201520ustar00rootroot00000000000000PNG  IHDRF: iTXtXML:com.adobe.xmp JiCCPsRGB IEC61966-2.1(uKA?_hW(;MHQMrKwK [ (bcV"؊s. $ew,xsaae14H^BmbԴG{jkiVYxL5pt3o| Wɴp%~vT][_XTqլe w |%>mmaեwag0 LaAFe!@~YQ#>X.&[X!K>Q7$&.&-'bK'fqz s83J]n+}u@\T9\A狙E+ݣym 踇R=6/&gI9u pHYs   IDATxw$U?U6@^2"T2%eNp-EK,%H/H$)PP ts~==qf=o0HHtowOW.[rb: dXvM}Lh=F;0uxn[s S}t @Qqӳ[|#ׯ]so[;r!IE_iS{0P# <`Ȏ׮&t;0e8+d84 `aQmK߄b Scrm# dr dddΐ,nwZG2"I[ Յ1nR,DIE]o~&DHzf"C2I_5Y}I<!I)ju /ܣ"YM3ݽĭ$" ͅɪ "$GFNj|v)* A[گH&`0M%\a>0SaH)@ R"BJRIFHc!$1*fi)H&N-CO?jIh25A r0! ~"D Ŷ\\> ]]i$?\ӔM( $cFGRчaH H iHm;RbH!ċ}i QD> 1a(@1Bl<Bɴ$dsyL@8 ()44ɇҔ0*$iJ"$ clw[DzdM|^q0#I$vgg>=aP5bH2"$o Ʋ4-712[xcfp9 m$B2Clw"5KH-YAiZNG2l}!܅,ݫJh0, !5-NpeiD"$3ص}q3H({.pe5 !e{pLsjm|!2z-]'4dw`ٮGnJǀ+[Os?=BZяƒ²ݥ>UI^.s.gRa4uUFG)Z{9nO"$eĢt[ђDVҍ|j-4;HdIlwp( ԇݚpgQ;ԲTqT"?]JadZfٮ{Nİ H"$-Ʋ.@H(&?%B9xmS- xvN~aHO"H]YI aZcM ޘ'pJXn=Z^sBI XUXԔei ?Q*=gA}[< +sxp9{)Gʣb9^@7RAb4ua}K2NK INIAh[ @\C-_f,=:gV־ǝX)\ , R1'Fe Jr2Z}}ϩ-l9!$BRpx_h܈Zzm/eSUt(AiPɟ/X{NV_cQ9x%*='I"!xe?P±˩lx8 6P.^jSjFE@O.%sI$&:P} lBt^Ez7y`QԨq{d]x9klTP< r}qDH?NepC{T[8x}kYm:Ld{&{τI 9T?/ޖfXq"L|86*[x+JTf :ZJ$BR[cT5kmA/|]ooײ>([D_CT]J(O"$sjd_. JہssN?հl*~9W?`PJ< |l|f) U_|ۛޱIJsV8|9lee/DٿNr}ˉTg ^9{ο[ֱ)@S4 cWϼ9Abioj[Fx'׮z] z T؂kߙ S >)\|9iV*A|=ntY~b}i2,i")iLW,;Ǟ՗]cY3 D-3rSiY/Z9'z;7,eGL)^=4<1g_QۺRi3YlLL.O&#0gu_{qwj[{LȌ\J}?@Ǯ:]cҌTgdr92#,#<#,ll>O&#r*ַ&v v? ۽B-[H,f= PQWl&7h`0\L&G.$#12e$&%σ2n1d|+2wdF}9oF,~6*8h|mxD>9<|@6W[Np&Kc*Ue24*c^fĈd<@cF`e_v4|?2߭_8 uR叡Wftsdf,:OIlka:m=9y%Nb4|j#xsc -]W~7_""f[f@ۇJ9ye0<Ͱ*o²CP.xt o}$<Z)BpeӺqHDY!STi" Xk]Ϫd+0{/kwBYfM\t Ηu_K\S2YHp=hd7ފo@#%"[FTفJ"0*5 ;2<0ȏ?I|Ϲg4lR&)KSJΛ]=A9D`I)a~X0ɲQ1vR{$&A_ax6IY=vXĢYb-BԦ":IP ]W64yf'1ysNJdGhAem+U뀲{Τ wq[/M&/RB6l֑7܏SvOF(8js[۫!8x)@J?D; HH)0ok],jyw&g꤯^`y;,[R2 "!R2Mcfm'_6wl(Tr%RN5`ipтy'tcRJ(BK&@I$Qa݄H)"RI)n]t=9oNTT)!>}+ǖ/Y;{AQLźzya`k3$EW5X^px9wbA]j&,~ *W‡8PC2"͜e܀R$|삹s=wh` (H SMn!__ mAdʩ=?{ηZإR=aHD C("a!0DHI>| AEj#%Baٽa\ '"Pdɐɪᑌ ̨PBH&KENb{?CPeSz:mAN]A@jBk[!|0^=ϩ)Imf/( gbJX d.VkX?P`F8BߟR*A(*-KaR: GۇјcAbp;lbv!%|1(wwmzA.sQcGH+qP0"*HԔE詌6Y¾T'H$rBϲ3-۝S!&UWKe+='# M>g-Rcl34 c(ZjxR¸a0ɒNwduoCX}dk8!r>R*ӻ>#(oL# 0HS 1+4` Ђb@1JViIWOMJ'"zv BFA_,!&*}sNDxzue ݕ޸n;{̮.1%{˗,?pl_GDy_@%q`h[yQH)c+ DtϼC z& f߳8-AÃO#L㧙Hd=_|QK;9MyuyFG^=*Y~|^=A@#m!^=kV:ͽmn}Nm,=% cMk?A)DT_y JXy5[_u'~YH0hH!Q#]⵱v Q~ hNae7PHtECHtҨWrC z?s*e;5iFB" "!!12$0Ri 3]3׮I 4vJ!Zu1S-Y8誆6KZ(쏄gkFbb#vG='+GNR-??|U5)%i ! )GrF))DQDgAk)q֯]O;BҸ{!tY|PҮ(gI Ya(5;0ZrR鲃+ӒTl r\{Ω_}ꉆi5RE^Q},ӟIjKhc&lwld{li [(cCTIuLIÔ2M0%bPp4LHMěeV%}z/[-۽KV[oͮga>I:h**y۵q,TYU1DOv>_#u ; E(B˸ksc$%RPy*fv_dɽot$ C(ɏ;cˢ(~hS75i.L)d)R}k:CJ )0k)JJ辨x63L+iDB5軆̙(d>dE2 R`d1g1Ӄfkx{_fTF׮ɍG-QE'b+}͘(x\W!OnLTr÷ò›YMt6>2?&*s͛=gquTӀߡ 3v_ |xq6#C[1DKذ*[s !CR(EAe;oxd=p_ )A@Olݴ, 5lw$vr2;v_Jӹ^{#-ZGae 2Dabx2e*y.0R_[00L4jW}g׭_yfJ%QQFUsRHd)Us"Fc Qټ@yNjS;/nRf{c%n쟖Xm'REDrR)S i`> EB2ah|>Px.L j駁MTJqQ '(B.iNyq";* cgCr GkmFKr xXi۳zDṃȌvplהf@u7,pV-/$mBt߀='h6]-Go$^Q i.EJjsXHcKZ Uku˩XJ!J"RJ!z~09Q}֢xe=gTOPӇ'@|ewM;Ѳt^ZތCg~ubťZQTڕeE 4n)8ilw 17Tc_aT<ղ#&*@UBвף`V}4PC%/z /}0삪 4tʏ+#0M0d_f>+^5jdвlnk49cZ3b`ş'slxj*xo PuE\KI^,={GwdUF "r| ޔA.fyY-`s#Aĸ#peG3%oQ="Ul~|?3@ w<{xڋؓo'7|=i#pSnQ?q~`eOS^t%*[Cnڲ}Zz`O9s ԒoN`GsޟX*͎ltaoQUf!#0"ԣ$SiFe݀r}(cˁ-0QѨqR!*ߟp|R7s.l&,?ub|e$xq?]ؾǝ)›g^a(CJ z^{R( H)B#SLW 궰}Y[F_ǦR*;u:K@}+tQaG[8?(TRFExJ&TƲFnp9T" H<$@BH"0Hwك_~/C9{SwH-\K7h{ZЏ-׮YhO3*}kʭWTUC Fa#|,][W?{ucNY$\r'$+A" )0[_J?=gM\f%jE"=*/AHVRu_{ۡC@V-kLX݃P6jUKL3|EP[^ =OةL'})j!l(rD?T- #A{]MGU*[4AMc>ĩ <tgCqRf%p&lr.RJ ߦOj$ꛌ9?laں.Jxl| s&L0zUͨQbx9ʉLI|,݊y;""6 x5Nlcp*h1y53U Uurn:&j*y4p N*Sll%"||4yXxseZw%#i9v)BƹplyFzQ/8s^u,:YI.l1b.d}tѾ sH]:FHZ9wZE}fKQ±+}WD~8cE97OR/.Q7΃M@ SV3'$Wu u'F|QTJ|QThǻVp>ĩ~\*&aUF6ي!NIvFJy#"WH&RJVj|9eϸŷ3p:ja |*ʹ*9RQ E7HP[M#t1S_V̇3\S%WH\P= 0jB;XU ۇD\nPŲݗ-Mja>%(]rGu?A /{`\f*![ҷ+$g߯LI -&Q&(Q޶~! s3:uS͙o&@+9r^˿7!v.KXDn=ͭL,}J+9۹ӲPU oj@{eqϯw' t.e/sz[7[{ee{ po#8BedܑOm}*hF߷l([:G?s}ϑ Iܰ(\Bgm!izG^J}J TvXʖ 塼؟d+qS CZLlt"Ɉ=gfv/@R޵KHxw6S$nNוu'9$B2E=r$  IܴYG5!4 9C*^!oj:p9.Y&$C\WK6Q$N tңˀ,P'|ϹVMmD': =as>{{Jx%֍H*fNh}d{N^ԓ_6ssbB&-]PmM;$B2ؙ 3\5!*ysp=) cqR6RG"$ӏU1<ՌRuHt严4_ZϽNI?-F)MF)fNY$#Ǫmjƍ!$1۽||;,'B2#U4'#WBę@6ը:XHH^ۀ%O!ye=eM;vjE+$>28xTt[Ht_ܔo %Ih484f7F%e[Nh=&O2"~MU4JHھAL #ъly@8%& \{ۚԥ%^%kx|cKϝCWW)rsAw#4vJ8L)vd֜oJP|y5vf"w{p0f'/%l8+}.u$}G7b9CR CFTe&4b,M$Nj4$" JNIHҤ1 5- pHh0vVxp9-_)BBӎG3s,ϟ;{% `R_P%JG`z6瘦zHR3aHE,jDH% wzg!J=|YiqhꏁO8ж8!x73~:eF##P#a`BL, E (I±A$f~! M8;k.0Mwi:b'sj<`s64K5֬dzzF`2#+-۝^u/HUna"$)Ta Ԧt Yn+s%G0]]eyl1]$(ߠ{,}ICvOv8D<")`I~'jhlvt2])A"1bYy9za0&'iIJR24;aR|PSvOk<sބ.Mȓ{j+U!ouiax&"QaD0(IFBMSEDDD{\;B"!JJغii/rX:."A[:BH4/l%Ųiy>PS)iJ%,j6q%*)GKDJG%eq:5{Ku|HXFTnWf\{5y:FH Įظ^taze叄PvHs/*&p.-FTҮ2SNA`@P0, J !4`_ZvmkJb)O\|Ϲ]s|:=drXj5jo&ӍQ1 ӗê0Z(Hp\es^5 FQ^"*Li3 ]h^j=O(ݷ34=w!tu<.~ |9Ai ZDnAHٴqԁ+{t\ 2Me5 9J9[X-H ec|P߳2!`.)%{NKt"ODG ԓf-<՘^5HџfR&/dX )l+a3%NjcJVd-"!% -ea!8Fgʛ:?& scu:VH,z9N=X;唷WrBR4 Rv23#Z{tЮ㗐KQcRTцRN.-Xy 'zOΔR,] 4 ^{ T(xtɲpebq"RZ!P/˓E UhԠ:Zæʎʰ󢒕!ìZRiƸ7 w7Gw@ 9L KvW4c @h|IH GyPq7pA0HFT 8}A19x_^kcɨ)I2 84WcBX5Detl~Yukt=e;X94VD~*1mE鈤meWLxj⛖^q8qP_ g+Y<*t^#ܗဍrnwa(NMFHsD}h߅79*i[~]Tr莵s"[߲݅~r3j"r79ߟ "3`DReز_R1Fs{WV/GBx> Em k%qY{NYlD jҭ6j2%R|6%$tu-jN|BnKT(aVzRy1U*|(p8|yXf=g8ѲWF(W9lXR SvT2TC%4@Hxƨx"M:4cBRt.~ԈD?IBajǪeDd7@ٿN5*G+gd"!U~vL>M^=lxK[}ɛtr)%$m-$CUSce3M[d5)$B2Sv/F%Q0ʥҹe>׎稞D@qv*!ґ(v(F|_%It6-js*,{<QR0F70(7EHl x!p/uokdR)$,}!AToon]s>Xx7>Me}/쭅z7Bh5sizIDATHj@xe LNIb6EcRS]&s'tzdDR|PNCM{fҕǼPxF# fvcsH3/>v>U؄JcP#4vß+x9X}IG#`0LS q3!d>#Iw< &#QkK}$B,}>ʣm\s><~cOz"(x1R֠ZXhH #H2:SGYXt*}T!i"`LjHܥRߘ4BD!H!zAN"4RTʼnd~]\y W5I<\%ƓI 㟢|Ϲ>@d?/@ݑrX&/4 W|Բ9DEa_\{ߪODHZeP[oQ~ؓwy8YG4L?C*te_>ިH%W -=WSFu`˞v>B#_Jh@b[xøv+2\vGŞgPnN m vkMޝ^ image/svg+xml Openclipart Network 2011-08-09T15:31:22 Network https://openclipart.org/detail/154453/network-by-printerkiller PrinterKiller Network web internet connect www easysnmp-0.2.6/setup-py2.7.cfg000066400000000000000000000000551426455670300160700ustar00rootroot00000000000000[tool:pytest] addopts = -s testpaths = tests easysnmp-0.2.6/setup.cfg000066400000000000000000000003251426455670300152130ustar00rootroot00000000000000[tool:pytest] addopts = -s --cov=easysnmp --cov-report=term-missing --flake8 testpaths = tests flake8-ignore = build/*.py ALL [flake8] max-line-length = 88 exclude = build tests dev dist extend-ignore = E203 easysnmp-0.2.6/setup.py000066400000000000000000000155351426455670300151150ustar00rootroot00000000000000from subprocess import check_output from sys import argv, platform, exit from shlex import split as s_split from distutils import sysconfig from distutils.command import build from setuptools import setup, Extension from setuptools.command.test import test as TestCommand from setuptools import dist # Determine if a base directory has been provided with the --basedir option basedir = None in_tree = False # Add compiler flags if debug is set compile_args = [] link_args = [] for arg in argv: if arg.startswith('--debug'): # Note from GCC manual: # If you use multiple -O options, with or without level numbers, # the last such option is the one that is effective. compile_args.extend(['-Wall', '-O0', '-g']) elif arg.startswith('--basedir='): basedir = arg.split('=')[1] in_tree = True # If a base directory has been provided, we use it if in_tree: base_cmd = '{0}/net-snmp-config {{{0}}}'.format(basedir) libs_cmd = base_cmd.format('--build-lib-dirs {0}'.format(basedir)) incl_cmd = base_cmd.format('--build-includes {0}'.format(basedir)) netsnmp_libs = check_output(base_cmd.format('--libs'), shell=True).decode() libdirs = check_output(libs_cmd, shell=True).decode() incdirs = check_output(incl_cmd, shell=True).decode() libs = [flag[2:] for flag in s_split(netsnmp_libs) if flag[:2] == '-l'] libdirs = [flag[2:] for flag in s_split(libdirs) if flag[:2] == '-L'] incdirs = [flag[2:] for flag in s_split(incdirs) if flag[:2] == '-I'] # Otherwise, we use the system-installed SNMP libraries else: netsnmp_libs = check_output('net-snmp-config --libs', shell=True).decode() pass_next = False has_arg = ('-framework',) for flag in s_split(netsnmp_libs): if pass_next: link_args.append(flag) pass_next = False elif flag in has_arg: # -framework CoreFoundation link_args.append(flag) pass_next = True elif flag[:2] == '-f': # -flat_namespace link_args.append(flag) pass_next = False # link_args += [flag for flag in s_split(netsnmp_libs) if flag[:2] == '-f'] libs = [flag[2:] for flag in s_split(netsnmp_libs) if flag[:2] == '-l'] libdirs = [flag[2:] for flag in s_split(netsnmp_libs) if flag[:2] == '-L'] incdirs = [] if platform == 'darwin': # OS X brew = check_output('brew info net-snmp', shell=True).decode() if 'command not found' not in brew: # /usr/local/opt is the default brew `opt` prefix, however the user # may have installed it elsewhere. The `brew info ` includes # an apostrophe, which breaks shlex. We'll simply replace it buildvars = list( map(lambda e: e.split('"', 1)[1].strip('"'), filter(lambda var: '="' in var, brew.split()))) libdirs += [flag[2:] for flag in buildvars if flag[:2] == '-L'] incdirs += [flag[2:] for flag in buildvars if flag[:2] == '-I'] # The homebrew version also depends on the Openssl keg openssl_ver = list(filter(lambda o: 'openssl' in o, *map(str.split, filter(lambda l: 'openssl' in l, str(brew.replace('\'', '')).split('\n') ))))[0] brew = check_output( 'brew info {0}'.format(openssl_ver), shell=True ).decode() buildvars = list( map(lambda e: e.split('"', 1)[1].strip('"'), filter(lambda var: '="' in var, brew.split()))) libdirs += [flag[2:] for flag in buildvars if flag[:2] == '-L'] incdirs += [flag[2:] for flag in buildvars if flag[:2] == '-I'] # Setup the py.test class for use with the test command class PyTest(TestCommand): user_options = [('pytest-args=', 'a', 'Arguments to pass to py.test')] def initialize_options(self): TestCommand.initialize_options(self) self.pytest_args = [] def finalize_options(self): TestCommand.finalize_options(self) self.test_args = [] self.test_suite = True def run_tests(self): # Import here, cause outside the eggs aren't loaded import pytest errno = pytest.main(self.pytest_args) exit(errno) # Read the long description from README.rst with open('README.rst') as f: long_description = f.read() setup( name='easysnmp', version='0.2.6', description='A blazingly fast and Pythonic SNMP library based on the ' 'official Net-SNMP bindings', long_description=long_description, author='Kent Coble', author_email='coblekent@gmail.com', url='https://github.com/kamakazikamikaze/easysnmp', license='BSD', packages=['easysnmp'], tests_require=['pytest-cov', 'pytest-flake8', 'pytest-sugar', 'pytest'], cmdclass={'test': PyTest}, ext_modules=[ Extension( 'easysnmp.interface', ['easysnmp/interface.c'], library_dirs=libdirs, include_dirs=incdirs, libraries=libs, extra_compile_args=compile_args, extra_link_args=link_args ) ], classifiers=[ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Topic :: System :: Networking', 'Topic :: System :: Networking :: Monitoring' ] ) if platform == 'darwin': # Newer Net-SNMP dylib may not be linked to properly b = build.build(dist.Distribution()) # Dynamically determine build path b.finalize_options() ext = sysconfig.get_config_var('EXT_SUFFIX') or '.so' # None for Python 2 linked = check_output(( "otool -L {0}/easysnmp/interface{1} | " r"egrep 'libnetsnmp\.' | " "tr -s '\t' ' ' | " "cut -d' ' -f2").format( b.build_platlib, ext ), shell=True).decode().strip() target_libs = check_output( "find {0} -name libnetsnmp.*.dylib".format(' '.join(libdirs)), shell=True).decode().strip().split() prefix = check_output( "net-snmp-config --prefix", shell=True).decode().strip() for lib in target_libs: if prefix in lib: target_lib = lib break _ = check_output( 'install_name_tool -change {0} {1} {2}/easysnmp/interface{3}'.format( linked, target_lib, b.build_platlib, ext), shell=True) easysnmp-0.2.6/tests/000077500000000000000000000000001426455670300145345ustar00rootroot00000000000000easysnmp-0.2.6/tests/__init__.py000066400000000000000000000000001426455670300166330ustar00rootroot00000000000000easysnmp-0.2.6/tests/conftest.py000066400000000000000000000070371426455670300167420ustar00rootroot00000000000000from __future__ import unicode_literals import logging import pytest from sys import version_info if version_info[0] >= 3: from subprocess import Popen, DEVNULL else: from subprocess import Popen from os import devnull import easysnmp class SNMPSetCLIError(Exception): """An exception raised when an SNMP SET fails via the CLI.""" pass if version_info[0] >= 3: # Required to avoid UnboundLocalError in Python 3 def snmp_set_via_cli(oid, value, type): """ Sets an SNMP variable using the snmpset command. :param oid: the OID to update :param value: the new value to set the OID to :param type: a single character type as required by the snmpset command (i: INTEGER, u: unsigned INTEGER, t: TIMETICKS, a: IPADDRESS o: OBJID, s: STRING, x: HEX STRING, d: DECIMAL STRING, b: BITS U: unsigned int64, I: signed int64, F: float, D: double) """ process = Popen( "snmpset -v2c -c public localhost:11161 {} {} {}".format( oid, type, '"{}"'.format(value) if type == "s" else value ), stdout=DEVNULL, stderr=DEVNULL, shell=True, ) process.communicate() if process.returncode != 0: raise SNMPSetCLIError( "failed to set {0} to {1} (type {2})".format(oid, value, type) ) else: def snmp_set_via_cli(oid, value, type): """ Sets an SNMP variable using the snmpset command. :param oid: the OID to update :param value: the new value to set the OID to :param type: a single character type as required by the snmpset command (i: INTEGER, u: unsigned INTEGER, t: TIMETICKS, a: IPADDRESS o: OBJID, s: STRING, x: HEX STRING, d: DECIMAL STRING, b: BITS U: unsigned int64, I: signed int64, F: float, D: double) """ DEVNULL = open(devnull, "w") process = Popen( "snmpset -v2c -c public localhost:11161 {} {} {}".format( oid, type, '"{}"'.format(value) if type == "s" else value ), stdout=DEVNULL, stderr=DEVNULL, shell=True, ) process.communicate() if process.returncode != 0: raise SNMPSetCLIError( "failed to set {0} to {1} (type {2})".format(oid, value, type) ) DEVNULL.close() # Disable logging for the C interface snmp_logger = logging.getLogger("easysnmp.interface") snmp_logger.disabled = True SESS_V1_ARGS = { "version": 1, "hostname": "localhost", "remote_port": 11161, "community": "public", } SESS_V2_ARGS = { "version": 2, "hostname": "localhost", "remote_port": 11161, "community": "public", } SESS_V3_ARGS = { "version": 3, "hostname": "localhost", "remote_port": 11161, "security_level": "authPriv", "security_username": "initial", "privacy_password": "priv_pass", "auth_password": "auth_pass", } @pytest.fixture(params=[SESS_V1_ARGS, SESS_V2_ARGS, SESS_V3_ARGS]) def sess_args(request): return request.param @pytest.fixture def sess(sess_args): return easysnmp.Session(**sess_args) @pytest.fixture def reset_values(): yield None snmp_set_via_cli("sysLocation.0", "my original location", "s") snmp_set_via_cli("nsCacheTimeout.1.3.6.1.2.1.2.2", "0", "i") @pytest.fixture def sess_v3(): return SESS_V3_ARGS easysnmp-0.2.6/tests/snmpd.conf000066400000000000000000000400351426455670300165260ustar00rootroot00000000000000############################################################################### # # EXAMPLE.conf: # An example configuration file for configuring the ucd-snmp snmpd agent. # ############################################################################### # # This file is intended to only be an example. If, however, you want # to use it, it should be placed in /usr/local/etc/snmp/snmpd.conf. # When the snmpd agent starts up, this is where it will look for it. # # You might be interested in generating your own snmpd.conf file using # the "snmpconf" program (perl script) instead. It's a nice menu # based interface to writing well commented configuration files. Try it! # # Note: This file is automatically generated from EXAMPLE.conf.def. # Do NOT read the EXAMPLE.conf.def file! Instead, after you have run # configure & make, and then make sure you read the EXAMPLE.conf file # instead, as it will tailor itself to your configuration. # All lines beginning with a '#' are comments and are intended for you # to read. All other lines are configuration commands for the agent. # # PLEASE: read the snmpd.conf(5) manual page as well! # ############################################################################### # Access Control ############################################################################### # YOU SHOULD CHANGE THE "COMMUNITY" TOKEN BELOW TO A NEW KEYWORD ONLY # KNOWN AT YOUR SITE. YOU *MUST* CHANGE THE NETWORK TOKEN BELOW TO # SOMETHING REFLECTING YOUR LOCAL NETWORK ADDRESS SPACE. # By far, the most common question I get about the agent is "why won't # it work?", when really it should be "how do I configure the agent to # allow me to access it?" # # By default, the agent responds to the "public" community for read # only access, if run out of the box without any configuration file in # place. The following examples show you other ways of configuring # the agent so that you can change the community names, and give # yourself write access as well. # # The following lines change the access permissions of the agent so # that the COMMUNITY string provides read-only access to your entire # NETWORK (EG: 10.10.10.0/24), and read/write access to only the # localhost (127.0.0.1, not its real ipaddress). # # For more information, read the FAQ as well as the snmpd.conf(5) # manual page. #### # First, map the community name (COMMUNITY) into a security name # (local and mynetwork, depending on where the request is coming # from): # sec.name source community com2sec local localhost public com2sec mynetwork 192.168.1.0/24 public #### # Second, map the security names into group names: # sec.model sec.name group MyRWGroup v1 local group MyRWGroup v2c local group MyRWGroup usm local group MyRWGroup usm initial group MyRWGroup usm secondary group MyROGroup v1 mynetwork group MyROGroup v2c mynetwork group MyROGroup usm mynetwork #### # Third, create a view for us to let the groups have rights to: # incl/excl subtree mask view all included .1 80 #### # Finally, grant the 2 groups access to the 1 view with different # write permissions: # context sec.model sec.level match read write notif access MyROGroup "" any noauth exact all none none access MyRWGroup "" any noauth exact all all none # ----------------------------------------------------------------------------- rwuser initial priv createUser initial MD5 auth_pass DES priv_pass rwuser secondary priv createUser secondary SHA auth_second AES priv_second ############################################################################### # System contact information # # It is also possible to set the sysContact and sysLocation system # variables through the snmpd.conf file. **PLEASE NOTE** that setting # the value of these objects here makes these objects READ-ONLY # (regardless of any access control settings). Any attempt to set the # value of an object whose value is given here will fail with an error # status of notWritable. # syslocation Right here, right now. syscontact G. S. Marzot # Example output of snmpwalk: # % snmpwalk -v 1 -c public localhost system # system.sysDescr.0 = "SunOS name sun4c" # system.sysObjectID.0 = OID: enterprises.ucdavis.ucdSnmpAgent.sunos4 # system.sysUpTime.0 = Timeticks: (595637548) 68 days, 22:32:55 # system.sysContact.0 = "Me " # system.sysName.0 = "name" # system.sysLocation.0 = "Right here, right now." # system.sysServices.0 = 72 # ----------------------------------------------------------------------------- ############################################################################### # Process checks. # # The following are examples of how to use the agent to check for # processes running on the host. The syntax looks something like: # # proc NAME [MAX=0] [MIN=0] # # NAME: the name of the process to check for. It must match # exactly (ie, http will not find httpd processes). # MAX: the maximum number allowed to be running. Defaults to 0. # MIN: the minimum number to be running. Defaults to 0. # # Examples: # # Make sure mountd is running proc mountd # Make sure there are no more than 4 ntalkds running, but 0 is ok too. proc ntalkd 4 # Make sure at least one sendmail, but less than or equal to 10 are running. proc sendmail 10 1 # A snmpwalk of the prTable would look something like this: # # % snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.2 # enterprises.ucdavis.procTable.prEntry.prIndex.1 = 1 # enterprises.ucdavis.procTable.prEntry.prIndex.2 = 2 # enterprises.ucdavis.procTable.prEntry.prIndex.3 = 3 # enterprises.ucdavis.procTable.prEntry.prNames.1 = "mountd" # enterprises.ucdavis.procTable.prEntry.prNames.2 = "ntalkd" # enterprises.ucdavis.procTable.prEntry.prNames.3 = "sendmail" # enterprises.ucdavis.procTable.prEntry.prMin.1 = 0 # enterprises.ucdavis.procTable.prEntry.prMin.2 = 0 # enterprises.ucdavis.procTable.prEntry.prMin.3 = 1 # enterprises.ucdavis.procTable.prEntry.prMax.1 = 0 # enterprises.ucdavis.procTable.prEntry.prMax.2 = 4 # enterprises.ucdavis.procTable.prEntry.prMax.3 = 10 # enterprises.ucdavis.procTable.prEntry.prCount.1 = 0 # enterprises.ucdavis.procTable.prEntry.prCount.2 = 0 # enterprises.ucdavis.procTable.prEntry.prCount.3 = 1 # enterprises.ucdavis.procTable.prEntry.prErrorFlag.1 = 1 # enterprises.ucdavis.procTable.prEntry.prErrorFlag.2 = 0 # enterprises.ucdavis.procTable.prEntry.prErrorFlag.3 = 0 # enterprises.ucdavis.procTable.prEntry.prErrMessage.1 = "No mountd process running." # enterprises.ucdavis.procTable.prEntry.prErrMessage.2 = "" # enterprises.ucdavis.procTable.prEntry.prErrMessage.3 = "" # enterprises.ucdavis.procTable.prEntry.prErrFix.1 = 0 # enterprises.ucdavis.procTable.prEntry.prErrFix.2 = 0 # enterprises.ucdavis.procTable.prEntry.prErrFix.3 = 0 # # Note that the errorFlag for mountd is set to 1 because one is not # running (in this case an rpc.mountd is, but thats not good enough), # and the ErrMessage tells you what's wrong. The configuration # imposed in the snmpd.conf file is also shown. # # Special Case: When the min and max numbers are both 0, it assumes # you want a max of infinity and a min of 1. # # ----------------------------------------------------------------------------- ############################################################################### # Executables/scripts # # # You can also have programs run by the agent that return a single # line of output and an exit code. Here are two examples. # # exec NAME PROGRAM [ARGS ...] # # NAME: A generic name. # PROGRAM: The program to run. Include the path! # ARGS: optional arguments to be passed to the program # a simple hello world exec echotest /bin/echo hello world # Run a shell script containing: # # #!/bin/sh # echo hello world # echo hi there # exit 35 # # Note: this has been specifically commented out to prevent # accidental security holes due to someone else on your system writing # a /tmp/shtest before you do. Uncomment to use it. # #exec shelltest /bin/sh /tmp/shtest # Then, # % snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.8 # enterprises.ucdavis.extTable.extEntry.extIndex.1 = 1 # enterprises.ucdavis.extTable.extEntry.extIndex.2 = 2 # enterprises.ucdavis.extTable.extEntry.extNames.1 = "echotest" # enterprises.ucdavis.extTable.extEntry.extNames.2 = "shelltest" # enterprises.ucdavis.extTable.extEntry.extCommand.1 = "/bin/echo hello world" # enterprises.ucdavis.extTable.extEntry.extCommand.2 = "/bin/sh /tmp/shtest" # enterprises.ucdavis.extTable.extEntry.extResult.1 = 0 # enterprises.ucdavis.extTable.extEntry.extResult.2 = 35 # enterprises.ucdavis.extTable.extEntry.extOutput.1 = "hello world." # enterprises.ucdavis.extTable.extEntry.extOutput.2 = "hello world." # enterprises.ucdavis.extTable.extEntry.extErrFix.1 = 0 # enterprises.ucdavis.extTable.extEntry.extErrFix.2 = 0 # Note that the second line of the /tmp/shtest shell script is cut # off. Also note that the exit status of 35 was returned. # ----------------------------------------------------------------------------- ############################################################################### # disk checks # # The agent can check the amount of available disk space, and make # sure it is above a set limit. # disk PATH [MIN=DEFDISKMINIMUMSPACE] # # PATH: mount path to the disk in question. # MIN: Disks with space below this value will have the Mib's errorFlag set. # Default value = DEFDISKMINIMUMSPACE. # Check the / partition and make sure it contains at least 10 megs. disk / 10000 # % snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.9 # enterprises.ucdavis.diskTable.dskEntry.diskIndex.1 = 0 # enterprises.ucdavis.diskTable.dskEntry.diskPath.1 = "/" Hex: 2F # enterprises.ucdavis.diskTable.dskEntry.diskDevice.1 = "/dev/dsk/c201d6s0" # enterprises.ucdavis.diskTable.dskEntry.diskMinimum.1 = 10000 # enterprises.ucdavis.diskTable.dskEntry.diskTotal.1 = 837130 # enterprises.ucdavis.diskTable.dskEntry.diskAvail.1 = 316325 # enterprises.ucdavis.diskTable.dskEntry.diskUsed.1 = 437092 # enterprises.ucdavis.diskTable.dskEntry.diskPercent.1 = 58 # enterprises.ucdavis.diskTable.dskEntry.diskErrorFlag.1 = 0 # enterprises.ucdavis.diskTable.dskEntry.diskErrorMsg.1 = "" # ----------------------------------------------------------------------------- ############################################################################### # load average checks # # load [1MAX=DEFMAXLOADAVE] [5MAX=DEFMAXLOADAVE] [15MAX=DEFMAXLOADAVE] # # 1MAX: If the 1 minute load average is above this limit at query # time, the errorFlag will be set. # 5MAX: Similar, but for 5 min average. # 15MAX: Similar, but for 15 min average. # Check for loads: load 12 14 14 # % snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.10 # enterprises.ucdavis.loadTable.laEntry.loadaveIndex.1 = 1 # enterprises.ucdavis.loadTable.laEntry.loadaveIndex.2 = 2 # enterprises.ucdavis.loadTable.laEntry.loadaveIndex.3 = 3 # enterprises.ucdavis.loadTable.laEntry.loadaveNames.1 = "Load-1" # enterprises.ucdavis.loadTable.laEntry.loadaveNames.2 = "Load-5" # enterprises.ucdavis.loadTable.laEntry.loadaveNames.3 = "Load-15" # enterprises.ucdavis.loadTable.laEntry.loadaveLoad.1 = "0.49" Hex: 30 2E 34 39 # enterprises.ucdavis.loadTable.laEntry.loadaveLoad.2 = "0.31" Hex: 30 2E 33 31 # enterprises.ucdavis.loadTable.laEntry.loadaveLoad.3 = "0.26" Hex: 30 2E 32 36 # enterprises.ucdavis.loadTable.laEntry.loadaveConfig.1 = "12.00" # enterprises.ucdavis.loadTable.laEntry.loadaveConfig.2 = "14.00" # enterprises.ucdavis.loadTable.laEntry.loadaveConfig.3 = "14.00" # enterprises.ucdavis.loadTable.laEntry.loadaveErrorFlag.1 = 0 # enterprises.ucdavis.loadTable.laEntry.loadaveErrorFlag.2 = 0 # enterprises.ucdavis.loadTable.laEntry.loadaveErrorFlag.3 = 0 # enterprises.ucdavis.loadTable.laEntry.loadaveErrMessage.1 = "" # enterprises.ucdavis.loadTable.laEntry.loadaveErrMessage.2 = "" # enterprises.ucdavis.loadTable.laEntry.loadaveErrMessage.3 = "" # ----------------------------------------------------------------------------- ############################################################################### # Extensible sections. # # This alleviates the multiple line output problem found in the # previous executable mib by placing each mib in its own mib table: # Run a shell script containing: # # #!/bin/sh # echo hello world # echo hi there # exit 35 # # Note: this has been specifically commented out to prevent # accidental security holes due to someone else on your system writing # a /tmp/shtest before you do. Uncomment to use it. # # exec .1.3.6.1.4.1.2021.50 shelltest /bin/sh /tmp/shtest # % snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.50 # enterprises.ucdavis.50.1.1 = 1 # enterprises.ucdavis.50.2.1 = "shelltest" # enterprises.ucdavis.50.3.1 = "/bin/sh /tmp/shtest" # enterprises.ucdavis.50.100.1 = 35 # enterprises.ucdavis.50.101.1 = "hello world." # enterprises.ucdavis.50.101.2 = "hi there." # enterprises.ucdavis.50.102.1 = 0 # Now the Output has grown to two lines, and we can see the 'hi # there.' output as the second line from our shell script. # # Note that you must alter the mib.txt file to be correct if you want # the .50.* outputs above to change to reasonable text descriptions. # Other ideas: # # exec .1.3.6.1.4.1.2021.51 ps /bin/ps # exec .1.3.6.1.4.1.2021.52 top /usr/local/bin/top # exec .1.3.6.1.4.1.2021.53 mailq /usr/bin/mailq # ----------------------------------------------------------------------------- ############################################################################### # Pass through control. # # Usage: # pass MIBOID EXEC-COMMAND # # This will pass total control of the mib underneath the MIBOID # portion of the mib to the EXEC-COMMAND. # # Note: You'll have to change the path of the passtest script to your # source directory or install it in the given location. # # Example: (see the script for details) # (commented out here since it requires that you place the # script in the right location. (its not installed by default)) # pass .1.3.6.1.4.1.2021.255 /bin/sh PREFIX/local/passtest # % snmpwalk -v 1 -c public localhost .1.3.6.1.4.1.2021.255 # enterprises.ucdavis.255.1 = "life the universe and everything" # enterprises.ucdavis.255.2.1 = 42 # enterprises.ucdavis.255.2.2 = OID: 42.42.42 # enterprises.ucdavis.255.3 = Timeticks: (363136200) 42 days, 0:42:42 # enterprises.ucdavis.255.4 = IpAddress: 127.0.0.1 # enterprises.ucdavis.255.5 = 42 # enterprises.ucdavis.255.6 = Gauge: 42 # # % snmpget -v 1 -c public localhost .1.3.6.1.4.1.2021.255.5 # enterprises.ucdavis.255.5 = 42 # # % snmpset -v 1 -c public localhost .1.3.6.1.4.1.2021.255.1 s "New string" # enterprises.ucdavis.255.1 = "New string" # # For specific usage information, see the man/snmpd.conf.5 manual page # as well as the local/passtest script used in the above example. ############################################################################### # Subagent control # # The agent can support subagents using a number of extension mechanisms. # From the 4.2.1 release, AgentX support is being compiled in by default. # However, this is still experimental code, so should not be used on # critical production systems. # Please see the file README.agentx for more details. # # If having read, marked, learnt and inwardly digested this information, # you decide that you do wish to make use of this mechanism, simply # uncomment the following directive. # # master agentx # # I repeat - this is *NOT* regarded as suitable for front-line production # systems, though it is probably stable enough for day-to-day use. # Probably. # # No refunds will be given. ############################################################################### # Further Information # # See the snmpd.conf manual page, and the output of "snmpd -H". # MUCH more can be done with the snmpd.conf than is shown as an # example here. # certSecName 10 D020A78EAF99FCE276AA9F43063A69698E4F75D1 --rfc822 # rwuser -s tsm hardaker@wjh.hardakers.net # trustCert D020A78EAF99FCE276AA9F43063A69698E4F75D1 agentaddress 11161 smuxsocket 11162 easysnmp-0.2.6/tests/test_easy.py000066400000000000000000000331531426455670300171130ustar00rootroot00000000000000from __future__ import unicode_literals import platform import pytest from easysnmp.easy import ( snmp_get, snmp_set, snmp_set_multiple, snmp_get_next, snmp_get_bulk, snmp_walk, snmp_bulkwalk, ) from easysnmp.exceptions import ( EasySNMPError, EasySNMPUnknownObjectIDError, EasySNMPNoSuchObjectError, EasySNMPNoSuchInstanceError, EasySNMPNoSuchNameError, ) def test_snmp_get_regular(sess_args): res = snmp_get("sysDescr.0", **sess_args) assert platform.version() in res.value assert res.oid == "sysDescr" assert res.oid_index == "0" assert res.snmp_type == "OCTETSTR" def test_snmp_get_tuple(sess_args): res = snmp_get(("sysDescr", "0"), **sess_args) assert platform.version() in res.value assert res.oid == "sysDescr" assert res.oid_index == "0" assert res.snmp_type == "OCTETSTR" def test_snmp_get_fully_qualified(sess_args): res = snmp_get(".iso.org.dod.internet.mgmt.mib-2.system.sysDescr.0", **sess_args) assert platform.version() in res.value assert res.oid == "sysDescr" assert res.oid_index == "0" assert res.snmp_type == "OCTETSTR" def test_snmp_get_fully_qualified_tuple(sess_args): res = snmp_get( (".iso.org.dod.internet.mgmt.mib-2.system.sysDescr", "0"), **sess_args ) assert platform.version() in res.value assert res.oid == "sysDescr" assert res.oid_index == "0" assert res.snmp_type == "OCTETSTR" def test_snmp_get_numeric(sess_args): res = snmp_get(".1.3.6.1.2.1.1.1.0", **sess_args) assert platform.version() in res.value assert res.oid == "sysDescr" assert res.oid_index == "0" assert res.snmp_type == "OCTETSTR" def test_snmp_get_numeric_no_leading_dot(sess_args): res = snmp_get("1.3.6.1.2.1.1.1.0", **sess_args) assert platform.version() in res.value assert res.oid == "sysDescr" assert res.oid_index == "0" assert res.snmp_type == "OCTETSTR" def test_snmp_get_numeric_tuple(sess_args): res = snmp_get((".1.3.6.1.2.1.1.1", "0"), **sess_args) assert platform.version() in res.value assert res.oid == "sysDescr" assert res.oid_index == "0" assert res.snmp_type == "OCTETSTR" def test_snmp_get_unknown(sess_args): with pytest.raises(EasySNMPUnknownObjectIDError): snmp_get("sysDescripto.0", **sess_args) def test_snmp_v1_get_with_retry_no_such(sess_args): res = snmp_get(["iso", "sysDescr.0", "iso"], retry_no_such=True, **sess_args) assert res[0] if sess_args["version"] == 1: assert res[0].oid == "iso" assert res[0].snmp_type == "NOSUCHNAME" else: assert res[0].snmp_type == "NOSUCHOBJECT" assert res[1] assert platform.version() in res[1].value assert res[1].oid == "sysDescr" assert res[1].oid_index == "0" assert res[1].snmp_type == "OCTETSTR" assert res[2] if sess_args["version"] == 1: assert res[2].oid == "iso" assert res[2].snmp_type == "NOSUCHNAME" else: assert res[2].snmp_type == "NOSUCHOBJECT" def test_snmp_get_invalid_instance(sess_args): # Sadly, SNMP v1 doesn't distuingish between an invalid instance and an # invalid object ID, instead it excepts with noSuchName if sess_args["version"] == 1: with pytest.raises(EasySNMPNoSuchNameError): snmp_get("sysContact.1", **sess_args) else: res = snmp_get("sysContact.1", **sess_args) assert res.snmp_type == "NOSUCHINSTANCE" def test_snmp_get_invalid_instance_with_abort_enabled(sess_args): # Sadly, SNMP v1 doesn't distuingish between an invalid instance and an # invalid object ID, so it raises the same exception for both if sess_args["version"] == 1: with pytest.raises(EasySNMPNoSuchNameError): snmp_get("sysContact.1", abort_on_nonexistent=True, **sess_args) else: with pytest.raises(EasySNMPNoSuchInstanceError): snmp_get("sysContact.1", abort_on_nonexistent=True, **sess_args) def test_snmp_get_invalid_object(sess_args): if sess_args["version"] == 1: with pytest.raises(EasySNMPNoSuchNameError): snmp_get("iso", **sess_args) else: res = snmp_get("iso", **sess_args) assert res.snmp_type == "NOSUCHOBJECT" def test_snmp_get_invalid_object_with_abort_enabled(sess_args): if sess_args["version"] == 1: with pytest.raises(EasySNMPNoSuchNameError): snmp_get("iso", abort_on_nonexistent=True, **sess_args) else: with pytest.raises(EasySNMPNoSuchObjectError): snmp_get("iso", abort_on_nonexistent=True, **sess_args) def test_snmp_get_next(sess_args): res = snmp_get_next("nsCacheEntry", **sess_args) assert res.oid == "nsCacheTimeout" assert res.oid_index == "1.3.6.1.2.1.2.2" assert int(res.value) >= 0 assert res.snmp_type == "INTEGER" def test_snmp_get_next_numeric(sess_args): res = snmp_get_next((".1.3.6.1.2.1.1.1", "0"), **sess_args) assert res.oid == "sysObjectID" assert res.oid_index == "0" # .10 == Linux, .16 == macosx, .13 == win32, .255 == UNKNOWN assert res.value.rsplit(".", 1)[0] == ".1.3.6.1.4.1.8072.3.2" assert res.snmp_type == "OBJECTID" def test_snmp_get_next_with_retry_no_such(sess_args): res = snmp_get(["iso.9", "sysDescr.0", "iso.9"], retry_no_such=True, **sess_args) assert res[0] if sess_args["version"] == 1: assert res[0].value == "NOSUCHNAME" assert res[0].oid == "iso" assert res[0].oid_index == "9" assert res[0].snmp_type == "NOSUCHNAME" else: assert res[0].snmp_type == "NOSUCHOBJECT" assert res[1] assert platform.version() in res[1].value assert res[1].oid == "sysDescr" assert res[1].oid_index == "0" assert res[1].snmp_type == "OCTETSTR" assert res[2] if sess_args["version"] == 1: assert res[2].value == "NOSUCHNAME" assert res[2].oid == "iso" assert res[2].oid_index == "9" assert res[2].snmp_type == "NOSUCHNAME" else: assert res[2].snmp_type == "NOSUCHOBJECT" def test_snmp_get_next_end_of_mib_view(sess_args): if sess_args["version"] == 1: with pytest.raises(EasySNMPNoSuchNameError): snmp_get_next(["iso.9", "sysDescr", "iso.9"], **sess_args) else: res = snmp_get_next(["iso.9", "sysDescr", "iso.9"], **sess_args) assert res[0] assert res[0].value == "ENDOFMIBVIEW" assert res[0].oid == "iso.9" assert res[0].snmp_type == "ENDOFMIBVIEW" assert res[1] assert platform.version() in res[1].value assert res[1].oid == "sysDescr" assert res[1].oid_index == "0" assert res[1].snmp_type == "OCTETSTR" assert res[2] assert res[2].value == "ENDOFMIBVIEW" assert res[2].oid == "iso.9" assert res[2].snmp_type == "ENDOFMIBVIEW" def test_snmp_get_next_unknown(sess_args): with pytest.raises(EasySNMPUnknownObjectIDError): snmp_get_next("sysDescripto.0", **sess_args) def test_snmp_set_string(sess_args, request, reset_values): res = snmp_get(("sysLocation", "0"), **sess_args) assert res.oid == "sysLocation" assert res.oid_index == "0" assert res.value != "my newer location" assert res.snmp_type == "OCTETSTR" success = snmp_set(("sysLocation", "0"), "my newer location", **sess_args) assert success res = snmp_get(("sysLocation", "0"), **sess_args) assert res.oid == "sysLocation" assert res.oid_index == "0" assert res.value == "my newer location" assert res.snmp_type == "OCTETSTR" def test_snmp_set_string_long_type(sess_args, reset_values): res = snmp_get(("sysLocation", "0"), **sess_args) assert res.oid == "sysLocation" assert res.oid_index == "0" assert res.value != "my newer location" assert res.snmp_type == "OCTETSTR" success = snmp_set( ("sysLocation", "0"), "my newer location", "OCTETSTR", **sess_args ) assert success res = snmp_get(("sysLocation", "0"), **sess_args) assert res.oid == "sysLocation" assert res.oid_index == "0" assert res.value == "my newer location" assert res.snmp_type == "OCTETSTR" def test_snmp_set_string_short_type(sess_args, reset_values): res = snmp_get(("sysLocation", "0"), **sess_args) assert res.oid == "sysLocation" assert res.oid_index == "0" assert res.value != "my newer location" assert res.snmp_type == "OCTETSTR" success = snmp_set(("sysLocation", "0"), "my newer location", "s", **sess_args) assert success res = snmp_get(("sysLocation", "0"), **sess_args) assert res.oid == "sysLocation" assert res.oid_index == "0" assert res.value == "my newer location" assert res.snmp_type == "OCTETSTR" def test_snmp_set_integer(sess_args, reset_values): success = snmp_set(("nsCacheTimeout", ".1.3.6.1.2.1.2.2"), 65, **sess_args) assert success res = snmp_get(("nsCacheTimeout", ".1.3.6.1.2.1.2.2"), **sess_args) assert res.oid == "nsCacheTimeout" assert res.oid_index == "1.3.6.1.2.1.2.2" assert res.value == "65" assert res.snmp_type == "INTEGER" def test_snmp_set_integer_long_type(sess_args, reset_values): success = snmp_set( ("nsCacheTimeout", ".1.3.6.1.2.1.2.2"), 65, "INTEGER", **sess_args ) assert success res = snmp_get(("nsCacheTimeout", ".1.3.6.1.2.1.2.2"), **sess_args) assert res.oid == "nsCacheTimeout" assert res.oid_index == "1.3.6.1.2.1.2.2" assert res.value == "65" assert res.snmp_type == "INTEGER" def test_snmp_set_integer_short_type(sess_args, reset_values): success = snmp_set(("nsCacheTimeout", ".1.3.6.1.2.1.2.2"), 65, "i", **sess_args) assert success res = snmp_get(("nsCacheTimeout", ".1.3.6.1.2.1.2.2"), **sess_args) assert res.oid == "nsCacheTimeout" assert res.oid_index == "1.3.6.1.2.1.2.2" assert res.value == "65" assert res.snmp_type == "INTEGER" def test_snmp_set_unknown(sess_args): with pytest.raises(EasySNMPUnknownObjectIDError): snmp_set("nsCacheTimeoooout", 1234, **sess_args) def test_snmp_set_multiple(sess_args, reset_values): res = snmp_get(["sysLocation.0", "nsCacheTimeout.1.3.6.1.2.1.2.2"], **sess_args) assert res[0].value != "my newer location" assert res[1].value != "162" success = snmp_set_multiple( [ ("sysLocation.0", "my newer location"), (("nsCacheTimeout", ".1.3.6.1.2.1.2.2"), 162), ], **sess_args ) assert success res = snmp_get(["sysLocation.0", "nsCacheTimeout.1.3.6.1.2.1.2.2"], **sess_args) assert res[0].value == "my newer location" assert res[1].value == "162" def test_snmp_get_bulk(sess_args): if sess_args["version"] == 1: with pytest.raises(EasySNMPError): snmp_get_bulk( [ "sysUpTime", "sysORLastChange", "sysORID", "sysORDescr", "sysORUpTime", ], 2, 8, **sess_args ) else: res = snmp_get_bulk( ["sysUpTime", "sysORLastChange", "sysORID", "sysORDescr", "sysORUpTime"], 2, 8, **sess_args ) assert len(res) == 26 assert res[0].oid == "sysUpTimeInstance" assert res[0].oid_index == "" assert int(res[0].value) > 0 assert res[0].snmp_type == "TICKS" assert res[4].oid == "sysORUpTime" assert res[4].oid_index == "1" assert int(res[4].value) >= 0 assert res[4].snmp_type == "TICKS" def test_snmp_walk(sess_args): res = snmp_walk("system", **sess_args) assert len(res) >= 7 assert platform.version() in res[0].value assert res[3].value == "G. S. Marzot " assert res[4].value == platform.node() assert res[5].value == "my original location" def test_snmp_walk_res(sess_args): res = snmp_walk("system", **sess_args) assert len(res) >= 7 assert res[0].oid == "sysDescr" assert res[0].oid_index == "0" assert platform.version() in res[0].value assert res[0].snmp_type == "OCTETSTR" assert res[3].oid == "sysContact" assert res[3].oid_index == "0" assert res[3].value == "G. S. Marzot " assert res[3].snmp_type == "OCTETSTR" assert res[4].oid == "sysName" assert res[4].oid_index == "0" assert res[4].value == platform.node() assert res[4].snmp_type == "OCTETSTR" assert res[5].oid == "sysLocation" assert res[5].oid_index == "0" assert res[5].value == "my original location" assert res[5].snmp_type == "OCTETSTR" def test_snmp_bulkwalk_res(sess_args): if sess_args["version"] == 1: with pytest.raises(EasySNMPError): snmp_bulkwalk("system", **sess_args) else: res = snmp_bulkwalk("system", **sess_args) assert len(res) >= 7 assert res[0].oid == "sysDescr" assert res[0].oid_index == "0" assert platform.version() in res[0].value assert res[0].snmp_type == "OCTETSTR" assert res[3].oid == "sysContact" assert res[3].oid_index == "0" assert res[3].value == "G. S. Marzot " assert res[3].snmp_type == "OCTETSTR" assert res[4].oid == "sysName" assert res[4].oid_index == "0" assert res[4].value == platform.node() assert res[4].snmp_type == "OCTETSTR" assert res[5].oid == "sysLocation" assert res[5].oid_index == "0" assert res[5].value == "my original location" assert res[5].snmp_type == "OCTETSTR" def test_snmp_walk_unknown(sess_args): with pytest.raises(EasySNMPUnknownObjectIDError): snmp_walk("systemo", **sess_args) easysnmp-0.2.6/tests/test_helpers.py000066400000000000000000000013701426455670300176100ustar00rootroot00000000000000from __future__ import unicode_literals from easysnmp.helpers import normalize_oid def test_normalize_oid_regular(): oid, oid_index = normalize_oid("sysContact.0") assert oid == "sysContact" assert oid_index == "0" def test_normalize_oid_numeric(): oid, oid_index = normalize_oid(".1.3.6.1.2.1.1.1.0") assert oid == ".1.3.6.1.2.1.1.1.0" assert oid_index == "" def test_normalize_oid_full_qualified(): oid, oid_index = normalize_oid(".iso.org.dod.internet.mgmt.mib-2.system.sysDescr.0") assert oid == ".iso.org.dod.internet.mgmt.mib-2.system.sysDescr" assert oid_index == "0" def test_normalize_oid_with_index(): oid, oid_index = normalize_oid("abc", "def") assert oid == "abc" assert oid_index == "def" easysnmp-0.2.6/tests/test_session.py000066400000000000000000000270041426455670300176330ustar00rootroot00000000000000from __future__ import unicode_literals import platform import re import pytest from easysnmp.exceptions import ( EasySNMPError, EasySNMPConnectionError, EasySNMPTimeoutError, EasySNMPNoSuchObjectError, EasySNMPNoSuchInstanceError, EasySNMPNoSuchNameError, ) from easysnmp.session import Session def test_session_invalid_snmp_version(): with pytest.raises(ValueError): Session(version=4) @pytest.mark.parametrize("version", [1, 2, 3]) def test_session_invalid_hostname(version): with pytest.raises(EasySNMPConnectionError): session = Session(hostname="invalid", version=version) session.get("sysContact.0") @pytest.mark.parametrize("version", [1, 2, 3]) def test_session_invalid_hostname_and_remote_port(version): with pytest.raises(ValueError): Session(hostname="localhost:162", remote_port=163, version=version) @pytest.mark.parametrize("version", [1, 2, 3]) def test_session_hostname_and_remote_port_split(version): session = Session(hostname="localhost:162", version=version) assert session.hostname == "localhost" assert session.remote_port == 162 @pytest.mark.parametrize("version", [1, 2, 3]) def test_session_invalid_port(version): with pytest.raises(EasySNMPTimeoutError): session = Session(remote_port=1234, version=version, timeout=0.2, retries=1) session.get("sysContact.0") def test_session_set_multiple_next(sess, reset_values): # Destroy succeeds even if no row exists sess.set(".1.3.6.1.6.3.12.1.2.1.9.116.101.115.116", 6) success = sess.set_multiple( [ (".1.3.6.1.6.3.12.1.2.1.2.116.101.115.116", ".1.3.6.1.6.1.1"), (".1.3.6.1.6.3.12.1.2.1.3.116.101.115.116", "1234"), (".1.3.6.1.6.3.12.1.2.1.9.116.101.115.116", 4), ] ) assert success res = sess.get_next( ["snmpTargetAddrTDomain", "snmpTargetAddrTAddress", "snmpTargetAddrRowStatus"] ) assert len(res) == 3 assert res[0].oid == "snmpTargetAddrTDomain" assert res[0].oid_index == "116.101.115.116" assert res[0].value == ".1.3.6.1.6.1.1" assert res[0].snmp_type == "OBJECTID" assert res[1].oid == "snmpTargetAddrTAddress" assert res[1].oid_index == "116.101.115.116" assert res[1].value == "1234" assert res[1].snmp_type == "OCTETSTR" assert res[2].oid == "snmpTargetAddrRowStatus" assert res[2].oid_index == "116.101.115.116" assert res[2].value == "3" assert res[2].snmp_type == "INTEGER" def test_session_set_clear(sess): res = sess.set(".1.3.6.1.6.3.12.1.2.1.9.116.101.115.116", 6) assert res == 1 res = sess.get_next( ["snmpTargetAddrTDomain", "snmpTargetAddrTAddress", "snmpTargetAddrRowStatus"] ) assert len(res) == 3 assert res[0].oid == "snmpUnavailableContexts" assert res[0].oid_index == "0" assert res[0].value == "0" assert res[0].snmp_type == "COUNTER" assert res[1].oid == "snmpUnavailableContexts" assert res[1].oid_index == "0" assert res[1].value == "0" assert res[1].snmp_type == "COUNTER" assert res[2].oid == "snmpUnavailableContexts" assert res[2].oid_index == "0" assert res[2].value == "0" assert res[2].snmp_type == "COUNTER" def test_session_get(sess): res = sess.get([("sysUpTime", "0"), ("sysContact", "0"), ("sysLocation", "0")]) assert len(res) == 3 assert res[0].oid == "sysUpTimeInstance" assert res[0].oid_index == "" assert int(res[0].value) > 0 assert res[0].snmp_type == "TICKS" assert res[1].oid == "sysContact" assert res[1].oid_index == "0" assert res[1].value == "G. S. Marzot " assert res[1].snmp_type == "OCTETSTR" assert res[2].oid == "sysLocation" assert res[2].oid_index == "0" assert res[2].value == "my original location" assert res[2].snmp_type == "OCTETSTR" def test_session_get_use_numeric(sess): sess.use_numeric = True res = sess.get("sysContact.0") assert res.oid == ".1.3.6.1.2.1.1.4" assert res.oid_index == "0" assert res.value == "G. S. Marzot " assert res.snmp_type == "OCTETSTR" def test_session_get_use_sprint_value(sess): sess.use_sprint_value = True res = sess.get("sysUpTimeInstance") assert res.oid == "sysUpTimeInstance" assert res.oid_index == "" assert re.match(r"^\d+:\d+:\d+:\d+\.\d+$", res.value) assert res.snmp_type == "TICKS" def test_session_get_use_enums(sess): sess.use_enums = True res = sess.get("ifAdminStatus.1") assert res.oid == "ifAdminStatus" assert res.oid_index == "1" assert res.value == "up" assert res.snmp_type == "INTEGER" def test_session_get_next(sess): res = sess.get_next([("sysUpTime", "0"), ("sysContact", "0"), ("sysLocation", "0")]) assert len(res) == 3 assert res[0].oid == "sysContact" assert res[0].oid_index == "0" assert res[0].value == "G. S. Marzot " assert res[0].snmp_type == "OCTETSTR" assert res[1].oid == "sysName" assert res[1].oid_index == "0" assert res[1].value == platform.node() assert res[1].snmp_type == "OCTETSTR" assert res[2].oid == "sysORLastChange" assert res[2].oid_index == "0" assert int(res[2].value) >= 0 assert res[2].snmp_type == "TICKS" def test_session_set(sess, reset_values): res = sess.get(("sysLocation", "0")) assert res.value != "my newer location" success = sess.set(("sysLocation", "0"), "my newer location") assert success res = sess.get(("sysLocation", "0")) assert res.value == "my newer location" def test_session_set_multiple(sess, reset_values): res = sess.get(["sysLocation.0", "nsCacheTimeout.1.3.6.1.2.1.2.2"]) assert res[0].value != "my newer location" assert res[1].value != "160" success = sess.set_multiple( [ ("sysLocation.0", "my newer location"), (("nsCacheTimeout", ".1.3.6.1.2.1.2.2"), 160), ] ) assert success res = sess.get(["sysLocation.0", "nsCacheTimeout.1.3.6.1.2.1.2.2"]) assert res[0].value == "my newer location" assert res[1].value == "160" def test_session_get_bulk(sess): # noqa if sess.version == 1: with pytest.raises(EasySNMPError): sess.get_bulk( [ "sysUpTime", "sysORLastChange", "sysORID", "sysORDescr", "sysORUpTime", ], 2, 8, ) else: res = sess.get_bulk( ["sysUpTime", "sysORLastChange", "sysORID", "sysORDescr", "sysORUpTime"], 2, 8, ) assert len(res) == 26 assert res[0].oid == "sysUpTimeInstance" assert res[0].oid_index == "" assert int(res[0].value) > 0 assert res[0].snmp_type == "TICKS" assert res[4].oid == "sysORUpTime" assert res[4].oid_index == "1" assert int(res[4].value) >= 0 assert res[4].snmp_type == "TICKS" def test_session_get_invalid_instance(sess): # Sadly, SNMP v1 doesn't distuingish between an invalid instance and an # invalid object ID, instead it excepts with noSuchName if sess.version == 1: with pytest.raises(EasySNMPNoSuchNameError): sess.get("sysDescr.100") else: res = sess.get("sysDescr.100") assert res.snmp_type == "NOSUCHINSTANCE" def test_session_get_invalid_instance_with_abort_enabled(sess): # Sadly, SNMP v1 doesn't distuingish between an invalid instance and an # invalid object ID, instead it excepts with noSuchName sess.abort_on_nonexistent = True if sess.version == 1: with pytest.raises(EasySNMPNoSuchNameError): sess.get("sysDescr.100") else: with pytest.raises(EasySNMPNoSuchInstanceError): sess.get("sysDescr.100") def test_session_get_invalid_object(sess): if sess.version == 1: with pytest.raises(EasySNMPNoSuchNameError): sess.get("iso") else: res = sess.get("iso") assert res.snmp_type == "NOSUCHOBJECT" def test_session_get_invalid_object_with_abort_enabled(sess): sess.abort_on_nonexistent = True if sess.version == 1: with pytest.raises(EasySNMPNoSuchNameError): sess.get("iso") else: with pytest.raises(EasySNMPNoSuchObjectError): sess.get("iso") def test_session_walk(sess): res = sess.walk("system") assert len(res) >= 7 assert res[0].oid == "sysDescr" assert res[0].oid_index == "0" assert platform.version() in res[0].value assert res[0].snmp_type == "OCTETSTR" assert res[3].oid == "sysContact" assert res[3].oid_index == "0" assert res[3].value == "G. S. Marzot " assert res[3].snmp_type == "OCTETSTR" assert res[4].oid == "sysName" assert res[4].oid_index == "0" assert res[4].value == platform.node() assert res[4].snmp_type == "OCTETSTR" assert res[5].oid == "sysLocation" assert res[5].oid_index == "0" assert res[5].value == "my original location" assert res[5].snmp_type == "OCTETSTR" def test_session_bulkwalk(sess): if sess.version == 1: with pytest.raises(EasySNMPError): sess.bulkwalk("system") else: res = sess.walk("system") assert len(res) >= 7 assert res[0].oid == "sysDescr" assert res[0].oid_index == "0" assert platform.version() in res[0].value assert res[0].snmp_type == "OCTETSTR" assert res[3].oid == "sysContact" assert res[3].oid_index == "0" assert res[3].value == "G. S. Marzot " assert res[3].snmp_type == "OCTETSTR" assert res[4].oid == "sysName" assert res[4].oid_index == "0" assert res[4].value == platform.node() assert res[4].snmp_type == "OCTETSTR" assert res[5].oid == "sysLocation" assert res[5].oid_index == "0" assert res[5].value == "my original location" assert res[5].snmp_type == "OCTETSTR" def test_session_walk_all(sess): # OID 1.3.6.1.6.3.16.1.5.2.1.6.6.95.110.111.110.101.95.1.2 # or SNMP-VIEW-BASED-ACM-MIB::vacmViewTreeFamilyStatus."_none_".1.2 # appears to return a noSuchName error when using v1, but not with v2c. # This may be a Net-SNMP snmpd bug. if sess.version == 1: with pytest.raises(EasySNMPNoSuchNameError): sess.walk(".") else: res = sess.walk(".") assert len(res) > 0 assert res[0].oid == "sysDescr" assert res[0].oid_index == "0" assert platform.version() in res[0].value assert res[0].snmp_type == "OCTETSTR" assert res[3].oid == "sysContact" assert res[3].oid_index == "0" assert res[3].value == "G. S. Marzot " assert res[3].snmp_type == "OCTETSTR" assert res[4].oid == "sysName" assert res[4].oid_index == "0" assert res[4].value == platform.node() assert res[4].snmp_type == "OCTETSTR" assert res[5].oid == "sysLocation" assert res[5].oid_index == "0" assert res[5].value == "my original location" assert res[5].snmp_type == "OCTETSTR" def test_session_update(): s = Session(version=3) ptr = s.sess_ptr s.version = 1 s.update_session() assert ptr != s.sess_ptr s.tunneled = True ptr = s.sess_ptr with pytest.raises(ValueError): s.update_session() assert ptr == s.sess_ptr s.update_session(tunneled=False, version=2) assert s.version == 2 assert s.tunneled is False easysnmp-0.2.6/tests/test_session_build_varlist.py000066400000000000000000000015111426455670300225510ustar00rootroot00000000000000from __future__ import unicode_literals from easysnmp.session import build_varlist def test_build_varlist(): varlist, is_list = build_varlist("sysContact.0") assert len(varlist) == 1 assert varlist[0].oid == "sysContact" assert varlist[0].oid_index == "0" assert varlist[0].value is None assert varlist[0].snmp_type is None assert not is_list def test_build_varlist_list(): varlist, is_list = build_varlist(["sysContact.0", ("sysDescr", "0")]) assert len(varlist) == 2 assert varlist[0].oid == "sysContact" assert varlist[0].oid_index == "0" assert varlist[0].value is None assert varlist[0].snmp_type is None assert varlist[1].oid == "sysDescr" assert varlist[1].oid_index == "0" assert varlist[1].value is None assert varlist[1].snmp_type is None assert is_list easysnmp-0.2.6/tests/test_utils.py000066400000000000000000000013261426455670300173070ustar00rootroot00000000000000from __future__ import unicode_literals from easysnmp.compat import ub from easysnmp.utils import strip_non_printable, tostr def test_strip_non_printable_regular(): assert strip_non_printable("hello there") == "hello there" def test_strip_non_printable_contains_binary(): assert strip_non_printable(ub(chr(20)) + "my thingo" + ub(chr(155))) == ( "my thingo (contains binary)" ) def test_strip_non_printable_only_binary(): assert strip_non_printable(ub(chr(20)) + ub(chr(155))) == ("(contains binary)") def test_tostr_none(): assert tostr(None) is None def test_tostr_string(): assert tostr("hello there") == "hello there" def test_tostr_integer(): assert tostr(1234) == "1234" easysnmp-0.2.6/tests/test_v3_cache.py000066400000000000000000000015041426455670300176200ustar00rootroot00000000000000from __future__ import unicode_literals import pytest from easysnmp import Session from easysnmp.exceptions import EasySNMPConnectionError def test_v3_not_caching_user(sess_v3): s = Session(**sess_v3) res = s.get('sysDescr.0') assert res.oid == "sysDescr" assert res.oid_index == "0" assert res.snmp_type == "OCTETSTR" s.update_session(privacy_password="wrong_pass") with pytest.raises(EasySNMPConnectionError): res = s.get('sysDescr.0') d = dict(**sess_v3) d["privacy_password"] = "wrong_pass" s = Session(**d) with pytest.raises(EasySNMPConnectionError): res = s.get("sysDescr.0") s.update_session(privacy_password="priv_pass") res = s.get('sysDescr.0') assert res.oid == "sysDescr" assert res.oid_index == "0" assert res.snmp_type == "OCTETSTR" easysnmp-0.2.6/tests/test_variables.py000066400000000000000000000051561426455670300201240ustar00rootroot00000000000000from __future__ import unicode_literals from easysnmp.compat import ub from easysnmp.variables import SNMPVariable, SNMPVariableList def test_snmp_variable_regular(): var = SNMPVariable("sysDescr", "0") assert var.oid == "sysDescr" assert var.oid_index == "0" def test_snmp_variable_value(): var = SNMPVariable("sysDescr", "0", "my thingo") assert var.value == "my thingo" assert var.oid == "sysDescr" assert var.oid_index == "0" def test_snmp_variable_repr(): var = SNMPVariable("sysDescr", "0", "my thingo", "OCTETSTR") assert var.__repr__() == ( "" ) def test_snmp_variable_repr_binary(): var = SNMPVariable( "sysDescr", "0", ub(chr(20)) + "my thingo" + ub(chr(155)), "OCTETSTR" ) assert var.__repr__() == ( "" ) def test_snmp_variable_repr_binary_only(): var = SNMPVariable("sysDescr", "0", ub(chr(20)) + ub(chr(155)), "OCTETSTR") assert var.__repr__() == ( "" ) def test_snmp_variable_repr_none(): var = SNMPVariable() assert var.__repr__() == ( "" ) def test_snmp_variable_extract_oid_index(): var = SNMPVariable("sysDescr.0") assert var.oid == "sysDescr" assert var.oid_index == "0" assert var.value is None assert var.snmp_type is None def test_snmp_variable_long(): var = SNMPVariable(".iso.org.dod.internet.mgmt.mib-2.system.sysDescr", "0") assert var.oid == ".iso.org.dod.internet.mgmt.mib-2.system.sysDescr" assert var.oid_index == "0" assert var.value is None assert var.snmp_type is None def test_snmp_variable_doesnt_extract_oid_index(): var = SNMPVariable(".iso.org.dod.internet.mgmt.mib-2.system.sysDescr.0") assert var.oid == ".iso.org.dod.internet.mgmt.mib-2.system.sysDescr" assert var.oid_index == "0" assert var.value is None assert var.snmp_type is None def test_snmp_variable_numeric(): var = SNMPVariable(".1.3.6.1.2.1.1.1.0") assert var.oid == ".1.3.6.1.2.1.1.1.0" assert var.oid_index == "" assert var.value is None assert var.snmp_type is None def test_snmp_variable_list(): varlist = SNMPVariableList(["sysContact.0", "sysLocation.0", "sysDescr.0"]) assert varlist.varbinds == ["sysContact.0", "sysLocation.0", "sysDescr.0"]