假设我有许多可通过SSH访问的主机,以及(可以多得多)可以提交给任何这些主机的命令。以下假设是正确的:
考虑到这一点,我实现了一些基于concurrent.futures
的代码来完成此调度。下面提供了一个独立的版本,仅假定您可以SSH到localhost
。
import concurrent.futures
import shlex
import subprocess
import random
import logging
from collections import deque
logging.basicConfig(format=logging.BASIC_FORMAT, level=logging.DEBUG)
random.seed(13)
MAX_DURATION = 10
NUM_COMMANDS = 100
NUM_HOSTS = 5
HOSTS = ['localhost'] * NUM_HOSTS
def simulate_remote_call(host: str) -> None:
duration = random.randrange(1, MAX_DURATION + 1)
cmd = ['sleep', str(duration)]
quoted_cmd = [shlex.quote(token) for token in cmd]
logging.debug("Sleeping for %d seconds on %s", duration, host)
subprocess.check_call(['ssh', host, *quoted_cmd])
logging.debug("Done sleeping on %s", host)
with concurrent.futures.ThreadPoolExecutor(max_workers=len(HOSTS)) as executor:
it = iter(range(1, NUM_COMMANDS + 1))
queue = deque(HOSTS, len(HOSTS))
future_to_host = {}
while True:
return_when = concurrent.futures.FIRST_EXCEPTION
while queue:
try:
command = next(it)
except StopIteration:
break
else:
host = queue.popleft()
logging.debug("Submitting command %d", command)
future = executor.submit(simulate_remote_call, host)
future_to_host[future] = host
else:
return_when = concurrent.futures.FIRST_COMPLETED
if not future_to_host:
break
done, not_done = concurrent.futures.wait(future_to_host,
return_when=return_when)
for future in done:
host = future_to_host[future]
exception = future.exception()
if exception is not None:
for other in not_done:
other.cancel()
raise exception
del future_to_host[future]
queue.append(host)
但是,这段代码看起来非常不自然,我正在寻找一种更简单的方法来完成此操作。有什么想法吗?