python+paramiko —— run cmd through middle host

来源:互联网 发布:宝宝照片创意软件 编辑:程序博客网 时间:2024/06/16 20:36

参考:

1. https://stackoverflow.com/questions/42208655/paramiko-nest-ssh-session-to-another-machine-while-preserving-paramiko-function

2. https://github.com/paramiko/paramiko/issues/109

目的:代码在本地,需要一个mid_host 做中转,才能连接到end_host,并最终让code运行在end_host上。

参考1写的非常好,但是如果所执行的命令不能在一瞬间完成,会导致命令只运行了一点,比如我做得是解压文件,文件解压了一半就终止了,因此参考2,对代码进行改进

我的code:

其中有两行在测试的时候不加是没有问题的,可是我的程序必须要加,不然会导致hang

可以找一个比较大的压缩包来测试

对于为什么要加

ParaProxy 这个类,如果用默认的ProxyCommand ,mid_host在连接的时候需要免密,在stackoverflow上也有例子

import paramikoimport socketfrom paramiko.ssh_exception import SSHExceptionclass ParaProxy(paramiko.util.ClosingContextManager):    """    Instead of ProxyCommand.    Solving ProxyCommand cannot connect to machine with password.    """    def __init__(self, stdin, stdout, stderr):        self.stdin = stdin        self.stdout = stdout        self.stderr = stderr        self.timeout = None    def send(self, content):        try:            self.stdin.write(content)        except IOError as exc:            print('IOError exception.')            return        return len(content)    def recv(self, size):        buffer = b''        start = time.time()        while len(buffer) < size:            if self.timeout is not None:                elapsed = (time.time() - start)                if elapsed >= self.timeout:                    raise socket.timeout()            buffer += self.stdout.read(size - len(buffer))        return buffer    def close(self):        self.stdin.close()        self.stdout.close()        self.stderr.close()    def settimeout(self, timeout):        self.timeout = timeoutdef run_cmd_between_remotes(mid_host_ip, mid_host_user, mid_host_password,                            end_host_ip, end_host_user, end_host_password,                            cmd, cmd_mid=None, timeout=10):    """    ssh to another machine via a middle machine.    :param mid_host_ip:     :param mid_host_user:     :param mid_host_password:     :param end_host_ip:     :param end_host_user:     :param end_host_password:     :param cmd: The cmd need to run on end host    :param cmd_mid: The cmd need to run on mid host    :param timeout: Time to delay    :return:     """    # Connect to middle machine    mid_cli = paramiko.SSHClient()    mid_cli.set_missing_host_key_policy(paramiko.AutoAddPolicy())    mid_cli.connect(hostname=mid_host_ip, username=mid_host_user,                    password=mid_host_password)    logging.info('Connect to %s successfully !' % mid_host_ip)    if cmd_mid:        stdin, stdout, stderr = mid_cli.exec_command(cmd_mid)        logging.debug('stdout %s' % (''.join(stdout.readlines())))        error_msg = ''.join(stderr.readlines())        if error_msg != '':            raise SSHException(error_msg)        else:            logging.info('run cmd %s on %s successfully!' % (cmd, mid_host_ip))        stderr.close()        stdout.close()    io_tupple = mid_cli.exec_command('nc %s %s' % (end_host_ip, 22))    proxy = ParaProxy(*io_tupple)    proxy.settimeout(1000)    # Connecting to AnotherMachine and executing... anything...    end_cli = paramiko.SSHClient()    end_cli.set_missing_host_key_policy(paramiko.AutoAddPolicy())    end_cli.connect(hostname=end_host_ip, username=end_host_user,                    password=end_host_password, sock=proxy)    logging.info('Connect to %s successfully !' % end_host_ip)    logging.info('run cmd %s.......' % cmd)    stdin, stdout, stderr = end_cli.exec_command(cmd, timeout=timeout)    # waiting for exit status (that means cmd finished)    exit_status = stdout.channel.recv_exit_status()    # flush commands and cut off more writes
    # if del follow two line, it will cause my code hang, but when i just test ,it runs ok    stdin.flush()    stdin.channel.shutdown_write()    # close connection    end_cli.close()    mid_cli.close()    logging.debug('stdout %s' % (''.join(stdout.readlines())))    error_msg = ''.join(stderr.readlines())    if error_msg != '':        raise SSHException('ERROR STATUS %s ; ERROR MSG %s' % (exit_status,                                                               error_msg))    else:        logging.info('run cmd %s on %s successfully!' % (cmd, end_host_ip))    stderr.close()    stdout.close()


原创粉丝点击