【问题标题】:Reading command output with Paramiko invoke_shell/send/recv never finishes使用 Paramiko invoke_shell/send/recv 读取命令输出永远不会完成
【发布时间】:2021-12-28 11:08:14
【问题描述】:

我正在尝试在 Paramiko 中使用 send/recv 函数。据我所见,该行抛出了超时异常

评估:self.shell.recv(1024) 未在 3.00 秒后完成。

tmp = shell.recv(1024)

函数实现有什么问题? 我从while True 的退出条件是一个异常,我怎样才能将其更改为无异常退出?

完整代码:

self.shell = self.SSHConnect(ip, username, password)

def SSHConnect(self, ip, username, passowrd):

ssh = paramiko.SSHClient()
LOGGER.debug(msg="Open SSH Client to :" + str(ip))
try:
    ssh.set_missing_host_key_policy(policy=paramiko.AutoAddPolicy())
    ssh.connect(ip, port=22, username=username, password=passowrd, allow_agent=False, look_for_keys=True)
    if self.device_type != 'linux_host':
        session = ssh.invoke_shell()
        return session
except Exception as ex:
    LOGGER.critical(msg="SSH Client wasn't established! Device name : " + str(self.device_name))
    return None 

LOGGER.info(msg="Open SSH Client to :" + str(ip) + " established!")
return ssh

def run_command(self,cmd):
#New run command without throwing exception at the end:
LOGGER.debug('Start new Run command with cmd =  ' + str(cmd))
try:
    #Check the shell is activated before sending command:
    LOGGER.debug('Check the shell is activated before sending command: ' + cmd)
    if self.shell.get_transport().active:
        LOGGER.debug('Shell is Activated ! Running the command ')
        if self.device_type == 'linux_host':
            stdin, stdout, stderr = self.shell.exec_command(cmd)
        else:
            try:
                #Command for switch of UFMAPL
                LOGGER.debug('Sending command to UFMAPL/Switch with send()')
                out = ''
                self.shell.send(cmd)
                while not self.shell.recv_ready():
                    time.sleep(3)
                counter = 1
                print('\ncommand is : ' + cmd + '\n' )
                while True:
                    try:
                        print('iteration number is : #' + str(counter))
                        tmp = self.shell.recv(1024)
                        counter = counter + 1 
                        if not tmp:
                            break
                    except Exception as e:
                        break
                    out += tmp.decode("utf-8")
                    print('After iteration #' + str(counter) + ' out = ' + out + '\n\n')
                ansi_escape = re.compile(r'\x1B\[[0-?]*[ -/]*[@-~]')
                out = ansi_escape.sub('', out)
                print('Printing final value before return : ' + str(out +'\n'))
                return out
            except Exception as e:
                LOGGER.error('Exception in send() : ' +str(e) )
                return None
    else:
        LOGGER.critical('Shell is not activated !')
        return ""

    if stderr.read():
        LOGGER.critical('stderr is not empty which means the last command was failed, the command might not exist on host/switch ' )
        return stderr.read().decode('utf-8')

    out = stdout.read()
    if out:
        return out.decode("utf-8")
    else:
        LOGGER.critical('Run command sucussfully but return empty...')
        return out.decode("utf-8")
except Exception as e:
    LOGGER.error('Exception received in run command : ' + str(e))

日志(打印到屏幕):

IM HEREEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE


command is : enable


iteration number is : #1

After iteration #2 out =
UFM Appliance

UFM is configured as standalone.
UFM mode: Management.
RAID state is: Degraded ( DRIVE1:Online,SpunUp, DRIVE2:Failed )
smg-ib-apl008-gen2 [ mgmt-sa ] >


iteration number is : #2

After iteration #3 out =
UFM Appliance

UFM is configured as standalone.
UFM mode: Management.
RAID state is: Degraded ( DRIVE1:Online,SpunUp, DRIVE2:Failed )
smg-ib-apl008-gen2 [ mgmt-sa ] > e


iteration number is : #3

After iteration #4 out =
UFM Appliance

UFM is configured as standalone.
UFM mode: Management.
RAID state is: Degraded ( DRIVE1:Online,SpunUp, DRIVE2:Failed )
smg-ib-apl008-gen2 [ mgmt-sa ] > enable
smg-ib-a


iteration number is : #4

After iteration #5 out = 
UFM Appliance

UFM is configured as standalone.
UFM mode: Management.
RAID state is: Degraded ( DRIVE1:Online,SpunUp, DRIVE2:Failed )
smg-ib-apl008-gen2 [ mgmt-sa ] > enable
smg-ib-apl00


iteration number is : #5

After iteration #6 out =
UFM Appliance

UFM is configured as standalone.
UFM mode: Management.
RAID state is: Degraded ( DRIVE1:Online,SpunUp, DRIVE2:Failed )
smg-ib-apl008-gen2 [ mgmt-sa ] > enable
smg-ib-apl008-gen2 [ mgmt-sa ] #


iteration number is : #6

如您所见,调试器卡在iteration #6(冻结)。 为什么它会冻结并且不发送输出?

环境详情:
视窗 10
Eclipse 最新

我会很感激这里的任何帮助。如果您需要更多详细信息,请告诉我。

【问题讨论】:

    标签: python shell ssh paramiko


    【解决方案1】:

    您的代码到达了服务器停止等待另一个输入的位置。同时你等待服务器输出一些东西。它永远不会。这称为deadlock

    您可能已经预料到第一个命令执行已完成的某种信号。没有这样的信号。您正在使用“shell”(SSHClient.invoke_shell)。 shell 是一个带有输入和输出的黑盒子。除了输出之外没有其他信号,你已经在阅读了。

    “shell”不应用于自动执行命令。对于命令自动化,有“exec”通道(Paramiko 中的SSHClient.exec_command)。

    虽然我了解使用某些特殊设备,您的服务器似乎是什么,但您可能没有任何其他选择(请参阅Executing command using Paramiko exec_command on device is not working)。此外,我不确定enable 命令是如何工作的。无论是已经完成的命令,还是它启动了一种新的 shell,它仍在运行并等待子命令。

    所以最后你能做的就是解析输出,等待命令提示符(smg-ib-apl008-gen2 [ mgmt-sa ] #)。


    是的,它很丑。你正在尝试自动化一些不适合自动化的东西。也许你的服务器有一个比enable shell 命令更好的API,这样自动化会更好。但这是另一个问题。从 SSH/Python/Paramiko 的角度来看,如果您需要坚持在 shell 中执行 enable 命令,则没有更好的解决方案。


    相关问题:

    虽然它们是关于更常规的服务器,例如 Linux。所以他们真的帮不上忙。我将它们链接起来只是为了提供更广泛的图片和背景。

    【讨论】:

      猜你喜欢
      • 2015-11-07
      • 1970-01-01
      • 2020-12-05
      • 1970-01-01
      • 2013-06-12
      • 1970-01-01
      • 1970-01-01
      • 2017-01-27
      相关资源
      最近更新 更多