Do not generate the ganeti_pub_keys file with --no-ssh-init
[ganeti-github.git] / test / py / ganeti.client.gnt_cluster_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2011 Google Inc.
5 # All rights reserved.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
9 # met:
10 #
11 # 1. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
13 #
14 # 2. Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in the
16 # documentation and/or other materials provided with the distribution.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19 # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
31 """Script for testing ganeti.client.gnt_cluster"""
32
33 import unittest
34 import optparse
35 import os
36 import shutil
37 import tempfile
38
39 from ganeti import errors
40 from ganeti.client import gnt_cluster
41 from ganeti import utils
42 from ganeti import compat
43 from ganeti import constants
44 from ganeti import ssh
45 from ganeti import cli
46
47 import mock
48 import testutils
49
50
51 class TestEpoUtilities(unittest.TestCase):
52 def setUp(self):
53 self.nodes2ip = dict(("node%s" % i, "192.0.2.%s" % i) for i in range(1, 10))
54 self.nodes = set(self.nodes2ip.keys())
55 self.ips2node = dict((v, k) for (k, v) in self.nodes2ip.items())
56
57 def _FakeAction(*args):
58 return True
59
60 def _FakePing(ip, port, live_port_needed=False):
61 self.assert_(live_port_needed)
62 self.assertEqual(port, 0)
63 return True
64
65 def _FakeSleep(secs):
66 self.assert_(secs >= 0 and secs <= 5)
67 return
68
69 def _NoopFeedback(self, text):
70 return
71
72 def testPingFnRemoveHostsUp(self):
73 seen = set()
74 def _FakeSeenPing(ip, *args, **kwargs):
75 node = self.ips2node[ip]
76 self.assertFalse(node in seen)
77 seen.add(node)
78 return True
79
80 helper = gnt_cluster._RunWhenNodesReachableHelper(self.nodes,
81 self._FakeAction,
82 self.nodes2ip, 0,
83 self._NoopFeedback,
84 _ping_fn=_FakeSeenPing,
85 _sleep_fn=self._FakeSleep)
86
87 nodes_len = len(self.nodes)
88 for (num, _) in enumerate(self.nodes):
89 helper.Wait(5)
90 if num < nodes_len - 1:
91 self.assertRaises(utils.RetryAgain, helper)
92 else:
93 helper()
94
95 self.assertEqual(seen, self.nodes)
96 self.assertFalse(helper.down)
97 self.assertEqual(helper.up, self.nodes)
98
99 def testActionReturnFalseSetsHelperFalse(self):
100 called = False
101 def _FalseAction(*args):
102 return called
103
104 helper = gnt_cluster._RunWhenNodesReachableHelper(self.nodes, _FalseAction,
105 self.nodes2ip, 0,
106 self._NoopFeedback,
107 _ping_fn=self._FakePing,
108 _sleep_fn=self._FakeSleep)
109 for _ in self.nodes:
110 try:
111 helper()
112 except utils.RetryAgain:
113 called = True
114
115 self.assertFalse(helper.success)
116
117 def testMaybeInstanceStartup(self):
118 instances_arg = []
119 def _FakeInstanceStart(opts, instances, start):
120 instances_arg.append(set(instances))
121 return None
122
123 inst_map = {
124 "inst1": set(["node1", "node2"]),
125 "inst2": set(["node1", "node3"]),
126 "inst3": set(["node2", "node1"]),
127 "inst4": set(["node2", "node1", "node3"]),
128 "inst5": set(["node4"]),
129 }
130
131 fn = _FakeInstanceStart
132 self.assert_(gnt_cluster._MaybeInstanceStartup(None, inst_map, set(),
133 _instance_start_fn=fn))
134 self.assertFalse(instances_arg)
135 result = gnt_cluster._MaybeInstanceStartup(None, inst_map, set(["node1"]),
136 _instance_start_fn=fn)
137 self.assert_(result)
138 self.assertFalse(instances_arg)
139 result = gnt_cluster._MaybeInstanceStartup(None, inst_map,
140 set(["node1", "node3"]),
141 _instance_start_fn=fn)
142 self.assert_(result is None)
143 self.assertEqual(instances_arg.pop(0), set(["inst2"]))
144 self.assertFalse("inst2" in inst_map)
145 result = gnt_cluster._MaybeInstanceStartup(None, inst_map,
146 set(["node1", "node3"]),
147 _instance_start_fn=fn)
148 self.assert_(result)
149 self.assertFalse(instances_arg)
150 result = gnt_cluster._MaybeInstanceStartup(None, inst_map,
151 set(["node1", "node3", "node2"]),
152 _instance_start_fn=fn)
153 self.assertEqual(instances_arg.pop(0), set(["inst1", "inst3", "inst4"]))
154 self.assert_(result is None)
155 result = gnt_cluster._MaybeInstanceStartup(None, inst_map,
156 set(["node1", "node3", "node2",
157 "node4"]),
158 _instance_start_fn=fn)
159 self.assert_(result is None)
160 self.assertEqual(instances_arg.pop(0), set(["inst5"]))
161 self.assertFalse(inst_map)
162
163
164 class _ClientForEpo:
165 def __init__(self, groups, nodes):
166 self._groups = groups
167 self._nodes = nodes
168
169 def QueryGroups(self, names, fields, use_locking):
170 assert not use_locking
171 assert fields == ["node_list"]
172 return self._groups
173
174 def QueryNodes(self, names, fields, use_locking):
175 assert not use_locking
176 assert fields == ["name", "master", "pinst_list", "sinst_list", "powered",
177 "offline"]
178 return self._nodes
179
180
181 class TestEpo(unittest.TestCase):
182 _ON_EXITCODE = 253
183 _OFF_EXITCODE = 254
184
185 def _ConfirmForce(self, *args):
186 self.fail("Shouldn't need confirmation")
187
188 def _Confirm(self, exp_names, result, names, ltype, text):
189 self.assertEqual(names, exp_names)
190 self.assertFalse(result is NotImplemented)
191 return result
192
193 def _Off(self, exp_node_list, opts, node_list, inst_map):
194 self.assertEqual(node_list, exp_node_list)
195 self.assertFalse(inst_map)
196 return self._OFF_EXITCODE
197
198 def _Test(self, *args, **kwargs):
199 defaults = dict(qcl=NotImplemented, _on_fn=NotImplemented,
200 _off_fn=NotImplemented,
201 _stdout_fn=lambda *args: None,
202 _stderr_fn=lambda *args: None)
203 defaults.update(kwargs)
204 return gnt_cluster.Epo(*args, **defaults)
205
206 def testShowAllWithGroups(self):
207 opts = optparse.Values(dict(groups=True, show_all=True))
208 result = self._Test(opts, NotImplemented)
209 self.assertEqual(result, constants.EXIT_FAILURE)
210
211 def testShowAllWithArgs(self):
212 opts = optparse.Values(dict(groups=False, show_all=True))
213 result = self._Test(opts, ["a", "b", "c"])
214 self.assertEqual(result, constants.EXIT_FAILURE)
215
216 def testNoArgumentsNoParameters(self):
217 for (force, confirm_result) in [(True, NotImplemented), (False, False),
218 (False, True)]:
219 opts = optparse.Values(dict(groups=False, show_all=False, force=force,
220 on=False))
221 client = _ClientForEpo(NotImplemented, [
222 ("node1.example.com", False, [], [], True, False),
223 ])
224
225 if force:
226 confirm_fn = self._ConfirmForce
227 else:
228 confirm_fn = compat.partial(self._Confirm, ["node1.example.com"],
229 confirm_result)
230
231 off_fn = compat.partial(self._Off, ["node1.example.com"])
232
233 result = self._Test(opts, [], qcl=client, _off_fn=off_fn,
234 _confirm_fn=confirm_fn)
235 if force or confirm_result:
236 self.assertEqual(result, self._OFF_EXITCODE)
237 else:
238 self.assertEqual(result, constants.EXIT_FAILURE)
239
240 def testPowerOn(self):
241 for master in [False, True]:
242 opts = optparse.Values(dict(groups=False, show_all=True,
243 force=True, on=True))
244 client = _ClientForEpo(NotImplemented, [
245 ("node1.example.com", False, [], [], True, False),
246 ("node2.example.com", False, [], [], False, False),
247 ("node3.example.com", False, [], [], True, True),
248 ("node4.example.com", False, [], [], None, True),
249 ("node5.example.com", master, [], [], False, False),
250 ])
251
252 def _On(_, all_nodes, node_list, inst_map):
253 self.assertEqual(all_nodes,
254 ["node%s.example.com" % i for i in range(1, 6)])
255 if master:
256 self.assertEqual(node_list, ["node2.example.com"])
257 else:
258 self.assertEqual(node_list, ["node2.example.com",
259 "node5.example.com"])
260 self.assertFalse(inst_map)
261 return self._ON_EXITCODE
262
263 result = self._Test(opts, [], qcl=client, _on_fn=_On,
264 _confirm_fn=self._ConfirmForce)
265 self.assertEqual(result, self._ON_EXITCODE)
266
267 def testMasterWithoutShowAll(self):
268 opts = optparse.Values(dict(groups=False, show_all=False,
269 force=True, on=False))
270 client = _ClientForEpo(NotImplemented, [
271 ("node1.example.com", True, [], [], True, False),
272 ])
273 result = self._Test(opts, [], qcl=client, _confirm_fn=self._ConfirmForce)
274 self.assertEqual(result, constants.EXIT_FAILURE)
275
276
277 class DrbdHelperTestCase(unittest.TestCase):
278
279 def setUp(self):
280 unittest.TestCase.setUp(self)
281 self.enabled_disk_templates = []
282
283 def enableDrbd(self):
284 self.enabled_disk_templates = [constants.DT_DRBD8]
285
286 def disableDrbd(self):
287 self.enabled_disk_templates = [constants.DT_DISKLESS]
288
289
290 class InitDrbdHelper(DrbdHelperTestCase):
291
292 def testNoDrbdNoHelper(self):
293 opts = mock.Mock()
294 opts.drbd_helper = None
295 self.disableDrbd()
296 helper = gnt_cluster._InitDrbdHelper(opts, self.enabled_disk_templates)
297 self.assertEquals(None, helper)
298
299 def testNoDrbdHelper(self):
300 opts = mock.Mock()
301 self.disableDrbd()
302 opts.drbd_helper = "/bin/true"
303 helper = gnt_cluster._InitDrbdHelper(opts, self.enabled_disk_templates)
304 self.assertEquals(opts.drbd_helper, helper)
305
306 def testDrbdHelperNone(self):
307 opts = mock.Mock()
308 self.enableDrbd()
309 opts.drbd_helper = None
310 helper = gnt_cluster._InitDrbdHelper(opts, self.enabled_disk_templates)
311 self.assertEquals(constants.DEFAULT_DRBD_HELPER, helper)
312
313 def testDrbdHelperEmpty(self):
314 opts = mock.Mock()
315 self.enableDrbd()
316 opts.drbd_helper = ''
317 self.assertRaises(errors.OpPrereqError, gnt_cluster._InitDrbdHelper, opts,
318 self.enabled_disk_templates)
319
320 def testDrbdHelper(self):
321 opts = mock.Mock()
322 self.enableDrbd()
323 opts.drbd_helper = "/bin/true"
324 helper = gnt_cluster._InitDrbdHelper(opts, self.enabled_disk_templates)
325 self.assertEquals(opts.drbd_helper, helper)
326
327
328 class GetDrbdHelper(DrbdHelperTestCase):
329
330 def testNoDrbdNoHelper(self):
331 opts = mock.Mock()
332 self.disableDrbd()
333 opts.drbd_helper = None
334 helper = gnt_cluster._GetDrbdHelper(opts, self.enabled_disk_templates)
335 self.assertEquals(None, helper)
336
337 def testNoTemplateInfoNoHelper(self):
338 opts = mock.Mock()
339 opts.drbd_helper = None
340 helper = gnt_cluster._GetDrbdHelper(opts, None)
341 self.assertEquals(None, helper)
342
343 def testNoTemplateInfoHelper(self):
344 opts = mock.Mock()
345 opts.drbd_helper = "/bin/true"
346 helper = gnt_cluster._GetDrbdHelper(opts, None)
347 self.assertEquals(opts.drbd_helper, helper)
348
349 def testNoDrbdHelper(self):
350 opts = mock.Mock()
351 self.disableDrbd()
352 opts.drbd_helper = "/bin/true"
353 helper = gnt_cluster._GetDrbdHelper(opts, None)
354 self.assertEquals(opts.drbd_helper, helper)
355
356 def testDrbdNoHelper(self):
357 opts = mock.Mock()
358 self.enableDrbd()
359 opts.drbd_helper = None
360 helper = gnt_cluster._GetDrbdHelper(opts, self.enabled_disk_templates)
361 self.assertEquals(None, helper)
362
363 def testDrbdHelper(self):
364 opts = mock.Mock()
365 self.enableDrbd()
366 opts.drbd_helper = "/bin/true"
367 helper = gnt_cluster._GetDrbdHelper(opts, self.enabled_disk_templates)
368 self.assertEquals(opts.drbd_helper, helper)
369
370
371 class TestBuildGanetiPubKeys(testutils.GanetiTestCase):
372
373 _SOME_KEY_DICT = {"rsa": "key_rsa",
374 "dsa": "key_dsa"}
375 _MASTER_NODE_NAME = "master_node"
376 _MASTER_NODE_UUID = "master_uuid"
377 _NUM_NODES = 2 # excluding master node
378 _ONLINE_NODE_NAMES = ["node%s_name" % i for i in range(_NUM_NODES)]
379 _ONLINE_NODE_UUIDS = ["node%s_uuid" % i for i in range(_NUM_NODES)]
380 _CLUSTER_NAME = "cluster_name"
381 _PRIV_KEY = "master_private_key"
382 _PUB_KEY = "master_public_key"
383 _MODIFY_SSH_SETUP = True
384 _AUTH_KEYS = "a\nb\nc"
385
386 def _setUpFakeKeys(self):
387 os.makedirs(os.path.join(self.tmpdir, ".ssh"))
388
389 for key_type in ["rsa", "dsa"]:
390 self.priv_filename = os.path.join(self.tmpdir, ".ssh", "id_%s" % key_type)
391 utils.WriteFile(self.priv_filename, data=self._PRIV_KEY)
392
393 self.pub_filename = os.path.join(
394 self.tmpdir, ".ssh", "id_%s.pub" % key_type)
395 utils.WriteFile(self.pub_filename, data=self._PUB_KEY)
396
397 self.auth_filename = os.path.join(self.tmpdir, ".ssh", "authorized_keys")
398 utils.WriteFile(self.auth_filename, data=self._AUTH_KEYS)
399
400 def setUp(self):
401 testutils.GanetiTestCase.setUp(self)
402 self.tmpdir = tempfile.mkdtemp()
403 self.pub_key_filename = os.path.join(self.tmpdir, "ganeti_test_pub_keys")
404 self._setUpFakeKeys()
405
406 self._ssh_read_remote_ssh_pub_keys_patcher = testutils \
407 .patch_object(ssh, "ReadRemoteSshPubKeys")
408 self._ssh_read_remote_ssh_pub_keys_mock = \
409 self._ssh_read_remote_ssh_pub_keys_patcher.start()
410 self._ssh_read_remote_ssh_pub_keys_mock.return_value = self._SOME_KEY_DICT
411
412 self.mock_cl = mock.Mock()
413 self.mock_cl.QueryConfigValues = mock.Mock()
414 self.mock_cl.QueryConfigValues.return_value = \
415 (self._CLUSTER_NAME, self._MASTER_NODE_NAME, self._MODIFY_SSH_SETUP)
416
417 self._get_online_nodes_mock = mock.Mock()
418 self._get_online_nodes_mock.return_value = \
419 self._ONLINE_NODE_NAMES
420
421 self._get_nodes_ssh_ports_mock = mock.Mock()
422 self._get_nodes_ssh_ports_mock.return_value = \
423 [22 for i in range(self._NUM_NODES + 1)]
424
425 self._get_node_uuids_mock = mock.Mock()
426 self._get_node_uuids_mock.return_value = \
427 self._ONLINE_NODE_UUIDS + [self._MASTER_NODE_UUID]
428
429 self._options = mock.Mock()
430 self._options.ssh_key_check = False
431
432 def _GetTempHomedir(self, _):
433 return self.tmpdir
434
435 def tearDown(self):
436 super(testutils.GanetiTestCase, self).tearDown()
437 shutil.rmtree(self.tmpdir)
438 self._ssh_read_remote_ssh_pub_keys_patcher.stop()
439
440 def testNewPubKeyFile(self):
441 gnt_cluster._BuildGanetiPubKeys(
442 self._options,
443 pub_key_file=self.pub_key_filename,
444 cl=self.mock_cl,
445 get_online_nodes_fn=self._get_online_nodes_mock,
446 get_nodes_ssh_ports_fn=self._get_nodes_ssh_ports_mock,
447 get_node_uuids_fn=self._get_node_uuids_mock,
448 homedir_fn=self._GetTempHomedir)
449 key_file_result = utils.ReadFile(self.pub_key_filename)
450 for node_uuid in self._ONLINE_NODE_UUIDS + [self._MASTER_NODE_UUID]:
451 self.assertTrue(node_uuid in key_file_result)
452 self.assertTrue(self._PUB_KEY in key_file_result)
453
454 def testOverridePubKeyFile(self):
455 fd = open(self.pub_key_filename, "w")
456 fd.write("Pink Bunny")
457 fd.close()
458 gnt_cluster._BuildGanetiPubKeys(
459 self._options,
460 pub_key_file=self.pub_key_filename,
461 cl=self.mock_cl,
462 get_online_nodes_fn=self._get_online_nodes_mock,
463 get_nodes_ssh_ports_fn=self._get_nodes_ssh_ports_mock,
464 get_node_uuids_fn=self._get_node_uuids_mock,
465 homedir_fn=self._GetTempHomedir)
466 self.assertFalse("Pink Bunny" in self.pub_key_filename)
467
468
469 if __name__ == "__main__":
470 testutils.GanetiTestProgram()