Mock InitDrbdHelper's output in unittests
[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 feedback_fn=mock.Mock())
298 self.assertEquals(None, helper)
299
300 def testNoDrbdHelper(self):
301 opts = mock.Mock()
302 self.disableDrbd()
303 opts.drbd_helper = "/bin/true"
304 helper = gnt_cluster._InitDrbdHelper(opts, self.enabled_disk_templates,
305 feedback_fn=mock.Mock())
306 self.assertEquals(opts.drbd_helper, helper)
307
308 def testDrbdHelperNone(self):
309 opts = mock.Mock()
310 self.enableDrbd()
311 opts.drbd_helper = None
312 helper = gnt_cluster._InitDrbdHelper(opts, self.enabled_disk_templates,
313 feedback_fn=mock.Mock())
314 self.assertEquals(constants.DEFAULT_DRBD_HELPER, helper)
315
316 def testDrbdHelperEmpty(self):
317 opts = mock.Mock()
318 self.enableDrbd()
319 opts.drbd_helper = ''
320 self.assertRaises(errors.OpPrereqError, gnt_cluster._InitDrbdHelper, opts,
321 self.enabled_disk_templates, feedback_fn=mock.Mock())
322
323 def testDrbdHelper(self):
324 opts = mock.Mock()
325 self.enableDrbd()
326 opts.drbd_helper = "/bin/true"
327 helper = gnt_cluster._InitDrbdHelper(opts, self.enabled_disk_templates,
328 feedback_fn=mock.Mock())
329 self.assertEquals(opts.drbd_helper, helper)
330
331
332 class GetDrbdHelper(DrbdHelperTestCase):
333
334 def testNoDrbdNoHelper(self):
335 opts = mock.Mock()
336 self.disableDrbd()
337 opts.drbd_helper = None
338 helper = gnt_cluster._GetDrbdHelper(opts, self.enabled_disk_templates)
339 self.assertEquals(None, helper)
340
341 def testNoTemplateInfoNoHelper(self):
342 opts = mock.Mock()
343 opts.drbd_helper = None
344 helper = gnt_cluster._GetDrbdHelper(opts, None)
345 self.assertEquals(None, helper)
346
347 def testNoTemplateInfoHelper(self):
348 opts = mock.Mock()
349 opts.drbd_helper = "/bin/true"
350 helper = gnt_cluster._GetDrbdHelper(opts, None)
351 self.assertEquals(opts.drbd_helper, helper)
352
353 def testNoDrbdHelper(self):
354 opts = mock.Mock()
355 self.disableDrbd()
356 opts.drbd_helper = "/bin/true"
357 helper = gnt_cluster._GetDrbdHelper(opts, None)
358 self.assertEquals(opts.drbd_helper, helper)
359
360 def testDrbdNoHelper(self):
361 opts = mock.Mock()
362 self.enableDrbd()
363 opts.drbd_helper = None
364 helper = gnt_cluster._GetDrbdHelper(opts, self.enabled_disk_templates)
365 self.assertEquals(None, helper)
366
367 def testDrbdHelper(self):
368 opts = mock.Mock()
369 self.enableDrbd()
370 opts.drbd_helper = "/bin/true"
371 helper = gnt_cluster._GetDrbdHelper(opts, self.enabled_disk_templates)
372 self.assertEquals(opts.drbd_helper, helper)
373
374
375 class TestBuildGanetiPubKeys(testutils.GanetiTestCase):
376
377 _SOME_KEY_DICT = {"rsa": "key_rsa",
378 "dsa": "key_dsa"}
379 _MASTER_NODE_NAME = "master_node"
380 _MASTER_NODE_UUID = "master_uuid"
381 _NUM_NODES = 2 # excluding master node
382 _ONLINE_NODE_NAMES = ["node%s_name" % i for i in range(_NUM_NODES)]
383 _ONLINE_NODE_UUIDS = ["node%s_uuid" % i for i in range(_NUM_NODES)]
384 _CLUSTER_NAME = "cluster_name"
385 _PRIV_KEY = "master_private_key"
386 _PUB_KEY = "master_public_key"
387 _AUTH_KEYS = "a\nb\nc"
388
389 def _setUpFakeKeys(self):
390 os.makedirs(os.path.join(self.tmpdir, ".ssh"))
391
392 for key_type in ["rsa", "dsa"]:
393 self.priv_filename = os.path.join(self.tmpdir, ".ssh", "id_%s" % key_type)
394 utils.WriteFile(self.priv_filename, data=self._PRIV_KEY)
395
396 self.pub_filename = os.path.join(
397 self.tmpdir, ".ssh", "id_%s.pub" % key_type)
398 utils.WriteFile(self.pub_filename, data=self._PUB_KEY)
399
400 self.auth_filename = os.path.join(self.tmpdir, ".ssh", "authorized_keys")
401 utils.WriteFile(self.auth_filename, data=self._AUTH_KEYS)
402
403 def setUp(self):
404 testutils.GanetiTestCase.setUp(self)
405 self.tmpdir = tempfile.mkdtemp()
406 self.pub_key_filename = os.path.join(self.tmpdir, "ganeti_test_pub_keys")
407 self._setUpFakeKeys()
408
409 self._ssh_read_remote_ssh_pub_keys_patcher = testutils \
410 .patch_object(ssh, "ReadRemoteSshPubKeys")
411 self._ssh_read_remote_ssh_pub_keys_mock = \
412 self._ssh_read_remote_ssh_pub_keys_patcher.start()
413 self._ssh_read_remote_ssh_pub_keys_mock.return_value = self._SOME_KEY_DICT
414
415 self.mock_cl = mock.Mock()
416 self.mock_cl.QueryConfigValues = mock.Mock()
417 self.mock_cl.QueryConfigValues.return_value = \
418 (self._CLUSTER_NAME, self._MASTER_NODE_NAME)
419
420 self._get_online_nodes_mock = mock.Mock()
421 self._get_online_nodes_mock.return_value = \
422 self._ONLINE_NODE_NAMES
423
424 self._get_nodes_ssh_ports_mock = mock.Mock()
425 self._get_nodes_ssh_ports_mock.return_value = \
426 [22 for i in range(self._NUM_NODES + 1)]
427
428 self._get_node_uuids_mock = mock.Mock()
429 self._get_node_uuids_mock.return_value = \
430 self._ONLINE_NODE_UUIDS + [self._MASTER_NODE_UUID]
431
432 self._options = mock.Mock()
433 self._options.ssh_key_check = False
434
435 def _GetTempHomedir(self, _):
436 return self.tmpdir
437
438 def tearDown(self):
439 super(testutils.GanetiTestCase, self).tearDown()
440 shutil.rmtree(self.tmpdir)
441 self._ssh_read_remote_ssh_pub_keys_patcher.stop()
442
443 def testNewPubKeyFile(self):
444 gnt_cluster._BuildGanetiPubKeys(
445 self._options,
446 pub_key_file=self.pub_key_filename,
447 cl=self.mock_cl,
448 get_online_nodes_fn=self._get_online_nodes_mock,
449 get_nodes_ssh_ports_fn=self._get_nodes_ssh_ports_mock,
450 get_node_uuids_fn=self._get_node_uuids_mock,
451 homedir_fn=self._GetTempHomedir)
452 key_file_result = utils.ReadFile(self.pub_key_filename)
453 for node_uuid in self._ONLINE_NODE_UUIDS + [self._MASTER_NODE_UUID]:
454 self.assertTrue(node_uuid in key_file_result)
455 self.assertTrue(self._PUB_KEY in key_file_result)
456
457 def testOverridePubKeyFile(self):
458 fd = open(self.pub_key_filename, "w")
459 fd.write("Pink Bunny")
460 fd.close()
461 gnt_cluster._BuildGanetiPubKeys(
462 self._options,
463 pub_key_file=self.pub_key_filename,
464 cl=self.mock_cl,
465 get_online_nodes_fn=self._get_online_nodes_mock,
466 get_nodes_ssh_ports_fn=self._get_nodes_ssh_ports_mock,
467 get_node_uuids_fn=self._get_node_uuids_mock,
468 homedir_fn=self._GetTempHomedir)
469 self.assertFalse("Pink Bunny" in self.pub_key_filename)
470
471
472 if __name__ == "__main__":
473 testutils.GanetiTestProgram()