diff --git a/.gitignore b/.gitignore index ad3233a8a..8a2e1e4fa 100644 --- a/.gitignore +++ b/.gitignore @@ -63,7 +63,8 @@ test/unit/test_devices.py report.json tags +.pytest_cache/ docs/integrations/ansible/modules/napalm_*/ docs/integrations/ansible/modules/source/*.json -docs/napalm_ansible_repo/ \ No newline at end of file +docs/napalm_ansible_repo/ diff --git a/napalm/nxos/nxos.py b/napalm/nxos/nxos.py index 5eb8b7ba9..f323ceeb5 100644 --- a/napalm/nxos/nxos.py +++ b/napalm/nxos/nxos.py @@ -63,6 +63,115 @@ def commit_config(self): else: raise ReplaceConfigException('No config loaded.') + def ping(self, + destination, + source=c.PING_SOURCE, + ttl=c.PING_TTL, + timeout=c.PING_TIMEOUT, + size=c.PING_SIZE, + count=c.PING_COUNT, + vrf=c.PING_VRF): + """ + Execute ping on the device and returns a dictionary with the result. + Output dictionary has one of following keys: + * success + * error + In case of success, inner dictionary will have the followin keys: + * probes_sent (int) + * packet_loss (int) + * rtt_min (float) + * rtt_max (float) + * rtt_avg (float) + * rtt_stddev (float) + * results (list) + 'results' is a list of dictionaries with the following keys: + * ip_address (str) + * rtt (float) + """ + ping_dict = {} + + version = '' + try: + version = '6' if IPAddress(destination).version == 6 else '' + except AddrFormatError: + # Allow use of DNS names + pass + + command = 'ping{version} {destination}'.format( + version=version, + destination=destination) + command += ' timeout {}'.format(timeout) + command += ' packet-size {}'.format(size) + command += ' count {}'.format(count) + if source != '': + command += ' source {}'.format(source) + + if vrf != '': + command += ' vrf {}'.format(vrf) + output = self._send_command(command) + + if 'connect:' in output: + ping_dict['error'] = output + elif 'PING' in output: + ping_dict['success'] = { + 'probes_sent': 0, + 'packet_loss': 0, + 'rtt_min': 0.0, + 'rtt_max': 0.0, + 'rtt_avg': 0.0, + 'rtt_stddev': 0.0, + 'results': [] + } + results_array = [] + for line in output.splitlines(): + fields = line.split() + if 'icmp' in line: + if 'Unreachable' in line: + if "(" in fields[2]: + results_array.append( + { + 'ip_address': py23_compat.text_type(fields[2][1:-1]), + 'rtt': 0.0, + } + ) + else: + results_array.append({'ip_address': py23_compat.text_type(fields[1]), + 'rtt': 0.0}) + elif 'truncated' in line: + if "(" in fields[4]: + results_array.append( + { + 'ip_address': py23_compat.text_type(fields[4][1:-2]), + 'rtt': 0.0, + } + ) + else: + results_array.append( + { + 'ip_address': py23_compat.text_type(fields[3][:-1]), + 'rtt': 0.0, + } + ) + elif fields[1] == 'bytes': + if version == '6': + m = fields[5][5:] + else: + m = fields[6][5:] + results_array.append({'ip_address': py23_compat.text_type(fields[3][:-1]), + 'rtt': float(m)}) + elif 'packets transmitted' in line: + ping_dict['success']['probes_sent'] = int(fields[0]) + ping_dict['success']['packet_loss'] = int(fields[0]) - int(fields[3]) + elif 'min/avg/max' in line: + m = fields[3].split('/') + ping_dict['success'].update({ + 'rtt_min': float(m[0]), + 'rtt_avg': float(m[1]), + 'rtt_max': float(m[2]), + }) + ping_dict['success'].update({'results': results_array}) + return ping_dict + class NXOSDriver(NXOSDriverBase): def __init__(self, hostname, username, password, timeout=60, optional_args=None): @@ -110,6 +219,13 @@ def close(self): self._delete_file(self.backup_file) self.device = None + def _send_command(self, command): + """Wrapper for CLI method in NX-API. + + Allows more code sharing between NX-API and SSH. + """ + return self.cli([command]).get(command) + @staticmethod def _compute_timestamp(stupid_cisco_output): """ diff --git a/napalm/nxos_ssh/nxos_ssh.py b/napalm/nxos_ssh/nxos_ssh.py index cb7c4b565..656bb114c 100644 --- a/napalm/nxos_ssh/nxos_ssh.py +++ b/napalm/nxos_ssh/nxos_ssh.py @@ -426,6 +426,10 @@ def close(self): self.device.disconnect() self.device = None + def _send_command(self, command): + """Wrapper for Netmiko's send_command method.""" + return self.device.send_command(command) + @staticmethod def parse_uptime(uptime_str): """ diff --git a/test/nxos/mocked_data/test_ping/normal/expected_result.json b/test/nxos/mocked_data/test_ping/normal/expected_result.json new file mode 100644 index 000000000..bdc59811f --- /dev/null +++ b/test/nxos/mocked_data/test_ping/normal/expected_result.json @@ -0,0 +1,32 @@ +{ + "success": { + "rtt_stddev": 0, + "rtt_min": 18.104, + "results": [ + { + "ip_address": "8.8.8.8", + "rtt": 18.411 + }, + { + "ip_address": "8.8.8.8", + "rtt": 18.189 + }, + { + "ip_address": "8.8.8.8", + "rtt": 18.13 + }, + { + "ip_address": "8.8.8.8", + "rtt": 18.16 + }, + { + "ip_address": "8.8.8.8", + "rtt": 18.104 + } + ], + "rtt_max": 18.411, + "packet_loss": 0, + "rtt_avg": 18.198, + "probes_sent": 5 + } +} diff --git a/test/nxos/mocked_data/test_ping/normal/ping_8.8.8.8_timeout_2_packet-size_100_count_5.json b/test/nxos/mocked_data/test_ping/normal/ping_8.8.8.8_timeout_2_packet-size_100_count_5.json new file mode 100644 index 000000000..8b6ea862e --- /dev/null +++ b/test/nxos/mocked_data/test_ping/normal/ping_8.8.8.8_timeout_2_packet-size_100_count_5.json @@ -0,0 +1,10 @@ +PING 8.8.8.8 (8.8.8.8): 100 data bytes +108 bytes from 8.8.8.8: icmp_seq=0 ttl=57 time=18.411 ms +108 bytes from 8.8.8.8: icmp_seq=1 ttl=57 time=18.189 ms +108 bytes from 8.8.8.8: icmp_seq=2 ttl=57 time=18.13 ms +108 bytes from 8.8.8.8: icmp_seq=3 ttl=57 time=18.16 ms +108 bytes from 8.8.8.8: icmp_seq=4 ttl=57 time=18.104 ms + +--- 8.8.8.8 ping statistics --- +5 packets transmitted, 5 packets received, 0.00% packet loss +round-trip min/avg/max = 18.104/18.198/18.411 ms diff --git a/test/nxos/mocked_data/test_ping/ping_timed_out/expected_result.json b/test/nxos/mocked_data/test_ping/ping_timed_out/expected_result.json new file mode 100644 index 000000000..678229e0e --- /dev/null +++ b/test/nxos/mocked_data/test_ping/ping_timed_out/expected_result.json @@ -0,0 +1,11 @@ +{ + "success": { + "rtt_stddev": 0.0, + "rtt_min": 0.0, + "results": [], + "rtt_max": 0.0, + "packet_loss": 5, + "rtt_avg": 0.0, + "probes_sent": 5 + } +} diff --git a/test/nxos/mocked_data/test_ping/ping_timed_out/ping_8.8.8.8_timeout_2_packet-size_100_count_5.json b/test/nxos/mocked_data/test_ping/ping_timed_out/ping_8.8.8.8_timeout_2_packet-size_100_count_5.json new file mode 100644 index 000000000..3adc08678 --- /dev/null +++ b/test/nxos/mocked_data/test_ping/ping_timed_out/ping_8.8.8.8_timeout_2_packet-size_100_count_5.json @@ -0,0 +1,9 @@ +PING 8.8.8.8 (8.8.8.8): 100 data bytes +Request 0 timed out +Request 1 timed out +Request 2 timed out +Request 3 timed out +Request 4 timed out + +--- 8.8.8.8 ping statistics --- +5 packets transmitted, 0 packets received, 100.00% packet loss diff --git a/test/nxos/mocked_data/test_ping/ping_unreachable/expected_result.json b/test/nxos/mocked_data/test_ping/ping_unreachable/expected_result.json new file mode 100644 index 000000000..678229e0e --- /dev/null +++ b/test/nxos/mocked_data/test_ping/ping_unreachable/expected_result.json @@ -0,0 +1,11 @@ +{ + "success": { + "rtt_stddev": 0.0, + "rtt_min": 0.0, + "results": [], + "rtt_max": 0.0, + "packet_loss": 5, + "rtt_avg": 0.0, + "probes_sent": 5 + } +} diff --git a/test/nxos/mocked_data/test_ping/ping_unreachable/ping_8.8.8.8_timeout_2_packet-size_100_count_5.json b/test/nxos/mocked_data/test_ping/ping_unreachable/ping_8.8.8.8_timeout_2_packet-size_100_count_5.json new file mode 100644 index 000000000..360a8bc3f --- /dev/null +++ b/test/nxos/mocked_data/test_ping/ping_unreachable/ping_8.8.8.8_timeout_2_packet-size_100_count_5.json @@ -0,0 +1,16 @@ +5 packets transmitted, 0 packets received, 100.00% packet loss + +PING 8.8.8.8 (8.8.8.8): 100 data bytes +36 bytes from 10.150.102.3: Destination Host Unreachable +Request 0 timed out +36 bytes from 10.150.102.3: Destination Host Unreachable +Request 1 timed out +36 bytes from 10.150.102.3: Destination Host Unreachable +Request 2 timed out +36 bytes from 10.150.102.3: Destination Host Unreachable +Request 3 timed out +36 bytes from 10.150.102.3: Destination Host Unreachable +Request 4 timed out + +--- 8.8.8.8 ping statistics --- +5 packets transmitted, 0 packets received, 100.00% packet loss diff --git a/test/nxos_ssh/mocked_data/test_ping/normal/expected_result.json b/test/nxos_ssh/mocked_data/test_ping/normal/expected_result.json new file mode 100644 index 000000000..bdc59811f --- /dev/null +++ b/test/nxos_ssh/mocked_data/test_ping/normal/expected_result.json @@ -0,0 +1,32 @@ +{ + "success": { + "rtt_stddev": 0, + "rtt_min": 18.104, + "results": [ + { + "ip_address": "8.8.8.8", + "rtt": 18.411 + }, + { + "ip_address": "8.8.8.8", + "rtt": 18.189 + }, + { + "ip_address": "8.8.8.8", + "rtt": 18.13 + }, + { + "ip_address": "8.8.8.8", + "rtt": 18.16 + }, + { + "ip_address": "8.8.8.8", + "rtt": 18.104 + } + ], + "rtt_max": 18.411, + "packet_loss": 0, + "rtt_avg": 18.198, + "probes_sent": 5 + } +} diff --git a/test/nxos_ssh/mocked_data/test_ping/normal/ping_8_8_8_8_timeout_2_packet_size_100_count_5.txt b/test/nxos_ssh/mocked_data/test_ping/normal/ping_8_8_8_8_timeout_2_packet_size_100_count_5.txt new file mode 100644 index 000000000..8b6ea862e --- /dev/null +++ b/test/nxos_ssh/mocked_data/test_ping/normal/ping_8_8_8_8_timeout_2_packet_size_100_count_5.txt @@ -0,0 +1,10 @@ +PING 8.8.8.8 (8.8.8.8): 100 data bytes +108 bytes from 8.8.8.8: icmp_seq=0 ttl=57 time=18.411 ms +108 bytes from 8.8.8.8: icmp_seq=1 ttl=57 time=18.189 ms +108 bytes from 8.8.8.8: icmp_seq=2 ttl=57 time=18.13 ms +108 bytes from 8.8.8.8: icmp_seq=3 ttl=57 time=18.16 ms +108 bytes from 8.8.8.8: icmp_seq=4 ttl=57 time=18.104 ms + +--- 8.8.8.8 ping statistics --- +5 packets transmitted, 5 packets received, 0.00% packet loss +round-trip min/avg/max = 18.104/18.198/18.411 ms