diff --git a/README.md b/README.md index eda987b9e..cddc92d5b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![PyPI](https://img.shields.io/pypi/v/netmiko.svg)](https://pypi.python.org/pypi/netmiko) - - + + Netmiko ======= @@ -10,70 +10,75 @@ Python 2.7, 3.5, 3.6 #### Requires: -Paramiko >= 2 -scp >= 0.10.0 -pyyaml -pyserial -textfsm +- Paramiko >= 2.4.1 +- scp >= 0.10.0 +- pyyaml +- pyserial +- textfsm #### Supports: ###### Regularly tested -Arista vEOS -Cisco ASA -Cisco IOS -Cisco IOS-XE -Cisco IOS-XR -Cisco NX-OS -Cisco SG300 -HP Comware7 -HP ProCurve -Juniper Junos -Linux - +- Arista vEOS +- Cisco ASA +- Cisco IOS +- Cisco IOS-XE +- Cisco IOS-XR +- Cisco NX-OS +- Cisco SG300 +- HP Comware7 +- HP ProCurve +- Juniper Junos +- Linux + ###### Limited testing -Alcatel AOS6/AOS8 -Apresia Systems AEOS -Avaya ERS -Avaya VSP -Brocade VDX -Brocade MLX/NetIron -Calix B6 -Cisco WLC -Dell OS10 -Dell-Force10 -Dell PowerConnect -Huawei -Mellanox -NetApp cDOT -Palo Alto PAN-OS -Pluribus -Ruckus ICX/FastIron -Ubiquiti EdgeSwitch -Vyatta VyOS +- Alcatel AOS6/AOS8 +- Apresia Systems AEOS +- Calix B6 +- Cisco WLC +- Dell OS9 (Force10) +- Dell OS10 +- Dell PowerConnect +- Extreme ERS (Avaya) +- Extreme VSP (Avaya) +- Extreme VDX (Brocade) +- Extreme MLX/NetIron (Brocade/Foundry) +- Huawei +- IP Infusion OcNOS +- Mellanox +- NetApp cDOT +- Palo Alto PAN-OS +- Pluribus +- Ruckus ICX/FastIron +- Ubiquiti EdgeSwitch +- Vyatta VyOS ###### Experimental -A10 -Accedian -Aruba -Ciena SAOS -Citrix Netscaler -Cisco Telepresence -Check Point GAiA -Coriant -Dell EMC Isilon -Eltex -Enterasys -Extreme EXOS -Extreme Wing -F5 LTM -Fortinet -MRV Communications OptiSwitch -Nokia/Alcatel SR-OS -QuantaMesh +- A10 +- Accedian +- Aruba +- Ciena SAOS +- Citrix Netscaler +- Cisco Telepresence +- Check Point GAiA +- Coriant +- Dell OS6 +- Dell EMC Isilon +- Eltex +- Enterasys +- Extreme EXOS +- Extreme Wing +- Extreme SLX (Brocade) +- F5 TMSH +- F5 Linux +- Fortinet +- MRV Communications OptiSwitch +- Nokia/Alcatel SR-OS +- QuantaMesh +- Rad ETX ## Tutorials: @@ -131,12 +136,12 @@ print(output) ``` ``` Interface IP-Address OK? Method Status Protocol -FastEthernet0 unassigned YES unset down down -FastEthernet1 unassigned YES unset down down -FastEthernet2 unassigned YES unset down down -FastEthernet3 unassigned YES unset down down -FastEthernet4 10.10.10.10 YES manual up up -Vlan1 unassigned YES unset down down +FastEthernet0 unassigned YES unset down down +FastEthernet1 unassigned YES unset down down +FastEthernet2 unassigned YES unset down down +FastEthernet3 unassigned YES unset down down +FastEthernet4 10.10.10.10 YES manual up up +Vlan1 unassigned YES unset down down ``` #### Execute configuration change commands (will automatically enter into config mode) @@ -158,6 +163,16 @@ pynet-rtr1(config)#end pynet-rtr1# ``` +## TextFSM Integration + +Netmiko has been configured to automatically look in `~/ntc-template/templates/index` for the ntc-templates index file. Alternatively, you can explicitly tell Netmiko where to look for the TextFSM template directory by setting the `NET_TEXTFSM` environment variable (note, there must be an index file in this directory): + +``` +export NET_TEXTFSM=/path/to/ntc-templates/templates/ +``` + +[More info on TextFSM and Netmiko](https://pynet.twb-tech.com/blog/automation/netmiko-textfsm.html). + ## Questions/Discussion If 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) @@ -167,7 +182,7 @@ If you have questions or would like to discuss Netmiko, a Netmiko channel exists ---- +--- Kirk Byers Python for Network Engineers https://pynet.twb-tech.com diff --git a/docs/build/html/classes/base_connection.html b/docs/build/html/classes/base_connection.html index 4f2ff9a05..ed5a82338 100644 --- a/docs/build/html/classes/base_connection.html +++ b/docs/build/html/classes/base_connection.html @@ -434,7 +434,7 @@

BaseConnection diff --git a/netmiko/__init__.py b/netmiko/__init__.py index 8dcd39a0a..ec0c9a3d5 100644 --- a/netmiko/__init__.py +++ b/netmiko/__init__.py @@ -23,7 +23,7 @@ NetmikoAuthError = NetMikoAuthenticationException Netmiko = ConnectHandler -__version__ = '2.2.2' +__version__ = '2.3.0' __all__ = ('ConnectHandler', 'ssh_dispatcher', 'platforms', 'SCPConn', 'FileTransfer', 'NetMikoTimeoutException', 'NetMikoAuthenticationException', 'NetmikoTimeoutError', 'NetmikoAuthError', 'InLineTransfer', 'redispatch', diff --git a/netmiko/alcatel/alcatel_sros_ssh.py b/netmiko/alcatel/alcatel_sros_ssh.py index 21ec9cf0b..192bb3b5b 100644 --- a/netmiko/alcatel/alcatel_sros_ssh.py +++ b/netmiko/alcatel/alcatel_sros_ssh.py @@ -25,7 +25,33 @@ def set_base_prompt(self, *args, **kwargs): self.base_prompt = match.group(1) return self.base_prompt - def enable(self, *args, **kwargs): + def enable(self, cmd='enable-admin', pattern='ssword', re_flags=re.IGNORECASE): + """Enter enable mode.""" + return super(AlcatelSrosSSH, self).enable(cmd=cmd, pattern=pattern, re_flags=re_flags) + + def check_enable_mode(self, check_string='CLI Already in admin mode'): + """Check whether we are in enable-admin mode. + SROS requires us to do this: + *A:HOSTNAME# enable-admin + MINOR: CLI Already in admin mode. + *A:HOSTNAME# + *A:HOSTNAME# enable-admin + Password: + MINOR: CLI Invalid password. + *A:HOSTNAME# + """ + output = self.send_command_timing('enable-admin') + if re.search(r"ssword", output): + # Just hit enter as we don't actually want to enter enable here + self.write_channel(self.normalize_cmd(self.RETURN)) + self.read_until_prompt() + return False + elif check_string in output: + return True + raise ValueError("Unexpected response in check_enable_mode() method") + + def exit_enable_mode(self, exit_command=''): + """No corresponding exit of enable mode on SROS.""" pass def config_mode(self, config_command='configure', pattern='#'): diff --git a/netmiko/apresia/apresia_aeos.py b/netmiko/apresia/apresia_aeos.py index e310218ad..2f1dfc900 100644 --- a/netmiko/apresia/apresia_aeos.py +++ b/netmiko/apresia/apresia_aeos.py @@ -19,7 +19,7 @@ def disable_paging(self, command="", delay_factor=1): check_command = "show running-config | include terminal length 0" output = self.send_command(check_command) - if "terminal length 0" not in output: + if self.allow_auto_change and "terminal length 0" not in output: self.send_config_set("terminal length 0") self.exit_enable_mode() diff --git a/netmiko/aruba/aruba_ssh.py b/netmiko/aruba/aruba_ssh.py index 98e929b29..b5ecf7f81 100644 --- a/netmiko/aruba/aruba_ssh.py +++ b/netmiko/aruba/aruba_ssh.py @@ -7,6 +7,11 @@ class ArubaSSH(CiscoSSHConnection): """Aruba OS support""" + def __init__(self, **kwargs): + if kwargs.get('default_enter') is None: + kwargs['default_enter'] = '\r' + return super(ArubaSSH, self).__init__(**kwargs) + def session_preparation(self): """Aruba OS requires enable mode to disable paging.""" delay_factor = self.select_delay_factor(delay_factor=0) diff --git a/netmiko/avaya/__init__.py b/netmiko/avaya/__init__.py deleted file mode 100644 index 54bd407a9..000000000 --- a/netmiko/avaya/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import unicode_literals -from netmiko.avaya.avaya_vsp_ssh import AvayaVspSSH -from netmiko.avaya.avaya_ers_ssh import AvayaErsSSH - -__all__ = ['AvayaVspSSH', 'AvayaErsSSH'] diff --git a/netmiko/base_connection.py b/netmiko/base_connection.py index 975e7ada3..c7e5a3334 100644 --- a/netmiko/base_connection.py +++ b/netmiko/base_connection.py @@ -23,7 +23,7 @@ from netmiko import log from netmiko.netmiko_globals import MAX_BUFFER, BACKSPACE_CHAR -from netmiko.py23_compat import string_types, bufferedio_types +from netmiko.py23_compat import string_types, bufferedio_types, text_type from netmiko.ssh_exception import NetMikoTimeoutException, NetMikoAuthenticationException from netmiko.utilities import write_bytes, check_serial_port, get_structured_data @@ -36,11 +36,13 @@ class BaseConnection(object): """ def __init__(self, ip='', host='', username='', password='', secret='', port=None, device_type='', verbose=False, global_delay_factor=1, use_keys=False, - key_file=None, allow_agent=False, ssh_strict=False, system_host_keys=False, + key_file=None, pkey=None, passphrase=None, allow_agent=False, + ssh_strict=False, system_host_keys=False, alt_host_keys=False, alt_key_file='', ssh_config_file=None, timeout=100, - session_timeout=60, blocking_timeout=8, keepalive=0, default_enter=None, - response_return=None, serial_settings=None, fast_cli=False, session_log=None, - session_log_record_writes=False, session_log_file_mode='write'): + session_timeout=60, auth_timeout=None, blocking_timeout=8, keepalive=0, + default_enter=None, response_return=None, serial_settings=None, fast_cli=False, + session_log=None, session_log_record_writes=False, session_log_file_mode='write', + allow_auto_change=False, encoding='ascii'): """ Initialize attributes for establishing connection to target device. @@ -82,6 +84,13 @@ def __init__(self, ip='', host='', username='', password='', secret='', port=Non :param key_file: Filename path of the SSH key file to use. :type key_file: str + :param pkey: SSH key object to use. + :type pkey: paramiko.PKey + + :param passphrase: Passphrase to use for encrypted key; password will be used for key + decryption if not specified. + :type passphrase: str + :param allow_agent: Enable use of SSH key-agent. :type allow_agent: bool @@ -107,6 +116,9 @@ def __init__(self, ip='', host='', username='', password='', secret='', port=Non :param session_timeout: Set a timeout for parallel requests. :type session_timeout: float + :param auth_timeout: Set a timeout (in seconds) to wait for an authentication response. + :type auth_timeout: float + :param keepalive: Send SSH keepalive packets at a specific interval, in seconds. Currently defaults to 0, for backwards compatibility (it will not attempt to keep the connection alive). @@ -135,6 +147,14 @@ def __init__(self, ip='', host='', username='', password='', secret='', port=Non :param session_log_file_mode: "write" or "append" for session_log file mode (default: "write") :type session_log_file_mode: str + + :param allow_auto_change: Allow automatic configuration changes for terminal settings. + (default: False) + :type allow_auto_change: bool + + :param encoding: Encoding to be used when writing bytes to the output channel. + (default: 'ascii') + :type encoding: str """ self.remote_conn = None self.RETURN = '\n' if default_enter is None else default_enter @@ -162,9 +182,12 @@ def __init__(self, ip='', host='', username='', password='', secret='', port=Non self.ansi_escape_codes = False self.verbose = verbose self.timeout = timeout + self.auth_timeout = auth_timeout self.session_timeout = session_timeout self.blocking_timeout = blocking_timeout self.keepalive = keepalive + self.allow_auto_change = allow_auto_change + self.encoding = encoding # Netmiko will close the session_log if we open the file self.session_log = None @@ -216,12 +239,12 @@ def __init__(self, ip='', host='', username='', password='', secret='', port=Non self.protocol = 'telnet' self._modify_connection_params() self.establish_connection() - self.session_preparation() + self._try_session_preparation() elif '_serial' in device_type: self.protocol = 'serial' self._modify_connection_params() self.establish_connection() - self.session_preparation() + self._try_session_preparation() else: self.protocol = 'ssh' @@ -233,6 +256,8 @@ def __init__(self, ip='', host='', username='', password='', secret='', port=Non # Options for SSH host_keys self.use_keys = use_keys self.key_file = key_file + self.pkey = pkey + self.passphrase = passphrase self.allow_agent = allow_agent self.system_host_keys = system_host_keys self.alt_host_keys = alt_host_keys @@ -243,7 +268,7 @@ def __init__(self, ip='', host='', username='', password='', secret='', port=Non self._modify_connection_params() self.establish_connection() - self.session_preparation() + self._try_session_preparation() def __enter__(self): """Establish a session using a Context Manager.""" @@ -303,16 +328,16 @@ def _write_channel(self, out_data): :type out_data: str (can be either unicode/byte string) """ if self.protocol == 'ssh': - self.remote_conn.sendall(write_bytes(out_data)) + self.remote_conn.sendall(write_bytes(out_data, encoding=self.encoding)) elif self.protocol == 'telnet': - self.remote_conn.write(write_bytes(out_data)) + self.remote_conn.write(write_bytes(out_data, encoding=self.encoding)) elif self.protocol == 'serial': - self.remote_conn.write(write_bytes(out_data)) + self.remote_conn.write(write_bytes(out_data, encoding=self.encoding)) self.remote_conn.flush() else: raise ValueError("Invalid protocol specified") try: - log.debug("write_channel: {}".format(write_bytes(out_data))) + log.debug("write_channel: {}".format(write_bytes(out_data, encoding=self.encoding))) if self._session_log_fin or self.session_log_record_writes: self._write_session_log(out_data) except UnicodeDecodeError: @@ -321,7 +346,7 @@ def _write_channel(self, out_data): def _write_session_log(self, data): if self.session_log is not None and len(data) > 0: - self.session_log.write(write_bytes(data)) + self.session_log.write(write_bytes(data, encoding=self.encoding)) self.session_log.flush() def write_channel(self, out_data): @@ -345,10 +370,13 @@ def is_alive(self): return False if self.protocol == 'telnet': try: - # Try sending IAC + NOP (IAC is telnet way of sending command - # IAC = Interpret as Command (it comes before the NOP) + # Try sending IAC + NOP (IAC is telnet way of sending command) + # IAC = Interpret as Command; it comes before the NOP. log.debug("Sending IAC + NOP") - self.write_channel(telnetlib.IAC + telnetlib.NOP) + # Need to send multiple times to test connection + self.remote_conn.sock.sendall(telnetlib.IAC + telnetlib.NOP) + self.remote_conn.sock.sendall(telnetlib.IAC + telnetlib.NOP) + self.remote_conn.sock.sendall(telnetlib.IAC + telnetlib.NOP) return True except AttributeError: return False @@ -611,6 +639,19 @@ def telnet_login(self, pri_prompt_terminator=r'#\s*$', alt_prompt_terminator=r'> self.remote_conn.close() raise NetMikoAuthenticationException(msg) + def _try_session_preparation(self): + """ + In case of an exception happening during `session_preparation()` Netmiko should + gracefully clean-up after itself. This might be challenging for library users + to do since they don't have a reference to the object. This is possibly related + to threads used in Paramiko. + """ + try: + self.session_preparation() + except Exception: + self.disconnect() + raise + def session_preparation(self): """ Prepare the session after the connection has been established @@ -681,7 +722,10 @@ def _connect_params_dict(self): 'look_for_keys': self.use_keys, 'allow_agent': self.allow_agent, 'key_filename': self.key_file, + 'pkey': self.pkey, + 'passphrase': self.passphrase, 'timeout': self.timeout, + 'auth_timeout': self.auth_timeout } # Check if using SSH 'config' file mainly for SSH proxy support @@ -745,7 +789,7 @@ def establish_connection(self, width=None, height=None): self.paramiko_cleanup() msg = "Authentication failure: unable to connect {device_type} {ip}:{port}".format( device_type=self.device_type, ip=self.host, port=self.port) - msg += self.RETURN + str(auth_err) + msg += self.RETURN + text_type(auth_err) raise NetMikoAuthenticationException(msg) if self.verbose: @@ -841,7 +885,7 @@ def select_delay_factor(self, delay_factor): return self.global_delay_factor def special_login_handler(self, delay_factor=1): - """Handler for devices like WLC, Avaya ERS that throw up characters prior to login.""" + """Handler for devices like WLC, Extreme ERS that throw up characters prior to login.""" pass def disable_paging(self, command="terminal length 0", delay_factor=1): @@ -1016,6 +1060,33 @@ def strip_prompt(self, a_string): else: return a_string + def _first_line_handler(self, data, search_pattern): + """ + In certain situations the first line will get repainted which causes a false + match on the terminating pattern. + + Filter this out. + + returns a tuple of (data, first_line_processed) + + Where data is the original data potentially with the first line modified + and the first_line_processed is a flag indicating that we have handled the + first line. + """ + try: + # First line is the echo line containing the command. In certain situations + # it gets repainted and needs filtered + lines = data.split(self.RETURN) + first_line = lines[0] + if BACKSPACE_CHAR in first_line: + pattern = search_pattern + r'.*$' + first_line = re.sub(pattern, repl='', string=first_line) + lines[0] = first_line + data = self.RETURN.join(lines) + return (data, True) + except IndexError: + return (data, False) + def send_command(self, command_string, expect_string=None, delay_factor=1, max_loops=500, auto_find_prompt=True, strip_prompt=True, strip_command=True, normalize=True, @@ -1083,27 +1154,32 @@ def send_command(self, command_string, expect_string=None, i = 1 output = '' + first_line_processed = False + # Keep reading data until search_pattern is found or until max_loops is reached. while i <= max_loops: new_data = self.read_channel() if new_data: if self.ansi_escape_codes: new_data = self.strip_ansi_escape_codes(new_data) - output += new_data - try: - lines = output.split(self.RETURN) - first_line = lines[0] - # First line is the echo line containing the command. In certain situations - # it gets repainted and needs filtered - if BACKSPACE_CHAR in first_line: - pattern = search_pattern + r'.*$' - first_line = re.sub(pattern, repl='', string=first_line) - lines[0] = first_line - output = self.RETURN.join(lines) - except IndexError: - pass - if re.search(search_pattern, output): - break + + # Case where we haven't processed the first_line yet (there is a potential issue + # in the first line (in cases where the line is repainted). + if not first_line_processed: + output += new_data + output, first_line_processed = self._first_line_handler( + output, + search_pattern + ) + # Check if we have already found our pattern + if re.search(search_pattern, output): + break + + else: + output += new_data + # Check if pattern is in the incremental data + if re.search(search_pattern, new_data): + break time.sleep(delay_factor * loop_delay) i += 1 @@ -1391,7 +1467,7 @@ def strip_ansi_escape_codes(self, string_buffer): ESC[\d\d;\d\dm and ESC[\d\d;\d\d;\d\dm ESC[6n Get cursor position - HP ProCurve's, Cisco SG300, and F5 LTM's require this (possible others) + HP ProCurve and Cisco SG300 require this (possible others). :param string_buffer: The string to be processed to remove ANSI escape codes :type string_buffer: str diff --git a/netmiko/brocade/__init__.py b/netmiko/brocade/__init__.py deleted file mode 100644 index 6b3eb01ec..000000000 --- a/netmiko/brocade/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import unicode_literals -from netmiko.brocade.brocade_nos_ssh import BrocadeNosSSH -from netmiko.brocade.brocade_netiron import BrocadeNetironSSH -from netmiko.brocade.brocade_netiron import BrocadeNetironTelnet - -__all__ = ['BrocadeNosSSH', 'BrocadeNetironSSH', 'BrocadeNetironTelnet'] diff --git a/netmiko/cisco/cisco_asa_ssh.py b/netmiko/cisco/cisco_asa_ssh.py index ce46bf6c2..6067a76ff 100644 --- a/netmiko/cisco/cisco_asa_ssh.py +++ b/netmiko/cisco/cisco_asa_ssh.py @@ -17,7 +17,13 @@ def session_preparation(self): else: self.asa_login() self.disable_paging(command="terminal pager 0") - self.set_terminal_width(command="terminal width 511") + if self.allow_auto_change: + try: + self.send_config_set("terminal width 511") + except ValueError: + # Don't fail for the terminal width + pass + # Clear the read buffer time.sleep(.3 * self.global_delay_factor) self.clear_buffer() diff --git a/netmiko/cisco/cisco_xr_ssh.py b/netmiko/cisco/cisco_xr_ssh.py index 7ada09150..12c79ce33 100644 --- a/netmiko/cisco/cisco_xr_ssh.py +++ b/netmiko/cisco/cisco_xr_ssh.py @@ -3,6 +3,7 @@ import time import re from netmiko.cisco_base_connection import CiscoSSHConnection, CiscoFileTransfer +from netmiko.py23_compat import text_type class CiscoXrSSH(CiscoSSHConnection): @@ -70,22 +71,23 @@ def commit(self, confirm=False, confirm_delay=None, comment='', label='', delay_ raise ValueError("Invalid comment contains double quote") comment = '"{0}"'.format(comment) - label = str(label) + label = text_type(label) error_marker = 'Failed to' alt_error_marker = 'One or more commits have occurred from other' # Select proper command string based on arguments provided if label: if comment: - command_string = 'commit label {0} comment {1}'.format(label, comment) + command_string = 'commit label {} comment {}'.format(label, comment) elif confirm: - command_string = 'commit label {0} confirmed {1}'.format(label, str(confirm_delay)) + command_string = 'commit label {} confirmed {}'.format(label, + text_type(confirm_delay)) else: - command_string = 'commit label {0}'.format(label) + command_string = 'commit label {}'.format(label) elif confirm: - command_string = 'commit confirmed {0}'.format(str(confirm_delay)) + command_string = 'commit confirmed {}'.format(text_type(confirm_delay)) elif comment: - command_string = 'commit comment {0}'.format(comment) + command_string = 'commit comment {}'.format(comment) else: command_string = 'commit' @@ -99,7 +101,7 @@ def commit(self, confirm=False, confirm_delay=None, comment='', label='', delay_ # Other commits occurred, don't proceed with commit output += self.send_command_timing("no", strip_prompt=False, strip_command=False, delay_factor=delay_factor) - raise ValueError("Commit failed with the following errors:\n\n{0}".format(output)) + raise ValueError("Commit failed with the following errors:\n\n{}".format(output)) return output diff --git a/netmiko/dell/__init__.py b/netmiko/dell/__init__.py index c2958189c..308a5d76d 100644 --- a/netmiko/dell/__init__.py +++ b/netmiko/dell/__init__.py @@ -1,4 +1,6 @@ from __future__ import unicode_literals +from netmiko.dell.dell_dnos6 import DellDNOS6SSH +from netmiko.dell.dell_dnos6 import DellDNOS6Telnet from netmiko.dell.dell_force10_ssh import DellForce10SSH from netmiko.dell.dell_os10_ssh import DellOS10SSH, DellOS10FileTransfer from netmiko.dell.dell_powerconnect import DellPowerConnectSSH @@ -6,4 +8,5 @@ from netmiko.dell.dell_isilon_ssh import DellIsilonSSH __all__ = ['DellForce10SSH', 'DellPowerConnectSSH', 'DellPowerConnectTelnet', - 'DellOS10SSH', 'DellOS10FileTransfer', 'DellIsilonSSH'] + 'DellOS10SSH', 'DellOS10FileTransfer', 'DellIsilonSSH', + 'DellDNOS6SSH', 'DellDNOS6Telnet'] diff --git a/netmiko/dell/dell_dnos6.py b/netmiko/dell/dell_dnos6.py new file mode 100644 index 000000000..7bf3d3465 --- /dev/null +++ b/netmiko/dell/dell_dnos6.py @@ -0,0 +1,30 @@ +"""Dell N2/3/4000 base driver- supports DNOS6.""" +from __future__ import unicode_literals +from netmiko.dell.dell_powerconnect import DellPowerConnectBase +import time + + +class DellDNOS6Base(DellPowerConnectBase): + 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.enable() + self.disable_paging(command="terminal length 0") + self.set_terminal_width() + # Clear the read buffer + time.sleep(.3 * self.global_delay_factor) + self.clear_buffer() + + def save_config(self, cmd='copy running-configuration startup-configuration', confirm=False): + """Saves Config""" + return super(DellDNOS6SSH, self).save_config(cmd=cmd, confirm=confirm) + + +class DellDNOS6SSH(DellDNOS6Base): + pass + + +class DellDNOS6Telnet(DellDNOS6Base): + pass diff --git a/netmiko/extreme/__init__.py b/netmiko/extreme/__init__.py index 1074d9428..5cf4877f1 100644 --- a/netmiko/extreme/__init__.py +++ b/netmiko/extreme/__init__.py @@ -1,6 +1,14 @@ from __future__ import unicode_literals -from netmiko.extreme.extreme_exos import ExtremeSSH -from netmiko.extreme.extreme_exos import ExtremeTelnet +from netmiko.extreme.extreme_ers_ssh import ExtremeErsSSH +from netmiko.extreme.extreme_exos import ExtremeExosSSH +from netmiko.extreme.extreme_exos import ExtremeExosTelnet +from netmiko.extreme.extreme_netiron import ExtremeNetironSSH +from netmiko.extreme.extreme_netiron import ExtremeNetironTelnet +from netmiko.extreme.extreme_nos_ssh import ExtremeNosSSH +from netmiko.extreme.extreme_slx_ssh import ExtremeSlxSSH +from netmiko.extreme.extreme_vsp_ssh import ExtremeVspSSH from netmiko.extreme.extreme_wing_ssh import ExtremeWingSSH -__all__ = ['ExtremeSSH', 'ExtremeWingSSH', 'ExtremeTelnet'] +__all__ = ['ExtremeErsSSH', 'ExtremeExosSSH', 'ExtremeExosTelnet', + 'ExtremeNetironSSH', 'ExtremeNetironTelnet', 'ExtremeNosSSH', + 'ExtremeSlxSSH', 'ExtremeVspSSH', 'ExtremeWingSSH'] diff --git a/netmiko/avaya/avaya_ers_ssh.py b/netmiko/extreme/extreme_ers_ssh.py similarity index 75% rename from netmiko/avaya/avaya_ers_ssh.py rename to netmiko/extreme/extreme_ers_ssh.py index f234b78dc..e51848405 100644 --- a/netmiko/avaya/avaya_ers_ssh.py +++ b/netmiko/extreme/extreme_ers_ssh.py @@ -1,18 +1,18 @@ -"""Netmiko support for Avaya Ethernet Routing Switch.""" +"""Netmiko support for Extreme Ethernet Routing Switch.""" from __future__ import print_function from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection -# Avaya presents Enter Ctrl-Y to begin. +# Extreme ERS presents Enter Ctrl-Y to begin. CTRL_Y = '\x19' -class AvayaErsSSH(CiscoSSHConnection): - """Netmiko support for Avaya Ethernet Routing Switch.""" +class ExtremeErsSSH(CiscoSSHConnection): + """Netmiko support for Extreme Ethernet Routing Switch.""" def special_login_handler(self, delay_factor=1): """ - Avaya ERS presents the following as part of the login process: + Extreme ERS presents the following as part of the login process: Enter Ctrl-Y to begin. """ @@ -39,4 +39,4 @@ def special_login_handler(self, delay_factor=1): def save_config(self, cmd='save config', confirm=False): """Save Config""" - return super(AvayaErsSSH, self).save_config(cmd=cmd, confirm=confirm) + return super(ExtremeErsSSH, self).save_config(cmd=cmd, confirm=confirm) diff --git a/netmiko/extreme/extreme_exos.py b/netmiko/extreme/extreme_exos.py index 94e406a81..a116c82ac 100644 --- a/netmiko/extreme/extreme_exos.py +++ b/netmiko/extreme/extreme_exos.py @@ -5,8 +5,8 @@ from netmiko.cisco_base_connection import CiscoSSHConnection -class ExtremeBase(CiscoSSHConnection): - """Extreme support. +class ExtremeExosBase(CiscoSSHConnection): + """Extreme Exos support. Designed for EXOS >= 15.0 """ @@ -34,7 +34,7 @@ def set_base_prompt(self, *args, **kwargs): * testhost.4 # * testhost.5 # """ - cur_base_prompt = super(ExtremeBase, self).set_base_prompt(*args, **kwargs) + cur_base_prompt = super(ExtremeExosBase, self).set_base_prompt(*args, **kwargs) # Strip off any leading * or whitespace chars; strip off trailing period and digits match = re.search(r'[\*\s]*(.*)\.\d+', cur_base_prompt) if match: @@ -51,31 +51,31 @@ def send_command(self, *args, **kwargs): # refresh self.base_prompt self.set_base_prompt() - return super(ExtremeBase, self).send_command(*args, **kwargs) + return super(ExtremeExosBase, self).send_command(*args, **kwargs) def config_mode(self, config_command=''): - """No configuration mode on Extreme.""" + """No configuration mode on Extreme Exos.""" return '' def check_config_mode(self, check_string='#'): """Checks whether in configuration mode. Returns a boolean.""" - return super(ExtremeBase, self).check_config_mode(check_string=check_string) + return super(ExtremeExosBase, self).check_config_mode(check_string=check_string) def exit_config_mode(self, exit_config=''): - """No configuration mode on Extreme.""" + """No configuration mode on Extreme Exos.""" return '' def save_config(self, cmd='save configuration primary', confirm=False): """Saves configuration.""" - return super(ExtremeBase, self).save_config(cmd=cmd, confirm=confirm) + return super(ExtremeExosBase, self).save_config(cmd=cmd, confirm=confirm) -class ExtremeSSH(ExtremeBase): +class ExtremeExosSSH(ExtremeExosBase): pass -class ExtremeTelnet(ExtremeBase): +class ExtremeExosTelnet(ExtremeExosBase): 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(ExtremeTelnet, self).__init__(*args, **kwargs) + super(ExtremeExosTelnet, self).__init__(*args, **kwargs) diff --git a/netmiko/brocade/brocade_netiron.py b/netmiko/extreme/extreme_netiron.py similarity index 60% rename from netmiko/brocade/brocade_netiron.py rename to netmiko/extreme/extreme_netiron.py index 153879cdb..39ad3861b 100644 --- a/netmiko/brocade/brocade_netiron.py +++ b/netmiko/extreme/extreme_netiron.py @@ -2,18 +2,18 @@ from netmiko.cisco_base_connection import CiscoSSHConnection -class BrocadeNetironBase(CiscoSSHConnection): +class ExtremeNetironBase(CiscoSSHConnection): def save_config(self, cmd='write memory', confirm=False): """Save Config""" - return super(BrocadeNetironBase, self).save_config(cmd=cmd, confirm=confirm) + return super(ExtremeNetironBase, self).save_config(cmd=cmd, confirm=confirm) -class BrocadeNetironSSH(BrocadeNetironBase): +class ExtremeNetironSSH(ExtremeNetironBase): pass -class BrocadeNetironTelnet(BrocadeNetironBase): +class ExtremeNetironTelnet(ExtremeNetironBase): 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(BrocadeNetironTelnet, self).__init__(*args, **kwargs) + super(ExtremeNetironTelnet, self).__init__(*args, **kwargs) diff --git a/netmiko/brocade/brocade_nos_ssh.py b/netmiko/extreme/extreme_nos_ssh.py similarity index 69% rename from netmiko/brocade/brocade_nos_ssh.py rename to netmiko/extreme/extreme_nos_ssh.py index 4eb315b31..49135fec1 100644 --- a/netmiko/brocade/brocade_nos_ssh.py +++ b/netmiko/extreme/extreme_nos_ssh.py @@ -1,17 +1,17 @@ -"""Support for Brocade NOS/VDX.""" +"""Support for Extreme NOS/VDX.""" from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection -class BrocadeNosSSH(CiscoSSHConnection): - """Support for Brocade NOS/VDX.""" +class ExtremeNosSSH(CiscoSSHConnection): + """Support for Extreme NOS/VDX.""" def enable(self, *args, **kwargs): - """No enable mode on Brocade VDX.""" + """No enable mode on Extreme VDX.""" pass def exit_enable_mode(self, *args, **kwargs): - """No enable mode on Brocade VDX.""" + """No enable mode on Extreme VDX.""" pass def special_login_handler(self, delay_factor=1): @@ -22,6 +22,6 @@ def special_login_handler(self, delay_factor=1): def save_config(self, cmd='copy running-config startup-config', confirm=True, confirm_response='y'): - """Save Config for Brocade VDX.""" - return super(BrocadeNosSSH, self).save_config(cmd=cmd, confirm=confirm, + """Save Config for Extreme VDX.""" + return super(ExtremeNosSSH, self).save_config(cmd=cmd, confirm=confirm, confirm_response=confirm_response) diff --git a/netmiko/extreme/extreme_slx_ssh.py b/netmiko/extreme/extreme_slx_ssh.py new file mode 100644 index 000000000..74aa91495 --- /dev/null +++ b/netmiko/extreme/extreme_slx_ssh.py @@ -0,0 +1,28 @@ +"""Support for Extreme SLX.""" +from __future__ import unicode_literals +import time +from netmiko.cisco_base_connection import CiscoSSHConnection + + +class ExtremeSlxSSH(CiscoSSHConnection): + """Support for Extreme SLX.""" + def enable(self, *args, **kwargs): + """No enable mode on Extreme SLX.""" + pass + + def exit_enable_mode(self, *args, **kwargs): + """No enable mode on Extreme Slx.""" + pass + + def special_login_handler(self, delay_factor=1): + """Adding a delay after login.""" + delay_factor = self.select_delay_factor(delay_factor) + self.write_channel(self.RETURN) + time.sleep(1 * delay_factor) + + def save_config(self, cmd='copy running-config startup-config', + confirm=True, confirm_response='y'): + """Save Config for Extreme SLX.""" + return super(ExtremeSlxSSH, self).save_config(cmd=cmd, + confirm=confirm, + confirm_response=confirm_response) diff --git a/netmiko/avaya/avaya_vsp_ssh.py b/netmiko/extreme/extreme_vsp_ssh.py similarity index 72% rename from netmiko/avaya/avaya_vsp_ssh.py rename to netmiko/extreme/extreme_vsp_ssh.py index a5cade6c2..daf8e0967 100644 --- a/netmiko/avaya/avaya_vsp_ssh.py +++ b/netmiko/extreme/extreme_vsp_ssh.py @@ -1,12 +1,12 @@ -"""Avaya Virtual Services Platform Support.""" +"""Extreme Virtual Services Platform Support.""" from __future__ import print_function from __future__ import unicode_literals import time from netmiko.cisco_base_connection import CiscoSSHConnection -class AvayaVspSSH(CiscoSSHConnection): - """Avaya Virtual Services Platform Support.""" +class ExtremeVspSSH(CiscoSSHConnection): + """Extreme Virtual Services Platform Support.""" def session_preparation(self): """Prepare the session after the connection has been established.""" self._test_channel_read() @@ -18,4 +18,4 @@ def session_preparation(self): def save_config(self, cmd='save config', confirm=False): """Save Config""" - return super(AvayaVspSSH, self).save_config(cmd=cmd, confirm=confirm) + return super(ExtremeVspSSH, self).save_config(cmd=cmd, confirm=confirm) diff --git a/netmiko/f5/__init__.py b/netmiko/f5/__init__.py index a6c60f8cc..6189a1ee5 100644 --- a/netmiko/f5/__init__.py +++ b/netmiko/f5/__init__.py @@ -1,4 +1,5 @@ from __future__ import unicode_literals -from netmiko.f5.f5_ltm_ssh import F5LtmSSH +from netmiko.f5.f5_tmsh_ssh import F5TmshSSH +from netmiko.f5.f5_linux_ssh import F5LinuxSSH -__all__ = ['F5LtmSSH'] +__all__ = ['F5TmshSSH', 'F5LinuxSSH'] diff --git a/netmiko/f5/f5_linux_ssh.py b/netmiko/f5/f5_linux_ssh.py new file mode 100644 index 000000000..3f31c38a5 --- /dev/null +++ b/netmiko/f5/f5_linux_ssh.py @@ -0,0 +1,6 @@ +from __future__ import unicode_literals +from netmiko.linux.linux_ssh import LinuxSSH + + +class F5LinuxSSH(LinuxSSH): + pass diff --git a/netmiko/f5/f5_ltm_ssh.py b/netmiko/f5/f5_tmsh_ssh.py similarity index 96% rename from netmiko/f5/f5_ltm_ssh.py rename to netmiko/f5/f5_tmsh_ssh.py index 0b7c84a29..a439c9791 100644 --- a/netmiko/f5/f5_ltm_ssh.py +++ b/netmiko/f5/f5_tmsh_ssh.py @@ -3,7 +3,7 @@ from netmiko.base_connection import BaseConnection -class F5LtmSSH(BaseConnection): +class F5TmshSSH(BaseConnection): def session_preparation(self): """Prepare the session after the connection has been established.""" diff --git a/netmiko/huawei/huawei_ssh.py b/netmiko/huawei/huawei_ssh.py index 2c584a762..a42feb0ce 100644 --- a/netmiko/huawei/huawei_ssh.py +++ b/netmiko/huawei/huawei_ssh.py @@ -21,9 +21,10 @@ def config_mode(self, config_command='system-view'): """Enter configuration mode.""" return super(HuaweiSSH, self).config_mode(config_command=config_command) - def exit_config_mode(self, exit_config='return'): + def exit_config_mode(self, exit_config='return', pattern=r'>'): """Exit configuration mode.""" - return super(HuaweiSSH, self).exit_config_mode(exit_config=exit_config) + return super(HuaweiSSH, self).exit_config_mode(exit_config=exit_config, + pattern=pattern) def check_config_mode(self, check_string=']'): """Checks whether in configuration mode. Returns a boolean.""" diff --git a/netmiko/ipinfusion/__init__.py b/netmiko/ipinfusion/__init__.py new file mode 100644 index 000000000..66158c272 --- /dev/null +++ b/netmiko/ipinfusion/__init__.py @@ -0,0 +1,4 @@ +from __future__ import unicode_literals +from netmiko.ipinfusion.ipinfusion_ocnos import IpInfusionOcNOSSSH, IpInfusionOcNOSTelnet + +__all__ = ['IpInfusionOcNOSSSH', 'IpInfusionOcNOSTelnet'] diff --git a/netmiko/ipinfusion/ipinfusion_ocnos.py b/netmiko/ipinfusion/ipinfusion_ocnos.py new file mode 100644 index 000000000..ad3d44990 --- /dev/null +++ b/netmiko/ipinfusion/ipinfusion_ocnos.py @@ -0,0 +1,60 @@ +from __future__ import unicode_literals +import time +from telnetlib import IAC, DO, DONT, WILL, WONT, SB, SE, TTYPE +from netmiko.cisco_base_connection import CiscoBaseConnection + + +class IpInfusionOcNOSBase(CiscoBaseConnection): + """Common Methods for IP Infusion OcNOS support.""" + def __init__(self, *args, **kwargs): + if kwargs.get('default_enter') is None: + kwargs['default_enter'] = '\r' + return super(IpInfusionOcNOSBase, self).__init__(**kwargs) + + def session_preparation(self): + self._test_channel_read() + self.set_base_prompt() + self.disable_paging(command="terminal length 0") + + # Clear the read buffer + time.sleep(.3 * self.global_delay_factor) + self.clear_buffer() + + def save_config(self, cmd='write', confirm=False, confirm_response=''): + """Saves Config Using write command""" + return super(IpInfusionOcNOSBase, self).save_config( + cmd=cmd, confirm=confirm, confirm_response=confirm_response) + + +class IpInfusionOcNOSSSH(IpInfusionOcNOSBase): + """IP Infusion OcNOS SSH driver.""" + pass + + +class IpInfusionOcNOSTelnet(IpInfusionOcNOSBase): + """IP Infusion OcNOS Telnet driver.""" + + def _process_option(self, tsocket, command, option): + """ + For all telnet options, re-implement the default telnetlib behaviour + and refuse to handle any options. If the server expresses interest in + 'terminal type' option, then reply back with 'xterm' terminal type. + """ + if command == DO and option == TTYPE: + tsocket.sendall(IAC + WILL + TTYPE) + tsocket.sendall(IAC + SB + TTYPE + b'\0' + b'xterm' + IAC + SE) + elif command in (DO, DONT): + tsocket.sendall(IAC + WONT + option) + elif command in (WILL, WONT): + tsocket.sendall(IAC + DONT + option) + + def telnet_login(self, pri_prompt_terminator='#', alt_prompt_terminator='>', + username_pattern=r"(?:user:|sername|login|user name)", pwd_pattern=r"assword:", + delay_factor=1, max_loops=20): + # set callback function to handle telnet options. + self.remote_conn.set_option_negotiation_callback(self._process_option) + return super(IpInfusionOcNOSTelnet, self).telnet_login( + pri_prompt_terminator=pri_prompt_terminator, + alt_prompt_terminator=alt_prompt_terminator, + username_pattern=username_pattern, pwd_pattern=pwd_pattern, + delay_factor=delay_factor, max_loops=max_loops) diff --git a/netmiko/juniper/juniper.py b/netmiko/juniper/juniper.py index 269b745d2..e40a41bf7 100644 --- a/netmiko/juniper/juniper.py +++ b/netmiko/juniper/juniper.py @@ -5,6 +5,7 @@ from netmiko.base_connection import BaseConnection from netmiko.scp_handler import BaseFileTransfer +from netmiko.py23_compat import text_type class JuniperBase(BaseConnection): @@ -127,7 +128,7 @@ def commit(self, confirm=False, confirm_delay=None, check=False, comment='', commit_marker = 'configuration check succeeds' elif confirm: if confirm_delay: - command_string = 'commit confirmed ' + str(confirm_delay) + command_string = 'commit confirmed ' + text_type(confirm_delay) else: command_string = 'commit confirmed' commit_marker = 'commit confirmed will be automatically rolled back in' diff --git a/netmiko/mrv/mrv_ssh.py b/netmiko/mrv/mrv_ssh.py index dddbe7412..14bee22df 100644 --- a/netmiko/mrv/mrv_ssh.py +++ b/netmiko/mrv/mrv_ssh.py @@ -11,11 +11,12 @@ class MrvOptiswitchSSH(CiscoSSHConnection): def session_preparation(self): """Prepare the session after the connection has been established.""" self._test_channel_read(pattern=r'[>#]') - self.enable() self.set_base_prompt() + self.enable() self.disable_paging(command="no cli-paging") # Clear the read buffer time.sleep(.3 * self.global_delay_factor) + self.set_base_prompt() self.clear_buffer() def enable(self, cmd='enable', pattern=r'#', re_flags=re.IGNORECASE): diff --git a/netmiko/paloalto/__init__.py b/netmiko/paloalto/__init__.py index f34f9fdfa..57262d6fe 100644 --- a/netmiko/paloalto/__init__.py +++ b/netmiko/paloalto/__init__.py @@ -1,4 +1,4 @@ from __future__ import unicode_literals -from netmiko.paloalto.paloalto_panos_ssh import PaloAltoPanosSSH +from netmiko.paloalto.paloalto_panos import PaloAltoPanosSSH, PaloAltoPanosTelnet -__all__ = ['PaloAltoPanosSSH'] +__all__ = ['PaloAltoPanosSSH', 'PaloAltoPanosTelnet'] diff --git a/netmiko/paloalto/paloalto_panos_ssh.py b/netmiko/paloalto/paloalto_panos.py similarity index 89% rename from netmiko/paloalto/paloalto_panos_ssh.py rename to netmiko/paloalto/paloalto_panos.py index 8dc0b07e2..f43c20128 100644 --- a/netmiko/paloalto/paloalto_panos_ssh.py +++ b/netmiko/paloalto/paloalto_panos.py @@ -4,7 +4,7 @@ from netmiko.base_connection import BaseConnection -class PaloAltoPanosSSH(BaseConnection): +class PaloAltoPanosBase(BaseConnection): """ Implement methods for interacting with PaloAlto devices. @@ -39,16 +39,16 @@ def exit_enable_mode(self, *args, **kwargs): def check_config_mode(self, check_string=']'): """Checks if the device is in configuration mode or not.""" - return super(PaloAltoPanosSSH, self).check_config_mode(check_string=check_string) + return super(PaloAltoPanosBase, self).check_config_mode(check_string=check_string) def config_mode(self, config_command='configure'): """Enter configuration mode.""" - return super(PaloAltoPanosSSH, self).config_mode(config_command=config_command) + return super(PaloAltoPanosBase, self).config_mode(config_command=config_command) def exit_config_mode(self, exit_config='exit', pattern=r'>'): """Exit configuration mode.""" - return super(PaloAltoPanosSSH, self).exit_config_mode(exit_config=exit_config, - pattern=pattern) + return super(PaloAltoPanosBase, self).exit_config_mode(exit_config=exit_config, + pattern=pattern) def commit(self, force=False, partial=False, device_and_network=False, policy_and_objects=False, vsys='', no_vsys=False, delay_factor=.1): @@ -146,4 +146,12 @@ def send_command_expect(self, *args, **kwargs): def send_command(self, *args, **kwargs): """Palo Alto requires an extra delay""" kwargs['delay_factor'] = kwargs.get('delay_factor', 2.5) - return super(PaloAltoPanosSSH, self).send_command(*args, **kwargs) + return super(PaloAltoPanosBase, self).send_command(*args, **kwargs) + + +class PaloAltoPanosSSH(PaloAltoPanosBase): + pass + + +class PaloAltoPanosTelnet(PaloAltoPanosBase): + pass diff --git a/netmiko/rad/__init__.py b/netmiko/rad/__init__.py new file mode 100644 index 000000000..22a125b3b --- /dev/null +++ b/netmiko/rad/__init__.py @@ -0,0 +1,5 @@ +from __future__ import unicode_literals +from netmiko.rad.rad_etx import RadETXSSH +from netmiko.rad.rad_etx import RadETXTelnet + +__all__ = ['RadETXSSH', 'RadETXTelnet'] diff --git a/netmiko/rad/rad_etx.py b/netmiko/rad/rad_etx.py new file mode 100644 index 000000000..0aeb62153 --- /dev/null +++ b/netmiko/rad/rad_etx.py @@ -0,0 +1,85 @@ +from __future__ import unicode_literals +from __future__ import print_function +import time +from netmiko.base_connection import BaseConnection + + +class RadETXBase(BaseConnection): + """RAD ETX Support, Tested on RAD 203AX, 205A and 220A.""" + def session_preparation(self): + self._test_channel_read() + self.set_base_prompt() + self.disable_paging(command='config term length 0') + # Clear the read buffer + time.sleep(.3 * self.global_delay_factor) + self.clear_buffer() + + def save_config(self, cmd='admin save', confirm=False, confirm_response=''): + """Saves Config Using admin save.""" + if confirm: + output = self.send_command_timing(command_string=cmd) + if confirm_response: + output += self.send_command_timing(confirm_response) + else: + # Send enter by default + output += self.send_command_timing(self.RETURN) + else: + # Some devices are slow so match on trailing-prompt if you can + output = self.send_command(command_string=cmd) + return output + + def check_enable_mode(self, *args, **kwargs): + """The Rad ETX software does not have an enable.""" + pass + + def enable(self, *args, **kwargs): + """The Rad ETX software does not have an enable.""" + pass + + def exit_enable_mode(self, *args, **kwargs): + """The Rad ETX software does not have an enable.""" + pass + + def config_mode(self, config_command='config', pattern='>config'): + """Enter into configuration mode on remote device.""" + return super(RadETXBase, self).config_mode(config_command=config_command, + pattern=pattern) + + def check_config_mode(self, check_string='>config', pattern=''): + """ + Checks if the device is in configuration mode or not. + + Rad config starts with baseprompt>config. + """ + return super(RadETXBase, self).check_config_mode(check_string=check_string, + pattern=pattern) + + def exit_config_mode(self, exit_config='exit all', pattern='#'): + """Exit from configuration mode.""" + return super(RadETXBase, self).exit_config_mode(exit_config=exit_config, + pattern=pattern) + + +class RadETXSSH(RadETXBase): + """RAD ETX SSH Support.""" + def __init__(self, **kwargs): + # Found that a global_delay_factor of 2 is needed at minimum for SSH to the Rad ETX. + kwargs.setdefault('global_delay_factor', 2) + return super(RadETXSSH, self).__init__(**kwargs) + + +class RadETXTelnet(RadETXBase): + """RAD ETX Telnet Support.""" + def telnet_login(self, username_pattern=r"(?:user>)", + alt_prompt_term=r"#\s*$", **kwargs): + """ + RAD presents with the following on login + + user> + + password> **** + """ + self.TELNET_RETURN = self.RETURN + return super(RadETXTelnet, + self).telnet_login(username_pattern=username_pattern, + alt_prompt_terminator=alt_prompt_term, **kwargs) diff --git a/netmiko/snmp_autodetect.py b/netmiko/snmp_autodetect.py index 29d819a2f..9cbd0fb16 100644 --- a/netmiko/snmp_autodetect.py +++ b/netmiko/snmp_autodetect.py @@ -29,6 +29,7 @@ raise ImportError("pysnmp not installed; please install it: 'pip install pysnmp'") from netmiko.ssh_dispatcher import CLASS_MAPPER +from netmiko.py23_compat import text_type # Higher priority indicates a better match. @@ -42,6 +43,9 @@ 'hp_comware': {"oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".*HP Comware.*", re.IGNORECASE), "priority": 99}, + 'hp_procurve': {"oid": ".1.3.6.1.2.1.1.1.0", + "expr": re.compile(r".ProCurve", re.IGNORECASE), + "priority": 99}, 'cisco_ios': {"oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".*Cisco IOS Software,.*", re.IGNORECASE), "priority": 60}, @@ -60,9 +64,9 @@ 'cisco_wlc': {"oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r".*Cisco Controller.*", re.IGNORECASE), "priority": 99}, - 'f5_ltm': {"oid": ".1.3.6.1.4.1.3375.2.1.4.1.0", - "expr": re.compile(r".*BIG-IP.*", re.IGNORECASE), - "priority": 99}, + 'f5_tmsh': {"oid": ".1.3.6.1.4.1.3375.2.1.4.1.0", + "expr": re.compile(r".*BIG-IP.*", re.IGNORECASE), + "priority": 99}, 'fortinet': {"oid": ".1.3.6.1.2.1.1.1.0", "expr": re.compile(r"Forti.*", re.IGNORECASE), "priority": 80}, @@ -198,7 +202,7 @@ def _get_snmpv3(self, oid): oid, lookupNames=True, lookupValues=True) if not error_detected and snmp_data[0][1]: - return str(snmp_data[0][1]) + return text_type(snmp_data[0][1]) return "" def _get_snmpv2c(self, oid): @@ -224,7 +228,7 @@ def _get_snmpv2c(self, oid): oid, lookupNames=True, lookupValues=True) if not error_detected and snmp_data[0][1]: - return str(snmp_data[0][1]) + return text_type(snmp_data[0][1]) return "" def _get_snmp(self, oid): diff --git a/netmiko/ssh_autodetect.py b/netmiko/ssh_autodetect.py index 8297f704d..7f1fe9bec 100644 --- a/netmiko/ssh_autodetect.py +++ b/netmiko/ssh_autodetect.py @@ -77,6 +77,12 @@ "priority": 99, "dispatch": "_autodetect_std", }, + 'cisco_asa': { + "cmd": "show version", + "search_patterns": [r"Cisco Adaptive Security Appliance", r"Cisco ASA"], + "priority": 99, + "dispatch": "_autodetect_std", + }, 'cisco_ios': { "cmd": "show version", "search_patterns": [ @@ -86,12 +92,6 @@ "priority": 99, "dispatch": "_autodetect_std", }, - 'cisco_asa': { - "cmd": "show version", - "search_patterns": [r"Cisco Adaptive Security Appliance", r"Cisco ASA"], - "priority": 99, - "dispatch": "_autodetect_std", - }, 'cisco_nxos': { "cmd": "show version", "search_patterns": [r"Cisco Nexus Operating System", r"NX-OS"], @@ -104,6 +104,30 @@ "priority": 99, "dispatch": "_autodetect_std", }, + 'dell_force10': { + "cmd": "show version", + "search_patterns": [r"S4048-ON"], + "priority": 99, + "dispatch": "_autodetect_std", + }, + 'dell_os10': { + "cmd": "show version", + "search_patterns": [r"Dell EMC Networking OS10-Enterprise"], + "priority": 99, + "dispatch": "_autodetect_std", + }, + 'f5_tmsh': { + "cmd": "show sys version", + "search_patterns": [r"BIG-IP"], + "priority": 99, + "dispatch": "_autodetect_std", + }, + 'f5_linux': { + "cmd": "cat /etc/issue", + "search_patterns": [r"BIG-IP"], + "priority": 99, + "dispatch": "_autodetect_std", + }, 'huawei': { "cmd": "display version", "search_patterns": [ @@ -123,18 +147,12 @@ "priority": 99, "dispatch": "_autodetect_std", }, - 'dell_force10': { - "cmd": "show version", - "search_patterns": [r"S4048-ON"], - "priority": 99, - "dispatch": "_autodetect_std", - }, - 'dell_os10': { - "cmd": "show version", - "search_patterns": [r"Dell EMC Networking OS10-Enterprise"], + 'linux': { + "cmd": "uname -a", + "search_patterns": [r"Linux"], "priority": 99, "dispatch": "_autodetect_std", - }, + } } @@ -274,7 +292,9 @@ def _autodetect_std(self, cmd="", search_patterns=None, re_flags=re.I, priority= r'% Invalid input detected', r'syntax error, expecting', r'Error: Unrecognized command', - r'%Error' + r'%Error', + r'command not found', + r'Syntax Error: unexpected argument', ] if not cmd or not search_patterns: return 0 diff --git a/netmiko/ssh_dispatcher.py b/netmiko/ssh_dispatcher.py index 987def376..9837a402d 100644 --- a/netmiko/ssh_dispatcher.py +++ b/netmiko/ssh_dispatcher.py @@ -9,11 +9,6 @@ from netmiko.arista import AristaFileTransfer from netmiko.apresia import ApresiaAeosSSH, ApresiaAeosTelnet from netmiko.aruba import ArubaSSH -from netmiko.avaya import AvayaErsSSH -from netmiko.avaya import AvayaVspSSH -from netmiko.brocade import BrocadeNetironSSH -from netmiko.brocade import BrocadeNetironTelnet -from netmiko.brocade import BrocadeNosSSH from netmiko.calix import CalixB6SSH, CalixB6Telnet from netmiko.checkpoint import CheckPointGaiaSSH from netmiko.ciena import CienaSaosSSH @@ -26,6 +21,8 @@ from netmiko.cisco import CiscoXrSSH, CiscoXrFileTransfer from netmiko.citrix import NetscalerSSH from netmiko.coriant import CoriantSSH +from netmiko.dell import DellDNOS6SSH +from netmiko.dell import DellDNOS6Telnet from netmiko.dell import DellForce10SSH from netmiko.dell import DellOS10SSH, DellOS10FileTransfer from netmiko.dell import DellPowerConnectSSH @@ -33,13 +30,21 @@ from netmiko.dell import DellIsilonSSH from netmiko.eltex import EltexSSH from netmiko.enterasys import EnterasysSSH -from netmiko.extreme import ExtremeSSH +from netmiko.extreme import ExtremeErsSSH +from netmiko.extreme import ExtremeExosSSH +from netmiko.extreme import ExtremeExosTelnet +from netmiko.extreme import ExtremeNetironSSH +from netmiko.extreme import ExtremeNetironTelnet +from netmiko.extreme import ExtremeNosSSH +from netmiko.extreme import ExtremeSlxSSH +from netmiko.extreme import ExtremeVspSSH from netmiko.extreme import ExtremeWingSSH -from netmiko.extreme import ExtremeTelnet -from netmiko.f5 import F5LtmSSH +from netmiko.f5 import F5TmshSSH +from netmiko.f5 import F5LinuxSSH from netmiko.fortinet import FortinetSSH from netmiko.hp import HPProcurveSSH, HPProcurveTelnet, HPComwareSSH, HPComwareTelnet from netmiko.huawei import HuaweiSSH, HuaweiVrpv8SSH +from netmiko.ipinfusion import IpInfusionOcNOSSSH, IpInfusionOcNOSTelnet from netmiko.juniper import JuniperSSH, JuniperTelnet from netmiko.juniper import JuniperFileTransfer from netmiko.linux import LinuxSSH, LinuxFileTransfer @@ -48,8 +53,11 @@ from netmiko.netapp import NetAppcDotSSH from netmiko.ovs import OvsLinuxSSH from netmiko.paloalto import PaloAltoPanosSSH +from netmiko.paloalto import PaloAltoPanosTelnet from netmiko.pluribus import PluribusSSH from netmiko.quanta import QuantaMeshSSH +from netmiko.rad import RadETXSSH +from netmiko.rad import RadETXTelnet from netmiko.ruckus import RuckusFastironSSH from netmiko.ruckus import RuckusFastironTelnet from netmiko.terminal_server import TerminalServerSSH @@ -67,12 +75,12 @@ 'apresia_aeos': ApresiaAeosSSH, 'arista_eos': AristaSSH, 'aruba_os': ArubaSSH, - 'avaya_ers': AvayaErsSSH, - 'avaya_vsp': AvayaVspSSH, + 'avaya_ers': ExtremeErsSSH, + 'avaya_vsp': ExtremeVspSSH, 'brocade_fastiron': RuckusFastironSSH, - 'brocade_netiron': BrocadeNetironSSH, - 'brocade_nos': BrocadeNosSSH, - 'brocade_vdx': BrocadeNosSSH, + 'brocade_netiron': ExtremeNetironSSH, + 'brocade_nos': ExtremeNosSSH, + 'brocade_vdx': ExtremeNosSSH, 'brocade_vyos': VyOSSSH, 'checkpoint_gaia': CheckPointGaiaSSH, 'calix_b6': CalixB6SSH, @@ -86,21 +94,34 @@ 'cisco_xe': CiscoIosSSH, 'cisco_xr': CiscoXrSSH, 'coriant': CoriantSSH, + 'dell_dnos9': DellForce10SSH, 'dell_force10': DellForce10SSH, + 'dell_os6': DellDNOS6SSH, + 'dell_os9': DellForce10SSH, 'dell_os10': DellOS10SSH, 'dell_powerconnect': DellPowerConnectSSH, 'dell_isilon': DellIsilonSSH, 'eltex': EltexSSH, 'enterasys': EnterasysSSH, - 'extreme': ExtremeSSH, + 'extreme': ExtremeExosSSH, + 'extreme_ers': ExtremeErsSSH, + 'extreme_exos': ExtremeExosSSH, + 'extreme_netiron': ExtremeNetironSSH, + 'extreme_nos': ExtremeNosSSH, + 'extreme_slx': ExtremeSlxSSH, + 'extreme_vdx': ExtremeNosSSH, + 'extreme_vsp': ExtremeVspSSH, 'extreme_wing': ExtremeWingSSH, - 'f5_ltm': F5LtmSSH, + 'f5_ltm': F5TmshSSH, + 'f5_tmsh': F5TmshSSH, + 'f5_linux': F5LinuxSSH, 'fortinet': FortinetSSH, 'generic_termserver': TerminalServerSSH, 'hp_comware': HPComwareSSH, 'hp_procurve': HPProcurveSSH, 'huawei': HuaweiSSH, 'huawei_vrpv8': HuaweiVrpv8SSH, + 'ipinfusion_ocnos': IpInfusionOcNOSSSH, 'juniper': JuniperSSH, 'juniper_junos': JuniperSSH, 'linux': LinuxSSH, @@ -112,6 +133,7 @@ 'paloalto_panos': PaloAltoPanosSSH, 'pluribus': PluribusSSH, 'quanta_mesh': QuantaMeshSSH, + 'rad_etx': RadETXSSH, 'ruckus_fastiron': RuckusFastironSSH, 'ubiquiti_edge': UbiquitiEdgeSSH, 'ubiquiti_edgeswitch': UbiquitiEdgeSSH, @@ -147,18 +169,24 @@ FILE_TRANSFER_MAP = new_mapper # Add telnet drivers -CLASS_MAPPER['brocade_fastiron_telnet'] = RuckusFastironTelnet -CLASS_MAPPER['brocade_netiron_telnet'] = BrocadeNetironTelnet -CLASS_MAPPER['cisco_ios_telnet'] = CiscoIosTelnet CLASS_MAPPER['apresia_aeos_telnet'] = ApresiaAeosTelnet CLASS_MAPPER['arista_eos_telnet'] = AristaTelnet -CLASS_MAPPER['hp_procurve_telnet'] = HPProcurveTelnet -CLASS_MAPPER['hp_comware_telnet'] = HPComwareTelnet -CLASS_MAPPER['juniper_junos_telnet'] = JuniperTelnet +CLASS_MAPPER['brocade_fastiron_telnet'] = RuckusFastironTelnet +CLASS_MAPPER['brocade_netiron_telnet'] = ExtremeNetironTelnet CLASS_MAPPER['calix_b6_telnet'] = CalixB6Telnet +CLASS_MAPPER['cisco_ios_telnet'] = CiscoIosTelnet +CLASS_MAPPER['dell_dnos6_telnet'] = DellDNOS6Telnet CLASS_MAPPER['dell_powerconnect_telnet'] = DellPowerConnectTelnet +CLASS_MAPPER['extreme_telnet'] = ExtremeExosTelnet +CLASS_MAPPER['extreme_exos_telnet'] = ExtremeExosTelnet +CLASS_MAPPER['extreme_netiron_telnet'] = ExtremeNetironTelnet CLASS_MAPPER['generic_termserver_telnet'] = TerminalServerTelnet -CLASS_MAPPER['extreme_telnet'] = ExtremeTelnet +CLASS_MAPPER['hp_procurve_telnet'] = HPProcurveTelnet +CLASS_MAPPER['hp_comware_telnet'] = HPComwareTelnet +CLASS_MAPPER['ipinfusion_ocnos_telnet'] = IpInfusionOcNOSTelnet +CLASS_MAPPER['juniper_junos_telnet'] = JuniperTelnet +CLASS_MAPPER['paloalto_panos_telnet'] = PaloAltoPanosTelnet +CLASS_MAPPER['rad_etx_telnet'] = RadETXTelnet CLASS_MAPPER['ruckus_fastiron_telnet'] = RuckusFastironTelnet # Add serial drivers @@ -205,7 +233,7 @@ def redispatch(obj, device_type, session_prep=True): obj.device_type = device_type obj.__class__ = new_class if session_prep: - obj.session_preparation() + obj._try_session_preparation() def FileTransfer(*args, **kwargs): diff --git a/netmiko/utilities.py b/netmiko/utilities.py index fd7d3896d..9820f739d 100644 --- a/netmiko/utilities.py +++ b/netmiko/utilities.py @@ -8,6 +8,7 @@ import serial.tools.list_ports from netmiko._textfsm import _clitable as clitable from netmiko._textfsm._clitable import CliTableError +from netmiko.py23_compat import text_type # Dictionary mapping 'show run' for vendors with different command @@ -15,6 +16,13 @@ 'juniper': 'show configuration', 'juniper_junos': 'show configuration', 'extreme': 'show configuration', + 'extreme_ers': 'show running-config', + 'extreme_exos': 'show configuration', + 'extreme_netiron': 'show running-config', + 'extreme_nos': 'show running-config', + 'extreme_slx': 'show running-config', + 'extreme_vdx': 'show running-config', + 'extreme_vsp': 'show running-config', 'extreme_wing': 'show running-config', 'hp_comware': 'display current-configuration', 'huawei': 'display current-configuration', @@ -150,19 +158,25 @@ def find_netmiko_dir(): return (netmiko_base_dir, netmiko_full_dir) -def write_bytes(out_data): +def write_bytes(out_data, encoding='ascii'): """Write Python2 and Python3 compatible byte stream.""" if sys.version_info[0] >= 3: if isinstance(out_data, type(u'')): - return out_data.encode('ascii', 'ignore') + if encoding == 'utf-8': + return out_data.encode('utf-8') + else: + return out_data.encode('ascii', 'ignore') elif isinstance(out_data, type(b'')): return out_data else: if isinstance(out_data, type(u'')): - return out_data.encode('ascii', 'ignore') + if encoding == 'utf-8': + return out_data.encode('utf-8') + else: + return out_data.encode('ascii', 'ignore') elif isinstance(out_data, type(str(''))): return out_data - msg = "Invalid value for out_data neither unicode nor byte string: {0}".format(out_data) + msg = "Invalid value for out_data neither unicode nor byte string: {}".format(out_data) raise ValueError(msg) @@ -176,7 +190,7 @@ def check_serial_port(name): msg += "available devices are: " ports = list(serial.tools.list_ports.comports()) for p in ports: - msg += "{},".format(str(p)) + msg += "{},".format(text_type(p)) raise ValueError(msg) diff --git a/setup.py b/setup.py index 3ad8fda75..6648c02a8 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ requirements = [ - 'paramiko>=2.0.0', + 'paramiko>=2.4.1', 'scp>=0.10.0', 'pyyaml', 'pyserial', diff --git a/tests/etc/commands.yml.example b/tests/etc/commands.yml.example index 4715ee8d1..b41f8852a 100644 --- a/tests/etc/commands.yml.example +++ b/tests/etc/commands.yml.example @@ -9,7 +9,10 @@ cisco_ios: - "no logging console" - "logging buffered 20010" # something you can verify has changed config_verification: "show run | inc logging buffer" - config_file: "cisco_ios_commands.txt" + config_file: 'cisco_ios_commands.txt' + save_config_cmd: 'copy run start' + save_config_confirm: True + save_config_response: '' cisco_asa: version: "show version" @@ -179,3 +182,29 @@ calix_b6_ssh: - "access-list ethernet 999 permit ip" - "access-list ethernet 999 permit arp" config_verification: "find running-config 999" + +ipinfusion_ocnos: + version: "show version" + basic: "show ip interface eth0 brief" + extended_output: "show ip interface brief" # requires paging to be disabled + config: + - "logging level ospf 4" # base command + - "no logging level ospf" + - "logging level ospf 5" # something you can verify has changed + config_verification: "show logging level ospf" + save_config_cmd: 'write' + save_config_confirm: False + save_config_response: "[OK]" + +ipinfusion_ocnos_telnet: + version: "show version" + basic: "show ip interface eth0 brief" + extended_output: "show ip interface brief" # requires paging to be disabled + config: + - "logging level ospf 4" # base command + - "no logging level ospf" + - "logging level ospf 5" # something you can verify has changed + config_verification: "show logging level ospf" + save_config_cmd: 'write' + save_config_confirm: False + save_config_response: "[OK]" diff --git a/tests/etc/responses.yml.example b/tests/etc/responses.yml.example index d900fbc6e..767d63f2a 100644 --- a/tests/etc/responses.yml.example +++ b/tests/etc/responses.yml.example @@ -8,6 +8,7 @@ cisco_ios: version_banner: "Cisco IOS Software" multiple_line_output: "Configuration register is" file_check_cmd: "logging buffered 8880" + save_config: 'OK' juniper: base_prompt: pyclass@pynet-jnpr-srx1 @@ -132,3 +133,25 @@ calix_b6_ssh: multiple_line_output: "rtcPwrUptimeTotal" cmd_response_init: "Building configuration... Done" cmd_response_final: "access-list ethernet 999 permit ip" + +ipinfusion_ocnos: + base_prompt: rtr1 + router_prompt: rtr1> + enable_prompt: rtr1# + interface_ip: 10.12.39.34 + version_banner: "Software Product: OcNOS" + multiple_line_output: "lo 127.0.0.1" + cmd_response_init: "ospfd 2 4" + cmd_response_final: "ospfd 2 5" + save_config: '[OK]' + +ipinfusion_ocnos_telnet: + base_prompt: rtr1 + router_prompt: rtr1> + enable_prompt: rtr1# + interface_ip: 10.12.39.34 + version_banner: "Software Product: OcNOS" + multiple_line_output: "lo 127.0.0.1" + cmd_response_init: "ospfd 2 4" + cmd_response_final: "ospfd 2 5" + save_config: '[OK]' diff --git a/tests/etc/test_devices.yml.example b/tests/etc/test_devices.yml.example index d322da5be..18161b2d9 100644 --- a/tests/etc/test_devices.yml.example +++ b/tests/etc/test_devices.yml.example @@ -119,3 +119,18 @@ calix_b6_ssh: username: cli password: occam secret: razor + +ipinfusion_ocnos: + device_type: ipinfusion_ocnos + ip: 10.12.39.34 + username: ocnos + password: ocnos + secret: ocnos + +ipinfusion_ocnos_telnet: + device_type: ipinfusion_ocnos_telnet + ip: 10.12.39.34 + username: ocnos + password: ocnos + secret: ocnos + diff --git a/tests/test_ipinfusion.sh b/tests/test_ipinfusion.sh new file mode 100755 index 000000000..26dbffaf7 --- /dev/null +++ b/tests/test_ipinfusion.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +RETURN_CODE=0 + +# Exit on the first test failure and set RETURN_CODE = 1 +echo "Starting tests...good luck:" \ +&& py.test -v test_netmiko_show.py --test_device ipinfusion_ocnos \ +&& py.test -v test_netmiko_config.py --test_device ipinfusion_ocnos \ +|| RETURN_CODE=1 + +exit $RETURN_CODE diff --git a/tests/test_ipinfusion_telnet.sh b/tests/test_ipinfusion_telnet.sh new file mode 100755 index 000000000..3a8a862e6 --- /dev/null +++ b/tests/test_ipinfusion_telnet.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +RETURN_CODE=0 + +# Exit on the first test failure and set RETURN_CODE = 1 +echo "Starting tests...good luck:" \ +&& py.test -v test_netmiko_show.py --test_device ipinfusion_ocnos_telnet \ +&& py.test -v test_netmiko_config.py --test_device ipinfusion_ocnos_telnet \ +|| RETURN_CODE=1 + +exit $RETURN_CODE diff --git a/tests/test_netmiko_config.py b/tests/test_netmiko_config.py index 275e3e96d..237627f95 100755 --- a/tests/test_netmiko_config.py +++ b/tests/test_netmiko_config.py @@ -1,19 +1,4 @@ #!/usr/bin/env python -""" -This module runs tests against Cisco IOS devices. - -setup_module: setup variables for later use. - -test_ssh_connect: verify ssh connectivity -test_enable_mode: verify enter enable mode -test_config_mode: verify enter config mode -test_exit_config_mode: verify exit config mode -test_command_set: verify sending a set of config commands -test_commands_from_file: verify sending a set of config commands from a file -test_disconnect: cleanly disconnect the SSH session - -""" - from __future__ import print_function from __future__ import unicode_literals @@ -106,5 +91,3 @@ def test_disconnect(net_connect, commands, expected_responses): Terminate the SSH session ''' net_connect.disconnect() - - diff --git a/tests/test_netmiko_save.py b/tests/test_netmiko_save.py new file mode 100755 index 000000000..9be4acdae --- /dev/null +++ b/tests/test_netmiko_save.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +from __future__ import print_function +from __future__ import unicode_literals + + +def test_save_base(net_connect, commands, expected_responses): + ''' + Test save config with no options. + ''' + save_verify = expected_responses['save_config'] + + cmd_response = net_connect.save_config() + assert save_verify in cmd_response + +def test_save_confirm(net_connect, commands, expected_responses): + ''' + Test save config with the confirm parameter. + ''' + confirm = commands['save_config_confirm'] + save_verify = expected_responses['save_config'] + + cmd_response = net_connect.save_config(confirm) + assert save_verify in cmd_response + +def test_save_response(net_connect, commands, expected_responses): + ''' + Test save config with the confirm response parameter. + ''' + confirm_response = commands['save_config_response'] + save_verify = expected_responses['save_config'] + + cmd_response = net_connect.save_config(confirm_response=confirm_response) + assert save_verify in cmd_response + +def test_save_cmd(net_connect, commands, expected_responses): + ''' + Test save config with cmd parameter. + ''' + cmd = commands['save_config_cmd'] + save_verify = expected_responses['save_config'] + + cmd_response = net_connect.save_config(cmd=cmd) + assert save_verify in cmd_response + +def test_save_confirm_response(net_connect, commands, expected_responses): + ''' + Test save config with confirm and confirm response parameters + ''' + confirm = commands['save_config_confirm'] + confirm_response = commands['save_config_response'] + save_verify = expected_responses['save_config'] + + cmd_response = net_connect.save_config(confirm=confirm, + confirm_response=confirm_response) + assert save_verify in cmd_response + +def test_save_all(net_connect, commands, expected_responses): + ''' + Test the save config method with all additional parameters. + ''' + cmd = commands['save_config_cmd'] + confirm = commands['save_config_confirm'] + confirm_response = commands['save_config_response'] + save_verify = expected_responses['save_config'] + + cmd_response = net_connect.save_config(cmd=cmd, confirm=confirm, + confirm_response=confirm_response) + assert save_verify in cmd_response + +def test_disconnect(net_connect, commands, expected_responses): + ''' + Terminate the SSH session + ''' + net_connect.disconnect() diff --git a/tests/test_netmiko_scp.py b/tests/test_netmiko_scp.py index ad5c938fb..965362dc6 100755 --- a/tests/test_netmiko_scp.py +++ b/tests/test_netmiko_scp.py @@ -43,7 +43,7 @@ def test_verify_space_available_put(scp_fixture): ssh_conn, scp_transfer = scp_fixture assert scp_transfer.verify_space_available() == True # intentional make there not be enough space available - scp_transfer.file_size = 10000000000 + scp_transfer.file_size = 100000000000 assert scp_transfer.verify_space_available() == False def test_remote_file_size(scp_fixture): diff --git a/tests/test_suite_alt.sh b/tests/test_suite_alt.sh index c84b6e424..82f8f75d2 100755 --- a/tests/test_suite_alt.sh +++ b/tests/test_suite_alt.sh @@ -44,10 +44,6 @@ echo "Starting tests...good luck:" \ && py.test -v test_netmiko_show.py --test_device hp_procurve \ && py.test -v test_netmiko_config.py --test_device hp_procurve \ \ -&& echo "HP Comware7" \ -&& py.test -v test_netmiko_show.py --test_device hp_comware \ -&& py.test -v test_netmiko_config.py --test_device hp_comware \ -\ && echo "Juniper" \ && py.test -v test_netmiko_scp.py --test_device juniper_srx \ && py.test -v test_netmiko_show.py --test_device juniper_srx \ @@ -85,3 +81,8 @@ echo "Starting tests...good luck:" \ || RETURN_CODE=1 exit $RETURN_CODE + +# && echo "HP Comware7" \ +# && py.test -v test_netmiko_show.py --test_device hp_comware \ +# && py.test -v test_netmiko_config.py --test_device hp_comware \ +# \