def _init(self, retries=1): self.session = Session() if self.timeout: # libssh2 timeout is in ms self.session.set_timeout(self.timeout * 1000) try: self.session.handshake(self.sock) except Exception as ex: while retries < self.num_retries: return self._connect_init_retry(retries) msg = "Error connecting to host %s:%s - %s" logger.error(msg, self.host, self.port, ex) if isinstance(ex, SSH2Timeout): raise Timeout(msg, self.host, self.port, ex) raise try: self.auth() except Exception as ex: while retries < self.num_retries: return self._connect_init_retry(retries) msg = "Authentication error while connecting to %s:%s - %s" raise AuthenticationException(msg, self.host, self.port, ex) self.session.set_blocking(0) if self.keepalive_seconds: self.configure_keepalive() self._keepalive_greenlet = self.spawn_send_keepalive()
def _connect(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self._host, self._port)) self._sess = Session() self._sess.handshake(sock) if self._auth == 'key': # self._pubkey_path must be generated from private key # s.userauth_publickey_fromfile(self._user, self._pubkey_path, # self._key_path, '') raise IrmaSFTPv2Error("Pub key authentication not implemented") else: self._sess.userauth_password(self._user, self._passwd) self._client = self._sess.sftp_init()
def sshconn(host, user, password, cmd): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, 22)) session = Session() session.handshake(sock) session.userauth_password(user, password) channel = session.open_session() channel.execute(cmd) size, data = channel.read() while size > 0: print(data.decode()) size, data = channel.read() channel.close() a = channel.get_exit_status() if a == 0: logger.info("Exit status: {0}".format(channel.get_exit_status())) else: logger.error("Exit status: {0} - verify the command".format( channel.get_exit_status()))
def main(): args = parser.parse_args() if not os.path.isfile(args.privatekey): print("No such private key %s" % (args.privatekey, )) sys.exit(1) publickey = "%s.pub" % (args.privatekey, ) if not os.path.isfile(publickey): print("Expected public key at %s, found none" % (publickey, )) sys.exit(1) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((args.host, args.port)) s = Session() s.handshake(sock) s.userauth_publickey_fromfile(args.user, publickey, args.privatekey, args.passphrase) chan = s.open_session() chan.execute(args.cmd) size, data = chan.read() while size > 0: print(data) size, data = chan.read()
def main(): user = '******' host = '192.168.100.201' # publickey = os.path.expanduser('~\\.ssh\\id_rsa-sag-golddrive.pub') privatekey = os.path.expanduser('~\\.ssh\\id_rsa-sag-golddrive') if not os.path.isfile(privatekey): print("No such private key %s" % (privatekey,)) if not os.path.isfile(privatekey): print("No such public key %s" % (publickey,)) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, 22)) s = Session() s.handshake(sock) s.userauth_publickey_fromfile(user, privatekey) sftp = s.sftp_init() now = datetime.now() print("Starting read for remote file...") with sftp.open('/tmp/file.bin', LIBSSH2_FXF_READ, LIBSSH2_SFTP_S_IRUSR) as r, \ open('file.bin','wb') as w: for size, data in r: w.write(data) print("Finished file read in %s" % (datetime.now() - now,))
class SSHClient(object): """ssh2-python (libssh2) based non-blocking SSH client.""" IDENTITIES = ( os.path.expanduser('~/.ssh/id_rsa'), os.path.expanduser('~/.ssh/id_dsa'), os.path.expanduser('~/.ssh/identity'), os.path.expanduser('~/.ssh/id_ecdsa'), ) def __init__(self, host, user=None, password=None, port=None, pkey=None, num_retries=DEFAULT_RETRIES, retry_delay=RETRY_DELAY, allow_agent=True, timeout=None, forward_ssh_agent=False, proxy_host=None, _auth_thread_pool=True, keepalive_seconds=60): """:param host: Host name or IP to connect to. :type host: str :param user: User to connect as. Defaults to logged in user. :type user: str :param password: Password to use for password authentication. :type password: str :param port: SSH port to connect to. Defaults to SSH default (22) :type port: int :param pkey: Private key file path to use for authentication. Path must be either absolute path or relative to user home directory like ``~/<path>``. :type pkey: str :param num_retries: (Optional) Number of connection and authentication attempts before the client gives up. Defaults to 3. :type num_retries: int :param retry_delay: Number of seconds to wait between retries. Defaults to :py:class:`pssh.constants.RETRY_DELAY` :type retry_delay: int :param timeout: SSH session timeout setting in seconds. This controls timeout setting of authenticated SSH sessions. :type timeout: int :param allow_agent: (Optional) set to False to disable connecting to the system's SSH agent :type allow_agent: bool :param forward_ssh_agent: (Optional) Turn on SSH agent forwarding - equivalent to `ssh -A` from the `ssh` command line utility. Defaults to True if not set. :type forward_ssh_agent: bool :param proxy_host: Connection to host is via provided proxy host and client should use self.proxy_host for connection attempts. :type proxy_host: str :param keepalive_seconds: Interval of keep alive messages being sent to server. Set to ``0`` or ``False`` to disable. :raises: :py:class:`pssh.exceptions.PKeyFileError` on errors finding provided private key. """ self.host = host self.user = user if user else None if self.user is None and not WIN_PLATFORM: self.user = pwd.getpwuid(os.geteuid()).pw_name elif self.user is None and WIN_PLATFORM: raise ValueError("Must provide user parameter on Windows") self.password = password self.port = port if port else 22 self.num_retries = num_retries self.sock = None self.timeout = timeout self.retry_delay = retry_delay self.allow_agent = allow_agent self.forward_ssh_agent = forward_ssh_agent self._forward_requested = False self.session = None self.keepalive_seconds = keepalive_seconds self._keepalive_greenlet = None self._host = proxy_host if proxy_host else host self.pkey = _validate_pkey_path(pkey, self.host) self._connect(self._host, self.port) if _auth_thread_pool: THREAD_POOL.apply(self._init) else: self._init() def disconnect(self): """Disconnect session, close socket if needed.""" if self.session is not None: try: self._eagain(self.session.disconnect) except Exception: pass if self.sock is not None and not self.sock.closed: self.sock.close() def __del__(self): self.disconnect() def __enter__(self): return self def __exit__(self, *args): self.disconnect() def spawn_send_keepalive(self): """Spawns a new greenlet that sends keep alive messages every self.keepalive_seconds""" return spawn(self._send_keepalive) def _send_keepalive(self): while True: sleep(self._eagain(self.session.keepalive_send)) def configure_keepalive(self): self.session.keepalive_config(False, self.keepalive_seconds) def _connect_init_retry(self, retries): retries += 1 self.session = None if not self.sock.closed: self.sock.close() sleep(self.retry_delay) self._connect(self._host, self.port, retries=retries) return self._init(retries=retries) def _init(self, retries=1): self.session = Session() if self.timeout: # libssh2 timeout is in ms self.session.set_timeout(self.timeout * 1000) try: self.session.handshake(self.sock) except Exception as ex: while retries < self.num_retries: return self._connect_init_retry(retries) msg = "Error connecting to host %s:%s - %s" logger.error(msg, self.host, self.port, ex) if isinstance(ex, SSH2Timeout): raise Timeout(msg, self.host, self.port, ex) raise try: self.auth() except Exception as ex: while retries < self.num_retries: return self._connect_init_retry(retries) msg = "Authentication error while connecting to %s:%s - %s" raise AuthenticationException(msg, self.host, self.port, ex) self.session.set_blocking(0) if self.keepalive_seconds: self.configure_keepalive() self._keepalive_greenlet = self.spawn_send_keepalive() def _connect(self, host, port, retries=1): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if self.timeout: self.sock.settimeout(self.timeout) logger.debug("Connecting to %s:%s", host, port) try: self.sock.connect((host, port)) except sock_gaierror as ex: logger.error("Could not resolve host '%s' - retry %s/%s", host, retries, self.num_retries) while retries < self.num_retries: sleep(self.retry_delay) return self._connect(host, port, retries=retries+1) raise UnknownHostException("Unknown host %s - %s - retry %s/%s", host, str(ex.args[1]), retries, self.num_retries) except sock_error as ex: logger.error("Error connecting to host '%s:%s' - retry %s/%s", host, port, retries, self.num_retries) while retries < self.num_retries: sleep(self.retry_delay) return self._connect(host, port, retries=retries+1) error_type = ex.args[1] if len(ex.args) > 1 else ex.args[0] raise ConnectionErrorException( "Error connecting to host '%s:%s' - %s - retry %s/%s", host, port, str(error_type), retries, self.num_retries,) def _pkey_auth(self): self.session.userauth_publickey_fromfile( self.user, self.pkey, passphrase=self.password if self.password is not None else '') def _identity_auth(self): passphrase = self.password if self.password is not None else '' for identity_file in self.IDENTITIES: if not os.path.isfile(identity_file): continue logger.debug( "Trying to authenticate with identity file %s", identity_file) try: self.session.userauth_publickey_fromfile( self.user, identity_file, passphrase=passphrase) except Exception: logger.debug("Authentication with identity file %s failed, " "continuing with other identities", identity_file) continue else: logger.debug("Authentication succeeded with identity file %s", identity_file) return raise AuthenticationException("No authentication methods succeeded") def auth(self): if self.pkey is not None: logger.debug( "Proceeding with private key file authentication") return self._pkey_auth() if self.allow_agent: try: self.session.agent_auth(self.user) except Exception as ex: logger.debug("Agent auth failed with %s, " "continuing with other authentication methods", ex) else: logger.debug("Authentication with SSH Agent succeeded") return try: self._identity_auth() except AuthenticationException: if self.password is None: raise logger.debug("Private key auth failed, trying password") self._password_auth() def _password_auth(self): try: self.session.userauth_password(self.user, self.password) except Exception: raise AuthenticationException("Password authentication failed") def open_session(self): """Open new channel from session""" try: chan = self.session.open_session() except Exception as ex: raise SessionError(ex) while chan == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) try: chan = self.session.open_session() except Exception as ex: raise SessionError(ex) # Multiple forward requests result in ChannelRequestDenied # errors, flag is used to avoid this. if self.forward_ssh_agent and not self._forward_requested: if not hasattr(chan, 'request_auth_agent'): warn("Requested SSH Agent forwarding but libssh2 version used " "does not support it - ignoring") return chan self._eagain(chan.request_auth_agent) self._forward_requested = True return chan def execute(self, cmd, use_pty=False, channel=None): """Execute command on remote server. :param cmd: Command to execute. :type cmd: str :param use_pty: Whether or not to obtain a PTY on the channel. :type use_pty: bool :param channel: Use provided channel for execute rather than creating a new one. :type channel: :py:class:`ssh2.channel.Channel` """ channel = self.open_session() if channel is None else channel if use_pty: self._eagain(channel.pty) logger.debug("Executing command '%s'" % cmd) self._eagain(channel.execute, cmd) return channel def read_stderr(self, channel, timeout=None): """Read standard error buffer from channel. :param channel: Channel to read output from. :type channel: :py:class:`ssh2.channel.Channel` """ return _read_output(self.session, channel.read_stderr, timeout=timeout) def read_output(self, channel, timeout=None): """Read standard output buffer from channel. :param channel: Channel to read output from. :type channel: :py:class:`ssh2.channel.Channel` """ return _read_output(self.session, channel.read, timeout=timeout) def _select_timeout(self, func, timeout): ret = func() while ret == LIBSSH2_ERROR_EAGAIN: wait_select(self.session, timeout=timeout) ret = func() if ret == LIBSSH2_ERROR_EAGAIN and timeout is not None: raise Timeout def wait_finished(self, channel, timeout=None): """Wait for EOF from channel and close channel. Used to wait for remote command completion and be able to gather exit code. :param channel: The channel to use. :type channel: :py:class:`ssh2.channel.Channel` """ if channel is None: return # If .eof() returns EAGAIN after a select with a timeout, it means # it reached timeout without EOF and _select_timeout will raise # timeout exception causing the channel to appropriately # not be closed as the command is still running. self._select_timeout(channel.wait_eof, timeout) # Close channel to indicate no more commands will be sent over it self.close_channel(channel) def close_channel(self, channel): logger.debug("Closing channel") self._eagain(channel.close) def _eagain(self, func, *args, **kwargs): ret = func(*args, **kwargs) while ret == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) ret = func(*args, **kwargs) return ret def read_output_buffer(self, output_buffer, prefix=None, callback=None, callback_args=None, encoding='utf-8'): """Read from output buffers and log to ``host_logger``. :param output_buffer: Iterator containing buffer :type output_buffer: iterator :param prefix: String to prefix log output to ``host_logger`` with :type prefix: str :param callback: Function to call back once buffer is depleted: :type callback: function :param callback_args: Arguments for call back function :type callback_args: tuple """ prefix = '' if prefix is None else prefix for line in output_buffer: output = line.decode(encoding) host_logger.info("[%s]%s\t%s", self.host, prefix, output) yield output if callback: callback(*callback_args) def run_command(self, command, sudo=False, user=None, use_pty=False, shell=None, encoding='utf-8', timeout=None): """Run remote command. :param command: Command to run. :type command: str :param sudo: Run command via sudo as super-user. :type sudo: bool :param user: Run command as user via sudo :type user: str :param use_pty: Whether or not to obtain a PTY on the channel. :type use_pty: bool :param shell: (Optional) Override shell to use to run command with. Defaults to login user's defined shell. Use the shell's command syntax, eg `shell='bash -c'` or `shell='zsh -c'`. :type shell: str :param encoding: Encoding to use for output. Must be valid `Python codec <https://docs.python.org/2.7/library/codecs.html>`_ :type encoding: str :rtype: (channel, host, stdout, stderr, stdin) tuple. """ # Fast path for no command substitution needed if not sudo and not user and not shell: _command = command else: _command = '' if sudo and not user: _command = 'sudo -S ' elif user: _command = 'sudo -u %s -S ' % (user,) _shell = shell if shell else '$SHELL -c' _command += "%s '%s'" % (_shell, command,) channel = self.execute(_command, use_pty=use_pty) return channel, self.host, \ self.read_output_buffer( self.read_output(channel, timeout=timeout), encoding=encoding), \ self.read_output_buffer( self.read_stderr(channel, timeout=timeout), encoding=encoding, prefix='\t[err]'), channel def _make_sftp(self): """Make SFTP client from open transport""" try: sftp = self.session.sftp_init() except Exception as ex: raise SFTPError(ex) while sftp == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) try: sftp = self.session.sftp_init() except Exception as ex: raise SFTPError(ex) return sftp def _mkdir(self, sftp, directory): """Make directory via SFTP channel :param sftp: SFTP client object :type sftp: :py:class:`ssh2.sftp.SFTP` :param directory: Remote directory to create :type directory: str :raises: :py:class:`pssh.exceptions.SFTPIOError` on SFTP IO errors """ mode = LIBSSH2_SFTP_S_IRUSR | \ LIBSSH2_SFTP_S_IWUSR | \ LIBSSH2_SFTP_S_IXUSR | \ LIBSSH2_SFTP_S_IRGRP | \ LIBSSH2_SFTP_S_IROTH | \ LIBSSH2_SFTP_S_IXGRP | \ LIBSSH2_SFTP_S_IXOTH try: self._eagain(sftp.mkdir, directory, mode) except SFTPProtocolError as error: msg = "Error occured creating directory %s on host %s - %s" logger.error(msg, directory, self.host, error) raise SFTPIOError(msg, directory, self.host, error) logger.debug("Created remote directory %s", directory) def copy_file(self, local_file, remote_file, recurse=False, sftp=None, _dir=None): """Copy local file to host via SFTP. :param local_file: Local filepath to copy to remote host :type local_file: str :param remote_file: Remote filepath on remote host to copy file to :type remote_file: str :param recurse: Whether or not to descend into directories recursively. :type recurse: bool :raises: :py:class:`ValueError` when a directory is supplied to ``local_file`` and ``recurse`` is not set :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation errors :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors writing via SFTP :raises: :py:class:`IOError` on local file IO errors :raises: :py:class:`OSError` on local OS errors like permission denied """ sftp = self._make_sftp() if sftp is None else sftp if os.path.isdir(local_file) and recurse: return self._copy_dir(local_file, remote_file, sftp) elif os.path.isdir(local_file) and not recurse: raise ValueError("Recurse must be true if local_file is a " "directory.") destination = self._remote_paths_split(remote_file) if destination is not None: try: self._eagain(sftp.stat, destination) except (SFTPHandleError, SFTPProtocolError): self.mkdir(sftp, destination) self.sftp_put(sftp, local_file, remote_file) logger.info("Copied local file %s to remote destination %s:%s", local_file, self.host, remote_file) def _sftp_put(self, remote_fh, local_file): with open(local_file, 'rb', 2097152) as local_fh: for data in local_fh: eagain_write(remote_fh.write, data, self.session) def sftp_put(self, sftp, local_file, remote_file): mode = LIBSSH2_SFTP_S_IRUSR | \ LIBSSH2_SFTP_S_IWUSR | \ LIBSSH2_SFTP_S_IRGRP | \ LIBSSH2_SFTP_S_IROTH f_flags = LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC with self._sftp_openfh( sftp.open, remote_file, f_flags, mode) as remote_fh: try: self._sftp_put(remote_fh, local_file) # THREAD_POOL.apply( # sftp_put, args=(self.session, remote_fh, local_file)) except SFTPProtocolError as ex: msg = "Error writing to remote file %s - %s" logger.error(msg, remote_file, ex) raise SFTPIOError(msg, remote_file, ex) def mkdir(self, sftp, directory, _parent_path=None): """Make directory via SFTP channel. Parent paths in the directory are created if they do not exist. :param sftp: SFTP client object :type sftp: :py:class:`paramiko.sftp_client.SFTPClient` :param directory: Remote directory to create :type directory: str Catches and logs at error level remote IOErrors on creating directory. """ try: _dir, sub_dirs = directory.split('/', 1) except ValueError: _dir = directory.split('/', 1)[0] sub_dirs = None if not _dir and directory.startswith('/'): try: _dir, sub_dirs = sub_dirs.split(os.path.sep, 1) except ValueError: return True if _parent_path is not None: _dir = '/'.join((_parent_path, _dir)) try: self._eagain(sftp.stat, _dir) except (SFTPHandleError, SFTPProtocolError) as ex: logger.debug("Stat for %s failed with %s", _dir, ex) self._mkdir(sftp, _dir) if sub_dirs is not None: if directory.startswith('/'): _dir = ''.join(('/', _dir)) return self.mkdir(sftp, sub_dirs, _parent_path=_dir) def _copy_dir(self, local_dir, remote_dir, sftp): """Call copy_file on every file in the specified directory, copying them to the specified remote directory.""" file_list = os.listdir(local_dir) for file_name in file_list: local_path = os.path.join(local_dir, file_name) remote_path = '/'.join([remote_dir, file_name]) self.copy_file(local_path, remote_path, recurse=True, sftp=sftp) def copy_remote_file(self, remote_file, local_file, recurse=False, sftp=None, encoding='utf-8'): """Copy remote file to local host via SFTP. :param remote_file: Remote filepath to copy from :type remote_file: str :param local_file: Local filepath where file(s) will be copied to :type local_file: str :param recurse: Whether or not to recursively copy directories :type recurse: bool :param encoding: Encoding to use for file paths. :type encoding: str :raises: :py:class:`ValueError` when a directory is supplied to ``local_file`` and ``recurse`` is not set :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation errors :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors reading from SFTP :raises: :py:class:`IOError` on local file IO errors :raises: :py:class:`OSError` on local OS errors like permission denied """ sftp = self._make_sftp() if sftp is None else sftp try: self._eagain(sftp.stat, remote_file) except (SFTPHandleError, SFTPProtocolError): msg = "Remote file or directory %s does not exist" logger.error(msg, remote_file) raise SFTPIOError(msg, remote_file) try: dir_h = self._sftp_openfh(sftp.opendir, remote_file) except SFTPError: pass else: if not recurse: raise ValueError("Recurse must be true if remote_file is a " "directory.") file_list = self._sftp_readdir(dir_h) return self._copy_remote_dir(file_list, remote_file, local_file, sftp, encoding=encoding) destination = os.path.join(os.path.sep, os.path.sep.join( [_dir for _dir in local_file.split('/') if _dir][:-1])) self._make_local_dir(destination) self.sftp_get(sftp, remote_file, local_file) logger.info("Copied local file %s from remote destination %s:%s", local_file, self.host, remote_file) def scp_recv(self, remote_file, local_file, recurse=False, sftp=None, encoding='utf-8'): """Copy remote file to local host via SCP. Note - Remote directory listings are gather via SFTP when ``recurse`` is enabled - SCP lacks directory list support. Enabling recursion therefore involves creating an extra SFTP channel and requires SFTP support on the server. :param remote_file: Remote filepath to copy from :type remote_file: str :param local_file: Local filepath where file(s) will be copied to :type local_file: str :param recurse: Whether or not to recursively copy directories :type recurse: bool :param encoding: Encoding to use for file paths when recursion is enabled. :type encoding: str :raises: :py:class:`pssh.exceptions.SCPError` when a directory is supplied to ``local_file`` and ``recurse`` is not set. :raises: :py:class:`pssh.exceptions.SCPError` on errors copying file. :raises: :py:class:`IOError` on local file IO errors. :raises: :py:class:`OSError` on local OS errors like permission denied. """ sftp = self._make_sftp() if (sftp is None and recurse) else sftp if recurse: try: self._eagain(sftp.stat, remote_file) except (SFTPHandleError, SFTPProtocolError): msg = "Remote file or directory %s does not exist" logger.error(msg, remote_file) raise SCPError(msg, remote_file) try: dir_h = self._sftp_openfh(sftp.opendir, remote_file) except SFTPError: pass else: file_list = self._sftp_readdir(dir_h) return self._scp_recv_dir(file_list, remote_file, local_file, sftp, encoding=encoding) destination = os.path.join(os.path.sep, os.path.sep.join( [_dir for _dir in local_file.split('/') if _dir][:-1])) self._make_local_dir(destination) self._scp_recv(remote_file, local_file) logger.info("SCP local file %s from remote destination %s:%s", local_file, self.host, remote_file) def _scp_recv(self, remote_file, local_file): try: (file_chan, fileinfo) = self._eagain( self.session.scp_recv2, remote_file) except Exception as ex: msg = "Error copying file %s from host %s - %s" logger.error(msg, remote_file, self.host, ex) raise SCPError(msg, remote_file, self.host, ex) local_fh = open(local_file, 'wb') try: total = 0 size, data = file_chan.read(size=fileinfo.st_size) while size == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) size, data = file_chan.read(size=fileinfo.st_size) total += size local_fh.write(data) while total < fileinfo.st_size: size, data = file_chan.read(size=fileinfo.st_size - total) while size == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) continue total += size local_fh.write(data) if total != fileinfo.st_size: msg = "Error copying data from remote file %s on host %s. " \ "Copied %s out of %s total bytes" raise SCPError(msg, remote_file, self.host, total, fileinfo.st_size) finally: local_fh.close() def _scp_send_dir(self, local_dir, remote_dir, sftp): file_list = os.listdir(local_dir) for file_name in file_list: local_path = os.path.join(local_dir, file_name) remote_path = '/'.join([remote_dir, file_name]) self.scp_send(local_path, remote_path, recurse=True, sftp=sftp) def _scp_recv_dir(self, file_list, remote_dir, local_dir, sftp, encoding='utf-8'): for file_name in file_list: file_name = file_name.decode(encoding) if file_name in ('.', '..'): continue remote_path = os.path.join(remote_dir, file_name) local_path = os.path.join(local_dir, file_name) logger.debug("Attempting recursive copy from %s:%s to %s", self.host, remote_path, local_path) self.scp_recv(remote_path, local_path, sftp=sftp, recurse=True) def scp_send(self, local_file, remote_file, recurse=False, sftp=None): """Copy local file to host via SCP. Note - Directories are created via SFTP when ``recurse`` is enabled - SCP lacks directory create support. Enabling recursion therefore involves creating an extra SFTP channel and requires SFTP support on the server. :param local_file: Local filepath to copy to remote host :type local_file: str :param remote_file: Remote filepath on remote host to copy file to :type remote_file: str :param recurse: Whether or not to descend into directories recursively. :type recurse: bool :raises: :py:class:`ValueError` when a directory is supplied to ``local_file`` and ``recurse`` is not set :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation errors :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors writing via SFTP :raises: :py:class:`IOError` on local file IO errors :raises: :py:class:`OSError` on local OS errors like permission denied """ if os.path.isdir(local_file) and recurse: sftp = self._make_sftp() if sftp is None else sftp return self._scp_send_dir(local_file, remote_file, sftp) elif os.path.isdir(local_file) and not recurse: raise ValueError("Recurse must be True if local_file is a " "directory.") destination = self._remote_paths_split(remote_file) if destination is not None: sftp = self._make_sftp() if sftp is None else sftp try: self._eagain(sftp.stat, destination) except (SFTPHandleError, SFTPProtocolError): self.mkdir(sftp, destination) self._scp_send(local_file, remote_file) logger.info("SCP local file %s to remote destination %s:%s", local_file, self.host, remote_file) def _scp_send(self, local_file, remote_file): fileinfo = os.stat(local_file) try: chan = self._eagain( self.session.scp_send64, remote_file, fileinfo.st_mode & 0o777, fileinfo.st_size, fileinfo.st_mtime, fileinfo.st_atime) except Exception as ex: msg = "Error opening remote file %s for writing on host %s - %s" logger.error(msg, remote_file, self.host, ex) raise SCPError(msg, remote_file, self.host, ex) try: with open(local_file, 'rb', 2097152) as local_fh: for data in local_fh: eagain_write(chan.write, data, self.session) except Exception as ex: msg = "Error writing to remote file %s on host %s - %s" logger.error(msg, remote_file, self.host, ex) raise SCPError(msg, remote_file, self.host, ex) def _sftp_readdir(self, dir_h): for size, buf, attrs in dir_h.readdir(): for line in buf.splitlines(): yield line def _sftp_openfh(self, open_func, remote_file, *args): try: fh = open_func(remote_file, *args) except Exception as ex: raise SFTPError(ex) while fh == LIBSSH2_ERROR_EAGAIN: wait_select(self.session, timeout=0.1) try: fh = open_func(remote_file, *args) except Exception as ex: raise SFTPError(ex) return fh def _sftp_get(self, remote_fh, local_file): with open(local_file, 'wb') as local_fh: for size, data in remote_fh: if size == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) continue local_fh.write(data) def sftp_get(self, sftp, remote_file, local_file): with self._sftp_openfh( sftp.open, remote_file, LIBSSH2_FXF_READ, LIBSSH2_SFTP_S_IRUSR) as remote_fh: try: self._sftp_get(remote_fh, local_file) # Running SFTP in a thread requires a new session # as session handles or any handles created by a session # cannot be used simultaneously in multiple threads. # THREAD_POOL.apply( # sftp_get, args=(self.session, remote_fh, local_file)) except SFTPProtocolError as ex: msg = "Error reading from remote file %s - %s" logger.error(msg, remote_file, ex) raise SFTPIOError(msg, remote_file, ex) def _copy_remote_dir(self, file_list, remote_dir, local_dir, sftp, encoding='utf-8'): for file_name in file_list: file_name = file_name.decode(encoding) if file_name in ('.', '..'): continue remote_path = os.path.join(remote_dir, file_name) local_path = os.path.join(local_dir, file_name) self.copy_remote_file(remote_path, local_path, sftp=sftp, recurse=True) def _make_local_dir(self, dirpath): if os.path.exists(dirpath): return try: os.makedirs(dirpath) except OSError: logger.error("Unable to create local directory structure for " "directory %s", dirpath) raise def _remote_paths_split(self, file_path): _sep = file_path.rfind('/') if _sep > 0: return file_path[:_sep] return
def login_fun(user="******", password="******", port=22, host='192.168.1.1'): #we name aour output file using currency data and hour data_obj = datetime.datetime.now() data_obj = str(data_obj) data_obj = data_obj.replace("-", "_") data_obj = data_obj.replace(" ", "_") data_obj = data_obj.replace(":", "_") print(data_obj[0:16]) nazwa = "output_all_1" + data_obj[0:16] + ".txt" os.system(f"touch {nazwa}") directory = os.getcwd() filepath = directory + "/" + nazwa #we create secound file which get singal above cut_of_signal which we load from keyboard nazwa_v2 = "extreme_signal" + data_obj[0:16] + ".txt" filepath_v2 = directory + "/" + nazwa_v2 f = open(filepath, "a") main_list = [] #iteration over all hosts for hostname in host: #print(type(hostname)) #print(hostname) #print(port) #print(type(port)) #I checked port if is open, if open return return 0, above I first search with nmap open ports sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) if (sock.connect_ex((hostname, port)) == 0): print(f"Port {port} is open ") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((hostname, port)) #we are create our sesssion session = Session() # initializing sesion session.handshake(sock) #try authorize by login and password which we read from keyboard try: session.userauth_password(user, password) except: print("Incorretc login or password") continue channel = session.open_session() # start the interactive shell in the channnel channel.shell() # channel.write("vcgencmd measure_temp\n") # channel.write("clear\n") #we search our signal in Ubiquity file channel.write("mca-status | grep signal\n") channel.write("mca-status | grep chain\n") time.sleep(1) size, data = channel.read() my_list = [] my_list.append(hostname) parametr = data.decode() parametr = parametr.split('\r\n') parametr.pop(-1) my_list.extend(parametr) #write to file our output form Ubiquity #print(my_list) zapis = data.decode() f.write(str(my_list)) f.write("\n") main_list.append(my_list) channel.close() print("Exit status: {0}".format(channel.get_exit_status())) else: print("Port is close") continue return main_list, filepath_v2
emailMsg = MIMEText('Failed to reach destination') emailMsg['Subject'] = "Failed to reach host" emailMsg['From'], emailMsg['To'] = emailAddr # SSH Host Credentials host = "HOSTNAME OR IP" user = "******" port = 22 password = "******" # Intialize IPV4 socket stream sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Initate connection sock.connect((host, port)) session = Session() session.handshake(sock) session.userauth_password(user, password) channel = session.open_session() # Execute command, limit numner of packets to X. channel.execute("ping 192.168.1.222 -c 5") size, data = channel.read() while size > 0: if "Destination Host Unreachable" in data.decode(): print("Host unreachable") SendEmail() break else:
class SSHClient(object): """ssh2-python (libssh2) based non-blocking SSH client.""" IDENTITIES = [ os.path.expanduser('~/.ssh/id_rsa'), os.path.expanduser('~/.ssh/id_dsa'), os.path.expanduser('~/.ssh/identity') ] def __init__(self, host, user=None, password=None, port=None, pkey=None, num_retries=DEFAULT_RETRIES, retry_delay=RETRY_DELAY, allow_agent=True, timeout=None, forward_ssh_agent=True, proxy_host=None, _auth_thread_pool=True): """:param host: Host name or IP to connect to. :type host: str :param user: User to connect as. Defaults to logged in user. :type user: str :param password: Password to use for password authentication. :type password: str :param port: SSH port to connect to. Defaults to SSH default (22) :type port: int :param pkey: Private key file path to use for authentication. Note that the public key file pair *must* also exist in the same location with name ``<pkey>.pub`` :type pkey: str :param num_retries: (Optional) Number of connection and authentication attempts before the client gives up. Defaults to 3. :type num_retries: int :param retry_delay: Number of seconds to wait between retries. Defaults to :py:class:`pssh.constants.RETRY_DELAY` :type retry_delay: int :param timeout: SSH session timeout setting in seconds. This controls timeout setting of authenticated SSH sessions. :type timeout: int :param allow_agent: (Optional) set to False to disable connecting to the system's SSH agent :type allow_agent: bool :param forward_ssh_agent: (Optional) Turn on SSH agent forwarding - equivalent to `ssh -A` from the `ssh` command line utility. Defaults to True if not set. :type forward_ssh_agent: bool :param proxy_host: Connection to host is via provided proxy host and client should use self.proxy_host for connection attempts. :type proxy_host: str """ self.host = host self.user = user if user else None if self.user is None and not WIN_PLATFORM: self.user = pwd.getpwuid(os.geteuid()).pw_name elif self.user is None and WIN_PLATFORM: raise ValueError("Must provide user parameter on Windows") self.password = password self.port = port if port else 22 self.pkey = pkey self.num_retries = num_retries self.sock = None self.timeout = timeout * 1000 if timeout else None self.retry_delay = retry_delay self.allow_agent = allow_agent self.forward_ssh_agent = forward_ssh_agent self._forward_requested = False self.session = None self._host = proxy_host if proxy_host else host self._connect(self._host, self.port) if _auth_thread_pool: THREAD_POOL.apply(self._init) else: self._init() def disconnect(self): """Disconnect session, close socket if needed.""" if self.session is not None: try: self._eagain(self.session.disconnect) except Exception: pass if not self.sock.closed: self.sock.close() def __del__(self): self.disconnect() def __enter__(self): return self def __exit__(self, *args): self.disconnect() def _connect_init_retry(self, retries): retries += 1 self.session = None if not self.sock.closed: self.sock.close() sleep(self.retry_delay) self._connect(self._host, self.port, retries=retries) return self._init(retries=retries) def _init(self, retries=1): self.session = Session() if self.timeout: self.session.set_timeout(self.timeout) try: self.session.handshake(self.sock) except Exception as ex: while retries < self.num_retries: return self._connect_init_retry(retries) msg = "Error connecting to host %s:%s - %s" logger.error(msg, self.host, self.port, ex) if isinstance(ex, SSH2Timeout): raise Timeout(msg, self.host, self.port, ex) raise try: self.auth() except Exception as ex: while retries < self.num_retries: return self._connect_init_retry(retries) msg = "Authentication error while connecting to %s:%s - %s" raise AuthenticationException(msg, self.host, self.port, ex) self.session.set_blocking(0) def _connect(self, host, port, retries=1): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.settimeout(self.timeout) logger.debug("Connecting to %s:%s", host, port) try: self.sock.connect((host, port)) except sock_gaierror as ex: logger.error("Could not resolve host '%s' - retry %s/%s", host, retries, self.num_retries) while retries < self.num_retries: sleep(self.retry_delay) return self._connect(host, port, retries=retries+1) raise UnknownHostException("Unknown host %s - %s - retry %s/%s", host, str(ex.args[1]), retries, self.num_retries) except sock_error as ex: logger.error("Error connecting to host '%s:%s' - retry %s/%s", host, port, retries, self.num_retries) while retries < self.num_retries: sleep(self.retry_delay) return self._connect(host, port, retries=retries+1) error_type = ex.args[1] if len(ex.args) > 1 else ex.args[0] raise ConnectionErrorException( "Error connecting to host '%s:%s' - %s - retry %s/%s", host, port, str(error_type), retries, self.num_retries,) def _pkey_auth(self): pub_file = "%s.pub" % self.pkey logger.debug("Attempting authentication with public key %s for user %s", pub_file, self.user) self.session.userauth_publickey_fromfile( self.user, pub_file, self.pkey, self.password if self.password is not None else '') def _identity_auth(self): for identity_file in self.IDENTITIES: if not os.path.isfile(identity_file): continue pub_file = "%s.pub" % (identity_file) logger.debug( "Trying to authenticate with identity file %s", identity_file) try: self.session.userauth_publickey_fromfile( self.user, pub_file, identity_file, self.password if self.password is not None else '') except Exception: logger.debug("Authentication with identity file %s failed, " "continuing with other identities", identity_file) continue else: logger.debug("Authentication succeeded with identity file %s", identity_file) return raise AuthenticationException("No authentication methods succeeded") def auth(self): if self.pkey is not None: logger.debug( "Proceeding with public key file authentication") return self._pkey_auth() if self.allow_agent: try: self.session.agent_auth(self.user) except Exception as ex: logger.debug("Agent auth failed with %s, " "continuing with other authentication methods", ex) else: logger.debug("Authentication with SSH Agent succeeded") return try: self._identity_auth() except AuthenticationException: if self.password is None: raise logger.debug("Public key auth failed, trying password") self._password_auth() def _password_auth(self): if self.session.userauth_password(self.user, self.password) != 0: raise AuthenticationException("Password authentication failed") def open_session(self): """Open new channel from session""" try: chan = self.session.open_session() except Exception as ex: raise SessionError(ex) while chan == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) try: chan = self.session.open_session() except Exception as ex: raise SessionError(ex) # Multiple forward requests result in ChannelRequestDenied # errors, flag is used to avoid this. if self.forward_ssh_agent and not self._forward_requested: self._eagain(chan.request_auth_agent) self._forward_requested = True return chan def execute(self, cmd, use_pty=False, channel=None): """Execute command on remote server. :param cmd: Command to execute. :type cmd: str :param use_pty: Whether or not to obtain a PTY on the channel. :type use_pty: bool :param channel: Use provided channel for execute rather than creating a new one. :type channel: :py:class:`ssh2.channel.Channel` """ channel = self.open_session() if channel is None else channel if use_pty: self._eagain(channel.pty) logger.debug("Executing command '%s'" % cmd) self._eagain(channel.execute, cmd) return channel def read_stderr(self, channel, timeout=None): """Read standard error buffer from channel. :param channel: Channel to read output from. :type channel: :py:class:`ssh2.channel.Channel` """ return _read_output(self.session, channel.read_stderr, timeout=timeout) def read_output(self, channel, timeout=None): """Read standard output buffer from channel. :param channel: Channel to read output from. :type channel: :py:class:`ssh2.channel.Channel` """ return _read_output(self.session, channel.read, timeout=timeout) def _select_timeout(self, func, timeout): ret = func() while ret == LIBSSH2_ERROR_EAGAIN: wait_select(self.session, timeout=timeout) ret = func() if ret == LIBSSH2_ERROR_EAGAIN and timeout is not None: raise Timeout def wait_finished(self, channel, timeout=None): """Wait for EOF from channel and close channel. Used to wait for remote command completion and be able to gather exit code. :param channel: The channel to use. :type channel: :py:class:`ssh2.channel.Channel` """ if channel is None: return # If .eof() returns EAGAIN after a select with a timeout, it means # it reached timeout without EOF and _select_timeout will raise # timeout exception causing the channel to appropriately # not be closed as the command is still running. self._select_timeout(channel.wait_eof, timeout) # Close channel to indicate no more commands will be sent over it self.close_channel(channel) def close_channel(self, channel): logger.debug("Closing channel") self._eagain(channel.close) def _eagain(self, func, *args, **kwargs): ret = func(*args, **kwargs) while ret == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) ret = func(*args, **kwargs) return ret def read_output_buffer(self, output_buffer, prefix=None, callback=None, callback_args=None, encoding='utf-8'): """Read from output buffers and log to ``host_logger``. :param output_buffer: Iterator containing buffer :type output_buffer: iterator :param prefix: String to prefix log output to ``host_logger`` with :type prefix: str :param callback: Function to call back once buffer is depleted: :type callback: function :param callback_args: Arguments for call back function :type callback_args: tuple """ prefix = '' if prefix is None else prefix for line in output_buffer: output = line.decode(encoding) host_logger.info("[%s]%s\t%s", self.host, prefix, output) yield output if callback: callback(*callback_args) def run_command(self, command, sudo=False, user=None, use_pty=False, shell=None, encoding='utf-8', timeout=None): """Run remote command. :param command: Command to run. :type command: str :param sudo: Run command via sudo as super-user. :type sudo: bool :param user: Run command as user via sudo :type user: str :param use_pty: Whether or not to obtain a PTY on the channel. :type use_pty: bool :param shell: (Optional) Override shell to use to run command with. Defaults to login user's defined shell. Use the shell's command syntax, eg `shell='bash -c'` or `shell='zsh -c'`. :type shell: str :param encoding: Encoding to use for output. Must be valid `Python codec <https://docs.python.org/2.7/library/codecs.html>`_ :type encoding: str """ # Fast path for no command substitution needed if not sudo and not user and not shell: _command = command else: _command = '' if sudo and not user: _command = 'sudo -S ' elif user: _command = 'sudo -u %s -S ' % (user,) _shell = shell if shell else '$SHELL -c' _command += "%s '%s'" % (_shell, command,) channel = self.execute(_command, use_pty=use_pty) return channel, self.host, \ self.read_output_buffer( self.read_output(channel, timeout=timeout), encoding=encoding), \ self.read_output_buffer( self.read_stderr(channel, timeout=timeout), encoding=encoding, prefix='\t[err]'), channel def _make_sftp(self): """Make SFTP client from open transport""" try: sftp = self.session.sftp_init() except Exception as ex: raise SFTPError(ex) while sftp == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) try: sftp = self.session.sftp_init() except Exception as ex: raise SFTPError(ex) return sftp def _mkdir(self, sftp, directory): """Make directory via SFTP channel :param sftp: SFTP client object :type sftp: :py:class:`ssh2.sftp.SFTP` :param directory: Remote directory to create :type directory: str :raises: :py:class:`pssh.exceptions.SFTPIOError` on SFTP IO errors """ mode = LIBSSH2_SFTP_S_IRUSR | \ LIBSSH2_SFTP_S_IWUSR | \ LIBSSH2_SFTP_S_IXUSR | \ LIBSSH2_SFTP_S_IRGRP | \ LIBSSH2_SFTP_S_IROTH | \ LIBSSH2_SFTP_S_IXGRP | \ LIBSSH2_SFTP_S_IXOTH try: self._eagain(sftp.mkdir, directory, mode) except SFTPProtocolError as error: msg = "Error occured creating directory %s on host %s - %s" logger.error(msg, directory, self.host, error) raise SFTPIOError(msg, directory, self.host, error) logger.debug("Created remote directory %s", directory) def copy_file(self, local_file, remote_file, recurse=False, sftp=None, _dir=None): """Copy local file to host via SFTP. :param local_file: Local filepath to copy to remote host :type local_file: str :param remote_file: Remote filepath on remote host to copy file to :type remote_file: str :param recurse: Whether or not to descend into directories recursively. :type recurse: bool :raises: :py:class:`ValueError` when a directory is supplied to ``local_file`` and ``recurse`` is not set :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation errors :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors writing via SFTP :raises: :py:class:`IOError` on local file IO errors :raises: :py:class:`OSError` on local OS errors like permission denied """ sftp = self._make_sftp() if sftp is None else sftp if os.path.isdir(local_file) and recurse: return self._copy_dir(local_file, remote_file, sftp) elif os.path.isdir(local_file) and not recurse: raise ValueError("Recurse must be true if local_file is a " "directory.") destination = self._remote_paths_split(remote_file) if destination is not None: try: self._eagain(sftp.stat, destination) except (SFTPHandleError, SFTPProtocolError): self.mkdir(sftp, destination) self.sftp_put(sftp, local_file, remote_file) logger.info("Copied local file %s to remote destination %s:%s", local_file, self.host, remote_file) def _sftp_put(self, remote_fh, local_file): with open(local_file, 'rb') as local_fh: for data in local_fh: self._eagain(remote_fh.write, data) def sftp_put(self, sftp, local_file, remote_file): mode = LIBSSH2_SFTP_S_IRUSR | \ LIBSSH2_SFTP_S_IWUSR | \ LIBSSH2_SFTP_S_IRGRP | \ LIBSSH2_SFTP_S_IROTH f_flags = LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC with self._sftp_openfh( sftp.open, remote_file, f_flags, mode) as remote_fh: try: self._sftp_put(remote_fh, local_file) # THREAD_POOL.apply( # sftp_put, args=(self.session, remote_fh, local_file)) except SFTPProtocolError as ex: msg = "Error writing to remote file %s - %s" logger.error(msg, remote_file, ex) raise SFTPIOError(msg, remote_file, ex) def mkdir(self, sftp, directory, _parent_path=None): """Make directory via SFTP channel. Parent paths in the directory are created if they do not exist. :param sftp: SFTP client object :type sftp: :py:class:`paramiko.sftp_client.SFTPClient` :param directory: Remote directory to create :type directory: str Catches and logs at error level remote IOErrors on creating directory. """ try: _dir, sub_dirs = directory.split('/', 1) except ValueError: _dir = directory.split('/', 1)[0] sub_dirs = None if not _dir and directory.startswith('/'): try: _dir, sub_dirs = sub_dirs.split(os.path.sep, 1) except ValueError: return True if _parent_path is not None: _dir = '/'.join((_parent_path, _dir)) try: self._eagain(sftp.stat, _dir) except (SFTPHandleError, SFTPProtocolError) as ex: logger.debug("Stat for %s failed with %s", _dir, ex) self._mkdir(sftp, _dir) if sub_dirs is not None: if directory.startswith('/'): _dir = ''.join(('/', _dir)) return self.mkdir(sftp, sub_dirs, _parent_path=_dir) def _copy_dir(self, local_dir, remote_dir, sftp): """Call copy_file on every file in the specified directory, copying them to the specified remote directory.""" file_list = os.listdir(local_dir) for file_name in file_list: local_path = os.path.join(local_dir, file_name) remote_path = '/'.join([remote_dir, file_name]) self.copy_file(local_path, remote_path, recurse=True, sftp=sftp) def copy_remote_file(self, remote_file, local_file, recurse=False, sftp=None, encoding='utf-8'): """Copy remote file to local host via SFTP. :param remote_file: Remote filepath to copy from :type remote_file: str :param local_file: Local filepath where file(s) will be copied to :type local_file: str :param recurse: Whether or not to recursively copy directories :type recurse: bool :param encoding: Encoding to use for file paths. :type encoding: str :raises: :py:class:`ValueError` when a directory is supplied to ``local_file`` and ``recurse`` is not set :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation errors :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors reading from SFTP :raises: :py:class:`IOError` on local file IO errors :raises: :py:class:`OSError` on local OS errors like permission denied """ sftp = self._make_sftp() if sftp is None else sftp try: self._eagain(sftp.stat, remote_file) except (SFTPHandleError, SFTPProtocolError): msg = "Remote file or directory %s does not exist" logger.error(msg, remote_file) raise SFTPIOError(msg, remote_file) try: dir_h = self._sftp_openfh(sftp.opendir, remote_file) except SFTPError: pass else: if not recurse: raise ValueError("Recurse must be true if remote_file is a " "directory.") file_list = self._sftp_readdir(dir_h) return self._copy_remote_dir(file_list, remote_file, local_file, sftp, encoding=encoding) destination = os.path.join(os.path.sep, os.path.sep.join( [_dir for _dir in local_file.split('/') if _dir][:-1])) self._make_local_dir(destination) self.sftp_get(sftp, remote_file, local_file) logger.info("Copied local file %s from remote destination %s:%s", local_file, self.host, remote_file) def scp_recv(self, remote_file, local_file, recurse=False, sftp=None, encoding='utf-8'): """Copy remote file to local host via SCP. Note - Remote directory listings are gather via SFTP when ``recurse`` is enabled - SCP lacks directory list support. Enabling recursion therefore involves creating an extra SFTP channel and requires SFTP support on the server. :param remote_file: Remote filepath to copy from :type remote_file: str :param local_file: Local filepath where file(s) will be copied to :type local_file: str :param recurse: Whether or not to recursively copy directories :type recurse: bool :param encoding: Encoding to use for file paths when recursion is enabled. :type encoding: str :raises: :py:class:`pssh.exceptions.SCPError` when a directory is supplied to ``local_file`` and ``recurse`` is not set. :raises: :py:class:`pssh.exceptions.SCPError` on errors copying file. :raises: :py:class:`IOError` on local file IO errors. :raises: :py:class:`OSError` on local OS errors like permission denied. """ sftp = self._make_sftp() if (sftp is None and recurse) else sftp if recurse: try: self._eagain(sftp.stat, remote_file) except (SFTPHandleError, SFTPProtocolError): msg = "Remote file or directory %s does not exist" logger.error(msg, remote_file) raise SCPError(msg, remote_file) try: dir_h = self._sftp_openfh(sftp.opendir, remote_file) except SFTPError: pass else: file_list = self._sftp_readdir(dir_h) return self._scp_recv_dir(file_list, remote_file, local_file, sftp, encoding=encoding) destination = os.path.join(os.path.sep, os.path.sep.join( [_dir for _dir in local_file.split('/') if _dir][:-1])) self._make_local_dir(destination) self._scp_recv(remote_file, local_file) logger.info("SCP local file %s from remote destination %s:%s", local_file, self.host, remote_file) def _scp_recv(self, remote_file, local_file): try: (file_chan, fileinfo) = self._eagain( self.session.scp_recv2, remote_file) except Exception as ex: msg = "Error copying file %s from host %s - %s" logger.error(msg, remote_file, self.host, ex) raise SCPError(msg, remote_file, self.host, ex) local_fh = open(local_file, 'wb') try: total = 0 size, data = file_chan.read(size=fileinfo.st_size) while size == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) size, data = file_chan.read(size=fileinfo.st_size) total += size local_fh.write(data) while total < fileinfo.st_size: size, data = file_chan.read(size=fileinfo.st_size - total) while size == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) continue total += size local_fh.write(data) if total != fileinfo.st_size: msg = "Error copying data from remote file %s on host %s. " \ "Copied %s out of %s total bytes" raise SCPError(msg, remote_file, self.host, total, fileinfo.st_size) finally: local_fh.close() def _scp_send_dir(self, local_dir, remote_dir, sftp): file_list = os.listdir(local_dir) for file_name in file_list: local_path = os.path.join(local_dir, file_name) remote_path = '/'.join([remote_dir, file_name]) self.scp_send(local_path, remote_path, recurse=True, sftp=sftp) def _scp_recv_dir(self, file_list, remote_dir, local_dir, sftp, encoding='utf-8'): for file_name in file_list: file_name = file_name.decode(encoding) if file_name in ('.', '..'): continue remote_path = os.path.join(remote_dir, file_name) local_path = os.path.join(local_dir, file_name) logger.debug("Attempting recursive copy from %s:%s to %s", self.host, remote_path, local_path) self.scp_recv(remote_path, local_path, sftp=sftp, recurse=True) def scp_send(self, local_file, remote_file, recurse=False, sftp=None): """Copy local file to host via SCP. Note - Directories are created via SFTP when ``recurse`` is enabled - SCP lacks directory create support. Enabling recursion therefore involves creating an extra SFTP channel and requires SFTP support on the server. :param local_file: Local filepath to copy to remote host :type local_file: str :param remote_file: Remote filepath on remote host to copy file to :type remote_file: str :param recurse: Whether or not to descend into directories recursively. :type recurse: bool :raises: :py:class:`ValueError` when a directory is supplied to ``local_file`` and ``recurse`` is not set :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation errors :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors writing via SFTP :raises: :py:class:`IOError` on local file IO errors :raises: :py:class:`OSError` on local OS errors like permission denied """ if os.path.isdir(local_file) and recurse: sftp = self._make_sftp() if sftp is None else sftp return self._scp_send_dir(local_file, remote_file, sftp) elif os.path.isdir(local_file) and not recurse: raise ValueError("Recurse must be True if local_file is a " "directory.") destination = self._remote_paths_split(remote_file) if destination is not None: sftp = self._make_sftp() if sftp is None else sftp try: self._eagain(sftp.stat, destination) except (SFTPHandleError, SFTPProtocolError): self.mkdir(sftp, destination) self._scp_send(local_file, remote_file) logger.info("SCP local file %s to remote destination %s:%s", local_file, self.host, remote_file) def _scp_send(self, local_file, remote_file): fileinfo = os.stat(local_file) try: chan = self._eagain( self.session.scp_send64, remote_file, fileinfo.st_mode & 777, fileinfo.st_size, fileinfo.st_mtime, fileinfo.st_atime) except Exception as ex: msg = "Error opening remote file %s for writing on host %s - %s" logger.error(msg, remote_file, self.host, ex) raise SCPError(msg, remote_file, self.host, ex) try: with open(local_file, 'rb') as local_fh: for data in local_fh: chan.write(data) except Exception as ex: msg = "Error writing to remote file %s on host %s - %s" logger.error(msg, remote_file, self.host, ex) raise SCPError(msg, remote_file, self.host, ex) def _sftp_readdir(self, dir_h): for size, buf, attrs in dir_h.readdir(): for line in buf.splitlines(): yield line def _sftp_openfh(self, open_func, remote_file, *args): try: fh = open_func(remote_file, *args) except Exception as ex: raise SFTPError(ex) while fh == LIBSSH2_ERROR_EAGAIN: wait_select(self.session, timeout=0.1) try: fh = open_func(remote_file, *args) except Exception as ex: raise SFTPError(ex) return fh def _sftp_get(self, remote_fh, local_file): with open(local_file, 'wb') as local_fh: for size, data in remote_fh: if size == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) continue local_fh.write(data) def sftp_get(self, sftp, remote_file, local_file): with self._sftp_openfh( sftp.open, remote_file, LIBSSH2_FXF_READ, LIBSSH2_SFTP_S_IRUSR) as remote_fh: try: self._sftp_get(remote_fh, local_file) # Running SFTP in a thread requires a new session # as session handles or any handles created by a session # cannot be used simultaneously in multiple threads. # THREAD_POOL.apply( # sftp_get, args=(self.session, remote_fh, local_file)) except SFTPProtocolError as ex: msg = "Error reading from remote file %s - %s" logger.error(msg, remote_file, ex) raise SFTPIOError(msg, remote_file, ex) def _copy_remote_dir(self, file_list, remote_dir, local_dir, sftp, encoding='utf-8'): for file_name in file_list: file_name = file_name.decode(encoding) if file_name in ('.', '..'): continue remote_path = os.path.join(remote_dir, file_name) local_path = os.path.join(local_dir, file_name) self.copy_remote_file(remote_path, local_path, sftp=sftp, recurse=True) def _make_local_dir(self, dirpath): if os.path.exists(dirpath): return try: os.makedirs(dirpath) except OSError: logger.error("Unable to create local directory structure for " "directory %s", dirpath) raise def _remote_paths_split(self, file_path): _sep = file_path.rfind('/') if _sep > 0: return file_path[:_sep] return
def connect(self, host, port, username, password): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, port)) try: ssh_session = Session() try: ssh_session.set_timeout(self.timeoutMs) ssh_session.handshake(sock) ssh_session.userauth_password(username, password) self._sftp_session = ssh_session.sftp_init() # set non_blocking_mode ssh_session.set_blocking(False) self._socket = sock self._ssh_session = ssh_session except: self._close_session(ssh_session) raise except: self._close_socket(sock) raise return self
class SSHClient(BaseSSHClient): """ssh2-python (libssh2) based non-blocking SSH client.""" def __init__(self, host, user=None, password=None, port=None, pkey=None, num_retries=DEFAULT_RETRIES, retry_delay=RETRY_DELAY, allow_agent=True, timeout=None, forward_ssh_agent=False, proxy_host=None, _auth_thread_pool=True, keepalive_seconds=60, identity_auth=True,): """:param host: Host name or IP to connect to. :type host: str :param user: User to connect as. Defaults to logged in user. :type user: str :param password: Password to use for password authentication. :type password: str :param port: SSH port to connect to. Defaults to SSH default (22) :type port: int :param pkey: Private key file path to use for authentication. Path must be either absolute path or relative to user home directory like ``~/<path>``. :type pkey: str :param num_retries: (Optional) Number of connection and authentication attempts before the client gives up. Defaults to 3. :type num_retries: int :param retry_delay: Number of seconds to wait between retries. Defaults to :py:class:`pssh.constants.RETRY_DELAY` :type retry_delay: int :param timeout: SSH session timeout setting in seconds. This controls timeout setting of authenticated SSH sessions. :type timeout: int :param allow_agent: (Optional) set to False to disable connecting to the system's SSH agent :type allow_agent: bool :param identity_auth: (Optional) set to False to disable attempting to authenticate with default identity files from `pssh.clients.base_ssh_client.BaseSSHClient.IDENTITIES` :type identity_auth: bool :param forward_ssh_agent: (Optional) Turn on SSH agent forwarding - equivalent to `ssh -A` from the `ssh` command line utility. Defaults to True if not set. :type forward_ssh_agent: bool :param proxy_host: Connection to host is via provided proxy host and client should use self.proxy_host for connection attempts. :type proxy_host: str :param keepalive_seconds: Interval of keep alive messages being sent to server. Set to ``0`` or ``False`` to disable. :raises: :py:class:`pssh.exceptions.PKeyFileError` on errors finding provided private key. """ self.forward_ssh_agent = forward_ssh_agent self._forward_requested = False self.keepalive_seconds = keepalive_seconds self._keepalive_greenlet = None super(SSHClient, self).__init__( host, user=user, password=password, port=port, pkey=pkey, num_retries=num_retries, retry_delay=retry_delay, allow_agent=allow_agent, _auth_thread_pool=_auth_thread_pool, timeout=timeout, proxy_host=proxy_host, identity_auth=identity_auth) def disconnect(self): """Disconnect session, close socket if needed.""" logger.debug("Disconnecting client for host %s", self.host) self._keepalive_greenlet = None if self.session is not None: try: self._eagain(self.session.disconnect) except Exception: pass self.session = None self.sock = None def spawn_send_keepalive(self): """Spawns a new greenlet that sends keep alive messages every self.keepalive_seconds""" return spawn(self._send_keepalive) def _send_keepalive(self): while True: sleep(self._eagain(self.session.keepalive_send)) def configure_keepalive(self): self.session.keepalive_config(False, self.keepalive_seconds) def _init(self, retries=1): self.session = Session() if self.timeout: # libssh2 timeout is in ms self.session.set_timeout(self.timeout * 1000) try: self.session.handshake(self.sock) except Exception as ex: while retries < self.num_retries: return self._connect_init_retry(retries) msg = "Error connecting to host %s:%s - %s" logger.error(msg, self.host, self.port, ex) if isinstance(ex, SSH2Timeout): raise Timeout(msg, self.host, self.port, ex) ex.host = self.host ex.port = self.port raise try: self.auth() except Exception as ex: while retries < self.num_retries: return self._connect_init_retry(retries) msg = "Authentication error while connecting to %s:%s - %s" raise AuthenticationException(msg, self.host, self.port, ex) self.session.set_blocking(0) if self.keepalive_seconds: self.configure_keepalive() self._keepalive_greenlet = self.spawn_send_keepalive() def auth(self): if self.pkey is not None: logger.debug( "Proceeding with private key file authentication") return self._pkey_auth(password=self.password) if self.allow_agent: try: self.session.agent_auth(self.user) except (AgentAuthenticationError, AgentConnectionError, AgentGetIdentityError, AgentListIdentitiesError) as ex: logger.debug("Agent auth failed with %s" "continuing with other authentication methods", ex) except Exception as ex: logger.error("Unknown error during agent authentication - %s", ex) else: logger.debug("Authentication with SSH Agent succeeded") return try: self._identity_auth() except AuthenticationException: if self.password is None: raise logger.debug("Private key auth failed, trying password") self._password_auth() def _pkey_auth(self, password=None): self.session.userauth_publickey_fromfile( self.user, self.pkey, passphrase=password if password is not None else '') def _password_auth(self): try: self.session.userauth_password(self.user, self.password) except Exception: raise AuthenticationException("Password authentication failed") def open_session(self): """Open new channel from session""" try: chan = self.session.open_session() except Exception as ex: raise SessionError(ex) while chan == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) try: chan = self.session.open_session() except Exception as ex: raise SessionError(ex) # Multiple forward requests result in ChannelRequestDenied # errors, flag is used to avoid this. if self.forward_ssh_agent and not self._forward_requested: if not hasattr(chan, 'request_auth_agent'): warn("Requested SSH Agent forwarding but libssh2 version used " "does not support it - ignoring") return chan self._eagain(chan.request_auth_agent) self._forward_requested = True return chan def execute(self, cmd, use_pty=False, channel=None): """Execute command on remote server. :param cmd: Command to execute. :type cmd: str :param use_pty: Whether or not to obtain a PTY on the channel. :type use_pty: bool :param channel: Use provided channel for execute rather than creating a new one. :type channel: :py:class:`ssh2.channel.Channel` """ channel = self.open_session() if channel is None else channel if use_pty: self._eagain(channel.pty) logger.debug("Executing command '%s'", cmd) self._eagain(channel.execute, cmd) return channel def read_stderr(self, channel, timeout=None): """Read standard error buffer from channel. Returns a generator of line by line output. :param channel: Channel to read output from. :type channel: :py:class:`ssh2.channel.Channel` :rtype: generator """ return _read_output(self.session, channel.read_stderr, timeout=timeout) def read_output(self, channel, timeout=None): """Read standard output buffer from channel. Returns a generator of line by line output. :param channel: Channel to read output from. :type channel: :py:class:`ssh2.channel.Channel` :rtype: generator """ return _read_output(self.session, channel.read, timeout=timeout) def wait_finished(self, channel, timeout=None): """Wait for EOF from channel and close channel. Used to wait for remote command completion and be able to gather exit code. :param channel: The channel to use. :type channel: :py:class:`ssh2.channel.Channel` """ if channel is None: return # If wait_eof() returns EAGAIN after a select with a timeout, it means # it reached timeout without EOF and _select_timeout will raise # timeout exception causing the channel to appropriately # not be closed as the command is still running. self._eagain(channel.wait_eof) # Close channel to indicate no more commands will be sent over it self.close_channel(channel) def close_channel(self, channel): logger.debug("Closing channel") self._eagain(channel.close) def _eagain(self, func, *args, **kwargs): ret = func(*args, **kwargs) while ret == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) ret = func(*args, **kwargs) return ret def _make_sftp(self): """Make SFTP client from open transport""" try: sftp = self.session.sftp_init() except Exception as ex: raise SFTPError(ex) while sftp == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) try: sftp = self.session.sftp_init() except Exception as ex: raise SFTPError(ex) return sftp def _mkdir(self, sftp, directory): """Make directory via SFTP channel :param sftp: SFTP client object :type sftp: :py:class:`ssh2.sftp.SFTP` :param directory: Remote directory to create :type directory: str :raises: :py:class:`pssh.exceptions.SFTPIOError` on SFTP IO errors """ mode = LIBSSH2_SFTP_S_IRUSR | \ LIBSSH2_SFTP_S_IWUSR | \ LIBSSH2_SFTP_S_IXUSR | \ LIBSSH2_SFTP_S_IRGRP | \ LIBSSH2_SFTP_S_IROTH | \ LIBSSH2_SFTP_S_IXGRP | \ LIBSSH2_SFTP_S_IXOTH try: self._eagain(sftp.mkdir, directory, mode) except SFTPProtocolError as error: msg = "Error occured creating directory %s on host %s - %s" logger.error(msg, directory, self.host, error) raise SFTPIOError(msg, directory, self.host, error) logger.debug("Created remote directory %s", directory) def copy_file(self, local_file, remote_file, recurse=False, sftp=None): """Copy local file to host via SFTP. :param local_file: Local filepath to copy to remote host :type local_file: str :param remote_file: Remote filepath on remote host to copy file to :type remote_file: str :param recurse: Whether or not to descend into directories recursively. :type recurse: bool :raises: :py:class:`ValueError` when a directory is supplied to ``local_file`` and ``recurse`` is not set :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation errors :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors writing via SFTP :raises: :py:class:`IOError` on local file IO errors :raises: :py:class:`OSError` on local OS errors like permission denied """ sftp = self._make_sftp() if sftp is None else sftp if os.path.isdir(local_file) and recurse: return self._copy_dir(local_file, remote_file, sftp) elif os.path.isdir(local_file) and not recurse: raise ValueError("Recurse must be true if local_file is a " "directory.") destination = self._remote_paths_split(remote_file) if destination is not None: try: self._eagain(sftp.stat, destination) except (SFTPHandleError, SFTPProtocolError): self.mkdir(sftp, destination) self.sftp_put(sftp, local_file, remote_file) logger.info("Copied local file %s to remote destination %s:%s", local_file, self.host, remote_file) def _sftp_put(self, remote_fh, local_file): with open(local_file, 'rb', 2097152) as local_fh: for data in local_fh: eagain_write(remote_fh.write, data, self.session) def sftp_put(self, sftp, local_file, remote_file): mode = LIBSSH2_SFTP_S_IRUSR | \ LIBSSH2_SFTP_S_IWUSR | \ LIBSSH2_SFTP_S_IRGRP | \ LIBSSH2_SFTP_S_IROTH f_flags = LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC with self._sftp_openfh( sftp.open, remote_file, f_flags, mode) as remote_fh: try: self._sftp_put(remote_fh, local_file) # THREAD_POOL.apply( # sftp_put, args=(self.session, remote_fh, local_file)) except SFTPProtocolError as ex: msg = "Error writing to remote file %s - %s" logger.error(msg, remote_file, ex) raise SFTPIOError(msg, remote_file, ex) def mkdir(self, sftp, directory): """Make directory via SFTP channel. Parent paths in the directory are created if they do not exist. :param sftp: SFTP client object :type sftp: :py:class:`paramiko.sftp_client.SFTPClient` :param directory: Remote directory to create :type directory: str Catches and logs at error level remote IOErrors on creating directory. """ _paths_to_create = deque() for d in directory.split('/'): if not d: continue _paths_to_create.append(d) cwd = '' if directory.startswith('/') else '.' while _paths_to_create: cur_dir = _paths_to_create.popleft() cwd = '/'.join([cwd, cur_dir]) try: self._eagain(sftp.stat, cwd) except (SFTPHandleError, SFTPProtocolError) as ex: logger.debug("Stat for %s failed with %s", cwd, ex) self._mkdir(sftp, cwd) def copy_remote_file(self, remote_file, local_file, recurse=False, sftp=None, encoding='utf-8'): """Copy remote file to local host via SFTP. :param remote_file: Remote filepath to copy from :type remote_file: str :param local_file: Local filepath where file(s) will be copied to :type local_file: str :param recurse: Whether or not to recursively copy directories :type recurse: bool :param encoding: Encoding to use for file paths. :type encoding: str :raises: :py:class:`ValueError` when a directory is supplied to ``local_file`` and ``recurse`` is not set :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation errors :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors reading from SFTP :raises: :py:class:`IOError` on local file IO errors :raises: :py:class:`OSError` on local OS errors like permission denied """ sftp = self._make_sftp() if sftp is None else sftp try: self._eagain(sftp.stat, remote_file) except (SFTPHandleError, SFTPProtocolError): msg = "Remote file or directory %s does not exist" logger.error(msg, remote_file) raise SFTPIOError(msg, remote_file) try: dir_h = self._sftp_openfh(sftp.opendir, remote_file) except SFTPError: pass else: if not recurse: raise ValueError("Recurse must be true if remote_file is a " "directory.") file_list = self._sftp_readdir(dir_h) return self._copy_remote_dir(file_list, remote_file, local_file, sftp, encoding=encoding) destination = os.path.join(os.path.sep, os.path.sep.join( [_dir for _dir in local_file.split('/') if _dir][:-1])) self._make_local_dir(destination) self.sftp_get(sftp, remote_file, local_file) logger.info("Copied local file %s from remote destination %s:%s", local_file, self.host, remote_file) def scp_recv(self, remote_file, local_file, recurse=False, sftp=None, encoding='utf-8'): """Copy remote file to local host via SCP. Note - Remote directory listings are gathered via SFTP when ``recurse`` is enabled - SCP lacks directory list support. Enabling recursion therefore involves creating an extra SFTP channel and requires SFTP support on the server. :param remote_file: Remote filepath to copy from :type remote_file: str :param local_file: Local filepath where file(s) will be copied to :type local_file: str :param recurse: Whether or not to recursively copy directories :type recurse: bool :param encoding: Encoding to use for file paths when recursion is enabled. :type encoding: str :raises: :py:class:`pssh.exceptions.SCPError` when a directory is supplied to ``local_file`` and ``recurse`` is not set. :raises: :py:class:`pssh.exceptions.SCPError` on errors copying file. :raises: :py:class:`IOError` on local file IO errors. :raises: :py:class:`OSError` on local OS errors like permission denied. """ if recurse: sftp = self._make_sftp() if sftp is None else sftp try: self._eagain(sftp.stat, remote_file) except (SFTPHandleError, SFTPProtocolError): msg = "Remote file or directory %s does not exist" logger.error(msg, remote_file) raise SCPError(msg, remote_file) try: dir_h = self._sftp_openfh(sftp.opendir, remote_file) except SFTPError: pass else: try: os.makedirs(local_file) except OSError: pass file_list = self._sftp_readdir(dir_h) return self._scp_recv_dir(file_list, remote_file, local_file, sftp, encoding=encoding) elif local_file.endswith('/'): remote_filename = remote_file.rsplit('/')[-1] local_file += remote_filename destination = os.path.join(os.path.sep, os.path.sep.join( [_dir for _dir in local_file.split('/') if _dir][:-1])) self._make_local_dir(destination) self._scp_recv(remote_file, local_file) logger.info("SCP local file %s from remote destination %s:%s", local_file, self.host, remote_file) def _scp_recv(self, remote_file, local_file): try: (file_chan, fileinfo) = self._eagain( self.session.scp_recv2, remote_file) except Exception as ex: msg = "Error copying file %s from host %s - %s" logger.error(msg, remote_file, self.host, ex) raise SCPError(msg, remote_file, self.host, ex) local_fh = open(local_file, 'wb') try: total = 0 while total < fileinfo.st_size: size, data = file_chan.read(size=fileinfo.st_size - total) if size == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) continue total += size local_fh.write(data) if total != fileinfo.st_size: msg = "Error copying data from remote file %s on host %s. " \ "Copied %s out of %s total bytes" raise SCPError(msg, remote_file, self.host, total, fileinfo.st_size) finally: local_fh.close() def scp_send(self, local_file, remote_file, recurse=False, sftp=None): """Copy local file to host via SCP. Note - Directories are created via SFTP when ``recurse`` is enabled - SCP lacks directory create support. Enabling recursion therefore involves creating an extra SFTP channel and requires SFTP support on the server. :param local_file: Local filepath to copy to remote host :type local_file: str :param remote_file: Remote filepath on remote host to copy file to :type remote_file: str :param recurse: Whether or not to descend into directories recursively. :type recurse: bool :raises: :py:class:`ValueError` when a directory is supplied to ``local_file`` and ``recurse`` is not set :raises: :py:class:`pss.exceptions.SFTPError` on SFTP initialisation errors :raises: :py:class:`pssh.exceptions.SFTPIOError` on I/O errors writing via SFTP :raises: :py:class:`IOError` on local file IO errors :raises: :py:class:`OSError` on local OS errors like permission denied """ if os.path.isdir(local_file) and recurse: sftp = self._make_sftp() if sftp is None else sftp return self._scp_send_dir(local_file, remote_file, sftp) elif os.path.isdir(local_file) and not recurse: raise ValueError("Recurse must be True if local_file is a " "directory.") if recurse: destination = self._remote_paths_split(remote_file) if destination is not None: sftp = self._make_sftp() if sftp is None else sftp try: self._eagain(sftp.stat, destination) except (SFTPHandleError, SFTPProtocolError): self.mkdir(sftp, destination) elif remote_file.endswith('/'): local_filename = local_file.rsplit('/')[-1] remote_file += local_filename self._scp_send(local_file, remote_file) logger.info("SCP local file %s to remote destination %s:%s", local_file, self.host, remote_file) def _scp_send(self, local_file, remote_file): fileinfo = os.stat(local_file) try: chan = self._eagain( self.session.scp_send64, remote_file, fileinfo.st_mode & 0o777, fileinfo.st_size, fileinfo.st_mtime, fileinfo.st_atime) except Exception as ex: msg = "Error opening remote file %s for writing on host %s - %s" logger.error(msg, remote_file, self.host, ex) raise SCPError(msg, remote_file, self.host, ex) try: with open(local_file, 'rb', 2097152) as local_fh: for data in local_fh: eagain_write(chan.write, data, self.session) except Exception as ex: msg = "Error writing to remote file %s on host %s - %s" logger.error(msg, remote_file, self.host, ex) raise SCPError(msg, remote_file, self.host, ex) def _sftp_openfh(self, open_func, remote_file, *args): try: fh = open_func(remote_file, *args) except Exception as ex: raise SFTPError(ex) while fh == LIBSSH2_ERROR_EAGAIN: wait_select(self.session, timeout=0.1) try: fh = open_func(remote_file, *args) except Exception as ex: raise SFTPError(ex) return fh def _sftp_get(self, remote_fh, local_file): with open(local_file, 'wb') as local_fh: for size, data in remote_fh: if size == LIBSSH2_ERROR_EAGAIN: wait_select(self.session) continue local_fh.write(data) def sftp_get(self, sftp, remote_file, local_file): with self._sftp_openfh( sftp.open, remote_file, LIBSSH2_FXF_READ, LIBSSH2_SFTP_S_IRUSR) as remote_fh: try: self._sftp_get(remote_fh, local_file) # Running SFTP in a thread requires a new session # as session handles or any handles created by a session # cannot be used simultaneously in multiple threads. # THREAD_POOL.apply( # sftp_get, args=(self.session, remote_fh, local_file)) except SFTPProtocolError as ex: msg = "Error reading from remote file %s - %s" logger.error(msg, remote_file, ex) raise SFTPIOError(msg, remote_file, ex) def get_exit_status(self, channel): if not channel.eof(): return return channel.get_exit_status() def finished(self, channel): """Checks if remote command has finished - has server sent client EOF. :rtype: bool """ if channel is None: return return channel.eof()
class SSHStream(BaseStream): default_port = 22 SSH_KEY_PREFIX = config.path.ssh_key_prefix _key_cache = cachetools.TTLCache(100, ttl=60) def __init__(self, cli: CLI): super().__init__(cli) self.script = cli.script # @todo: Remove self.session = None self.channel = None self.credentials = cli.script.credentials def __del__(self): self.channel = None self.session = None @classmethod @cachetools.cachedmethod(operator.attrgetter("_key_cache"), lock=lambda _: key_lock) def get_publickey(cls, pool: str) -> Tuple[Optional[bytes], Optional[bytes]]: """ Return public, private key pair :return: bytes, bytes or None, None """ logger.debug("Getting keys for pool %s", pool) pub_path = os.path.join(config.path.ssh_key_prefix, pool, "id_rsa.pub") priv_path = os.path.join(config.path.ssh_key_prefix, pool, "id_rsa") if not os.path.exists(pub_path): logger.debug("No public key for pool %s (%s)", pool, pub_path) return None, None if not os.path.exists(priv_path): logger.debug("No private key for pool %s (%s)", pool, priv_path) return None, None with open(pub_path, "rb") as fpub, open(priv_path, "rb") as fpriv: return fpub.read(), fpriv.read() async def startup(self): """ SSH session startup """ user = self.credentials["user"] if user is None: user = "" self.logger.debug("Startup ssh session for user '%s'", user) self.session = Session() try: self.session.handshake(self.socket) host_hash = smart_bytes( self.session.hostkey_hash(LIBSSH2_HOSTKEY_HASH_SHA1)) hex_hash = smart_text(codecs.encode(host_hash, "hex")) self.logger.debug("Connected. Host fingerprint is %s", hex_hash) auth_methods = self.session.userauth_list(user) if not auth_methods: self.logger.info( "No supported authentication methods. Failed to log in") raise CLIAuthFailed("Failed to log in") self.logger.debug("Supported authentication methods: %s", ", ".join(auth_methods)) # Try to authenticate authenticated = False for method in auth_methods: ah = getattr(self, "auth_%s" % method.replace("-", ""), None) if ah: metrics["ssh_auth", ("method", method)] += 1 authenticated |= ah() if authenticated: metrics["ssh_auth_success", ("method", method)] += 1 break metrics["ssh_auth_fail", ("method", method)] += 1 if not authenticated: self.logger.error("Failed to authenticate user '%s'", user) raise CLIAuthFailed("Failed to log in") self.logger.debug("User is authenticated") self.logger.debug("Open channel") self.channel = self.session.open_session() self.channel.pty("xterm") self.channel.shell() self.session.keepalive_config(False, 60) self.session.set_blocking(False) except SSH2Error as e: self.logger.info("SSH Error: %s", e) raise CLISSHProtocolError("SSH Error: %s" % e) async def read(self, n: int) -> bytes: while True: try: await self.wait_for_read() metrics["ssh_reads"] += 1 code, data = self.channel.read(n) if code == 0: if self.channel.eof(): self.logger.info("SSH session reset") self.close() return b"" metrics["ssh_reads_blocked"] += 1 continue elif code > 0: n = len(data) metrics["ssh_read_bytes"] += n return data elif code == LIBSSH2_ERROR_EAGAIN: metrics["ssh_reads_blocked"] += 1 continue metrics["ssh_errors", ("code", code)] += 1 raise CLISSHProtocolError("SSH Error code %s" % code) except SSH2Error as e: raise CLISSHProtocolError("SSH Error: %s" % e) async def write(self, data: bytes): metrics["ssh_writes"] += 1 while data: await self.wait_for_write() try: _, sent = self.channel.write(data) metrics["ssh_write_bytes"] += sent data = data[sent:] except SSH2Error as e: raise CLISSHProtocolError("SSH Error: %s" % e) def close(self, exc_info=False): if self.channel: self.logger.debug("Closing channel") try: self.channel.close() except SSH2Error as e: self.logger.debug("Cannot close channel clearly: %s", e) # The causes of memory leak # self.channel = None if self.session: self.logger.debug("Closing ssh session") self.session = None super().close() def get_user(self) -> str: """ Get current user """ return self.script.credentials["user"] or "" def get_password(self) -> str: """ Get current user's password """ return self.script.credentials["password"] or "" def auth_publickey(self) -> bool: """ Public key authentication """ self.logger.debug("Trying publickey authentication") pub_key, priv_key = self.get_publickey(self.script.pool) if not pub_key or not priv_key: self.logger.debug("No keys for pool. Skipping") return False user = self.get_user() try: self.session.userauth_publickey_frommemory(user, priv_key, "", pub_key) return True except SSH2Error: msg = self.session.last_error() self.logger.debug("Failed: %s", msg) return False def auth_keyboardinteractive(self): """ Keyboard-interactive authentication. Send username and password """ self.logger.debug("Trying keyboard-interactive") if not hasattr(self.session, "userauth_keyboardinteractive"): self.logger.debug( "keyboard-interactive is not supported by ssh library. Skipping" ) return False try: self.session.userauth_keyboardinteractive(self.get_user(), self.get_password()) self.logger.debug("Success") return True except SSH2Error: msg = self.session.last_error() self.logger.debug("Failed: %s", msg) return False def auth_password(self): """ Password authentication. Send username and password """ self.logger.debug("Trying password authentication") try: self.session.userauth_password(self.get_user(), self.get_password()) self.logger.debug("Success") return True except SSH2Error: msg = self.session.last_error() self.logger.debug("Failed: %s", msg) return False
def _init_session(self): self.session = Session() if self.timeout: self.session.set_timeout(self.timeout * 1000) self.session.handshake(self.sock)
class SSH2Transport(Transport): def __init__( self, host: str, port: int = -1, auth_username: str = "", auth_private_key: str = "", auth_password: str = "", auth_strict_key: bool = True, timeout_socket: int = 5, timeout_transport: int = 5, timeout_exit: bool = True, ssh_config_file: str = "", ssh_known_hosts_file: str = "", ) -> None: """ SSH2Transport Object Inherit from Transport ABC SSH2Transport <- Transport (ABC) Args: host: host ip/name to connect to port: port to connect to auth_username: username for authentication auth_private_key: path to private key for authentication auth_password: password for authentication auth_strict_key: True/False to enforce strict key checking (default is True) timeout_socket: timeout for establishing socket in seconds timeout_transport: timeout for ssh2 transport in seconds timeout_exit: True/False close transport if timeout encountered ssh_config_file: string to path for ssh config file ssh_known_hosts_file: string to path for ssh known hosts file Returns: N/A # noqa: DAR202 Raises: N/A """ cfg_port, cfg_user, cfg_private_key = self._process_ssh_config(host, ssh_config_file) if port == -1: port = cfg_port or 22 super().__init__( host, port, timeout_socket, timeout_transport, timeout_exit, ) self.auth_username: str = auth_username or cfg_user self.auth_private_key: str = auth_private_key or cfg_private_key self.auth_password: str = auth_password self.auth_strict_key: bool = auth_strict_key self.ssh_known_hosts_file: str = ssh_known_hosts_file self.session: Session = None self.channel: Channel = None self.socket = Socket(host=self.host, port=self.port, timeout=self.timeout_socket) @staticmethod def _process_ssh_config(host: str, ssh_config_file: str) -> Tuple[Optional[int], str, str]: """ Method to parse ssh config file In the future this may move to be a "helper" function as it should be very similar between paramiko and and ssh2-python... for now it can be a static method as there may be varying supported args between the two transport drivers. Args: host: host to lookup in ssh config file ssh_config_file: string path to ssh config file; passed down from `Scrape`, or the `NetworkDriver` or subclasses of it, in most cases. Returns: Tuple: port to use for ssh, username to use for ssh, identity file (private key) to use for ssh auth Raises: N/A """ ssh = SSHConfig(ssh_config_file) host_config = ssh.lookup(host) return host_config.port, host_config.user or "", host_config.identity_file or "" def open(self) -> None: """ Parent method to open session, authenticate and acquire shell Args: N/A Returns: N/A # noqa: DAR202 Raises: Exception: if socket handshake fails ScrapliAuthenticationFailed: if all authentication means fail """ if not self.socket.socket_isalive(): self.socket.socket_open() self.session = Session() self.set_timeout(self.timeout_transport) try: self.session.handshake(self.socket.sock) except Exception as exc: LOG.critical( f"Failed to complete handshake with host {self.host}; " f"Exception: {exc}" ) raise exc if self.auth_strict_key: LOG.debug(f"Attempting to validate {self.host} public key") self._verify_key() LOG.debug(f"Session to host {self.host} opened") self._authenticate() if not self._isauthenticated(): msg = f"Authentication to host {self.host} failed" LOG.critical(msg) raise ScrapliAuthenticationFailed(msg) self._open_channel() def _verify_key(self) -> None: """ Verify target host public key, raise exception if invalid/unknown Args: N/A Returns: N/A # noqa: DAR202 Raises: KeyVerificationFailed: if public key verification fails """ known_hosts = SSHKnownHosts(self.ssh_known_hosts_file) if self.host not in known_hosts.hosts.keys(): raise KeyVerificationFailed(f"{self.host} not in known_hosts!") remote_server_key_info = self.session.hostkey() encoded_remote_server_key = remote_server_key_info[0] raw_remote_public_key = base64.encodebytes(encoded_remote_server_key) remote_public_key = raw_remote_public_key.replace(b"\n", b"").decode() if known_hosts.hosts[self.host]["public_key"] != remote_public_key: raise KeyVerificationFailed( f"{self.host} in known_hosts but public key does not match!" ) def _authenticate(self) -> None: """ Parent method to try all means of authentication Args: N/A Returns: N/A # noqa: DAR202 Raises: ScrapliAuthenticationFailed: if auth fails """ if self.auth_private_key: self._authenticate_public_key() if self._isauthenticated(): LOG.debug(f"Authenticated to host {self.host} with public key auth") return if not self.auth_password or not self.auth_username: msg = ( f"Failed to authenticate to host {self.host} with private key " f"`{self.auth_private_key}`. Unable to continue authentication, " "missing username, password, or both." ) LOG.critical(msg) raise ScrapliAuthenticationFailed(msg) self._authenticate_password() if self._isauthenticated(): LOG.debug(f"Authenticated to host {self.host} with password") return self._authenticate_keyboard_interactive() if self._isauthenticated(): LOG.debug(f"Authenticated to host {self.host} with keyboard interactive") def _authenticate_public_key(self) -> None: """ Attempt to authenticate with public key authentication Args: N/A Returns: N/A # noqa: DAR202 Raises: N/A """ try: self.session.userauth_publickey_fromfile( self.auth_username, self.auth_private_key.encode() ) except AuthenticationError as exc: LOG.critical( f"Public key authentication with host {self.host} failed. Exception: {exc}." ) except SSH2Error as exc: LOG.critical( "Unknown error occurred during public key authentication with host " f"{self.host}; Exception: {exc}" ) def _authenticate_password(self) -> None: """ Attempt to authenticate with password authentication Args: N/A Returns: N/A # noqa: DAR202 Raises: Exception: if unknown (i.e. not auth failed) exception occurs """ try: self.session.userauth_password(self.auth_username, self.auth_password) except AuthenticationError: LOG.critical( f"Password authentication with host {self.host} failed. Exception: " f"`AuthenticationError`." ) except Exception as exc: LOG.critical( "Unknown error occurred during password authentication with host " f"{self.host}; Exception: {exc}" ) raise exc def _authenticate_keyboard_interactive(self) -> None: """ Attempt to authenticate with keyboard interactive authentication Args: N/A Returns: N/A # noqa: DAR202 Raises: Exception: if unknown (i.e. not auth failed) exception occurs """ try: self.session.userauth_keyboardinteractive( # pylint: disable=C0415 self.auth_username, self.auth_password ) except AttributeError as exc: LOG.critical( "Keyboard interactive authentication not supported in your ssh2-python version. " f"Exception: {exc}" ) except AuthenticationError: LOG.critical( f"Keyboard interactive authentication with host {self.host} failed. " f"Exception: `AuthenticationError`." ) except Exception as exc: LOG.critical( "Unknown error occurred during keyboard interactive authentication with host " f"{self.host}; Exception: {exc}" ) raise exc def _isauthenticated(self) -> bool: """ Check if session is authenticated Args: N/A Returns: bool: True if authenticated, else False Raises: N/A """ authenticated: bool = self.session.userauth_authenticated() return authenticated def _open_channel(self) -> None: """ Open channel, acquire pty, request interactive shell Args: N/A Returns: N/A # noqa: DAR202 Raises: N/A """ self.channel = self.session.open_session() self.channel.pty() self.channel.shell() LOG.debug(f"Channel to host {self.host} opened") def close(self) -> None: """ Close session and socket Args: N/A Returns: N/A # noqa: DAR202 Raises: N/A """ self.channel.close() LOG.debug(f"Channel to host {self.host} closed") self.socket.socket_close() def isalive(self) -> bool: """ Check if socket is alive and session is authenticated Args: N/A Returns: bool: True if socket is alive and session authenticated, else False Raises: N/A """ if self.socket.socket_isalive() and not self.channel.eof() and self._isauthenticated(): return True return False def read(self) -> bytes: """ Read data from the channel Args: N/A Returns: bytes: bytes output as read from channel Raises: N/A """ output: bytes _, output = self.channel.read(65535) return output def write(self, channel_input: str) -> None: """ Write data to the channel Args: channel_input: string to send to channel Returns: N/A # noqa: DAR202 Raises: N/A """ self.channel.write(channel_input) def set_timeout(self, timeout: int) -> None: """ Set session timeout Args: timeout: timeout in seconds Returns: N/A # noqa: DAR202 Raises: N/A """ # ssh2-python expects timeout in milliseconds self.session.set_timeout(timeout * 1000)
class IrmaSFTPv2(FTPInterface): """Irma SFTPv2 handler This class handles the connection with a sftp server functions for interacting with it. """ _Exception = IrmaSFTPv2Error # ================================== # Constructor and Destructor stuff # ================================== def __init__(self, host, port, auth, key_path, user, passwd, dst_user=None, upload_path='uploads', hash_check=False, autoconnect=True): self._sess = None self._client = None super().__init__(host, port, auth, key_path, user, passwd, dst_user, upload_path, hash_check, autoconnect) def connected(self): return self._sess is not None # ============================ # Overridden private methods # ============================ def _connect(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self._host, self._port)) self._sess = Session() self._sess.handshake(sock) if self._auth == 'key': # self._pubkey_path must be generated from private key # s.userauth_publickey_fromfile(self._user, self._pubkey_path, # self._key_path, '') raise IrmaSFTPv2Error("Pub key authentication not implemented") else: self._sess.userauth_password(self._user, self._passwd) self._client = self._sess.sftp_init() def _disconnect(self, *, force=False): self._client = None if not force: self._sess.disconnect() self._sess = None def _upload(self, remote, fobj): mode = LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | \ LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH opt = LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE with self._client.open(remote, opt, mode) as rfh: for chunk in iter(lambda: fobj.read(1024*1024), b""): rfh.write(chunk) def _download(self, remote, fobj): with self._client.open(remote, 0, 0) as rfh: for size, data in rfh: fobj.write(data) def _ls(self, remote): with self._client.opendir(remote) as rfh: paths = (p[1].decode('utf-8') for p in rfh.readdir()) return [p for p in paths if p not in ['.', '..']] def _is_file(self, remote): return not self._is_dir(remote) def _is_dir(self, remote): st = self._client.stat(remote) return stat.S_ISDIR(st.st_mode) def _rm(self, remote): self._client.unlink(remote) def _rmdir(self, remote): self._client.rmdir(remote) def _mkdir(self, remote): mode = LIBSSH2_SFTP_S_IRUSR | \ LIBSSH2_SFTP_S_IWUSR | \ LIBSSH2_SFTP_S_IXUSR self._client.mkdir(remote, mode) def _mv(self, oldremote, newremote): self._client.rename(oldremote, newremote)
import smtplib import socket from ssh2.session import Session s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print("Connecting to Machine...") s.connect(("172.18.51.115", 22)) session = Session() session.handshake(s) session.userauth_password('*****', '****') try: # Creating a session channel = session.open_session() channel.execute('ls') size, data = channel.read() while size > 0: print(data.decode()) size, data = channel.read() channel.close() if channel.get_exit_status() == 0: print("Connection established") s1 = smtplib.SMTP('smtp.gmail.com', 587) s1 = smtplib.SMTP_SSL("smtp.gmail.com", 465) s1.ehlo() s1.starttls()
# asyncssh # Source: https://asyncssh.readthedocs.io/en/latest/ import asyncssh async with asyncssh.connect('ssh.example.com' username="******", password="******") as conn: result = await conn.run(command) # ssh2-python # Source: https://pypi.org/project/ssh2-python/ from ssh2.session import Session import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('ssh.example.com', 22)) session = Session() session.handshake(sock) session.userauth_password("username", "password") channel = session.open_session() channel.execute(command) channel.wait_eof() channel.close() channel.wait_closed() # twisted.conch # Source: https://twistedmatrix.com/documents/current/conch/examples/index.html # https://stackoverflow.com/questions/22196373/sshcommandclientendpoint-twisted-how-to-execute-more-than-one-commands # https://twistedmatrix.com/documents/current/conch/howto/conch_client.html from twisted.conch.endpoints import SSHCommandClientEndpoint
def test_internal_methods(self): self.session = Session() self.session.flag(LIBSSH2_FLAG_COMPRESS, True) self.session.handshake(self.sock) response = self.session.methods(LIBSSH2_METHOD_COMP_CS) assert response[0] in COMP_BTYES
def upload(self, screenshot, name): self.loadSettings() #Save to a temporary file timestamp = time.time() try: tmpFilename = QDesktopServices.storageLocation(QDesktopServices.TempLocation) + "/" + ScreenCloud.formatFilename(str(timestamp)) except AttributeError: from PythonQt.QtCore import QStandardPaths #fix for Qt5 tmpFilename = QStandardPaths.writableLocation(QStandardPaths.TempLocation) + "/" + ScreenCloud.formatFilename(str(timestamp)) screenshot.save(QFile(tmpFilename), ScreenCloud.getScreenshotFormat()) #Connect to server try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.host, self.port)) session = Session() session.handshake(sock) except Exception as e: ScreenCloud.setError(e.message) return False if self.authMethod == "Password": try: session.userauth_password(self.username, self.password) except ssh2.exceptions.AuthenticationError: ScreenCloud.setError("Authentication failed (password)") return False else: try: session.userauth_publickey_fromfile(self.username, self.keyfile, passphrase=self.passphrase) except ssh2.exceptions.AuthenticationError: ScreenCloud.setError("Authentication failed (key)") return False except Exception as e: ScreenCloud.setError("Unknown error: " + e.message) return False sftp = session.sftp_init() mode = LIBSSH2_SFTP_S_IRUSR | \ LIBSSH2_SFTP_S_IWUSR | \ LIBSSH2_SFTP_S_IRGRP | \ LIBSSH2_SFTP_S_IROTH f_flags = LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE try: try: sftp.opendir(self.folder) except ssh2.exceptions.SFTPError: sftp.mkdir(self.folder, mode | LIBSSH2_SFTP_S_IXUSR) (filepath, filename) = os.path.split(ScreenCloud.formatFilename(name)) if len(filepath): for folder in filepath.split("/"): try: sftp.mkdir(folder) except IOError: pass source = tmpFilename destination = self.folder + "/" + ScreenCloud.formatFilename(filename) with open(source, 'rb') as local_fh, sftp.open(destination, f_flags, mode) as remote_fh: for data in local_fh: remote_fh.write(data) except IOError: ScreenCloud.setError("Failed to write " + self.folder + "/" + ScreenCloud.formatFilename(name) + ". Check permissions.") return False sock.close() if self.url: ScreenCloud.setUrl(self.url + ScreenCloud.formatFilename(name)) return True
def exec_ssh_cmd(cmd, host): username = '******' sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, 22)) session = Session() session.handshake(sock) session.agent_auth(username) session = Session() session.userauth_publickey_fromfile(username, '~/.ssh/keys/exoscale') channel = session.open_session() channel.execute(cmd) size, data = channel.read() while size > 0: print(data) size, data = channel.read() channel.close() print("Exit status: %s" % channel.get_exit_status())
import os import socket from ssh2.session import Session from ssh2.utils import version # Connection settings host = 'localhost' user = os.getlogin() # Make socket, connect sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, 22)) # Initialise session = Session() session.handshake(sock) # List available authentication methods print(session.userauth_list(user)) # Convenience function for agent based authentication session.agent_auth(user) # Agent capabilities agent = session.agent_init() agent.connect() identities = agent.get_identities() print(identities) print(identities[0].magic) del agent
def show_entry_fields(): # sys.stderr.write("\x1b[2J\x1b[H") U = Username.get() # USERNAME P = Password.get() # PASSWORD H = Hostname.get() # DEVICE HOSTNAME L = log.get() # EVENT LOG OR ACS LOG D = device.get() # CISCO HUAWEI(9306) # T=typ.get()# INFO CRITICAL TACCACCT DEBUG M = mon.get() # MONTH Day = day.get() # DAY K = Keywords.get() #keywords T = '' ############################################################################### LOGIN host = '79.128.180.20' H = H.replace(' ', '') if len(H) == 0: print('\nEnter a Hostname...\n') return if len(K) > 0: K = '|' + K.replace(' ', ' |') try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, 22)) #host/port #start session session = Session() session.handshake(sock) session.userauth_password(U, P) channel = session.open_session() print('Login Successful!!!\n') except: print('WRONG CREDENTIALS\n') return channel.shell() channel.write('host ' + H + '\n') size, data = channel.read() ip = re.findall(r'(\d{2}.\d{1}\d?\d?.\d{1}\d?\d?.\d{1}\d?\d?)', str(data)) print(data.decode()) if 'not found:' in data.decode(): return ############################################################################### PATHFINDER H = ' ' + H + ' ' if L == "Machine Logs": command = K if D == 'Cisco': # if T == 'Tacacct':#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ # print('Wrong input--> Type') # return print('>>>', 'cd /netlog/cisco') channel.write('cd /netlog/cisco\n') elif not D == 'Cisco': T = 'info' # if not T== 'Info':#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ # print('Wrong input--> Type') # return print('>>>', 'cd /netlog/misco') channel.write('cd /netlog/misco\n') print('>>>', 'ls -lt') channel.write('ls -lt\n') ################################################################################################# elif L == "ACS (Tacaccs)": # check the condidions that do not work with #GET IP FROM HOSTNAME HH = H if D == 'Cisco': H = " '" + ip[0] + ",' " command = r''' | awk -F"atheacs|CmdSet=|RequestLatency|User=|Port=" '/CmdAV/ {print$1$5$3}'| egrep -v "acctman|scriptman" | grep CmdAV | more''' # if not T == 'Tacacct':#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ # print('Wrong input--> Type') # return T = 'tacacct' print('>>>', 'cd /netlog/acslogs') channel.write('cd /netlog/acslogs\n') elif D == 'Huawei 9306': H = " '" + ip[0] + ",' " command = r''' | awk -F"atheacs|CmdSet=|RequestLatency|User=|Authen-Method=|Protocol" '/CmdAV/ {print$1$6$3}' | egrep -v "acctman|scriptman" | grep CmdAV | more''' # if not T == 'Tacacct':#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ # print('Wrong input--> Type') # return T = 'tacacct' print('>>>', 'cd /netlog/acslogs') channel.write('cd /netlog/acslogs\n') elif D == 'Huawei': command = r''' | awk -F":" '/command/ {printf"%s-%s-%s %s -> %s %s \n",$1,$2,$6,$7,$8,$9}' | more''' # if not T == 'Info':#$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ # print('Wrong input--> Type') # return T = 'info' print('>>>', 'cd /netlog/misco') channel.write('cd /netlog/misco\n') print('>>>', 'ls -lt') channel.write('ls -lt\n') # ################################################################################ GIVE COMMAND AND EXPORT # READ FROM SCREEN AND CREATE STRING NAMED TEXT text = '' count = 0 buffer = 1024 while True: size, data = channel.read(buffer) text += str(data.decode()) count += int(size) if size < buffer: break # print(text) text = re.sub(' +', ' ', text).strip() #remove blanks df = pd.DataFrame([x.split(' ') for x in text.split('\n')]).dropna() df = df[[3, 5, 6, 7, 8]] df = df.rename(columns={ 3: "Type", 5: "Month", 6: "Day", 7: "Time", 8: "File_name" }) try: df2 = df.loc[(df['Month'] == M).reset_index(drop=True) & (df['Day'] == Day) & (df["File_name"].str.contains(T.lower()))].reset_index( drop=True) #month date and type || except: print('\nNo records for', M, Day, '-->', T + '.log') return ij = 0 if len(df2) > 1: print('\nSelect file\n', df2) while True: ij = input("Please enter a number: ") try: if ij == '': print('Canceled..\n\n') return elif int(ij) >= 0 and int(ij) <= (len(df2) - 1): break except: print('\nInvalid input. Please select a number between 0 and', len(df2) - 1) print('Opening: ', df2.at[int(ij), 'File_name']) string = df2.at[int(ij), 'File_name'] if '.gz' in string: c1 = 'zgrep' else: c1 = 'grep' final_command = c1 + H + string + command print('\n>>>', final_command) channel.write(final_command + '\n') channel.write('date\n') text = '' count = 0 buffer = 4096 while True: size, data = channel.read(buffer) text += str(data.decode()) count += int(size) print('reading:', count) if size < buffer: print('reading complete..') break text = text.replace('CmdAV=', '') text = text.replace('CmdArgAV=', '') text = text.replace('<cr>', '') text_file = open("out.txt", "w") text_file.write(text) text_file.close() os.startfile("out.txt")
class SSHIOStream(IOStream): SSH_KEY_PREFIX = config.path.ssh_key_prefix _key_cache = cachetools.TTLCache(100, ttl=60) def __init__(self, sock, cli, *args, **kwargs): super(SSHIOStream, self).__init__(sock, *args, **kwargs) self.cli = cli self.script = self.cli.script self.logger = cli.logger self.session = None self.channel = None def __del__(self): self.channel = None self.session = None @classmethod @cachetools.cachedmethod(operator.attrgetter("_key_cache"), lock=lambda _: key_lock) def get_publickey(cls, pool): """ Return public, private key pair :return: bytes, bytes or None, None """ logger.debug("Getting keys for pool %s", pool) pub_path = os.path.join(config.path.ssh_key_prefix, pool, "id_rsa.pub") priv_path = os.path.join(config.path.ssh_key_prefix, pool, "id_rsa") if not os.path.exists(pub_path): logger.debug("No public key for pool %s (%s)", pool, pub_path) return None, None if not os.path.exists(priv_path): logger.debug("No private key for pool %s (%s)", pool, priv_path) return None, None with open(pub_path) as fpub, open(priv_path) as fpriv: return fpub.read(), fpriv.read() @tornado.gen.coroutine def startup(self): """ SSH session startup """ user = self.script.credentials["user"] if user is None: user = "" self.logger.debug("Startup ssh session for user '%s'", user) self.session = Session() try: self.session.handshake(self.socket) host_hash = smart_bytes(self.session.hostkey_hash(LIBSSH2_HOSTKEY_HASH_SHA1)) hex_hash = smart_text(codecs.encode(host_hash, "hex")) self.logger.debug("Connected. Host fingerprint is %s", hex_hash) auth_methods = self.session.userauth_list(user) if not auth_methods: self.logger.info("No supported authentication methods. Failed to log in") raise CLIAuthFailed("Failed to log in") self.logger.debug("Supported authentication methods: %s", ", ".join(auth_methods)) # Try to authenticate authenticated = False for method in auth_methods: ah = getattr(self, "auth_%s" % method.replace("-", ""), None) if ah: metrics["ssh_auth", ("method", method)] += 1 authenticated |= ah() if authenticated: metrics["ssh_auth_success", ("method", method)] += 1 break metrics["ssh_auth_fail", ("method", method)] += 1 if not authenticated: self.logger.error("Failed to authenticate user '%s'", user) raise CLIAuthFailed("Failed to log in") self.logger.debug("User is authenticated") self.logger.debug("Open channel") self.channel = self.session.open_session() self.channel.pty("xterm") self.channel.shell() self.session.keepalive_config(False, 60) self.session.set_blocking(False) except SSH2Error as e: self.logger.info("SSH Error: %s", e) raise CLISSHProtocolError("SSH Error: %s" % e) def read_from_fd(self): try: metrics["ssh_reads"] += 1 code, data = self.channel.read(self.read_chunk_size) if code == 0: if self.channel.eof(): self.logger.info("SSH session reset") self.close() metrics["ssh_reads_blocked"] += 1 return None elif code > 0: metrics["ssh_read_bytes"] += len(data) return data elif code == LIBSSH2_ERROR_EAGAIN: metrics["ssh_reads_blocked"] += 1 return None # Blocking call metrics["ssh_errors", ("code", code)] += 1 raise CLISSHProtocolError("SSH Error code %s" % code) except SSH2Error as e: raise CLISSHProtocolError("SSH Error: %s" % e) def write_to_fd(self, data): # ssh2 doesn't accept memoryview metrics["ssh_writes"] += 1 if isinstance(data, memoryview): data = data.tobytes() try: _, written = self.channel.write(data) metrics["ssh_write_bytes"] += written return written except SSH2Error as e: raise CLISSHProtocolError("SSH Error: %s" % e) def close(self, exc_info=False): if not self.closed(): if self.channel: self.logger.debug("Closing channel") try: self.channel.close() except SSH2Error as e: self.logger.debug("Cannot close channel clearly: %s", e) self.channel = None if self.session: self.logger.debug("Closing ssh session") self.session = None super(SSHIOStream, self).close(exc_info=exc_info) def get_user(self): # type: () -> str """ Get current user """ return self.script.credentials["user"] or "" def get_password(self): # type: () -> str """ Get current user's password """ return self.script.credentials["password"] or "" def auth_publickey(self): """ Public key authentication """ self.logger.debug("Trying publickey authentication") pub_key, priv_key = self.get_publickey(self.script.pool) if not pub_key or not priv_key: self.logger.debug("No keys for pool. Skipping") return False user = self.get_user() try: self.session.userauth_publickey_frommemory(user, priv_key, "", pub_key) return True except SSH2Error: msg = self.session.last_error() self.logger.debug("Failed: %s", msg) return False def auth_keyboardinteractive(self): """ Keyboard-interactive authentication. Send username and password """ self.logger.debug("Trying keyboard-interactive") if not hasattr(self.session, "userauth_keyboardinteractive"): self.logger.debug("keyboard-interactive is not supported by ssh library. Skipping") return False try: self.session.userauth_keyboardinteractive(self.get_user(), self.get_password()) self.logger.debug("Success") return True except SSH2Error: msg = self.session.last_error() self.logger.debug("Failed: %s", msg) return False def auth_password(self): """ Password authentication. Send username and password """ self.logger.debug("Trying password authentication") try: self.session.userauth_password(self.get_user(), self.get_password()) self.logger.debug("Success") return True except SSH2Error: msg = self.session.last_error() self.logger.debug("Failed: %s", msg) return False
from ssh2.session import Session from ssh2.session import LIBSSH2_HOSTKEY_HASH_SHA1, LIBSSH2_HOSTKEY_TYPE_RSA from ssh2.knownhost import LIBSSH2_KNOWNHOST_TYPE_PLAIN, \ LIBSSH2_KNOWNHOST_KEYENC_RAW, LIBSSH2_KNOWNHOST_KEY_SSHRSA # Connection settings host = 'localhost' user = os.getlogin() known_hosts = os.sep.join([os.path.expanduser('~'), '.ssh', 'known_hosts']) # Make socket, connect sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, 22)) # Initialise session = Session() session.handshake(sock) host_key, key_type = session.hostkey() server_key_type = LIBSSH2_KNOWNHOST_KEY_SSHRSA \ if key_type == LIBSSH2_HOSTKEY_TYPE_RSA \ else LIBSSH2_KNOWNHOST_KEY_SSHDSS kh = session.knownhost_init() _read_hosts = kh.readfile(known_hosts) print("Read %s hosts from known hosts file at %s" % (_read_hosts, known_hosts)) # Verification type_mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | \ LIBSSH2_KNOWNHOST_KEYENC_RAW | \
from ssh2.session import Session from ssh2.channel import Channel s = Session() c = Channel(s)
class IrmaSFTPv2(FTPInterface): """Irma SFTPv2 handler This class handles the connection with a sftp server functions for interacting with it. """ _Exception = IrmaSFTPv2Error # ================================== # Constructor and Destructor stuff # ================================== def __init__(self, host, port, auth, key_path, user, passwd, dst_user=None, upload_path='uploads', hash_check=False, autoconnect=True): self._sess = None self._client = None super().__init__(host, port, auth, key_path, user, passwd, dst_user, upload_path, hash_check, autoconnect) def connected(self): return self._sess is not None # ============================ # Overridden private methods # ============================ def _connect(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self._host, self._port)) self._sess = Session() self._sess.handshake(sock) if self._auth == 'key': # self._pubkey_path must be generated from private key # s.userauth_publickey_fromfile(self._user, self._pubkey_path, # self._key_path, '') raise IrmaSFTPv2Error("Pub key authentication not implemented") else: self._sess.userauth_password(self._user, self._passwd) self._client = self._sess.sftp_init() def _disconnect(self, *, force=False): self._client = None if not force: self._sess.disconnect() self._sess = None def _upload(self, remote, fobj): mode = LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | \ LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH opt = LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE with self._client.open(remote, opt, mode) as rfh: for chunk in iter(lambda: fobj.read(1024 * 1024), b""): rfh.write(chunk) def _download(self, remote, fobj): with self._client.open(remote, 0, 0) as rfh: for size, data in rfh: fobj.write(data) def _ls(self, remote): with self._client.opendir(remote) as rfh: paths = (p[1].decode('utf-8') for p in rfh.readdir()) return [p for p in paths if p not in ['.', '..']] def _is_file(self, remote): return not self._is_dir(remote) def _is_dir(self, remote): st = self._client.stat(remote) return stat.S_ISDIR(st.st_mode) def _rm(self, remote): self._client.unlink(remote) def _rmdir(self, remote): self._client.rmdir(remote) def _mkdir(self, remote): mode = LIBSSH2_SFTP_S_IRUSR | \ LIBSSH2_SFTP_S_IWUSR | \ LIBSSH2_SFTP_S_IXUSR self._client.mkdir(remote, mode) def _mv(self, oldremote, newremote): self._client.rename(oldremote, newremote)
from __future__ import print_function import os import socket from ssh2.session import Session host = '192.168.0.16' user = os.getlogin() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, 22)) session = Session() session.handshake(sock) session.userauth_password(os.getenv('remoteUser'), os.getenv('remotePassword')) channel = session.open_session() channel.execute('echo walrus | sudo -S reboot') size, data = channel.read() while size > 0: print(data) size, data = channel.read() channel.close() print("Exit status: %s" % channel.get_exit_status())
class ssh: """ format of the config file(eg. ~/.emuhelper/server.conf): [server] ip = xx.xx.xx.xx user = xxx serverdir = /xxx/xxx/xxx/ password = xxx """ def __init__(self): self.ip = None self.user = None self.password = None def get_info(self, conf): config = configparser.ConfigParser() config.read(conf) self.ip = config.get("server", "ip") self.user = config.get("server", "user") self.password = config.get("server", "password") self.serverdir = config.get("server", "serverdir") def login(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((self.ip, 22)) self.session = Session() self.session.handshake(self.sock) self.session.userauth_password(self.user, self.password) def submit(self, workdir, jobfile, server="pbs"): """ server: pbs or llhpc """ channel = self.session.open_session() if server == "llhpc": channel.execute("cd %s; yhbatch %s" % (os.path.join(self.serverdir, workdir), jobfile)) elif server == "pbs": channel.execute("cd %s; qsub %s" % (os.path.join(self.serverdir, workdir), jobfile)) print("\n\n") print("=========================================\n") print(" information from remote server\n") print("=========================================\n") all_data = b'' size, data = channel.read() while size > 0: #print(data.decode()) all_data = all_data + data size, data = channel.read() channel.close() print(all_data.decode()) print("Exit status: %s" % channel.get_exit_status()) def execute(self, cmd): channel = self.session.open_session() channel.execute(cmd) print("\n\n") print("=========================================\n") print(" information from remote server\n") print("=========================================\n") all_data = b'' size, data = channel.read() while size > 0: #print(data.decode()) all_data = all_data + data size, data = channel.read() channel.close() print(all_data.decode()) print("Exit status: %s" % channel.get_exit_status()) def execute_silent(self, cmd): channel = self.session.open_session() channel.execute(cmd) all_data = b'' size, data = channel.read() while size > 0: all_data = all_data + data size, data = channel.read() channel.close() return all_data
def _establish_ssh_session(self): # Connect to remote host. try: sock = socket.create_connection( (str(self._ssh_host), self._ssh_port)) except Exception: log.error("Cannot connect to host '%s' (%s, %d).", self.name, self._ssh_host, self._ssh_port) raise # SSH handshake. ssh_session = Session() ssh_session.handshake(sock) # Verify host key. Accept keys from previously unknown hosts on first connection. hosts = ssh_session.knownhost_init() testbed_root = os.path.dirname(os.path.abspath(inspect.stack()[-1][1])) known_hosts_path = os.path.join(testbed_root, KNOWN_HOSTS_FILE) try: hosts.readfile(known_hosts_path) except ssh2.exceptions.KnownHostReadFileError: pass # ignore, file is created/overwritten later host_key, key_type = ssh_session.hostkey() server_type = None if key_type == LIBSSH2_HOSTKEY_TYPE_RSA: server_type = LIBSSH2_KNOWNHOST_KEY_SSHRSA else: server_type = LIBSSH2_KNOWNHOST_KEY_SSHDSS type_mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | server_type try: hosts.checkp( str(self._ssh_host).encode('utf-8'), self._ssh_port, host_key, type_mask) except ssh2.exceptions.KnownHostCheckNotFoundError: log.warn("Host key of '%s' (%s, %d) added to known hosts.", self.name, self._ssh_host, self._ssh_port) hosts.addc( str(self._ssh_host).encode('utf-8'), host_key, type_mask) hosts.writefile(known_hosts_path) except ssh2.exceptions.KnownHostCheckMisMatchError: log.error("Host key of '%s' (%s, %d) does not match known key.", self.name, self._ssh_host, self._ssh_port) raise # Authenticate at remote host. try: if self._identity_file is None: ssh_session.agent_auth(self._username) else: ssh_session.userauth_publickey_fromfile( self._username, self._identity_file) except Exception: log.error("Authentication at host '%s' (%s, %d) failed.", self.name, self._ssh_host, self._ssh_port) ssh_session.disconnect() raise return ssh_session
def _connect(self): """create a connection for this thread""" self._abort = False size_bk = 0 if self.parent_ui: size_bk += self.parent_ui.progress["maximum"] self.parent_ui.progress.configure(mode="indeterminate", maximum=100) self.parent_ui.progress.start() if self._mode == "SFTP": try: sock = socket(AF_INET, SOCK_STREAM) sock.settimeout(10) sock.connect((self._host, self._port)) cli = Session() cli.set_timeout(10000) cli.handshake(sock) cli.userauth_password(self._name, self._passwd) cli.set_timeout(0) self._connection = cli.sftp_init() except Timeout: self._abort = True messagebox.showerror("Connection Error", "Connection timeout on login.") except AuthenticationError as e: self._abort = True messagebox.showerror("Authentication Error", "Wrong login credentials.") except Exception as e: print(type(e), e.args, str(e)) self._abort = True messagebox.showerror( "Connection Error", "Could not establish a connection.\n%s" % e) finally: if self.parent_ui: self.parent_ui.progress.stop() self.parent_ui.progress.configure(value=0, mode="determinate", maximum=size_bk) else: # FTP try: ftp = FTP() ftp.encoding = self._enc ftp.connect(self._host, self._port, 10) ftp.login(self._name, self._passwd) self._connection = ftp except Exception as e: self._abort = True messagebox.showerror("Connection Error", str(e)) finally: if self.parent_ui: self.parent_ui.progress.stop() self.parent_ui.progress.configure(value=0, mode="determinate", maximum=size_bk)
def create_session(self): self.session = Session() self.session.set_timeout(0.5) self.session.handshake(self.socket) return self.session.userauth_password(self.username, self.password)
def linux_host_runshell(self, commands): assert type(commands) is list and len( commands ) != 0, "[Assert Error] Input commands is not a list or it's empty!" auth_method = 'password' # Make socket, connect sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((self.host_ip, self.port)) # Initialise session = Session() session.handshake(sock) userauth_list = session.userauth_list(self.username) self.LOG.print_log( 'DEBUG', "In host {}, user {} authentication list is: {}.".format( self.host_ip, self.username, userauth_list)) assert auth_method in userauth_list, "[Assert Error] The user in linux host must support {} authentication!".format( auth_method) session.userauth_password(self.username, self.password) # Channel initialise, exec and wait for end channel = session.open_session() channel.shell() for command in commands: self.LOG.print_log('INFO', "Run cmd: {}".format(command)) channel.write(command + '\n') # check session is in non-blocking mode blocking_mode = session.get_blocking() self.LOG.print_log( 'INFO', 'Now the session is in blocking mode ? {}'.format(blocking_mode)) if blocking_mode: self.LOG.print_log('INFO', "Set session blocking mode to False...") session.set_blocking(False) blocking_mode = session.get_blocking() self.LOG.print_log( 'INFO', 'Now the session is in blocking mode ? {}'.format( blocking_mode)) # Get output round_num = 0 timeout = 40 begin_time = time.time() output = b'' while True: if len(output) == 0 and time.time() - begin_time > timeout * 3: self.LOG.print_log( 'INFO', 'Nothing is received in {} seconds, exit.'.format( timeout * 3)) break elif len(output) > 0 and time.time() - begin_time > timeout: self.LOG.print_log( 'INFO', 'No data is received in {} seconds, exit.'.format(timeout)) break size, data = channel.read() if size > 0: self.LOG.print_log( 'DEBUG', "round_number {} Got new data:\n".format(round_num, data)) self.LOG.print_plain_log('DEBUG', data) output += data begin_time = time.time() else: self.LOG.print_log( 'DEBUG', "round_number {} No new data.".format(round_num)) time.sleep(0.5) round_num += 1 channel.close() output = output.decode("utf-8").strip() return_code = channel.get_exit_status() self.LOG.print_log('INFO', "output is:\n") self.LOG.print_plain_log('INFO', output) self.LOG.print_log('INFO', "exit status is:{}.".format(return_code)) return output, return_code
class SSH2NetSessionSSH2: def __init__(self, p_self): """ Initialize SSH2NetSessionSSH2 Object This is the default underlying "driver" for ssh2net. This has been pulled out of the "base" SSH2NetSession class to provide a mechanism for supporting both "ssh2-python" and "paramiko". Args: p_self: SSH2Net object Returns: N/A # noqa Raises: N/A # noqa """ self.__dict__ = p_self.__dict__ self._session_alive = p_self._session_alive self._session_open = p_self._session_open self._channel_alive = p_self._channel_alive def _session_open_connect(self) -> None: """ Perform session handshake Args: N/A # noqa Returns: N/A # noqa Raises: Exception: catch all for unknown exceptions during session handshake """ self.session = Session() if self.session_timeout: self.session.set_timeout(self.session_timeout) try: self.session.handshake(self.sock) except Exception as exc: logging.critical( f"Failed to complete handshake with host {self.host}; " f"Exception: {exc}" ) raise exc def _session_public_key_auth(self) -> None: """ Perform public key based auth on SSH2NetSession Args: N/A # noqa Returns: N/A # noqa Raises: Exception: catch all for unhandled exceptions """ try: self.session.userauth_publickey_fromfile(self.auth_user, self.auth_public_key) except AuthenticationError: logging.critical(f"Public key authentication with host {self.host} failed. ") except Exception as exc: logging.critical( "Unknown error occurred during public key authentication with host " f"{self.host}; Exception: {exc}" ) raise exc def _session_password_auth(self) -> None: """ Perform password based auth on SSH2NetSession Args: N/A # noqa Returns: N/A # noqa Raises: AuthenticationFailed: if authentication fails Exception: catch all for unknown other exceptions """ try: self.session.userauth_password(self.auth_user, self.auth_password) except AuthenticationError as exc: logging.critical( f"Password authentication with host {self.host} failed. Exception: {exc}." f"\n\tTrying keyboard interactive auth..." ) try: self.session.userauth_keyboardinteractive(self.auth_user, self.auth_password) except AuthenticationError as exc: logging.critical( f"Keyboard interactive authentication with host {self.host} failed. " f"Exception: {exc}." ) raise AuthenticationFailed except Exception as exc: logging.critical( "Unknown error occurred during keyboard interactive authentication with host " f"{self.host}; Exception: {exc}" ) raise exc except Exception as exc: logging.critical( "Unknown error occurred during password authentication with host " f"{self.host}; Exception: {exc}" ) raise exc def _channel_open_driver(self) -> None: """ Open channel Args: N/A # noqa Returns: N/A # noqa Raises: N/A # noqa """ self.channel = self.session.open_session() self.channel.pty() logging.debug(f"Channel to host {self.host} opened") def _channel_invoke_shell(self) -> None: """ Invoke shell on channel Args: N/A # noqa Returns: N/A # noqa Raises: N/A # noqa """ self._shell = True self.channel.shell()
def runcmd(self, sp_ip, command, background=False, stdin_str=None): assert type(command) is str and command != "" and not command.isspace(), \ "[Assert Error] Input command is invalid!" if stdin_str: assert background is False, "[Assert Error] Input background and stdin_str, you can only select one of them." assert type( stdin_str ) is str, "[Assert Error] Input stdin_str must be a string!" auth_method = 'publickey' # Make socket, connect sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((sp_ip, self.port)) # Initialise session = Session() session.handshake(sock) userauth_list = session.userauth_list(self.username) self.LOG.print_log( 'DEBUG', "In host {}, user {} authentication list is: {}.".format( sp_ip, self.username, userauth_list)) assert auth_method in userauth_list, "[Assert Error] The user in storage must support {} authentication!".format( auth_method) session.userauth_publickey_fromfile(self.username, self.private_key_file) # Channel initialise, exec and wait for end self.LOG.print_log('INFO', "Run cmd: {}.".format(command)) channel = session.open_session() # channel.execute(command) # channel.wait_eof() # channel.close() # channel.wait_closed() if background: channel.execute(command) time.sleep(5) channel.close() elif stdin_str: channel.execute(command) time.sleep(5) channel.write(stdin_str) channel.wait_eof() channel.close() channel.wait_closed() else: channel.execute(command) channel.wait_eof() channel.close() channel.wait_closed() # Get output output = b'' size, data = channel.read() while size > 0: output += data size, data = channel.read() output = output.decode("utf-8").strip() # Get exit status return_code = channel.get_exit_status() self.LOG.print_log('INFO', "Command {} - output is:\n".format(command)) self.LOG.print_plain_log('INFO', output) self.LOG.print_log( 'INFO', "Command {} - exit status is:{}.".format(command, return_code)) return output, return_code