4 # Usage: pssh [OPTIONS] -h hosts.txt prog [arg0] [arg1] ..
6 # Parallel ssh to the set of nodes in hosts.txt. For each node, this
7 # essentially does an "ssh host -l user prog [arg0] [arg1] ...". The -o
8 # option can be used to store stdout from each remote node in a
9 # directory. Each output file in that directory will be named by the
10 # corresponding remote node's hostname or IP address.
12 # Created: 16 August 2003
15 # patched by thierry from 1.4.3 for adding -k and multiple -O
26 basedir, bin = os.path.split(os.path.dirname(os.path.abspath(sys.argv[0])))
27 sys.path.append("%s" % basedir)
29 from psshlib import psshutil
30 from psshlib.basethread import BaseThread
32 _DEFAULT_PARALLELISM = 32
36 print "Usage: pssh [OPTIONS] -h hosts.txt prog [arg0] .."
38 print " -h --hosts hosts file (each line \"host[:port] [user]\")"
39 print " -l --user username (OPTIONAL)"
40 print " -p --par max number of parallel threads (OPTIONAL)"
41 print " -o --outdir output directory for stdout files (OPTIONAL)"
42 print " -e --errdir output directory for stderr files (OPTIONAL)"
43 print " -t --timeout timeout (secs) (-1 = no timeout) per host (OPTIONAL)"
44 print " -O --options SSH options (OPTIONAL)"
45 print " -v --verbose turn on warning and diagnostic messages (OPTIONAL)"
46 print " -P --print print output as we get it (OPTIONAL)"
47 print " -i --inline inline aggregated output for each server (OPTIONAL)"
48 print " -k --key alternate key to pass to ssh -i (OPTIONAL)"
50 print "Example: pssh -h nodes.txt -l irb2 -o /tmp/foo uptime"
53 def read_envvars(flags):
54 if os.getenv("PSSH_HOSTS"):
55 flags["hosts"] = os.getenv("PSSH_HOSTS")
56 if os.getenv("PSSH_USER"):
57 flags["user"] = os.getenv("PSSH_USER")
58 if os.getenv("PSSH_PAR"):
59 flags["par"] = int(os.getenv("PSSH_PAR"))
60 if os.getenv("PSSH_OUTDIR"):
61 flags["outdir"] = os.getenv("PSSH_OUTDIR")
62 if os.getenv("PSSH_ERRDIR"):
63 flags["errdir"] = os.getenv("PSSH_ERRDIR")
64 if os.getenv("PSSH_TIMEOUT"):
65 timeout = int(os.getenv("PSSH_TIMEOUT"))
67 flags["timeout"] = timeout
69 flags["timeout"] = None
70 if os.getenv("PSSH_OPTIONS"):
71 flags["options"] = os.getenv("PSSH_OPTIONS").split(" ")
72 if os.getenv("PSSH_VERBOSE"): # "0" or "1"
73 flags["verbose"] = int(os.getenv("PSSH_VERBOSE"))
74 if os.getenv("PSSH_PRINT"):
75 flags["print"] = int(os.getenv("PSSH_PRINT"))
76 if os.getenv("PSSH_INLINE"):
77 flags["inline"] = int(os.getenv("PSSH_INLINE"))
78 if os.getenv("PSSH_KEY"):
79 flags["inline"] = int(os.getenv("PSSH_KEY"))
81 def parsecmdline(argv):
82 shortopts = "h:l:p:o:e:t:O:Pvik:"
83 longopts = [ "hosts=",
95 flags = { "hosts" : None,
97 "par" : _DEFAULT_PARALLELISM,
100 "timeout" : _DEFAULT_TIMEOUT,
108 if not flags["user"]:
109 flags["user"] = pwd.getpwuid(os.getuid())[0] # Default to current user
111 opts, args = getopt.getopt(argv[1:], shortopts, longopts)
112 except getopt.GetoptError, e:
113 print "Error: %s\n" % str(e)
117 if o in ("-h", "--hosts"):
119 elif o in ("-l", "--user"):
121 elif o in ("-p", "--par"):
122 flags["par"] = int(v)
123 elif o in ("-o", "--outdir"):
125 elif o in ("-e", "--errdir"):
127 elif o in ("-t", "--timeout"):
130 flags["timeout"] = timeout
132 flags["timeout"] = None
133 elif o in ("-O", "--options"):
134 flags["options"].append(v)
135 elif o in ("-v", "--verbose"):
137 elif o in ("-P", "--print"):
139 elif o in ("-i", "--inline"):
141 elif o in ["-k","--key"]:
144 if not flags["hosts"]:
150 origfl = fcntl.fcntl(sys.stdin.fileno(), fcntl.F_GETFL)
151 fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, origfl | os.O_NONBLOCK)
153 stdin = sys.stdin.read()
154 except IOError: # Stdin contained no information
156 fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, origfl)
159 def do_pssh(hosts, ports, users, cmdline, flags):
160 if flags["outdir"] and not os.path.exists(flags["outdir"]):
161 os.makedirs(flags["outdir"])
162 if flags["errdir"] and not os.path.exists(flags["errdir"]):
163 os.makedirs(flags["errdir"])
164 stdin = buffer_input()
165 sem = threading.Semaphore(flags["par"])
167 for i in range(len(hosts)):
170 for option in flags["options"]:
171 cmd += " -o %s"%option
172 if not flags["verbose"]:
175 cmd += " -i %s"%flags["key"]
176 cmd += " -p %s" % ports[i]
177 cmd += " -l %s" % users[i]
178 cmd += " %s" % hosts[i]
179 cmd += " \"%s\"" % cmdline
181 print 'Triggering %s'%cmd
182 t = BaseThread(hosts[i], ports[i], cmd, flags, sem, stdin)
188 if __name__ == "__main__":
189 subprocess._cleanup = lambda : None
190 args, flags = parsecmdline(sys.argv)
194 cmdline = " ".join(args)
195 hosts, ports, users = psshutil.read_hosts(flags["hosts"])
196 psshutil.patch_users(hosts, ports, users, flags["user"])
197 signal.signal(signal.SIGCHLD, signal.SIG_DFL)
199 do_pssh(hosts, ports, users, cmdline, flags)