diff --git a/trollmoves/movers.py b/trollmoves/movers.py index 5361b4e1..ab5b1349 100644 --- a/trollmoves/movers.py +++ b/trollmoves/movers.py @@ -23,6 +23,7 @@ """Movers for the move_it scripts.""" +from doctest import ELLIPSIS_MARKER import logging import os import shutil @@ -35,7 +36,7 @@ from ftplib import FTP, all_errors, error_perm from paramiko import SSHClient, SSHException, AutoAddPolicy -from scp import SCPClient +from scp import SCPClient, SCPException try: from s3fs import S3FileSystem except ImportError: @@ -358,7 +359,8 @@ def copy(self): self._dest_username) try: - scp = SCPClient(ssh_connection.get_transport()) + scp = SCPClient(ssh_connection.get_transport(), + socket_timeout=int(self.attrs.get('scpclient_timeout_seconds', 10))) except Exception as err: LOGGER.error("Failed to initiate SCPClient: %s", str(err)) ssh_connection.close() @@ -374,6 +376,13 @@ def copy(self): else: LOGGER.error("OSError in scp.put: %s", str(osex)) raise + except SCPException as scpe: + if str(scpe) in "Timeout waiting for scp response": + LOGGER.error("SCPClient put got a socket timeout. You could add scpclient_timeout_seconds " + "to your config to increase the timeout interval. Default timeout is 10 seconds.") + else: + LOGGER.error("SCPException: %s", str(scpe)) + raise except Exception as err: LOGGER.error("Something went wrong with scp: %s", str(err)) LOGGER.error("Exception name %s", type(err).__name__) diff --git a/trollmoves/tests/test_ssh_server.py b/trollmoves/tests/test_ssh_server.py index 51411348..bb0be4db 100644 --- a/trollmoves/tests/test_ssh_server.py +++ b/trollmoves/tests/test_ssh_server.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # -# Copyright (c) 2019 +# Copyright (c) 2019, 2022 # # Author(s): # @@ -54,6 +54,7 @@ def setUp(self): self._attrs_empty = {} self._attrs_connection_uptime = {'connection_uptime': 0} + self._attrs_timeout = {'scpclient_timeout_seconds': 1} def tearDown(self): try: @@ -178,6 +179,18 @@ def test_scp_copy(self, mock_scp_client, mock_sshclient): mocked_scp_client.put.assert_called_once_with(self.origin, urlparse(self.destination_no_port).path) + @patch('trollmoves.movers.SSHClient', autospec=True) + @patch('trollmoves.movers.SCPClient', autospec=True) + def test_scp_copy_custom_timeout(self, mock_scp_client, mock_sshclient): + """Check scp copy.""" + mocked_scp_client = MagicMock() + mock_scp_client.return_value = mocked_scp_client + + scp_mover = trollmoves.movers.ScpMover(self.origin, self.destination_no_port, attrs=self._attrs_timeout) + scp_mover.copy() + + mocked_scp_client.put.assert_called_once_with(self.origin, urlparse(self.destination_no_port).path) + @patch('trollmoves.movers.SSHClient', autospec=True) @patch('trollmoves.movers.SCPClient', autospec=True) def test_scp_copy_generic_exception(self, mock_scp_client, mock_sshclient): @@ -216,6 +229,26 @@ def test_scp_copy_put_exception(self, mock_scp_client): with pytest.raises(Exception): scp_mover.copy() + @patch('trollmoves.movers.SCPClient', autospec=True) + def test_scp_copy_put_SCPException(self, mock_scp_client): + """Check scp client.put() raising SCPException.""" + from scp import SCPException + scp_mover = trollmoves.movers.ScpMover(self.origin, self.destination_no_port, attrs=self._attrs_empty) + mock_scp_client.return_value.put.side_effect = SCPException('Timeout waiting for scp response') + + with pytest.raises(SCPException, match='Timeout waiting for scp response'): + scp_mover.copy() + + @patch('trollmoves.movers.SCPClient', autospec=True) + def test_scp_copy_put_SCPException2(self, mock_scp_client): + """Check scp client.put() raising SCPException.""" + from scp import SCPException + scp_mover = trollmoves.movers.ScpMover(self.origin, self.destination_no_port, attrs=self._attrs_empty) + mock_scp_client.return_value.put.side_effect = SCPException('Other exception') + + with pytest.raises(SCPException, match='Other exception'): + scp_mover.copy() + @patch('trollmoves.movers.SSHClient', autospec=True) @patch('trollmoves.movers.SCPClient', autospec=True) def test_scp_move(self, mock_scp_client, mock_sshclient):