0

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

我已尝试按照此处的建议手动加载我的私钥:

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。

4

1 回答 1

1

文档-

parallel-ssh 使用 gevent 的猴子补丁来启用 Python 标准库的网络 I/O 的异步使用。

确保 ParallelSSH 导入位于代码中的任何其他导入之前。否则,在加载标准库之前可能无法进行修补,这将导致 ParallelSSH 阻塞。

如果您看到类似“此操作将永远阻塞”的消息,这就是原因。

Monkey 修补仅针对 pssh.pssh_client 和 pssh.ssh_client 下的客户端分别用于并行和单主机客户端。

pssh.pssh2_client 和 pssh.ssh2_client 下基于本机库的新客户端不执行猴子补丁,如果猴子补丁不适合,则可以选择。这些客户端将在未来的主要版本 2.0.0 中成为默认客户端。

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

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

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

from pssh.pssh2_client import ParallelSSHClient

<..>

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

于 2017-11-23T13:09:46.663 回答