Codebase list pysmi / 8896a54
import pysmi_0.2.2.orig.tar.gz Vincent Bernat 6 years ago
124 changed file(s) with 12663 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0
1 Revision 0.2.2, 13-11-2017
2 --------------------------
3
4 - Library documentation refactored and updated
5 - Fixed malformed Python code being produced by pysnmp code generator
6
7 Revision 0.2.1, 11-11-2017
8 --------------------------
9
10 - Added MIB *status*, *product release* and *revision description* set
11 calls at pysnmp code generator
12 - Changed REVISION field format in JSON representation - it is now
13 a list of dicts each with *revision* timestamp and *description* text
14 - MIB REFERENCE fields are only exported if --with-mib-text is on
15 - Sphinx documentation theme changed to Alabaster
16 - Multiple fixes to pysnmp codegen not to produce function calls
17 with more than 255 parameters
18
19 Revision 0.1.4, 14-10-2017
20 --------------------------
21
22 - Fix to SMI lexer to treat tokens starting from a digit as belonging
23 to a lower-cased class. This fixes sub-OID parsing bug (specifically,
24 802dot3(10006))
25 - Fix to the mibdump.py local MIB path automatic injection in front
26 of existing --mib-sources
27
28 Revision 0.1.3, 19-05-2017
29 --------------------------
30
31 * INET-ADDRESS-MIB configured as pre-built at pysnmp codegen
32 * JSON codegen produces "nodetype" element for OBJECT-TYPE
33 * Fix to mibdump.py --destination-directory option
34 * Fix to pysnmp and JSON code generators to properly refer to MIB module
35 defining particular MIB object
36
37 Revision 0.1.2, 12-04-2017
38 --------------------------
39
40 * The @mib@ magic in reader's URL template made optional. If it is not present,
41 MIB module name is just appended to URL template
42 * Send User-Agent containing pysmi and Python versions as well as platform name.
43 * Fixed missing STATUS/DISPLAY-HINT/REFERENCE/etc fields generation at pysnmp
44 backend when running in the non-full-text mode
45 * Fixed broken `ordereddict` dependency on Python 2.6-
46
47 Revision 0.1.1, 30-03-2017
48 --------------------------
49
50 * Generate REFERENCE and STATUS fields at various SMI objects
51 * Generate DESCRIPTION field followed REVISION field at MODULE-IDENTITY objects
52 * Generate PRODUCT-RELEASE field at AGENT-CAPABILITIES objects
53 * Generated Python source aligned with PEP8
54 * MIB texts cleaned up by default, --keep-texts-layout preserves original formatting
55 * Fix to the `ordereddict` conditional dependency
56 * Missing test module recovered
57 * Failing tests fixed
58
59 Revision 0.1.0, 25-03-2017
60 --------------------------
61
62 * JSON code generating backend implemented
63 * Experimental JSON OID->MIB indices generation implemented
64 * Package structure flattened for easier use
65 * Minor refactoring to the test suite
66 * Source code statically analyzed, hardened and PEP8-ized
67 * Files closed explicitly to mute ResourceWarnings
68 * Fixed to Python 2.4 (and aged ply) compatibility
69 * Added a workaround to avoid generating pysnmp TextualConvention classes
70 inheriting from TextualConvention (when MIB defines a TEXTUAL-CONVENTION
71 based on another TEXTUAL-CONVENTION as SYNTAX)
72 * Author's e-mail changed, copyright extended to year 2017
73
74 Revision 0.0.7, 12-02-2016
75 --------------------------
76
77 * Crash on existing .py file handling fixed.
78 * Fix to __doc__ use in setup.py to make -O0 installation mode working.
79 * Fix to PyPackageSearcher not to fail on broken Python packages.
80 * Source code pep8'ed
81 * Copyright added to source files.
82
83 Revision 0.0.6, 01-10-2015
84 --------------------------
85
86 * Several typos fixed, source code linted again.
87 * Some dead code cleaned up.
88
89 Revision 0.0.5, 28-09-2015
90 --------------------------
91
92 * Wheel distribution format now supported.
93 * Handle the case of MIB symbols conflict with Python reserved words.
94 * Handle binary DEFVAL initializer for INTEGER's.
95 * Generate LAST-UPDATED at pysnmp code generator.
96
97 Revision 0.0.4, 01-07-2015
98 --------------------------
99
100 * Fix to MRO compliance for mixin classes generation at pysnmp backend
101 * Fix to repeated imports in generated code at pysnmp backend
102 * Fix to mibdump tool to properly handle the --generate-mib-texts option.
103 * Fix to Python compile() - optimize flag is valid only past Python 3.1
104 * Fix to SMIv1 INDEX clause code generation for pysnmp backend.
105 * Tighten file creation security at pysmi.writer.pyfile
106
107 Revision 0.0.3, 28-06-2015
108 --------------------------
109
110 * Two-pass compiler design allows for much accurate code generation.
111 * Sphinx-based documentation first introduced
112
113 Revision 0.0.0, 11-04-2015
114 --------------------------
115
116 * First public release, not fully operational yet
0 Copyright (c) 2015-2017 Ilya Etingof <etingof@gmail.com>
1 All rights reserved.
2
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions are met:
5
6 * Redistributions of source code must retain the above copyright notice,
7 this list of conditions and the following disclaimer.
8
9 * Redistributions in binary form must reproduce the above copyright notice,
10 this list of conditions and the following disclaimer in the documentation
11 and/or other materials provided with the distribution.
12
13 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23 POSSIBILITY OF SUCH DAMAGE.
0 include *.txt *.rst *.md
1 recursive-include tests *.py
2 recursive-include examples *.py
3 recursive-include docs *.txt *.rst *.svg *.py Makefile
0 Metadata-Version: 1.1
1 Name: pysmi
2 Version: 0.2.2
3 Summary: SNMP SMI/MIB Parser
4 Home-page: https://github.com/etingof/pysmi
5 Author: Ilya Etingof <etingof@gmail.com>
6 Author-email: etingof@gmail.com
7 License: BSD
8 Description: A pure-Python implementation of SNMP/SMI MIB parsing and conversion library.
9 Platform: any
10 Classifier: Development Status :: 5 - Production/Stable
11 Classifier: Environment :: Console
12 Classifier: Intended Audience :: Developers
13 Classifier: Intended Audience :: Education
14 Classifier: Intended Audience :: Information Technology
15 Classifier: Intended Audience :: System Administrators
16 Classifier: Intended Audience :: Telecommunications Industry
17 Classifier: License :: OSI Approved :: BSD License
18 Classifier: Natural Language :: English
19 Classifier: Operating System :: OS Independent
20 Classifier: Programming Language :: Python :: 2
21 Classifier: Programming Language :: Python :: 2.4
22 Classifier: Programming Language :: Python :: 2.5
23 Classifier: Programming Language :: Python :: 2.6
24 Classifier: Programming Language :: Python :: 2.7
25 Classifier: Programming Language :: Python :: 3
26 Classifier: Programming Language :: Python :: 3.2
27 Classifier: Programming Language :: Python :: 3.3
28 Classifier: Programming Language :: Python :: 3.4
29 Classifier: Programming Language :: Python :: 3.5
30 Classifier: Programming Language :: Python :: 3.6
31 Classifier: Topic :: Communications
32 Classifier: Topic :: System :: Monitoring
33 Classifier: Topic :: System :: Networking :: Monitoring
34 Classifier: Topic :: Software Development :: Libraries :: Python Modules
0
1 SNMP MIB parser
2 ---------------
3 [![Python Versions](https://img.shields.io/pypi/pyversions/pysmi.svg)](https://pypi.python.org/pypi/pysmi/)
4 [![Build status](https://travis-ci.org/etingof/pysmi.svg?branch=master)](https://secure.travis-ci.org/etingof/pysmi)
5 [![Coverage Status](https://img.shields.io/codecov/c/github/etingof/pysmi.svg)](https://codecov.io/github/etingof/pysmi)
6 [![GitHub license](https://img.shields.io/badge/license-BSD-blue.svg)](https://raw.githubusercontent.com/etingof/pysmi/master/LICENSE.rst)
7
8 PySMI is a pure-Python implementation of
9 [SNMP SMI](https://en.wikipedia.org/wiki/Management_information_base) MIB parser.
10 This tool is designed to turn ASN.1 MIBs into various formats. As of this moment,
11 JSON and [pysnmp](https://github.com/etingof/pysnmp) modules can be generated
12 from ASN.1 MIBs.
13
14 Features
15 --------
16
17 * Understands SMIv1, SMIv2 and de-facto SMI dialects
18 * Turns MIBs into pysnmp classes and JSON documents
19 * Maintains an index of MIB objects over many MIB modules
20 * Automatically pulls ASN.1 MIBs from local directories, ZIP archives,
21 HTTP and FTP servers
22 * 100% Python, works with Python 2.4 up to Python 3.6
23
24 Rendered PySMI documentation can be found at [pysmi site](http://pysmi.sf.net).
25
26 How to use PySMI
27 ----------------
28
29 If you are using pysnmp, you might never notice pysmi presence - pysnmp
30 calls pysmi for MIB download and compilation behind the scenes (you can
31 still can do that manually by invoking *mibdump.py* tool).
32
33 To turn ASN.1 MIB into a JSON document, call *mibdump.py* tool like this:
34
35 ```
36 $ mibdump.py --generate-mib-texts --destination-format json IF-MIB
37 Source MIB repositories: file:///usr/share/snmp/mibs, http://mibs.snmplabs.com/asn1/@mib@
38 Borrow missing/failed MIBs from: http://mibs.snmplabs.com/json/fulltexts/@mib@
39 Existing/compiled MIB locations:
40 Compiled MIBs destination directory: .
41 MIBs excluded from code generation: RFC-1212, RFC-1215, RFC1065-SMI, RFC1155-SMI,
42 RFC1158-MIB, RFC1213-MIB, SNMPv2-CONF, SNMPv2-SMI, SNMPv2-TC, SNMPv2-TM
43 MIBs to compile: IF-MIB
44 Destination format: json
45 Parser grammar cache directory: not used
46 Also compile all relevant MIBs: yes
47 Rebuild MIBs regardless of age: yes
48 Do not create/update MIBs: no
49 Byte-compile Python modules: no (optimization level no)
50 Ignore compilation errors: no
51 Generate OID->MIB index: no
52 Generate texts in MIBs: yes
53 Keep original texts layout: no
54 Try various filenames while searching for MIB module: yes
55 Created/updated MIBs: IANAifType-MIB, IF-MIB, SNMPv2-MIB
56 Pre-compiled MIBs borrowed:
57 Up to date MIBs: SNMPv2-CONF, SNMPv2-SMI, SNMPv2-TC
58 Missing source MIBs:
59 Ignored MIBs:
60 Failed MIBs:
61 ```
62
63 JSON document build from
64 [IF-MIB module](http://mibs.snmplabs.com/asn1/IF-MIB)
65 would hold information such as:
66
67 ```
68 {
69 "ifMIB": {
70 "name": "ifMIB",
71 "oid": "1.3.6.1.2.1.31",
72 "class": "moduleidentity",
73 "revisions": [
74 "2007-02-15 00:00",
75 "1996-02-28 21:55",
76 "1993-11-08 21:55"
77 ]
78 },
79 ...
80 "ifTestTable": {
81 "name": "ifTestTable",
82 "oid": "1.3.6.1.2.1.31.1.3",
83 "nodetype": "table",
84 "class": "objecttype",
85 "maxaccess": "not-accessible"
86 },
87 "ifTestEntry": {
88 "name": "ifTestEntry",
89 "oid": "1.3.6.1.2.1.31.1.3.1",
90 "nodetype": "row",
91 "class": "objecttype",
92 "maxaccess": "not-accessible",
93 "augmention": {
94 "name": "ifTestEntry",
95 "module": "IF-MIB",
96 "object": "ifEntry"
97 }
98 },
99 "ifTestId": {
100 "name": "ifTestId",
101 "oid": "1.3.6.1.2.1.31.1.3.1.1",
102 "nodetype": "column",
103 "class": "objecttype",
104 "syntax": {
105 "type": "TestAndIncr",
106 "class": "type"
107 },
108 "maxaccess": "read-write"
109 },
110 ...
111 }
112 ```
113
114 In general, converted MIBs capture all aspects of original (ASN.1) MIB contents
115 and layout. The snippet above is just a partial example, but here is the
116 complete [IF-MIB.json](http://mibs.snmplabs.com/json/fulltexts/IF-MIB.json)
117 file.
118
119 Besides one-to-one MIB conversion, PySMI library can produce JSON index to
120 facilitate fast MIB information lookup across large collection of MIB files.
121 For example, JSON index for
122 [IP-MIB.json](http://mibs.snmplabs.com/json/asn1/IP-MIB),
123 [TCP-MIB.json](http://mibs.snmplabs.com/json/asn1/TCP-MIB) and
124 [UDP-MIB.json](http://mibs.snmplabs.com/json/asn1/UDP-MIB)
125 modules would keep information like this:
126
127 ```
128 {
129 "compliance": {
130 "1.3.6.1.2.1.48.2.1.1": [
131 "IP-MIB"
132 ],
133 "1.3.6.1.2.1.49.2.1.1": [
134 "TCP-MIB"
135 ],
136 "1.3.6.1.2.1.50.2.1.1": [
137 "UDP-MIB"
138 ]
139 },
140 "identity": {
141 "1.3.6.1.2.1.48": [
142 "IP-MIB"
143 ],
144 "1.3.6.1.2.1.49": [
145 "TCP-MIB"
146 ],
147 "1.3.6.1.2.1.50": [
148 "UDP-MIB"
149 ]
150 },
151 "oids": {
152 "1.3.6.1.2.1.4": [
153 "IP-MIB"
154 ],
155 "1.3.6.1.2.1.5": [
156 "IP-MIB"
157 ],
158 "1.3.6.1.2.1.6": [
159 "TCP-MIB"
160 ],
161 "1.3.6.1.2.1.7": [
162 "UDP-MIB"
163 ],
164 "1.3.6.1.2.1.49": [
165 "TCP-MIB"
166 ],
167 "1.3.6.1.2.1.50": [
168 "UDP-MIB"
169 ]
170 }
171 }
172 ```
173
174 With this example, *compliance* and *identity* keys point to
175 *MODULE-COMPLIANCE* and *MODULE-IDENTITY* MIB objects, *oids*
176 list top-level OIDs branches defined in MIB modules. Full index
177 build over thousands of MIBs could be seen
178 [here](http://mibs.snmplabs.com/json/index.json).
179
180 The PySMI library can automatically fetch required MIBs from HTTP, FTP sites
181 or local directories. You could configure any MIB source available to you (including
182 [http://mibs.snmplabs.com/asn1/](http://mibs.snmplabs.com/asn1/)) for that purpose.
183
184 How to get PySMI
185 ----------------
186
187 The pysmi package is distributed under terms and conditions of 2-clause
188 BSD [license](http://pyasn1.sourceforge.net/license.html). Source code is freely
189 available as a GitHub [repo](https://github.com/etingof/pysmi).
190
191 You could `pip install pysmi` or download it from [PyPI](https://pypi.python.org/pypi/pysmi).
192
193 If something does not work as expected,
194 [open an issue](https://github.com/etingof/pysmi/issues) at GitHub or
195 post your question [on Stack Overflow](http://stackoverflow.com/questions/ask).
196
197 Copyright (c) 2015-2017, [Ilya Etingof](mailto:etingof@gmail.com).
198 All rights reserved.
0 Lars Michelsen
1 Tanya Tereschenko
0 * add more tests on edge cases
1 * generate reverse OID -> MIB index
2 * handle SMIv1 MAX clause in range constraint
3 * support MAX clause mapping it into type-specific value
4 * possibly split symbol table code generator onto imported modules and symbol
5 table code generators.
6 * support more common broken SMI constructs
7 * DEFVAL and AUGMENTS should not depend on the order of symbols in MIB
8 * make a collection of fixed MIBs (e.g. Huawei has lots of bad/broken MIBs)
9 * some MIBs use hex as DEFVAL for types other than OCTET STRING (additional AST processing phase will help)
10 * handle the case when the symbol and the enumaration value (which can be in DEFVAL) have the same name - llc2 in NETLINK-SPECIFIC-MIB (additional AST processing phase will help)
11 * check imports and try to add necessary ones if missed (like OBJECT-TYPE, MODULE-IDENTITY, etc)
12 * generate TextualConvention first or add additional AST processing phase
13 * implement a cache of available files at abstract reader path
14 * create a command-line tool for splitting MIBs stored in a single file
15 * create a MIB querying tool: get MIB module's OIDs, enterprise IDs,
16 canonical name(s); search MIB objects by regexp; build source .index
17 * implement xml/html/yaml codegeneration backend
18 * write a tool to fetch canonical MIB name from MIB file, rename MIB file, split merged MIBs onto individual files
19 * write a tool to turn IANA assigned numbers into JSON
20 * introduce cached MIBs invalidation feature -- may be useful when we screw up and produce bad MIBs
21 * json codegen:
22 - make schema configurable
23 - further simplify/review codegen code
24 - rebuild the docs
25 - conditionally require simplejson and ordereddict
26 - add tests
27 - is it a good idea to make writers also reading data?
28 - review symtable for unused pieces
29 - write a tool to split json index onto separate documents
30 - index NOTIFICATION-TYPE's
31 - add example script on index build
0 sphinx
1 pysnmp
0 # Makefile for Sphinx documentation
1 #
2
3 # You can set these variables from the command line.
4 SPHINXOPTS =
5 SPHINXBUILD = sphinx-build
6 PAPER =
7 BUILDDIR = build
8
9 # User-friendly check for sphinx-build
10 ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
11 $(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/)
12 endif
13
14 # Internal variables.
15 PAPEROPT_a4 = -D latex_paper_size=a4
16 PAPEROPT_letter = -D latex_paper_size=letter
17 ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
18 # the i18n builder cannot share the environment and doctrees with the others
19 I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
20
21 .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
22
23 help:
24 @echo "Please use \`make <target>' where <target> is one of"
25 @echo " html to make standalone HTML files"
26 @echo " dirhtml to make HTML files named index.html in directories"
27 @echo " singlehtml to make a single large HTML file"
28 @echo " pickle to make pickle files"
29 @echo " json to make JSON files"
30 @echo " htmlhelp to make HTML files and a HTML help project"
31 @echo " qthelp to make HTML files and a qthelp project"
32 @echo " applehelp to make an Apple Help Book"
33 @echo " devhelp to make HTML files and a Devhelp project"
34 @echo " epub to make an epub"
35 @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
36 @echo " latexpdf to make LaTeX files and run them through pdflatex"
37 @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
38 @echo " text to make text files"
39 @echo " man to make manual pages"
40 @echo " texinfo to make Texinfo files"
41 @echo " info to make Texinfo files and run them through makeinfo"
42 @echo " gettext to make PO message catalogs"
43 @echo " changes to make an overview of all changed/added/deprecated items"
44 @echo " xml to make Docutils-native XML files"
45 @echo " pseudoxml to make pseudoxml-XML files for display purposes"
46 @echo " linkcheck to check all external links for integrity"
47 @echo " doctest to run all doctests embedded in the documentation (if enabled)"
48 @echo " coverage to run coverage check of the documentation (if enabled)"
49
50 clean:
51 rm -rf $(BUILDDIR)/*
52
53 html:
54 $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
55 @echo
56 @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
57
58 dirhtml:
59 $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
60 @echo
61 @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
62
63 singlehtml:
64 $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
65 @echo
66 @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
67
68 pickle:
69 $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
70 @echo
71 @echo "Build finished; now you can process the pickle files."
72
73 json:
74 $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
75 @echo
76 @echo "Build finished; now you can process the JSON files."
77
78 htmlhelp:
79 $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
80 @echo
81 @echo "Build finished; now you can run HTML Help Workshop with the" \
82 ".hhp project file in $(BUILDDIR)/htmlhelp."
83
84 qthelp:
85 $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
86 @echo
87 @echo "Build finished; now you can run "qcollectiongenerator" with the" \
88 ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
89 @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PySMI.qhcp"
90 @echo "To view the help file:"
91 @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PySMI.qhc"
92
93 applehelp:
94 $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
95 @echo
96 @echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
97 @echo "N.B. You won't be able to view it unless you put it in" \
98 "~/Library/Documentation/Help or install it in your application" \
99 "bundle."
100
101 devhelp:
102 $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
103 @echo
104 @echo "Build finished."
105 @echo "To view the help file:"
106 @echo "# mkdir -p $$HOME/.local/share/devhelp/PySMI"
107 @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PySMI"
108 @echo "# devhelp"
109
110 epub:
111 $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
112 @echo
113 @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
114
115 latex:
116 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
117 @echo
118 @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
119 @echo "Run \`make' in that directory to run these through (pdf)latex" \
120 "(use \`make latexpdf' here to do that automatically)."
121
122 latexpdf:
123 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
124 @echo "Running LaTeX files through pdflatex..."
125 $(MAKE) -C $(BUILDDIR)/latex all-pdf
126 @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
127
128 latexpdfja:
129 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
130 @echo "Running LaTeX files through platex and dvipdfmx..."
131 $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
132 @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
133
134 text:
135 $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
136 @echo
137 @echo "Build finished. The text files are in $(BUILDDIR)/text."
138
139 man:
140 $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
141 @echo
142 @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
143
144 texinfo:
145 $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
146 @echo
147 @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
148 @echo "Run \`make' in that directory to run these through makeinfo" \
149 "(use \`make info' here to do that automatically)."
150
151 info:
152 $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
153 @echo "Running Texinfo files through makeinfo..."
154 make -C $(BUILDDIR)/texinfo info
155 @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
156
157 gettext:
158 $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
159 @echo
160 @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
161
162 changes:
163 $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
164 @echo
165 @echo "The overview file is in $(BUILDDIR)/changes."
166
167 linkcheck:
168 $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
169 @echo
170 @echo "Link check complete; look for any errors in the above output " \
171 "or in $(BUILDDIR)/linkcheck/output.txt."
172
173 doctest:
174 $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
175 @echo "Testing of doctests in the sources finished, look at the " \
176 "results in $(BUILDDIR)/doctest/output.txt."
177
178 coverage:
179 $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
180 @echo "Testing of coverage in the sources finished, look at the " \
181 "results in $(BUILDDIR)/coverage/python.txt."
182
183 xml:
184 $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
185 @echo
186 @echo "Build finished. The XML files are in $(BUILDDIR)/xml."
187
188 pseudoxml:
189 $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
190 @echo
191 @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
0 You need Sphinx too build this documentation. Or you can read it in ASCII. ;)
1
2 Better run:
3
4 # pip sphinx
5
6 and once Sphinx is installed on your system, run:
7
8 $ make html
9
10 To build a copy of HTML'ed PySMI documentation.
0 <?xml version="1.0" encoding="UTF-8"?>
1 <svg viewBox="0 0 788.22937 829.02386" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
2 <defs>
3 <filter id="j">
4 <feGaussianBlur stdDeviation="6.2732"/>
5 </filter>
6 <filter id="g">
7 <feGaussianBlur stdDeviation=".64973"/>
8 </filter>
9 <filter id="l" x="-.06193" y="-.19499" width="1.1239" height="1.39">
10 <feGaussianBlur stdDeviation="7.46855"/>
11 </filter>
12 <filter id="k" x="-.25093" y="-.14188" width="1.5019" height="1.2838">
13 <feGaussianBlur stdDeviation="3.07792"/>
14 </filter>
15 <filter id="i" x="-.03871" y="-.12187" width="1.0774" height="1.2437">
16 <feGaussianBlur stdDeviation="4.66785"/>
17 </filter>
18 <filter id="h" x="-.12783" y="-.07846" width="1.2557" height="1.1569">
19 <feGaussianBlur stdDeviation="1.708"/>
20 </filter>
21 <linearGradient id="e">
22 <stop stop-color="#817e7e" offset="0"/>
23 <stop stop-color="#f4ebeb" stop-opacity=".98824" offset=".5"/>
24 <stop stop-color="#241b1b" stop-opacity=".97826" offset="1"/>
25 </linearGradient>
26 <linearGradient id="f">
27 <stop stop-color="#1a5b78" offset="0"/>
28 <stop stop-color="#136890" offset="1"/>
29 </linearGradient>
30 <linearGradient id="r" x1="301.38" x2="318.52" y1="716.86" y2="106.67" gradientUnits="userSpaceOnUse">
31 <stop stop-color="#3aacfa" offset="0"/>
32 <stop stop-color="#2699d7" stop-opacity=".99608" offset=".83216"/>
33 <stop stop-color="#78c1e7" stop-opacity=".99216" offset=".98972"/>
34 <stop stop-color="#cae9f7" stop-opacity=".99216" offset="1"/>
35 </linearGradient>
36 <linearGradient id="q" x1="-6.9187" x2="583.27" gradientUnits="userSpaceOnUse">
37 <stop stop-color="#d4f6f7" offset="0"/>
38 <stop stop-color="#bdf0f2" offset=".5"/>
39 <stop stop-color="#76eaf0" offset=".75"/>
40 <stop stop-color="#465758" offset="1"/>
41 </linearGradient>
42 <linearGradient id="d" x1="172.72" x2="402.47" gradientUnits="userSpaceOnUse">
43 <stop stop-color="#2a9cf9" offset="0"/>
44 <stop stop-color="#afe2eb" offset="1"/>
45 </linearGradient>
46 <linearGradient id="c" x1="286.18" x2="292.27" y1="332.78" y2="297.07" gradientUnits="userSpaceOnUse">
47 <stop stop-color="#241b1b" offset="0"/>
48 <stop stop-color="#241b1b" stop-opacity="0" offset="1"/>
49 </linearGradient>
50 <linearGradient id="b" x1="291.93" x2="290.42" y1="654.44" y2="584.74" gradientUnits="userSpaceOnUse">
51 <stop stop-color="#4ec7ff" offset="0"/>
52 <stop stop-color="#177ba9" offset="1"/>
53 </linearGradient>
54 <linearGradient id="p" x1="166.1" x2="410.06" y1="529.93" y2="527.91" gradientUnits="userSpaceOnUse" xlink:href="#f"/>
55 <linearGradient id="o" x1="257.31" x2="320.16" gradientUnits="userSpaceOnUse" xlink:href="#e"/>
56 <linearGradient id="n" x1="229.49" x2="343.96" gradientUnits="userSpaceOnUse" xlink:href="#e"/>
57 <linearGradient id="m" x1="63.378" x2="507.69" y1="571" y2="567.46" gradientUnits="userSpaceOnUse" xlink:href="#f"/>
58 <radialGradient id="a" cx="288.79" cy="314.87" r="47.676" gradientTransform="matrix(.95889 0 0 .36279 11.873 202.26)" gradientUnits="userSpaceOnUse">
59 <stop stop-color="#f9faf8" offset="0"/>
60 <stop stop-color="#ccceca" stop-opacity=".67816" offset=".80581"/>
61 <stop stop-color="#a0a39d" stop-opacity=".98551" offset="1"/>
62 </radialGradient>
63 </defs>
64 <g transform="translate(36.408 -60.696)">
65 <path d="m217.12 214.13 1.8398 157.37s-68.072 76.035-114.07 139.69-123.27 152.07-110.39 206.89 147.18 111.4 270.45 109.63 316.44-30.06 318.28-100.79-88.31-185.67-121.43-229.87-101.19-123.78-101.19-123.78l-3.6796-159.14s-27.597 14.146-69.912 14.146-69.912-14.146-69.912-14.146z" fill="url(#q)" opacity=".59004"/>
66 <g transform="matrix(1.0193 0 0 .9797 275.35 -97.577)">
67 <path transform="matrix(1.0949 0 0 1.1946 -27.22 -91.679)" d="m433.86 608.91c0 25.384-64.786 45.962-144.7 45.962s-144.7-20.578-144.7-45.962 64.786-45.962 144.7-45.962 144.7 20.578 144.7 45.962z" filter="url(#l)"/>
68 <path d="m320.16 384.59c-20.034 6.3449-42.056 6.5046-62.156 0.15625 0.29535 26.623 0.5955 5.2459 0.875 31.875 6e-3 1.2834-0.46958 2.5635-1.3125 3.5312-27.411 31.834-52.856 65.234-76.938 99.75-17.564 25.17-36.956 49.209-44.688 77.531l-0.0312 0.0625c-2.7636 9.7018-0.36414 20.52 6.75 27.375l0.0937 0.0937c19.862 20.02 48.023 30.265 75.875 37.5 41.373 10.675 85.409 6.8178 128.31 1.0625v-0.0312c28.981-4.768 58.19-10.111 82.5-24.812-3e-5 -0.0104-3e-5 -0.0208 0-0.0312 4.5579-2.7227 8.8864-6.5506 11.625-10.781s3.9643-8.6335 3.0312-13.531c-0.0253-0.1242-0.0461-0.24931-0.0625-0.375-3.0304-25.717-17.046-49.326-30.906-72.375-0.0239-0.0398-0.0386-0.0852-0.0625-0.125-26.805-42.168-58.009-81.435-89.844-120.41-0.75007-0.90889-1.1862-2.072-1.2188-3.25-0.64083-27.08-1.2331-6.1494-1.8438-33.219z" fill="url(#p)"/>
69 <path d="m308.76 387.93c-15.75 1.6761-28.556 1.9621-44.482-1.3589 0.21917 26.636 0.31563 3.3544 0.52303 29.996 5e-3 1.284-0.34845 2.5647-0.97395 3.533-20.341 31.85-39.222 65.266-57.092 99.798-13.034 25.182-27.423 49.233-33.161 77.569l-0.0232 0.0625c-2.0508 9.7065-0.27021 20.53 5.0089 27.388l0.0696 0.0937c11.203 12.958 20.695 21.066 48.728 28.023s68.254 7.0598 102.79 2.5782c20.824-2.7021 47.44-9.1506 61.22-16.876-3e-5 -0.0104-3e-5 -0.0208 0-0.0312 3.3822-2.724 6.5943-6.5538 8.6265-10.786s2.9418-8.6377 2.2494-13.538c-0.0188-0.12426-0.0342-0.24943-0.0464-0.37518-2.2487-25.729-12.649-49.35-22.934-72.41-0.0178-0.0398-0.0286-0.0852-0.0464-0.12506-19.891-42.189-43.047-81.475-66.67-120.46-0.5566-0.90934-0.88028-2.073-0.90439-3.2516-0.47553-27.093 0.0951-3.1219-0.35803-30.204z" fill="url(#d)" filter="url(#j)"/>
70 <path transform="matrix(1 0 0 .9375 0 20.254)" d="m324.07 315.36c0 4.8113-15.991 8.7116-35.718 8.7116s-35.718-3.9003-35.718-8.7116 15.991-8.7116 35.718-8.7116 35.718 3.9003 35.718 8.7116z" fill="url(#c)"/>
71 <path transform="matrix(1 0 0 1.087 0 -51.618)" d="m433.86 608.91c0 25.384-64.786 45.962-144.7 45.962s-144.7-20.578-144.7-45.962 64.786-45.962 144.7-45.962 144.7 20.578 144.7 45.962z" fill="url(#b)" filter="url(#i)" opacity=".7"/>
72 <path transform="matrix(.74812 .4869 -.42145 .93332 324.55 94.283)" d="m105.06 429.6c0 15.342-4.7487 27.779-10.607 27.779s-10.607-12.437-10.607-27.779 4.7487-27.779 10.607-27.779 10.607 12.437 10.607 27.779z" fill="#fff" filter="url(#h)"/>
73 <path transform="matrix(.69501 .29687 -.29983 .73496 329.84 101.99)" d="m105.06 429.6c0 15.342-4.7487 27.779-10.607 27.779s-10.607-12.437-10.607-27.779 4.7487-27.779 10.607-27.779 10.607 12.437 10.607 27.779z" fill="#fff" filter="url(#k)"/>
74 <path d="m293.58 299.25c4.5514 0.12881 9.3278 0.24858 13.379 0.77697 5.2851 0.68931 10.077 1.7034 14.201 3.0024s7.6027 2.8509 10.281 4.932 4.6532 4.9568 4.3302 8.2969-2.8562 6.2388-5.9368 8.3199-6.8597 3.633-11.235 4.932c-8.7499 2.598-19.953 4.0562-32.144 4.0562s-23.083-1.4582-31.33-4.0562c-4.1238-1.299-7.6317-2.8509-10.31-4.932s-4.6509-4.9799-4.328-8.3199 2.8539-6.2158 5.9346-8.2969 6.8887-3.633 11.264-4.932c6.6932-1.9873 14.805-3.3077 23.705-3.8187 2.7349-0.15701-1.2073-0.23758 1.6582-0.23758l0.0765 9.2646c-3.7487 0.11199-7.3905 0.29917-9.7411 0.60179-4.7649 0.61344-9.0159 1.4835-12.472 2.5098s-6.0905 2.2331-7.5611 3.2266-1.6214 1.4742-1.6415 1.6824 0.0354 0.71198 1.3139 1.7055 3.6792 2.2003 6.9372 3.2266c6.5161 2.0526 16.331 3.4801 27.355 3.4801s21.144-1.4275 28.057-3.4801c3.4565-1.0263 6.0905-2.2331 7.5612-3.2266s1.5946-1.4972 1.6147-1.7055-9e-3 -0.68892-1.2872-1.6824-3.6792-2.2002-6.9372-3.2266-7.348-1.8963-12.002-2.5098-5.0792-0.75252-10.591-0.75252z" fill="url(#a)" filter="url(#g)" opacity=".64751"/>
75 <path d="m257.31 330.38c17.886 5.8187 39.891 3.5219 62.41-1.0835l0.44026 55.295c-21.953 6.8399-42.524 6.0827-62.156 0.15625z" fill="url(#o)" opacity=".49808"/>
76 <path d="m286.61 386.36h0.43558v3.0491h-0.43558z" fill="#241b1b" opacity=".64751"/>
77 <path d="m290.1 385.92h0.43558v3.4846h-0.43558z" fill="#241b1b" opacity=".64751"/>
78 <path d="m317.86 382.77c0 3.7423-12.687 6.776-28.336 6.776s-28.336-3.0337-28.336-6.776 12.687-6.776 28.336-6.776 28.336 3.0337 28.336 6.776z" fill="#135f9b" opacity=".68199"/>
79 </g>
80 <path transform="matrix(1.9941 0 0 2.091 -288.72 -517.12)" d="m433.86 608.91c0 25.384-64.786 45.962-144.7 45.962s-144.7-20.578-144.7-45.962 64.786-45.962 144.7-45.962 144.7 20.578 144.7 45.962z" filter="url(#l)"/>
81 <path d="m343.96 316.59c-36.488 11.107-76.596 11.386-113.2 0.27351 0.53792 46.603 1.0846 9.1828 1.5936 55.796 0.0109 2.2465-0.85524 4.4873-2.3904 6.1814-49.924 55.725-96.267 114.19-140.13 174.61-31.99 44.059-67.307 86.139-81.389 135.72l-0.05682 0.1094c-5.0334 16.983-0.66321 35.921 12.294 47.919 0.05755 0.0539 0.11456 0.10871 0.17066 0.16402 36.175 35.044 87.464 52.978 138.19 65.643 75.353 18.686 155.55 11.934 233.69 1.8599v-0.0546c52.783-8.3462 105.98-17.699 150.26-43.434-5e-5 -0.0182-5e-5 -0.0364 0-0.0546 8.3013-4.766 16.185-11.467 21.173-18.872s7.2202-15.113 5.5208-23.686c-0.0461-0.21741-0.084-0.43641-0.11383-0.65643-5.5192-45.016-31.045-86.344-56.289-126.69-0.0435-0.0697-0.0703-0.14914-0.11383-0.21881-48.821-73.814-105.65-142.55-163.63-210.77-1.3661-1.591-2.1605-3.627-2.2197-5.689-1.1671-47.402-2.2458-10.764-3.358-58.149z" fill="url(#m)"/>
82 <path transform="matrix(1.8213 0 0 1.7505 -239.14 -356.63)" d="m308.76 387.93c-15.75 1.6761-28.556 1.9621-44.482-1.3589 0.21917 26.636 0.31563 3.3544 0.52303 29.996 5e-3 1.284-0.34845 2.5647-0.97395 3.533-20.341 31.85-39.222 65.266-57.092 99.798-13.034 25.182-27.423 49.233-33.161 77.569l-0.0232 0.0625c-2.0508 9.7065-0.27021 20.53 5.0089 27.388l0.0696 0.0937c11.203 12.958 20.695 21.066 48.728 28.023s68.254 7.0598 102.79 2.5782c20.824-2.7021 47.44-9.1506 61.22-16.876-3e-5 -0.0104-3e-5 -0.0208 0-0.0312 3.3822-2.724 6.5943-6.5538 8.6265-10.786s2.9418-8.6377 2.2494-13.538c-0.0188-0.12426-0.0342-0.24943-0.0464-0.37518-2.2487-25.729-12.649-49.35-22.934-72.41-0.0178-0.0398-0.0286-0.0852-0.0464-0.12506-19.891-42.189-43.047-81.475-66.67-120.46-0.5566-0.90934-0.88028-2.073-0.90439-3.2516-0.47553-27.093 0.0951-3.1219-0.35803-30.204z" fill="url(#d)" filter="url(#j)"/>
83 <path transform="matrix(1.8213 0 0 1.6411 -239.14 -321.18)" d="m324.07 315.36c0 4.8113-15.991 8.7116-35.718 8.7116s-35.718-3.9003-35.718-8.7116 15.991-8.7116 35.718-8.7116 35.718 3.9003 35.718 8.7116z" fill="url(#c)"/>
84 <path transform="matrix(1.8213 0 0 1.9027 -239.14 -446.99)" d="m433.86 608.91c0 25.384-64.786 45.962-144.7 45.962s-144.7-20.578-144.7-45.962 64.786-45.962 144.7-45.962 144.7 20.578 144.7 45.962z" fill="url(#b)" filter="url(#i)" opacity=".7"/>
85 <path transform="matrix(1.3625 .8523 -.76759 1.6338 351.96 -191.6)" d="m105.06 429.6c0 15.342-4.7487 27.779-10.607 27.779s-10.607-12.437-10.607-27.779 4.7487-27.779 10.607-27.779 10.607 12.437 10.607 27.779z" fill="#fff" filter="url(#h)"/>
86 <path transform="matrix(1.2658 .51966 -.54607 1.2865 361.6 -178.11)" d="m105.06 429.6c0 15.342-4.7487 27.779-10.607 27.779s-10.607-12.437-10.607-27.779 4.7487-27.779 10.607-27.779 10.607 12.437 10.607 27.779z" fill="#fff" filter="url(#k)"/>
87 <path d="m282.86 319.68h0.79332v5.3373h-0.79332z" fill="#241b1b" opacity=".64751"/>
88 <path d="m289.21 318.92h0.79332v6.0998h-0.79332z" fill="#241b1b" opacity=".64751"/>
89 <path d="m229.49 221.69c32.576 10.186 72.653 6.165 113.67-1.8966l0.80184 96.793c-39.983 11.973-77.45 10.648-113.2 0.27351z" fill="url(#n)" opacity=".49808"/>
90 <path d="m314.53 88.096c11.175-7.4188 26.676-9.6276 40.922-9.6276 20.515 0 42.424 0.67751 63.119 1.3129-14.157 12.706-20.02 32.833-20.603 60.884s7.4772 46.002 19.522 56.234 26.603 12.035 36.71 12.035c9.5446 0 23.331-0.79894 35.231-9.8464s20.303-25.487 22.083-52.296c1.8812-28.327-6.4708-49.691-19.01-63.838 12.054 0.0088 22.878-0.32242 30.962 0.43762 11.434 1.0751 18.465 3.5429 26.636 14.168 6.5038 8.4571 10.278 28.096 11.099 50.764s-0.56916 48.252-0.56916 70.183h18.213c0-21.201 1.4303-46.992 0.56916-70.785s-3.2322-45.719-14.684-60.61c-10.902-14.176-25.129-19.764-39.499-21.115s-29.235 0.31775-46.898-0.71113c-4.6471-0.27069-8.9502-0.18951-13.261-0.10941-4.37-1.2458-8.7613-2.0787-13.091-2.0787-25.276 0-63.498-2.1334-96.529-2.1334-16.045 1.8e-5 -35.249 2.0482-51.281 12.691s-27.971 33.041-27.875 62.294l-0.30018 580.36c6.7459 3.5107 13.492 6.3162 20.238 0l-0.41466-580.94c0.37865-26.774 7.5356-39.862 18.711-47.281zm147.01-5.525h0.0568c15.896 5.5007 34.337 24.301 31.759 63.127-1.5853 23.871-8.3043 34.419-15.196 39.659s-15.555 6.072-23.961 6.072c-7.843 0-17.341-1.4545-24.644-7.6583s-13.892-17.899-13.375-42.723c0.53821-25.865 5.9461-40.522 14.798-48.466 6.639-5.9586 16.402-9.3 30.564-10.011z" fill="url(#r)" stroke="#000" stroke-opacity=".51587" stroke-width=".53566"/>
91 <path transform="matrix(1.8213 0 0 1.7505 -239.14 -356.63)" d="m293.58 299.25c4.5514 0.12881 9.3278 0.24858 13.379 0.77697 5.2851 0.68931 10.077 1.7034 14.201 3.0024s7.6027 2.8509 10.281 4.932 4.6532 4.9568 4.3302 8.2969-2.8562 6.2388-5.9368 8.3199-6.8597 3.633-11.235 4.932c-8.7499 2.598-19.953 4.0562-32.144 4.0562s-23.083-1.4582-31.33-4.0562c-4.1238-1.299-7.6317-2.8509-10.31-4.932s-4.6509-4.9799-4.328-8.3199 2.8539-6.2158 5.9346-8.2969 6.8887-3.633 11.264-4.932c6.6932-1.9873 14.805-3.3077 23.705-3.8187 2.7349-0.15701-1.2073-0.23758 1.6582-0.23758l0.0765 9.2646c-3.7487 0.11199-7.3905 0.29917-9.7411 0.60179-4.7649 0.61344-9.0159 1.4835-12.472 2.5098s-6.0905 2.2331-7.5611 3.2266-1.6214 1.4742-1.6415 1.6824 0.0354 0.71198 1.3139 1.7055 3.6792 2.2003 6.9372 3.2266c6.5161 2.0526 16.331 3.4801 27.355 3.4801s21.144-1.4275 28.057-3.4801c3.4565-1.0263 6.0905-2.2331 7.5612-3.2266s1.5946-1.4972 1.6147-1.7055-9e-3 -0.68892-1.2872-1.6824-3.6792-2.2002-6.9372-3.2266-7.348-1.8963-12.002-2.5098-5.0792-0.75252-10.591-0.75252z" fill="url(#a)" filter="url(#g)" opacity=".64751"/>
92 <path transform="matrix(1.8213 0 0 1.7505 -239.14 -356.63)" d="m317.86 382.77c0 3.7423-12.687 6.776-28.336 6.776s-28.336-3.0337-28.336-6.776 12.687-6.776 28.336-6.776 28.336 3.0337 28.336 6.776z" fill="#135f9b" opacity=".68199"/>
93 </g>
94 </svg>
0
1 Changelog
2 =========
3
4 .. include:: ../../CHANGES.rst
0 # -*- coding: utf-8 -*-
1 #
2 # PySMI documentation build configuration file, created by
3 # sphinx-quickstart on Sat Jun 27 23:15:54 2015.
4 #
5 # This file is execfile()d with the current directory set to its
6 # containing dir.
7 #
8 # Note that not all possible configuration values are present in this
9 # autogenerated file.
10 #
11 # All configuration values have a default; values that are commented out
12 # serve to show the default.
13
14 import sys
15 import os
16 import shlex
17
18 # If extensions (or modules to document with autodoc) are in another directory,
19 # add these directories to sys.path here. If the directory is relative to the
20 # documentation root, use os.path.abspath to make it absolute, like shown here.
21 #sys.path.insert(0, os.path.abspath('.'))
22
23 # -- General configuration ------------------------------------------------
24
25 # If your documentation needs a minimal Sphinx version, state it here.
26 #needs_sphinx = '1.0'
27
28 # Add any Sphinx extension module names here, as strings. They can be
29 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
30 # ones.
31 extensions = [
32 'sphinx.ext.autodoc',
33 'sphinx.ext.napoleon',
34 'sphinx.ext.doctest',
35 'sphinx.ext.intersphinx',
36 'sphinx.ext.todo'
37 ]
38
39 # Add any paths that contain templates here, relative to this directory.
40 templates_path = ['.templates']
41
42 # The suffix(es) of source filenames.
43 # You can specify multiple suffix as a list of string:
44 # source_suffix = ['.rst', '.md']
45 source_suffix = '.rst'
46
47 # The encoding of source files.
48 #source_encoding = 'utf-8-sig'
49
50 # The master toctree document.
51 master_doc = 'contents'
52
53 # General information about the project.
54 project = u'SNMP SMI compiler'
55 copyright = u'2015-2017, Ilya Etingof <etingof@gmail.com>'
56 author = u'Ilya Etingof <etingof@gmail.com>'
57
58 # The version info for the project you're documenting, acts as replacement for
59 # |version| and |release|, also used in various other places throughout the
60 # built documents.
61 #
62 # The short X.Y version.
63 version = '0.1'
64 # The full version, including alpha/beta/rc tags.
65 release = '0.1'
66
67 # The language for content autogenerated by Sphinx. Refer to documentation
68 # for a list of supported languages.
69 #
70 # This is also used if you do content translation via gettext catalogs.
71 # Usually you set "language" from the command line for these cases.
72 language = None
73
74 # There are two options for replacing |today|: either, you set today to some
75 # non-false value, then it is used:
76 #today = ''
77 # Else, today_fmt is used as the format for a strftime call.
78 #today_fmt = '%B %d, %Y'
79
80 # List of patterns, relative to source directory, that match files and
81 # directories to ignore when looking for source files.
82 exclude_patterns = []
83
84 # The reST default role (used for this markup: `text`) to use for all
85 # documents.
86 #default_role = None
87
88 # If true, '()' will be appended to :func: etc. cross-reference text.
89 #add_function_parentheses = True
90
91 # If true, the current module name will be prepended to all description
92 # unit titles (such as .. function::).
93 #add_module_names = True
94
95 # If true, sectionauthor and moduleauthor directives will be shown in the
96 # output. They are ignored by default.
97 #show_authors = False
98
99 # The name of the Pygments (syntax highlighting) style to use.
100 pygments_style = 'sphinx'
101
102 # A list of ignored prefixes for module index sorting.
103 #modindex_common_prefix = []
104
105 # If true, keep warnings as "system message" paragraphs in the built documents.
106 #keep_warnings = False
107
108 # If true, `todo` and `todoList` produce output, else they produce nothing.
109 todo_include_todos = True
110
111
112 # -- Options for HTML output ----------------------------------------------
113
114 # The theme to use for HTML and HTML Help pages. See the documentation for
115 # a list of builtin themes.
116 html_theme = 'alabaster'
117
118 # Theme options are theme-specific and customize the look and feel of a theme
119 # further. For a list of options available for each theme, see the
120 # documentation.
121 #html_theme_options = { 'collapse_navigation': False }
122 html_theme_options = {
123 'logo': 'logo.svg',
124 'description': '<p align=left><i><b>Brewing free software for the greater good</i></b></p>',
125 'show_powered_by': False,
126 'github_user': 'etingof',
127 'github_repo': 'pysmi',
128 'fixed_sidebar': True,
129 }
130
131 html_sidebars = {
132 '**': [
133 'about.html',
134 'navigation.html',
135 'relations.html',
136 'searchbox.html',
137 'donate.html',
138 ]
139 }
140
141 # The name for this set of Sphinx documents. If None, it defaults to
142 # "<project> v<release> documentation".
143 #html_title = None
144
145 # A shorter title for the navigation bar. Default is the same as html_title.
146 #html_short_title = None
147
148 # The name of an image file (relative to this directory) to place at the top
149 # of the sidebar.
150 #html_logo = None
151
152 # The name of an image file (within the static path) to use as favicon of the
153 # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
154 # pixels large.
155 html_favicon = '.static/favicon.ico'
156
157 # Add any paths that contain custom static files (such as style sheets) here,
158 # relative to this directory. They are copied after the builtin static files,
159 # so a file named "default.css" will overwrite the builtin "default.css".
160 html_static_path = ['.static']
161
162 # Custom CSS theme
163 #html_style = 'css/rtdimproved.css'
164
165 # Add any extra paths that contain custom files (such as robots.txt or
166 # .htaccess) here, relative to this directory. These files are copied
167 # directly to the root of the documentation.
168 #html_extra_path = []
169
170 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
171 # using the given strftime format.
172 #html_last_updated_fmt = '%b %d, %Y'
173
174 # If true, SmartyPants will be used to convert quotes and dashes to
175 # typographically correct entities.
176 #html_use_smartypants = True
177
178 # Custom sidebar templates, maps document names to template names.
179 #html_sidebars = {}
180
181 # Additional templates that should be rendered to pages, maps page names to
182 # template names.
183 #html_additional_pages = {}
184
185 # If false, no module index is generated.
186 html_domain_indices = False
187
188 # If false, no index is generated.
189 html_use_index = False
190
191 # If true, the index is split into individual pages for each letter.
192 #html_split_index = False
193
194 # If true, links to the reST sources are added to the pages.
195 html_show_sourcelink = False
196
197 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
198 html_show_sphinx = False
199
200 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
201 #html_show_copyright = True
202
203 # If true, an OpenSearch description file will be output, and all pages will
204 # contain a <link> tag referring to it. The value of this option must be the
205 # base URL from which the finished HTML is served.
206 #html_use_opensearch = ''
207
208 # This is the file name suffix for HTML files (e.g. ".xhtml").
209 #html_file_suffix = None
210
211 # Language to be used for generating the HTML full-text search index.
212 # Sphinx supports the following languages:
213 # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
214 # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
215 #html_search_language = 'en'
216
217 # A dictionary with options for the search language support, empty by default.
218 # Now only 'ja' uses this config value
219 #html_search_options = {'type': 'default'}
220
221 # The name of a javascript file (relative to the configuration directory) that
222 # implements a search results scorer. If empty, the default will be used.
223 #html_search_scorer = 'scorer.js'
224
225 # Output file base name for HTML help builder.
226 htmlhelp_basename = 'PySMIdoc'
227
228 # -- Options for LaTeX output ---------------------------------------------
229
230 latex_elements = {
231 # The paper size ('letterpaper' or 'a4paper').
232 #'papersize': 'letterpaper',
233
234 # The font size ('10pt', '11pt' or '12pt').
235 #'pointsize': '10pt',
236
237 # Additional stuff for the LaTeX preamble.
238 #'preamble': '',
239
240 # Latex figure (float) alignment
241 #'figure_align': 'htbp',
242 }
243
244 # Grouping the document tree into LaTeX files. List of tuples
245 # (source start file, target name, title,
246 # author, documentclass [howto, manual, or own class]).
247 latex_documents = [
248 (master_doc, 'PySMI.tex', u'SNMP SMI compiler',
249 u'Ilya Etingof \\textless{}etingof@gmail.com\\textgreater{}', 'manual'),
250 ]
251
252 # The name of an image file (relative to this directory) to place at the top of
253 # the title page.
254 #latex_logo = None
255
256 # For "manual" documents, if this is true, then toplevel headings are parts,
257 # not chapters.
258 #latex_use_parts = False
259
260 # If true, show page references after internal links.
261 #latex_show_pagerefs = False
262
263 # If true, show URL addresses after external links.
264 #latex_show_urls = False
265
266 # Documents to append as an appendix to all manuals.
267 #latex_appendices = []
268
269 # If false, no module index is generated.
270 #latex_domain_indices = True
271
272
273 # -- Options for manual page output ---------------------------------------
274
275 # One entry per manual page. List of tuples
276 # (source start file, name, description, authors, manual section).
277 man_pages = [
278 (master_doc, 'pysmi', u'SNMP SMI compiler',
279 [author], 1)
280 ]
281
282 # If true, show URL addresses after external links.
283 #man_show_urls = False
284
285
286 # -- Options for Texinfo output -------------------------------------------
287
288 # Grouping the document tree into Texinfo files. List of tuples
289 # (source start file, target name, title, author,
290 # dir menu entry, description, category)
291 texinfo_documents = [
292 (master_doc, 'PySMI', u'SNMP SMI compiler',
293 author, 'PySMI', 'One line description of project.',
294 'Miscellaneous'),
295 ]
296
297 # Documents to append as an appendix to all manuals.
298 #texinfo_appendices = []
299
300 # If false, no module index is generated.
301 #texinfo_domain_indices = True
302
303 # How to display URL addresses: 'footnote', 'no', or 'inline'.
304 #texinfo_show_urls = 'footnote'
305
306 # If true, do not generate a @detailmenu in the "Top" node's menu.
307 #texinfo_no_detailmenu = False
308
309
310 # Example configuration for intersphinx: refer to the Python standard library.
311 intersphinx_mapping = {'https://docs.python.org/': None}
312
313 # this merges constructor docstring with class docstring
314 autoclass_content = 'both'
315
316 # Napoleon settings
317 napoleon_google_docstring = True
318 napoleon_numpy_docstring = False
319 napoleon_include_private_with_doc = False
320 napoleon_include_special_with_doc = True
321 napoleon_use_admonition_for_examples = False
322 napoleon_use_admonition_for_notes = False
323 napoleon_use_admonition_for_references = False
324 napoleon_use_ivar = False
325 napoleon_use_param = False
326 napoleon_use_rtype = False
0
1 SNMP SMI compiler
2 =================
3
4 .. toctree::
5 :maxdepth: 2
6
7 The PySMI library and tools are designed to parse, verify and transform
8 `SNMP SMI <https://en.wikipedia.org/wiki/Management_information_base>`_ MIB
9 modules from their original ASN.1 form into JSON or `pysnmp <http://snmplabs.com/pysnmp/>`_
10 representation.
11
12 Documentation
13 -------------
14
15 .. toctree::
16 :maxdepth: 2
17
18 /documentation
19
20 Source code & Changelog
21 -----------------------
22
23 Project source code is hosted at `GitHub <https://github.com/etingof/pysmi>`_.
24 Everyone is welcome to fork and contribute back!
25
26 We maintain the detailed :doc:`log of changes </changelog>` to our software.
27
28 Download & Install
29 ------------------
30
31 .. toctree::
32 :maxdepth: 2
33
34 /download
35
36 Changes
37 -------
38
39 .. toctree::
40 :maxdepth: 1
41
42 /changelog
43
44 License
45 -------
46
47 The SNMP SMI library software is distributed under 2-clause BSD License.
48
49 .. toctree::
50 :maxdepth: 2
51
52 /license
53
54 MIB files archive
55 -----------------
56
57 The PySMI project maintains a `collection <http://mibs.snmplabs.com/asn1/>`_
58 of publicly available ASN.1 MIB files collected on the Internet. You are
59 welcome to use this MIBs archive however we can't guarantee any degree
60 of consistency or reliability when it comes to these MIB modules.
61
62 The *mibdump.py* tool as well as many other utilities based on PySMI
63 are programmed to use this MIB repository for automatic download and
64 dependency resolution.
65
66 You can always reconfigure PySMI to use some other remote MIB repository
67 instead or in addition to this one.
68
69 Contact
70 -------
71
72 In case of questions or troubles using SNMP SMI library, please open up an
73 `issue <https://github.com/etingof/pysmi/issues>`_ at GitHub or ask at
74 `Stack Overflow <http://stackoverflow.com/questions/tagged/pysmi>`_ .
0
1 PySMI documentation
2 ===================
3
4 PySMI library is highly modular. The top-level component is called
5 *compiler* and it acts as main user-facing object. Most of other
6 components are plugged into the *compiler* object prior to its use.
7
8 Normally, user asks *compiler* to perform certain transformation of
9 named MIB module. Compiler will:
10
11 * Search its data sources for given MIB module (identified by name)
12 noting their last modification times.
13 * Search compiler-managed repositories of already converted MIB modules
14 for modules that are more recent than corresponding source MIB module.
15 * If freshly transformed MIB module is found, processing stops here.
16 * Otherwise compiler passes ASN.1 MIB module content to the *lexer*
17 component.
18 * Lexer returns a sequence of tokenized ASN.1 MIB contents. Compiler
19 then passes that sequence of tokens to the *parser* component.
20 * Parser runs LR algorithm on tokenized MIB thus transforming MIB
21 contents into Abstract Syntax Tree (AST) and also noting what other
22 MIB modules are referred to from the MIB being parsed.
23 * In case of parser failure, what is usually an indication of broken
24 ASN.1 MIB syntax, compiler may attempt to fetch pre-transformed MIB
25 contents from configured source. This process is called *borrowing*
26 in PySMI.
27 * In case of successful parser completion, compiler will pass produced
28 AST to *code generator* component.
29 * Code generator walks its input AST and performs actual data
30 transformation.
31 * The above steps may be repeated for each of the MIB modules referred
32 to as parser figures out. Once no more unresolved dependencies remain,
33 compiler will call its *writer* component to store all transformed MIB
34 modules.
35
36 The location of ASN.1 MIB modules and flavor of their syntax, as well as
37 desired transformation format, is determined by respective components
38 chosen and configured to compiler.
39
40 .. toctree::
41 :maxdepth: 2
42
43 /mibdump
44 /library-reference
0
1 Download & Install
2 ==================
3
4 The best way to obtain SNMP SMI library is by running `pip`:
5
6 .. code-block:: bash
7
8 $ virtualenv venv
9 $ source venv/bin/activate
10 $ pip install pysmi
11
12 Alternatively, you can download the latest release from
13 `GitHub <https://github.com/etingof/pysmi/releases>`_
14 or `PyPI <https://pypi.python.org/pypi/pysmi>`_.
0
1 .. include:: /../../examples/always-borrow-precompiled-pysnmp-files.py
2 :start-after: """
3 :end-before: """#
4
5 .. literalinclude:: /../../examples/always-borrow-precompiled-pysnmp-files.py
6 :start-after: """#
7 :language: python
8
9 :download:`Download</../../examples/always-borrow-precompiled-pysnmp-files.py>` script.
10
0
1 .. include:: /../../examples/borrow-precompiled-pysnmp-files-on-failure.py
2 :start-after: """
3 :end-before: """#
4
5 .. literalinclude:: /../../examples/borrow-precompiled-pysnmp-files-on-failure.py
6 :start-after: """#
7 :language: python
8
9 :download:`Download</../../examples/borrow-precompiled-pysnmp-files-on-failure.py>` script.
10
0
1 .. include:: /../../examples/compile-smistar-mibs-into-pysnmp-files-if-needed.py
2 :start-after: """
3 :end-before: """#
4
5 .. literalinclude:: /../../examples/compile-smistar-mibs-into-pysnmp-files-if-needed.py
6 :start-after: """#
7 :language: python
8
9 :download:`Download</../../examples/compile-smistar-mibs-into-pysnmp-files-if-needed.py>` script.
10
0
1 .. include:: /../../examples/compile-smiv2-mibs-from-text-into-pysnmp-code.py
2 :start-after: """
3 :end-before: """#
4
5 .. literalinclude:: /../../examples/compile-smiv2-mibs-from-text-into-pysnmp-code.py
6 :start-after: """#
7 :language: python
8
9 :download:`Download</../../examples/compile-smiv2-mibs-from-text-into-pysnmp-code.py>` script.
10
0
1 .. include:: /../../examples/download-and-compile-smistar-mibs-into-json.py
2 :start-after: """
3 :end-before: """#
4
5 .. literalinclude:: /../../examples/download-and-compile-smistar-mibs-into-json.py
6 :start-after: """#
7 :language: python
8
9 :download:`Download</../../examples/download-and-compile-smistar-mibs-into-json.py>` script.
10
0
1 .. include:: /../../examples/download-and-compile-smistar-mibs-into-pysnmp-files.py
2 :start-after: """
3 :end-before: """#
4
5 .. literalinclude:: /../../examples/download-and-compile-smistar-mibs-into-pysnmp-files.py
6 :start-after: """#
7 :language: python
8
9 :download:`Download</../../examples/download-and-compile-smistar-mibs-into-pysnmp-files.py>` script.
10
0
1 PySMI library
2 =============
3
4 The *MibCompiler* object is the top-most interface to PySMI library features.
5 It holds together the otherwise isolated pieces of the compiler infrastructure
6 and manages the workflow of ASN.1 MIB transformation.
7
8 This example showcases some of its features:
9
10 .. code-block:: python
11
12 from pysmi.reader import HttpReader
13 from pysmi.searcher import StubSearcher
14 from pysmi.writer import CallbackWriter
15 from pysmi.parser import SmiStarParser
16 from pysmi.codegen import JsonCodeGen
17 from pysmi.compiler import MibCompiler
18
19 inputMibs = ['IF-MIB', 'IP-MIB']
20
21 httpSources = [('mibs.snmplabs.com', 80, '/asn1/@mib@')]
22
23 # store compiled MIBs by calling this function
24 def store_mibs(mibName, jsonDoc, cbCtx):
25 print('# MIB module %s' % mibName)
26 print(jsonDoc)
27
28 mibCompiler = MibCompiler(
29 SmiStarParser(), JsonCodeGen(), CallbackWriter(store_mibs)
30 )
31
32 # pull ASN.1 MIBs over HTTP
33 mibCompiler.addSources(*[HttpReader(*x) for x in httpSources])
34
35 # never recompile MIBs with ASN.1 MACROs
36 mibCompiler.addSearchers(StubSearcher(*JsonCodeGen.baseMibs))
37
38 status = mibCompiler.compile(*inputMibs)
39
40 print(status)
41
42 .. toctree::
43 :maxdepth: 2
44
45 /pysmi/compiler/mibcompiler
46 /pysmi/compiler/mibstatus
47
48 MIB sources
49 -----------
50
51 PySMI offers a handful of distinct transport mechanisms for fetching MIBs by
52 name from specific locations. In all cases MIB module name to file name match
53 may not be exact -- some name fuzzying can be performed to mitigate
54 possible changes to MIB file name.
55
56 .. toctree::
57 :maxdepth: 2
58
59 /pysmi/reader/localfile/filereader
60 /pysmi/reader/zipreader/zipreader
61 /pysmi/reader/httpclient/httpreader
62 /pysmi/reader/ftpclient/ftpreader
63 /pysmi/reader/callback/callbackreader
64
65 Conditional compilation
66 -----------------------
67
68 There are cases when MIB transformation may or must not be performed.
69 Such cases include:
70
71 * foundation MIBs containing manually implemented pieces or ASN.1 MACRO's
72 * obsolete MIBs fully reimplemented within modern MIBs
73 * already transformed MIBs
74
75 :ref:`MibCompiler <compiler.MibCompiler>` expects user to supply a
76 *searcher* object that would allow or skip MIB transformation for particular
77 name based on whatever reason it is aware of.
78
79 In general, *searcher* logic is specific to target format. At the time being,
80 only `pysnmp <http://snmplabs.com/pysnmp>`_ code generation backend requires
81 such filtering.
82
83 .. toctree::
84 :maxdepth: 2
85
86 /pysmi/searcher/pyfile/pyfilesearcher
87 /pysmi/searcher/pypackage/pypackagesearcher
88 /pysmi/searcher/stub/stubsearcher
89
90 Parser configuration
91 --------------------
92
93 MIBs may be written in one of the two major SMI language versions (v1 and v2).
94 Some MIBs may contain typical errors.
95
96 PySMI offers a way to customize the parser to consume either of the major SMI
97 grammars as well as to recover from well-known errors in MIB files.
98
99 .. toctree::
100 :maxdepth: 2
101
102 /pysmi/parser/smi/parserfactory
103 /pysmi/parser/smi/dialect
104
105 Code generators
106 ---------------
107
108 Once ASN.1 MIB is parsed up, AST is passed to a code generator which turns
109 AST into desired representation of the MIB.
110
111 .. toctree::
112 :maxdepth: 2
113
114 /pysmi/codegen/jsondoc/jsoncodegen
115 /pysmi/codegen/pysnmp/pysnmpcodegen
116 /pysmi/codegen/null/nullcodegen
117
118 Borrow pre-compiled MIBs
119 ------------------------
120
121 Some MIBs in circulation appear broken beyond automatic repair. To
122 handle such cases PySMI introduces the *MIB borrowing*
123 functionality. When :ref:`MibCompiler <compiler.MibCompiler>`
124 gives up compiling a MIB, it can try to go out and take a copy of
125 already transformed MIB to complete the request successfully.
126
127 .. toctree::
128 :maxdepth: 2
129
130 /pysmi/borrower/anyfile/anyfileborrower
131 /pysmi/borrower/pyfile/pyfileborrower
132
133 Write compiled MIBs
134 -------------------
135
136 Successfully transformed MIB modules' contents will be passed to *writer*
137 object given to :ref:`MibCompiler <compiler.MibCompiler>` on instantiation.
138
139 .. toctree::
140 :maxdepth: 2
141
142 /pysmi/writer/localfile/filewriter
143 /pysmi/writer/pyfile/pyfilewriter
144 /pysmi/writer/callback/callbackwriter
145
146 Examples
147 --------
148
149 The following examples focus on various feature of the PySMI library.
150
151 .. toctree::
152 :maxdepth: 2
153
154 /examples/download-and-compile-smistar-mibs-into-json.rst
155 /examples/download-and-compile-smistar-mibs-into-pysnmp-files.rst
156 /examples/compile-smistar-mibs-into-pysnmp-files-if-needed.rst
157 /examples/compile-smiv2-mibs-from-text-into-pysnmp-code.rst
158 /examples/borrow-precompiled-pysnmp-files-on-failure.rst
159 /examples/always-borrow-precompiled-pysnmp-files.rst
160
161 In case of any troubles or confusion, try enabling PySMI debugging
162 and watch the output:
163
164 .. code-block:: python
165
166 from pysmi import debug
167
168 debug.setLogger(debug.Debug('all'))
169
0
1 License
2 =======
3
4 .. include:: ../../LICENSE.rst
0
1 The *mibdump* tool
2 ==================
3
4 .. toctree::
5 :maxdepth: 2
6
7 The *mibdump.py* tool is a command-line frontend to the PySMI library. This
8 tool can be used for automatic downloading and transforming SNMP MIB modules
9 into various formats.
10
11 .. code-block:: bash
12
13 $ mibdump.py --help
14 Synopsis:
15 SNMP SMI/MIB files conversion tool
16 Documentation:
17 http://pysmi.sourceforge.net
18 Usage: mibdump.py [--help]
19 [--version]
20 [--quiet]
21 [--debug=<all|borrower|codegen|compiler|grammar|lexer|parser|reader|searcher|writer>]
22 [--mib-source=<URI>]
23 [--mib-searcher=<PATH|PACKAGE>]
24 [--mib-stub=<MIB-NAME>]
25 [--mib-borrower=<PATH>]
26 [--destination-format=<FORMAT>]
27 [--destination-directory=<DIRECTORY>]
28 [--cache-directory=<DIRECTORY>]
29 [--disable-fuzzy-source]
30 [--no-dependencies]
31 [--no-python-compile]
32 [--python-optimization-level]
33 [--ignore-errors]
34 [--build-index]
35 [--rebuild]
36 [--dry-run]
37 [--no-mib-writes]
38 [--generate-mib-texts]
39 [--keep-texts-layout]
40 <MIB-NAME> [MIB-NAME [...]]]
41 Where:
42 URI - file, zip, http, https, ftp, sftp schemes are supported.
43 Use @mib@ placeholder token in URI to refer directly to
44 the required MIB module when source does not support
45 directory listing (e.g. HTTP).
46 FORMAT - pysnmp, json, null
47
48
49 When JSON destination format is requested, for each MIB module *mibdump.py*
50 will produce a JSON document containing all MIB objects. For example,
51 `IF-MIB <http://mibs.snmplabs.com/asn1/IF-MIB>`_ module in JSON form
52 would look like:
53
54 .. code-block:: python
55
56 {
57 "ifMIB": {
58 "name": "ifMIB",
59 "oid": "1.3.6.1.2.1.31",
60 "class": "moduleidentity",
61 "revisions": [
62 "2007-02-15 00:00",
63 "1996-02-28 21:55",
64 "1993-11-08 21:55"
65 ]
66 },
67
68 ...
69 "ifTestTable": {
70 "name": "ifTestTable",
71 "oid": "1.3.6.1.2.1.31.1.3",
72 "class": "objecttype",
73 "maxaccess": "not-accessible"
74 },
75 "ifTestEntry": {
76 "name": "ifTestEntry",
77 "oid": "1.3.6.1.2.1.31.1.3.1",
78 "class": "objecttype",
79 "maxaccess": "not-accessible",
80 "augmention": {
81 "name": "ifTestEntry",
82 "module": "IF-MIB",
83 "object": "ifEntry"
84 }
85 },
86 "ifTestId": {
87 "name": "ifTestId",
88 "oid": "1.3.6.1.2.1.31.1.3.1.1",
89 "class": "objecttype",
90 "syntax": {
91 "type": "TestAndIncr",
92 "class": "type"
93 },
94 "maxaccess": "read-write"
95 },
96 ...
97 }
98
99 In general, JSON MIB captures all aspects of original (ASN.1) MIB contents
100 and layout. The snippet above is just an example, here is the complete
101 `IF-MIB.json <http://mibs.snmplabs.com/json/fulltext/IF-MIB.json>`_
102 file.
103
104 Specifying MIB source
105 ---------------------
106
107 The --mib-source option can be given multiple times. Each instance of
108 --mib-source must specify a URL where ASN.1 MIB modules should be
109 looked up and downloaded from. At this moment three MIB sourcing
110 methods are supported:
111
112 * Local files. This could be a top-level directory where MIB files are
113 located. Subdirectories will be automatically traversed as well.
114 Example: file:///usr/share/snmp
115 * ZIP archives containing MIB files. Subdirectories and embedded ZIP
116 archives will be automatically traversed.
117 Example: zip://mymibs.zip
118 * HTTP/HTTPS. A fully specified URL where MIB module name is specified by
119 a @mib@ placeholder. When specific MIB is looked up, PySMI will replace
120 that placeholder with MIB module name it is looking for.
121 Example: `http://mibs.snmplabs.com/asn1/@mib@ <http://mibs.snmplabs.com/asn1/>`_
122 * SFTP/FTP. A fully specified URL including FTP username and password.
123 MIB module name is specified by a @mib@ placeholder. When specific MIB
124 is looked up, PySMI will replace that placeholder with MIB module name
125 it is looking for.
126 Example: `http://mibs.snmplabs.com/asn1/@mib@ <http://mibs.snmplabs.com/asn1/>`_
127
128 When trying to fetch a MIB module, the *mibdump.py* tool will try each of
129 configured --mib-source transports in order of specification till
130 first successful hit.
131
132 By default *mibdump.py* will search:
133
134 * file:///usr/share/snmp
135 * http://mibs.snmplabs.com/asn1/@mib@
136
137 Once another --mib-source option is given, those defaults will not be used
138 and should be manually given to *mibdump.py* if needed.
139
140 Fuzzying MIB module names
141 -------------------------
142
143 There is no single convention on how MIB module files should be named. By
144 default *mibdump.py* will try a handful of guesses when trying to find a file
145 containing specific MIB module. It will try upper and lower cases, a file
146 named after MIB module, try adding different extensions to a file (.mib,
147 .my etc), try adding/cutting the '-MIB' part of the file name.
148 If nothing matches, *mibdump.py* will consider that probed --mib-source
149 does not contain MIB module it is looking for.
150
151 There is a small chance, though, that fuzzy natching may result in getting
152 a wrong MIB. If that happens, you can disable the above fuzzyness by
153 giving *mibdump.py* the --disable-fuzzy-source flag.
154
155 Avoiding excessive transformation
156 ---------------------------------
157
158 It well may happen that many MIB modules refer to a common single MIB
159 module. In that case *mibdump.py* may transform it many times unless you
160 tell *mibdump.py* where to search for already transformed MIBs. That place
161 could of course be a directory where *mibdump.py* writes its transforms into
162 and/or some other local locations.
163
164 The --mib-searcher option specifies either local directory or importable
165 Python package (applicable to pysnmp transformation) containing transformed
166 MIB modules. Multiple --mib-searcher options could be given, *mibdump.py*
167 will use each of them in order of specification till first hit.
168
169 If no transformed MIB module is found, *mibdump.py* will go on running its full
170 transformation cycle.
171
172 By default *mibdump.py* will use:
173
174 * --mib-searcher=$HOME/.pysnmp/mibs
175 * --mib-searcher=pysnmp_mibs
176
177 Once another --mib-searcher option is given, those defaults will not be used
178 and should be manually given to *mibdump.py* if needed.
179
180 Blacklisting MIBs
181 -----------------
182
183 Some MIBs may not be automatically transformed into another form and
184 therefore must be explicitly excluded from processing. Such MIBs are
185 normally manually implemented for each target MIB format. Examples
186 include MIBs containing base SMI types or ASN.1 MACRO definitions
187 (SNMPv2-SMI, SNMPV2-TC), initially compiled but later manually modified
188 MIBs and others.
189
190 Default list of blacklisted MIBs for pysnmp transformation target
191 is: RFC-1212, RFC-1215, RFC1065-SMI, RFC1155-SMI, RFC1158-MIB,
192 RFC1213-MIB, SNMP-FRAMEWORK-MIB, SNMP-TARGET-MIB, SNMPv2-CONF, SNMPv2-SMI,
193 SNMPv2-TC, SNMPv2-TM, TRANSPORT-ADDRESS-MIB.
194
195 If you need to modify this list use the --mib-stub option.
196
197 Dealing with broken MIBs
198 ------------------------
199
200 Curiously enough, some MIBs coming from quite prominent vendors
201 appear syntactically incorrect. That leads to MIB compilers fail on
202 such MIBs. While many MIB compiler implementations (PySMI included)
203 introduce workarounds and grammar relaxations allowing slightly
204 broken MIBs to compile, however severely broken MIBs can't be
205 reliably compiled.
206
207 As another workaround PySMI offers the *borrow* feature. It allows
208 PySMI to fetch already transformed MIBs even if corresponding
209 ASN.1 MIB can't be found or parsed.
210
211 Default source of pre-compiled MIBs for pysnmp target is:
212
213 * http://mibs.snmplabs.com/pysnmp/fulltexts/@mib@
214 * http://mibs.snmplabs.com/pysnmp/notexts/@mib@
215
216 If you wish to modify this default list use one or more
217 --mib-borrower options.
218
219 Choosing target transformation
220 ------------------------------
221
222 PySMI design allows many transformation formats to be
223 supported in form of specialized code generation components.
224 At the moment PySMI can produce MIBs in form of pysnmp classes
225 and JSON documents.
226
227 JSON document schema is chosen to preserve as much of MIB
228 information as possible. There's no established JSON schema
229 known to the authors.
230
231 Setting destination directory
232 -----------------------------
233
234 By default *mibdump.py* writes pysnmp MIBs into:
235
236 * $HOME/.pysnmp/mibs (on UNIX)
237 * @HOME@\PySNMP Configuration\MIBs\ (on Windows)
238
239 and JSON files in current working directory.
240
241 Use --destination-directory option to change default output
242 diretory.
243
244 Performing unconditional transformation
245 ---------------------------------------
246
247 By default PySMI will avoid creating new transformations if fresh
248 enough versions already exist. By using --rebuild option you could
249 trick PySMI doing requested transformation for all given MIB modules.
250
251 Ignoring transformation errors
252 ------------------------------
253
254 By default PySMI will stop on first fatal error occurred during
255 transformations of a series of MIBs. If you wish PySMI to ignore
256 fatal errors and therefore skipping failed MIB, use the --ignore-errors
257 option.
258
259 Keep in mind that skipping transformation of MIBs that are imported
260 by other MIBs might make dependant MIBs inconsistent for use.
261
262 Skipping dependencies
263 ---------------------
264
265 Most MIBs rely on other MIBs for their operations. This is indicated
266 by the IMPORT statement in ASN.1 language. PySMI attempts to transform
267 all MIBs IMPORT'ed by MIB being transformed. That is done in recurrsive
268 manner.
269
270 By using --no-dependencies flag you can tell PySMI not to transform any
271 MIBs other than those explicitly requested to be transformed.
272
273 Keep in mind that skipping dependencies may make the whole set of
274 transformed MIBs inconsistent.
275
276 Generating MIB texts
277 --------------------
278
279 Most MIBs are very verbose. They contain many human-oriented descriptions
280 and clarifications written in plain English. Those texts may be useful
281 for MIB browser applications (to display those texts to human operator)
282 but might not make any sense in other applications.
283
284 To save space and CPU time, PySMI does not by default include those texts
285 into transformed MIBs. However this can be reverted by adding
286 --generate-mib-texts option.
287
288 When MIB texts are generated, whitespaces and new lines are stripped by
289 default. Sometimes that breaks down ASCII art should it occur in MIB texts.
290 To preserve original text formatting, --keep-texts-layout option may
291 be used.
292
293 Building MIB indices
294 --------------------
295
296 If --build-index option is given, depending on the destination format chosen,
297 the *mibdump.py* tool may create new (or update existing) document containing
298 MIB information in a form that is convenient for querying cornerstone
299 properties of MIB files.
300
301 For example, building JSON index for
302 `IP-MIB.json <http://mibs.snmplabs.com/json/asn1/IP-MIB>`_,
303 `TCP-MIB.json <http://mibs.snmplabs.com/json/asn1/TCP-MIB>`_ and
304 `UDP-MIB.json <http://mibs.snmplabs.com/json/asn1/UDP-MIB>`_
305 MIB modules would emit something like this:
306
307 .. code-block:: json
308
309 {
310 "compliance": {
311 "1.3.6.1.2.1.48.2.1.1": [
312 "IP-MIB"
313 ],
314 "1.3.6.1.2.1.49.2.1.1": [
315 "TCP-MIB"
316 ],
317 "1.3.6.1.2.1.50.2.1.1": [
318 "UDP-MIB"
319 ]
320 },
321 "identity": {
322 "1.3.6.1.2.1.48": [
323 "IP-MIB"
324 ],
325 "1.3.6.1.2.1.49": [
326 "TCP-MIB"
327 ],
328 "1.3.6.1.2.1.50": [
329 "UDP-MIB"
330 ]
331 },
332 "oids": {
333 "1.3.6.1.2.1.4": [
334 "IP-MIB"
335 ],
336 "1.3.6.1.2.1.5": [
337 "IP-MIB"
338 ],
339 "1.3.6.1.2.1.6": [
340 "TCP-MIB"
341 ],
342 "1.3.6.1.2.1.7": [
343 "UDP-MIB"
344 ],
345 "1.3.6.1.2.1.49": [
346 "TCP-MIB"
347 ],
348 "1.3.6.1.2.1.50": [
349 "UDP-MIB"
350 ]
351 }
352 }
353
354 With this example, *compliance* and *identity* keys point to
355 *MODULE-COMPLIANCE* and *MODULE-IDENTITY* MIB objects, *oids*
356 list top-level OIDs branches defined in MIB modules. Full index
357 build over thousands of MIBs could be seen
358 `here <http://mibs.snmplabs.com/json/index.json>`_.
359
360 Minor speedups
361 --------------
362
363 There are a few options that may improve PySMI performance.
364
365 The --cache-directory option may be used to point to a temporary
366 writable directory where PySMI parser (e.g. Ply) would store its
367 lookup tables.
368
369 By default PySMI performing transformation into pysnmp format will
370 also pre-compile Python source into interpreter bytecode. That takes
371 some time and space. If you wish not to cache Python bytecode
372 or to do that later, use the --no-python-compile option.
373
0
1 .. _borrower.anyfile.AnyFileBorrower:
2
3 Any file borrower
4 -----------------
5
6 .. autoclass:: pysmi.borrower.anyfile.AnyFileBorrower
7 :members:
0
1 .. _borrower.pyfile.PyFileBorrower:
2
3 Python file borrower
4 --------------------
5
6 .. autoclass:: pysmi.borrower.pyfile.PyFileBorrower
7 :members:
0
1 .. _codegen.jsondoc.JsonCodeGen:
2
3 JSON document generator
4 -----------------------
5
6 .. autoclass:: pysmi.codegen.jsondoc.JsonCodeGen
7 :members:
0
1 .. _codegen.null.NullCodeGen:
2
3 Code generation stub
4 --------------------
5
6 .. autoclass:: pysmi.codegen.null.NullCodeGen
7 :members:
0
1 .. _codegen.pysnmp.PySnmpCodeGen:
2
3 PySNMP MIB generator
4 --------------------
5
6 .. autoclass:: pysmi.codegen.pysnmp.PySnmpCodeGen
7 :members:
0
1 .. _compiler.MibCompiler:
2
3 MIB compiler
4 ------------
5
6 .. autoclass:: pysmi.compiler.MibCompiler
7 :members:
0
1 .. _reader.compiler.MibStatus:
2
3 Compilation status
4 ------------------
5
6 *MibStatus* class instance is used by :func:`MibCompiler.compiler` to
7 indicate the outcome of MIB transformation operation.
8
9 .. autoclass:: pysmi.compiler.MibStatus
10 :members:
0
1 .. _parser.smi.dialect:
2
3 SMI language dialects
4 ---------------------
5
6 PySMI offers a pre-built collection of parser grammar relaxation options
7 to simplify its use:
8
9 * *pysmi.parser.dialect.smiV2* - canonical SMIv2 grammar
10 * *pysmi.parser.dialect.smiV1* - canonical SMIv1 grammar
11 * *pysmi.parser.dialect.smiV1Relaxed* - relaxed SMIv1 grammar allowing some deviations
12
13 The grammar object should be passed to the :ref:`parserFactory <parser.smi.parserFactory>` function.
14
15 .. code-block:: python
16
17 from pysmi.parser.dialect import smiV1
18 from pysmi.parser.smi import parserFactory
19
20 SmiV1Parser = parserFactory(**smiV1)
21
22 Apparently, many production MIBs were shipped in syntactically broken
23 condition. PySMI attempts to work around such issues by allowing some
24 extra SMI grammar relaxations. You can enable all those relaxations at
25 once to maximize the number of MIBs, found in the wild, successfully
26 compiled.
27
28 .. code-block:: python
29
30 from pysmi.parser.dialect import smiV1Relaxed
31 from pysmi.parser.smi import parserFactory
32
33 RelaxedSmiV1Parser = parserFactory(**smiV1Relaxed)
0
1 .. _parser.smi.parserFactory:
2
3 SMI parser
4 ----------
5
6 SNMP MIBs are written in two kinds of special language - SMIv1 and SMIv2.
7 The first SMI version is obsolete, most MIBs by now are written in SMIv2
8 grammar. There are also efforts aimed at improving SMIv2, but those MIBs
9 are in great minority at the time of this writing.
10
11 PySMI is designed to handle both SMIv1 and SMIv2. The way it is done is
12 that SMIv2 is considered the most basic and complete, whereas SMIv1 is a
13 specialization of SMIv2 syntax.
14
15 For a user to acquire SMIv2 parser the *parserFactory* function should
16 be called with the :ref:`SMI dialect object <parser.smi.dialect>`.
17
18 The parser object should be passed to the :ref:`MibCompiler <compiler.MibCompiler>` object.
19
20 .. autofunction:: pysmi.parser.smi.parserFactory
21
22 .. note::
23
24 Please, note that *parserFactory* function returns a class, not
25 class instance. Make sure to instantiate it when passing to
26 :ref:`MibCompiler <compiler.MibCompiler>` class constructor.
0
1 .. _reader.callback.CallbackReader:
2
3 Callback reader
4 ---------------
5
6 *CallbackReader* class instance tries to fetch MIB files by calling user object.
7
8 .. autoclass:: pysmi.reader.callback.CallbackReader
9 :members:
0
1 .. _reader.ftpclient.FtpReader:
2
3 FTP reader
4 ----------
5
6 *FtpReader* class instance tries to download MIB files from configured FTP server.
7
8 .. autoclass:: pysmi.reader.ftpclient.FtpReader
9 :members:
0
1 .. _reader.httpclient.HttpReader:
2
3 HTTP reader
4 -----------
5
6 *HttpReader* class instance tries to download MIB files using configured URL.
7
8 .. autoclass:: pysmi.reader.httpclient.HttpReader
9 :members:
0
1 .. _reader.localfile.FileReader:
2
3 Local file reader
4 -----------------
5
6 *FileReader* class instance looks up MIB files in given directories on
7 the host running PySMI.
8
9 .. autoclass:: pysmi.reader.localfile.FileReader
10 :members:
0
1 .. _reader.zipreader.ZipReader:
2
3 ZIP archive reader
4 ------------------
5
6 *ZipReader* class instance looks up MIB files in local ZIP archive.
7 ZIP subdirectories and embedded ZIP archives would be traversed.
8
9 .. autoclass:: pysmi.reader.zipreader.ZipReader
10 :members:
0
1 .. _searcher.pyfile.PyFileSearcher:
2
3 Python files searcher
4 ---------------------
5
6 Transformed MIBs that were saved in form of Python files can be checked
7 with *PyFileSearcher* class instances.
8
9 .. autoclass:: pysmi.searcher.pyfile.PyFileSearcher
10 :members:
0
1 .. _searcher.pypackage.PyPackageSearcher:
2
3 Search Python packages
4 ----------------------
5
6 Some MIBs, most frequently the base ones, can be stored at a Python package.
7 There existence can be checked with the *PyPackageSearcher* class instances.
8
9 .. autoclass:: pysmi.searcher.pypackage.PyPackageSearcher
10 :members:
0
1 .. _searcher.stub.StubSearcher:
2
3 Unconditionally ignore MIBs
4 ---------------------------
5
6 Foundation or obsolete MIBs that should never be transformed can be
7 blindly excluded by listing their names at the *StubSearcher* class
8 instance.
9
10 .. autoclass:: pysmi.searcher.stub.StubSearcher
11 :members:
12
13 .. note::
14
15 A pysnmp-specific list of MIB names to be permanently excluded from
16 transformation can be found at :py:const:`pysmi.codegen.pysnmp.baseMibs`.
0
1 .. _writer.callback.CallbackReader:
2
3 Callback writer
4 ---------------
5
6 *CallbackWriter* class instance passes the contents of the compiled MIB files to a user object.
7
8 .. autoclass:: pysmi.writer.callback.CallbackWriter
9 :members:
0
1 .. _writer.localfile.FileWriter:
2
3 File writer
4 -----------
5
6 .. autoclass:: pysmi.writer.localfile.FileWriter
7 :members:
0
1 .. _writer.pyfile.PyFileWriter:
2
3 Python file writer
4 ------------------
5
6 .. autoclass:: pysmi.writer.pyfile.PyFileWriter
7 :members:
0 """
1 Always borrow pysnmp MIBs
2 +++++++++++++++++++++++++
3
4 Try to borrow precompiled pysnmp MIB file(s) from a web-site.
5
6 In this example no attempt is made to find and compile ASN.1
7 MIB source.
8
9 Fetched pysnmp MIB(s) are stored in a local directory.
10 """#
11 from pysmi.reader import HttpReader
12 from pysmi.searcher import PyFileSearcher
13 from pysmi.borrower import PyFileBorrower
14 from pysmi.writer import PyFileWriter
15 from pysmi.parser import NullParser
16 from pysmi.codegen import NullCodeGen
17 from pysmi.compiler import MibCompiler
18
19 inputMibs = ['BORROWED-MIB']
20
21 httpBorrowers = [
22 ('mibs.snmplabs.com', 80, '/pysnmp/notexts/@mib@')
23 ]
24 dstDirectory = '.pysnmp-mibs'
25
26 # Initialize compiler infrastructure
27
28 mibCompiler = MibCompiler(
29 NullParser(), NullCodeGen(), PyFileWriter(dstDirectory)
30 )
31
32 # check compiled/borrowed MIBs in our own productions
33 mibCompiler.addSearchers(PyFileSearcher(dstDirectory))
34
35 # search for precompiled MIBs at Web sites
36 mibCompiler.addBorrowers(
37 *[PyFileBorrower(HttpReader(*x)) for x in httpBorrowers]
38 )
39
40 # run MIB compilation
41 results = mibCompiler.compile(*inputMibs)
42
43 print('Results: %s' % ', '.join(['%s:%s' % (x, results[x]) for x in results]))
0 """
1 Borrow pysnmp MIBs on failure
2 +++++++++++++++++++++++++++++
3
4 Look up specific ASN.1 MIBs at configured Web/FTP sites.
5 If no required MIB is found or its compilation fails for
6 some reason, attempt to download precompiled version of
7 failed MIB and store it locally as if we had compiled it.
8 """#
9 from pysmi.reader import HttpReader
10 from pysmi.searcher import PyFileSearcher
11 from pysmi.searcher import StubSearcher
12 from pysmi.borrower import PyFileBorrower
13 from pysmi.writer import PyFileWriter
14 from pysmi.parser import SmiStarParser
15 from pysmi.codegen import PySnmpCodeGen
16 from pysmi.compiler import MibCompiler
17 # from pysmi import debug
18
19 # debug.setLogger(debug.Debug('borrower', 'reader', 'searcher'))
20
21 inputMibs = ['BORROWED-MIB']
22 httpSources = [
23 ('mibs.snmplabs.com', 80, '/asn1/@mib@')
24 ]
25 httpBorrowers = [
26 ('mibs.snmplabs.com', 80, '/pysnmp/notexts/@mib@')
27 ]
28 dstDirectory = '.pysnmp-mibs'
29
30 # Initialize compiler infrastructure
31
32 mibCompiler = MibCompiler(
33 SmiStarParser(), PySnmpCodeGen(), PyFileWriter(dstDirectory)
34 )
35
36 # search for source MIBs at Web sites
37 mibCompiler.addSources(*[HttpReader(*x) for x in httpSources])
38
39 # never recompile MIBs with MACROs
40 mibCompiler.addSearchers(StubSearcher(*PySnmpCodeGen.baseMibs))
41
42 # check compiled/borrowed MIBs in our own productions
43 mibCompiler.addSearchers(PyFileSearcher(dstDirectory))
44
45 # search for compiled MIBs at Web sites if source is not available or broken
46 mibCompiler.addBorrowers(*[PyFileBorrower(HttpReader(*x)).setOptions(genTexts=False) for x in httpBorrowers])
47
48 # run non-recursive MIB compilation
49 results = mibCompiler.compile(*inputMibs)
50
51 print('Results: %s' % ', '.join(['%s:%s' % (x, results[x]) for x in results]))
0 """
1 Compile SMIv1/v2 MIBs
2 +++++++++++++++++++++
3
4 Look up specific ASN.1 MIBs at configured local directories,
5 compile them into pysnmp form if not done yet and save Python
6 modules as plain-text files in a local directory.
7
8 Try to support both SMIv1 and SMIv2 flavors of SMI as well as
9 popular deviations from official syntax found in the wild.
10
11 When figuring out if compilation is needed, check all known
12 places where pysnmp MIBs could possibly be found.
13
14 You can force MIB re-compilation by passing rebuild flag to
15 MIB compiler (see below).
16
17 Default invocation of MIB compiler does not generate [potentially
18 large] comments and texts found in MIBs. If you need them in pysnmp
19 MIB modules, just pass genTexts flag to MIB compiler.
20 """#
21 from pysmi.reader import FileReader
22 from pysmi.searcher import PyFileSearcher, PyPackageSearcher, StubSearcher
23 from pysmi.writer import PyFileWriter
24 from pysmi.parser import SmiStarParser
25 from pysmi.codegen import PySnmpCodeGen
26 from pysmi.compiler import MibCompiler
27
28 inputMibs = ['IF-MIB', 'IP-MIB']
29 srcDirectories = ['/usr/share/snmp/mibs']
30 dstDirectory = '.pysnmp-mibs'
31
32 # Initialize compiler infrastructure
33
34 mibCompiler = MibCompiler(SmiStarParser(),
35 PySnmpCodeGen(),
36 PyFileWriter(dstDirectory))
37
38 # search for source MIBs here
39 mibCompiler.addSources(*[FileReader(x) for x in srcDirectories])
40
41 # check compiled MIBs in our own productions
42 mibCompiler.addSearchers(PyFileSearcher(dstDirectory))
43 # ...and at default PySNMP MIBs packages
44 mibCompiler.addSearchers(*[PyPackageSearcher(x) for x in PySnmpCodeGen.defaultMibPackages])
45
46 # never recompile MIBs with MACROs
47 mibCompiler.addSearchers(StubSearcher(*PySnmpCodeGen.baseMibs))
48
49 # run [possibly recursive] MIB compilation
50 results = mibCompiler.compile(*inputMibs) #, rebuild=True, genTexts=True)
51
52 print('Results: %s' % ', '.join(['%s:%s' % (x, results[x]) for x in results]))
0 """
1 Compile SMIv2 MIBs
2 ++++++++++++++++++
3
4 Invoke user callback function to provide MIB text,
5 compile given text string into pysnmp MIB form and pass
6 results to another user callback function for storing.
7
8 Here we expect to deal only with SMIv2-valid MIBs.
9
10 We use noDeps flag to prevent MIB compiler from attemping
11 to compile IMPORT'ed MIBs as well.
12 """#
13 import sys
14 from pysmi.reader import CallbackReader
15 from pysmi.searcher import StubSearcher
16 from pysmi.writer import CallbackWriter
17 from pysmi.parser import SmiV2Parser
18 from pysmi.codegen import PySnmpCodeGen
19 from pysmi.compiler import MibCompiler
20
21 inputMibs = ['IF-MIB', 'IP-MIB']
22 srcDir = '/usr/share/snmp/mibs/' # we will read MIBs from here
23
24 # Initialize compiler infrastructure
25
26 mibCompiler = MibCompiler(
27 SmiV2Parser(),
28 PySnmpCodeGen(),
29 # out own callback function stores results in its own way
30 CallbackWriter(lambda m, d, c: sys.stdout.write(d))
31 )
32
33 # our own callback function serves as a MIB source here
34 mibCompiler.addSources(
35 CallbackReader(lambda m, c: open(srcDir+m+'.txt').read())
36 )
37
38 # never recompile MIBs with MACROs
39 mibCompiler.addSearchers(StubSearcher(*PySnmpCodeGen.baseMibs))
40
41 # run non-recursive MIB compilation
42 results = mibCompiler.compile(*inputMibs, **dict(noDeps=True))
43
44 print('Results: %s' % ', '.join(['%s:%s' % (x, results[x]) for x in results]))
0 """
1 Compile MIBs into JSON
2 ++++++++++++++++++++++
3
4 Look up specific ASN.1 MIBs at configured Web and FTP sites,
5 compile them into JSON documents and print them out to stdout.
6
7 Try to support both SMIv1 and SMIv2 flavors of SMI as well as
8 popular deviations from official syntax found in the wild.
9 """#
10 from pysmi.reader import FileReader, HttpReader
11 from pysmi.searcher import StubSearcher
12 from pysmi.writer import CallbackWriter
13 from pysmi.parser import SmiStarParser
14 from pysmi.codegen import JsonCodeGen
15 from pysmi.compiler import MibCompiler
16 # from pysmi import debug
17
18 # debug.setLogger(debug.Debug('reader', 'compiler'))
19
20 inputMibs = ['IF-MIB', 'IP-MIB']
21 srcDirectories = ['/usr/share/snmp/mibs']
22 httpSources = [
23 ('mibs.snmplabs.com', 80, '/asn1/@mib@')
24 ]
25
26
27 def printOut(mibName, jsonDoc, cbCtx):
28 print('\n\n# MIB module %s' % mibName)
29 print(jsonDoc)
30
31 # Initialize compiler infrastructure
32
33 mibCompiler = MibCompiler(
34 SmiStarParser(), JsonCodeGen(), CallbackWriter(printOut)
35 )
36
37 # search for source MIBs here
38 mibCompiler.addSources(*[FileReader(x) for x in srcDirectories])
39
40 # search for source MIBs at Web sites
41 mibCompiler.addSources(*[HttpReader(*x) for x in httpSources])
42
43 # never recompile MIBs with MACROs
44 mibCompiler.addSearchers(StubSearcher(*JsonCodeGen.baseMibs))
45
46 # run recursive MIB compilation
47 results = mibCompiler.compile(*inputMibs)
48
49 print('\n# Results: %s' % ', '.join(['%s:%s' % (x, results[x]) for x in results]))
0 """
1 Compile MIBs from web
2 +++++++++++++++++++++
3
4 Look up specific ASN.1 MIBs at configured Web and FTP sites,
5 compile them into pysnmp form and save Python modules as plain-text
6 files in a local directory.
7
8 Try to support both SMIv1 and SMIv2 flavors of SMI as well as
9 popular deviations from official syntax found in the wild.
10
11 In this example we disable automatic dependency checking on MIB
12 compilation using noDeps flag.
13
14 Also, we do not check if target file already exists thus MIB
15 compilation occurs on every invocation.
16 """#
17 from pysmi.reader import HttpReader
18 from pysmi.reader import FtpReader
19 from pysmi.searcher import StubSearcher
20 from pysmi.writer import PyFileWriter
21 from pysmi.parser import SmiStarParser
22 from pysmi.codegen import PySnmpCodeGen
23 from pysmi.compiler import MibCompiler
24
25 inputMibs = ['IF-MIB', 'IP-MIB']
26 httpSources = [
27 ('mibs.snmplabs.com', 80, '/asn1/@mib@')
28 ]
29 ftpSources = [
30 ('ftp.cisco.com', '/pub/mibs/v2/@mib@')
31 ]
32 dstDirectory = '.pysnmp-mibs'
33
34 # Initialize compiler infrastructure
35
36 mibCompiler = MibCompiler(
37 SmiStarParser(), PySnmpCodeGen(), PyFileWriter(dstDirectory)
38 )
39
40 # search for source MIBs at Web and FTP sites
41 mibCompiler.addSources(*[HttpReader(*x) for x in httpSources])
42 mibCompiler.addSources(*[FtpReader(*x) for x in ftpSources])
43
44 # never recompile MIBs with MACROs
45 mibCompiler.addSearchers(StubSearcher(*PySnmpCodeGen.baseMibs))
46
47 # run non-recursive MIB compilation
48 results = mibCompiler.compile(*inputMibs, **dict(noDeps=True))
49
50 print('Results: %s' % ', '.join(['%s:%s' % (x, results[x]) for x in results]))
0 # http://www.python.org/dev/peps/pep-0396/
1 __version__ = '0.2.2'
2
3 import sys
4
5 if sys.version_info[:2] < (2, 4):
6 raise RuntimeError('PySMI requires Python 2.4 or later')
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi.borrower.pyfile import PyFileBorrower
7 from pysmi.borrower.anyfile import AnyFileBorrower
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi.borrower.base import AbstractBorrower
7
8
9 class AnyFileBorrower(AbstractBorrower):
10 """Create arbitrary MIB file borrowing object"""
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi import error
7 from pysmi import debug
8
9
10 class AbstractBorrower(object):
11 genTexts = False
12 exts = ''
13
14 def __init__(self, reader, genTexts=False):
15 """Creates an instance of *Borrower* class.
16
17 Args:
18 reader: a *reader* object
19
20 Keyword Args:
21 genText: indicates whether this borrower should be looking
22 for transformed MIBs that include human-oriented texts
23 """
24 if genTexts is not None:
25 self.genTexts = genTexts
26
27 self._reader = reader
28
29 def __str__(self):
30 return '%s{%s, genTexts=%s, exts=%s}' % (self.__class__.__name__,
31 self._reader, self.genTexts,
32 self.exts)
33
34 def setOptions(self, **kwargs):
35 self._reader.setOptions(**kwargs)
36
37 for k in kwargs:
38 setattr(self, k, kwargs[k])
39
40 return self
41
42 def getData(self, mibname, **kwargs):
43 if bool(kwargs.get('genTexts')) != self.genTexts:
44 debug.logger & debug.flagBorrower and debug.logger(
45 'skipping incompatible borrower %s for file %s' % (self, mibname))
46 raise error.PySmiFileNotFoundError(mibname=mibname, reader=self._reader)
47
48 debug.logger & debug.flagBorrower and (
49 debug.logger('trying to borrow file %s from %s' % (mibname, self._reader))
50 )
51
52 return self._reader.getData(mibname)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import imp
7 from pysmi.borrower.base import AbstractBorrower
8
9
10 class PyFileBorrower(AbstractBorrower):
11 """Create PySNMP MIB file borrowing object"""
12 for sfx, mode, typ in imp.get_suffixes():
13 if typ == imp.PY_SOURCE:
14 exts = [sfx]
15 break
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi.codegen.pysnmp import PySnmpCodeGen
7 from pysmi.codegen.jsondoc import JsonCodeGen
8 from pysmi.codegen.null import NullCodeGen
9
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 from pysmi import error
8
9
10 if sys.version_info[0] > 2:
11 # noinspection PyShadowingBuiltins
12 unicode = str
13 # noinspection PyShadowingBuiltins
14 long = int
15
16 def dorepr(s):
17 return repr(s)
18 else:
19 def dorepr(s):
20 return repr(s.encode('utf-8')).decode('utf-8')
21
22 def updateDict(d1, d2):
23 d1.update(d2)
24 return d1
25
26
27 class AbstractCodeGen(object):
28 # never compile these, they either:
29 # - define MACROs (implementation supplies them)
30 # - or carry conflicting OIDs (so that all IMPORT's of them will be rewritten)
31 # - or have manual fixes
32 # - or import base ASN.1 types from implementation-specific MIBs
33 baseMibs = ('RFC1065-SMI',
34 'RFC1155-SMI',
35 'RFC1158-MIB',
36 'RFC-1212',
37 'RFC1213-MIB',
38 'RFC-1215',
39 'SNMPv2-SMI',
40 'SNMPv2-TC',
41 'SNMPv2-TM',
42 'SNMPv2-CONF')
43
44 # Explicit SMIv1 -> SMIv2 mapping for standard MIBs
45 commonSyms = {'RFC1155-SMI/RFC1065-SMI':
46 {'internet': [('SNMPv2-SMI', 'internet')],
47 'directory': [('SNMPv2-SMI', 'directory')],
48 'mgmt': [('SNMPv2-SMI', 'mgmt')],
49 'experimental': [('SNMPv2-SMI', 'experimental')],
50 'private': [('SNMPv2-SMI', 'private')],
51 'enterprises': [('SNMPv2-SMI', 'enterprises')],
52 'OBJECT-TYPE': [('SNMPv2-SMI', 'OBJECT-TYPE')],
53 'ObjectName': [('SNMPv2-SMI', 'ObjectName')],
54 'ObjectSyntax': [('SNMPv2-SMI', 'ObjectSyntax')],
55 'SimpleSyntax': [('SNMPv2-SMI', 'SimpleSyntax')],
56 'ApplicationSyntax': [('SNMPv2-SMI', 'ApplicationSyntax')],
57 'NetworkAddress': [('SNMPv2-SMI', 'IpAddress')],
58 'IpAddress': [('SNMPv2-SMI', 'IpAddress')],
59 'Counter': [('SNMPv2-SMI', 'Counter32')],
60 'Gauge': [('SNMPv2-SMI', 'Gauge32')],
61 'TimeTicks': [('SNMPv2-SMI', 'TimeTicks')],
62 'Opaque': [('SNMPv2-SMI', 'Opaque')]},
63 'RFC1158-MIB/RFC1213-MIB':
64 {'mib-2': [('SNMPv2-SMI', 'mib-2')],
65 'DisplayString': [('SNMPv2-TC', 'DisplayString')],
66 'system': [('SNMPv2-MIB', 'system')],
67 'interfaces': [('IF-MIB', 'interfaces')],
68 'ip': [('IP-MIB', 'ip')],
69 'icmp': [('IP-MIB', 'icmp')],
70 'tcp': [('TCP-MIB', 'tcp')],
71 'udp': [('UDP-MIB', 'udp')],
72 'transmission': [('SNMPv2-SMI', 'transmission')],
73 'snmp': [('SNMPv2-MIB', 'snmp')],
74 'sysDescr': [('SNMPv2-MIB', 'sysDescr')],
75 'sysObjectID': [('SNMPv2-MIB', 'sysObjectID')],
76 'sysUpTime': [('SNMPv2-MIB', 'sysUpTime')],
77 'sysContact': [('SNMPv2-MIB', 'sysContact')],
78 'sysName': [('SNMPv2-MIB', 'sysName')],
79 'sysLocation': [('SNMPv2-MIB', 'sysLocation')],
80 'sysServices': [('SNMPv2-MIB', 'sysServices')],
81 'ifNumber': [('IF-MIB', 'ifNumber')],
82 'ifTable': [('IF-MIB', 'ifTable')],
83 'ifEntry': [('IF-MIB', 'ifEntry')],
84 'ifIndex': [('IF-MIB', 'ifIndex')],
85 'ifDescr': [('IF-MIB', 'ifDescr')],
86 'ifType': [('IF-MIB', 'ifType')],
87 'ifMtu': [('IF-MIB', 'ifMtu')],
88 'ifSpeed': [('IF-MIB', 'ifSpeed')],
89 'ifPhysAddress': [('IF-MIB', 'ifPhysAddress')],
90 'ifAdminStatus': [('IF-MIB', 'ifAdminStatus')],
91 'ifOperStatus': [('IF-MIB', 'ifOperStatus')],
92 'ifLastChange': [('IF-MIB', 'ifLastChange')],
93 'ifInOctets': [('IF-MIB', 'ifInOctets')],
94 'ifInUcastPkts': [('IF-MIB', 'ifInUcastPkts')],
95 'ifInNUcastPkts': [('IF-MIB', 'ifInNUcastPkts')],
96 'ifInDiscards': [('IF-MIB', 'ifInDiscards')],
97 'ifInErrors': [('IF-MIB', 'ifInErrors')],
98 'ifInUnknownProtos': [('IF-MIB', 'ifInUnknownProtos')],
99 'ifOutOctets': [('IF-MIB', 'ifOutOctets')],
100 'ifOutUcastPkts': [('IF-MIB', 'ifOutUcastPkts')],
101 'ifOutNUcastPkts': [('IF-MIB', 'ifOutNUcastPkts')],
102 'ifOutDiscards': [('IF-MIB', 'ifOutDiscards')],
103 'ifOutErrors': [('IF-MIB', 'ifOutErrors')],
104 'ifOutQLen': [('IF-MIB', 'ifOutQLen')],
105 'ifSpecific': [('IF-MIB', 'ifSpecific')],
106 'ipForwarding': [('IP-MIB', 'ipForwarding')],
107 'ipDefaultTTL': [('IP-MIB', 'ipDefaultTTL')],
108 'ipInReceives': [('IP-MIB', 'ipInReceives')],
109 'ipInHdrErrors': [('IP-MIB', 'ipInHdrErrors')],
110 'ipInAddrErrors': [('IP-MIB', 'ipInAddrErrors')],
111 'ipForwDatagrams': [('IP-MIB', 'ipForwDatagrams')],
112 'ipInUnknownProtos': [('IP-MIB', 'ipInUnknownProtos')],
113 'ipInDiscards': [('IP-MIB', 'ipInDiscards')],
114 'ipInDelivers': [('IP-MIB', 'ipInDelivers')],
115 'ipOutRequests': [('IP-MIB', 'ipOutRequests')],
116 'ipOutDiscards': [('IP-MIB', 'ipOutDiscards')],
117 'ipOutNoRoutes': [('IP-MIB', 'ipOutNoRoutes')],
118 'ipReasmTimeout': [('IP-MIB', 'ipReasmTimeout')],
119 'ipReasmReqds': [('IP-MIB', 'ipReasmReqds')],
120 'ipReasmOKs': [('IP-MIB', 'ipReasmOKs')],
121 'ipReasmFails': [('IP-MIB', 'ipReasmFails')],
122 'ipFragOKs': [('IP-MIB', 'ipFragOKs')],
123 'ipFragFails': [('IP-MIB', 'ipFragFails')],
124 'ipFragCreates': [('IP-MIB', 'ipFragCreates')],
125 'ipAddrTable': [('IP-MIB', 'ipAddrTable')],
126 'ipAddrEntry': [('IP-MIB', 'ipAddrEntry')],
127 'ipAdEntAddr': [('IP-MIB', 'ipAdEntAddr')],
128 'ipAdEntIfIndex': [('IP-MIB', 'ipAdEntIfIndex')],
129 'ipAdEntNetMask': [('IP-MIB', 'ipAdEntNetMask')],
130 'ipAdEntBcastAddr': [('IP-MIB', 'ipAdEntBcastAddr')],
131 'ipAdEntReasmMaxSize': [('IP-MIB', 'ipAdEntReasmMaxSize')],
132 'ipNetToMediaTable': [('IP-MIB', 'ipNetToMediaTable')],
133 'ipNetToMediaEntry': [('IP-MIB', 'ipNetToMediaEntry')],
134 'ipNetToMediaIfIndex': [('IP-MIB', 'ipNetToMediaIfIndex')],
135 'ipNetToMediaPhysAddress': [('IP-MIB', 'ipNetToMediaPhysAddress')],
136 'ipNetToMediaNetAddress': [('IP-MIB', 'ipNetToMediaNetAddress')],
137 'ipNetToMediaType': [('IP-MIB', 'ipNetToMediaType')],
138 'icmpInMsgs': [('IP-MIB', 'icmpInMsgs')],
139 'icmpInErrors': [('IP-MIB', 'icmpInErrors')],
140 'icmpInDestUnreachs': [('IP-MIB', 'icmpInDestUnreachs')],
141 'icmpInTimeExcds': [('IP-MIB', 'icmpInTimeExcds')],
142 'icmpInParmProbs': [('IP-MIB', 'icmpInParmProbs')],
143 'icmpInSrcQuenchs': [('IP-MIB', 'icmpInSrcQuenchs')],
144 'icmpInRedirects': [('IP-MIB', 'icmpInRedirects')],
145 'icmpInEchos': [('IP-MIB', 'icmpInEchos')],
146 'icmpInEchoReps': [('IP-MIB', 'icmpInEchoReps')],
147 'icmpInTimestamps': [('IP-MIB', 'icmpInTimestamps')],
148 'icmpInTimestampReps': [('IP-MIB', 'icmpInTimestampReps')],
149 'icmpInAddrMasks': [('IP-MIB', 'icmpInAddrMasks')],
150 'icmpInAddrMaskReps': [('IP-MIB', 'icmpInAddrMaskReps')],
151 'icmpOutMsgs': [('IP-MIB', 'icmpOutMsgs')],
152 'icmpOutErrors': [('IP-MIB', 'icmpOutErrors')],
153 'icmpOutDestUnreachs': [('IP-MIB', 'icmpOutDestUnreachs')],
154 'icmpOutTimeExcds': [('IP-MIB', 'icmpOutTimeExcds')],
155 'icmpOutParmProbs': [('IP-MIB', 'icmpOutParmProbs')],
156 'icmpOutSrcQuenchs': [('IP-MIB', 'icmpOutSrcQuenchs')],
157 'icmpOutRedirects': [('IP-MIB', 'icmpOutRedirects')],
158 'icmpOutEchos': [('IP-MIB', 'icmpOutEchos')],
159 'icmpOutEchoReps': [('IP-MIB', 'icmpOutEchoReps')],
160 'icmpOutTimestamps': [('IP-MIB', 'icmpOutTimestamps')],
161 'icmpOutTimestampReps': [('IP-MIB', 'icmpOutTimestampReps')],
162 'icmpOutAddrMasks': [('IP-MIB', 'icmpOutAddrMasks')],
163 'icmpOutAddrMaskReps': [('IP-MIB', 'icmpOutAddrMaskReps')],
164 'tcpRtoAlgorithm': [('TCP-MIB', 'tcpRtoAlgorithm')],
165 'tcpRtoMin': [('TCP-MIB', 'tcpRtoMin')],
166 'tcpRtoMax': [('TCP-MIB', 'tcpRtoMax')],
167 'tcpMaxConn': [('TCP-MIB', 'tcpMaxConn')],
168 'tcpActiveOpens': [('TCP-MIB', 'tcpActiveOpens')],
169 'tcpPassiveOpens': [('TCP-MIB', 'tcpPassiveOpens')],
170 'tcpAttemptFails': [('TCP-MIB', 'tcpAttemptFails')],
171 'tcpEstabResets': [('TCP-MIB', 'tcpEstabResets')],
172 'tcpCurrEstab': [('TCP-MIB', 'tcpCurrEstab')],
173 'tcpInSegs': [('TCP-MIB', 'tcpInSegs')],
174 'tcpOutSegs': [('TCP-MIB', 'tcpOutSegs')],
175 'tcpRetransSegs': [('TCP-MIB', 'tcpRetransSegs')],
176 'tcpConnTable': [('TCP-MIB', 'tcpConnTable')],
177 'tcpConnEntry': [('TCP-MIB', 'tcpConnEntry')],
178 'tcpConnState': [('TCP-MIB', 'tcpConnState')],
179 'tcpConnLocalAddress': [('TCP-MIB', 'tcpConnLocalAddress')],
180 'tcpConnLocalPort': [('TCP-MIB', 'tcpConnLocalPort')],
181 'tcpConnRemAddress': [('TCP-MIB', 'tcpConnRemAddress')],
182 'tcpConnRemPort': [('TCP-MIB', 'tcpConnRemPort')],
183 'tcpInErrs': [('TCP-MIB', 'tcpInErrs')],
184 'tcpOutRsts': [('TCP-MIB', 'tcpOutRsts')],
185 'udpInDatagrams': [('UDP-MIB', 'udpInDatagrams')],
186 'udpNoPorts': [('UDP-MIB', 'udpNoPorts')],
187 'udpInErrors': [('UDP-MIB', 'udpInErrors')],
188 'udpOutDatagrams': [('UDP-MIB', 'udpOutDatagrams')],
189 'udpTable': [('UDP-MIB', 'udpTable')],
190 'udpEntry': [('UDP-MIB', 'udpEntry')],
191 'udpLocalAddress': [('UDP-MIB', 'udpLocalAddress')],
192 'udpLocalPort': [('UDP-MIB', 'udpLocalPort')],
193 'snmpInPkts': [('SNMPv2-MIB', 'snmpInPkts')],
194 'snmpOutPkts': [('SNMPv2-MIB', 'snmpOutPkts')],
195 'snmpInBadVersions': [('SNMPv2-MIB', 'snmpInBadVersions')],
196 'snmpInBadCommunityNames': [('SNMPv2-MIB', 'snmpInBadCommunityNames')],
197 'snmpInBadCommunityUses': [('SNMPv2-MIB', 'snmpInBadCommunityUses')],
198 'snmpInASNParseErrs': [('SNMPv2-MIB', 'snmpInASNParseErrs')],
199 'snmpInTooBigs': [('SNMPv2-MIB', 'snmpInTooBigs')],
200 'snmpInNoSuchNames': [('SNMPv2-MIB', 'snmpInNoSuchNames')],
201 'snmpInBadValues': [('SNMPv2-MIB', 'snmpInBadValues')],
202 'snmpInReadOnlys': [('SNMPv2-MIB', 'snmpInReadOnlys')],
203 'snmpInGenErrs': [('SNMPv2-MIB', 'snmpInGenErrs')],
204 'snmpInTotalReqVars': [('SNMPv2-MIB', 'snmpInTotalReqVars')],
205 'snmpInTotalSetVars': [('SNMPv2-MIB', 'snmpInTotalSetVars')],
206 'snmpInGetRequests': [('SNMPv2-MIB', 'snmpInGetRequests')],
207 'snmpInGetNexts': [('SNMPv2-MIB', 'snmpInGetNexts')],
208 'snmpInSetRequests': [('SNMPv2-MIB', 'snmpInSetRequests')],
209 'snmpInGetResponses': [('SNMPv2-MIB', 'snmpInGetResponses')],
210 'snmpInTraps': [('SNMPv2-MIB', 'snmpInTraps')],
211 'snmpOutTooBigs': [('SNMPv2-MIB', 'snmpOutTooBigs')],
212 'snmpOutNoSuchNames': [('SNMPv2-MIB', 'snmpOutNoSuchNames')],
213 'snmpOutBadValues': [('SNMPv2-MIB', 'snmpOutBadValues')],
214 'snmpOutGenErrs': [('SNMPv2-MIB', 'snmpOutGenErrs')],
215 'snmpOutGetRequests': [('SNMPv2-MIB', 'snmpOutGetRequests')],
216 'snmpOutGetNexts': [('SNMPv2-MIB', 'snmpOutGetNexts')],
217 'snmpOutSetRequests': [('SNMPv2-MIB', 'snmpOutSetRequests')],
218 'snmpOutGetResponses': [('SNMPv2-MIB', 'snmpOutGetResponses')],
219 'snmpOutTraps': [('SNMPv2-MIB', 'snmpOutTraps')],
220 'snmpEnableAuthenTraps': [('SNMPv2-MIB', 'snmpEnableAuthenTraps')]}}
221
222 convertImportv2 = {
223 'RFC1065-SMI': commonSyms['RFC1155-SMI/RFC1065-SMI'],
224 'RFC1155-SMI': commonSyms['RFC1155-SMI/RFC1065-SMI'],
225 'RFC1158-MIB': updateDict(
226 dict(commonSyms['RFC1155-SMI/RFC1065-SMI']),
227 (('nullSpecific', [('SNMPv2-SMI', 'zeroDotZero')]),
228 ('ipRoutingTable', [('RFC1213-MIB', 'ipRouteTable')]),
229 ('ipRouteEntry', [('RFC1213-MIB', 'ipRouteEntry')]),
230 ('ipRouteDest', [('RFC1213-MIB', 'ipRouteDest')]),
231 ('ipRouteIfIndex', [('RFC1213-MIB', 'ipRouteIfIndex')]),
232 ('ipRouteMetric1', [('RFC1213-MIB', 'ipRouteMetric1')]),
233 ('ipRouteMetric2', [('RFC1213-MIB', 'ipRouteMetric2')]),
234 ('ipRouteMetric3', [('RFC1213-MIB', 'ipRouteMetric3')]),
235 ('ipRouteMetric4', [('RFC1213-MIB', 'ipRouteMetric4')]),
236 ('ipRouteNextHop', [('RFC1213-MIB', 'ipRouteNextHop')]),
237 ('ipRouteType', [('RFC1213-MIB', 'ipRouteType')]),
238 ('ipRouteProto', [('RFC1213-MIB', 'ipRouteProto')]),
239 ('ipRouteAge', [('RFC1213-MIB', 'ipRouteAge')]),
240 ('ipRouteMask', [('RFC1213-MIB', 'ipRouteMask')]),
241 ('egpInMsgs', [('RFC1213-MIB', 'egpInMsgs')]),
242 ('egpInErrors', [('RFC1213-MIB', 'egpInErrors')]),
243 ('egpOutMsgs', [('RFC1213-MIB', 'egpOutMsgs')]),
244 ('egpOutErrors', [('RFC1213-MIB', 'egpOutErrors')]),
245 ('egpNeighTable', [('RFC1213-MIB', 'egpNeighTable')]),
246 ('egpNeighEntry', [('RFC1213-MIB', 'egpNeighEntry')]),
247 ('egpNeighState', [('RFC1213-MIB', 'egpNeighState')]),
248 ('egpNeighAddr', [('RFC1213-MIB', 'egpNeighAddr')]),
249 ('egpNeighAs', [('RFC1213-MIB', 'egpNeighAs')]),
250 ('egpNeighInMsgs', [('RFC1213-MIB', 'egpNeighInMsgs')]),
251 ('egpNeighInErrs', [('RFC1213-MIB', 'egpNeighInErrs')]),
252 ('egpNeighOutMsgs', [('RFC1213-MIB', 'egpNeighOutMsgs')]),
253 ('egpNeighOutErrs', [('RFC1213-MIB', 'egpNeighOutErrs')]),
254 ('egpNeighInErrMsgs', [('RFC1213-MIB', 'egpNeighInErrMsgs')]),
255 ('egpNeighOutErrMsgs', [('RFC1213-MIB', 'egpNeighOutErrMsgs')]),
256 ('egpNeighStateUps', [('RFC1213-MIB', 'egpNeighStateUps')]),
257 ('egpNeighStateDowns', [('RFC1213-MIB', 'egpNeighStateDowns')]),
258 ('egpNeighIntervalHello', [('RFC1213-MIB', 'egpNeighIntervalHello')]),
259 ('egpNeighIntervalPoll', [('RFC1213-MIB', 'egpNeighIntervalPoll')]),
260 ('egpNeighMode', [('RFC1213-MIB', 'egpNeighMode')]),
261 ('egpNeighEventTrigger', [('RFC1213-MIB', 'egpNeighEventTrigger')]),
262 ('egpAs', [('RFC1213-MIB', 'egpAs')]),
263 ('snmpEnableAuthTraps', [('SNMPv2-MIB', 'snmpEnableAuthenTraps')]))
264 ),
265 'RFC-1212': {'OBJECT-TYPE': [('SNMPv2-SMI', 'OBJECT-TYPE')]},
266 # XXX 'IndexSyntax': ???
267 'RFC1213-MIB': updateDict(dict(commonSyms['RFC1158-MIB/RFC1213-MIB']),
268 (('PhysAddress', [('SNMPv2-TC', 'PhysAddress')]),)),
269 'RFC-1215': {'TRAP-TYPE': [('SNMPv2-SMI', 'TRAP-TYPE')]}
270 }
271
272
273 def genCode(self, ast, symbolTable, **kwargs):
274 raise NotImplementedError()
275
276 def genIndex(self, mibsMap, **kwargs):
277 raise NotImplementedError()
278
279 @staticmethod
280 def isBinary(s):
281 return (isinstance(s, (str, unicode)) and
282 s[0] == '\'' and s[-2:] in ('\'b', '\'B'))
283
284 @staticmethod
285 def isHex(s):
286 return (isinstance(s, (str, unicode)) and s[0] == '\''
287 and s[-2:] in ('\'h', '\'H'))
288
289 def str2int(self, s):
290 if self.isBinary(s):
291 if s[1:-2]:
292 return int(s[1:-2], 2)
293 else:
294 raise error.PySmiSemanticError('empty binary string to int conversion')
295
296 elif self.isHex(s):
297 if s[1:-2]:
298 return int(s[1:-2], 16)
299 else:
300 raise error.PySmiSemanticError('empty hex string to int conversion')
301 else:
302 return int(s)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 import re
8 from time import strptime, strftime
9 try:
10 import json
11 except ImportError:
12 import simplejson as json
13 try:
14 from collections import OrderedDict
15 except ImportError:
16 from ordereddict import OrderedDict
17 from pysmi.mibinfo import MibInfo
18 from pysmi.codegen.base import AbstractCodeGen
19 from pysmi import error
20 from pysmi import debug
21
22 if sys.version_info[0] > 2:
23 # noinspection PyShadowingBuiltins
24 unicode = str
25 # noinspection PyShadowingBuiltins
26 long = int
27
28
29 class JsonCodeGen(AbstractCodeGen):
30 """Builds JSON document representing MIB module supplied
31 in form of an Abstract Syntax Tree on input.
32
33 Instance of this class is supposed to be passed to *MibCompiler*,
34 the rest is internal to *MibCompiler*.
35 """
36 constImports = {
37 'SNMPv2-SMI': ('iso',
38 'NOTIFICATION-TYPE', # bug in some MIBs (e.g. A3COM-HUAWEI-DHCPSNOOP-MIB)
39 'MODULE-IDENTITY', 'OBJECT-TYPE', 'OBJECT-IDENTITY'),
40 'SNMPv2-TC': ('DisplayString', 'TEXTUAL-CONVENTION',), # XXX
41 'SNMPv2-CONF': ('MODULE-COMPLIANCE', 'NOTIFICATION-GROUP',), # XXX
42 }
43
44 # never compile these, they either:
45 # - define MACROs (implementation supplies them)
46 # - or carry conflicting OIDs (so that all IMPORT's of them will be rewritten)
47 # - or have manual fixes
48 # - or import base ASN.1 types from implementation-specific MIBs
49 fakeMibs = ('ASN1',
50 'ASN1-ENUMERATION',
51 'ASN1-REFINEMENT') + AbstractCodeGen.baseMibs
52
53 baseTypes = ['Integer', 'Integer32', 'Bits', 'ObjectIdentifier', 'OctetString']
54
55 typeClasses = {
56 'NetworkAddress': 'IpAddress', # RFC1065-SMI, RFC1155-SMI -> SNMPv2-SMI
57 'nullSpecific': 'zeroDotZero', # RFC1158-MIB -> SNMPv2-SMI
58 'ipRoutingTable': 'ipRouteTable', # RFC1158-MIB -> RFC1213-MIB
59 'snmpEnableAuthTraps': 'snmpEnableAuthenTraps' # RFC1158-MIB -> SNMPv2-MIB
60 }
61
62 smiv1IdxTypes = ['INTEGER', 'OCTET STRING', 'IPADDRESS', 'NETWORKADDRESS']
63
64 indent = ' ' * 4
65 fakeidx = 1000 # starting index for fake symbols
66
67 def __init__(self):
68 self._rows = set()
69 self._cols = {} # k, v = name, datatype
70 self._seenSyms = set()
71 self._importMap = {}
72 self._out = {} # k, v = name, generated code
73 self._moduleIdentityOid = None
74 self._enterpriseOid = None
75 self._oids = set()
76 self._complianceOids = []
77 self.moduleName = ['DUMMY']
78 self.genRules = {'text': True}
79 self.symbolTable = {}
80
81 @staticmethod
82 def transOpers(symbol):
83 return symbol.replace('-', '_')
84
85 def prepData(self, pdata):
86 data = []
87 for el in pdata:
88 if not isinstance(el, tuple):
89 data.append(el)
90 elif len(el) == 1:
91 data.append(el[0])
92 else:
93 data.append(
94 self.handlersTable[el[0]](self, self.prepData(el[1:]))
95 )
96 return data
97
98 def genImports(self, imports):
99 # convertion to SNMPv2
100 toDel = []
101 for module in list(imports):
102
103 if module in self.convertImportv2:
104
105 for symbol in imports[module]:
106
107 if symbol in self.convertImportv2[module]:
108 toDel.append((module, symbol))
109
110 for newImport in self.convertImportv2[module][symbol]:
111 newModule, newSymbol = newImport
112
113 if newModule in imports:
114 imports[newModule].append(newSymbol)
115 else:
116 imports[newModule] = [newSymbol]
117
118 # removing converted symbols
119 for d in toDel:
120 imports[d[0]].remove(d[1])
121
122 # merging mib and constant imports
123 for module in self.constImports:
124 if module in imports:
125 imports[module] += self.constImports[module]
126 else:
127 imports[module] = self.constImports[module]
128
129 outDict = OrderedDict()
130 outDict['class'] = 'imports'
131 for module in sorted(imports):
132 symbols = []
133 for symbol in set(imports[module]):
134 symbols.append(symbol)
135
136 if symbols:
137 self._seenSyms.update(
138 [self.transOpers(s) for s in symbols]
139 )
140 self._importMap.update(
141 [(self.transOpers(s), module) for s in symbols]
142 )
143 if module not in outDict:
144 outDict[module] = []
145
146 outDict[module].extend(symbols)
147
148 return OrderedDict(imports=outDict), tuple(sorted(imports))
149
150 # noinspection PyMethodMayBeStatic
151 def genLabel(self, symbol):
152 return '-' in symbol and symbol or ''
153
154 def addToExports(self, symbol, moduleIdentity=0):
155 self._seenSyms.add(symbol)
156
157 # noinspection PyUnusedLocal
158 def regSym(self, symbol, outDict, parentOid=None, moduleIdentity=False, moduleCompliance=False):
159 if symbol in self._seenSyms and symbol not in self._importMap:
160 raise error.PySmiSemanticError('Duplicate symbol found: %s' % symbol)
161
162 self.addToExports(symbol, moduleIdentity)
163 self._out[symbol] = outDict
164
165 if 'oid' in outDict:
166 self._oids.add(outDict['oid'])
167
168 if not self._enterpriseOid and outDict['oid'].startswith('1.3.6.1.4.1.'):
169 self._enterpriseOid = '.'.join(outDict['oid'].split('.')[:7])
170
171 if moduleIdentity:
172 if self._moduleIdentityOid:
173 raise error.PySmiSemanticError('Duplicate module identity')
174 self._moduleIdentityOid = outDict['oid']
175
176 if moduleCompliance:
177 self._complianceOids.append(outDict['oid'])
178
179 def genNumericOid(self, oid):
180 numericOid = ()
181
182 for part in oid:
183 if isinstance(part, tuple):
184 parent, module = part
185 if parent == 'iso':
186 numericOid += (1,)
187 continue
188
189 if module not in self.symbolTable:
190 # XXX do getname for possible future borrowed mibs
191 raise error.PySmiSemanticError('no module "%s" in symbolTable' % module)
192
193 if parent not in self.symbolTable[module]:
194 raise error.PySmiSemanticError('no symbol "%s" in module "%s"' % (parent, module))
195 numericOid += self.genNumericOid(self.symbolTable[module][parent]['oid'])
196
197 else:
198 numericOid += (part,)
199
200 return numericOid
201
202 def getBaseType(self, symName, module):
203 if module not in self.symbolTable:
204 raise error.PySmiSemanticError('no module "%s" in symbolTable' % module)
205
206 if symName not in self.symbolTable[module]:
207 raise error.PySmiSemanticError('no symbol "%s" in module "%s"' % (symName, module))
208
209 symType, symSubtype = self.symbolTable[module][symName].get('syntax', (('', ''), ''))
210 if not symType[0]:
211 raise error.PySmiSemanticError('unknown type for symbol "%s"' % symName)
212
213 if symType[0] in self.baseTypes:
214 return symType, symSubtype
215
216 else:
217 baseSymType, baseSymSubtype = self.getBaseType(*symType)
218 if isinstance(baseSymSubtype, list):
219 if isinstance(symSubtype, list):
220 symSubtype += baseSymSubtype
221 else:
222 symSubtype = baseSymSubtype
223
224 return baseSymType, symSubtype
225
226 # Clause generation functions
227
228 # noinspection PyUnusedLocal
229 def genAgentCapabilities(self, data):
230 name, productRelease, status, description, reference, oid = data
231
232 label = self.genLabel(name)
233 name = self.transOpers(name)
234
235 oidStr, parentOid = oid
236
237 outDict = OrderedDict()
238 outDict['name'] = name
239 outDict['oid'] = oidStr
240 outDict['class'] = 'agentcapabilities'
241
242 if productRelease:
243 outDict['productrelease'] = productRelease
244
245 if status:
246 outDict['status'] = status
247
248 if self.genRules['text'] and description:
249 outDict['description'] = description
250
251 if self.genRules['text'] and reference:
252 outDict['reference'] = reference
253
254 self.regSym(name, outDict, parentOid)
255
256 return outDict
257
258 # noinspection PyUnusedLocal
259 def genModuleIdentity(self, data):
260 name, lastUpdated, organization, contactInfo, description, revisions, oid = data
261
262 label = self.genLabel(name)
263 name = self.transOpers(name)
264
265 oidStr, parentOid = oid
266
267 outDict = OrderedDict()
268 outDict['name'] = name
269 outDict['oid'] = oidStr
270 outDict['class'] = 'moduleidentity'
271
272 if revisions:
273 outDict['revisions'] = revisions
274
275 if self.genRules['text']:
276 if lastUpdated:
277 outDict['lastupdated'] = lastUpdated
278 if organization:
279 outDict['organization'] = organization
280 if contactInfo:
281 outDict['contactinfo'] = contactInfo
282 if description:
283 outDict['description'] = description
284
285 self.regSym(name, outDict, parentOid, moduleIdentity=True)
286
287 return outDict
288
289 # noinspection PyUnusedLocal
290 def genModuleCompliance(self, data):
291 name, status, description, reference, compliances, oid = data
292
293 label = self.genLabel(name)
294 name = self.transOpers(name)
295
296 oidStr, parentOid = oid
297
298 outDict = OrderedDict()
299 outDict['name'] = name
300 outDict['oid'] = oidStr
301 outDict['class'] = 'modulecompliance'
302
303 if compliances:
304 outDict['modulecompliance'] = compliances
305
306 if status:
307 outDict['status'] = status
308
309 if self.genRules['text'] and description:
310 outDict['description'] = description
311
312 if self.genRules['text'] and reference:
313 outDict['reference'] = reference
314
315 self.regSym(name, outDict, parentOid, moduleCompliance=True)
316
317 return outDict
318
319 # noinspection PyUnusedLocal
320 def genNotificationGroup(self, data):
321 name, objects, status, description, reference, oid = data
322
323 label = self.genLabel(name)
324 name = self.transOpers(name)
325
326 oidStr, parentOid = oid
327 outDict = OrderedDict()
328 outDict['name'] = name
329 outDict['oid'] = oidStr
330 outDict['class'] = 'notificationgroup'
331
332 if objects:
333 outDict['objects'] = [{'module': self._importMap.get(obj, self.moduleName[0]), 'object': self.transOpers(obj)} for obj in objects]
334
335 if status:
336 outDict['status'] = status
337
338 if self.genRules['text'] and description:
339 outDict['description'] = description
340
341 if self.genRules['text'] and reference:
342 outDict['reference'] = reference
343
344 self.regSym(name, outDict, parentOid)
345
346 return outDict
347
348 # noinspection PyUnusedLocal
349 def genNotificationType(self, data):
350 name, objects, status, description, reference, oid = data
351
352 label = self.genLabel(name)
353 name = self.transOpers(name)
354
355 oidStr, parentOid = oid
356 outDict = OrderedDict()
357 outDict['name'] = name
358 outDict['oid'] = oidStr
359 outDict['class'] = 'notificationtype'
360
361 if objects:
362 outDict['objects'] = [{'module': self._importMap.get(obj, self.moduleName[0]), 'object': self.transOpers(obj)} for obj in objects]
363
364 if status:
365 outDict['status'] = status
366
367 if self.genRules['text'] and description:
368 outDict['description'] = description
369
370 if self.genRules['text'] and reference:
371 outDict['reference'] = reference
372
373 self.regSym(name, outDict, parentOid)
374
375 return outDict
376
377 # noinspection PyUnusedLocal
378 def genObjectGroup(self, data):
379 name, objects, status, description, reference, oid = data
380
381 label = self.genLabel(name)
382 name = self.transOpers(name)
383
384 oidStr, parentOid = oid
385 outDict = OrderedDict({'name': name,
386 'oid': oidStr,
387 'class': 'objectgroup'})
388
389 if objects:
390 outDict['objects'] = [{'module': self._importMap.get(obj, self.moduleName[0]), 'object': self.transOpers(obj)} for obj in objects]
391
392 if status:
393 outDict['status'] = status
394
395 if self.genRules['text'] and description:
396 outDict['description'] = description
397
398 if self.genRules['text'] and reference:
399 outDict['reference'] = reference
400
401 self.regSym(name, outDict, parentOid)
402
403 return outDict
404
405 # noinspection PyUnusedLocal
406 def genObjectIdentity(self, data):
407 name, status, description, reference, oid = data
408
409 label = self.genLabel(name)
410 name = self.transOpers(name)
411
412 oidStr, parentOid = oid
413
414 outDict = OrderedDict()
415 outDict['name'] = name
416 outDict['oid'] = oidStr
417 outDict['class'] = 'objectidentity'
418
419 if status:
420 outDict['status'] = status
421
422 if self.genRules['text'] and description:
423 outDict['description'] = description
424
425 if self.genRules['text'] and reference:
426 outDict['reference'] = reference
427
428 self.regSym(name, outDict, parentOid)
429
430 return outDict
431
432 # noinspection PyUnusedLocal
433 def genObjectType(self, data):
434 name, syntax, units, maxaccess, status, description, reference, augmention, index, defval, oid = data
435
436 label = self.genLabel(name)
437 name = self.transOpers(name)
438
439 oidStr, parentOid = oid
440 indexStr, fakeStrlist, fakeSyms = index or ('', '', [])
441
442 defval = self.genDefVal(defval, objname=name)
443
444 outDict = OrderedDict()
445 outDict['name'] = name
446 outDict['oid'] = oidStr
447
448 if syntax[0]:
449 nodetype = syntax[0] == 'Bits' and 'scalar' or syntax[0] # Bits hack
450 nodetype = name in self.symbolTable[self.moduleName[0]]['_symtable_cols'] and 'column' or nodetype
451 outDict['nodetype'] = nodetype
452
453 outDict['class'] = 'objecttype'
454
455 if syntax[1]:
456 outDict['syntax'] = syntax[1]
457 if defval:
458 outDict['default'] = defval
459 if units:
460 outDict['units'] = units
461 if maxaccess:
462 outDict['maxaccess'] = maxaccess
463 if indexStr:
464 outDict['indices'] = indexStr
465 if self.genRules['text'] and reference:
466 outDict['reference'] = reference
467 if augmention:
468 augmention = self.transOpers(augmention)
469 outDict['augmention'] = OrderedDict()
470 outDict['augmention']['name'] = name
471 outDict['augmention']['module'] = self.moduleName[0]
472 outDict['augmention']['object'] = augmention
473 if status:
474 outDict['status'] = status
475
476 if self.genRules['text'] and description:
477 outDict['description'] = description
478
479 self.regSym(name, outDict, parentOid)
480 # TODO
481 # if fakeSyms: # fake symbols for INDEX to support SMIv1
482 # for i in range(len(fakeSyms)):
483 # fakeOutStr = fakeStrlist[i] % oidStr
484 # self.regSym(fakeSyms[i], fakeOutStr, name)
485
486 return outDict
487
488 # noinspection PyUnusedLocal
489 def genTrapType(self, data):
490 name, enterprise, variables, description, reference, value = data
491
492 label = self.genLabel(name)
493 name = self.transOpers(name)
494
495 enterpriseStr, parentOid = enterprise
496
497 outDict = OrderedDict()
498 outDict['name'] = name
499 outDict['oid'] = enterpriseStr + '0.' + str(value)
500 outDict['class'] = 'notificationtype'
501
502 if variables:
503 outDict['objects'] = [{'module': self._importMap.get(obj, self.moduleName[0]), 'object': self.transOpers(obj)} for obj in variables]
504
505 if self.genRules['text'] and description:
506 outDict['description'] = description
507
508 if self.genRules['text'] and reference:
509 outDict['reference'] = reference
510
511 self.regSym(name, outDict, parentOid)
512
513 return outDict
514
515 # noinspection PyUnusedLocal
516 def genTypeDeclaration(self, data):
517 name, declaration = data
518
519 outDict = OrderedDict()
520 outDict['name'] = name
521 outDict['class'] = 'type'
522
523 if declaration:
524 parentType, attrs = declaration
525 if parentType: # skipping SEQUENCE case
526 name = self.transOpers(name)
527 outDict.update(attrs)
528 self.regSym(name, outDict)
529
530 return outDict
531
532 # noinspection PyUnusedLocal
533 def genValueDeclaration(self, data):
534 name, oid = data
535
536 label = self.genLabel(name)
537 name = self.transOpers(name)
538
539 oidStr, parentOid = oid
540 outDict = OrderedDict()
541 outDict['name'] = name
542 outDict['oid'] = oidStr
543 outDict['class'] = 'objectidentity'
544
545 self.regSym(name, outDict, parentOid)
546
547 return outDict
548
549 # Subparts generation functions
550
551 # noinspection PyMethodMayBeStatic,PyUnusedLocal
552 def genBitNames(self, data):
553 names = data[0]
554 return names
555
556 def genBits(self, data):
557 bits = data[0]
558
559 outDict = OrderedDict()
560 outDict['type'] = 'Bits'
561 outDict['class'] = 'type'
562 outDict['bits'] = OrderedDict()
563
564 for name, bit in sorted(bits, key=lambda x: x[1]):
565 outDict['bits'][name] = bit
566
567 return 'scalar', outDict
568
569 # noinspection PyUnusedLocal
570 def genCompliances(self, data):
571 compliances = []
572
573 for complianceModule in data[0]:
574 name = complianceModule[0] or self.moduleName[0]
575 compliances += [{'object': self.transOpers(compl), 'module': name} for compl in complianceModule[1]]
576
577 return compliances
578
579 # noinspection PyUnusedLocal
580 def genConceptualTable(self, data):
581 row = data[0]
582
583 if row[1] and row[1][-2:] == '()':
584 row = row[1][:-2]
585 self._rows.add(row)
586
587 return 'table', ''
588
589 # noinspection PyMethodMayBeStatic,PyUnusedLocal
590 def genContactInfo(self, data):
591 text = data[0]
592 return self.textFilter('contact-info', text)
593
594 # noinspection PyUnusedLocal
595 def genDisplayHint(self, data):
596 return data[0]
597
598 # noinspection PyUnusedLocal
599 def genDefVal(self, data, objname=None):
600 if not data:
601 return {}
602 if not objname:
603 return data
604
605 outDict = OrderedDict()
606
607 defval = data[0]
608 defvalType = self.getBaseType(objname, self.moduleName[0])
609
610 if isinstance(defval, (int, long)): # number
611 outDict.update(value=defval, format='decimal')
612
613 elif self.isHex(defval): # hex
614 if defvalType[0][0] in ('Integer32', 'Integer'): # common bug in MIBs
615 outDict.update(value=str(int(len(defval) > 3 and defval[1:-2] or '0', 16)), format='hex')
616 else:
617 outDict.update(value=defval[1:-2], format='hex')
618
619 elif self.isBinary(defval): # binary
620 binval = defval[1:-2]
621 if defvalType[0][0] in ('Integer32', 'Integer'): # common bug in MIBs
622 outDict.update(value=str(int(binval or '0', 2)), format='bin')
623 else:
624 hexval = binval and hex(int(binval, 2))[2:] or ''
625 outDict.update(value=hexval, format='hex')
626
627 elif defval[0] == defval[-1] and defval[0] == '"': # quoted string
628 if defval[1:-1] == '' and defvalType != 'OctetString': # common bug
629 # a warning should be here
630 return {} # we will set no default value
631 outDict.update(value=defval[1:-1], format='string')
632
633 else: # symbol (oid as defval) or name for enumeration member
634 if (defvalType[0][0] == 'ObjectIdentifier' and
635 (defval in self.symbolTable[self.moduleName[0]] or defval in self._importMap)): # oid
636
637 module = self._importMap.get(defval, self.moduleName[0])
638
639 try:
640 val = str(self.genNumericOid(self.symbolTable[module][defval]['oid']))
641 outDict.update(value=val, format='oid')
642 except:
643 # or no module if it will be borrowed later
644 raise error.PySmiSemanticError('no symbol "%s" in module "%s"' % (defval, module))
645
646 # enumeration
647 elif (defvalType[0][0] in ('Integer32', 'Integer') and
648 isinstance(defvalType[1], list) and defval in dict(defvalType[1])):
649 outDict.update(value=defval, format='enum')
650
651 elif defvalType[0][0] == 'Bits':
652 defvalBits = []
653
654 bits = dict(defvalType[1])
655
656 for bit in defval:
657 bitValue = bits.get(bit, None)
658 if bitValue is not None:
659 defvalBits.append((bit, bitValue))
660 else:
661 raise error.PySmiSemanticError('no such bit as "%s" for symbol "%s"' % (bit, objname))
662
663 outDict.update(value=self.genBits([defvalBits])[1], format='bits')
664
665 return outDict
666
667 else:
668 raise error.PySmiSemanticError(
669 'unknown type "%s" for defval "%s" of symbol "%s"' % (defvalType, defval, objname))
670
671 return {'default': outDict}
672
673 # noinspection PyMethodMayBeStatic
674 def genDescription(self, data):
675 return self.textFilter('description', data[0])
676
677 # noinspection PyMethodMayBeStatic
678 def genReference(self, data):
679 return self.textFilter('reference', data[0])
680
681 # noinspection PyMethodMayBeStatic
682 def genStatus(self, data):
683 return data[0]
684
685 def genEnumSpec(self, data):
686 items = data[0]
687 return {'enumeration': dict(items)}
688
689 # noinspection PyUnusedLocal
690 def genTableIndex(self, data):
691 def genFakeSyms(fakeidx, idxType):
692 objType = self.typeClasses.get(idxType, idxType)
693 objType = self.transOpers(objType)
694
695 return {'module': self.moduleName[0],
696 object: objType}
697
698 indexes = data[0]
699 idxStrlist, fakeSyms, fakeStrlist = [], [], []
700
701 for idx in indexes:
702 idxName = idx[1]
703 if idxName in self.smiv1IdxTypes: # SMIv1 support
704 idxType = idxName
705 fakeSymStr, idxName = genFakeSyms(self.fakeidx, idxType)
706 fakeStrlist.append(fakeSymStr)
707 fakeSyms.append(idxName)
708 self.fakeidx += 1
709
710 index = OrderedDict()
711 index['module'] = self._importMap.get(idxName, self.moduleName[0])
712 index['object'] = idxName
713 idxStrlist.append(index)
714
715 return idxStrlist, fakeStrlist, fakeSyms
716
717 def genIntegerSubType(self, data):
718 ranges = []
719 for rng in data[0]:
720 vmin, vmax = len(rng) == 1 and (rng[0], rng[0]) or rng
721 vmin, vmax = self.str2int(vmin), self.str2int(vmax)
722 ran = OrderedDict()
723 ran['min'] = vmin
724 ran['max'] = vmax
725 ranges.append(ran)
726
727 return {'range': ranges}
728
729 # noinspection PyMethodMayBeStatic,PyUnusedLocal
730 def genMaxAccess(self, data):
731 return data[0]
732
733 def genOctetStringSubType(self, data):
734 sizes = []
735 for rng in data[0]:
736 vmin, vmax = len(rng) == 1 and (rng[0], rng[0]) or rng
737 vmin, vmax = self.str2int(vmin), self.str2int(vmax)
738
739 size = OrderedDict()
740 size['min'] = vmin
741 size['max'] = vmax
742 sizes.append(size)
743
744 return {'size': sizes}
745
746 # noinspection PyUnusedLocal
747 def genOid(self, data):
748 out = ()
749 parent = ''
750 for el in data[0]:
751 if isinstance(el, (str, unicode)):
752 parent = self.transOpers(el)
753 out += ((parent, self._importMap.get(parent, self.moduleName[0])),)
754
755 elif isinstance(el, (int, long)):
756 out += (el,)
757
758 elif isinstance(el, tuple):
759 out += (el[1],) # XXX Do we need to create a new object el[0]?
760
761 else:
762 raise error.PySmiSemanticError('unknown datatype for OID: %s' % el)
763
764 return '.'.join([str(x) for x in self.genNumericOid(out)]), parent
765
766 # noinspection PyUnusedLocal
767 def genObjects(self, data):
768 if data[0]:
769 return [self.transOpers(obj) for obj in data[0]] # XXX self.transOpers or not??
770 return []
771
772 # noinspection PyMethodMayBeStatic,PyUnusedLocal
773 def genTime(self, data):
774 times = []
775 for timeStr in data:
776
777 if len(timeStr) == 11:
778 timeStr = '19' + timeStr
779
780 # XXX raise in strict mode
781 # elif lenTimeStr != 13:
782 # raise error.PySmiSemanticError("Invalid date %s" % t)
783 try:
784 times.append(strftime('%Y-%m-%d %H:%M', strptime(timeStr, '%Y%m%d%H%MZ')))
785
786 except ValueError:
787 # XXX raise in strict mode
788 # raise error.PySmiSemanticError("Invalid date %s: %s" % (t, sys.exc_info()[1]))
789 timeStr = '197001010000Z' # dummy date for dates with typos
790 times.append(strftime('%Y-%m-%d %H:%M', strptime(timeStr, '%Y%m%d%H%MZ')))
791
792 return times
793
794 # noinspection PyMethodMayBeStatic,PyUnusedLocal
795 def genLastUpdated(self, data):
796 return data[0]
797
798 # noinspection PyMethodMayBeStatic,PyUnusedLocal
799 def genOrganization(self, data):
800 return self.textFilter('organization', data[0])
801
802 # noinspection PyUnusedLocal
803 def genRevisions(self, data):
804 revisions = []
805 for x in data[0]:
806 revision = OrderedDict()
807 revision['revision'] = self.genTime([x[0]])[0]
808 revision['description'] = self.textFilter('description', x[1][1])
809 revisions.append(revision)
810 return revisions
811
812 def genRow(self, data):
813 row = data[0]
814 row = self.transOpers(row)
815
816 return row in self.symbolTable[self.moduleName[0]]['_symtable_rows'] and (
817 'row', '') or self.genSimpleSyntax(data)
818
819 # noinspection PyUnusedLocal
820 def genSequence(self, data):
821 cols = data[0]
822 self._cols.update(cols)
823 return '', ''
824
825 def genSimpleSyntax(self, data):
826 objType = data[0]
827 objType = self.typeClasses.get(objType, objType)
828 objType = self.transOpers(objType)
829
830 subtype = len(data) == 2 and data[1] or {}
831
832 outDict = OrderedDict()
833 outDict['type'] = objType
834 outDict['class'] = 'type'
835
836 if subtype:
837 outDict['constraints'] = subtype
838
839 return 'scalar', outDict
840
841 # noinspection PyUnusedLocal
842 def genTypeDeclarationRHS(self, data):
843 if len(data) == 1:
844 parentType, attrs = data[0]
845
846 outDict = OrderedDict()
847 if not attrs:
848 return outDict
849 # just syntax
850 outDict['type'] = attrs
851
852 else:
853 # Textual convention
854 display, status, description, reference, syntax = data
855 parentType, attrs = syntax
856
857 outDict = OrderedDict()
858 outDict['type'] = attrs
859 outDict['class'] = 'textualconvention'
860 if display:
861 outDict['displayhint'] = display
862 if status:
863 outDict['status'] = status
864 if self.genRules['text'] and description:
865 outDict['description'] = description
866 if self.genRules['text'] and reference:
867 outDict['reference'] = reference
868
869 return parentType, outDict
870
871 # noinspection PyMethodMayBeStatic,PyUnusedLocal
872 def genUnits(self, data):
873 text = data[0]
874 return self.textFilter('units', text)
875
876 handlersTable = {
877 'agentCapabilitiesClause': genAgentCapabilities,
878 'moduleIdentityClause': genModuleIdentity,
879 'moduleComplianceClause': genModuleCompliance,
880 'notificationGroupClause': genNotificationGroup,
881 'notificationTypeClause': genNotificationType,
882 'objectGroupClause': genObjectGroup,
883 'objectIdentityClause': genObjectIdentity,
884 'objectTypeClause': genObjectType,
885 'trapTypeClause': genTrapType,
886 'typeDeclaration': genTypeDeclaration,
887 'valueDeclaration': genValueDeclaration,
888
889 'ApplicationSyntax': genSimpleSyntax,
890 'BitNames': genBitNames,
891 'BITS': genBits,
892 'ComplianceModules': genCompliances,
893 'conceptualTable': genConceptualTable,
894 'CONTACT-INFO': genContactInfo,
895 'DISPLAY-HINT': genDisplayHint,
896 'DEFVAL': genDefVal,
897 'DESCRIPTION': genDescription,
898 'REFERENCE': genReference,
899 'Status': genStatus,
900 'enumSpec': genEnumSpec,
901 'INDEX': genTableIndex,
902 'integerSubType': genIntegerSubType,
903 'MaxAccessPart': genMaxAccess,
904 'Notifications': genObjects,
905 'octetStringSubType': genOctetStringSubType,
906 'objectIdentifier': genOid,
907 'Objects': genObjects,
908 'LAST-UPDATED': genLastUpdated,
909 'ORGANIZATION': genOrganization,
910 'Revisions': genRevisions,
911 'row': genRow,
912 'SEQUENCE': genSequence,
913 'SimpleSyntax': genSimpleSyntax,
914 'typeDeclarationRHS': genTypeDeclarationRHS,
915 'UNITS': genUnits,
916 'VarTypes': genObjects,
917 # 'a': lambda x: genXXX(x, 'CONSTRAINT')
918 }
919
920 def genCode(self, ast, symbolTable, **kwargs):
921 self.genRules['text'] = kwargs.get('genTexts', False)
922 self.textFilter = kwargs.get('textFilter') or (lambda symbol, text: re.sub('\s+', ' ', text))
923 self.symbolTable = symbolTable
924 self._rows.clear()
925 self._cols.clear()
926 self._seenSyms.clear()
927 self._importMap.clear()
928 self._out.clear()
929 self._moduleIdentityOid = None
930 self._enterpriseOid = None
931 self._oids = set()
932 self._complianceOids = []
933 self.moduleName[0], moduleOid, imports, declarations = ast
934
935 outDict, importedModules = self.genImports(imports and imports or {})
936
937 for declr in declarations or []:
938 if declr:
939 self.handlersTable[declr[0]](self, self.prepData(declr[1:]))
940
941 for sym in self.symbolTable[self.moduleName[0]]['_symtable_order']:
942 if sym not in self._out:
943 raise error.PySmiCodegenError('No generated code for symbol %s' % sym)
944
945 outDict[sym] = self._out[sym]
946
947 if 'comments' in kwargs:
948 outDict['meta'] = OrderedDict()
949 outDict['meta']['comments'] = kwargs['comments']
950 outDict['meta']['module'] = self.moduleName[0]
951
952 debug.logger & debug.flagCodegen and debug.logger(
953 'canonical MIB name %s (%s), imported MIB(s) %s, Python code size %s bytes' % (
954 self.moduleName[0], moduleOid, ','.join(importedModules) or '<none>', len(outDict)))
955
956 return MibInfo(oid=moduleOid,
957 identity=self._moduleIdentityOid,
958 name=self.moduleName[0],
959 oids=self._oids,
960 enterprise=self._enterpriseOid,
961 compliance=self._complianceOids,
962 imported=tuple([x for x in importedModules if x not in self.fakeMibs])), json.dumps(outDict, indent=2)
963
964 def genIndex(self, processed, **kwargs):
965 outDict = {
966 'meta': {},
967 'identity': {},
968 'enterprise': {},
969 'compliance': {},
970 'oids': {},
971 }
972 if kwargs.get('old_index_data'):
973 try:
974 outDict.update(
975 json.loads(kwargs['old_index_data'])
976 )
977
978 except Exception:
979 raise error.PySmiCodegenError('Index load error: %s' % sys.exc_info()[1])
980
981 def order(top):
982 if isinstance(top, dict):
983 new_top = OrderedDict()
984 try:
985 # first try to sort keys as OIDs
986 for k in sorted(top, key=lambda x: [int(y) for y in x.split('.')]):
987 new_top[k] = order(top[k])
988
989 except ValueError:
990 for k in sorted(top):
991 new_top[k] = order(top[k])
992
993 return new_top
994 elif isinstance(top, list):
995 new_top = []
996 for e in sorted(set(top)):
997 new_top.append(order(e))
998
999 return new_top
1000
1001 return top
1002
1003 for module, status in processed.items():
1004 modData = outDict['identity']
1005 identity_oid = getattr(status, 'identity', None)
1006 if identity_oid:
1007 if identity_oid not in modData:
1008 modData[identity_oid] = []
1009
1010 modData[identity_oid].append(module)
1011
1012 modData = outDict['enterprise']
1013 enterprise_oid = getattr(status, 'enterprise', None)
1014 if enterprise_oid:
1015 if enterprise_oid not in modData:
1016 modData[enterprise_oid] = []
1017
1018 modData[enterprise_oid].append(module)
1019
1020 modData = outDict['compliance']
1021 compliance_oids = getattr(status, 'compliance', ())
1022 for compliance_oid in compliance_oids:
1023 if compliance_oid not in modData:
1024 modData[compliance_oid] = []
1025 modData[compliance_oid].append(module)
1026
1027 modData = outDict['oids']
1028 objects_oids = getattr(status, 'oids', ())
1029 for object_oid in objects_oids:
1030 if object_oid not in modData:
1031 modData[object_oid] = []
1032
1033 modData[object_oid].append(module)
1034
1035 if modData:
1036 unique_prefixes = {}
1037 for oid in sorted(modData, key=lambda x: x.count('.')):
1038 for oid_prefix, modules in unique_prefixes.items():
1039 if oid.startswith(oid_prefix) and set(modules).issuperset(modData[oid]):
1040 break
1041 else:
1042 unique_prefixes[oid] = modData[oid]
1043
1044 outDict['oids'] = unique_prefixes
1045
1046 if 'comments' in kwargs:
1047 outDict['meta']['comments'] = kwargs['comments']
1048
1049 debug.logger & debug.flagCodegen and debug.logger(
1050 'OID->MIB index built, %s entries' % len(processed))
1051
1052 return json.dumps(order(outDict), indent=2)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi.mibinfo import MibInfo
7 from pysmi.codegen.base import AbstractCodeGen
8 from pysmi import debug
9
10
11 class NullCodeGen(AbstractCodeGen):
12 """Dummy code generation backend.
13
14 Could be used for disabling code generation at *MibCompiler*.
15 """
16
17 def genCode(self, ast, symbolTable, **kwargs):
18 debug.logger & debug.flagCodegen and debug.logger('%s invoked' % self.__class__.__name__)
19 return MibInfo(oid=None, name='', imported=[]), ''
20
21 def genIndex(self, mibsMap, **kwargs):
22 return ''
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 import re
8 from time import strptime, strftime
9 from keyword import iskeyword
10 from pysmi.mibinfo import MibInfo
11 from pysmi.codegen.base import AbstractCodeGen, dorepr
12 from pysmi import error
13 from pysmi import debug
14
15
16 if sys.version_info[0] > 2:
17 # noinspection PyShadowingBuiltins
18 unicode = str
19 # noinspection PyShadowingBuiltins
20 long = int
21
22
23 class PySnmpCodeGen(AbstractCodeGen):
24 """Builds PySNMP-specific Python code representing MIB module supplied
25 in form of an Abstract Syntax Tree on input.
26
27 Instance of this class is supposed to be passed to *MibCompiler*,
28 the rest is internal to *MibCompiler*.
29 """
30 defaultMibPackages = ('pysnmp.smi.mibs', 'pysnmp_mibs')
31
32 symsTable = {
33 'MODULE-IDENTITY': ('ModuleIdentity',),
34 'OBJECT-TYPE': ('MibScalar', 'MibTable', 'MibTableRow', 'MibTableColumn'),
35 'NOTIFICATION-TYPE': ('NotificationType',),
36 'TEXTUAL-CONVENTION': ('TextualConvention',),
37 'MODULE-COMPLIANCE': ('ModuleCompliance',),
38 'OBJECT-GROUP': ('ObjectGroup',),
39 'NOTIFICATION-GROUP': ('NotificationGroup',),
40 'AGENT-CAPABILITIES': ('AgentCapabilities',),
41 'OBJECT-IDENTITY': ('ObjectIdentity',),
42 'TRAP-TYPE': ('NotificationType',), # smidump always uses NotificationType
43 'BITS': ('Bits',),
44 }
45
46 constImports = {
47 'ASN1': ('Integer', 'OctetString', 'ObjectIdentifier'),
48 'ASN1-ENUMERATION': ('NamedValues',),
49 'ASN1-REFINEMENT': ('ConstraintsUnion', 'ConstraintsIntersection', 'SingleValueConstraint',
50 'ValueRangeConstraint', 'ValueSizeConstraint'),
51 'SNMPv2-SMI': ('iso',
52 'Bits', # XXX
53 'Integer32', # XXX
54 'TimeTicks', # bug in some IETF MIBs
55 'Counter32', # bug in some IETF MIBs (e.g. DSA-MIB)
56 'Counter64', # bug in some MIBs (e.g.A3COM-HUAWEI-LswINF-MIB)
57 'NOTIFICATION-TYPE', # bug in some MIBs (e.g. A3COM-HUAWEI-DHCPSNOOP-MIB)
58 'Gauge32', # bug in some IETF MIBs (e.g. DSA-MIB)
59 'MODULE-IDENTITY', 'OBJECT-TYPE', 'OBJECT-IDENTITY', 'Unsigned32', 'IpAddress', # XXX
60 'MibIdentifier'), # OBJECT IDENTIFIER
61 'SNMPv2-TC': ('DisplayString', 'TEXTUAL-CONVENTION',), # XXX
62 'SNMPv2-CONF': ('MODULE-COMPLIANCE', 'NOTIFICATION-GROUP',), # XXX
63 }
64
65 # never compile these, they either:
66 # - define MACROs (implementation supplies them)
67 # - or carry conflicting OIDs (so that all IMPORT's of them will be rewritten)
68 # - or have manual fixes
69 # - or import base ASN.1 types from implementation-specific MIBs
70 fakeMibs = ('ASN1',
71 'ASN1-ENUMERATION',
72 'ASN1-REFINEMENT')
73 baseMibs = ('PYSNMP-USM-MIB',
74 'SNMP-FRAMEWORK-MIB',
75 'SNMP-TARGET-MIB',
76 'TRANSPORT-ADDRESS-MIB',
77 'INET-ADDRESS-MIB') + AbstractCodeGen.baseMibs
78
79 baseTypes = ['Integer', 'Integer32', 'Bits', 'ObjectIdentifier', 'OctetString']
80
81 typeClasses = {
82 'COUNTER32': 'Counter32',
83 'COUNTER64': 'Counter64',
84 'GAUGE32': 'Gauge32',
85 'INTEGER': 'Integer32', # XXX
86 'INTEGER32': 'Integer32',
87 'IPADDRESS': 'IpAddress',
88 'NETWORKADDRESS': 'IpAddress',
89 'OBJECT IDENTIFIER': 'ObjectIdentifier',
90 'OCTET STRING': 'OctetString',
91 'OPAQUE': 'Opaque',
92 'TIMETICKS': 'TimeTicks',
93 'UNSIGNED32': 'Unsigned32',
94 'Counter': 'Counter32',
95 'Gauge': 'Gauge32',
96 'NetworkAddress': 'IpAddress', # RFC1065-SMI, RFC1155-SMI -> SNMPv2-SMI
97 'nullSpecific': 'zeroDotZero', # RFC1158-MIB -> SNMPv2-SMI
98 'ipRoutingTable': 'ipRouteTable', # RFC1158-MIB -> RFC1213-MIB
99 'snmpEnableAuthTraps': 'snmpEnableAuthenTraps' # RFC1158-MIB -> SNMPv2-MIB
100 }
101
102 smiv1IdxTypes = ['INTEGER', 'OCTET STRING', 'IPADDRESS', 'NETWORKADDRESS']
103
104 ifTextStr = 'if mibBuilder.loadTexts: '
105 indent = ' ' * 4
106 fakeidx = 1000 # starting index for fake symbols
107
108 def __init__(self):
109 self._snmpTypes = set(self.typeClasses.values())
110 self._snmpTypes.add('Bits')
111 self._rows = set()
112 self._cols = {} # k, v = name, datatype
113 self._exports = set()
114 self._seenSyms = set()
115 self._importMap = {}
116 self._out = {} # k, v = name, generated code
117 self._moduleIdentityOid = None
118 self.moduleName = ['DUMMY']
119 self.genRules = {'text': True}
120 self.symbolTable = {}
121
122 def symTrans(self, symbol):
123 if symbol in self.symsTable:
124 return self.symsTable[symbol]
125
126 return symbol,
127
128 @staticmethod
129 def transOpers(symbol):
130 if iskeyword(symbol):
131 symbol = 'pysmi_' + symbol
132
133 return symbol.replace('-', '_')
134
135 def prepData(self, pdata, classmode=False):
136 data = []
137
138 for el in pdata:
139 if not isinstance(el, tuple):
140 data.append(el)
141
142 elif len(el) == 1:
143 data.append(el[0])
144
145 else:
146 data.append(
147 self.handlersTable[el[0]](self, self.prepData(el[1:], classmode=classmode), classmode=classmode)
148 )
149
150 return data
151
152 def genImports(self, imports):
153 outStr = ''
154
155 # conversion to SNMPv2
156 toDel = []
157 for module in list(imports):
158
159 if module in self.convertImportv2:
160
161 for symbol in imports[module]:
162
163 if symbol in self.convertImportv2[module]:
164 toDel.append((module, symbol))
165
166 for newImport in self.convertImportv2[module][symbol]:
167 newModule, newSymbol = newImport
168
169 if newModule in imports:
170 imports[newModule].append(newSymbol)
171 else:
172 imports[newModule] = [newSymbol]
173
174 # removing converted symbols
175 for d in toDel:
176 imports[d[0]].remove(d[1])
177
178 # merging mib and constant imports
179 for module in self.constImports:
180 if module in imports:
181 imports[module] += self.constImports[module]
182 else:
183 imports[module] = self.constImports[module]
184
185 for module in sorted(imports):
186 symbols = ()
187
188 for symbol in set(imports[module]):
189 symbols += self.symTrans(symbol)
190
191 if symbols:
192 self._seenSyms.update([self.transOpers(s) for s in symbols])
193 self._importMap.update([(self.transOpers(s), module) for s in symbols])
194
195 outStr += ', '.join([self.transOpers(s) for s in symbols])
196 if len(symbols) < 2:
197 outStr += ','
198 outStr += ' = mibBuilder.importSymbols("%s")\n' % ('", "'.join((module,) + symbols))
199
200 return outStr, tuple(sorted(imports))
201
202 def genExports(self, ):
203 exports = list(self._exports)
204 if not exports:
205 return ''
206
207 numFuncCalls = len(exports) // 254 + 1
208
209 outStr = ''
210
211 for idx in range(numFuncCalls):
212 outStr += 'mibBuilder.exportSymbols("' + self.moduleName[0] + '", '
213 outStr += ', '.join(exports[254 * idx:254 * (idx + 1)]) + ')\n'
214
215 return outStr
216
217 # noinspection PyMethodMayBeStatic
218 def genLabel(self, symbol, classmode=False):
219 if '-' in symbol or iskeyword(symbol):
220 return classmode and 'label = "' + symbol + '"\n' or '.setLabel("' + symbol + '")'
221
222 return ''
223
224 def addToExports(self, symbol, moduleIdentity=0):
225 if moduleIdentity:
226 self._exports.add('PYSNMP_MODULE_ID=%s' % symbol)
227
228 self._exports.add('%s=%s' % (symbol, symbol))
229 self._seenSyms.add(symbol)
230
231 # noinspection PyUnusedLocal
232 def regSym(self, symbol, outStr, oidStr=None, moduleIdentity=False):
233 if symbol in self._seenSyms and symbol not in self._importMap:
234 raise error.PySmiSemanticError('Duplicate symbol found: %s' % symbol)
235
236 self.addToExports(symbol, moduleIdentity)
237 self._out[symbol] = outStr
238
239 if moduleIdentity:
240 if self._moduleIdentityOid:
241 raise error.PySmiSemanticError('Duplicate module identity')
242 # TODO: turning literal tuple into a string - hackerish
243 self._moduleIdentityOid = '.'.join(oidStr.split(', '))[1:-1]
244
245 def genNumericOid(self, oid):
246 numericOid = ()
247
248 for part in oid:
249 if isinstance(part, tuple):
250 parent, module = part
251
252 if parent == 'iso':
253 numericOid += (1,)
254 continue
255
256 if module not in self.symbolTable:
257 # XXX do getname for possible future borrowed mibs
258 raise error.PySmiSemanticError('no module "%s" in symbolTable' % module)
259
260 if parent not in self.symbolTable[module]:
261 raise error.PySmiSemanticError('no symbol "%s" in module "%s"' % (parent, module))
262
263 numericOid += self.genNumericOid(self.symbolTable[module][parent]['oid'])
264
265 else:
266 numericOid += (part,)
267
268 return numericOid
269
270 def getBaseType(self, symName, module):
271 if module not in self.symbolTable:
272 raise error.PySmiSemanticError('no module "%s" in symbolTable' % module)
273
274 if symName not in self.symbolTable[module]:
275 raise error.PySmiSemanticError('no symbol "%s" in module "%s"' % (symName, module))
276
277 symType, symSubtype = self.symbolTable[module][symName].get('syntax', (('', ''), ''))
278
279 if not symType[0]:
280 raise error.PySmiSemanticError('unknown type for symbol "%s"' % symName)
281
282 if symType[0] in self.baseTypes:
283 return symType, symSubtype
284
285 else:
286 baseSymType, baseSymSubtype = self.getBaseType(*symType)
287
288 if isinstance(baseSymSubtype, list):
289 if isinstance(symSubtype, list):
290 symSubtype += baseSymSubtype
291 else:
292 symSubtype = baseSymSubtype
293
294 return baseSymType, symSubtype
295
296 # Clause generation functions
297
298 # noinspection PyUnusedLocal
299 def genAgentCapabilities(self, data, classmode=False):
300 name, productRelease, status, description, reference, oid = data
301
302 label = self.genLabel(name)
303 name = self.transOpers(name)
304
305 oidStr, parentOid = oid
306 outStr = name + ' = AgentCapabilities(' + oidStr + ')' + label + '\n'
307
308 if productRelease:
309 outStr += """\
310 if getattr(mibBuilder, 'version', (0, 0, 0)) > (4, 4, 0):
311 %(name)s = %(name)s%(productRelease)s
312 """ % dict(name=name, productRelease=productRelease)
313
314 if status:
315 outStr += """\
316 if getattr(mibBuilder, 'version', (0, 0, 0)) > (4, 4, 0):
317 %(name)s = %(name)s%(status)s
318 """ % dict(name=name, status=status)
319
320 if self.genRules['text'] and description:
321 outStr += self.ifTextStr + name + description + '\n'
322
323 if self.genRules['text'] and reference:
324 outStr += name + reference + '\n'
325
326 self.regSym(name, outStr, oidStr)
327
328 return outStr
329
330 # noinspection PyUnusedLocal
331 def genModuleIdentity(self, data, classmode=False):
332 name, lastUpdated, organization, contactInfo, description, revisionsAndDescrs, oid = data
333
334 label = self.genLabel(name)
335 name = self.transOpers(name)
336
337 oidStr, parentOid = oid
338
339 outStr = name + ' = ModuleIdentity(' + oidStr + ')' + label + '\n'
340
341 if revisionsAndDescrs:
342 revisions, descriptions = revisionsAndDescrs
343
344 if revisions:
345 outStr += name + revisions + '\n'
346
347 if self.genRules['text'] and descriptions:
348 outStr += """
349 if getattr(mibBuilder, 'version', (0, 0, 0)) > (4, 4, 0):
350 %(ifTextStr)s%(name)s%(descriptions)s
351 """ % dict(ifTextStr=self.ifTextStr, name=name, descriptions=descriptions)
352
353 if lastUpdated:
354 outStr += self.ifTextStr + name + lastUpdated + '\n'
355
356 if organization:
357 outStr += self.ifTextStr + name + organization + '\n'
358
359 if self.genRules['text'] and contactInfo:
360 outStr += self.ifTextStr + name + contactInfo + '\n'
361
362 if self.genRules['text'] and description:
363 outStr += self.ifTextStr + name + description + '\n'
364
365 self.regSym(name, outStr, oidStr, moduleIdentity=True)
366
367 return outStr
368
369 # noinspection PyUnusedLocal
370 def genModuleCompliance(self, data, classmode=False):
371 name, status, description, reference, compliances, oid = data
372
373 label = self.genLabel(name)
374 name = self.transOpers(name)
375
376 oidStr, parentOid = oid
377 outStr = name + ' = ModuleCompliance(' + oidStr + ')' + label
378 outStr += compliances + '\n'
379
380 if status:
381 outStr += """\
382 if getattr(mibBuilder, 'version', (0, 0, 0)) > (4, 4, 0):
383 %(name)s = %(name)s%(status)s
384 """ % dict(name=name, status=status)
385
386 if self.genRules['text'] and description:
387 outStr += self.ifTextStr + name + description + '\n'
388
389 if self.genRules['text'] and reference:
390 outStr += self.ifTextStr + name + reference + '\n'
391
392 self.regSym(name, outStr, oidStr)
393
394 return outStr
395
396 # noinspection PyUnusedLocal
397 def genNotificationGroup(self, data, classmode=False):
398 name, objects, status, description, reference, oid = data
399
400 label = self.genLabel(name)
401 name = self.transOpers(name)
402
403 oidStr, parentOid = oid
404
405 outStr = name + ' = NotificationGroup(' + oidStr + ')' + label
406
407 if objects:
408 objects = ['("' + self._importMap.get(obj, self.moduleName[0]) + '", "' + self.transOpers(obj) + '")'
409 for obj in objects]
410
411 numFuncCalls = len(objects) // 255 + 1
412
413 if numFuncCalls > 1:
414 objStrParts = []
415
416 for idx in range(numFuncCalls):
417 objStrParts.append('[' + ', '.join(objects[255 * idx:255 * (idx + 1)]) + ']')
418
419 outStr += """
420 for _%(name)s_obj in [%(objects)s]:
421 if getattr(mibBuilder, 'version', 0) < (4, 4, 2):
422 # WARNING: leading objects get lost here! Upgrade your pysnmp version!
423 %(name)s = %(name)s.setObjects(*_%(name)s_obj)
424 else:
425 %(name)s = %(name)s.setObjects(*_%(name)s_obj, **dict(append=True))\
426 """ % dict(name=name, objects=', '.join(objStrParts))
427
428 else:
429 outStr += '.setObjects(' + ', '.join(objects) + ')'
430
431 outStr += '\n'
432
433 if status:
434 outStr += """\
435 if getattr(mibBuilder, 'version', (0, 0, 0)) > (4, 4, 0):
436 %(name)s = %(name)s%(status)s
437 """ % dict(name=name, status=status)
438
439 if self.genRules['text'] and description:
440 outStr += self.ifTextStr + name + description + '\n'
441
442 if self.genRules['text'] and reference:
443 outStr += name + reference + '\n'
444
445 self.regSym(name, outStr, oidStr)
446
447 return outStr
448
449 # noinspection PyUnusedLocal
450 def genNotificationType(self, data, classmode=False):
451 name, objects, status, description, reference, oid = data
452
453 label = self.genLabel(name)
454 name = self.transOpers(name)
455
456 oidStr, parentOid = oid
457
458 outStr = name + ' = NotificationType(' + oidStr + ')' + label
459
460 if objects:
461 objects = ['("' + self._importMap.get(obj, self.moduleName[0]) + '", "' + self.transOpers(obj) + '")'
462 for obj in objects]
463
464 numFuncCalls = len(objects) // 255 + 1
465
466 if numFuncCalls > 1:
467 objStrParts = []
468
469 for idx in range(numFuncCalls):
470 objStrParts.append('[' + ', '.join(objects[255 * idx:255 * (idx + 1)]) + ']')
471
472 outStr += """
473 for _%(name)s_obj in [%(objects)s]:
474 if getattr(mibBuilder, 'version', 0) < (4, 4, 2):
475 # WARNING: leading objects get lost here! Upgrade your pysnmp version!
476 %(name)s = %(name)s.setObjects(*_%(name)s_obj)
477 else:
478 %(name)s = %(name)s.setObjects(*_%(name)s_obj, **dict(append=True))\
479 """ % dict(name=name, objects=', '.join(objStrParts))
480
481 else:
482 outStr += '.setObjects(' + ', '.join(objects) + ')'
483
484 outStr += '\n'
485
486 if status:
487 outStr += self.ifTextStr + name + status + '\n'
488
489 if self.genRules['text'] and description:
490 outStr += self.ifTextStr + name + description + '\n'
491
492 if self.genRules['text'] and reference:
493 outStr += self.ifTextStr + name + reference + '\n'
494
495 self.regSym(name, outStr, oidStr)
496
497 return outStr
498
499 # noinspection PyUnusedLocal
500 def genObjectGroup(self, data, classmode=False):
501 name, objects, status, description, reference, oid = data
502
503 label = self.genLabel(name)
504 name = self.transOpers(name)
505
506 oidStr, parentOid = oid
507
508 outStr = name + ' = ObjectGroup(' + oidStr + ')' + label
509
510 if objects:
511 objects = ['("' + self._importMap.get(obj, self.moduleName[0]) + '", "' + self.transOpers(obj) + '")'
512 for obj in objects]
513
514 numFuncCalls = len(objects) // 255 + 1
515
516 if numFuncCalls > 1:
517 objStrParts = []
518
519 for idx in range(numFuncCalls):
520 objStrParts.append('[' + ', '.join(objects[255 * idx:255 * (idx + 1)]) + ']')
521
522 outStr += """
523 for _%(name)s_obj in [%(objects)s]:
524 if getattr(mibBuilder, 'version', 0) < (4, 4, 2):
525 # WARNING: leading objects get lost here!
526 %(name)s = %(name)s.setObjects(*_%(name)s_obj)
527 else:
528 %(name)s = %(name)s.setObjects(*_%(name)s_obj, **dict(append=True))\
529 """ % dict(name=name, objects=', '.join(objStrParts))
530
531 else:
532 outStr += '.setObjects(' + ', '.join(objects) + ')'
533
534 outStr += '\n'
535
536 if status:
537 outStr += """\
538 if getattr(mibBuilder, 'version', (0, 0, 0)) > (4, 4, 0):
539 %(name)s = %(name)s%(status)s
540 """ % dict(name=name, status=status)
541
542 if self.genRules['text'] and description:
543 outStr += self.ifTextStr + name + description + '\n'
544
545 if self.genRules['text'] and reference:
546 outStr += self.ifTextStr + name + reference + '\n'
547
548 self.regSym(name, outStr, oidStr)
549
550 return outStr
551
552 # noinspection PyUnusedLocal
553 def genObjectIdentity(self, data, classmode=False):
554 name, status, description, reference, oid = data
555
556 label = self.genLabel(name)
557 name = self.transOpers(name)
558
559 oidStr, parentOid = oid
560 outStr = name + ' = ObjectIdentity(' + oidStr + ')' + label + '\n'
561
562 if status:
563 outStr += self.ifTextStr + name + status + '\n'
564
565 if self.genRules['text'] and description:
566 outStr += self.ifTextStr + name + description + '\n'
567
568 if self.genRules['text'] and reference:
569 outStr += self.ifTextStr + name + reference + '\n'
570
571 self.regSym(name, outStr, oidStr)
572
573 return outStr
574
575 # noinspection PyUnusedLocal
576 def genObjectType(self, data, classmode=False):
577 name, syntax, units, maxaccess, status, description, reference, augmention, index, defval, oid = data
578
579 label = self.genLabel(name)
580 name = self.transOpers(name)
581
582 oidStr, parentOid = oid
583
584 indexStr, fakeStrlist, fakeSyms = index or ('', '', [])
585 subtype = syntax[0] == 'Bits' and 'Bits()' + syntax[1] or syntax[1] # Bits hack #1
586
587 classtype = self.typeClasses.get(syntax[0], syntax[0])
588 classtype = self.transOpers(classtype)
589 classtype = syntax[0] == 'Bits' and 'MibScalar' or classtype # Bits hack #2
590 classtype = name in self.symbolTable[self.moduleName[0]]['_symtable_cols'] and 'MibTableColumn' or classtype
591
592 defval = self.genDefVal(defval, objname=name)
593
594 outStr = name + ' = ' + classtype + '(' + oidStr + ', ' + subtype + (defval or '') + ')' + label
595 outStr += units or ''
596 outStr += maxaccess or ''
597 outStr += indexStr or ''
598 outStr += '\n'
599
600 if self.genRules['text'] and reference:
601 outStr += self.ifTextStr + name + reference + '\n'
602
603 if augmention:
604 augmention = self.transOpers(augmention)
605 outStr += augmention + '.registerAugmentions(("' + self._importMap.get(name, self.moduleName[0]) + '", "' + name + '"))\n'
606 outStr += name + '.setIndexNames(*' + augmention + '.getIndexNames())\n'
607
608 if status:
609 outStr += self.ifTextStr + name + status + '\n'
610
611 if self.genRules['text'] and description:
612 outStr += self.ifTextStr + name + description + '\n'
613
614 self.regSym(name, outStr, parentOid)
615
616 if fakeSyms: # fake symbols for INDEX to support SMIv1
617 for idx, fakeSym in enumerate(fakeSyms):
618 fakeOutStr = fakeStrlist[idx] % oidStr
619 self.regSym(fakeSym, fakeOutStr, oidStr)
620
621 return outStr
622
623 # noinspection PyUnusedLocal
624 def genTrapType(self, data, classmode=False):
625 name, enterprise, objects, description, reference, value = data
626
627 label = self.genLabel(name)
628 name = self.transOpers(name)
629
630 enterpriseStr, parentOid = enterprise
631
632 outStr = name + ' = NotificationType(' + enterpriseStr + ' + (0,' + str(value) + '))' + label
633
634 if objects:
635 objects = ['("' + self._importMap.get(obj, self.moduleName[0]) + '", "' + self.transOpers(obj) + '")'
636 for obj in objects]
637
638 numFuncCalls = len(objects) // 255 + 1
639
640 if numFuncCalls > 1:
641 objStrParts = []
642
643 for idx in range(numFuncCalls):
644 objStrParts.append('[' + ', '.join(objects[255 * idx:255 * (idx + 1)]) + ']')
645
646 outStr += """
647 for _%(name)s_obj in [%(objects)s]:
648 if getattr(mibBuilder, 'version', 0) < (4, 4, 2):
649 # WARNING: leading objects get lost here! Upgrade your pysnmp version!
650 %(name)s = %(name)s.setObjects(*_%(name)s_obj)
651 else:
652 %(name)s = %(name)s.setObjects(*_%(name)s_obj, **dict(append=True))\
653 """ % dict(name=name, objects=', '.join(objStrParts))
654
655 else:
656 outStr += '.setObjects(' + ', '.join(objects) + ')'
657
658 outStr += '\n'
659
660 if self.genRules['text'] and description:
661 outStr += self.ifTextStr + name + description + '\n'
662
663 if self.genRules['text'] and reference:
664 outStr += self.ifTextStr + name + reference + '\n'
665
666 self.regSym(name, outStr, enterpriseStr)
667
668 return outStr
669
670 # noinspection PyUnusedLocal
671 def genTypeDeclaration(self, data, classmode=False):
672 outStr = ''
673
674 name, declaration = data
675
676 if declaration:
677 parentType, attrs = declaration
678 if parentType: # skipping SEQUENCE case
679 name = self.transOpers(name)
680 outStr = 'class ' + name + '(' + parentType + '):\n' + attrs + '\n'
681 self.regSym(name, outStr)
682
683 return outStr
684
685 # noinspection PyUnusedLocal
686 def genValueDeclaration(self, data, classmode=False):
687 name, oid = data
688
689 label = self.genLabel(name)
690 name = self.transOpers(name)
691
692 oidStr, parentOid = oid
693 outStr = name + ' = MibIdentifier(' + oidStr + ')' + label + '\n'
694
695 self.regSym(name, outStr, oidStr)
696
697 return outStr
698
699 # Subparts generation functions
700
701 # noinspection PyMethodMayBeStatic,PyUnusedLocal
702 def ftNames(self, data, classmode=False):
703 names = data[0]
704 return names
705
706 def genBitNames(self, data, classmode=False):
707 names = data[0]
708 return names
709
710 def genBits(self, data, classmode=False):
711 bits = data[0]
712
713 namedval = ['("' + bit[0] + '", ' + str(bit[1]) + ')' for bit in bits]
714
715 numFuncCalls = len(namedval) // 255 + 1
716
717 funcCalls = ''
718 for idx in range(numFuncCalls):
719 funcCalls += 'NamedValues(' + ', '.join(namedval[255 * idx:255 * (idx + 1)]) + ') + '
720
721 funcCalls = funcCalls[:-3]
722
723 outStr = classmode and self.indent + 'namedValues = ' + funcCalls + '\n' or '.clone(namedValues=' + funcCalls + ')'
724
725 return 'Bits', outStr
726
727 # noinspection PyUnusedLocal
728 def genCompliances(self, data, classmode=False):
729 if not data[0]:
730 return ''
731
732 objects = []
733
734 for complianceModule in data[0]:
735 name = complianceModule[0] or self.moduleName[0]
736 objects += ['("' + name + '", "' + self.transOpers(compl) + '")' for compl in complianceModule[1]]
737
738 outStr = ''
739
740 numFuncCalls = len(objects) // 255 + 1
741
742 if numFuncCalls > 1:
743 objStrParts = []
744
745 for idx in range(numFuncCalls):
746 objStrParts.append('[' + ', '.join(objects[255 * idx:255 * (idx + 1)]) + ']')
747
748 outStr += """
749 for _%(name)s_obj in [%(objects)s]:
750 if getattr(mibBuilder, 'version', 0) < (4, 4, 2):
751 # WARNING: leading objects get lost here! Upgrade your pysnmp version!
752 %(name)s = %(name)s.setObjects(*_%(name)s_obj)
753 else:
754 %(name)s = %(name)s.setObjects(*_%(name)s_obj, **dict(append=True))
755
756 """ % dict(name=name, objects=', '.join(objStrParts))
757
758 else:
759 outStr += '.setObjects(' + ', '.join(objects) + ')\n'
760
761 return outStr
762
763 # noinspection PyUnusedLocal
764 def genConceptualTable(self, data, classmode=False):
765 row = data[0]
766 if row[1] and row[1][-2:] == '()':
767 row = row[1][:-2]
768 self._rows.add(row)
769
770 return 'MibTable', ''
771
772 # noinspection PyMethodMayBeStatic,PyUnusedLocal
773 def genContactInfo(self, data, classmode=False):
774 text = self.textFilter('contact-info', data[0])
775 return '.setContactInfo(' + dorepr(text) + ')'
776
777 # noinspection PyUnusedLocal
778 def genDisplayHint(self, data, classmode=False):
779 return self.indent + 'displayHint = ' + dorepr(data[0]) + '\n'
780
781 # noinspection PyUnusedLocal
782 def genDefVal(self, data, classmode=False, objname=None):
783 if not data:
784 return ''
785
786 if not objname:
787 return data
788
789 defval = data[0]
790 defvalType = self.getBaseType(objname, self.moduleName[0])
791
792 if isinstance(defval, (int, long)): # number
793 val = str(defval)
794
795 elif self.isHex(defval): # hex
796 if defvalType[0][0] in ('Integer32', 'Integer'): # common bug in MIBs
797 val = str(int(defval[1:-2], 16))
798 else:
799 val = 'hexValue="' + defval[1:-2] + '"'
800
801 elif self.isBinary(defval): # binary
802 binval = defval[1:-2]
803 if defvalType[0][0] in ('Integer32', 'Integer'): # common bug in MIBs
804 val = str(int(binval or '0', 2))
805 else:
806 hexval = binval and hex(int(binval, 2))[2:] or ''
807 val = 'hexValue="' + hexval + '"'
808
809 elif defval[0] == defval[-1] and defval[0] == '"': # quoted string
810 if defval[1:-1] == '' and defvalType != 'OctetString': # common bug
811 # a warning should be here
812 return False # we will set no default value
813
814 val = dorepr(defval[1:-1])
815
816 else: # symbol (oid as defval) or name for enumeration member
817 if (defvalType[0][0] == 'ObjectIdentifier' and
818 (defval in self.symbolTable[self.moduleName[0]] or defval in self._importMap)): # oid
819 module = self._importMap.get(defval, self.moduleName[0])
820
821 try:
822 val = str(self.genNumericOid(self.symbolTable[module][defval]['oid']))
823 except:
824 # or no module if it will be borrowed later
825 raise error.PySmiSemanticError('no symbol "%s" in module "%s"' % (defval, module))
826
827 # enumeration
828 elif (defvalType[0][0] in ('Integer32', 'Integer') and
829 isinstance(defvalType[1], list) and
830 defval in dict(defvalType[1])):
831 val = dorepr(defval)
832
833 elif defvalType[0][0] == 'Bits':
834 defvalBits = []
835 bits = dict(defvalType[1])
836
837 for bit in defval:
838 bitValue = bits.get(bit, None)
839 if bitValue is not None:
840 defvalBits.append((bit, bitValue))
841 else:
842 raise error.PySmiSemanticError('no such bit as "%s" for symbol "%s"' % (bit, objname))
843
844 return self.genBits([defvalBits])[1]
845
846 else:
847 raise error.PySmiSemanticError(
848 'unknown type "%s" for defval "%s" of symbol "%s"' % (defvalType, defval, objname))
849
850 return '.clone(' + val + ')'
851
852 # noinspection PyMethodMayBeStatic,PyUnusedLocal
853 def genDescription(self, data, classmode=False):
854 text = self.textFilter('description', data[0])
855 return classmode and self.indent + 'description = ' + dorepr(text) + '\n' or '.setDescription(' + dorepr(text) + ')'
856
857 # noinspection PyMethodMayBeStatic
858 def genReference(self, data, classmode=False):
859 text = self.textFilter('reference', data[0])
860 return classmode and self.indent + 'reference = ' + dorepr(text) + '\n' or '.setReference(' + dorepr(text) + ')'
861
862 # noinspection PyMethodMayBeStatic
863 def genStatus(self, data, classmode=False):
864 text = data[0]
865 return classmode and self.indent + 'status = ' + dorepr(text) + '\n' or '.setStatus(' + dorepr(text) + ')'
866
867 # noinspection PyMethodMayBeStatic
868 def genProductRelease(self, data, classmode=False):
869 text = data[0]
870 return classmode and self.indent + 'productRelease = ' + dorepr(text) + '\n' or '.setProductRelease(' + dorepr(text) + ')'
871
872 def genEnumSpec(self, data, classmode=False):
873 items = data[0]
874 singleval = [str(item[1]) for item in items]
875 outStr = classmode and self.indent + 'subtypeSpec = %s.subtypeSpec + ' or '.subtype(subtypeSpec='
876 numFuncCalls = len(singleval) / 255 + 1
877 singleCall = numFuncCalls == 1
878 funcCalls = ''
879
880 outStr += not singleCall and 'ConstraintsUnion(' or ''
881
882 for idx in range(int(numFuncCalls)):
883 if funcCalls:
884 funcCalls += ', '
885 funcCalls += 'SingleValueConstraint(' + ', '.join(singleval[255 * idx:255 * (idx + 1)]) + ')'
886
887 outStr += funcCalls
888 outStr += not singleCall and (classmode and ')\n' or '))') or (not classmode and ')' or '\n')
889 outStr += self.genBits(data, classmode=classmode)[1]
890
891 return outStr
892
893 # noinspection PyUnusedLocal
894 def genTableIndex(self, data, classmode=False):
895 def genFakeSyms(fakeidx, idxType):
896 fakeSymName = 'pysmiFakeCol%s' % fakeidx
897
898 objType = self.typeClasses.get(idxType, idxType)
899 objType = self.transOpers(objType)
900
901 return (fakeSymName + ' = MibTableColumn(%s + (' + str(fakeidx) +
902 ', ), ' + objType + '())\n', # stub for parentOid
903 fakeSymName)
904
905 indexes = data[0]
906 idxStrlist, fakeSyms, fakeStrlist = [], [], []
907 for idx in indexes:
908 idxName = idx[1]
909 if idxName in self.smiv1IdxTypes: # SMIv1 support
910 idxType = idxName
911
912 fakeSymStr, idxName = genFakeSyms(self.fakeidx, idxType)
913 fakeStrlist.append(fakeSymStr)
914 fakeSyms.append(idxName)
915 self.fakeidx += 1
916
917 idxStrlist.append('(' + str(idx[0]) + ', "' +
918 self._importMap.get(idxName, self.moduleName[0]) +
919 '", "' + idxName + '")')
920
921 return '.setIndexNames(' + ', '.join(idxStrlist) + ')', fakeStrlist, fakeSyms
922
923 def genIntegerSubType(self, data, classmode=False):
924 singleRange = len(data[0]) == 1
925
926 outStr = classmode and self.indent + 'subtypeSpec = %s.subtypeSpec + ' or '.subtype(subtypeSpec='
927 outStr += not singleRange and 'ConstraintsUnion(' or ''
928
929 for rng in data[0]:
930 vmin, vmax = len(rng) == 1 and (rng[0], rng[0]) or rng
931 vmin, vmax = str(self.str2int(vmin)), str(self.str2int(vmax))
932 outStr += 'ValueRangeConstraint(' + vmin + ', ' + vmax + ')' + (not singleRange and ', ' or '')
933
934 outStr += not singleRange and (classmode and ')' or '))') or (not classmode and ')' or '\n')
935
936 return outStr
937
938 # noinspection PyMethodMayBeStatic,PyUnusedLocal
939 def genMaxAccess(self, data, classmode=False):
940 access = data[0].replace('-', '')
941 return access != 'notaccessible' and '.setMaxAccess("' + access + '")' or ''
942
943 def genOctetStringSubType(self, data, classmode=False):
944 singleRange = len(data[0]) == 1
945
946 outStr = classmode and self.indent + 'subtypeSpec = %s.subtypeSpec + ' or '.subtype(subtypeSpec='
947 outStr += not singleRange and 'ConstraintsUnion(' or ''
948
949 for rng in data[0]:
950 vmin, vmax = len(rng) == 1 and (rng[0], rng[0]) or rng
951 vmin, vmax = str(self.str2int(vmin)), str(self.str2int(vmax))
952 outStr += ('ValueSizeConstraint(' + vmin + ', ' + vmax + ')' +
953 (not singleRange and ', ' or ''))
954
955 outStr += not singleRange and (classmode and ')' or '))') or (not classmode and ')' or '\n')
956
957 if data[0]:
958 # noinspection PyUnboundLocalVariable
959 outStr += (singleRange
960 and vmin == vmax
961 and (classmode and self.indent + 'fixedLength = ' + vmin + '\n' or '.setFixedLength(' + vmin + ')') or '')
962
963 return outStr
964
965 # noinspection PyUnusedLocal
966 def genOid(self, data, classmode=False):
967 out = ()
968 parent = ''
969 for el in data[0]:
970 if isinstance(el, (str, unicode)):
971 parent = self.transOpers(el)
972 out += ((parent, self._importMap.get(parent, self.moduleName[0])),)
973
974 elif isinstance(el, (int, long)):
975 out += (el,)
976
977 elif isinstance(el, tuple):
978 out += (el[1],) # XXX Do we need to create a new object el[0]?
979
980 else:
981 raise error.PySmiSemanticError('unknown datatype for OID: %s' % el)
982
983 return str(self.genNumericOid(out)), parent
984
985 # noinspection PyUnusedLocal
986 def genObjects(self, data, classmode=False):
987 if data[0]:
988 return [self.transOpers(obj) for obj in data[0]] # XXX self.transOpers or not??
989 return []
990
991 # noinspection PyMethodMayBeStatic,PyUnusedLocal
992 def genTime(self, data, classmode=False):
993 times = []
994 for timeStr in data:
995 if len(timeStr) == 11:
996 timeStr = '19' + timeStr
997 # XXX raise in strict mode
998 # elif lenTimeStr != 13:
999 # raise error.PySmiSemanticError("Invalid date %s" % t)
1000 try:
1001 times.append(strftime('%Y-%m-%d %H:%M', strptime(timeStr, '%Y%m%d%H%MZ')))
1002
1003 except ValueError:
1004 # XXX raise in strict mode
1005 # raise error.PySmiSemanticError("Invalid date %s: %s" % (t, sys.exc_info()[1]))
1006 timeStr = '197001010000Z' # dummy date for dates with typos
1007 times.append(strftime('%Y-%m-%d %H:%M', strptime(timeStr, '%Y%m%d%H%MZ')))
1008
1009 return times
1010
1011 # noinspection PyMethodMayBeStatic,PyUnusedLocal
1012 def genLastUpdated(self, data, classmode=False):
1013 text = data[0]
1014 return '.setLastUpdated(' + dorepr(text) + ')'
1015
1016 # noinspection PyMethodMayBeStatic,PyUnusedLocal
1017 def genOrganization(self, data, classmode=False):
1018 text = self.textFilter('organization', data[0])
1019 return '.setOrganization(' + dorepr(text) + ')'
1020
1021 # noinspection PyUnusedLocal
1022 def genRevisions(self, data, classmode=False):
1023 times = self.genTime([x[0] for x in data[0]])
1024 times = [dorepr(x) for x in times]
1025
1026 revisions = '.setRevisions((%s,))' % ', '.join(times)
1027
1028 descriptions = '.setRevisionsDescriptions((%s,))' % ', '.join(
1029 [dorepr(self.textFilter('description', x[1][1])) for x in data[0]]
1030 )
1031
1032 return revisions, descriptions
1033
1034 def genRow(self, data, classmode=False):
1035 row = data[0]
1036 row = self.transOpers(row)
1037 return row in self.symbolTable[self.moduleName[0]]['_symtable_rows'] and (
1038 'MibTableRow', '') or self.genSimpleSyntax(data, classmode=classmode)
1039
1040 # noinspection PyUnusedLocal
1041 def genSequence(self, data, classmode=False):
1042 cols = data[0]
1043 self._cols.update(cols)
1044 return '', ''
1045
1046 def genSimpleSyntax(self, data, classmode=False):
1047 objType = data[0]
1048 objType = self.typeClasses.get(objType, objType)
1049 objType = self.transOpers(objType)
1050
1051 subtype = len(data) == 2 and data[1] or ''
1052
1053 if classmode:
1054 subtype = '%s' in subtype and subtype % objType or subtype # XXX hack?
1055 return objType, subtype
1056
1057 outStr = objType + '()' + subtype
1058
1059 return 'MibScalar', outStr
1060
1061 # noinspection PyUnusedLocal
1062 def genTypeDeclarationRHS(self, data, classmode=False):
1063 if len(data) == 1:
1064 parentType, attrs = data[0] # just syntax
1065
1066 else:
1067 # Textual convention
1068 display, status, description, reference, syntax = data
1069 parentType, attrs = syntax
1070
1071 if parentType in self._snmpTypes:
1072 parentType = 'TextualConvention, ' + parentType
1073
1074 if display:
1075 attrs = display + attrs
1076
1077 if status:
1078 attrs = status + attrs
1079
1080 if self.genRules['text'] and description:
1081 attrs = description + attrs
1082
1083 if reference:
1084 attrs = reference + attrs
1085
1086 attrs = attrs or self.indent + 'pass\n'
1087
1088 return parentType, attrs
1089
1090 # noinspection PyMethodMayBeStatic,PyUnusedLocal
1091 def genUnits(self, data, classmode=False):
1092 text = data[0]
1093 return '.setUnits(' + dorepr(self.textFilter('units', text)) + ')'
1094
1095 handlersTable = {
1096 'agentCapabilitiesClause': genAgentCapabilities,
1097 'moduleIdentityClause': genModuleIdentity,
1098 'moduleComplianceClause': genModuleCompliance,
1099 'notificationGroupClause': genNotificationGroup,
1100 'notificationTypeClause': genNotificationType,
1101 'objectGroupClause': genObjectGroup,
1102 'objectIdentityClause': genObjectIdentity,
1103 'objectTypeClause': genObjectType,
1104 'trapTypeClause': genTrapType,
1105 'typeDeclaration': genTypeDeclaration,
1106 'valueDeclaration': genValueDeclaration,
1107
1108 'ApplicationSyntax': genSimpleSyntax,
1109 'BitNames': genBitNames,
1110 'BITS': genBits,
1111 'ComplianceModules': genCompliances,
1112 'conceptualTable': genConceptualTable,
1113 'CONTACT-INFO': genContactInfo,
1114 'DISPLAY-HINT': genDisplayHint,
1115 'DEFVAL': genDefVal,
1116 'DESCRIPTION': genDescription,
1117 'REFERENCE': genReference,
1118 'Status': genStatus,
1119 'PRODUCT-RELEASE': genProductRelease,
1120 'enumSpec': genEnumSpec,
1121 'INDEX': genTableIndex,
1122 'integerSubType': genIntegerSubType,
1123 'MaxAccessPart': genMaxAccess,
1124 'Notifications': genObjects,
1125 'octetStringSubType': genOctetStringSubType,
1126 'objectIdentifier': genOid,
1127 'Objects': genObjects,
1128 'LAST-UPDATED': genLastUpdated,
1129 'ORGANIZATION': genOrganization,
1130 'Revisions': genRevisions,
1131 'row': genRow,
1132 'SEQUENCE': genSequence,
1133 'SimpleSyntax': genSimpleSyntax,
1134 'typeDeclarationRHS': genTypeDeclarationRHS,
1135 'UNITS': genUnits,
1136 'VarTypes': genObjects,
1137 # 'a': lambda x: genXXX(x, 'CONSTRAINT')
1138 }
1139
1140 def genCode(self, ast, symbolTable, **kwargs):
1141 self.genRules['text'] = kwargs.get('genTexts', False)
1142 self.textFilter = kwargs.get('textFilter') or (lambda symbol, text: re.sub('\s+', ' ', text))
1143 self.symbolTable = symbolTable
1144 self._rows.clear()
1145 self._cols.clear()
1146 self._exports.clear()
1147 self._seenSyms.clear()
1148 self._importMap.clear()
1149 self._out.clear()
1150 self._moduleIdentityOid = None
1151 self.moduleName[0], moduleOid, imports, declarations = ast
1152
1153 out, importedModules = self.genImports(imports or {})
1154
1155 for declr in declarations or []:
1156 if declr:
1157 clausetype = declr[0]
1158 classmode = clausetype == 'typeDeclaration'
1159 self.handlersTable[declr[0]](self, self.prepData(declr[1:], classmode), classmode)
1160
1161 for sym in self.symbolTable[self.moduleName[0]]['_symtable_order']:
1162 if sym not in self._out:
1163 raise error.PySmiCodegenError('No generated code for symbol %s' % sym)
1164 out += self._out[sym]
1165
1166 out += self.genExports()
1167
1168 if 'comments' in kwargs:
1169 out = ''.join(['# %s\n' % x for x in kwargs['comments']]) + '#\n' + out
1170 out = '#\n# PySNMP MIB module %s (http://pysnmp.sf.net)\n' % self.moduleName[0] + out
1171
1172 debug.logger & debug.flagCodegen and debug.logger(
1173 'canonical MIB name %s (%s), imported MIB(s) %s, Python code size %s bytes' % (
1174 self.moduleName[0], moduleOid, ','.join(importedModules) or '<none>', len(out)))
1175
1176 return MibInfo(oid=moduleOid,
1177 identity=self._moduleIdentityOid,
1178 name=self.moduleName[0],
1179 oids=[],
1180 enterprise=None,
1181 compliance=[],
1182 imported=tuple([x for x in importedModules if x not in self.fakeMibs])), out
1183
1184 def genIndex(self, processed, **kwargs):
1185 out = '\nfrom pysnmp.proto.rfc1902 import ObjectName\n\noidToMibMap = {\n'
1186 count = 0
1187 for module, status in processed.items():
1188 value = getattr(status, 'oid', None)
1189 if value:
1190 out += 'ObjectName("%s"): "%s",\n' % (value, module)
1191 count += 1
1192 out += '}\n'
1193
1194 if 'comments' in kwargs:
1195 out = ''.join(['# %s\n' % x for x in kwargs['comments']]) + '#\n' + out
1196 out = '#\n# PySNMP MIB indices (http://pysnmp.sf.net)\n' + out
1197
1198 debug.logger & debug.flagCodegen and debug.logger(
1199 'OID->MIB index built, %s entries, %s bytes' % (count, len(out)))
1200
1201 return out
1202
1203 # backward compatibility
1204 baseMibs = PySnmpCodeGen.baseMibs
1205 fakeMibs = PySnmpCodeGen.fakeMibs
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 # Build an internally used symbol table for each passed MIB.
7 #
8 import sys
9 from keyword import iskeyword
10 from pysmi.mibinfo import MibInfo
11 from pysmi.codegen.base import AbstractCodeGen, dorepr
12 from pysmi import error
13 from pysmi import debug
14
15 if sys.version_info[0] > 2:
16 # noinspection PyShadowingBuiltins
17 unicode = str
18 # noinspection PyShadowingBuiltins
19 long = int
20
21
22 class SymtableCodeGen(AbstractCodeGen):
23 symsTable = {
24 'MODULE-IDENTITY': ('ModuleIdentity',),
25 'OBJECT-TYPE': ('MibScalar', 'MibTable', 'MibTableRow', 'MibTableColumn'),
26 'NOTIFICATION-TYPE': ('NotificationType',),
27 'TEXTUAL-CONVENTION': ('TextualConvention',),
28 'MODULE-COMPLIANCE': ('ModuleCompliance',),
29 'OBJECT-GROUP': ('ObjectGroup',),
30 'NOTIFICATION-GROUP': ('NotificationGroup',),
31 'AGENT-CAPABILITIES': ('AgentCapabilities',),
32 'OBJECT-IDENTITY': ('ObjectIdentity',),
33 'TRAP-TYPE': ('NotificationType',), # smidump always uses NotificationType
34 'BITS': ('Bits',),
35 }
36
37 constImports = {
38 'SNMPv2-SMI': ('iso',
39 'Bits', # XXX
40 'Integer32', # XXX
41 'TimeTicks', # bug in some IETF MIBs
42 'Counter32', # bug in some IETF MIBs (e.g. DSA-MIB)
43 'Counter64', # bug in some MIBs (e.g.A3COM-HUAWEI-LswINF-MIB)
44 'NOTIFICATION-TYPE', # bug in some MIBs (e.g. A3COM-HUAWEI-DHCPSNOOP-MIB)
45 'Gauge32', # bug in some IETF MIBs (e.g. DSA-MIB)
46 'MODULE-IDENTITY', 'OBJECT-TYPE', 'OBJECT-IDENTITY', 'Unsigned32', 'IpAddress', # XXX
47 'MibIdentifier'), # OBJECT IDENTIFIER
48 'SNMPv2-TC': ('DisplayString', 'TEXTUAL-CONVENTION',), # XXX
49 'SNMPv2-CONF': ('MODULE-COMPLIANCE', 'NOTIFICATION-GROUP',), # XXX
50 }
51
52 baseTypes = ['Integer', 'Integer32', 'Bits', 'ObjectIdentifier', 'OctetString']
53
54 typeClasses = {
55 'COUNTER32': 'Counter32',
56 'COUNTER64': 'Counter64',
57 'GAUGE32': 'Gauge32',
58 'INTEGER': 'Integer32', # XXX
59 'INTEGER32': 'Integer32',
60 'IPADDRESS': 'IpAddress',
61 'NETWORKADDRESS': 'IpAddress',
62 'OBJECT IDENTIFIER': 'ObjectIdentifier',
63 'OCTET STRING': 'OctetString',
64 'OPAQUE': 'Opaque',
65 'TIMETICKS': 'TimeTicks',
66 'UNSIGNED32': 'Unsigned32',
67 'Counter': 'Counter32',
68 'Gauge': 'Gauge32',
69 'NetworkAddress': 'IpAddress', # RFC1065-SMI, RFC1155-SMI -> SNMPv2-SMI
70 'nullSpecific': 'zeroDotZero', # RFC1158-MIB -> SNMPv2-SMI
71 'ipRoutingTable': 'ipRouteTable', # RFC1158-MIB -> RFC1213-MIB
72 'snmpEnableAuthTraps': 'snmpEnableAuthenTraps' # RFC1158-MIB -> SNMPv2-MIB
73 }
74
75 smiv1IdxTypes = ['INTEGER', 'OCTET STRING', 'IPADDRESS', 'NETWORKADDRESS']
76 ifTextStr = 'if mibBuilder.loadTexts: '
77 indent = ' ' * 4
78 fakeidx = 1000 # starting index for fake symbols
79
80 def __init__(self):
81 self._rows = set()
82 self._cols = {} # k, v = name, datatype
83 self._exports = set()
84 self._postponedSyms = {} # k, v = symbol, (parents, properties)
85 self._parentOids = set()
86 self._importMap = {} # k, v = symbol, MIB
87 self._symsOrder = []
88 self._out = {} # k, v = symbol, properties
89 self.moduleName = ['DUMMY']
90 self.genRules = {'text': True}
91
92 def symTrans(self, symbol):
93 if symbol in self.symsTable:
94 return self.symsTable[symbol]
95
96 return symbol,
97
98 @staticmethod
99 def transOpers(symbol):
100 if iskeyword(symbol):
101 symbol = 'pysmi_' + symbol
102
103 return symbol.replace('-', '_')
104
105 def prepData(self, pdata, classmode=False):
106 data = []
107 for el in pdata:
108 if not isinstance(el, tuple):
109 data.append(el)
110
111 elif len(el) == 1:
112 data.append(el[0])
113
114 else:
115 data.append(self.handlersTable[el[0]](self, self.prepData(el[1:], classmode=classmode), classmode=classmode))
116
117 return data
118
119 def genImports(self, imports):
120 # convertion to SNMPv2
121 toDel = []
122 for module in list(imports):
123
124 if module in self.convertImportv2:
125
126 for symbol in imports[module]:
127
128 if symbol in self.convertImportv2[module]:
129 toDel.append((module, symbol))
130
131 for newImport in self.convertImportv2[module][symbol]:
132 newModule, newSymbol = newImport
133
134 if newModule in imports:
135 imports[newModule].append(newSymbol)
136 else:
137 imports[newModule] = [newSymbol]
138
139 # removing converted symbols
140 for d in toDel:
141 imports[d[0]].remove(d[1])
142
143 # merging mib and constant imports
144 for module in self.constImports:
145 if module in imports:
146 imports[module] += self.constImports[module]
147 else:
148 imports[module] = self.constImports[module]
149
150 for module in sorted(imports):
151 symbols = ()
152 for symbol in set(imports[module]):
153 symbols += self.symTrans(symbol)
154
155 if symbols:
156 self._importMap.update([(self.transOpers(s), module) for s in symbols])
157
158 return {}, tuple(sorted(imports))
159
160 def allParentsExists(self, parents):
161 parentsExists = True
162 for parent in parents:
163 if not (parent in self._out or
164 parent in self._importMap or
165 parent in self.baseTypes or
166 parent in ('MibTable', 'MibTableRow', 'MibTableColumn') or
167 parent in self._rows):
168 parentsExists = False
169 break
170
171 return parentsExists
172
173 def regSym(self, symbol, symProps, parents=()):
174 if symbol in self._out or symbol in self._postponedSyms: # add to strict mode - or symbol in self._importMap:
175 raise error.PySmiSemanticError('Duplicate symbol found: %s' % symbol)
176
177 if self.allParentsExists(parents):
178 self._out[symbol] = symProps
179 self._symsOrder.append(symbol)
180 self.regPostponedSyms()
181
182 else:
183 self._postponedSyms[symbol] = (parents, symProps)
184
185 def regPostponedSyms(self):
186 regedSyms = []
187 for sym, val in self._postponedSyms.items():
188 parents, symProps = val
189
190 if self.allParentsExists(parents):
191 self._out[sym] = symProps
192 self._symsOrder.append(sym)
193 regedSyms.append(sym)
194
195 for sym in regedSyms:
196 self._postponedSyms.pop(sym)
197
198 # Clause handlers
199
200 # noinspection PyUnusedLocal
201 def genAgentCapabilities(self, data, classmode=False):
202 origName, release, status, description, reference, oid = data
203
204 pysmiName = self.transOpers(origName)
205
206 symProps = {'type': 'AgentCapabilities',
207 'oid': oid,
208 'origName': origName}
209
210 self.regSym(pysmiName, symProps)
211
212 # noinspection PyUnusedLocal
213 def genModuleIdentity(self, data, classmode=False):
214 origName, lastUpdated, organization, contactInfo, description, revisions, oid = data
215
216 pysmiName = self.transOpers(origName)
217
218 symProps = {'type': 'ModuleIdentity',
219 'oid': oid,
220 'origName': origName}
221
222 self.regSym(pysmiName, symProps)
223
224 # noinspection PyUnusedLocal
225 def genModuleCompliance(self, data, classmode=False):
226 origName, status, description, reference, compliances, oid = data
227
228 pysmiName = self.transOpers(origName)
229
230 symProps = {'type': 'ModuleCompliance',
231 'oid': oid,
232 'origName': origName}
233
234 self.regSym(pysmiName, symProps)
235
236 # noinspection PyUnusedLocal
237 def genNotificationGroup(self, data, classmode=False):
238 origName, objects, status, description, reference, oid = data
239
240 pysmiName = self.transOpers(origName)
241
242 symProps = {'type': 'NotificationGroup',
243 'oid': oid,
244 'origName': origName}
245
246 self.regSym(pysmiName, symProps)
247
248 # noinspection PyUnusedLocal
249 def genNotificationType(self, data, classmode=False):
250 origName, objects, status, description, reference, oid = data
251
252 pysmiName = self.transOpers(origName)
253
254 symProps = {'type': 'NotificationType',
255 'oid': oid,
256 'origName': origName}
257
258 self.regSym(pysmiName, symProps)
259
260 # noinspection PyUnusedLocal
261 def genObjectGroup(self, data, classmode=False):
262 origName, objects, status, description, reference, oid = data
263
264 pysmiName = self.transOpers(origName)
265
266 symProps = {'type': 'ObjectGroup',
267 'oid': oid,
268 'origName': origName}
269
270 self.regSym(pysmiName, symProps)
271
272 # noinspection PyUnusedLocal
273 def genObjectIdentity(self, data, classmode=False):
274 origName, status, description, reference, oid = data
275
276 pysmiName = self.transOpers(origName)
277
278 symProps = {'type': 'ObjectIdentity',
279 'oid': oid,
280 'origName': origName}
281
282 self.regSym(pysmiName, symProps)
283
284 # noinspection PyUnusedLocal
285 def genObjectType(self, data, classmode=False):
286 origName, syntax, units, maxaccess, status, description, reference, augmention, index, defval, oid = data
287
288 pysmiName = self.transOpers(origName)
289
290 symProps = {'type': 'ObjectType',
291 'oid': oid,
292 'syntax': syntax, # (type, module), subtype
293 'origName': origName}
294
295 parents = [syntax[0][0]]
296
297 if augmention:
298 parents.append(self.transOpers(augmention))
299
300 if defval: # XXX
301 symProps['defval'] = defval
302
303 if index and index[1]:
304 namepart, fakeIndexes, fakeSymSyntax = index
305 for fakeIdx, fakeSyntax in zip(fakeIndexes, fakeSymSyntax):
306 fakeName = namepart + str(fakeIdx)
307
308 fakeSymProps = {'type': 'fakeColumn',
309 'oid': oid + (fakeIdx,),
310 'syntax': fakeSyntax,
311 'origName': fakeName}
312
313 self.regSym(fakeName, fakeSymProps)
314
315 self.regSym(pysmiName, symProps, parents)
316
317 # noinspection PyUnusedLocal
318 def genTrapType(self, data, classmode=False):
319 origName, enterprise, variables, description, reference, value = data
320
321 pysmiName = self.transOpers(origName)
322
323 symProps = {'type': 'NotificationType',
324 'oid': enterprise + (0, value),
325 'origName': origName}
326
327 self.regSym(pysmiName, symProps)
328
329 # noinspection PyUnusedLocal
330 def genTypeDeclaration(self, data, classmode=False):
331 origName, declaration = data
332
333 pysmiName = self.transOpers(origName)
334
335 if declaration:
336 parentType, attrs = declaration
337 if parentType: # skipping SEQUENCE case
338 symProps = {'type': 'TypeDeclaration',
339 'syntax': declaration, # (type, module), subtype
340 'origName': origName}
341
342 self.regSym(pysmiName, symProps, [declaration[0][0]])
343
344 # noinspection PyUnusedLocal
345 def genValueDeclaration(self, data, classmode=False):
346 origName, oid = data
347
348 pysmiName = self.transOpers(origName)
349
350 symProps = {'type': 'MibIdentifier',
351 'oid': oid,
352 'origName': origName}
353
354 self.regSym(pysmiName, symProps)
355
356 # Subparts generation functions
357 # noinspection PyUnusedLocal,PyMethodMayBeStatic
358 def genBitNames(self, data, classmode=False):
359 names = data[0]
360 return names
361
362 # noinspection PyUnusedLocal,PyMethodMayBeStatic
363 def genBits(self, data, classmode=False):
364 bits = data[0]
365 return ('Bits', ''), bits
366
367 # noinspection PyUnusedLocal,PyUnusedLocal,PyMethodMayBeStatic
368 def genCompliances(self, data, classmode=False):
369 return ''
370
371 # noinspection PyUnusedLocal
372 def genConceptualTable(self, data, classmode=False):
373 row = data[0]
374 if row[0] and row[0][0]:
375 self._rows.add(self.transOpers(row[0][0]))
376 return ('MibTable', ''), ''
377
378 # noinspection PyUnusedLocal,PyUnusedLocal,PyMethodMayBeStatic
379 def genContactInfo(self, data, classmode=False):
380 return ''
381
382 # noinspection PyUnusedLocal,PyUnusedLocal,PyMethodMayBeStatic
383 def genDisplayHint(self, data, classmode=False):
384 return ''
385
386 # noinspection PyUnusedLocal
387 def genDefVal(self, data, classmode=False): # XXX should be fixed, see pysnmp.py
388 defval = data[0]
389
390 if isinstance(defval, (int, long)): # number
391 val = str(defval)
392
393 elif self.isHex(defval): # hex
394 val = 'hexValue="' + defval[1:-2] + '"' # not working for Integer baseTypes
395
396 elif self.isBinary(defval): # binary
397 binval = defval[1:-2]
398 hexval = binval and hex(int(binval, 2))[2:] or ''
399 val = 'hexValue="' + hexval + '"'
400
401 elif isinstance(defval, list): # bits list
402 val = defval
403
404 elif defval[0] == defval[-1] and defval[0] == '"': # quoted strimg
405 val = dorepr(defval[1:-1])
406
407 else: # symbol (oid as defval) or name for enumeration member
408 if defval in self._out or defval in self._importMap:
409 val = defval + '.getName()'
410 else:
411 val = dorepr(defval)
412
413 return val
414
415 # noinspection PyUnusedLocal,PyUnusedLocal,PyMethodMayBeStatic
416 def genDescription(self, data, classmode=False):
417 return ''
418
419 def genReference(self, data, classmode=False):
420 return ''
421
422 def genStatus(self, data, classmode=False):
423 return ''
424
425 def genProductRelease(self, data, classmode=False):
426 return ''
427
428 def genEnumSpec(self, data, classmode=False):
429 return self.genBits(data, classmode=classmode)[1]
430
431 def genIndex(self, data, classmode=False):
432 indexes = data[0]
433
434 fakeIdxName = 'pysmiFakeCol'
435 fakeIndexes, fakeSymsSyntax = [], []
436
437 for idx in indexes:
438 idxName = idx[1]
439 if idxName in self.smiv1IdxTypes: # SMIv1 support
440 idxType = idxName
441
442 objType = self.typeClasses.get(idxType, idxType)
443 objType = self.transOpers(objType)
444
445 fakeIndexes.append(self.fakeidx)
446 fakeSymsSyntax.append((('MibTableColumn', ''), objType))
447 self.fakeidx += 1
448
449 return fakeIdxName, fakeIndexes, fakeSymsSyntax
450
451 # noinspection PyUnusedLocal,PyUnusedLocal,PyMethodMayBeStatic
452 def genIntegerSubType(self, data, classmode=False):
453 return ''
454
455 # noinspection PyUnusedLocal,PyUnusedLocal,PyMethodMayBeStatic
456 def genMaxAccess(self, data, classmode=False):
457 return ''
458
459 # noinspection PyUnusedLocal,PyUnusedLocal,PyMethodMayBeStatic
460 def genOctetStringSubType(self, data, classmode=False):
461 return ''
462
463 # noinspection PyUnusedLocal
464 def genOid(self, data, classmode=False):
465 out = ()
466 for el in data[0]:
467 if isinstance(el, (str, unicode)):
468 parent = self.transOpers(el)
469 self._parentOids.add(parent)
470 out += ((parent, self._importMap.get(parent, self.moduleName[0])),)
471
472 elif isinstance(el, (int, long)):
473 out += (el,)
474
475 elif isinstance(el, tuple):
476 out += (el[1],) # XXX Do we need to create a new object el[0]?
477
478 else:
479 raise error.PySmiSemanticError('unknown datatype for OID: %s' % el)
480
481 return out
482
483 # noinspection PyUnusedLocal,PyUnusedLocal,PyMethodMayBeStatic
484 def genObjects(self, data, classmode=False):
485 return ''
486
487 # noinspection PyUnusedLocal,PyUnusedLocal,PyMethodMayBeStatic
488 def genTime(self, data, classmode=False):
489 return ''
490
491 # noinspection PyUnusedLocal,PyUnusedLocal,PyMethodMayBeStatic
492 def genLastUpdated(self, data, classmode=False):
493 return ''
494
495 # noinspection PyUnusedLocal,PyUnusedLocal,PyMethodMayBeStatic
496 def genOrganization(self, data, classmode=False):
497 return ''
498
499 # noinspection PyUnusedLocal,PyUnusedLocal,PyMethodMayBeStatic
500 def genRevisions(self, data, classmode=False):
501 return ''
502
503 def genRow(self, data, classmode=False):
504 row = data[0]
505 row = self.transOpers(row)
506 return row in self._rows and (('MibTableRow', ''), '') or self.genSimpleSyntax(data, classmode=classmode)
507
508 # noinspection PyUnusedLocal
509 def genSequence(self, data, classmode=False):
510 cols = data[0]
511 self._cols.update(cols)
512 return '', ''
513
514 # noinspection PyUnusedLocal
515 def genSimpleSyntax(self, data, classmode=False):
516 objType = data[0]
517
518 module = ''
519
520 objType = self.typeClasses.get(objType, objType)
521 objType = self.transOpers(objType)
522
523 if objType not in self.baseTypes:
524 module = self._importMap.get(objType, self.moduleName[0])
525
526 subtype = len(data) == 2 and data[1] or ''
527
528 return (objType, module), subtype
529
530 # noinspection PyUnusedLocal,PyMethodMayBeStatic
531 def genTypeDeclarationRHS(self, data, classmode=False):
532 if len(data) == 1:
533 parentType, attrs = data[0] # just syntax
534
535 else:
536 # Textual convention
537 display, status, description, reference, syntax = data
538 parentType, attrs = syntax
539
540 return parentType, attrs
541
542 # noinspection PyUnusedLocal,PyUnusedLocal,PyMethodMayBeStatic
543 def genUnits(self, data, classmode=False):
544 return ''
545
546 handlersTable = {
547 'agentCapabilitiesClause': genAgentCapabilities,
548 'moduleIdentityClause': genModuleIdentity,
549 'moduleComplianceClause': genModuleCompliance,
550 'notificationGroupClause': genNotificationGroup,
551 'notificationTypeClause': genNotificationType,
552 'objectGroupClause': genObjectGroup,
553 'objectIdentityClause': genObjectIdentity,
554 'objectTypeClause': genObjectType,
555 'trapTypeClause': genTrapType,
556 'typeDeclaration': genTypeDeclaration,
557 'valueDeclaration': genValueDeclaration,
558
559 'ApplicationSyntax': genSimpleSyntax,
560 'BitNames': genBitNames,
561 'BITS': genBits,
562 'ComplianceModules': genCompliances,
563 'conceptualTable': genConceptualTable,
564 'CONTACT-INFO': genContactInfo,
565 'DISPLAY-HINT': genDisplayHint,
566 'DEFVAL': genDefVal,
567 'DESCRIPTION': genDescription,
568 'REFERENCE': genReference,
569 'Status': genStatus,
570 'PRODUCT-RELEASE': genProductRelease,
571 'enumSpec': genEnumSpec,
572 'INDEX': genIndex,
573 'integerSubType': genIntegerSubType,
574 'MaxAccessPart': genMaxAccess,
575 'Notifications': genObjects,
576 'octetStringSubType': genOctetStringSubType,
577 'objectIdentifier': genOid,
578 'Objects': genObjects,
579 'LAST-UPDATED': genLastUpdated,
580 'ORGANIZATION': genOrganization,
581 'Revisions': genRevisions,
582 'row': genRow,
583 'SEQUENCE': genSequence,
584 'SimpleSyntax': genSimpleSyntax,
585 'typeDeclarationRHS': genTypeDeclarationRHS,
586 'UNITS': genUnits,
587 'VarTypes': genObjects,
588 }
589
590 def genCode(self, ast, symbolTable, **kwargs):
591 self.genRules['text'] = kwargs.get('genTexts', False)
592 self._rows.clear()
593 self._cols.clear()
594 self._parentOids.clear()
595 self._symsOrder = []
596 self._postponedSyms.clear()
597 self._importMap.clear()
598 self._out = {} # should be new object, do not use `clear` method
599 self.moduleName[0], moduleOid, imports, declarations = ast
600
601 out, importedModules = self.genImports(imports or {})
602
603 for declr in declarations or []:
604 if declr:
605 clausetype = declr[0]
606 classmode = clausetype == 'typeDeclaration'
607 self.handlersTable[declr[0]](self, self.prepData(declr[1:], classmode), classmode)
608
609 if self._postponedSyms:
610 raise error.PySmiSemanticError('Unknown parents for symbols: %s' % ', '.join(self._postponedSyms))
611
612 for sym in self._parentOids:
613 if sym not in self._out and sym not in self._importMap:
614 raise error.PySmiSemanticError('Unknown parent symbol: %s' % sym)
615
616 self._out['_symtable_order'] = list(self._symsOrder)
617 self._out['_symtable_cols'] = list(self._cols)
618 self._out['_symtable_rows'] = list(self._rows)
619
620 debug.logger & debug.flagCodegen and debug.logger(
621 'canonical MIB name %s (%s), imported MIB(s) %s, Symbol table size %s symbols' % (
622 self.moduleName[0], moduleOid, ','.join(importedModules) or '<none>', len(self._out)))
623
624 return MibInfo(oid=None, name=self.moduleName[0], imported=tuple([x for x in importedModules])), self._out
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7
8 if sys.version_info[0] > 2:
9 def encode(s):
10 if isinstance(s, str):
11 s = s.encode('utf-8', 'ignore')
12 return s
13
14
15 def decode(s):
16 if isinstance(s, bytes):
17 s = s.decode('utf-8', 'ignore')
18 return s
19 else:
20 def encode(s):
21 if isinstance(s, unicode):
22 s = s.encode('utf-8', 'ignore')
23 return s
24
25
26 def decode(s):
27 if isinstance(s, str):
28 s = s.decode('utf-8', 'ignore')
29 return s
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 import os
8 import time
9
10 try:
11 from pwd import getpwuid
12 except ImportError:
13 # noinspection PyPep8
14 getpwuid = lambda x: ['<unknown>']
15 from pysmi import __name__ as packageName
16 from pysmi import __version__ as packageVersion
17 from pysmi.mibinfo import MibInfo
18 from pysmi.codegen.symtable import SymtableCodeGen
19 from pysmi import error
20 from pysmi import debug
21
22
23 class MibStatus(str):
24 """Indicate MIB transformation result.
25
26 *MibStatus* is a subclass of Python string type. Some additional
27 attributes may be set to indicate the details.
28
29 The following *MibStatus* class instances are defined:
30
31 * *compiled* - MIB is successfully transformed
32 * *untouched* - fresh transformed version of this MIB already exisits
33 * *failed* - MIB transformation failed. *error* attribute carries details.
34 * *unprocessed* - MIB transformation required but waived for some reason
35 * *missing* - ASN.1 MIB source can't be found
36 * *borrowed* - MIB transformation failed but pre-transformed version was used
37 """
38
39 def setOptions(self, **kwargs):
40 n = self.__class__(self)
41 for k in kwargs:
42 setattr(n, k, kwargs[k])
43 return n
44
45
46 statusCompiled = MibStatus('compiled')
47 statusUntouched = MibStatus('untouched')
48 statusFailed = MibStatus('failed')
49 statusUnprocessed = MibStatus('unprocessed')
50 statusMissing = MibStatus('missing')
51 statusBorrowed = MibStatus('borrowed')
52
53
54 class MibCompiler(object):
55 """Top-level, user-facing, composite MIB compiler object.
56
57 MibCompiler implements high-level MIB transformation processing logic.
58 It executes its actions by calling the following specialized objects:
59
60 * *readers* - to acquire ASN.1 MIB data
61 * *searchers* - to see if transformed MIB already exists and no processing is necessary
62 * *parser* - to parse ASN.1 MIB into AST
63 * *code generator* - to perform actual MIB transformation
64 * *borrowers* - to fetch pre-transformed MIB if transformation is impossible
65 * *writer* - to store transformed MIB data
66
67 Required components must be passed to MibCompiler on instantiation. Those
68 components are: *parser*, *codegenerator* and *writer*.
69
70 Optional components could be set or modified at later phases of MibCompiler
71 life. Unlike singular, required components, optional one can be present
72 in sequences to address many possible sources of data. They are
73 *readers*, *searchers* and *borrowers*.
74 """
75 indexFile = 'index'
76
77 def __init__(self, parser, codegen, writer):
78 """Creates an instance of *MibCompiler* class.
79
80 Args:
81 parser: ASN.1 MIB parser object
82 codegen: MIB transformation object
83 writer: transformed MIB storing object
84 """
85 self._parser = parser
86 self._codegen = codegen
87 self._symbolgen = SymtableCodeGen()
88 self._writer = writer
89 self._sources = []
90 self._searchers = []
91 self._borrowers = []
92
93 def addSources(self, *sources):
94 """Add more ASN.1 MIB source repositories.
95
96 MibCompiler.compile will invoke each of configured source objects
97 in order of their addition asking each to fetch MIB module specified
98 by name.
99
100 Args:
101 sources: reader object(s)
102
103 Returns:
104 reference to itself (can be used for call chaining)
105
106 """
107 self._sources.extend(sources)
108
109 debug.logger & debug.flagCompiler and debug.logger(
110 'current MIB source(s): %s' % ', '.join([str(x) for x in self._sources]))
111
112 return self
113
114 def addSearchers(self, *searchers):
115 """Add more transformed MIBs repositories.
116
117 MibCompiler.compile will invoke each of configured searcher objects
118 in order of their addition asking each if already transformed MIB
119 module already exists and is more recent than specified.
120
121 Args:
122 searchers: searcher object(s)
123
124 Returns:
125 reference to itself (can be used for call chaining)
126
127 """
128 self._searchers.extend(searchers)
129
130 debug.logger & debug.flagCompiler and debug.logger(
131 'current compiled MIBs location(s): %s' % ', '.join([str(x) for x in self._searchers]))
132
133 return self
134
135 def addBorrowers(self, *borrowers):
136 """Add more transformed MIBs repositories to borrow MIBs from.
137
138 Whenever MibCompiler.compile encounters MIB module which neither of
139 the *searchers* can find or fetched ASN.1 MIB module can not be
140 parsed (due to syntax errors), these *borrowers* objects will be
141 invoked in order of their addition asking each if already transformed
142 MIB can be fetched (borrowed).
143
144 Args:
145 borrowers: borrower object(s)
146
147 Returns:
148 reference to itself (can be used for call chaining)
149
150 """
151 self._borrowers.extend(borrowers)
152
153 debug.logger & debug.flagCompiler and debug.logger(
154 'current MIB borrower(s): %s' % ', '.join([str(x) for x in self._borrowers]))
155
156 return self
157
158 def compile(self, *mibnames, **options):
159 """Transform requested and possibly referred MIBs.
160
161 The *compile* method should be invoked when *MibCompiler* object
162 is operational meaning at least *sources* are specified.
163
164 Once called with a MIB module name, *compile* will:
165
166 * fetch ASN.1 MIB module with given name by calling *sources*
167 * make sure no such transformed MIB already exists (with *searchers*)
168 * parse ASN.1 MIB text with *parser*
169 * perform actual MIB transformation into target format with *code generator*
170 * may attempt to borrow pre-transformed MIB through *borrowers*
171 * write transformed MIB through *writer*
172
173 The above sequence will be performed for each MIB name given in
174 *mibnames* and may be performed for all MIBs referred to from
175 MIBs being processed.
176
177 Args:
178 mibnames: list of ASN.1 MIBs names
179 options: options that affect the way PySMI components work
180
181 Returns:
182 A dictionary of MIB module names processed (keys) and *MibStatus*
183 class instances (values)
184
185 """
186 processed = {}
187 parsedMibs = {}
188 failedMibs = {}
189 borrowedMibs = {}
190 builtMibs = {}
191 symbolTableMap = {}
192 mibsToParse = [x for x in mibnames]
193
194 while mibsToParse:
195 mibname = mibsToParse.pop(0)
196
197 if mibname in parsedMibs:
198 debug.logger & debug.flagCompiler and debug.logger('MIB %s already parsed' % mibname)
199 continue
200
201 if mibname in failedMibs:
202 debug.logger & debug.flagCompiler and debug.logger('MIB %s already failed' % mibname)
203 continue
204
205 for source in self._sources:
206 debug.logger & debug.flagCompiler and debug.logger('trying source %s' % source)
207
208 try:
209 fileInfo, fileData = source.getData(mibname)
210
211 for mibTree in self._parser.parse(fileData):
212 mibInfo, symbolTable = self._symbolgen.genCode(
213 mibTree, symbolTableMap
214 )
215
216 symbolTableMap[mibInfo.name] = symbolTable
217
218 parsedMibs[mibInfo.name] = fileInfo, mibInfo, mibTree
219
220 if mibname in failedMibs:
221 del failedMibs[mibname]
222
223 mibsToParse.extend(mibInfo.imported)
224
225 debug.logger & debug.flagCompiler and debug.logger(
226 '%s (%s) read from %s, immediate dependencies: %s' % (
227 mibInfo.name, mibname, fileInfo.path, ', '.join(mibInfo.imported) or '<none>'))
228
229 break
230
231 except error.PySmiReaderFileNotFoundError:
232 debug.logger & debug.flagCompiler and debug.logger('no %s found at %s' % (mibname, source))
233 continue
234
235 except error.PySmiError:
236 exc_class, exc, tb = sys.exc_info()
237 exc.source = source
238 exc.mibname = mibname
239 exc.msg += ' at MIB %s' % mibname
240
241 debug.logger & debug.flagCompiler and debug.logger('%serror %s from %s' % (
242 options.get('ignoreErrors') and 'ignoring ' or 'failing on ', exc, source))
243
244 failedMibs[mibname] = exc
245
246 processed[mibname] = statusFailed.setOptions(error=exc)
247
248 else:
249 exc = error.PySmiError('MIB source %s not found' % mibname)
250 exc.mibname = mibname
251 debug.logger & debug.flagCompiler and debug.logger('no %s found everywhere' % mibname)
252
253 if mibname not in failedMibs:
254 failedMibs[mibname] = exc
255
256 if mibname not in processed:
257 processed[mibname] = statusMissing
258
259 debug.logger & debug.flagCompiler and debug.logger(
260 'MIBs analized %s, MIBs failed %s' % (len(parsedMibs), len(failedMibs)))
261
262 #
263 # See what MIBs need generating
264 #
265
266 for mibname in tuple(parsedMibs):
267 fileInfo, mibInfo, mibTree = parsedMibs[mibname]
268
269 debug.logger & debug.flagCompiler and debug.logger('checking if %s requires updating' % mibname)
270
271 for searcher in self._searchers:
272 try:
273 searcher.fileExists(mibname, fileInfo.mtime, rebuild=options.get('rebuild'))
274
275 except error.PySmiFileNotFoundError:
276 debug.logger & debug.flagCompiler and debug.logger(
277 'no compiled MIB %s available through %s' % (mibname, searcher))
278 continue
279
280 except error.PySmiFileNotModifiedError:
281 debug.logger & debug.flagCompiler and debug.logger(
282 'will be using existing compiled MIB %s found by %s' % (mibname, searcher))
283 del parsedMibs[mibname]
284 processed[mibname] = statusUntouched
285 break
286
287 except error.PySmiError:
288 exc_class, exc, tb = sys.exc_info()
289 exc.searcher = searcher
290 exc.mibname = mibname
291 exc.msg += ' at MIB %s' % mibname
292 debug.logger & debug.flagCompiler and debug.logger('error from %s: %s' % (searcher, exc))
293 continue
294
295 else:
296 debug.logger & debug.flagCompiler and debug.logger(
297 'no suitable compiled MIB %s found anywhere' % mibname)
298
299 if options.get('noDeps') and mibname not in mibnames:
300 debug.logger & debug.flagCompiler and debug.logger(
301 'excluding imported MIB %s from code generation' % mibname)
302 del parsedMibs[mibname]
303 processed[mibname] = statusUntouched
304 continue
305
306 debug.logger & debug.flagCompiler and debug.logger(
307 'MIBs parsed %s, MIBs failed %s' % (len(parsedMibs), len(failedMibs)))
308
309 #
310 # Generate code for parsed MIBs
311 #
312
313 for mibname in parsedMibs.copy():
314 fileInfo, mibInfo, mibTree = parsedMibs[mibname]
315
316 debug.logger & debug.flagCompiler and debug.logger('compiling %s read from %s' % (mibname, fileInfo.path))
317
318 comments = [
319 'ASN.1 source %s' % fileInfo.path,
320 'Produced by %s-%s at %s' % (packageName, packageVersion, time.asctime()),
321 'On host %s platform %s version %s by user %s' % (
322 hasattr(os, 'uname') and os.uname()[1] or '?', hasattr(os, 'uname') and os.uname()[0] or '?',
323 hasattr(os, 'uname') and os.uname()[2] or '?',
324 hasattr(os, 'getuid') and getpwuid(os.getuid())[0] or '?'),
325 'Using Python version %s' % sys.version.split('\n')[0]
326 ]
327
328 try:
329 mibInfo, mibData = self._codegen.genCode(
330 mibTree,
331 symbolTableMap,
332 comments=comments,
333 genTexts=options.get('genTexts'),
334 textFilter=options.get('textFilter')
335 )
336
337 builtMibs[mibname] = fileInfo, mibInfo, mibData
338 del parsedMibs[mibname]
339
340 debug.logger & debug.flagCompiler and debug.logger(
341 '%s read from %s and compiled by %s' % (mibname, fileInfo.path, self._writer))
342
343 except error.PySmiError:
344 exc_class, exc, tb = sys.exc_info()
345 exc.handler = self._codegen
346 exc.mibname = mibname
347 exc.msg += ' at MIB %s' % mibname
348
349 debug.logger & debug.flagCompiler and debug.logger('error from %s: %s' % (self._codegen, exc))
350
351 processed[mibname] = statusFailed.setOptions(error=exc)
352
353 failedMibs[mibname] = exc
354 del parsedMibs[mibname]
355
356 debug.logger & debug.flagCompiler and debug.logger(
357 'MIBs built %s, MIBs failed %s' % (len(parsedMibs), len(failedMibs)))
358
359 #
360 # Try to borrow pre-compiled MIBs for failed ones
361 #
362
363 for mibname in failedMibs.copy():
364 if options.get('noDeps') and mibname not in mibnames:
365 debug.logger & debug.flagCompiler and debug.logger('excluding imported MIB %s from borrowing' % mibname)
366 continue
367
368 for borrower in self._borrowers:
369 debug.logger & debug.flagCompiler and debug.logger('trying to borrow %s from %s' % (mibname, borrower))
370 try:
371 fileInfo, fileData = borrower.getData(
372 mibname,
373 genTexts=options.get('genTexts')
374 )
375
376 borrowedMibs[mibname] = fileInfo, MibInfo(name=mibname, imported=[]), fileData
377
378 del failedMibs[mibname]
379
380 debug.logger & debug.flagCompiler and debug.logger('%s borrowed with %s' % (mibname, borrower))
381 break
382
383 except error.PySmiError:
384 debug.logger & debug.flagCompiler and debug.logger('error from %s: %s' % (borrower, sys.exc_info()[1]))
385
386 debug.logger & debug.flagCompiler and debug.logger(
387 'MIBs available for borrowing %s, MIBs failed %s' % (len(borrowedMibs), len(failedMibs)))
388
389 #
390 # See what MIBs need borrowing
391 #
392
393 for mibname in borrowedMibs.copy():
394 debug.logger & debug.flagCompiler and debug.logger('checking if failed MIB %s requires borrowing' % mibname)
395
396 fileInfo, mibInfo, mibData = borrowedMibs[mibname]
397
398 for searcher in self._searchers:
399 try:
400 searcher.fileExists(mibname, fileInfo.mtime, rebuild=options.get('rebuild'))
401
402 except error.PySmiFileNotFoundError:
403 debug.logger & debug.flagCompiler and debug.logger(
404 'no compiled MIB %s available through %s' % (mibname, searcher))
405 continue
406
407 except error.PySmiFileNotModifiedError:
408 debug.logger & debug.flagCompiler and debug.logger(
409 'will be using existing compiled MIB %s found by %s' % (mibname, searcher))
410 del borrowedMibs[mibname]
411 processed[mibname] = statusUntouched
412 break
413
414 except error.PySmiError:
415 exc_class, exc, tb = sys.exc_info()
416 exc.searcher = searcher
417 exc.mibname = mibname
418 exc.msg += ' at MIB %s' % mibname
419
420 debug.logger & debug.flagCompiler and debug.logger('error from %s: %s' % (searcher, exc))
421
422 continue
423 else:
424 debug.logger & debug.flagCompiler and debug.logger(
425 'no suitable compiled MIB %s found anywhere' % mibname)
426
427 if options.get('noDeps') and mibname not in mibnames:
428 debug.logger & debug.flagCompiler and debug.logger(
429 'excluding imported MIB %s from borrowing' % mibname)
430 processed[mibname] = statusUntouched
431
432 else:
433 debug.logger & debug.flagCompiler and debug.logger('will borrow MIB %s' % mibname)
434 builtMibs[mibname] = borrowedMibs[mibname]
435
436 processed[mibname] = statusBorrowed.setOptions(
437 path=fileInfo.path, file=fileInfo.file,
438 alias=fileInfo.name
439 )
440
441 del borrowedMibs[mibname]
442
443 debug.logger & debug.flagCompiler and debug.logger(
444 'MIBs built %s, MIBs failed %s' % (len(builtMibs), len(failedMibs)))
445
446 #
447 # We could attempt to ignore missing/failed MIBs
448 #
449
450 if failedMibs and not options.get('ignoreErrors'):
451 debug.logger & debug.flagCompiler and debug.logger('failing with problem MIBs %s' % ', '.join(failedMibs))
452
453 for mibname in builtMibs:
454 processed[mibname] = statusUnprocessed
455
456 return processed
457
458 debug.logger & debug.flagCompiler and debug.logger(
459 'proceeding with built MIBs %s, failed MIBs %s' % (', '.join(builtMibs), ', '.join(failedMibs)))
460
461 #
462 # Store compiled MIBs
463 #
464
465 for mibname in builtMibs.copy():
466 fileInfo, mibInfo, mibData = builtMibs[mibname]
467
468 try:
469 if options.get('writeMibs', True):
470 self._writer.putData(
471 mibname, mibData, dryRun=options.get('dryRun')
472 )
473
474 debug.logger & debug.flagCompiler and debug.logger('%s stored by %s' % (mibname, self._writer))
475
476 del builtMibs[mibname]
477
478 if mibname not in processed:
479 processed[mibname] = statusCompiled.setOptions(
480 path=fileInfo.path,
481 file=fileInfo.file,
482 alias=fileInfo.name,
483 oid=mibInfo.oid,
484 oids=mibInfo.oids,
485 identity=mibInfo.identity,
486 enterprise=mibInfo.enterprise,
487 compliance=mibInfo.compliance,
488 )
489
490 except error.PySmiError:
491 exc_class, exc, tb = sys.exc_info()
492 exc.handler = self._codegen
493 exc.mibname = mibname
494 exc.msg += ' at MIB %s' % mibname
495
496 debug.logger & debug.flagCompiler and debug.logger('error %s from %s' % (exc, self._writer))
497
498 processed[mibname] = statusFailed.setOptions(error=exc)
499 failedMibs[mibname] = exc
500 del builtMibs[mibname]
501
502 debug.logger & debug.flagCompiler and debug.logger(
503 'MIBs modified: %s' % ', '.join([x for x in processed if processed[x] in ('compiled', 'borrowed')]))
504
505 return processed
506
507 def buildIndex(self, processedMibs, **options):
508 comments = [
509 'Produced by %s-%s at %s' % (packageName, packageVersion, time.asctime()),
510 'On host %s platform %s version %s by user %s' % (
511 hasattr(os, 'uname') and os.uname()[1] or '?', hasattr(os, 'uname') and os.uname()[0] or '?',
512 hasattr(os, 'uname') and os.uname()[2] or '?', hasattr(os, 'getuid') and getpwuid(os.getuid())[0]) or '?',
513 'Using Python version %s' % sys.version.split('\n')[0]
514 ]
515 try:
516 self._writer.putData(
517 self.indexFile,
518 self._codegen.genIndex(
519 processedMibs,
520 comments=comments,
521 old_index_data=self._writer.getData(self.indexFile)
522 ),
523 dryRun=options.get('dryRun')
524 )
525 except error.PySmiError:
526 exc_class, exc, tb = sys.exc_info()
527 exc.msg += ' at MIB index %s' % self.indexFile
528
529 debug.logger & debug.flagCompiler and debug.logger('error %s when building %s' % (exc, self.indexFile))
530
531 if options.get('ignoreErrors'):
532 return
533
534 if hasattr(exc, 'with_traceback'):
535 raise exc.with_traceback(tb)
536 else:
537 raise exc
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import logging
7 from pysmi import error
8 from pysmi import __version__
9
10 flagNone = 0x0000
11 flagSearcher = 0x0001
12 flagReader = 0x0002
13 flagLexer = 0x0004
14 flagParser = 0x0008
15 flagGrammar = 0x0010
16 flagCodegen = 0x0020
17 flagWriter = 0x0040
18 flagCompiler = 0x0080
19 flagBorrower = 0x0100
20 flagAll = 0xffff
21
22 flagMap = {
23 'searcher': flagSearcher,
24 'reader': flagReader,
25 'lexer': flagLexer,
26 'parser': flagParser,
27 'grammar': flagGrammar,
28 'codegen': flagCodegen,
29 'writer': flagWriter,
30 'compiler': flagCompiler,
31 'borrower': flagBorrower,
32 'all': flagAll
33 }
34
35
36 class Printer(object):
37 def __init__(self, logger=None, handler=None, formatter=None):
38 if logger is None:
39 logger = logging.getLogger('pysmi')
40
41 logger.setLevel(logging.DEBUG)
42
43 if handler is None:
44 handler = logging.StreamHandler()
45
46 if formatter is None:
47 formatter = logging.Formatter('%(asctime)s %(name)s: %(message)s')
48
49 handler.setFormatter(formatter)
50 handler.setLevel(logging.DEBUG)
51
52 logger.addHandler(handler)
53
54 self.__logger = logger
55
56 def __call__(self, msg):
57 self.__logger.debug(msg)
58
59 def __str__(self):
60 return '<python built-in logging>'
61
62 def getCurrentLogger(self):
63 return self.__logger
64
65
66 if hasattr(logging, 'NullHandler'):
67 NullHandler = logging.NullHandler
68 else:
69 # Python 2.6 and older
70 class NullHandler(logging.Handler):
71 def emit(self, record):
72 pass
73
74
75 class Debug(object):
76 defaultPrinter = None
77
78 def __init__(self, *flags, **options):
79 self._flags = flagNone
80 if options.get('printer') is not None:
81 self._printer = options.get('printer')
82
83 elif self.defaultPrinter is not None:
84 self._printer = self.defaultPrinter
85
86 else:
87 if 'loggerName' in options:
88 # route our logs to parent logger
89 self._printer = Printer(
90 logger=logging.getLogger(options['loggerName']),
91 handler=NullHandler()
92 )
93 else:
94 self._printer = Printer()
95
96 self('running pysmi version %s' % __version__)
97
98 for flag in flags:
99 inverse = flag and flag[0] in ('!', '~')
100
101 if inverse:
102 flag = flag[1:]
103
104 try:
105 if inverse:
106 self._flags &= ~flagMap[flag]
107 else:
108 self._flags |= flagMap[flag]
109
110 except KeyError:
111 raise error.PySmiError('bad debug flag %s' % flag)
112
113 self('debug category \'%s\' %s' % (flag, inverse and 'disabled' or 'enabled'))
114
115 def __str__(self):
116 return 'logger %s, flags %x' % (self._printer, self._flags)
117
118 def __call__(self, msg):
119 self._printer(msg)
120
121 def __and__(self, flag):
122 return self._flags & flag
123
124 def __rand__(self, flag):
125 return flag & self._flags
126
127 def getCurrentPrinter(self):
128 return self._printer
129
130 def getCurrentLogger(self):
131 return self._printer and self._printer.getCurrentLogger() or None
132
133
134 # This will yield false from bitwise and with a flag, and save
135 # on unnecessary calls
136 logger = 0
137
138
139 def setLogger(l):
140 global logger
141 logger = l
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 # Package exception model:
7 # Here we subclass base Python exception overriding its constructor to
8 # accomodate error message string as its first parameter and an open
9 # set of keyword arguments that become exception object attributes.
10 # While exception object is bubbling up the call stack, intermediate
11 # exception handlers may insert their own attributes into exception
12 # object.
13 #
14
15
16 class PySmiError(Exception):
17 def __init__(self, *args, **kwargs):
18 Exception.__init__(self, *args)
19 self.msg = args and args[0] or ''
20 for k in kwargs:
21 setattr(self, k, kwargs[k])
22
23 def __repr__(self):
24 return '%s(%s)' % (self.__class__.__name__, ', '.join(
25 ['%s=%r' % (k, getattr(self, k)) for k in dir(self) if k[0] != '_' and k != 'args']))
26
27 def __str__(self):
28 return self.msg
29
30
31 class PySmiLexerError(PySmiError):
32 lineno = '?'
33
34 def __str__(self):
35 return self.msg + ', line %s' % self.lineno
36
37
38 class PySmiParserError(PySmiLexerError):
39 pass
40
41
42 class PySmiSyntaxError(PySmiParserError):
43 pass
44
45
46 class PySmiSearcherError(PySmiError):
47 pass
48
49
50 class PySmiFileNotModifiedError(PySmiSearcherError):
51 pass
52
53
54 class PySmiFileNotFoundError(PySmiSearcherError):
55 pass
56
57
58 class PySmiReaderError(PySmiError):
59 pass
60
61
62 class PySmiReaderFileNotModifiedError(PySmiReaderError):
63 pass
64
65
66 class PySmiReaderFileNotFoundError(PySmiReaderError):
67 pass
68
69
70 class PySmiCodegenError(PySmiError):
71 pass
72
73
74 class PySmiSemanticError(PySmiCodegenError):
75 pass
76
77
78 class PySmiWriterError(PySmiError):
79 pass
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi.lexer.smi import SmiV2Lexer
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6
7
8 class AbstractLexer(object):
9 def reset(self):
10 raise NotImplementedError()
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 import re
8 import ply.lex as lex
9 from pysmi.lexer.base import AbstractLexer
10 from pysmi import error
11 from pysmi import debug
12
13 UNSIGNED32_MAX = 4294967295
14 UNSIGNED64_MAX = 18446744073709551615
15 LEX_VERSION = [int(x) for x in lex.__version__.split('.')]
16
17 # Do not overload single lexer methods - overload all or none of them!
18 # noinspection PySingleQuotedDocstring,PyMethodMayBeStatic,PyIncorrectDocstring
19 class SmiV2Lexer(AbstractLexer):
20 reserved_words = [
21 'ACCESS', 'AGENT-CAPABILITIES', 'APPLICATION', 'AUGMENTS', 'BEGIN', 'BITS',
22 'CONTACT-INFO', 'CREATION-REQUIRES', 'Counter', 'Counter32', 'Counter64',
23 'DEFINITIONS', 'DEFVAL', 'DESCRIPTION', 'DISPLAY-HINT', 'END', 'ENTERPRISE',
24 'EXTENDS', 'FROM', 'GROUP', 'Gauge', 'Gauge32', 'IDENTIFIER', 'IMPLICIT',
25 'IMPLIED', 'IMPORTS', 'INCLUDES', 'INDEX', 'INSTALL-ERRORS', 'INTEGER',
26 'Integer32', 'IpAddress', 'LAST-UPDATED', 'MANDATORY-GROUPS',
27 'MAX-ACCESS', 'MIN-ACCESS', 'MODULE', 'MODULE-COMPLIANCE',
28 'MODULE-IDENTITY', 'NOTIFICATION-GROUP', 'NOTIFICATION-TYPE',
29 'NOTIFICATIONS', 'OBJECT', 'OBJECT-GROUP', 'OBJECT-IDENTITY', 'OBJECT-TYPE',
30 'OBJECTS', 'OCTET', 'OF', 'ORGANIZATION', 'Opaque', 'PIB-ACCESS',
31 'PIB-DEFINITIONS', 'PIB-INDEX', 'PIB-MIN-ACCESS', 'PIB-REFERENCES',
32 'PIB-TAG', 'POLICY-ACCESS', 'PRODUCT-RELEASE', 'REFERENCE', 'REVISION',
33 'SEQUENCE', 'SIZE', 'STATUS', 'STRING', 'SUBJECT-CATEGORIES', 'SUPPORTS',
34 'SYNTAX', 'TEXTUAL-CONVENTION', 'TimeTicks', 'TRAP-TYPE', 'UNIQUENESS',
35 'UNITS', 'UNIVERSAL', 'Unsigned32', 'VALUE', 'VARIABLES',
36 'VARIATION', 'WRITE-SYNTAX'
37 ]
38
39 reserved = {}
40 for w in reserved_words:
41 reserved[w] = w.replace('-', '_').upper()
42 # hack to support SMIv1
43 if w == 'Counter':
44 reserved[w] = 'COUNTER32'
45 elif w == 'Gauge':
46 reserved[w] = 'GAUGE32'
47
48 forbidden_words = [
49 'ABSENT', 'ANY', 'BIT', 'BOOLEAN', 'BY', 'COMPONENT', 'COMPONENTS',
50 'DEFAULT', 'DEFINED', 'ENUMERATED', 'EXPLICIT', 'EXTERNAL', 'FALSE', 'MAX',
51 'MIN', 'MINUS-INFINITY', 'NULL', 'OPTIONAL', 'PLUS-INFINITY', 'PRESENT',
52 'PRIVATE', 'REAL', 'SET', 'TAGS', 'TRUE', 'WITH'
53 ]
54
55 # Token names required!
56 tokens = list(set([
57 'BIN_STRING',
58 'CHOICE',
59 'COLON_COLON_EQUAL',
60 'DOT_DOT',
61 'EXPORTS',
62 'HEX_STRING',
63 'LOWERCASE_IDENTIFIER',
64 'MACRO',
65 'NEGATIVENUMBER',
66 'NEGATIVENUMBER64',
67 'NUMBER',
68 'NUMBER64',
69 'QUOTED_STRING',
70 'UPPERCASE_IDENTIFIER',
71 ] + list(reserved.values())
72 ))
73
74 states = (
75 ('macro', 'exclusive'),
76 ('choice', 'exclusive'),
77 ('exports', 'exclusive'),
78 ('comment', 'exclusive'),
79 )
80
81 literals = '[]{}():;,-.|'
82
83 t_DOT_DOT = r'\.\.'
84 t_COLON_COLON_EQUAL = r'::='
85
86 t_ignore = ' \t'
87
88 def __init__(self, tempdir=''):
89 self._tempdir = tempdir
90 self.lexer = None
91 self.reset()
92
93 def reset(self):
94 if LEX_VERSION < [3, 0]:
95 self.lexer = lex.lex(module=self,
96 reflags=re.DOTALL,
97 outputdir=self._tempdir,
98 debug=False)
99 else:
100 if debug.logger & debug.flagLexer:
101 logger = debug.logger.getCurrentLogger()
102 else:
103 logger = lex.NullLogger()
104
105 if debug.logger & debug.flagGrammar:
106 debuglogger = debug.logger.getCurrentLogger()
107 else:
108 debuglogger = None
109
110 self.lexer = lex.lex(module=self,
111 reflags=re.DOTALL,
112 outputdir=self._tempdir,
113 debuglog=debuglogger,
114 errorlog=logger)
115
116 def t_newline(self, t):
117 r'\r\n|\n|\r'
118 t.lexer.lineno += 1
119
120 # Skipping MACRO
121 def t_MACRO(self, t):
122 r'MACRO'
123 t.lexer.begin('macro')
124 return t
125
126 def t_macro_newline(self, t):
127 r'\r\n|\n|\r'
128 t.lexer.lineno += 1
129
130 def t_macro_END(self, t):
131 r'END'
132 t.lexer.begin('INITIAL')
133 return t
134
135 def t_macro_body(self, t):
136 r'.+?(?=END)'
137 pass
138
139 # Skipping EXPORTS
140 def t_EXPORTS(self, t):
141 r'EXPORTS'
142 t.lexer.begin('exports')
143 return t
144
145 def t_exports_newline(self, t):
146 r'\r\n|\n|\r'
147 t.lexer.lineno += 1
148
149 def t_exports_end(self, t):
150 r';'
151 t.lexer.begin('INITIAL')
152
153 def t_exports_body(self, t):
154 r'[^;]+'
155 pass
156
157 # Skipping CHOICE
158 def t_CHOICE(self, t):
159 r'CHOICE'
160 t.lexer.begin('choice')
161 return t
162
163 def t_choice_newline(self, t):
164 r'\r\n|\n|\r'
165 t.lexer.lineno += 1
166
167 def t_choice_end(self, t):
168 r'\}'
169 t.lexer.begin('INITIAL')
170
171 def t_choice_body(self, t):
172 r'[^\}]+'
173 pass
174
175 # Comment handling
176 def t_begin_comment(self, t):
177 r'--'
178 t.lexer.begin('comment')
179
180 def t_comment_newline(self, t):
181 r'\r\n|\n|\r'
182 t.lexer.lineno += 1
183 t.lexer.begin('INITIAL')
184
185 # def t_comment_end(self, t):
186 # r'--'
187 # t.lexer.begin('INITIAL')
188
189 def t_comment_body(self, t):
190 r'[^\r\n]+'
191 pass
192
193 def t_UPPERCASE_IDENTIFIER(self, t):
194 r'[A-Z][-a-zA-z0-9]*'
195 if t.value in self.forbidden_words:
196 raise error.PySmiLexerError("%s is forbidden" % t.value, lineno=t.lineno)
197
198 if t.value[-1] == '-':
199 raise error.PySmiLexerError("Identifier should not end with '-': %s" % t.value, lineno=t.lineno)
200
201 t.type = self.reserved.get(t.value, 'UPPERCASE_IDENTIFIER')
202
203 return t
204
205 def t_LOWERCASE_IDENTIFIER(self, t):
206 r'[0-9]*[a-z][-a-zA-z0-9]*'
207 if t.value[-1] == '-':
208 raise error.PySmiLexerError("Identifier should not end with '-': %s" % t.value, lineno=t.lineno)
209 return t
210
211 def t_NUMBER(self, t):
212 r'-?[0-9]+'
213 t.value = int(t.value)
214 neg = 0
215 if t.value < 0:
216 neg = 1
217
218 val = abs(t.value)
219
220 if val <= UNSIGNED32_MAX:
221 if neg:
222 t.type = 'NEGATIVENUMBER'
223
224 elif val <= UNSIGNED64_MAX:
225 if neg:
226 t.type = 'NEGATIVENUMBER64'
227 else:
228 t.type = 'NUMBER64'
229
230 else:
231 raise error.PySmiLexerError("Number %s is too big" % t.value, lineno=t.lineno)
232
233 return t
234
235 def t_BIN_STRING(self, t):
236 r'\'[01]*\'[bB]'
237 value = t.value[1:-2]
238 while value and value[0] == '0' and len(value) % 8:
239 value = value[1:]
240 # XXX raise in strict mode
241 # if len(value) % 8:
242 # raise error.PySmiLexerError("Number of 0s and 1s have to divide by 8 in binary string %s" % t.value, lineno=t.lineno)
243 return t
244
245 def t_HEX_STRING(self, t):
246 r'\'[0-9a-fA-F]*\'[hH]'
247 value = t.value[1:-2]
248 while value and value[0] == '0' and len(value) % 2:
249 value = value[1:]
250 # XXX raise in strict mode
251 # if len(value) % 2:
252 # raise error.PySmiLexerError("Number of symbols have to be even in hex string %s" % t.value, lineno=t.lineno)
253 return t
254
255 def t_QUOTED_STRING(self, t):
256 r'\"[^\"]*\"'
257 t.lexer.lineno += len(re.findall(r'\r\n|\n|\r', t.value))
258 return t
259
260 def t_error(self, t):
261 raise error.PySmiLexerError(
262 "Illegal character '%s', %s characters left unparsed at this stage" % (t.value[0], len(t.value) - 1),
263 lineno=t.lineno)
264 # t.lexer.skip(1)
265
266
267 class SupportSmiV1Keywords(object):
268 @staticmethod
269 def reserved():
270 reserved_words = [
271 'ACCESS', 'AGENT-CAPABILITIES', 'APPLICATION', 'AUGMENTS', 'BEGIN', 'BITS',
272 'CONTACT-INFO', 'CREATION-REQUIRES', 'Counter', 'Counter32', 'Counter64',
273 'DEFINITIONS', 'DEFVAL', 'DESCRIPTION', 'DISPLAY-HINT', 'END', 'ENTERPRISE',
274 'EXTENDS', 'FROM', 'GROUP', 'Gauge', 'Gauge32', 'IDENTIFIER', 'IMPLICIT',
275 'IMPLIED', 'IMPORTS', 'INCLUDES', 'INDEX', 'INSTALL-ERRORS', 'INTEGER',
276 'Integer32', 'IpAddress', 'LAST-UPDATED', 'MANDATORY-GROUPS',
277 'MAX-ACCESS', 'MIN-ACCESS', 'MODULE', 'MODULE-COMPLIANCE', 'MAX',
278 'MODULE-IDENTITY', 'NetworkAddress', 'NOTIFICATION-GROUP',
279 'NOTIFICATION-TYPE', 'NOTIFICATIONS', 'OBJECT', 'OBJECT-GROUP',
280 'OBJECT-IDENTITY', 'OBJECT-TYPE', 'OBJECTS', 'OCTET', 'OF', 'ORGANIZATION',
281 'Opaque', 'PIB-ACCESS', 'PIB-DEFINITIONS', 'PIB-INDEX', 'PIB-MIN-ACCESS',
282 'PIB-REFERENCES', 'PIB-TAG', 'POLICY-ACCESS', 'PRODUCT-RELEASE',
283 'REFERENCE', 'REVISION', 'SEQUENCE', 'SIZE', 'STATUS', 'STRING',
284 'SUBJECT-CATEGORIES', 'SUPPORTS', 'SYNTAX', 'TEXTUAL-CONVENTION',
285 'TimeTicks', 'TRAP-TYPE', 'UNIQUENESS', 'UNITS', 'UNIVERSAL', 'Unsigned32',
286 'VALUE', 'VARIABLES', 'VARIATION', 'WRITE-SYNTAX'
287 ]
288
289 reserved = {}
290 for w in reserved_words:
291 reserved[w] = w.replace('-', '_').upper()
292 # hack to support SMIv1
293 if w == 'Counter':
294 reserved[w] = 'COUNTER32'
295 elif w == 'Gauge':
296 reserved[w] = 'GAUGE32'
297
298 return reserved
299
300 @staticmethod
301 def forbidden_words():
302 return [
303 'ABSENT', 'ANY', 'BIT', 'BOOLEAN', 'BY', 'COMPONENT', 'COMPONENTS',
304 'DEFAULT', 'DEFINED', 'ENUMERATED', 'EXPLICIT', 'EXTERNAL', 'FALSE',
305 'MIN', 'MINUS-INFINITY', 'NULL', 'OPTIONAL', 'PLUS-INFINITY', 'PRESENT',
306 'PRIVATE', 'REAL', 'SET', 'TAGS', 'TRUE', 'WITH'
307 ]
308
309 @staticmethod
310 def tokens():
311 # Token names required!
312 tokens = [
313 'BIN_STRING',
314 'CHOICE',
315 'COLON_COLON_EQUAL',
316 'DOT_DOT',
317 'EXPORTS',
318 'HEX_STRING',
319 'LOWERCASE_IDENTIFIER',
320 'MACRO',
321 'NEGATIVENUMBER',
322 'NEGATIVENUMBER64',
323 'NUMBER',
324 'NUMBER64',
325 'QUOTED_STRING',
326 'UPPERCASE_IDENTIFIER',
327 ]
328 tokens += list(SupportSmiV1Keywords.reserved().values())
329 return list(set(tokens))
330
331
332 relaxedGrammar = {
333 'supportSmiV1Keywords': [
334 SupportSmiV1Keywords.reserved,
335 SupportSmiV1Keywords.forbidden_words,
336 SupportSmiV1Keywords.tokens
337 ],
338 'supportIndex': [],
339 'commaAtTheEndOfImport': [],
340 'commaAtTheEndOfSequence': [],
341 'mixOfCommasAndSpaces': [],
342 'uppercaseIdentifier': [],
343 'lowcaseIdentifier': [],
344 'curlyBracesAroundEnterpriseInTrap': [],
345 'noCells': []
346 }
347
348
349 def lexerFactory(**grammarOptions):
350 classAttr = {}
351
352 for option in grammarOptions:
353 if grammarOptions[option]:
354 if option not in relaxedGrammar:
355 raise error.PySmiError('Unknown lexer relaxation option: %s' % option)
356
357 for func in relaxedGrammar[option]:
358 if sys.version_info[0] > 2:
359 classAttr[func.__name__] = func()
360 else:
361 classAttr[func.func_name] = func()
362
363 return type('SmiLexer', (SmiV2Lexer,), classAttr)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6
7
8 class MibInfo(object):
9 #: actual MIB name
10 name = ''
11
12 #: possible alternative to MIB name
13 alias = ''
14
15 #: URL to MIB file
16 path = ''
17
18 #: MIB file name
19 file = ''
20
21 #: MIB file modification time
22 mtime = 0
23
24 #: module OID
25 oid = ''
26
27 #: all OIDs defined in this module
28 oids = ()
29
30 #: MODULE-IDENTITY OID
31 identity = ''
32
33 #: Enterprise OID
34 enterprise = ()
35
36 #: MODULE-COMPLIANCE OIDs
37 compliance = ()
38
39 #: imported MIB names
40 imported = ()
41
42 def __init__(self, **kwargs):
43 for k in kwargs:
44 setattr(self, k, kwargs[k])
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi.parser.smiv1 import SmiV1Parser
7 from pysmi.parser.smiv1compat import SmiV1CompatParser, SmiStarParser
8 from pysmi.parser.smiv2 import SmiV2Parser
9 from pysmi.parser.null import NullParser
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6
7
8 class AbstractParser(object):
9 def reset(self):
10 raise NotImplementedError()
11
12 def parse(self, data, **kwargs):
13 raise NotImplementedError()
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6
7 #
8 # Preconfigured sets of parser options.
9 # Individual options could be used in certain combinations.
10 #
11 smiV2 = {}
12
13 smiV1 = smiV2.copy()
14 smiV1.update(
15 supportSmiV1Keywords=True,
16 supportIndex=True
17 )
18
19 smiV1Relaxed = smiV1.copy()
20 smiV1Relaxed.update(
21 commaAtTheEndOfImport=True,
22 commaAtTheEndOfSequence=True,
23 mixOfCommasAndSpaces=True,
24 uppercaseIdentifier=True,
25 lowcaseIdentifier=True,
26 curlyBracesAroundEnterpriseInTrap=True,
27 noCells=True
28 )
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi.parser.base import AbstractParser
7
8
9 class NullParser(AbstractParser):
10 def __init__(self, startSym='mibFile', tempdir=''):
11 pass
12
13 def reset(self):
14 pass
15
16 def parse(self, data, **kwargs):
17 return []
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import os
7 import sys
8 import ply.yacc as yacc
9 from pysmi.lexer.smi import lexerFactory
10 from pysmi.parser.base import AbstractParser
11 from pysmi import error
12 from pysmi import debug
13
14 YACC_VERSION = [int(x) for x in yacc.__version__.split('.')]
15
16
17 # noinspection PyMethodMayBeStatic,PyIncorrectDocstring
18 class SmiV2Parser(AbstractParser):
19 defaultLexer = lexerFactory()
20
21 def __init__(self, startSym='mibFile', tempdir=''):
22 if tempdir:
23 tempdir = os.path.join(tempdir, startSym)
24 try:
25 os.makedirs(tempdir)
26 except OSError:
27 if sys.exc_info()[1].errno != 17:
28 raise error.PySmiError('Failed to create cache directory %s: %s' % (tempdir, sys.exc_info()[1]))
29
30 self.lexer = self.defaultLexer(tempdir=tempdir)
31
32 # tokens are required for parser
33 self.tokens = self.lexer.tokens
34
35 if YACC_VERSION < [3, 0]:
36 self.parser = yacc.yacc(module=self,
37 start=startSym,
38 write_tables=bool(tempdir),
39 debug=False,
40 outputdir=tempdir)
41 else:
42 if debug.logger & debug.flagParser:
43 logger = debug.logger.getCurrentLogger()
44 else:
45 logger = yacc.NullLogger()
46
47 if debug.logger & debug.flagGrammar:
48 debuglogger = debug.logger.getCurrentLogger()
49 else:
50 debuglogger = None
51
52 self.parser = yacc.yacc(module=self,
53 start=startSym,
54 write_tables=bool(tempdir),
55 debug=False,
56 outputdir=tempdir,
57 debuglog=debuglogger,
58 errorlog=logger)
59
60 def reset(self):
61 # Ply requires lexer reinitialization for (at least) resetting lineno
62 self.lexer.reset()
63
64 def parse(self, data, **kwargs):
65 debug.logger & debug.flagParser and debug.logger(
66 'source MIB size is %s characters, first 50 characters are "%s..."' % (len(data), data[:50]))
67
68 ast = self.parser.parse(data, lexer=self.lexer.lexer)
69
70 self.reset()
71
72 if ast and ast[0] == 'mibFile' and ast[1]: # mibfile is not empty
73 return ast[1]
74 else:
75 return []
76
77 #
78 # SMIv2 grammar follows
79 #
80
81 def p_mibFile(self, p):
82 """mibFile : modules
83 | empty"""
84 p[0] = ('mibFile', p[1])
85
86 def p_modules(self, p):
87 """modules : modules module
88 | module"""
89 n = len(p)
90 if n == 3:
91 p[0] = p[1] + [p[2]]
92 elif n == 2:
93 p[0] = [p[1]]
94
95 def p_module(self, p):
96 """module : moduleName moduleOid DEFINITIONS COLON_COLON_EQUAL BEGIN exportsClause linkagePart declarationPart END"""
97 p[0] = (p[1], # name
98 p[2], # oid
99 p[7], # linkage (imports)
100 p[8]) # declaration
101
102 def p_moduleOid(self, p):
103 """moduleOid : '{' objectIdentifier '}'
104 | empty"""
105 n = len(p)
106 if n == 4:
107 p[0] = p[2]
108
109 def p_linkagePart(self, p):
110 """linkagePart : linkageClause
111 | empty"""
112 if p[1]:
113 p[0] = p[1]
114
115 def p_linkageClause(self, p):
116 """linkageClause : IMPORTS importPart ';'"""
117 p[0] = p[2]
118
119 def p_exportsClause(self, p):
120 """exportsClause : EXPORTS
121 | empty"""
122
123 def p_importPart(self, p):
124 """importPart : imports
125 | empty"""
126 # libsmi: TODO: ``IMPORTS ;'' allowed? refer ASN.1!
127 if p[1]:
128 importDict = {}
129 for imp in p[1]: # don't do just dict() because moduleNames may be repeated
130 fromModule, symbols = imp
131 if fromModule in importDict:
132 importDict[fromModule] += symbols
133 else:
134 importDict[fromModule] = symbols
135
136 p[0] = importDict
137
138 def p_imports(self, p):
139 """imports : imports import
140 | import"""
141 n = len(p)
142 if n == 3:
143 p[0] = p[1] + [p[2]]
144 elif n == 2:
145 p[0] = [p[1]]
146
147 def p_import(self, p):
148 """import : importIdentifiers FROM moduleName"""
149 # libsmi: TODO: multiple clauses with same moduleName allowed?
150 # I guess so. refer ASN.1!
151 p[0] = (p[3], # moduleName
152 p[1]) # ids
153
154 def p_importIdentifiers(self, p):
155 """importIdentifiers : importIdentifiers ',' importIdentifier
156 | importIdentifier"""
157 n = len(p)
158 if n == 4:
159 p[0] = p[1] + [p[3]]
160 elif n == 2:
161 p[0] = [p[1]]
162
163 # Note that some named types must not be imported, REF:RFC1902,590
164 def p_importIdentifier(self, p):
165 """importIdentifier : LOWERCASE_IDENTIFIER
166 | UPPERCASE_IDENTIFIER
167 | importedKeyword"""
168 p[0] = p[1]
169
170 def p_importedKeyword(self, p):
171 """importedKeyword : importedSMIKeyword
172 | BITS
173 | INTEGER32
174 | IPADDRESS
175 | MANDATORY_GROUPS
176 | MODULE_COMPLIANCE
177 | MODULE_IDENTITY
178 | OBJECT_GROUP
179 | OBJECT_IDENTITY
180 | OBJECT_TYPE
181 | OPAQUE
182 | TEXTUAL_CONVENTION
183 | TIMETICKS
184 | UNSIGNED32"""
185 p[0] = p[1]
186
187 def p_importedSMIKeyword(self, p):
188 """importedSMIKeyword : AGENT_CAPABILITIES
189 | COUNTER32
190 | COUNTER64
191 | GAUGE32
192 | NOTIFICATION_GROUP
193 | NOTIFICATION_TYPE
194 | TRAP_TYPE"""
195 p[0] = p[1]
196
197 def p_moduleName(self, p):
198 """moduleName : UPPERCASE_IDENTIFIER"""
199 p[0] = p[1]
200
201 def p_declarationPart(self, p):
202 """declarationPart : declarations
203 | empty"""
204 if p[1]:
205 p[0] = p[1]
206
207 def p_declarations(self, p):
208 """declarations : declarations declaration
209 | declaration"""
210 n = len(p)
211 if n == 3:
212 p[0] = p[1] + [p[2]]
213 elif n == 2:
214 p[0] = [p[1]]
215
216 def p_declaration(self, p):
217 """declaration : typeDeclaration
218 | valueDeclaration
219 | objectIdentityClause
220 | objectTypeClause
221 | trapTypeClause
222 | notificationTypeClause
223 | moduleIdentityClause
224 | moduleComplianceClause
225 | objectGroupClause
226 | notificationGroupClause
227 | agentCapabilitiesClause
228 | macroClause"""
229 if p[1]:
230 p[0] = p[1]
231
232 def p_macroClause(self, p):
233 """macroClause : macroName MACRO END"""
234
235 def p_macroName(self, p):
236 """macroName : MODULE_IDENTITY
237 | OBJECT_TYPE
238 | TRAP_TYPE
239 | NOTIFICATION_TYPE
240 | OBJECT_IDENTITY
241 | TEXTUAL_CONVENTION
242 | OBJECT_GROUP
243 | NOTIFICATION_GROUP
244 | MODULE_COMPLIANCE
245 | AGENT_CAPABILITIES"""
246
247 def p_choiceClause(self, p):
248 """choiceClause : CHOICE """
249
250 # libsmi: The only ASN.1 value declarations are for OIDs, REF:RFC1902,491.
251 def p_fuzzy_lowercase_identifier(self, p):
252 """fuzzy_lowercase_identifier : LOWERCASE_IDENTIFIER
253 | UPPERCASE_IDENTIFIER"""
254 p[0] = p[1]
255
256 def p_valueDeclaration(self, p):
257 """valueDeclaration : fuzzy_lowercase_identifier OBJECT IDENTIFIER COLON_COLON_EQUAL '{' objectIdentifier '}'"""
258 p[0] = ('valueDeclaration', p[1], # id
259 p[6]) # objectIdentifier
260
261 def p_typeDeclaration(self, p):
262 """typeDeclaration : typeName COLON_COLON_EQUAL typeDeclarationRHS"""
263 p[0] = ('typeDeclaration', p[1], # name
264 p[3]) # declarationRHS
265
266 def p_typeName(self, p):
267 """typeName : UPPERCASE_IDENTIFIER
268 | typeSMI"""
269 p[0] = p[1]
270
271 def p_typeSMI(self, p):
272 """typeSMI : typeSMIandSPPI
273 | typeSMIonly"""
274 p[0] = p[1]
275
276 def p_typeSMIandSPPI(self, p):
277 """typeSMIandSPPI : IPADDRESS
278 | TIMETICKS
279 | OPAQUE
280 | INTEGER32
281 | UNSIGNED32"""
282 p[0] = p[1]
283
284 def p_typeSMIonly(self, p):
285 """typeSMIonly : COUNTER32
286 | GAUGE32
287 | COUNTER64"""
288 p[0] = p[1]
289
290 def p_typeDeclarationRHS(self, p):
291 """typeDeclarationRHS : Syntax
292 | TEXTUAL_CONVENTION DisplayPart STATUS Status DESCRIPTION Text ReferPart SYNTAX Syntax
293 | choiceClause"""
294 if p[1]:
295 if p[1] == 'TEXTUAL-CONVENTION':
296 p[0] = ('typeDeclarationRHS', p[2], # display
297 p[4], # status
298 (p[5], p[6]), # description
299 p[7], # reference
300 p[9]) # syntax
301 else:
302 p[0] = ('typeDeclarationRHS', p[1])
303 # ignore the choiceClause
304
305 def p_conceptualTable(self, p):
306 """conceptualTable : SEQUENCE OF row"""
307 p[0] = ('conceptualTable', p[3])
308
309 def p_row(self, p):
310 """row : UPPERCASE_IDENTIFIER"""
311 # libsmi: TODO: this must be an entryType
312 p[0] = ('row', p[1])
313
314 def p_entryType(self, p):
315 """entryType : SEQUENCE '{' sequenceItems '}'"""
316 p[0] = (p[1], p[3])
317
318 def p_sequenceItems(self, p):
319 """sequenceItems : sequenceItems ',' sequenceItem
320 | sequenceItem"""
321 # libsmi: TODO: might this list be emtpy?
322 n = len(p)
323 if n == 4:
324 p[0] = p[1] + [p[3]]
325 elif n == 2:
326 p[0] = [p[1]]
327
328 def p_sequenceItem(self, p):
329 """sequenceItem : LOWERCASE_IDENTIFIER sequenceSyntax"""
330 p[0] = (p[1], p[2])
331
332 def p_Syntax(self, p):
333 """Syntax : ObjectSyntax
334 | BITS '{' NamedBits '}'"""
335 # libsmi: TODO: standalone `BITS' ok? seen in RMON2-MIB
336 # libsmi: -> no, it's only allowed in a SEQUENCE {...}
337 n = len(p)
338 if n == 2:
339 p[0] = p[1]
340 elif n == 5:
341 p[0] = (p[1], p[3])
342
343 def p_sequenceSyntax(self, p):
344 """sequenceSyntax : BITS
345 | UPPERCASE_IDENTIFIER anySubType
346 | sequenceObjectSyntax"""
347 p[0] = p[1] # no subtype or complex syntax supported
348
349 def p_NamedBits(self, p):
350 """NamedBits : NamedBits ',' NamedBit
351 | NamedBit"""
352 n = len(p)
353 if n == 4:
354 p[0] = p[1] + [p[3]]
355 elif n == 2:
356 p[0] = [p[1]]
357
358 def p_NamedBit(self, p):
359 """NamedBit : LOWERCASE_IDENTIFIER '(' NUMBER ')'"""
360 p[0] = (p[1], p[3])
361
362 def p_objectIdentityClause(self, p):
363 """objectIdentityClause : LOWERCASE_IDENTIFIER OBJECT_IDENTITY STATUS Status DESCRIPTION Text ReferPart COLON_COLON_EQUAL '{' objectIdentifier '}'"""
364 p[0] = ('objectIdentityClause', p[1], # id
365 # p[2], # OBJECT_IDENTITY
366 p[4], # status
367 (p[5], p[6]), # description
368 p[7], # reference
369 p[10]) # objectIdentifier
370
371 def p_objectTypeClause(self, p):
372 """objectTypeClause : LOWERCASE_IDENTIFIER OBJECT_TYPE SYNTAX Syntax UnitsPart MaxOrPIBAccessPart STATUS Status descriptionClause ReferPart IndexPart MibIndex DefValPart COLON_COLON_EQUAL '{' ObjectName '}'"""
373 p[0] = ('objectTypeClause', p[1], # id
374 # p[2], # OBJECT_TYPE
375 p[4], # syntax
376 p[5], # UnitsPart
377 p[6], # MaxOrPIBAccessPart
378 p[8], # status
379 p[9], # descriptionClause
380 p[10], # reference
381 p[11], # augmentions
382 p[12], # index
383 p[13], # DefValPart
384 p[16]) # ObjectName
385
386 def p_descriptionClause(self, p):
387 """descriptionClause : DESCRIPTION Text
388 | empty"""
389 if p[1]:
390 p[0] = (p[1], p[2])
391
392 def p_trapTypeClause(self, p):
393 """trapTypeClause : fuzzy_lowercase_identifier TRAP_TYPE ENTERPRISE objectIdentifier VarPart DescrPart ReferPart COLON_COLON_EQUAL NUMBER"""
394 # libsmi: TODO: range of number?
395 p[0] = ('trapTypeClause', p[1], # fuzzy_lowercase_identifier
396 # p[2], # TRAP_TYPE
397 p[4], # objectIdentifier
398 p[5], # VarPart
399 p[6], # description
400 p[7], # reference
401 p[9]) # NUMBER
402
403 def p_VarPart(self, p):
404 """VarPart : VARIABLES '{' VarTypes '}'
405 | empty"""
406 p[0] = p[1] and p[3] or []
407
408 def p_VarTypes(self, p):
409 """VarTypes : VarTypes ',' VarType
410 | VarType"""
411 n = len(p)
412 if n == 4:
413 p[0] = ('VarTypes', p[1][1] + [p[3]])
414 elif n == 2:
415 p[0] = ('VarTypes', [p[1]])
416
417 def p_VarType(self, p):
418 """VarType : ObjectName"""
419 p[0] = p[1][1][0]
420
421 def p_DescrPart(self, p):
422 """DescrPart : DESCRIPTION Text
423 | empty"""
424 if p[1]:
425 p[0] = (p[1], p[2])
426
427 def p_MaxOrPIBAccessPart(self, p):
428 """MaxOrPIBAccessPart : MaxAccessPart
429 | empty"""
430 if p[1]:
431 p[0] = p[1]
432
433 def p_MaxAccessPart(self, p):
434 """MaxAccessPart : MAX_ACCESS Access
435 | ACCESS Access"""
436 p[0] = ('MaxAccessPart', p[2])
437
438 def p_notificationTypeClause(self, p):
439 """notificationTypeClause : LOWERCASE_IDENTIFIER NOTIFICATION_TYPE NotificationObjectsPart STATUS Status DESCRIPTION Text ReferPart COLON_COLON_EQUAL '{' NotificationName '}'"""
440 p[0] = ('notificationTypeClause',
441 p[1], # id
442 # p[2], # NOTIFICATION_TYPE
443 p[3], # NotificationObjectsPart
444 p[5], # status
445 (p[6], p[7]), # description
446 p[8], # reference
447 p[11]) # NotificationName aka objectIdentifier
448
449 def p_moduleIdentityClause(self, p):
450 """moduleIdentityClause : LOWERCASE_IDENTIFIER MODULE_IDENTITY SubjectCategoriesPart LAST_UPDATED ExtUTCTime ORGANIZATION Text CONTACT_INFO Text DESCRIPTION Text RevisionPart COLON_COLON_EQUAL '{' objectIdentifier '}'"""
451 p[0] = ('moduleIdentityClause', p[1], # id
452 # p[2], # MODULE_IDENTITY
453 # XXX p[3], # SubjectCategoriesPart
454 (p[4], p[5]), # last updated
455 (p[6], p[7]), # organization
456 (p[8], p[9]), # contact info
457 (p[10], p[11]), # description
458 p[12], # RevisionPart
459 p[15]) # objectIdentifier
460
461 # Subject categories: RFC3159
462
463 def p_SubjectCategoriesPart(self, p):
464 """SubjectCategoriesPart : SUBJECT_CATEGORIES '{' SubjectCategories '}'
465 | empty"""
466 # if p[1]:
467 # p[0] = (p[1], p[3])
468
469 def p_SubjectCategories(self, p):
470 """SubjectCategories : CategoryIDs"""
471 # p[0] = p[1]
472
473 def p_CategoryIDs(self, p):
474 """CategoryIDs : CategoryIDs ',' CategoryID
475 | CategoryID"""
476 # n = len(p)
477 # if n == 4:
478 # p[0] = ('CategoryIDs', p[1][1] + [p[3]])
479 # elif n == 2:
480 # p[0] = ('CategoryIDs', [p[1]])
481
482 def p_CategoryID(self, p):
483 """CategoryID : LOWERCASE_IDENTIFIER '(' NUMBER ')'
484 | LOWERCASE_IDENTIFIER"""
485 # n = len(p)
486 # if n == 2:
487 # p[0] = ('CategoryID', p[1])
488 # elif n == 5:
489 # p[0] = ('CategoryID', p[3])
490
491 # ...subject categories
492
493 def p_ObjectSyntax(self, p):
494 """ObjectSyntax : SimpleSyntax
495 | conceptualTable
496 | row
497 | entryType
498 | ApplicationSyntax
499 | typeTag SimpleSyntax"""
500 n = len(p)
501 if n == 2:
502 p[0] = p[1]
503 elif n == 3:
504 p[0] = p[2]
505
506 def p_typeTag(self, p):
507 """typeTag : '[' APPLICATION NUMBER ']' IMPLICIT
508 | '[' UNIVERSAL NUMBER ']' IMPLICIT"""
509
510 def p_sequenceObjectSyntax(self, p):
511 """sequenceObjectSyntax : sequenceSimpleSyntax
512 | sequenceApplicationSyntax"""
513 # libsmi: TO DO: add to this rule conceptualTable, row, entryType
514 p[0] = p[1]
515
516 def p_valueofObjectSyntax(self, p):
517 """valueofObjectSyntax : valueofSimpleSyntax"""
518 p[0] = p[1]
519
520 def p_SimpleSyntax(self, p):
521 """SimpleSyntax : INTEGER
522 | INTEGER integerSubType
523 | INTEGER enumSpec
524 | INTEGER32
525 | INTEGER32 integerSubType
526 | UPPERCASE_IDENTIFIER enumSpec
527 | UPPERCASE_IDENTIFIER integerSubType
528 | OCTET STRING
529 | OCTET STRING octetStringSubType
530 | UPPERCASE_IDENTIFIER octetStringSubType
531 | OBJECT IDENTIFIER anySubType"""
532 n = len(p)
533 if n == 2:
534 p[0] = ('SimpleSyntax', p[1])
535
536 elif n == 3:
537 if p[1] == 'OCTET':
538 p[0] = ('SimpleSyntax', p[1] + ' ' + p[2])
539 else:
540 p[0] = ('SimpleSyntax', p[1], p[2])
541
542 elif n == 4:
543 p[0] = ('SimpleSyntax', p[1] + ' ' + p[2], p[3])
544
545 def p_valueofSimpleSyntax(self, p):
546 """valueofSimpleSyntax : NUMBER
547 | NEGATIVENUMBER
548 | NUMBER64
549 | NEGATIVENUMBER64
550 | HEX_STRING
551 | BIN_STRING
552 | LOWERCASE_IDENTIFIER
553 | QUOTED_STRING
554 | '{' objectIdentifier_defval '}'"""
555 # libsmi for objectIdentifier_defval:
556 # This is only for some MIBs with invalid numerical
557 # OID notation for DEFVALs. We DO NOT parse them
558 # correctly. We just don't want to produce a
559 # parser error.
560 n = len(p)
561 if n == 2:
562 p[0] = p[1]
563 elif n == 4: # XXX
564 pass
565
566 def p_sequenceSimpleSyntax(self, p):
567 """sequenceSimpleSyntax : INTEGER anySubType
568 | INTEGER32 anySubType
569 | OCTET STRING anySubType
570 | OBJECT IDENTIFIER anySubType"""
571 n = len(p)
572 if n == 3:
573 p[0] = p[1] # XXX not supporting subtypes here
574 elif n == 4:
575 p[0] = p[1] + ' ' + p[2] # XXX not supporting subtypes here
576
577 def p_ApplicationSyntax(self, p):
578 """ApplicationSyntax : IPADDRESS anySubType
579 | COUNTER32
580 | COUNTER32 integerSubType
581 | GAUGE32
582 | GAUGE32 integerSubType
583 | UNSIGNED32
584 | UNSIGNED32 integerSubType
585 | TIMETICKS anySubType
586 | OPAQUE
587 | OPAQUE octetStringSubType
588 | COUNTER64
589 | COUNTER64 integerSubType"""
590 # COUNTER32 and COUNTER64 was with anySubType in libsmi
591 n = len(p)
592 if n == 2:
593 p[0] = ('ApplicationSyntax', p[1])
594 elif n == 3:
595 p[0] = ('ApplicationSyntax', p[1], p[2])
596
597 def p_sequenceApplicationSyntax(self, p):
598 """sequenceApplicationSyntax : IPADDRESS anySubType
599 | COUNTER32 anySubType
600 | GAUGE32 anySubType
601 | UNSIGNED32 anySubType
602 | TIMETICKS anySubType
603 | OPAQUE
604 | COUNTER64 anySubType"""
605 n = len(p)
606 if n == 2:
607 p[0] = p[1]
608 elif n == 3:
609 p[0] = p[1] # XXX not supporting subtypes here
610
611 def p_anySubType(self, p):
612 """anySubType : integerSubType
613 | octetStringSubType
614 | enumSpec
615 | empty"""
616 if p[1]:
617 p[0] = p[1]
618
619 def p_integerSubType(self, p):
620 """integerSubType : '(' ranges ')'"""
621 p[0] = ('integerSubType', p[2])
622
623 def p_octetStringSubType(self, p):
624 """octetStringSubType : '(' SIZE '(' ranges ')' ')'"""
625 p[0] = ('octetStringSubType', p[4])
626
627 def p_ranges(self, p):
628 """ranges : ranges '|' range
629 | range"""
630 n = len(p)
631 if n == 4:
632 p[0] = p[1] + [p[3]]
633 elif n == 2:
634 p[0] = [p[1]]
635
636 def p_range(self, p):
637 """range : value DOT_DOT value
638 | value"""
639 n = len(p)
640 if n == 2:
641 p[0] = (p[1],)
642 elif n == 4:
643 p[0] = (p[1], p[3])
644
645 def p_value(self, p):
646 """value : NEGATIVENUMBER
647 | NUMBER
648 | NEGATIVENUMBER64
649 | NUMBER64
650 | HEX_STRING
651 | BIN_STRING"""
652 p[0] = p[1]
653
654 def p_enumSpec(self, p):
655 """enumSpec : '{' enumItems '}'"""
656 p[0] = ('enumSpec', p[2])
657
658 def p_enumItems(self, p):
659 """enumItems : enumItems ',' enumItem
660 | enumItem"""
661 n = len(p)
662 if n == 4:
663 p[0] = p[1] + [p[3]]
664 elif n == 2:
665 p[0] = [p[1]]
666
667 def p_enumItem(self, p):
668 """enumItem : LOWERCASE_IDENTIFIER '(' enumNumber ')'"""
669 p[0] = (p[1], p[3])
670
671 def p_enumNumber(self, p):
672 """enumNumber : NUMBER
673 | NEGATIVENUMBER"""
674 # XXX | LOWERCASE_IDENTIFIER"""
675 p[0] = p[1]
676
677 def p_Status(self, p):
678 """Status : LOWERCASE_IDENTIFIER"""
679 p[0] = ('Status', p[1])
680
681 def p_DisplayPart(self, p):
682 """DisplayPart : DISPLAY_HINT Text
683 | empty"""
684 if p[1]:
685 p[0] = (p[1], p[2])
686
687 def p_UnitsPart(self, p):
688 """UnitsPart : UNITS Text
689 | empty"""
690 if p[1]:
691 p[0] = (p[1], p[2])
692
693 def p_Access(self, p):
694 """Access : LOWERCASE_IDENTIFIER"""
695 p[0] = p[1]
696
697 def p_IndexPart(self, p):
698 """IndexPart : AUGMENTS '{' Entry '}'
699 | empty"""
700 if p[1]:
701 p[0] = p[3]
702
703 def p_MibIndex(self, p):
704 """MibIndex : INDEX '{' IndexTypes '}'
705 | empty"""
706 if p[1]:
707 p[0] = (p[1], p[3])
708
709 def p_IndexTypes(self, p):
710 """IndexTypes : IndexTypes ',' IndexType
711 | IndexType"""
712 n = len(p)
713 if n == 4:
714 p[0] = p[1] + [p[3]]
715 elif n == 2:
716 p[0] = [p[1]]
717
718 def p_IndexType(self, p):
719 """IndexType : IMPLIED Index
720 | Index"""
721 n = len(p)
722 if n == 2:
723 p[0] = (0, p[1])
724 elif n == 3:
725 p[0] = (1, p[2]) # IMPLIED
726
727 def p_Index(self, p):
728 """Index : ObjectName"""
729 # libsmi: TODO: use the SYNTAX value of the correspondent
730 # OBJECT-TYPE invocation
731 p[0] = p[1][1][0] # XXX just name???
732
733 def p_Entry(self, p):
734 """Entry : ObjectName"""
735 p[0] = p[1][1][0]
736
737 def p_DefValPart(self, p):
738 """DefValPart : DEFVAL '{' Value '}'
739 | empty"""
740 if p[1] and p[3]:
741 p[0] = (p[1], p[3])
742
743 def p_Value(self, p):
744 """Value : valueofObjectSyntax
745 | '{' BitsValue '}'"""
746 n = len(p)
747 if n == 2:
748 p[0] = p[1]
749 elif n == 4:
750 p[0] = p[2]
751
752 def p_BitsValue(self, p):
753 """BitsValue : BitNames
754 | empty"""
755 if p[1]:
756 p[0] = p[1]
757
758 def p_BitNames(self, p):
759 """BitNames : BitNames ',' LOWERCASE_IDENTIFIER
760 | LOWERCASE_IDENTIFIER"""
761 n = len(p)
762 if n == 4:
763 p[0] = ('BitNames', p[1][1] + [p[3]])
764 elif n == 2:
765 p[0] = ('BitNames', [p[1]])
766
767 def p_ObjectName(self, p):
768 """ObjectName : objectIdentifier"""
769 p[0] = p[1]
770
771 def p_NotificationName(self, p):
772 """NotificationName : objectIdentifier"""
773 p[0] = p[1]
774
775 def p_ReferPart(self, p):
776 """ReferPart : REFERENCE Text
777 | empty"""
778 if p[1]:
779 p[0] = (p[1], p[2])
780
781 def p_RevisionPart(self, p):
782 """RevisionPart : Revisions
783 | empty"""
784 if p[1]:
785 p[0] = p[1]
786
787 def p_Revisions(self, p):
788 """Revisions : Revisions Revision
789 | Revision"""
790 n = len(p)
791 if n == 3:
792 p[0] = ('Revisions', p[1][1] + [p[2]])
793 elif n == 2:
794 p[0] = ('Revisions', [p[1]])
795
796 def p_Revision(self, p):
797 """Revision : REVISION ExtUTCTime DESCRIPTION Text"""
798 p[0] = (p[2], # revision time
799 (p[3], p[4])) # description
800
801 def p_NotificationObjectsPart(self, p):
802 """NotificationObjectsPart : OBJECTS '{' Objects '}'
803 | empty"""
804 p[0] = p[1] and p[3] or []
805
806 def p_ObjectGroupObjectsPart(self, p):
807 """ObjectGroupObjectsPart : OBJECTS '{' Objects '}'"""
808 p[0] = p[3]
809
810 def p_Objects(self, p):
811 """Objects : Objects ',' Object
812 | Object"""
813 n = len(p)
814 if n == 4:
815 p[0] = ('Objects', p[1][1] + [p[3]])
816 elif n == 2:
817 p[0] = ('Objects', [p[1]])
818
819 def p_Object(self, p):
820 """Object : ObjectName"""
821 p[0] = p[1][1][0]
822
823 def p_NotificationsPart(self, p):
824 """NotificationsPart : NOTIFICATIONS '{' Notifications '}'"""
825 p[0] = p[3]
826
827 def p_Notifications(self, p):
828 """Notifications : Notifications ',' Notification
829 | Notification"""
830 n = len(p)
831 if n == 4:
832 p[0] = ('Notifications', p[1][1] + [p[3]])
833 elif n == 2:
834 p[0] = ('Notifications', [p[1]])
835
836 def p_Notification(self, p):
837 """Notification : NotificationName"""
838 p[0] = p[1][1][0]
839
840 def p_Text(self, p):
841 """Text : QUOTED_STRING"""
842 p[0] = p[1][1:-1] # getting rid of quotes
843
844 def p_ExtUTCTime(self, p):
845 """ExtUTCTime : QUOTED_STRING"""
846 p[0] = p[1][1:-1] # getting rid of quotes
847
848 def p_objectIdentifier(self, p):
849 """objectIdentifier : subidentifiers"""
850 p[0] = ('objectIdentifier', p[1])
851
852 def p_subidentifiers(self, p):
853 """subidentifiers : subidentifiers subidentifier
854 | subidentifier"""
855 n = len(p)
856 if n == 3:
857 p[0] = p[1] + [p[2]]
858 elif n == 2:
859 p[0] = [p[1]]
860
861 def p_subidentifier(self, p):
862 """subidentifier : fuzzy_lowercase_identifier
863 | NUMBER
864 | LOWERCASE_IDENTIFIER '(' NUMBER ')'"""
865 n = len(p)
866 if n == 2:
867 p[0] = p[1]
868 elif n == 5:
869 # NOTE: we are not creating new symbol p[1] because formally
870 # it is not defined in *this* MIB
871 p[0] = (p[1], p[3])
872
873 def p_objectIdentifier_defval(self, p):
874 """objectIdentifier_defval : subidentifiers_defval"""
875 p[0] = ('objectIdentifier_defval', p[1])
876
877 def p_subidentifiers_defval(self, p):
878 """subidentifiers_defval : subidentifiers_defval subidentifier_defval
879 | subidentifier_defval"""
880 n = len(p)
881 if n == 3:
882 p[0] = ('subidentifiers_defval', p[1][1] + [p[2]])
883 elif n == 2:
884 p[0] = ('subidentifiers_defval', [p[1]])
885
886 def p_subidentifier_defval(self, p):
887 """subidentifier_defval : LOWERCASE_IDENTIFIER '(' NUMBER ')'
888 | NUMBER"""
889 n = len(p)
890 if n == 2:
891 p[0] = ('subidentifier_defval', p[1])
892 elif n == 5:
893 p[0] = ('subidentifier_defval', p[1], p[3])
894
895 def p_objectGroupClause(self, p):
896 """objectGroupClause : LOWERCASE_IDENTIFIER OBJECT_GROUP ObjectGroupObjectsPart STATUS Status DESCRIPTION Text ReferPart COLON_COLON_EQUAL '{' objectIdentifier '}'"""
897 p[0] = ('objectGroupClause',
898 p[1], # id
899 p[3], # objects
900 p[5], # status
901 (p[6], p[7]), # description
902 p[8], # reference
903 p[11]) # objectIdentifier
904
905 def p_notificationGroupClause(self, p):
906 """notificationGroupClause : LOWERCASE_IDENTIFIER NOTIFICATION_GROUP NotificationsPart STATUS Status DESCRIPTION Text ReferPart COLON_COLON_EQUAL '{' objectIdentifier '}'"""
907 p[0] = ('notificationGroupClause',
908 p[1], # id
909 p[3], # notifications
910 p[5], # status
911 (p[6], p[7]), # description
912 p[8], # reference
913 p[11]) # objectIdentifier
914
915 def p_moduleComplianceClause(self, p):
916 """moduleComplianceClause : LOWERCASE_IDENTIFIER MODULE_COMPLIANCE STATUS Status DESCRIPTION Text ReferPart ComplianceModulePart COLON_COLON_EQUAL '{' objectIdentifier '}'"""
917 p[0] = ('moduleComplianceClause',
918 p[1], # id
919 # p[2], # MODULE_COMPLIANCE
920 p[4], # status
921 (p[5], p[6]), # description
922 p[7], # reference
923 p[8], # ComplianceModules
924 p[11]) # objectIdentifier
925
926 def p_ComplianceModulePart(self, p):
927 """ComplianceModulePart : ComplianceModules"""
928 p[0] = p[1]
929
930 def p_ComplianceModules(self, p):
931 """ComplianceModules : ComplianceModules ComplianceModule
932 | ComplianceModule"""
933 n = len(p)
934 if n == 3:
935 p[0] = ('ComplianceModules', p[1][1] + [p[2]])
936 elif n == 2:
937 p[0] = ('ComplianceModules', [p[1]])
938
939 def p_ComplianceModule(self, p):
940 """ComplianceModule : MODULE ComplianceModuleName MandatoryPart CompliancePart"""
941 objects = p[3] and p[3][1] or []
942 objects += p[4] and p[4][1] or []
943 p[0] = (p[2], # ModuleName
944 objects) # MandatoryPart + CompliancePart
945
946 def p_ComplianceModuleName(self, p):
947 """ComplianceModuleName : UPPERCASE_IDENTIFIER
948 | empty"""
949 # XXX | UPPERCASE_IDENTIFIER objectIdentifier
950 p[0] = p[1]
951
952 def p_MandatoryPart(self, p):
953 """MandatoryPart : MANDATORY_GROUPS '{' MandatoryGroups '}'
954 | empty"""
955 if p[1]:
956 p[0] = p[3]
957
958 def p_MandatoryGroups(self, p):
959 """MandatoryGroups : MandatoryGroups ',' MandatoryGroup
960 | MandatoryGroup"""
961 n = len(p)
962 if n == 4:
963 p[0] = ('MandatoryGroups', p[1][1] + [p[3]])
964 elif n == 2:
965 p[0] = ('MandatoryGroups', [p[1]])
966
967 def p_MandatoryGroup(self, p):
968 """MandatoryGroup : objectIdentifier"""
969 p[0] = p[1][1][0] # objectIdentifier? Maybe name?
970
971 def p_CompliancePart(self, p):
972 """CompliancePart : Compliances
973 | empty"""
974 if p[1]:
975 p[0] = p[1]
976
977 def p_Compliances(self, p):
978 """Compliances : Compliances Compliance
979 | Compliance"""
980 n = len(p)
981 if n == 3:
982 p[0] = p[1] and p[2] and ('Compliances', p[1][1] + [p[2]]) or p[1]
983 elif n == 2:
984 p[0] = p[1] and ('Compliances', [p[1]]) or None
985
986 def p_Compliance(self, p):
987 """Compliance : ComplianceGroup
988 | ComplianceObject"""
989 if p[1]:
990 p[0] = p[1]
991
992 def p_ComplianceGroup(self, p):
993 """ComplianceGroup : GROUP objectIdentifier DESCRIPTION Text"""
994 p[0] = p[2][1][0] # objectIdentifier
995 # p[1], # GROUP
996 # (p[3], p[4])) # description
997
998 def p_ComplianceObject(self, p):
999 """ComplianceObject : OBJECT ObjectName SyntaxPart WriteSyntaxPart AccessPart DESCRIPTION Text"""
1000 # p[0] = (p[1], # object
1001 # p[2], # name
1002 # p[3], # syntax
1003 # p[4], # write syntax
1004 # p[5], # access
1005 # (p[6], p[7])) # description
1006
1007 def p_SyntaxPart(self, p):
1008 """SyntaxPart : SYNTAX Syntax
1009 | empty"""
1010 if p[1]:
1011 p[0] = p[2]
1012
1013 def p_WriteSyntaxPart(self, p):
1014 """WriteSyntaxPart : WRITE_SYNTAX WriteSyntax
1015 | empty"""
1016 if p[1]:
1017 p[0] = p[2]
1018
1019 def p_WriteSyntax(self, p):
1020 """WriteSyntax : Syntax"""
1021 p[0] = ('WriteSyntax', p[1])
1022
1023 def p_AccessPart(self, p):
1024 """AccessPart : MIN_ACCESS Access
1025 | empty"""
1026 if p[1]:
1027 p[0] = (p[1], p[2])
1028
1029 def p_agentCapabilitiesClause(self, p):
1030 """agentCapabilitiesClause : LOWERCASE_IDENTIFIER AGENT_CAPABILITIES PRODUCT_RELEASE Text STATUS Status DESCRIPTION Text ReferPart ModulePart_Capabilities COLON_COLON_EQUAL '{' objectIdentifier '}'"""
1031 p[0] = ('agentCapabilitiesClause', p[1], # id
1032 # p[2], # AGENT_CAPABILITIES
1033 (p[3], p[4]), # product release
1034 p[6], # status
1035 (p[7], p[8]), # description
1036 p[9], # reference
1037 # p[10], # module capabilities
1038 p[13]) # objectIdentifier
1039
1040 def p_ModulePart_Capabilities(self, p):
1041 """ModulePart_Capabilities : Modules_Capabilities
1042 | empty"""
1043 # if p[1]:
1044 # p[0] = p[1]
1045
1046 def p_Modules_Capabilities(self, p):
1047 """Modules_Capabilities : Modules_Capabilities Module_Capabilities
1048 | Module_Capabilities"""
1049 # n = len(p)
1050 # if n == 3:
1051 # p[0] = ('Modules_Capabilities', p[1][1] + [p[2]])
1052 # elif n == 2:
1053 # p[0] = ('Modules_Capabilities', [p[1]])
1054
1055 def p_Module_Capabilities(self, p):
1056 """Module_Capabilities : SUPPORTS ModuleName_Capabilities INCLUDES '{' CapabilitiesGroups '}' VariationPart"""
1057 # p[0] = ('Module_Capabilities', (p[1], p[2]), # supports
1058 # (p[3], p[5]), # includes
1059 # p[7]) # variations
1060
1061 def p_CapabilitiesGroups(self, p):
1062 """CapabilitiesGroups : CapabilitiesGroups ',' CapabilitiesGroup
1063 | CapabilitiesGroup"""
1064 # n = len(p)
1065 # if n == 4:
1066 # p[0] = ('CapabilitiesGroups', p[1][1] + [p[3]])
1067 # elif n == 2:
1068 # p[0] = ('CapabilitiesGroups', [p[1]])
1069
1070 def p_CapabilitiesGroup(self, p):
1071 """CapabilitiesGroup : objectIdentifier"""
1072 # p[0] = ('CapabilitiesGroup', p[1])
1073
1074 def p_ModuleName_Capabilities(self, p):
1075 """ModuleName_Capabilities : UPPERCASE_IDENTIFIER objectIdentifier
1076 | UPPERCASE_IDENTIFIER"""
1077 # n = len(p)
1078 # if n == 2:
1079 # p[0] = ('ModuleName_Capabilities', p[1])
1080 # elif n == 3:
1081 # p[0] = ('ModuleName_Capabilities', p[1], p[2])
1082
1083 def p_VariationPart(self, p):
1084 """VariationPart : Variations
1085 | empty"""
1086 # if p[1]:
1087 # p[0] = p[1]
1088
1089 def p_Variations(self, p):
1090 """Variations : Variations Variation
1091 | Variation"""
1092 # n = len(p)
1093 # if n == 3:
1094 # p[0] = ('Variations', p[1][1] + [p[2]])
1095 # elif n == 2:
1096 # p[0] = ('Variations', [p[1]]) pass
1097
1098 def p_Variation(self, p):
1099 """Variation : VARIATION ObjectName SyntaxPart WriteSyntaxPart VariationAccessPart CreationPart DefValPart DESCRIPTION Text"""
1100 # p[0] = (p[1], # variation
1101 # p[2], # name
1102 # p[3], # syntax
1103 # p[4], # write syntax
1104 # p[5], # access
1105 # p[6], # creation
1106 # p[7], # defval
1107 # (p[8], p[9])) # description
1108
1109 def p_VariationAccessPart(self, p):
1110 """VariationAccessPart : ACCESS VariationAccess
1111 | empty"""
1112 # if p[1]:
1113 # p[0] = (p[1], p[2])
1114
1115 def p_VariationAccess(self, p):
1116 """VariationAccess : LOWERCASE_IDENTIFIER"""
1117 # p[0] = p[1]
1118
1119 def p_CreationPart(self, p):
1120 """CreationPart : CREATION_REQUIRES '{' Cells '}'
1121 | empty"""
1122 if p[1]:
1123 p[0] = (p[1], p[3])
1124
1125 def p_Cells(self, p):
1126 """Cells : Cells ',' Cell
1127 | Cell"""
1128 n = len(p)
1129 if n == 4:
1130 p[0] = ('Cells', p[1][1] + [p[3]])
1131 elif n == 2:
1132 p[0] = ('Cells', [p[1]])
1133
1134 def p_Cell(self, p):
1135 """Cell : ObjectName"""
1136 p[0] = ('Cell', p[1])
1137
1138 def p_empty(self, p):
1139 """empty :"""
1140
1141 # Error rule for syntax errors
1142 def p_error(self, p):
1143 if p:
1144 raise error.PySmiParserError("Bad grammar near token type %s, value %s" % (p.type, p.value),
1145 lineno=p.lineno)
1146
1147
1148 #
1149 # Parser grammar relaxation follows.
1150 #
1151 # The classes that follow serve a purpose of encapsulating assorted functions
1152 # into a namespace. The namespace type is not universally supported across all
1153 # Python versions we want to run on, thus the hack with `staticmethod` decorator
1154 # and `self` first parameter.
1155 #
1156
1157 #
1158 # SMIv1 grammar
1159 #
1160
1161 # noinspection PyIncorrectDocstring
1162 class SupportSmiV1Keywords(object):
1163 # NETWORKADDRESS added
1164 @staticmethod
1165 def p_importedKeyword(self, p):
1166 """importedKeyword : importedSMIKeyword
1167 | BITS
1168 | INTEGER32
1169 | IPADDRESS
1170 | NETWORKADDRESS
1171 | MANDATORY_GROUPS
1172 | MODULE_COMPLIANCE
1173 | MODULE_IDENTITY
1174 | OBJECT_GROUP
1175 | OBJECT_IDENTITY
1176 | OBJECT_TYPE
1177 | OPAQUE
1178 | TEXTUAL_CONVENTION
1179 | TIMETICKS
1180 | UNSIGNED32"""
1181 p[0] = p[1]
1182
1183 # NETWORKADDRESS added
1184 @staticmethod
1185 def p_typeSMIandSPPI(self, p):
1186 """typeSMIandSPPI : IPADDRESS
1187 | NETWORKADDRESS
1188 | TIMETICKS
1189 | OPAQUE
1190 | INTEGER32
1191 | UNSIGNED32"""
1192 p[0] = p[1]
1193
1194 # NETWORKADDRESS added
1195 @staticmethod
1196 def p_ApplicationSyntax(self, p):
1197 """ApplicationSyntax : IPADDRESS anySubType
1198 | NETWORKADDRESS anySubType
1199 | COUNTER32
1200 | COUNTER32 integerSubType
1201 | GAUGE32
1202 | GAUGE32 integerSubType
1203 | UNSIGNED32
1204 | UNSIGNED32 integerSubType
1205 | TIMETICKS anySubType
1206 | OPAQUE
1207 | OPAQUE octetStringSubType
1208 | COUNTER64
1209 | COUNTER64 integerSubType"""
1210 n = len(p)
1211 if n == 2:
1212 p[0] = ('ApplicationSyntax', p[1])
1213 elif n == 3:
1214 p[0] = ('ApplicationSyntax', p[1], p[2])
1215
1216 # NETWORKADDRESS added for SEQUENCE syntax
1217 @staticmethod
1218 def p_sequenceApplicationSyntax(self, p):
1219 """sequenceApplicationSyntax : IPADDRESS anySubType
1220 | NETWORKADDRESS anySubType
1221 | COUNTER32 anySubType
1222 | GAUGE32 anySubType
1223 | UNSIGNED32 anySubType
1224 | TIMETICKS anySubType
1225 | OPAQUE
1226 | COUNTER64 anySubType"""
1227 n = len(p)
1228 if n == 2:
1229 p[0] = p[1]
1230 elif n == 3:
1231 p[0] = p[1] # XXX not supporting subtypes here
1232
1233
1234 # noinspection PyIncorrectDocstring
1235 class SupportIndex(object):
1236 # SMIv1 IndexTypes added
1237 @staticmethod
1238 def p_Index(self, p):
1239 """Index : ObjectName
1240 | typeSMIv1"""
1241
1242 # libsmi: TODO: use the SYNTAX value of the correspondent
1243 # OBJECT-TYPE invocation
1244 p[0] = isinstance(p[1], tuple) and p[1][1][0] or p[1]
1245
1246 # for Index rule
1247 @staticmethod
1248 def p_typeSMIv1(self, p):
1249 """typeSMIv1 : INTEGER
1250 | OCTET STRING
1251 | IPADDRESS
1252 | NETWORKADDRESS"""
1253 n = len(p)
1254 indextype = n == 3 and p[1] + ' ' + p[2] or p[1]
1255 p[0] = indextype
1256
1257
1258 #
1259 # Some changes in grammar to handle common mistakes in MIBs
1260 #
1261
1262 # noinspection PyIncorrectDocstring
1263 class CommaInImport(object):
1264 # comma at the end of import list
1265 @staticmethod
1266 def p_importIdentifiers(self, p):
1267 """importIdentifiers : importIdentifiers ',' importIdentifier
1268 | importIdentifier
1269 | importIdentifiers ','"""
1270 n = len(p)
1271 if n == 4:
1272 p[0] = p[1] + [p[3]]
1273 elif n == 2:
1274 p[0] = [p[1]]
1275 elif n == 3: # excessive comma case
1276 p[0] = p[1]
1277
1278
1279 # noinspection PyIncorrectDocstring
1280 class CommaInSequence(object):
1281 # comma at the end of sequence list
1282 @staticmethod
1283 def p_sequenceItems(self, p):
1284 """sequenceItems : sequenceItems ',' sequenceItem
1285 | sequenceItem
1286 | sequenceItems ','"""
1287 # libsmi: TODO: might this list be emtpy?
1288 n = len(p)
1289 if n == 4:
1290 p[0] = p[1] + [p[3]]
1291 elif n == 2:
1292 p[0] = [p[1]]
1293 elif n == 3: # excessive comma case
1294 p[0] = p[1]
1295
1296
1297 # noinspection PyIncorrectDocstring
1298 class CommaAndSpaces(object):
1299 # common typos handled (mix of commas and spaces)
1300 @staticmethod
1301 def p_enumItems(self, p):
1302 """enumItems : enumItems ',' enumItem
1303 | enumItem
1304 | enumItems enumItem
1305 | enumItems ','"""
1306 n = len(p)
1307 if n == 4:
1308 p[0] = p[1] + [p[3]]
1309 elif n == 2:
1310 p[0] = [p[1]]
1311 elif n == 3: # typo case
1312 if p[2] == ',':
1313 p[0] = p[1]
1314 else:
1315 p[0] = p[1] + [p[2]]
1316
1317
1318 # noinspection PyIncorrectDocstring
1319 class UppercaseIdentifier(object):
1320 # common mistake - using UPPERCASE_IDENTIFIER
1321 @staticmethod
1322 def p_enumItem(self, p):
1323 """enumItem : LOWERCASE_IDENTIFIER '(' enumNumber ')'
1324 | UPPERCASE_IDENTIFIER '(' enumNumber ')'"""
1325 p[0] = (p[1], p[3])
1326
1327
1328 # noinspection PyIncorrectDocstring
1329 class LowcaseIdentifier(object):
1330 # common mistake - LOWERCASE_IDENTIFIER in symbol's name
1331 @staticmethod
1332 def p_notificationTypeClause(self, p):
1333 """notificationTypeClause : fuzzy_lowercase_identifier NOTIFICATION_TYPE NotificationObjectsPart STATUS Status DESCRIPTION Text ReferPart COLON_COLON_EQUAL '{' NotificationName '}'""" # some MIBs have uppercase and/or lowercase id
1334 p[0] = ('notificationTypeClause', p[1], # id
1335 # p[2], # NOTIFICATION_TYPE
1336 p[3], # NotificationObjectsPart
1337 p[5], # status
1338 (p[6], p[7]), # description
1339 p[8], # Reference
1340 p[11]) # NotificationName aka objectIdentifier
1341
1342
1343 # noinspection PyIncorrectDocstring,PyIncorrectDocstring
1344 class CurlyBracesInEnterprises(object):
1345 # common mistake - curly brackets around enterprise symbol
1346 @staticmethod
1347 def p_trapTypeClause(self, p):
1348 """trapTypeClause : fuzzy_lowercase_identifier TRAP_TYPE EnterprisePart VarPart DescrPart ReferPart COLON_COLON_EQUAL NUMBER"""
1349 # libsmi: TODO: range of number?
1350 p[0] = ('trapTypeClause', p[1], # fuzzy_lowercase_identifier
1351 # p[2], # TRAP_TYPE
1352 p[3], # EnterprisePart (objectIdentifier)
1353 p[4], # VarPart
1354 p[5], # description
1355 p[6], # reference
1356 p[8]) # NUMBER
1357
1358 @staticmethod
1359 def p_EnterprisePart(self, p):
1360 """EnterprisePart : ENTERPRISE objectIdentifier
1361 | ENTERPRISE '{' objectIdentifier '}'"""
1362 n = len(p)
1363 if n == 3:
1364 p[0] = p[2]
1365 elif n == 5: # common mistake case
1366 p[0] = p[3]
1367
1368
1369 # noinspection PyIncorrectDocstring
1370 class NoCells(object):
1371 # common mistake - no Cells
1372 @staticmethod
1373 def p_CreationPart(self, p):
1374 """CreationPart : CREATION_REQUIRES '{' Cells '}'
1375 | CREATION_REQUIRES '{' '}'
1376 | empty"""
1377 n = len(p)
1378 if n == 5:
1379 p[0] = (p[1], p[3])
1380
1381
1382 relaxedGrammar = {
1383 'supportSmiV1Keywords': [
1384 SupportSmiV1Keywords.p_importedKeyword,
1385 SupportSmiV1Keywords.p_typeSMIandSPPI,
1386 SupportSmiV1Keywords.p_ApplicationSyntax,
1387 SupportSmiV1Keywords.p_sequenceApplicationSyntax
1388 ],
1389 'supportIndex': [
1390 SupportIndex.p_Index,
1391 SupportIndex.p_typeSMIv1
1392 ],
1393 'commaAtTheEndOfImport': [CommaInImport.p_importIdentifiers],
1394 'commaAtTheEndOfSequence': [CommaInSequence.p_sequenceItems],
1395 'mixOfCommasAndSpaces': [CommaAndSpaces.p_enumItems],
1396 'uppercaseIdentifier': [UppercaseIdentifier.p_enumItem],
1397 'lowcaseIdentifier': [LowcaseIdentifier.p_notificationTypeClause],
1398 'curlyBracesAroundEnterpriseInTrap': [
1399 CurlyBracesInEnterprises.p_trapTypeClause,
1400 CurlyBracesInEnterprises.p_EnterprisePart
1401 ],
1402 'noCells': [NoCells.p_CreationPart]
1403 }
1404
1405
1406 def parserFactory(**grammarOptions):
1407 """Factory function producing custom specializations of base *SmiV2Parser*
1408 class.
1409
1410 Keyword Args:
1411 grammarOptions: a list of (bool) typed optional keyword parameters
1412 enabling particular set of SMIv2 grammar relaxations.
1413
1414 Returns:
1415 Specialized copy of *SmiV2Parser* class.
1416
1417 Notes:
1418 The following SMIv2 grammar relaxation parameters are defined:
1419
1420 * supportSmiV1Keywords - parses SMIv1 grammar
1421 * supportIndex - tolerates ASN.1 types in INDEX clause
1422 * commaAtTheEndOfImport - tolerates stray comma at the end of IMPORT section
1423 * commaAtTheEndOfSequence - tolerates stray comma at the end of sequence of elements in MIB
1424 * mixOfCommasAndSpaces - tolerate a mix of comma and spaces in MIB enumerations
1425 * uppercaseIdentifier - tolerate uppercased MIB identifiers
1426 * lowcaseIdentifier - tolerate lowercase MIB identifiers
1427 * curlyBracesAroundEnterpriseInTrap - tolerate curly braces around enterprise ID in TRAP MACRO
1428 * noCells - tolerate missing cells (XXX)
1429
1430 Examples:
1431
1432 >>> from pysmi.parser import smi
1433 >>> SmiV1Parser = smi.parserFactory(supportSmiV1Keywords=True, supportIndex=True)
1434
1435 """
1436 classAttr = {}
1437
1438 for option in grammarOptions:
1439 if grammarOptions[option]:
1440 if option not in relaxedGrammar:
1441 raise error.PySmiError('Unknown parser relaxation option: %s' % option)
1442
1443 for func in relaxedGrammar[option]:
1444 if sys.version_info[0] > 2:
1445 classAttr[func.__name__] = func
1446 else:
1447 classAttr[func.func_name] = func
1448
1449 classAttr['defaultLexer'] = lexerFactory(**grammarOptions)
1450
1451 return type('SmiParser', (SmiV2Parser,), classAttr)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi.parser.smi import parserFactory
7 from pysmi.parser.dialect import smiV1
8
9 # compatibility stub
10 SmiV1Parser = parserFactory(**smiV1)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi.parser.smi import parserFactory
7 from pysmi.parser.dialect import smiV1Relaxed
8
9 # compatibility stub
10 SmiV1CompatParser = parserFactory(**smiV1Relaxed)
11 SmiStarParser = SmiV1CompatParser
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi.parser.smi import parserFactory
7 from pysmi.parser.dialect import smiV2
8
9 # compatibility stub
10 SmiV2Parser = parserFactory(**smiV2)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi.reader.callback import CallbackReader
7 from pysmi.reader.ftpclient import FtpReader
8 from pysmi.reader.httpclient import HttpReader
9 from pysmi.reader.zipreader import ZipReader
10 from pysmi.reader.localfile import FileReader
11 from pysmi.reader.url import getReadersFromUrls
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import os
7
8
9 class AbstractReader(object):
10 maxMibSize = 10000000 # MIBs can't be that large
11 fuzzyMatching = True # try different file names while searching for MIB
12 originalMatching = uppercaseMatching = lowcaseMatching = True
13 exts = ['',
14 os.path.extsep + 'txt',
15 os.path.extsep + 'mib',
16 os.path.extsep + 'my']
17 exts.extend([x.upper() for x in exts if x])
18
19 def setOptions(self, **kwargs):
20 for k in kwargs:
21 setattr(self, k, kwargs[k])
22 return self
23
24 def getMibVariants(self, mibname):
25 filenames = []
26
27 if self.originalMatching:
28 filenames.append(mibname)
29
30 if self.uppercaseMatching:
31 filenames.append(mibname.upper())
32
33 if self.lowcaseMatching:
34 filenames.append(mibname.lower())
35
36 if self.fuzzyMatching:
37 part = filenames[-1].find('-mib')
38 if part != -1:
39 filenames.extend(
40 [x[:part] for x in filenames]
41 )
42 else:
43 suffixed = mibname + '-mib'
44 filenames.append(suffixed.upper())
45 filenames.append(suffixed.lower())
46
47 return ((x, x + y) for x in filenames for y in self.exts)
48
49 def getData(self, filename):
50 raise NotImplementedError()
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import time
7 from pysmi.reader.base import AbstractReader
8 from pysmi.mibinfo import MibInfo
9 from pysmi import error
10 from pysmi import debug
11
12
13 class CallbackReader(AbstractReader):
14 """Fetch ASN.1 MIB text by name by calling user-defined callable.
15
16 *CallbackReader* class instance tries to retrieve ASN.1 MIB files
17 by name and return their contents to caller.
18 """
19 def __init__(self, cbFun, cbCtx=None):
20 """Create an instance of *CallbackReader* bound to specific URL.
21
22 Args:
23 cbFun (callable): user callable accepting *MIB name* and *cbCtx* objects
24
25 Keyword Args:
26 cbCtx (object): user object that can be used to communicate state information
27 between user-scope code and the *cbFun* callable scope
28 """
29 self._cbFun = cbFun
30 self._cbCtx = cbCtx
31
32 def __str__(self):
33 return '%s{"%s"}' % (self.__class__.__name__, self._cbFun)
34
35 def getData(self, mibname):
36 debug.logger & debug.flagReader and debug.logger('calling user callback %s for MIB %s' % (self._cbFun, mibname))
37
38 res = self._cbFun(mibname, self._cbCtx)
39 if res:
40 return MibInfo(path='file:///dev/stdin', file='', name=mibname, mtime=time.time()), res
41
42 raise error.PySmiReaderFileNotFoundError(mibname=mibname, reader=self)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 import time
8 import ftplib
9 from pysmi.reader.base import AbstractReader
10 from pysmi.mibinfo import MibInfo
11 from pysmi.compat import decode
12 from pysmi import error
13 from pysmi import debug
14
15
16 class FtpReader(AbstractReader):
17 """Fetch ASN.1 MIB text by name from FTP server.
18 *FtpReader* class instance tries to download ASN.1 MIB files
19 by name and return their contents to caller.
20 """
21
22 def __init__(self, host, locationTemplate, timeout=5, ssl=False, port=21,
23 user='anonymous', password='anonymous@'):
24 """Create an instance of *FtpReader* bound to specific FTP server
25 directory.
26
27 Args:
28 host (str): domain name or IP address of web server
29 locationTemplate (str): location part of the directory containing @mib@ magic placeholder to be
30 replaced with MIB name fetch.
31
32 Keyword Args:
33 timeout (int): response timeout
34 ssl (bool): access HTTPS web site
35 port (int): TCP port web server is listening
36 user (str): username at FTP server
37 password (str): password for *username* at FTP server
38 """
39 self._host = host
40 self._locationTemplate = locationTemplate
41 self._timeout = timeout
42 self._ssl = ssl
43 self._port = port
44 self._user = user
45 self._password = password
46 if '@mib@' not in locationTemplate:
47 raise error.PySmiError('@mib@ placeholder not specified in location at %s' % self)
48
49 def __str__(self):
50 return '%s{"ftp://%s%s"}' % (self.__class__.__name__, self._host, self._locationTemplate)
51
52 def getData(self, mibname):
53 if self._ssl:
54 conn = ftplib.FTP_TLS()
55 else:
56 conn = ftplib.FTP()
57
58 try:
59 conn.connect(self._host, self._port, self._timeout)
60
61 except ftplib.all_errors:
62 raise error.PySmiReaderFileNotFoundError(
63 'failed to connect to FTP server %s:%s: %s' % (self._host, self._port, sys.exc_info()[1]), reader=self)
64
65 try:
66 conn.login(self._user, self._password)
67
68 except ftplib.all_errors:
69 conn.close()
70 raise error.PySmiReaderFileNotFoundError('failed to log in to FTP server %s:%s as %s/%s: %s' % (self._host, self._port, self._user, self._password, sys.exc_info()[1]), reader=self)
71
72 mibname = decode(mibname)
73
74 debug.logger & debug.flagReader and debug.logger('looking for MIB %s' % mibname)
75
76 for mibalias, mibfile in self.getMibVariants(mibname):
77 location = self._locationTemplate.replace('@mib@', mibfile)
78
79 mtime = time.time()
80
81 debug.logger & debug.flagReader and debug.logger(
82 'trying to fetch MIB %s from %s:%s' % (location, self._host, self._port))
83
84 data = []
85
86 try:
87 try:
88 response = conn.sendcmd('MDTM %s' % location)
89
90 except ftplib.all_errors:
91 debug.logger & debug.flagReader and debug.logger(
92 'server %s:%s does not support MDTM command, fetching file %s' % (
93 self._host, self._port, location))
94
95 else:
96 debug.logger & debug.flagReader and debug.logger(
97 'server %s:%s MDTM response is %s' % (self._host, self._port, response))
98
99 if response[:3] == 213:
100 mtime = time.mktime(time.strptime(response[4:], "%Y%m%d%H%M%S"))
101
102 debug.logger & debug.flagReader and debug.logger('fetching source MIB %s, mtime %s' % (location, time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(mtime))))
103
104 conn.retrlines('RETR %s' % location, lambda x, y=data: y.append(x))
105
106 except ftplib.all_errors:
107 debug.logger & debug.flagReader and debug.logger(
108 'failed to fetch MIB %s from %s:%s: %s' % (location, self._host, self._port, sys.exc_info()[1]))
109 continue
110
111 data = decode('\n'.join(data))
112
113 debug.logger & debug.flagReader and debug.logger('fetched %s bytes in %s' % (len(data), location))
114
115 conn.close()
116
117 return MibInfo(path='ftp://%s%s' % (self._host, location), file=mibfile, name=mibalias, mtime=mtime), data
118
119 conn.close()
120
121 raise error.PySmiReaderFileNotFoundError('source MIB %s not found' % mibname, reader=self)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 import time
8
9 try:
10 # noinspection PyUnresolvedReferences
11 import httplib
12 except ImportError:
13 # noinspection PyUnresolvedReferences
14 import http.client as httplib
15 from pysmi.reader.base import AbstractReader
16 from pysmi.mibinfo import MibInfo
17 from pysmi.compat import decode
18 from pysmi import __version__ as pysmi_version
19 from pysmi import error
20 from pysmi import debug
21
22
23 class HttpReader(AbstractReader):
24 """Fetch ASN.1 MIB text by name from a web site.
25
26 *HttpReader* class instance tries to download ASN.1 MIB files
27 by name and return their contents to caller.
28 """
29 MIB_MAGIC = '@mib@'
30
31 def __init__(self, host, port, locationTemplate, timeout=5, ssl=False):
32 """Create an instance of *HttpReader* bound to specific URL.
33
34 Args:
35 host (str): domain name or IP address of web server
36 port (int): TCP port web server is listening
37 locationTemplate (str): location part of the URL optionally containing @mib@
38 magic placeholder to be replaced with MIB name. If @mib@ magic is not present,
39 MIB name is appended to `locationTemplate`
40
41 Keyword Args:
42 timeout (int): response timeout
43 ssl (bool): access HTTPS web site
44 """
45 self._schema = ssl and 'https' or 'http'
46 self._host = host
47 self._port = port
48 self._locationTemplate = decode(locationTemplate)
49 self._timeout = timeout
50 self._user_agent = 'pysmi-%s; python-%s.%s.%s; %s' % (
51 pysmi_version, sys.version_info[0], sys.version_info[1],
52 sys.version_info[2], sys.platform
53 )
54
55 def __str__(self):
56 return '%s{"%s://%s:%s%s"}' % (
57 self.__class__.__name__, self._schema, self._host, self._port, self._locationTemplate)
58
59 def getData(self, mibname):
60 headers = {
61 'Accept': 'text/plain',
62 'User-Agent': self._user_agent
63 }
64 if sys.version_info[:2] < (2, 6):
65 conn = httplib.HTTPConnection(self._host, self._port)
66 else:
67 conn = httplib.HTTPConnection(self._host, self._port, timeout=self._timeout)
68
69 mibname = decode(mibname)
70
71 debug.logger & debug.flagReader and debug.logger('looking for MIB %s' % mibname)
72
73 for mibalias, mibfile in self.getMibVariants(mibname):
74 if self.MIB_MAGIC in self._locationTemplate:
75 location = self._locationTemplate.replace(self.MIB_MAGIC, mibfile)
76 else:
77 location = self._locationTemplate + mibfile
78
79 debug.logger & debug.flagReader and debug.logger(
80 'trying to fetch MIB from %s://%s:%s%s' % (self._schema, self._host, self._port, location))
81
82 try:
83 conn.request('GET', location, '', headers)
84 response = conn.getresponse()
85
86 except Exception:
87 debug.logger & debug.flagReader and debug.logger('failed to fetch MIB from %s://%s:%s%s: %s' % (
88 self._schema, self._host, self._port, location, sys.exc_info()[1]))
89 continue
90
91 debug.logger & debug.flagReader and debug.logger('HTTP response %s' % response.status)
92
93 if response.status == 200:
94 try:
95 mtime = time.mktime(time.strptime(response.getheader('Last-Modified'), "%a, %d %b %Y %H:%M:%S %Z"))
96
97 except Exception:
98 debug.logger & debug.flagReader and debug.logger('malformed HTTP headers: %s' % sys.exc_info()[1])
99 mtime = time.time()
100
101 debug.logger & debug.flagReader and debug.logger(
102 'fetching source MIB %s, mtime %s' % (location, response.getheader('Last-Modified')))
103
104 return MibInfo(path='%s://%s:%s%s' % (self._schema, self._host, self._port, location), file=mibfile,
105 name=mibalias, mtime=mtime), decode(response.read(self.maxMibSize))
106
107 raise error.PySmiReaderFileNotFoundError('source MIB %s not found' % mibname, reader=self)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import os
7 import sys
8 import time
9 from pysmi.reader.base import AbstractReader
10 from pysmi.mibinfo import MibInfo
11 from pysmi.compat import decode
12 from pysmi import debug
13 from pysmi import error
14
15
16 class FileReader(AbstractReader):
17 """Fetch ASN.1 MIB text by name from local file.
18
19 *FileReader* class instance tries to locate ASN.1 MIB files
20 by name, fetch and return their contents to caller.
21 """
22 useIndexFile = True # optional .index file mapping MIB to file name
23 indexFile = '.index'
24
25 def __init__(self, path, recursive=True, ignoreErrors=True):
26 """Create an instance of *FileReader* serving a directory.
27
28 Args:
29 path (str): directory to search MIB files
30
31 Keyword Args:
32 recursive (bool): whether to include subdirectories
33 ignoreErrors (bool): ignore filesystem access errors
34 """
35 self._path = os.path.normpath(path)
36 self._recursive = recursive
37 self._ignoreErrors = ignoreErrors
38 self._indexLoaded = False
39 self._mibIndex = None
40
41 def __str__(self):
42 return '%s{"%s"}' % (self.__class__.__name__, self._path)
43
44 def getSubdirs(self, path, recursive=True, ignoreErrors=True):
45 if not recursive:
46 return [path]
47
48 dirs = [path]
49
50 try:
51 subdirs = os.listdir(path)
52
53 except OSError:
54 if ignoreErrors:
55 return dirs
56
57 else:
58 raise error.PySmiError('directory %s access error: %s' % (path, sys.exc_info()[1]))
59
60 for d in subdirs:
61 d = os.path.join(decode(path), decode(d))
62 if os.path.isdir(d):
63 dirs.extend(self.getSubdirs(d, recursive))
64
65 return dirs
66
67 @staticmethod
68 def loadIndex(indexFile):
69 mibIndex = {}
70 if os.path.exists(indexFile):
71 try:
72 f = open(indexFile)
73 mibIndex = dict(
74 [x.split()[:2] for x in f.readlines()]
75 )
76 f.close()
77 debug.logger & debug.flagReader and debug.logger(
78 'loaded MIB index map from %s file, %s entries' % (indexFile, len(mibIndex)))
79
80 except IOError:
81 pass
82
83 return mibIndex
84
85 def getMibVariants(self, mibname):
86 if self.useIndexFile:
87 if not self._indexLoaded:
88 self._mibIndex = self.loadIndex(
89 os.path.join(self._path, self.indexFile)
90 )
91 self._indexLoaded = True
92
93 if mibname in self._mibIndex:
94 debug.logger & debug.flagReader and debug.logger(
95 'found %s in MIB index: %s' % (mibname, self._mibIndex[mibname]))
96 return [(mibname, self._mibIndex[mibname])]
97
98 return super(FileReader, self).getMibVariants(mibname)
99
100 def getData(self, mibname):
101 debug.logger & debug.flagReader and debug.logger(
102 '%slooking for MIB %s' % (self._recursive and 'recursively ' or '', mibname))
103
104 for path in self.getSubdirs(self._path, self._recursive, self._ignoreErrors):
105
106 for mibalias, mibfile in self.getMibVariants(mibname):
107 f = os.path.join(decode(path), decode(mibfile))
108
109 debug.logger & debug.flagReader and debug.logger('trying MIB %s' % f)
110
111 if os.path.exists(f) and os.path.isfile(f):
112 try:
113 mtime = os.stat(f)[8]
114
115 debug.logger & debug.flagReader and debug.logger(
116 'source MIB %s mtime is %s, fetching data...' % (
117 f, time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(mtime))))
118
119 fp = open(f, mode='rb')
120 mibData = fp.read(self.maxMibSize)
121 fp.close()
122
123 if len(mibData) == self.maxMibSize:
124 raise IOError('MIB %s too large' % f)
125
126 return MibInfo(path='file://%s' % f, file=mibfile, name=mibalias, mtime=mtime), decode(mibData)
127
128 except (OSError, IOError):
129 debug.logger & debug.flagReader and debug.logger(
130 'source file %s open failure: %s' % (f, sys.exc_info()[1]))
131
132 if not self._ignoreErrors:
133 raise error.PySmiError('file %s access error: %s' % (f, sys.exc_info()[1]))
134
135 raise error.PySmiReaderFileNotModifiedError('source MIB %s is older than needed' % f, reader=self)
136
137 raise error.PySmiReaderFileNotFoundError('source MIB %s not found' % mibname, reader=self)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7
8 try:
9 # noinspection PyUnresolvedReferences
10 import urlparse
11 except ImportError:
12 # noinspection PyUnresolvedReferences
13 from urllib import parse as urlparse
14 from pysmi.reader.localfile import FileReader
15 from pysmi.reader.zipreader import ZipReader
16 from pysmi.reader.httpclient import HttpReader
17 from pysmi.reader.ftpclient import FtpReader
18 from pysmi import error
19
20
21 def getReadersFromUrls(*sourceUrls, **options):
22 readers = []
23 for sourceUrl in sourceUrls:
24 mibSource = urlparse.urlparse(sourceUrl)
25
26 if sys.version_info[0:2] < (2, 5):
27 class ParseResult(tuple):
28 pass
29
30 mibSource = ParseResult(mibSource)
31
32 for k, v in zip(('scheme', 'netloc', 'path', 'params',
33 'query', 'fragment', 'username', 'password',
34 'hostname', 'port'), mibSource + ('', '', '', None)):
35 if k == 'scheme':
36 if not mibSource[0] or mibSource[0] == 'file':
37 if mibSource[2].endswith('.zip') or mibSource[2].endswith('.ZIP'):
38 v = 'zip'
39
40 setattr(mibSource, k, v)
41
42 if mibSource.scheme in ('', 'file', 'zip'):
43 scheme = mibSource.scheme
44 if scheme != 'file' and (mibSource.path.endswith('.zip') or
45 mibSource.path.endswith('.ZIP')):
46 scheme = 'zip'
47
48 else:
49 scheme = 'file'
50
51 if scheme == 'file':
52 readers.append(FileReader(mibSource.path).setOptions(**options))
53 else:
54 readers.append(ZipReader(mibSource.path).setOptions(**options))
55
56 elif mibSource.scheme in ('http', 'https'):
57 readers.append(HttpReader(mibSource.hostname or mibSource.netloc, mibSource.port or 80, mibSource.path,
58 ssl=mibSource.scheme == 'https').setOptions(**options))
59
60 elif mibSource.scheme in ('ftp', 'sftp'):
61 readers.append(
62 FtpReader(mibSource.hostname or mibSource.netloc, mibSource.path, ssl=mibSource.scheme == 'sftp',
63 port=mibSource.port or 21, user=mibSource.username or 'anonymous',
64 password=mibSource.password or 'anonymous@').setOptions(**options))
65
66 else:
67 raise error.PySmiError('Unsupported URL scheme %s' % sourceUrl)
68
69 return readers
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import os
7 import sys
8 import time
9 import datetime
10 import zipfile
11 from pysmi.reader.base import AbstractReader
12 from pysmi.mibinfo import MibInfo
13 from pysmi.compat import decode
14 from pysmi import debug
15 from pysmi import error
16
17
18 class FileLike(object):
19 """Stripped down, binary file mock to work with ZipFile"""
20 def __init__(self, buf, name):
21 self.name = name
22 self.buf = buf
23 self.null = buf[:0]
24 self.len = len(buf)
25 self.buflist = []
26 self.pos = 0
27 self.closed = False
28 self.softspace = 0
29
30 def close(self):
31 if not self.closed:
32 self.closed = True
33 self.buf = self.null
34 self.pos = 0
35
36 def seek(self, pos, mode = 0):
37 if self.buflist:
38 self.buf += self.null.join(self.buflist)
39 self.buflist = []
40
41 if mode == 1:
42 pos += self.pos
43
44 elif mode == 2:
45 pos += self.len
46
47 self.pos = max(0, pos)
48
49 def tell(self):
50 return self.pos
51
52 def read(self, n=-1):
53 if self.buflist:
54 self.buf += self.null.join(self.buflist)
55 self.buflist = []
56
57 if n < 0:
58 newpos = self.len
59 else:
60 newpos = min(self.pos + n, self.len)
61
62 r = self.buf[self.pos:newpos]
63
64 self.pos = newpos
65
66 return r
67
68
69 class ZipReader(AbstractReader):
70 """Fetch ASN.1 MIB text by name from a ZIP archive.
71
72 *ZipReader* class instance tries to locate ASN.1 MIB files
73 by name, fetch and return their contents to caller.
74 """
75 useIndexFile = False
76
77 def __init__(self, path, ignoreErrors=True):
78 """Create an instance of *ZipReader* serving a ZIP archive.
79
80 Args:
81 path (str): path to ZIP archive containing MIB files
82
83 Keyword Args:
84 ignoreErrors (bool): ignore ZIP archive access errors
85 """
86 self._name = path
87 self._members = {}
88 self._pendingError = None
89
90 try:
91 self._members = self._readZipDirectory(fileObj=open(path, 'rb'))
92
93 except Exception:
94 debug.logger & debug.flagReader and debug.logger(
95 'ZIP file %s open failure: %s' % (self._name, sys.exc_info()[1]))
96
97 if not ignoreErrors:
98 self._pendingError = error.PySmiError('file %s access error: %s' % (self._name, sys.exc_info()[1]))
99
100 def _readZipDirectory(self, fileObj):
101
102 archive = zipfile.ZipFile(fileObj)
103
104 if isinstance(fileObj, FileLike):
105 fileObj = None
106
107 members = {}
108
109 for member in archive.infolist():
110
111 filename = os.path.basename(member.filename)
112 if not filename:
113 continue
114
115 if (member.filename.endswith('.zip') or
116 member.filename.endswith('.ZIP')):
117
118 innerZipBlob = archive.read(member.filename)
119
120 innerMembers = self._readZipDirectory(FileLike(innerZipBlob, member.filename))
121
122 for innerFilename, ref in innerMembers.items():
123
124 while innerFilename in members:
125 innerFilename += '+'
126
127 members[innerFilename] = [[fileObj, member.filename, None]]
128 members[innerFilename].extend(ref)
129
130 else:
131 mtime = time.mktime(datetime.datetime(*member.date_time[:6]).timetuple())
132
133 members[filename] = [[fileObj, member.filename, mtime]]
134
135 return members
136
137 def _readZipFile(self, refs):
138
139 for fileObj, filename, mtime in refs:
140
141 if not fileObj:
142 fileObj = FileLike(dataObj, name=self._name)
143
144 archive = zipfile.ZipFile(fileObj)
145
146 try:
147 dataObj = archive.read(filename)
148
149 except Exception:
150 debug.logger & debug.flagReader and debug.logger('ZIP read component %s read error: %s' % (fileObj.name, sys.exc_info()[1]))
151 return '', 0
152
153 return dataObj, mtime
154
155 def __str__(self):
156 return '%s{"%s"}' % (self.__class__.__name__, self._name)
157
158 def getData(self, mibname, zipBlob=None):
159 debug.logger & debug.flagReader and debug.logger('looking for MIB %s at %s' % (mibname, self._name))
160
161 if self._pendingError:
162 raise self._pendingError
163
164 if not self._members:
165 raise error.PySmiReaderFileNotFoundError('source MIB %s not found' % mibname, reader=self)
166
167 for mibalias, mibfile in self.getMibVariants(mibname):
168
169 debug.logger & debug.flagReader and debug.logger('trying MIB %s' % mibfile)
170
171 try:
172 refs = self._members[mibfile]
173
174 except KeyError:
175 continue
176
177 mibData, mtime = self._readZipFile(refs)
178
179 if not mibData:
180 continue
181
182 debug.logger & debug.flagReader and debug.logger(
183 'source MIB %s, mtime %s, read from %s/%s' % (mibfile, time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(mtime)), self._name, mibfile)
184 )
185
186 if len(mibData) == self.maxMibSize:
187 raise IOError('MIB %s/%s too large' % (self._name, mibfile))
188
189 return MibInfo(path='zip://%s/%s' % (self._name, mibfile),
190 file=mibfile, name=mibalias, mtime=mtime), decode(mibData)
191
192 raise error.PySmiReaderFileNotFoundError('source MIB %s not found' % mibname, reader=self)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi.searcher.pyfile import PyFileSearcher
7 from pysmi.searcher.pypackage import PyPackageSearcher
8 from pysmi.searcher.stub import StubSearcher
9 from pysmi.searcher.anyfile import AnyFileSearcher
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import os
7 import sys
8 import time
9 from pysmi.searcher.base import AbstractSearcher
10 from pysmi.compat import decode
11 from pysmi import debug
12 from pysmi import error
13
14
15 class AnyFileSearcher(AbstractSearcher):
16 """Figures out if given file exists at given location.
17 """
18 exts = []
19
20 def __init__(self, path):
21 """Create an instance of *AnyFileSearcher* bound to specific directory.
22
23 Args:
24 path (str): path to local directory
25 """
26 self._path = os.path.normpath(decode(path))
27
28 def __str__(self):
29 return '%s{"%s"}' % (self.__class__.__name__, self._path)
30
31 def fileExists(self, mibname, mtime, rebuild=False):
32 if rebuild:
33 debug.logger & debug.flagSearcher and debug.logger('pretend %s is very old' % mibname)
34 return
35
36 mibname = decode(mibname)
37 basename = os.path.join(self._path, mibname)
38
39 for sfx in self.exts:
40 f = basename + sfx
41 if not os.path.exists(f) or not os.path.isfile(f):
42 debug.logger & debug.flagSearcher and debug.logger('%s not present or not a file' % f)
43 continue
44
45 try:
46 fileTime = os.stat(f)[8]
47
48 except OSError:
49 raise error.PySmiSearcherError('failure opening compiled file %s: %s' % (f, sys.exc_info()[1]),
50 searcher=self)
51
52 debug.logger & debug.flagSearcher and debug.logger(
53 'found %s, mtime %s' % (f, time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(fileTime))))
54
55 if fileTime >= mtime:
56 raise error.PySmiFileNotModifiedError()
57
58 raise error.PySmiFileNotFoundError('no compiled file %s found' % mibname, searcher=self)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6
7
8 class AbstractSearcher(object):
9
10 def setOptions(self, **kwargs):
11 for k in kwargs:
12 setattr(self, k, kwargs[k])
13 return self
14
15 def fileExists(self, mibname, mtime, rebuild=False):
16 raise NotImplementedError()
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import os
7 import sys
8 import time
9 import imp
10 import struct
11 from pysmi.searcher.base import AbstractSearcher
12 from pysmi.compat import decode
13 from pysmi import debug
14 from pysmi import error
15
16
17 class PyFileSearcher(AbstractSearcher):
18 """Figures out if given Python file (source or bytecode) exists at given
19 location.
20 """
21 suffixes = {}
22 for sfx, mode, typ in imp.get_suffixes():
23 if typ not in suffixes:
24 suffixes[typ] = []
25 suffixes[typ].append((sfx, mode))
26
27 def __init__(self, path):
28 """Create an instance of *PyFileSearcher* bound to specific directory.
29
30 Args:
31 path (str): path to local directory
32 """
33 self._path = os.path.normpath(decode(path))
34
35 def __str__(self):
36 return '%s{"%s"}' % (self.__class__.__name__, self._path)
37
38 def fileExists(self, mibname, mtime, rebuild=False):
39 if rebuild:
40 debug.logger & debug.flagSearcher and debug.logger('pretend %s is very old' % mibname)
41 return
42
43 mibname = decode(mibname)
44 pyfile = os.path.join(self._path, mibname)
45
46 for fmt in imp.PY_COMPILED, imp.PY_SOURCE:
47 for pySfx, pyMode in self.suffixes[fmt]:
48 f = pyfile + pySfx
49
50 if not os.path.exists(f) or not os.path.isfile(f):
51 debug.logger & debug.flagSearcher and debug.logger('%s not present or not a file' % f)
52 continue
53
54 if fmt == imp.PY_COMPILED:
55 try:
56 fp = open(f, pyMode)
57 pyData = fp.read(8)
58 fp.close()
59
60 except IOError:
61 raise error.PySmiSearcherError('failure opening compiled file %s: %s' % (f, sys.exc_info()[1]),
62 searcher=self)
63 if pyData[:4] == imp.get_magic():
64 pyData = pyData[4:]
65 pyTime = struct.unpack('<L', pyData[:4])[0]
66 debug.logger & debug.flagSearcher and debug.logger(
67 'found %s, mtime %s' % (f, time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(pyTime))))
68 if pyTime >= mtime:
69 raise error.PySmiFileNotModifiedError()
70
71 else:
72 raise error.PySmiFileNotFoundError('older file %s exists' % mibname, searcher=self)
73
74 else:
75 debug.logger & debug.flagSearcher and debug.logger('bad magic in %s' % f)
76 continue
77 else:
78 try:
79 pyTime = os.stat(f)[8]
80
81 except OSError:
82 raise error.PySmiSearcherError('failure opening compiled file %s: %s' % (f, sys.exc_info()[1]),
83 searcher=self)
84
85 debug.logger & debug.flagSearcher and debug.logger(
86 'found %s, mtime %s' % (f, time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(pyTime))))
87
88 if pyTime >= mtime:
89 raise error.PySmiFileNotModifiedError()
90
91 raise error.PySmiFileNotFoundError('no compiled file %s found' % mibname, searcher=self)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import os
7 import time
8 import imp
9 import struct
10 from pysmi.searcher.base import AbstractSearcher
11 from pysmi.searcher.pyfile import PyFileSearcher
12 from pysmi.compat import decode
13 from pysmi import debug
14 from pysmi import error
15
16
17 class PyPackageSearcher(AbstractSearcher):
18 """Figures out if given Python module (source or bytecode) exists in given
19 Python package.
20
21 Python package must be importable.
22 """
23 suffixes = {}
24 for sfx, mode, typ in imp.get_suffixes():
25 if typ not in suffixes:
26 suffixes[typ] = []
27
28 suffixes[typ].append((sfx, mode))
29
30 def __init__(self, package):
31 """Create an instance of *PyPackageSearcher* bound to specific Python
32 package.
33
34 Args:
35 package (str): name of the Python package to look up Python
36 modules at.
37 """
38 self._package = package
39 self.__loader = None
40
41 def __str__(self):
42 return '%s{"%s"}' % (self.__class__.__name__, self._package)
43
44 @staticmethod
45 def _parseDosTime(dosdate, dostime):
46 t = (((dosdate >> 9) & 0x7f) + 1980, # year
47 ((dosdate >> 5) & 0x0f), # month
48 dosdate & 0x1f, # mday
49 (dostime >> 11) & 0x1f, # hour
50 (dostime >> 5) & 0x3f, # min
51 (dostime & 0x1f) * 2, # sec
52 -1, # wday
53 -1, # yday
54 -1) # dst
55 return time.mktime(t)
56
57 def fileExists(self, mibname, mtime, rebuild=False):
58 if rebuild:
59 debug.logger & debug.flagSearcher and debug.logger('pretend %s is very old' % mibname)
60 return
61
62 mibname = decode(mibname)
63
64 try:
65 p = __import__(self._package, globals(), locals(), ['__init__'])
66
67 if hasattr(p, '__loader__') and hasattr(p.__loader__, '_files'):
68 self.__loader = p.__loader__
69 self._package = self._package.replace('.', os.sep)
70 debug.logger & debug.flagSearcher and debug.logger(
71 '%s is an importable egg at %s' % (self._package, os.path.split(p.__file__)[0]))
72
73 elif hasattr(p, '__file__'):
74 debug.logger & debug.flagSearcher and debug.logger(
75 '%s is not an egg, trying it as a package directory' % self._package)
76 return PyFileSearcher(os.path.split(p.__file__)[0]).fileExists(mibname, mtime, rebuild=rebuild)
77
78 else:
79 raise error.PySmiFileNotFoundError('%s is neither importable nor a file' % self._package, searcher=self)
80
81 except ImportError:
82 raise error.PySmiFileNotFoundError('%s is not importable, trying as a path' % self._package, searcher=self)
83
84 for fmt in imp.PY_COMPILED, imp.PY_SOURCE:
85 for pySfx, pyMode in self.suffixes[fmt]:
86 f = os.path.join(self._package, mibname.upper()) + pySfx
87
88 if f not in self.__loader._files:
89 debug.logger & debug.flagSearcher and debug.logger('%s is not in %s' % (f, self._package))
90 continue
91
92 if fmt == imp.PY_COMPILED:
93 pyData = self.__loader.get_data(f)
94 if pyData[:4] == imp.get_magic():
95 pyData = pyData[4:]
96 pyTime = struct.unpack('<L', pyData[:4])[0]
97 debug.logger & debug.flagSearcher and debug.logger(
98 'found %s, mtime %s' % (f, time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(pyTime))))
99 if pyTime >= mtime:
100 raise error.PySmiFileNotModifiedError()
101 else:
102 raise error.PySmiFileNotFoundError('older file %s exists' % mibname, searcher=self)
103
104 else:
105 debug.logger & debug.flagSearcher and debug.logger('bad magic in %s' % f)
106 continue
107
108 else:
109 pyTime = self._parseDosTime(
110 self.__loader._files[f][6],
111 self.__loader._files[f][5]
112 )
113
114 debug.logger & debug.flagSearcher and debug.logger(
115 'found %s, mtime %s' % (f, time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(pyTime))))
116 if pyTime >= mtime:
117 raise error.PySmiFileNotModifiedError()
118 else:
119 raise error.PySmiFileNotFoundError('older file %s exists' % mibname, searcher=self)
120
121 raise error.PySmiFileNotFoundError('no file %s found' % mibname, searcher=self)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi.searcher.base import AbstractSearcher
7 from pysmi import debug
8 from pysmi import error
9
10
11 class StubSearcher(AbstractSearcher):
12 """Figures out if given MIB module is present in a fixed list of modules.
13 """
14
15 def __init__(self, *mibnames):
16 """Create an instance of *StubSearcher* initialized with a fixed list
17 or MIB modules names.
18
19 Args:
20 mibnames (str): blacklisted MIB names
21 """
22 self._mibnames = mibnames
23
24 def __str__(self):
25 return '%s' % self.__class__.__name__
26
27 def fileExists(self, mibname, mtime, rebuild=False):
28 if mibname in self._mibnames:
29 debug.logger & debug.flagSearcher and debug.logger('pretend compiled %s exists and is very new' % mibname)
30 raise error.PySmiFileNotModifiedError('compiled file %s is among %s' % (mibname, ', '.join(self._mibnames)),
31 searcher=self)
32
33 raise error.PySmiFileNotFoundError('no compiled file %s found among %s' % (mibname, ', '.join(self._mibnames)),
34 searcher=self)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 from pysmi.writer.localfile import FileWriter
7 from pysmi.writer.pyfile import PyFileWriter
8 from pysmi.writer.callback import CallbackWriter
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6
7 class AbstractWriter(object):
8 def setOptions(self, **kwargs):
9 for k in kwargs:
10 setattr(self, k, kwargs[k])
11 return self
12
13 def putData(self, mibname, data, comments=(), dryRun=False):
14 raise NotImplementedError()
15
16 def getData(self, filename):
17 raise NotImplementedError()
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 from pysmi.writer.base import AbstractWriter
8 from pysmi import debug
9 from pysmi import error
10
11
12 class CallbackWriter(AbstractWriter):
13 """Invokes user-specified callable and passes transformed
14 MIB module to it.
15
16 Note: user callable object signature must be as follows
17
18 .. function:: cbFun(mibname, contents, cbCtx)
19
20 """
21
22 def __init__(self, cbFun, cbCtx=None):
23 """Creates an instance of *CallbackWriter* class.
24
25 Args:
26 cbFun (callable): user-supplied callable
27 Keyword Args:
28 cbCtx: user-supplied object passed intact to user callback
29 """
30 self._cbFun = cbFun
31 self._cbCtx = cbCtx
32
33 def __str__(self):
34 return '%s{"%s"}' % (self.__class__.__name__, self._cbFun)
35
36 def putData(self, mibname, data, comments=(), dryRun=False):
37 if dryRun:
38 debug.logger & debug.flagWriter and debug.logger('dry run mode')
39 return
40
41 try:
42 self._cbFun(mibname, data, self._cbCtx)
43
44 except Exception:
45 raise error.PySmiWriterError(
46 'user callback %s failure writing %s: %s' % (self._cbFun, mibname, sys.exc_info()[1]), writer=self)
47
48 debug.logger & debug.flagWriter and debug.logger('user callback for %s succeeded' % mibname)
49
50 def getData(self, filename):
51 return ''
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import os
7 import sys
8 import tempfile
9 from pysmi.writer.base import AbstractWriter
10 from pysmi.compat import encode, decode
11 from pysmi import debug
12 from pysmi import error
13
14
15 class FileWriter(AbstractWriter):
16 """Stores transformed MIB modules in files at specified location.
17
18 User is expected to pass *FileReader* class instance to
19 *MibCompiler* on instantiation. The rest is internal to *MibCompiler*.
20 """
21 suffix = ''
22
23 def __init__(self, path):
24 """Creates an instance of *FileReader* class.
25
26 Args:
27 path: writable directory to store created files
28 """
29 self._path = decode(os.path.normpath(path))
30
31 def __str__(self):
32 return '%s{"%s"}' % (self.__class__.__name__, self._path)
33
34 def getData(self, mibname, dryRun=False):
35 filename = os.path.join(self._path, decode(mibname)) + self.suffix
36
37 f = None
38
39 try:
40 f = open(filename)
41 data = f.read()
42 f.close()
43 return data
44
45 except (OSError, IOError, UnicodeEncodeError):
46 if f:
47 f.close()
48 return ''
49
50 def putData(self, mibname, data, comments=(), dryRun=False):
51 if dryRun:
52 debug.logger & debug.flagWriter and debug.logger('dry run mode')
53 return
54
55 if not os.path.exists(self._path):
56 try:
57 os.makedirs(self._path)
58
59 except OSError:
60 raise error.PySmiWriterError(
61 'failure creating destination directory %s: %s' % (self._path, sys.exc_info()[1]), writer=self)
62
63 if comments:
64 data = '#\n' + ''.join(['# %s\n' % x for x in comments]) + '#\n' + data
65
66 filename = os.path.join(self._path, decode(mibname)) + self.suffix
67
68 tfile = None
69
70 try:
71 fd, tfile = tempfile.mkstemp(dir=self._path)
72 os.write(fd, encode(data))
73 os.close(fd)
74 os.rename(tfile, filename)
75
76 except (OSError, IOError, UnicodeEncodeError):
77 exc = sys.exc_info()
78 if tfile:
79 try:
80 os.unlink(tfile)
81
82 except OSError:
83 pass
84
85 raise error.PySmiWriterError('failure writing file %s: %s' % (filename, exc[1]), file=filename, writer=self)
86
87 debug.logger & debug.flagWriter and debug.logger('%s stored in %s' % (mibname, filename))
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import os
7 import sys
8 import imp
9 import tempfile
10 import py_compile
11 from pysmi.writer.base import AbstractWriter
12 from pysmi.compat import encode, decode
13 from pysmi import debug
14 from pysmi import error
15
16
17 class PyFileWriter(AbstractWriter):
18 """Stores transformed MIB modules as Python files at specified location.
19
20 User is expected to pass *PyFileWriter* class instance to
21 *MibCompiler* on instantiation. The rest is internal to *MibCompiler*.
22 """
23 pyCompile = True
24 pyOptimizationLevel = -1
25
26 suffixes = {}
27
28 for sfx, mode, typ in imp.get_suffixes():
29 if typ not in suffixes:
30 suffixes[typ] = []
31
32 suffixes[typ].append((decode(sfx), mode))
33
34 def __init__(self, path):
35 """Creates an instance of *PyFileWriter* class.
36
37 Args:
38 path: writable directory to store Python modules
39 """
40 self._path = decode(os.path.normpath(path))
41
42 def __str__(self):
43 return '%s{"%s"}' % (self.__class__.__name__, self._path)
44
45 def putData(self, mibname, data, comments=(), dryRun=False):
46 if dryRun:
47 debug.logger & debug.flagWriter and debug.logger('dry run mode')
48 return
49
50 if not os.path.exists(self._path):
51 try:
52 os.makedirs(self._path)
53
54 except OSError:
55 raise error.PySmiWriterError(
56 'failure creating destination directory %s: %s' % (self._path, sys.exc_info()[1]), writer=self)
57
58 if comments:
59 data = '#\n' + ''.join(['# %s\n' % x for x in comments]) + '#\n' + data
60
61 pyfile = os.path.join(self._path, decode(mibname)) + self.suffixes[imp.PY_SOURCE][0][0]
62
63 tfile = None
64
65 try:
66 fd, tfile = tempfile.mkstemp(dir=self._path)
67 os.write(fd, encode(data))
68 os.close(fd)
69 os.rename(tfile, pyfile)
70
71 except (OSError, IOError, UnicodeEncodeError):
72 exc = sys.exc_info()
73 if tfile:
74 try:
75 os.unlink(tfile)
76
77 except OSError:
78 pass
79
80 raise error.PySmiWriterError('failure writing file %s: %s' % (pyfile, exc[1]), file=pyfile, writer=self)
81
82 debug.logger & debug.flagWriter and debug.logger('created file %s' % pyfile)
83
84 if self.pyCompile:
85 try:
86 if sys.version_info[0:2] > (3, 1):
87 # noinspection PyArgumentList
88 py_compile.compile(pyfile, doraise=True, optimize=self.pyOptimizationLevel)
89
90 else:
91 py_compile.compile(pyfile, doraise=True)
92
93 except (SyntaxError, py_compile.PyCompileError):
94 pass # XXX
95
96 except:
97 try:
98 os.unlink(pyfile)
99 except Exception:
100 pass
101
102 raise error.PySmiWriterError('failure compiling %s: %s' % (pyfile, sys.exc_info()[1]), file=mibname, writer=self)
103
104 debug.logger & debug.flagWriter and debug.logger('%s stored' % mibname)
105
106 def getData(self, filename):
107 return ''
108
0 Metadata-Version: 1.1
1 Name: pysmi
2 Version: 0.2.2
3 Summary: SNMP SMI/MIB Parser
4 Home-page: https://github.com/etingof/pysmi
5 Author: Ilya Etingof <etingof@gmail.com>
6 Author-email: etingof@gmail.com
7 License: BSD
8 Description: A pure-Python implementation of SNMP/SMI MIB parsing and conversion library.
9 Platform: any
10 Classifier: Development Status :: 5 - Production/Stable
11 Classifier: Environment :: Console
12 Classifier: Intended Audience :: Developers
13 Classifier: Intended Audience :: Education
14 Classifier: Intended Audience :: Information Technology
15 Classifier: Intended Audience :: System Administrators
16 Classifier: Intended Audience :: Telecommunications Industry
17 Classifier: License :: OSI Approved :: BSD License
18 Classifier: Natural Language :: English
19 Classifier: Operating System :: OS Independent
20 Classifier: Programming Language :: Python :: 2
21 Classifier: Programming Language :: Python :: 2.4
22 Classifier: Programming Language :: Python :: 2.5
23 Classifier: Programming Language :: Python :: 2.6
24 Classifier: Programming Language :: Python :: 2.7
25 Classifier: Programming Language :: Python :: 3
26 Classifier: Programming Language :: Python :: 3.2
27 Classifier: Programming Language :: Python :: 3.3
28 Classifier: Programming Language :: Python :: 3.4
29 Classifier: Programming Language :: Python :: 3.5
30 Classifier: Programming Language :: Python :: 3.6
31 Classifier: Topic :: Communications
32 Classifier: Topic :: System :: Monitoring
33 Classifier: Topic :: System :: Networking :: Monitoring
34 Classifier: Topic :: Software Development :: Libraries :: Python Modules
0 CHANGES.rst
1 LICENSE.rst
2 MANIFEST.in
3 README.md
4 THANKS.txt
5 TODO.txt
6 devel-requirements.txt
7 requirements.txt
8 setup.cfg
9 setup.py
10 docs/Makefile
11 docs/README.txt
12 docs/source/changelog.rst
13 docs/source/conf.py
14 docs/source/contents.rst
15 docs/source/documentation.rst
16 docs/source/download.rst
17 docs/source/library-reference.rst
18 docs/source/license.rst
19 docs/source/mibdump.rst
20 docs/source/.static/logo.svg
21 docs/source/examples/always-borrow-precompiled-pysnmp-files.rst
22 docs/source/examples/borrow-precompiled-pysnmp-files-on-failure.rst
23 docs/source/examples/compile-smistar-mibs-into-pysnmp-files-if-needed.rst
24 docs/source/examples/compile-smiv2-mibs-from-text-into-pysnmp-code.rst
25 docs/source/examples/download-and-compile-smistar-mibs-into-json.rst
26 docs/source/examples/download-and-compile-smistar-mibs-into-pysnmp-files.rst
27 docs/source/pysmi/borrower/anyfile/anyfileborrower.rst
28 docs/source/pysmi/borrower/pyfile/pyfileborrower.rst
29 docs/source/pysmi/codegen/jsondoc/jsoncodegen.rst
30 docs/source/pysmi/codegen/null/nullcodegen.rst
31 docs/source/pysmi/codegen/pysnmp/pysnmpcodegen.rst
32 docs/source/pysmi/compiler/mibcompiler.rst
33 docs/source/pysmi/compiler/mibstatus.rst
34 docs/source/pysmi/parser/smi/dialect.rst
35 docs/source/pysmi/parser/smi/parserfactory.rst
36 docs/source/pysmi/reader/callback/callbackreader.rst
37 docs/source/pysmi/reader/ftpclient/ftpreader.rst
38 docs/source/pysmi/reader/httpclient/httpreader.rst
39 docs/source/pysmi/reader/localfile/filereader.rst
40 docs/source/pysmi/reader/zipreader/zipreader.rst
41 docs/source/pysmi/searcher/pyfile/pyfilesearcher.rst
42 docs/source/pysmi/searcher/pypackage/pypackagesearcher.rst
43 docs/source/pysmi/searcher/stub/stubsearcher.rst
44 docs/source/pysmi/writer/callback/callbackwriter.rst
45 docs/source/pysmi/writer/localfile/filewriter.rst
46 docs/source/pysmi/writer/pyfile/pyfilewriter.rst
47 examples/always-borrow-precompiled-pysnmp-files.py
48 examples/borrow-precompiled-pysnmp-files-on-failure.py
49 examples/compile-smistar-mibs-into-pysnmp-files-if-needed.py
50 examples/compile-smiv2-mibs-from-text-into-pysnmp-code.py
51 examples/download-and-compile-smistar-mibs-into-json.py
52 examples/download-and-compile-smistar-mibs-into-pysnmp-files.py
53 pysmi/__init__.py
54 pysmi/compat.py
55 pysmi/compiler.py
56 pysmi/debug.py
57 pysmi/error.py
58 pysmi/mibinfo.py
59 pysmi.egg-info/PKG-INFO
60 pysmi.egg-info/SOURCES.txt
61 pysmi.egg-info/dependency_links.txt
62 pysmi.egg-info/requires.txt
63 pysmi.egg-info/top_level.txt
64 pysmi.egg-info/zip-safe
65 pysmi/borrower/__init__.py
66 pysmi/borrower/anyfile.py
67 pysmi/borrower/base.py
68 pysmi/borrower/pyfile.py
69 pysmi/codegen/__init__.py
70 pysmi/codegen/base.py
71 pysmi/codegen/jsondoc.py
72 pysmi/codegen/null.py
73 pysmi/codegen/pysnmp.py
74 pysmi/codegen/symtable.py
75 pysmi/lexer/__init__.py
76 pysmi/lexer/base.py
77 pysmi/lexer/smi.py
78 pysmi/parser/__init__.py
79 pysmi/parser/base.py
80 pysmi/parser/dialect.py
81 pysmi/parser/null.py
82 pysmi/parser/smi.py
83 pysmi/parser/smiv1.py
84 pysmi/parser/smiv1compat.py
85 pysmi/parser/smiv2.py
86 pysmi/reader/__init__.py
87 pysmi/reader/base.py
88 pysmi/reader/callback.py
89 pysmi/reader/ftpclient.py
90 pysmi/reader/httpclient.py
91 pysmi/reader/localfile.py
92 pysmi/reader/url.py
93 pysmi/reader/zipreader.py
94 pysmi/searcher/__init__.py
95 pysmi/searcher/anyfile.py
96 pysmi/searcher/base.py
97 pysmi/searcher/pyfile.py
98 pysmi/searcher/pypackage.py
99 pysmi/searcher/stub.py
100 pysmi/writer/__init__.py
101 pysmi/writer/base.py
102 pysmi/writer/callback.py
103 pysmi/writer/localfile.py
104 pysmi/writer/pyfile.py
105 scripts/mibdump.py
106 tests/__init__.py
107 tests/__main__.py
108 tests/test_agentcapabilities_smiv2_pysnmp.py
109 tests/test_imports_smiv2_pysnmp.py
110 tests/test_modulecompliance_smiv2_pysnmp.py
111 tests/test_moduleidentity_smiv2_pysnmp.py
112 tests/test_notificationgroup_smiv2_pysnmp.py
113 tests/test_notificationtype_smiv2_pysnmp.py
114 tests/test_objectgroup_smiv2_pysnmp.py
115 tests/test_objectidentity_smiv2_pysnmp.py
116 tests/test_objecttype_smiv2_pysnmp.py
117 tests/test_smiv1_smiv2_pysnmp.py
118 tests/test_traptype_smiv2_pysnmp.py
119 tests/test_typedeclaration_smiv1_pysnmp.py
120 tests/test_typedeclaration_smiv2_pysnmp.py
121 tests/test_valuedeclaration_smiv2_pysnmp.py
122 tests/test_zipreader.py
0 ply==3.4; python_version < '2.6'
1 ply; python_version >= '2.6'
2 ordereddict; python_version < '2.7'
3 simplejson; python_version < '2.6'
0 #!/usr/bin/env python
1 #
2 # This file is part of pysmi software.
3 #
4 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
5 # License: http://pysmi.sf.net/license.html
6 #
7 # SNMP SMI/MIB data management tool
8 #
9 import os
10 import sys
11 import getopt
12 from pysmi.reader import getReadersFromUrls
13 from pysmi.searcher import AnyFileSearcher, PyFileSearcher, PyPackageSearcher, StubSearcher
14 from pysmi.borrower import AnyFileBorrower, PyFileBorrower
15 from pysmi.writer import PyFileWriter, FileWriter, CallbackWriter
16 from pysmi.parser import SmiV1CompatParser
17 from pysmi.codegen import PySnmpCodeGen, JsonCodeGen, NullCodeGen
18 from pysmi.compiler import MibCompiler
19 from pysmi import debug
20 from pysmi import error
21
22 # Defaults
23 verboseFlag = True
24 mibSources = []
25 doFuzzyMatchingFlag = True
26 mibSearchers = []
27 mibStubs = []
28 mibBorrowers = []
29 dstFormat = None
30 dstDirectory = None
31 cacheDirectory = ''
32 nodepsFlag = False
33 rebuildFlag = False
34 dryrunFlag = False
35 genMibTextsFlag = False
36 keepTextsLayout = False
37 pyCompileFlag = True
38 pyOptimizationLevel = 0
39 ignoreErrorsFlag = False
40 buildIndexFlag = False
41 writeMibsFlag = True
42
43 helpMessage = """\
44 Usage: %s [--help]
45 [--version]
46 [--quiet]
47 [--debug=<%s>]
48 [--mib-source=<URI>]
49 [--mib-searcher=<PATH|PACKAGE>]
50 [--mib-stub=<MIB-NAME>]
51 [--mib-borrower=<PATH>]
52 [--destination-format=<FORMAT>]
53 [--destination-directory=<DIRECTORY>]
54 [--cache-directory=<DIRECTORY>]
55 [--disable-fuzzy-source]
56 [--no-dependencies]
57 [--no-python-compile]
58 [--python-optimization-level]
59 [--ignore-errors]
60 [--build-index]
61 [--rebuild]
62 [--dry-run]
63 [--no-mib-writes]
64 [--generate-mib-texts]
65 [--keep-texts-layout]
66 <MIB-NAME> [MIB-NAME [...]]]
67 Where:
68 URI - file, zip, http, https, ftp, sftp schemes are supported.
69 Use @mib@ placeholder token in URI to refer directly to
70 the required MIB module when source does not support
71 directory listing (e.g. HTTP).
72 FORMAT - pysnmp, json, null""" % (
73 sys.argv[0],
74 '|'.join([x for x in sorted(debug.flagMap)])
75 )
76
77 try:
78 opts, inputMibs = getopt.getopt(
79 sys.argv[1:], 'hv',
80 ['help', 'version', 'quiet', 'debug=',
81 'mib-source=', 'mib-searcher=', 'mib-stub=', 'mib-borrower=',
82 'destination-format=', 'destination-directory=', 'cache-directory=',
83 'no-dependencies', 'no-python-compile', 'python-optimization-level=',
84 'ignore-errors', 'build-index', 'rebuild', 'dry-run', 'no-mib-writes',
85 'generate-mib-texts', 'disable-fuzzy-source', 'keep-texts-layout']
86 )
87
88 except getopt.GetoptError:
89 if verboseFlag:
90 sys.stderr.write('ERROR: %s\r\n%s\r\n' % (sys.exc_info()[1], helpMessage))
91
92 sys.exit(1)
93
94 for opt in opts:
95 if opt[0] == '-h' or opt[0] == '--help':
96 sys.stderr.write("""\
97 Synopsis:
98 SNMP SMI/MIB files conversion tool
99 Documentation:
100 http://pysmi.sourceforge.net
101 %s
102 """ % helpMessage)
103 sys.exit(-1)
104
105 if opt[0] == '-v' or opt[0] == '--version':
106 from pysmi import __version__
107
108 sys.stderr.write("""\
109 SNMP SMI/MIB library version %s, written by Ilya Etingof <etingof@gmail.com>
110 Python interpreter: %s
111 Software documentation and support at http://pysmi.sf.net
112 %s
113 """ % (__version__, sys.version, helpMessage))
114 sys.exit(1)
115
116 if opt[0] == '--quiet':
117 verboseFlag = False
118
119 if opt[0] == '--debug':
120 debug.setLogger(debug.Debug(*opt[1].split(',')))
121
122 if opt[0] == '--mib-source':
123 mibSources.append(opt[1])
124
125 if opt[0] == '--mib-searcher':
126 mibSearchers.append(opt[1])
127
128 if opt[0] == '--mib-stub':
129 mibStubs.append(opt[1])
130
131 if opt[0] == '--mib-borrower':
132 mibBorrowers.append((opt[1], genMibTextsFlag))
133
134 if opt[0] == '--destination-format':
135 dstFormat = opt[1]
136
137 if opt[0] == '--destination-directory':
138 dstDirectory = opt[1]
139
140 if opt[0] == '--cache-directory':
141 cacheDirectory = opt[1]
142
143 if opt[0] == '--no-dependencies':
144 nodepsFlag = True
145
146 if opt[0] == '--no-python-compile':
147 pyCompileFlag = False
148
149 if opt[0] == '--python-optimization-level':
150 try:
151 pyOptimizationLevel = int(opt[1])
152
153 except ValueError:
154 sys.stderr.write('ERROR: known Python optimization levels: -1, 0, 1, 2\r\n%s\r\n' % helpMessage)
155 sys.exit(-1)
156
157 if opt[0] == '--ignore-errors':
158 ignoreErrorsFlag = True
159
160 if opt[0] == '--build-index':
161 buildIndexFlag = True
162
163 if opt[0] == '--rebuild':
164 rebuildFlag = True
165
166 if opt[0] == '--dry-run':
167 dryrunFlag = True
168
169 if opt[0] == '--no-mib-writes':
170 writeMibsFlag = False
171
172 if opt[0] == '--generate-mib-texts':
173 genMibTextsFlag = True
174
175 if opt[0] == '--disable-fuzzy-source':
176 doFuzzyMatchingFlag = False
177
178 if opt[0] == '--keep-texts-layout':
179 keepTextsLayout = True
180
181 if not mibSources:
182 mibSources = ['file:///usr/share/snmp/mibs',
183 'http://mibs.snmplabs.com/asn1/@mib@']
184
185 if inputMibs:
186 mibSources = sorted(
187 set([os.path.abspath(os.path.dirname(x))
188 for x in inputMibs
189 if os.path.sep in x])
190 ) + mibSources
191
192 inputMibs = [os.path.basename(os.path.splitext(x)[0]) for x in inputMibs]
193
194 if not inputMibs:
195 sys.stderr.write('ERROR: MIB modules names not specified\r\n%s\r\n' % helpMessage)
196 sys.exit(-1)
197
198 if not dstFormat:
199 dstFormat = 'pysnmp'
200
201 if dstFormat == 'pysnmp':
202 if not mibSearchers:
203 mibSearchers = PySnmpCodeGen.defaultMibPackages
204
205 if not mibStubs:
206 mibStubs = [x for x in PySnmpCodeGen.baseMibs if x not in PySnmpCodeGen.fakeMibs]
207
208 if not mibBorrowers:
209 mibBorrowers = [('http://mibs.snmplabs.com/pysnmp/notexts/@mib@', False),
210 ('http://mibs.snmplabs.com/pysnmp/fulltexts/@mib@', True)]
211
212 if not dstDirectory:
213 dstDirectory = os.path.expanduser("~")
214 if sys.platform[:3] == 'win':
215 dstDirectory = os.path.join(dstDirectory, 'PySNMP Configuration', 'mibs')
216 else:
217 dstDirectory = os.path.join(dstDirectory, '.pysnmp', 'mibs')
218
219 # Compiler infrastructure
220
221 borrowers = [PyFileBorrower(x[1], genTexts=mibBorrowers[x[0]][1])
222 for x in enumerate(getReadersFromUrls(*[m[0] for m in mibBorrowers], **dict(lowcaseMatching=False)))]
223
224 searchers = [PyFileSearcher(dstDirectory)]
225
226 for mibSearcher in mibSearchers:
227 searchers.append(PyPackageSearcher(mibSearcher))
228
229 searchers.append(StubSearcher(*mibStubs))
230
231 codeGenerator = PySnmpCodeGen()
232
233 fileWriter = PyFileWriter(dstDirectory).setOptions(pyCompile=pyCompileFlag,
234 pyOptimizationLevel=pyOptimizationLevel)
235
236 elif dstFormat == 'json':
237 if not mibStubs:
238 mibStubs = JsonCodeGen.baseMibs
239
240 if not mibBorrowers:
241 mibBorrowers = [('http://mibs.snmplabs.com/json/notexts/@mib@', False),
242 ('http://mibs.snmplabs.com/json/fulltexts/@mib@', True)]
243
244 if not dstDirectory:
245 dstDirectory = os.path.join('.')
246
247 # Compiler infrastructure
248
249 borrowers = [AnyFileBorrower(x[1], genTexts=mibBorrowers[x[0]][1]).setOptions(exts=['.json'])
250 for x in enumerate(getReadersFromUrls(*[m[0] for m in mibBorrowers], **dict(lowcaseMatching=False)))]
251
252 searchers = [AnyFileSearcher(dstDirectory).setOptions(exts=['.json']), StubSearcher(*mibStubs)]
253
254 codeGenerator = JsonCodeGen()
255
256 fileWriter = FileWriter(dstDirectory).setOptions(suffix='.json')
257
258 elif dstFormat == 'null':
259 if not mibStubs:
260 mibStubs = NullCodeGen.baseMibs
261
262 if not mibBorrowers:
263 mibBorrowers = [('http://mibs.snmplabs.com/null/notexts/@mib@', False),
264 ('http://mibs.snmplabs.com/null/fulltexts/@mib@', True)]
265
266 if not dstDirectory:
267 dstDirectory = ''
268
269 # Compiler infrastructure
270
271 codeGenerator = NullCodeGen()
272
273 searchers = [StubSearcher(*mibStubs)]
274
275 borrowers = [AnyFileBorrower(x[1], genTexts=mibBorrowers[x[0]][1])
276 for x in enumerate(getReadersFromUrls(*[m[0] for m in mibBorrowers], **dict(lowcaseMatching=False)))]
277
278 fileWriter = CallbackWriter(lambda *x: None)
279
280 else:
281 sys.stderr.write('ERROR: unknown destination format: %s\r\n%s\r\n' % (dstFormat, helpMessage))
282 sys.exit(-1)
283
284 if verboseFlag:
285 sys.stderr.write("""Source MIB repositories: %s
286 Borrow missing/failed MIBs from: %s
287 Existing/compiled MIB locations: %s
288 Compiled MIBs destination directory: %s
289 MIBs excluded from code generation: %s
290 MIBs to compile: %s
291 Destination format: %s
292 Parser grammar cache directory: %s
293 Also compile all relevant MIBs: %s
294 Rebuild MIBs regardless of age: %s
295 Dry run mode: %s
296 Create/update MIBs: %s
297 Byte-compile Python modules: %s (optimization level %s)
298 Ignore compilation errors: %s
299 Generate OID->MIB index: %s
300 Generate texts in MIBs: %s
301 Keep original texts layout: %s
302 Try various file names while searching for MIB module: %s
303 """ % (', '.join(mibSources),
304 ', '.join([x[0] for x in mibBorrowers if x[1] == genMibTextsFlag]),
305 ', '.join(mibSearchers),
306 dstDirectory,
307 ', '.join(sorted(mibStubs)),
308 ', '.join(inputMibs),
309 dstFormat,
310 cacheDirectory or 'not used',
311 nodepsFlag and 'no' or 'yes',
312 rebuildFlag and 'yes' or 'no',
313 dryrunFlag and 'yes' or 'no',
314 writeMibsFlag and 'yes' or 'no',
315 dstFormat == 'pysnmp' and pyCompileFlag and 'yes' or 'no',
316 dstFormat == 'pysnmp' and pyOptimizationLevel and 'yes' or 'no',
317 ignoreErrorsFlag and 'yes' or 'no',
318 buildIndexFlag and 'yes' or 'no',
319 genMibTextsFlag and 'yes' or 'no',
320 keepTextsLayout and 'yes' or 'no',
321 doFuzzyMatchingFlag and 'yes' or 'no'))
322
323 # Initialize compiler infrastructure
324
325 mibCompiler = MibCompiler(
326 SmiV1CompatParser(tempdir=cacheDirectory),
327 codeGenerator,
328 fileWriter
329 )
330
331 try:
332 mibCompiler.addSources(
333 *getReadersFromUrls(
334 *mibSources, **dict(fuzzyMatching=doFuzzyMatchingFlag)
335 )
336 )
337
338 mibCompiler.addSearchers(*searchers)
339
340 mibCompiler.addBorrowers(*borrowers)
341
342 processed = mibCompiler.compile(
343 *inputMibs, **dict(noDeps=nodepsFlag,
344 rebuild=rebuildFlag,
345 dryRun=dryrunFlag,
346 genTexts=genMibTextsFlag,
347 textFilter=keepTextsLayout and (lambda symbol, text: text) or None,
348 writeMibs=writeMibsFlag,
349 ignoreErrors=ignoreErrorsFlag)
350 )
351
352 if buildIndexFlag:
353 mibCompiler.buildIndex(
354 processed,
355 dryRun=dryrunFlag,
356 ignoreErrors=ignoreErrorsFlag
357 )
358
359 except error.PySmiError:
360 sys.stderr.write('ERROR: %s\r\n' % sys.exc_info()[1])
361 sys.exit(-1)
362
363 else:
364 if verboseFlag:
365 sys.stderr.write('%sreated/updated MIBs: %s\r\n' % (dryrunFlag and 'Would be c' or 'C', ', '.join(
366 ['%s%s' % (x, x != processed[x].alias and ' (%s)' % processed[x].alias or '') for x in sorted(processed) if processed[x] == 'compiled'])))
367
368 sys.stderr.write('Pre-compiled MIBs %sborrowed: %s\r\n' % (dryrunFlag and 'Would be ' or '', ', '.join(
369 ['%s (%s)' % (x, processed[x].path) for x in sorted(processed) if processed[x] == 'borrowed'])))
370
371 sys.stderr.write(
372 'Up to date MIBs: %s\r\n' % ', '.join(['%s' % x for x in sorted(processed) if processed[x] == 'untouched']))
373
374 sys.stderr.write('Missing source MIBs: %s\r\n' % ', '.join(
375 ['%s' % x for x in sorted(processed) if processed[x] == 'missing']))
376
377 sys.stderr.write(
378 'Ignored MIBs: %s\r\n' % ', '.join(['%s' % x for x in sorted(processed) if processed[x] == 'unprocessed']))
379
380 sys.stderr.write('Failed MIBs: %s\r\n' % ', '.join(
381 ['%s (%s)' % (x, processed[x].error) for x in sorted(processed) if processed[x] == 'failed']))
382
383 sys.exit(0)
0 [bdist_wheel]
1 universal = 1
2
3 [egg_info]
4 tag_build =
5 tag_date = 0
6 tag_svn_revision = 0
7
0 #!/usr/bin/env python
1 #
2 # This file is part of pysmi software.
3 #
4 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
5 # License: http://pysmi.sf.net/license.html
6 #
7 """SNMP SMI/MIB Parser
8
9 A pure-Python implementation of SNMP/SMI MIB parsing and conversion library.
10 """
11
12 import os
13 import sys
14
15 classifiers = """\
16 Development Status :: 5 - Production/Stable
17 Environment :: Console
18 Intended Audience :: Developers
19 Intended Audience :: Education
20 Intended Audience :: Information Technology
21 Intended Audience :: System Administrators
22 Intended Audience :: Telecommunications Industry
23 License :: OSI Approved :: BSD License
24 Natural Language :: English
25 Operating System :: OS Independent
26 Programming Language :: Python :: 2
27 Programming Language :: Python :: 2.4
28 Programming Language :: Python :: 2.5
29 Programming Language :: Python :: 2.6
30 Programming Language :: Python :: 2.7
31 Programming Language :: Python :: 3
32 Programming Language :: Python :: 3.2
33 Programming Language :: Python :: 3.3
34 Programming Language :: Python :: 3.4
35 Programming Language :: Python :: 3.5
36 Programming Language :: Python :: 3.6
37 Topic :: Communications
38 Topic :: System :: Monitoring
39 Topic :: System :: Networking :: Monitoring
40 Topic :: Software Development :: Libraries :: Python Modules
41 """
42
43
44 def howto_install_setuptools():
45 print("""
46 Error: You need setuptools Python package!
47
48 It's very easy to install it, just type:
49
50 wget https://bootstrap.pypa.io/ez_setup.py
51 python ez_setup.py
52
53 Then you could make eggs from this package.
54 """)
55
56
57 if sys.version_info[:2] < (2, 4):
58 print("ERROR: this package requires Python 2.4 or later!")
59 sys.exit(1)
60
61
62 try:
63 from setuptools import setup, Command
64
65 params = {'zip_safe': True}
66
67 except ImportError:
68 for arg in sys.argv:
69 if 'egg' in arg:
70 howto_install_setuptools()
71 sys.exit(1)
72
73 from distutils.core import setup, Command
74
75 params = {}
76
77 if sys.version_info[:2] < (2, 6):
78 params['requires'] = ['ply(==3.4)', 'simplejson(==2.1)']
79 else:
80 params['requires'] = ['ply']
81
82 if sys.version_info[:2] < (2, 7):
83 params['requires'].append('ordereddict')
84 else:
85 if sys.version_info[:2] < (2, 6):
86 params['install_requires'] = ['ply==3.4', 'simplejson==2.1']
87 else:
88 params['install_requires'] = ['ply']
89
90 if sys.version_info[:2] < (2, 7):
91 params['install_requires'].append('ordereddict')
92
93 doclines = [x.strip() for x in (__doc__ or '').split('\n') if x]
94
95 params.update({
96 'name': 'pysmi',
97 'version': open(os.path.join('pysmi', '__init__.py')).read().split('\'')[1],
98 'description': doclines[0],
99 'long_description': ' '.join(doclines[1:]),
100 'maintainer': 'Ilya Etingof <etingof@gmail.com>',
101 'author': 'Ilya Etingof',
102 'author_email': 'etingof@gmail.com',
103 'url': 'https://github.com/etingof/pysmi',
104 'platforms': ['any'],
105 'classifiers': [x for x in classifiers.split('\n') if x],
106 'license': 'BSD',
107 'packages': ['pysmi',
108 'pysmi.reader',
109 'pysmi.searcher',
110 'pysmi.lexer',
111 'pysmi.parser',
112 'pysmi.codegen',
113 'pysmi.borrower',
114 'pysmi.writer'],
115 'scripts': [os.path.join('scripts', 'mibdump.py')]
116 })
117
118 # handle unittest discovery feature
119 if sys.version_info[0:2] < (2, 7) or \
120 sys.version_info[0:2] in ((3, 0), (3, 1)):
121 try:
122 import unittest2 as unittest
123 except ImportError:
124 unittest = None
125 else:
126 import unittest
127
128 if unittest:
129 class PyTest(Command):
130 user_options = []
131
132 def initialize_options(self): pass
133
134 def finalize_options(self): pass
135
136 def run(self):
137 suite = unittest.defaultTestLoader.discover('tests')
138 unittest.TextTestRunner(verbosity=2).run(suite)
139
140
141 params['cmdclass'] = {'test': PyTest}
142
143 setup(**params)
0 # This file is necessary to make this directory a package.
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 try:
7 import unittest2 as unittest
8
9 except ImportError:
10 import unittest
11
12 suite = unittest.TestLoader().loadTestsFromNames(
13 ['test_zipreader',
14 'test_agentcapabilities_smiv2_pysnmp',
15 'test_imports_smiv2_pysnmp',
16 'test_modulecompliance_smiv2_pysnmp',
17 'test_moduleidentity_smiv2_pysnmp',
18 'test_notificationgroup_smiv2_pysnmp',
19 'test_notificationtype_smiv2_pysnmp',
20 'test_objectgroup_smiv2_pysnmp',
21 'test_objectidentity_smiv2_pysnmp',
22 'test_objecttype_smiv2_pysnmp',
23 'test_smiv1_smiv2_pysnmp',
24 'test_traptype_smiv2_pysnmp',
25 'test_typedeclaration_smiv1_pysnmp',
26 'test_typedeclaration_smiv2_pysnmp',
27 'test_valuedeclaration_smiv2_pysnmp']
28 )
29
30
31 if __name__ == '__main__':
32 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 try:
8 import unittest2 as unittest
9
10 except ImportError:
11 import unittest
12
13 from pysmi.parser.smi import parserFactory
14 from pysmi.codegen.pysnmp import PySnmpCodeGen
15 from pysmi.codegen.symtable import SymtableCodeGen
16 from pysnmp.smi.builder import MibBuilder
17
18
19 class AgentCapabilitiesTestCase(unittest.TestCase):
20 """
21 TEST-MIB DEFINITIONS ::= BEGIN
22 IMPORTS
23 MODULE-IDENTITY
24 FROM SNMPv2-SMI
25 AGENT-CAPABILITIES
26 FROM SNMPv2-CONF;
27
28 testCapability AGENT-CAPABILITIES
29 PRODUCT-RELEASE "Test produce"
30 STATUS current
31 DESCRIPTION
32 "test capabilities"
33
34 SUPPORTS TEST-MIB
35 INCLUDES {
36 testSystemGroup,
37 testNotificationObjectGroup,
38 testNotificationGroup
39 }
40 VARIATION testSysLevelType
41 ACCESS read-only
42 DESCRIPTION
43 "Not supported."
44
45 VARIATION testSysLevelType
46 ACCESS read-only
47 DESCRIPTION
48 "Supported."
49
50 ::= { 1 3 }
51
52 END
53 """
54
55 def setUp(self):
56 ast = parserFactory()().parse(self.__class__.__doc__)[0]
57 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
58 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
59 codeobj = compile(pycode, 'test', 'exec')
60
61 mibBuilder = MibBuilder()
62 mibBuilder.loadTexts = True
63
64 self.ctx = {'mibBuilder': mibBuilder}
65
66 exec(codeobj, self.ctx, self.ctx)
67
68 def testAgentCapabilitiesSymbol(self):
69 self.assertTrue(
70 'testCapability' in self.ctx,
71 'symbol not present'
72 )
73
74 def testAgentCapabilitiesName(self):
75 self.assertEqual(
76 self.ctx['testCapability'].getName(),
77 (1, 3),
78 'bad name'
79 )
80
81 def testAgentCapabilitiesDescription(self):
82 self.assertEqual(
83 self.ctx['testCapability'].getDescription(),
84 'test capabilities',
85 'bad DESCRIPTION'
86 )
87
88 # XXX SUPPORTS/INCLUDES/VARIATION/ACCESS not supported by pysnmp
89
90 def testAgentCapabilitiesClass(self):
91 self.assertEqual(
92 self.ctx['testCapability'].__class__.__name__,
93 'AgentCapabilities',
94 'bad SYNTAX class'
95 )
96
97 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
98
99 if __name__ == '__main__':
100 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 try:
8 import unittest2 as unittest
9
10 except ImportError:
11 import unittest
12
13 from pysmi.parser.smi import parserFactory
14 from pysmi.codegen.pysnmp import PySnmpCodeGen
15 from pysmi.codegen.symtable import SymtableCodeGen
16 from pysnmp.smi.builder import MibBuilder
17
18
19 class ImportClauseTestCase(unittest.TestCase):
20 """
21 TEST-MIB DEFINITIONS ::= BEGIN
22 IMPORTS
23 MODULE-IDENTITY, OBJECT-TYPE, Unsigned32, mib-2
24 FROM SNMPv2-SMI
25 SnmpAdminString
26 FROM SNMP-FRAMEWORK-MIB;
27
28
29 END
30 """
31
32 def setUp(self):
33 ast = parserFactory()().parse(self.__class__.__doc__)[0]
34 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
35 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
36 codeobj = compile(pycode, 'test', 'exec')
37
38 self.ctx = {'mibBuilder': MibBuilder()}
39
40 exec(codeobj, self.ctx, self.ctx)
41
42 def testModuleImportsRequiredMibs(self):
43 self.assertEqual(
44 self.mibInfo.imported,
45 ('SNMP-FRAMEWORK-MIB', 'SNMPv2-CONF', 'SNMPv2-SMI', 'SNMPv2-TC'),
46 'imported MIBs not reported'
47 )
48
49 def testModuleCheckImportedSymbol(self):
50 self.assertTrue(
51 'SnmpAdminString' in self.ctx,
52 'imported symbol not present'
53 )
54
55 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
56
57 if __name__ == '__main__':
58 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 try:
8 import unittest2 as unittest
9
10 except ImportError:
11 import unittest
12
13 from pysmi.parser.smi import parserFactory
14 from pysmi.codegen.pysnmp import PySnmpCodeGen
15 from pysmi.codegen.symtable import SymtableCodeGen
16 from pysnmp.smi.builder import MibBuilder
17
18
19 class ModuleComplianceTestCase(unittest.TestCase):
20 """
21 TEST-MIB DEFINITIONS ::= BEGIN
22 IMPORTS
23 MODULE-COMPLIANCE
24 FROM SNMPv2-CONF;
25
26 testCompliance MODULE-COMPLIANCE
27 STATUS current
28 DESCRIPTION "This is the MIB compliance statement"
29 MODULE
30 MANDATORY-GROUPS {
31 testComplianceInfoGroup,
32 testNotificationInfoGroup
33 }
34 GROUP testNotificationGroup
35 DESCRIPTION
36 "Support for these notifications is optional."
37 ::= { 1 3 }
38
39 END
40 """
41
42 def setUp(self):
43 ast = parserFactory()().parse(self.__class__.__doc__)[0]
44 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
45 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
46 codeobj = compile(pycode, 'test', 'exec')
47
48 mibBuilder = MibBuilder()
49 mibBuilder.loadTexts = True
50
51 self.ctx = {'mibBuilder': mibBuilder}
52
53 exec(codeobj, self.ctx, self.ctx)
54
55 def testModuleComplianceSymbol(self):
56 self.assertTrue(
57 'testCompliance' in self.ctx,
58 'symbol not present'
59 )
60
61 def testModuleComplianceName(self):
62 self.assertEqual(
63 self.ctx['testCompliance'].getName(),
64 (1, 3),
65 'bad name'
66 )
67
68 def testModuleComplianceDescription(self):
69 self.assertEqual(
70 self.ctx['testCompliance'].getDescription(),
71 'This is the MIB compliance statement',
72 'bad DESCRIPTION'
73 )
74
75 def testModuleComplianceClass(self):
76 self.assertEqual(
77 self.ctx['testCompliance'].__class__.__name__,
78 'ModuleCompliance',
79 'bad SYNTAX class'
80 )
81
82 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
83
84 if __name__ == '__main__':
85 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 try:
8 import unittest2 as unittest
9
10 except ImportError:
11 import unittest
12
13 from pysmi.parser.smi import parserFactory
14 from pysmi.codegen.pysnmp import PySnmpCodeGen
15 from pysmi.codegen.symtable import SymtableCodeGen
16 from pysnmp.smi.builder import MibBuilder
17
18
19 class ModuleIdentityTestCase(unittest.TestCase):
20 """
21 TEST-MIB DEFINITIONS ::= BEGIN
22 IMPORTS
23 MODULE-IDENTITY
24 FROM SNMPv2-SMI;
25
26 testModule MODULE-IDENTITY
27 LAST-UPDATED "200001100000Z" -- Midnight 10 January 2000
28 ORGANIZATION "AgentX Working Group"
29 CONTACT-INFO "WG-email: agentx@dorothy.bmc.com"
30 DESCRIPTION "This is the MIB module for the SNMP"
31 REVISION "200001100000Z" -- Midnight 10 January 2000
32 DESCRIPTION "Initial version published as RFC 2742."
33 ::= { 1 3 }
34
35 END
36 """
37
38 def setUp(self):
39 ast = parserFactory()().parse(self.__class__.__doc__)[0]
40 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
41 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
42 codeobj = compile(pycode, 'test', 'exec')
43
44 mibBuilder = MibBuilder()
45 mibBuilder.loadTexts = True
46
47 self.ctx = {'mibBuilder': mibBuilder}
48
49 exec(codeobj, self.ctx, self.ctx)
50
51 def testModuleIdentitySymbol(self):
52 self.assertTrue(
53 'testModule' in self.ctx,
54 'symbol not present'
55 )
56
57 def testModuleIdentityName(self):
58 self.assertEqual(
59 self.ctx['testModule'].getName(),
60 (1, 3),
61 'bad name'
62 )
63
64 def testModuleIdentityLastUpdated(self):
65 self.assertEqual(
66 self.ctx['testModule'].getLastUpdated(),
67 '200001100000Z',
68 'bad LAST-UPDATED'
69 )
70
71 def testModuleIdentityOrganization(self):
72 self.assertEqual(
73 self.ctx['testModule'].getOrganization(),
74 'AgentX Working Group',
75 'bad ORGANIZATION'
76 )
77
78 def testModuleIdentityRevisions(self):
79 self.assertEqual(
80 self.ctx['testModule'].getRevisions(),
81 ('2000-01-10 00:00',),
82 'bad REVISIONS'
83 )
84 # TODO: pysnmp does not implement .getRevisionsDescriptions()
85 # self.assertEqual(
86 # self.ctx['testModule'].getRevisionsDescriptions(),
87 # ('Initial version published as RFC 2742.',),
88 # 'bad REVISIONS'
89 # )
90
91 def testModuleIdentityContactInfo(self):
92 self.assertEqual(
93 self.ctx['testModule'].getContactInfo(),
94 'WG-email: agentx@dorothy.bmc.com',
95 'bad CONTACT-INFO'
96 )
97
98 def testModuleIdentityDescription(self):
99 self.assertEqual(
100 self.ctx['testModule'].getDescription(),
101 'This is the MIB module for the SNMP',
102 'bad DESCRIPTION'
103 )
104
105 def testModuleIdentityClass(self):
106 self.assertEqual(
107 self.ctx['testModule'].__class__.__name__,
108 'ModuleIdentity',
109 'bad SYNTAX class'
110 )
111
112 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
113
114 if __name__ == '__main__':
115 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 try:
8 import unittest2 as unittest
9
10 except ImportError:
11 import unittest
12
13 from pysmi.parser.smi import parserFactory
14 from pysmi.codegen.pysnmp import PySnmpCodeGen
15 from pysmi.codegen.symtable import SymtableCodeGen
16 from pysnmp.smi.builder import MibBuilder
17
18
19 class NotificationGroupTestCase(unittest.TestCase):
20 """
21 TEST-MIB DEFINITIONS ::= BEGIN
22 IMPORTS
23 NOTIFICATION-GROUP
24 FROM SNMPv2-CONF;
25
26 testNotificationGroup NOTIFICATION-GROUP
27 NOTIFICATIONS {
28 testStatusChangeNotify,
29 testClassEventNotify,
30 testThresholdBelowNotify
31 }
32 STATUS current
33 DESCRIPTION
34 "A collection of test notifications."
35 ::= { 1 3 }
36
37 END
38 """
39
40 def setUp(self):
41 ast = parserFactory()().parse(self.__class__.__doc__)[0]
42 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
43 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
44 codeobj = compile(pycode, 'test', 'exec')
45
46 mibBuilder = MibBuilder()
47 mibBuilder.loadTexts = True
48
49 self.ctx = {'mibBuilder': mibBuilder}
50
51 exec(codeobj, self.ctx, self.ctx)
52
53 def testNotificationGroupSymbol(self):
54 self.assertTrue(
55 'testNotificationGroup' in self.ctx,
56 'symbol not present'
57 )
58
59 def testNotificationGroupName(self):
60 self.assertEqual(
61 self.ctx['testNotificationGroup'].getName(),
62 (1, 3),
63 'bad name'
64 )
65
66 def testNotificationGroupDescription(self):
67 self.assertEqual(
68 self.ctx['testNotificationGroup'].getDescription(),
69 'A collection of test notifications.',
70 'bad DESCRIPTION'
71 )
72
73 def testNotificationGroupClass(self):
74 self.assertEqual(
75 self.ctx['testNotificationGroup'].__class__.__name__,
76 'NotificationGroup',
77 'bad SYNTAX class'
78 )
79
80 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
81
82 if __name__ == '__main__':
83 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 try:
8 import unittest2 as unittest
9
10 except ImportError:
11 import unittest
12
13 from pysmi.parser.smi import parserFactory
14 from pysmi.codegen.pysnmp import PySnmpCodeGen
15 from pysmi.codegen.symtable import SymtableCodeGen
16 from pysnmp.smi.builder import MibBuilder
17
18
19 class NotificationTypeTestCase(unittest.TestCase):
20 """
21 TEST-MIB DEFINITIONS ::= BEGIN
22 IMPORTS
23 NOTIFICATION-TYPE
24 FROM SNMPv2-SMI;
25
26 testNotificationType NOTIFICATION-TYPE
27 OBJECTS {
28 testChangeConfigType,
29 testChangeConfigValue
30 }
31 STATUS current
32 DESCRIPTION
33 "A collection of test notification types."
34 ::= { 1 3 }
35
36 END
37 """
38
39 def setUp(self):
40 ast = parserFactory()().parse(self.__class__.__doc__)[0]
41 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
42 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
43 codeobj = compile(pycode, 'test', 'exec')
44
45 mibBuilder = MibBuilder()
46 mibBuilder.loadTexts = True
47
48 self.ctx = {'mibBuilder': mibBuilder}
49
50 exec(codeobj, self.ctx, self.ctx)
51
52 def testNotificationTypeSymbol(self):
53 self.assertTrue(
54 'testNotificationType' in self.ctx,
55 'symbol not present'
56 )
57
58 def testNotificationTypeName(self):
59 self.assertEqual(
60 self.ctx['testNotificationType'].getName(),
61 (1, 3),
62 'bad name'
63 )
64
65 def testNotificationTypeDescription(self):
66 self.assertEqual(
67 self.ctx['testNotificationType'].getDescription(),
68 'A collection of test notification types.',
69 'bad DESCRIPTION'
70 )
71
72 def testNotificationTypeClass(self):
73 self.assertEqual(
74 self.ctx['testNotificationType'].__class__.__name__,
75 'NotificationType',
76 'bad SYNTAX class'
77 )
78
79 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
80
81 if __name__ == '__main__':
82 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 try:
8 import unittest2 as unittest
9
10 except ImportError:
11 import unittest
12
13 from pysmi.parser.smi import parserFactory
14 from pysmi.parser.dialect import smiV2
15 from pysmi.codegen.pysnmp import PySnmpCodeGen
16 from pysmi.codegen.symtable import SymtableCodeGen
17 from pysnmp.smi.builder import MibBuilder
18
19
20 class ObjectGroupTestCase(unittest.TestCase):
21 """
22 TEST-MIB DEFINITIONS ::= BEGIN
23 IMPORTS
24 OBJECT-GROUP
25 FROM SNMPv2-CONF;
26
27 testObjectGroup OBJECT-GROUP
28 OBJECTS {
29 testStorageType,
30 testRowStatus
31 }
32 STATUS current
33 DESCRIPTION
34 "A collection of test objects."
35 ::= { 1 3 }
36
37 END
38 """
39
40 def setUp(self):
41 ast = parserFactory(**smiV2)().parse(self.__class__.__doc__)[0]
42 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
43 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
44 codeobj = compile(pycode, 'test', 'exec')
45
46 mibBuilder = MibBuilder()
47 mibBuilder.loadTexts = True
48
49 self.ctx = {'mibBuilder': mibBuilder}
50
51 exec(codeobj, self.ctx, self.ctx)
52
53 def testObjectGroupSymbol(self):
54 self.assertTrue(
55 'testObjectGroup' in self.ctx,
56 'symbol not present'
57 )
58
59 def testObjectGroupName(self):
60 self.assertEqual(
61 self.ctx['testObjectGroup'].getName(),
62 (1, 3),
63 'bad name'
64 )
65
66 def testObjectGroupDescription(self):
67 self.assertEqual(
68 self.ctx['testObjectGroup'].getDescription(),
69 'A collection of test objects.',
70 'bad DESCRIPTION'
71 )
72
73 def testObjectGroupObjects(self):
74 self.assertEqual(
75 self.ctx['testObjectGroup'].getObjects(),
76 (('TEST-MIB', 'testStorageType'), ('TEST-MIB', 'testRowStatus')),
77 'bad OBJECTS'
78 )
79
80 def testObjectGroupClass(self):
81 self.assertEqual(
82 self.ctx['testObjectGroup'].__class__.__name__,
83 'ObjectGroup',
84 'bad SYNTAX class'
85 )
86
87
88 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
89
90 if __name__ == '__main__':
91 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 try:
8 import unittest2 as unittest
9
10 except ImportError:
11 import unittest
12
13 from pysmi.parser.smi import parserFactory
14 from pysmi.codegen.pysnmp import PySnmpCodeGen
15 from pysmi.codegen.symtable import SymtableCodeGen
16 from pysnmp.smi.builder import MibBuilder
17
18
19 class ObjectIdentityTestCase(unittest.TestCase):
20 """
21 TEST-MIB DEFINITIONS ::= BEGIN
22 IMPORTS
23 OBJECT-IDENTITY
24 FROM SNMPv2-SMI;
25
26 testObject OBJECT-IDENTITY
27 STATUS current
28 DESCRIPTION "Initial version"
29 REFERENCE "ABC"
30
31 ::= { 1 3 }
32
33 END
34 """
35
36 def setUp(self):
37 ast = parserFactory()().parse(self.__class__.__doc__)[0]
38 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
39 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
40 codeobj = compile(pycode, 'test', 'exec')
41
42 mibBuilder = MibBuilder()
43 mibBuilder.loadTexts = True
44
45 self.ctx = {'mibBuilder': mibBuilder}
46
47 exec(codeobj, self.ctx, self.ctx)
48
49 def testObjectIdentitySymbol(self):
50 self.assertTrue(
51 'testObject' in self.ctx,
52 'symbol not present'
53 )
54
55 def testObjectIdentityName(self):
56 self.assertEqual(
57 self.ctx['testObject'].getName(),
58 (1, 3),
59 'bad name'
60 )
61
62 def testObjectIdentityDescription(self):
63 self.assertEqual(
64 self.ctx['testObject'].getDescription(),
65 'Initial version',
66 'bad DESCRIPTION'
67 )
68
69 def testObjectIdentityReference(self):
70 self.assertEqual(
71 self.ctx['testObject'].getReference(),
72 'ABC',
73 'bad REFERENCE'
74 )
75
76 def testObjectIdentityClass(self):
77 self.assertEqual(
78 self.ctx['testObject'].__class__.__name__,
79 'ObjectIdentity',
80 'bad SYNTAX class'
81 )
82
83
84 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
85
86 if __name__ == '__main__':
87 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 try:
8 import unittest2 as unittest
9
10 except ImportError:
11 import unittest
12
13 from pyasn1.compat.octets import str2octs
14 from pysmi.parser.smi import parserFactory
15 from pysmi.codegen.pysnmp import PySnmpCodeGen
16 from pysmi.codegen.symtable import SymtableCodeGen
17 from pysnmp.smi.builder import MibBuilder
18
19
20 class ObjectTypeBasicTestCase(unittest.TestCase):
21 """
22 TEST-MIB DEFINITIONS ::= BEGIN
23 IMPORTS
24 OBJECT-TYPE
25 FROM SNMPv2-SMI;
26
27 testObjectType OBJECT-TYPE
28 SYNTAX Integer32
29 UNITS "seconds"
30 MAX-ACCESS accessible-for-notify
31 STATUS current
32 DESCRIPTION "Test object"
33 REFERENCE "ABC"
34 ::= { 1 3 }
35
36 END
37 """
38
39 def setUp(self):
40 ast = parserFactory()().parse(self.__class__.__doc__)[0]
41 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
42 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
43 codeobj = compile(pycode, 'test', 'exec')
44
45 mibBuilder = MibBuilder()
46 mibBuilder.loadTexts = True
47
48 self.ctx = {'mibBuilder': mibBuilder}
49
50 exec(codeobj, self.ctx, self.ctx)
51
52 def testObjectTypeSymbol(self):
53 self.assertTrue(
54 'testObjectType' in self.ctx,
55 'symbol not present'
56 )
57
58 def testObjectTypeName(self):
59 self.assertEqual(
60 self.ctx['testObjectType'].getName(),
61 (1, 3),
62 'bad name'
63 )
64
65 def testObjectTypeDescription(self):
66 self.assertEqual(
67 self.ctx['testObjectType'].getDescription(),
68 'Test object',
69 'bad DESCRIPTION'
70 )
71
72 def testObjectTypeStatus(self):
73 self.assertEqual(
74 self.ctx['testObjectType'].getStatus(),
75 'current',
76 'bad STATUS'
77 )
78
79 # TODO:revisit
80 # def testObjectTypeReference(self):
81 # self.assertEqual(
82 # self.ctx['testObjectType'].getReference(), str2octs('ABC'),
83 # 'bad REFERENCE'
84 # )
85
86 def testObjectTypeMaxAccess(self):
87 self.assertEqual(
88 self.ctx['testObjectType'].getMaxAccess(),
89 'accessiblefornotify',
90 'bad MAX-ACCESS'
91 )
92
93 def testObjectTypeUnits(self):
94 self.assertEqual(
95 self.ctx['testObjectType'].getUnits(),
96 'seconds',
97 'bad UNITS'
98 )
99
100 def testObjectTypeSyntax(self):
101 self.assertEqual(
102 self.ctx['testObjectType'].getSyntax().clone(123),
103 123,
104 'bad SYNTAX'
105 )
106
107 def testObjectTypeClass(self):
108 self.assertEqual(
109 self.ctx['testObjectType'].__class__.__name__,
110 'MibScalar',
111 'bad SYNTAX'
112 )
113
114
115 class ObjectTypeIntegerDefaultTestCase(unittest.TestCase):
116 """
117 TEST-MIB DEFINITIONS ::= BEGIN
118 IMPORTS
119 OBJECT-TYPE,
120 Integer32
121 FROM SNMPv2-SMI;
122
123 testObjectType OBJECT-TYPE
124 SYNTAX Integer32
125 MAX-ACCESS read-only
126 STATUS current
127 DESCRIPTION "Test object"
128 DEFVAL { 123456 }
129 ::= { 1 3 }
130
131 END
132 """
133
134 def setUp(self):
135 ast = parserFactory()().parse(self.__class__.__doc__)[0]
136 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
137 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
138 codeobj = compile(pycode, 'test', 'exec')
139
140 self.ctx = {'mibBuilder': MibBuilder()}
141
142 exec(codeobj, self.ctx, self.ctx)
143
144 def testObjectTypeSyntax(self):
145 self.assertEqual(
146 self.ctx['testObjectType'].getSyntax(),
147 123456,
148 'bad DEFVAL'
149 )
150
151
152 class ObjectTypeEnumDefaultTestCase(unittest.TestCase):
153 """
154 TEST-MIB DEFINITIONS ::= BEGIN
155 IMPORTS
156 OBJECT-TYPE
157 FROM SNMPv2-SMI;
158
159 testObjectType OBJECT-TYPE
160 SYNTAX INTEGER {
161 enable(1),
162 disable(2)
163 }
164 MAX-ACCESS read-only
165 STATUS current
166 DESCRIPTION "Test object"
167 DEFVAL { enable }
168 ::= { 1 3 }
169
170 END
171 """
172
173 def setUp(self):
174 ast = parserFactory()().parse(self.__class__.__doc__)[0]
175 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
176 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
177 codeobj = compile(pycode, 'test', 'exec')
178
179 self.ctx = {'mibBuilder': MibBuilder()}
180
181 exec(codeobj, self.ctx, self.ctx)
182
183 def testObjectTypeSyntax(self):
184 self.assertEqual(
185 self.ctx['testObjectType'].getSyntax(),
186 1,
187 'bad DEFVAL'
188 )
189
190
191 class ObjectTypeStringDefaultTestCase(unittest.TestCase):
192 """
193 TEST-MIB DEFINITIONS ::= BEGIN
194 IMPORTS
195 OBJECT-TYPE
196 FROM SNMPv2-SMI;
197
198 testObjectType OBJECT-TYPE
199 SYNTAX OCTET STRING
200 MAX-ACCESS read-only
201 STATUS current
202 DESCRIPTION "Test object"
203 DEFVAL { "test value" }
204 ::= { 1 3 }
205
206 END
207 """
208
209 def setUp(self):
210 ast = parserFactory()().parse(self.__class__.__doc__)[0]
211 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
212 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
213 codeobj = compile(pycode, 'test', 'exec')
214
215 self.ctx = {'mibBuilder': MibBuilder()}
216
217 exec(codeobj, self.ctx, self.ctx)
218
219 def testObjectTypeSyntax(self):
220 self.assertEqual(
221 self.ctx['testObjectType'].getSyntax(), str2octs('test value'),
222 'bad DEFVAL'
223 )
224
225
226 class ObjectTypeWithIntegerConstraintTestCase(unittest.TestCase):
227 """
228 TEST-MIB DEFINITIONS ::= BEGIN
229 IMPORTS
230 OBJECT-TYPE,
231 Unsigned32
232 FROM SNMPv2-SMI;
233
234 testObjectType OBJECT-TYPE
235 SYNTAX Unsigned32 (0..4294967295)
236 MAX-ACCESS read-only
237 STATUS current
238 DESCRIPTION "Test object"
239 DEFVAL { 0 }
240 ::= { 1 3 }
241
242 END
243 """
244
245 def setUp(self):
246 ast = parserFactory()().parse(self.__class__.__doc__)[0]
247 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
248 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
249 codeobj = compile(pycode, 'test', 'exec')
250
251 self.ctx = {'mibBuilder': MibBuilder()}
252
253 exec(codeobj, self.ctx, self.ctx)
254
255 def testObjectTypeSyntax(self):
256 self.assertEqual(
257 self.ctx['testObjectType'].getSyntax().clone(123),
258 123,
259 'bad integer range constrained SYNTAX'
260 )
261
262
263 class ObjectTypeWithIntegerSetConstraintTestCase(unittest.TestCase):
264 """
265 TEST-MIB DEFINITIONS ::= BEGIN
266 IMPORTS
267 OBJECT-TYPE,
268 Unsigned32
269 FROM SNMPv2-SMI;
270
271 testObjectType OBJECT-TYPE
272 SYNTAX Unsigned32 (0|2|44)
273 MAX-ACCESS read-only
274 STATUS current
275 DESCRIPTION "Test object"
276 ::= { 1 3 }
277
278 END
279 """
280
281 def setUp(self):
282 ast = parserFactory()().parse(self.__class__.__doc__)[0]
283 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
284 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
285 codeobj = compile(pycode, 'test', 'exec')
286
287 self.ctx = {'mibBuilder': MibBuilder()}
288
289 exec(codeobj, self.ctx, self.ctx)
290
291 def testObjectTypeSyntax(self):
292 self.assertEqual(
293 self.ctx['testObjectType'].getSyntax().clone(44),
294 44,
295 'bad multiple integer constrained SYNTAX'
296 )
297
298
299 class ObjectTypeWithStringSizeConstraintTestCase(unittest.TestCase):
300 """
301 TEST-MIB DEFINITIONS ::= BEGIN
302 IMPORTS
303 OBJECT-TYPE,
304 Unsigned32
305 FROM SNMPv2-SMI;
306
307 testObjectType OBJECT-TYPE
308 SYNTAX OCTET STRING (SIZE (0..512))
309 MAX-ACCESS read-only
310 STATUS current
311 DESCRIPTION "Test object"
312 ::= { 1 3 }
313
314 END
315 """
316
317 def setUp(self):
318 ast = parserFactory()().parse(self.__class__.__doc__)[0]
319 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
320 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
321 codeobj = compile(pycode, 'test', 'exec')
322
323 self.ctx = {'mibBuilder': MibBuilder()}
324
325 exec(codeobj, self.ctx, self.ctx)
326
327 def testObjectTypeSyntax(self):
328 self.assertEqual(
329 self.ctx['testObjectType'].getSyntax().clone(''), str2octs(''),
330 'bad size constrained SYNTAX'
331 )
332
333
334 class ObjectTypeBitsTestCase(unittest.TestCase):
335 """
336 TEST-MIB DEFINITIONS ::= BEGIN
337 IMPORTS
338 OBJECT-TYPE,
339 Unsigned32
340 FROM SNMPv2-SMI;
341
342 testObjectType OBJECT-TYPE
343 SYNTAX BITS { notification(0), set(1) }
344 MAX-ACCESS read-only
345 STATUS current
346 DESCRIPTION "Test object"
347 ::= { 1 3 }
348
349 END
350 """
351
352 def setUp(self):
353 ast = parserFactory()().parse(self.__class__.__doc__)[0]
354 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
355 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
356 codeobj = compile(pycode, 'test', 'exec')
357
358 self.ctx = {'mibBuilder': MibBuilder()}
359
360 exec(codeobj, self.ctx, self.ctx)
361
362 def testObjectTypeSyntax(self):
363 self.assertEqual(
364 self.ctx['testObjectType'].getSyntax().clone(('set',)), str2octs('@'),
365 'bad BITS SYNTAX'
366 )
367
368
369 class ObjectTypeMibTableTestCase(unittest.TestCase):
370 """
371 TEST-MIB DEFINITIONS ::= BEGIN
372 IMPORTS
373 OBJECT-TYPE
374 FROM SNMPv2-SMI;
375
376 testTable OBJECT-TYPE
377 SYNTAX SEQUENCE OF TestEntry
378 MAX-ACCESS not-accessible
379 STATUS current
380 DESCRIPTION "Test table"
381 ::= { 1 3 }
382
383 testEntry OBJECT-TYPE
384 SYNTAX TestEntry
385 MAX-ACCESS not-accessible
386 STATUS current
387 DESCRIPTION "Test row"
388 INDEX { testIndex }
389 ::= { testTable 1 }
390
391 TestEntry ::= SEQUENCE {
392 testIndex INTEGER,
393 testValue OCTET STRING
394 }
395
396 testIndex OBJECT-TYPE
397 SYNTAX INTEGER
398 MAX-ACCESS read-create
399 STATUS current
400 DESCRIPTION "Test column"
401 ::= { testEntry 1 }
402
403 testValue OBJECT-TYPE
404 SYNTAX OCTET STRING
405 MAX-ACCESS read-create
406 STATUS current
407 DESCRIPTION "Test column"
408 ::= { testEntry 2 }
409
410 END
411 """
412
413 def setUp(self):
414 ast = parserFactory()().parse(self.__class__.__doc__)[0]
415 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
416 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
417 codeobj = compile(pycode, 'test', 'exec')
418
419 self.ctx = {'mibBuilder': MibBuilder()}
420
421 exec(codeobj, self.ctx, self.ctx)
422
423 def testObjectTypeTableClass(self):
424 self.assertEqual(
425 self.ctx['testTable'].__class__.__name__,
426 'MibTable',
427 'bad table class'
428 )
429
430 def testObjectTypeTableRowClass(self):
431 self.assertEqual(
432 self.ctx['testEntry'].__class__.__name__,
433 'MibTableRow',
434 'bad table row class'
435 )
436
437 def testObjectTypeTableColumnClass(self):
438 self.assertEqual(
439 self.ctx['testIndex'].__class__.__name__,
440 'MibTableColumn',
441 'bad table column class'
442 )
443
444 def testObjectTypeTableRowIndex(self):
445 self.assertEqual(
446 self.ctx['testEntry'].getIndexNames(),
447 ((0, 'TEST-MIB', 'testIndex'),),
448 'bad table index'
449 )
450
451
452 class ObjectTypeMibTableImpliedIndexTestCase(unittest.TestCase):
453 """
454 TEST-MIB DEFINITIONS ::= BEGIN
455 IMPORTS
456 OBJECT-TYPE
457 FROM SNMPv2-SMI;
458
459 testTable OBJECT-TYPE
460 SYNTAX SEQUENCE OF TestEntry
461 MAX-ACCESS not-accessible
462 STATUS current
463 DESCRIPTION "Test table"
464 ::= { 1 3 }
465
466 testEntry OBJECT-TYPE
467 SYNTAX TestEntry
468 MAX-ACCESS not-accessible
469 STATUS current
470 DESCRIPTION "Test row"
471 INDEX { IMPLIED testIndex }
472 ::= { testTable 3 }
473
474 TestEntry ::= SEQUENCE {
475 testIndex INTEGER
476 }
477
478 testIndex OBJECT-TYPE
479 SYNTAX INTEGER
480 MAX-ACCESS read-create
481 STATUS current
482 DESCRIPTION "Test column"
483 ::= { testEntry 1 }
484
485 END
486 """
487
488 def setUp(self):
489 ast = parserFactory()().parse(self.__class__.__doc__)[0]
490 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
491 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
492 codeobj = compile(pycode, 'test', 'exec')
493
494 self.ctx = {'mibBuilder': MibBuilder()}
495
496 exec(codeobj, self.ctx, self.ctx)
497
498 def testObjectTypeTableRowIndex(self):
499 self.assertEqual(
500 self.ctx['testEntry'].getIndexNames(),
501 ((1, 'TEST-MIB', 'testIndex'),),
502 'bad IMPLIED table index'
503 )
504
505
506 class ObjectTypeMibTableMultipleIndicesTestCase(unittest.TestCase):
507 """
508 TEST-MIB DEFINITIONS ::= BEGIN
509 IMPORTS
510 OBJECT-TYPE
511 FROM SNMPv2-SMI;
512
513 testTable OBJECT-TYPE
514 SYNTAX SEQUENCE OF TestEntry
515 MAX-ACCESS not-accessible
516 STATUS current
517 DESCRIPTION "Test table"
518 ::= { 1 3 }
519
520 testEntry OBJECT-TYPE
521 SYNTAX TestEntry
522 MAX-ACCESS not-accessible
523 STATUS current
524 DESCRIPTION "Test row"
525 INDEX { testIndex, testValue }
526 ::= { testTable 3 }
527
528 TestEntry ::= SEQUENCE {
529 testIndex INTEGER,
530 testValue OCTET STRING
531 }
532
533 testIndex OBJECT-TYPE
534 SYNTAX INTEGER
535 MAX-ACCESS read-create
536 STATUS current
537 DESCRIPTION "Test column"
538 ::= { testEntry 1 }
539
540 testValue OBJECT-TYPE
541 SYNTAX OCTET STRING
542 MAX-ACCESS read-create
543 STATUS current
544 DESCRIPTION "Test column"
545 ::= { testEntry 2 }
546
547 END
548 """
549
550 def setUp(self):
551 ast = parserFactory()().parse(self.__class__.__doc__)[0]
552 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
553 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
554 codeobj = compile(pycode, 'test', 'exec')
555
556 self.ctx = {'mibBuilder': MibBuilder()}
557
558 exec(codeobj, self.ctx, self.ctx)
559
560 def testObjectTypeTableRowIndex(self):
561 self.assertEqual(
562 self.ctx['testEntry'].getIndexNames(),
563 ((0, 'TEST-MIB', 'testIndex'), (0, 'TEST-MIB', 'testValue')),
564 'bad multiple table indices'
565 )
566
567
568 class ObjectTypeAurmentingMibTableTestCase(unittest.TestCase):
569 """
570 TEST-MIB DEFINITIONS ::= BEGIN
571 IMPORTS
572 OBJECT-TYPE
573 FROM SNMPv2-SMI;
574
575 testTable OBJECT-TYPE
576 SYNTAX SEQUENCE OF TestEntry
577 MAX-ACCESS not-accessible
578 STATUS current
579 DESCRIPTION "Test table"
580 ::= { 1 3 }
581
582 testEntry OBJECT-TYPE
583 SYNTAX TestEntry
584 MAX-ACCESS not-accessible
585 STATUS current
586 DESCRIPTION "Test row"
587 INDEX { testIndex }
588 ::= { testTable 3 }
589
590 TestEntry ::= SEQUENCE {
591 testIndex INTEGER
592 }
593
594 testIndex OBJECT-TYPE
595 SYNTAX INTEGER
596 MAX-ACCESS read-create
597 STATUS current
598 DESCRIPTION "Test column"
599 ::= { testEntry 1 }
600
601 testTableExt OBJECT-TYPE
602 SYNTAX SEQUENCE OF TestEntryExt
603 MAX-ACCESS not-accessible
604 STATUS current
605 DESCRIPTION "Test table"
606 ::= { 1 4 }
607
608 testEntryExt OBJECT-TYPE
609 SYNTAX TestEntryExt
610 MAX-ACCESS not-accessible
611 STATUS current
612 DESCRIPTION "Test row"
613 AUGMENTS { testEntry }
614 ::= { testTableExt 3 }
615
616 TestEntryExt ::= SEQUENCE {
617 testIndexExt INTEGER
618 }
619
620 testIndexExt OBJECT-TYPE
621 SYNTAX INTEGER
622 MAX-ACCESS read-create
623 STATUS current
624 DESCRIPTION "Test column"
625 ::= { testEntryExt 1 }
626
627 END
628 """
629
630 def setUp(self):
631 ast = parserFactory()().parse(self.__class__.__doc__)[0]
632 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
633 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
634 codeobj = compile(pycode, 'test', 'exec')
635
636 self.ctx = {'mibBuilder': MibBuilder()}
637
638 exec(codeobj, self.ctx, self.ctx)
639
640 def testObjectTypeTableRowAugmention(self):
641 # XXX provide getAugmentation() method
642 self.assertEqual(
643 list(self.ctx['testEntry'].augmentingRows.keys())[0],
644 ('TEST-MIB', 'testEntryExt'),
645 'bad AUGMENTS table clause'
646 )
647
648 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
649
650 if __name__ == '__main__':
651 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 try:
8 import unittest2 as unittest
9
10 except ImportError:
11 import unittest
12
13 from pysmi.parser.smi import parserFactory
14 from pysmi.codegen.pysnmp import PySnmpCodeGen
15 from pysmi.codegen.symtable import SymtableCodeGen
16 from pysnmp.smi.builder import MibBuilder
17
18
19 class SmiV1TestCase(unittest.TestCase):
20 """
21 TEST-MIB DEFINITIONS ::= BEGIN
22
23 IMPORTS
24 Counter, IpAddress, TimeTicks
25 FROM RFC1155-SMI
26 DisplayString, mib-2
27 FROM RFC1213-MIB
28 OBJECT-TYPE
29 FROM RFC-1212
30
31 NOTIFICATION-GROUP
32 FROM SNMPv2-CONF;
33
34 testSmiV1 NOTIFICATION-GROUP
35 NOTIFICATIONS {
36 testStatusChangeNotify,
37 testClassEventNotify,
38 testThresholdBelowNotify
39 }
40 STATUS current
41 DESCRIPTION
42 "A collection of test notifications."
43 ::= { 1 3 }
44
45 END
46 """
47
48 def setUp(self):
49 ast = parserFactory()().parse(self.__class__.__doc__)[0]
50 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
51 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
52 codeobj = compile(pycode, 'test', 'exec')
53
54 mibBuilder = MibBuilder()
55 mibBuilder.loadTexts = True
56
57 self.ctx = {'mibBuilder': mibBuilder}
58
59 exec(codeobj, self.ctx, self.ctx)
60
61 def testSmiV1Symbol(self):
62 self.assertTrue(
63 'testSmiV1' in self.ctx,
64 'symbol not present'
65 )
66
67 def testSmiV1Name(self):
68 self.assertEqual(
69 self.ctx['testSmiV1'].getName(),
70 (1, 3),
71 'bad name'
72 )
73
74 def testSmiV1Description(self):
75 self.assertEqual(
76 self.ctx['testSmiV1'].getDescription(),
77 'A collection of test notifications.',
78 'bad DESCRIPTION'
79 )
80
81 def testSmiV1Class(self):
82 self.assertEqual(
83 self.ctx['testSmiV1'].__class__.__name__,
84 'NotificationGroup',
85 'bad SYNTAX class'
86 )
87
88 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
89
90 if __name__ == '__main__':
91 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 try:
8 import unittest2 as unittest
9
10 except ImportError:
11 import unittest
12
13 from pysmi.parser.smi import parserFactory
14 from pysmi.codegen.pysnmp import PySnmpCodeGen
15 from pysmi.codegen.symtable import SymtableCodeGen
16 from pysnmp.smi.builder import MibBuilder
17
18
19 class TrapTypeTestCase(unittest.TestCase):
20 """
21 TEST-MIB DEFINITIONS ::= BEGIN
22 IMPORTS
23 TRAP-TYPE
24 FROM RFC-1215
25
26 OBJECT-TYPE
27 FROM RFC1155-SMI;
28
29 testId OBJECT IDENTIFIER ::= { 1 3 }
30
31 testObject OBJECT-TYPE
32 SYNTAX INTEGER
33 MAX-ACCESS accessible-for-notify
34 STATUS current
35 DESCRIPTION "Test object"
36 ::= { 1 3 }
37
38 testTrap TRAP-TYPE
39 ENTERPRISE testId
40 VARIABLES { testObject }
41 DESCRIPTION
42 "Test trap"
43 ::= 1
44
45 END
46 """
47
48 def setUp(self):
49 ast = parserFactory()().parse(self.__class__.__doc__)[0]
50 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
51 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
52 codeobj = compile(pycode, 'test', 'exec')
53
54 mibBuilder = MibBuilder()
55 mibBuilder.loadTexts = True
56
57 self.ctx = {'mibBuilder': mibBuilder}
58
59 exec(codeobj, self.ctx, self.ctx)
60
61 def testTrapTypeSymbol(self):
62 self.assertTrue(
63 'testTrap' in self.ctx,
64 'symbol not present'
65 )
66
67 def testTrapTypeName(self):
68 self.assertEqual(
69 self.ctx['testTrap'].getName(),
70 (1, 3, 0, 1),
71 'bad name'
72 )
73
74 def testTrapTypeDescription(self):
75 self.assertEqual(
76 self.ctx['testTrap'].getDescription(),
77 'Test trap',
78 'bad DESCRIPTION'
79 )
80
81 def testTrapTypeClass(self):
82 self.assertEqual(
83 self.ctx['testTrap'].__class__.__name__,
84 'NotificationType',
85 'bad SYNTAX class'
86 )
87
88 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
89
90 if __name__ == '__main__':
91 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 try:
8 import unittest2 as unittest
9
10 except ImportError:
11 import unittest
12
13 from pysmi.parser.smi import parserFactory
14 from pysmi.parser.dialect import smiV1Relaxed
15 from pysmi.codegen.pysnmp import PySnmpCodeGen
16 from pysmi.codegen.symtable import SymtableCodeGen
17 from pysnmp.smi.builder import MibBuilder
18
19
20 class TypeDeclarationTestCase(unittest.TestCase):
21 """
22 TEST-MIB DEFINITIONS ::= BEGIN
23 IMPORTS
24
25 NetworkAddress,
26 IpAddress,
27 Counter,
28 Gauge,
29 TimeTicks,
30 Opaque
31 FROM RFC1155-SMI;
32
33 -- simple types
34 TestTypeInteger ::= INTEGER
35 TestTypeOctetString ::= OCTET STRING
36 TestTypeObjectIdentifier ::= OBJECT IDENTIFIER
37
38 -- application types
39 TestTypeNetworkAddress::= NetworkAddress
40 TestTypeIpAddress ::= IpAddress
41 TestTypeCounter ::= Counter
42 TestTypeGauge ::= Gauge
43 TestTypeTimeTicks ::= TimeTicks
44 TestTypeOpaque ::= Opaque
45
46 END
47 """
48
49 def setUp(self):
50 ast = parserFactory(**smiV1Relaxed)().parse(self.__class__.__doc__)[0]
51 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
52 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
53 codeobj = compile(pycode, 'test', 'exec')
54
55 mibBuilder = MibBuilder()
56 mibBuilder.loadTexts = True
57
58 self.ctx = {'mibBuilder': mibBuilder}
59
60 exec(codeobj, self.ctx, self.ctx)
61
62 def protoTestSymbol(self, symbol, klass):
63 self.assertTrue(
64 symbol in self.ctx, 'symbol %s not present' % symbol
65 )
66
67 def protoTestClass(self, symbol, klass):
68 self.assertEqual(
69 self.ctx[symbol].__bases__[0].__name__, klass,
70 'expected class %s, got %s at %s' % (klass, self.ctx[symbol].__bases__[0].__name__, symbol)
71 )
72
73
74 # populate test case class with per-type methods
75
76 typesMap = (
77 ('TestTypeInteger', 'Integer32'),
78 ('TestTypeOctetString', 'OctetString'),
79 ('TestTypeObjectIdentifier', 'ObjectIdentifier'),
80 ('TestTypeNetworkAddress', 'IpAddress'),
81 ('TestTypeIpAddress', 'IpAddress'),
82 ('TestTypeCounter', 'Counter32'),
83 ('TestTypeGauge', 'Gauge32'),
84 ('TestTypeTimeTicks', 'TimeTicks'),
85 ('TestTypeOpaque', 'Opaque')
86 )
87
88
89 def decor(func, symbol, klass):
90 def inner(self):
91 func(self, symbol, klass)
92
93 return inner
94
95
96 for s, k in typesMap:
97 setattr(TypeDeclarationTestCase, 'testTypeDeclaration' + k + 'SymbolTestCase',
98 decor(TypeDeclarationTestCase.protoTestSymbol, s, k))
99 setattr(TypeDeclarationTestCase, 'testTypeDeclaration' + k + 'ClassTestCase',
100 decor(TypeDeclarationTestCase.protoTestClass, s, k))
101
102 # XXX constraints flavor not checked
103
104 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
105
106 if __name__ == '__main__':
107 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 try:
8 import unittest2 as unittest
9
10 except ImportError:
11 import unittest
12
13 from pysmi.parser.smi import parserFactory
14 from pysmi.codegen.pysnmp import PySnmpCodeGen
15 from pysmi.codegen.symtable import SymtableCodeGen
16 from pysnmp.smi.builder import MibBuilder
17
18
19 class TypeDeclarationTestCase(unittest.TestCase):
20 """
21 TEST-MIB DEFINITIONS ::= BEGIN
22 IMPORTS
23
24 IpAddress,
25 Counter32,
26 Gauge32,
27 TimeTicks,
28 Opaque,
29 Integer32,
30 Unsigned32,
31 Counter64
32 FROM SNMPv2-SMI
33
34 TEXTUAL-CONVENTION
35 FROM SNMPv2-TC;
36
37 -- simple types
38 TestTypeInteger ::= INTEGER
39 TestTypeOctetString ::= OCTET STRING
40 TestTypeObjectIdentifier ::= OBJECT IDENTIFIER
41
42 -- application types
43 TestTypeIpAddress ::= IpAddress
44 TestTypeInteger32 ::= Integer32
45 TestTypeCounter32 ::= Counter32
46 TestTypeGauge32 ::= Gauge32
47 TestTypeTimeTicks ::= TimeTicks
48 TestTypeOpaque ::= Opaque
49 TestTypeCounter64 ::= Counter64
50 TestTypeUnsigned32 ::= Unsigned32
51
52 -- constrained subtypes
53
54 TestTypeEnum ::= INTEGER {
55 noResponse(-1),
56 noError(0),
57 tooBig(1)
58 }
59 TestTypeSizeRangeConstraint ::= OCTET STRING (SIZE (0..255))
60 TestTypeSizeConstraint ::= OCTET STRING (SIZE (8 | 11))
61 TestTypeRangeConstraint ::= INTEGER (0..2)
62 TestTypeSingleValueConstraint ::= INTEGER (0|2|4)
63
64 TestTypeBits ::= BITS {
65 sunday(0),
66 monday(1),
67 tuesday(2),
68 wednesday(3),
69 thursday(4),
70 friday(5),
71 saturday(6)
72 }
73
74
75 TestTextualConvention ::= TEXTUAL-CONVENTION
76 DISPLAY-HINT "1x:"
77 STATUS current
78 DESCRIPTION
79 "Test TC"
80 REFERENCE
81 "Test reference"
82 SYNTAX OCTET STRING
83
84 END
85 """
86
87 def setUp(self):
88 ast = parserFactory()().parse(self.__class__.__doc__)[0]
89 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
90 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
91 codeobj = compile(pycode, 'test', 'exec')
92
93 mibBuilder = MibBuilder()
94 mibBuilder.loadTexts = True
95
96 self.ctx = {'mibBuilder': mibBuilder}
97
98 exec(codeobj, self.ctx, self.ctx)
99
100 def protoTestSymbol(self, symbol, klass):
101 self.assertTrue(
102 symbol in self.ctx, 'symbol %s not present' % symbol
103 )
104
105 def protoTestClass(self, symbol, klass):
106 self.assertEqual(
107 self.ctx[symbol].__bases__[0].__name__, klass,
108 'expected class %s, got %s at %s' % (klass, self.ctx[symbol].__bases__[0].__name__, symbol)
109 )
110
111 def TestTextualConventionSymbol(self):
112 self.assertTrue(
113 'TestTextualConvention' in self.ctx,
114 'symbol not present'
115 )
116
117 def TestTextualConventionDisplayHint(self):
118 self.assertEqual(
119 self.ctx['TestTextualConvention'].getDisplayHint(),
120 '1x:',
121 'bad DISPLAY-HINT'
122 )
123
124 def TestTextualConventionStatus(self):
125 self.assertEqual(
126 self.ctx['TestTextualConvention'].getStatus(),
127 'current',
128 'bad STATUS'
129 )
130
131 def TestTextualConventionDescription(self):
132 self.assertEqual(
133 self.ctx['TestTextualConvention'].getDescription(),
134 'Test TC',
135 'bad DESCRIPTION'
136 )
137
138 def TestTextualConventionReference(self):
139 self.assertEqual(
140 self.ctx['TestTextualConvention'].getReference(),
141 'Test reference',
142 'bad REFERENCE'
143 )
144
145 def TestTextualConventionClass(self):
146 self.assertEqual(
147 self.ctx['TestTextualConvention'].__class__.__name__,
148 'TextualConvention',
149 'bad SYNTAX class'
150 )
151
152
153 # populate test case class with per-type methods
154
155 typesMap = (
156 # TODO: Integer/Integer32?
157 ('TestTypeInteger', 'Integer32'),
158 ('TestTypeOctetString', 'OctetString'),
159 ('TestTypeObjectIdentifier', 'ObjectIdentifier'),
160 ('TestTypeIpAddress', 'IpAddress'),
161 ('TestTypeInteger32', 'Integer32'),
162 ('TestTypeCounter32', 'Counter32'),
163 ('TestTypeGauge32', 'Gauge32'),
164 ('TestTypeTimeTicks', 'TimeTicks'),
165 ('TestTypeOpaque', 'Opaque'),
166 ('TestTypeCounter64', 'Counter64'),
167 ('TestTypeUnsigned32', 'Unsigned32'),
168 ('TestTypeTestTypeEnum', 'Integer32'),
169 ('TestTypeSizeRangeConstraint', 'OctetString'),
170 ('TestTypeSizeConstraint', 'OctetString'),
171 ('TestTypeRangeConstraint', 'Integer32'),
172 ('TestTypeSingleValueConstraint', 'Integer32')
173 )
174
175
176 def decor(func, symbol, klass):
177 def inner(self):
178 func(self, symbol, klass)
179
180 return inner
181
182
183 for s, k in typesMap:
184 setattr(TypeDeclarationTestCase, 'testTypeDeclaration' + k + 'SymbolTestCase',
185 decor(TypeDeclarationTestCase.protoTestSymbol, s, k))
186 setattr(TypeDeclarationTestCase, 'testTypeDeclaration' + k + 'ClassTestCase',
187 decor(TypeDeclarationTestCase.protoTestClass, s, k))
188
189 # XXX constraints flavor not checked
190
191 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
192
193 if __name__ == '__main__':
194 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 try:
8 import unittest2 as unittest
9
10 except ImportError:
11 import unittest
12
13 from pysmi.parser.smi import parserFactory
14 from pysmi.codegen.pysnmp import PySnmpCodeGen
15 from pysmi.codegen.symtable import SymtableCodeGen
16 from pysnmp.smi.builder import MibBuilder
17
18
19 class ValueDeclarationTestCase(unittest.TestCase):
20 """
21 TEST-MIB DEFINITIONS ::= BEGIN
22 IMPORTS
23
24 OBJECT-TYPE
25 FROM SNMPv2-SMI;
26
27 -- simple values
28
29 testValue1 OBJECT IDENTIFIER ::= { 1 }
30 testValue2 OBJECT IDENTIFIER ::= { testValue1 3 }
31 testValue3 OBJECT IDENTIFIER ::= { 1 3 6 1 2 }
32
33 -- testValue01 INTEGER ::= 123
34 -- testValue02 INTEGER ::= -123
35 -- testValue04 OCTET STRING ::= h'test string'
36 -- testValue05 INTEGER ::= testValue01
37 -- testValue06 OCTET STRING ::= "test string"
38 -- testValue07 OCTET STRING ::= b'010101'
39
40 -- application syntax
41
42 -- testValue03 Integer32 ::= 123
43 -- testValue03 Counter32 ::= 123
44 -- testValue03 Gauge32 ::= 123
45 -- testValue03 Unsigned32 ::= 123
46 -- testValue03 TimeTicks ::= 123
47 -- testValue03 Opaque ::= "0123"
48 -- testValue03 Counter64 ::= 123456789123456789
49 -- testValue03 IpAddress ::= "127.0.0.1"
50
51 END
52 """
53
54 def setUp(self):
55 ast = parserFactory()().parse(self.__class__.__doc__)[0]
56 mibInfo, symtable = SymtableCodeGen().genCode(ast, {}, genTexts=True)
57 self.mibInfo, pycode = PySnmpCodeGen().genCode(ast, {mibInfo.name: symtable}, genTexts=True)
58 codeobj = compile(pycode, 'test', 'exec')
59
60 mibBuilder = MibBuilder()
61 mibBuilder.loadTexts = True
62
63 self.ctx = {'mibBuilder': mibBuilder}
64
65 exec(codeobj, self.ctx, self.ctx)
66
67 def testValueDeclarationSymbol(self):
68 self.assertTrue(
69 'testValue1' in self.ctx and
70 'testValue2' in self.ctx and
71 'testValue3' in self.ctx,
72 'symbol not present'
73 )
74
75 def testValueDeclarationName1(self):
76 self.assertEqual(
77 self.ctx['testValue1'].getName(),
78 (1,),
79 'bad value'
80 )
81
82 def testValueDeclarationName2(self):
83 self.assertEqual(
84 self.ctx['testValue2'].getName(),
85 (1, 3),
86 'bad value'
87 )
88
89 def testValueDeclarationName3(self):
90 self.assertEqual(
91 self.ctx['testValue3'].getName(),
92 (1, 3, 6, 1, 2),
93 'bad value'
94 )
95
96 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
97
98 if __name__ == '__main__':
99 unittest.TextTestRunner(verbosity=2).run(suite)
0 #
1 # This file is part of pysmi software.
2 #
3 # Copyright (c) 2015-2017, Ilya Etingof <etingof@gmail.com>
4 # License: http://pysmi.sf.net/license.html
5 #
6 import sys
7 import os
8 import tempfile
9
10 try:
11 import unittest2 as unittest
12
13 except ImportError:
14 import unittest
15
16 try:
17 import StringIO
18
19 except ImportError:
20 from io import StringIO
21
22 from pysmi.reader.zipreader import ZipReader
23
24
25 class ZipReaderTestCase(unittest.TestCase):
26
27 zipArchive = [
28 80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 8, 135, 53, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29 0, 0, 5, 0, 28, 0, 116, 101, 115, 116, 47, 85, 84, 9, 0, 3, 16, 211, 195, 89,
30 25, 211, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0, 80,
31 75, 3, 4, 10, 0, 0, 0, 0, 0, 230, 134, 53, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
32 0, 0, 12, 0, 28, 0, 116, 101, 115, 116, 47, 115, 117, 98, 100, 105, 114, 47,
33 85, 84, 9, 0, 3, 207, 210, 195, 89, 3, 211, 195, 89, 117, 120, 11, 0, 1, 4,
34 140, 102, 0, 0, 4, 140, 102, 0, 0, 80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 230, 134,
35 53, 75, 102, 214, 67, 99, 2, 0, 0, 0, 2, 0, 0, 0, 17, 0, 28, 0, 116, 101, 115,
36 116, 47, 115, 117, 98, 100, 105, 114, 47, 116, 101, 115, 116, 65, 85, 84, 9,
37 0, 3, 207, 210, 195, 89, 3, 211, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0,
38 0, 4, 140, 102, 0, 0, 66, 10, 80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 2, 135, 53, 75,
39 162, 170, 2, 92, 138, 7, 0, 0, 138, 7, 0, 0, 13, 0, 28, 0, 116, 101, 115, 116,
40 47, 116, 101, 115, 116, 46, 122, 105, 112, 85, 84, 9, 0, 3, 3, 211, 195, 89,
41 3, 211, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0, 80,
42 75, 3, 4, 10, 0, 0, 0, 0, 0, 253, 134, 53, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
43 0, 5, 0, 28, 0, 116, 101, 115, 116, 47, 85, 84, 9, 0, 3, 253, 210, 195, 89, 3,
44 211, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0, 80, 75,
45 3, 4, 10, 0, 0, 0, 0, 0, 230, 134, 53, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
46 12, 0, 28, 0, 116, 101, 115, 116, 47, 115, 117, 98, 100, 105, 114, 47, 85, 84,
47 9, 0, 3, 207, 210, 195, 89, 3, 211, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102,
48 0, 0, 4, 140, 102, 0, 0, 80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 130, 131, 53, 75,
49 227, 250, 30, 37, 12, 0, 0, 0, 12, 0, 0, 0, 21, 0, 28, 0, 116, 101, 115, 116,
50 47, 115, 117, 98, 100, 105, 114, 47, 116, 101, 115, 116, 65, 46, 116, 120,
51 116, 85, 84, 9, 0, 3, 116, 204, 195, 89, 134, 204, 195, 89, 117, 120, 11, 0,
52 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0, 115, 117, 98, 100, 105, 114, 116,
53 101, 115, 116, 65, 10, 80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 109, 131, 53, 75, 237,
54 78, 102, 83, 6, 0, 0, 0, 6, 0, 0, 0, 14, 0, 28, 0, 116, 101, 115, 116, 47,
55 116, 101, 115, 116, 65, 46, 116, 120, 116, 85, 84, 9, 0, 3, 78, 204, 195, 89,
56 134, 204, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0,
57 116, 101, 115, 116, 65, 10, 80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 144, 131, 53,
58 75, 204, 176, 61, 249, 144, 2, 0, 0, 144, 2, 0, 0, 13, 0, 28, 0, 116, 101,
59 115, 116, 47, 116, 101, 115, 116, 46, 122, 105, 112, 85, 84, 9, 0, 3, 143,
60 204, 195, 89, 143, 204, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4,
61 140, 102, 0, 0, 80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 117, 131, 53, 75, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 28, 0, 116, 101, 115, 116, 47, 85, 84, 9, 0,
63 3, 94, 204, 195, 89, 98, 204, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0,
64 4, 140, 102, 0, 0, 80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 130, 131, 53, 75, 0, 0,
65 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 28, 0, 116, 101, 115, 116, 47, 115, 117,
66 98, 100, 105, 114, 47, 85, 84, 9, 0, 3, 116, 204, 195, 89, 134, 204, 195,
67 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0, 80, 75, 3, 4,
68 10, 0, 0, 0, 0, 0, 130, 131, 53, 75, 227, 250, 30, 37, 12, 0, 0, 0, 12, 0, 0,
69 0, 21, 0, 28, 0, 116, 101, 115, 116, 47, 115, 117, 98, 100, 105, 114, 47, 116,
70 101, 115, 116, 65, 46, 116, 120, 116, 85, 84, 9, 0, 3, 116, 204, 195, 89, 116,
71 204, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0, 115,
72 117, 98, 100, 105, 114, 116, 101, 115, 116, 65, 10, 80, 75, 3, 4, 10, 0, 0, 0,
73 0, 0, 109, 131, 53, 75, 237, 78, 102, 83, 6, 0, 0, 0, 6, 0, 0, 0, 14, 0, 28,
74 0, 116, 101, 115, 116, 47, 116, 101, 115, 116, 65, 46, 116, 120, 116, 85, 84,
75 9, 0, 3, 78, 204, 195, 89, 78, 204, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102,
76 0, 0, 4, 140, 102, 0, 0, 116, 101, 115, 116, 65, 10, 80, 75, 1, 2, 30, 3, 10,
77 0, 0, 0, 0, 0, 117, 131, 53, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 24,
78 0, 0, 0, 0, 0, 0, 0, 16, 0, 253, 65, 0, 0, 0, 0, 116, 101, 115, 116, 47, 85,
79 84, 5, 0, 3, 94, 204, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140,
80 102, 0, 0, 80, 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, 130, 131, 53, 75, 0, 0, 0,
81 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 24, 0, 0, 0, 0, 0, 0, 0, 16, 0, 253, 65, 63,
82 0, 0, 0, 116, 101, 115, 116, 47, 115, 117, 98, 100, 105, 114, 47, 85, 84, 5,
83 0, 3, 116, 204, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102,
84 0, 0, 80, 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, 130, 131, 53, 75, 227, 250, 30,
85 37, 12, 0, 0, 0, 12, 0, 0, 0, 21, 0, 24, 0, 0, 0, 0, 0, 1, 0, 0, 0, 180, 129,
86 133, 0, 0, 0, 116, 101, 115, 116, 47, 115, 117, 98, 100, 105, 114, 47, 116,
87 101, 115, 116, 65, 46, 116, 120, 116, 85, 84, 5, 0, 3, 116, 204, 195, 89, 117,
88 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0, 80, 75, 1, 2, 30, 3, 10,
89 0, 0, 0, 0, 0, 109, 131, 53, 75, 237, 78, 102, 83, 6, 0, 0, 0, 6, 0, 0, 0, 14,
90 0, 24, 0, 0, 0, 0, 0, 1, 0, 0, 0, 180, 129, 224, 0, 0, 0, 116, 101, 115, 116,
91 47, 116, 101, 115, 116, 65, 46, 116, 120, 116, 85, 84, 5, 0, 3, 78, 204, 195,
92 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0, 80, 75, 5, 6, 0,
93 0, 0, 0, 4, 0, 4, 0, 76, 1, 0, 0, 46, 1, 0, 0, 0, 0, 80, 75, 3, 4, 10, 0, 0, 0,
94 0, 0, 230, 134, 53, 75, 102, 214, 67, 99, 2, 0, 0, 0, 2, 0, 0, 0, 17, 0, 28, 0,
95 116, 101, 115, 116, 47, 115, 117, 98, 100, 105, 114, 47, 116, 101, 115, 116,
96 65, 85, 84, 9, 0, 3, 207, 210, 195, 89, 207, 210, 195, 89, 117, 120, 11, 0, 1,
97 4, 140, 102, 0, 0, 4, 140, 102, 0, 0, 66, 10, 80, 75, 3, 4, 10, 0, 0, 0, 0,
98 0, 253, 134, 53, 75, 39, 231, 88, 122, 2, 0, 0, 0, 2, 0, 0, 0, 10, 0, 28, 0,
99 116, 101, 115, 116, 47, 116, 101, 115, 116, 67, 85, 84, 9, 0, 3, 253, 210,
100 195, 89, 253, 210, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140,
101 102, 0, 0, 67, 10, 80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 211, 134, 53, 75, 165,
102 133, 110, 72, 2, 0, 0, 0, 2, 0, 0, 0, 10, 0, 28, 0, 116, 101, 115, 116, 47,
103 116, 101, 115, 116, 65, 85, 84, 9, 0, 3, 173, 210, 195, 89, 173, 210, 195, 89,
104 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0, 65, 10, 80, 75, 1,
105 2, 30, 3, 10, 0, 0, 0, 0, 0, 253, 134, 53, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
106 0, 5, 0, 24, 0, 0, 0, 0, 0, 0, 0, 16, 0, 253, 65, 0, 0, 0, 0, 116, 101, 115,
107 116, 47, 85, 84, 5, 0, 3, 253, 210, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102,
108 0, 0, 4, 140, 102, 0, 0, 80, 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, 230, 134, 53,
109 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 24, 0, 0, 0, 0, 0, 0, 0, 16,
110 0, 253, 65, 63, 0, 0, 0, 116, 101, 115, 116, 47, 115, 117, 98, 100, 105, 114,
111 47, 85, 84, 5, 0, 3, 207, 210, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0,
112 0, 4, 140, 102, 0, 0, 80, 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, 130, 131, 53,
113 75, 227, 250, 30, 37, 12, 0, 0, 0, 12, 0, 0, 0, 21, 0, 24, 0, 0, 0, 0, 0, 1,
114 0, 0, 0, 180, 129, 133, 0, 0, 0, 116, 101, 115, 116, 47, 115, 117, 98, 100,
115 105, 114, 47, 116, 101, 115, 116, 65, 46, 116, 120, 116, 85, 84, 5, 0, 3, 116,
116 204, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0, 80,
117 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, 109, 131, 53, 75, 237, 78, 102, 83, 6, 0,
118 0, 0, 6, 0, 0, 0, 14, 0, 24, 0, 0, 0, 0, 0, 1, 0, 0, 0, 180, 129, 224, 0, 0,
119 0, 116, 101, 115, 116, 47, 116, 101, 115, 116, 65, 46, 116, 120, 116, 85, 84,
120 5, 0, 3, 78, 204, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102,
121 0, 0, 80, 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, 144, 131, 53, 75, 204, 176, 61,
122 249, 144, 2, 0, 0, 144, 2, 0, 0, 13, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180,
123 129, 46, 1, 0, 0, 116, 101, 115, 116, 47, 116, 101, 115, 116, 46, 122, 105,
124 112, 85, 84, 5, 0, 3, 143, 204, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0,
125 0, 4, 140, 102, 0, 0, 80, 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, 230, 134, 53, 75,
126 102, 214, 67, 99, 2, 0, 0, 0, 2, 0, 0, 0, 17, 0, 24, 0, 0, 0, 0, 0, 1, 0, 0,
127 0, 180, 129, 5, 4, 0, 0, 116, 101, 115, 116, 47, 115, 117, 98, 100, 105, 114,
128 47, 116, 101, 115, 116, 65, 85, 84, 5, 0, 3, 207, 210, 195, 89, 117, 120, 11,
129 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0, 80, 75, 1, 2, 30, 3, 10, 0, 0, 0,
130 0, 0, 253, 134, 53, 75, 39, 231, 88, 122, 2, 0, 0, 0, 2, 0, 0, 0, 10, 0, 24,
131 0, 0, 0, 0, 0, 1, 0, 0, 0, 180, 129, 82, 4, 0, 0, 116, 101, 115, 116, 47, 116,
132 101, 115, 116, 67, 85, 84, 5, 0, 3, 253, 210, 195, 89, 117, 120, 11, 0, 1, 4,
133 140, 102, 0, 0, 4, 140, 102, 0, 0, 80, 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0,
134 211, 134, 53, 75, 165, 133, 110, 72, 2, 0, 0, 0, 2, 0, 0, 0, 10, 0, 24, 0, 0,
135 0, 0, 0, 1, 0, 0, 0, 180, 129, 152, 4, 0, 0, 116, 101, 115, 116, 47, 116, 101,
136 115, 116, 65, 85, 84, 5, 0, 3, 173, 210, 195, 89, 117, 120, 11, 0, 1, 4, 140,
137 102, 0, 0, 4, 140, 102, 0, 0, 80, 75, 5, 6, 0, 0, 0, 0, 8, 0, 8, 0, 150, 2,
138 0, 0, 222, 4, 0, 0, 0, 0, 80, 75, 3, 4, 10, 0, 0, 0, 0, 0, 211, 134, 53, 75,
139 165, 133, 110, 72, 2, 0, 0, 0, 2, 0, 0, 0, 10, 0, 28, 0, 116, 101, 115, 116,
140 47, 116, 101, 115, 116, 65, 85, 84, 9, 0, 3, 173, 210, 195, 89, 3, 211, 195,
141 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0, 65, 10, 80, 75,
142 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, 8, 135, 53, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
143 0, 0, 5, 0, 24, 0, 0, 0, 0, 0, 0, 0, 16, 0, 253, 65, 0, 0, 0, 0, 116, 101,
144 115, 116, 47, 85, 84, 5, 0, 3, 16, 211, 195, 89, 117, 120, 11, 0, 1, 4, 140,
145 102, 0, 0, 4, 140, 102, 0, 0, 80, 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, 230,
146 134, 53, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 24, 0, 0, 0, 0, 0, 0,
147 0, 16, 0, 253, 65, 63, 0, 0, 0, 116, 101, 115, 116, 47, 115, 117, 98, 100,
148 105, 114, 47, 85, 84, 5, 0, 3, 207, 210, 195, 89, 117, 120, 11, 0, 1, 4, 140,
149 102, 0, 0, 4, 140, 102, 0, 0, 80, 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, 230,
150 134, 53, 75, 102, 214, 67, 99, 2, 0, 0, 0, 2, 0, 0, 0, 17, 0, 24, 0, 0, 0, 0,
151 0, 1, 0, 0, 0, 180, 129, 133, 0, 0, 0, 116, 101, 115, 116, 47, 115, 117, 98,
152 100, 105, 114, 47, 116, 101, 115, 116, 65, 85, 84, 5, 0, 3, 207, 210, 195,
153 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0, 80, 75, 1, 2,
154 30, 3, 10, 0, 0, 0, 0, 0, 2, 135, 53, 75, 162, 170, 2, 92, 138, 7, 0, 0, 138,
155 7, 0, 0, 13, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180, 129, 210, 0, 0, 0, 116,
156 101, 115, 116, 47, 116, 101, 115, 116, 46, 122, 105, 112, 85, 84, 5, 0, 3,
157 3, 211, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0,
158 80, 75, 1, 2, 30, 3, 10, 0, 0, 0, 0, 0, 211, 134, 53, 75, 165, 133, 110, 72,
159 2, 0, 0, 0, 2, 0, 0, 0, 10, 0, 24, 0, 0, 0, 0, 0, 1, 0, 0, 0, 180, 129, 163,
160 8, 0, 0, 116, 101, 115, 116, 47, 116, 101, 115, 116, 65, 85, 84, 5, 0, 3,
161 173, 210, 195, 89, 117, 120, 11, 0, 1, 4, 140, 102, 0, 0, 4, 140, 102, 0, 0,
162 80, 75, 5, 6, 0, 0, 0, 0, 5, 0, 5, 0, 151, 1, 0, 0, 233, 8, 0, 0, 0, 0]
163
164
165 if sys.version_info[0] < 3:
166 zipContents = ''.join([chr(x) for x in zipArchive])
167 else:
168 zipContents = bytes(zipArchive)
169
170 def testGetDataFromFile(self):
171
172 try:
173 fd, filename = tempfile.mkstemp()
174 os.write(fd, self.zipContents)
175 os.close(fd)
176
177 zipReader = ZipReader(filename)
178
179 mibinfo, data = zipReader.getData('testA')
180
181 assert data == 'A\n'
182
183 finally:
184 try:
185 os.remove(filename)
186
187 except:
188 pass
189
190 def testGetInnerZipData(self):
191
192 try:
193 fd, filename = tempfile.mkstemp()
194 os.write(fd, self.zipContents)
195 os.close(fd)
196
197 zipReader = ZipReader(filename)
198
199 mibinfo, data = zipReader.getData('testC')
200
201 assert data == 'C\n'
202
203 finally:
204 try:
205 os.remove(filename)
206
207 except:
208 pass
209
210
211 suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
212
213 if __name__ == '__main__':
214 unittest.TextTestRunner(verbosity=2).run(suite)