diff --git a/README.md b/README.md
index 40f2b8358..c23793e7d 100644
--- a/README.md
+++ b/README.md
@@ -8,18 +8,17 @@ Multi-vendor library to simplify Paramiko SSH connections to network devices
Python 2.7, 3.4, 3.5
-
#### Requires:
+
Paramiko >= 1.13+
scp >= 0.10.0
pyyaml
pytest (for unit tests)
-
-
#### Supports:
###### Regularly tested
+
Arista vEOS
Cisco ASA
Cisco IOS
@@ -33,6 +32,7 @@ Juniper Junos
Linux
###### Limited testing
+
Avaya ERS
Avaya VSP
Brocade VDX
@@ -48,26 +48,27 @@ Pluribus
Vyatta VyOS
###### Experimental
+
A10
Alcatel-Lucent SR-OS
Ciena SAOS
Cisco Telepresence
+CheckPoint Gaia
Enterasys
Extreme
F5 LTM
Fortinet
-
## Tutorials:
##### Standard Tutorial:
+
https://pynet.twb-tech.com/blog/automation/netmiko.html
##### SSH Proxy:
-https://pynet.twb-tech.com/blog/automation/netmiko-proxy.html
+https://pynet.twb-tech.com/blog/automation/netmiko-proxy.html
-
## Examples:
#### Create a dictionary representing the device.
@@ -88,14 +89,14 @@ cisco_881 = {
```
-
#### Establish an SSH connection to the device by passing in the device dictionary.
+
```py
net_connect = ConnectHandler(**cisco_881)
```
-
#### Execute show commands.
+
```py
output = net_connect.send_command('show ip int brief')
print(output)
@@ -110,27 +111,27 @@ FastEthernet4 10.10.10.10 YES manual up up
Vlan1 unassigned YES unset down down
```
-
#### For long-running commands, use `send_command_expect()`
`send_command_expect` waits for the trailing prompt (or for an optional pattern)
```py
net_connect.send_command_expect('write memory')
```
+
```
Building configuration...
[OK]
```
-
#### Enter and exit enable mode.
+
```py
net_connect.enable()
net_connect.exit_enable_mode()
```
-
#### Execute configuration change commands (will automatically enter into config mode)
+
```py
config_commands = [ 'logging buffered 20000',
'logging buffered 20010',
@@ -157,7 +158,6 @@ If you have questions or would like to discuss Netmiko, a Netmiko channel exists
-
---
Kirk Byers
Python for Network Engineers
diff --git a/netmiko/__init__.py b/netmiko/__init__.py
index e97075d7a..033ea7235 100644
--- a/netmiko/__init__.py
+++ b/netmiko/__init__.py
@@ -15,17 +15,18 @@
from netmiko.ssh_exception import NetMikoTimeoutException
from netmiko.ssh_exception import NetMikoAuthenticationException
from netmiko.ssh_autodetect import SSHDetect
+from netmiko.base_connection import BaseConnection
# Alternate naming
NetmikoTimeoutError = NetMikoTimeoutException
NetmikoAuthError = NetMikoAuthenticationException
-__version__ = '1.3.0'
+__version__ = '1.4.0'
__all__ = ('ConnectHandler', 'ssh_dispatcher', 'platforms', 'SCPConn', 'FileTransfer',
'NetMikoTimeoutException', 'NetMikoAuthenticationException',
'NetmikoTimeoutError', 'NetmikoAuthError', 'InLineTransfer', 'redispatch',
- 'SSHDetect')
+ 'SSHDetect', 'BaseConnection')
# Cisco cntl-shift-six sequence
CNTL_SHIFT_6 = chr(30)
diff --git a/netmiko/base_connection.py b/netmiko/base_connection.py
index 7abc2c4a0..67614a052 100644
--- a/netmiko/base_connection.py
+++ b/netmiko/base_connection.py
@@ -22,6 +22,7 @@
from netmiko.netmiko_globals import MAX_BUFFER, BACKSPACE_CHAR
from netmiko.ssh_exception import NetMikoTimeoutException, NetMikoAuthenticationException
from netmiko.utilities import write_bytes
+from netmiko.py23_compat import string_types
from netmiko import log
@@ -58,40 +59,31 @@ def __init__(self, ip='', host='', username='', password='', secret='', port=Non
:type port: int or None
:param device_type: Class selection based on device type.
:type device_type: str
- :param verbose: If `True` enables more verbose logging.
+ :param verbose: Enable additional messages to standard output.
:type verbose: bool
- :param global_delay_factor: Controls global delay factor value.
+ :param global_delay_factor: Multiplication factor affecting Netmiko delays (default: 1).
:type global_delay_factor: int
- :param use_keys: If true, Paramiko will attempt to connect to
- target device using SSH keys.
+ :param use_keys: Connect to target device using SSH keys.
:type use_keys: bool
- :param key_file: Name of the SSH key file to use for Paramiko
- SSH connection authentication.
+ :param key_file: Filename path of the SSH key file to use.
:type key_file: str
- :param allow_agent: Set to True to enable connect to the SSH agent
+ :param allow_agent: Enable use of SSH key-agent.
:type allow_agent: bool
- :param ssh_strict: If `True` Paramiko will automatically reject
- unknown hostname and keys. If 'False' Paramiko will
- automatically add the hostname and new host key.
+ :param ssh_strict: Automatically reject unknown SSH host keys (default: False, which
+ means unknown SSH host keys will be accepted).
:type ssh_strict: bool
- :param system_host_keys: If `True` Paramiko will load host keys
- from the user's local 'known hosts' file.
+ :param system_host_keys: Load host keys from the user's 'known_hosts' file.
:type system_host_keys: bool
- :param alt_host_keys: If `True` host keys will be loaded from
- a local host-key file.
+ :param alt_host_keys: If `True` host keys will be loaded from the file specified in
+ 'alt_key_file'.
:type alt_host_keys: bool
- :param alt_key_file: If `alt_host_keys` is set to `True`, provide
- the filename of the local host-key file to load.
+ :param alt_key_file: SSH host key file to use (if alt_host_keys=True).
:type alt_key_file: str
- :param ssh_config_file: File name of a OpenSSH configuration file
- to load SSH connection parameters from.
+ :param ssh_config_file: File name of OpenSSH configuration file.
:type ssh_config_file: str
- :param timeout: Set a timeout on blocking read/write operations.
+ :param timeout: Connection timeout.
:type timeout: float
- :param session_timeout: Set a timeout for parallel requests. When
- the channel is busy serving other tasks and the queue is
- very long, in case the wait time is higher than this value,
- NetMikoTimeoutException will be raised.
+ :param session_timeout: Set a timeout for parallel requests.
:type session_timeout: float
"""
if ip:
@@ -127,6 +119,7 @@ def __init__(self, ip='', host='', username='', password='', secret='', port=Non
# determine if telnet or SSH
if '_telnet' in device_type:
self.protocol = 'telnet'
+ self._modify_connection_params()
self.establish_connection()
self.session_preparation()
else:
@@ -148,6 +141,7 @@ def __init__(self, ip='', host='', username='', password='', secret='', port=Non
# For SSH proxy support
self.ssh_config_file = ssh_config_file
+ self._modify_connection_params()
self.establish_connection()
self.session_preparation()
@@ -156,15 +150,19 @@ def __init__(self, ip='', host='', username='', password='', secret='', port=Non
self.clear_buffer()
def __enter__(self):
- """Enter runtime context"""
+ """Establish a session using a Context Manager."""
return self
def __exit__(self, exc_type, exc_value, traceback):
- """Gracefully close connection on context manager exit"""
+ """Gracefully close connection on Context Manager exit."""
self.disconnect()
if exc_type is not None:
raise exc_type(exc_value)
+ def _modify_connection_params(self):
+ """Modify connection parameters prior to SSH connection."""
+ pass
+
def _timeout_exceeded(self, start, msg='Timeout exceeded!'):
"""
Raise NetMikoTimeoutException if waiting too much in the
@@ -242,7 +240,7 @@ def read_channel(self):
self._unlock_netmiko_session()
return output
- def _read_channel_expect(self, pattern='', re_flags=0):
+ def _read_channel_expect(self, pattern='', re_flags=0, max_loops=None):
"""
Function that reads channel until pattern is detected.
@@ -263,10 +261,11 @@ def _read_channel_expect(self, pattern='', re_flags=0):
if debug:
print("Pattern is: {}".format(pattern))
- # Will loop for self.timeout time (unless modified by global_delay_factor)
+ # Will loop for self.timeout time (override with max_loops argument)
i = 1
loop_delay = .1
- max_loops = self.timeout / loop_delay
+ if not max_loops:
+ max_loops = self.timeout / loop_delay
while i < max_loops:
if self.protocol == 'ssh':
try:
@@ -329,18 +328,10 @@ def read_until_pattern(self, *args, **kwargs):
def read_until_prompt_or_pattern(self, pattern='', re_flags=0):
"""Read until either self.base_prompt or pattern is detected. Return ALL data available."""
- output = ''
- if not pattern:
- pattern = re.escape(self.base_prompt)
- base_prompt_pattern = re.escape(self.base_prompt)
- while True:
- try:
- output += self.read_channel()
- if re.search(pattern, output, flags=re_flags) or re.search(base_prompt_pattern,
- output, flags=re_flags):
- return output
- except socket.timeout:
- raise NetMikoTimeoutException("Timed-out reading channel, data not available.")
+ combined_pattern = re.escape(self.base_prompt)
+ if pattern:
+ combined_pattern += "|{}".format(pattern)
+ return self._read_channel_expect(combined_pattern, re_flags=re_flags)
def telnet_login(self, pri_prompt_terminator='#', alt_prompt_terminator='>',
username_pattern=r"sername", pwd_pattern=r"assword",
@@ -426,8 +417,7 @@ def _use_ssh_config(self, dict_arg):
ssh_config_instance = paramiko.SSHConfig()
with io.open(full_path, "rt", encoding='utf-8') as f:
ssh_config_instance.parse(f)
- host_specifier = "{0}:{1}".format(self.host, self.port)
- source = ssh_config_instance.lookup(host_specifier)
+ source = ssh_config_instance.lookup(self.host)
else:
source = {}
@@ -948,6 +938,9 @@ def send_config_set(self, config_commands=None, exit_config_mode=True, delay_fac
delay_factor = self.select_delay_factor(delay_factor)
if config_commands is None:
return ''
+ elif isinstance(config_commands, string_types):
+ config_commands = (config_commands,)
+
if not hasattr(config_commands, '__iter__'):
raise ValueError("Invalid argument passed into send_config_set")
diff --git a/netmiko/checkpoint/__init__.py b/netmiko/checkpoint/__init__.py
new file mode 100644
index 000000000..56967ca39
--- /dev/null
+++ b/netmiko/checkpoint/__init__.py
@@ -0,0 +1,4 @@
+from __future__ import unicode_literals
+from netmiko.checkpoint.checkpoint_gaia_ssh import CheckPointGaiaSSH
+
+__all__ = ['CheckPointGaiaSSH']
diff --git a/netmiko/checkpoint/checkpoint_gaia_ssh.py b/netmiko/checkpoint/checkpoint_gaia_ssh.py
new file mode 100644
index 000000000..591f3db09
--- /dev/null
+++ b/netmiko/checkpoint/checkpoint_gaia_ssh.py
@@ -0,0 +1,26 @@
+from __future__ import unicode_literals
+from netmiko.base_connection import BaseConnection
+
+
+class CheckPointGaiaSSH(BaseConnection):
+ """
+ Implements methods for communicating with Check Point Gaia
+ firewalls.
+ """
+ def session_preparation(self):
+ """
+ Prepare the session after the connection has been established.
+
+ Set the base prompt for interaction ('>').
+ """
+ self._test_channel_read()
+ self.set_base_prompt()
+ self.disable_paging(command="set clienv rows 0\n")
+
+ def config_mode(self, config_command=''):
+ """No config mode for Check Point devices."""
+ return ''
+
+ def exit_config_mode(self, exit_config=''):
+ """No config mode for Check Point devices."""
+ return ''
diff --git a/netmiko/cisco/cisco_asa_ssh.py b/netmiko/cisco/cisco_asa_ssh.py
index a34977006..71e22e085 100644
--- a/netmiko/cisco/cisco_asa_ssh.py
+++ b/netmiko/cisco/cisco_asa_ssh.py
@@ -2,6 +2,7 @@
from __future__ import unicode_literals
import re
+import time
from netmiko.cisco_base_connection import CiscoSSHConnection
@@ -11,7 +12,10 @@ def session_preparation(self):
"""Prepare the session after the connection has been established."""
self._test_channel_read()
self.set_base_prompt()
- self.enable()
+ if self.secret:
+ self.enable()
+ else:
+ self.asa_login()
self.disable_paging(command="terminal pager 0\n")
self.set_terminal_width(command="terminal width 511\n")
@@ -69,3 +73,29 @@ def set_base_prompt(self, *args, **kwargs):
# strip off (conf.* from base_prompt
self.base_prompt = match.group(1)
return self.base_prompt
+
+ def asa_login(self):
+ """
+ Handle ASA reaching privilege level 15 using login
+
+ twb-dc-fw1> login
+ Username: admin
+ Password: ************
+ """
+ delay_factor = self.select_delay_factor(0)
+
+ i = 1
+ max_attempts = 50
+ self.write_channel("login\n")
+ while i <= max_attempts:
+ time.sleep(.5 * delay_factor)
+ output = self.read_channel()
+ if 'sername' in output:
+ self.write_channel(self.username + '\n')
+ elif 'ssword' in output:
+ self.write_channel(self.password + '\n')
+ elif '#' in output:
+ break
+ else:
+ self.write_channel("login\n")
+ i += 1
diff --git a/netmiko/cisco/cisco_wlc_ssh.py b/netmiko/cisco/cisco_wlc_ssh.py
index e9990d52b..2a357b270 100644
--- a/netmiko/cisco/cisco_wlc_ssh.py
+++ b/netmiko/cisco/cisco_wlc_ssh.py
@@ -4,6 +4,7 @@
import time
from netmiko.base_connection import BaseConnection
+from netmiko.py23_compat import string_types
class CiscoWlcSSH(BaseConnection):
@@ -137,6 +138,9 @@ def send_config_set(self, config_commands=None, exit_config_mode=True, delay_fac
delay_factor = self.select_delay_factor(delay_factor)
if config_commands is None:
return ''
+ elif isinstance(config_commands, string_types):
+ config_commands = (config_commands,)
+
if not hasattr(config_commands, '__iter__'):
raise ValueError("Invalid argument passed into send_config_set")
diff --git a/netmiko/cisco_base_connection.py b/netmiko/cisco_base_connection.py
index ff229d5bc..4614287a2 100644
--- a/netmiko/cisco_base_connection.py
+++ b/netmiko/cisco_base_connection.py
@@ -27,7 +27,7 @@ def check_config_mode(self, check_string=')#', pattern=''):
Cisco IOS devices abbreviate the prompt at 20 chars in config mode
"""
if not pattern:
- pattern = self.base_prompt[:16]
+ pattern = re.escape(self.base_prompt[:16])
return super(CiscoBaseConnection, self).check_config_mode(check_string=check_string,
pattern=pattern)
@@ -38,14 +38,14 @@ def config_mode(self, config_command='config term', pattern=''):
Cisco IOS devices abbreviate the prompt at 20 chars in config mode
"""
if not pattern:
- pattern = self.base_prompt[:16]
+ pattern = re.escape(self.base_prompt[:16])
return super(CiscoBaseConnection, self).config_mode(config_command=config_command,
pattern=pattern)
def exit_config_mode(self, exit_config='end', pattern=''):
"""Exit from configuration mode."""
if not pattern:
- pattern = self.base_prompt[:16]
+ pattern = re.escape(self.base_prompt[:16])
return super(CiscoBaseConnection, self).exit_config_mode(exit_config=exit_config,
pattern=pattern)
diff --git a/netmiko/dell/__init__.py b/netmiko/dell/__init__.py
index d9dd7c4cd..15868b82b 100644
--- a/netmiko/dell/__init__.py
+++ b/netmiko/dell/__init__.py
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
from netmiko.dell.dell_force10_ssh import DellForce10SSH
from netmiko.dell.dell_powerconnect_ssh import DellPowerConnectSSH
+from netmiko.dell.dell_powerconnect_telnet import DellPowerConnectTelnet
-__all__ = ['DellForce10SSH', 'DellPowerConnectSSH']
+__all__ = ['DellForce10SSH', 'DellPowerConnectSSH', 'DellPowerConnectTelnet']
diff --git a/netmiko/dell/dell_powerconnect_telnet.py b/netmiko/dell/dell_powerconnect_telnet.py
new file mode 100644
index 000000000..c4167707a
--- /dev/null
+++ b/netmiko/dell/dell_powerconnect_telnet.py
@@ -0,0 +1,40 @@
+"""Dell Telnet Driver."""
+from __future__ import unicode_literals
+
+import time
+from netmiko.cisco_base_connection import CiscoBaseConnection
+
+
+class DellPowerConnectTelnet(CiscoBaseConnection):
+ def disable_paging(self, command="terminal length 0", delay_factor=1):
+ """Must be in enable mode to disable paging."""
+ debug = False
+
+ self.enable()
+ delay_factor = self.select_delay_factor(delay_factor)
+ time.sleep(delay_factor * .1)
+ self.clear_buffer()
+ command = self.normalize_cmd(command)
+ if debug:
+ print("In disable_paging")
+ print("Command: {}".format(command))
+ self.write_channel(command)
+ output = self.read_until_prompt()
+ if self.ansi_escape_codes:
+ output = self.strip_ansi_escape_codes(output)
+ if debug:
+ print(output)
+ print("Exiting disable_paging")
+ return output
+
+ def telnet_login(self, pri_prompt_terminator='#', alt_prompt_terminator='>',
+ username_pattern=r"User:", pwd_pattern=r"assword",
+ delay_factor=1, max_loops=60):
+ """Telnet login. Can be username/password or just password."""
+ super(DellPowerConnectTelnet, 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/extreme/extreme_ssh.py b/netmiko/extreme/extreme_ssh.py
index 2ef0465d0..2b4c64ebe 100644
--- a/netmiko/extreme/extreme_ssh.py
+++ b/netmiko/extreme/extreme_ssh.py
@@ -1,13 +1,64 @@
"""Extreme support."""
from __future__ import unicode_literals
+
+import re
from netmiko.cisco_base_connection import CiscoSSHConnection
class ExtremeSSH(CiscoSSHConnection):
- """Extreme support."""
+ """Extreme support.
+
+ Designed for EXOS >= 15.0
+ """
def session_preparation(self):
"""Extreme requires enable mode to disable paging."""
self._test_channel_read()
self.enable()
self.set_base_prompt()
self.disable_paging(command="disable clipaging\n")
+
+ def set_base_prompt(self, *args, **kwargs):
+ """
+ Extreme attaches an id to the prompt. The id increases with every command.
+ It needs to br stripped off to match the prompt. Eg.
+
+ testhost.1 #
+ testhost.2 #
+ testhost.3 #
+
+ If new config is loaded and not saved yet, a '* ' prefix appears before the
+ prompt, eg.
+
+ * testhost.4 #
+ * testhost.5 #
+ """
+ cur_base_prompt = super(ExtremeSSH, 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:
+ self.base_prompt = match.group(1)
+ return self.base_prompt
+ else:
+ return self.base_prompt
+
+ def send_command(self, *args, **kwargs):
+ """Extreme needs special handler here due to the prompt changes."""
+
+ # Change send_command behavior to use self.base_prompt
+ kwargs.setdefault('auto_find_prompt', False)
+
+ # refresh self.base_prompt
+ self.set_base_prompt()
+ return super(ExtremeSSH, self).send_command(*args, **kwargs)
+
+ def config_mode(self, config_command=''):
+ """No configuration mode on Extreme."""
+ return ''
+
+ def check_config_mode(self, check_string='#'):
+ """Checks whether in configuration mode. Returns a boolean."""
+ return super(ExtremeSSH, self).check_config_mode(check_string=check_string)
+
+ def exit_config_mode(self, exit_config=''):
+ """No configuration mode on Extreme."""
+ return ''
diff --git a/netmiko/fortinet/fortinet_ssh.py b/netmiko/fortinet/fortinet_ssh.py
index 1f5840a2e..4cfa0084d 100644
--- a/netmiko/fortinet/fortinet_ssh.py
+++ b/netmiko/fortinet/fortinet_ssh.py
@@ -1,9 +1,17 @@
from __future__ import unicode_literals
+import paramiko
from netmiko.cisco_base_connection import CiscoSSHConnection
class FortinetSSH(CiscoSSHConnection):
+ def _modify_connection_params(self):
+ """Modify connection parameters prior to SSH connection."""
+ paramiko.Transport._preferred_kex = ('diffie-hellman-group14-sha1',
+ 'diffie-hellman-group-exchange-sha1',
+ 'diffie-hellman-group-exchange-sha256',
+ 'diffie-hellman-group1-sha1',)
+
def session_preparation(self):
"""Prepare the session after the connection has been established."""
self._test_channel_read()
diff --git a/netmiko/py23_compat.py b/netmiko/py23_compat.py
new file mode 100644
index 000000000..fb753e176
--- /dev/null
+++ b/netmiko/py23_compat.py
@@ -0,0 +1,15 @@
+"""Simplify Python3 compatibility. Modeled after six behavior for small set of things"""
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import sys
+
+PY2 = sys.version_info.major == 2
+PY3 = sys.version_info.major == 3
+
+if sys.version_info.major == 3:
+ string_types = (str,)
+ text_type = str
+else:
+ string_types = (basestring,) # noqa
+ text_type = unicode # noqa
diff --git a/netmiko/scp_handler.py b/netmiko/scp_handler.py
index 21743cc30..6a2649812 100644
--- a/netmiko/scp_handler.py
+++ b/netmiko/scp_handler.py
@@ -312,8 +312,8 @@ def __exit__(self, exc_type, exc_value, traceback):
def _enter_tcl_mode(self):
TCL_ENTER = 'tclsh'
cmd_failed = ['Translating "tclsh"', '% Unknown command', '% Bad IP address']
- output = self.ssh_ctl_chan.send_command_timing(TCL_ENTER, strip_prompt=False,
- strip_command=False)
+ output = self.ssh_ctl_chan.send_command(TCL_ENTER, expect_string=r"\(tcl\)#",
+ strip_prompt=False, strip_command=False)
for pattern in cmd_failed:
if pattern in output:
raise ValueError("Failed to enter tclsh mode on router: {}".format(output))
@@ -364,24 +364,37 @@ def put_file(self):
file_contents = self.source_config
file_contents = self._tcl_newline_rationalize(file_contents)
+ # Try to remove any existing data
+ self.ssh_ctl_chan.clear_buffer()
+
self.ssh_ctl_chan.write_channel(TCL_FILECMD_ENTER)
+ time.sleep(.25)
self.ssh_ctl_chan.write_channel(file_contents)
self.ssh_ctl_chan.write_channel(TCL_FILECMD_EXIT + "\r")
- output = ''
- while True:
- time.sleep(1.5)
- new_output = self.ssh_ctl_chan.read_channel()
- if not new_output:
- break
- output += new_output
+ # This operation can be slow (depends on the size of the file)
+ max_loops = 400
+ sleep_time = 4
+ if self.file_size >= 2500:
+ max_loops = 1500
+ sleep_time = 12
+ elif self.file_size >= 7500:
+ max_loops = 3000
+ sleep_time = 25
+
+ # Initial delay
+ time.sleep(sleep_time)
+
+ # File paste and TCL_FILECMD_exit should be indicated by "router(tcl)#"
+ output = self.ssh_ctl_chan._read_channel_expect(pattern=r"\(tcl\)", max_loops=max_loops)
# The file doesn't write until tclquit
TCL_EXIT = 'tclquit'
self.ssh_ctl_chan.write_channel(TCL_EXIT + "\r")
time.sleep(1)
- output += self.ssh_ctl_chan.read_channel()
+ # Read all data remaining from the TCLSH session
+ output += self.ssh_ctl_chan._read_channel_expect(max_loops=max_loops)
return output
def get_file(self):
diff --git a/netmiko/snmp_autodetect.py b/netmiko/snmp_autodetect.py
index 70924f235..75afc6d68 100644
--- a/netmiko/snmp_autodetect.py
+++ b/netmiko/snmp_autodetect.py
@@ -30,10 +30,11 @@
from netmiko.ssh_dispatcher import CLASS_MAPPER
+
# Higher priority indicates a better match.
SNMP_MAPPER_BASE = {
'arista_eos': {"oid": ".1.3.6.1.2.1.1.1.0",
- "expr": re.compile(r".Arista Networks EOS.", re.IGNORECASE),
+ "expr": re.compile(r".*Arista Networks EOS.*", re.IGNORECASE),
"priority": 99},
'hp_comware': {"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r".*HP Comware.*", re.IGNORECASE),
@@ -59,6 +60,9 @@
'fortinet': {"oid": ".1.3.6.1.2.1.1.1.0",
"expr": re.compile(r"Forti.*", re.IGNORECASE),
"priority": 80},
+ 'checkpoint': {"oid": ".1.3.6.1.4.1.2620.1.6.16.9.0",
+ "expr": re.compile(r"CheckPoint"),
+ "priority": 79},
}
# Ensure all SNMP device types are supported by Netmiko
diff --git a/netmiko/ssh_autodetect.py b/netmiko/ssh_autodetect.py
index c773c3794..17b2de94d 100644
--- a/netmiko/ssh_autodetect.py
+++ b/netmiko/ssh_autodetect.py
@@ -90,7 +90,7 @@
},
'juniper_junos': {
"cmd": "show version | match JUNOS",
- "search_patterns": ["JUNOS Software Release"],
+ "search_patterns": ["JUNOS Software Release", "JUNOS .+ Software"],
"priority": 99,
"dispatch": "_autodetect_std",
},
diff --git a/netmiko/ssh_dispatcher.py b/netmiko/ssh_dispatcher.py
index 6b269ffca..68bf39101 100644
--- a/netmiko/ssh_dispatcher.py
+++ b/netmiko/ssh_dispatcher.py
@@ -16,6 +16,7 @@
from netmiko.brocade import BrocadeNetironSSH
from netmiko.brocade import BrocadeFastironSSH
from netmiko.fortinet import FortinetSSH
+from netmiko.checkpoint import CheckPointGaiaSSH
from netmiko.a10 import A10SSH
from netmiko.avaya import AvayaVspSSH
from netmiko.avaya import AvayaErsSSH
@@ -26,6 +27,7 @@
from netmiko.alcatel import AlcatelSrosSSH
from netmiko.dell import DellForce10SSH
from netmiko.dell import DellPowerConnectSSH
+from netmiko.dell import DellPowerConnectTelnet
from netmiko.paloalto import PaloAltoPanosSSH
from netmiko.quanta import QuantaMeshSSH
from netmiko.aruba import ArubaSSH
@@ -72,6 +74,7 @@
'extreme': ExtremeSSH,
'alcatel_sros': AlcatelSrosSSH,
'fortinet': FortinetSSH,
+ 'checkpoint_gaia': CheckPointGaiaSSH,
'dell_force10': DellForce10SSH,
'dell_powerconnect': DellPowerConnectSSH,
'paloalto_panos': PaloAltoPanosSSH,
@@ -95,12 +98,11 @@
# Add telnet drivers
CLASS_MAPPER['cisco_ios_telnet'] = CiscoIosBase
+CLASS_MAPPER['dell_powerconnect_telnet'] = DellPowerConnectTelnet
CLASS_MAPPER['generic_termserver_telnet'] = TerminalServerTelnet
-# Add general terminal_server driver
+# Add general terminal_server driver and autodetect
CLASS_MAPPER['terminal_server'] = TerminalServerSSH
-
-# Add autodetect driver (mapped to TerminalServerSSH)
CLASS_MAPPER['autodetect'] = TerminalServerSSH
platforms = list(CLASS_MAPPER.keys())
diff --git a/netmiko/utilities.py b/netmiko/utilities.py
index a66adf6e9..6d25d3ddf 100644
--- a/netmiko/utilities.py
+++ b/netmiko/utilities.py
@@ -14,6 +14,7 @@
'hp_comware': 'display current-configuration',
'huawei': 'display current-configuration',
'fortinet': 'show full-configuration',
+ 'checkpoint': 'show configuration',
'cisco_wlc': 'show run-config',
'enterasys': 'show running-config',
'dell_force10': 'show running-config',
diff --git a/netmiko/vyos/vyos_ssh.py b/netmiko/vyos/vyos_ssh.py
index 98faf4283..6a6bd50cb 100644
--- a/netmiko/vyos/vyos_ssh.py
+++ b/netmiko/vyos/vyos_ssh.py
@@ -28,11 +28,11 @@ def check_config_mode(self, check_string='#'):
"""Checks if the device is in configuration mode"""
return super(VyOSSSH, self).check_config_mode(check_string=check_string)
- def config_mode(self, config_command='configure', pattern='[edit]'):
+ def config_mode(self, config_command='configure', pattern=r'[edit]'):
"""Enter configuration mode."""
return super(VyOSSSH, self).config_mode(config_command=config_command, pattern=pattern)
- def exit_config_mode(self, exit_config='exit', pattern='exit'):
+ def exit_config_mode(self, exit_config='exit', pattern=r'exit'):
"""Exit configuration mode"""
output = ""
if self.check_config_mode():
diff --git a/setup.py b/setup.py
index 8b6517895..1129a522d 100644
--- a/setup.py
+++ b/setup.py
@@ -53,6 +53,7 @@ def find_version(*file_paths):
'netmiko/extreme',
'netmiko/f5',
'netmiko/fortinet',
+ 'netmiko/checkpoint',
'netmiko/hp',
'netmiko/huawei',
'netmiko/juniper',
diff --git a/tests/test_asa.sh b/tests/test_asa.sh
index 80b371ce0..0b7b90a0c 100755
--- a/tests/test_asa.sh
+++ b/tests/test_asa.sh
@@ -4,6 +4,8 @@ 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 cisco_asa_login \
+&& py.test -v test_netmiko_config.py --test_device cisco_asa_login \
&& py.test -v test_netmiko_show.py --test_device cisco_asa \
&& py.test -v test_netmiko_config.py --test_device cisco_asa \
|| RETURN_CODE=1
diff --git a/tests/test_cisco.sh b/tests/test_cisco.sh
new file mode 100755
index 000000000..cb4e5f341
--- /dev/null
+++ b/tests/test_cisco.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+RETURN_CODE=0
+
+# Exit on the first test failure and set RETURN_CODE = 1
+echo "Cisco IOS SSH" \
+&& py.test -v test_netmiko_show.py --test_device cisco881_ssh_config \
+&& py.test -v test_netmiko_config.py --test_device cisco881_ssh_config \
+|| RETURN_CODE=1
+
+exit $RETURN_CODE
+
diff --git a/tests/test_netmiko_config.py b/tests/test_netmiko_config.py
index f8bf6f368..275e3e96d 100755
--- a/tests/test_netmiko_config.py
+++ b/tests/test_netmiko_config.py
@@ -56,22 +56,18 @@ def test_exit_config_mode(net_connect, commands, expected_responses):
assert net_connect.check_config_mode() == False
def test_command_set(net_connect, commands, expected_responses):
- '''
- Test sending configuration commands
- '''
- print(1)
+ """Test sending configuration commands."""
config_commands = commands['config']
support_commit = commands.get('support_commit')
config_verify = commands['config_verification']
- print(2)
- net_connect.send_config_set(config_commands[0:1])
+ # Set to initial value and testing sending command as a string
+ net_connect.send_config_set(config_commands[0])
if support_commit:
net_connect.commit()
- print(3)
cmd_response = expected_responses.get('cmd_response_init')
- config_commands_output = net_connect.send_command_expect(config_verify)
+ config_commands_output = net_connect.send_command(config_verify)
print(config_verify)
print(config_commands_output)
if cmd_response:
@@ -79,15 +75,12 @@ def test_command_set(net_connect, commands, expected_responses):
else:
assert config_commands[0] in config_commands_output
- print(4)
net_connect.send_config_set(config_commands)
if support_commit:
net_connect.commit()
- print(5)
cmd_response = expected_responses.get('cmd_response_final')
config_commands_output = net_connect.send_command_expect(config_verify)
- print(6)
if cmd_response:
assert cmd_response in config_commands_output
else:
diff --git a/tests/test_suite_alt.sh b/tests/test_suite_alt.sh
index 2c09d705e..5ab0bb7b1 100755
--- a/tests/test_suite_alt.sh
+++ b/tests/test_suite_alt.sh
@@ -7,12 +7,22 @@ echo "Starting tests...good luck:" \
&& echo "Linux SSH (using keys)" \
&& py.test -s -v test_netmiko_show.py --test_device linux_srv1 \
\
+&& echo "Cisco IOS SSH (including SCP) using key auth" \
+&& py.test -v test_netmiko_scp.py --test_device cisco881_key \
+&& py.test -v test_netmiko_tcl.py --test_device cisco881_key \
+&& py.test -v test_netmiko_show.py --test_device cisco881_key \
+&& py.test -v test_netmiko_config.py --test_device cisco881_key \
+\
&& echo "Cisco IOS SSH (including SCP)" \
&& py.test -v test_netmiko_scp.py --test_device cisco881 \
&& py.test -v test_netmiko_tcl.py --test_device cisco881 \
&& py.test -v test_netmiko_show.py --test_device cisco881 \
&& py.test -v test_netmiko_config.py --test_device cisco881 \
\
+&& echo "Cisco IOS using SSH config with SSH Proxy" \
+&& py.test -v test_netmiko_show.py --test_device cisco881_ssh_config \
+&& py.test -v test_netmiko_config.py --test_device cisco881_ssh_config \
+\
&& echo "Cisco IOS telnet" \
&& py.test -v test_netmiko_show.py --test_device cisco881_telnet \
&& py.test -v test_netmiko_config.py --test_device cisco881_telnet \
@@ -41,6 +51,8 @@ echo "Starting tests...good luck:" \
&& echo "Cisco ASA" \
&& py.test -v test_netmiko_show.py --test_device cisco_asa \
&& py.test -v test_netmiko_config.py --test_device cisco_asa \
+&& py.test -v test_netmiko_show.py --test_device cisco_asa_login \
+&& py.test -v test_netmiko_config.py --test_device cisco_asa_login \
\
&& echo "Cisco IOS-XR" \
&& py.test -v test_netmiko_show.py --test_device cisco_xrv \