summaryrefslogtreecommitdiffstats
path: root/src/pybind/mgr/cephadm/tests/test_ssh.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/pybind/mgr/cephadm/tests/test_ssh.py')
-rw-r--r--src/pybind/mgr/cephadm/tests/test_ssh.py105
1 files changed, 105 insertions, 0 deletions
diff --git a/src/pybind/mgr/cephadm/tests/test_ssh.py b/src/pybind/mgr/cephadm/tests/test_ssh.py
new file mode 100644
index 000000000..29f01b6c7
--- /dev/null
+++ b/src/pybind/mgr/cephadm/tests/test_ssh.py
@@ -0,0 +1,105 @@
+import asyncssh
+from asyncssh.process import SSHCompletedProcess
+from unittest import mock
+try:
+ # AsyncMock was not added until python 3.8
+ from unittest.mock import AsyncMock
+except ImportError:
+ from asyncmock import AsyncMock
+except ImportError:
+ AsyncMock = None
+import pytest
+
+
+try:
+ from asyncssh.misc import ConnectionLost
+except ImportError:
+ ConnectionLost = None
+
+from ceph.deployment.hostspec import HostSpec
+
+from cephadm import CephadmOrchestrator
+from cephadm.serve import CephadmServe
+from cephadm.tests.fixtures import with_host, wait, async_side_effect
+from orchestrator import OrchestratorError
+
+
+@pytest.mark.skipif(ConnectionLost is None, reason='no asyncssh')
+class TestWithSSH:
+ @mock.patch("cephadm.ssh.SSHManager._execute_command")
+ @mock.patch("cephadm.ssh.SSHManager._check_execute_command")
+ def test_offline(self, check_execute_command, execute_command, cephadm_module):
+ check_execute_command.side_effect = async_side_effect('')
+ execute_command.side_effect = async_side_effect(('', '', 0))
+
+ if not AsyncMock:
+ # can't run this test if we could not import AsyncMock
+ return
+ mock_connect = AsyncMock(return_value='')
+ with mock.patch("asyncssh.connect", new=mock_connect) as asyncssh_connect:
+ with with_host(cephadm_module, 'test'):
+ asyncssh_connect.side_effect = ConnectionLost('reason')
+ code, out, err = cephadm_module.check_host('test')
+ assert out == ''
+ assert "Failed to connect to test at address (1::4)" in err
+
+ out = wait(cephadm_module, cephadm_module.get_hosts())[0].to_json()
+ assert out == HostSpec('test', '1::4', status='Offline').to_json()
+
+ asyncssh_connect.return_value = mock.MagicMock()
+ asyncssh_connect.side_effect = None
+ assert CephadmServe(cephadm_module)._check_host('test') is None
+ out = wait(cephadm_module, cephadm_module.get_hosts())[0].to_json()
+ assert out == HostSpec('test', '1::4').to_json()
+
+ def test_ssh_remote_cmds_execution(self, cephadm_module):
+
+ if not AsyncMock:
+ # can't run this test if we could not import AsyncMock
+ return
+
+ class FakeConn:
+ def __init__(self, exception=None, returncode=0):
+ self.exception = exception
+ self.returncode = returncode
+
+ async def run(self, *args, **kwargs):
+ if self.exception:
+ raise self.exception
+ else:
+ return SSHCompletedProcess(returncode=self.returncode, stdout="", stderr="")
+
+ async def close(self):
+ pass
+
+ def run_test(host, conn, expected_error):
+ mock_connect = AsyncMock(return_value=conn)
+ with pytest.raises(OrchestratorError, match=expected_error):
+ with mock.patch("asyncssh.connect", new=mock_connect):
+ with with_host(cephadm_module, host):
+ CephadmServe(cephadm_module)._check_host(host)
+
+ # Test case 1: command failure
+ run_test('test1', FakeConn(returncode=1), "Command .+ failed")
+
+ # Test case 2: connection error
+ run_test('test2', FakeConn(exception=asyncssh.ChannelOpenError(1, "", "")), "Unable to reach remote host test2.")
+
+ # Test case 3: asyncssh ProcessError
+ stderr = "my-process-stderr"
+ run_test('test3', FakeConn(exception=asyncssh.ProcessError(returncode=3,
+ env="",
+ command="",
+ subsystem="",
+ exit_status="",
+ exit_signal="",
+ stderr=stderr,
+ stdout="")), f"Cannot execute the command.+{stderr}")
+ # Test case 4: generic error
+ run_test('test4', FakeConn(exception=Exception), "Generic error while executing command.+")
+
+
+@pytest.mark.skipif(ConnectionLost is not None, reason='asyncssh')
+class TestWithoutSSH:
+ def test_can_run(self, cephadm_module: CephadmOrchestrator):
+ assert cephadm_module.can_run() == (False, "loading asyncssh library:No module named 'asyncssh'")