参考:
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()