Hello community, here is the log from the commit of package python-netmiko for openSUSE:Factory checked in at 2020-03-27 21:57:59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-netmiko (Old) and /work/SRC/openSUSE:Factory/.python-netmiko.new.3160 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-netmiko" Fri Mar 27 21:57:59 2020 rev:8 rq:788896 version:3.1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-netmiko/python-netmiko.changes 2020-01-16 18:22:34.105019870 +0100 +++ /work/SRC/openSUSE:Factory/.python-netmiko.new.3160/python-netmiko.changes 2020-03-27 21:58:41.882841635 +0100 @@ -1,0 +2,13 @@ +Mon Mar 23 19:35:36 UTC 2020 - Martin Hauke <[email protected]> + +- Update to version 3.1.0: + New Platforms / Transports + * D-Link DGS/DES + * WatchGuard Firebox + * Sophos SG Firewalls + * Huawei Telnet Support + Bug Fixes/Enhancements + * Add cmd_verify argument to send_command + * Add global_cmd_verify argument to ConnectHandler + +------------------------------------------------------------------- Old: ---- netmiko-3.0.0.tar.gz New: ---- netmiko-3.1.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-netmiko.spec ++++++ --- /var/tmp/diff_new_pack.ewDe2C/_old 2020-03-27 21:58:44.198842982 +0100 +++ /var/tmp/diff_new_pack.ewDe2C/_new 2020-03-27 21:58:44.214842992 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-netmiko # -# Copyright (c) 2020 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-netmiko -Version: 3.0.0 +Version: 3.1.0 Release: 0 Summary: Multi-vendor library to simplify Paramiko SSH connections to network devices License: MIT ++++++ netmiko-3.0.0.tar.gz -> netmiko-3.1.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/PKG-INFO new/netmiko-3.1.0/PKG-INFO --- old/netmiko-3.0.0/PKG-INFO 2020-01-16 05:32:35.000000000 +0100 +++ new/netmiko-3.1.0/PKG-INFO 2020-03-23 17:49:02.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: netmiko -Version: 3.0.0 +Version: 3.1.0 Summary: Multi-vendor library to simplify Paramiko SSH connections to network devices Home-page: https://github.com/ktbyers/netmiko Author: Kirk Byers @@ -203,5 +203,6 @@ Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 Description-Content-Type: text/markdown Provides-Extra: test diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/PLATFORMS.md new/netmiko-3.1.0/PLATFORMS.md --- old/netmiko-3.0.0/PLATFORMS.md 2020-01-06 05:30:41.000000000 +0100 +++ new/netmiko-3.1.0/PLATFORMS.md 2020-03-23 17:47:24.000000000 +0100 @@ -35,6 +35,7 @@ - MikroTik RouterOS - MikroTik SwitchOS - NetApp cDOT +- Nokia/Alcatel SR OS - OneAccess - Palo Alto PAN-OS - Pluribus @@ -69,5 +70,6 @@ - Nokia/Alcatel SR-OS - QuantaMesh - Rad ETX +- Sophos SFOS - Versa Networks FlexVNF - +- Watchguard Firebox diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/__init__.py new/netmiko-3.1.0/netmiko/__init__.py --- old/netmiko-3.0.0/netmiko/__init__.py 2019-12-11 19:37:51.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/__init__.py 2020-03-23 17:27:20.000000000 +0100 @@ -23,7 +23,7 @@ # Alternate naming Netmiko = ConnectHandler -__version__ = "3.0.0" +__version__ = "3.1.0" __all__ = ( "ConnectHandler", "ssh_dispatcher", @@ -34,8 +34,6 @@ "NetmikoAuthenticationException", "NetMikoTimeoutException", "NetMikoAuthenticationException", - "NetmikoTimeoutError", - "NetmikoAuthError", "InLineTransfer", "redispatch", "SSHDetect", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/base_connection.py new/netmiko-3.1.0/netmiko/base_connection.py --- old/netmiko-3.0.0/netmiko/base_connection.py 2020-01-15 22:50:05.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/base_connection.py 2020-03-20 16:58:01.000000000 +0100 @@ -29,6 +29,7 @@ check_serial_port, get_structured_data, get_structured_data_genie, + select_cmd_verify, ) @@ -50,6 +51,7 @@ device_type="", verbose=False, global_delay_factor=1, + global_cmd_verify=None, use_keys=False, key_file=None, pkey=None, @@ -196,6 +198,12 @@ :param sock: An open socket or socket-like object (such as a `.Channel`) to use for communication to the target host (default: None). :type sock: socket + + :param global_cmd_verify: Control whether command echo verification is enabled or disabled + (default: None). Global attribute takes precedence over function `cmd_verify` + argument. Value of `None` indicates to use function `cmd_verify` argument. + :type global_cmd_verify: bool|None + """ self.remote_conn = None @@ -279,6 +287,7 @@ self.fast_cli = fast_cli self.global_delay_factor = global_delay_factor + self.global_cmd_verify = global_cmd_verify if self.fast_cli and self.global_delay_factor == 1: self.global_delay_factor = 0.1 @@ -1130,6 +1139,7 @@ sleep_time *= 2 sleep_time = 3 if sleep_time >= 3 else sleep_time + @select_cmd_verify def send_command_timing( self, command_string, @@ -1141,7 +1151,8 @@ use_textfsm=False, textfsm_template=None, use_genie=False, - cmd_echo=False, + cmd_verify=False, + cmd_echo=None, ): """Execute command_string on the SSH channel using a delay-based mechanism. Generally used for show commands. @@ -1175,9 +1186,16 @@ :param use_genie: Process command output through PyATS/Genie parser (default: False). :type use_genie: bool - :param cmd_echo: Verify command echo before proceeding (default: False). + :param cmd_verify: Verify command echo before proceeding (default: False). + :type cmd_verify: bool + + :param cmd_echo: Deprecated (use cmd_verify instead) :type cmd_echo: bool """ + # For compatibility remove cmd_echo in Netmiko 4.x.x + if cmd_echo is not None: + cmd_verify = cmd_echo + output = "" delay_factor = self.select_delay_factor(delay_factor) self.clear_buffer() @@ -1188,7 +1206,7 @@ cmd = command_string.strip() # if cmd is just an "enter" skip this section - if cmd and cmd_echo: + if cmd and cmd_verify: # Make sure you read until you detect the command echo (avoid getting out of sync) new_data = self.read_until_pattern(pattern=re.escape(cmd)) new_data = self.normalize_linefeeds(new_data) @@ -1277,6 +1295,7 @@ except IndexError: return (data, False) + @select_cmd_verify def send_command( self, command_string, @@ -1290,6 +1309,7 @@ use_textfsm=False, textfsm_template=None, use_genie=False, + cmd_verify=True, ): """Execute command_string on the SSH channel using a pattern-based mechanism. Generally used for show commands. By default this method will keep waiting to receive data until the @@ -1327,6 +1347,9 @@ :param use_genie: Process command output through PyATS/Genie parser (default: False). :type normalize: bool + + :param cmd_verify: Verify command echo before proceeding (default: True). + :type cmd_verify: bool """ # Time to delay in each read loop loop_delay = 0.2 @@ -1360,8 +1383,8 @@ new_data = "" cmd = command_string.strip() - # if cmd is just and "enter" skip this section - if cmd: + # if cmd is just an "enter" skip this section + if cmd and cmd_verify: # Make sure you read until you detect the command echo (avoid getting out of sync) new_data = self.read_until_pattern(pattern=re.escape(cmd)) new_data = self.normalize_linefeeds(new_data) @@ -1699,9 +1722,10 @@ raise ValueError("Invalid argument passed into send_config_set") # Send config commands + output = "" if enter_config_mode: cfg_mode_args = (config_mode_command,) if config_mode_command else tuple() - output = self.config_mode(*cfg_mode_args) + output += self.config_mode(*cfg_mode_args) if self.fast_cli: for cmd in config_commands: @@ -1765,6 +1789,7 @@ ESC[00;32m Color Green (30 to 37 are different colors) more general pattern is ESC[\d\d;\d\dm and ESC[\d\d;\d\d;\d\dm ESC[6n Get cursor position + ESC[1D Move cursor position leftward by x characters (1 in this case) HP ProCurve and Cisco SG300 require this (possible others). @@ -1796,6 +1821,7 @@ code_erase_display = chr(27) + r"\[J" code_attrs_off = chr(27) + r"\[0m" code_reverse = chr(27) + r"\[7m" + code_cursor_left = chr(27) + r"\[\d+D" code_set = [ code_position_cursor, @@ -1819,6 +1845,7 @@ code_erase_display, code_attrs_off, code_reverse, + code_cursor_left, ] output = string_buffer @@ -1834,8 +1861,8 @@ return output - def cleanup(self): - """Any needed cleanup before closing connection.""" + def cleanup(self, command=""): + """Logout of the session on the network device plus any additional cleanup.""" pass def paramiko_cleanup(self): @@ -1844,7 +1871,7 @@ del self.remote_conn_pre def disconnect(self): - """Try to gracefully close the SSH connection.""" + """Try to gracefully close the session.""" try: self.cleanup() if self.protocol == "ssh": diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/cisco/cisco_nxos_ssh.py new/netmiko-3.1.0/netmiko/cisco/cisco_nxos_ssh.py --- old/netmiko-3.0.0/netmiko/cisco/cisco_nxos_ssh.py 2019-12-11 19:37:51.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/cisco/cisco_nxos_ssh.py 2020-03-20 16:58:01.000000000 +0100 @@ -12,6 +12,7 @@ self.ansi_escape_codes = True self.set_base_prompt() self.disable_paging() + self.set_terminal_width(command="terminal width 511") # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/cisco/cisco_wlc_ssh.py new/netmiko-3.1.0/netmiko/cisco/cisco_wlc_ssh.py --- old/netmiko-3.0.0/netmiko/cisco/cisco_wlc_ssh.py 2019-12-11 19:37:51.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/cisco/cisco_wlc_ssh.py 2020-03-20 16:58:01.000000000 +0100 @@ -1,6 +1,7 @@ """Netmiko Cisco WLC support.""" import time import re +import socket from netmiko.ssh_exception import NetmikoAuthenticationException from netmiko.base_connection import BaseConnection @@ -109,10 +110,42 @@ time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() - def cleanup(self): - """Reset WLC back to normal paging.""" + def cleanup(self, command="logout"): + """Reset WLC back to normal paging and gracefully close session.""" self.send_command_timing("config paging enable") + # Exit configuration mode + try: + # The pattern="" forces use of send_command_timing + if self.check_config_mode(pattern=""): + self.exit_config_mode() + except Exception: + pass + + # End SSH/telnet session + self.write_channel(command + self.RETURN) + count = 0 + output = "" + while count <= 5: + time.sleep(0.5) + + # The connection might be dead at this point. + try: + output += self.read_channel() + except socket.error: + break + + # Don't automatically save the config (user's responsibility) + if "Would you like to save them now" in output: + self._session_log_fin = True + self.write_channel("n" + self.RETURN) + + try: + self.write_channel(self.RETURN) + except socket.error: + break + count += 1 + def check_config_mode(self, check_string="config", pattern=""): """Checks if the device is in configuration mode or not.""" if not pattern: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/cisco_base_connection.py new/netmiko-3.1.0/netmiko/cisco_base_connection.py --- old/netmiko-3.0.0/netmiko/cisco_base_connection.py 2019-12-11 19:37:51.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/cisco_base_connection.py 2020-03-20 16:58:01.000000000 +0100 @@ -157,15 +157,17 @@ msg = f"Login failed: {self.host}" raise NetmikoAuthenticationException(msg) - def cleanup(self): + def cleanup(self, command="exit"): """Gracefully exit the SSH session.""" try: - self.exit_config_mode() + # The pattern="" forces use of send_command_timing + if self.check_config_mode(pattern=""): + self.exit_config_mode() except Exception: pass - # Always try to send final 'exit' regardless of whether exit_config_mode works or not. + # Always try to send final 'exit' (command) self._session_log_fin = True - self.write_channel("exit" + self.RETURN) + self.write_channel(command + self.RETURN) def _autodetect_fs(self, cmd="dir", pattern=r"Directory of (.*)/"): """Autodetect the file system on the remote device. Used by SCP operations.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/dlink/__init__.py new/netmiko-3.1.0/netmiko/dlink/__init__.py --- old/netmiko-3.0.0/netmiko/dlink/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/dlink/__init__.py 2020-03-21 00:33:27.000000000 +0100 @@ -0,0 +1,3 @@ +from netmiko.dlink.dlink_ds import DlinkDSTelnet, DlinkDSSSH + +__all__ = ["DlinkDSTelnet", "DlinkDSSSH"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/dlink/dlink_ds.py new/netmiko-3.1.0/netmiko/dlink/dlink_ds.py --- old/netmiko-3.0.0/netmiko/dlink/dlink_ds.py 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/dlink/dlink_ds.py 2020-03-21 00:33:27.000000000 +0100 @@ -0,0 +1,65 @@ +from netmiko.cisco_base_connection import CiscoSSHConnection +import time + + +class DlinkDSBase(CiscoSSHConnection): + """Supports D-Link DGS/DES device series (there are some DGS/DES devices that are web-only)""" + + def session_preparation(self): + """Prepare the session after the connection has been established.""" + self.ansi_escape_codes = True + self._test_channel_read() + self.set_base_prompt() + self.disable_paging() + # Clear the read buffer + time.sleep(0.3 * self.global_delay_factor) + self.clear_buffer() + + def disable_paging(self, command="disable clipaging", delay_factor=1): + return super().disable_paging(command=command, delay_factor=delay_factor) + + def enable(self, *args, **kwargs): + """No implemented enable mode on D-Link yet""" + return "" + + def check_enable_mode(self, *args, **kwargs): + """No implemented enable mode on D-Link yet""" + return True + + def exit_enable_mode(self, *args, **kwargs): + """No implemented enable mode on D-Link yet""" + return "" + + def check_config_mode(self, *args, **kwargs): + """No config mode on D-Link""" + return False + + def config_mode(self, *args, **kwargs): + """No config mode on D-Link""" + return "" + + def exit_config_mode(self, *args, **kwargs): + """No config mode on D-Link""" + return "" + + def save_config(self, cmd="save", confirm=False, confirm_response=""): + """Saves configuration.""" + return super().save_config( + cmd=cmd, confirm=confirm, confirm_response=confirm_response + ) + + def cleanup(self): + """Return paging before disconnect""" + self.send_command_timing("enable clipaging") + return super().cleanup() + + +class DlinkDSSSH(DlinkDSBase): + pass + + +class DlinkDSTelnet(DlinkDSBase): + def __init__(self, *args, **kwargs): + default_enter = kwargs.get("default_enter") + kwargs["default_enter"] = "\r\n" if default_enter is None else default_enter + super().__init__(*args, **kwargs) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/fortinet/fortinet_ssh.py new/netmiko-3.1.0/netmiko/fortinet/fortinet_ssh.py --- old/netmiko-3.0.0/netmiko/fortinet/fortinet_ssh.py 2020-01-06 05:31:27.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/fortinet/fortinet_ssh.py 2020-03-20 16:58:01.000000000 +0100 @@ -85,7 +85,7 @@ if result_mode in ["more", "standard"]: self._output_mode = result_mode - def cleanup(self): + def cleanup(self, command="exit"): """Re-enable paging globally.""" if self.allow_disable_global: # Return paging state @@ -96,6 +96,7 @@ # Should test output is valid for command in enable_paging_commands: self.send_command_timing(command) + return super().cleanup(command=command) def config_mode(self, config_command=""): """No config mode for Fortinet devices.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/hp/hp_procurve.py new/netmiko-3.1.0/netmiko/hp/hp_procurve.py --- old/netmiko-3.0.0/netmiko/hp/hp_procurve.py 2019-12-12 04:00:03.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/hp/hp_procurve.py 2020-03-20 16:58:01.000000000 +0100 @@ -75,20 +75,38 @@ raise ValueError(msg) return output - def cleanup(self): + def cleanup(self, command="logout"): """Gracefully exit the SSH session.""" - self.exit_config_mode() - self.write_channel("logout" + self.RETURN) + + # Exit configuration mode. + try: + # The pattern="" forces use of send_command_timing + if self.check_config_mode(pattern=""): + self.exit_config_mode() + except Exception: + pass + + # Terminate SSH/telnet session + self.write_channel(command + self.RETURN) count = 0 + output = "" while count <= 5: time.sleep(0.5) - output = self.read_channel() - if "Do you want to log out" in output: - self._session_log_fin = True + + # The connection might be dead here. + try: + new_output = self.read_channel() + output += new_output + except socket.error: + break + + if "Do you want to log out" in new_output: self.write_channel("y" + self.RETURN) + time.sleep(0.5) + output += self.read_channel() + # Don't automatically save the config (user's responsibility) - elif "Do you want to save the current" in output: - self._session_log_fin = True + if "Do you want to save the current" in output: self.write_channel("n" + self.RETURN) try: @@ -97,6 +115,9 @@ break count += 1 + # Set outside of loop + self._session_log_fin = True + def save_config(self, cmd="write memory", confirm=False, confirm_response=""): """Save Config.""" return super().save_config( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/huawei/huawei.py new/netmiko-3.1.0/netmiko/huawei/huawei.py --- old/netmiko-3.0.0/netmiko/huawei/huawei.py 2020-01-15 22:50:05.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/huawei/huawei.py 2020-03-20 16:58:01.000000000 +0100 @@ -8,6 +8,7 @@ class HuaweiBase(CiscoBaseConnection): def session_preparation(self): """Prepare the session after the connection has been established.""" + self.ansi_escape_codes = True self._test_channel_read() self.set_base_prompt() self.disable_paging(command="screen-length 0 temporary") @@ -15,6 +16,23 @@ time.sleep(0.3 * self.global_delay_factor) self.clear_buffer() + def strip_ansi_escape_codes(self, string_buffer): + """ + Huawei does a strange thing where they add a space and then add ESC[1D + to move the cursor to the left one. + + The extra space is problematic. + """ + code_cursor_left = chr(27) + r"\[\d+D" + output = string_buffer + pattern = rf" {code_cursor_left}" + output = re.sub(pattern, "", output) + + log.debug("Stripping ANSI escape codes") + log.debug(f"new_output = {output}") + log.debug(f"repr = {repr(output)}") + return super().strip_ansi_escape_codes(output) + def config_mode(self, config_command="system-view"): """Enter configuration mode.""" return super().config_mode(config_command=config_command) @@ -47,12 +65,12 @@ Used as delimiter for stripping of trailing prompt in output. - Should be set to something that is general and applies in multiple contexts. For Comware - this will be the router prompt with < > or [ ] stripped off. + Should be set to something that is general and applies in multiple contexts. + For Huawei this will be the router prompt with < > or [ ] stripped off. This will be set on logging in, but not when entering system-view """ - log.debug("In set_base_prompt") + # log.debug("In set_base_prompt") delay_factor = self.select_delay_factor(delay_factor) self.clear_buffer() self.write_channel(self.RETURN) @@ -126,26 +144,21 @@ i = 1 while i <= max_loops: try: - output = self.read_channel() - return_msg += output - # Search for username pattern / send username - if re.search(username_pattern, output, flags=re.I): - self.write_channel(self.username + self.TELNET_RETURN) - time.sleep(1 * delay_factor) - output = self.read_channel() - return_msg += output + output = self.read_until_pattern( + pattern=username_pattern, re_flags=re.I + ) + return_msg += output + self.write_channel(self.username + self.TELNET_RETURN) # Search for password pattern / send password - if re.search(pwd_pattern, output, flags=re.I): - self.write_channel(self.password + self.TELNET_RETURN) - time.sleep(0.5 * delay_factor) - output = self.read_channel() - return_msg += output - if re.search( - pri_prompt_terminator, output, flags=re.M - ) or re.search(alt_prompt_terminator, output, flags=re.M): - return return_msg + output = self.read_until_pattern(pattern=pwd_pattern, re_flags=re.I) + return_msg += output + self.write_channel(self.password + self.TELNET_RETURN) + + # Waiting for combined output + output = self.read_until_pattern(pattern=combined_pattern) + return_msg += output # Search for password change prompt, send "N" if re.search(password_change_prompt, output): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/linux/linux_ssh.py new/netmiko-3.1.0/netmiko/linux/linux_ssh.py --- old/netmiko-3.0.0/netmiko/linux/linux_ssh.py 2019-12-11 19:37:51.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/linux/linux_ssh.py 2020-03-20 16:58:01.000000000 +0100 @@ -51,7 +51,7 @@ config_commands=config_commands, exit_config_mode=exit_config_mode, **kwargs ) - def check_config_mode(self, check_string=LINUX_PROMPT_ROOT): + def check_config_mode(self, check_string=LINUX_PROMPT_ROOT, pattern=""): """Verify root""" return self.check_enable_mode(check_string=check_string) @@ -102,10 +102,9 @@ raise ValueError(msg) return output - def cleanup(self): + def cleanup(self, command="exit"): """Try to Gracefully exit the SSH session.""" - self._session_log_fin = True - self.write_channel("exit" + self.RETURN) + return super().cleanup(command=command) def save_config(self, *args, **kwargs): """Not Implemented""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/nokia/nokia_sros_ssh.py new/netmiko-3.1.0/netmiko/nokia/nokia_sros_ssh.py --- old/netmiko-3.0.0/netmiko/nokia/nokia_sros_ssh.py 2019-12-11 19:37:57.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/nokia/nokia_sros_ssh.py 2020-03-20 16:58:01.000000000 +0100 @@ -156,3 +156,15 @@ return re.sub(strips, "", output) else: return output + + def cleanup(self, command="logout"): + """Gracefully exit the SSH session.""" + try: + # The pattern="" forces use of send_command_timing + if self.check_config_mode(pattern=""): + self.exit_config_mode() + except Exception: + pass + # Always try to send final 'logout'. + self._session_log_fin = True + self.write_channel(command + self.RETURN) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/paloalto/paloalto_panos.py new/netmiko-3.1.0/netmiko/paloalto/paloalto_panos.py --- old/netmiko-3.0.0/netmiko/paloalto/paloalto_panos.py 2019-12-11 19:37:51.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/paloalto/paloalto_panos.py 2020-03-20 16:58:01.000000000 +0100 @@ -159,15 +159,17 @@ kwargs["delay_factor"] = kwargs.get("delay_factor", 2.5) return super().send_command(*args, **kwargs) - def cleanup(self): + def cleanup(self, command="exit"): """Gracefully exit the SSH session.""" try: - self.exit_config_mode() + # The pattern="" forces use of send_command_timing + if self.check_config_mode(pattern=""): + self.exit_config_mode() except Exception: - # Always try to send 'exit' regardless of whether exit_config_mode works or not. pass + # Always try to send final 'exit' (command) self._session_log_fin = True - self.write_channel("exit" + self.RETURN) + self.write_channel(command + self.RETURN) class PaloAltoPanosSSH(PaloAltoPanosBase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/sophos/__init__.py new/netmiko-3.1.0/netmiko/sophos/__init__.py --- old/netmiko-3.0.0/netmiko/sophos/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/sophos/__init__.py 2020-03-22 01:38:04.000000000 +0100 @@ -0,0 +1,3 @@ +from netmiko.sophos.sophos_sfos_ssh import SophosSfosSSH + +__all__ = ["SophosSfosSSH"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/sophos/sophos_sfos_ssh.py new/netmiko-3.1.0/netmiko/sophos/sophos_sfos_ssh.py --- old/netmiko-3.0.0/netmiko/sophos/sophos_sfos_ssh.py 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/sophos/sophos_sfos_ssh.py 2020-03-22 01:38:04.000000000 +0100 @@ -0,0 +1,59 @@ +"""SophosXG (SFOS) Firewall support""" +import time +from netmiko.cisco_base_connection import CiscoSSHConnection + + +class SophosSfosSSH(CiscoSSHConnection): + def session_preparation(self): + """Prepare the session after the connection has been established.""" + self._test_channel_read() + """ + Sophos Firmware Version SFOS 18.0.0 GA-Build339 + + Main Menu + + 1. Network Configuration + 2. System Configuration + 3. Route Configuration + 4. Device Console + 5. Device Management + 6. VPN Management + 7. Shutdown/Reboot Device + 0. Exit + + Select Menu Number [0-7]: + """ + self.write_channel("4" + self.RETURN) + self._test_channel_read(pattern=r"[console>]") + self.set_base_prompt() + # Clear the read buffer + time.sleep(0.3 * self.global_delay_factor) + self.clear_buffer() + + def check_enable_mode(self, *args, **kwargs): + """No enable mode on SFOS""" + return True + + def enable(self, *args, **kwargs): + """No enable mode on SFOS""" + return "" + + def exit_enable_mode(self, *args, **kwargs): + """No enable mode on SFOS""" + return "" + + def check_config_mode(self, *args, **kwargs): + """No config mode on SFOS""" + return False + + def config_mode(self, *args, **kwargs): + """No config mode on SFOS""" + return "" + + def exit_config_mode(self, *args, **kwargs): + """No config mode on SFOS""" + return "" + + def save_config(self, *args, **kwargs): + """Not Implemented""" + raise NotImplementedError diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/ssh_autodetect.py new/netmiko-3.1.0/netmiko/ssh_autodetect.py --- old/netmiko-3.0.0/netmiko/ssh_autodetect.py 2019-12-11 19:37:51.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/ssh_autodetect.py 2020-03-23 17:10:22.000000000 +0100 @@ -203,6 +203,8 @@ """ if kwargs["device_type"] != "autodetect": raise ValueError("The connection device_type must be 'autodetect'") + # Always set cmd_verify to False for autodetect + kwargs["global_cmd_verify"] = False self.connection = ConnectHandler(*args, **kwargs) # Call the _test_channel_read() in base to clear initial data output = BaseConnection._test_channel_read(self.connection) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/ssh_dispatcher.py new/netmiko-3.1.0/netmiko/ssh_dispatcher.py --- old/netmiko-3.0.0/netmiko/ssh_dispatcher.py 2020-01-06 05:30:41.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/ssh_dispatcher.py 2020-03-22 23:23:29.000000000 +0100 @@ -31,6 +31,7 @@ from netmiko.dell import DellPowerConnectSSH from netmiko.dell import DellPowerConnectTelnet from netmiko.dell import DellIsilonSSH +from netmiko.dlink import DlinkDSTelnet, DlinkDSSSH from netmiko.eltex import EltexSSH, EltexEsrSSH from netmiko.endace import EndaceSSH from netmiko.enterasys import EnterasysSSH @@ -72,10 +73,12 @@ from netmiko.ruckus import RuckusFastironSSH from netmiko.ruckus import RuckusFastironTelnet from netmiko.ruijie import RuijieOSSSH, RuijieOSTelnet +from netmiko.sophos import SophosSfosSSH from netmiko.terminal_server import TerminalServerSSH from netmiko.terminal_server import TerminalServerTelnet from netmiko.ubiquiti import UbiquitiEdgeSSH from netmiko.vyos import VyOSSSH +from netmiko.watchguard import WatchguardFirewareSSH # The keys of this dictionary are the supported device_types @@ -114,6 +117,7 @@ "dell_os10": DellOS10SSH, "dell_powerconnect": DellPowerConnectSSH, "dell_isilon": DellIsilonSSH, + "dlink_ds": DlinkDSSSH, "endace": EndaceSSH, "eltex": EltexSSH, "eltex_esr": EltexEsrSSH, @@ -161,10 +165,12 @@ "rad_etx": RadETXSSH, "ruckus_fastiron": RuckusFastironSSH, "ruijie_os": RuijieOSSSH, + "sophos_sfos": SophosSfosSSH, "ubiquiti_edge": UbiquitiEdgeSSH, "ubiquiti_edgeswitch": UbiquitiEdgeSSH, "vyatta_vyos": VyOSSSH, "vyos": VyOSSSH, + "watchguard_fireware": WatchguardFirewareSSH, } FILE_TRANSFER_MAP = { @@ -206,6 +212,7 @@ CLASS_MAPPER["cisco_xr_telnet"] = CiscoXrTelnet CLASS_MAPPER["dell_dnos6_telnet"] = DellDNOS6Telnet CLASS_MAPPER["dell_powerconnect_telnet"] = DellPowerConnectTelnet +CLASS_MAPPER["dlink_ds_telnet"] = DlinkDSTelnet CLASS_MAPPER["extreme_telnet"] = ExtremeExosTelnet CLASS_MAPPER["extreme_exos_telnet"] = ExtremeExosTelnet CLASS_MAPPER["extreme_netiron_telnet"] = ExtremeNetironTelnet diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/utilities.py new/netmiko-3.1.0/netmiko/utilities.py --- old/netmiko-3.0.0/netmiko/utilities.py 2019-12-11 19:37:51.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/utilities.py 2020-03-20 16:58:01.000000000 +0100 @@ -4,6 +4,7 @@ import io import os from pathlib import Path +import functools import serial.tools.list_ports from netmiko._textfsm import _clitable as clitable from netmiko._textfsm._clitable import CliTableError @@ -334,3 +335,15 @@ return parsed_output except Exception: return raw_output + + +def select_cmd_verify(func): + """Override function cmd_verify argument with global setting.""" + + @functools.wraps(func) + def wrapper_decorator(self, *args, **kwargs): + if self.global_cmd_verify is not None: + kwargs["cmd_verify"] = self.global_cmd_verify + return func(self, *args, **kwargs) + + return wrapper_decorator diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/watchguard/__init__.py new/netmiko-3.1.0/netmiko/watchguard/__init__.py --- old/netmiko-3.0.0/netmiko/watchguard/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/watchguard/__init__.py 2020-03-20 16:58:01.000000000 +0100 @@ -0,0 +1,3 @@ +from netmiko.watchguard.fireware_ssh import WatchguardFirewareSSH + +__all__ = ["WatchguardFirewareSSH"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko/watchguard/fireware_ssh.py new/netmiko-3.1.0/netmiko/watchguard/fireware_ssh.py --- old/netmiko-3.0.0/netmiko/watchguard/fireware_ssh.py 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-3.1.0/netmiko/watchguard/fireware_ssh.py 2020-03-20 16:58:01.000000000 +0100 @@ -0,0 +1,36 @@ +import time +from netmiko.base_connection import BaseConnection + + +class WatchguardFirewareSSH(BaseConnection): + """ + Implements methods for communicating with Watchguard Firebox firewalls. + """ + + def session_preparation(self): + """ + Prepare the session after the connection has been established. + + Set the base prompt for interaction ('#'). + """ + self._test_channel_read() + self.set_base_prompt() + # Clear the read buffer + time.sleep(0.3 * self.global_delay_factor) + self.clear_buffer() + + def check_config_mode(self, check_string=")#", pattern="#"): + """ + Checks if the device is in configuration mode or not. + """ + return super().check_config_mode(check_string=check_string, pattern=pattern) + + def config_mode(self, config_command="configure", pattern="#"): + return super().config_mode(config_command=config_command, pattern=pattern) + + def exit_config_mode(self, exit_config="exit", pattern="#"): + return super().exit_config_mode(exit_config=exit_config, pattern=pattern) + + def save_config(self, *args, **kwargs): + """No save config on Watchguard.""" + pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko.egg-info/PKG-INFO new/netmiko-3.1.0/netmiko.egg-info/PKG-INFO --- old/netmiko-3.0.0/netmiko.egg-info/PKG-INFO 2020-01-16 05:32:35.000000000 +0100 +++ new/netmiko-3.1.0/netmiko.egg-info/PKG-INFO 2020-03-23 17:49:02.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: netmiko -Version: 3.0.0 +Version: 3.1.0 Summary: Multi-vendor library to simplify Paramiko SSH connections to network devices Home-page: https://github.com/ktbyers/netmiko Author: Kirk Byers @@ -203,5 +203,6 @@ Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 Description-Content-Type: text/markdown Provides-Extra: test diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/netmiko.egg-info/SOURCES.txt new/netmiko-3.1.0/netmiko.egg-info/SOURCES.txt --- old/netmiko-3.0.0/netmiko.egg-info/SOURCES.txt 2020-01-16 05:32:35.000000000 +0100 +++ new/netmiko-3.1.0/netmiko.egg-info/SOURCES.txt 2020-03-23 17:49:02.000000000 +0100 @@ -66,6 +66,8 @@ netmiko/dell/dell_isilon_ssh.py netmiko/dell/dell_os10_ssh.py netmiko/dell/dell_powerconnect.py +netmiko/dlink/__init__.py +netmiko/dlink/dlink_ds.py netmiko/eltex/__init__.py netmiko/eltex/eltex_esr_ssh.py netmiko/eltex/eltex_ssh.py @@ -130,12 +132,16 @@ netmiko/ruckus/ruckus_fastiron.py netmiko/ruijie/__init__.py netmiko/ruijie/ruijie_os.py +netmiko/sophos/__init__.py +netmiko/sophos/sophos_sfos_ssh.py netmiko/terminal_server/__init__.py netmiko/terminal_server/terminal_server.py netmiko/ubiquiti/__init__.py netmiko/ubiquiti/edge_ssh.py netmiko/vyos/__init__.py netmiko/vyos/vyos_ssh.py +netmiko/watchguard/__init__.py +netmiko/watchguard/fireware_ssh.py tests/__init__.py tests/add_delay.sh tests/brocade_fastiron_commands.txt @@ -189,11 +195,11 @@ tests/test_oneaccess_oneos_telnet.sh tests/test_s300.sh tests/test_scp.sh +tests/test_sophos_sfos.sh tests/test_suite_alt.sh tests/test_suite_tmp.sh tests/test_ubiquiti_edgeswitch.sh tests/test_utils.py -tests/testx.txt tests/SLOG/cisco881_slog.log tests/SLOG/cisco881_slog_append.log tests/SLOG/cisco881_slog_append_compare.log diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/setup.py new/netmiko-3.1.0/setup.py --- old/netmiko-3.0.0/setup.py 2019-12-11 19:37:51.000000000 +0100 +++ new/netmiko-3.1.0/setup.py 2020-03-20 16:58:01.000000000 +0100 @@ -42,6 +42,7 @@ "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", ], packages=find_packages(exclude=("test*",)), install_requires=[ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/tests/SLOG/cisco881_slog_wr_compare.log new/netmiko-3.1.0/tests/SLOG/cisco881_slog_wr_compare.log --- old/netmiko-3.0.0/tests/SLOG/cisco881_slog_wr_compare.log 2019-12-11 19:37:51.000000000 +0100 +++ new/netmiko-3.1.0/tests/SLOG/cisco881_slog_wr_compare.log 2020-03-20 16:58:01.000000000 +0100 @@ -1,3 +1,4 @@ + cisco1# cisco1#terminal length 0 Binary files old/netmiko-3.0.0/tests/__pycache__/conftest.cpython-36-pytest-5.1.2.pyc and new/netmiko-3.1.0/tests/__pycache__/conftest.cpython-36-pytest-5.1.2.pyc differ Binary files old/netmiko-3.0.0/tests/__pycache__/conftest.cpython-36-pytest-5.2.1.pyc and new/netmiko-3.1.0/tests/__pycache__/conftest.cpython-36-pytest-5.2.1.pyc differ Binary files old/netmiko-3.0.0/tests/__pycache__/test_netmiko_autodetect.cpython-36-pytest-5.1.2.pyc and new/netmiko-3.1.0/tests/__pycache__/test_netmiko_autodetect.cpython-36-pytest-5.1.2.pyc differ Binary files old/netmiko-3.0.0/tests/__pycache__/test_netmiko_commit.cpython-36-pytest-5.1.2.pyc and new/netmiko-3.1.0/tests/__pycache__/test_netmiko_commit.cpython-36-pytest-5.1.2.pyc differ Binary files old/netmiko-3.0.0/tests/__pycache__/test_netmiko_config.cpython-36-pytest-5.1.2.pyc and new/netmiko-3.1.0/tests/__pycache__/test_netmiko_config.cpython-36-pytest-5.1.2.pyc differ Binary files old/netmiko-3.0.0/tests/__pycache__/test_netmiko_config_acl.cpython-36-pytest-5.1.2.pyc and new/netmiko-3.1.0/tests/__pycache__/test_netmiko_config_acl.cpython-36-pytest-5.1.2.pyc differ Binary files old/netmiko-3.0.0/tests/__pycache__/test_netmiko_scp.cpython-36-pytest-5.1.2.pyc and new/netmiko-3.1.0/tests/__pycache__/test_netmiko_scp.cpython-36-pytest-5.1.2.pyc differ Binary files old/netmiko-3.0.0/tests/__pycache__/test_netmiko_session_log.cpython-36-pytest-5.1.2.pyc and new/netmiko-3.1.0/tests/__pycache__/test_netmiko_session_log.cpython-36-pytest-5.1.2.pyc differ Binary files old/netmiko-3.0.0/tests/__pycache__/test_netmiko_show.cpython-36-pytest-5.1.2.pyc and new/netmiko-3.1.0/tests/__pycache__/test_netmiko_show.cpython-36-pytest-5.1.2.pyc differ Binary files old/netmiko-3.0.0/tests/__pycache__/test_netmiko_show.cpython-36-pytest-5.2.1.pyc and new/netmiko-3.1.0/tests/__pycache__/test_netmiko_show.cpython-36-pytest-5.2.1.pyc differ Binary files old/netmiko-3.0.0/tests/__pycache__/test_netmiko_tcl.cpython-36-pytest-5.1.2.pyc and new/netmiko-3.1.0/tests/__pycache__/test_netmiko_tcl.cpython-36-pytest-5.1.2.pyc differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/tests/cisco3-out.txt new/netmiko-3.1.0/tests/cisco3-out.txt --- old/netmiko-3.0.0/tests/cisco3-out.txt 2020-01-16 04:48:43.000000000 +0100 +++ new/netmiko-3.1.0/tests/cisco3-out.txt 2020-03-23 17:13:55.000000000 +0100 @@ -1,10 +1,5 @@ - -cisco3# -cisco3# -cisco3# -cisco3# cisco3# cisco3#terminal length 0 cisco3#terminal width 511 @@ -125,10 +120,6 @@ cisco3(config-ext-nacl)#end cisco3# cisco3# -cisco3# -cisco3# -cisco3# -cisco3# cisco3#show ip access-lists netmiko_test_large_acl Extended IP access list netmiko_test_large_acl 10 permit ip host 192.168.0.1 any diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/tests/conftest.py new/netmiko-3.1.0/tests/conftest.py --- old/netmiko-3.0.0/tests/conftest.py 2019-12-11 19:37:51.000000000 +0100 +++ new/netmiko-3.1.0/tests/conftest.py 2020-03-20 16:58:01.000000000 +0100 @@ -38,6 +38,40 @@ return conn [email protected](scope="module") +def net_connect_cmd_verify(request): + """ + Create the SSH connection to the remote device + + Return the netmiko connection object + + Set global_cmd_verify = False + """ + device_under_test = request.config.getoption("test_device") + test_devices = parse_yaml(PWD + "/etc/test_devices.yml") + device = test_devices[device_under_test] + device["verbose"] = False + device["global_cmd_verify"] = False + conn = ConnectHandler(**device) + return conn + + [email protected](scope="function") +def net_connect_newconn(request): + """ + Create the SSH connection to the remote device + + Return the netmiko connection object. + Force a new connection for each test. + """ + device_under_test = request.config.getoption("test_device") + test_devices = parse_yaml(PWD + "/etc/test_devices.yml") + device = test_devices[device_under_test] + device["verbose"] = False + conn = ConnectHandler(**device) + return conn + + @pytest.fixture() def net_connect_cm(request): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/tests/etc/commands.yml new/netmiko-3.1.0/tests/etc/commands.yml --- old/netmiko-3.0.0/tests/etc/commands.yml 2019-10-15 21:01:37.000000000 +0200 +++ new/netmiko-3.1.0/tests/etc/commands.yml 2020-02-22 00:55:42.000000000 +0100 @@ -110,6 +110,8 @@ version: "uname -a" basic: "ifconfig -a | grep inet | grep -v inet6 " extended_output: "netstat -an" # requires paging to be disabled + # config_long_command: "ls verylongcommandnamethatwillcauselinewrapissuesasitdoesnotfitonesinglescreengreaterthan127charsandthensomesothingsreallyarennotright" + # config_verification: "ls" dell_force10: version: "show version" @@ -131,6 +133,7 @@ - "no logging console" - "logging console 4" # something you can verify has changed config_verification: "show run | inc logging" + config_long_command: "snmp-server location verylongsnmplocationnamethatwillcauselinewrapissuesasitdoesnotfitonesinglescreengreaterthan127charsandthensomesothingsreallyarennotright" cisco_xe: version: "show version" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/tests/etc/commands.yml.example new/netmiko-3.1.0/tests/etc/commands.yml.example --- old/netmiko-3.0.0/tests/etc/commands.yml.example 2020-01-06 05:30:41.000000000 +0100 +++ new/netmiko-3.1.0/tests/etc/commands.yml.example 2020-03-22 01:38:04.000000000 +0100 @@ -282,6 +282,22 @@ save_config_confirm: True save_config_response: '[OK]' +dlink_ds: + version: "show greeting_message" + basic: "show ipif" + config: + - enable command logging + config_verification: "show config current_config include \"logging\"" + extended_output: "show config current_config" # requires paging to be disabled + +dlink_ds_telnet: + version: "show greeting_message" + basic: "show ipif" + config: + - enable command logging + config_verification: "show config current_config include \"logging\"" + extended_output: "show config current_config" # requires paging to be disabled + ruijie_os: version: "show version" basic: "show ip interface brief" @@ -294,3 +310,9 @@ save_config_cmd: 'write' save_config_confirm: False save_config_response: 'OK' + +sophos_sfos: + version: "system diagnostics show version-info" + basic: "system diagnostics utilities route lookup 172.16.16.16" + extended_output: "system diagnostics show version-info" + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/tests/etc/responses.yml.example new/netmiko-3.1.0/tests/etc/responses.yml.example --- old/netmiko-3.0.0/tests/etc/responses.yml.example 2020-01-06 05:30:41.000000000 +0100 +++ new/netmiko-3.1.0/tests/etc/responses.yml.example 2020-03-22 01:38:04.000000000 +0100 @@ -223,6 +223,22 @@ interface_ip: 1.2.3.5 version_banner: "NOS version 2.09 #0001" multiple_line_output: "Interface br328" + +dlink_ds: + base_prompt: DGS-3120-24TC:admin + router_prompt: DGS-3120-24TC:admin# + enable_prompt: DGS-3120-24TC:admin# + version_banner: "D-Link Corporation" + multiple_line_output: "End of configuration file" + interface_ip: 192.168.50.10 + +dlink_ds_telnet: + base_prompt: DGS-3120-24TC:admin + router_prompt: DGS-3120-24TC:admin# + enable_prompt: DGS-3120-24TC:admin# + version_banner: "D-Link Corporation" + multiple_line_output: "End of configuration file" + interface_ip: 192.168.50.10 ruijie_os: base_prompt: Ruijie @@ -233,3 +249,12 @@ multiple_line_output: "" file_check_cmd: "logging buffered 8880" save_config: 'OK' + +sophos_sfos: + base_prompt: "console" + router_prompt: "console>" + enable_prompt: "console>" + interface_ip: 172.16.16.16 + version_banner: "Serial Number" + multiple_line_output: "" + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/tests/etc/test_devices.yml.example new/netmiko-3.1.0/tests/etc/test_devices.yml.example --- old/netmiko-3.0.0/tests/etc/test_devices.yml.example 2020-01-06 05:30:41.000000000 +0100 +++ new/netmiko-3.1.0/tests/etc/test_devices.yml.example 2020-03-22 01:38:04.000000000 +0100 @@ -178,9 +178,29 @@ username: TEST password: TEST +dlink_ds: + device_type: dlink_ds + ip: 192.168.50.10 + username: admin + password: admin + +dlink_ds_telnet: + device_type: dlink_ds_telnet + ip: 192.168.50.10 + username: admin + password: admin + ruijie_os: device_type: ruijie_os ip: 1.1.1.1 username: ruijie password: ruijie secret: ruijie + +sophos_sfos: + device_type: sophos_sfos + ip: 172.16.16.16 + username: admin + password: admin + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/tests/test_netmiko_config.py new/netmiko-3.1.0/tests/test_netmiko_config.py --- old/netmiko-3.0.0/tests/test_netmiko_config.py 2019-12-11 19:37:57.000000000 +0100 +++ new/netmiko-3.1.0/tests/test_netmiko_config.py 2020-03-21 00:33:27.000000000 +0100 @@ -1,4 +1,6 @@ #!/usr/bin/env python + + def test_ssh_connect(net_connect, commands, expected_responses): """ Verify the connection was established successfully @@ -40,7 +42,7 @@ assert net_connect.check_config_mode() is False -def test_command_set(net_connect, commands, expected_responses): +def test_config_set(net_connect, commands, expected_responses): """Test sending configuration commands.""" config_commands = commands["config"] support_commit = commands.get("support_commit") @@ -50,20 +52,15 @@ net_connect.send_config_set(config_commands[0]) if support_commit: net_connect.commit() - cmd_response = expected_responses.get("cmd_response_init") config_commands_output = net_connect.send_command(config_verify) - print(config_verify) - print(config_commands_output) if cmd_response: assert cmd_response in config_commands_output else: assert config_commands[0] in config_commands_output - net_connect.send_config_set(config_commands) if support_commit: net_connect.commit() - cmd_response = expected_responses.get("cmd_response_final") config_commands_output = net_connect.send_command_expect(config_verify) if cmd_response: @@ -72,7 +69,18 @@ assert config_commands[-1] in config_commands_output -def test_commands_from_file(net_connect, commands, expected_responses): +def test_config_set_longcommand(net_connect, commands, expected_responses): + """Test sending configuration commands using long commands""" + config_commands = commands.get("config_long_command") + config_verify = commands["config_verification"] # noqa + if not config_commands: + assert True + return + output = net_connect.send_config_set(config_commands) # noqa + assert True + + +def test_config_from_file(net_connect, commands, expected_responses): """ Test sending configuration commands from a file """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/tests/test_netmiko_show.py new/netmiko-3.1.0/tests/test_netmiko_show.py --- old/netmiko-3.0.0/tests/test_netmiko_show.py 2019-12-14 19:57:44.000000000 +0100 +++ new/netmiko-3.1.0/tests/test_netmiko_show.py 2020-03-21 00:33:27.000000000 +0100 @@ -16,11 +16,14 @@ """ import pytest import time +from datetime import datetime +from netmiko.utilities import select_cmd_verify -# import logging -# logging.basicConfig(filename="test.log", level=logging.DEBUG) -# logger = logging.getLogger("netmiko") +@select_cmd_verify +def bogus_func(obj, *args, **kwargs): + """Function that just returns the arguments modified by the decorator.""" + return (obj, args, kwargs) def test_disable_paging(net_connect, commands, expected_responses): @@ -51,16 +54,62 @@ show_ip = net_connect.send_command_timing(commands["basic"]) assert expected_responses["interface_ip"] in show_ip # Force verification of command echo - show_ip = net_connect.send_command_timing(commands["basic"], cmd_echo=True) + show_ip = net_connect.send_command_timing(commands["basic"], cmd_verify=True) assert expected_responses["interface_ip"] in show_ip def test_send_command(net_connect, commands, expected_responses): """Verify a command can be sent down the channel successfully using send_command method.""" - time.sleep(1) net_connect.clear_buffer() show_ip_alt = net_connect.send_command(commands["basic"]) assert expected_responses["interface_ip"] in show_ip_alt + show_ip_alt = net_connect.send_command(commands["basic"], cmd_verify=False) + assert expected_responses["interface_ip"] in show_ip_alt + + +def test_cmd_verify_decorator(net_connect_cmd_verify): + obj = net_connect_cmd_verify + # Global False should have precedence + assert obj.global_cmd_verify is False + (obj, args, kwargs) = bogus_func(net_connect_cmd_verify, cmd_verify=True) + assert kwargs["cmd_verify"] is False + (obj, args, kwargs) = bogus_func(net_connect_cmd_verify, cmd_verify=False) + assert kwargs["cmd_verify"] is False + + # Global True should have precedence + obj.global_cmd_verify = True + assert obj.global_cmd_verify is True + (obj, args, kwargs) = bogus_func(net_connect_cmd_verify, cmd_verify=True) + assert kwargs["cmd_verify"] is True + (obj, args, kwargs) = bogus_func(net_connect_cmd_verify, cmd_verify=False) + assert kwargs["cmd_verify"] is True + + # None should track the local argument + obj.global_cmd_verify = None + assert obj.global_cmd_verify is None + (obj, args, kwargs) = bogus_func(net_connect_cmd_verify, cmd_verify=True) + assert kwargs["cmd_verify"] is True + (obj, args, kwargs) = bogus_func(net_connect_cmd_verify, cmd_verify=False) + assert kwargs["cmd_verify"] is False + + # Set it back to proper False value (so later tests aren't messed up). + obj.global_cmd_verify = False + + +def test_send_command_global_cmd_verify( + net_connect_cmd_verify, commands, expected_responses +): + """ + Verify a command can be sent down the channel successfully using send_command method. + + Disable cmd_verify globally. + """ + net_connect = net_connect_cmd_verify + net_connect.clear_buffer() + # cmd_verify should be disabled globally at this point + assert net_connect.global_cmd_verify is False + show_ip_alt = net_connect.send_command(commands["basic"]) + assert expected_responses["interface_ip"] in show_ip_alt def test_send_command_juniper(net_connect, commands, expected_responses): @@ -82,7 +131,6 @@ # Strip off the _ssh, _telnet, _serial base_platform = base_platform.split("_")[:-1] base_platform = "_".join(base_platform) - if base_platform not in [ "cisco_ios", "cisco_xe", @@ -111,7 +159,6 @@ # Strip off the _ssh, _telnet, _serial base_platform = base_platform.split("_")[:-1] base_platform = "_".join(base_platform) - if base_platform not in [ "cisco_ios", "cisco_xe", @@ -149,6 +196,11 @@ """Ensure that the command that was executed does not show up in the command output.""" show_ip = net_connect.send_command_timing(commands["basic"]) show_ip_alt = net_connect.send_command(commands["basic"]) + + # dlink_ds has an echo of the command in the command output + if "dlink_ds" in net_connect.device_type: + show_ip = "\n".join(show_ip.split("\n")[2:]) + show_ip_alt = "\n".join(show_ip_alt.split("\n")[2:]) assert commands["basic"] not in show_ip assert commands["basic"] not in show_ip_alt @@ -189,4 +241,24 @@ def test_disconnect(net_connect, commands, expected_responses): """Terminate the SSH session.""" + start_time = datetime.now() net_connect.disconnect() + end_time = datetime.now() + time_delta = end_time - start_time + assert net_connect.remote_conn is None + assert time_delta.total_seconds() < 8 + + +def test_disconnect_no_enable(net_connect_newconn, commands, expected_responses): + """Terminate the SSH session from privilege level1""" + net_connect = net_connect_newconn + if "cisco_ios" in net_connect.device_type: + net_connect.send_command_timing("disable") + start_time = datetime.now() + net_connect.disconnect() + end_time = datetime.now() + time_delta = end_time - start_time + assert net_connect.remote_conn is None + assert time_delta.total_seconds() < 5 + else: + assert True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/tests/test_sophos_sfos.sh new/netmiko-3.1.0/tests/test_sophos_sfos.sh --- old/netmiko-3.0.0/tests/test_sophos_sfos.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-3.1.0/tests/test_sophos_sfos.sh 2020-03-22 01:38:04.000000000 +0100 @@ -0,0 +1,12 @@ +#!/bin/sh + +RETURN_CODE=0 + +# Exit on the first test failure and set RETURN_CODE = 1 +echo "Sophos SFOS SSH" \ +&& date \ +&& py.test -v test_netmiko_show.py --test_device sophos_sfos \ +&& date \ +|| RETURN_CODE=1 + +exit $RETURN_CODE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/tests/testx.txt new/netmiko-3.1.0/tests/testx.txt --- old/netmiko-3.0.0/tests/testx.txt 2020-01-16 00:03:09.000000000 +0100 +++ new/netmiko-3.1.0/tests/testx.txt 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -no logging console diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-3.0.0/tests/unit/test_utilities.py new/netmiko-3.1.0/tests/unit/test_utilities.py --- old/netmiko-3.0.0/tests/unit/test_utilities.py 2019-12-11 19:37:51.000000000 +0100 +++ new/netmiko-3.1.0/tests/unit/test_utilities.py 2020-03-20 16:58:01.000000000 +0100 @@ -2,9 +2,6 @@ import os from os.path import dirname, join, relpath -import sys - -import pytest from netmiko import utilities from netmiko._textfsm import _clitable as clitable @@ -251,10 +248,6 @@ assert result == raw_output [email protected]( - sys.version_info >= (3, 8), - reason="The genie package is not available for Python 3.8 yet", -) def test_get_structured_data_genie(): """Convert raw CLI output to structured data using Genie"""
