Skip to content

Commit a98114e

Browse files
authored
Host err (#323)
* Always use host name in HostOutput when stop errors is False. * Updated exception host handling, tests * Updated readme * Updated documentation, minor updates * Updated tests, tunnel func
1 parent dbf6573 commit a98114e

File tree

12 files changed

+42
-80
lines changed

12 files changed

+42
-80
lines changed

Changelog.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ Changes
1111
* Added ``ipv6_only`` flag to ``ParallelSSHClient`` and ``SSHClient`` for choosing only IPv6 addresses when both v4 and
1212
v6 are available.
1313
* Removed Python 2 from binary wheel compatibility as it is no longer supported and not guaranteed to work.
14+
* Host name is now an argument for all exceptions raised by single clients.
15+
16+
Fixes
17+
-----
18+
19+
* ``HostOutput`` would have empty host on some exceptions when ``stop_on_errors`` is ``False`` - #297
1420

1521
2.6.0
1622
+++++

pssh/clients/base/parallel.py

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ def _open_shell(self, host_i, host,
105105
encoding=encoding, read_timeout=read_timeout)
106106
return shell
107107
except (GTimeout, Exception) as ex:
108-
host = ex.host if hasattr(ex, 'host') else None
109108
logger.error("Failed to run on host %s - %s", host, ex)
110109
raise ex
111110

@@ -199,17 +198,17 @@ def run_command(self, command, user=None, stop_on_errors=True,
199198
return self._get_output_from_cmds(cmds, raise_error=stop_on_errors)
200199

201200
def _get_output_from_cmds(self, cmds, raise_error=False):
202-
_cmds = [spawn(self._get_output_from_greenlet, cmd, raise_error=raise_error)
203-
for cmd in cmds]
201+
_cmds = [spawn(self._get_output_from_greenlet, cmd_i, cmd, raise_error=raise_error)
202+
for cmd_i, cmd in enumerate(cmds)]
204203
finished = joinall(_cmds, raise_error=True)
205204
return [f.get() for f in finished]
206205

207-
def _get_output_from_greenlet(self, cmd, raise_error=False):
206+
def _get_output_from_greenlet(self, cmd_i, cmd, raise_error=False):
207+
host = self.hosts[cmd_i]
208208
try:
209209
host_out = cmd.get()
210210
return host_out
211211
except (GTimeout, Exception) as ex:
212-
host = ex.host if hasattr(ex, 'host') else None
213212
if isinstance(ex, GTimeout):
214213
ex = Timeout()
215214
if raise_error:
@@ -266,7 +265,6 @@ def _run_command(self, host_i, host, command, sudo=False, user=None,
266265
use_pty=use_pty, encoding=encoding, read_timeout=read_timeout)
267266
return host_out
268267
except (GTimeout, Exception) as ex:
269-
host = ex.host if hasattr(ex, 'host') else None
270268
logger.error("Failed to run on host %s - %s", host, ex)
271269
raise ex
272270

@@ -312,7 +310,7 @@ def join(self, output=None, consume_output=False, timeout=None):
312310
self.pool.
313311
Since self.timeout is passed onto each individual SSH session it is
314312
**not** used for any parallel functions like `run_command` or `join`.
315-
:type timeout: int
313+
:type timeout: float
316314
317315
:raises: :py:class:`pssh.exceptions.Timeout` on timeout requested and
318316
reached with commands still running.
@@ -431,13 +429,9 @@ def copy_file(self, local_file, remote_file, recurse=False, copy_args=None):
431429

432430
def _copy_file(self, host_i, host, local_file, remote_file, recurse=False):
433431
"""Make sftp client, copy file"""
434-
try:
435-
self._make_ssh_client(host_i, host)
436-
return self._host_clients[(host_i, host)].copy_file(
437-
local_file, remote_file, recurse=recurse)
438-
except Exception as ex:
439-
ex.host = host
440-
raise ex
432+
client = self._make_ssh_client(host_i, host)
433+
return client.copy_file(
434+
local_file, remote_file, recurse=recurse)
441435

442436
def copy_remote_file(self, remote_file, local_file, recurse=False,
443437
suffix_separator='_', copy_args=None, **kwargs):
@@ -518,19 +512,14 @@ def copy_remote_file(self, remote_file, local_file, recurse=False,
518512
def _copy_remote_file(self, host_i, host, remote_file, local_file, recurse,
519513
**kwargs):
520514
"""Make sftp client, copy file to local"""
521-
try:
522-
self._make_ssh_client(host_i, host)
523-
return self._host_clients[(host_i, host)].copy_remote_file(
524-
remote_file, local_file, recurse=recurse, **kwargs)
525-
except Exception as ex:
526-
ex.host = host
527-
raise ex
515+
client = self._make_ssh_client(host_i, host)
516+
return client.copy_remote_file(
517+
remote_file, local_file, recurse=recurse, **kwargs)
528518

529519
def _handle_greenlet_exc(self, func, host, *args, **kwargs):
530520
try:
531521
return func(*args, **kwargs)
532522
except Exception as ex:
533-
ex.host = host
534523
raise ex
535524

536525
def _make_ssh_client(self, host_i, host):

pssh/clients/base/single.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,8 +299,6 @@ def _connect(self, host, port, retries=1):
299299
"Error connecting to host '%s:%s' - %s - retry %s/%s",
300300
host, port, str(error_type), retries,
301301
self.num_retries,)
302-
ex.host = host
303-
ex.port = port
304302
raise ex
305303

306304
def _identity_auth(self):

pssh/clients/common.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,5 @@ def _validate_pkey_path(pkey, host=None):
2929
"Please use either absolute or relative to user directory " \
3030
"paths like '~/.ssh/my_key' for pkey parameter"
3131
ex = PKeyFileError(msg, pkey)
32-
ex.host = host
3332
raise ex
3433
return pkey

pssh/clients/native/parallel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def __init__(self, hosts, user=None, password=None, port=22, pkey=None,
8282
:type pool_size: int
8383
:param host_config: (Optional) Per-host configuration for cases where
8484
not all hosts use the same configuration.
85-
:type host_config: list
85+
:type host_config: list(:py:class:`pssh.config.HostConfig`)
8686
:param allow_agent: (Optional) set to False to disable connecting to
8787
the system's SSH agent.
8888
:type allow_agent: bool

pssh/clients/native/single.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,6 @@ def _init_session(self, retries=1):
225225
logger.error(msg, self.host, self.port, ex)
226226
if isinstance(ex, SSH2Timeout):
227227
raise Timeout(msg, self.host, self.port, ex)
228-
ex.host = self.host
229-
ex.port = self.port
230228
raise
231229

232230
def _keepalive(self):
@@ -483,9 +481,9 @@ def copy_remote_file(self, remote_file, local_file, recurse=False,
483481
try:
484482
self._eagain(sftp.stat, remote_file)
485483
except (SFTPHandleError, SFTPProtocolError):
486-
msg = "Remote file or directory %s does not exist"
487-
logger.error(msg, remote_file)
488-
raise SFTPIOError(msg, remote_file)
484+
msg = "Remote file or directory %s on host %s does not exist"
485+
logger.error(msg, remote_file, self.host)
486+
raise SFTPIOError(msg, remote_file, self.host)
489487
try:
490488
dir_h = self._sftp_openfh(sftp.opendir, remote_file)
491489
except SFTPError:

pssh/clients/native/tunnel.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,7 @@ def run(self):
112112
continue
113113
self._start_server()
114114
except Exception:
115-
logger.error("Tunnel thread caught exception and will exit:",
116-
exc_info=1)
115+
logger.exception("Tunnel thread caught exception and will exit:")
117116
self.shutdown()
118117

119118
def cleanup_server(self, client):
@@ -141,6 +140,7 @@ def __init__(self, client, host, port, bind_address='127.0.0.1',
141140
self._client = client
142141
self._retries = num_retries
143142
self.bind_address = bind_address
143+
self.exception = None
144144

145145
@property
146146
def listen_port(self):
@@ -164,9 +164,9 @@ def _read_rw(self, socket, address):
164164
logger.debug("Waiting for read/write greenlets")
165165
self._source_let = source
166166
self._dest_let = dest
167-
self._wait_send_receive_lets(source, dest, channel, socket)
167+
self._wait_send_receive_lets(source, dest, channel)
168168

169-
def _wait_send_receive_lets(self, source, dest, channel, forward_sock):
169+
def _wait_send_receive_lets(self, source, dest, channel):
170170
try:
171171
joinall((source, dest), raise_error=True)
172172
finally:

pssh/clients/ssh/parallel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def __init__(self, hosts, user=None, password=None, port=22, pkey=None,
8989
:type pool_size: int
9090
:param host_config: (Optional) Per-host configuration for cases where
9191
not all hosts use the same configuration.
92-
:type host_config: dict
92+
:type host_config: list(:py:class:`pssh.config.HostConfig`)
9393
:param allow_agent: (Optional) set to False to disable connecting to
9494
the system's SSH agent. Currently unused - always off.
9595
:type allow_agent: bool

pssh/clients/ssh/single.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,6 @@ def _init_session(self, retries=1):
157157
return self._connect_init_session_retry(retries=retries+1)
158158
msg = "Error connecting to host %s:%s - %s"
159159
logger.error(msg, self.host, self.port, ex)
160-
ex.host = self.host
161-
ex.port = self.port
162160
raise ex
163161

164162
def _session_connect(self):

tests/native/test_parallel_client.py

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ def test_pssh_client_hosts_list_part_failure(self):
270270
self.assertTrue(client.finished(output))
271271
self.assertEqual(len(hosts), len(output))
272272
self.assertIsNotNone(output[1].exception)
273-
self.assertEqual(output[1].exception.host, hosts[1])
273+
self.assertEqual(output[1].exception.args[1], hosts[1])
274274
self.assertIsInstance(output[1].exception, ConnectionErrorException)
275275

276276
def test_pssh_client_timeout(self):
@@ -374,7 +374,7 @@ def test_sftp_exceptions(self):
374374
try:
375375
cmd.get()
376376
except Exception as ex:
377-
self.assertEqual(ex.host, self.host)
377+
self.assertEqual(ex.args[1], self.host)
378378
self.assertIsInstance(ex, ConnectionErrorException)
379379
else:
380380
raise Exception("Expected ConnectionErrorException, got none")
@@ -582,7 +582,7 @@ def test_pssh_copy_remote_file_failure(self):
582582
try:
583583
cmds[0].get()
584584
except Exception as ex:
585-
self.assertEqual(ex.host, self.host)
585+
self.assertEqual(ex.args[2], self.host)
586586
self.assertIsInstance(ex, SFTPIOError)
587587
else:
588588
raise Exception("Expected SFTPIOError, got none")
@@ -871,12 +871,8 @@ def test_connection_error_exception(self):
871871
try:
872872
raise output[0].exception
873873
except ConnectionErrorException as ex:
874-
self.assertEqual(ex.host, host,
875-
msg="Exception host argument is %s, should be %s" % (
876-
ex.host, host,))
877-
self.assertEqual(ex.args[2], port,
878-
msg="Exception port argument is %s, should be %s" % (
879-
ex.args[2], port,))
874+
self.assertEqual(ex.args[1], host)
875+
self.assertEqual(ex.args[2], port)
880876
else:
881877
raise Exception("Expected ConnectionErrorException")
882878

@@ -950,7 +946,7 @@ def test_host_config(self):
950946
try:
951947
raise output[1].exception
952948
except PKeyFileError as ex:
953-
self.assertEqual(ex.host, host)
949+
self.assertEqual(output[1].host, hosts[1][0])
954950
else:
955951
raise AssertionError("Expected ValueError on host %s",
956952
hosts[0][0])
@@ -988,7 +984,7 @@ def test_host_config_list_type(self):
988984
try:
989985
raise output[1].exception
990986
except PKeyFileError as ex:
991-
self.assertEqual(ex.host, host)
987+
self.assertEqual(output[1].host, hosts[1][0])
992988
else:
993989
raise AssertionError("Expected ValueError on host %s",
994990
hosts[0][0])
@@ -1293,15 +1289,6 @@ def test_unknown_host_failure(self):
12931289
num_retries=1)
12941290
self.assertRaises(UnknownHostException, client.run_command, self.cmd)
12951291

1296-
def test_open_channel_failure(self):
1297-
client = ParallelSSHClient([self.host], port=self.port,
1298-
pkey=self.user_key)
1299-
output = client.run_command(self.cmd)
1300-
client.join(output)
1301-
output[0].client.session.disconnect()
1302-
self.assertRaises(SessionError, output[0].client.open_session)
1303-
self.assertEqual(output[0].exit_code, 0)
1304-
13051292
def test_invalid_host_out(self):
13061293
output = {'blah': None}
13071294
self.assertRaises(ValueError, self.client.join, output)
@@ -1621,7 +1608,7 @@ def test_scp_recv_failure(self):
16211608
try:
16221609
joinall(cmds, raise_error=True)
16231610
except Exception as ex:
1624-
self.assertEqual(ex.host, self.host)
1611+
self.assertEqual(ex.args[2], self.host)
16251612
self.assertIsInstance(ex, SCPError)
16261613
else:
16271614
raise Exception("Expected SCPError, got none")
@@ -1912,7 +1899,7 @@ def test_no_ipv6(self):
19121899
client = ParallelSSHClient([self.host], port=self.port, pkey=self.user_key, num_retries=1, ipv6_only=True)
19131900
output = client.run_command(self.cmd, stop_on_errors=False)
19141901
for host_out in output:
1915-
# self.assertEqual(self.host, host_out.host)
1902+
self.assertEqual(self.host, host_out.host)
19161903
self.assertIsInstance(host_out.exception, NoIPv6AddressFoundError)
19171904

19181905
# TODO:

0 commit comments

Comments
 (0)