blob: 27cd6b53a031ced72202d6d3613bb8f5cff7e1cd [file] [log] [blame]
Jay Pipes8fe53922014-01-14 20:08:16 -05001# Copyright 2014 OpenStack Foundation
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may
4# not use this file except in compliance with the License. You may obtain
5# a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations
13# under the License.
14
15import contextlib
16import socket
Marc Kodereree3d7ce2014-05-17 09:35:46 -040017import time
Jay Pipes8fe53922014-01-14 20:08:16 -050018
19import mock
20import testtools
21
22from tempest.common import ssh
23from tempest import exceptions
24from tempest.tests import base
25
26
27class TestSshClient(base.TestCase):
28
29 def test_pkey_calls_paramiko_RSAKey(self):
30 with contextlib.nested(
31 mock.patch('paramiko.RSAKey.from_private_key'),
32 mock.patch('cStringIO.StringIO')) as (rsa_mock, cs_mock):
33 cs_mock.return_value = mock.sentinel.csio
34 pkey = 'mykey'
35 ssh.Client('localhost', 'root', pkey=pkey)
36 rsa_mock.assert_called_once_with(mock.sentinel.csio)
37 cs_mock.assert_called_once_with('mykey')
38 rsa_mock.reset_mock()
ChangBo Guo(gcb)b4e00962014-05-14 13:52:08 +080039 cs_mock.reset_mock()
Jay Pipes8fe53922014-01-14 20:08:16 -050040 pkey = mock.sentinel.pkey
41 # Shouldn't call out to load a file from RSAKey, since
42 # a sentinel isn't a basestring...
43 ssh.Client('localhost', 'root', pkey=pkey)
ChangBo Guo(gcb)b4e00962014-05-14 13:52:08 +080044 self.assertEqual(0, rsa_mock.call_count)
45 self.assertEqual(0, cs_mock.call_count)
Jay Pipes8fe53922014-01-14 20:08:16 -050046
Marc Kodereree3d7ce2014-05-17 09:35:46 -040047 def _set_ssh_connection_mocks(self):
48 client_mock = mock.MagicMock()
49 client_mock.connect.return_value = True
50 return (self.patch('paramiko.SSHClient'),
51 self.patch('paramiko.AutoAddPolicy'),
52 client_mock)
Jay Pipes8fe53922014-01-14 20:08:16 -050053
Marc Kodereree3d7ce2014-05-17 09:35:46 -040054 def test_get_ssh_connection(self):
55 c_mock, aa_mock, client_mock = self._set_ssh_connection_mocks()
56 s_mock = self.patch('time.sleep')
57
58 c_mock.return_value = client_mock
Jay Pipes8fe53922014-01-14 20:08:16 -050059 aa_mock.return_value = mock.sentinel.aa
60
Jay Pipes8fe53922014-01-14 20:08:16 -050061 # Test normal case for successful connection on first try
Jay Pipes8fe53922014-01-14 20:08:16 -050062 client = ssh.Client('localhost', 'root', timeout=2)
63 client._get_ssh_connection(sleep=1)
64
65 aa_mock.assert_called_once_with()
66 client_mock.set_missing_host_key_policy.assert_called_once_with(
67 mock.sentinel.aa)
68 expected_connect = [mock.call(
69 'localhost',
70 username='root',
71 pkey=None,
72 key_filename=None,
73 look_for_keys=False,
74 timeout=10.0,
75 password=None
76 )]
77 self.assertEqual(expected_connect, client_mock.connect.mock_calls)
ChangBo Guo(gcb)b4e00962014-05-14 13:52:08 +080078 self.assertEqual(0, s_mock.call_count)
Jay Pipes8fe53922014-01-14 20:08:16 -050079
Marc Kodereree3d7ce2014-05-17 09:35:46 -040080 def test_get_ssh_connection_two_attemps(self):
81 c_mock, aa_mock, client_mock = self._set_ssh_connection_mocks()
Jay Pipes8fe53922014-01-14 20:08:16 -050082
Marc Kodereree3d7ce2014-05-17 09:35:46 -040083 c_mock.return_value = client_mock
84 client_mock.connect.side_effect = [
85 socket.error,
86 mock.MagicMock()
Jay Pipes8fe53922014-01-14 20:08:16 -050087 ]
88
Marc Kodereree3d7ce2014-05-17 09:35:46 -040089 client = ssh.Client('localhost', 'root', timeout=1)
90 start_time = int(time.time())
Jay Pipes8fe53922014-01-14 20:08:16 -050091 client._get_ssh_connection(sleep=1)
Marc Kodereree3d7ce2014-05-17 09:35:46 -040092 end_time = int(time.time())
Sean Daguea5149582014-06-20 08:20:05 -040093 self.assertLess((end_time - start_time), 4)
94 self.assertGreater((end_time - start_time), 1)
Jay Pipes8fe53922014-01-14 20:08:16 -050095
Marc Kodereree3d7ce2014-05-17 09:35:46 -040096 def test_get_ssh_connection_timeout(self):
97 c_mock, aa_mock, client_mock = self._set_ssh_connection_mocks()
Jay Pipes8fe53922014-01-14 20:08:16 -050098
Marc Kodereree3d7ce2014-05-17 09:35:46 -040099 c_mock.return_value = client_mock
Jay Pipes8fe53922014-01-14 20:08:16 -0500100 client_mock.connect.side_effect = [
101 socket.error,
102 socket.error,
Marc Kodereree3d7ce2014-05-17 09:35:46 -0400103 socket.error,
Jay Pipes8fe53922014-01-14 20:08:16 -0500104 ]
105
Marc Kodereree3d7ce2014-05-17 09:35:46 -0400106 client = ssh.Client('localhost', 'root', timeout=2)
107 start_time = int(time.time())
Jay Pipes8fe53922014-01-14 20:08:16 -0500108 with testtools.ExpectedException(exceptions.SSHTimeout):
109 client._get_ssh_connection()
Marc Kodereree3d7ce2014-05-17 09:35:46 -0400110 end_time = int(time.time())
Sean Daguea5149582014-06-20 08:20:05 -0400111 self.assertLess((end_time - start_time), 5)
112 self.assertGreaterEqual((end_time - start_time), 2)
Jay Pipes8fe53922014-01-14 20:08:16 -0500113
114 def test_exec_command(self):
115 gsc_mock = self.patch('tempest.common.ssh.Client._get_ssh_connection')
116 ito_mock = self.patch('tempest.common.ssh.Client._is_timed_out')
117 select_mock = self.patch('select.poll')
118
119 client_mock = mock.MagicMock()
120 tran_mock = mock.MagicMock()
121 chan_mock = mock.MagicMock()
122 poll_mock = mock.MagicMock()
123
124 def reset_mocks():
125 gsc_mock.reset_mock()
126 ito_mock.reset_mock()
127 select_mock.reset_mock()
128 poll_mock.reset_mock()
129 client_mock.reset_mock()
130 tran_mock.reset_mock()
131 chan_mock.reset_mock()
132
133 select_mock.return_value = poll_mock
134 gsc_mock.return_value = client_mock
135 ito_mock.return_value = True
136 client_mock.get_transport.return_value = tran_mock
137 tran_mock.open_session.return_value = chan_mock
138 poll_mock.poll.side_effect = [
139 [0, 0, 0]
140 ]
141
142 # Test for a timeout condition immediately raised
143 client = ssh.Client('localhost', 'root', timeout=2)
144 with testtools.ExpectedException(exceptions.TimeoutException):
145 client.exec_command("test")
146
147 chan_mock.fileno.assert_called_once_with()
148 chan_mock.exec_command.assert_called_once_with("test")
149 chan_mock.shutdown_write.assert_called_once_with()
150
151 SELECT_POLLIN = 1
152 poll_mock.register.assert_called_once_with(chan_mock, SELECT_POLLIN)
153 poll_mock.poll.assert_called_once_with(10)
154
155 # Test for proper reading of STDOUT and STDERROR and closing
156 # of all file descriptors.
157
158 reset_mocks()
159
160 select_mock.return_value = poll_mock
161 gsc_mock.return_value = client_mock
162 ito_mock.return_value = False
163 client_mock.get_transport.return_value = tran_mock
164 tran_mock.open_session.return_value = chan_mock
165 poll_mock.poll.side_effect = [
166 [1, 0, 0]
167 ]
168 closed_prop = mock.PropertyMock(return_value=True)
169 type(chan_mock).closed = closed_prop
170 chan_mock.recv_exit_status.return_value = 0
171 chan_mock.recv.return_value = ''
172 chan_mock.recv_stderr.return_value = ''
173
174 client = ssh.Client('localhost', 'root', timeout=2)
175 client.exec_command("test")
176
177 chan_mock.fileno.assert_called_once_with()
178 chan_mock.exec_command.assert_called_once_with("test")
179 chan_mock.shutdown_write.assert_called_once_with()
180
181 SELECT_POLLIN = 1
182 poll_mock.register.assert_called_once_with(chan_mock, SELECT_POLLIN)
183 poll_mock.poll.assert_called_once_with(10)
184 chan_mock.recv_ready.assert_called_once_with()
185 chan_mock.recv.assert_called_once_with(1024)
186 chan_mock.recv_stderr_ready.assert_called_once_with()
187 chan_mock.recv_stderr.assert_called_once_with(1024)
188 chan_mock.recv_exit_status.assert_called_once_with()
189 closed_prop.assert_called_once_with()