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.
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 greenlet if no data exists
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
- Writers should ``eof.set()`` when finished writing data via ``write``.
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
- elif self._read_pos == self._write_pos:
77
- sleep(.1)
91
+ else:
92
+ self._unread_data.wait()
@@ -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,
@@ -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, timeout=self.timeout)
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
- while True:
246
- self.poll()
247
- try:
248
- size, data = channel.read_nonblocking(is_stderr=is_stderr)
249
- except EOF:
250
- _buffer.eof.set()
251
- sleep(.1)
252
- return
253
- if size > 0:
254
- _buffer.write(data)
255
- else:
256
- # Yield event loop to other greenlets if we have no data to
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, timeout=self.timeout)
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, timeout=None):
333
- return self._eagain_write_errcode(write_func, data, SSH_AGAIN, timeout=timeout)
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 rw_bufffer: Read/write buffer
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', 'exception', 'encoding', 'read_timeout',
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,,