def run_interactive(ssh_info, interactive, cmd, request_pty, forwarded_ports): """Runs a command on an SSH server. If `interactive` is True, we'll try to find an ``ssh`` executable, falling back to paramiko if it's not found. The terminal handling code is a bit wonky, so using ``ssh`` is definitely a good idea, especially on Windows. Non-interactive commands should run fine. :param ssh_info: dict with `hostname`, `port`, `username`, `key_filename`, passed directly to paramiko :type ssh_info: dict :param interactive: whether to connect local input to the remote process :type interactive: bool :param cmd: command-line to run on the server :type cmd: basestring :param request_pty: whether to request a PTY from the SSH server :type request_pty: bool :param forwarded_ports: ports to forward back to us; iterable of pairs ``(port_number, connector)`` where `port_number` is the remote port number and `connector` is the connector object used to build the connected socket to forward to on this side """ if interactive: ssh_exe = find_ssh_executable() else: ssh_exe = None if interactive and ssh_exe: record_usage(vagrant_ssh='ssh') args = [ ssh_exe, '-t' if request_pty else '-T', # Force allocation of PTY '-o', 'StrictHostKeyChecking=no', # Silently accept host keys '-o', 'UserKnownHostsFile=/dev/null', # Don't store host keys '-i', ssh_info['key_filename'], '-p', '%d' % ssh_info['port'] ] for remote_port, connector in forwarded_ports: # Remote port will connect to a local port fwd = LocalForwarder(connector) args.append('-R%d:127.0.0.1:%d' % (remote_port, fwd.local_port)) args.append('%s@%s' % (ssh_info['username'], ssh_info['hostname'])) args.append(cmd) return interruptible_call(args) else: record_usage(vagrant_ssh='interactive' if interactive else 'simple') # Connects to the machine ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(IgnoreMissingKey()) ssh.connect(**ssh_info) # Starts forwarding forwarders = [] for remote_port, connector in forwarded_ports: forwarders.append( SSHForwarder(ssh.get_transport(), remote_port, connector)) chan = ssh.get_transport().open_session() if request_pty: chan.get_pty() # Execute command logging.info("Connected via SSH, running command...") chan.exec_command(cmd) # Get output if interactive: interactive_shell(chan) else: chan.shutdown_write() while True: data = chan.recv(1024) if len(data) == 0: break sys.stdout.buffer.write(data) sys.stdout.flush() retcode = chan.recv_exit_status() ssh.close() return retcode
def run_interactive(ssh_info, interactive, cmd, request_pty, forwarded_ports): """Runs a command on an SSH server. If `interactive` is True, we'll try to find an ``ssh`` executable, falling back to paramiko if it's not found. The terminal handling code is a bit wonky, so using ``ssh`` is definitely a good idea, especially on Windows. Non-interactive commands should run fine. :param ssh_info: dict with `hostname`, `port`, `username`, `key_filename`, passed directly to paramiko :type ssh_info: dict :param interactive: whether to connect local input to the remote process :type interactive: bool :param cmd: command-line to run on the server :type cmd: str :param request_pty: whether to request a PTY from the SSH server :type request_pty: bool :param forwarded_ports: ports to forward back to us; iterable of pairs ``(port_number, connector)`` where `port_number` is the remote port number and `connector` is the connector object used to build the connected socket to forward to on this side :type forwarded_ports: collections.Iterable[(int, object)] """ if interactive: ssh_exe = find_ssh_executable() else: ssh_exe = None if interactive and ssh_exe: record_usage(vagrant_ssh='ssh') args = [ssh_exe, '-t' if request_pty else '-T', # Force allocation of PTY '-o', 'StrictHostKeyChecking=no', # Silently accept host keys '-o', 'UserKnownHostsFile=/dev/null', # Don't store host keys '-i', ssh_info['key_filename'], '-p', '%d' % ssh_info['port']] for remote_port, connector in forwarded_ports: # Remote port will connect to a local port fwd = LocalForwarder(connector) args.append('-R%d:127.0.0.1:%d' % (remote_port, fwd.local_port)) args.append('%s@%s' % (ssh_info['username'], ssh_info['hostname'])) args.append(cmd) return interruptible_call(args) else: record_usage(vagrant_ssh='interactive' if interactive else 'simple') # Connects to the machine ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(IgnoreMissingKey()) ssh.connect(**ssh_info) # Starts forwarding forwarders = [] for remote_port, connector in forwarded_ports: forwarders.append( SSHForwarder(ssh.get_transport(), remote_port, connector)) chan = ssh.get_transport().open_session() if request_pty: chan.get_pty() # Execute command logging.info("Connected via SSH, running command...") chan.exec_command(cmd) # Get output if interactive: interactive_shell(chan) else: chan.shutdown_write() while True: data = chan.recv(1024) if len(data) == 0: break stdout_bytes.write(data) stdout_bytes.flush() retcode = chan.recv_exit_status() ssh.close() return retcode