@@ -201,7 +201,7 @@ def get_exit_code(self, host_output):
201201 return
202202 return self ._get_exit_code (host_output .channel )
203203
204- def copy_file (self , local_file , remote_file , recurse = False ):
204+ def copy_file (self , local_file , remote_file , recurse = False , copy_args = None ):
205205 """Copy local file to remote file in parallel
206206
207207 This function returns a list of greenlets which can be
@@ -224,11 +224,19 @@ def copy_file(self, local_file, remote_file, recurse=False):
224224 :type remote_file: str
225225 :param recurse: Whether or not to descend into directories recursively.
226226 :type recurse: bool
227+ :param copy_args: (Optional) format local_file and remote_file strings
228+ with per-host arguments in ``copy_args``. ``copy_args`` length must
229+ equal length of host list -
230+ :py:class:`pssh.exceptions.HostArgumentException` is raised otherwise
231+ :type copy_args: tuple or list
232+
227233 :rtype: List(:py:class:`gevent.Greenlet`) of greenlets for remote copy
228234 commands
229235
230236 :raises: :py:class:`ValueError` when a directory is supplied to
231237 local_file and recurse is not set
238+ :raises: :py:class:`pssh.exceptions.HostArgumentException` on number of
239+ per-host copy arguments not equal to number of hosts
232240 :raises: :py:class:`IOError` on I/O errors writing files
233241 :raises: :py:class:`OSError` on OS errors like permission denied
234242
@@ -238,9 +246,21 @@ def copy_file(self, local_file, remote_file, recurse=False):
238246 created as long as permissions allow.
239247
240248 """
241- return [self .pool .spawn (self ._copy_file , host , local_file , remote_file ,
242- {'recurse' : recurse })
243- for host in self .hosts ]
249+ if copy_args :
250+ try :
251+ return [self .pool .spawn (self ._copy_file , host ,
252+ local_file % copy_args [host_i ],
253+ remote_file % copy_args [host_i ],
254+ {'recurse' : recurse })
255+ for host_i , host in enumerate (self .hosts )]
256+ except IndexError :
257+ raise HostArgumentException (
258+ "Number of per-host copy arguments provided does not match "
259+ "number of hosts" )
260+ else :
261+ return [self .pool .spawn (self ._copy_file , host , local_file ,
262+ remote_file , {'recurse' : recurse })
263+ for host in self .hosts ]
244264
245265 def _copy_file (self , host , local_file , remote_file , recurse = False ):
246266 """Make sftp client, copy file"""
@@ -249,7 +269,7 @@ def _copy_file(self, host, local_file, remote_file, recurse=False):
249269 recurse = recurse )
250270
251271 def copy_remote_file (self , remote_file , local_file , recurse = False ,
252- suffix_separator = '_' , ** kwargs ):
272+ suffix_separator = '_' , copy_args = None , ** kwargs ):
253273 """Copy remote file(s) in parallel as
254274 <local_file><suffix_separator><host>
255275
@@ -281,13 +301,22 @@ def copy_remote_file(self, remote_file, local_file, recurse=False,
281301 filename and host, defaults to ``_``. For example, for a
282302 ``local_file`` value of ``myfile`` and default separator the
283303 resulting filename will be ``myfile_myhost`` for the file from
284- host ``myhost``
304+ host ``myhost``. ``suffix_separator`` has no meaning if
305+ ``copy_args`` is provided
285306 :type suffix_separator: str
307+ :param copy_args: (Optional) format remote_file and local_file strings
308+ with per-host arguments in ``copy_args``. ``copy_args`` length must
309+ equal length of host list -
310+ :py:class:`pssh.exceptions.HostArgumentException` is raised otherwise
311+ :type copy_args: tuple or list
312+
286313 :rtype: list(:py:class:`gevent.Greenlet`) of greenlets for remote copy
287314 commands
288315
289316 :raises: :py:class:`ValueError` when a directory is supplied to
290317 local_file and recurse is not set
318+ :raises: :py:class:`pssh.exceptions.HostArgumentException` on number of
319+ per-host copy arguments not equal to number of hosts
291320 :raises: :py:class:`IOError` on I/O errors writing files
292321 :raises: :py:class:`OSError` on OS errors like permission denied
293322
@@ -300,17 +329,29 @@ def copy_remote_file(self, remote_file, local_file, recurse=False,
300329 filepath separated by ``suffix_separator``.
301330
302331 """
303- return [self .pool .spawn (
304- self ._copy_remote_file , host , remote_file ,
305- local_file , recurse , suffix_separator = suffix_separator ,
306- ** kwargs )
307- for host in self .hosts ]
332+ if copy_args :
333+ try :
334+ return [self .pool .spawn (
335+ self ._copy_remote_file , host ,
336+ remote_file % copy_args [host_i ],
337+ local_file % copy_args [host_i ], {'recurse' : recurse },
338+ ** kwargs )
339+ for host_i , host in enumerate (self .hosts )]
340+ except IndexError :
341+ raise HostArgumentException (
342+ "Number of per-host copy arguments provided does not match "
343+ "number of hosts" )
344+ else :
345+ return [self .pool .spawn (
346+ self ._copy_remote_file , host , remote_file ,
347+ suffix_separator .join ([local_file , host ]), recurse ,
348+ ** kwargs )
349+ for host in self .hosts ]
308350
309351 def _copy_remote_file (self , host , remote_file , local_file , recurse ,
310- suffix_separator = '_' , ** kwargs ):
352+ ** kwargs ):
311353 """Make sftp client, copy file to local"""
312- file_w_suffix = suffix_separator .join ([local_file , host ])
313354 self ._make_ssh_client (host )
314355 return self .host_clients [host ].copy_remote_file (
315- remote_file , file_w_suffix , recurse = recurse ,
356+ remote_file , local_file , recurse = recurse ,
316357 ** kwargs )
0 commit comments