@@ -177,21 +177,11 @@ def __init__(self, hosts,
177
177
>>> import paramiko
178
178
>>> client_key = paramiko.RSAKey.from_private_key_file('user.key')
179
179
>>> client = ParallelSSHClient(['myhost1', 'myhost2'], pkey=client_key)
180
-
181
- **Example with expression as host list**
182
180
183
- Any type of iterator may be used as host list, including generator and
184
- list comprehension expressions.
181
+ **Multiple commands**
185
182
186
- >>> hosts = ['dc1.myhost1', 'dc2.myhost2']
187
- >>> client = ParallelSSHClient([h for h in hosts if h.find('dc1')])
188
- >>> client.run_command(<..>)
189
-
190
- **Overriding host list**
191
-
192
- >>> client.hosts = ['otherhost']
193
- >>> print client.run_command('exit 0')
194
- >>> {'otherhost': {'exit_code':0}, <..>}
183
+ >>> for cmd in ['uname', 'whoami']:
184
+ ... client.run_command(cmd)
195
185
196
186
.. note ::
197
187
@@ -225,7 +215,7 @@ def __init__(self, hosts,
225
215
self .timeout = timeout
226
216
self .proxy_host , self .proxy_port = proxy_host , proxy_port
227
217
# To hold host clients
228
- self .host_clients = dict (( host , None ) for host in hosts )
218
+ self .host_clients = {}
229
219
self .agent = agent
230
220
231
221
def run_command (self , * args , ** kwargs ):
@@ -259,9 +249,9 @@ def run_command(self, *args, **kwargs):
259
249
:raises: :mod:`pssh.exceptions.UnknownHostException` on DNS resolution error
260
250
:raises: :mod:`pssh.exceptions.ConnectionErrorException` on error connecting
261
251
:raises: :mod:`pssh.exceptions.SSHException` on other undefined SSH errors
262
-
252
+
263
253
**Example Usage**
264
-
254
+
265
255
**Simple run command**
266
256
267
257
>>> output = client.run_command('ls -ltrh')
@@ -291,41 +281,79 @@ def run_command(self, *args, **kwargs):
291
281
292
282
Capture stdout - **WARNING** - this will store the entirety of stdout
293
283
into memory and may exhaust available memory if command output is
294
- large enough:
284
+ large enough.
285
+
286
+ Iterating over stdout/stderr by definition implies blocking until
287
+ command has finished. To only see output as it comes in without blocking
288
+ the host logger can be enabled - see `Enabling Host Logger` above.
295
289
296
290
>>> for host in output:
297
291
>>> stdout = list(output[host]['stdout'])
298
292
>>> print "Complete stdout for host %s is %s" % (host, stdout,)
299
-
293
+
294
+ **Expression as host list**
295
+
296
+ Any type of iterator may be used as host list, including generator and
297
+ list comprehension expressions.
298
+
299
+ >>> hosts = ['dc1.myhost1', 'dc2.myhost2']
300
+ # List comprehension
301
+ >>> client = ParallelSSHClient([h for h in hosts if h.find('dc1')])
302
+ # Generator
303
+ >>> client = ParallelSSHClient((h for h in hosts if h.find('dc1')))
304
+ # Filter
305
+ >>> client = ParallelSSHClient(filter(lambda h: h.find('dc1'), hosts))
306
+ >>> client.run_command(<..>)
307
+
308
+ .. note ::
309
+
310
+ Since iterators by design only iterate over a sequence once then stop,
311
+ `client.hosts` should be re-assigned after each call to `run_command`
312
+ when using iterators as target of `client.hosts`.
313
+
314
+ **Overriding host list**
315
+
316
+ Host list can be modified in place. Call to `run_command` will create
317
+ new connections as necessary and output will only contain output for
318
+ hosts command ran on.
319
+
320
+ >>> client.hosts = ['otherhost']
321
+ >>> print client.run_command('exit 0')
322
+ >>> {'otherhost': {'exit_code':0}, <..>}
323
+
300
324
**Run multiple commands in parallel**
301
325
302
- This short example demonstrates running long running commands in parallel
303
- and how long it takes for all commands to start, blocking until they
304
- complete and how long it takes for all commands to complete.
305
-
306
- See examples directory for complete example script. ::
326
+ This short example demonstrates running long running commands in
327
+ parallel, how long it takes for all commands to start, blocking until
328
+ they complete and how long it takes for all commands to complete.
307
329
308
- output = []
330
+ See examples directory for complete script. ::
309
331
310
- start = datetime.datetime.now()
311
- cmds = ['sleep 5' for _ in xrange(10)]
312
- for cmd in cmds:
313
- output.append(client.run_command(cmd, stop_on_errors=False))
314
- end = datetime.datetime.now()
315
- print "Started %s commands in %s" % (len(cmds), end-start,)
316
- start = datetime.datetime.now()
317
- for _output in output:
318
- for line in _output[host]['stdout']:
319
- print line
320
- end = datetime.datetime.now()
321
- print "All commands finished in %s" % (end-start,)
332
+ output = []
333
+ host = 'localhost'
334
+
335
+ # Run 10 five second sleeps
336
+ cmds = ['sleep 5' for _ in xrange(10)]
337
+ start = datetime.datetime.now()
338
+ for cmd in cmds:
339
+ output.append(client.run_command(cmd, stop_on_errors=False))
340
+ end = datetime.datetime.now()
341
+ print "Started %s commands in %s" % (len(cmds), end-start,)
342
+ start = datetime.datetime.now()
343
+ for _output in output:
344
+ for line in _output[host]['stdout']:
345
+ print line
346
+ end = datetime.datetime.now()
347
+ print "All commands finished in %s" % (end-start,)
322
348
323
349
*Output*
324
350
325
- Started 10 commands in 0:00:00.428629
326
- All commands finished in 0:00:05.014757
351
+ ::
327
352
328
- **Example Output**
353
+ Started 10 commands in 0:00:00.428629
354
+ All commands finished in 0:00:05.014757
355
+
356
+ **Output dictionary**
329
357
330
358
::
331
359
@@ -575,7 +603,7 @@ def copy_file(self, local_file, remote_file, recurse=False):
575
603
576
604
def _copy_file (self , host , local_file , remote_file , recurse = False ):
577
605
"""Make sftp client, copy file"""
578
- if not self .host_clients [host ]:
606
+ if not host in self . host_clients or not self .host_clients [host ]:
579
607
self .host_clients [host ] = SSHClient (
580
608
host , user = self .user , password = self .password ,
581
609
port = self .port , pkey = self .pkey ,
0 commit comments