def _Connect(self, username, password=None, ssh_keys=None, enable_password=None, ssl_cert_set=None): _ = ssl_cert_set self._connection = pexpect_connection.ParamikoSshConnection( self.loopback_ipv4, username, password, self._success, timeout=self.timeout_connect, find_prompt=True, ssh_keys=ssh_keys, enable_password=enable_password) try: self._connection.Connect() self._DisablePager() self.connected = True except pexpect_connection.ConnectionError as e: self.connected = False raise exceptions.ConnectError(e) except pexpect_connection.TimeoutError as e: self.connected = False raise exceptions.ConnectError( 'Timed out connecting to %s(%s) after ' '%s seconds.' % (self.host, self.loopback_ipv4, str(e)))
def _DisablePager(self): """Disables the pager.""" try: self._connection.child.send('\r') self._connection.child.expect(r'\r\n', timeout=self.timeout_connect) self._connection.child.expect(self._connection.re_prompt, timeout=self.timeout_connect, searchwindowsize=128) self._connection.child.send('terminal length 0\r') pindex = self._connection.child.expect( [ self._connection.re_prompt, r'Command authorization failed\.' ], timeout=self.timeout_connect) if pindex == 1: self.connected = False raise exceptions.ConnectError( 'terminal length 0 command denied.') # Pause momentarily to avoid a TAC+ packet drop. time.sleep(0.5) except (pexpect.EOF, pexpect.TIMEOUT) as e: self.connected = False raise exceptions.ConnectError('%s: %s' % (e.__class__, str(e))) logging.debug('terminal length set to 0')
def _Connect(self, username=None, password=None, ssh_keys=None, enable_password=None, ssl_cert_set=None): _ = enable_password, ssl_cert_set self._connection = pexpect_connection.ParamikoSshConnection( self.loopback_ipv4, username, password, self._success, timeout=self.timeout_connect, find_prompt=True, ssh_keys=ssh_keys, # Brocade case 1101014 - \n\r\0 newlines in some 'tm voq' outputs. ssh_client=self.ssh_client, find_prompt_prefix=r'(?:^|\n|\n\r\0)') try: self._connection.Connect() self._DisablePager() self.connected = True except pexpect_connection.ConnectionError, e: self.connected = False raise exceptions.ConnectError(e)
class HpDevice(base_device.BaseDevice): """A base device model for Hewlett-Packard ProCurve switches.""" RE_INVALID = re.compile(r'^(Invalid|Ambiguous) input:', re.I | re.M) RE_PAGER = re.compile(r'-- MORE --, next page: Space, next line: Enter, ' 'quit: Control-C') def __init__(self, **kwargs): self.vendor_name = 'hp' super(HpDevice, self).__init__(**kwargs) # The response regexp indicating connection success. self._success = r'ProCurve .*[Ss]witch' def _Connect(self, username, password=None, ssh_keys=None, enable_password=None, ssl_cert_set=None): # Quieten pylint. _ = ssl_cert_set self._connection = pexpect_connection.HpSshFilterConnection( self.loopback_ipv4, username, password, success=self._success, timeout=self.timeout_connect, find_prompt=True, ssh_keys=ssh_keys, enable_password=enable_password) try: self._connection.Connect() self._DisablePager() except pexpect_connection.ConnectionError, e: self.connected = False raise exceptions.ConnectError(e) except pexpect_connection.TimeoutError, e: self.connected = False raise exceptions.ConnectError('Timed out connecting to %s(%s) after ' '%s seconds.' % (self.host, self.loopback_ipv4, str(e)))
def _DisablePager(self): """Disables the paging.""" try: self._connection.child.send('no paging\r') self._connection.child.expect(self._connection.re_prompt, timeout=self.timeout_connect, searchwindowsize=128) except (pexpect.EOF, pexpect.TIMEOUT) as e: self.connected = False raise exceptions.ConnectError('%s: %s' % (e.__class__, str(e))) logging.debug('Disabled paging on aruba device')
class BrocadeDevice(base_device.BaseDevice): """A common superclass for Brocade devices.""" verboten_commands = ( 'monitor ', 'terminal length ', 'terminal monitor', 'page-display', 'quit', 'exit', ) verboten_config = ('quit', ) disable_pager_command = '' def __init__(self, **kwargs): self.ssh_client = kwargs.pop('ssh_client', None) super(BrocadeDevice, self).__init__(**kwargs) self._success = r'(?:^|\n)([A-Za-z0-9@\.\-]+[>#])' def _Connect(self, username=None, password=None, ssh_keys=None, enable_password=None, ssl_cert_set=None): _ = enable_password, ssl_cert_set self._connection = pexpect_connection.ParamikoSshConnection( self.loopback_ipv4, username, password, self._success, timeout=self.timeout_connect, find_prompt=True, ssh_keys=ssh_keys, # Brocade case 1101014 - \n\r\0 newlines in some 'tm voq' outputs. ssh_client=self.ssh_client, find_prompt_prefix=r'(?:^|\n|\n\r\0)') try: self._connection.Connect() self._DisablePager() self.connected = True except pexpect_connection.ConnectionError, e: self.connected = False raise exceptions.ConnectError(e) except pexpect_connection.TimeoutError, e: self.connected = False raise exceptions.ConnectError( 'Timed out connecting to %s(%s) after ' '%s seconds.' % (self.host, self.loopback_ipv4, str(e)))
def _Connect(self, username, password=None, ssh_keys=None, enable_password=None, ssl_cert_set=None): # Quieten pylint. _ = ssl_cert_set self._connection = pexpect_connection.HpSshFilterConnection( self.loopback_ipv4, username, password, success=self._success, timeout=self.timeout_connect, find_prompt=True, ssh_keys=ssh_keys, enable_password=enable_password) try: self._connection.Connect() self._DisablePager() except pexpect_connection.ConnectionError, e: self.connected = False raise exceptions.ConnectError(e)
def Connect(self, username, password=None, ssh_keys=None, enable_password=None, ssl_cert_set=None): """Sets up a connection to the device. Concrete classes must implement _Connect() instead, with the same arguments. Concrete classes are expected not to disconnect the connection until it is cleaned-up by Disconnect(). A generic exception handler at the top- level should ensure sessions have an opportunity to be cleaned-up upon abnormal program termination. Args: username: A string, the username (role account) to use. password: A string, the password to use (optional; may be None). ssh_keys: A tuple of strings, SSH private keys (optional; may be None). enable_password: A string, an optional enable password (may be None). ssl_cert_set: An optional SSLCertificateSet protobuf (may be None). Raises: exceptions.ConnectError: the connection could not be established. exceptions.AuthenticationError: A device authentication error occurred, or neither a password nor an SSH private key was supplied. """ # Either an SSH key or password must be supplied for authentication. if (password is None and not ssh_keys and not ssl_cert_set and not FLAGS.use_ssh_agent): raise exceptions.AuthenticationError( 'Cannot connect. No authentication information provided to device ' 'Connect method.') self._username = username self._password = password self._ssh_keys = ssh_keys or () self._enable_password = enable_password self._ssl_cert_set = ssl_cert_set if not self.loopback_ipv4 and not self.accessproxy_device_dict: raise exceptions.ConnectError( 'Device %r, or any access proxies, need to have an IPv4 ' 'management address.' % self.host) logging.debug('In BaseDevice.Connect, host is %s, _connected is %s', self.host, self._connected) while not self.connected: try: if self._host_status: logging.debug('CONNECTING %s(%s)', self.host, self.loopback_ipv4) self._Connect(username, password=password, ssh_keys=self._ssh_keys, enable_password=enable_password, ssl_cert_set=ssl_cert_set) self.connected = True logging.debug('CONNECTED %s(%s)', self.host, self.loopback_ipv4) self._last_failure_time = None else: self._HostDownPrepareConnect() except (exceptions.ConnectError, exceptions.AuthenticationError), e: logging.error('CONNECT FAILURE %s(%s)', self.host, self.loopback_ipv4) self._host_status = False self.__exc = e raise
def Connect(hostname, username, password=None, port=22, ssh_keys=(), timeout=TIMEOUT_DEFAULT): """Makes a paramiko SSH connection to a device. Args: hostname: A string, the hostname or IP address to connect to. username: A string, the username to use on the connection. password: A string, the password to use on the connection. port: An int, the port number to connect to. ssh_keys: A tuple of strings, SSH private keys (optional; may be None). timeout: A float, the number of seconds before a connection times out. Returns: A paramiko.SSHClient() instance """ options = SshOptions() hostname, port, username = options.Lookup(hostname, port, username) ssh_client = None def RaiseError(e, msg): """Raises an exception, disconnecting the SSH client. Args: e: An Exception. msg: An object, exception arguments. """ raise e(msg) try: ssh_client = paramiko.SSHClient() # Always auto-add remote SSH host keys. ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh_client.load_system_host_keys() # Connect using paramiko with a timeout parameter (requires paramiko 1.7) if ssh_keys: pkeys = [] for key in ssh_keys: logging.debug( 'Using SSH private key for device authentication.') # Use a virtual temporary file to store the key. ssh_key_fileobj = cStringIO.StringIO() ssh_key_fileobj.write(key) ssh_key_fileobj.reset() try: pkeys.append(paramiko.DSSKey(file_obj=ssh_key_fileobj)) logging.debug('Using SSH DSA key for %r', hostname) except (IndexError, paramiko.SSHException) as e: if (isinstance(e, IndexError) or 'not a valid DSA private key file' in str(e)): ssh_key_fileobj.reset() try: logging.debug('Using SSH RSA key for %r', hostname) pkeys.append( paramiko.RSAKey(file_obj=ssh_key_fileobj)) except (IndexError, paramiko.SSHException) as e: raise exceptions.AuthenticationError(str(e)) else: raise exceptions.ConnectError('SSHException: %s' % str(e)) else: logging.debug('Using password for %r', hostname) pkeys = [None] for pkey in pkeys: saved_exception = None try: ssh_client.connect(hostname=hostname, port=port, username=username, password=password, pkey=pkey, timeout=timeout, allow_agent=FLAGS.use_ssh_agent, look_for_keys=False) break except (paramiko.AuthenticationException, paramiko.SSHException) as e: saved_exception = e if saved_exception is not None: raise saved_exception # pylint: disable=raising-bad-type transport = ssh_client.get_transport() # Sometimes we have to authenticate a second time, eg. on Force10 # we always fail the first authentication (if we try pkey + pass, # the pass succeeds; but if we do pass only, we have to do it # twice). connect() above will have authenticated once. if not transport.is_authenticated(): if pkeys != [None]: for pkey in pkeys: try: transport.auth_publickey(username, pkey) break except paramiko.SSHException: pass if not transport.is_authenticated(): if password is not None: try: transport.auth_password(username, password) except paramiko.SSHException: pass if not transport.is_authenticated(): msg = 'Not authenticated after two attempts on %r' % hostname RaiseError(exceptions.ConnectError, msg) except EOFError: msg = 'EOFError connecting to: %r' % hostname RaiseError(exceptions.ConnectError, msg) except paramiko.AuthenticationException as e: msg = 'Authentication error connecting to %s: %s' % (hostname, str(e)) RaiseError(exceptions.AuthenticationError, msg) except paramiko.SSHException as e: msg = 'SSHException connecting to %s: %s' % (hostname, str(e)) RaiseError(exceptions.ConnectError, msg) except socket.timeout as e: msg = 'Timed-out while connecting to %s: %s' % (hostname, str(e)) RaiseError(exceptions.ConnectError, msg) except socket.error as e: msg = 'Socket error connecting to %r: %s %s' % (hostname, e.__class__, e) RaiseError(exceptions.ConnectError, msg) return ssh_client
def _Cmd(self, command, mode=None, merge_stderr_first=False, send=None, require_low_chanid=False): response = '' retries_left = 1 while True: try: chan = self._ssh_client.get_transport().open_session() chan.settimeout(self.timeout_response) if require_low_chanid and chan.remote_chanid > _LOW_CHANID_THRESHOLD: # We should not be having multiple channels open. If we do, # close them before proceeding. logging.error( 'Remote ssh channel id %d exceeded %d when opening session to ' '%s(%s), reconnecting.', chan.remote_chanid, _LOW_CHANID_THRESHOLD, self.host, self.loopback_ipv4) self.Disconnect() self.Connect(self._username, self._password, self._ssh_keys, self._enable_password) chan = self._ssh_client.get_transport().open_session() chan.exec_command(command) stdin = chan.makefile('wb', -1) stdout = chan.makefile('rb', -1) stderr = chan.makefile_stderr('rb', -1) if send is not None: stdin.write(send) stdout_data = stdout.read() stderr_data = stderr.read() # Request channel close by remote peer. chan.close() break except paramiko.SSHException as e: msg = str(e) logging.error('%s(%s) Cmd(%r, mode=%r): %s', self.host, self.loopback_ipv4, command, mode, msg) raise exceptions.CmdError(msg) except AttributeError: # This occurs when self._ssh_client becomes None after a Paramiko # failure. Pause momentarily, try to reconnect and loop to resend # the command. time.sleep(0.25) try: if retries_left: self._Connect(self._username, self._password, self._ssh_keys) retries_left -= 1 continue else: raise exceptions.CmdError( 'Failed to exec_command after retry.') except paramiko.SSHException as e: msg = str(e) logging.error('%s(%s) Cmd(%r, mode=%r): %s', self.host, self.loopback_ipv4, command, mode, msg) raise exceptions.ConnectError(msg) except Exception as e: # Paramiko may raise any exception, so catch and log it here. msg = '%s:%s(%s) Cmd(%r, mode=%r): %s: %s' % ( type(e), self.host, self.loopback_ipv4, command, mode, e.__class__.__name__, str(e)) logging.exception(msg) raise exceptions.CmdError('%s: %s' % (e.__class__.__name__, str(e))) # Remove stderr lines started with 'waiting for'. if stderr_data and not merge_stderr_first: out = [] for l in stderr_data.splitlines(): if not l.startswith('waiting for'): out.append(l) stderr_data = '\n'.join(out) # Marshal the response from the stdout/err channels and handle errors. if stderr_data and not merge_stderr_first: raise exceptions.CmdError(stderr_data) elif stdout_data: if merge_stderr_first and stderr_data: response = stderr_data response += stdout_data else: # Sometimes, a command (e.g., 'show system license keys') returns # nothing. This can mean that the channel went away on us, and we # got no data back (and no error). if self.connected: logging.warn('Both STDOUT and STDERR empty after %s on %s(%s)', repr(command), self.host, self.loopback_ipv4) else: raise exceptions.CmdError( 'Connection to %s(%s) was terminated.' % (self.host, self.loopback_ipv4)) return response