USER = '******' PASSWORD = '******' TIMEOUT = 30 PORT = 22 try: ssh.connect( host=HOST, user=USER, password=PASSWORD, timeout=TIMEOUT, port=PORT, ) except LibsshSessionException as ssh_exc: print(f'Failed to connect to {HOST}:{PORT} over SSH: {ssh_exc!s}') print(f'{ssh.is_connected=}') remote_file = '/etc/hosts' local_file = '/tmp/hosts' sftp = ssh.sftp() sftp.get(remote_file, local_file) sftp.close() remote_file = '/etc/hosts' local_file = '/tmp/hosts' sftp = ssh.sftp() sftp.put(remote_file, local_file) sftp.close() ssh.close()
class Connection(ConnectionBase): """SSH based connections with Paramiko""" transport = "ansible.netcommon.libssh" _log_channel = None def _cache_key(self): return "%s__%s__" % ( self._play_context.remote_addr, self._play_context.remote_user, ) def _connect(self): cache_key = self._cache_key() if cache_key in SSH_CONNECTION_CACHE: self.ssh = SSH_CONNECTION_CACHE[cache_key] else: self.ssh = SSH_CONNECTION_CACHE[ cache_key] = self._connect_uncached() return self def _set_log_channel(self, name): self._log_channel = name def _get_proxy_command(self, port=22): proxy_command = None # Parse ansible_ssh_common_args, specifically looking for ProxyCommand ssh_args = [ getattr(self._play_context, "ssh_extra_args", "") or "", getattr(self._play_context, "ssh_common_args", "") or "", getattr(self._play_context, "ssh_args", "") or "", ] if ssh_args is not None: args = self._split_ssh_args(" ".join(ssh_args)) for i, arg in enumerate(args): if arg.lower() == "proxycommand": # _split_ssh_args split ProxyCommand from the command itself proxy_command = args[i + 1] else: # ProxyCommand and the command itself are a single string match = SETTINGS_REGEX.match(arg) if match: if match.group(1).lower() == "proxycommand": proxy_command = match.group(2) if proxy_command: break proxy_command = proxy_command or self.get_option("proxy_command") if proxy_command: replacers = { "%h": self._play_context.remote_addr, "%p": port, "%r": self._play_context.remote_user, } for find, replace in replacers.items(): proxy_command = proxy_command.replace(find, str(replace)) return proxy_command def _connect_uncached(self): """activates the connection object""" if not HAS_PYLIBSSH: raise AnsibleError(missing_required_lib("ansible-pylibssh")) ssh_connect_kwargs = {} remote_user = self.get_option("remote_user") remote_addr = self.get_option("remote_addr") port = self._play_context.port or 22 display.vvv( "ESTABLISH LIBSSH CONNECTION FOR USER: %s on PORT %s TO %s" % (remote_user, port, remote_addr), host=remote_addr, ) self.ssh = Session() if self._play_context.verbosity > 3: self.ssh.set_log_level(logging.INFO) self.keyfile = os.path.expanduser("~/.ssh/known_hosts") proxy_command = self._get_proxy_command(port) try: private_key = None if self._play_context.private_key_file: with open( os.path.expanduser( self._play_context.private_key_file)) as fp: b_content = fp.read() private_key = to_bytes(b_content, errors="surrogate_or_strict") if proxy_command: ssh_connect_kwargs["proxycommand"] = proxy_command self.ssh.set_missing_host_key_policy( MyAddPolicy(self._new_stdin, self)) self.ssh.connect( host=remote_addr.lower(), user=remote_user, look_for_keys=self.get_option("look_for_keys"), host_key_checking=self.get_option("host_key_checking"), password=self.get_option("password"), private_key=private_key, timeout=self._play_context.timeout, port=port, **ssh_connect_kwargs) except LibsshSessionException as e: msg = "ssh connection failed: " + to_text(e) raise AnsibleConnectionFailure(msg) except Exception as e: raise AnsibleConnectionFailure(to_text(e)) display.vvv("ssh connection is OK: " + str(self.ssh)) return self.ssh def exec_command(self, cmd, in_data=None, sudoable=True): """run a command on the remote host""" super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) if in_data: raise AnsibleError( "Internal Error: this module does not support optimized module pipelining" ) bufsize = 4096 try: self.chan = self.ssh.new_channel() except Exception as e: text_e = to_text(e) msg = "Failed to open session" if text_e: msg += ": %s" % text_e raise AnsibleConnectionFailure(to_native(msg)) # sudo usually requires a PTY (cf. requiretty option), therefore # we give it one by default (pty=True in ansible.cfg), and we try # to initialise from the calling environment when sudoable is enabled if self.get_option("pty") and sudoable: self.chan.request_shell() display.vvv("EXEC %s" % cmd, host=self._play_context.remote_addr) cmd = to_bytes(cmd, errors="surrogate_or_strict") result = None no_prompt_out = b"" no_prompt_err = b"" become_output = b"" out = b"" err = b"" try: if self.become and self.become.expect_prompt(): passprompt = False become_sucess = False self.chan.sendall(cmd) while not (become_sucess or passprompt): display.debug("Waiting for Privilege Escalation input") self.chan.poll(timeout=self._play_context.timeout) chunk = self.chan.recv(bufsize) display.debug("chunk is: %s" % chunk) if not chunk: if b"unknown user" in become_output: n_become_user = to_native( self.become.get_option( "become_user", playcontext=self._play_context, )) raise AnsibleError("user %s does not exist" % n_become_user) else: break # raise AnsibleError('ssh connection closed waiting for password prompt') become_output += chunk # need to check every line because we might get lectured # and we might get the middle of a line in a chunk for line in become_output.splitlines(True): if self.become.check_success(line): become_sucess = True break if self.become.check_password_prompt(line): passprompt = True break if passprompt: if self.become: become_pass = self.become.get_option( "become_pass", playcontext=self._play_context) self.chan.sendall( to_bytes(become_pass, errors="surrogate_or_strict") + b"\n") else: raise AnsibleError( "A password is required but none was supplied") else: no_prompt_out += become_output no_prompt_err += become_output else: result = self.chan.exec_command( to_text(cmd, errors="surrogate_or_strict")) except socket.timeout: raise AnsibleError( "ssh timed out waiting for privilege escalation.\n" + become_output) if result: rc = result.returncode out = result.stdout err = result.stderr else: rc = self.chan.get_channel_exit_status() return rc, out, err def put_file(self, in_path, out_path, proto="sftp"): """transfer a file from local to remote""" super(Connection, self).put_file(in_path, out_path) display.vvv( "PUT %s TO %s" % (in_path, out_path), host=self._play_context.remote_addr, ) if not os.path.exists(to_bytes(in_path, errors="surrogate_or_strict")): raise AnsibleFileNotFound("file or module does not exist: %s" % in_path) if proto == "sftp": try: self.sftp = self.ssh.sftp() except Exception as e: raise AnsibleError("failed to open a SFTP connection (%s)" % e) try: self.sftp.put( to_bytes(in_path, errors="surrogate_or_strict"), to_bytes(out_path, errors="surrogate_or_strict"), ) except IOError: raise AnsibleError("failed to transfer file to %s" % out_path) elif proto == "scp": scp = self.ssh.scp() try: scp.put(in_path, out_path) except LibsshSCPException as exc: raise AnsibleError("Error transferring file to %s: %s" % (out_path, to_text(exc))) else: raise AnsibleError( "Don't know how to transfer file over protocol %s" % proto) def _connect_sftp(self): cache_key = "%s__%s__" % ( self._play_context.remote_addr, self._play_context.remote_user, ) if cache_key in SFTP_CONNECTION_CACHE: return SFTP_CONNECTION_CACHE[cache_key] else: result = SFTP_CONNECTION_CACHE[cache_key] = self._connect( ).ssh.sftp() return result def fetch_file(self, in_path, out_path, proto="sftp"): """save a remote file to the specified path""" super(Connection, self).fetch_file(in_path, out_path) display.vvv( "FETCH %s TO %s" % (in_path, out_path), host=self._play_context.remote_addr, ) if proto == "sftp": try: self.sftp = self._connect_sftp() except Exception as e: raise AnsibleError("failed to open a SFTP connection (%s)" % to_native(e)) try: self.sftp.get( to_bytes(in_path, errors="surrogate_or_strict"), to_bytes(out_path, errors="surrogate_or_strict"), ) except IOError: raise AnsibleError("failed to transfer file from %s" % in_path) elif proto == "scp": scp = self.ssh.scp() try: scp.get(out_path, in_path) except LibsshSCPException as exc: raise AnsibleError("Error transferring file from %s: %s" % (out_path, to_text(exc))) else: raise AnsibleError( "Don't know how to transfer file over protocol %s" % proto) def reset(self): self.close() self._connect() def close(self): """terminate the connection""" cache_key = self._cache_key() SSH_CONNECTION_CACHE.pop(cache_key, None) SFTP_CONNECTION_CACHE.pop(cache_key, None) if hasattr(self, "sftp"): if self.sftp is not None: self.sftp.close() if hasattr(self, "chan"): if self.chan is not None: self.chan.close() self.ssh.close() self._connected = False