Skip to content

Instantly share code, notes, and snippets.

@zceemja
Created June 13, 2024 11:08
Show Gist options
  • Select an option

  • Save zceemja/1cb84b93dff56de74c3da796ca33f2c7 to your computer and use it in GitHub Desktop.

Select an option

Save zceemja/1cb84b93dff56de74c3da796ca33f2c7 to your computer and use it in GitHub Desktop.
Run command over ssh for multiple servers at once, live updates output for given command, one line for each server
#!/usr/bin/env python3
"""
This scripts executes multiple ssh (hence mssh) commands in multiple servers.
Result for each server is printed as a single line.
Steaming is also supported.
Author: [email protected]
"""
import sys
import select
from subprocess import PIPE, STDOUT, Popen
def multissh(cmd, servers):
poll = select.poll()
procs = {}
srv_spacing = max(map(lambda x: len(x), servers))
for i, server in enumerate(servers):
sp = server.split(':')
host = sp[0]
port = '22'
if len(sp) == 2:
port = sp[1]
proc = Popen(["ssh", "-p", port, "-o", "StrictHostKeyChecking=no", host, cmd], stdout=PIPE, stderr=STDOUT)
poll.register(proc.stdout, select.POLLIN)
procs[proc.stdout.name] = i, proc
print(f'\r\033[1;31m{{:>{srv_spacing}}}\033[0m: ..'.format(server))
print('\033[s', end='') # Save cursor in last line
while len(procs) > 0:
try:
events = poll.poll(0.1)
for fd, event in events:
if event & select.POLLHUP:
poll.unregister(fd)
i, proc = procs[fd]
server = servers[i]
line = proc.stdout.readline()
if event & select.POLLHUP:
print(f"\033[K{server} terminated", end='')
proc.terminate()
del procs[fd]
if len(line) == 0:
continue
line = line.decode().strip()
lno = len(servers) - i
print(
f'\033[u\033[{lno}A\r\033[K' + # go to line, clean up
f'\r\033[1;31m{{:>{srv_spacing}}}\033[0m: {{}}'.format(server, line) +
# f'\033[1;31m{server}:\033[0m\t{line}' # print line
f'\033[{lno}B', # try to go back
end='')
print(f"\033[u\r\033[K", end='')
except KeyboardInterrupt:
print("\033[u\r\033[KInterrupt!")
break
for _, proc in procs.values():
proc.terminate()
if __name__ == '__main__':
if len(sys.argv) < 3:
print(f"Usage: {sys.argv[0]} 'remote cmd' [server..]")
exit(1)
_cmd = sys.argv[1]
_servers = sys.argv[2:]
#print(f"cmd: {_cmd}")
#print(f"srv: {_servers}")
multissh(_cmd, _servers)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment