parallel-ssh 2.14.0__tar.gz → 2.16.0__tar.gz
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.14.0 → parallel_ssh-2.16.0}/PKG-INFO +19 -6
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/parallel_ssh.egg-info/PKG-INFO +19 -6
- parallel_ssh-2.16.0/pssh/__init__.py +39 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/pssh/_version.py +3 -3
- parallel_ssh-2.16.0/pssh/clients/__init__.py +19 -0
- parallel_ssh-2.16.0/pssh/clients/base/__init__.py +16 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/pssh/clients/base/parallel.py +19 -14
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/pssh/clients/base/single.py +28 -20
- parallel_ssh-2.16.0/pssh/clients/common.py +51 -0
- parallel_ssh-2.16.0/pssh/clients/native/__init__.py +20 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/pssh/clients/native/parallel.py +26 -13
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/pssh/clients/native/single.py +39 -16
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/pssh/clients/native/tunnel.py +13 -13
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/pssh/clients/reader.py +13 -13
- parallel_ssh-2.16.0/pssh/clients/ssh/__init__.py +20 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/pssh/clients/ssh/parallel.py +18 -13
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/pssh/clients/ssh/single.py +19 -13
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/pssh/config.py +35 -14
- parallel_ssh-2.16.0/pssh/constants.py +28 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/pssh/exceptions.py +17 -13
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/pssh/output.py +30 -15
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/pssh/utils.py +13 -13
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/setup.py +21 -15
- parallel_ssh-2.14.0/pssh/__init__.py +0 -39
- parallel_ssh-2.14.0/pssh/clients/__init__.py +0 -19
- parallel_ssh-2.14.0/pssh/clients/base/__init__.py +0 -0
- parallel_ssh-2.14.0/pssh/clients/common.py +0 -41
- parallel_ssh-2.14.0/pssh/clients/native/__init__.py +0 -20
- parallel_ssh-2.14.0/pssh/clients/ssh/__init__.py +0 -20
- parallel_ssh-2.14.0/pssh/constants.py +0 -28
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/COPYING +0 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/COPYING.LESSER +0 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/LICENSE +0 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/MANIFEST.in +0 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/README.rst +0 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/parallel_ssh.egg-info/SOURCES.txt +0 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/parallel_ssh.egg-info/dependency_links.txt +0 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/parallel_ssh.egg-info/requires.txt +0 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/parallel_ssh.egg-info/top_level.txt +0 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/setup.cfg +0 -0
- {parallel_ssh-2.14.0 → parallel_ssh-2.16.0}/versioneer.py +0 -0
@@ -1,25 +1,29 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: parallel-ssh
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.16.0
|
4
4
|
Summary: Asynchronous parallel SSH library
|
5
5
|
Home-page: https://github.com/ParallelSSH/parallel-ssh
|
6
6
|
Author: Panos Kittenis
|
7
|
-
Author-email:
|
8
|
-
License:
|
7
|
+
Author-email: danst@tutanota.com
|
8
|
+
License: LGPL-2.1-only
|
9
9
|
Classifier: Development Status :: 5 - Production/Stable
|
10
|
-
Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)
|
11
10
|
Classifier: Intended Audience :: Developers
|
12
11
|
Classifier: Operating System :: OS Independent
|
13
|
-
Classifier: Programming Language :: Python
|
14
12
|
Classifier: Programming Language :: Python :: 3
|
15
13
|
Classifier: Programming Language :: Python :: 3.8
|
16
14
|
Classifier: Programming Language :: Python :: 3.9
|
17
15
|
Classifier: Programming Language :: Python :: 3.10
|
18
16
|
Classifier: Programming Language :: Python :: 3.11
|
19
17
|
Classifier: Programming Language :: Python :: 3.12
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
20
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
21
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
22
|
+
Classifier: Topic :: System :: Shells
|
20
23
|
Classifier: Topic :: System :: Networking
|
21
24
|
Classifier: Topic :: Software Development :: Libraries
|
22
25
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
26
|
+
Classifier: Operating System :: POSIX
|
23
27
|
Classifier: Operating System :: POSIX :: Linux
|
24
28
|
Classifier: Operating System :: POSIX :: BSD
|
25
29
|
Classifier: Operating System :: Microsoft :: Windows
|
@@ -30,6 +34,15 @@ License-File: COPYING.LESSER
|
|
30
34
|
Requires-Dist: gevent
|
31
35
|
Requires-Dist: ssh2-python
|
32
36
|
Requires-Dist: ssh-python
|
37
|
+
Dynamic: author
|
38
|
+
Dynamic: author-email
|
39
|
+
Dynamic: classifier
|
40
|
+
Dynamic: description
|
41
|
+
Dynamic: home-page
|
42
|
+
Dynamic: license
|
43
|
+
Dynamic: license-file
|
44
|
+
Dynamic: requires-dist
|
45
|
+
Dynamic: summary
|
33
46
|
|
34
47
|
============
|
35
48
|
parallel-ssh
|
@@ -1,25 +1,29 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: parallel-ssh
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.16.0
|
4
4
|
Summary: Asynchronous parallel SSH library
|
5
5
|
Home-page: https://github.com/ParallelSSH/parallel-ssh
|
6
6
|
Author: Panos Kittenis
|
7
|
-
Author-email:
|
8
|
-
License:
|
7
|
+
Author-email: danst@tutanota.com
|
8
|
+
License: LGPL-2.1-only
|
9
9
|
Classifier: Development Status :: 5 - Production/Stable
|
10
|
-
Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)
|
11
10
|
Classifier: Intended Audience :: Developers
|
12
11
|
Classifier: Operating System :: OS Independent
|
13
|
-
Classifier: Programming Language :: Python
|
14
12
|
Classifier: Programming Language :: Python :: 3
|
15
13
|
Classifier: Programming Language :: Python :: 3.8
|
16
14
|
Classifier: Programming Language :: Python :: 3.9
|
17
15
|
Classifier: Programming Language :: Python :: 3.10
|
18
16
|
Classifier: Programming Language :: Python :: 3.11
|
19
17
|
Classifier: Programming Language :: Python :: 3.12
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
20
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
21
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
22
|
+
Classifier: Topic :: System :: Shells
|
20
23
|
Classifier: Topic :: System :: Networking
|
21
24
|
Classifier: Topic :: Software Development :: Libraries
|
22
25
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
26
|
+
Classifier: Operating System :: POSIX
|
23
27
|
Classifier: Operating System :: POSIX :: Linux
|
24
28
|
Classifier: Operating System :: POSIX :: BSD
|
25
29
|
Classifier: Operating System :: Microsoft :: Windows
|
@@ -30,6 +34,15 @@ License-File: COPYING.LESSER
|
|
30
34
|
Requires-Dist: gevent
|
31
35
|
Requires-Dist: ssh2-python
|
32
36
|
Requires-Dist: ssh-python
|
37
|
+
Dynamic: author
|
38
|
+
Dynamic: author-email
|
39
|
+
Dynamic: classifier
|
40
|
+
Dynamic: description
|
41
|
+
Dynamic: home-page
|
42
|
+
Dynamic: license
|
43
|
+
Dynamic: license-file
|
44
|
+
Dynamic: requires-dist
|
45
|
+
Dynamic: summary
|
33
46
|
|
34
47
|
============
|
35
48
|
parallel-ssh
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# This file is part of parallel-ssh.
|
2
|
+
# Copyright (C) 2014-2025 Panos Kittenis.
|
3
|
+
# Copyright (C) 2014-2025 parallel-ssh Contributors.
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation, version 2.1.
|
8
|
+
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this library; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
17
|
+
|
18
|
+
"""Asynchronous parallel SSH client library.
|
19
|
+
|
20
|
+
Run SSH commands over many - hundreds/hundreds of thousands - number of servers
|
21
|
+
asynchronously and with minimal system load on the client host.
|
22
|
+
|
23
|
+
New users should start with `pssh.clients.ParallelSSHClient.run_command` and
|
24
|
+
`pssh.clients.SSHClient.run_command`
|
25
|
+
|
26
|
+
See also `pssh.clients.ParallelSSHClient` and pssh.clients.SSHClient`
|
27
|
+
for class documentation.
|
28
|
+
"""
|
29
|
+
|
30
|
+
|
31
|
+
from logging import getLogger, NullHandler
|
32
|
+
from . import _version
|
33
|
+
__version__ = _version.get_versions()['version']
|
34
|
+
del _version
|
35
|
+
|
36
|
+
host_logger = getLogger('pssh.host_logger')
|
37
|
+
logger = getLogger('pssh')
|
38
|
+
host_logger.addHandler(NullHandler())
|
39
|
+
logger.addHandler(NullHandler())
|
@@ -8,11 +8,11 @@ import json
|
|
8
8
|
|
9
9
|
version_json = '''
|
10
10
|
{
|
11
|
-
"date": "2025-
|
11
|
+
"date": "2025-10-12T16:35:40+0100",
|
12
12
|
"dirty": false,
|
13
13
|
"error": null,
|
14
|
-
"full-revisionid": "
|
15
|
-
"version": "2.
|
14
|
+
"full-revisionid": "38d14ee6bb797eacc20308348ca1c827238af9b4",
|
15
|
+
"version": "2.16.0"
|
16
16
|
}
|
17
17
|
''' # END VERSION_JSON
|
18
18
|
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# This file is part of parallel-ssh.
|
2
|
+
# Copyright (C) 2014-2025 Panos Kittenis.
|
3
|
+
# Copyright (C) 2014-2025 parallel-ssh Contributors.
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation, version 2.1.
|
8
|
+
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this library; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
17
|
+
|
18
|
+
# flake8: noqa: F401
|
19
|
+
from .native import ParallelSSHClient, SSHClient
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# This file is part of parallel-ssh.
|
2
|
+
# Copyright (C) 2014-2025 Panos Kittenis.
|
3
|
+
# Copyright (C) 2014-2025 parallel-ssh Contributors.
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation, version 2.1.
|
8
|
+
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this library; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
@@ -1,19 +1,19 @@
|
|
1
|
-
#
|
1
|
+
# This file is part of parallel-ssh.
|
2
|
+
# Copyright (C) 2014-2025 Panos Kittenis.
|
3
|
+
# Copyright (C) 2014-2025 parallel-ssh Contributors.
|
2
4
|
#
|
3
|
-
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation, version 2.1.
|
4
8
|
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
8
13
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# Lesser General Public License for more details.
|
13
|
-
#
|
14
|
-
# You should have received a copy of the GNU Lesser General Public
|
15
|
-
# License along with this library; if not, write to the Free Software
|
16
|
-
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this library; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
17
17
|
|
18
18
|
"""Abstract parallel SSH client package"""
|
19
19
|
|
@@ -23,7 +23,7 @@ import gevent.pool
|
|
23
23
|
from gevent import joinall, spawn, Timeout as GTimeout
|
24
24
|
from gevent.hub import Hub
|
25
25
|
|
26
|
-
from ..common import _validate_pkey_path, _validate_pkey
|
26
|
+
from ..common import _validate_pkey_path, _validate_pkey, _validate_api
|
27
27
|
from ...config import HostConfig
|
28
28
|
from ...constants import DEFAULT_RETRIES, RETRY_DELAY
|
29
29
|
from ...exceptions import HostArgumentError, Timeout, ShellError, HostConfigError
|
@@ -55,6 +55,8 @@ class BaseParallelSSHClient(object):
|
|
55
55
|
gssapi_client_identity=None,
|
56
56
|
gssapi_delegate_credentials=False,
|
57
57
|
forward_ssh_agent=False,
|
58
|
+
compress=False,
|
59
|
+
keyboard_interactive=False,
|
58
60
|
_auth_thread_pool=True,
|
59
61
|
):
|
60
62
|
self.allow_agent = allow_agent
|
@@ -86,6 +88,9 @@ class BaseParallelSSHClient(object):
|
|
86
88
|
self.gssapi_server_identity = gssapi_server_identity
|
87
89
|
self.gssapi_client_identity = gssapi_client_identity
|
88
90
|
self.gssapi_delegate_credentials = gssapi_delegate_credentials
|
91
|
+
self.compress = compress
|
92
|
+
self.keyboard_interactive = keyboard_interactive
|
93
|
+
_validate_api(self.keyboard_interactive, self.password)
|
89
94
|
self._auth_thread_pool = _auth_thread_pool
|
90
95
|
self._check_host_config()
|
91
96
|
|
@@ -1,19 +1,19 @@
|
|
1
|
-
#
|
1
|
+
# This file is part of parallel-ssh.
|
2
|
+
# Copyright (C) 2014-2025 Panos Kittenis.
|
3
|
+
# Copyright (C) 2014-2025 parallel-ssh Contributors.
|
2
4
|
#
|
3
|
-
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation, version 2.1.
|
4
8
|
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
8
13
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# Lesser General Public License for more details.
|
13
|
-
#
|
14
|
-
# You should have received a copy of the GNU Lesser General Public
|
15
|
-
# License along with this library; if not, write to the Free Software
|
16
|
-
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this library; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
17
17
|
|
18
18
|
import logging
|
19
19
|
import os
|
@@ -23,14 +23,14 @@ from warnings import warn
|
|
23
23
|
|
24
24
|
from gevent import sleep, socket, Timeout as GTimeout
|
25
25
|
from gevent.hub import Hub
|
26
|
+
from gevent.pool import Pool
|
26
27
|
from gevent.select import poll, POLLIN, POLLOUT
|
27
28
|
from gevent.socket import SHUT_RDWR
|
28
|
-
from gevent.pool import Pool
|
29
29
|
from ssh2.exceptions import AgentConnectionError, AgentListIdentitiesError, \
|
30
30
|
AgentAuthenticationError, AgentGetIdentityError
|
31
31
|
from ssh2.utils import find_eol
|
32
32
|
|
33
|
-
from ..common import _validate_pkey
|
33
|
+
from ..common import _validate_pkey, _validate_api
|
34
34
|
from ..reader import ConcurrentRWBuffer
|
35
35
|
from ...constants import DEFAULT_RETRIES, RETRY_DELAY
|
36
36
|
from ...exceptions import UnknownHostError, AuthenticationError, \
|
@@ -158,7 +158,9 @@ class InteractiveShell(object):
|
|
158
158
|
self._client._shell(self._chan)
|
159
159
|
self._encoding = encoding
|
160
160
|
self.output = self._client._make_host_output(
|
161
|
-
self._chan, encoding=encoding, read_timeout=read_timeout
|
161
|
+
self._chan, encoding=encoding, read_timeout=read_timeout,
|
162
|
+
fully_qualified_command=None,
|
163
|
+
)
|
162
164
|
|
163
165
|
@property
|
164
166
|
def stdout(self):
|
@@ -226,6 +228,8 @@ class BaseSSHClient(PollMixIn):
|
|
226
228
|
_auth_thread_pool=True,
|
227
229
|
identity_auth=True,
|
228
230
|
ipv6_only=False,
|
231
|
+
compress=False,
|
232
|
+
keyboard_interactive=False,
|
229
233
|
):
|
230
234
|
super(PollMixIn, self).__init__()
|
231
235
|
self._auth_thread_pool = _auth_thread_pool
|
@@ -233,6 +237,7 @@ class BaseSSHClient(PollMixIn):
|
|
233
237
|
self.alias = alias
|
234
238
|
self.user = user if user else getuser()
|
235
239
|
self.password = password
|
240
|
+
self.keyboard_interactive = keyboard_interactive
|
236
241
|
self.port = port if port else 22
|
237
242
|
self.num_retries = num_retries
|
238
243
|
self.timeout = timeout if timeout else None
|
@@ -245,6 +250,9 @@ class BaseSSHClient(PollMixIn):
|
|
245
250
|
self.identity_auth = identity_auth
|
246
251
|
self._keepalive_greenlet = None
|
247
252
|
self.ipv6_only = ipv6_only
|
253
|
+
self.compress = compress
|
254
|
+
self.keyboard_interactive = keyboard_interactive
|
255
|
+
_validate_api(self.keyboard_interactive, self.password)
|
248
256
|
self._pool = Pool()
|
249
257
|
self._init()
|
250
258
|
|
@@ -437,7 +445,7 @@ class BaseSSHClient(PollMixIn):
|
|
437
445
|
msg = "No remaining authentication methods"
|
438
446
|
logger.error(msg)
|
439
447
|
raise AuthenticationError(msg)
|
440
|
-
logger.debug("Private key auth failed, trying password")
|
448
|
+
logger.debug("Private key auth failed or not enabled, trying password")
|
441
449
|
self._password_auth()
|
442
450
|
|
443
451
|
def _agent_auth(self):
|
@@ -465,7 +473,7 @@ class BaseSSHClient(PollMixIn):
|
|
465
473
|
def open_session(self):
|
466
474
|
raise NotImplementedError
|
467
475
|
|
468
|
-
def _make_host_output(self, channel, encoding, read_timeout):
|
476
|
+
def _make_host_output(self, channel, encoding, read_timeout, fully_qualified_command=None):
|
469
477
|
_stdout_buffer = ConcurrentRWBuffer()
|
470
478
|
_stderr_buffer = ConcurrentRWBuffer()
|
471
479
|
_stdout_reader, _stderr_reader = self._make_output_readers(
|
@@ -478,7 +486,7 @@ class BaseSSHClient(PollMixIn):
|
|
478
486
|
host_out = HostOutput(
|
479
487
|
host=self.host, alias=self.alias, channel=channel, stdin=Stdin(channel, self),
|
480
488
|
client=self, encoding=encoding, read_timeout=read_timeout,
|
481
|
-
buffers=_buffers,
|
489
|
+
buffers=_buffers, fully_qualified_command=fully_qualified_command,
|
482
490
|
)
|
483
491
|
return host_out
|
484
492
|
|
@@ -629,7 +637,7 @@ class BaseSSHClient(PollMixIn):
|
|
629
637
|
with GTimeout(seconds=self.timeout):
|
630
638
|
channel = self._execute(_command, use_pty=use_pty)
|
631
639
|
_timeout = read_timeout if read_timeout else timeout
|
632
|
-
host_out = self._make_host_output(channel, encoding, _timeout)
|
640
|
+
host_out = self._make_host_output(channel, encoding, _timeout, fully_qualified_command=_command)
|
633
641
|
return host_out
|
634
642
|
|
635
643
|
def _make_sftp(self):
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# This file is part of parallel-ssh.
|
2
|
+
# Copyright (C) 2014-2025 Panos Kittenis.
|
3
|
+
# Copyright (C) 2014-2025 parallel-ssh Contributors.
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation, version 2.1.
|
8
|
+
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this library; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
17
|
+
|
18
|
+
import logging
|
19
|
+
import os
|
20
|
+
|
21
|
+
from ..exceptions import PKeyFileError, InvalidAPIUseError
|
22
|
+
|
23
|
+
logger = logging.getLogger('pssh')
|
24
|
+
|
25
|
+
|
26
|
+
def _validate_pkey_path(pkey):
|
27
|
+
if pkey is None:
|
28
|
+
return
|
29
|
+
pkey = os.path.normpath(os.path.expanduser(pkey))
|
30
|
+
if not os.path.exists(pkey):
|
31
|
+
msg = "File %s does not exist. " \
|
32
|
+
"Please use either absolute or relative to user directory " \
|
33
|
+
"paths like '~/.ssh/my_key' for pkey parameter"
|
34
|
+
ex = PKeyFileError(msg, pkey)
|
35
|
+
raise ex
|
36
|
+
return pkey
|
37
|
+
|
38
|
+
|
39
|
+
def _validate_pkey(pkey):
|
40
|
+
if pkey is None:
|
41
|
+
return
|
42
|
+
if isinstance(pkey, str):
|
43
|
+
return _validate_pkey_path(pkey)
|
44
|
+
return pkey
|
45
|
+
|
46
|
+
|
47
|
+
def _validate_api(keyboard_interactive, password):
|
48
|
+
if keyboard_interactive and not password:
|
49
|
+
msg = "Keyboard interactive authentication is enabled but no password is provided - cannot continue"
|
50
|
+
logger.error(msg)
|
51
|
+
raise InvalidAPIUseError(msg)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# This file is part of parallel-ssh.
|
2
|
+
# Copyright (C) 2014-2025 Panos Kittenis.
|
3
|
+
# Copyright (C) 2014-2025 parallel-ssh Contributors.
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation, version 2.1.
|
8
|
+
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this library; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
17
|
+
|
18
|
+
# flake8: noqa: F401
|
19
|
+
from .parallel import ParallelSSHClient
|
20
|
+
from .single import SSHClient, logger
|
@@ -1,19 +1,19 @@
|
|
1
|
-
#
|
1
|
+
# This file is part of parallel-ssh.
|
2
|
+
# Copyright (C) 2014-2025 Panos Kittenis.
|
3
|
+
# Copyright (C) 2014-2025 parallel-ssh Contributors.
|
2
4
|
#
|
3
|
-
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation, version 2.1.
|
4
8
|
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
8
13
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# Lesser General Public License for more details.
|
13
|
-
#
|
14
|
-
# You should have received a copy of the GNU Lesser General Public
|
15
|
-
# License along with this library; if not, write to the Free Software
|
16
|
-
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this library; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
17
17
|
|
18
18
|
import logging
|
19
19
|
|
@@ -37,6 +37,8 @@ class ParallelSSHClient(BaseParallelSSHClient):
|
|
37
37
|
forward_ssh_agent=False,
|
38
38
|
keepalive_seconds=60, identity_auth=True,
|
39
39
|
ipv6_only=False,
|
40
|
+
compress=False,
|
41
|
+
keyboard_interactive=False,
|
40
42
|
):
|
41
43
|
"""
|
42
44
|
:param hosts: Hosts to connect to
|
@@ -115,9 +117,17 @@ class ParallelSSHClient(BaseParallelSSHClient):
|
|
115
117
|
for the host(s) or raise NoIPv6AddressFoundError otherwise. Note this will
|
116
118
|
disable connecting to an IPv4 address if an IP address is provided instead.
|
117
119
|
:type ipv6_only: bool
|
120
|
+
:param compress: Enable/Disable compression on the client. Defaults to off.
|
121
|
+
:type compress: bool
|
122
|
+
:param keyboard_interactive: Enable/Disable keyboard interactive authentication with provided username and
|
123
|
+
password. An `InvalidAPIUse` error is raised when keyboard_interactive is enabled without a provided password.
|
124
|
+
Defaults to off.
|
125
|
+
:type keyboard_interactive: bool
|
118
126
|
|
119
127
|
:raises: :py:class:`pssh.exceptions.PKeyFileError` on errors finding
|
120
128
|
provided private key.
|
129
|
+
:raises: :py:class:`pssh.exceptions.InvalidAPIUseError` when `keyboard_interactive=True` with no password
|
130
|
+
provided.
|
121
131
|
"""
|
122
132
|
BaseParallelSSHClient.__init__(
|
123
133
|
self, hosts, user=user, password=password, port=port, pkey=pkey,
|
@@ -126,6 +136,8 @@ class ParallelSSHClient(BaseParallelSSHClient):
|
|
126
136
|
host_config=host_config, retry_delay=retry_delay,
|
127
137
|
identity_auth=identity_auth,
|
128
138
|
ipv6_only=ipv6_only,
|
139
|
+
compress=compress,
|
140
|
+
keyboard_interactive=keyboard_interactive,
|
129
141
|
)
|
130
142
|
self.proxy_host = proxy_host
|
131
143
|
self.proxy_port = proxy_port
|
@@ -232,6 +244,7 @@ class ParallelSSHClient(BaseParallelSSHClient):
|
|
232
244
|
keepalive_seconds=cfg.keepalive_seconds or self.keepalive_seconds,
|
233
245
|
identity_auth=cfg.identity_auth or self.identity_auth,
|
234
246
|
ipv6_only=cfg.ipv6_only or self.ipv6_only,
|
247
|
+
compress=cfg.compress or self.compress,
|
235
248
|
)
|
236
249
|
return _client
|
237
250
|
|
@@ -1,19 +1,19 @@
|
|
1
|
-
#
|
1
|
+
# This file is part of parallel-ssh.
|
2
|
+
# Copyright (C) 2014-2025 Panos Kittenis.
|
3
|
+
# Copyright (C) 2014-2025 parallel-ssh Contributors.
|
2
4
|
#
|
3
|
-
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation, version 2.1.
|
4
8
|
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
8
13
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# Lesser General Public License for more details.
|
13
|
-
#
|
14
|
-
# You should have received a copy of the GNU Lesser General Public
|
15
|
-
# License along with this library; if not, write to the Free Software
|
16
|
-
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this library; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
17
17
|
|
18
18
|
from collections import deque
|
19
19
|
|
@@ -27,7 +27,7 @@ from gevent.timeout import Timeout as GTimeout
|
|
27
27
|
from ssh2.error_codes import LIBSSH2_ERROR_EAGAIN
|
28
28
|
from ssh2.exceptions import SFTPHandleError, SFTPProtocolError, \
|
29
29
|
Timeout as SSH2Timeout
|
30
|
-
from ssh2.session import Session, LIBSSH2_SESSION_BLOCK_INBOUND, LIBSSH2_SESSION_BLOCK_OUTBOUND
|
30
|
+
from ssh2.session import Session, LIBSSH2_SESSION_BLOCK_INBOUND, LIBSSH2_SESSION_BLOCK_OUTBOUND, LIBSSH2_FLAG_COMPRESS
|
31
31
|
from ssh2.sftp import LIBSSH2_FXF_READ, LIBSSH2_FXF_CREAT, LIBSSH2_FXF_WRITE, \
|
32
32
|
LIBSSH2_FXF_TRUNC, LIBSSH2_SFTP_S_IRUSR, LIBSSH2_SFTP_S_IRGRP, \
|
33
33
|
LIBSSH2_SFTP_S_IWUSR, LIBSSH2_SFTP_S_IXUSR, LIBSSH2_SFTP_S_IROTH, \
|
@@ -110,6 +110,8 @@ class SSHClient(BaseSSHClient):
|
|
110
110
|
keepalive_seconds=60,
|
111
111
|
identity_auth=True,
|
112
112
|
ipv6_only=False,
|
113
|
+
compress=False,
|
114
|
+
keyboard_interactive=False,
|
113
115
|
):
|
114
116
|
"""
|
115
117
|
:param host: Host name or IP to connect to.
|
@@ -158,9 +160,17 @@ class SSHClient(BaseSSHClient):
|
|
158
160
|
for the host or raise NoIPv6AddressFoundError otherwise. Note this will
|
159
161
|
disable connecting to an IPv4 address if an IP address is provided instead.
|
160
162
|
:type ipv6_only: bool
|
163
|
+
:param compress: Enable/Disable compression on the client. Defaults to off.
|
164
|
+
:type compress: bool
|
165
|
+
:param keyboard_interactive: Enable/Disable keyboard interactive authentication with provided username and
|
166
|
+
password. An `InvalidAPIUse` error is raised when keyboard_interactive is enabled without a provided password.
|
167
|
+
Defaults to off.
|
168
|
+
:type keyboard_interactive: bool
|
161
169
|
|
162
170
|
:raises: :py:class:`pssh.exceptions.PKeyFileError` on errors finding
|
163
171
|
provided private key.
|
172
|
+
:raises: :py:class:`pssh.exceptions.InvalidAPIUseError` when `keyboard_interactive=True` with no password
|
173
|
+
provided.
|
164
174
|
"""
|
165
175
|
self.forward_ssh_agent = forward_ssh_agent
|
166
176
|
self._forward_requested = False
|
@@ -182,6 +192,8 @@ class SSHClient(BaseSSHClient):
|
|
182
192
|
timeout=timeout,
|
183
193
|
keepalive_seconds=keepalive_seconds,
|
184
194
|
identity_auth=identity_auth,
|
195
|
+
compress=compress,
|
196
|
+
keyboard_interactive=keyboard_interactive,
|
185
197
|
)
|
186
198
|
proxy_host = '127.0.0.1'
|
187
199
|
self._chan_stdout_lock = RLock()
|
@@ -194,6 +206,8 @@ class SSHClient(BaseSSHClient):
|
|
194
206
|
proxy_host=proxy_host, proxy_port=proxy_port,
|
195
207
|
identity_auth=identity_auth,
|
196
208
|
ipv6_only=ipv6_only,
|
209
|
+
compress=compress,
|
210
|
+
keyboard_interactive=keyboard_interactive,
|
197
211
|
)
|
198
212
|
|
199
213
|
def _shell(self, channel):
|
@@ -206,7 +220,10 @@ class SSHClient(BaseSSHClient):
|
|
206
220
|
allow_agent=True, timeout=None,
|
207
221
|
forward_ssh_agent=False,
|
208
222
|
keepalive_seconds=60,
|
209
|
-
identity_auth=True
|
223
|
+
identity_auth=True,
|
224
|
+
compress=False,
|
225
|
+
keyboard_interactive=False,
|
226
|
+
):
|
210
227
|
assert isinstance(self.port, int)
|
211
228
|
try:
|
212
229
|
self._proxy_client = SSHClient(
|
@@ -216,6 +233,8 @@ class SSHClient(BaseSSHClient):
|
|
216
233
|
timeout=timeout, forward_ssh_agent=forward_ssh_agent,
|
217
234
|
identity_auth=identity_auth,
|
218
235
|
keepalive_seconds=keepalive_seconds,
|
236
|
+
compress=compress,
|
237
|
+
keyboard_interactive=keyboard_interactive,
|
219
238
|
_auth_thread_pool=False)
|
220
239
|
except Exception as ex:
|
221
240
|
msg = "Proxy authentication failed. " \
|
@@ -263,6 +282,8 @@ class SSHClient(BaseSSHClient):
|
|
263
282
|
|
264
283
|
def _init_session(self, retries=1):
|
265
284
|
self.session = Session()
|
285
|
+
if self.compress:
|
286
|
+
self.session.flag(LIBSSH2_FLAG_COMPRESS)
|
266
287
|
|
267
288
|
if self.timeout:
|
268
289
|
# libssh2 timeout is in ms
|
@@ -310,7 +331,9 @@ class SSHClient(BaseSSHClient):
|
|
310
331
|
)
|
311
332
|
|
312
333
|
def _password_auth(self):
|
313
|
-
|
334
|
+
if self.keyboard_interactive:
|
335
|
+
return self.session.userauth_keyboardinteractive(self.user, self.password)
|
336
|
+
return self.session.userauth_password(self.user, self.password)
|
314
337
|
|
315
338
|
def _open_session(self):
|
316
339
|
chan = self.eagain(self.session.open_session)
|