@@ -163,6 +163,15 @@ def __init__(self, hosts,
163163 >>> import paramiko
164164 >>> client_key = paramiko.RSAKey.from_private_key_file('user.key')
165165 >>> client = ParallelSSHClient(['myhost1', 'myhost2'], pkey=client_key)
166+
167+ **Example with expression as host list**
168+
169+ Any type of iterator may be used as host list, including generator and
170+ list comprehension expressions.
171+
172+ >>> hosts = ['dc1.myhost1', 'dc2.myhost2']
173+ >>> client = ParallelSSHClient([h for h in hosts if h.find('dc1')])
174+ >>> client.run_command(<..>)
166175
167176 .. note ::
168177
@@ -184,7 +193,7 @@ def __init__(self, hosts,
184193
185194 Connection is terminated.
186195 """
187- self .pool_size = len ( hosts ) if len ( hosts ) < pool_size else pool_size
196+ self .pool_size = pool_size
188197 self .pool = gevent .pool .Pool (size = self .pool_size )
189198 self .hosts = hosts
190199 self .user = user
@@ -202,15 +211,21 @@ def __init__(self, hosts,
202211 def run_command (self , * args , ** kwargs ):
203212 """Run command on all hosts in parallel, honoring self.pool_size,
204213 and return output buffers.
205-
214+
206215 This function will block until all commands have **started** and
207- then return immediately. Any connection and/or authentication exceptions
208- will be raised here and need catching.
209-
216+ then return immediately.
217+
218+ Any connection and/or authentication exceptions will be raised here
219+ and need catching _unless_ `run_command` is called with
220+ `stop_on_errors=False`.
221+
210222 :param args: Positional arguments for command
211223 :type args: tuple
212224 :param sudo: (Optional) Run with sudo. Defaults to False
213225 :type sudo: bool
226+ :param user: (Optional) User to run command as. Requires sudo access \
227+ for that user from the logged in user account.
228+ :type user: str
214229 :param stop_on_errors: (Optional) Raise exception on errors running command. \
215230 Defaults to True. With stop_on_errors set to False, exceptions are instead \
216231 added to output of `run_command`. See example usage below.
@@ -235,18 +250,20 @@ def run_command(self, *args, **kwargs):
235250
236251 >>> for host in output:
237252 >>> for line in output[host]['stdout']: print line
238-
253+
239254 *Get exit codes after command has finished*
240-
255+
241256 >>> client.get_exit_codes(output)
242257 >>> for host in output:
243258 >>> ... print output[host]['exit_code']
244259 0
245260 0
246261
247- *Wait for completion, no stdout printing *
262+ *Wait for completion, no stdout/stderr *
248263
249264 >>> client.join(output)
265+ >>> print output[host]['exit_code']
266+ 0
250267
251268 *Run with sudo*
252269
@@ -370,33 +387,40 @@ def get_output(self, cmd, output):
370387 try :
371388 host = ex .args [1 ]
372389 except IndexError :
373- logger .error ("Got exception with no host argument - cannot update output data with %s" , ex )
390+ logger .error ("Got exception with no host argument - "
391+ "cannot update output data with %s" , ex )
374392 raise ex
375393 self ._update_host_output (output , host , None , None , None , None , cmd ,
376394 exception = ex )
377395 raise ex
378396 self ._update_host_output (output , host , self ._get_exit_code (channel ),
379397 channel , stdout , stderr , cmd )
380398
381- def _update_host_output (self , output , host , exit_code , channel , stdout , stderr , cmd ,
382- exception = None ):
399+ def _update_host_output (self , output , host , exit_code , channel , stdout ,
400+ stderr , cmd , exception = None ):
383401 """Update host output with given data"""
384402 if host in output :
385403 new_host = "_" .join ([host ,
386404 '' .join (random .choice (
387405 string .ascii_lowercase + string .digits )
388406 for _ in xrange (8 ))])
389- logger .warning ("Already have output for host %s - changing host key for %s to %s" ,
390- host , host , new_host )
407+ logger .warning ("Already have output for host %s - changing host "
408+ "key for %s to %s" , host , host , new_host )
391409 host = new_host
392410 output .setdefault (host , {})
393411 output [host ].update ({'exit_code' : exit_code ,
394412 'channel' : channel ,
395- 'stdout' : stdout ,
396- 'stderr' : stderr ,
413+ 'stdout' : self . _read_buff_ex_code ( stdout , output ) ,
414+ 'stderr' : self . _read_buff_ex_code ( stderr , output ) ,
397415 'cmd' : cmd ,
398416 'exception' : exception ,})
399-
417+
418+ def _read_buff_ex_code (self , _buffer , output ):
419+ if _buffer :
420+ for line in _buffer :
421+ yield line
422+ self .get_exit_codes (output )
423+
400424 def join (self , output ):
401425 """Block until all remote commands in output have finished
402426 and retrieve exit codes"""
0 commit comments