Skip to content

Commit a3b6697

Browse files
author
Pan
committed
Added scp send/recv functions to native clients.
Updated changelog. Renamed tests.
1 parent 1c877db commit a3b6697

15 files changed

+409
-61
lines changed

.appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,4 @@ artifacts:
112112
- path: "*.whl"
113113

114114
deploy_script:
115-
- IF "%APPVEYOR_REPO_TAG%" == "true"( python ci/appveyor/pypi_upload.py *.whl )
115+
- python ci/appveyor/pypi_upload.py *.whl

Changelog.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ Change Log
77
Changes
88
--------
99

10+
* Added ``scp_send`` and ``scp_recv`` functions to native clients for sending and receiving files via SCP respectively.
11+
* Refactoring - clients moved to their own sub-package - ``pssh.clients`` - with backwards compatibility for imports from ``pssh.pssh_client`` and ``pssh.pssh2_client``.
12+
* Show underlying exception from native client library when raising ``parallel-ssh`` exceptions.
13+
* ``host`` parameter added to all exceptions raised by parallel clients - #116
14+
* Deprecation warning for client imports.
15+
* Deprecation warning for default client changing from paramiko to native client as of ``2.0.0``.
1016
* Upgrade embedded ``libssh2`` in binary wheels to latest version plus enhancements.
1117
* Adds support for ECDSA host keys for native client.
1218
* Adds support for SHA-256 host key fingerprints for native client.
@@ -19,7 +25,7 @@ Fixes
1925

2026
* Windows native client could not connect to newer SSH servers - thanks Pavel.
2127

22-
Note - changes apply to binary wheels only. See building from source instructions to make system packages.
28+
Note - libssh2 changes apply to binary wheels only. For building from source, `see documentation <http://parallel-ssh.readthedocs.io/en/latest/installation.html#building-from-source>`_.
2329

2430
1.5.5
2531
++++++

ci/appveyor/pypi_upload.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55

66
def upload_pypi(files):
7+
repo_tag = os.environ['APPVEYOR_REPO_TAG']
8+
if repo_tag == 'false':
9+
sys.stderr.write("Not a tagged release, skipping upload" + os.linesep)
10+
return
711
_user, _pass = os.environ['PYPI_USER'], os.environ['PYPI_PASS']
812
try:
913
subprocess.check_call(['twine', 'upload', '-u', _user,
@@ -16,8 +20,4 @@ def upload_pypi(files):
1620
if not len(sys.argv) > 1:
1721
sys.stderr.write("Need files to upload argument" + os.linesep)
1822
sys.exit(1)
19-
if os.environ['APPVEYOR_REPO_TAG'] != 'true':
20-
sys.stderr.write(
21-
"Not a tagged build - skipping PyPi upload" + os.linesep)
22-
sys.exit(0)
2323
upload_pypi(os.path.abspath(sys.argv[1]))

pssh/clients/base_pssh.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -303,15 +303,13 @@ def copy_remote_file(self, remote_file, local_file, recurse=False,
303303
host ``myhost``. ``suffix_separator`` has no meaning if
304304
``copy_args`` is provided
305305
:type suffix_separator: str
306-
:param copy_args: (Optional) format remote_file and local_file strings
307-
with per-host arguments in ``copy_args``. ``copy_args`` length must
306+
:param copy_args: (Optional) Format remote_file and local_file strings
307+
with per-host arguments in ``copy_args``. ``copy_args`` length must
308308
equal length of host list -
309309
:py:class:`pssh.exceptions.HostArgumentException` is raised otherwise
310310
:type copy_args: tuple or list
311-
312311
:rtype: list(:py:class:`gevent.Greenlet`) of greenlets for remote copy
313312
commands
314-
315313
:raises: :py:class:`ValueError` when a directory is supplied to
316314
local_file and recurse is not set
317315
:raises: :py:class:`pssh.exceptions.HostArgumentException` on number of
@@ -320,7 +318,7 @@ def copy_remote_file(self, remote_file, local_file, recurse=False,
320318
:raises: :py:class:`OSError` on OS errors like permission denied
321319
322320
.. note ::
323-
Local directories in `local_file` that do not exist will be
321+
Local directories in ``local_file`` that do not exist will be
324322
created as long as permissions allow.
325323
326324
.. note ::
@@ -333,8 +331,7 @@ def copy_remote_file(self, remote_file, local_file, recurse=False,
333331
return [self.pool.spawn(
334332
self._copy_remote_file, host,
335333
remote_file % copy_args[host_i],
336-
local_file % copy_args[host_i], {'recurse': recurse},
337-
**kwargs)
334+
local_file % copy_args[host_i], recurse=recurse, **kwargs)
338335
for host_i, host in enumerate(self.hosts)]
339336
except IndexError:
340337
raise HostArgumentException(
@@ -343,8 +340,7 @@ def copy_remote_file(self, remote_file, local_file, recurse=False,
343340
else:
344341
return [self.pool.spawn(
345342
self._copy_remote_file, host, remote_file,
346-
suffix_separator.join([local_file, host]), recurse,
347-
**kwargs)
343+
suffix_separator.join([local_file, host]), recurse, **kwargs)
348344
for host in self.hosts]
349345

350346
def _copy_remote_file(self, host, remote_file, local_file, recurse,
@@ -353,8 +349,7 @@ def _copy_remote_file(self, host, remote_file, local_file, recurse,
353349
try:
354350
self._make_ssh_client(host)
355351
return self.host_clients[host].copy_remote_file(
356-
remote_file, local_file, recurse=recurse,
357-
**kwargs)
352+
remote_file, local_file, recurse=recurse, **kwargs)
358353
except Exception as ex:
359354
ex.host = host
360355
raise ex

pssh/clients/native/parallel.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from ..base_pssh import BaseParallelSSHClient
2222
from ...constants import DEFAULT_RETRIES, RETRY_DELAY
2323
from .single import SSHClient
24-
from ...exceptions import ProxyError, Timeout
24+
from ...exceptions import ProxyError, Timeout, HostArgumentException
2525
from ...tunnel import Tunnel
2626

2727

@@ -446,3 +446,46 @@ def copy_remote_file(self, remote_file, local_file, recurse=False,
446446
self, remote_file, local_file, recurse=recurse,
447447
suffix_separator=suffix_separator, copy_args=copy_args,
448448
encoding=encoding)
449+
450+
def _scp_send(self, host, local_file, remote_file, recurse=False):
451+
self._make_ssh_client(host)
452+
return self._handle_greenlet_exc(
453+
self.host_clients[host].scp_send, host,
454+
local_file, remote_file, recurse=recurse)
455+
456+
def _scp_recv(self, host, remote_file, local_file, recurse=False):
457+
self._make_ssh_client(host)
458+
return self._handle_greenlet_exc(
459+
self.host_clients[host].scp_recv, host,
460+
remote_file, local_file, recurse=recurse)
461+
462+
def scp_send(self, local_file, remote_file, recurse=False):
463+
return [self.pool.spawn(self._scp_send, host, local_file,
464+
remote_file, recurse=recurse)
465+
for host in self.hosts]
466+
467+
def scp_recv(self, remote_file, local_file, recurse=False, copy_args=None):
468+
copy_args = [{'local_file': '_'.join([local_file, host]),
469+
'remote_file': remote_file}
470+
for i, host in enumerate(self.hosts)] \
471+
if copy_args is None else copy_args
472+
local_file = "%(local_file)s"
473+
remote_file = "%(remote_file)s"
474+
try:
475+
return [self.pool.spawn(
476+
self._scp_recv, host,
477+
remote_file % copy_args[host_i],
478+
local_file % copy_args[host_i], recurse=recurse)
479+
for host_i, host in enumerate(self.hosts)]
480+
except IndexError:
481+
raise HostArgumentException(
482+
"Number of per-host copy arguments provided does not match "
483+
"number of hosts")
484+
485+
def _handle_greenlet_exc(self, func, host, *args, **kwargs):
486+
try:
487+
self._make_ssh_client(host)
488+
return func(*args, **kwargs)
489+
except Exception as ex:
490+
ex.host = host
491+
raise ex

0 commit comments

Comments
 (0)