2
# Copyright (c) 2011, Dejan Muhamedagic
3
# Copyright (c) 2009, Andrew McNabb
4
# Copyright (c) 2003-2008, Brent N. Chun
6
"""Parallel ssh to the set of nodes in hosts.txt.
8
For each node, this essentially does an "ssh host -l user prog [arg0] [arg1]
9
...". The -o option can be used to store stdout from each remote node in a
10
directory. Each output file in that directory will be named by the
11
corresponding remote node's hostname or IP address.
20
parent, bindir = os.path.split(os.path.dirname(os.path.abspath(sys.argv[0])))
21
if os.path.exists(os.path.join(parent, 'psshlib')):
22
sys.path.insert(0, parent)
24
from psshlib import psshutil
25
from psshlib.manager import Manager, FatalError
26
from psshlib.task import Task
27
from psshlib.cli import common_parser, common_defaults
35
parser = common_parser()
36
parser.usage = "%prog [OPTIONS] command [...]"
37
parser.epilog = "Example: pssh -h hosts.txt -l irb2 -o /tmp/foo uptime"
39
parser.add_option('-i', '--inline', dest='inline', action='store_true',
40
help='inline aggregated output for each server')
41
parser.add_option('-I', '--send-input', dest='send_input',
43
help='read from standard input and send as input to ssh')
44
parser.add_option('-P', '--print', dest='print_out', action='store_true',
45
help='print output as we get it')
49
def parse_args(myargs, t = _DEFAULT_TIMEOUT):
50
parser = option_parser()
51
defaults = common_defaults(timeout=t)
52
parser.set_defaults(**defaults)
53
opts, args = parser.parse_args(myargs)
56
def get_output(dir, host):
58
fl = glob.glob("%s/*%s*" % (dir,host))
61
if os.stat(fname).st_size == 0:
70
def show_output(dir, hosts, desc):
72
out_l = get_output(dir, host)
74
print "%s %s:" % (host, desc)
78
if opts.outdir and not os.path.exists(opts.outdir):
79
os.makedirs(opts.outdir)
80
if opts.errdir and not os.path.exists(opts.errdir):
81
os.makedirs(opts.errdir)
83
stdin = sys.stdin.read()
86
manager = Manager(opts)
90
for host, cmdline in l:
91
cmd = ['ssh', host, '-o', 'PasswordAuthentication=no',
92
'-o', 'SendEnv=PSSH_NODENUM',
93
'-o', 'StrictHostKeyChecking=no']
95
for opt in opts.options:
102
cmd.extend(opts.extra)
106
t = Task(host, port, user, cmd, opts, stdin)
109
return manager.run() # returns a list of exit codes
111
common_err("pssh to nodes failed")
112
show_output(opts.errdir, hosts, "stderr")
115
def examine_outcome(l, opts, statuses):
117
A custom function to show stderr in case there were issues.
118
Not suited for callers who want better control of output or
121
hosts = [x[0] for x in l]
122
if min(statuses) < 0:
123
# At least one process was killed.
124
common_err("ssh process was killed")
125
show_output(opts.errdir, hosts, "stderr")
127
# The any builtin was introduced in Python 2.5 (so we can't use it yet):
128
#elif any(x==255 for x in statuses):
129
for status in statuses:
131
common_warn("ssh processes failed")
132
show_output(opts.errdir, hosts, "stderr")
134
for status in statuses:
135
if status not in (0, _EC_LOGROT):
136
common_warn("some ssh processes failed")
137
show_output(opts.errdir, hosts, "stderr")
141
def next_loglines(a, outdir, errdir):
143
pssh to nodes to collect new logs.
146
for node,rptlog,logfile,nextpos in a:
147
common_debug("updating %s from %s (pos %d)" % (logfile, node, nextpos))
148
cmdline = "perl -e 'exit(%d) if (stat(\"%s\"))[7]<%d' && tail -c +%d %s" % (_EC_LOGROT, logfile, nextpos-1, nextpos, logfile)
149
myopts = ["-q", "-o", outdir, "-e", errdir]
150
opts, args = parse_args(myopts)
151
l.append([node, cmdline])
152
statuses = do_pssh(l, opts)
154
return examine_outcome(l, opts, statuses)
158
def next_peinputs(node_pe_l, outdir, errdir):
160
pssh to nodes to collect new logs.
163
for node,pe_l in node_pe_l:
164
r = re.search("(.*)/pengine/", pe_l[0])
166
common_err("strange, %s doesn't contain string pengine" % pe_l[0])
168
dir = "/%s" % r.group(1)
169
red_pe_l = [x.replace("%s/" % r.group(1),"") for x in pe_l]
170
common_debug("getting new PE inputs %s from %s" % (red_pe_l, node))
171
cmdline = "tar -C %s -cf - %s" % (dir, ' '.join(red_pe_l))
172
myopts = ["-q", "-o", outdir, "-e", errdir]
173
opts, args = parse_args(myopts)
174
l.append([node, cmdline])
178
statuses = do_pssh(l, opts)
180
return examine_outcome(l, opts, statuses)
184
def do_pssh_cmd(cmd, node_l, outdir, errdir, timeout = 20000):
186
pssh to nodes and run cmd.
190
l.append([node, cmd])
193
myopts = ["-q", "-o", outdir, "-e", errdir]
194
opts, args = parse_args(myopts, t = str(int(timeout/1000)))
195
return do_pssh(l, opts)