这与我之前的问题有关: Python SSH Paramiko set_keepalive 在 Linux 上不起作用
我正在尝试编写一段代码,使用Paramiko的通道在CISCO网络设备上执行命令,并在标准输出中遇到特定字符串(指示交换机开始重新启动)时关闭/终止SSH会话。 该代码的主要用途是在网络交换机上启动固件升级。最后,交换机会重新启动,而不会先关闭 SSH 会话,因此通过 SSHClient.exec_command() 执行的命令会挂起/被阻止,直到底层 TCP 会话超时(可能需要很多分钟)。在传输上设置 keepalive 并没有多大帮助,因为 Paramiko 中没有类似于 OpenSSH 的 -o ServerAliveCountMax 的功能。 不过,我在开发此类代码时遇到了问题。
我写的代码(简化)是:
import paramiko
from time import sleep
HOST = "host"
USERNAME = "user"
PASSWORD = "password"
COMMAND = "NX-OS command"
READSIZE = 1024
ABORT_STRING = "switch rebooting"
client = paramiko.SSHClient()
client.connect(HOST, username=USERNAME, password=PASSWORD)
transport = client.get_transport()
channel = transport.open_session()
channel.exec_command(COMMAND)
stdout_output = ""
stderr_output = ""
while True:
if channel.recv_ready():
stdout_output += channel.recv(READSIZE).decode()
if ABORT_STRING in stdout_output:
channel.close()
break
if channel.recv_stderr_ready():
stderr_output +=channel.recv_stderr(READSIZE).decode()
#sleep(1)
if channel.exit_status_ready():
break
client.close()
我开始在“常规”“show”交换机命令上测试它,该命令只需一秒钟即可执行并退出,从不显示指示交换机开始重新启动的消息。 这段代码只适用于 10 种情况中的 8 种 - 然后它包含 stdout_output 中 stdout 输出的全部内容。
在十分之一的情况下,stdout 仅包含输出的一部分,而在其余情况下,循环结束时 stderr_output 为空。
如果我添加一秒延迟(其中注释了 sleep),那么在 10 个案例中有 10 个 stdout_output 在循环退出后为空。
经过测试/调试,似乎channel.exit_status_ready()返回的内容完全独立于执行命令返回的数据是否完全(或根本)读取。 可能会发生这样的情况:recv_ready() 返回 False(channel.exec_command() 还没有数据到达),几行后,channel.exit_status_ready() 已经返回 True,因此循环退出,但没有从通道读取(或部分数据)。
我尝试以这种方式改进这个循环:
while True:
some_stdout_read = False
if channel.recv_ready():
stdout_output += channel.recv(READSIZE).decode()
some_stdout_read = True
if ABORT_STRING in stdout_output:
channel.close()
break
if channel.recv_stderr_ready():
stderr_output +=channel.recv_stderr(READSIZE).decode()
if some_stdout_read:
continue
#sleep(1)
if channel.exit_status_ready():
break
client.close()
这消除了在循环退出之前仅读取部分 stdout 的情况(一旦某些数据到达,它就会返回到循环的开头)。 但仍然存在根本没有读取数据并且循环在channel.exit_status_ready()处退出的情况。
我正在考虑如果根本没有从标准输出读取数据,则不允许循环到达channel.exit_status_ready()。但是,使用不返回数据(仅返回提示符)的 switch 命令时,代码无法工作(挂起)。
如何改进?
根据评论中链接中提供的示例,此类代码最终对我有用:
import paramiko
HOST = "host"
USERNAME = "user"
PASSWORD = "password"
COMMAND = "NX-OS firmware upgrade command"
READSIZE = 1024
SSH_ABORT_STRING = "switch rebooting"
client = paramiko.SSHClient()
client.connect(HOST, username=USERNAME, password=PASSWORD)
transport = client.get_transport()
transport.set_keepalive(10)
channel = transport.open_session()
channel.exec_command(COMMAND)
stdout_output = ""
stderr_output = ""
abort_detected = False
while True:
channel_exited = channel.exit_status_ready()
if channel.recv_ready():
data_received = channel.recv(READSIZE).decode()
while data_received:
stdout_output += data_received
if SSH_ABORT_STRING in stdout_output:
abort_detected = True
break
data_received = channel.recv(READSIZE).decode()
if channel.recv_stderr_ready():
data_received = channel.recv_stderr(READSIZE).decode()
while data_received:
stderr_output += data_received
data_received = channel.recv_stderr(READSIZE).decode()
if abort_detected:
channel.close()
break
if channel_exited:
break
client.close()