python+paramiko —— run cmd through middle host

参考:

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 paramiko
import socket
from paramiko.ssh_exception import SSHException

class 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 = timeout


def 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()


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值