[Python-modules-commits] [pyeapi] 07/12: Import pyeapi_0.7.0.orig.tar.gz
Vincent Bernat
bernat at moszumanska.debian.org
Sun Sep 25 15:45:45 UTC 2016
This is an automated email from the git hooks/post-receive script.
bernat pushed a commit to branch master
in repository pyeapi.
commit 32d585d9cdf039bde78ddb31d3f00c2d589cf96c
Author: Vincent Bernat <bernat at debian.org>
Date: Sun Sep 25 17:41:58 2016 +0200
Import pyeapi_0.7.0.orig.tar.gz
---
.coveragerc | 3 +
LICENSE | 2 +-
MANIFEST.in | 1 +
Makefile | 3 +-
PKG-INFO | 2 +-
VERSION | 2 +-
docs/release-notes-0.7.0.rst | 27 +++
docs/release-notes.rst | 29 ++--
pyeapi.egg-info/PKG-INFO | 2 +-
pyeapi.egg-info/SOURCES.txt | 5 +
pyeapi/__init__.py | 2 +-
pyeapi/api/mlag.py | 2 +-
pyeapi/api/ospf.py | 338 ++++++++++++++++++++++++++++++++++++++
pyeapi/client.py | 40 +++--
pyeapi/eapilib.py | 2 +-
pyeapi/utils.py | 39 ++---
test/fixtures/running_config.ospf | 29 ++++
test/system/test_api_mlag.py | 7 +-
test/system/test_api_ospf.py | 185 +++++++++++++++++++++
test/system/test_client.py | 42 ++++-
test/unit/test_api_mlag.py | 4 +-
test/unit/test_api_ospf.py | 137 +++++++++++++++
test/unit/test_client.py | 12 ++
test/unit/test_utils.py | 2 +-
24 files changed, 847 insertions(+), 70 deletions(-)
diff --git a/.coveragerc b/.coveragerc
index 8726c6c..deafaca 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -4,6 +4,7 @@ branch = True
omit = *mock*
*test*
*netaddr*
+ *site-packages*
[report]
# Regexes for lines to exclude from consideration
@@ -23,6 +24,8 @@ exclude_lines =
if 0:
if __name__ == .__main__.:
+show_missing = True
+
ignore_errors = True
[html]
diff --git a/LICENSE b/LICENSE
index 60a2b6a..7370f69 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2014, Arista Networks EOS+
+Copyright (c) 2016, Arista Networks EOS+
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/MANIFEST.in b/MANIFEST.in
index df2bb8e..70e6dc3 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -13,6 +13,7 @@ recursive-include test *.py
recursive-include test *.text
recursive-include test *.vxlan
recursive-include test *.bgp
+recursive-include test *.ospf
recursive-include test *.routemaps
recursive-include test *.varp
recursive-include test *.varp_null
diff --git a/Makefile b/Makefile
index 9829dcb..7cbd456 100644
--- a/Makefile
+++ b/Makefile
@@ -13,6 +13,7 @@
# make unittest -- runs the unit tests
# make systest -- runs the system tests
# make clean -- clean distutils
+# make coverage_report -- code coverage report
#
########################################################
# variable section
@@ -67,4 +68,4 @@ systest: clean
$(COVERAGE) run -m unittest discover test/system -v
coverage_report:
- $(COVERAGE) report -m
+ $(COVERAGE) report --rcfile=".coveragerc"
diff --git a/PKG-INFO b/PKG-INFO
index aa201b0..cb9b450 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: pyeapi
-Version: 0.6.1
+Version: 0.7.0
Summary: Python Client for eAPI
Home-page: https://github.com/arista-eosplus/pyeapi
Author: Arista EOS+ CS
diff --git a/VERSION b/VERSION
index ee6cdce..faef31a 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.6.1
+0.7.0
diff --git a/docs/release-notes-0.7.0.rst b/docs/release-notes-0.7.0.rst
new file mode 100644
index 0000000..6970888
--- /dev/null
+++ b/docs/release-notes-0.7.0.rst
@@ -0,0 +1,27 @@
+######
+v0.7.0
+######
+
+2016-09-08
+
+New Modules
+^^^^^^^^^^^
+
+* Add OSPF API (`95 <https://github.com/arista-eosplus/pyeapi/pull/95>`_) [`brigoldberg <https://github.com/brigoldberg>`_]
+ Big thanks for the community support!
+
+Enhancements
+^^^^^^^^^^^^
+
+* Enhance Node enable() method (`100 <https://github.com/arista-eosplus/pyeapi/pull/100>`_) [`dathelen <https://github.com/dathelen>`_]
+ This enhancement adds a send_enable flag to the enable and run_commands Node methods. By default the enable command will be sent, however you can now run commands without prepending the enable.
+* Finish OSPF API (`99 <https://github.com/arista-eosplus/pyeapi/pull/99>`_) [`dathelen <https://github.com/dathelen>`_]
+ Create system tests and add unit tests to increase code coverage.
+* Add Cross-Platform Support for pyeapi (`94 <https://github.com/arista-eosplus/pyeapi/pull/94>`_) [`grybak-arista <https://github.com/grybak-arista>`_]
+ Use logging instead of syslog for better cross-platform compatibility. This enhancement provides support for Windows users.
+
+Fixed
+^^^^^
+
+* Allow dot and hyphen in mlag domain-id (`91 <https://github.com/arista-eosplus/pyeapi/issues/91>`_)
+ Include handling any character in domain-id string, including dot, hyphen, and space.
diff --git a/docs/release-notes.rst b/docs/release-notes.rst
index b406f3a..c9a806c 100644
--- a/docs/release-notes.rst
+++ b/docs/release-notes.rst
@@ -6,18 +6,19 @@ Release Notes
:maxdepth: 2
:titlesonly:
- release-notes-0.1.0.rst
- release-notes-0.1.1.rst
- release-notes-0.2.0.rst
- release-notes-0.2.1.rst
- release-notes-0.2.2.rst
- release-notes-0.2.3.rst
- release-notes-0.2.4.rst
- release-notes-0.3.0.rst
- release-notes-0.3.1.rst
- release-notes-0.3.2.rst
- release-notes-0.3.3.rst
- release-notes-0.4.0.rst
- release-notes-0.5.0.rst
- release-notes-0.6.0.rst
+ release-notes-0.7.0.rst
release-notes-0.6.1.rst
+ release-notes-0.6.0.rst
+ release-notes-0.5.0.rst
+ release-notes-0.4.0.rst
+ release-notes-0.3.3.rst
+ release-notes-0.3.2.rst
+ release-notes-0.3.1.rst
+ release-notes-0.3.0.rst
+ release-notes-0.2.4.rst
+ release-notes-0.2.3.rst
+ release-notes-0.2.2.rst
+ release-notes-0.2.1.rst
+ release-notes-0.2.0.rst
+ release-notes-0.1.1.rst
+ release-notes-0.1.0.rst
diff --git a/pyeapi.egg-info/PKG-INFO b/pyeapi.egg-info/PKG-INFO
index aa201b0..cb9b450 100644
--- a/pyeapi.egg-info/PKG-INFO
+++ b/pyeapi.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: pyeapi
-Version: 0.6.1
+Version: 0.7.0
Summary: Python Client for eAPI
Home-page: https://github.com/arista-eosplus/pyeapi
Author: Arista EOS+ CS
diff --git a/pyeapi.egg-info/SOURCES.txt b/pyeapi.egg-info/SOURCES.txt
index 0a1f5c0..ec12dd7 100644
--- a/pyeapi.egg-info/SOURCES.txt
+++ b/pyeapi.egg-info/SOURCES.txt
@@ -34,6 +34,7 @@ docs/release-notes-0.4.0.rst
docs/release-notes-0.5.0.rst
docs/release-notes-0.6.0.rst
docs/release-notes-0.6.1.rst
+docs/release-notes-0.7.0.rst
docs/release-notes.rst
docs/requirements.rst
docs/support.rst
@@ -59,6 +60,7 @@ pyeapi/api/interfaces.py
pyeapi/api/ipinterfaces.py
pyeapi/api/mlag.py
pyeapi/api/ntp.py
+pyeapi/api/ospf.py
pyeapi/api/routemaps.py
pyeapi/api/spanningtree.py
pyeapi/api/staticroute.py
@@ -77,6 +79,7 @@ test/fixtures/env_path.conf
test/fixtures/ipinterfaces.json
test/fixtures/nohost.conf
test/fixtures/running_config.bgp
+test/fixtures/running_config.ospf
test/fixtures/running_config.portchannel
test/fixtures/running_config.routemaps
test/fixtures/running_config.text
@@ -97,6 +100,7 @@ test/system/test_api_interfaces.py
test/system/test_api_ipinterfaces.py
test/system/test_api_mlag.py
test/system/test_api_ntp.py
+test/system/test_api_ospf.py
test/system/test_api_routemaps.py
test/system/test_api_staticroute.py
test/system/test_api_stp.py
@@ -113,6 +117,7 @@ test/unit/test_api_interfaces.py
test/unit/test_api_ipinterfaces.py
test/unit/test_api_mlag.py
test/unit/test_api_ntp.py
+test/unit/test_api_ospf.py
test/unit/test_api_routemaps.py
test/unit/test_api_staticroute.py
test/unit/test_api_stp.py
diff --git a/pyeapi/__init__.py b/pyeapi/__init__.py
index 3d330ab..2d0d25f 100644
--- a/pyeapi/__init__.py
+++ b/pyeapi/__init__.py
@@ -29,7 +29,7 @@
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
-__version__ = '0.6.1'
+__version__ = '0.7.0'
__author__ = 'Arista EOS+'
diff --git a/pyeapi/api/mlag.py b/pyeapi/api/mlag.py
index f958f01..6c523d8 100644
--- a/pyeapi/api/mlag.py
+++ b/pyeapi/api/mlag.py
@@ -116,7 +116,7 @@ class Mlag(Entity):
dict: A dict object that is intended to be merged into the
resource dict
"""
- match = re.search(r'domain-id (\w+)', config)
+ match = re.search(r'domain-id (.+)$', config)
value = match.group(1) if match else None
return dict(domain_id=value)
diff --git a/pyeapi/api/ospf.py b/pyeapi/api/ospf.py
new file mode 100644
index 0000000..28c1325
--- /dev/null
+++ b/pyeapi/api/ospf.py
@@ -0,0 +1,338 @@
+#
+# Copyright (c) 2016, Arista Networks, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# Neither the name of Arista Networks nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARISTA NETWORKS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+"""Module for working with OSPF configuration in EOS
+
+This module provides an API for creating/modifying/deleting
+OSPF configurations
+
+"""
+
+import re
+from pyeapi.api import Entity
+from pyeapi.utils import make_iterable
+
+class Ospf(Entity):
+ """ The Ospf class implements global Ospf router configuration
+ """
+
+ def __init__(self, *args, **kwargs):
+ super(Ospf, self).__init__(*args, **kwargs)
+ pass
+
+ def get(self):
+ """Returns the OSPF routing configuration
+
+ Args:
+ None
+ Returns:
+ dict:
+ keys: router_id (int): OSPF router-id
+ networks (dict): All networks that
+ are advertised in OSPF
+ ospf_process_id (int): OSPF proc id
+ redistribution (dict): All protocols that
+ are configured to be
+ redistributed in OSPF
+ shutdown (bool): Gives the current shutdown
+ off the process
+ """
+
+ config = self.get_block('^router ospf .*')
+ if not config:
+ return None
+
+ response = dict()
+ response.update(self._parse_router_id(config))
+ response.update(self._parse_networks(config))
+ response.update(self._parse_ospf_process_id(config))
+ response.update(self._parse_redistribution(config))
+ response.update(self._parse_shutdown(config))
+
+ return response
+
+ def _parse_ospf_process_id(self, config):
+ """Parses config file for the OSPF proc ID
+
+ Args:
+ config(str): Running configuration
+ Returns:
+ dict: key: ospf_process_id (int)
+ """
+ match = re.search(r'^router ospf (\d+)', config)
+ return dict(ospf_process_id=int(match.group(1)))
+
+ def _parse_router_id(self, config):
+ """Parses config file for the OSPF router ID
+
+ Args:
+ config(str): Running configuration
+ Returns:
+ dict: key: router_id (str)
+ """
+ match = re.search(r'router-id ([^\s]+)', config)
+ value = match.group(1) if match else None
+ return dict(router_id=value)
+
+ def _parse_networks(self, config):
+ """Parses config file for the networks advertised
+ by the OSPF process
+
+ Args:
+ config(str): Running configuration
+ Returns:
+ list: dict:
+ keys: network (str)
+ netmask (str)
+ area (str)
+ """
+
+ networks = list()
+ regexp = r'network (.+)/(\d+) area (\d+\.\d+\.\d+\.\d+)'
+ matches = re.findall(regexp, config)
+ for (network, netmask, area) in matches:
+ networks.append(dict(network=network, netmask=netmask, area=area))
+ return dict(networks=networks)
+
+ def _parse_redistribution(self, config):
+ """Parses config file for the OSPF router ID
+
+ Args:
+ config (str): Running configuration
+ Returns:
+ list: dict:
+ keys: protocol (str)
+ route-map (optional) (str)
+ """
+ redistributions = list()
+ regexp = r'redistribute .*'
+ matches = re.findall(regexp, config)
+ for line in matches:
+ ospf_redist = line.split()
+ if len(ospf_redist) == 2:
+ # simple redist: eg 'redistribute bgp'
+ protocol = ospf_redist[1]
+ redistributions.append(dict(protocol=protocol))
+ if len(ospf_redist) == 4:
+ # complex redist eg 'redistribute bgp route-map NYSE-RP-MAP'
+ protocol = ospf_redist[1]
+ route_map_name = ospf_redist[3]
+ redistributions.append(dict(protocol=protocol,
+ route_map=route_map_name))
+ return dict(redistributions=redistributions)
+
+ def _parse_shutdown(self, config):
+ """Parses config file for the OSPF router ID
+
+ Args:
+ config(str): Running configuration
+ Returns:
+ dict: key: shutdown (bool)
+ """
+
+ value = 'no shutdown' in config
+ return dict(shutdown=not value)
+
+ def set_shutdown(self):
+ """Shutdowns the OSPF process
+
+ Args:
+ None
+ Returns:
+ bool: True if the commands are completed successfully
+ """
+
+ cmd = 'shutdown'
+ return self.configure_ospf(cmd)
+
+ def set_no_shutdown(self):
+ """Removes the shutdown property from the OSPF process
+
+ Args:
+ None
+ Returns:
+ bool: True if the commands are completed successfully
+ """
+
+
+ cmd = 'no shutdown'
+ return self.configure_ospf(cmd)
+
+ def delete(self):
+ """Removes the entire ospf process from the running configuration
+
+ Args:
+ None
+ Returns:
+ bool: True if the command completed succssfully
+ """
+ config = self.get()
+ if not config:
+ return True
+ command = 'no router ospf {}'.format(config['ospf_process_id'])
+ return self.configure(command)
+
+ def create(self, ospf_process_id):
+ """Creates a OSPF process in the default VRF
+
+ Args:
+ ospf_process_id (str): The OSPF proccess Id value
+ Returns:
+ bool: True if the command completed successfully
+ Exception:
+ ValueError: If the ospf_process_id passed in less
+ than 0 or greater than 65536
+ """
+ value = int(ospf_process_id)
+ if not 0 < value < 65536:
+ raise ValueError('ospf as must be between 1 and 65535')
+ command = 'router ospf {}'.format(ospf_process_id)
+ return self.configure(command)
+
+ def configure_ospf(self, cmd):
+ """Allows for a list of OSPF subcommands to be configured"
+
+ Args:
+ cmd: (list or str): Subcommand to be entered
+ Returns:
+ bool: True if all the commands completed successfully
+ """
+ config = self.get()
+ cmds = ['router ospf {}'.format(config['ospf_process_id'])]
+ cmds.extend(make_iterable(cmd))
+ return super(Ospf, self).configure(cmds)
+
+ def set_router_id(self, value=None, default=False, disable=False):
+ """Controls the router id property for the OSPF Proccess
+
+ Args:
+ value (str): The router-id value
+ default (bool): Controls the use of the default keyword
+ disable (bool): Controls the use of the no keyword
+ Returns:
+ bool: True if the commands are completed successfully
+ """
+ cmd = self.command_builder('router-id', value=value,
+ default=default, disable=disable)
+ return self.configure_ospf(cmd)
+
+ def add_network(self, network, netmask, area=0):
+ """Adds a network to be advertised by OSPF
+
+ Args:
+ network (str): The network to be advertised in dotted decimal
+ notation
+ netmask (str): The netmask to configure
+ area (str): The area the network belongs to.
+ By default this value is 0
+ Returns:
+ bool: True if the command completes successfully
+ Exception:
+ ValueError: This will get raised if network or netmask
+ are not passed to the method
+ """
+ if network == '' or netmask == '':
+ raise ValueError('network and mask values '
+ 'may not be empty')
+ cmd = 'network {}/{} area {}'.format(network, netmask, area)
+ return self.configure_ospf(cmd)
+
+ def remove_network(self, network, netmask, area=0):
+ """Removes a network advertisment by OSPF
+
+ Args:
+ network (str): The network to be removed in dotted decimal
+ notation
+ netmask (str): The netmask to configure
+ area (str): The area the network belongs to.
+ By default this value is 0
+ Returns:
+ bool: True if the command completes successfully
+ Exception:
+ ValueError: This will get raised if network or netmask
+ are not passed to the method
+ """
+
+ if network == '' or netmask == '':
+ raise ValueError('network and mask values '
+ 'may not be empty')
+ cmd = 'no network {}/{} area {}'.format(network, netmask, area)
+ return self.configure_ospf(cmd)
+
+ def add_redistribution(self, protocol, route_map_name=None):
+ """Adds a protocol redistribution to OSPF
+
+ Args:
+ protocol (str): protocol to redistribute
+ route_map_name (str): route-map to be used to
+ filter the protocols
+ Returns:
+ bool: True if the command completes successfully
+ Exception:
+ ValueError: This will be raised if the protocol pass is not one
+ of the following: [rip, bgp, static, connected]
+ """
+ protocols = ['bgp', 'rip', 'static', 'connected']
+ if protocol not in protocols:
+ raise ValueError('redistributed protocol must be'
+ 'bgp, connected, rip or static')
+ if route_map_name is None:
+ cmd = 'redistribute {}'.format(protocol)
+ else:
+ cmd = 'redistribute {} route-map {}'.format(protocol,
+ route_map_name)
+ return self.configure_ospf(cmd)
+
+ def remove_redistribution(self, protocol):
+ """Removes a protocol redistribution to OSPF
+
+ Args:
+ protocol (str): protocol to redistribute
+ route_map_name (str): route-map to be used to
+ filter the protocols
+ Returns:
+ bool: True if the command completes successfully
+ Exception:
+ ValueError: This will be raised if the protocol pass is not one
+ of the following: [rip, bgp, static, connected]
+ """
+
+ protocols = ['bgp', 'rip', 'static', 'connected']
+ if protocol not in protocols:
+ raise ValueError('redistributed protocol must be'
+ 'bgp, connected, rip or static')
+ cmd = 'no redistribute {}'.format(protocol)
+ return self.configure_ospf(cmd)
+
+def instance(api):
+ """Returns an instance of Ospf
+ """
+ return Ospf(api)
diff --git a/pyeapi/client.py b/pyeapi/client.py
index 6223197..ab6fb0f 100644
--- a/pyeapi/client.py
+++ b/pyeapi/client.py
@@ -91,6 +91,7 @@ contains the settings for nodes used by the connect_to function.
"""
import os
+import sys
import logging
import re
@@ -104,14 +105,12 @@ except ImportError:
from ConfigParser import SafeConfigParser
from ConfigParser import Error as SafeConfigParserError
-from pyeapi.utils import load_module, make_iterable, syslog_warning
+from pyeapi.utils import load_module, make_iterable, debug
from pyeapi.eapilib import HttpEapiConnection, HttpsEapiConnection
from pyeapi.eapilib import SocketEapiConnection, HttpLocalEapiConnection
from pyeapi.eapilib import CommandError
-LOGGER = logging.getLogger(__name__)
-
CONFIG_SEARCH_PATH = ['~/.eapi.conf', '/mnt/flash/eapi.conf']
TRANSPORTS = {
@@ -193,12 +192,14 @@ class Config(SafeConfigParser):
Args:
filename (str): The full path to the file to load
"""
+
try:
SafeConfigParser.read(self, filename)
except SafeConfigParserError as exc:
# Ignore file and syslog a message on SafeConfigParser errors
- syslog_warning("%s: parsing error in eapi conf file: %s" %
- (type(exc).__name__, filename))
+ msg = ("%s: parsing error in eapi conf file: %s" %
+ (type(exc).__name__, filename))
+ debug(msg)
self._add_default_connection()
@@ -559,7 +560,8 @@ class Node(object):
block_end = line_end + block_end
return config[block_start:block_end]
- def enable(self, commands, encoding='json', strict=False):
+ def enable(self, commands, encoding='json', strict=False,
+ send_enable=True):
"""Sends the array of commands to the node in enable mode
This method will send the commands to the node and evaluate
@@ -575,6 +577,8 @@ class Node(object):
strict (bool): If False, this method will attempt to run a
command with text encoding if JSON encoding fails
+ send_enable (bool): If True the enable command will be
+ prepended to the command list automatically.
Returns:
A dict object that includes the response for each command along
@@ -603,7 +607,7 @@ class Node(object):
# there in error and both are now present to avoid breaking
# existing scripts. 'response' will be removed in a future release.
if strict:
- responses = self.run_commands(commands, encoding)
+ responses = self.run_commands(commands, encoding, send_enable)
for index, response in enumerate(responses):
results.append(dict(command=commands[index],
result=response,
@@ -612,13 +616,13 @@ class Node(object):
else:
for command in commands:
try:
- resp = self.run_commands(command, encoding)
+ resp = self.run_commands(command, encoding, send_enable)
results.append(dict(command=command,
result=resp[0],
encoding=encoding))
except CommandError as exc:
if exc.error_code == 1003:
- resp = self.run_commands(command, 'text')
+ resp = self.run_commands(command, 'text', send_enable)
results.append(dict(command=command,
result=resp[0],
encoding='text'))
@@ -626,7 +630,7 @@ class Node(object):
raise
return results
- def run_commands(self, commands, encoding='json'):
+ def run_commands(self, commands, encoding='json', send_enable=True):
"""Sends the commands over the transport to the device
This method sends the commands to the device using the nodes
@@ -638,6 +642,8 @@ class Node(object):
device using the transport
encoding (str): The encoding method to use for the request and
excpected response.
+ send_enable (bool): If True the enable command will be
+ prepended to the command list automatically.
Returns:
This method will return the raw response from the connection
@@ -657,15 +663,17 @@ class Node(object):
'input': '%s\n' % (c.split('MULTILINE:')[1].strip())}
if 'MULTILINE:' in c else c for c in commands]
- if self._enablepwd:
- commands.insert(0, {'cmd': 'enable', 'input': self._enablepwd})
- else:
- commands.insert(0, 'enable')
+ if send_enable:
+ if self._enablepwd:
+ commands.insert(0, {'cmd': 'enable', 'input': self._enablepwd})
+ else:
+ commands.insert(0, 'enable')
response = self._connection.execute(commands, encoding)
- # pop enable command from the response
- response['result'].pop(0)
+ # pop enable command from the response only if we sent enable
+ if send_enable:
+ response['result'].pop(0)
return response['result']
diff --git a/pyeapi/eapilib.py b/pyeapi/eapilib.py
index 267ecfa..26b867e 100644
--- a/pyeapi/eapilib.py
+++ b/pyeapi/eapilib.py
@@ -389,7 +389,7 @@ class EapiConnection(object):
# For Python 3.x - decode bytes into string
response_content = response_content.decode()
decoded = json.loads(response_content)
- debug('eapi_response: %s' % decoded)
+ _LOGGER.debug('eapi_response: %s' % decoded)
if 'error' in decoded:
(code, msg, err, out) = self._parse_error_message(decoded)
diff --git a/pyeapi/utils.py b/pyeapi/utils.py
index 9af4841..82fd140 100644
--- a/pyeapi/utils.py
+++ b/pyeapi/utils.py
@@ -32,9 +32,9 @@
import os
import sys
import imp
+import inspect
import logging
import logging.handlers
-import syslog
import collections
from itertools import tee
@@ -47,10 +47,20 @@ except ImportError:
from itertools import izip_longest as zip_longest
_LOGGER = logging.getLogger(__name__)
+_LOGGER.setLevel(logging.DEBUG)
-_syslog_handler = logging.handlers.SysLogHandler()
+# Create a handler to log messages to syslog
+if sys.platform == "darwin":
+ _syslog_handler = logging.handlers.SysLogHandler(address='/var/run/syslog')
+else:
+ _syslog_handler = logging.handlers.SysLogHandler()
_LOGGER.addHandler(_syslog_handler)
-_LOGGER.setLevel(logging.INFO)
+
+# Create a handler to log messages to stderr
+_stderr_formatter = logging.Formatter('\n\n******** LOG NOTE ********\n%(message)s\n')
+_stderr_handler = logging.StreamHandler()
+_stderr_handler.setFormatter(_stderr_formatter)
+_LOGGER.addHandler(_stderr_handler)
def import_module(name):
""" Imports a module into the current runtime environment
@@ -140,26 +150,17 @@ def islocalconnection():
return os.path.exists('/etc/Eos-release')
def debug(text):
- """Prints text to syslog when on a local connection
-
- Args:
- text (str): The string object to print to syslog
-
- """
-
- if islocalconnection():
- _LOGGER.debug(text)
-
-def syslog_warning(text):
- """Print text to syslog at warning level
+ """Log a message to syslog and stderr
Args:
- text (str): The string object to print to syslog
+ text (str): The string object to print
"""
-
- syslog.openlog("pyeapi")
- syslog.syslog(syslog.LOG_WARNING, text)
+ frame = inspect.currentframe().f_back
+ module = frame.f_globals['__name__']
+ func = frame.f_code.co_name
+ msg = "%s.%s: %s" % (module, func, text)
+ _LOGGER.debug(msg)
def make_iterable(value):
"""Converts the supplied value to a list object
diff --git a/test/fixtures/running_config.ospf b/test/fixtures/running_config.ospf
new file mode 100644
index 0000000..92f03b6
--- /dev/null
+++ b/test/fixtures/running_config.ospf
@@ -0,0 +1,29 @@
+!
+ip routing
+!
+router ospf 65000
+ router-id 1.1.1.1
+ no bfd all-interfaces
+ distance ospf intra-area 110
+ distance ospf external 110
+ distance ospf inter-area 110
+ redistribute bgp route-map RM-IN
+ redistribute bgp route-map RM-OUT
+ redistribute static
+ area 0.0.0.0 default-cost 10
+ network 172.16.10.0/24 area 0.0.0.0
+ network 172.17.0.0/16 area 0.0.0.0
+ max-lsa 12000 75 ignore-time 5 ignore-count 5 reset-time 5
+ adjacency exchange-start threshold 20
+ log-adjacency-changes
+ timers throttle spf 0 5000 5000
+ timers lsa arrival 1000
+ timers throttle lsa all 1000 5000 5000
+ no timers out-delay
+ maximum-paths 128
+ no timers pacing flood
+ no max-metric router-lsa
+ point-to-point routes
+ no graceful-restart
+!
+!
diff --git a/test/system/test_api_mlag.py b/test/system/test_api_mlag.py
index e7a75c7..7c395c7 100644
--- a/test/system/test_api_mlag.py
+++ b/test/system/test_api_mlag.py
@@ -55,9 +55,10 @@ class TestApiMlag(DutSystemTest):
dut.config('default mlag configuration')
api = dut.api('mlag')
self.assertIn('no domain-id', api.get_block('mlag configuration'))
- result = dut.api('mlag').set_domain_id('test')
- self.assertTrue(result)
- self.assertIn('domain-id test', api.get_block('mlag configuration'))
+ for domid in ['test_domain_id', 'test.dom-id', 'test domain id']:
+ result = dut.api('mlag').set_domain_id(domid)
+ self.assertTrue(result)
+ self.assertIn('domain-id %s' % domid, api.get_block('mlag configuration'))
def test_set_domain_id_with_no_value(self):
for dut in self.duts:
diff --git a/test/system/test_api_ospf.py b/test/system/test_api_ospf.py
new file mode 100644
index 0000000..bed21cd
--- /dev/null
+++ b/test/system/test_api_ospf.py
@@ -0,0 +1,185 @@
+#
+# Copyright (c) 2016, Arista Networks, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# Neither the name of Arista Networks nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARISTA NETWORKS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+import os
+import sys
+sys.path.append(os.path.join(os.path.dirname(__file__), '../lib'))
+
+from random import randint
+from systestlib import DutSystemTest
+
+def clear_ospf_config(dut, id=None):
+ if id is None:
+ try:
+ id = int(dut.get_config(params="section ospf")[0].split()[2])
+ dut.config(['no router ospf %d' % id])
+ except IndexError:
+ '''No OSPF configured'''
+ pass
+
+class TestApiOspf(DutSystemTest):
+ def test_get(self):
+ for dut in self.duts:
+ clear_ospf_config(dut)
+ dut.config(["router ospf 1", "router-id 1.1.1.1", "network 2.2.2.0/24 area 0",
+ "redistribute bgp"])
+ ospf_response = dut.api('ospf').get()
+ config = dict(router_id="1.1.1.1", ospf_process_id=1,
+ networks=[dict(netmask='24', network="2.2.2.0", area="0.0.0.0")],
+ redistributions=[dict(protocol="bgp")], shutdown=False)
+ self.assertEqual(ospf_response, config)
+
+ def test_shutdown(self):
+ for dut in self.duts:
+ clear_ospf_config(dut)
+ dut.config(["router ospf 1", "network 1.1.1.1/32 area 0"])
+ ospf = dut.api('ospf')
+ response = ospf.set_shutdown()
+ self.assertTrue(response)
+ self.assertIn('shutdown', ospf.get_block("router ospf 1"))
+
+ def test_no_shutown(self):
+ for dut in self.duts:
+ clear_ospf_config(dut)
+ dut.config(["router ospf 10", "network 1.1.1.0/24 area 0",
+ "shutdown"])
+ ospf = dut.api('ospf')
+ response = ospf.set_no_shutdown()
+ self.assertTrue(response)
+ self.assertIn('no shutdown', ospf.get_block("router ospf 10"))
+
+ def test_delete(self):
+ for dut in self.duts:
+ clear_ospf_config(dut)
+ dut.config(["router ospf 10"])
+ ospf = dut.api("ospf")
+ response = ospf.delete()
+ self.assertTrue(response)
+ self.assertEqual(None, ospf.get_block("router ospf"))
+
+ def test_create_valid_id(self):
+ for dut in self.duts:
+ clear_ospf_config(dut)
+ id = randint(1, 65536)
+ ospf = dut.api("ospf")
+ response = ospf.create(id)
+ self.assertTrue(response)
+ self.assertIn("router ospf {}".format(id), dut.get_config())
+
+ def test_create_invalid_id(self):
+ for dut in self.duts:
+ clear_ospf_config(dut)
+ id = randint(70000, 100000)
+ with self.assertRaises(ValueError):
+ dut.api("ospf").create(id)
+
+ def test_configure_ospf(self):
+ for dut in self.duts:
+ clear_ospf_config(dut)
+ dut.config(["router ospf 1"])
+ ospf = dut.api("ospf")
+ response = ospf.configure_ospf("router-id 1.1.1.1")
+ self.assertTrue(response)
+ self.assertIn("router-id 1.1.1.1", ospf.get_block("router ospf 1"))
+
+ def test_set_router_id(self):
+ for dut in self.duts:
+ clear_ospf_config(dut)
+ dut.config(["router ospf 1"])
... 341 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/pyeapi.git
More information about the Python-modules-commits
mailing list