【问题标题】:Executing reboot command over SSH using Paramiko使用 Paramiko 通过 SSH 执行重启命令
【发布时间】:2014-11-24 21:32:19
【问题描述】:

我使用Paramiko 与某个目标设备建立SSH 连接,我想执行reboot 命令。

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(zip_hostname, username=username, password=password, timeout=1)
try:
    stdin, stdout, stderr = ssh.exec_command("/sbin/reboot -f")
    # .........
    # some code
    # .........
except AuthenticationException, e:
    print ''
finally:
    ssh.close()

但是在执行ssh.exec_command("/sbin/reboot -f")之后,“some code”没有执行,因为程序卡在exec_command中(重启导致断线)。我应该怎么做才能解决我的问题?

【问题讨论】:

    标签: python linux ssh paramiko


    【解决方案1】:

    试试这个:

    ssh.exec_command("/sbin/reboot -f > /dev/null 2>&1 &")
    

    reboot 的所有输出都被重定向到 /dev/null 以使其不产生任何输出,并且由于末尾的“&”符号而在后台启动。希望程序不会以这种方式挂在该行,因为远程 shell 会返回提示符。

    【讨论】:

      【解决方案2】:

      从 ssh 获取传输并设置 keepalive 使用:

      transport = ssh.get_transport()
      transport.set_keepalive(5)
      

      sets the keepalive5 秒;请注意,我希望 timeout=1 也能实现同样的目标。

      【讨论】:

      • 只要连续产生输出,keepalive和timeout都不会触发,因为它们是在测量数据线的连续空闲时间。
      • 当时的情况是发出了重启命令,这会突然终止另一端的 ssh 连接。 keepalive 的目的是快速检测到这种情况。
      • 不完全是。令人惊讶的是,reboot 最终会优雅地终止 ssh 通道,因为系统会向 sshd 发送一个 TERM 信号,使其行为如此。据我了解,这是 OP 不想等待发生的事情。
      • 关键是最终。使用reboot-f 跳过标准的关闭过程,在那种情况下没有信号发送到sshd;您依赖 TCP 超时。不过,我没有看到与 OP 相同的问题,所以我认为这行评论没有实际意义。
      【解决方案3】:

      你需要做的就是调用channel.exec_command()而不是高级接口client.exec_command()

      # exec fire and forget
      timeout=0.5
      transport = ssh.get_transport()
      chan = ssh.get_transport().open_session(timeout=timeout)
      chan.settimeout(timeout)
      try:
        chan.exec_command(command)
      except socket.timeout:
        pass
      

      【讨论】:

      • 这对我帮助最大。其他解决方案似乎太老套了。在主机重新启动后,我一直在“生活”卡住的程序很长很长一段时间。这终于解决了。非常感谢!
      【解决方案4】:

      我遇到了这个问题,并通过切换到这个命令来避免它:

      /sbin/shutdown -r now
      

      注意此命令不会产生任何STDOUTSTDERR 输出

      【讨论】:

        【解决方案5】:

        如果您或其他人在尝试使用转发代理(ssh 密钥)或在我的情况下(yubikey)使用 sudo 重新启动主机时遇到困难

        如果您将其视为 bash,您会像这样以非 root 用户身份重新启动主机。

        ssh -t -A user@hostname sudo /sbin/reboot
        

        对于 -A 标志,来自 ssh 手册页

        启用身份验证代理连接的转发。这也可以在每个主机的基础上指定 配置文件。 应谨慎启用代理转发。能够绕过文件权限的用户 远程主机(用于代理的 Unix 域套接字)可以通过转发连接访问本地代理。 攻击者无法从代理获取密钥材料,但是他们可以对密钥执行操作 使他们能够使用加载到代理中的身份进行身份验证。*

        对于 -t 标志,来自 ssh 手册页

        强制伪 tty 分配。这可用于在远程机器上执行任意基于屏幕的程序, 这可能非常有用,例如实现菜单服务时。多个 -t 选项强制 tty 分配,甚至 如果 ssh 没有本地 tty。*

        让我们将其分解为您将如何在 paramiko 中执行此操作

        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(hostname=host, username=username)
        s = ssh.get_transport().open_session()
        paramiko.agent.AgentRequestHandler(s)
        
        ssh.exec_command("sudo /sbin/reboot", get_pty=True)
        

        用于身份验证转发(bash ssh 命令中的-A 标志) for paramiko

        ssh = paramiko.SSHClient() #'ssh' is client variable 
        s = ssh.get_transport().open_session() #get 'ssh' transport and open sessions assigned to 's' variable
        paramiko.agent.AgentRequestHandler(s) #call in 's' to the forwarding agent for current ssh session
        

        现在强制分配伪 tty (bash ssh 命令中的-t 标志) 用于 paramiko

        ssh.exec_command("sudo /sbin/reboot", get_pty=True)
        

        'get_pty=True' 添加到 exec_command 将允许您执行 sudo /sbin/reboot

        希望这会有所帮助,每个人的环境都不同,但这应该与您以 bash 运行时完全相同。

        【讨论】: