Import upstream version 0.8.4+git20210803.1.b0ffe0d
Debian Janitor
2 years ago
30 | 30 | all: clean check pep8 flake8 tests |
31 | 31 | |
32 | 32 | pep8: |
33 | -pep8 -r --ignore=E402,E731,E501,E221,W291,W391,E302,E251,E203,W293,E231,E303,E201,E225,E261,E241 pyeapi/ test/ | |
33 | pycodestyle -r --ignore=E402,E731,E501,E221,W291,W391,E302,E251,E203,W293,E231,E303,E201,E225,E261,E241 pyeapi/ test/ | |
34 | 34 | |
35 | 35 | pyflakes: |
36 | 36 | pyflakes pyeapi/ test/ |
0 | Metadata-Version: 1.1 | |
0 | Metadata-Version: 2.1 | |
1 | 1 | Name: pyeapi |
2 | Version: 0.8.1 | |
2 | Version: 0.8.4rc0 | |
3 | 3 | Summary: Python Client for eAPI |
4 | 4 | Home-page: https://github.com/arista-eosplus/pyeapi |
5 | 5 | Author: Arista EOS+ CS |
6 | 6 | Author-email: eosplus-dev@arista.com |
7 | 7 | License: BSD-3 |
8 | Description: The Python Client for eAPI | |
9 | ========================== | |
10 | ||
11 | The Python Client for eAPI (pyeapi) is a native Python library wrapper around | |
12 | Arista EOS eAPI. It provides a set of Python language bindings for configuring | |
13 | Arista EOS nodes. | |
14 | ||
15 | The Python library can be used to communicate with EOS either locally | |
16 | (on-box) or remotely (off-box). It uses a standard INI-style configuration file | |
17 | to specify one or more nodes and connection profiles. | |
18 | ||
19 | The pyeapi library also provides an API layer for building native Python | |
20 | objects to interact with the destination nodes. The API layer is a convenient | |
21 | implementation for working with the EOS configuration and is extensible for | |
22 | developing custom implementations. | |
23 | ||
24 | This library is freely provided to the open source community for building | |
25 | robust applications using Arista EOS. Support is provided as best effort | |
26 | through Github issues. | |
27 | ||
28 | 8 | Keywords: networking arista eos eapi |
29 | 9 | Platform: UNKNOWN |
30 | 10 | Classifier: Development Status :: 4 - Beta |
34 | 14 | Classifier: Programming Language :: Python :: 2 |
35 | 15 | Classifier: Programming Language :: Python :: 2.7 |
36 | 16 | Classifier: Programming Language :: Python :: 3 |
37 | Classifier: Programming Language :: Python :: 3.4 | |
17 | Classifier: Programming Language :: Python :: 3.8 | |
18 | Provides-Extra: dev | |
19 | Provides-Extra: test | |
20 | License-File: LICENSE | |
21 | ||
22 | The Python Client for eAPI | |
23 | ========================== | |
24 | ||
25 | The Python Client for eAPI (pyeapi) is a native Python library wrapper around | |
26 | Arista EOS eAPI. It provides a set of Python language bindings for configuring | |
27 | Arista EOS nodes. | |
28 | ||
29 | The Python library can be used to communicate with EOS either locally | |
30 | (on-box) or remotely (off-box). It uses a standard INI-style configuration file | |
31 | to specify one or more nodes and connection profiles. | |
32 | ||
33 | The pyeapi library also provides an API layer for building native Python | |
34 | objects to interact with the destination nodes. The API layer is a convenient | |
35 | implementation for working with the EOS configuration and is extensible for | |
36 | developing custom implementations. | |
37 | ||
38 | This library is freely provided to the open source community for building | |
39 | robust applications using Arista EOS. Support is provided as best effort | |
40 | through Github issues. | |
41 | ||
42 |
0 | netaddr | |
0 | -r requirements.txt | |
1 | 1 | mock |
2 | 2 | coveralls |
3 | 3 | twine |
4 | 4 | check-manifest |
5 | pep8 | |
5 | pycodestyle | |
6 | 6 | pyflakes |
7 | 7 | coverage |
8 | 8 | sphinx |
10 | 10 | flake8 |
11 | 11 | flake8-print |
12 | 12 | flake8-debugger |
13 | pep8-naming | |
14 | ||
15 |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Api | |
4 | === | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 2 | |
8 | ||
9 | abstract | |
10 | acl | |
11 | bgp | |
12 | interfaces | |
13 | ipinterfaces | |
14 | mlag | |
15 | ntp | |
16 | ospf | |
17 | routemaps | |
18 | spanningtree | |
19 | staticroute | |
20 | stp | |
21 | switchports | |
22 | system | |
23 | users | |
24 | varp | |
25 | vlans | |
26 | vrfs | |
27 | vrrp |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Abstract | |
4 | ======== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.abstract | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Acl | |
4 | === | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.acl | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Bgp | |
4 | === | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.bgp | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Interfaces | |
4 | ========== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.interfaces | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Ipinterfaces | |
4 | ============ | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.ipinterfaces | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Mlag | |
4 | ==== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.mlag | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Ntp | |
4 | === | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.ntp | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Ospf | |
4 | ==== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.ospf | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Routemaps | |
4 | ========= | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.routemaps | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Spanningtree | |
4 | ============ | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.spanningtree | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Staticroute | |
4 | =========== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.staticroute | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Stp | |
4 | === | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.stp | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Switchports | |
4 | =========== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.switchports | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | System | |
4 | ====== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.system | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Users | |
4 | ===== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.users | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Varp | |
4 | ==== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.varp | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Vlans | |
4 | ===== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.vlans | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Vrfs | |
4 | ==== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.vrfs | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Vrrp | |
4 | ==== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.api.vrrp | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Client | |
4 | ====== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 2 | |
8 | ||
9 | client | |
10 | eapilib | |
11 | utils |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Client | |
4 | ====== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.client | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Eapilib | |
4 | ======= | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.eapilib | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
0 | .. This file has been autogenerated by generate_modules.py | |
1 | ||
2 | ||
3 | Utils | |
4 | ===== | |
5 | ||
6 | .. toctree:: | |
7 | :maxdepth: 1 | |
8 | ||
9 | .. automodule:: pyeapi.utils | |
10 | :members: | |
11 | :undoc-members: | |
12 | :show-inheritance: |
38 | 38 | - http_local (available in EOS 4.14.5 or later) |
39 | 39 | - http |
40 | 40 | - https |
41 | - https_certs | |
41 | 42 | |
42 | 43 | :port: Configures the port to use for the eAPI connection. A default |
43 | 44 | port is used if this parameter is absent, based on the transport setting |
44 | 45 | using the following values: |
45 | 46 | |
46 | 47 | - transport: http, default port: 80 |
47 | - transport: https, deafult port: 443 | |
48 | - transport: https, default port: 443 | |
49 | - transport: https_certs, default port: 443 | |
48 | 50 | - transport: http_local, default port: 8080 |
49 | 51 | - transport: socket, default port: n/a |
50 | 52 | |
61 | 63 | =========== ================== =============== ======================== |
62 | 64 | http Yes On/Off-switch Yes |
63 | 65 | https Yes On/Off-switch Yes |
66 | https_certs Yes On/Off-switch Yes (Auth done via certs, not un/pw) | |
64 | 67 | http_local Yes On-switch only No |
65 | 68 | socket No On-switch only No |
66 | 69 | =========== ================== =============== ======================== |
168 | 171 | username: admin |
169 | 172 | password: admin |
170 | 173 | |
174 | .. Note:: avoid using ``localhost`` name in the connection description (i.e.: ``[connection:localhost]``). | |
175 | The name ``localhost`` is reserved solely for ``on-box`` connection method and it won't work when | |
176 | connecting ``off-box`` | |
177 | ||
178 | ||
179 | Using HTTPS with Certificates | |
180 | ============================= | |
181 | ||
182 | The https_certs transport options allows users to do authentication for pyeapi | |
183 | with certificates instead of username/password. This requires functional | |
184 | certificate chains are setup, copied to the proper location and trusted by | |
185 | EOS beforehand. The ca_file parameter is optional. If provided the switches | |
186 | certificate will also be validated against the provided CA cert. If no CA cert | |
187 | is provided then no server side validation will be done. | |
188 | ||
189 | .. code-block:: console | |
190 | ||
191 | [connection:veos01] | |
192 | transport: https_certs | |
193 | cert_file: /path/to/certificate/file | |
194 | key_file: /path/to/private/key/file | |
195 | ca_file: /path/to/CA/certificate/file | |
196 | ||
197 | [connection:veos02] | |
198 | transport: https_certs | |
199 | cert_file: /path/to/certificate/file | |
200 | key_file: /path/to/private/key/file | |
201 | ||
171 | 202 | ******************* |
172 | 203 | The DEFAULT Section |
173 | 204 | ******************* |
0 | Using Config Sessions via Python Client for eAPI | |
1 | ======================================================= | |
2 | ||
3 | Config Sessions can be used via Pyeapi. Config sessions allow a block of config | |
4 | to be added as one operation instead of as individual lines. Configurations applied | |
5 | in this manner allow the user to abort all the config being applied if an error occurs. | |
6 | ||
7 | Using Config Sessions: | |
8 | ||
9 | .. code-block:: python | |
10 | ||
11 | import pyeapi | |
12 | node = pyeapi.connect_to('veos01') | |
13 | vlans = node.api('vlans') | |
14 | ||
15 | node.configure_session() | |
16 | ||
17 | node.diff() # Sends "configure session 9c27d0e8-afef-4afd-95ae-3e3200bb7a3e" and "show session-config diff" | |
18 | ||
19 | """ | |
20 | => | |
21 | --- system:/running-config | |
22 | +++ session:/9c27d0e8-afef-4afd-95ae-3e3200bb7a3e-session-config | |
23 | @@ -32,7 +32,7 @@ | |
24 | ! | |
25 | clock timezone Asia/Tokyo | |
26 | ! | |
27 | -vlan 1000,3001-3006 | |
28 | +vlan 100,1000,3001-3006 | |
29 | ! | |
30 | interface Port-Channel1 | |
31 | switchport trunk allowed vlan 3001-3003 | |
32 | """ | |
33 | ||
34 | node.abort() # Sends "configure session 9c27d0e8-afef-4afd-95ae-3e3200bb7a3e" and "abort" | |
35 | # or | |
36 | node.commit() # Sends "configure session 9c27d0e8-afef-4afd-95ae-3e3200bb7a3e" and "commit" | |
37 | ||
38 | Config Session with invalid config line: | |
39 | ||
40 | .. code-block:: python | |
41 | ||
42 | node = pyeapi.connect_to('veos01') | |
43 | interfaces = node.api('interfaces') | |
44 | node.configure_session() | |
45 | ||
46 | if not (interfaces.configure(['interface Eth6', 'no switchport', 'ip address 172.16.0.1/30']) and \ | |
47 | interfaces.configure(['interface Eth7', 'no switchport', 'ip address 172.16.0.1/30'])): | |
48 | node.abort() # This aborts everything!! | |
49 | ||
50 | For more detailed information about using Configure Sessions in EOS, reference the user | |
51 | manual for the version of EOS running on your switch. |
18 | 18 | |
19 | 19 | - HTTP |
20 | 20 | - HTTPS |
21 | - HTTPS Certificates | |
21 | 22 | - HTTP Local |
22 | 23 | - Unix Socket |
23 | 24 | |
63 | 64 | |
64 | 65 | # send one or more commands to the node |
65 | 66 | node.enable('show hostname') |
66 | [{'command': 'show hostname', 'result': {u'hostname': u'veos01', u'fqdn': | |
67 | u'veos01.arista.com'}, 'encoding': 'json'}] | |
67 | [{'command': 'show hostname', | |
68 | 'encoding': 'json', | |
69 | 'result': {u'hostname': u'veos01', | |
70 | u'fqdn': u'veos01.arista.com'}}] | |
71 | ||
72 | # Request a specific revision of a command that has been updated | |
73 | node.enable({'cmd': 'show cvx', 'revision': 2}) | |
74 | [{'command': {'cmd': 'show cvx', 'revision': 2}, | |
75 | 'encoding': 'json', | |
76 | 'result': {u'clusterMode': False, | |
77 | u'controllerUUID': u'', | |
78 | u'enabled': False, | |
79 | u'heartbeatInterval': 20.0, | |
80 | u'heartbeatTimeout': 60.0}}] | |
68 | 81 | |
69 | 82 | # use the config method to send configuration commands |
70 | 83 | node.config('hostname veos01') |
0 | Release 0.8.2 | |
1 | ------------- | |
2 | ||
3 | 2018-02-09 | |
4 | ||
5 | New Modules | |
6 | ^^^^^^^^^^^ | |
7 | ||
8 | ||
9 | Enhancements | |
10 | ^^^^^^^^^^^^ | |
11 | ||
12 | * Support eapi command revision syntax (`158 <https://github.com/arista-eosplus/pyeapi/pull/158>`_) [`jerearista <https://github.com/jerearista>`_] | |
13 | Support requests for specific revisions of EOS command output | |
14 | ||
15 | .. code-block:: python | |
16 | ||
17 | >>> node.enable({'cmd': 'show cvx', 'revision': 2}) | |
18 | [{'command': {'cmd': 'show cvx', 'revision': 2}, | |
19 | 'encoding': 'json', | |
20 | 'result': {u'clusterMode': False, | |
21 | u'controllerUUID': u'', | |
22 | u'enabled': False, | |
23 | u'heartbeatInterval': 20.0, | |
24 | u'heartbeatTimeout': 60.0}}] | |
25 | ||
26 | * Add clearer error message for bad user credentials. (`152 <https://github.com/arista-eosplus/pyeapi/pull/152>`_) [`mharista <https://github.com/mharista>`_] | |
27 | .. comment | |
28 | * Reformat EapiConnection send methods exception handling. (`148 <https://github.com/arista-eosplus/pyeapi/pull/148>`_) [`mharista <https://github.com/mharista>`_] | |
29 | .. comment | |
30 | ||
31 | Fixed | |
32 | ^^^^^ | |
33 | ||
34 | * Fix route map getall function to find route maps with a hyphen in the name. (`154 <https://github.com/arista-eosplus/pyeapi/pull/154>`_) [`mharista <https://github.com/mharista>`_] | |
35 | .. comment | |
36 | ||
37 | Known Caveats | |
38 | ^^^^^^^^^^^^^ | |
39 | ||
40 |
0 | Release 0.8.3 | |
1 | ------------- | |
2 | ||
3 | 2020-01-26 | |
4 | ||
5 | New Modules | |
6 | ^^^^^^^^^^^ | |
7 | ||
8 | ||
9 | Enhancements | |
10 | ^^^^^^^^^^^^ | |
11 | ||
12 | * Support eapi command revision syntax (`181 <https://github.com/arista-eosplus/pyeapi/pull/181>`_) | |
13 | ||
14 | * Add ability to use pyeapi with certificates | |
15 | ||
16 | * Added check for 'match' statement to be valid before parsing regex ACL | |
17 | ||
18 | Fixed | |
19 | ^^^^^ | |
20 | ||
21 | Known Caveats | |
22 | ^^^^^^^^^^^^^ | |
23 | ||
24 |
0 | Release 0.8.4 | |
1 | ------------- | |
2 | ||
3 | 2020-11-13 | |
4 | ||
5 | New Modules | |
6 | ^^^^^^^^^^^ | |
7 | ||
8 | ||
9 | Enhancements | |
10 | ^^^^^^^^^^^^ | |
11 | ||
12 | * Add streaming capability for eapi interface | |
13 | ||
14 | * Add Power ppc64le support with CI | |
15 | ||
16 | Fixed | |
17 | ^^^^^ | |
18 | ||
19 | Known Caveats | |
20 | ^^^^^^^^^^^^^ | |
21 | ||
22 |
5 | 5 | :maxdepth: 2 |
6 | 6 | :titlesonly: |
7 | 7 | |
8 | release-notes-0.8.2.rst | |
8 | 9 | release-notes-0.8.1.rst |
9 | 10 | release-notes-0.8.0.rst |
10 | 11 | release-notes-0.7.0.rst |
28 | 28 | # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
29 | 29 | # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | 30 | # |
31 | __version__ = '0.8.1' | |
31 | __version__ = '0.8.4' | |
32 | 32 | __author__ = 'Arista EOS+' |
33 | 33 | |
34 | 34 |
39 | 39 | ultimately derive from BaseEntity which provides some common functions to |
40 | 40 | make building API modules easier. |
41 | 41 | """ |
42 | from collections import Callable, Mapping | |
42 | from collections.abc import Callable, Mapping | |
43 | 43 | |
44 | 44 | from pyeapi.eapilib import CommandError, ConnectionError |
45 | 45 | from pyeapi.utils import make_iterable |
67 | 67 | self.node = node |
68 | 68 | |
69 | 69 | @property |
70 | def version_number(self): | |
71 | return self.node.version_number | |
72 | ||
73 | @property | |
70 | 74 | def config(self): |
71 | 75 | return self.node.running_config |
72 | 76 | |
118 | 122 | try: |
119 | 123 | self.node.config(commands) |
120 | 124 | return True |
121 | except (CommandError, ConnectionError): | |
125 | except (CommandError): | |
122 | 126 | return False |
123 | 127 | |
124 | 128 | def command_builder(self, string, value=None, default=None, disable=None): |
242 | 242 | entries = dict() |
243 | 243 | for item in re.finditer(r'\d+ [p|d].*$', config, re.M): |
244 | 244 | match = self.entry_re.match(item.group(0)) |
245 | entry = dict() | |
246 | entry['action'] = match.group(2) | |
247 | entry['protocol'] = match.group(3) | |
248 | entry['srcaddr'] = match.group(5) or 'any' | |
249 | entry['srclen'] = match.group(6) | |
250 | entry['srcport'] = match.group(7) | |
251 | entry['dstaddr'] = match.group(9) or 'any' | |
252 | entry['dstlen'] = match.group(10) | |
253 | entry['dstport'] = match.group(12) | |
254 | entry['other'] = match.group(13) | |
255 | entries[match.group(1)] = entry | |
245 | if match: | |
246 | entry = dict() | |
247 | entry['action'] = match.group(2) | |
248 | entry['protocol'] = match.group(3) | |
249 | entry['srcaddr'] = match.group(5) or 'any' | |
250 | entry['srclen'] = match.group(6) | |
251 | entry['srcport'] = match.group(7) | |
252 | entry['dstaddr'] = match.group(9) or 'any' | |
253 | entry['dstlen'] = match.group(10) | |
254 | entry['dstport'] = match.group(12) | |
255 | entry['other'] = match.group(13) | |
256 | entries[match.group(1)] = entry | |
256 | 257 | return dict(entries=entries) |
257 | 258 | |
258 | 259 | def create(self, name): |
204 | 204 | return collection |
205 | 205 | |
206 | 206 | def _parse_peer_group(self, config, name): |
207 | regexp = r'neighbor {} peer-group ([^\s]+)'.format(name) | |
207 | if self.version_number >= '4.23': | |
208 | regexp = r'neighbor {} peer group ([^\s]+)'.format(name) | |
209 | else: | |
210 | regexp = r'neighbor {} peer-group ([^\s]+)'.format(name) | |
208 | 211 | match = re.search(regexp, config) |
209 | 212 | value = match.group(1) if match else None |
210 | 213 | return dict(peer_group=value) |
233 | 236 | return dict(description=value) |
234 | 237 | |
235 | 238 | def _parse_next_hop_self(self, config, name): |
236 | exp = 'no neighobr {} next-hop-self'.format(name) | |
239 | exp = 'no neighbor {} next-hop-self'.format(name) | |
237 | 240 | value = exp in config |
238 | 241 | return dict(next_hop_self=not value) |
239 | 242 | |
262 | 265 | def delete(self, name): |
263 | 266 | response = self.configure('no neighbor {}'.format(name)) |
264 | 267 | if not response: |
265 | response = self.configure('no neighbor {} peer-group'.format(name)) | |
268 | if self.version_number >= '4.23': | |
269 | response = self.configure('no neighbor {} ' | |
270 | 'peer group'.format(name)) | |
271 | else: | |
272 | response = self.configure('no neighbor {} ' | |
273 | 'peer-group'.format(name)) | |
266 | 274 | return response |
267 | 275 | |
268 | 276 | def configure(self, cmd): |
279 | 287 | |
280 | 288 | def set_peer_group(self, name, value=None, default=False, disable=False): |
281 | 289 | if not self.ispeergroup(name): |
282 | cmd = self.command_builder(name, 'peer-group', value, default, | |
283 | disable) | |
290 | if self.version_number >= '4.23': | |
291 | cmd = self.command_builder(name, 'peer group', value, default, | |
292 | disable) | |
293 | else: | |
294 | cmd = self.command_builder(name, 'peer-group', value, default, | |
295 | disable) | |
284 | 296 | return self.configure(cmd) |
285 | 297 | return False |
286 | 298 |
564 | 564 | True if the operation succeeds otherwise False is returned |
565 | 565 | """ |
566 | 566 | commands = ['interface %s' % name] |
567 | commands.append(self.command_builder('vrf forwarding', vrf, | |
568 | default=default, disable=disable)) | |
567 | if self.version_number >= '4.23': | |
568 | commands.append(self.command_builder('vrf', vrf, | |
569 | default=default, | |
570 | disable=disable)) | |
571 | else: | |
572 | commands.append(self.command_builder('vrf forwarding', vrf, | |
573 | default=default, | |
574 | disable=disable)) | |
569 | 575 | return self.configure(commands) |
570 | ||
571 | 576 | |
572 | 577 | class PortchannelInterface(BaseInterface): |
573 | 578 | |
825 | 830 | * udp_port (int): The vxlan udp-port value |
826 | 831 | * vlans (dict): The vlan to vni mappings |
827 | 832 | * flood_list (list): The list of global VTEP flood list |
828 | * multicast_decap (bool): If the mutlicast decap | |
833 | * multicast_decap (bool): If the multicast decap | |
829 | 834 | feature is configured |
830 | 835 | |
831 | 836 | Args: |
878 | 883 | return dict(multicast_group=value) |
879 | 884 | |
880 | 885 | def _parse_multicast_decap(self, config): |
881 | value = 'vxlan mutlicast-group decap' in config | |
886 | value = 'vxlan multicast-group decap' in config | |
882 | 887 | return dict(multicast_decap=bool(value)) |
883 | 888 | |
884 | 889 | def _parse_udp_port(self, config): |
1058 | 1063 | True if the command completes successfully |
1059 | 1064 | |
1060 | 1065 | """ |
1061 | cmd = 'vxlan vlan %s vni %s' % (vid, vni) | |
1066 | cmd = 'vxlan vlan add %s vni %s' % (vid, vni) | |
1062 | 1067 | return self.configure_interface(name, cmd) |
1063 | 1068 | |
1064 | 1069 | def remove_vlan(self, name, vid): |
1074 | 1079 | True if the command completes successfully |
1075 | 1080 | |
1076 | 1081 | """ |
1077 | return self.configure_interface(name, 'no vxlan vlan %s vni' % vid) | |
1082 | return self.configure_interface(name, 'vxlan vlan remove %s vni' % vid) | |
1078 | 1083 | |
1079 | 1084 | |
1080 | 1085 | INTERFACE_CLASS_MAP = { |
143 | 143 | dict: A dict object that is intended to be merged into the |
144 | 144 | resource dict |
145 | 145 | """ |
146 | match = re.search(r'peer-address ([^\s]+)', config) | |
146 | match = re.search(r'peer-address (\d+\.\d+\.\d+\.\d+)$', config) | |
147 | 147 | value = match.group(1) if match else None |
148 | 148 | return dict(peer_address=value) |
149 | 149 |
87 | 87 | return response |
88 | 88 | |
89 | 89 | def _parse_source_interface(self, config): |
90 | match = re.search(r'^ntp source ([^\s]+)', config, re.M) | |
90 | if self.version_number >= '4.23': | |
91 | match = re.search(r'^ntp local-interface ([^\s]+)', config, re.M) | |
92 | else: | |
93 | match = re.search(r'^ntp source ([^\s]+)', config, re.M) | |
91 | 94 | value = match.group(1) if match else None |
92 | 95 | return dict(source_interface=value) |
93 | 96 | |
117 | 120 | Returns: |
118 | 121 | True if the operation succeeds, otherwise False. |
119 | 122 | """ |
120 | cmd = self.command_builder('ntp source', disable=True) | |
123 | if self.version_number >= '4.23': | |
124 | cmd = self.command_builder('ntp local-interface', disable=True) | |
125 | else: | |
126 | cmd = self.command_builder('ntp source', disable=True) | |
121 | 127 | return self.configure(cmd) |
122 | 128 | |
123 | 129 | def default(self): |
126 | 132 | Returns: |
127 | 133 | True if the operation succeeds, otherwise False. |
128 | 134 | """ |
129 | cmd = self.command_builder('ntp source', default=True) | |
135 | if self.version_number >= '4.23': | |
136 | cmd = self.command_builder('ntp local-interface', default=True) | |
137 | else: | |
138 | cmd = self.command_builder('ntp source', default=True) | |
130 | 139 | return self.configure(cmd) |
131 | 140 | |
132 | 141 | def set_source_interface(self, name): |
138 | 147 | Returns: |
139 | 148 | True if the operation succeeds, otherwise False. |
140 | 149 | """ |
141 | cmd = self.command_builder('ntp source', value=name) | |
150 | if self.version_number >= '4.23': | |
151 | cmd = self.command_builder('ntp local-interface', value=name) | |
152 | else: | |
153 | cmd = self.command_builder('ntp source', value=name) | |
142 | 154 | return self.configure(cmd) |
143 | 155 | |
144 | 156 | def add_server(self, name, prefer=False): |
99 | 99 | |
100 | 100 | def getall(self): |
101 | 101 | resources = dict() |
102 | routemaps_re = re.compile(r'^route-map\s(\w+)\s\w+\s\d+$', re.M) | |
102 | routemaps_re = re.compile(r'^route-map\s([\w-]+)\s\w+\s\d+$', re.M) | |
103 | 103 | for name in routemaps_re.findall(self.config): |
104 | 104 | routemap = self.get(name) |
105 | 105 | if routemap: |
238 | 238 | """ |
239 | 239 | try: |
240 | 240 | current_statements = self.get(name)[action][seqno]['match'] |
241 | except: | |
241 | except Exception: | |
242 | 242 | current_statements = [] |
243 | 243 | |
244 | 244 | commands = list() |
274 | 274 | """ |
275 | 275 | try: |
276 | 276 | current_statements = self.get(name)[action][seqno]['set'] |
277 | except: | |
277 | except Exception: | |
278 | 278 | current_statements = [] |
279 | 279 | |
280 | 280 | commands = list() |
157 | 157 | # Get the four identifying components |
158 | 158 | ip_dest = match[0] |
159 | 159 | next_hop = match[1] |
160 | next_hop_ip = None if match[2] is '' else match[2] | |
160 | next_hop_ip = None if match[2] == '' else match[2] | |
161 | 161 | distance = int(match[3]) |
162 | 162 | |
163 | 163 | # Create the data dict with the remaining components |
164 | 164 | data = {} |
165 | data['tag'] = None if match[4] is '' else int(match[4]) | |
166 | data['route_name'] = None if match[5] is '' else match[5] | |
165 | data['tag'] = None if match[4] == '' else int(match[4]) | |
166 | data['route_name'] = None if match[5] == '' else match[5] | |
167 | 167 | |
168 | 168 | # Build the complete dict entry from the four components |
169 | 169 | # and the data. |
104 | 104 | into the resource dict |
105 | 105 | """ |
106 | 106 | motd_value = login_value = None |
107 | matches = re.findall('^banner\s+(login|motd)\s?$\n(.*?)$\nEOF$\n', | |
107 | matches = re.findall(r'^banner\s+(login|motd)\s?$\n(.*?)$\nEOF$\n', | |
108 | 108 | self.config, re.DOTALL | re.M) |
109 | 109 | for match in matches: |
110 | 110 | if match[0].strip() == "motd": |
81 | 81 | following configuration line that might contain the users sshkey. |
82 | 82 | """ |
83 | 83 | |
84 | users_re = re.compile(r'username (?P<user>[^\s]+) privilege (\d+)' | |
85 | r'(?: role ([^\s]+))?' | |
86 | r'(?: (nopassword))?' | |
87 | r'(?: secret (0|5|7|sha512) (.+))?' | |
88 | r'.*$\n(?:username (?P=user) sshkey (.+)$)?', re.M) | |
89 | ||
90 | 84 | def get(self, name): |
91 | 85 | """Returns the local user configuration as a resource dict |
92 | 86 | |
107 | 101 | Returns: |
108 | 102 | dict: A dict of usernames with a nested resource dict object |
109 | 103 | """ |
104 | if self.version_number >= '4.23': | |
105 | self.users_re = re.compile(r'username (?P<user>[^\s]+) ' | |
106 | r'privilege (\d+)' | |
107 | r'(?: role ([^\s]+))?' | |
108 | r'(?: (nopassword))?' | |
109 | r'(?: secret (0|5|7|sha512) (.+))?' | |
110 | r'.*$\n(?:username (?P=user) ' | |
111 | r'ssh.key (.+)$)?', re.M) | |
112 | else: | |
113 | self.users_re = re.compile(r'username (?P<user>[^\s]+) ' | |
114 | r'privilege (\d+)' | |
115 | r'(?: role ([^\s]+))?' | |
116 | r'(?: (nopassword))?' | |
117 | r'(?: secret (0|5|7|sha512) (.+))?' | |
118 | r'.*$\n(?:username (?P=user) ' | |
119 | r'sshkey (.+)$)?', re.M) | |
120 | ||
110 | 121 | users = self.users_re.findall(self.config, re.M) |
111 | 122 | resources = dict() |
112 | 123 | for user in users: |
130 | 141 | resource['nopassword'] = nopass == 'nopassword' |
131 | 142 | resource['format'] = fmt |
132 | 143 | resource['secret'] = secret |
133 | resource['sshkey'] = sshkey | |
144 | if self.version_number >= '4.23': | |
145 | resource['ssh-key'] = sshkey | |
146 | else: | |
147 | resource['sshkey'] = sshkey | |
134 | 148 | return {username: resource} |
135 | 149 | |
136 | 150 | def create(self, name, nopassword=None, secret=None, encryption=None): |
286 | 300 | Returns: |
287 | 301 | True if the operation was successful otherwise False |
288 | 302 | """ |
289 | cmd = self.command_builder('username %s sshkey' % name, value=value, | |
290 | default=default, disable=disable) | |
303 | if self.version_number >= '4.23': | |
304 | cmd = self.command_builder('username %s ssh-key' % name, | |
305 | value=value, | |
306 | default=default, disable=disable) | |
307 | else: | |
308 | cmd = self.command_builder('username %s sshkey' % name, | |
309 | value=value, | |
310 | default=default, disable=disable) | |
291 | 311 | return self.configure(cmd) |
292 | 312 | |
293 | 313 |
177 | 177 | elif addresses is not None: |
178 | 178 | try: |
179 | 179 | current_addresses = self.get(name)['addresses'] |
180 | except: | |
180 | except Exception: | |
181 | 181 | current_addresses = [] |
182 | 182 | |
183 | 183 | # remove virtual-router addresses not present in addresses list |
53 | 53 | from pyeapi.api import EntityCollection |
54 | 54 | from pyeapi.utils import make_iterable |
55 | 55 | |
56 | VLAN_ID_RE = re.compile(r'(?:vlan\s)(?P<value>.*)$', re.M) | |
56 | 57 | NAME_RE = re.compile(r'(?:name\s)(?P<value>.*)$', re.M) |
57 | 58 | STATE_RE = re.compile(r'(?:state\s)(?P<value>.*)$', re.M) |
58 | 59 | TRUNK_GROUP_RE = re.compile(r'(?:trunk\sgroup\s)(?P<value>.*)$', re.M) |
103 | 104 | if not config: |
104 | 105 | return None |
105 | 106 | |
106 | response = dict(vlan_id=value) | |
107 | response = dict(vlan_id=self._parse_vlan_id(config)) | |
107 | 108 | response.update(self._parse_name(config)) |
108 | 109 | response.update(self._parse_state(config)) |
109 | 110 | response.update(self._parse_trunk_groups(config)) |
110 | 111 | |
111 | 112 | return response |
113 | ||
114 | def _parse_vlan_id(self, config): | |
115 | """ _parse_vlan_id scans the provided configuration block and extracts | |
116 | the vlan id. The config block is expected to always return the | |
117 | vlan id. The return dict is intended to be merged into the response | |
118 | dict. | |
119 | ||
120 | Args: | |
121 | config (str): The vlan configuration block from the nodes running | |
122 | configuration | |
123 | ||
124 | Returns: | |
125 | Str: vlan id (or range/list of vlan ids) | |
126 | """ | |
127 | value = VLAN_ID_RE.search(config).group('value') | |
128 | return value | |
112 | 129 | |
113 | 130 | def _parse_name(self, config): |
114 | 131 | """ _parse_name scans the provided configuration block and extracts |
165 | 182 | A dict object of Vlan attributes |
166 | 183 | |
167 | 184 | """ |
168 | vlans_re = re.compile(r'(?<=^vlan\s)(\d+)', re.M) | |
185 | # regex to find standalone and grouped (ranged, enumerated) vlans (#197) | |
186 | vlans_re = re.compile(r'(?<=^vlan\s)[\d,\-]+', re.M) | |
169 | 187 | |
170 | 188 | response = dict() |
171 | 189 | for vid in vlans_re.findall(self.config): |
75 | 75 | key/value pairs. |
76 | 76 | |
77 | 77 | """ |
78 | config = self.get_block('vrf definition %s' % value) | |
78 | if self.version_number >= '4.23': | |
79 | config = self.get_block('vrf instance %s' % value) | |
80 | else: | |
81 | config = self.get_block('vrf definition %s' % value) | |
79 | 82 | if not config: |
80 | 83 | return None |
81 | 84 | response = dict(vrf_name=value) |
135 | 138 | A dict object of VRF attributes |
136 | 139 | |
137 | 140 | """ |
138 | vrfs_re = re.compile(r'(?<=^vrf definition\s)(\w+)', re.M) | |
141 | if self.version_number >= '4.23': | |
142 | vrfs_re = re.compile(r'(?<=^vrf instance\s)(\w+)', re.M) | |
143 | else: | |
144 | vrfs_re = re.compile(r'(?<=^vrf definition\s)(\w+)', re.M) | |
139 | 145 | |
140 | 146 | response = dict() |
141 | 147 | for vrf in vrfs_re.findall(self.config): |
159 | 165 | Returns: |
160 | 166 | True if create was successful otherwise False |
161 | 167 | """ |
162 | commands = ['vrf definition %s' % vrf_name] | |
168 | if self.version_number >= '4.23': | |
169 | commands = ['vrf instance %s' % vrf_name] | |
170 | else: | |
171 | commands = ['vrf definition %s' % vrf_name] | |
163 | 172 | if rd: |
164 | 173 | commands.append('rd %s' % rd) |
165 | 174 | return self.configure(commands) |
173 | 182 | Returns: |
174 | 183 | True if the operation was successful otherwise False |
175 | 184 | """ |
176 | command = 'no vrf definition %s' % vrf_name | |
185 | if self.version_number >= '4.23': | |
186 | command = 'no vrf instance %s' % vrf_name | |
187 | else: | |
188 | command = 'no vrf definition %s' % vrf_name | |
177 | 189 | return self.configure(command) |
178 | 190 | |
179 | 191 | def default(self, vrf_name): |
185 | 197 | Returns: |
186 | 198 | True if the operation was successful otherwise False |
187 | 199 | """ |
188 | command = 'default vrf definition %s' % vrf_name | |
200 | if self.version_number >= '4.23': | |
201 | command = 'default vrf instance %s' % vrf_name | |
202 | else: | |
203 | command = 'default vrf definition %s' % vrf_name | |
189 | 204 | return self.configure(command) |
190 | 205 | |
191 | 206 | def configure_vrf(self, vrf_name, commands): |
199 | 214 | True if the commands completed successfully |
200 | 215 | """ |
201 | 216 | commands = make_iterable(commands) |
202 | commands.insert(0, 'vrf definition %s' % vrf_name) | |
217 | if self.version_number >= '4.23': | |
218 | commands.insert(0, 'vrf instance %s' % vrf_name) | |
219 | else: | |
220 | commands.insert(0, 'vrf definition %s' % vrf_name) | |
221 | ||
203 | 222 | return self.configure(commands) |
204 | 223 | |
205 | 224 | def set_rd(self, vrf_name, rd): |
300 | 319 | True if the operation was successful otherwise False |
301 | 320 | """ |
302 | 321 | cmds = ['interface %s' % interface] |
303 | cmds.append(self.command_builder('vrf forwarding', value=vrf_name, | |
304 | default=default, disable=disable)) | |
322 | if self.version_number >= '4.23': | |
323 | cmds.append(self.command_builder('vrf', value=vrf_name, | |
324 | default=default, disable=disable)) | |
325 | else: | |
326 | cmds.append(self.command_builder('vrf forwarding', value=vrf_name, | |
327 | default=default, disable=disable)) | |
305 | 328 | return self.configure(cmds) |
306 | 329 | |
307 | 330 |
250 | 250 | return vrrps |
251 | 251 | |
252 | 252 | def _parse_enable(self, config, vrid): |
253 | match = re.search(r'^\s+vrrp %s shutdown$' % vrid, config, re.M) | |
253 | if self.version_number >= '4.21.3': | |
254 | match = re.search(r'^\s+vrrp %s disabled$' % vrid, config, re.M) | |
255 | else: | |
256 | match = re.search(r'^\s+vrrp %s shutdown$' % vrid, config, re.M) | |
254 | 257 | if match: |
255 | 258 | return dict(enable=False) |
256 | 259 | return dict(enable=True) |
257 | 260 | |
258 | 261 | def _parse_primary_ip(self, config, vrid): |
259 | match = re.search(r'^\s+vrrp %s ip (\d+\.\d+\.\d+\.\d+)$' % | |
260 | vrid, config, re.M) | |
262 | if self.version_number >= '4.21.3': | |
263 | match = re.search(r'^\s+vrrp %s ipv4 (\d+\.\d+\.\d+\.\d+)$' % | |
264 | vrid, config, re.M) | |
265 | else: | |
266 | match = re.search(r'^\s+vrrp %s ip (\d+\.\d+\.\d+\.\d+)$' % | |
267 | vrid, config, re.M) | |
261 | 268 | value = match.group(1) if match else None |
262 | 269 | return dict(primary_ip=value) |
263 | 270 | |
264 | 271 | def _parse_priority(self, config, vrid): |
265 | match = re.search(r'^\s+vrrp %s priority (\d+)$' % vrid, config, re.M) | |
272 | if self.version_number >= '4.21.3': | |
273 | match = re.search(r'^\s+vrrp %s priority-level (\d+)$' % | |
274 | vrid, config, re.M) | |
275 | else: | |
276 | match = re.search(r'^\s+vrrp %s priority (\d+)$' % | |
277 | vrid, config, re.M) | |
266 | 278 | value = int(match.group(1)) if match else None |
267 | 279 | return dict(priority=value) |
268 | 280 | |
269 | 281 | def _parse_timers_advertise(self, config, vrid): |
270 | match = re.search(r'^\s+vrrp %s timers advertise (\d+)$' % | |
271 | vrid, config, re.M) | |
282 | if self.version_number >= '4.21.3': | |
283 | match = re.search(r'^\s+vrrp %s advertisement interval (\d+)$' % | |
284 | vrid, config, re.M) | |
285 | else: | |
286 | match = re.search(r'^\s+vrrp %s timers advertise (\d+)$' % | |
287 | vrid, config, re.M) | |
272 | 288 | value = int(match.group(1)) if match else None |
273 | 289 | return dict(timers_advertise=value) |
274 | 290 | |
279 | 295 | return dict(preempt=False) |
280 | 296 | |
281 | 297 | def _parse_secondary_ip(self, config, vrid): |
282 | matches = re.findall(r'^\s+vrrp %s ip (\d+\.\d+\.\d+\.\d+) ' | |
283 | r'secondary$' % vrid, config, re.M) | |
298 | if self.version_number >= '4.21.3': | |
299 | matches = re.findall(r'^\s+vrrp %s ipv4 (\d+\.\d+\.\d+\.\d+) ' | |
300 | r'secondary$' % vrid, config, re.M) | |
301 | else: | |
302 | matches = re.findall(r'^\s+vrrp %s ip (\d+\.\d+\.\d+\.\d+) ' | |
303 | r'secondary$' % vrid, config, re.M) | |
284 | 304 | value = matches if matches else [] |
285 | 305 | return dict(secondary_ip=value) |
286 | 306 | |
287 | 307 | def _parse_description(self, config, vrid): |
288 | match = re.search(r'^\s+vrrp %s description(.*)$' % | |
289 | vrid, config, re.M) | |
308 | if self.version_number >= '4.21.3': | |
309 | match = re.search(r'^\s+vrrp %s session description(.*)$' % | |
310 | vrid, config, re.M) | |
311 | else: | |
312 | match = re.search(r'^\s+vrrp %s description(.*)$' % | |
313 | vrid, config, re.M) | |
290 | 314 | if match: |
291 | 315 | return dict(description=match.group(1).lstrip()) |
292 | 316 | return dict(description='') |
318 | 342 | return dict(bfd_ip='') |
319 | 343 | |
320 | 344 | def _parse_ip_version(self, config, vrid): |
321 | match = re.search(r'^\s+vrrp %s ip version (\d+)$' % | |
322 | vrid, config, re.M) | |
345 | if self.version_number >= '4.21.3': | |
346 | match = re.search(r'^\s+vrrp %s ipv4 version (\d+)$' % | |
347 | vrid, config, re.M) | |
348 | else: | |
349 | match = re.search(r'^\s+vrrp %s ip version (\d+)$' % | |
350 | vrid, config, re.M) | |
323 | 351 | value = int(match.group(1)) if match else None |
324 | 352 | return dict(ip_version=value) |
325 | 353 | |
326 | 354 | def _parse_delay_reload(self, config, vrid): |
327 | match = re.search(r'^\s+vrrp %s delay reload (\d+)$' % | |
328 | vrid, config, re.M) | |
355 | if self.version_number >= '4.21.3': | |
356 | match = re.search(r'^\s+vrrp %s timers delay reload (\d+)$' % | |
357 | vrid, config, re.M) | |
358 | else: | |
359 | match = re.search(r'^\s+vrrp %s delay reload (\d+)$' % | |
360 | vrid, config, re.M) | |
329 | 361 | value = int(match.group(1)) if match else None |
330 | 362 | return dict(delay_reload=value) |
331 | 363 | |
332 | 364 | def _parse_track(self, config, vrid): |
333 | matches = re.findall(r'^\s+vrrp %s track (\S+) ' | |
334 | r'(decrement|shutdown)(?:( \d+$|$))' % | |
335 | vrid, config, re.M) | |
365 | if self.version_number >= '4.21.3': | |
366 | matches = re.findall(r'^\s+vrrp %s tracked-object (\S+) ' | |
367 | r'(decrement|shutdown)(?:( \d+$|$))' % | |
368 | vrid, config, re.M) | |
369 | else: | |
370 | matches = re.findall(r'^\s+vrrp %s track (\S+) ' | |
371 | r'(decrement|shutdown)(?:( \d+$|$))' % | |
372 | vrid, config, re.M) | |
336 | 373 | value = [] |
337 | 374 | for match in matches: |
338 | 375 | tr_obj = match[0] |
440 | 477 | """ |
441 | 478 | |
442 | 479 | if value is False: |
443 | cmd = "vrrp %d shutdown" % vrid | |
480 | if self.version_number >= '4.21.3': | |
481 | cmd = "vrrp %d disabled" % vrid | |
482 | else: | |
483 | cmd = "vrrp %d shutdown" % vrid | |
444 | 484 | elif value is True: |
445 | cmd = "no vrrp %d shutdown" % vrid | |
485 | if self.version_number >= '4.21.3': | |
486 | cmd = "no vrrp %d disabled" % vrid | |
487 | else: | |
488 | cmd = "no vrrp %d shutdown" % vrid | |
446 | 489 | else: |
447 | 490 | raise ValueError("vrrp property 'enable' must be " |
448 | 491 | "True or False") |
483 | 526 | if default is True: |
484 | 527 | vrrps = self.get(name) |
485 | 528 | primary_ip = vrrps[vrid]['primary_ip'] |
486 | cmd = "default vrrp %d ip %s" % (vrid, primary_ip) | |
529 | if self.version_number >= '4.21.3': | |
530 | cmd = "default vrrp %d ipv4 %s" % (vrid, primary_ip) | |
531 | else: | |
532 | cmd = "default vrrp %d ip %s" % (vrid, primary_ip) | |
487 | 533 | elif disable is True or value is None: |
488 | 534 | vrrps = self.get(name) |
489 | 535 | primary_ip = vrrps[vrid]['primary_ip'] |
490 | cmd = "no vrrp %d ip %s" % (vrid, primary_ip) | |
536 | if self.version_number >= '4.21.3': | |
537 | cmd = "no vrrp %d ipv4 %s" % (vrid, primary_ip) | |
538 | else: | |
539 | cmd = "no vrrp %d ip %s" % (vrid, primary_ip) | |
491 | 540 | elif re.match(r'^\d+\.\d+\.\d+\.\d+$', str(value)): |
492 | cmd = "vrrp %d ip %s" % (vrid, value) | |
541 | if self.version_number >= '4.21.3': | |
542 | cmd = "vrrp %d ipv4 %s" % (vrid, value) | |
543 | else: | |
544 | cmd = "vrrp %d ip %s" % (vrid, value) | |
493 | 545 | else: |
494 | 546 | raise ValueError("vrrp property 'primary_ip' must be " |
495 | 547 | "a properly formatted IP address") |
531 | 583 | if not str(value).isdigit() or value < 1 or value > 254: |
532 | 584 | raise ValueError("vrrp property 'priority' must be " |
533 | 585 | "an integer in the range 1-254") |
534 | ||
535 | cmd = self.command_builder('vrrp %d priority' % vrid, value=value, | |
536 | default=default, disable=disable) | |
586 | if self.version_number >= '4.21.3': | |
587 | cmd = self.command_builder('vrrp %d priority-level' % | |
588 | vrid, value=value, | |
589 | default=default, disable=disable) | |
590 | else: | |
591 | cmd = self.command_builder('vrrp %d priority' % | |
592 | vrid, value=value, | |
593 | default=default, disable=disable) | |
537 | 594 | |
538 | 595 | # Run the command if requested |
539 | 596 | if run: |
567 | 624 | be passed to the node |
568 | 625 | |
569 | 626 | """ |
570 | ||
571 | cmd = self.command_builder('vrrp %d description' % vrid, value=value, | |
572 | default=default, disable=disable) | |
627 | if self.version_number >= '4.21.3': | |
628 | cmd = self.command_builder('vrrp %d session description' % | |
629 | vrid, value=value, | |
630 | default=default, disable=disable) | |
631 | else: | |
632 | cmd = self.command_builder('vrrp %d description' % | |
633 | vrid, value=value, | |
634 | default=default, disable=disable) | |
573 | 635 | |
574 | 636 | # Run the command if requested |
575 | 637 | if run: |
607 | 669 | if not default and not disable: |
608 | 670 | if value not in (2, 3): |
609 | 671 | raise ValueError("vrrp property 'ip_version' must be 2 or 3") |
610 | ||
611 | cmd = self.command_builder('vrrp %d ip version' % vrid, value=value, | |
612 | default=default, disable=disable) | |
672 | if self.version_number >= '4.21.3': | |
673 | cmd = self.command_builder('vrrp %d ipv4 version' % | |
674 | vrid, value=value, | |
675 | default=default, disable=disable) | |
676 | else: | |
677 | cmd = self.command_builder('vrrp %d ip version' % | |
678 | vrid, value=value, | |
679 | default=default, disable=disable) | |
613 | 680 | |
614 | 681 | # Run the command if requested |
615 | 682 | if run: |
674 | 741 | |
675 | 742 | # Build the commands to add and remove the secondary ip addresses |
676 | 743 | for sec_ip in remove: |
677 | cmds.append("no vrrp %d ip %s secondary" % (vrid, sec_ip)) | |
744 | if self.version_number >= '4.21.3': | |
745 | cmds.append("no vrrp %d ipv4 %s secondary" % (vrid, sec_ip)) | |
746 | else: | |
747 | cmds.append("no vrrp %d ip %s secondary" % (vrid, sec_ip)) | |
678 | 748 | |
679 | 749 | for sec_ip in add: |
680 | cmds.append("vrrp %d ip %s secondary" % (vrid, sec_ip)) | |
750 | if self.version_number >= '4.21.3': | |
751 | cmds.append("vrrp %d ipv4 %s secondary" % (vrid, sec_ip)) | |
752 | else: | |
753 | cmds.append("vrrp %d ip %s secondary" % (vrid, sec_ip)) | |
681 | 754 | |
682 | 755 | cmds = sorted(cmds) |
683 | 756 | |
718 | 791 | if not int(value) or int(value) < 1 or int(value) > 255: |
719 | 792 | raise ValueError("vrrp property 'timers_advertise' must be" |
720 | 793 | "in the range 1-255") |
721 | ||
722 | cmd = self.command_builder('vrrp %d timers advertise' % vrid, | |
723 | value=value, default=default, | |
724 | disable=disable) | |
794 | if self.version_number >= '4.21.3': | |
795 | cmd = self.command_builder('vrrp %d advertisement interval' % | |
796 | vrid, | |
797 | value=value, default=default, | |
798 | disable=disable) | |
799 | else: | |
800 | cmd = self.command_builder('vrrp %d timers advertise' % | |
801 | vrid, | |
802 | value=value, default=default, | |
803 | disable=disable) | |
725 | 804 | |
726 | 805 | # Run the command if requested |
727 | 806 | if run: |
931 | 1010 | if not int(value) or int(value) < 1 or int(value) > 3600: |
932 | 1011 | raise ValueError("vrrp property 'delay_reload' must be" |
933 | 1012 | "in the range 0-3600 %r" % value) |
934 | ||
935 | cmd = self.command_builder('vrrp %d delay reload' % vrid, value=value, | |
936 | default=default, disable=disable) | |
1013 | if self.version_number >= '4.21.3': | |
1014 | cmd = self.command_builder('vrrp %d timers delay reload' % | |
1015 | vrid, value=value, | |
1016 | default=default, disable=disable) | |
1017 | else: | |
1018 | cmd = self.command_builder('vrrp %d delay reload' % | |
1019 | vrid, value=value, | |
1020 | default=default, disable=disable) | |
937 | 1021 | |
938 | 1022 | # Run the command if requested |
939 | 1023 | if run: |
1067 | 1151 | |
1068 | 1152 | if amount == unset: |
1069 | 1153 | amount = '' |
1070 | t_cmd = ("no vrrp %d track %s %s %s" | |
1071 | % (vrid, tr_obj, action, amount)) | |
1154 | if self.version_number >= '4.21.3': | |
1155 | t_cmd = ("no vrrp %d tracked-object %s %s %s" | |
1156 | % (vrid, tr_obj, action, amount)) | |
1157 | else: | |
1158 | t_cmd = ("no vrrp %d track %s %s %s" | |
1159 | % (vrid, tr_obj, action, amount)) | |
1072 | 1160 | cmds.append(t_cmd.rstrip()) |
1073 | 1161 | |
1074 | 1162 | for track in add: |
1079 | 1167 | |
1080 | 1168 | if amount == unset: |
1081 | 1169 | amount = '' |
1082 | t_cmd = ("vrrp %d track %s %s %s" | |
1083 | % (vrid, tr_obj, action, amount)) | |
1170 | if self.version_number >= '4.21.3': | |
1171 | t_cmd = ("vrrp %d tracked-object %s %s %s" | |
1172 | % (vrid, tr_obj, action, amount)) | |
1173 | else: | |
1174 | t_cmd = ("vrrp %d track %s %s %s" | |
1175 | % (vrid, tr_obj, action, amount)) | |
1084 | 1176 | cmds.append(t_cmd.rstrip()) |
1085 | 1177 | |
1086 | 1178 | cmds = sorted(cmds) |
1155 | 1247 | if primary_ip in ('no', None): |
1156 | 1248 | cmd = self.set_primary_ip(name, vrid, value=None, |
1157 | 1249 | disable=True, run=False) |
1158 | elif primary_ip is 'default': | |
1250 | elif primary_ip == 'default': | |
1159 | 1251 | cmd = self.set_primary_ip(name, vrid, value=None, |
1160 | 1252 | default=True, run=False) |
1161 | 1253 | else: |
47 | 47 | |
48 | 48 | >>> import pyeapi |
49 | 49 | >>> conn = pyeapi.connect(host='10.1.1.1', transport='http') |
50 | >>> conn.execute(['show verson']) | |
50 | >>> conn.execute(['show version']) | |
51 | 51 | {u'jsonrpc': u'2.0', u'result': [{u'memTotal': 2028008, u'version': |
52 | 52 | u'4.14.5F', u'internalVersion': u'4.14.5F-2209869.4145F', u'serialNumber': |
53 | 53 | u'', u'systemMacAddress': u'00:0c:29:f5:d2:7d', u'bootupTimestamp': |
89 | 89 | contains the settings for nodes used by the connect_to function. |
90 | 90 | |
91 | 91 | """ |
92 | from uuid import uuid4 | |
92 | 93 | import os |
93 | 94 | import re |
94 | 95 | |
105 | 106 | from pyeapi.utils import load_module, make_iterable, debug |
106 | 107 | |
107 | 108 | from pyeapi.eapilib import HttpEapiConnection, HttpsEapiConnection |
109 | from pyeapi.eapilib import HttpsEapiCertConnection | |
108 | 110 | from pyeapi.eapilib import SocketEapiConnection, HttpLocalEapiConnection |
109 | 111 | from pyeapi.eapilib import CommandError |
110 | 112 | |
114 | 116 | 'socket': SocketEapiConnection, |
115 | 117 | 'http_local': HttpLocalEapiConnection, |
116 | 118 | 'http': HttpEapiConnection, |
117 | 'https': HttpsEapiConnection | |
119 | 'https': HttpsEapiConnection, | |
120 | 'https_certs': HttpsEapiCertConnection | |
118 | 121 | } |
119 | 122 | |
120 | 123 | DEFAULT_TRANSPORT = 'https' |
326 | 329 | """ |
327 | 330 | return config.load(filename) |
328 | 331 | |
332 | ||
329 | 333 | def config_for(name): |
330 | 334 | """ Function to get settings for named config |
331 | 335 | |
343 | 347 | """ |
344 | 348 | return config.get_connection(name) |
345 | 349 | |
350 | ||
346 | 351 | def hosts_for_tag(tag): |
347 | 352 | """ Returns the hosts assocated with the specified tag |
348 | 353 | |
359 | 364 | None: If the specified tag does not exist, then None is returned. |
360 | 365 | """ |
361 | 366 | return config.tags.get(tag) |
367 | ||
362 | 368 | |
363 | 369 | def make_connection(transport, **kwargs): |
364 | 370 | """ Creates a connection instance based on the transport |
385 | 391 | |
386 | 392 | |
387 | 393 | def connect(transport=None, host='localhost', username='admin', |
388 | password='', port=None, timeout=60, return_node=False, **kwargs): | |
394 | password='', port=None, key_file=None, cert_file=None, | |
395 | ca_file=None, timeout=60, return_node=False, **kwargs): | |
389 | 396 | """ Creates a connection using the supplied settings |
390 | 397 | |
391 | 398 | This function will create a connection to an Arista EOS node using |
404 | 411 | port (int): The TCP port of the endpoint for the eAPI connection. If |
405 | 412 | this keyword is not specified, the default value is automatically |
406 | 413 | determined by the transport type. (http=80, https=443) |
414 | key_file (str): Path to private key file for ssl validation | |
415 | cert_file (str): Path to PEM formatted cert file for ssl validation | |
416 | ca_file (str): Path to CA PEM formatted cert file for ssl validation | |
417 | timeout (int): timeout | |
407 | 418 | return_node (bool): Returns a Node object if True, otherwise |
408 | 419 | returns an EapiConnection object. |
409 | 420 | |
414 | 425 | """ |
415 | 426 | transport = transport or DEFAULT_TRANSPORT |
416 | 427 | connection = make_connection(transport, host=host, username=username, |
417 | password=password, port=port, timeout=timeout) | |
428 | password=password, key_file=key_file, | |
429 | cert_file=cert_file, ca_file=ca_file, | |
430 | port=port, timeout=timeout) | |
418 | 431 | if return_node: |
419 | 432 | return Node(connection, transport=transport, host=host, |
420 | username=username, password=password, port=port, **kwargs) | |
433 | username=username, password=password, key_file=key_file, | |
434 | cert_file=cert_file, ca_file=ca_file, port=port, **kwargs) | |
421 | 435 | return connection |
422 | 436 | |
423 | 437 | |
457 | 471 | self._version = None |
458 | 472 | self._version_number = None |
459 | 473 | self._model = None |
474 | self._session_name = None | |
460 | 475 | |
461 | 476 | self._enablepwd = kwargs.get('enablepwd') |
462 | 477 | self.autorefresh = kwargs.get('autorefresh', True) |
516 | 531 | # Parse out version info |
517 | 532 | output = self.enable('show version') |
518 | 533 | self._version = str(output[0]['result']['version']) |
519 | match = re.match('[\d.\d]+', output[0]['result']['version']) | |
534 | match = re.match(r'[\d.\d]+', str(output[0]['result']['version'])) | |
520 | 535 | if match: |
521 | 536 | self._version_number = str(match.group(0)) |
522 | 537 | else: |
523 | 538 | self._version_number = str(output[0]['result']['version']) |
524 | 539 | # Parse out model number |
525 | match = re.search('\d\d\d\d', output[0]['result']['modelName']) | |
540 | match = re.search(r'\d\d\d\d', str(output[0]['result']['modelName'])) | |
526 | 541 | if match: |
527 | 542 | self._model = str(match.group(0)) |
528 | 543 | else: |
564 | 579 | output from each command. The function will strip the |
565 | 580 | response from any commands it prepends. |
566 | 581 | """ |
582 | if self._session_name: # If in a config session | |
583 | return self._configure_session(commands, **kwargs) | |
584 | ||
585 | return self._configure_terminal(commands, **kwargs) | |
586 | ||
587 | def _configure_terminal(self, commands, **kwargs): | |
588 | """Configures the node with the specified commands with leading "configure terminal" | |
589 | """ | |
567 | 590 | commands = make_iterable(commands) |
568 | 591 | commands = list(commands) |
569 | 592 | |
573 | 596 | |
574 | 597 | if self.autorefresh: |
575 | 598 | self.refresh() |
599 | ||
600 | # pop the configure command output off the stack | |
601 | response.pop(0) | |
602 | ||
603 | return response | |
604 | ||
605 | def _configure_session(self, commands, **kwargs): | |
606 | """Configures the node with the specified commands with leading "configure session <session name>" | |
607 | """ | |
608 | if not self._session_name: | |
609 | raise CommandError('Not currently in a session') | |
610 | ||
611 | commands = make_iterable(commands) | |
612 | commands = list(commands) | |
613 | ||
614 | # push the configure command onto the command stack | |
615 | commands.insert(0, 'configure session %s' % self._session_name) | |
616 | response = self.run_commands(commands, **kwargs) | |
576 | 617 | |
577 | 618 | # pop the configure command output off the stack |
578 | 619 | response.pop(0) |
810 | 851 | self._running_config = None |
811 | 852 | self._startup_config = None |
812 | 853 | |
854 | def configure_session(self): | |
855 | """Enter a config session | |
856 | """ | |
857 | self._session_name = self._session_name or uuid4() | |
858 | ||
859 | def diff(self): | |
860 | """Returns session-config diffs in text encoding | |
861 | ||
862 | Note: "show session-config diffs" doesn't support json encoding | |
863 | """ | |
864 | response = self._configure_session(['show session-config diffs'], encoding='text') | |
865 | ||
866 | return response[0]['output'] | |
867 | ||
868 | def commit(self): | |
869 | """Commits the current config session | |
870 | """ | |
871 | return self._configure_and_exit_session(['commit']) | |
872 | ||
873 | def abort(self): | |
874 | """Aborts the current config session | |
875 | """ | |
876 | return self._configure_session(['abort']) | |
877 | ||
878 | def _configure_and_exit_session(self, commands, **kwargs): | |
879 | response = self._configure_session(commands, **kwargs) | |
880 | ||
881 | if self.autorefresh: | |
882 | self.refresh() | |
883 | ||
884 | # Exit the current config session | |
885 | self._session_name = None | |
886 | ||
887 | return response | |
888 | ||
813 | 889 | |
814 | 890 | def connect_to(name): |
815 | 891 | """Creates a node instance based on an entry from the config |
57 | 57 | DEFAULT_HTTP_PORT = 80 |
58 | 58 | DEFAULT_HTTPS_PORT = 443 |
59 | 59 | DEFAULT_HTTP_LOCAL_PORT = 8080 |
60 | DEFAULT_HTTPS_LOCAL_PORT = 8443 | |
60 | 61 | DEFAULT_HTTP_PATH = '/command-api' |
61 | 62 | DEFAULT_UNIX_SOCKET = '/var/run/command-api.sock' |
62 | 63 | |
206 | 207 | return 'https://%s:%s/%s' % (self.host, self.port, self.path) |
207 | 208 | |
208 | 209 | |
210 | class HTTPSCertConnection(HTTPSConnection): | |
211 | """ Class to make a HTTPS connection, with support | |
212 | for full client-based SSL Authentication. | |
213 | """ | |
214 | ||
215 | def __init__(self, path, host, port, key_file, cert_file, ca_file, | |
216 | timeout=None): | |
217 | HTTPSConnection.__init__(self, host, key_file=key_file, | |
218 | cert_file=cert_file) | |
219 | self.key_file = key_file | |
220 | self.cert_file = cert_file | |
221 | self.ca_file = ca_file | |
222 | self.timeout = timeout | |
223 | self.path = path | |
224 | self.port = port | |
225 | ||
226 | def __str__(self): | |
227 | return 'https://%s:%s/%s - %s,%s' % (self.host, self.port, self.path, | |
228 | self.key_file, self.cert_file) | |
229 | ||
230 | def __repr__(self): | |
231 | return 'https://%s:%s/%s - %s,%s' % (self.host, self.port, self.path, | |
232 | self.key_file, self.cert_file) | |
233 | ||
234 | def connect(self): | |
235 | """ Connect to a host on a given (SSL) port. | |
236 | If ca_file is pointing somewhere, use it | |
237 | to check Server Certificate. | |
238 | ||
239 | Redefined/copied and extended from httplib.py:1105 (Python 2.6.x). | |
240 | This is needed to pass cert_reqs=ssl.CERT_REQUIRED as parameter | |
241 | to ssl.wrap_socket(), which forces SSL to check server certificate | |
242 | against our client certificate. | |
243 | """ | |
244 | sock = socket.create_connection((self.host, self.port), self.timeout) | |
245 | if self._tunnel_host: | |
246 | self.sock = sock | |
247 | self._tunnel() | |
248 | # If there's no CA File, don't force Server Certificate Check | |
249 | if self.ca_file: | |
250 | self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, | |
251 | ca_certs=self.ca_file, | |
252 | cert_reqs=ssl.CERT_REQUIRED) | |
253 | else: | |
254 | self.sock = ssl.wrap_socket(sock, self.key_file, | |
255 | self.cert_file, | |
256 | cert_reqs=ssl.CERT_NONE) | |
257 | ||
258 | ||
209 | 259 | class EapiConnection(object): |
210 | 260 | """Creates a connection to eAPI for sending and receiving eAPI requests |
211 | 261 | |
239 | 289 | the eAPI connection with |
240 | 290 | |
241 | 291 | """ |
292 | _auth_text = '{}:{}'.format(username, password) | |
293 | ||
242 | 294 | # Work around for Python 2.7/3.x compatibility |
243 | 295 | if int(sys.version[0]) > 2: |
244 | 296 | # For Python 3.x |
245 | _auth_text = '{}:{}'.format(username, password) | |
246 | 297 | _auth_bin = base64.encodebytes(_auth_text.encode()) |
247 | 298 | _auth = _auth_bin.decode() |
248 | 299 | _auth = _auth.replace('\n', '') |
249 | 300 | self._auth = _auth |
250 | 301 | else: |
251 | 302 | # For Python 2.7 |
252 | _auth = base64.encodestring('{}:{}'.format(username, password)) | |
303 | _auth = base64.encodestring(_auth_text) | |
253 | 304 | self._auth = str(_auth).replace('\n', '') |
254 | 305 | |
255 | _LOGGER.debug('Autentication string is: {}'.format(self._auth)) | |
306 | _LOGGER.debug('Autentication string is: {}:***'.format(username)) | |
256 | 307 | |
257 | 308 | def request(self, commands, encoding=None, reqid=None, **kwargs): |
258 | 309 | """Generates an eAPI request object |
296 | 347 | commands = make_iterable(commands) |
297 | 348 | reqid = id(self) if reqid is None else reqid |
298 | 349 | params = {'version': 1, 'cmds': commands, 'format': encoding} |
350 | streaming = False | |
299 | 351 | if 'autoComplete' in kwargs: |
300 | 352 | params['autoComplete'] = kwargs['autoComplete'] |
301 | 353 | if 'expandAliases' in kwargs: |
302 | 354 | params['expandAliases'] = kwargs['expandAliases'] |
355 | if 'streaming' in kwargs: | |
356 | streaming = kwargs['streaming'] | |
303 | 357 | return json.dumps({'jsonrpc': '2.0', 'method': 'runCmds', |
304 | 'params': params, 'id': str(reqid)}) | |
358 | 'params': params, 'id': str(reqid), | |
359 | 'streaming': streaming}) | |
305 | 360 | |
306 | 361 | def send(self, data): |
307 | 362 | """Sends the eAPI request to the destination node |
394 | 449 | reason=response.reason)) |
395 | 450 | _LOGGER.debug('Response content: {}'.format(response_content)) |
396 | 451 | |
452 | if response.status == 401: | |
453 | raise ConnectionError(str(self), '%s. %s' % (response.reason, | |
454 | response_content)) | |
455 | ||
397 | 456 | # Work around for Python 2.7/3.x compatibility |
398 | 457 | if not type(response_content) == str: |
399 | 458 | # For Python 3.x - decode bytes into string |
415 | 474 | return decoded |
416 | 475 | |
417 | 476 | # socket.error is deprecated in python 3 and replaced with OSError. |
418 | except (socket.error, OSError, ValueError) as exc: | |
419 | if isinstance(exc, socket.error) or isinstance(exc, OSError): | |
420 | self.socket_error = exc | |
477 | except (socket.error, OSError) as exc: | |
421 | 478 | _LOGGER.exception(exc) |
479 | self.socket_error = exc | |
422 | 480 | self.error = exc |
423 | error_msg = 'unable to connect to eAPI' | |
424 | if self.socket_error: | |
425 | error_msg = ('Socket error during eAPI connection: %s' | |
426 | % str(exc)) | |
481 | error_msg = 'Socket error during eAPI connection: %s' % str(exc) | |
427 | 482 | raise ConnectionError(str(self), error_msg) |
428 | ||
483 | except ValueError as exc: | |
484 | _LOGGER.exception(exc) | |
485 | self.socket_error = None | |
486 | self.error = exc | |
487 | raise ConnectionError(str(self), 'unable to connect to eAPI') | |
429 | 488 | finally: |
430 | 489 | self.transport.close() |
431 | 490 | |
545 | 604 | def disable_certificate_verification(self): |
546 | 605 | # SSL/TLS certificate verification is enabled by default in latest |
547 | 606 | # Python releases and causes self-signed certificates generated |
548 | # on EOS to fail validation (unless explicitely imported). | |
607 | # on EOS to fail validation (unless explicitly imported). | |
549 | 608 | # Disable the SSL/TLS certificate verification for now. |
550 | 609 | # Use the approach in PEP476 to disable certificate validation. |
551 | 610 | # TODO: |
554 | 613 | # temporary until a proper fix is implemented. |
555 | 614 | if hasattr(ssl, '_create_unverified_context'): |
556 | 615 | return ssl._create_unverified_context() |
616 | ||
617 | ||
618 | class HttpsEapiCertConnection(EapiConnection): | |
619 | def __init__(self, host, port=None, path=None, key_file=None, | |
620 | cert_file=None, ca_file=None, timeout=60, **kwargs): | |
621 | if key_file is None or cert_file is None: | |
622 | raise ValueError("For https_cert connections both a key_file and " | |
623 | "cert_file are required. A ca_file is also " | |
624 | "recommended") | |
625 | super(HttpsEapiCertConnection, self).__init__() | |
626 | port = port or DEFAULT_HTTPS_PORT | |
627 | path = path or DEFAULT_HTTP_PATH | |
628 | ||
629 | self.transport = HTTPSCertConnection(path, host, int(port), | |
630 | key_file=key_file, | |
631 | cert_file=cert_file, | |
632 | ca_file=ca_file, timeout=timeout) |
30 | 30 | # |
31 | 31 | import os |
32 | 32 | import sys |
33 | import imp | |
33 | import importlib | |
34 | 34 | import inspect |
35 | 35 | import logging |
36 | 36 | import logging.handlers |
77 | 77 | The module that was imported |
78 | 78 | |
79 | 79 | """ |
80 | parts = name.split('.') | |
81 | path = None | |
82 | module_name = '' | |
83 | fhandle = None | |
84 | ||
85 | for index, part in enumerate(parts): | |
86 | module_name = part if index == 0 else '%s.%s' % (module_name, part) | |
87 | path = [path] if path is not None else path | |
88 | ||
89 | try: | |
90 | fhandle, path, descr = imp.find_module(part, path) | |
91 | if module_name in sys.modules: | |
92 | # since imp.load_module works like reload, need to be sure not | |
93 | # to reload a previously loaded module | |
94 | mod = sys.modules[module_name] | |
95 | else: | |
96 | mod = imp.load_module(module_name, fhandle, path, descr) | |
97 | finally: | |
98 | # lets be sure to clean up after ourselves | |
99 | if fhandle: | |
100 | fhandle.close() | |
101 | ||
80 | if name in sys.modules: | |
81 | # Be sure not to reload a previously loaded module | |
82 | mod = sys.modules[name] | |
83 | else: | |
84 | mod = importlib.import_module(name) | |
102 | 85 | return mod |
103 | 86 | |
104 | 87 | |
183 | 166 | # Convert unicode values to strings for Python 2 |
184 | 167 | if isinstance(value, unicode): |
185 | 168 | value = str(value) |
186 | if isinstance(value, str): | |
169 | if isinstance(value, str) or isinstance(value, dict): | |
187 | 170 | value = [value] |
188 | 171 | |
189 | if not isinstance(value, collections.Iterable): | |
190 | raise TypeError('value must be an iterable object') | |
172 | if sys.version_info <= (3, 3): | |
173 | if not isinstance(value, collections.Iterable): | |
174 | raise TypeError('value must be an iterable object') | |
175 | else: | |
176 | if not isinstance(value, collections.abc.Iterable): | |
177 | raise TypeError('value must be an iterable object') | |
191 | 178 | |
192 | 179 | return value |
193 | 180 |
0 | Metadata-Version: 1.1 | |
0 | Metadata-Version: 2.1 | |
1 | 1 | Name: pyeapi |
2 | Version: 0.8.1 | |
2 | Version: 0.8.4rc0 | |
3 | 3 | Summary: Python Client for eAPI |
4 | 4 | Home-page: https://github.com/arista-eosplus/pyeapi |
5 | 5 | Author: Arista EOS+ CS |
6 | 6 | Author-email: eosplus-dev@arista.com |
7 | 7 | License: BSD-3 |
8 | Description: The Python Client for eAPI | |
9 | ========================== | |
10 | ||
11 | The Python Client for eAPI (pyeapi) is a native Python library wrapper around | |
12 | Arista EOS eAPI. It provides a set of Python language bindings for configuring | |
13 | Arista EOS nodes. | |
14 | ||
15 | The Python library can be used to communicate with EOS either locally | |
16 | (on-box) or remotely (off-box). It uses a standard INI-style configuration file | |
17 | to specify one or more nodes and connection profiles. | |
18 | ||
19 | The pyeapi library also provides an API layer for building native Python | |
20 | objects to interact with the destination nodes. The API layer is a convenient | |
21 | implementation for working with the EOS configuration and is extensible for | |
22 | developing custom implementations. | |
23 | ||
24 | This library is freely provided to the open source community for building | |
25 | robust applications using Arista EOS. Support is provided as best effort | |
26 | through Github issues. | |
27 | ||
28 | 8 | Keywords: networking arista eos eapi |
29 | 9 | Platform: UNKNOWN |
30 | 10 | Classifier: Development Status :: 4 - Beta |
34 | 14 | Classifier: Programming Language :: Python :: 2 |
35 | 15 | Classifier: Programming Language :: Python :: 2.7 |
36 | 16 | Classifier: Programming Language :: Python :: 3 |
37 | Classifier: Programming Language :: Python :: 3.4 | |
17 | Classifier: Programming Language :: Python :: 3.8 | |
18 | Provides-Extra: dev | |
19 | Provides-Extra: test | |
20 | License-File: LICENSE | |
21 | ||
22 | The Python Client for eAPI | |
23 | ========================== | |
24 | ||
25 | The Python Client for eAPI (pyeapi) is a native Python library wrapper around | |
26 | Arista EOS eAPI. It provides a set of Python language bindings for configuring | |
27 | Arista EOS nodes. | |
28 | ||
29 | The Python library can be used to communicate with EOS either locally | |
30 | (on-box) or remotely (off-box). It uses a standard INI-style configuration file | |
31 | to specify one or more nodes and connection profiles. | |
32 | ||
33 | The pyeapi library also provides an API layer for building native Python | |
34 | objects to interact with the destination nodes. The API layer is a convenient | |
35 | implementation for working with the EOS configuration and is extensible for | |
36 | developing custom implementations. | |
37 | ||
38 | This library is freely provided to the open source community for building | |
39 | robust applications using Arista EOS. Support is provided as best effort | |
40 | through Github issues. | |
41 | ||
42 |
10 | 10 | docs/Makefile |
11 | 11 | docs/conf.py |
12 | 12 | docs/configfile.rst |
13 | docs/configsessions.rst | |
13 | 14 | docs/contribute.rst |
14 | 15 | docs/description.rst |
15 | 16 | docs/examples.rst |
37 | 38 | docs/release-notes-0.7.0.rst |
38 | 39 | docs/release-notes-0.8.0.rst |
39 | 40 | docs/release-notes-0.8.1.rst |
41 | docs/release-notes-0.8.2.rst | |
42 | docs/release-notes-0.8.3.rst | |
43 | docs/release-notes-0.8.4.rst | |
40 | 44 | docs/release-notes.rst |
41 | 45 | docs/requirements.rst |
42 | 46 | docs/subinterfaces.rst |
43 | 47 | docs/support.rst |
44 | docs/api_modules/_list_of_modules.rst | |
45 | docs/api_modules/abstract.rst | |
46 | docs/api_modules/acl.rst | |
47 | docs/api_modules/bgp.rst | |
48 | docs/api_modules/interfaces.rst | |
49 | docs/api_modules/ipinterfaces.rst | |
50 | docs/api_modules/mlag.rst | |
51 | docs/api_modules/ntp.rst | |
52 | docs/api_modules/ospf.rst | |
53 | docs/api_modules/routemaps.rst | |
54 | docs/api_modules/spanningtree.rst | |
55 | docs/api_modules/staticroute.rst | |
56 | docs/api_modules/stp.rst | |
57 | docs/api_modules/switchports.rst | |
58 | docs/api_modules/system.rst | |
59 | docs/api_modules/users.rst | |
60 | docs/api_modules/varp.rst | |
61 | docs/api_modules/vlans.rst | |
62 | docs/api_modules/vrfs.rst | |
63 | docs/api_modules/vrrp.rst | |
64 | docs/client_modules/_list_of_modules.rst | |
65 | docs/client_modules/client.rst | |
66 | docs/client_modules/eapilib.rst | |
67 | docs/client_modules/utils.rst | |
68 | 48 | examples/get_config.py |
69 | 49 | examples/nodes.conf |
70 | 50 | examples/simple.py |
48 | 48 | 'Programming Language :: Python :: 2', |
49 | 49 | 'Programming Language :: Python :: 2.7', |
50 | 50 | 'Programming Language :: Python :: 3', |
51 | 'Programming Language :: Python :: 3.4', | |
51 | 'Programming Language :: Python :: 3.8', | |
52 | 52 | ], |
53 | 53 | |
54 | 54 | keywords='networking arista eos eapi', |
0 | [connection:veos] | |
1 | host: localhost | |
2 | username: eapitest | |
3 | password: test | |
0 | [connection:veos01] | |
1 | host: 192.168.1.16 | |
2 | username: eapi | |
3 | password: password | |
4 | 4 | transport: http |
5 | port: 8080 |
32 | 32 | match interface Ethernet2 |
33 | 33 | continue 200 |
34 | 34 | ! |
35 | route-map FOO-BAR permit 10 | |
36 | match as 2000 | |
37 | match source-protocol ospf | |
38 | match interface Ethernet2 | |
39 | continue 200 | |
40 | ! | |
41 | route-map FOO-BAR deny 20 | |
42 | match as 2000 | |
43 | match source-protocol ospf | |
44 | match interface Ethernet2 | |
45 | continue 200 | |
46 | ! |
314 | 314 | spanning-tree max-hops 20 |
315 | 315 | no spanning-tree portfast bpduguard default |
316 | 316 | no spanning-tree portfast bpdufilter default |
317 | spanning-tree bridge assurance | |
318 | no spanning-tree loopguard default | |
317 | spanning-tree transmit active | |
318 | no spanning-tree guard loop default | |
319 | 319 | no spanning-tree portchannel guard misconfig |
320 | 320 | spanning-tree bpduguard rate-limit default |
321 | 321 | logging event spanning-tree global |
347 | 347 | no aaa accounting dot1x default |
348 | 348 | no aaa accounting commands all default |
349 | 349 | ! |
350 | no enable secret | |
350 | no enable password | |
351 | 351 | no aaa root |
352 | 352 | aaa authentication policy local allow-nopassword-remote-login |
353 | 353 | no aaa authorization policy local default-role |
390 | 390 | ! |
391 | 391 | vlan 100 |
392 | 392 | name mytest |
393 | state active | |
394 | no private-vlan | |
395 | ! | |
396 | vlan 200-202,204 | |
397 | name grouping | |
393 | 398 | state active |
394 | 399 | no private-vlan |
395 | 400 | ! |
33 | 33 | import string |
34 | 34 | import unittest |
35 | 35 | |
36 | from mock import Mock | |
36 | from mock import MagicMock as Mock | |
37 | 37 | |
38 | 38 | from pyeapi.client import Node |
39 | 39 | |
70 | 70 | |
71 | 71 | def setUp(self): |
72 | 72 | self.node = Node(None) |
73 | ||
73 | self.node._version_number = '4.17.1.1' | |
74 | 74 | self.node._running_config = self.config |
75 | 75 | |
76 | 76 | self.mock_config = Mock(name='node.config') |
225 | 225 | # Verify set_vrf returns False if no vrf by name is configured |
226 | 226 | result = dut.api('interfaces').set_vrf(intf, 'test') |
227 | 227 | self.assertFalse(result) |
228 | dut.config('vrf definition test') | |
228 | if dut.version_number >= '4.23': | |
229 | dut.config('vrf instance test') | |
230 | else: | |
231 | dut.config('vrf definition test') | |
229 | 232 | # Verify interface has vrf applied |
230 | 233 | result = dut.api('interfaces').set_vrf(intf, 'test') |
231 | 234 | self.assertTrue(result) |
232 | 235 | config = dut.run_commands('show running-config interfaces %s' % |
233 | 236 | intf, 'text') |
234 | self.assertIn('vrf forwarding test', config[0]['output']) | |
237 | if dut.version_number >= '4.23': | |
238 | self.assertIn('vrf test', config[0]['output']) | |
239 | else: | |
240 | self.assertIn('vrf forwarding test', config[0]['output']) | |
235 | 241 | # Verify interface has vrf removed |
236 | 242 | result = dut.api('interfaces').set_vrf(intf, 'test', disable=True) |
237 | 243 | self.assertTrue(result) |
238 | 244 | config = dut.run_commands('show running-config interfaces %s' % |
239 | 245 | intf, 'text') |
240 | self.assertNotIn('vrf forwarding test', config[0]['output']) | |
241 | # Remove test vrf | |
242 | dut.config('no vrf definition test') | |
246 | if dut.version_number >= '4.23': | |
247 | self.assertIn('vrf test', config[0]['output']) | |
248 | # Remove test vrf | |
249 | dut.config('no vrf instance test') | |
250 | else: | |
251 | self.assertIn('vrf forwarding test', config[0]['output']) | |
252 | # Remove test vrf | |
253 | dut.config('no vrf definition test') | |
243 | 254 | |
244 | 255 | |
245 | 256 | class TestPortchannelInterface(DutSystemTest): |
623 | 634 | api = dut.api('interfaces') |
624 | 635 | instance = api.update_vlan('Vxlan1', '10', '10') |
625 | 636 | self.assertTrue(instance) |
626 | self.contains('vxlan vlan 10 vni 10', dut) | |
637 | self.contains('vxlan vlan add 10 vni 10', dut) | |
627 | 638 | |
628 | 639 | def test_remove_vlan(self): |
629 | 640 | for dut in self.duts: |
631 | 642 | api = dut.api('interfaces') |
632 | 643 | instance = api.remove_vlan('Vxlan1', '10') |
633 | 644 | self.assertTrue(instance) |
634 | self.notcontains('vxlan vlan 10 vni 10', dut) | |
645 | self.notcontains('vxlan vlan remove 10 vni 10', dut) | |
635 | 646 | |
636 | 647 | |
637 | 648 | if __name__ == '__main__': |
30 | 30 | # |
31 | 31 | import os |
32 | 32 | import unittest |
33 | import time | |
33 | 34 | |
34 | 35 | import sys |
35 | 36 | sys.path.append(os.path.join(os.path.dirname(__file__), '../lib')) |
55 | 56 | intf = random_interface(dut) |
56 | 57 | dut.config(['default interface %s' % intf, 'interface %s' % intf, |
57 | 58 | 'no switchport']) |
59 | time.sleep(2) | |
58 | 60 | result = dut.api('ipinterfaces').get(intf) |
59 | 61 | self.assertIsNone(result['address']) |
60 | 62 |
42 | 42 | |
43 | 43 | def test_get(self): |
44 | 44 | for dut in self.duts: |
45 | dut.config(['ntp source Ethernet1', 'ntp server 99.99.1.1']) | |
45 | if dut.version_number >= '4.23': | |
46 | dut.config(['ntp local-interface Ethernet1', 'ntp server 99.99.1.1']) | |
47 | else: | |
48 | dut.config(['ntp source Ethernet1', 'ntp server 99.99.1.1']) | |
46 | 49 | response = dut.api('ntp').get() |
47 | 50 | self.assertIsNotNone(response) |
48 | 51 | |
49 | 52 | def test_create(self): |
50 | 53 | intf = 'Ethernet1' |
51 | 54 | for dut in self.duts: |
52 | dut.config(['no ntp source']) | |
55 | if dut.version_number >= '4.23': | |
56 | dut.config(['no ntp local-interface']) | |
57 | else: | |
58 | dut.config(['no ntp source']) | |
53 | 59 | response = dut.api('ntp').create(intf) |
54 | 60 | self.assertTrue(response) |
55 | 61 | response = dut.api('ntp').get() |
57 | 63 | |
58 | 64 | def test_delete(self): |
59 | 65 | for dut in self.duts: |
60 | dut.config(['ntp source Ethernet1']) | |
66 | if dut.version_number >= '4.23': | |
67 | dut.config(['ntp local-interface Ethernet1']) | |
68 | else: | |
69 | dut.config(['ntp source Ethernet1']) | |
61 | 70 | response = dut.api('ntp').delete() |
62 | 71 | self.assertTrue(response) |
63 | 72 | response = dut.api('ntp').get() |
65 | 74 | |
66 | 75 | def test_default(self): |
67 | 76 | for dut in self.duts: |
68 | dut.config(['ntp source Ethernet1']) | |
77 | if dut.version_number >= '4.23': | |
78 | dut.config(['ntp local-interface Ethernet1']) | |
79 | else: | |
80 | dut.config(['ntp source Ethernet1']) | |
69 | 81 | response = dut.api('ntp').default() |
70 | 82 | self.assertTrue(response) |
71 | 83 | response = dut.api('ntp').get() |
74 | 86 | def test_set_source_interface(self): |
75 | 87 | intf = 'Ethernet1' |
76 | 88 | for dut in self.duts: |
77 | dut.config(['ntp source Loopback0']) | |
89 | if dut.version_number >= '4.23': | |
90 | dut.config(['ntp local-interface Loopback0']) | |
91 | else: | |
92 | dut.config(['ntp source Loopback0']) | |
78 | 93 | response = dut.api('ntp').set_source_interface(intf) |
79 | 94 | self.assertTrue(response) |
80 | 95 | response = dut.api('ntp').get() |
83 | 98 | def test_add_server_single(self): |
84 | 99 | server = '10.10.10.35' |
85 | 100 | for dut in self.duts: |
86 | dut.config(['ntp source Ethernet1', 'no ntp']) | |
101 | if dut.version_number >= '4.23': | |
102 | dut.config(['ntp local-interface Ethernet1', 'no ntp']) | |
103 | else: | |
104 | dut.config(['ntp source Ethernet1', 'no ntp']) | |
87 | 105 | response = dut.api('ntp').add_server(server) |
88 | 106 | self.assertTrue(response) |
89 | 107 | response = dut.api('ntp').get() |
94 | 112 | def test_add_server_multiple(self): |
95 | 113 | servers = ['10.10.10.37', '10.10.10.36', '10.10.10.34'] |
96 | 114 | for dut in self.duts: |
97 | dut.config(['ntp source Ethernet1', 'no ntp']) | |
115 | if dut.version_number >= '4.23': | |
116 | dut.config(['ntp local-interface Ethernet1', 'no ntp']) | |
117 | else: | |
118 | dut.config(['ntp source Ethernet1', 'no ntp']) | |
98 | 119 | for server in servers: |
99 | 120 | response = dut.api('ntp').add_server(server) |
100 | 121 | self.assertTrue(response) |
106 | 127 | def test_add_server_prefer(self): |
107 | 128 | server = '10.10.10.35' |
108 | 129 | for dut in self.duts: |
109 | dut.config(['ntp source Ethernet1', 'no ntp']) | |
130 | if dut.version_number >= '4.23': | |
131 | dut.config(['ntp local-interface Ethernet1', 'no ntp']) | |
132 | else: | |
133 | dut.config(['ntp source Ethernet1', 'no ntp']) | |
110 | 134 | response = dut.api('ntp').add_server(server, prefer=False) |
111 | 135 | self.assertTrue(response) |
112 | 136 | response = dut.api('ntp').get() |
119 | 143 | |
120 | 144 | def test_add_server_invalid(self): |
121 | 145 | for dut in self.duts: |
122 | dut.config(['ntp source Ethernet1', 'no ntp']) | |
146 | if dut.version_number >= '4.23': | |
147 | dut.config(['ntp local-interface Ethernet1', 'no ntp']) | |
148 | else: | |
149 | dut.config(['ntp source Ethernet1', 'no ntp']) | |
123 | 150 | with self.assertRaises(ValueError): |
124 | 151 | dut.api('ntp').add_server(None) |
125 | 152 | dut.api('ntp').add_server('') |
129 | 156 | server = '10.10.10.35' |
130 | 157 | servers = ['10.10.10.37', '10.10.10.36', '10.10.10.34'] |
131 | 158 | for dut in self.duts: |
132 | dut.config(['ntp source Ethernet1', 'no ntp', | |
133 | 'ntp server %s' % server]) | |
159 | if dut.version_number >= '4.23': | |
160 | dut.config(['ntp local-interface Ethernet1', 'no ntp', | |
161 | 'ntp server %s' % server]) | |
162 | else: | |
163 | dut.config(['ntp source Ethernet1', 'no ntp', | |
164 | 'ntp server %s' % server]) | |
134 | 165 | for addserver in servers: |
135 | 166 | dut.config(['ntp server %s' % addserver]) |
136 | 167 | response = dut.api('ntp').remove_server(server) |
143 | 174 | def test_remove_all_servers(self): |
144 | 175 | servers = ['10.10.10.37', '10.10.10.36', '10.10.10.34'] |
145 | 176 | for dut in self.duts: |
146 | dut.config(['ntp source Ethernet1', 'no ntp']) | |
177 | if dut.version_number >= '4.23': | |
178 | dut.config(['ntp local-interface Ethernet1', 'no ntp']) | |
179 | else: | |
180 | dut.config(['ntp source Ethernet1', 'no ntp']) | |
147 | 181 | for addserver in servers: |
148 | 182 | dut.config(['ntp server %s' % addserver]) |
149 | 183 | response = dut.api('ntp').remove_all_servers() |
60 | 60 | dut.config(['no route-map TEST deny 10', |
61 | 61 | 'route-map TEST deny 10', |
62 | 62 | 'set weight 100', |
63 | 'no route-map TEST2 permit 50', | |
64 | 'route-map TEST2 permit 50', | |
63 | 'no route-map TEST-2 permit 50', | |
64 | 'route-map TEST-2 permit 50', | |
65 | 65 | 'match tag 50']) |
66 | 66 | result = dut.api('routemaps').getall() |
67 | 67 | self.assertIn(('TEST'), result) |
68 | self.assertIn(('TEST2'), result) | |
68 | self.assertIn(('TEST-2'), result) | |
69 | 69 | |
70 | 70 | def test_create(self): |
71 | 71 | for dut in self.duts: |
51 | 51 | |
52 | 52 | def _next_hop(): |
53 | 53 | next_hop = choice(NEXT_HOPS) |
54 | if next_hop is 'Null0': | |
54 | if next_hop == 'Null0': | |
55 | 55 | return (next_hop, None) |
56 | 56 | ip1 = random_int(0, 223) |
57 | 57 | ip2 = random_int(0, 255) |
58 | 58 | ip3 = random_int(0, 255) |
59 | 59 | ip4 = random_int(0, 255) |
60 | 60 | ip_addr = "%s.%s.%s.%s" % (ip1, ip2, ip3, ip4) |
61 | if next_hop is 'IP': | |
61 | if next_hop == 'IP': | |
62 | 62 | return (ip_addr, None) |
63 | 63 | return (next_hop, ip_addr) |
64 | 64 |
72 | 72 | |
73 | 73 | def test_get_banner_with_EOF(self): |
74 | 74 | for dut in self.duts: |
75 | motd_banner_value = '!!!newlinebaner\n\nSecondLIneEOF!!!newlinebanner\n' | |
75 | motd_banner_value = '!!!newlinebaner\nSecondLIneEOF!!!newlinebanner\n' | |
76 | 76 | dut.config([dict(cmd="banner motd", input=motd_banner_value)]) |
77 | 77 | resp = dut.api('system').get() |
78 | 78 | self.assertEqual(resp['banner_motd'], motd_banner_value.rstrip()) |
159 | 159 | |
160 | 160 | def test_set_banner_motd_donkey(self): |
161 | 161 | for dut in self.duts: |
162 | donkey_chicken = """ | |
162 | donkey_chicken = r""" | |
163 | 163 | /\ /\ |
164 | 164 | ( \\ // ) |
165 | 165 | \ \\ // / |
186 | 186 | self.assertTrue(resp, 'dut=%s' % dut) |
187 | 187 | self.assertIn(donkey_chicken, dut.running_config) |
188 | 188 | |
189 | ||
190 | ||
191 | 189 | def test_set_banner_motd_default(self): |
192 | 190 | for dut in self.duts: |
193 | 191 | dut.config([dict(cmd="banner motd", |
48 | 48 | |
49 | 49 | def test_get(self): |
50 | 50 | for dut in self.duts: |
51 | dut.config(['no username test', 'username test nopassword', | |
52 | 'username test sshkey %s' % TEST_SSH_KEY]) | |
51 | if dut.version_number >= '4.23': | |
52 | dut.config(['no username test', 'username test nopassword', | |
53 | 'username test ssh-key %s' % TEST_SSH_KEY]) | |
54 | else: | |
55 | dut.config(['no username test', 'username test nopassword', | |
56 | 'username test sshkey %s' % TEST_SSH_KEY]) | |
53 | 57 | |
54 | 58 | result = dut.api('users').get('test') |
55 | values = dict(nopassword=True, privilege='1', secret='', | |
56 | role='', format='', | |
57 | sshkey=TEST_SSH_KEY) | |
58 | ||
59 | if dut.version_number >= '4.23': | |
60 | values = dict(nopassword=True, privilege='1', secret='', | |
61 | role='', format='') | |
62 | values["ssh-key"] = TEST_SSH_KEY | |
63 | else: | |
64 | values = dict(nopassword=True, privilege='1', secret='', | |
65 | role='', format='', sshkey=TEST_SSH_KEY) | |
59 | 66 | result = self.sort_dict_by_keys(result) |
60 | 67 | values = self.sort_dict_by_keys(values) |
61 | 68 | |
138 | 145 | dut.config(['no username test', 'username test nopassword']) |
139 | 146 | api = dut.api('users') |
140 | 147 | self.assertIn('username test privilege 1 nopassword', api.config) |
141 | self.assertNotIn('username test sshkey', api.config) | |
148 | if dut.version_number >= '4.23': | |
149 | self.assertNotIn('username test ssh-key', api.config) | |
150 | else: | |
151 | self.assertNotIn('username test sshkey', api.config) | |
142 | 152 | result = api.set_sshkey('test', TEST_SSH_KEY) |
143 | 153 | self.assertTrue(result) |
144 | self.assertIn('username test sshkey %s' % TEST_SSH_KEY, api.config) | |
154 | if dut.version_number >= '4.23': | |
155 | self.assertIn('username test ssh-key %s' % TEST_SSH_KEY, api.config) | |
156 | else: | |
157 | self.assertIn('username test sshkey %s' % TEST_SSH_KEY, api.config) | |
145 | 158 | |
146 | 159 | def test_set_sshkey_with_empty_string(self): |
147 | 160 | for dut in self.duts: |
148 | 161 | dut.config(['no username test', 'username test nopassword']) |
149 | 162 | api = dut.api('users') |
150 | 163 | self.assertIn('username test privilege 1 nopassword', api.config) |
151 | self.assertNotIn('username test sshkey', api.config) | |
164 | if dut.version_number >= '4.23': | |
165 | self.assertNotIn('username test ssh-key', api.config) | |
166 | else: | |
167 | self.assertNotIn('username test sshkey', api.config) | |
152 | 168 | result = api.set_sshkey('test', '') |
153 | 169 | self.assertTrue(result) |
154 | self.assertNotIn('username test sshkey %s' | |
155 | % TEST_SSH_KEY, api.config) | |
170 | if dut.version_number >= '4.23': | |
171 | self.assertNotIn('username test ssh-key %s' | |
172 | % TEST_SSH_KEY, api.config) | |
173 | else: | |
174 | self.assertNotIn('username test sshkey %s' | |
175 | % TEST_SSH_KEY, api.config) | |
156 | 176 | |
157 | 177 | def test_set_sshkey_with_None(self): |
158 | 178 | for dut in self.duts: |
159 | 179 | dut.config(['no username test', 'username test nopassword']) |
160 | 180 | api = dut.api('users') |
161 | 181 | self.assertIn('username test privilege 1 nopassword', api.config) |
162 | self.assertNotIn('username test sshkey', api.config) | |
182 | if dut.version_number >= '4.23': | |
183 | self.assertNotIn('username test ssh-key', api.config) | |
184 | else: | |
185 | self.assertNotIn('username test sshkey', api.config) | |
163 | 186 | result = api.set_sshkey('test', None) |
164 | 187 | self.assertTrue(result) |
165 | self.assertNotIn('username test sshkey %s' | |
166 | % TEST_SSH_KEY, api.config) | |
188 | if dut.version_number >= '4.23': | |
189 | self.assertNotIn('username test ssh-key %s' | |
190 | % TEST_SSH_KEY, api.config) | |
191 | else: | |
192 | self.assertNotIn('username test sshkey %s' | |
193 | % TEST_SSH_KEY, api.config) | |
167 | 194 | |
168 | 195 | def test_set_sshkey_with_no_value(self): |
169 | 196 | for dut in self.duts: |
173 | 200 | self.assertIn('username test privilege 1 nopassword', api.config) |
174 | 201 | result = api.set_sshkey('test', disable=True) |
175 | 202 | self.assertTrue(result) |
176 | self.assertNotIn('username test sshkey %s' % TEST_SSH_KEY, | |
177 | api.config) | |
203 | if dut.version_number >= '4.23': | |
204 | self.assertNotIn('username test ssh-key %s' % TEST_SSH_KEY, | |
205 | api.config) | |
206 | else: | |
207 | self.assertNotIn('username test sshkey %s' % TEST_SSH_KEY, | |
208 | api.config) | |
178 | 209 | |
179 | 210 | |
180 | 211 | if __name__ == '__main__': |
42 | 42 | |
43 | 43 | def test_get(self): |
44 | 44 | for dut in self.duts: |
45 | dut.config(['no vrf definition blah', 'vrf definition blah', | |
46 | 'rd 10:10', 'description blah desc']) | |
45 | if dut.version_number >= '4.23': | |
46 | dut.config(['no vrf instance blah', 'vrf instance blah', | |
47 | 'rd 10:10', 'description blah desc']) | |
48 | else: | |
49 | dut.config(['no vrf definition blah', 'vrf definition blah', | |
50 | 'rd 10:10', 'description blah desc']) | |
47 | 51 | response = dut.api('vrfs').get('blah') |
48 | 52 | values = dict(rd='10:10', vrf_name='blah', description='blah desc', |
49 | 53 | ipv4_routing=False, ipv6_routing=False) |
50 | 54 | self.assertEqual(values, response) |
51 | dut.config(['no vrf definition blah']) | |
55 | if dut.version_number >= '4.23': | |
56 | dut.config(['no vrf instance blah']) | |
57 | else: | |
58 | dut.config(['no vrf definition blah']) | |
52 | 59 | |
53 | 60 | def test_getall(self): |
54 | 61 | for dut in self.duts: |
55 | dut.config(['no vrf definition blah', 'vrf definition blah', | |
56 | 'no vrf definition second', 'vrf definition second']) | |
62 | if dut.version_number >= '4.23': | |
63 | dut.config(['no vrf instance blah', 'vrf instance blah', | |
64 | 'no vrf instance second', 'vrf instance second']) | |
65 | else: | |
66 | dut.config(['no vrf definition blah', 'vrf definition blah', | |
67 | 'no vrf definition second', 'vrf definition second']) | |
57 | 68 | response = dut.api('vrfs').getall() |
58 | 69 | self.assertIsInstance(response, dict, 'dut=%s' % dut) |
59 | 70 | self.assertEqual(len(response), 2) |
60 | 71 | for vrf_name in ['blah', 'second']: |
61 | 72 | self.assertIn(vrf_name, response, 'dut=%s' % dut) |
62 | dut.config(['no vrf definition blah', 'no vrf definition second']) | |
73 | if dut.version_number >= '4.23': | |
74 | dut.config(['no vrf instance blah', 'no vrf instance second']) | |
75 | else: | |
76 | dut.config(['no vrf definition blah', 'no vrf definition second']) | |
63 | 77 | |
64 | 78 | def test_create_and_return_true(self): |
65 | 79 | for dut in self.duts: |
66 | dut.config(['no vrf definition blah', 'vrf definition blah']) | |
80 | if dut.version_number >= '4.23': | |
81 | dut.config(['no vrf instance blah', 'vrf instance blah']) | |
82 | else: | |
83 | dut.config(['no vrf definition blah', 'vrf definition blah']) | |
67 | 84 | result = dut.api('vrfs').create('blah') |
68 | 85 | self.assertTrue(result, 'dut=%s' % dut) |
69 | 86 | config = dut.run_commands('show vrf', encoding='text') |
70 | 87 | self.assertIn('blah', config[0]['output'], 'dut=%s' % dut) |
71 | dut.config(['no vrf definition blah']) | |
88 | if dut.version_number >= '4.23': | |
89 | dut.config(['no vrf instance blah']) | |
90 | else: | |
91 | dut.config(['no vrf definition blah']) | |
72 | 92 | |
73 | 93 | def test_create_with_valid_rd(self): |
74 | 94 | for dut in self.duts: |
75 | dut.config(['no vrf definition blah', 'vrf definition blah']) | |
95 | if dut.version_number >= '4.23': | |
96 | dut.config(['no vrf instance blah', 'vrf instance blah']) | |
97 | else: | |
98 | dut.config(['no vrf definition blah', 'vrf definition blah']) | |
76 | 99 | result = dut.api('vrfs').create('blah', rd='10:10') |
77 | 100 | self.assertTrue(result, 'dut=%s' % dut) |
78 | 101 | command = 'show running-config section vrf' |
79 | 102 | config = dut.run_commands(command, encoding='text') |
80 | self.assertIn('vrf definition blah', config[0]['output'], | |
81 | 'dut=%s' % dut) | |
103 | if dut.version_number >= '4.23': | |
104 | self.assertIn('vrf instance blah', config[0]['output'], | |
105 | 'dut=%s' % dut) | |
106 | else: | |
107 | self.assertIn('vrf definition blah', config[0]['output'], | |
108 | 'dut=%s' % dut) | |
82 | 109 | self.assertIn('rd 10:10', config[0]['output'], 'dut=%s' % dut) |
83 | dut.config(['no vrf definition blah']) | |
110 | if dut.version_number >= '4.23': | |
111 | dut.config(['no vrf instance blah']) | |
112 | else: | |
113 | dut.config(['no vrf definition blah']) | |
84 | 114 | |
85 | 115 | def test_create_and_return_false(self): |
86 | 116 | for dut in self.duts: |
89 | 119 | |
90 | 120 | def test_create_with_invalid_rd(self): |
91 | 121 | for dut in self.duts: |
92 | dut.config(['no vrf definition blah', 'vrf definition blah']) | |
122 | if dut.version_number >= '4.23': | |
123 | dut.config(['no vrf instance blah', 'vrf instance blah']) | |
124 | else: | |
125 | dut.config(['no vrf definition blah', 'vrf definition blah']) | |
93 | 126 | result = dut.api('vrfs').create('blah', rd='192.168.1.1:99999999') |
94 | 127 | self.assertFalse(result, 'dut=%s' % dut) |
95 | 128 | command = 'show running-config section vrf' |
96 | 129 | config = dut.run_commands(command, encoding='text') |
97 | self.assertIn('vrf definition blah', config[0]['output'], | |
98 | 'dut=%s' % dut) | |
130 | if dut.version_number >= '4.23': | |
131 | self.assertIn('vrf instance blah', config[0]['output'], | |
132 | 'dut=%s' % dut) | |
133 | else: | |
134 | self.assertIn('vrf definition blah', config[0]['output'], | |
135 | 'dut=%s' % dut) | |
99 | 136 | self.assertNotIn('rd', config[0]['output'], |
100 | 137 | 'dut=%s' % dut) |
101 | dut.config(['no vrf definition blah']) | |
138 | if dut.version_number >= '4.23': | |
139 | dut.config(['no vrf instance blah']) | |
140 | else: | |
141 | dut.config(['no vrf definition blah']) | |
102 | 142 | |
103 | 143 | def test_delete_and_return_true(self): |
104 | 144 | for dut in self.duts: |
105 | dut.config('vrf definition blah') | |
145 | if dut.version_number >= '4.23': | |
146 | dut.config('vrf instance blah') | |
147 | else: | |
148 | dut.config('vrf definition blah') | |
106 | 149 | result = dut.api('vrfs').delete('blah') |
107 | 150 | self.assertTrue(result, 'dut=%s' % dut) |
108 | 151 | command = 'show running-config section vrf' |
109 | 152 | config = dut.run_commands(command, encoding='text') |
110 | self.assertNotIn('vrf definition blah', config[0]['output'], | |
111 | 'dut=%s' % dut) | |
153 | if dut.version_number >= '4.23': | |
154 | self.assertNotIn('vrf instance blah', config[0]['output'], | |
155 | 'dut=%s' % dut) | |
156 | else: | |
157 | self.assertNotIn('vrf definition blah', config[0]['output'], | |
158 | 'dut=%s' % dut) | |
112 | 159 | |
113 | 160 | def test_delete_and_return_false(self): |
114 | 161 | for dut in self.duts: |
117 | 164 | |
118 | 165 | def test_default(self): |
119 | 166 | for dut in self.duts: |
120 | dut.config(['no vrf definition blah', 'vrf definition blah', | |
121 | 'description test desc']) | |
167 | if dut.version_number >= '4.23': | |
168 | dut.config(['no vrf instance blah', 'vrf instance blah', | |
169 | 'description test desc']) | |
170 | else: | |
171 | dut.config(['no vrf definition blah', 'vrf definition blah', | |
172 | 'description test desc']) | |
122 | 173 | result = dut.api('vrfs').default('blah') |
123 | 174 | self.assertTrue(result, 'dut=%s' % dut) |
124 | 175 | command = 'show running-config section vrf' |
125 | 176 | config = dut.run_commands(command, encoding='text') |
126 | self.assertNotIn('vrf definition blah', config[0]['output'], | |
127 | 'dut=%s' % dut) | |
128 | dut.config(['no vrf definition blah']) | |
177 | if dut.version_number >= '4.23': | |
178 | self.assertNotIn('vrf instance blah', config[0]['output'], | |
179 | 'dut=%s' % dut) | |
180 | dut.config(['no vrf instance blah']) | |
181 | else: | |
182 | self.assertNotIn('vrf definition blah', config[0]['output'], | |
183 | 'dut=%s' % dut) | |
184 | dut.config(['no vrf definition blah']) | |
129 | 185 | |
130 | 186 | def test_set_rd(self): |
131 | 187 | for dut in self.duts: |
132 | dut.config(['no vrf definition blah', 'vrf definition blah']) | |
188 | if dut.version_number >= '4.23': | |
189 | dut.config(['no vrf instance blah', 'vrf instance blah']) | |
190 | else: | |
191 | dut.config(['no vrf definition blah', 'vrf definition blah']) | |
133 | 192 | result = dut.api('vrfs').set_rd('blah', '10:10') |
134 | 193 | self.assertTrue(result, 'dut=%s' % dut) |
135 | 194 | command = 'show running-config section vrf' |
136 | 195 | config = dut.run_commands(command, encoding='text') |
137 | 196 | self.assertIn('blah', config[0]['output'], 'dut=%s' % dut) |
138 | 197 | self.assertIn('10:10', config[0]['output'], 'dut=%s' % dut) |
139 | dut.config(['no vrf definition blah']) | |
198 | if dut.version_number >= '4.23': | |
199 | dut.config(['no vrf instance blah']) | |
200 | else: | |
201 | dut.config(['no vrf definition blah']) | |
140 | 202 | |
141 | 203 | def test_set_description(self): |
142 | 204 | for dut in self.duts: |
143 | 205 | description = random_string() |
144 | dut.config(['no vrf definition blah', 'vrf definition blah']) | |
206 | if dut.version_number >= '4.23': | |
207 | dut.config(['no vrf instance blah', 'vrf instance blah']) | |
208 | else: | |
209 | dut.config(['no vrf definition blah', 'vrf definition blah']) | |
145 | 210 | result = dut.api('vrfs').set_description('blah', description) |
146 | 211 | self.assertTrue(result, 'dut=%s' % dut) |
147 | 212 | command = 'show running-config section vrf' |
153 | 218 | config = dut.run_commands(command, encoding='text') |
154 | 219 | self.assertNotIn('description %s' % description, |
155 | 220 | config[0]['output'], 'dut=%s' % dut) |
156 | dut.config(['no vrf definition blah']) | |
221 | if dut.version_number >= '4.23': | |
222 | dut.config(['no vrf instance blah']) | |
223 | else: | |
224 | dut.config(['no vrf definition blah']) | |
157 | 225 | |
158 | 226 | def test_set_ipv4_routing(self): |
159 | 227 | for dut in self.duts: |
160 | dut.config(['no vrf definition blah', 'vrf definition blah', | |
161 | 'rd 10:10', 'description test']) | |
228 | if dut.version_number >= '4.23': | |
229 | dut.config(['no vrf instance blah', 'vrf instance blah', | |
230 | 'rd 10:10', 'description test']) | |
231 | else: | |
232 | dut.config(['no vrf definition blah', 'vrf definition blah', | |
233 | 'rd 10:10', 'description test']) | |
162 | 234 | result = dut.api('vrfs').set_ipv4_routing('blah') |
163 | 235 | self.assertTrue(result, 'dut=%s' % dut) |
164 | 236 | command = 'show running-config section vrf' |
170 | 242 | config = dut.run_commands(command, encoding='text') |
171 | 243 | self.assertIn('no ip routing vrf blah', config[0]['output'], |
172 | 244 | 'dut=%s' % dut) |
173 | dut.config(['no vrf definition blah']) | |
245 | if dut.version_number >= '4.23': | |
246 | dut.config(['no vrf instance blah']) | |
247 | else: | |
248 | dut.config(['no vrf definition blah']) | |
174 | 249 | |
175 | 250 | def test_set_ipv6_routing(self): |
176 | 251 | for dut in self.duts: |
177 | dut.config(['no vrf definition blah', 'vrf definition blah', | |
178 | 'rd 10:10', 'description test']) | |
252 | if dut.version_number >= '4.23': | |
253 | dut.config(['no vrf instance blah', 'vrf instance blah', | |
254 | 'rd 10:10', 'description test']) | |
255 | else: | |
256 | dut.config(['no vrf definition blah', 'vrf definition blah', | |
257 | 'rd 10:10', 'description test']) | |
179 | 258 | result = dut.api('vrfs').set_ipv6_routing('blah') |
180 | 259 | self.assertTrue(result, 'dut=%s' % dut) |
181 | 260 | command = 'show running-config all section vrf' |
187 | 266 | config = dut.run_commands(command, encoding='text') |
188 | 267 | self.assertIn('no ipv6 unicast-routing vrf blah', |
189 | 268 | config[0]['output'], 'dut=%s' % dut) |
190 | dut.config(['no vrf definition blah']) | |
269 | if dut.version_number >= '4.23': | |
270 | dut.config(['no vrf instance blah']) | |
271 | else: | |
272 | dut.config(['no vrf definition blah']) | |
191 | 273 | |
192 | 274 | def test_set_interface(self): |
193 | 275 | for dut in self.duts: |
194 | dut.config(['no vrf definition blah', 'vrf definition blah', | |
195 | 'rd 10:10', 'default interface Ethernet1', | |
196 | 'interface Ethernet1', 'no switchport']) | |
276 | if dut.version_number >= '4.23': | |
277 | dut.config(['no vrf instance blah', 'vrf instance blah', | |
278 | 'rd 10:10', 'default interface Ethernet1', | |
279 | 'interface Ethernet1', 'no switchport']) | |
280 | else: | |
281 | dut.config(['no vrf definition blah', 'vrf definition blah', | |
282 | 'rd 10:10', 'default interface Ethernet1', | |
283 | 'interface Ethernet1', 'no switchport']) | |
197 | 284 | result = dut.api('vrfs').set_interface('blah', 'Ethernet1') |
198 | 285 | self.assertTrue(result, 'dut=%s' % dut) |
199 | 286 | command = 'show running-config interfaces Ethernet1' |
200 | 287 | config = dut.run_commands(command, encoding='text') |
201 | self.assertIn('vrf forwarding blah', config[0]['output'], | |
202 | 'dut=%s' % dut) | |
288 | if dut.version_number >= '4.23': | |
289 | self.assertIn('vrf blah', config[0]['output'], | |
290 | 'dut=%s' % dut) | |
291 | else: | |
292 | self.assertIn('vrf forwarding blah', config[0]['output'], | |
293 | 'dut=%s' % dut) | |
203 | 294 | result = dut.api('vrfs').set_interface('blah', 'Ethernet1', |
204 | 295 | disable=True) |
205 | 296 | self.assertTrue(result, 'dut=%s' % dut) |
206 | 297 | config = dut.run_commands(command, encoding='text') |
207 | self.assertNotIn('vrf forwarding blah', config[0]['output'], | |
208 | 'dut=%s' % dut) | |
209 | dut.config(['no vrf definition blah', | |
210 | 'default interface Ethernet1']) | |
298 | if dut.version_number >= '4.23': | |
299 | self.assertNotIn('vrf blah', config[0]['output'], | |
300 | 'dut=%s' % dut) | |
301 | dut.config(['no vrf instance blah', | |
302 | 'default interface Ethernet1']) | |
303 | else: | |
304 | self.assertNotIn('vrf forwarding blah', config[0]['output'], | |
305 | 'dut=%s' % dut) | |
306 | dut.config(['no vrf definition blah', | |
307 | 'default interface Ethernet1']) | |
211 | 308 | |
212 | 309 | |
213 | 310 | if __name__ == '__main__': |
72 | 72 | vrid = 98 |
73 | 73 | for dut in self.duts: |
74 | 74 | interface = self._vlan_setup(dut) |
75 | dut.config(['interface %s' % interface, | |
76 | 'vrrp %d shutdown' % vrid, | |
77 | 'exit']) | |
75 | if dut.version_number >= '4.23': | |
76 | dut.config(['interface %s' % interface, | |
77 | 'vrrp %d disabled' % vrid, | |
78 | 'exit']) | |
79 | else: | |
80 | dut.config(['interface %s' % interface, | |
81 | 'vrrp %d shutdown' % vrid, | |
82 | 'exit']) | |
78 | 83 | response = dut.api('vrrp').get(interface) |
79 | 84 | |
80 | 85 | self.assertIsNotNone(response) |
84 | 89 | vrid2 = 198 |
85 | 90 | for dut in self.duts: |
86 | 91 | interface = self._vlan_setup(dut) |
87 | dut.config(['interface %s' % interface, | |
88 | 'vrrp %d shutdown' % vrid, | |
89 | 'exit', | |
90 | 'interface vlan $', | |
91 | 'vrrp %d shutdown' % vrid, | |
92 | 'vrrp %d shutdown' % vrid2, | |
93 | 'exit']) | |
92 | if dut.version_number >= '4.23': | |
93 | dut.config(['interface %s' % interface, | |
94 | 'vrrp %d disabled' % vrid, | |
95 | 'exit', | |
96 | 'interface vlan $', | |
97 | 'vrrp %d disabled' % vrid, | |
98 | 'vrrp %d disabled' % vrid2, | |
99 | 'exit']) | |
100 | else: | |
101 | dut.config(['interface %s' % interface, | |
102 | 'vrrp %d shutdown' % vrid, | |
103 | 'exit', | |
104 | 'interface vlan $', | |
105 | 'vrrp %d shutdown' % vrid, | |
106 | 'vrrp %d shutdown' % vrid2, | |
107 | 'exit']) | |
94 | 108 | response = dut.api('vrrp').getall() |
95 | 109 | |
96 | 110 | self.assertIsNotNone(response) |
121 | 135 | vrid = 101 |
122 | 136 | for dut in self.duts: |
123 | 137 | interface = self._vlan_setup(dut) |
124 | dut.config(['interface %s' % interface, | |
125 | 'no vrrp %d' % vrid, | |
126 | 'vrrp %d shutdown' % vrid, | |
127 | 'exit']) | |
138 | if dut.version_number >= '4.23': | |
139 | dut.config(['interface %s' % interface, | |
140 | 'no vrrp %d' % vrid, | |
141 | 'vrrp %d disabled' % vrid, | |
142 | 'exit']) | |
143 | else: | |
144 | dut.config(['interface %s' % interface, | |
145 | 'no vrrp %d' % vrid, | |
146 | 'vrrp %d shutdown' % vrid, | |
147 | 'exit']) | |
128 | 148 | |
129 | 149 | response = dut.api('vrrp').delete(interface, vrid) |
130 | 150 | self.assertIs(response, True) |
133 | 153 | vrid = 102 |
134 | 154 | for dut in self.duts: |
135 | 155 | interface = self._vlan_setup(dut) |
136 | dut.config(['interface %s' % interface, | |
137 | 'no vrrp %d' % vrid, | |
138 | 'vrrp %d shutdown' % vrid, | |
139 | 'exit']) | |
156 | if dut.version_number >= '4.23': | |
157 | dut.config(['interface %s' % interface, | |
158 | 'no vrrp %d' % vrid, | |
159 | 'vrrp %d disabled' % vrid, | |
160 | 'exit']) | |
161 | else: | |
162 | dut.config(['interface %s' % interface, | |
163 | 'no vrrp %d' % vrid, | |
164 | 'vrrp %d shutdown' % vrid, | |
165 | 'exit']) | |
140 | 166 | |
141 | 167 | response = dut.api('vrrp').delete(interface, vrid) |
142 | 168 | self.assertIs(response, True) |
197 | 223 | ] |
198 | 224 | for dut in self.duts: |
199 | 225 | interface = self._vlan_setup(dut) |
200 | dut.config(['interface %s' % interface, | |
201 | 'no vrrp %d' % vrid, | |
202 | 'vrrp %d shutdown' % vrid, | |
203 | 'vrrp %d ip 10.10.10.2' % vrid, | |
204 | 'exit']) | |
226 | if dut.version_number >= '4.23': | |
227 | dut.config(['interface %s' % interface, | |
228 | 'no vrrp %d' % vrid, | |
229 | 'vrrp %d disabled' % vrid, | |
230 | 'vrrp %d ipv4 10.10.10.2' % vrid, | |
231 | 'exit']) | |
232 | else: | |
233 | dut.config(['interface %s' % interface, | |
234 | 'no vrrp %d' % vrid, | |
235 | 'vrrp %d shutdown' % vrid, | |
236 | 'vrrp %d ip 10.10.10.2' % vrid, | |
237 | 'exit']) | |
205 | 238 | |
206 | 239 | for enable in enable_cases: |
207 | 240 | response = dut.api('vrrp').set_enable( |
219 | 252 | ] |
220 | 253 | for dut in self.duts: |
221 | 254 | interface = self._vlan_setup(dut) |
222 | dut.config(['interface %s' % interface, | |
223 | 'no vrrp %d' % vrid, | |
224 | 'vrrp %d shutdown' % vrid, | |
225 | 'exit']) | |
255 | if dut.version_number >= '4.23': | |
256 | dut.config(['interface %s' % interface, | |
257 | 'no vrrp %d' % vrid, | |
258 | 'vrrp %d disabled' % vrid, | |
259 | 'exit']) | |
260 | else: | |
261 | dut.config(['interface %s' % interface, | |
262 | 'no vrrp %d' % vrid, | |
263 | 'vrrp %d shutdown' % vrid, | |
264 | 'exit']) | |
226 | 265 | |
227 | 266 | for p_ip in primary_ip_cases: |
228 | 267 | response = dut.api('vrrp').set_primary_ip( |
240 | 279 | ] |
241 | 280 | for dut in self.duts: |
242 | 281 | interface = self._vlan_setup(dut) |
243 | dut.config(['interface %s' % interface, | |
244 | 'no vrrp %d' % vrid, | |
245 | 'vrrp %d shutdown' % vrid, | |
246 | 'exit']) | |
282 | if dut.version_number >= '4.23': | |
283 | dut.config(['interface %s' % interface, | |
284 | 'no vrrp %d' % vrid, | |
285 | 'vrrp %d disabled' % vrid, | |
286 | 'exit']) | |
287 | else: | |
288 | dut.config(['interface %s' % interface, | |
289 | 'no vrrp %d' % vrid, | |
290 | 'vrrp %d shutdown' % vrid, | |
291 | 'exit']) | |
247 | 292 | |
248 | 293 | for priority in priority_cases: |
249 | 294 | response = dut.api('vrrp').set_priority( |
261 | 306 | ] |
262 | 307 | for dut in self.duts: |
263 | 308 | interface = self._vlan_setup(dut) |
264 | dut.config(['interface %s' % interface, | |
265 | 'no vrrp %d' % vrid, | |
266 | 'vrrp %d shutdown' % vrid, | |
267 | 'exit']) | |
309 | if dut.version_number >= '4.23': | |
310 | dut.config(['interface %s' % interface, | |
311 | 'no vrrp %d' % vrid, | |
312 | 'vrrp %d disabled' % vrid, | |
313 | 'exit']) | |
314 | else: | |
315 | dut.config(['interface %s' % interface, | |
316 | 'no vrrp %d' % vrid, | |
317 | 'vrrp %d shutdown' % vrid, | |
318 | 'exit']) | |
268 | 319 | |
269 | 320 | for description in desc_cases: |
270 | 321 | response = dut.api('vrrp').set_description( |
280 | 331 | ] |
281 | 332 | for dut in self.duts: |
282 | 333 | interface = self._vlan_setup(dut) |
283 | dut.config(['interface %s' % interface, | |
284 | 'no vrrp %d' % vrid, | |
285 | 'vrrp %d shutdown' % vrid, | |
286 | 'exit']) | |
334 | if dut.version_number >= '4.23': | |
335 | dut.config(['interface %s' % interface, | |
336 | 'no vrrp %d' % vrid, | |
337 | 'vrrp %d disabled' % vrid, | |
338 | 'exit']) | |
339 | else: | |
340 | dut.config(['interface %s' % interface, | |
341 | 'no vrrp %d' % vrid, | |
342 | 'vrrp %d shutdown' % vrid, | |
343 | 'exit']) | |
287 | 344 | |
288 | 345 | for s_ip_list in secondary_ip_cases: |
289 | 346 | response = dut.api('vrrp').set_secondary_ips( |
302 | 359 | ] |
303 | 360 | for dut in self.duts: |
304 | 361 | interface = self._vlan_setup(dut) |
305 | dut.config(['interface %s' % interface, | |
306 | 'no vrrp %d' % vrid, | |
307 | 'vrrp %d shutdown' % vrid, | |
308 | 'exit']) | |
362 | if dut.version_number >= '4.23': | |
363 | dut.config(['interface %s' % interface, | |
364 | 'no vrrp %d' % vrid, | |
365 | 'vrrp %d disabled' % vrid, | |
366 | 'exit']) | |
367 | else: | |
368 | dut.config(['interface %s' % interface, | |
369 | 'no vrrp %d' % vrid, | |
370 | 'vrrp %d shutdown' % vrid, | |
371 | 'exit']) | |
309 | 372 | |
310 | 373 | for ip_version in ip_version_cases: |
311 | 374 | response = dut.api('vrrp').set_ip_version( |
323 | 386 | ] |
324 | 387 | for dut in self.duts: |
325 | 388 | interface = self._vlan_setup(dut) |
326 | dut.config(['interface %s' % interface, | |
327 | 'no vrrp %d' % vrid, | |
328 | 'vrrp %d shutdown' % vrid, | |
329 | 'exit']) | |
389 | if dut.version_number >= '4.23': | |
390 | dut.config(['interface %s' % interface, | |
391 | 'no vrrp %d' % vrid, | |
392 | 'vrrp %d disabled' % vrid, | |
393 | 'exit']) | |
394 | else: | |
395 | dut.config(['interface %s' % interface, | |
396 | 'no vrrp %d' % vrid, | |
397 | 'vrrp %d shutdown' % vrid, | |
398 | 'exit']) | |
330 | 399 | |
331 | 400 | for timers_advertise in timers_adv_cases: |
332 | 401 | response = dut.api('vrrp').set_timers_advertise( |
344 | 413 | ] |
345 | 414 | for dut in self.duts: |
346 | 415 | interface = self._vlan_setup(dut) |
347 | dut.config(['interface %s' % interface, | |
348 | 'no vrrp %d' % vrid, | |
349 | 'vrrp %d shutdown' % vrid, | |
350 | 'exit']) | |
416 | if dut.version_number >= '4.23': | |
417 | dut.config(['interface %s' % interface, | |
418 | 'no vrrp %d' % vrid, | |
419 | 'vrrp %d disabled' % vrid, | |
420 | 'exit']) | |
421 | else: | |
422 | dut.config(['interface %s' % interface, | |
423 | 'no vrrp %d' % vrid, | |
424 | 'vrrp %d shutdown' % vrid, | |
425 | 'exit']) | |
351 | 426 | |
352 | 427 | for mac_addr_adv_intvl in mac_addr_adv_int_cases: |
353 | 428 | response = dut.api('vrrp').set_mac_addr_adv_interval( |
366 | 441 | ] |
367 | 442 | for dut in self.duts: |
368 | 443 | interface = self._vlan_setup(dut) |
369 | dut.config(['interface %s' % interface, | |
370 | 'no vrrp %d' % vrid, | |
371 | 'vrrp %d shutdown' % vrid, | |
372 | 'exit']) | |
444 | if dut.version_number >= '4.23': | |
445 | dut.config(['interface %s' % interface, | |
446 | 'no vrrp %d' % vrid, | |
447 | 'vrrp %d disabled' % vrid, | |
448 | 'exit']) | |
449 | else: | |
450 | dut.config(['interface %s' % interface, | |
451 | 'no vrrp %d' % vrid, | |
452 | 'vrrp %d shutdown' % vrid, | |
453 | 'exit']) | |
373 | 454 | |
374 | 455 | for preempt in preempt_cases: |
375 | 456 | response = dut.api('vrrp').set_preempt( |
387 | 468 | ] |
388 | 469 | for dut in self.duts: |
389 | 470 | interface = self._vlan_setup(dut) |
390 | dut.config(['interface %s' % interface, | |
391 | 'no vrrp %d' % vrid, | |
392 | 'vrrp %d shutdown' % vrid, | |
393 | 'exit']) | |
471 | if dut.version_number >= '4.23': | |
472 | dut.config(['interface %s' % interface, | |
473 | 'no vrrp %d' % vrid, | |
474 | 'vrrp %d disabled' % vrid, | |
475 | 'exit']) | |
476 | else: | |
477 | dut.config(['interface %s' % interface, | |
478 | 'no vrrp %d' % vrid, | |
479 | 'vrrp %d shutdown' % vrid, | |
480 | 'exit']) | |
394 | 481 | |
395 | 482 | for preempt_delay_min in preempt_delay_min_cases: |
396 | 483 | response = dut.api('vrrp').set_preempt_delay_min( |
408 | 495 | ] |
409 | 496 | for dut in self.duts: |
410 | 497 | interface = self._vlan_setup(dut) |
411 | dut.config(['interface %s' % interface, | |
412 | 'no vrrp %d' % vrid, | |
413 | 'vrrp %d shutdown' % vrid, | |
414 | 'exit']) | |
498 | if dut.version_number >= '4.23': | |
499 | dut.config(['interface %s' % interface, | |
500 | 'no vrrp %d' % vrid, | |
501 | 'vrrp %d disabled' % vrid, | |
502 | 'exit']) | |
503 | else: | |
504 | dut.config(['interface %s' % interface, | |
505 | 'no vrrp %d' % vrid, | |
506 | 'vrrp %d shutdown' % vrid, | |
507 | 'exit']) | |
415 | 508 | |
416 | 509 | for preempt_delay_reload in preempt_delay_reload_cases: |
417 | 510 | response = dut.api('vrrp').set_preempt_delay_reload( |
429 | 522 | ] |
430 | 523 | for dut in self.duts: |
431 | 524 | interface = self._vlan_setup(dut) |
432 | dut.config(['interface %s' % interface, | |
433 | 'no vrrp %d' % vrid, | |
434 | 'vrrp %d shutdown' % vrid, | |
435 | 'exit']) | |
525 | if dut.version_number >= '4.23': | |
526 | dut.config(['interface %s' % interface, | |
527 | 'no vrrp %d' % vrid, | |
528 | 'vrrp %d disabled' % vrid, | |
529 | 'exit']) | |
530 | else: | |
531 | dut.config(['interface %s' % interface, | |
532 | 'no vrrp %d' % vrid, | |
533 | 'vrrp %d shutdown' % vrid, | |
534 | 'exit']) | |
436 | 535 | |
437 | 536 | for delay_reload in delay_reload_cases: |
438 | 537 | response = dut.api('vrrp').set_delay_reload( |
462 | 561 | ] |
463 | 562 | for dut in self.duts: |
464 | 563 | interface = self._vlan_setup(dut) |
465 | dut.config(['interface %s' % interface, | |
466 | 'no vrrp %d' % vrid, | |
467 | 'vrrp %d shutdown' % vrid, | |
468 | 'exit']) | |
564 | if dut.version_number >= '4.23': | |
565 | dut.config(['interface %s' % interface, | |
566 | 'no vrrp %d' % vrid, | |
567 | 'vrrp %d disabled' % vrid, | |
568 | 'exit']) | |
569 | else: | |
570 | dut.config(['interface %s' % interface, | |
571 | 'no vrrp %d' % vrid, | |
572 | 'vrrp %d shutdown' % vrid, | |
573 | 'exit']) | |
469 | 574 | |
470 | 575 | for track_list in track_cases: |
471 | 576 | response = dut.api('vrrp').set_tracks( |
483 | 588 | ] |
484 | 589 | for dut in self.duts: |
485 | 590 | interface = self._vlan_setup(dut) |
486 | dut.config(['interface %s' % interface, | |
487 | 'no vrrp %d' % vrid, | |
488 | 'vrrp %d shutdown' % vrid, | |
489 | 'exit']) | |
591 | if dut.version_number >= '4.23': | |
592 | dut.config(['interface %s' % interface, | |
593 | 'no vrrp %d' % vrid, | |
594 | 'vrrp %d disabled' % vrid, | |
595 | 'exit']) | |
596 | else: | |
597 | dut.config(['interface %s' % interface, | |
598 | 'no vrrp %d' % vrid, | |
599 | 'vrrp %d shutdown' % vrid, | |
600 | 'exit']) | |
490 | 601 | |
491 | 602 | for bfd_ip in bfd_ip_cases: |
492 | 603 | response = dut.api('vrrp').set_bfd_ip( |
28 | 28 | # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
29 | 29 | # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | 30 | # |
31 | ||
31 | 32 | import os |
32 | 33 | import unittest |
33 | 34 | |
56 | 57 | if dut._enablepwd is not None: |
57 | 58 | # If enable password defined for dut, set the |
58 | 59 | # enable password on the dut and clear it on tearDown |
59 | dut.config("enable secret %s" % dut._enablepwd) | |
60 | if dut.version_number >= '4.23': | |
61 | dut.config("enable password %s" % dut._enablepwd) | |
62 | else: | |
63 | dut.config("enable secret %s" % dut._enablepwd) | |
64 | ||
65 | def test_unauthorized_user(self): | |
66 | error_string = ('Unauthorized. Unable to authenticate user: Bad' | |
67 | ' username/password combination') | |
68 | for dut in self.duts: | |
69 | temp_node = pyeapi.connect(host=dut.settings['host'], | |
70 | transport=dut.settings['transport'], | |
71 | username='wrong', password='nope', | |
72 | port=dut.settings['port'], | |
73 | return_node=True) | |
74 | try: | |
75 | temp_node.run_commands('show version') | |
76 | except pyeapi.eapilib.ConnectionError as err: | |
77 | self.assertEqual(err.message, error_string) | |
60 | 78 | |
61 | 79 | def test_populate_version_properties(self): |
62 | 80 | for dut in self.duts: |
70 | 88 | result = dut.run_commands('show version') |
71 | 89 | self.assertIsInstance(result, list, 'dut=%s' % dut) |
72 | 90 | self.assertEqual(len(result), 1, 'dut=%s' % dut) |
91 | ||
92 | def test_enable_single_extended_command(self): | |
93 | for dut in self.duts: | |
94 | result = dut.run_commands({'cmd': 'show cvx', 'revision': 1}) | |
95 | self.assertIsInstance(result, list, 'dut=%s' % dut) | |
96 | self.assertEqual(len(result), 1, 'dut=%s' % dut) | |
97 | self.assertTrue('clusterMode' not in result[0].keys()) | |
98 | ||
99 | result2 = dut.run_commands({'cmd': 'show cvx', 'revision': 2}) | |
100 | self.assertIsInstance(result2, list, 'dut=%s' % dut) | |
101 | self.assertEqual(len(result2), 1, 'dut=%s' % dut) | |
102 | self.assertTrue('clusterMode' in result2[0].keys()) | |
73 | 103 | |
74 | 104 | def test_enable_single_unicode_command(self): |
75 | 105 | for dut in self.duts: |
222 | 252 | |
223 | 253 | def tearDown(self): |
224 | 254 | for dut in self.duts: |
225 | dut.config("no enable secret") | |
255 | if dut.version_number >= '4.23': | |
256 | dut.config("no enable password") | |
257 | else: | |
258 | dut.config("no enable secret") | |
226 | 259 | |
227 | 260 | |
228 | 261 | class TestNode(unittest.TestCase): |
247 | 280 | # Send an incomplete command |
248 | 281 | cases.append(('show run', rfmt |
249 | 282 | % (1002, 'invalid command', |
250 | 'incomplete token \(at token \d+: \'.*\'\)'))) | |
283 | r'incomplete token \(at token \d+: \'.*\'\)'))) | |
251 | 284 | # Send a mangled command |
252 | 285 | cases.append(('shwo version', rfmt |
253 | 286 | % (1002, 'invalid command', |
254 | 'Invalid input \(at token \d+: \'.*\'\)'))) | |
287 | r'Invalid input \(at token \d+: \'.*\'\)'))) | |
255 | 288 | # Send a command that cannot be run through the api |
256 | 289 | # note the command for reload looks to change in new EOS |
257 | 290 | # in 4.15 the reload now is replaced with 'force' if you are |
261 | 294 | cases.append(('reload', rfmt |
262 | 295 | % (1004, 'incompatible command', |
263 | 296 | 'Command not permitted via API access..*'))) |
264 | # Send a continuous command that requires a break | |
265 | cases.append(('watch 10 show int e1 count rates', rfmt | |
266 | % (1000, 'could not run command', | |
267 | 'init error.*'))) | |
268 | 297 | # Send a command that has insufficient priv |
269 | 298 | cases.append(('show running-config', rfmt |
270 | 299 | % (1002, 'invalid command', |
271 | 'Invalid input \(privileged mode required\)'))) | |
272 | ||
300 | r'Invalid input \(privileged mode required\)'))) | |
273 | 301 | |
274 | 302 | for dut in self.duts: |
275 | 303 | for (cmd, regex) in cases: |
500 | 500 | self.eapi_positive_config_test(func, cmds) |
501 | 501 | |
502 | 502 | def test_update_vlan(self): |
503 | cmds = ['interface Vxlan1', 'vxlan vlan 10 vni 10'] | |
503 | cmds = ['interface Vxlan1', 'vxlan vlan add 10 vni 10'] | |
504 | 504 | func = function('update_vlan', 'Vxlan1', 10, 10) |
505 | 505 | self.eapi_positive_config_test(func, cmds) |
506 | 506 | |
507 | 507 | def test_remove_vlan(self): |
508 | cmds = ['interface Vxlan1', 'no vxlan vlan 10 vni'] | |
508 | cmds = ['interface Vxlan1', 'vxlan vlan remove 10 vni'] | |
509 | 509 | func = function('remove_vlan', 'Vxlan1', 10) |
510 | 510 | self.eapi_positive_config_test(func, cmds) |
511 | 511 |
60 | 60 | self.assertIsNone(self.instance.get('blah')) |
61 | 61 | |
62 | 62 | def test_getall(self): |
63 | # Review fixtures/running_config.routemaps to see the default | |
64 | # running-config that is the basis for this test | |
63 | 65 | result = self.instance.getall() |
64 | 66 | self.assertIsInstance(result, dict) |
67 | self.assertEqual(len(result.keys()), 4) | |
65 | 68 | |
66 | 69 | def test_routemaps_functions(self): |
67 | 70 | for name in ['create', 'delete', 'default']: |
62 | 62 | trunk_groups=[]) |
63 | 63 | self.assertEqual(vlan, result) |
64 | 64 | |
65 | # ensure capturing grouppped vlans | |
66 | result = self.instance.get('200-.*') | |
67 | vlan = dict(vlan_id='200-202,204', name='grouping', state='active', | |
68 | trunk_groups=[]) | |
69 | self.assertEqual(vlan, result) | |
70 | ||
65 | 71 | def test_get_not_configured(self): |
66 | 72 | self.assertIsNone(self.instance.get('1000')) |
67 | 73 | |
68 | 74 | def test_getall(self): |
69 | 75 | result = self.instance.getall() |
70 | 76 | self.assertIsInstance(result, dict) |
71 | self.assertEqual(len(result), 4) | |
77 | self.assertEqual(len(result), 5) | |
72 | 78 | |
73 | 79 | def test_vlan_functions(self): |
74 | 80 | for name in ['create', 'delete', 'default']: |
418 | 418 | |
419 | 419 | def test_set_ip_version(self): |
420 | 420 | # vrrp 10 ip version 2 |
421 | ||
422 | 421 | # Test set_description gives properly formatted commands |
423 | 422 | cases = [ |
424 | 423 | (2, None, None, 'vrrp %d ip version 2' % upd_vrid), |
100 | 100 | self.connection.execute.assert_called_once_with(response, 'json') |
101 | 101 | self.assertEqual(command, result[0]['result']) |
102 | 102 | |
103 | def test_enable_with_single_extended_command(self): | |
104 | command = {'cmd': 'show cvx', 'revision': 2} | |
105 | response = ['enable', command] | |
106 | ||
107 | self.connection.execute.return_value = {'result': list(response)} | |
108 | result = self.node.enable(command) | |
109 | ||
110 | self.connection.execute.assert_called_once_with(response, 'json') | |
111 | self.assertEqual(command, result[0]['result']) | |
112 | ||
103 | 113 | def test_no_enable_with_single_command(self): |
104 | 114 | command = random_string() |
105 | 115 | response = [command] |
299 | 309 | def test_connect_types(self, connection): |
300 | 310 | transports = list(pyeapi.client.TRANSPORTS.keys()) |
301 | 311 | kwargs = dict(host='localhost', username='admin', password='', |
302 | port=None, timeout=60) | |
312 | port=None, key_file=None, cert_file=None, | |
313 | ca_file=None, timeout=60) | |
303 | 314 | |
304 | 315 | for transport in transports: |
305 | 316 | pyeapi.client.connect(transport) |
397 | 408 | with patch.dict(pyeapi.client.TRANSPORTS, {'https': transport}): |
398 | 409 | pyeapi.client.connect() |
399 | 410 | kwargs = dict(host='localhost', username='admin', password='', |
400 | port=None, timeout=60) | |
411 | port=None, key_file=None, cert_file=None, | |
412 | ca_file=None, timeout=60) | |
401 | 413 | transport.assert_called_once_with(**kwargs) |
402 | 414 | |
403 | 415 | def test_connect_return_node(self): |
409 | 421 | password='password', port=None, |
410 | 422 | timeout=60, return_node=True) |
411 | 423 | kwargs = dict(host='192.168.1.16', username='eapi', |
412 | password='password', port=None, timeout=60) | |
424 | password='password', port=None, key_file=None, | |
425 | cert_file=None, ca_file=None, timeout=60) | |
413 | 426 | transport.assert_called_once_with(**kwargs) |
414 | 427 | self.assertIsNone(node._enablepwd) |
415 | 428 | |
423 | 436 | timeout=60, enablepwd='enablepwd', |
424 | 437 | return_node=True) |
425 | 438 | kwargs = dict(host='192.168.1.16', username='eapi', |
426 | password='password', port=None, timeout=60) | |
439 | password='password', port=None, key_file=None, | |
440 | cert_file=None, ca_file=None, timeout=60) | |
427 | 441 | transport.assert_called_once_with(**kwargs) |
428 | 442 | self.assertEqual(node._enablepwd, 'enablepwd') |
429 | 443 | |
434 | 448 | pyeapi.client.load_config(filename=conf) |
435 | 449 | node = pyeapi.client.connect_to('test1') |
436 | 450 | kwargs = dict(host='192.168.1.16', username='eapi', |
437 | password='password', port=None, timeout=60) | |
451 | password='password', port=None, key_file=None, | |
452 | cert_file=None, ca_file=None, timeout=60) | |
438 | 453 | transport.assert_called_once_with(**kwargs) |
439 | 454 | self.assertEqual(node._enablepwd, 'enablepwd') |
440 | 455 |
103 | 103 | |
104 | 104 | self.assertTrue(mock_transport.close.called) |
105 | 105 | |
106 | def test_send_unauthorized_user(self): | |
107 | error_string = ('Unauthorized. Unable to authenticate user: Bad' | |
108 | ' username/password combination') | |
109 | response_str = ('Unable to authenticate user: Bad username/password' | |
110 | ' combination') | |
111 | mock_transport = Mock(name='transport') | |
112 | mockcfg = {'getresponse.return_value.read.return_value': response_str, | |
113 | 'getresponse.return_value.status': 401, | |
114 | 'getresponse.return_value.reason': 'Unauthorized'} | |
115 | mock_transport.configure_mock(**mockcfg) | |
116 | ||
117 | instance = pyeapi.eapilib.EapiConnection() | |
118 | instance.authentication('username', 'password') | |
119 | instance.transport = mock_transport | |
120 | try: | |
121 | instance.send('test') | |
122 | except pyeapi.eapilib.ConnectionError as err: | |
123 | self.assertEqual(err.message, error_string) | |
124 | ||
106 | 125 | def test_send_raises_connection_error(self): |
107 | 126 | mock_transport = Mock(name='transport') |
108 | 127 | mockcfg = {'getresponse.return_value.read.side_effect': ValueError} |
3 | 3 | from mock import patch, Mock |
4 | 4 | |
5 | 5 | import pyeapi.utils |
6 | ||
6 | 7 | |
7 | 8 | class TestUtils(unittest.TestCase): |
8 | 9 | |
22 | 23 | def test_make_iterable_from_string(self): |
23 | 24 | result = pyeapi.utils.make_iterable('test') |
24 | 25 | self.assertIsInstance(result, collections.Iterable) |
25 | self.assertEquals(len(result), 1) | |
26 | self.assertEqual(len(result), 1) | |
26 | 27 | |
27 | 28 | def test_make_iterable_from_unicode(self): |
28 | 29 | result = pyeapi.utils.make_iterable(u'test') |
29 | 30 | self.assertIsInstance(result, collections.Iterable) |
30 | self.assertEquals(len(result), 1) | |
31 | self.assertEqual(len(result), 1) | |
31 | 32 | |
32 | 33 | def test_make_iterable_from_iterable(self): |
33 | 34 | result = pyeapi.utils.make_iterable(['test']) |
34 | 35 | self.assertIsInstance(result, collections.Iterable) |
35 | self.assertEquals(len(result), 1) | |
36 | self.assertEqual(len(result), 1) | |
36 | 37 | |
37 | 38 | def test_make_iterable_raises_type_error(self): |
38 | 39 | with self.assertRaises(TypeError): |