bug fix: ssh authentiation error when run compass scenario in fuel
[yardstick.git] / tests / unit / test_ssh.py
1 # Copyright 2013: Mirantis Inc.
2 # All Rights Reserved.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15
16 # yardstick comment: this file is a modified copy of
17 # rally/tests/unit/common/test_sshutils.py
18
19 import os
20 import unittest
21 import mock
22
23 from yardstick import ssh
24
25
26 class FakeParamikoException(Exception):
27     pass
28
29
30 class SSHTestCase(unittest.TestCase):
31     """Test all small SSH methods."""
32
33     def setUp(self):
34         super(SSHTestCase, self).setUp()
35         self.test_client = ssh.SSH("root", "example.net")
36
37     @mock.patch("yardstick.ssh.SSH._get_pkey")
38     def test_construct(self, mock_ssh__get_pkey):
39         mock_ssh__get_pkey.return_value = "pkey"
40         test_ssh = ssh.SSH("root", "example.net", port=33, pkey="key",
41                            key_filename="kf", password="secret")
42         mock_ssh__get_pkey.assert_called_once_with("key")
43         self.assertEqual("root", test_ssh.user)
44         self.assertEqual("example.net", test_ssh.host)
45         self.assertEqual(33, test_ssh.port)
46         self.assertEqual("pkey", test_ssh.pkey)
47         self.assertEqual("kf", test_ssh.key_filename)
48         self.assertEqual("secret", test_ssh.password)
49
50     def test_construct_default(self):
51         self.assertEqual("root", self.test_client.user)
52         self.assertEqual("example.net", self.test_client.host)
53         self.assertEqual(22, self.test_client.port)
54         self.assertIsNone(self.test_client.pkey)
55         self.assertIsNone(self.test_client.key_filename)
56         self.assertIsNone(self.test_client.password)
57
58     @mock.patch("yardstick.ssh.paramiko")
59     def test__get_pkey_invalid(self, mock_paramiko):
60         mock_paramiko.SSHException = FakeParamikoException
61         rsa = mock_paramiko.rsakey.RSAKey
62         dss = mock_paramiko.dsskey.DSSKey
63         rsa.from_private_key.side_effect = mock_paramiko.SSHException
64         dss.from_private_key.side_effect = mock_paramiko.SSHException
65         self.assertRaises(ssh.SSHError, self.test_client._get_pkey, "key")
66
67     @mock.patch("yardstick.ssh.six.moves.StringIO")
68     @mock.patch("yardstick.ssh.paramiko")
69     def test__get_pkey_dss(self, mock_paramiko, mock_string_io):
70         mock_paramiko.SSHException = FakeParamikoException
71         mock_string_io.return_value = "string_key"
72         mock_paramiko.dsskey.DSSKey.from_private_key.return_value = "dss_key"
73         rsa = mock_paramiko.rsakey.RSAKey
74         rsa.from_private_key.side_effect = mock_paramiko.SSHException
75         key = self.test_client._get_pkey("key")
76         dss_calls = mock_paramiko.dsskey.DSSKey.from_private_key.mock_calls
77         self.assertEqual([mock.call("string_key")], dss_calls)
78         self.assertEqual(key, "dss_key")
79         mock_string_io.assert_called_once_with("key")
80
81     @mock.patch("yardstick.ssh.six.moves.StringIO")
82     @mock.patch("yardstick.ssh.paramiko")
83     def test__get_pkey_rsa(self, mock_paramiko, mock_string_io):
84         mock_paramiko.SSHException = FakeParamikoException
85         mock_string_io.return_value = "string_key"
86         mock_paramiko.rsakey.RSAKey.from_private_key.return_value = "rsa_key"
87         dss = mock_paramiko.dsskey.DSSKey
88         dss.from_private_key.side_effect = mock_paramiko.SSHException
89         key = self.test_client._get_pkey("key")
90         rsa_calls = mock_paramiko.rsakey.RSAKey.from_private_key.mock_calls
91         self.assertEqual([mock.call("string_key")], rsa_calls)
92         self.assertEqual(key, "rsa_key")
93         mock_string_io.assert_called_once_with("key")
94
95     @mock.patch("yardstick.ssh.SSH._get_pkey")
96     @mock.patch("yardstick.ssh.paramiko")
97     def test__get_client(self, mock_paramiko, mock_ssh__get_pkey):
98         mock_ssh__get_pkey.return_value = "key"
99         fake_client = mock.Mock()
100         mock_paramiko.SSHClient.return_value = fake_client
101         mock_paramiko.AutoAddPolicy.return_value = "autoadd"
102
103         test_ssh = ssh.SSH("admin", "example.net", pkey="key")
104         client = test_ssh._get_client()
105
106         self.assertEqual(fake_client, client)
107         client_calls = [
108             mock.call.set_missing_host_key_policy("autoadd"),
109             mock.call.connect("example.net", username="admin",
110                               port=22, pkey="key", key_filename=None,
111                               password=None,
112                               allow_agent=False, look_for_keys=False,
113                               timeout=1),
114         ]
115         self.assertEqual(client_calls, client.mock_calls)
116
117     def test_close(self):
118         with mock.patch.object(self.test_client, "_client") as m_client:
119             self.test_client.close()
120         m_client.close.assert_called_once_with()
121         self.assertFalse(self.test_client._client)
122
123     @mock.patch("yardstick.ssh.six.moves.StringIO")
124     def test_execute(self, mock_string_io):
125         mock_string_io.side_effect = stdio = [mock.Mock(), mock.Mock()]
126         stdio[0].read.return_value = "stdout fake data"
127         stdio[1].read.return_value = "stderr fake data"
128         with mock.patch.object(self.test_client, "run", return_value=0)\
129                 as mock_run:
130             status, stdout, stderr = self.test_client.execute(
131                 "cmd",
132                 stdin="fake_stdin",
133                 timeout=43)
134         mock_run.assert_called_once_with(
135             "cmd", stdin="fake_stdin", stdout=stdio[0],
136             stderr=stdio[1], timeout=43, raise_on_error=False)
137         self.assertEqual(0, status)
138         self.assertEqual("stdout fake data", stdout)
139         self.assertEqual("stderr fake data", stderr)
140
141     @mock.patch("yardstick.ssh.time")
142     def test_wait_timeout(self, mock_time):
143         mock_time.time.side_effect = [1, 50, 150]
144         self.test_client.execute = mock.Mock(side_effect=[ssh.SSHError,
145                                                           ssh.SSHError,
146                                                           0])
147         self.assertRaises(ssh.SSHTimeout, self.test_client.wait)
148         self.assertEqual([mock.call("uname")] * 2,
149                          self.test_client.execute.mock_calls)
150
151     @mock.patch("yardstick.ssh.time")
152     def test_wait(self, mock_time):
153         mock_time.time.side_effect = [1, 50, 100]
154         self.test_client.execute = mock.Mock(side_effect=[ssh.SSHError,
155                                                           ssh.SSHError,
156                                                           0])
157         self.test_client.wait()
158         self.assertEqual([mock.call("uname")] * 3,
159                          self.test_client.execute.mock_calls)
160
161     @mock.patch("yardstick.ssh.paramiko")
162     def test_send_command(self, mock_paramiko):
163         paramiko_sshclient = self.test_client._get_client()
164         with mock.patch.object(paramiko_sshclient, "exec_command") \
165             as mock_paramiko_exec_command:
166             self.test_client.send_command('cmd')
167         mock_paramiko_exec_command.assert_called_once_with('cmd',
168                                                             get_pty=True)
169
170
171 class SSHRunTestCase(unittest.TestCase):
172     """Test SSH.run method in different aspects.
173
174     Also tested method "execute".
175     """
176
177     def setUp(self):
178         super(SSHRunTestCase, self).setUp()
179
180         self.fake_client = mock.Mock()
181         self.fake_session = mock.Mock()
182         self.fake_transport = mock.Mock()
183
184         self.fake_transport.open_session.return_value = self.fake_session
185         self.fake_client.get_transport.return_value = self.fake_transport
186
187         self.fake_session.recv_ready.return_value = False
188         self.fake_session.recv_stderr_ready.return_value = False
189         self.fake_session.send_ready.return_value = False
190         self.fake_session.exit_status_ready.return_value = True
191         self.fake_session.recv_exit_status.return_value = 0
192
193         self.test_client = ssh.SSH("admin", "example.net")
194         self.test_client._get_client = mock.Mock(return_value=self.fake_client)
195
196     @mock.patch("yardstick.ssh.select")
197     def test_execute(self, mock_select):
198         mock_select.select.return_value = ([], [], [])
199         self.fake_session.recv_ready.side_effect = [1, 0, 0]
200         self.fake_session.recv_stderr_ready.side_effect = [1, 0]
201         self.fake_session.recv.return_value = "ok"
202         self.fake_session.recv_stderr.return_value = "error"
203         self.fake_session.exit_status_ready.return_value = 1
204         self.fake_session.recv_exit_status.return_value = 127
205         self.assertEqual((127, "ok", "error"), self.test_client.execute("cmd"))
206         self.fake_session.exec_command.assert_called_once_with("cmd")
207
208     @mock.patch("yardstick.ssh.select")
209     def test_execute_args(self, mock_select):
210         mock_select.select.return_value = ([], [], [])
211         self.fake_session.recv_ready.side_effect = [1, 0, 0]
212         self.fake_session.recv_stderr_ready.side_effect = [1, 0]
213         self.fake_session.recv.return_value = "ok"
214         self.fake_session.recv_stderr.return_value = "error"
215         self.fake_session.exit_status_ready.return_value = 1
216         self.fake_session.recv_exit_status.return_value = 127
217
218         result = self.test_client.execute("cmd arg1 'arg2 with space'")
219         self.assertEqual((127, "ok", "error"), result)
220         self.fake_session.exec_command.assert_called_once_with(
221             "cmd arg1 'arg2 with space'")
222
223     @mock.patch("yardstick.ssh.select")
224     def test_run(self, mock_select):
225         mock_select.select.return_value = ([], [], [])
226         self.assertEqual(0, self.test_client.run("cmd"))
227
228     @mock.patch("yardstick.ssh.select")
229     def test_run_nonzero_status(self, mock_select):
230         mock_select.select.return_value = ([], [], [])
231         self.fake_session.recv_exit_status.return_value = 1
232         self.assertRaises(ssh.SSHError, self.test_client.run, "cmd")
233         self.assertEqual(1, self.test_client.run("cmd", raise_on_error=False))
234
235     @mock.patch("yardstick.ssh.select")
236     def test_run_stdout(self, mock_select):
237         mock_select.select.return_value = ([], [], [])
238         self.fake_session.recv_ready.side_effect = [True, True, False]
239         self.fake_session.recv.side_effect = ["ok1", "ok2"]
240         stdout = mock.Mock()
241         self.test_client.run("cmd", stdout=stdout)
242         self.assertEqual([mock.call("ok1"), mock.call("ok2")],
243                          stdout.write.mock_calls)
244
245     @mock.patch("yardstick.ssh.select")
246     def test_run_stderr(self, mock_select):
247         mock_select.select.return_value = ([], [], [])
248         self.fake_session.recv_stderr_ready.side_effect = [True, False]
249         self.fake_session.recv_stderr.return_value = "error"
250         stderr = mock.Mock()
251         self.test_client.run("cmd", stderr=stderr)
252         stderr.write.assert_called_once_with("error")
253
254     @mock.patch("yardstick.ssh.select")
255     def test_run_stdin(self, mock_select):
256         """Test run method with stdin.
257
258         Third send call was called with "e2" because only 3 bytes was sent
259         by second call. So remainig 2 bytes of "line2" was sent by third call.
260         """
261         mock_select.select.return_value = ([], [], [])
262         self.fake_session.exit_status_ready.side_effect = [0, 0, 0, True]
263         self.fake_session.send_ready.return_value = True
264         self.fake_session.send.side_effect = [5, 3, 2]
265         fake_stdin = mock.Mock()
266         fake_stdin.read.side_effect = ["line1", "line2", ""]
267         fake_stdin.closed = False
268
269         def close():
270             fake_stdin.closed = True
271         fake_stdin.close = mock.Mock(side_effect=close)
272         self.test_client.run("cmd", stdin=fake_stdin)
273         call = mock.call
274         send_calls = [call("line1"), call("line2"), call("e2")]
275         self.assertEqual(send_calls, self.fake_session.send.mock_calls)
276
277     @mock.patch("yardstick.ssh.select")
278     def test_run_select_error(self, mock_select):
279         self.fake_session.exit_status_ready.return_value = False
280         mock_select.select.return_value = ([], [], [True])
281         self.assertRaises(ssh.SSHError, self.test_client.run, "cmd")
282
283     @mock.patch("yardstick.ssh.time")
284     @mock.patch("yardstick.ssh.select")
285     def test_run_timemout(self, mock_select, mock_time):
286         mock_time.time.side_effect = [1, 3700]
287         mock_select.select.return_value = ([], [], [])
288         self.fake_session.exit_status_ready.return_value = False
289         self.assertRaises(ssh.SSHTimeout, self.test_client.run, "cmd")
290
291
292 def main():
293     unittest.main()
294
295 if __name__ == '__main__':
296     main()