Skip to content

Commit a62c7b0

Browse files
author
Dan
committed
Added extra paramiko keyword argument passing from parallel and single host SSH clients. Updated docstrings. Added test for paramiko extra argument passing from parallel client
1 parent a6bd540 commit a62c7b0

File tree

4 files changed

+44
-21
lines changed

4 files changed

+44
-21
lines changed

pssh/pssh_client.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ def __init__(self, hosts, user=None, password=None, port=None, pkey=None,
369369

370370
def run_command(self, command, sudo=False, user=None, stop_on_errors=True,
371371
shell=None, use_shell=True, use_pty=True, host_args=None,
372-
encoding='utf-8'):
372+
encoding='utf-8', **paramiko_kwargs):
373373
"""Run command on all hosts in parallel, honoring self.pool_size,
374374
and return output buffers.
375375
@@ -420,6 +420,9 @@ def run_command(self, command, sudo=False, user=None, stop_on_errors=True,
420420
:param encoding: Encoding to use for output. Must be valid
421421
`Python codec <https://docs.python.org/2.7/library/codecs.html>`_
422422
:type encoding: str
423+
:param paramiko_kwargs: (Optional) Extra keyword arguments to be
424+
passed on to :py:func:`paramiko.client.SSHClient.connect`
425+
:type paramiko_kwargs: dict
423426
424427
:rtype: Dictionary with host as key and
425428
:py:class:`pssh.output.HostOutput` as value as per
@@ -667,7 +670,8 @@ def run_command(self, command, sudo=False, user=None, stop_on_errors=True,
667670
cmds = [self.pool.spawn(self._exec_command, host,
668671
command % host_args[host_i],
669672
sudo=sudo, user=user, shell=shell,
670-
use_shell=use_shell, use_pty=use_pty)
673+
use_shell=use_shell, use_pty=use_pty,
674+
**paramiko_kwargs)
671675
for host_i, host in enumerate(self.hosts)]
672676
except IndexError:
673677
raise HostArgumentException(
@@ -677,7 +681,8 @@ def run_command(self, command, sudo=False, user=None, stop_on_errors=True,
677681
cmds = [self.pool.spawn(
678682
self._exec_command, host, command,
679683
sudo=sudo, user=user, shell=shell,
680-
use_shell=use_shell, use_pty=use_pty)
684+
use_shell=use_shell, use_pty=use_pty,
685+
**paramiko_kwargs)
681686
for host in self.hosts]
682687
for cmd in cmds:
683688
try:
@@ -696,7 +701,8 @@ def _get_host_config_values(self, host):
696701
return _user, _port, _password, _pkey
697702

698703
def _exec_command(self, host, command, sudo=False, user=None,
699-
shell=None, use_shell=True, use_pty=True):
704+
shell=None, use_shell=True, use_pty=True,
705+
**paramiko_kwargs):
700706
"""Make SSHClient, run command on host"""
701707
if host not in self.host_clients or self.host_clients[host] is None:
702708
_user, _port, _password, _pkey = self._get_host_config_values(host)
@@ -708,7 +714,8 @@ def _exec_command(self, host, command, sudo=False, user=None,
708714
proxy_host=self.proxy_host, proxy_port=self.proxy_port,
709715
proxy_user=self.proxy_user, proxy_password=self.proxy_password,
710716
proxy_pkey=self.proxy_pkey, allow_agent=self.allow_agent,
711-
agent=self.agent, channel_timeout=self.channel_timeout)
717+
agent=self.agent, channel_timeout=self.channel_timeout,
718+
**paramiko_kwargs)
712719
return self.host_clients[host].exec_command(
713720
command, sudo=sudo, user=user, shell=shell,
714721
use_shell=use_shell, use_pty=use_pty)

pssh/ssh_client.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ def __init__(self, host,
4848
allow_agent=True, timeout=10, proxy_host=None,
4949
proxy_port=22, proxy_user=None, proxy_password=None,
5050
proxy_pkey=None, channel_timeout=None,
51-
_openssh_config_file=None):
51+
_openssh_config_file=None,
52+
**paramiko_kwargs):
5253
"""
5354
:param host: Hostname to connect to
5455
:type host: str
@@ -95,6 +96,9 @@ def __init__(self, host,
9596
:param allow_agent: (Optional) set to False to disable connecting to
9697
the SSH agent
9798
:type allow_agent: bool
99+
:param paramiko_kwargs: (Optional) Extra keyword arguments to be
100+
passed on to :py:func:`paramiko.client.SSHClient.connect`
101+
:type paramiko_kwargs: dict
98102
"""
99103
try:
100104
host, _user, _port, _pkey = read_openssh_config(
@@ -125,11 +129,11 @@ def __init__(self, host,
125129
logger.debug(
126130
"Proxy configured for destination host %s - Proxy host: %s:%s",
127131
self.host, self.proxy_host, self.proxy_port,)
128-
self._connect_tunnel()
132+
self._connect_tunnel(**paramiko_kwargs)
129133
else:
130-
self._connect(self.client, self.host, self.port)
134+
self._connect(self.client, self.host, self.port, **paramiko_kwargs)
131135

132-
def _connect_tunnel(self):
136+
def _connect_tunnel(self, **paramiko_kwargs):
133137
"""Connects to SSH server via an intermediate SSH tunnel server.
134138
client (me) -> tunnel (ssh server to proxy through) ->
135139
``self.host`` (ssh server to run command)
@@ -142,7 +146,7 @@ def _connect_tunnel(self):
142146
paramiko.MissingHostKeyPolicy())
143147
self._connect(self.proxy_client, self.proxy_host, self.proxy_port,
144148
user=self.proxy_user, password=self.proxy_password,
145-
pkey=self.proxy_pkey)
149+
pkey=self.proxy_pkey, **paramiko_kwargs)
146150
logger.info("Connecting via SSH proxy %s:%s -> %s:%s", self.proxy_host,
147151
self.proxy_port, self.host, self.port,)
148152
try:
@@ -151,15 +155,17 @@ def _connect_tunnel(self):
151155
timeout=self.timeout)
152156
sleep(0)
153157
return self._connect(self.client, self.host, self.port,
154-
sock=proxy_channel)
158+
sock=proxy_channel,
159+
**paramiko_kwargs)
155160
except (ChannelException, paramiko.SSHException) as ex:
156161
error_type = ex.args[1] if len(ex.args) > 1 else ex.args[0]
157162
raise ConnectionErrorException(
158163
"Error connecting to host '%s:%s' - %s",
159164
self.host, self.port, str(error_type))
160165

161166
def _connect(self, client, host, port, sock=None, retries=1,
162-
user=None, password=None, pkey=None):
167+
user=None, password=None, pkey=None,
168+
**paramiko_kwargs):
163169
"""Connect to host
164170
165171
:raises: :py:class:`pssh.exceptions.AuthenticationException`
@@ -172,18 +178,22 @@ def _connect(self, client, host, port, sock=None, retries=1,
172178
SSH errors
173179
"""
174180
try:
175-
client.connect(host, username=user if user else self.user,
181+
client.connect(host,
182+
username=user if user else self.user,
176183
password=password if password else self.password,
177184
port=port, pkey=pkey if pkey else self.pkey,
178185
sock=sock, timeout=self.timeout,
179-
allow_agent=self.allow_agent)
186+
allow_agent=self.allow_agent,
187+
**paramiko_kwargs)
180188
except sock_gaierror as ex:
181189
logger.error("Could not resolve host '%s' - retry %s/%s",
182190
host, retries, self.num_retries)
183191
while retries < self.num_retries:
184192
sleep(5)
185-
return self._connect(client, host, port, sock=sock,
186-
retries=retries+1)
193+
return self._connect(client, host, port,
194+
sock=sock,
195+
retries=retries+1,
196+
**paramiko_kwargs)
187197
raise UnknownHostException("Unknown host %s - %s - retry %s/%s",
188198
host, str(ex.args[1]), retries,
189199
self.num_retries)
@@ -192,8 +202,10 @@ def _connect(self, client, host, port, sock=None, retries=1,
192202
self.host, self.port, retries, self.num_retries)
193203
while retries < self.num_retries:
194204
sleep(5)
195-
return self._connect(client, host, port, sock=sock,
196-
retries=retries+1)
205+
return self._connect(client, host, port,
206+
sock=sock,
207+
retries=retries+1,
208+
**paramiko_kwargs)
197209
error_type = ex.args[1] if len(ex.args) > 1 else ex.args[0]
198210
raise ConnectionErrorException(
199211
"Error connecting to host '%s:%s' - %s - retry %s/%s",
@@ -234,9 +246,6 @@ def exec_command(self, command, sudo=False, user=None,
234246
being where a shell is not used and/or stdout/stderr/stdin buffers
235247
are not required. Defaults to ``True``
236248
:type use_pty: bool
237-
:param kwargs: (Optional) Keyword arguments to be passed to remote
238-
command
239-
:type kwargs: dict
240249
:rtype: Tuple of `(channel, hostname, stdout, stderr, stdin)`.
241250
Channel is the remote SSH channel, needed to ensure all of stdout has
242251
been got, hostname is remote hostname the copy is to, stdout and

requirements_dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ flake8
33
sphinx
44
sphinx_rtd_theme
55
nose
6+
python-gssapi
67
-e .

tests/test_pssh_client.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,6 +1051,12 @@ def test_run_command_no_shell(self):
10511051
self.assertTrue(len(stdout) > 0)
10521052
self.assertTrue(output[self.host].exit_code == 0)
10531053

1054+
def test_extra_paramiko_args(self):
1055+
output = self.client.run_command('id', gss_auth=True, gss_kex=True,
1056+
gss_host='gss_host')
1057+
trans = self.client.host_clients[self.host].client.get_transport()
1058+
self.assertEqual(trans.gss_host, 'gss_host')
1059+
10541060
def test_proxy_remote_host_failure_timeout(self):
10551061
"""Test that timeout setting is passed on to proxy to be used for the
10561062
proxy->remote host connection timeout

0 commit comments

Comments
 (0)