Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-netmiko for openSUSE:Factory checked in at 2023-12-12 19:32:43 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-netmiko (Old) and /work/SRC/openSUSE:Factory/.python-netmiko.new.25432 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-netmiko" Tue Dec 12 19:32:43 2023 rev:21 rq:1132682 version:4.3.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-netmiko/python-netmiko.changes 2023-08-28 17:17:21.413265197 +0200 +++ /work/SRC/openSUSE:Factory/.python-netmiko.new.25432/python-netmiko.changes 2023-12-12 19:33:11.662595793 +0100 @@ -1,0 +2,42 @@ +Tue Dec 12 13:37:47 UTC 2023 - Dirk Müller <dmuel...@suse.com> + +- update to 4.3.0: + * Netmiko's session_log has certain scenarios where it was + failing to hide the default no_log items ("secret" and + "password"). This bug has generally been fixed though there + are likely edge scenarios where this could still happen. + Given the nature of the session_log it should always be + viewed as a security sensitive file. + * Drop PY3.7 support + * Improve Ciena SAOS logic in set_base_prompt + * Improve Nokia SROS disable paging behavior + * Remove Arris and Casa from SSH autodetect + * Add support for Genie and Linux + * Improve TP-Link session_preparation behavior + * Improve MikoTik terminal size definition + * Improve Arista SSH autodetection + * Fix SNMP autodetection when hostname is used (instead of + IPv4/IPv6 address) + * Improve SCP behavior for remote_file_size when subdirectories + are used + * Implement TelnetFallback entry point + * Improve Extreme SLX-OS autodetect + * Improve Fortinet driver _get_output_mode_v6 behavior + * Improve HP comware and HP procurve behavior in + session_preparation + * Add Allied Telesis in SNMP and SSH autodetect + * Update hp_comware.py handle RBM in prompt + * Update vyos_ssh.py to enter and exit config mode on save + * Handle Huawei secure configuration message + * Add support for a SessionLog object in dunder-init(). This + allows a custom no_log definition. + * CI-CD fixes for various things breaking + * Improve ers driver cntl-y behavior + * Adtran driver enable global_cmd_verify by default + * Adtran driver improve enable() behavior + * Add Telnet Proxy support for SOCKS Proxy + * Add support for Fiberstore FSOS + * Add support for Maipu + * Add Digi TransPort Router Support + +------------------------------------------------------------------- Old: ---- netmiko-4.2.0.tar.gz New: ---- netmiko-4.3.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-netmiko.spec ++++++ --- /var/tmp/diff_new_pack.fJgb0x/_old 2023-12-12 19:33:12.150613798 +0100 +++ /var/tmp/diff_new_pack.fJgb0x/_new 2023-12-12 19:33:12.154613945 +0100 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-netmiko -Version: 4.2.0 +Version: 4.3.0 Release: 0 Summary: Multi-vendor library to simplify Paramiko SSH connections to network devices License: MIT ++++++ netmiko-4.2.0.tar.gz -> netmiko-4.3.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/PKG-INFO new/netmiko-4.3.0/PKG-INFO --- old/netmiko-4.2.0/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-4.3.0/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 @@ -1,22 +1,21 @@ Metadata-Version: 2.1 Name: netmiko -Version: 4.2.0 +Version: 4.3.0 Summary: Multi-vendor library to simplify legacy CLI connections to network devices Home-page: https://github.com/ktbyers/netmiko License: MIT Author: Kirk Byers Author-email: ktby...@twb-tech.com -Requires-Python: >=3.7,<4.0 +Requires-Python: >=3.8,<4.0 Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Requires-Dist: ntc-templates (>=2.0.0) @@ -30,7 +29,7 @@ [](https://img.shields.io/pypi/pyversions/netmiko) [](https://pypi.python.org/pypi/netmiko) -[](https://pepy.tech/project/netmiko) +[](https://static.pepy.tech/badge/netmiko) [](https://GitHub.com/ktbyers/netmiko/graphs/contributors/) [](https://github.com/ambv/black) @@ -43,6 +42,12 @@ <br /> +## Contributing to Netmiko + +[CONTRIBUTING.md](https://github.com/ktbyers/netmiko/blob/develop/CONTRIBUTING.md) + +<br /> + ## Why Netmiko? Network automation to screen-scraping devices is primarily concerned with gathering output from show commands and with making configuration changes. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/README.md new/netmiko-4.3.0/README.md --- old/netmiko-4.2.0/README.md 2023-05-05 18:30:15.008753300 +0200 +++ new/netmiko-4.3.0/README.md 2023-11-17 18:35:13.596568000 +0100 @@ -1,6 +1,6 @@ [](https://img.shields.io/pypi/pyversions/netmiko) [](https://pypi.python.org/pypi/netmiko) -[](https://pepy.tech/project/netmiko) +[](https://static.pepy.tech/badge/netmiko) [](https://GitHub.com/ktbyers/netmiko/graphs/contributors/) [](https://github.com/ambv/black) @@ -13,6 +13,12 @@ <br /> +## Contributing to Netmiko + +[CONTRIBUTING.md](https://github.com/ktbyers/netmiko/blob/develop/CONTRIBUTING.md) + +<br /> + ## Why Netmiko? Network automation to screen-scraping devices is primarily concerned with gathering output from show commands and with making configuration changes. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/__init__.py new/netmiko-4.3.0/netmiko/__init__.py --- old/netmiko-4.2.0/netmiko/__init__.py 2023-05-05 18:30:15.060753600 +0200 +++ new/netmiko-4.3.0/netmiko/__init__.py 2023-11-17 18:35:13.604568000 +0100 @@ -1,9 +1,9 @@ import sys -__version__ = "4.2.0" +__version__ = "4.3.0" PY_MAJ_VER = 3 -PY_MIN_VER = 7 -MIN_PYTHON_VER = "3.7" +PY_MIN_VER = 8 +MIN_PYTHON_VER = "3.8" # Make sure user is using a valid Python version (for Netmiko) @@ -38,6 +38,7 @@ from netmiko.ssh_dispatcher import ConnectHandler # noqa +from netmiko.ssh_dispatcher import TelnetFallback # noqa from netmiko.ssh_dispatcher import ConnLogOnly # noqa from netmiko.ssh_dispatcher import ConnUnify # noqa from netmiko.ssh_dispatcher import ssh_dispatcher # noqa @@ -66,6 +67,7 @@ __all__ = ( "ConnectHandler", + "AgnosticHandler", "ConnLogOnly", "ConnUnify", "ssh_dispatcher", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/adtran/adtran.py new/netmiko-4.3.0/netmiko/adtran/adtran.py --- old/netmiko-4.2.0/netmiko/adtran/adtran.py 2023-05-05 18:30:15.060753600 +0200 +++ new/netmiko-4.3.0/netmiko/adtran/adtran.py 2023-11-17 18:35:13.604568000 +0100 @@ -1,16 +1,12 @@ from typing import Any, Optional import re from netmiko.cisco_base_connection import CiscoBaseConnection +from netmiko.exceptions import NetmikoTimeoutException class AdtranOSBase(CiscoBaseConnection): prompt_pattern = r"[>#]" - def __init__(self, *args: Any, **kwargs: Any) -> None: - if kwargs.get("global_cmd_verify") is None: - kwargs["global_cmd_verify"] = False - return super().__init__(*args, **kwargs) - def session_preparation(self) -> None: """Prepare the session after the connection has been established.""" self.ansi_escape_codes = True @@ -31,14 +27,55 @@ check_state: bool = True, re_flags: int = re.IGNORECASE, ) -> str: - return super().enable( - cmd=cmd, - pattern=pattern, - enable_pattern=enable_pattern, - check_state=check_state, - re_flags=re_flags, + output = "" + msg = ( + "Failed to enter enable mode. Please ensure you pass " + "the 'secret' argument to ConnectHandler." ) + # Check if in enable mode already. + if check_state and self.check_enable_mode(): + return output + + # Send "enable" mode command + self.write_channel(self.normalize_cmd(cmd)) + try: + # Read the command echo + if self.global_cmd_verify is not False: + output += self.read_until_pattern(pattern=re.escape(cmd.strip())) + + # Search for trailing prompt or password pattern + output += self.read_until_prompt_or_pattern( + pattern=pattern, re_flags=re_flags + ) + + # Send the "secret" in response to password pattern + if re.search(pattern, output): + self.write_channel(self.normalize_cmd(self.secret)) + + # Handle the fallback to local authentication case + fallback_pattern = r"Falling back" + new_output = self.read_until_prompt_or_pattern( + pattern=fallback_pattern, re_flags=re_flags + ) + output += new_output + + if "Falling back" in new_output: + self.write_channel(self.normalize_cmd(self.secret)) + output += self.read_until_prompt() + + # Search for terminating pattern if defined + if enable_pattern and not re.search(enable_pattern, output): + output += self.read_until_pattern(pattern=enable_pattern) + else: + if not self.check_enable_mode(): + raise ValueError(msg) + + except NetmikoTimeoutException: + raise ValueError(msg) + + return output + def exit_enable_mode(self, exit_command: str = "disable") -> str: return super().exit_enable_mode(exit_command=exit_command) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/base_connection.py new/netmiko-4.3.0/netmiko/base_connection.py --- old/netmiko-4.2.0/netmiko/base_connection.py 2023-05-05 18:30:15.068753700 +0200 +++ new/netmiko-4.3.0/netmiko/base_connection.py 2023-11-17 18:35:13.608568000 +0100 @@ -61,6 +61,7 @@ calc_old_timeout, ) from netmiko.utilities import m_exec_time # noqa +from netmiko import telnet_proxy if TYPE_CHECKING: from os import PathLike @@ -102,6 +103,20 @@ return cast(F, wrapper_decorator) +def flush_session_log(func: F) -> F: + @functools.wraps(func) + def wrapper_decorator(self: "BaseConnection", *args: Any, **kwargs: Any) -> Any: + try: + return_val = func(self, *args, **kwargs) + finally: + # Always flush the session_log + if self.session_log: + self.session_log.flush() + return return_val + + return cast(F, wrapper_decorator) + + def log_writes(func: F) -> F: """Handle both session_log and log of writes.""" @@ -181,6 +196,7 @@ allow_auto_change: bool = False, encoding: str = "utf-8", sock: Optional[socket.socket] = None, + sock_telnet: Optional[Dict[str, Any]] = None, auto_connect: bool = True, delay_factor_compat: bool = False, disable_lf_normalization: bool = False, @@ -267,7 +283,8 @@ to select smallest of global and specific. Sets default global_delay_factor to .1 (default: True) - :param session_log: File path or BufferedIOBase subclass object to write the session log to. + :param session_log: File path, SessionLog object, or BufferedIOBase subclass object + to write the session log to. :param session_log_record_writes: The session log generally only records channel reads due to eliminate command duplication due to command echo. You can enable this if you @@ -280,11 +297,14 @@ (default: False) :param encoding: Encoding to be used when writing bytes to the output channel. - (default: ascii) + (default: "utf-8") :param sock: An open socket or socket-like object (such as a `.Channel`) to use for communication to the target host (default: None). + :param sock_telnet: A dictionary of telnet socket parameters (SOCKS proxy). See + telnet_proxy.py code for details. + :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. @@ -351,6 +371,7 @@ self.allow_auto_change = allow_auto_change self.encoding = encoding self.sock = sock + self.sock_telnet = sock_telnet self.fast_cli = fast_cli self._legacy_mode = _legacy_mode self.global_delay_factor = global_delay_factor @@ -366,6 +387,7 @@ no_log["password"] = self.password if self.secret: no_log["secret"] = self.secret + # Always sanitize username and password log.addFilter(SecretsFilter(no_log=no_log)) # Netmiko will close the session_log if we open the file @@ -386,10 +408,14 @@ no_log=no_log, record_writes=session_log_record_writes, ) + elif isinstance(session_log, SessionLog): + # SessionLog object + self.session_log = session_log + self.session_log.open() else: raise ValueError( "session_log must be a path to a file, a file handle, " - "or a BufferedIOBase subclass." + "SessionLog object, or a BufferedIOBase subclass." ) # Default values @@ -667,7 +693,6 @@ start_time = time.time() # if read_timeout == 0 or 0.0 keep reading indefinitely while (time.time() - start_time < read_timeout) or (not read_timeout): - output += self.read_channel() if re.search(pattern, output, flags=re_flags): @@ -1084,9 +1109,17 @@ """ self.channel: Channel if self.protocol == "telnet": - self.remote_conn = telnetlib.Telnet( - self.host, port=self.port, timeout=self.timeout - ) + if self.sock_telnet: + self.remote_conn = telnet_proxy.Telnet( + self.host, + port=self.port, + timeout=self.timeout, + proxy_dict=self.sock_telnet, + ) + else: + self.remote_conn = telnetlib.Telnet( + self.host, port=self.port, timeout=self.timeout + ) # Migrating communication to channel class self.channel = TelnetChannel(conn=self.remote_conn, encoding=self.encoding) self.telnet_login() @@ -1447,7 +1480,6 @@ return output def command_echo_read(self, cmd: str, read_timeout: float) -> str: - # 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), read_timeout=read_timeout @@ -1464,6 +1496,7 @@ pass return new_data + @flush_session_log @select_cmd_verify def send_command_timing( self, @@ -1612,6 +1645,7 @@ prompt = self.base_prompt return re.escape(prompt.strip()) + @flush_session_log @select_cmd_verify def send_command( self, @@ -2135,6 +2169,7 @@ commands = cfg_file.readlines() return self.send_config_set(commands, **kwargs) + @flush_session_log def send_config_set( self, config_commands: Union[str, Sequence[str], Iterator[str], TextIO, None] = None, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/ciena/ciena_saos.py new/netmiko-4.3.0/netmiko/ciena/ciena_saos.py --- old/netmiko-4.2.0/netmiko/ciena/ciena_saos.py 2023-05-05 18:30:15.072753700 +0200 +++ new/netmiko-4.3.0/netmiko/ciena/ciena_saos.py 2023-11-17 18:35:13.608568000 +0100 @@ -28,7 +28,7 @@ prompt = self.find_prompt(delay_factor=delay_factor) pattern = rf"^.+{self.prompt_pattern}$" - if re.search(pattern, prompt): + if not re.search(pattern, prompt): raise ValueError(f"Router prompt not found: {repr(prompt)}") # Strip off trailing terminator self.base_prompt = prompt[:-1] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/digi/__init__.py new/netmiko-4.3.0/netmiko/digi/__init__.py --- old/netmiko-4.2.0/netmiko/digi/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-4.3.0/netmiko/digi/__init__.py 2023-11-17 18:35:13.608568000 +0100 @@ -0,0 +1,3 @@ +from netmiko.digi.digi_transport import DigiTransportSSH + +__all__ = ["DigiTransportSSH"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/digi/digi_transport.py new/netmiko-4.3.0/netmiko/digi/digi_transport.py --- old/netmiko-4.2.0/netmiko/digi/digi_transport.py 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-4.3.0/netmiko/digi/digi_transport.py 2023-11-17 18:35:13.608568000 +0100 @@ -0,0 +1,27 @@ +"""Digi TransPort Routers""" +from typing import Any +from netmiko.no_enable import NoEnable +from netmiko.no_config import NoConfig +from netmiko.cisco_base_connection import CiscoSSHConnection + + +class DigiTransportBase(NoEnable, NoConfig, CiscoSSHConnection): + def __init__(self, *args: Any, **kwargs: Any) -> None: + default_enter = kwargs.get("default_enter") + kwargs["default_enter"] = "\r\n" if default_enter is None else default_enter + super().__init__(*args, **kwargs) + + def save_config( + self, + cmd: str = "config 0 save", + confirm: bool = False, + confirm_response: str = "", + ) -> str: + output = self._send_command_str( + command_string=cmd, expect_string="Please wait..." + ) + return output + + +class DigiTransportSSH(DigiTransportBase): + pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/extreme/extreme_ers_ssh.py new/netmiko-4.3.0/netmiko/extreme/extreme_ers_ssh.py --- old/netmiko-4.2.0/netmiko/extreme/extreme_ers_ssh.py 2023-05-05 18:30:15.076753600 +0200 +++ new/netmiko-4.3.0/netmiko/extreme/extreme_ers_ssh.py 2023-11-17 18:35:13.608568000 +0100 @@ -2,6 +2,7 @@ import re from netmiko.cisco_base_connection import CiscoSSHConnection from netmiko.exceptions import NetmikoAuthenticationException +import time # Extreme ERS presents Enter Ctrl-Y to begin. CTRL_Y = "\x19" @@ -27,6 +28,10 @@ Older devices the Ctrl-Y is before SSH-login (not 100% sure of this). + Few devices after SSH-login the Ctrl-Y turns to blank screen-no pattern \ + + prompt appears after Enter/Return ( tested ). + Newer devices this is after SSH-login. """ @@ -46,6 +51,9 @@ if cntl_y in new_data: self.write_channel(CTRL_Y) + time.sleep(1 * delay_factor) + # no pattern, blank for few devices till Return keypress + self.write_channel(self.RETURN) elif "Press ENTER" in new_data: self.write_channel(self.RETURN) elif uname in new_data: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/fiberstore/__init__.py new/netmiko-4.3.0/netmiko/fiberstore/__init__.py --- old/netmiko-4.2.0/netmiko/fiberstore/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-4.3.0/netmiko/fiberstore/__init__.py 2023-11-17 18:35:13.612568100 +0100 @@ -0,0 +1,3 @@ +from netmiko.fiberstore.fiberstore_fsos import FiberstoreFsosSSH + +__all__ = ["FiberstoreFsosSSH"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/fiberstore/fiberstore_fsos.py new/netmiko-4.3.0/netmiko/fiberstore/fiberstore_fsos.py --- old/netmiko-4.2.0/netmiko/fiberstore/fiberstore_fsos.py 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-4.3.0/netmiko/fiberstore/fiberstore_fsos.py 2023-11-17 18:35:13.612568100 +0100 @@ -0,0 +1,108 @@ +"""Fiberstore FSOS Driver.""" +from typing import Optional +from paramiko import SSHClient +from netmiko.ssh_auth import SSHClient_noauth +from netmiko.no_enable import NoEnable +import time +from os import path +from netmiko.cisco_base_connection import CiscoSSHConnection +from netmiko.exceptions import NetmikoAuthenticationException + + +class FiberstoreFsosSSH(NoEnable, CiscoSSHConnection): + """ + Fiberstore FSOS uses a non-standard SSH login mechanism. Consequently, + to make the login work, we have to override the SSHClient _auth method. + """ + + def session_preparation(self) -> None: + """Prepare the session after the connection has been established.""" + self.ansi_escape_codes = True + output = self._test_channel_read() + if "% Authentication Failed" in output: + assert self.remote_conn is not None + self.remote_conn.close() + msg = f"Login failed: {self.host}" + raise NetmikoAuthenticationException(msg) + + self.set_base_prompt() + self.disable_paging(command="terminal length 0") + # Clear the read buffer + time.sleep(0.3 * self.global_delay_factor) + self.clear_buffer() + + def set_base_prompt( + self, + pri_prompt_terminator: str = ">", + alt_prompt_terminator: str = "#", + delay_factor: float = 1.0, + pattern: Optional[str] = None, + ) -> str: + prompt = super().set_base_prompt( + pri_prompt_terminator=pri_prompt_terminator, + alt_prompt_terminator=alt_prompt_terminator, + delay_factor=delay_factor, + pattern=pattern, + ) + prompt = prompt.strip() + self.base_prompt = prompt + return self.base_prompt + + def check_config_mode( + self, + check_string: str = "(config)#", + pattern: str = "", + force_regex: bool = False, + ) -> bool: + return super().check_config_mode( + check_string=check_string, pattern=pattern, force_regex=force_regex + ) + + def config_mode( + self, config_command: str = "config", pattern: str = "", re_flags: int = 0 + ) -> str: + return super().config_mode( + config_command=config_command, pattern=pattern, re_flags=re_flags + ) + + def _build_ssh_client(self) -> SSHClient: + """Allows you to bypass standard SSH auth while still supporting SSH keys.""" + + # If user does not provide SSH key, we use noauth + remote_conn_pre: SSHClient + if not self.use_keys: + remote_conn_pre = SSHClient_noauth() + else: + remote_conn_pre = SSHClient() + + # Load host_keys for better SSH security + if self.system_host_keys: + remote_conn_pre.load_system_host_keys() + if self.alt_host_keys and path.isfile(self.alt_key_file): + remote_conn_pre.load_host_keys(self.alt_key_file) + + # Default is to automatically add untrusted hosts (make sure appropriate for your env) + remote_conn_pre.set_missing_host_key_policy(self.key_policy) + return remote_conn_pre + + def special_login_handler(self, delay_factor: float = 1.0) -> None: + """ + Fiberstore S3200 presents with the following on login + Username: + Password: **** + """ + delay_factor = self.select_delay_factor(delay_factor) + i = 0 + output = "" + while i <= 12: + i += 1 + time.sleep(delay_factor) + output = self.read_channel() + if output: + if "Username:" in output: + assert self.username is not None + self.write_channel(self.username + self.RETURN) + elif "Password:" in output: + assert self.password is not None + self.write_channel(self.password + self.RETURN) + return diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/fortinet/fortinet_ssh.py new/netmiko-4.3.0/netmiko/fortinet/fortinet_ssh.py --- old/netmiko-4.2.0/netmiko/fortinet/fortinet_ssh.py 2023-05-05 18:30:15.080753800 +0200 +++ new/netmiko-4.3.0/netmiko/fortinet/fortinet_ssh.py 2023-11-17 18:35:13.612568100 +0100 @@ -148,13 +148,7 @@ if self._vdoms: self._config_global() - self._send_command_str( - "config system console", expect_string=self.prompt_pattern - ) - output = self._send_command_str( - "show full-configuration", expect_string=self.prompt_pattern - ) - self._send_command_str("end", expect_string=self.prompt_pattern) + output = self._send_command_str("show full-configuration system console") if self._vdoms: self._exit_config_global() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/hp/hp_comware.py new/netmiko-4.3.0/netmiko/hp/hp_comware.py --- old/netmiko-4.2.0/netmiko/hp/hp_comware.py 2023-05-05 18:30:15.080753800 +0200 +++ new/netmiko-4.3.0/netmiko/hp/hp_comware.py 2023-11-17 18:35:13.612568100 +0100 @@ -22,7 +22,7 @@ self._test_channel_read(pattern=r"[>\]]") self.set_base_prompt() - command = self.RETURN + "screen-length disable" + command = "screen-length disable" self.disable_paging(command=command) def config_mode( @@ -98,6 +98,9 @@ pattern=pattern, ) + # Strip off any leading RBM_. characters for firewall HA + prompt = re.sub(r"^RBM_.", "", prompt, flags=re.M) + # Strip off leading character prompt = prompt[1:] prompt = prompt.strip() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/hp/hp_procurve.py new/netmiko-4.3.0/netmiko/hp/hp_procurve.py --- old/netmiko-4.2.0/netmiko/hp/hp_procurve.py 2023-05-05 18:30:15.080753800 +0200 +++ new/netmiko-4.3.0/netmiko/hp/hp_procurve.py 2023-11-17 18:35:13.612568100 +0100 @@ -59,7 +59,7 @@ # ProCurve requires elevated privileges to disable output paging :-( self.enable() self.set_terminal_width(command="terminal width 511", pattern="terminal") - command = self.RETURN + "no page" + command = "no page" self.disable_paging(command=command) def check_config_mode( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/huawei/huawei.py new/netmiko-4.3.0/netmiko/huawei/huawei.py --- old/netmiko-4.2.0/netmiko/huawei/huawei.py 2023-05-05 18:30:15.080753800 +0200 +++ new/netmiko-4.3.0/netmiko/huawei/huawei.py 2023-11-17 18:35:13.616568000 +0100 @@ -151,6 +151,16 @@ self.write_channel("N" + self.RETURN) self.read_until_pattern(pattern=self.prompt_pattern) + # Huawei prompts for secure the configuration before displaying the initial base prompt. + if re.search( + r"security\srisks\sin\sthe\sconfiguration\sfile.*\[y\/n\]", data, flags=re.I + ): + self.send_command("Y", expect_string=r"(?i)continue.*\[y\/n\]") + self.send_command( + "Y", expect_string=r"saved\ssuccessfully", read_timeout=60 + ) + self.read_until_pattern(pattern=self.prompt_pattern) + class HuaweiTelnet(HuaweiBase): """Huawei Telnet driver.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/maipu/__init__.py new/netmiko-4.3.0/netmiko/maipu/__init__.py --- old/netmiko-4.2.0/netmiko/maipu/__init__.py 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-4.3.0/netmiko/maipu/__init__.py 2023-11-17 18:35:13.616568000 +0100 @@ -0,0 +1,4 @@ +from netmiko.maipu.maipu import MaipuSSH +from netmiko.maipu.maipu import MaipuTelnet + +__all__ = ["MaipuSSH", "MaipuTelnet"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/maipu/maipu.py new/netmiko-4.3.0/netmiko/maipu/maipu.py --- old/netmiko-4.2.0/netmiko/maipu/maipu.py 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-4.3.0/netmiko/maipu/maipu.py 2023-11-17 18:35:13.616568000 +0100 @@ -0,0 +1,35 @@ +import time + +from netmiko.cisco_base_connection import CiscoBaseConnection + + +class MaipuBase(CiscoBaseConnection): + def session_preparation(self) -> None: + """Prepare the session after the connection has been established.""" + self._test_channel_read(pattern=r"[>#]") + self.set_base_prompt() + self.enable() + self.disable_paging(command="more off") + # Clear the read buffer + time.sleep(0.3 * self.global_delay_factor) + self.clear_buffer() + + def save_config( + self, cmd: str = "write", confirm: bool = False, confirm_response: str = "" + ) -> str: + """Saves Config Using Copy Run Start""" + return super().save_config( + cmd=cmd, confirm=confirm, confirm_response=confirm_response + ) + + +class MaipuSSH(MaipuBase): + """MAIPU SSH driver""" + + pass + + +class MaipuTelnet(MaipuBase): + """MAIPU telnet driver""" + + pass diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/mikrotik/mikrotik_ssh.py new/netmiko-4.3.0/netmiko/mikrotik/mikrotik_ssh.py --- old/netmiko-4.2.0/netmiko/mikrotik/mikrotik_ssh.py 2023-05-05 18:30:15.084753800 +0200 +++ new/netmiko-4.3.0/netmiko/mikrotik/mikrotik_ssh.py 2023-11-17 18:35:13.616568000 +0100 @@ -39,10 +39,10 @@ c: disable console colors e: enable dumb terminal mode t: disable auto detect terminal capabilities - w511: set term width - h4098: set term height + 511w: set term width + 4098h: set term height """ - self.username += "+ctw511h4098" + self.username += "+ct511w4098h" def disable_paging(self, *args: Any, **kwargs: Any) -> str: """Mikrotik does not have paging by default.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/nokia/nokia_sros.py new/netmiko-4.3.0/netmiko/nokia/nokia_sros.py --- old/netmiko-4.2.0/netmiko/nokia/nokia_sros.py 2023-05-05 18:30:15.084753800 +0200 +++ new/netmiko-4.3.0/netmiko/nokia/nokia_sros.py 2023-11-17 18:35:13.616568000 +0100 @@ -48,7 +48,6 @@ command="environment console width 512", pattern="environment" ) self.disable_paging(command="environment more false") - # To perform file operations we need to disable paging in classical-CLI also self.disable_paging(command="//environment no more") else: # Classical CLI has no method to set the terminal width nor to disable command @@ -56,6 +55,8 @@ # Only disabled if not set under the ConnectHandler. if self.global_cmd_verify is None: self.global_cmd_verify = False + # Disable paging in both modes, file operations require no paging in classic + self.disable_paging(command="//environment more false") self.disable_paging(command="environment no more", pattern="environment") # Clear the read buffer diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/scp_handler.py new/netmiko-4.3.0/netmiko/scp_handler.py --- old/netmiko-4.2.0/netmiko/scp_handler.py 2023-05-05 18:30:15.088753700 +0200 +++ new/netmiko-4.3.0/netmiko/scp_handler.py 2023-11-17 18:35:13.620568000 +0100 @@ -271,7 +271,8 @@ remote_out = "".join(remote_out_lines) # Match line containing file name assert isinstance(remote_file, str) - escape_file_name = re.escape(remote_file) + remote_file_base = os.path.basename(remote_file) + escape_file_name = re.escape(remote_file_base) pattern = r".*({}).*".format(escape_file_name) match = re.search(pattern, remote_out) if match: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/session_log.py new/netmiko-4.3.0/netmiko/session_log.py --- old/netmiko-4.2.0/netmiko/session_log.py 2023-05-05 18:30:15.088753700 +0200 +++ new/netmiko-4.3.0/netmiko/session_log.py 2023-11-17 18:35:13.620568000 +0100 @@ -12,6 +12,7 @@ file_encoding: str = "utf-8", no_log: Optional[Dict[str, Any]] = None, record_writes: bool = False, + slog_buffer: Optional[io.StringIO] = None, ) -> None: if no_log is None: self.no_log = {} @@ -30,6 +31,13 @@ else: self.session_log = None + # In order to ensure all the no_log entries get hidden properly, + # we must first store everying in memory and then write out to file. + # Otherwise, we might miss the data we are supposed to hide (since + # the no_log data potentially spans multiple reads). + if slog_buffer is None: + self.slog_buffer = io.StringIO() + # Ensures last write operations prior to disconnect are recorded. self.fin = False @@ -49,15 +57,30 @@ def close(self) -> None: """Close the session_log file (if it is a file that we opened).""" + self.flush() if self.session_log and self._session_log_close: self.session_log.close() self.session_log = None - def write(self, data: str) -> None: - if self.session_log is not None and len(data) > 0: - # Hide the password and secret in the session_log - for hidden_data in self.no_log.values(): - data = data.replace(hidden_data, "********") + def no_log_filter(self, data: str) -> str: + """Filter content from the session_log.""" + for hidden_data in self.no_log.values(): + data = data.replace(hidden_data, "********") + return data + + def _read_buffer(self) -> str: + self.slog_buffer.seek(0) + data = self.slog_buffer.read() + # Once read, create a new buffer + self.slog_buffer = io.StringIO() + return data + + def flush(self) -> None: + """Force the slog_buffer to be written out to the actual file""" + + if self.session_log is not None: + data = self._read_buffer() + data = self.no_log_filter(data) if isinstance(self.session_log, io.BufferedIOBase): self.session_log.write(write_bytes(data, encoding=self.file_encoding)) @@ -67,4 +90,10 @@ assert isinstance(self.session_log, io.BufferedIOBase) or isinstance( self.session_log, io.TextIOBase ) + + # Flush the underlying file self.session_log.flush() + + def write(self, data: str) -> None: + if len(data) > 0: + self.slog_buffer.write(data) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/snmp_autodetect.py new/netmiko-4.3.0/netmiko/snmp_autodetect.py --- old/netmiko-4.2.0/netmiko/snmp_autodetect.py 2023-05-05 18:30:15.088753700 +0200 +++ new/netmiko-4.3.0/netmiko/snmp_autodetect.py 2023-11-17 18:35:13.620568000 +0100 @@ -20,10 +20,10 @@ Note, pysnmp is a required dependency for SNMPDetect and is intentionally not included in netmiko requirements. So installation of pysnmp might be required. """ -import ipaddress -from typing import Optional, Dict +from typing import Optional, Dict, List from typing.re import Pattern import re +import socket try: from pysnmp.entity.rfc3413.oneliner import cmdgen @@ -40,6 +40,11 @@ "expr": re.compile(r".*Arista Networks EOS.*", re.IGNORECASE), "priority": 99, }, + "allied_telesis_awplus": { + "oid": ".1.3.6.1.2.1.1.1.0", + "expr": re.compile(r".*AlliedWare Plus.*", re.IGNORECASE), + "priority": 99, + }, "paloalto_panos": { "oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".*Palo Alto Networks.*", re.IGNORECASE), @@ -130,6 +135,51 @@ SNMP_MAPPER[device_type] = SNMP_MAPPER_BASE[device_type] +def identify_address_type(entry: str) -> List[str]: + """ + Return a list containing all ip types found. An empty list means no valid ip were found + Parameters + ---------- + entry: str + Can be an ipv4, an ipv6 or an FQDN. + + Returns + ------- + list of string: list + A list of string 'IPv4' | 'IPv6' which indicates if entry is a valid ipv4 and/or ipv6. + """ + try: + socket.inet_pton(socket.AF_INET, entry) + return ["IPv4"] + except socket.error: + pass + + try: + socket.inet_pton(socket.AF_INET6, entry) + return ["IPv6"] + except socket.error: + pass + + ip_types = [] + try: + addrinfo = socket.getaddrinfo(entry, None) + for info in addrinfo: + ip = info[4][0] + try: + socket.inet_pton(socket.AF_INET, ip) + ip_types.append("IPv4") + except socket.error: + pass + try: + socket.inet_pton(socket.AF_INET6, ip) + ip_types.append("IPv6") + except socket.error: + pass + except socket.gaierror: + pass + return ip_types + + class SNMPDetect(object): """ The SNMPDetect class tries to automatically determine the device type. @@ -197,7 +247,6 @@ auth_proto: str = "sha", encrypt_proto: str = "aes128", ) -> None: - # Check that the SNMP version is matching predefined type or raise ValueError if snmp_version == "v1" or snmp_version == "v2c": if not community: @@ -245,7 +294,7 @@ self._response_cache: Dict[str, str] = {} self.snmp_target = (self.hostname, self.snmp_port) - if ipaddress.ip_address(self.hostname).version == 6: + if "IPv6" in identify_address_type(self.hostname): self.udp_transport_target = cmdgen.Udp6TransportTarget( self.snmp_target, timeout=1.5, retries=2 ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/ssh_autodetect.py new/netmiko-4.3.0/netmiko/ssh_autodetect.py --- old/netmiko-4.2.0/netmiko/ssh_autodetect.py 2023-05-05 18:30:15.088753700 +0200 +++ new/netmiko-4.3.0/netmiko/ssh_autodetect.py 2023-11-17 18:35:13.620568000 +0100 @@ -64,27 +64,21 @@ "priority": 99, "dispatch": "_autodetect_std", }, - "apresia_aeos": { - "cmd": "show system", - "search_patterns": ["Apresia"], - "priority": 99, - "dispatch": "_autodetect_std", - }, - "arista_eos": { + "allied_telesis_awplus": { "cmd": "show version", - "search_patterns": [r"Arista"], + "search_patterns": ["AlliedWare Plus"], "priority": 99, "dispatch": "_autodetect_std", }, - "arris_cer": { - "cmd": "show version", - "search_patterns": [r"CER"], + "apresia_aeos": { + "cmd": "show system", + "search_patterns": ["Apresia"], "priority": 99, "dispatch": "_autodetect_std", }, - "casa_cmts": { + "arista_eos": { "cmd": "show version", - "search_patterns": [r"Casa"], + "search_patterns": [r"Arista", r"vEOS"], "priority": 99, "dispatch": "_autodetect_std", }, @@ -230,7 +224,9 @@ }, "extreme_slx": { "cmd": "show version", - "search_patterns": [r"SLX-OS Operating System Software"], + "search_patterns": [ + r"SLX-OS Operating System", + ], "priority": 99, "dispatch": "_autodetect_std", }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/ssh_dispatcher.py new/netmiko-4.3.0/netmiko/ssh_dispatcher.py --- old/netmiko-4.2.0/netmiko/ssh_dispatcher.py 2023-05-05 18:30:15.092754000 +0200 +++ new/netmiko-4.3.0/netmiko/ssh_dispatcher.py 2023-11-17 18:35:13.620568000 +0100 @@ -1,6 +1,7 @@ """Controls selection of proper class based on the device type.""" from typing import Any, Type, Optional from typing import TYPE_CHECKING +import re from netmiko.exceptions import ConnectionException from netmiko.exceptions import NetmikoTimeoutException, NetmikoAuthenticationException from netmiko.a10 import A10SSH @@ -56,6 +57,7 @@ from netmiko.dell import DellPowerConnectSSH from netmiko.dell import DellPowerConnectTelnet from netmiko.dell import DellIsilonSSH +from netmiko.digi import DigiTransportSSH from netmiko.dlink import DlinkDSTelnet, DlinkDSSSH from netmiko.eltex import EltexSSH, EltexEsrSSH from netmiko.endace import EndaceSSH @@ -77,8 +79,10 @@ from netmiko.extreme import ExtremeWingSSH from netmiko.f5 import F5TmshSSH from netmiko.f5 import F5LinuxSSH +from netmiko.fiberstore import FiberstoreFsosSSH from netmiko.flexvnf import FlexvnfSSH from netmiko.fortinet import FortinetSSH +from netmiko.hillstone import HillstoneStoneosSSH from netmiko.hp import HPProcurveSSH, HPProcurveTelnet, HPComwareSSH, HPComwareTelnet from netmiko.huawei import HuaweiSSH, HuaweiVrpv8SSH, HuaweiTelnet from netmiko.huawei import HuaweiSmartAXSSH @@ -87,6 +91,8 @@ from netmiko.juniper import JuniperFileTransfer from netmiko.keymile import KeymileSSH, KeymileNOSSSH from netmiko.linux import LinuxSSH, LinuxFileTransfer +from netmiko.maipu import MaipuSSH +from netmiko.maipu import MaipuTelnet from netmiko.mikrotik import MikrotikRouterOsSSH, MikrotikRouterOsFileTransfer from netmiko.mikrotik import MikrotikSwitchOsSSH from netmiko.mellanox import MellanoxMlnxosSSH @@ -131,7 +137,7 @@ from netmiko.supermicro import SmciSwitchSmisSSH from netmiko.supermicro import SmciSwitchSmisTelnet from netmiko.zyxel import ZyxelSSH -from netmiko.hillstone import HillstoneStoneosSSH + if TYPE_CHECKING: from netmiko.base_connection import BaseConnection @@ -196,6 +202,7 @@ "dell_powerconnect": DellPowerConnectSSH, "dell_isilon": DellIsilonSSH, "dlink_ds": DlinkDSSSH, + "digi_transport": DigiTransportSSH, "endace": EndaceSSH, "eltex": EltexSSH, "eltex_esr": EltexEsrSSH, @@ -216,6 +223,7 @@ "f5_ltm": F5TmshSSH, "f5_tmsh": F5TmshSSH, "f5_linux": F5LinuxSSH, + "fiberstore_fsos": FiberstoreFsosSSH, "flexvnf": FlexvnfSSH, "fortinet": FortinetSSH, "generic": GenericSSH, @@ -260,6 +268,7 @@ "supermicro_smis": SmciSwitchSmisSSH, "teldat_cit": TeldatCITSSH, "tplink_jetstream": TPLinkJetStreamSSH, + # ubiquiti_airos - Placeholder agreed to with NTC (if this driver is created in future) "ubiquiti_edge": UbiquitiEdgeSSH, "ubiquiti_edgerouter": UbiquitiEdgeRouterSSH, "ubiquiti_edgeswitch": UbiquitiEdgeSSH, @@ -270,6 +279,7 @@ "zte_zxros": ZteZxrosSSH, "yamaha": YamahaSSH, "zyxel_os": ZyxelSSH, + "maipu": MaipuSSH, } FILE_TRANSFER_MAP = { @@ -347,6 +357,7 @@ CLASS_MAPPER["tplink_jetstream_telnet"] = TPLinkJetStreamTelnet CLASS_MAPPER["yamaha_telnet"] = YamahaTelnet CLASS_MAPPER["zte_zxros_telnet"] = ZteZxrosTelnet +CLASS_MAPPER["maipu_telnet"] = MaipuTelnet # Add serial drivers CLASS_MAPPER["cisco_ios_serial"] = CiscoIosSerial @@ -388,6 +399,24 @@ return ConnectionClass(*args, **kwargs) +def TelnetFallback(*args: Any, **kwargs: Any) -> "BaseConnection": + """If an SSH connection fails, try to fallback to Telnet.""" + try: + return ConnectHandler(*args, **kwargs) + except (NetmikoTimeoutException, ConnectionRefusedError): + device_type = kwargs["device_type"] + # platforms_str is the base form (i.e. does not have the '_ssh' suffix) + if device_type in platforms_str: + alternative_device = f"{device_type}_telnet" + elif "_ssh" in device_type: + alternative_device = re.sub("_ssh", "_telnet", device_type) + + if alternative_device in platforms: + kwargs["device_type"] = alternative_device + return ConnectHandler(*args, **kwargs) + raise + + def ConnLogOnly( log_file: str = "netmiko.log", log_level: Optional[int] = None, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/telnet_proxy.py new/netmiko-4.3.0/netmiko/telnet_proxy.py --- old/netmiko-4.2.0/netmiko/telnet_proxy.py 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-4.3.0/netmiko/telnet_proxy.py 2023-11-17 18:35:13.624568200 +0100 @@ -0,0 +1,61 @@ +from typing import Dict, Any, Optional +import socket +import telnetlib + +try: + import socks + + SOCKS_SUPPORTED = True +except ModuleNotFoundError: + SOCKS_SUPPORTED = False + + +class Telnet(telnetlib.Telnet): + def __init__( + self, + host: Optional[str] = None, + port: int = 0, + timeout: float = socket._GLOBAL_DEFAULT_TIMEOUT, # type: ignore + proxy_dict: Optional[Dict[str, Any]] = None, + ) -> None: + self.proxy_dict = proxy_dict + super().__init__(host=host, port=port, timeout=timeout) + + def open( + self, + host: str, + port: int = 0, + timeout: float = socket._GLOBAL_DEFAULT_TIMEOUT, # type: ignore + ) -> None: + """ + Connect to a host. + The optional second argument is the port number, which + defaults to the standard telnet port (23). + + Don't try to reopen an already connected instance. + + proxy_dict = { + 'proxy_type': socks.SOCKS5, + 'proxy_addr': hostname, + 'proxy_port': port, + 'proxy_username': username, + 'proxy_password': password + } + """ + self.eof = 0 + if not port: + port = telnetlib.TELNET_PORT + self.host = host + self.port = port + self.timeout = timeout + + if SOCKS_SUPPORTED: + self.sock = socks.create_connection( + (host, port), timeout, **self.proxy_dict + ) + else: + msg = """ +In order to use the telnet socks proxy, you must 'pip install pysocks'. Note, pysocks is +unmaintained (so use at your own risk). +""" + raise ModuleNotFoundError(msg) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/tplink/tplink_jetstream.py new/netmiko-4.3.0/netmiko/tplink/tplink_jetstream.py --- old/netmiko-4.2.0/netmiko/tplink/tplink_jetstream.py 2023-05-05 18:30:15.092754000 +0200 +++ new/netmiko-4.3.0/netmiko/tplink/tplink_jetstream.py 2023-11-17 18:35:13.624568200 +0100 @@ -21,15 +21,12 @@ return super().__init__(**kwargs) def session_preparation(self) -> None: - """ - Prepare the session after the connection has been established. - """ delay_factor = self.select_delay_factor(delay_factor=0) time.sleep(0.3 * delay_factor) - self.clear_buffer() self._test_channel_read(pattern=r"[>#]") self.set_base_prompt() self.enable() + self.set_base_prompt() self.disable_paging() # Clear the read buffer time.sleep(0.3 * self.global_delay_factor) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/utilities.py new/netmiko-4.3.0/netmiko/utilities.py --- old/netmiko-4.2.0/netmiko/utilities.py 2022-08-09 23:16:34.350460500 +0200 +++ new/netmiko-4.3.0/netmiko/utilities.py 2023-11-17 18:35:13.624568200 +0100 @@ -499,7 +499,7 @@ ) raise ValueError(msg) - if "cisco" not in platform: + if "cisco" not in platform and "linux" not in platform: return raw_output genie_device_mapper = { @@ -508,6 +508,9 @@ "cisco_xr": "iosxr", "cisco_nxos": "nxos", "cisco_asa": "asa", + "linux": "linux", + "f5_linux": "linux", + "ovs_linux": "linux", } os = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/netmiko/vyos/vyos_ssh.py new/netmiko-4.3.0/netmiko/vyos/vyos_ssh.py --- old/netmiko-4.2.0/netmiko/vyos/vyos_ssh.py 2022-08-09 23:16:34.350460500 +0200 +++ new/netmiko-4.3.0/netmiko/vyos/vyos_ssh.py 2023-11-17 18:35:13.624568200 +0100 @@ -136,9 +136,11 @@ self, cmd: str = "save", confirm: bool = False, confirm_response: str = "" ) -> str: """Saves Config.""" - output = super().save_config( + output = self.config_mode() + output += super().save_config( cmd=cmd, confirm=confirm, confirm_response=confirm_response ) + output += self.exit_config_mode() if "Done" not in output: raise ValueError(f"Save failed with following errors:\n\n{output}") return output diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/pyproject.toml new/netmiko-4.3.0/pyproject.toml --- old/netmiko-4.2.0/pyproject.toml 2023-05-05 18:30:15.100753800 +0200 +++ new/netmiko-4.3.0/pyproject.toml 2023-11-17 18:35:13.628568200 +0100 @@ -4,7 +4,7 @@ [tool.poetry] name = "netmiko" -version = "4.2.0" +version = "4.3.0" description = "Multi-vendor library to simplify legacy CLI connections to network devices" authors = ["Kirk Byers <ktby...@twb-tech.com>"] license = "MIT" @@ -12,15 +12,15 @@ repository = "https://github.com/ktbyers/netmiko" classifiers = [ "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ] [tool.poetry.dependencies] -python = ">=3.7,<4.0" +python = ">=3.8,<4.0" paramiko = ">=2.9.5" scp = ">=0.13.6" pyyaml = ">=5.3" @@ -32,7 +32,7 @@ black = "22.3.0" mypy = "1.0.0" mypy-extensions = "1.0.0" -PyYAML = "5.4.1" +PyYAML = "6.0.1" pytest = "7.1.2" # Issue with build failure on pyflakes 2.5.0 pyflakes = "2.4.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netmiko-4.2.0/setup.py new/netmiko-4.3.0/setup.py --- old/netmiko-4.2.0/setup.py 1970-01-01 01:00:00.000000000 +0100 +++ new/netmiko-4.3.0/setup.py 1970-01-01 01:00:00.000000000 +0100 @@ -28,6 +28,7 @@ 'netmiko.cloudgenix', 'netmiko.coriant', 'netmiko.dell', + 'netmiko.digi', 'netmiko.dlink', 'netmiko.eltex', 'netmiko.endace', @@ -35,6 +36,7 @@ 'netmiko.ericsson', 'netmiko.extreme', 'netmiko.f5', + 'netmiko.fiberstore', 'netmiko.flexvnf', 'netmiko.fortinet', 'netmiko.hillstone', @@ -44,6 +46,7 @@ 'netmiko.juniper', 'netmiko.keymile', 'netmiko.linux', + 'netmiko.maipu', 'netmiko.mellanox', 'netmiko.mikrotik', 'netmiko.mrv', @@ -90,9 +93,9 @@ setup_kwargs = { 'name': 'netmiko', - 'version': '4.2.0', + 'version': '4.3.0', 'description': 'Multi-vendor library to simplify legacy CLI connections to network devices', - 'long_description': '[](https://img.shields.io/pypi/pyversions/netmiko)\n[](https://pypi.python.org/pypi/netmiko)\n[](https://pepy.tech/project/netmiko)\n[](https://GitHub.com/ktbyers/netmiko/graphs/contributors/)\n[](https://github.com/ambv/black)\n\n<img src="https://ktbyers.github.io/netmiko/images/netmiko_logo_gh.png" width="320">\n\nNetmiko\n=======\n\nMulti-vendor library to simplify CLI connections to network devices\n\n<br />\n\n## Why Netmiko?\nNetwork automation to screen-scraping devices is primarily concerned with gathering output from show commands and with making configuration changes.\n\nNetmiko aims to accomplish both of these operations a nd to do it across a very broad set of platforms. It seeks to do this while abstracting away low-level state control (i.e. eliminate low-level regex pattern matching to the extent practical).\n\n<br />\n\n## Getting Started\n- [Getting Started](#getting-started-1)\n\n<br />\n\n## Examples\n*You really should look here.*\n\n- [Netmiko Examples](https://github.com/ktbyers/netmiko/blob/develop/EXAMPLES.md)\n\n<br />\n\n\n## Supported Platforms\n\n[PLATFORMS](PLATFORMS.md)\n\n<br />\n\n\n## Installation\n\nTo install netmiko, simply us pip:\n\n```\n$ pip install netmiko\n```\n\n<br />\n\n## API-Documentation\n\n[API-Documentation](https://ktbyers.github.io/netmiko/docs/netmiko/index.html)\n\n<br />\n\n## Common Issues/FAQ\n\nAnswers to some [common questions](COMMON_ISSUES.md)\n\n<br />\n\n### Tutorials\n\n- [Netmiko Overview](https://pynet.twb-tech.com/blog/automation/netmiko.html)\n- [Secure Copy](https://pynet.twb-tech.com/blog/automation/netmiko-scp.html)\n- [Netmiko through SSH Pro xy](https://pynet.twb-tech.com/blog/automation/netmiko-proxy.html)\n- [Netmiko and TextFSM](https://pynet.twb-tech.com/blog/automation/netmiko-textfsm.html)\n- [Netmiko and what constitutes done](https://pynet.twb-tech.com/blog/automation/netmiko-what-is-done.html)\n\n<br />\n\n### Getting Started:\n\n#### Create a dictionary representing the device.\n\nSupported device_types can be found in [ssh_dispatcher.py](https://github.com/ktbyers/netmiko/blob/master/netmiko/ssh_dispatcher.py), see CLASS_MAPPER keys.\n```py\nfrom netmiko import ConnectHandler\n\ncisco_881 = {\n \'device_type\': \'cisco_ios\',\n \'host\': \'10.10.10.10\',\n \'username\': \'test\',\n \'password\': \'password\',\n \'port\' : 8022, # optional, defaults to 22\n \'secret\': \'secret\', # optional, defaults to \'\'\n}\n\n```\n\n#### Establish an SSH connection to the device by passing in the device dictionary.\n\n```py\nnet_connect = ConnectHandler(**cisco_881)\n```\n\n#### Execute s how commands.\n\n```py\noutput = net_connect.send_command(\'show ip int brief\')\nprint(output)\n```\n```\nInterface IP-Address OK? Method Status Protocol\nFastEthernet0 unassigned YES unset down down\nFastEthernet1 unassigned YES unset down down\nFastEthernet2 unassigned YES unset down down\nFastEthernet3 unassigned YES unset down down\nFastEthernet4 10.10.10.10 YES manual up up\nVlan1 unassigned YES unset down down\n```\n\n#### Execute configuration change commands (will automatically enter into config mode)\n\n```py\nconfig_commands = [ \'logging buffered 20000\',\n \'logging buffered 20010\',\n \'no logging console\' ]\noutput = net_connect.send_config_set(config_commands)\nprint(outpu t)\n```\n```\npynet-rtr1#config term\nEnter configuration commands, one per line. End with CNTL/Z.\npynet-rtr1(config)#logging buffered 20000\npynet-rtr1(config)#logging buffered 20010\npynet-rtr1(config)#no logging console\npynet-rtr1(config)#end\npynet-rtr1#\n```\n\n<br />\n\n## API-Documentation\n\n<a href="https://ktbyers.github.io/netmiko/docs/netmiko/index.html" title="Docs">API Documentation</a>\n\nBelow are some of the particularly handy Classes/functions for easy reference:\n- [Base Connection Object](https://ktbyers.github.io/netmiko/docs/netmiko/base_connection.html)\n- [SSH Autodetect](https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.SSHDetect)\n- [SSH Dispatcher](https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.ssh_dispatcher)\n- [Redispatch](https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.redispatch)\n\n<br />\n\n## Contributing\n\nContributors are welcome.\n\nYou can contribute to Netmiko in a variety of ways: answe ring questions on Slack (see below in Questions/Discussions), responding to issues, adding to the common issues, reporting/fixing bugs, or even adding your own device type.\n\nBefore contributing a new vendor/platform device type, remember that any code added needs to be supported in some fashion. To add a vendor/platform you can follow the outline [here](VENDOR.md). Once you\'ve worked on your first pass of your driver and have it functional, you\'ll need to include test data in order for it to be merged into develop, you can see the general flow of how to do that [here](TESTING.md).\n\nFor all code contributions, please ensure that you have ran `black` against the code or your code will fail the Travis CI build.\n\n<br />\n\n## Questions/Discussion\n\nIf you find an issue with Netmiko, then you can open an issue on this projects issue page here: [https://github.com/ktbyers/netmiko/issues](https://github.com/ktbyers/netmiko/issues). Please make sure you\'ve read through the common issues and examples prior to opening an issue. Please only open issues for bugs, feature requests, or other topics related to development of Netmiko. If you simply have a question, join us on Slack...\n\nIf you have questions or would like to discuss Netmiko, a #netmiko channel exists in [this Slack](https://pynet.slack.com) workspace. To join, use [this invitation](https://join.slack.com/t/pynet/shared_invite/zt-km2k3upf-AkWHY4YEx3sI1R5irMmc7Q). Once you have entered the workspace, then you can join the #netmiko channel.\n\n\n---\nKirk Byers \nPython for Network Engineers \nhttps://pynet.twb-tech.com \n', + 'long_description': '[](https://img.shields.io/pypi/pyversions/netmiko)\n[](https://pypi.python.org/pypi/netmiko)\n[](https://static.pepy.tech/badge/netmiko)\n[](https://GitHub.com/ktbyers/netmiko/graphs/contributors/)\n[](https://github.com/ambv/black)\n\n<img src="https://ktbyers.github.io/netmiko/images/netmiko_logo_gh.png" width="320">\n\nNetmiko\n=======\n\nMulti-vendor library to simplify CLI connections to network devices\n\n<br />\n\n## Contributing to Netmiko\n\n[CONTRIBUTING.md](https://github.com/ktbyers/netmiko/blob/develop/CONTRIBUTING.md)\n\n<br />\n\n## Why Netmiko?\nNetwork automation to screen-scraping devices is primarily concerne d with gathering output from show commands and with making configuration changes.\n\nNetmiko aims to accomplish both of these operations and to do it across a very broad set of platforms. It seeks to do this while abstracting away low-level state control (i.e. eliminate low-level regex pattern matching to the extent practical).\n\n<br />\n\n## Getting Started\n- [Getting Started](#getting-started-1)\n\n<br />\n\n## Examples\n*You really should look here.*\n\n- [Netmiko Examples](https://github.com/ktbyers/netmiko/blob/develop/EXAMPLES.md)\n\n<br />\n\n\n## Supported Platforms\n\n[PLATFORMS](PLATFORMS.md)\n\n<br />\n\n\n## Installation\n\nTo install netmiko, simply us pip:\n\n```\n$ pip install netmiko\n```\n\n<br />\n\n## API-Documentation\n\n[API-Documentation](https://ktbyers.github.io/netmiko/docs/netmiko/index.html)\n\n<br />\n\n## Common Issues/FAQ\n\nAnswers to some [common questions](COMMON_ISSUES.md)\n\n<br />\n\n### Tutorials\n\n- [Netmiko Overview](https://pynet.twb-tech.c om/blog/automation/netmiko.html)\n- [Secure Copy](https://pynet.twb-tech.com/blog/automation/netmiko-scp.html)\n- [Netmiko through SSH Proxy](https://pynet.twb-tech.com/blog/automation/netmiko-proxy.html)\n- [Netmiko and TextFSM](https://pynet.twb-tech.com/blog/automation/netmiko-textfsm.html)\n- [Netmiko and what constitutes done](https://pynet.twb-tech.com/blog/automation/netmiko-what-is-done.html)\n\n<br />\n\n### Getting Started:\n\n#### Create a dictionary representing the device.\n\nSupported device_types can be found in [ssh_dispatcher.py](https://github.com/ktbyers/netmiko/blob/master/netmiko/ssh_dispatcher.py), see CLASS_MAPPER keys.\n```py\nfrom netmiko import ConnectHandler\n\ncisco_881 = {\n \'device_type\': \'cisco_ios\',\n \'host\': \'10.10.10.10\',\n \'username\': \'test\',\n \'password\': \'password\',\n \'port\' : 8022, # optional, defaults to 22\n \'secret\': \'secret\', # optional, defaults to \'\'\n}\n\n```\n\n#### Establish an SS H connection to the device by passing in the device dictionary.\n\n```py\nnet_connect = ConnectHandler(**cisco_881)\n```\n\n#### Execute show commands.\n\n```py\noutput = net_connect.send_command(\'show ip int brief\')\nprint(output)\n```\n```\nInterface IP-Address OK? Method Status Protocol\nFastEthernet0 unassigned YES unset down down\nFastEthernet1 unassigned YES unset down down\nFastEthernet2 unassigned YES unset down down\nFastEthernet3 unassigned YES unset down down\nFastEthernet4 10.10.10.10 YES manual up up\nVlan1 unassigned YES unset down down\n```\n\n#### Execute configuration change commands (will automatically enter into config mode)\n\n```py\nconfig_commands = [ \'logging buffered 20000\',\n \'l ogging buffered 20010\',\n \'no logging console\' ]\noutput = net_connect.send_config_set(config_commands)\nprint(output)\n```\n```\npynet-rtr1#config term\nEnter configuration commands, one per line. End with CNTL/Z.\npynet-rtr1(config)#logging buffered 20000\npynet-rtr1(config)#logging buffered 20010\npynet-rtr1(config)#no logging console\npynet-rtr1(config)#end\npynet-rtr1#\n```\n\n<br />\n\n## API-Documentation\n\n<a href="https://ktbyers.github.io/netmiko/docs/netmiko/index.html" title="Docs">API Documentation</a>\n\nBelow are some of the particularly handy Classes/functions for easy reference:\n- [Base Connection Object](https://ktbyers.github.io/netmiko/docs/netmiko/base_connection.html)\n- [SSH Autodetect](https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.SSHDetect)\n- [SSH Dispatcher](https://ktbyers.github.io/netmiko/docs/netmiko/index.html#netmiko.ssh_dispatcher)\n- [Redispatch](https://ktbyers.github.io/netmiko/docs/netmiko/index.html# netmiko.redispatch)\n\n<br />\n\n## Contributing\n\nContributors are welcome.\n\nYou can contribute to Netmiko in a variety of ways: answering questions on Slack (see below in Questions/Discussions), responding to issues, adding to the common issues, reporting/fixing bugs, or even adding your own device type.\n\nBefore contributing a new vendor/platform device type, remember that any code added needs to be supported in some fashion. To add a vendor/platform you can follow the outline [here](VENDOR.md). Once you\'ve worked on your first pass of your driver and have it functional, you\'ll need to include test data in order for it to be merged into develop, you can see the general flow of how to do that [here](TESTING.md).\n\nFor all code contributions, please ensure that you have ran `black` against the code or your code will fail the Travis CI build.\n\n<br />\n\n## Questions/Discussion\n\nIf you find an issue with Netmiko, then you can open an issue on this projects issue page here: [https://github.com/ktbyers/netmiko/issues](https://github.com/ktbyers/netmiko/issues). Please make sure you\'ve read through the common issues and examples prior to opening an issue. Please only open issues for bugs, feature requests, or other topics related to development of Netmiko. If you simply have a question, join us on Slack...\n\nIf you have questions or would like to discuss Netmiko, a #netmiko channel exists in [this Slack](https://pynet.slack.com) workspace. To join, use [this invitation](https://join.slack.com/t/pynet/shared_invite/zt-km2k3upf-AkWHY4YEx3sI1R5irMmc7Q). Once you have entered the workspace, then you can join the #netmiko channel.\n\n\n---\nKirk Byers \nPython for Network Engineers \nhttps://pynet.twb-tech.com \n', 'author': 'Kirk Byers', 'author_email': 'ktby...@twb-tech.com', 'maintainer': 'None', @@ -102,7 +105,7 @@ 'package_data': package_data, 'install_requires': install_requires, 'entry_points': entry_points, - 'python_requires': '>=3.7,<4.0', + 'python_requires': '>=3.8,<4.0', }