/usr/bin/parallel-ssh is in pssh 2.3.1-1.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | #!/usr/bin/python
# -*- Mode: python -*-
# Copyright (c) 2009-2012, Andrew McNabb
# Copyright (c) 2003-2008, Brent N. Chun
"""Parallel ssh to the set of nodes in hosts.txt.
For each node, this essentially does an "ssh host -l user prog [arg0] [arg1]
...". The -o option can be used to store stdout from each remote node in a
directory. Each output file in that directory will be named by the
corresponding remote node's hostname or IP address.
"""
import fcntl
import os
import sys
parent, bindir = os.path.split(os.path.dirname(os.path.abspath(sys.argv[0])))
if os.path.exists(os.path.join(parent, 'psshlib')):
sys.path.insert(0, parent)
from psshlib import psshutil
from psshlib.manager import Manager, FatalError
from psshlib.task import Task
from psshlib.cli import common_parser, common_defaults
_DEFAULT_TIMEOUT = 60
def option_parser():
parser = common_parser()
parser.usage = "%prog [OPTIONS] command [...]"
parser.epilog = "Example: pssh -h hosts.txt -l irb2 -o /tmp/foo uptime"
parser.add_option('-i', '--inline', dest='inline', action='store_true',
help='inline aggregated output and error for each server')
parser.add_option('--inline-stdout', dest='inline_stdout',
action='store_true',
help='inline standard output for each server')
parser.add_option('-I', '--send-input', dest='send_input',
action='store_true',
help='read from standard input and send as input to ssh')
parser.add_option('-P', '--print', dest='print_out', action='store_true',
help='print output as we get it')
return parser
def parse_args():
parser = option_parser()
defaults = common_defaults(timeout=_DEFAULT_TIMEOUT)
parser.set_defaults(**defaults)
opts, args = parser.parse_args()
if len(args) == 0 and not opts.send_input:
parser.error('Command not specified.')
if not opts.host_files and not opts.host_strings:
parser.error('Hosts not specified.')
return opts, args
def do_pssh(hosts, cmdline, opts):
if opts.outdir and not os.path.exists(opts.outdir):
os.makedirs(opts.outdir)
if opts.errdir and not os.path.exists(opts.errdir):
os.makedirs(opts.errdir)
if opts.send_input:
stdin = sys.stdin.read()
else:
stdin = None
manager = Manager(opts)
for host, port, user in hosts:
cmd = ['ssh', host, '-o', 'NumberOfPasswordPrompts=1',
'-o', 'SendEnv=PSSH_NODENUM PSSH_HOST']
if opts.options:
for opt in opts.options:
cmd += ['-o', opt]
if user:
cmd += ['-l', user]
if port:
cmd += ['-p', port]
if opts.extra:
cmd.extend(opts.extra)
if cmdline:
cmd.append(cmdline)
t = Task(host, port, user, cmd, opts, stdin)
manager.add_task(t)
try:
statuses = manager.run()
except FatalError:
sys.exit(1)
if min(statuses) < 0:
# At least one process was killed.
sys.exit(3)
# The any builtin was introduced in Python 2.5 (so we can't use it yet):
#elif any(x==255 for x in statuses):
for status in statuses:
if status == 255:
sys.exit(4)
for status in statuses:
if status != 0:
sys.exit(5)
if __name__ == "__main__":
opts, args = parse_args()
cmdline = " ".join(args)
try:
hosts = psshutil.read_host_files(opts.host_files,
default_user=opts.user)
except IOError:
_, e, _ = sys.exc_info()
sys.stderr.write('Could not open hosts file: %s\n' % e.strerror)
sys.exit(1)
if opts.host_strings:
for s in opts.host_strings:
hosts.extend(psshutil.parse_host_string(s, default_user=opts.user))
do_pssh(hosts, cmdline, opts)
|