Codebase list pyeapi / 30a2e1a
Import upstream version 0.8.4+git20210803.1.b0ffe0d Debian Janitor 2 years ago
79 changed file(s) with 1296 addition(s) and 750 deletion(s). Raw diff Collapse all Expand all
3030 all: clean check pep8 flake8 tests
3131
3232 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/
3434
3535 pyflakes:
3636 pyflakes pyeapi/ test/
0 Metadata-Version: 1.1
0 Metadata-Version: 2.1
11 Name: pyeapi
2 Version: 0.8.1
2 Version: 0.8.4rc0
33 Summary: Python Client for eAPI
44 Home-page: https://github.com/arista-eosplus/pyeapi
55 Author: Arista EOS+ CS
66 Author-email: eosplus-dev@arista.com
77 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
288 Keywords: networking arista eos eapi
299 Platform: UNKNOWN
3010 Classifier: Development Status :: 4 - Beta
3414 Classifier: Programming Language :: Python :: 2
3515 Classifier: Programming Language :: Python :: 2.7
3616 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 0.8.1
0 0.8.4-rc0
0 netaddr
0 -r requirements.txt
11 mock
22 coveralls
33 twine
44 check-manifest
5 pep8
5 pycodestyle
66 pyflakes
77 coverage
88 sphinx
1010 flake8
1111 flake8-print
1212 flake8-debugger
13 pep8-naming
14
15
+0
-28
docs/api_modules/_list_of_modules.rst less more
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
-13
docs/api_modules/abstract.rst less more
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
-13
docs/api_modules/acl.rst less more
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
-13
docs/api_modules/bgp.rst less more
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
-13
docs/api_modules/interfaces.rst less more
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
-13
docs/api_modules/ipinterfaces.rst less more
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
-13
docs/api_modules/mlag.rst less more
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
-13
docs/api_modules/ntp.rst less more
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
-13
docs/api_modules/ospf.rst less more
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
-13
docs/api_modules/routemaps.rst less more
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
-13
docs/api_modules/spanningtree.rst less more
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
-13
docs/api_modules/staticroute.rst less more
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
-13
docs/api_modules/stp.rst less more
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
-13
docs/api_modules/switchports.rst less more
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
-13
docs/api_modules/system.rst less more
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
-13
docs/api_modules/users.rst less more
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
-13
docs/api_modules/varp.rst less more
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
-13
docs/api_modules/vlans.rst less more
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
-13
docs/api_modules/vrfs.rst less more
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
-13
docs/api_modules/vrrp.rst less more
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
-12
docs/client_modules/_list_of_modules.rst less more
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
-13
docs/client_modules/client.rst less more
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
-13
docs/client_modules/eapilib.rst less more
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
-13
docs/client_modules/utils.rst less more
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:
3838 - http_local (available in EOS 4.14.5 or later)
3939 - http
4040 - https
41 - https_certs
4142
4243 :port: Configures the port to use for the eAPI connection. A default
4344 port is used if this parameter is absent, based on the transport setting
4445 using the following values:
4546
4647 - transport: http, default port: 80
47 - transport: https, deafult port: 443
48 - transport: https, default port: 443
49 - transport: https_certs, default port: 443
4850 - transport: http_local, default port: 8080
4951 - transport: socket, default port: n/a
5052
6163 =========== ================== =============== ========================
6264 http Yes On/Off-switch Yes
6365 https Yes On/Off-switch Yes
66 https_certs Yes On/Off-switch Yes (Auth done via certs, not un/pw)
6467 http_local Yes On-switch only No
6568 socket No On-switch only No
6669 =========== ================== =============== ========================
168171 username: admin
169172 password: admin
170173
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
171202 *******************
172203 The DEFAULT Section
173204 *******************
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.
1818
1919 - HTTP
2020 - HTTPS
21 - HTTPS Certificates
2122 - HTTP Local
2223 - Unix Socket
2324
6364
6465 # send one or more commands to the node
6566 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}}]
6881
6982 # use the config method to send configuration commands
7083 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
55 :maxdepth: 2
66 :titlesonly:
77
8 release-notes-0.8.2.rst
89 release-notes-0.8.1.rst
910 release-notes-0.8.0.rst
1011 release-notes-0.7.0.rst
2828 # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
2929 # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3030 #
31 __version__ = '0.8.1'
31 __version__ = '0.8.4'
3232 __author__ = 'Arista EOS+'
3333
3434
3939 ultimately derive from BaseEntity which provides some common functions to
4040 make building API modules easier.
4141 """
42 from collections import Callable, Mapping
42 from collections.abc import Callable, Mapping
4343
4444 from pyeapi.eapilib import CommandError, ConnectionError
4545 from pyeapi.utils import make_iterable
6767 self.node = node
6868
6969 @property
70 def version_number(self):
71 return self.node.version_number
72
73 @property
7074 def config(self):
7175 return self.node.running_config
7276
118122 try:
119123 self.node.config(commands)
120124 return True
121 except (CommandError, ConnectionError):
125 except (CommandError):
122126 return False
123127
124128 def command_builder(self, string, value=None, default=None, disable=None):
242242 entries = dict()
243243 for item in re.finditer(r'\d+ [p|d].*$', config, re.M):
244244 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
256257 return dict(entries=entries)
257258
258259 def create(self, name):
204204 return collection
205205
206206 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)
208211 match = re.search(regexp, config)
209212 value = match.group(1) if match else None
210213 return dict(peer_group=value)
233236 return dict(description=value)
234237
235238 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)
237240 value = exp in config
238241 return dict(next_hop_self=not value)
239242
262265 def delete(self, name):
263266 response = self.configure('no neighbor {}'.format(name))
264267 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))
266274 return response
267275
268276 def configure(self, cmd):
279287
280288 def set_peer_group(self, name, value=None, default=False, disable=False):
281289 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)
284296 return self.configure(cmd)
285297 return False
286298
564564 True if the operation succeeds otherwise False is returned
565565 """
566566 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))
569575 return self.configure(commands)
570
571576
572577 class PortchannelInterface(BaseInterface):
573578
825830 * udp_port (int): The vxlan udp-port value
826831 * vlans (dict): The vlan to vni mappings
827832 * 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
829834 feature is configured
830835
831836 Args:
878883 return dict(multicast_group=value)
879884
880885 def _parse_multicast_decap(self, config):
881 value = 'vxlan mutlicast-group decap' in config
886 value = 'vxlan multicast-group decap' in config
882887 return dict(multicast_decap=bool(value))
883888
884889 def _parse_udp_port(self, config):
10581063 True if the command completes successfully
10591064
10601065 """
1061 cmd = 'vxlan vlan %s vni %s' % (vid, vni)
1066 cmd = 'vxlan vlan add %s vni %s' % (vid, vni)
10621067 return self.configure_interface(name, cmd)
10631068
10641069 def remove_vlan(self, name, vid):
10741079 True if the command completes successfully
10751080
10761081 """
1077 return self.configure_interface(name, 'no vxlan vlan %s vni' % vid)
1082 return self.configure_interface(name, 'vxlan vlan remove %s vni' % vid)
10781083
10791084
10801085 INTERFACE_CLASS_MAP = {
143143 dict: A dict object that is intended to be merged into the
144144 resource dict
145145 """
146 match = re.search(r'peer-address ([^\s]+)', config)
146 match = re.search(r'peer-address (\d+\.\d+\.\d+\.\d+)$', config)
147147 value = match.group(1) if match else None
148148 return dict(peer_address=value)
149149
8787 return response
8888
8989 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)
9194 value = match.group(1) if match else None
9295 return dict(source_interface=value)
9396
117120 Returns:
118121 True if the operation succeeds, otherwise False.
119122 """
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)
121127 return self.configure(cmd)
122128
123129 def default(self):
126132 Returns:
127133 True if the operation succeeds, otherwise False.
128134 """
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)
130139 return self.configure(cmd)
131140
132141 def set_source_interface(self, name):
138147 Returns:
139148 True if the operation succeeds, otherwise False.
140149 """
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)
142154 return self.configure(cmd)
143155
144156 def add_server(self, name, prefer=False):
9999
100100 def getall(self):
101101 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)
103103 for name in routemaps_re.findall(self.config):
104104 routemap = self.get(name)
105105 if routemap:
238238 """
239239 try:
240240 current_statements = self.get(name)[action][seqno]['match']
241 except:
241 except Exception:
242242 current_statements = []
243243
244244 commands = list()
274274 """
275275 try:
276276 current_statements = self.get(name)[action][seqno]['set']
277 except:
277 except Exception:
278278 current_statements = []
279279
280280 commands = list()
157157 # Get the four identifying components
158158 ip_dest = match[0]
159159 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]
161161 distance = int(match[3])
162162
163163 # Create the data dict with the remaining components
164164 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]
167167
168168 # Build the complete dict entry from the four components
169169 # and the data.
104104 into the resource dict
105105 """
106106 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',
108108 self.config, re.DOTALL | re.M)
109109 for match in matches:
110110 if match[0].strip() == "motd":
8181 following configuration line that might contain the users sshkey.
8282 """
8383
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
9084 def get(self, name):
9185 """Returns the local user configuration as a resource dict
9286
107101 Returns:
108102 dict: A dict of usernames with a nested resource dict object
109103 """
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
110121 users = self.users_re.findall(self.config, re.M)
111122 resources = dict()
112123 for user in users:
130141 resource['nopassword'] = nopass == 'nopassword'
131142 resource['format'] = fmt
132143 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
134148 return {username: resource}
135149
136150 def create(self, name, nopassword=None, secret=None, encryption=None):
286300 Returns:
287301 True if the operation was successful otherwise False
288302 """
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)
291311 return self.configure(cmd)
292312
293313
177177 elif addresses is not None:
178178 try:
179179 current_addresses = self.get(name)['addresses']
180 except:
180 except Exception:
181181 current_addresses = []
182182
183183 # remove virtual-router addresses not present in addresses list
5353 from pyeapi.api import EntityCollection
5454 from pyeapi.utils import make_iterable
5555
56 VLAN_ID_RE = re.compile(r'(?:vlan\s)(?P<value>.*)$', re.M)
5657 NAME_RE = re.compile(r'(?:name\s)(?P<value>.*)$', re.M)
5758 STATE_RE = re.compile(r'(?:state\s)(?P<value>.*)$', re.M)
5859 TRUNK_GROUP_RE = re.compile(r'(?:trunk\sgroup\s)(?P<value>.*)$', re.M)
103104 if not config:
104105 return None
105106
106 response = dict(vlan_id=value)
107 response = dict(vlan_id=self._parse_vlan_id(config))
107108 response.update(self._parse_name(config))
108109 response.update(self._parse_state(config))
109110 response.update(self._parse_trunk_groups(config))
110111
111112 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
112129
113130 def _parse_name(self, config):
114131 """ _parse_name scans the provided configuration block and extracts
165182 A dict object of Vlan attributes
166183
167184 """
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)
169187
170188 response = dict()
171189 for vid in vlans_re.findall(self.config):
7575 key/value pairs.
7676
7777 """
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)
7982 if not config:
8083 return None
8184 response = dict(vrf_name=value)
135138 A dict object of VRF attributes
136139
137140 """
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)
139145
140146 response = dict()
141147 for vrf in vrfs_re.findall(self.config):
159165 Returns:
160166 True if create was successful otherwise False
161167 """
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]
163172 if rd:
164173 commands.append('rd %s' % rd)
165174 return self.configure(commands)
173182 Returns:
174183 True if the operation was successful otherwise False
175184 """
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
177189 return self.configure(command)
178190
179191 def default(self, vrf_name):
185197 Returns:
186198 True if the operation was successful otherwise False
187199 """
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
189204 return self.configure(command)
190205
191206 def configure_vrf(self, vrf_name, commands):
199214 True if the commands completed successfully
200215 """
201216 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
203222 return self.configure(commands)
204223
205224 def set_rd(self, vrf_name, rd):
300319 True if the operation was successful otherwise False
301320 """
302321 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))
305328 return self.configure(cmds)
306329
307330
250250 return vrrps
251251
252252 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)
254257 if match:
255258 return dict(enable=False)
256259 return dict(enable=True)
257260
258261 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)
261268 value = match.group(1) if match else None
262269 return dict(primary_ip=value)
263270
264271 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)
266278 value = int(match.group(1)) if match else None
267279 return dict(priority=value)
268280
269281 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)
272288 value = int(match.group(1)) if match else None
273289 return dict(timers_advertise=value)
274290
279295 return dict(preempt=False)
280296
281297 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)
284304 value = matches if matches else []
285305 return dict(secondary_ip=value)
286306
287307 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)
290314 if match:
291315 return dict(description=match.group(1).lstrip())
292316 return dict(description='')
318342 return dict(bfd_ip='')
319343
320344 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)
323351 value = int(match.group(1)) if match else None
324352 return dict(ip_version=value)
325353
326354 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)
329361 value = int(match.group(1)) if match else None
330362 return dict(delay_reload=value)
331363
332364 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)
336373 value = []
337374 for match in matches:
338375 tr_obj = match[0]
440477 """
441478
442479 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
444484 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
446489 else:
447490 raise ValueError("vrrp property 'enable' must be "
448491 "True or False")
483526 if default is True:
484527 vrrps = self.get(name)
485528 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)
487533 elif disable is True or value is None:
488534 vrrps = self.get(name)
489535 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)
491540 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)
493545 else:
494546 raise ValueError("vrrp property 'primary_ip' must be "
495547 "a properly formatted IP address")
531583 if not str(value).isdigit() or value < 1 or value > 254:
532584 raise ValueError("vrrp property 'priority' must be "
533585 "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)
537594
538595 # Run the command if requested
539596 if run:
567624 be passed to the node
568625
569626 """
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)
573635
574636 # Run the command if requested
575637 if run:
607669 if not default and not disable:
608670 if value not in (2, 3):
609671 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)
613680
614681 # Run the command if requested
615682 if run:
674741
675742 # Build the commands to add and remove the secondary ip addresses
676743 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))
678748
679749 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))
681754
682755 cmds = sorted(cmds)
683756
718791 if not int(value) or int(value) < 1 or int(value) > 255:
719792 raise ValueError("vrrp property 'timers_advertise' must be"
720793 "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)
725804
726805 # Run the command if requested
727806 if run:
9311010 if not int(value) or int(value) < 1 or int(value) > 3600:
9321011 raise ValueError("vrrp property 'delay_reload' must be"
9331012 "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)
9371021
9381022 # Run the command if requested
9391023 if run:
10671151
10681152 if amount == unset:
10691153 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))
10721160 cmds.append(t_cmd.rstrip())
10731161
10741162 for track in add:
10791167
10801168 if amount == unset:
10811169 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))
10841176 cmds.append(t_cmd.rstrip())
10851177
10861178 cmds = sorted(cmds)
11551247 if primary_ip in ('no', None):
11561248 cmd = self.set_primary_ip(name, vrid, value=None,
11571249 disable=True, run=False)
1158 elif primary_ip is 'default':
1250 elif primary_ip == 'default':
11591251 cmd = self.set_primary_ip(name, vrid, value=None,
11601252 default=True, run=False)
11611253 else:
4747
4848 >>> import pyeapi
4949 >>> conn = pyeapi.connect(host='10.1.1.1', transport='http')
50 >>> conn.execute(['show verson'])
50 >>> conn.execute(['show version'])
5151 {u'jsonrpc': u'2.0', u'result': [{u'memTotal': 2028008, u'version':
5252 u'4.14.5F', u'internalVersion': u'4.14.5F-2209869.4145F', u'serialNumber':
5353 u'', u'systemMacAddress': u'00:0c:29:f5:d2:7d', u'bootupTimestamp':
8989 contains the settings for nodes used by the connect_to function.
9090
9191 """
92 from uuid import uuid4
9293 import os
9394 import re
9495
105106 from pyeapi.utils import load_module, make_iterable, debug
106107
107108 from pyeapi.eapilib import HttpEapiConnection, HttpsEapiConnection
109 from pyeapi.eapilib import HttpsEapiCertConnection
108110 from pyeapi.eapilib import SocketEapiConnection, HttpLocalEapiConnection
109111 from pyeapi.eapilib import CommandError
110112
114116 'socket': SocketEapiConnection,
115117 'http_local': HttpLocalEapiConnection,
116118 'http': HttpEapiConnection,
117 'https': HttpsEapiConnection
119 'https': HttpsEapiConnection,
120 'https_certs': HttpsEapiCertConnection
118121 }
119122
120123 DEFAULT_TRANSPORT = 'https'
326329 """
327330 return config.load(filename)
328331
332
329333 def config_for(name):
330334 """ Function to get settings for named config
331335
343347 """
344348 return config.get_connection(name)
345349
350
346351 def hosts_for_tag(tag):
347352 """ Returns the hosts assocated with the specified tag
348353
359364 None: If the specified tag does not exist, then None is returned.
360365 """
361366 return config.tags.get(tag)
367
362368
363369 def make_connection(transport, **kwargs):
364370 """ Creates a connection instance based on the transport
385391
386392
387393 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):
389396 """ Creates a connection using the supplied settings
390397
391398 This function will create a connection to an Arista EOS node using
404411 port (int): The TCP port of the endpoint for the eAPI connection. If
405412 this keyword is not specified, the default value is automatically
406413 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
407418 return_node (bool): Returns a Node object if True, otherwise
408419 returns an EapiConnection object.
409420
414425 """
415426 transport = transport or DEFAULT_TRANSPORT
416427 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)
418431 if return_node:
419432 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)
421435 return connection
422436
423437
457471 self._version = None
458472 self._version_number = None
459473 self._model = None
474 self._session_name = None
460475
461476 self._enablepwd = kwargs.get('enablepwd')
462477 self.autorefresh = kwargs.get('autorefresh', True)
516531 # Parse out version info
517532 output = self.enable('show version')
518533 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']))
520535 if match:
521536 self._version_number = str(match.group(0))
522537 else:
523538 self._version_number = str(output[0]['result']['version'])
524539 # 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']))
526541 if match:
527542 self._model = str(match.group(0))
528543 else:
564579 output from each command. The function will strip the
565580 response from any commands it prepends.
566581 """
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 """
567590 commands = make_iterable(commands)
568591 commands = list(commands)
569592
573596
574597 if self.autorefresh:
575598 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)
576617
577618 # pop the configure command output off the stack
578619 response.pop(0)
810851 self._running_config = None
811852 self._startup_config = None
812853
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
813889
814890 def connect_to(name):
815891 """Creates a node instance based on an entry from the config
5757 DEFAULT_HTTP_PORT = 80
5858 DEFAULT_HTTPS_PORT = 443
5959 DEFAULT_HTTP_LOCAL_PORT = 8080
60 DEFAULT_HTTPS_LOCAL_PORT = 8443
6061 DEFAULT_HTTP_PATH = '/command-api'
6162 DEFAULT_UNIX_SOCKET = '/var/run/command-api.sock'
6263
206207 return 'https://%s:%s/%s' % (self.host, self.port, self.path)
207208
208209
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
209259 class EapiConnection(object):
210260 """Creates a connection to eAPI for sending and receiving eAPI requests
211261
239289 the eAPI connection with
240290
241291 """
292 _auth_text = '{}:{}'.format(username, password)
293
242294 # Work around for Python 2.7/3.x compatibility
243295 if int(sys.version[0]) > 2:
244296 # For Python 3.x
245 _auth_text = '{}:{}'.format(username, password)
246297 _auth_bin = base64.encodebytes(_auth_text.encode())
247298 _auth = _auth_bin.decode()
248299 _auth = _auth.replace('\n', '')
249300 self._auth = _auth
250301 else:
251302 # For Python 2.7
252 _auth = base64.encodestring('{}:{}'.format(username, password))
303 _auth = base64.encodestring(_auth_text)
253304 self._auth = str(_auth).replace('\n', '')
254305
255 _LOGGER.debug('Autentication string is: {}'.format(self._auth))
306 _LOGGER.debug('Autentication string is: {}:***'.format(username))
256307
257308 def request(self, commands, encoding=None, reqid=None, **kwargs):
258309 """Generates an eAPI request object
296347 commands = make_iterable(commands)
297348 reqid = id(self) if reqid is None else reqid
298349 params = {'version': 1, 'cmds': commands, 'format': encoding}
350 streaming = False
299351 if 'autoComplete' in kwargs:
300352 params['autoComplete'] = kwargs['autoComplete']
301353 if 'expandAliases' in kwargs:
302354 params['expandAliases'] = kwargs['expandAliases']
355 if 'streaming' in kwargs:
356 streaming = kwargs['streaming']
303357 return json.dumps({'jsonrpc': '2.0', 'method': 'runCmds',
304 'params': params, 'id': str(reqid)})
358 'params': params, 'id': str(reqid),
359 'streaming': streaming})
305360
306361 def send(self, data):
307362 """Sends the eAPI request to the destination node
394449 reason=response.reason))
395450 _LOGGER.debug('Response content: {}'.format(response_content))
396451
452 if response.status == 401:
453 raise ConnectionError(str(self), '%s. %s' % (response.reason,
454 response_content))
455
397456 # Work around for Python 2.7/3.x compatibility
398457 if not type(response_content) == str:
399458 # For Python 3.x - decode bytes into string
415474 return decoded
416475
417476 # 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:
421478 _LOGGER.exception(exc)
479 self.socket_error = exc
422480 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)
427482 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')
429488 finally:
430489 self.transport.close()
431490
545604 def disable_certificate_verification(self):
546605 # SSL/TLS certificate verification is enabled by default in latest
547606 # 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).
549608 # Disable the SSL/TLS certificate verification for now.
550609 # Use the approach in PEP476 to disable certificate validation.
551610 # TODO:
554613 # temporary until a proper fix is implemented.
555614 if hasattr(ssl, '_create_unverified_context'):
556615 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)
3030 #
3131 import os
3232 import sys
33 import imp
33 import importlib
3434 import inspect
3535 import logging
3636 import logging.handlers
7777 The module that was imported
7878
7979 """
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)
10285 return mod
10386
10487
183166 # Convert unicode values to strings for Python 2
184167 if isinstance(value, unicode):
185168 value = str(value)
186 if isinstance(value, str):
169 if isinstance(value, str) or isinstance(value, dict):
187170 value = [value]
188171
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')
191178
192179 return value
193180
0 Metadata-Version: 1.1
0 Metadata-Version: 2.1
11 Name: pyeapi
2 Version: 0.8.1
2 Version: 0.8.4rc0
33 Summary: Python Client for eAPI
44 Home-page: https://github.com/arista-eosplus/pyeapi
55 Author: Arista EOS+ CS
66 Author-email: eosplus-dev@arista.com
77 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
288 Keywords: networking arista eos eapi
299 Platform: UNKNOWN
3010 Classifier: Development Status :: 4 - Beta
3414 Classifier: Programming Language :: Python :: 2
3515 Classifier: Programming Language :: Python :: 2.7
3616 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
1010 docs/Makefile
1111 docs/conf.py
1212 docs/configfile.rst
13 docs/configsessions.rst
1314 docs/contribute.rst
1415 docs/description.rst
1516 docs/examples.rst
3738 docs/release-notes-0.7.0.rst
3839 docs/release-notes-0.8.0.rst
3940 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
4044 docs/release-notes.rst
4145 docs/requirements.rst
4246 docs/subinterfaces.rst
4347 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
6848 examples/get_config.py
6949 examples/nodes.conf
7050 examples/simple.py
4848 'Programming Language :: Python :: 2',
4949 'Programming Language :: Python :: 2.7',
5050 'Programming Language :: Python :: 3',
51 'Programming Language :: Python :: 3.4',
51 'Programming Language :: Python :: 3.8',
5252 ],
5353
5454 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
44 transport: http
5 port: 8080
33 :use_ssl: true
44 :port: 199
55 :hostname: bogus
6
3232 match interface Ethernet2
3333 continue 200
3434 !
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 !
314314 spanning-tree max-hops 20
315315 no spanning-tree portfast bpduguard default
316316 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
319319 no spanning-tree portchannel guard misconfig
320320 spanning-tree bpduguard rate-limit default
321321 logging event spanning-tree global
347347 no aaa accounting dot1x default
348348 no aaa accounting commands all default
349349 !
350 no enable secret
350 no enable password
351351 no aaa root
352352 aaa authentication policy local allow-nopassword-remote-login
353353 no aaa authorization policy local default-role
390390 !
391391 vlan 100
392392 name mytest
393 state active
394 no private-vlan
395 !
396 vlan 200-202,204
397 name grouping
393398 state active
394399 no private-vlan
395400 !
3333 import string
3434 import unittest
3535
36 from mock import Mock
36 from mock import MagicMock as Mock
3737
3838 from pyeapi.client import Node
3939
7070
7171 def setUp(self):
7272 self.node = Node(None)
73
73 self.node._version_number = '4.17.1.1'
7474 self.node._running_config = self.config
7575
7676 self.mock_config = Mock(name='node.config')
225225 # Verify set_vrf returns False if no vrf by name is configured
226226 result = dut.api('interfaces').set_vrf(intf, 'test')
227227 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')
229232 # Verify interface has vrf applied
230233 result = dut.api('interfaces').set_vrf(intf, 'test')
231234 self.assertTrue(result)
232235 config = dut.run_commands('show running-config interfaces %s' %
233236 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'])
235241 # Verify interface has vrf removed
236242 result = dut.api('interfaces').set_vrf(intf, 'test', disable=True)
237243 self.assertTrue(result)
238244 config = dut.run_commands('show running-config interfaces %s' %
239245 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')
243254
244255
245256 class TestPortchannelInterface(DutSystemTest):
623634 api = dut.api('interfaces')
624635 instance = api.update_vlan('Vxlan1', '10', '10')
625636 self.assertTrue(instance)
626 self.contains('vxlan vlan 10 vni 10', dut)
637 self.contains('vxlan vlan add 10 vni 10', dut)
627638
628639 def test_remove_vlan(self):
629640 for dut in self.duts:
631642 api = dut.api('interfaces')
632643 instance = api.remove_vlan('Vxlan1', '10')
633644 self.assertTrue(instance)
634 self.notcontains('vxlan vlan 10 vni 10', dut)
645 self.notcontains('vxlan vlan remove 10 vni 10', dut)
635646
636647
637648 if __name__ == '__main__':
3030 #
3131 import os
3232 import unittest
33 import time
3334
3435 import sys
3536 sys.path.append(os.path.join(os.path.dirname(__file__), '../lib'))
5556 intf = random_interface(dut)
5657 dut.config(['default interface %s' % intf, 'interface %s' % intf,
5758 'no switchport'])
59 time.sleep(2)
5860 result = dut.api('ipinterfaces').get(intf)
5961 self.assertIsNone(result['address'])
6062
4242
4343 def test_get(self):
4444 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'])
4649 response = dut.api('ntp').get()
4750 self.assertIsNotNone(response)
4851
4952 def test_create(self):
5053 intf = 'Ethernet1'
5154 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'])
5359 response = dut.api('ntp').create(intf)
5460 self.assertTrue(response)
5561 response = dut.api('ntp').get()
5763
5864 def test_delete(self):
5965 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'])
6170 response = dut.api('ntp').delete()
6271 self.assertTrue(response)
6372 response = dut.api('ntp').get()
6574
6675 def test_default(self):
6776 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'])
6981 response = dut.api('ntp').default()
7082 self.assertTrue(response)
7183 response = dut.api('ntp').get()
7486 def test_set_source_interface(self):
7587 intf = 'Ethernet1'
7688 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'])
7893 response = dut.api('ntp').set_source_interface(intf)
7994 self.assertTrue(response)
8095 response = dut.api('ntp').get()
8398 def test_add_server_single(self):
8499 server = '10.10.10.35'
85100 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'])
87105 response = dut.api('ntp').add_server(server)
88106 self.assertTrue(response)
89107 response = dut.api('ntp').get()
94112 def test_add_server_multiple(self):
95113 servers = ['10.10.10.37', '10.10.10.36', '10.10.10.34']
96114 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'])
98119 for server in servers:
99120 response = dut.api('ntp').add_server(server)
100121 self.assertTrue(response)
106127 def test_add_server_prefer(self):
107128 server = '10.10.10.35'
108129 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'])
110134 response = dut.api('ntp').add_server(server, prefer=False)
111135 self.assertTrue(response)
112136 response = dut.api('ntp').get()
119143
120144 def test_add_server_invalid(self):
121145 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'])
123150 with self.assertRaises(ValueError):
124151 dut.api('ntp').add_server(None)
125152 dut.api('ntp').add_server('')
129156 server = '10.10.10.35'
130157 servers = ['10.10.10.37', '10.10.10.36', '10.10.10.34']
131158 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])
134165 for addserver in servers:
135166 dut.config(['ntp server %s' % addserver])
136167 response = dut.api('ntp').remove_server(server)
143174 def test_remove_all_servers(self):
144175 servers = ['10.10.10.37', '10.10.10.36', '10.10.10.34']
145176 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'])
147181 for addserver in servers:
148182 dut.config(['ntp server %s' % addserver])
149183 response = dut.api('ntp').remove_all_servers()
6060 dut.config(['no route-map TEST deny 10',
6161 'route-map TEST deny 10',
6262 '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',
6565 'match tag 50'])
6666 result = dut.api('routemaps').getall()
6767 self.assertIn(('TEST'), result)
68 self.assertIn(('TEST2'), result)
68 self.assertIn(('TEST-2'), result)
6969
7070 def test_create(self):
7171 for dut in self.duts:
5151
5252 def _next_hop():
5353 next_hop = choice(NEXT_HOPS)
54 if next_hop is 'Null0':
54 if next_hop == 'Null0':
5555 return (next_hop, None)
5656 ip1 = random_int(0, 223)
5757 ip2 = random_int(0, 255)
5858 ip3 = random_int(0, 255)
5959 ip4 = random_int(0, 255)
6060 ip_addr = "%s.%s.%s.%s" % (ip1, ip2, ip3, ip4)
61 if next_hop is 'IP':
61 if next_hop == 'IP':
6262 return (ip_addr, None)
6363 return (next_hop, ip_addr)
6464
7272
7373 def test_get_banner_with_EOF(self):
7474 for dut in self.duts:
75 motd_banner_value = '!!!newlinebaner\n\nSecondLIneEOF!!!newlinebanner\n'
75 motd_banner_value = '!!!newlinebaner\nSecondLIneEOF!!!newlinebanner\n'
7676 dut.config([dict(cmd="banner motd", input=motd_banner_value)])
7777 resp = dut.api('system').get()
7878 self.assertEqual(resp['banner_motd'], motd_banner_value.rstrip())
159159
160160 def test_set_banner_motd_donkey(self):
161161 for dut in self.duts:
162 donkey_chicken = """
162 donkey_chicken = r"""
163163 /\ /\
164164 ( \\ // )
165165 \ \\ // /
186186 self.assertTrue(resp, 'dut=%s' % dut)
187187 self.assertIn(donkey_chicken, dut.running_config)
188188
189
190
191189 def test_set_banner_motd_default(self):
192190 for dut in self.duts:
193191 dut.config([dict(cmd="banner motd",
4848
4949 def test_get(self):
5050 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])
5357
5458 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)
5966 result = self.sort_dict_by_keys(result)
6067 values = self.sort_dict_by_keys(values)
6168
138145 dut.config(['no username test', 'username test nopassword'])
139146 api = dut.api('users')
140147 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)
142152 result = api.set_sshkey('test', TEST_SSH_KEY)
143153 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)
145158
146159 def test_set_sshkey_with_empty_string(self):
147160 for dut in self.duts:
148161 dut.config(['no username test', 'username test nopassword'])
149162 api = dut.api('users')
150163 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)
152168 result = api.set_sshkey('test', '')
153169 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)
156176
157177 def test_set_sshkey_with_None(self):
158178 for dut in self.duts:
159179 dut.config(['no username test', 'username test nopassword'])
160180 api = dut.api('users')
161181 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)
163186 result = api.set_sshkey('test', None)
164187 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)
167194
168195 def test_set_sshkey_with_no_value(self):
169196 for dut in self.duts:
173200 self.assertIn('username test privilege 1 nopassword', api.config)
174201 result = api.set_sshkey('test', disable=True)
175202 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)
178209
179210
180211 if __name__ == '__main__':
4242
4343 def test_get(self):
4444 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'])
4751 response = dut.api('vrfs').get('blah')
4852 values = dict(rd='10:10', vrf_name='blah', description='blah desc',
4953 ipv4_routing=False, ipv6_routing=False)
5054 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'])
5259
5360 def test_getall(self):
5461 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'])
5768 response = dut.api('vrfs').getall()
5869 self.assertIsInstance(response, dict, 'dut=%s' % dut)
5970 self.assertEqual(len(response), 2)
6071 for vrf_name in ['blah', 'second']:
6172 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'])
6377
6478 def test_create_and_return_true(self):
6579 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'])
6784 result = dut.api('vrfs').create('blah')
6885 self.assertTrue(result, 'dut=%s' % dut)
6986 config = dut.run_commands('show vrf', encoding='text')
7087 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'])
7292
7393 def test_create_with_valid_rd(self):
7494 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'])
7699 result = dut.api('vrfs').create('blah', rd='10:10')
77100 self.assertTrue(result, 'dut=%s' % dut)
78101 command = 'show running-config section vrf'
79102 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)
82109 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'])
84114
85115 def test_create_and_return_false(self):
86116 for dut in self.duts:
89119
90120 def test_create_with_invalid_rd(self):
91121 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'])
93126 result = dut.api('vrfs').create('blah', rd='192.168.1.1:99999999')
94127 self.assertFalse(result, 'dut=%s' % dut)
95128 command = 'show running-config section vrf'
96129 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)
99136 self.assertNotIn('rd', config[0]['output'],
100137 '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'])
102142
103143 def test_delete_and_return_true(self):
104144 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')
106149 result = dut.api('vrfs').delete('blah')
107150 self.assertTrue(result, 'dut=%s' % dut)
108151 command = 'show running-config section vrf'
109152 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)
112159
113160 def test_delete_and_return_false(self):
114161 for dut in self.duts:
117164
118165 def test_default(self):
119166 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'])
122173 result = dut.api('vrfs').default('blah')
123174 self.assertTrue(result, 'dut=%s' % dut)
124175 command = 'show running-config section vrf'
125176 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'])
129185
130186 def test_set_rd(self):
131187 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'])
133192 result = dut.api('vrfs').set_rd('blah', '10:10')
134193 self.assertTrue(result, 'dut=%s' % dut)
135194 command = 'show running-config section vrf'
136195 config = dut.run_commands(command, encoding='text')
137196 self.assertIn('blah', config[0]['output'], 'dut=%s' % dut)
138197 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'])
140202
141203 def test_set_description(self):
142204 for dut in self.duts:
143205 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'])
145210 result = dut.api('vrfs').set_description('blah', description)
146211 self.assertTrue(result, 'dut=%s' % dut)
147212 command = 'show running-config section vrf'
153218 config = dut.run_commands(command, encoding='text')
154219 self.assertNotIn('description %s' % description,
155220 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'])
157225
158226 def test_set_ipv4_routing(self):
159227 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'])
162234 result = dut.api('vrfs').set_ipv4_routing('blah')
163235 self.assertTrue(result, 'dut=%s' % dut)
164236 command = 'show running-config section vrf'
170242 config = dut.run_commands(command, encoding='text')
171243 self.assertIn('no ip routing vrf blah', config[0]['output'],
172244 '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'])
174249
175250 def test_set_ipv6_routing(self):
176251 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'])
179258 result = dut.api('vrfs').set_ipv6_routing('blah')
180259 self.assertTrue(result, 'dut=%s' % dut)
181260 command = 'show running-config all section vrf'
187266 config = dut.run_commands(command, encoding='text')
188267 self.assertIn('no ipv6 unicast-routing vrf blah',
189268 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'])
191273
192274 def test_set_interface(self):
193275 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'])
197284 result = dut.api('vrfs').set_interface('blah', 'Ethernet1')
198285 self.assertTrue(result, 'dut=%s' % dut)
199286 command = 'show running-config interfaces Ethernet1'
200287 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)
203294 result = dut.api('vrfs').set_interface('blah', 'Ethernet1',
204295 disable=True)
205296 self.assertTrue(result, 'dut=%s' % dut)
206297 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'])
211308
212309
213310 if __name__ == '__main__':
7272 vrid = 98
7373 for dut in self.duts:
7474 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'])
7883 response = dut.api('vrrp').get(interface)
7984
8085 self.assertIsNotNone(response)
8489 vrid2 = 198
8590 for dut in self.duts:
8691 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'])
94108 response = dut.api('vrrp').getall()
95109
96110 self.assertIsNotNone(response)
121135 vrid = 101
122136 for dut in self.duts:
123137 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'])
128148
129149 response = dut.api('vrrp').delete(interface, vrid)
130150 self.assertIs(response, True)
133153 vrid = 102
134154 for dut in self.duts:
135155 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'])
140166
141167 response = dut.api('vrrp').delete(interface, vrid)
142168 self.assertIs(response, True)
197223 ]
198224 for dut in self.duts:
199225 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'])
205238
206239 for enable in enable_cases:
207240 response = dut.api('vrrp').set_enable(
219252 ]
220253 for dut in self.duts:
221254 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'])
226265
227266 for p_ip in primary_ip_cases:
228267 response = dut.api('vrrp').set_primary_ip(
240279 ]
241280 for dut in self.duts:
242281 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'])
247292
248293 for priority in priority_cases:
249294 response = dut.api('vrrp').set_priority(
261306 ]
262307 for dut in self.duts:
263308 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'])
268319
269320 for description in desc_cases:
270321 response = dut.api('vrrp').set_description(
280331 ]
281332 for dut in self.duts:
282333 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'])
287344
288345 for s_ip_list in secondary_ip_cases:
289346 response = dut.api('vrrp').set_secondary_ips(
302359 ]
303360 for dut in self.duts:
304361 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'])
309372
310373 for ip_version in ip_version_cases:
311374 response = dut.api('vrrp').set_ip_version(
323386 ]
324387 for dut in self.duts:
325388 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'])
330399
331400 for timers_advertise in timers_adv_cases:
332401 response = dut.api('vrrp').set_timers_advertise(
344413 ]
345414 for dut in self.duts:
346415 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'])
351426
352427 for mac_addr_adv_intvl in mac_addr_adv_int_cases:
353428 response = dut.api('vrrp').set_mac_addr_adv_interval(
366441 ]
367442 for dut in self.duts:
368443 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'])
373454
374455 for preempt in preempt_cases:
375456 response = dut.api('vrrp').set_preempt(
387468 ]
388469 for dut in self.duts:
389470 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'])
394481
395482 for preempt_delay_min in preempt_delay_min_cases:
396483 response = dut.api('vrrp').set_preempt_delay_min(
408495 ]
409496 for dut in self.duts:
410497 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'])
415508
416509 for preempt_delay_reload in preempt_delay_reload_cases:
417510 response = dut.api('vrrp').set_preempt_delay_reload(
429522 ]
430523 for dut in self.duts:
431524 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'])
436535
437536 for delay_reload in delay_reload_cases:
438537 response = dut.api('vrrp').set_delay_reload(
462561 ]
463562 for dut in self.duts:
464563 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'])
469574
470575 for track_list in track_cases:
471576 response = dut.api('vrrp').set_tracks(
483588 ]
484589 for dut in self.duts:
485590 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'])
490601
491602 for bfd_ip in bfd_ip_cases:
492603 response = dut.api('vrrp').set_bfd_ip(
2828 # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
2929 # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3030 #
31
3132 import os
3233 import unittest
3334
5657 if dut._enablepwd is not None:
5758 # If enable password defined for dut, set the
5859 # 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)
6078
6179 def test_populate_version_properties(self):
6280 for dut in self.duts:
7088 result = dut.run_commands('show version')
7189 self.assertIsInstance(result, list, 'dut=%s' % dut)
7290 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())
73103
74104 def test_enable_single_unicode_command(self):
75105 for dut in self.duts:
222252
223253 def tearDown(self):
224254 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")
226259
227260
228261 class TestNode(unittest.TestCase):
247280 # Send an incomplete command
248281 cases.append(('show run', rfmt
249282 % (1002, 'invalid command',
250 'incomplete token \(at token \d+: \'.*\'\)')))
283 r'incomplete token \(at token \d+: \'.*\'\)')))
251284 # Send a mangled command
252285 cases.append(('shwo version', rfmt
253286 % (1002, 'invalid command',
254 'Invalid input \(at token \d+: \'.*\'\)')))
287 r'Invalid input \(at token \d+: \'.*\'\)')))
255288 # Send a command that cannot be run through the api
256289 # note the command for reload looks to change in new EOS
257290 # in 4.15 the reload now is replaced with 'force' if you are
261294 cases.append(('reload', rfmt
262295 % (1004, 'incompatible command',
263296 '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.*')))
268297 # Send a command that has insufficient priv
269298 cases.append(('show running-config', rfmt
270299 % (1002, 'invalid command',
271 'Invalid input \(privileged mode required\)')))
272
300 r'Invalid input \(privileged mode required\)')))
273301
274302 for dut in self.duts:
275303 for (cmd, regex) in cases:
500500 self.eapi_positive_config_test(func, cmds)
501501
502502 def test_update_vlan(self):
503 cmds = ['interface Vxlan1', 'vxlan vlan 10 vni 10']
503 cmds = ['interface Vxlan1', 'vxlan vlan add 10 vni 10']
504504 func = function('update_vlan', 'Vxlan1', 10, 10)
505505 self.eapi_positive_config_test(func, cmds)
506506
507507 def test_remove_vlan(self):
508 cmds = ['interface Vxlan1', 'no vxlan vlan 10 vni']
508 cmds = ['interface Vxlan1', 'vxlan vlan remove 10 vni']
509509 func = function('remove_vlan', 'Vxlan1', 10)
510510 self.eapi_positive_config_test(func, cmds)
511511
6060 self.assertIsNone(self.instance.get('blah'))
6161
6262 def test_getall(self):
63 # Review fixtures/running_config.routemaps to see the default
64 # running-config that is the basis for this test
6365 result = self.instance.getall()
6466 self.assertIsInstance(result, dict)
67 self.assertEqual(len(result.keys()), 4)
6568
6669 def test_routemaps_functions(self):
6770 for name in ['create', 'delete', 'default']:
6262 trunk_groups=[])
6363 self.assertEqual(vlan, result)
6464
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
6571 def test_get_not_configured(self):
6672 self.assertIsNone(self.instance.get('1000'))
6773
6874 def test_getall(self):
6975 result = self.instance.getall()
7076 self.assertIsInstance(result, dict)
71 self.assertEqual(len(result), 4)
77 self.assertEqual(len(result), 5)
7278
7379 def test_vlan_functions(self):
7480 for name in ['create', 'delete', 'default']:
418418
419419 def test_set_ip_version(self):
420420 # vrrp 10 ip version 2
421
422421 # Test set_description gives properly formatted commands
423422 cases = [
424423 (2, None, None, 'vrrp %d ip version 2' % upd_vrid),
100100 self.connection.execute.assert_called_once_with(response, 'json')
101101 self.assertEqual(command, result[0]['result'])
102102
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
103113 def test_no_enable_with_single_command(self):
104114 command = random_string()
105115 response = [command]
299309 def test_connect_types(self, connection):
300310 transports = list(pyeapi.client.TRANSPORTS.keys())
301311 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)
303314
304315 for transport in transports:
305316 pyeapi.client.connect(transport)
397408 with patch.dict(pyeapi.client.TRANSPORTS, {'https': transport}):
398409 pyeapi.client.connect()
399410 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)
401413 transport.assert_called_once_with(**kwargs)
402414
403415 def test_connect_return_node(self):
409421 password='password', port=None,
410422 timeout=60, return_node=True)
411423 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)
413426 transport.assert_called_once_with(**kwargs)
414427 self.assertIsNone(node._enablepwd)
415428
423436 timeout=60, enablepwd='enablepwd',
424437 return_node=True)
425438 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)
427441 transport.assert_called_once_with(**kwargs)
428442 self.assertEqual(node._enablepwd, 'enablepwd')
429443
434448 pyeapi.client.load_config(filename=conf)
435449 node = pyeapi.client.connect_to('test1')
436450 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)
438453 transport.assert_called_once_with(**kwargs)
439454 self.assertEqual(node._enablepwd, 'enablepwd')
440455
103103
104104 self.assertTrue(mock_transport.close.called)
105105
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
106125 def test_send_raises_connection_error(self):
107126 mock_transport = Mock(name='transport')
108127 mockcfg = {'getresponse.return_value.read.side_effect': ValueError}
33 from mock import patch, Mock
44
55 import pyeapi.utils
6
67
78 class TestUtils(unittest.TestCase):
89
2223 def test_make_iterable_from_string(self):
2324 result = pyeapi.utils.make_iterable('test')
2425 self.assertIsInstance(result, collections.Iterable)
25 self.assertEquals(len(result), 1)
26 self.assertEqual(len(result), 1)
2627
2728 def test_make_iterable_from_unicode(self):
2829 result = pyeapi.utils.make_iterable(u'test')
2930 self.assertIsInstance(result, collections.Iterable)
30 self.assertEquals(len(result), 1)
31 self.assertEqual(len(result), 1)
3132
3233 def test_make_iterable_from_iterable(self):
3334 result = pyeapi.utils.make_iterable(['test'])
3435 self.assertIsInstance(result, collections.Iterable)
35 self.assertEquals(len(result), 1)
36 self.assertEqual(len(result), 1)
3637
3738 def test_make_iterable_raises_type_error(self):
3839 with self.assertRaises(TypeError):