一般 SSH 错误 - 读取 SSH 协议横幅时出错

General SSH error - Error reading SSH protocol banner

我正在用 kivy 编写一个 python UI 来管理一些带有 fabric 的远程机器。由于我无法在 Windows 10(参见 here)上使用 fabric 的并行实现,我希望使用 parallel-ssh 来实际执行并行远程操作。这个问题似乎是由库之间的交互引起的,而不是其中任何一个的问题。

我已尝试按照建议手动加载私钥 here:

from fabric.api import execute
import pssh
from pssh.utils import load_private_key

hosts = ['192.168.0.2']
private_key = load_private_key('C:/Users/democracy/.ssh/id_rsa')
pssh_client = pssh.ParallelSSHClient(hosts, user='XXX', password='YYY', pkey=private_key)
output = pssh_client.run_command('whoami', sudo=True)
pssh_client.join(output)
for host in output:
    for line in output[host]['stdout']:
        print("Host %s - output: %s" % (host, line))

以上代码产生以下回溯:

Exception: Error reading SSH protocol banner('This operation would block forever', <Hub at 0x242566ab9c8 select pending=0 ref=0>)
Traceback (most recent call last):
  File "C:\environments\democracy\lib\site-packages\paramiko\transport.py", line 1884, in _check_banner
buf = self.packetizer.readline(timeout)
  File "C:\environments\democracy\lib\site-packages\paramiko\packet.py", line 331, in readline
buf += self._read_timeout(timeout)
  File "C:\environments\democracy\lib\site-packages\paramiko\packet.py", line 485, in _read_timeout
x = self.__socket.recv(128)
  File "C:\environments\democracy\lib\site-packages\gevent\_socket3.py", line 317, in recv
self._wait(self._read_event)
  File "C:\environments\democracy\lib\site-packages\gevent\_socket3.py", line 144, in _wait
self.hub.wait(watcher)
  File "C:\environments\democracy\lib\site-packages\gevent\hub.py", line 630, in wait
result = waiter.get()
  File "C:\environments\democracy\lib\site-packages\gevent\hub.py", line 878, in get
return self.hub.switch()
  File "C:\environments\democracy\lib\site-packages\gevent\hub.py", line 609, in switch
return greenlet.switch(self)
gevent.hub.LoopExit: ('This operation would block forever', <Hub at 0x242566ab9c8 select pending=0 ref=0>)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\environments\democracy\lib\site-packages\paramiko\transport.py", line 1740, in run
self._check_banner()
  File "C:\environments\democracy\lib\site-packages\paramiko\transport.py", line 1888, in _check_banner
raise SSHException('Error reading SSH protocol banner' + str(e))
paramiko.ssh_exception.SSHException: Error reading SSH protocol banner('This operation would block forever', <Hub at 0x242566ab9c8 select pending=0 ref=0>)

General SSH error - Error reading SSH protocol banner('This operation would block forever', <Hub at 0x242566ab9c8 select pending=0 ref=0>)

如果我在 fabric 之前导入 pssh,上面的代码就可以工作。不幸的是,如果我这样做,我的 kivy 界面上的任何按钮(启动后台线程中的任何操作)似乎都会在按下时永远阻塞。如果我在按下按钮后转到控制台并发送键盘中断,kivy 将停止阻塞并开始清理,但会在退出前执行按下按钮的命令。发送此中断的堆栈跟踪如下:

[INFO   ] [Base        ] Leaving application in progress...
 Traceback (most recent call last):
   File "machine_control_ui.py", line 7, in <module>
 DemocracyControllerApp().run()
   File "C:\environments\democracy\lib\site-packages\kivy\app.py", line 828, in run
 runTouchApp()
   File "C:\environments\democracy\lib\site-packages\kivy\base.py", line 504, in runTouchApp
 EventLoop.window.mainloop()
   File "C:\environments\democracy\lib\site-packages\kivy\core\window\window_sdl2.py", line 659, in mainloop
 self._mainloop()
   File "C:\environments\democracy\lib\site-packages\kivy\core\window\window_sdl2.py", line 405, in _mainloop
 EventLoop.idle()
   File "C:\environments\democracy\lib\site-packages\kivy\base.py", line 339, in idle
 Clock.tick()
   File "C:\environments\democracy\lib\site-packages\kivy\clock.py", line 553, in tick
 current = self.idle()
   File "C:\environments\democracy\lib\site-packages\kivy\clock.py", line 533, in idle
 usleep(1000000 * sleeptime)
   File "C:\environments\democracy\lib\site-packages\kivy\clock.py", line 717, in usleep
 _usleep(microseconds, self._sleep_obj)
   File "C:\environments\democracy\lib\site-packages\kivy\clock.py", line 395, in _usleep
 _kernel32.WaitForSingleObject(obj, 0xffffffff)
 KeyboardInterrupt

*** BUTTON PRESS OPERATION OUTPUTS HERE ***

```

如果能深入了解为什么会发生这种情况以及如何避免这种情况,我们将不胜感激。我可能会研究其他并行 ssh 解决方案(尽管我想任何使用 paramiko 的东西都会有同样的问题),或者手动启动每个主机的线程以实现并行操作(这可能有它自己的头痛列表),但我'如果有可行的解决方案,我更愿意只使用 parallel-ssh 库。

我在 Python 3 和 Windows 10 上使用 parallel-ssh 0.92.2。

docs -

parallel-ssh uses gevent’s monkey patching to enable asynchronous use of the Python standard library’s network I/O.

Make sure that ParallelSSH imports come before any other imports in your code. Otherwise, patching may not be done before the standard library is loaded which will then cause ParallelSSH to block.

If you are seeing messages like This operation would block forever, this is the cause.

Monkey patching is only done for the clients under pssh.pssh_client and pssh.ssh_client for parallel and single host clients respectively.

New native library based clients under pssh.pssh2_client and pssh.ssh2_client do not perform monkey patching and are an option if monkey patching is not suitable. These clients will become the default in a future major release - 2.0.0.

由于猴子补丁用于您正在使用的客户端,因此您应用程序中 threadingsocket 等模块的其他用途也将被修补以使用 gevent,这意味着它们不再运行 在本机线程中,但在 co-routine/greenlet 中。

这就是您的后台线程操作阻塞的原因,因为它们 运行 在同一个线程的 greenlet 中而不是在新线程中。

1.2.0 开始,一个基于 libssh2 而不是 paramiko 的新客户端可用,它不使用猴子补丁:

from pssh.pssh2_client import ParallelSSHClient

<..>

然后您的应用程序的其余部分可以按原样使用标准库。