如何正确使用Python Paramiko通道从中读取数据

问题描述 投票:0回答:1

这与我之前的问题有关: 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 命令时,代码无法工作(挂起)。

如何改进?

python paramiko
1个回答
0
投票

根据评论中链接中提供的示例,此类代码最终对我有用:

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()
© www.soinside.com 2019 - 2024. All rights reserved.