blob: 429ed5614a19ebdfd9e1c3b488e940474083b7f8 [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
17
18import mock
19import testtools
20
21from tempest.common import ssh
22from tempest import exceptions
23from tempest.tests import base
24
25
26class TestSshClient(base.TestCase):
27
28 def test_pkey_calls_paramiko_RSAKey(self):
29 with contextlib.nested(
30 mock.patch('paramiko.RSAKey.from_private_key'),
31 mock.patch('cStringIO.StringIO')) as (rsa_mock, cs_mock):
32 cs_mock.return_value = mock.sentinel.csio
33 pkey = 'mykey'
34 ssh.Client('localhost', 'root', pkey=pkey)
35 rsa_mock.assert_called_once_with(mock.sentinel.csio)
36 cs_mock.assert_called_once_with('mykey')
37 rsa_mock.reset_mock()
38 cs_mock.rest_mock()
39 pkey = mock.sentinel.pkey
40 # Shouldn't call out to load a file from RSAKey, since
41 # a sentinel isn't a basestring...
42 ssh.Client('localhost', 'root', pkey=pkey)
43 rsa_mock.assert_not_called()
44 cs_mock.assert_not_called()
45
46 def test_get_ssh_connection(self):
47 c_mock = self.patch('paramiko.SSHClient')
48 aa_mock = self.patch('paramiko.AutoAddPolicy')
49 s_mock = self.patch('time.sleep')
50 t_mock = self.patch('time.time')
51
52 aa_mock.return_value = mock.sentinel.aa
53
54 def reset_mocks():
55 aa_mock.reset_mock()
56 c_mock.reset_mock()
57 s_mock.reset_mock()
58 t_mock.reset_mock()
59
60 # Test normal case for successful connection on first try
61 client_mock = mock.MagicMock()
62 c_mock.return_value = client_mock
63 client_mock.connect.return_value = True
64
65 client = ssh.Client('localhost', 'root', timeout=2)
66 client._get_ssh_connection(sleep=1)
67
68 aa_mock.assert_called_once_with()
69 client_mock.set_missing_host_key_policy.assert_called_once_with(
70 mock.sentinel.aa)
71 expected_connect = [mock.call(
72 'localhost',
73 username='root',
74 pkey=None,
75 key_filename=None,
76 look_for_keys=False,
77 timeout=10.0,
78 password=None
79 )]
80 self.assertEqual(expected_connect, client_mock.connect.mock_calls)
81 s_mock.assert_not_called()
82 t_mock.assert_called_once_with()
83
84 reset_mocks()
85
86 # Test case when connection fails on first two tries and
87 # succeeds on third try (this validates retry logic)
88 client_mock.connect.side_effect = [socket.error, socket.error, True]
89 t_mock.side_effect = [
90 1000, # Start time
91 1001, # Sleep loop 1
92 1002 # Sleep loop 2
93 ]
94
95 client._get_ssh_connection(sleep=1)
96
97 expected_sleeps = [
98 mock.call(1),
99 mock.call(1.01)
100 ]
101 self.assertEqual(expected_sleeps, s_mock.mock_calls)
102
103 reset_mocks()
104
105 # Test case when connection fails on first three tries and
106 # exceeds the timeout, so expect to raise a Timeout exception
107 client_mock.connect.side_effect = [
108 socket.error,
109 socket.error,
110 socket.error
111 ]
112 t_mock.side_effect = [
113 1000, # Start time
114 1001, # Sleep loop 1
115 1002, # Sleep loop 2
116 1003, # Sleep loop 3
117 1004 # LOG.error() calls time.time()
118 ]
119
120 with testtools.ExpectedException(exceptions.SSHTimeout):
121 client._get_ssh_connection()
122
123 def test_exec_command(self):
124 gsc_mock = self.patch('tempest.common.ssh.Client._get_ssh_connection')
125 ito_mock = self.patch('tempest.common.ssh.Client._is_timed_out')
126 select_mock = self.patch('select.poll')
127
128 client_mock = mock.MagicMock()
129 tran_mock = mock.MagicMock()
130 chan_mock = mock.MagicMock()
131 poll_mock = mock.MagicMock()
132
133 def reset_mocks():
134 gsc_mock.reset_mock()
135 ito_mock.reset_mock()
136 select_mock.reset_mock()
137 poll_mock.reset_mock()
138 client_mock.reset_mock()
139 tran_mock.reset_mock()
140 chan_mock.reset_mock()
141
142 select_mock.return_value = poll_mock
143 gsc_mock.return_value = client_mock
144 ito_mock.return_value = True
145 client_mock.get_transport.return_value = tran_mock
146 tran_mock.open_session.return_value = chan_mock
147 poll_mock.poll.side_effect = [
148 [0, 0, 0]
149 ]
150
151 # Test for a timeout condition immediately raised
152 client = ssh.Client('localhost', 'root', timeout=2)
153 with testtools.ExpectedException(exceptions.TimeoutException):
154 client.exec_command("test")
155
156 chan_mock.fileno.assert_called_once_with()
157 chan_mock.exec_command.assert_called_once_with("test")
158 chan_mock.shutdown_write.assert_called_once_with()
159
160 SELECT_POLLIN = 1
161 poll_mock.register.assert_called_once_with(chan_mock, SELECT_POLLIN)
162 poll_mock.poll.assert_called_once_with(10)
163
164 # Test for proper reading of STDOUT and STDERROR and closing
165 # of all file descriptors.
166
167 reset_mocks()
168
169 select_mock.return_value = poll_mock
170 gsc_mock.return_value = client_mock
171 ito_mock.return_value = False
172 client_mock.get_transport.return_value = tran_mock
173 tran_mock.open_session.return_value = chan_mock
174 poll_mock.poll.side_effect = [
175 [1, 0, 0]
176 ]
177 closed_prop = mock.PropertyMock(return_value=True)
178 type(chan_mock).closed = closed_prop
179 chan_mock.recv_exit_status.return_value = 0
180 chan_mock.recv.return_value = ''
181 chan_mock.recv_stderr.return_value = ''
182
183 client = ssh.Client('localhost', 'root', timeout=2)
184 client.exec_command("test")
185
186 chan_mock.fileno.assert_called_once_with()
187 chan_mock.exec_command.assert_called_once_with("test")
188 chan_mock.shutdown_write.assert_called_once_with()
189
190 SELECT_POLLIN = 1
191 poll_mock.register.assert_called_once_with(chan_mock, SELECT_POLLIN)
192 poll_mock.poll.assert_called_once_with(10)
193 chan_mock.recv_ready.assert_called_once_with()
194 chan_mock.recv.assert_called_once_with(1024)
195 chan_mock.recv_stderr_ready.assert_called_once_with()
196 chan_mock.recv_stderr.assert_called_once_with(1024)
197 chan_mock.recv_exit_status.assert_called_once_with()
198 closed_prop.assert_called_once_with()