From 690bae27c8df1a9c04ae0c0555e30db50c27c691 Mon Sep 17 00:00:00 2001 From: Gregory Thiemonge Date: Wed, 20 Nov 2019 11:33:56 +0100 Subject: [PATCH] Close ssh client after executing a command Each time ssh.Client.exec_command is called, _get_ssh_connection creates a new SSHClient instance. If this instance is not closed after command execution, it might cause a shortage of resources on the host (many open sockets, and dropbear processes in a cirros VM). This commit closes ssh client after processing the remote command. Closes-Bug: #1853264 Change-Id: If77f401d6d4a1282ce31a31bbd8827db34690e52 --- tempest/lib/common/ssh.py | 12 +++++++----- tempest/tests/lib/test_ssh.py | 15 ++++++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/tempest/lib/common/ssh.py b/tempest/lib/common/ssh.py index d4ec6ad594..2ac16057e3 100644 --- a/tempest/lib/common/ssh.py +++ b/tempest/lib/common/ssh.py @@ -196,11 +196,13 @@ class Client(object): exit_status = channel.recv_exit_status() - if 0 != exit_status: - raise exceptions.SSHExecCommandFailed( - command=cmd, exit_status=exit_status, - stderr=err_data, stdout=out_data) - return out_data + ssh.close() + + if 0 != exit_status: + raise exceptions.SSHExecCommandFailed( + command=cmd, exit_status=exit_status, + stderr=err_data, stdout=out_data) + return out_data def test_connection_auth(self): """Raises an exception when we can not connect to server via ssh.""" diff --git a/tempest/tests/lib/test_ssh.py b/tempest/tests/lib/test_ssh.py index 37fe64686b..c849231d7f 100644 --- a/tempest/tests/lib/test_ssh.py +++ b/tempest/tests/lib/test_ssh.py @@ -170,7 +170,8 @@ class TestSshClient(base.TestCase): @mock.patch('select.POLLIN', SELECT_POLLIN, create=True) def test_timeout_in_exec_command(self): - chan_mock, poll_mock, _ = self._set_mocks_for_select([0, 0, 0], True) + chan_mock, poll_mock, _, _ = ( + self._set_mocks_for_select([0, 0, 0], True)) # Test for a timeout condition immediately raised client = ssh.Client('localhost', 'root', timeout=2) @@ -187,7 +188,7 @@ class TestSshClient(base.TestCase): @mock.patch('select.POLLIN', SELECT_POLLIN, create=True) def test_exec_command(self): - chan_mock, poll_mock, select_mock = ( + chan_mock, poll_mock, select_mock, client_mock = ( self._set_mocks_for_select([[1, 0, 0]], True)) chan_mock.recv_exit_status.return_value = 0 @@ -211,6 +212,8 @@ class TestSshClient(base.TestCase): chan_mock.recv_stderr.assert_called_once_with(1024) chan_mock.recv_exit_status.assert_called_once_with() + client_mock.close.assert_called_once_with() + def _set_mocks_for_select(self, poll_data, ito_value=False): gsc_mock = self.patch('tempest.lib.common.ssh.Client.' '_get_ssh_connection') @@ -235,14 +238,15 @@ class TestSshClient(base.TestCase): else: poll_mock.poll.return_value = poll_data - return chan_mock, poll_mock, select_mock + return chan_mock, poll_mock, select_mock, client_mock _utf8_string = six.unichr(1071) _utf8_bytes = _utf8_string.encode("utf-8") @mock.patch('select.POLLIN', SELECT_POLLIN, create=True) def test_exec_good_command_output(self): - chan_mock, poll_mock, _ = self._set_mocks_for_select([1, 0, 0]) + chan_mock, poll_mock, _, _ = ( + self._set_mocks_for_select([1, 0, 0])) closed_prop = mock.PropertyMock(return_value=True) type(chan_mock).closed = closed_prop @@ -257,7 +261,8 @@ class TestSshClient(base.TestCase): @mock.patch('select.POLLIN', SELECT_POLLIN, create=True) def test_exec_bad_command_output(self): - chan_mock, poll_mock, _ = self._set_mocks_for_select([1, 0, 0]) + chan_mock, poll_mock, _, _ = ( + self._set_mocks_for_select([1, 0, 0])) closed_prop = mock.PropertyMock(return_value=True) type(chan_mock).closed = closed_prop