diff --git a/README.md b/README.md index 0da850d82..8a50dd615 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![PyPI](https://img.shields.io/pypi/v/napalm.svg)](https://pypi.python.org/pypi/napalm) [![PyPI versions](https://img.shields.io/pypi/pyversions/napalm.svg)](https://pypi.python.org/pypi/napalm) -[![Coverage Status](https://coveralls.io/repos/github/napalm-automation/napalm/badge.svg)](https://coveralls.io/github/napalm-automation/napalm) +[![Actions Build](https://github.com/napalm-automation/napalm/actions/workflows/commit.yaml/badge.svg?branch=develop)](https://github.com/napalm-automation/napalm/actions/workflows/commit.yaml) [![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) diff --git a/napalm/base/base.py b/napalm/base/base.py index 6cf0e2c77..4e245e1ca 100644 --- a/napalm/base/base.py +++ b/napalm/base/base.py @@ -1273,6 +1273,7 @@ def ping( size=c.PING_SIZE, count=c.PING_COUNT, vrf=c.PING_VRF, + source_interface=c.PING_SOURCE_INTERFACE, ): """ Executes ping on the device and returns a dictionary with the result @@ -1290,6 +1291,8 @@ def ping( :type count: optional :param vrf: Use a specific VRF to execute the ping :type vrf: optional + :param source_interface: Use an IP from a source interface as source address of echo request + :type source_interface: optional Output dictionary has one of following keys: diff --git a/napalm/base/constants.py b/napalm/base/constants.py index 23e7e8085..75f492b1a 100644 --- a/napalm/base/constants.py +++ b/napalm/base/constants.py @@ -57,6 +57,7 @@ PING_SIZE = 100 PING_COUNT = 5 PING_VRF = "" +PING_SOURCE_INTERFACE = "" NETMIKO_MAP = { "ios": "cisco_ios", diff --git a/napalm/eos/eos.py b/napalm/eos/eos.py index 8d11d71f7..083f00eb5 100644 --- a/napalm/eos/eos.py +++ b/napalm/eos/eos.py @@ -2006,6 +2006,7 @@ def ping( size=c.PING_SIZE, count=c.PING_COUNT, vrf=c.PING_VRF, + source_interface=c.PING_SOURCE_INTERFACE, ): """ Execute ping on the device and returns a dictionary with the result. @@ -2036,6 +2037,8 @@ def ping( command += " repeat {}".format(count) if source != "": command += " source {}".format(source) + if source_interface != "": + command += " interface {}".format(source_interface) commands.append(command) output = self.device.run_commands(commands, encoding="text")[-1]["output"] diff --git a/napalm/ios/ios.py b/napalm/ios/ios.py index f3dd84107..a09c81292 100644 --- a/napalm/ios/ios.py +++ b/napalm/ios/ios.py @@ -3178,6 +3178,7 @@ def ping( size=C.PING_SIZE, count=C.PING_COUNT, vrf=C.PING_VRF, + source_interface=C.PING_SOURCE_INTERFACE, ): """ Execute ping on the device and returns a dictionary with the result. @@ -3208,6 +3209,8 @@ def ping( command += " repeat {}".format(count) if source != "": command += " source {}".format(source) + elif source_interface != "": + command += " source {}".format(source_interface) output = self._send_command(command) if "%" in output: @@ -3442,7 +3445,8 @@ def get_network_instances(self, name=""): # remove interfaces in the VRF from the default VRF for item in interfaces: - del instances["default"]["interfaces"]["interface"][item] + if item in instances["default"]["interfaces"]["interface"]: + del instances["default"]["interfaces"]["interface"][item] instances[vrf_name] = { "name": vrf_name, diff --git a/napalm/iosxr/iosxr.py b/napalm/iosxr/iosxr.py index 190c32d1b..4e5043597 100644 --- a/napalm/iosxr/iosxr.py +++ b/napalm/iosxr/iosxr.py @@ -1283,7 +1283,7 @@ def get_bgp_neighbors_detail(self, neighbor_address=""): output_messages = napalm.base.helpers.convert( int, napalm.base.helpers.find_txt(neighbor, "MessagesSent"), 0 ) - connection_down_count = napalm.base.helpers.convert( + flap_count = napalm.base.helpers.convert( int, napalm.base.helpers.find_txt(neighbor, "ConnectionDownCount"), 0, @@ -1361,9 +1361,6 @@ def get_bgp_neighbors_detail(self, neighbor_address=""): napalm.base.helpers.find_txt(neighbor, "ConfiguredKeepalive"), 0, ) - flap_count = int(connection_down_count / 2) - if up: - flap_count -= 1 if remote_as not in bgp_neighbors_detail[vrf_name].keys(): bgp_neighbors_detail[vrf_name][remote_as] = [] bgp_neighbors_detail[vrf_name][remote_as].append( diff --git a/napalm/iosxr_netconf/iosxr_netconf.py b/napalm/iosxr_netconf/iosxr_netconf.py index bcead15c9..a2216d448 100644 --- a/napalm/iosxr_netconf/iosxr_netconf.py +++ b/napalm/iosxr_netconf/iosxr_netconf.py @@ -1855,7 +1855,7 @@ def get_vrf_neighbors_detail( ), 0, ) - connection_down_count = napalm.base.helpers.convert( + flap_count = napalm.base.helpers.convert( int, self._find_txt( neighbor, @@ -1995,10 +1995,6 @@ def get_vrf_neighbors_detail( ), 0, ) - flap_count = int(connection_down_count / 2) - if up: - flap_count -= 1 - if remote_as not in bgp_vrf_neighbors_detail[vrf_name].keys(): bgp_vrf_neighbors_detail[vrf_name][remote_as] = [] bgp_vrf_neighbors_detail[vrf_name][remote_as].append( diff --git a/napalm/junos/junos.py b/napalm/junos/junos.py index 5867fd90d..7ca5447fc 100644 --- a/napalm/junos/junos.py +++ b/napalm/junos/junos.py @@ -1115,7 +1115,11 @@ def _process_pipe(cmd, txt): ) ) raw_txt = self.device.cli(safe_command, warning=False) - cli_output[str(command)] = str(_process_pipe(command, raw_txt)) + if isinstance(raw_txt, etree._Element): + raw_txt = etree.tostring(raw_txt.get_parent()).decode() + cli_output[str(command)] = raw_txt + else: + cli_output[str(command)] = str(_process_pipe(command, raw_txt)) return cli_output def get_bgp_config(self, group="", neighbor=""): @@ -2061,6 +2065,7 @@ def ping( size=C.PING_SIZE, count=C.PING_COUNT, vrf=C.PING_VRF, + source_interface=C.PING_SOURCE_INTERFACE, ): ping_dict = {} @@ -2071,6 +2076,7 @@ def ping( size_str = "" count_str = "" vrf_str = "" + source_interface_str = "" if source: source_str = " source {source}".format(source=source) @@ -2084,17 +2090,22 @@ def ping( count_str = " count {count}".format(count=count) if vrf: vrf_str = " routing-instance {vrf}".format(vrf=vrf) + if source_interface: + source_interface_str = " interface {source_interface}".format( + source_interface=source_interface + ) ping_command = ( - "ping {destination}{source}{ttl}{timeout}{size}{count}{vrf}".format( - destination=destination, - source=source_str, - ttl=maxttl_str, - timeout=timeout_str, - size=size_str, - count=count_str, - vrf=vrf_str, - ) + "ping {destination}{source}{ttl}{timeout}{size}{count}{vrf}{source_interface}" + ).format( + destination=destination, + source=source_str, + ttl=maxttl_str, + timeout=timeout_str, + size=size_str, + count=count_str, + vrf=vrf_str, + source_interface=source_interface_str, ) ping_rpc = E("command", ping_command) diff --git a/napalm/nxos/nxos.py b/napalm/nxos/nxos.py index 2a55e7398..67fdc0595 100644 --- a/napalm/nxos/nxos.py +++ b/napalm/nxos/nxos.py @@ -276,6 +276,7 @@ def ping( size=c.PING_SIZE, count=c.PING_COUNT, vrf=c.PING_VRF, + source_interface=c.PING_SOURCE_INTERFACE, ): """ Execute ping on the device and returns a dictionary with the result. @@ -311,6 +312,8 @@ def ping( command += " count {}".format(count) if source != "": command += " source {}".format(source) + elif source_interface != "": + command += " source {}".format(source_interface) if vrf != "": command += " vrf {}".format(vrf) diff --git a/requirements-dev.txt b/requirements-dev.txt index b6bbb03af..106e77768 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,9 +1,9 @@ -black==21.5b0 -coveralls==3.0.1 +black==21.5b1 +coveralls==3.1.0 ddt==1.4.2 flake8-import-order==0.18.1 pytest==5.4.3 -pytest-cov==2.11.1 +pytest-cov==2.12.0 pytest-json==0.4.0 pytest-pythonpath==0.7.3 pylama==7.7.1 diff --git a/setup.py b/setup.py index d42506131..b6857fa2e 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name="napalm", - version="3.3.0", + version="3.3.1", packages=find_packages(exclude=("test*",)), test_suite="test_base", author="David Barroso, Kirk Byers, Mircea Ulinic", diff --git a/test/iosxr/mocked_data/test_get_bgp_neighbors_detail/normal/expected_result.json b/test/iosxr/mocked_data/test_get_bgp_neighbors_detail/normal/expected_result.json index 7c5db27f5..878e3114e 100644 --- a/test/iosxr/mocked_data/test_get_bgp_neighbors_detail/normal/expected_result.json +++ b/test/iosxr/mocked_data/test_get_bgp_neighbors_detail/normal/expected_result.json @@ -90,7 +90,7 @@ "active_prefix_count": 273802, "configured_holdtime": 180, "routing_table": "default", - "flap_count": 1, + "flap_count": 5, "suppressed_prefix_count": 9, "local_address": "20.20.20.21", "remote_port": 179, @@ -127,7 +127,7 @@ "active_prefix_count": 0, "configured_holdtime": 180, "routing_table": "default", - "flap_count": 3, + "flap_count": 9, "suppressed_prefix_count": 0, "local_address": "8.8.8.8", "remote_port": 63014, diff --git a/test/iosxr_netconf/mocked_data/test_get_bgp_neighbors_detail/normal/expected_result.json b/test/iosxr_netconf/mocked_data/test_get_bgp_neighbors_detail/normal/expected_result.json index 807ead743..3fcff00b9 100644 --- a/test/iosxr_netconf/mocked_data/test_get_bgp_neighbors_detail/normal/expected_result.json +++ b/test/iosxr_netconf/mocked_data/test_get_bgp_neighbors_detail/normal/expected_result.json @@ -9,7 +9,7 @@ "configured_keepalive": 60, "connection_state": "Established", "export_policy": "EBGP-OUT-POLICY", - "flap_count": -1, + "flap_count": 0, "holdtime": 180, "import_policy": "EBGP-IN-POLICY", "input_messages": 0, @@ -83,7 +83,7 @@ "configured_keepalive": 60, "connection_state": "Established", "export_policy": "IBGPv6-OUT-POLICY", - "flap_count": -1, + "flap_count": 0, "holdtime": 180, "import_policy": "IBGPv6-IN-POLICY", "input_messages": 0, @@ -161,7 +161,7 @@ "configured_keepalive": 60, "connection_state": "Established", "export_policy": "IBGP-OUT-POLICY", - "flap_count": 0, + "flap_count": 2, "holdtime": 180, "import_policy": "", "input_messages": 0, @@ -198,7 +198,7 @@ "configured_keepalive": 60, "connection_state": "Established", "export_policy": "IBGP-OUT-POLICY", - "flap_count": -1, + "flap_count": 0, "holdtime": 180, "import_policy": "", "input_messages": 0, diff --git a/test/junos/conftest.py b/test/junos/conftest.py index cca77f03e..a0a057328 100644 --- a/test/junos/conftest.py +++ b/test/junos/conftest.py @@ -10,6 +10,8 @@ from napalm.junos import junos +from ncclient.devices.junos import JunosDeviceHandler + @pytest.fixture(scope="class") def set_device_parameters(request): @@ -82,6 +84,15 @@ def __init__(self): # disable it to use the DOM parser which was used prior. self._use_filter = False + @property + def transform(self): + # Junos device transform, inherited from the ncclient class + return self._conn._device_handler.transform_reply + + @transform.setter + def transform(self, func): + self._conn._device_handler.transform_reply = func + @property def facts(self): # we want to reinitialize it every time to avoid side effects @@ -183,6 +194,7 @@ class FakeConnection: def __init__(self, rpc): self.rpc = FakeConnectionRPCObject(rpc) self._session = FakeSession() + self._device_handler = JunosDeviceHandler({}) class FakeSession: