import pysmi_0.2.2.orig.tar.gz
Vincent Bernat
6 years ago
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 | * 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 | # 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 | # -*- 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 | 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 |
0 | pysmi |
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) |