parallel-ssh 2.11.1__py3-none-any.whl → 2.13.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {parallel_ssh-2.11.1.dist-info → parallel_ssh-2.13.0.dist-info}/METADATA +7 -13
- parallel_ssh-2.13.0.dist-info/RECORD +27 -0
- {parallel_ssh-2.11.1.dist-info → parallel_ssh-2.13.0.dist-info}/WHEEL +1 -1
- pssh/__init__.py +3 -3
- pssh/_version.py +248 -85
- pssh/clients/__init__.py +2 -3
- pssh/clients/base/parallel.py +38 -27
- pssh/clients/base/single.py +22 -14
- pssh/clients/native/parallel.py +11 -21
- pssh/clients/native/single.py +56 -32
- pssh/clients/native/tunnel.py +2 -7
- pssh/clients/reader.py +24 -9
- pssh/clients/ssh/parallel.py +1 -0
- pssh/clients/ssh/single.py +26 -28
- pssh/config.py +7 -2
- pssh/output.py +10 -6
- parallel_ssh-2.11.1.dist-info/RECORD +0 -27
- {parallel_ssh-2.11.1.dist-info → parallel_ssh-2.13.0.dist-info}/COPYING +0 -0
- {parallel_ssh-2.11.1.dist-info → parallel_ssh-2.13.0.dist-info}/COPYING.LESSER +0 -0
- {parallel_ssh-2.11.1.dist-info → parallel_ssh-2.13.0.dist-info}/LICENSE +0 -0
- {parallel_ssh-2.11.1.dist-info → parallel_ssh-2.13.0.dist-info}/top_level.txt +0 -0
pssh/clients/reader.py
CHANGED
@@ -17,31 +17,43 @@
|
|
17
17
|
|
18
18
|
from io import BytesIO
|
19
19
|
|
20
|
-
from gevent import sleep
|
21
20
|
from gevent.event import Event
|
22
21
|
from gevent.lock import RLock
|
23
22
|
|
24
23
|
|
24
|
+
class _Eof(Event):
|
25
|
+
def __init__(self, unread_data):
|
26
|
+
self._unread_data = unread_data
|
27
|
+
Event.__init__(self)
|
28
|
+
|
29
|
+
def set(self):
|
30
|
+
self._unread_data.set()
|
31
|
+
Event.set(self)
|
32
|
+
|
33
|
+
|
25
34
|
class ConcurrentRWBuffer(object):
|
26
35
|
"""Concurrent reader/writer of bytes for use from multiple greenlets.
|
27
36
|
|
28
|
-
Supports both concurrent reading and writing.
|
37
|
+
Supports both concurrent reading and writing and combinations there of.
|
29
38
|
|
30
|
-
Iterate on buffer object to read data, yielding
|
39
|
+
Iterate on buffer object to read data, yielding event loop if no data exists
|
31
40
|
until self.eof has been set.
|
32
41
|
|
33
|
-
|
42
|
+
Check if end-of-file without blocking with ``ConcurrentRWBuffer.eof.is_set()``.
|
43
|
+
|
44
|
+
Writers should call ``ConcurrentRWBuffer.eof.set()`` when finished writing data via ``write``.
|
34
45
|
|
35
46
|
Readers can use ``read()`` to get any available data or ``None``.
|
36
47
|
"""
|
37
|
-
__slots__ = ('_buffer', '_read_pos', '_write_pos', 'eof', '_lock')
|
48
|
+
__slots__ = ('_buffer', '_read_pos', '_write_pos', 'eof', '_lock', '_unread_data')
|
38
49
|
|
39
50
|
def __init__(self):
|
40
51
|
self._buffer = BytesIO()
|
41
52
|
self._read_pos = 0
|
42
53
|
self._write_pos = 0
|
43
|
-
self.eof = Event()
|
44
54
|
self._lock = RLock()
|
55
|
+
self._unread_data = Event()
|
56
|
+
self.eof = _Eof(self._unread_data)
|
45
57
|
|
46
58
|
def write(self, data):
|
47
59
|
"""Write data to buffer.
|
@@ -53,14 +65,17 @@ class ConcurrentRWBuffer(object):
|
|
53
65
|
if not self._buffer.tell() == self._write_pos:
|
54
66
|
self._buffer.seek(self._write_pos)
|
55
67
|
self._write_pos += self._buffer.write(data)
|
68
|
+
if not self._unread_data.is_set() and self._read_pos < self._write_pos:
|
69
|
+
self._unread_data.set()
|
56
70
|
|
57
71
|
def read(self):
|
58
|
-
"""Read available data, or return None
|
72
|
+
"""Read available data, or return None.
|
59
73
|
|
60
74
|
:rtype: bytes
|
61
75
|
"""
|
62
76
|
with self._lock:
|
63
77
|
if self._write_pos == 0 or self._read_pos == self._write_pos:
|
78
|
+
self._unread_data.clear()
|
64
79
|
return
|
65
80
|
elif not self._buffer.tell() == self._read_pos:
|
66
81
|
self._buffer.seek(self._read_pos)
|
@@ -73,5 +88,5 @@ class ConcurrentRWBuffer(object):
|
|
73
88
|
data = self.read()
|
74
89
|
if data:
|
75
90
|
yield data
|
76
|
-
|
77
|
-
|
91
|
+
else:
|
92
|
+
self._unread_data.wait()
|
pssh/clients/ssh/parallel.py
CHANGED
@@ -217,6 +217,7 @@ class ParallelSSHClient(BaseParallelSSHClient):
|
|
217
217
|
_client = SSHClient(
|
218
218
|
host, user=cfg.user or self.user, password=cfg.password or self.password, port=cfg.port or self.port,
|
219
219
|
pkey=_pkey_data, num_retries=cfg.num_retries or self.num_retries,
|
220
|
+
alias=cfg.alias,
|
220
221
|
timeout=cfg.timeout or self.timeout,
|
221
222
|
allow_agent=cfg.allow_agent or self.allow_agent, retry_delay=cfg.retry_delay or self.retry_delay,
|
222
223
|
_auth_thread_pool=cfg.auth_thread_pool or self._auth_thread_pool,
|
pssh/clients/ssh/single.py
CHANGED
@@ -19,18 +19,17 @@ import logging
|
|
19
19
|
|
20
20
|
from gevent import sleep, spawn, Timeout as GTimeout, joinall
|
21
21
|
from ssh import options
|
22
|
-
from ssh.session import Session, SSH_READ_PENDING, SSH_WRITE_PENDING
|
23
|
-
from ssh.key import import_privkey_file, import_cert_file, copy_cert_to_privkey,\
|
24
|
-
import_privkey_base64
|
25
|
-
from ssh.exceptions import EOF
|
26
22
|
from ssh.error_codes import SSH_AGAIN
|
23
|
+
from ssh.exceptions import EOF
|
24
|
+
from ssh.key import import_privkey_file, import_cert_file, copy_cert_to_privkey, \
|
25
|
+
import_privkey_base64
|
26
|
+
from ssh.session import Session, SSH_READ_PENDING, SSH_WRITE_PENDING
|
27
27
|
|
28
28
|
from ..base.single import BaseSSHClient
|
29
29
|
from ..common import _validate_pkey_path
|
30
|
-
from ...output import HostOutput
|
31
|
-
from ...exceptions import SessionError, Timeout
|
32
30
|
from ...constants import DEFAULT_RETRIES, RETRY_DELAY
|
33
|
-
|
31
|
+
from ...exceptions import SessionError, Timeout
|
32
|
+
from ...output import HostOutput
|
34
33
|
|
35
34
|
logger = logging.getLogger(__name__)
|
36
35
|
|
@@ -40,7 +39,7 @@ class SSHClient(BaseSSHClient):
|
|
40
39
|
|
41
40
|
def __init__(self, host,
|
42
41
|
user=None, password=None, port=None,
|
43
|
-
pkey=None,
|
42
|
+
pkey=None, alias=None,
|
44
43
|
cert_file=None,
|
45
44
|
num_retries=DEFAULT_RETRIES,
|
46
45
|
retry_delay=RETRY_DELAY,
|
@@ -60,6 +59,8 @@ class SSHClient(BaseSSHClient):
|
|
60
59
|
:type password: str
|
61
60
|
:param port: SSH port to connect to. Defaults to SSH default (22)
|
62
61
|
:type port: int
|
62
|
+
:param alias: Use an alias for this host.
|
63
|
+
:type alias: str
|
63
64
|
:param pkey: Private key file path to use for authentication. Path must
|
64
65
|
be either absolute path or relative to user home directory
|
65
66
|
like ``~/<path>``.
|
@@ -114,7 +115,7 @@ class SSHClient(BaseSSHClient):
|
|
114
115
|
self.gssapi_client_identity = gssapi_client_identity
|
115
116
|
self.gssapi_delegate_credentials = gssapi_delegate_credentials
|
116
117
|
super(SSHClient, self).__init__(
|
117
|
-
host, user=user, password=password, port=port, pkey=pkey,
|
118
|
+
host, user=user, password=password, port=port, pkey=pkey, alias=alias,
|
118
119
|
num_retries=num_retries, retry_delay=retry_delay,
|
119
120
|
allow_agent=allow_agent,
|
120
121
|
_auth_thread_pool=_auth_thread_pool,
|
@@ -238,25 +239,22 @@ class SSHClient(BaseSSHClient):
|
|
238
239
|
if use_pty:
|
239
240
|
self._eagain(channel.request_pty, timeout=self.timeout)
|
240
241
|
logger.debug("Executing command '%s'", cmd)
|
241
|
-
self._eagain(channel.request_exec, cmd
|
242
|
+
self._eagain(channel.request_exec, cmd)
|
242
243
|
return channel
|
243
244
|
|
244
245
|
def _read_output_to_buffer(self, channel, _buffer, is_stderr=False):
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
# send back, meaning the generator does not yield and can there
|
258
|
-
# for block other generators/greenlets from running.
|
259
|
-
sleep(.1)
|
246
|
+
try:
|
247
|
+
while True:
|
248
|
+
self.poll()
|
249
|
+
try:
|
250
|
+
size, data = channel.read_nonblocking(is_stderr=is_stderr)
|
251
|
+
except EOF:
|
252
|
+
return
|
253
|
+
if size > 0:
|
254
|
+
_buffer.write(data)
|
255
|
+
sleep()
|
256
|
+
finally:
|
257
|
+
_buffer.eof.set()
|
260
258
|
|
261
259
|
def wait_finished(self, host_output, timeout=None):
|
262
260
|
"""Wait for EOF from channel and close channel.
|
@@ -313,7 +311,7 @@ class SSHClient(BaseSSHClient):
|
|
313
311
|
:type channel: :py:class:`ssh.channel.Channel`
|
314
312
|
"""
|
315
313
|
logger.debug("Closing channel")
|
316
|
-
self._eagain(channel.close
|
314
|
+
self._eagain(channel.close)
|
317
315
|
|
318
316
|
def poll(self, timeout=None):
|
319
317
|
"""ssh-python based co-operative gevent poll on session socket.
|
@@ -329,5 +327,5 @@ class SSHClient(BaseSSHClient):
|
|
329
327
|
"""Run function given and handle EAGAIN for an ssh-python session"""
|
330
328
|
return self._eagain_errcode(func, SSH_AGAIN, *args, **kwargs)
|
331
329
|
|
332
|
-
def _eagain_write(self, write_func, data
|
333
|
-
return self._eagain_write_errcode(write_func, data, SSH_AGAIN
|
330
|
+
def _eagain_write(self, write_func, data):
|
331
|
+
return self._eagain_write_errcode(write_func, data, SSH_AGAIN)
|
pssh/config.py
CHANGED
@@ -25,7 +25,7 @@ class HostConfig(object):
|
|
25
25
|
Used to hold individual configuration for each host in ParallelSSHClient host list.
|
26
26
|
"""
|
27
27
|
__slots__ = ('user', 'port', 'password', 'private_key', 'allow_agent',
|
28
|
-
'num_retries', 'retry_delay', 'timeout', 'identity_auth',
|
28
|
+
'alias', 'num_retries', 'retry_delay', 'timeout', 'identity_auth',
|
29
29
|
'proxy_host', 'proxy_port', 'proxy_user', 'proxy_password', 'proxy_pkey',
|
30
30
|
'keepalive_seconds', 'ipv6_only', 'cert_file', 'auth_thread_pool', 'gssapi_auth',
|
31
31
|
'gssapi_server_identity', 'gssapi_client_identity', 'gssapi_delegate_credentials',
|
@@ -33,7 +33,7 @@ class HostConfig(object):
|
|
33
33
|
)
|
34
34
|
|
35
35
|
def __init__(self, user=None, port=None, password=None, private_key=None,
|
36
|
-
allow_agent=None, num_retries=None, retry_delay=None, timeout=None,
|
36
|
+
allow_agent=None, alias=None, num_retries=None, retry_delay=None, timeout=None,
|
37
37
|
identity_auth=None,
|
38
38
|
proxy_host=None, proxy_port=None, proxy_user=None, proxy_password=None,
|
39
39
|
proxy_pkey=None,
|
@@ -58,6 +58,8 @@ class HostConfig(object):
|
|
58
58
|
:type private_key: str
|
59
59
|
:param allow_agent: Enable/disable SSH agent authentication.
|
60
60
|
:type allow_agent: bool
|
61
|
+
:param alias: Use an alias for this host.
|
62
|
+
:type alias: str
|
61
63
|
:param num_retries: Number of retry attempts before giving up on connection
|
62
64
|
and SSH operations.
|
63
65
|
:type num_retries: int
|
@@ -103,6 +105,7 @@ class HostConfig(object):
|
|
103
105
|
self.password = password
|
104
106
|
self.private_key = private_key
|
105
107
|
self.allow_agent = allow_agent
|
108
|
+
self.alias = alias
|
106
109
|
self.num_retries = num_retries
|
107
110
|
self.timeout = timeout
|
108
111
|
self.retry_delay = retry_delay
|
@@ -130,6 +133,8 @@ class HostConfig(object):
|
|
130
133
|
raise ValueError("Port %s is not an integer" % (self.port,))
|
131
134
|
if self.password is not None and not isinstance(self.password, str):
|
132
135
|
raise ValueError("Password %s is not a string" % (self.password,))
|
136
|
+
if self.alias is not None and not isinstance(self.alias, str):
|
137
|
+
raise ValueError("Alias %s is not a string" % (self.alias,))
|
133
138
|
if self.private_key is not None and not (
|
134
139
|
isinstance(self.private_key, str) or isinstance(self.private_key, bytes)
|
135
140
|
):
|
pssh/output.py
CHANGED
@@ -44,7 +44,7 @@ class BufferData(object):
|
|
44
44
|
"""
|
45
45
|
:param reader: Greenlet reading data from channel and writing to rw_buffer
|
46
46
|
:type reader: :py:class:`gevent.Greenlet`
|
47
|
-
:param
|
47
|
+
:param rw_buffer: Read/write buffer
|
48
48
|
:type rw_buffer: :py:class:`pssh.clients.reader.ConcurrentRWBuffer`
|
49
49
|
"""
|
50
50
|
self.reader = reader
|
@@ -55,12 +55,12 @@ class HostOutput(object):
|
|
55
55
|
"""Host output"""
|
56
56
|
|
57
57
|
__slots__ = ('host', 'channel', 'stdin',
|
58
|
-
'client', '
|
59
|
-
'buffers',
|
58
|
+
'client', 'alias', 'exception',
|
59
|
+
'encoding', 'read_timeout', 'buffers',
|
60
60
|
)
|
61
61
|
|
62
62
|
def __init__(self, host, channel, stdin,
|
63
|
-
client, exception=None, encoding='utf-8', read_timeout=None,
|
63
|
+
client, alias=None, exception=None, encoding='utf-8', read_timeout=None,
|
64
64
|
buffers=None):
|
65
65
|
"""
|
66
66
|
:param host: Host name output is for
|
@@ -70,7 +70,9 @@ class HostOutput(object):
|
|
70
70
|
:param stdin: Standard input buffer
|
71
71
|
:type stdin: :py:func:`file`-like object
|
72
72
|
:param client: `SSHClient` output is coming from.
|
73
|
-
:type client: :py:class:`pssh.clients.base.single.BaseSSHClient`
|
73
|
+
:type client: :py:class:`pssh.clients.base.single.BaseSSHClient` or `None`.
|
74
|
+
:param alias: Host alias.
|
75
|
+
:type alias: str
|
74
76
|
:param exception: Exception from host if any
|
75
77
|
:type exception: :py:class:`Exception` or ``None``
|
76
78
|
:param read_timeout: Timeout in seconds for reading from buffers.
|
@@ -82,6 +84,7 @@ class HostOutput(object):
|
|
82
84
|
self.channel = channel
|
83
85
|
self.stdin = stdin
|
84
86
|
self.client = client
|
87
|
+
self.alias = alias
|
85
88
|
self.exception = exception
|
86
89
|
self.encoding = encoding
|
87
90
|
self.read_timeout = read_timeout
|
@@ -117,12 +120,13 @@ class HostOutput(object):
|
|
117
120
|
|
118
121
|
def __repr__(self):
|
119
122
|
return "\thost={host}{linesep}" \
|
123
|
+
"\talias={alias}{linesep}" \
|
120
124
|
"\texit_code={exit_code}{linesep}" \
|
121
125
|
"\tchannel={channel}{linesep}" \
|
122
126
|
"\texception={exception}{linesep}" \
|
123
127
|
"\tencoding={encoding}{linesep}" \
|
124
128
|
"\tread_timeout={read_timeout}".format(
|
125
|
-
host=self.host, channel=self.channel,
|
129
|
+
host=self.host, alias=self.alias, channel=self.channel,
|
126
130
|
exception=self.exception, linesep=linesep,
|
127
131
|
exit_code=self.exit_code, encoding=self.encoding, read_timeout=self.read_timeout,
|
128
132
|
)
|
@@ -1,27 +0,0 @@
|
|
1
|
-
pssh/__init__.py,sha256=Kd_8EGJTiujApfM-2gB4JlCr1m37rGQvtrlIaWu3CzM,1451
|
2
|
-
pssh/_version.py,sha256=Oc3htkNS0sL05sUFjLa2gXhHYAZ65Defi2SyddJyChk,18445
|
3
|
-
pssh/config.py,sha256=ypMYKOEt-9VgeVEyIP7KYX59HDInpri0z0tl4YBxo00,9783
|
4
|
-
pssh/constants.py,sha256=toVllZuLBKxLYB8fuY_46UoPjmAA3J59oH1wS4OlL7g,1056
|
5
|
-
pssh/exceptions.py,sha256=ZjnrT_Ye1v4OH582jNwYUCkI4NVgujCWwu8aB8XymUM,2561
|
6
|
-
pssh/output.py,sha256=3m2EfWHBAadxyc-iwTVcHnskaUPKXv3OHm5OVr4vMQg,4489
|
7
|
-
pssh/utils.py,sha256=qdsVnKGxZovWGZpGS3eUyrfCaeWTKX7yETfA29zbLMk,1802
|
8
|
-
pssh/clients/__init__.py,sha256=ByjAWGrbhFnNz5dX3ED7sT3m8A-qKgWgBH4xsmZROrg,871
|
9
|
-
pssh/clients/common.py,sha256=fd_jJj8ezDjwr3peN3LonLff5EYSNkMEre0yjlnRjBU,1375
|
10
|
-
pssh/clients/reader.py,sha256=_9PgsPtlK1rsHbhwoxj_bSnWZlk1DgXFzYK8cGdzL88,2561
|
11
|
-
pssh/clients/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
-
pssh/clients/base/parallel.py,sha256=XjWFXai-pK5mwW-V49oUrOx41NHNrMt4ty_jZ4sR564,24526
|
13
|
-
pssh/clients/base/single.py,sha256=zDI1-lAn5SMFsV_8PedTyceIXacze2lCWRk7z6POQBs,25741
|
14
|
-
pssh/clients/native/__init__.py,sha256=dydX5Fae9U9gRBT8koPTM8H9u4t0rIgD1q9XammFYSs,865
|
15
|
-
pssh/clients/native/parallel.py,sha256=RhVTc9Try_PP8QuI4MHLqseQYo6gHFXrfbLNGWsYkwY,24335
|
16
|
-
pssh/clients/native/single.py,sha256=jKY--0H5szZiMkirfLJG5noDirsI4RLJpNDOSpUjksk,31162
|
17
|
-
pssh/clients/native/tunnel.py,sha256=3YWDNS6YXb1cdbCehaeLyXZEY7m4sHlqj0dH_LHxrlc,9369
|
18
|
-
pssh/clients/ssh/__init__.py,sha256=HgYZkVJ5QOgaVFp5snwyLZvQbp0Tbdxj8Gbyic2_Ibo,857
|
19
|
-
pssh/clients/ssh/parallel.py,sha256=GMobYTyxuylqNJNKUYIIAZEGMYNpZe7ZniG3CnId2KU,11368
|
20
|
-
pssh/clients/ssh/single.py,sha256=hqVRGUGy5srwQrbNwWP2adzSVFEQLh-8E3QNCpq4kD4,13805
|
21
|
-
parallel_ssh-2.11.1.dist-info/COPYING,sha256=ZA2Q9u5AEkH_YoNNDRsz-DBJ6ZuL_foE7RsKFjXd4-c,18093
|
22
|
-
parallel_ssh-2.11.1.dist-info/COPYING.LESSER,sha256=AKibDRiqzUEU3s95Ei24e_Nb3a8rxQ44PJyfTCYzkLI,24486
|
23
|
-
parallel_ssh-2.11.1.dist-info/LICENSE,sha256=m4cqigcLitMpxL04D7G_AAD1ZMdQI-yOHmgD8VNkuek,26461
|
24
|
-
parallel_ssh-2.11.1.dist-info/METADATA,sha256=BuxgH3pBR0R-5oF9kKPvVx-zT7z_OSfq2ck2sa9XC1w,10933
|
25
|
-
parallel_ssh-2.11.1.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
|
26
|
-
parallel_ssh-2.11.1.dist-info/top_level.txt,sha256=s8P6ZHOwt2BYgDc62Cpd2z7i-rebGzIhhnO09pger0U,5
|
27
|
-
parallel_ssh-2.11.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|