Init: add master client certificate to configuration
[ganeti-github.git] / test / py / cmdlib / cluster_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2008, 2011, 2012, 2013 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 """Tests for LUCluster*
32
33 """
34
35 import OpenSSL
36
37 import copy
38 import unittest
39 import operator
40 import re
41 import shutil
42 import os
43
44 from ganeti.cmdlib import cluster
45 from ganeti import constants
46 from ganeti import errors
47 from ganeti import netutils
48 from ganeti import objects
49 from ganeti import opcodes
50 from ganeti import utils
51 from ganeti import pathutils
52 from ganeti import query
53 from ganeti.hypervisor import hv_xen
54
55 from testsupport import *
56
57 import testutils
58
59
60 class TestClusterVerifySsh(unittest.TestCase):
61 def testMultipleGroups(self):
62 fn = cluster.LUClusterVerifyGroup._SelectSshCheckNodes
63 mygroupnodes = [
64 objects.Node(name="node20", group="my", offline=False),
65 objects.Node(name="node21", group="my", offline=False),
66 objects.Node(name="node22", group="my", offline=False),
67 objects.Node(name="node23", group="my", offline=False),
68 objects.Node(name="node24", group="my", offline=False),
69 objects.Node(name="node25", group="my", offline=False),
70 objects.Node(name="node26", group="my", offline=True),
71 ]
72 nodes = [
73 objects.Node(name="node1", group="g1", offline=True),
74 objects.Node(name="node2", group="g1", offline=False),
75 objects.Node(name="node3", group="g1", offline=False),
76 objects.Node(name="node4", group="g1", offline=True),
77 objects.Node(name="node5", group="g1", offline=False),
78 objects.Node(name="node10", group="xyz", offline=False),
79 objects.Node(name="node11", group="xyz", offline=False),
80 objects.Node(name="node40", group="alloff", offline=True),
81 objects.Node(name="node41", group="alloff", offline=True),
82 objects.Node(name="node50", group="aaa", offline=False),
83 ] + mygroupnodes
84 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
85
86 (online, perhost) = fn(mygroupnodes, "my", nodes)
87 self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
88 self.assertEqual(set(perhost.keys()), set(online))
89
90 self.assertEqual(perhost, {
91 "node20": ["node10", "node2", "node50"],
92 "node21": ["node11", "node3", "node50"],
93 "node22": ["node10", "node5", "node50"],
94 "node23": ["node11", "node2", "node50"],
95 "node24": ["node10", "node3", "node50"],
96 "node25": ["node11", "node5", "node50"],
97 })
98
99 def testSingleGroup(self):
100 fn = cluster.LUClusterVerifyGroup._SelectSshCheckNodes
101 nodes = [
102 objects.Node(name="node1", group="default", offline=True),
103 objects.Node(name="node2", group="default", offline=False),
104 objects.Node(name="node3", group="default", offline=False),
105 objects.Node(name="node4", group="default", offline=True),
106 ]
107 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
108
109 (online, perhost) = fn(nodes, "default", nodes)
110 self.assertEqual(online, ["node2", "node3"])
111 self.assertEqual(set(perhost.keys()), set(online))
112
113 self.assertEqual(perhost, {
114 "node2": [],
115 "node3": [],
116 })
117
118
119 class TestLUClusterActivateMasterIp(CmdlibTestCase):
120 def testSuccess(self):
121 op = opcodes.OpClusterActivateMasterIp()
122
123 self.rpc.call_node_activate_master_ip.return_value = \
124 self.RpcResultsBuilder() \
125 .CreateSuccessfulNodeResult(self.master)
126
127 self.ExecOpCode(op)
128
129 self.rpc.call_node_activate_master_ip.assert_called_once_with(
130 self.master_uuid, self.cfg.GetMasterNetworkParameters(), False)
131
132 def testFailure(self):
133 op = opcodes.OpClusterActivateMasterIp()
134
135 self.rpc.call_node_activate_master_ip.return_value = \
136 self.RpcResultsBuilder() \
137 .CreateFailedNodeResult(self.master) \
138
139 self.ExecOpCodeExpectOpExecError(op)
140
141
142 class TestLUClusterDeactivateMasterIp(CmdlibTestCase):
143 def testSuccess(self):
144 op = opcodes.OpClusterDeactivateMasterIp()
145
146 self.rpc.call_node_deactivate_master_ip.return_value = \
147 self.RpcResultsBuilder() \
148 .CreateSuccessfulNodeResult(self.master)
149
150 self.ExecOpCode(op)
151
152 self.rpc.call_node_deactivate_master_ip.assert_called_once_with(
153 self.master_uuid, self.cfg.GetMasterNetworkParameters(), False)
154
155 def testFailure(self):
156 op = opcodes.OpClusterDeactivateMasterIp()
157
158 self.rpc.call_node_deactivate_master_ip.return_value = \
159 self.RpcResultsBuilder() \
160 .CreateFailedNodeResult(self.master) \
161
162 self.ExecOpCodeExpectOpExecError(op)
163
164
165 class TestLUClusterConfigQuery(CmdlibTestCase):
166 def testInvalidField(self):
167 op = opcodes.OpClusterConfigQuery(output_fields=["pinky_bunny"])
168
169 self.ExecOpCodeExpectOpPrereqError(op, "pinky_bunny")
170
171 def testAllFields(self):
172 op = opcodes.OpClusterConfigQuery(output_fields=query.CLUSTER_FIELDS.keys())
173
174 self.rpc.call_get_watcher_pause.return_value = \
175 self.RpcResultsBuilder() \
176 .CreateSuccessfulNodeResult(self.master, -1)
177
178 ret = self.ExecOpCode(op)
179
180 self.assertEqual(1, self.rpc.call_get_watcher_pause.call_count)
181 self.assertEqual(len(ret), len(query.CLUSTER_FIELDS))
182
183 def testEmpytFields(self):
184 op = opcodes.OpClusterConfigQuery(output_fields=[])
185
186 self.ExecOpCode(op)
187
188 self.assertFalse(self.rpc.call_get_watcher_pause.called)
189
190
191 class TestLUClusterDestroy(CmdlibTestCase):
192 def testExistingNodes(self):
193 op = opcodes.OpClusterDestroy()
194
195 self.cfg.AddNewNode()
196 self.cfg.AddNewNode()
197
198 self.ExecOpCodeExpectOpPrereqError(op, "still 2 node\(s\)")
199
200 def testExistingInstances(self):
201 op = opcodes.OpClusterDestroy()
202
203 self.cfg.AddNewInstance()
204 self.cfg.AddNewInstance()
205
206 self.ExecOpCodeExpectOpPrereqError(op, "still 2 instance\(s\)")
207
208 def testEmptyCluster(self):
209 op = opcodes.OpClusterDestroy()
210
211 self.ExecOpCode(op)
212
213 self.assertSingleHooksCall([self.master.name],
214 "cluster-destroy",
215 constants.HOOKS_PHASE_POST)
216
217
218 class TestLUClusterPostInit(CmdlibTestCase):
219
220 def testExecution(self):
221 op = opcodes.OpClusterPostInit()
222
223 self.ExecOpCode(op)
224
225 self.assertSingleHooksCall([self.master.uuid],
226 "cluster-init",
227 constants.HOOKS_PHASE_POST)
228
229
230 class TestLUClusterQuery(CmdlibTestCase):
231 def testSimpleInvocation(self):
232 op = opcodes.OpClusterQuery()
233
234 self.ExecOpCode(op)
235
236 def testIPv6Cluster(self):
237 op = opcodes.OpClusterQuery()
238
239 self.cluster.primary_ip_family = netutils.IP6Address.family
240
241 self.ExecOpCode(op)
242
243
244 class TestLUClusterRedistConf(CmdlibTestCase):
245 def testSimpleInvocation(self):
246 op = opcodes.OpClusterRedistConf()
247
248 self.ExecOpCode(op)
249
250
251 class TestLUClusterRename(CmdlibTestCase):
252 NEW_NAME = "new-name.example.com"
253 NEW_IP = "203.0.113.100"
254
255 def testNoChanges(self):
256 op = opcodes.OpClusterRename(name=self.cfg.GetClusterName())
257
258 self.ExecOpCodeExpectOpPrereqError(op, "name nor the IP address")
259
260 def testReachableIp(self):
261 op = opcodes.OpClusterRename(name=self.NEW_NAME)
262
263 self.netutils_mod.GetHostname.return_value = \
264 HostnameMock(self.NEW_NAME, self.NEW_IP)
265 self.netutils_mod.TcpPing.return_value = True
266
267 self.ExecOpCodeExpectOpPrereqError(op, "is reachable on the network")
268
269 def testValidRename(self):
270 op = opcodes.OpClusterRename(name=self.NEW_NAME)
271
272 self.netutils_mod.GetHostname.return_value = \
273 HostnameMock(self.NEW_NAME, self.NEW_IP)
274
275 self.ExecOpCode(op)
276
277 self.assertEqual(1, self.ssh_mod.WriteKnownHostsFile.call_count)
278 self.rpc.call_node_deactivate_master_ip.assert_called_once_with(
279 self.master_uuid, self.cfg.GetMasterNetworkParameters(), False)
280 self.rpc.call_node_activate_master_ip.assert_called_once_with(
281 self.master_uuid, self.cfg.GetMasterNetworkParameters(), False)
282
283 def testRenameOfflineMaster(self):
284 op = opcodes.OpClusterRename(name=self.NEW_NAME)
285
286 self.master.offline = True
287 self.netutils_mod.GetHostname.return_value = \
288 HostnameMock(self.NEW_NAME, self.NEW_IP)
289
290 self.ExecOpCode(op)
291
292
293 class TestLUClusterRepairDiskSizes(CmdlibTestCase):
294 def testNoInstances(self):
295 op = opcodes.OpClusterRepairDiskSizes()
296
297 self.ExecOpCode(op)
298
299 def _SetUpInstanceSingleDisk(self, dev_type=constants.DT_PLAIN):
300 pnode = self.master
301 snode = self.cfg.AddNewNode()
302
303 disk = self.cfg.CreateDisk(dev_type=dev_type,
304 primary_node=pnode,
305 secondary_node=snode)
306 inst = self.cfg.AddNewInstance(disks=[disk])
307
308 return (inst, disk)
309
310 def testSingleInstanceOnFailingNode(self):
311 (inst, _) = self._SetUpInstanceSingleDisk()
312 op = opcodes.OpClusterRepairDiskSizes(instances=[inst.name])
313
314 self.rpc.call_blockdev_getdimensions.return_value = \
315 self.RpcResultsBuilder() \
316 .CreateFailedNodeResult(self.master)
317
318 self.ExecOpCode(op)
319
320 self.mcpu.assertLogContainsRegex("Failure in blockdev_getdimensions")
321
322 def _ExecOpClusterRepairDiskSizes(self, node_data):
323 # not specifying instances repairs all
324 op = opcodes.OpClusterRepairDiskSizes()
325
326 self.rpc.call_blockdev_getdimensions.return_value = \
327 self.RpcResultsBuilder() \
328 .CreateSuccessfulNodeResult(self.master, node_data)
329
330 return self.ExecOpCode(op)
331
332 def testInvalidResultData(self):
333 for data in [[], [None], ["invalid"], [("still", "invalid")]]:
334 self.ResetMocks()
335
336 self._SetUpInstanceSingleDisk()
337 self._ExecOpClusterRepairDiskSizes(data)
338
339 self.mcpu.assertLogContainsRegex("ignoring")
340
341 def testCorrectSize(self):
342 self._SetUpInstanceSingleDisk()
343 changed = self._ExecOpClusterRepairDiskSizes([(1024 * 1024 * 1024, None)])
344 self.mcpu.assertLogIsEmpty()
345 self.assertEqual(0, len(changed))
346
347 def testWrongSize(self):
348 self._SetUpInstanceSingleDisk()
349 changed = self._ExecOpClusterRepairDiskSizes([(512 * 1024 * 1024, None)])
350 self.assertEqual(1, len(changed))
351
352 def testCorrectDRBD(self):
353 self._SetUpInstanceSingleDisk(dev_type=constants.DT_DRBD8)
354 changed = self._ExecOpClusterRepairDiskSizes([(1024 * 1024 * 1024, None)])
355 self.mcpu.assertLogIsEmpty()
356 self.assertEqual(0, len(changed))
357
358 def testWrongDRBDChild(self):
359 (_, disk) = self._SetUpInstanceSingleDisk(dev_type=constants.DT_DRBD8)
360 disk.children[0].size = 512
361 changed = self._ExecOpClusterRepairDiskSizes([(1024 * 1024 * 1024, None)])
362 self.assertEqual(1, len(changed))
363
364 def testExclusiveStorageInvalidResultData(self):
365 self._SetUpInstanceSingleDisk()
366 self.master.ndparams[constants.ND_EXCLUSIVE_STORAGE] = True
367 self._ExecOpClusterRepairDiskSizes([(1024 * 1024 * 1024, None)])
368
369 self.mcpu.assertLogContainsRegex(
370 "did not return valid spindles information")
371
372 def testExclusiveStorageCorrectSpindles(self):
373 (_, disk) = self._SetUpInstanceSingleDisk()
374 disk.spindles = 1
375 self.master.ndparams[constants.ND_EXCLUSIVE_STORAGE] = True
376 changed = self._ExecOpClusterRepairDiskSizes([(1024 * 1024 * 1024, 1)])
377 self.assertEqual(0, len(changed))
378
379 def testExclusiveStorageWrongSpindles(self):
380 self._SetUpInstanceSingleDisk()
381 self.master.ndparams[constants.ND_EXCLUSIVE_STORAGE] = True
382 changed = self._ExecOpClusterRepairDiskSizes([(1024 * 1024 * 1024, 1)])
383 self.assertEqual(1, len(changed))
384
385
386 class TestLUClusterSetParams(CmdlibTestCase):
387 UID_POOL = [(10, 1000)]
388
389 def testUidPool(self):
390 op = opcodes.OpClusterSetParams(uid_pool=self.UID_POOL)
391 self.ExecOpCode(op)
392 self.assertEqual(self.UID_POOL, self.cluster.uid_pool)
393
394 def testAddUids(self):
395 old_pool = [(1, 9)]
396 self.cluster.uid_pool = list(old_pool)
397 op = opcodes.OpClusterSetParams(add_uids=self.UID_POOL)
398 self.ExecOpCode(op)
399 self.assertEqual(set(self.UID_POOL + old_pool),
400 set(self.cluster.uid_pool))
401
402 def testRemoveUids(self):
403 additional_pool = [(1, 9)]
404 self.cluster.uid_pool = self.UID_POOL + additional_pool
405 op = opcodes.OpClusterSetParams(remove_uids=self.UID_POOL)
406 self.ExecOpCode(op)
407 self.assertEqual(additional_pool, self.cluster.uid_pool)
408
409 def testMacPrefix(self):
410 mac_prefix = "aa:01:02"
411 op = opcodes.OpClusterSetParams(mac_prefix=mac_prefix)
412 self.ExecOpCode(op)
413 self.assertEqual(mac_prefix, self.cluster.mac_prefix)
414
415 def testEmptyMacPrefix(self):
416 mac_prefix = ""
417 op = opcodes.OpClusterSetParams(mac_prefix=mac_prefix)
418 self.ExecOpCodeExpectOpPrereqError(
419 op, "Parameter 'OP_CLUSTER_SET_PARAMS.mac_prefix' fails validation")
420
421 def testInvalidMacPrefix(self):
422 mac_prefix = "az:00:00"
423 op = opcodes.OpClusterSetParams(mac_prefix=mac_prefix)
424 self.ExecOpCodeExpectOpPrereqError(op, "Invalid MAC address prefix")
425
426 def testMasterNetmask(self):
427 op = opcodes.OpClusterSetParams(master_netmask=26)
428 self.ExecOpCode(op)
429 self.assertEqual(26, self.cluster.master_netmask)
430
431 def testInvalidDiskparams(self):
432 for diskparams in [{constants.DT_DISKLESS: {constants.LV_STRIPES: 0}},
433 {constants.DT_DRBD8: {constants.RBD_POOL: "pool"}},
434 {constants.DT_DRBD8: {constants.RBD_ACCESS: "bunny"}}]:
435 self.ResetMocks()
436 op = opcodes.OpClusterSetParams(diskparams=diskparams)
437 self.ExecOpCodeExpectOpPrereqError(op, "verify diskparams")
438
439 def testValidDiskparams(self):
440 diskparams = {constants.DT_RBD: {constants.RBD_POOL: "mock_pool",
441 constants.RBD_ACCESS: "kernelspace"}}
442 op = opcodes.OpClusterSetParams(diskparams=diskparams)
443 self.ExecOpCode(op)
444 self.assertEqual(diskparams[constants.DT_RBD],
445 self.cluster.diskparams[constants.DT_RBD])
446
447 def testMinimalDiskparams(self):
448 diskparams = {constants.DT_RBD: {constants.RBD_POOL: "mock_pool"}}
449 self.cluster.diskparams = {}
450 op = opcodes.OpClusterSetParams(diskparams=diskparams)
451 self.ExecOpCode(op)
452 self.assertEqual(diskparams, self.cluster.diskparams)
453
454 def testValidDiskparamsAccess(self):
455 for value in constants.DISK_VALID_ACCESS_MODES:
456 self.ResetMocks()
457 op = opcodes.OpClusterSetParams(diskparams={
458 constants.DT_RBD: {constants.RBD_ACCESS: value}
459 })
460 self.ExecOpCode(op)
461 got = self.cluster.diskparams[constants.DT_RBD][constants.RBD_ACCESS]
462 self.assertEqual(value, got)
463
464 def testInvalidDiskparamsAccess(self):
465 for value in ["default", "pinky_bunny"]:
466 self.ResetMocks()
467 op = opcodes.OpClusterSetParams(diskparams={
468 constants.DT_RBD: {constants.RBD_ACCESS: value}
469 })
470 self.ExecOpCodeExpectOpPrereqError(op, "Invalid value of 'rbd:access'")
471
472 def testUnsetDrbdHelperWithDrbdDisks(self):
473 self.cfg.AddNewInstance(disks=[
474 self.cfg.CreateDisk(dev_type=constants.DT_DRBD8, create_nodes=True)])
475 op = opcodes.OpClusterSetParams(drbd_helper="")
476 self.ExecOpCodeExpectOpPrereqError(op, "Cannot disable drbd helper")
477
478 def testFileStorageDir(self):
479 op = opcodes.OpClusterSetParams(file_storage_dir="/random/path")
480 self.ExecOpCode(op)
481 self.assertEqual("/random/path", self.cluster.file_storage_dir)
482
483 def testSetFileStorageDirToCurrentValue(self):
484 op = opcodes.OpClusterSetParams(
485 file_storage_dir=self.cluster.file_storage_dir)
486 self.ExecOpCode(op)
487
488 self.mcpu.assertLogContainsRegex("file storage dir already set to value")
489
490 def testUnsetFileStorageDirFileStorageEnabled(self):
491 self.cfg.SetEnabledDiskTemplates([constants.DT_FILE])
492 op = opcodes.OpClusterSetParams(file_storage_dir='')
493 self.ExecOpCodeExpectOpPrereqError(op, "Unsetting the 'file' storage")
494
495 def testUnsetFileStorageDirFileStorageDisabled(self):
496 self.cfg.SetEnabledDiskTemplates([constants.DT_PLAIN])
497 op = opcodes.OpClusterSetParams(file_storage_dir='')
498 self.ExecOpCode(op)
499
500 def testSetFileStorageDirFileStorageDisabled(self):
501 self.cfg.SetEnabledDiskTemplates([constants.DT_PLAIN])
502 op = opcodes.OpClusterSetParams(file_storage_dir='/some/path/')
503 self.ExecOpCode(op)
504 self.mcpu.assertLogContainsRegex("although file storage is not enabled")
505
506 def testSharedFileStorageDir(self):
507 op = opcodes.OpClusterSetParams(shared_file_storage_dir="/random/path")
508 self.ExecOpCode(op)
509 self.assertEqual("/random/path", self.cluster.shared_file_storage_dir)
510
511 def testSetSharedFileStorageDirToCurrentValue(self):
512 op = opcodes.OpClusterSetParams(shared_file_storage_dir="/random/path")
513 self.ExecOpCode(op)
514 op = opcodes.OpClusterSetParams(shared_file_storage_dir="/random/path")
515 self.ExecOpCode(op)
516 self.mcpu.assertLogContainsRegex("shared file storage dir already set to"
517 " value")
518
519 def testUnsetSharedFileStorageDirSharedFileStorageEnabled(self):
520 self.cfg.SetEnabledDiskTemplates([constants.DT_SHARED_FILE])
521 op = opcodes.OpClusterSetParams(shared_file_storage_dir='')
522 self.ExecOpCodeExpectOpPrereqError(op, "Unsetting the 'sharedfile' storage")
523
524 def testUnsetSharedFileStorageDirSharedFileStorageDisabled(self):
525 self.cfg.SetEnabledDiskTemplates([constants.DT_PLAIN])
526 op = opcodes.OpClusterSetParams(shared_file_storage_dir='')
527 self.ExecOpCode(op)
528
529 def testSetSharedFileStorageDirSharedFileStorageDisabled(self):
530 self.cfg.SetEnabledDiskTemplates([constants.DT_PLAIN])
531 op = opcodes.OpClusterSetParams(shared_file_storage_dir='/some/path/')
532 self.ExecOpCode(op)
533 self.mcpu.assertLogContainsRegex("although sharedfile storage is not"
534 " enabled")
535
536 def testValidDrbdHelper(self):
537 node1 = self.cfg.AddNewNode()
538 node1.offline = True
539 self.rpc.call_drbd_helper.return_value = \
540 self.RpcResultsBuilder() \
541 .AddSuccessfulNode(self.master, "/bin/true") \
542 .AddOfflineNode(node1) \
543 .Build()
544 op = opcodes.OpClusterSetParams(drbd_helper="/bin/true")
545 self.ExecOpCode(op)
546 self.mcpu.assertLogContainsRegex("Not checking drbd helper on offline node")
547
548 def testDrbdHelperFailingNode(self):
549 self.rpc.call_drbd_helper.return_value = \
550 self.RpcResultsBuilder() \
551 .AddFailedNode(self.master) \
552 .Build()
553 op = opcodes.OpClusterSetParams(drbd_helper="/bin/true")
554 self.ExecOpCodeExpectOpPrereqError(op, "Error checking drbd helper")
555
556 def testInvalidDrbdHelper(self):
557 self.rpc.call_drbd_helper.return_value = \
558 self.RpcResultsBuilder() \
559 .AddSuccessfulNode(self.master, "/bin/false") \
560 .Build()
561 op = opcodes.OpClusterSetParams(drbd_helper="/bin/true")
562 self.ExecOpCodeExpectOpPrereqError(op, "drbd helper is /bin/false")
563
564 def testDrbdHelperWithoutDrbdDiskTemplate(self):
565 drbd_helper = "/bin/random_helper"
566 self.cfg.SetEnabledDiskTemplates([constants.DT_DISKLESS])
567 self.rpc.call_drbd_helper.return_value = \
568 self.RpcResultsBuilder() \
569 .AddSuccessfulNode(self.master, drbd_helper) \
570 .Build()
571 op = opcodes.OpClusterSetParams(drbd_helper=drbd_helper)
572 self.ExecOpCode(op)
573
574 self.mcpu.assertLogContainsRegex("but did not enable")
575
576 def testResetDrbdHelperDrbdDisabled(self):
577 drbd_helper = ""
578 self.cfg.SetEnabledDiskTemplates([constants.DT_DISKLESS])
579 op = opcodes.OpClusterSetParams(drbd_helper=drbd_helper)
580 self.ExecOpCode(op)
581
582 self.assertEqual(None, self.cluster.drbd_usermode_helper)
583
584 def testResetDrbdHelperDrbdEnabled(self):
585 drbd_helper = ""
586 self.cluster.enabled_disk_templates = [constants.DT_DRBD8]
587 op = opcodes.OpClusterSetParams(drbd_helper=drbd_helper)
588 self.ExecOpCodeExpectOpPrereqError(
589 op, "Cannot disable drbd helper while DRBD is enabled.")
590
591 def testEnableDrbdNoHelper(self):
592 self.cluster.enabled_disk_templates = [constants.DT_DISKLESS]
593 self.cluster.drbd_usermode_helper = None
594 enabled_disk_templates = [constants.DT_DRBD8]
595 op = opcodes.OpClusterSetParams(
596 enabled_disk_templates=enabled_disk_templates)
597 self.ExecOpCodeExpectOpPrereqError(
598 op, "Cannot enable DRBD without a DRBD usermode helper set")
599
600 def testEnableDrbdHelperSet(self):
601 drbd_helper = "/bin/random_helper"
602 self.rpc.call_drbd_helper.return_value = \
603 self.RpcResultsBuilder() \
604 .AddSuccessfulNode(self.master, drbd_helper) \
605 .Build()
606 self.cfg.SetEnabledDiskTemplates([constants.DT_DISKLESS])
607 self.cluster.drbd_usermode_helper = drbd_helper
608 enabled_disk_templates = [constants.DT_DRBD8]
609 op = opcodes.OpClusterSetParams(
610 enabled_disk_templates=enabled_disk_templates,
611 ipolicy={constants.IPOLICY_DTS: enabled_disk_templates})
612 self.ExecOpCode(op)
613
614 self.assertEqual(drbd_helper, self.cluster.drbd_usermode_helper)
615
616 def testDrbdHelperAlreadySet(self):
617 drbd_helper = "/bin/true"
618 self.rpc.call_drbd_helper.return_value = \
619 self.RpcResultsBuilder() \
620 .AddSuccessfulNode(self.master, "/bin/true") \
621 .Build()
622 self.cfg.SetEnabledDiskTemplates([constants.DT_DISKLESS])
623 op = opcodes.OpClusterSetParams(drbd_helper=drbd_helper)
624 self.ExecOpCode(op)
625
626 self.assertEqual(drbd_helper, self.cluster.drbd_usermode_helper)
627 self.mcpu.assertLogContainsRegex("DRBD helper already in desired state")
628
629 def testSetDrbdHelper(self):
630 drbd_helper = "/bin/true"
631 self.rpc.call_drbd_helper.return_value = \
632 self.RpcResultsBuilder() \
633 .AddSuccessfulNode(self.master, "/bin/true") \
634 .Build()
635 self.cluster.drbd_usermode_helper = "/bin/false"
636 self.cfg.SetEnabledDiskTemplates([constants.DT_DRBD8])
637 op = opcodes.OpClusterSetParams(drbd_helper=drbd_helper)
638 self.ExecOpCode(op)
639
640 self.assertEqual(drbd_helper, self.cluster.drbd_usermode_helper)
641
642 def testBeparams(self):
643 beparams = {constants.BE_VCPUS: 32}
644 op = opcodes.OpClusterSetParams(beparams=beparams)
645 self.ExecOpCode(op)
646 self.assertEqual(32, self.cluster
647 .beparams[constants.PP_DEFAULT][constants.BE_VCPUS])
648
649 def testNdparams(self):
650 ndparams = {constants.ND_EXCLUSIVE_STORAGE: True}
651 op = opcodes.OpClusterSetParams(ndparams=ndparams)
652 self.ExecOpCode(op)
653 self.assertEqual(True, self.cluster
654 .ndparams[constants.ND_EXCLUSIVE_STORAGE])
655
656 def testNdparamsResetOobProgram(self):
657 ndparams = {constants.ND_OOB_PROGRAM: ""}
658 op = opcodes.OpClusterSetParams(ndparams=ndparams)
659 self.ExecOpCode(op)
660 self.assertEqual(constants.NDC_DEFAULTS[constants.ND_OOB_PROGRAM],
661 self.cluster.ndparams[constants.ND_OOB_PROGRAM])
662
663 def testHvState(self):
664 hv_state = {constants.HT_FAKE: {constants.HVST_CPU_TOTAL: 8}}
665 op = opcodes.OpClusterSetParams(hv_state=hv_state)
666 self.ExecOpCode(op)
667 self.assertEqual(8, self.cluster.hv_state_static
668 [constants.HT_FAKE][constants.HVST_CPU_TOTAL])
669
670 def testDiskState(self):
671 disk_state = {
672 constants.DT_PLAIN: {
673 "mock_vg": {constants.DS_DISK_TOTAL: 10}
674 }
675 }
676 op = opcodes.OpClusterSetParams(disk_state=disk_state)
677 self.ExecOpCode(op)
678 self.assertEqual(10, self.cluster
679 .disk_state_static[constants.DT_PLAIN]["mock_vg"]
680 [constants.DS_DISK_TOTAL])
681
682 def testDefaultIPolicy(self):
683 ipolicy = constants.IPOLICY_DEFAULTS
684 op = opcodes.OpClusterSetParams(ipolicy=ipolicy)
685 self.ExecOpCode(op)
686
687 def testIPolicyNewViolation(self):
688 import ganeti.constants as C
689 ipolicy = C.IPOLICY_DEFAULTS
690 ipolicy[C.ISPECS_MINMAX][0][C.ISPECS_MIN][C.ISPEC_MEM_SIZE] = 128
691 ipolicy[C.ISPECS_MINMAX][0][C.ISPECS_MAX][C.ISPEC_MEM_SIZE] = 128
692
693 self.cfg.AddNewInstance(beparams={C.BE_MINMEM: 512, C.BE_MAXMEM: 512})
694 op = opcodes.OpClusterSetParams(ipolicy=ipolicy)
695 self.ExecOpCode(op)
696
697 self.mcpu.assertLogContainsRegex("instances violate them")
698
699 def testNicparamsNoInstance(self):
700 nicparams = {
701 constants.NIC_LINK: "mock_bridge"
702 }
703 op = opcodes.OpClusterSetParams(nicparams=nicparams)
704 self.ExecOpCode(op)
705
706 self.assertEqual("mock_bridge",
707 self.cluster.nicparams
708 [constants.PP_DEFAULT][constants.NIC_LINK])
709
710 def testNicparamsInvalidConf(self):
711 nicparams = {
712 constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
713 constants.NIC_LINK: ""
714 }
715 op = opcodes.OpClusterSetParams(nicparams=nicparams)
716 self.ExecOpCodeExpectException(op, errors.ConfigurationError, "NIC link")
717
718 def testNicparamsInvalidInstanceConf(self):
719 nicparams = {
720 constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
721 constants.NIC_LINK: "mock_bridge"
722 }
723 self.cfg.AddNewInstance(nics=[
724 self.cfg.CreateNic(nicparams={constants.NIC_LINK: None})])
725 op = opcodes.OpClusterSetParams(nicparams=nicparams)
726 self.ExecOpCodeExpectOpPrereqError(op, "Missing bridged NIC link")
727
728 def testNicparamsMissingIp(self):
729 nicparams = {
730 constants.NIC_MODE: constants.NIC_MODE_ROUTED
731 }
732 self.cfg.AddNewInstance()
733 op = opcodes.OpClusterSetParams(nicparams=nicparams)
734 self.ExecOpCodeExpectOpPrereqError(op, "routed NIC with no ip address")
735
736 def testNicparamsWithInstance(self):
737 nicparams = {
738 constants.NIC_LINK: "mock_bridge"
739 }
740 self.cfg.AddNewInstance()
741 op = opcodes.OpClusterSetParams(nicparams=nicparams)
742 self.ExecOpCode(op)
743
744 def testDefaultHvparams(self):
745 hvparams = constants.HVC_DEFAULTS
746 op = opcodes.OpClusterSetParams(hvparams=hvparams)
747 self.ExecOpCode(op)
748
749 self.assertEqual(hvparams, self.cluster.hvparams)
750
751 def testMinimalHvparams(self):
752 hvparams = {
753 constants.HT_FAKE: {
754 constants.HV_MIGRATION_MODE: constants.HT_MIGRATION_NONLIVE
755 }
756 }
757 self.cluster.hvparams = {}
758 op = opcodes.OpClusterSetParams(hvparams=hvparams)
759 self.ExecOpCode(op)
760
761 self.assertEqual(hvparams, self.cluster.hvparams)
762
763 def testOsHvp(self):
764 os_hvp = {
765 "mocked_os": {
766 constants.HT_FAKE: {
767 constants.HV_MIGRATION_MODE: constants.HT_MIGRATION_NONLIVE
768 }
769 },
770 "other_os": constants.HVC_DEFAULTS
771 }
772 op = opcodes.OpClusterSetParams(os_hvp=os_hvp)
773 self.ExecOpCode(op)
774
775 self.assertEqual(constants.HT_MIGRATION_NONLIVE,
776 self.cluster.os_hvp["mocked_os"][constants.HT_FAKE]
777 [constants.HV_MIGRATION_MODE])
778 self.assertEqual(constants.HVC_DEFAULTS, self.cluster.os_hvp["other_os"])
779
780 def testRemoveOsHvp(self):
781 os_hvp = {"mocked_os": {constants.HT_FAKE: None}}
782 op = opcodes.OpClusterSetParams(os_hvp=os_hvp)
783 self.ExecOpCode(op)
784
785 assert constants.HT_FAKE not in self.cluster.os_hvp["mocked_os"]
786
787 def testDefaultOsHvp(self):
788 os_hvp = {"mocked_os": constants.HVC_DEFAULTS.copy()}
789 self.cluster.os_hvp = {"mocked_os": {}}
790 op = opcodes.OpClusterSetParams(os_hvp=os_hvp)
791 self.ExecOpCode(op)
792
793 self.assertEqual(os_hvp, self.cluster.os_hvp)
794
795 def testOsparams(self):
796 osparams = {
797 "mocked_os": {
798 "param1": "value1",
799 "param2": None
800 },
801 "other_os": {
802 "param1": None
803 }
804 }
805 self.cluster.osparams = {"other_os": {"param1": "value1"}}
806 self.cluster.osparams_private_cluster = {}
807 op = opcodes.OpClusterSetParams(osparams=osparams)
808 self.ExecOpCode(op)
809
810 self.assertEqual({"mocked_os": {"param1": "value1"}}, self.cluster.osparams)
811
812 def testEnabledHypervisors(self):
813 enabled_hypervisors = [constants.HT_XEN_HVM, constants.HT_XEN_PVM]
814 op = opcodes.OpClusterSetParams(enabled_hypervisors=enabled_hypervisors)
815 self.ExecOpCode(op)
816
817 self.assertEqual(enabled_hypervisors, self.cluster.enabled_hypervisors)
818
819 def testEnabledHypervisorsWithoutHypervisorParams(self):
820 enabled_hypervisors = [constants.HT_FAKE]
821 self.cluster.hvparams = {}
822 op = opcodes.OpClusterSetParams(enabled_hypervisors=enabled_hypervisors)
823 self.ExecOpCode(op)
824
825 self.assertEqual(enabled_hypervisors, self.cluster.enabled_hypervisors)
826 self.assertEqual(constants.HVC_DEFAULTS[constants.HT_FAKE],
827 self.cluster.hvparams[constants.HT_FAKE])
828
829 @testutils.patch_object(utils, "FindFile")
830 def testValidDefaultIallocator(self, find_file_mock):
831 find_file_mock.return_value = "/random/path"
832 default_iallocator = "/random/path"
833 op = opcodes.OpClusterSetParams(default_iallocator=default_iallocator)
834 self.ExecOpCode(op)
835
836 self.assertEqual(default_iallocator, self.cluster.default_iallocator)
837
838 @testutils.patch_object(utils, "FindFile")
839 def testInvalidDefaultIallocator(self, find_file_mock):
840 find_file_mock.return_value = None
841 default_iallocator = "/random/path"
842 op = opcodes.OpClusterSetParams(default_iallocator=default_iallocator)
843 self.ExecOpCodeExpectOpPrereqError(op, "Invalid default iallocator script")
844
845 def testEnabledDiskTemplates(self):
846 enabled_disk_templates = [constants.DT_DISKLESS, constants.DT_PLAIN]
847 op = opcodes.OpClusterSetParams(
848 enabled_disk_templates=enabled_disk_templates,
849 ipolicy={constants.IPOLICY_DTS: enabled_disk_templates})
850 self.ExecOpCode(op)
851
852 self.assertEqual(enabled_disk_templates,
853 self.cluster.enabled_disk_templates)
854
855 def testEnabledDiskTemplatesVsIpolicy(self):
856 enabled_disk_templates = [constants.DT_DISKLESS, constants.DT_PLAIN]
857 op = opcodes.OpClusterSetParams(
858 enabled_disk_templates=enabled_disk_templates,
859 ipolicy={constants.IPOLICY_DTS: [constants.DT_FILE]})
860 self.ExecOpCodeExpectOpPrereqError(op, "but not enabled on the cluster")
861
862 def testDisablingDiskTemplatesOfInstances(self):
863 old_disk_templates = [constants.DT_DISKLESS, constants.DT_PLAIN]
864 self.cfg.SetEnabledDiskTemplates(old_disk_templates)
865 self.cfg.AddNewInstance(
866 disks=[self.cfg.CreateDisk(dev_type=constants.DT_PLAIN)])
867 new_disk_templates = [constants.DT_DISKLESS, constants.DT_DRBD8]
868 op = opcodes.OpClusterSetParams(
869 enabled_disk_templates=new_disk_templates,
870 ipolicy={constants.IPOLICY_DTS: new_disk_templates})
871 self.ExecOpCodeExpectOpPrereqError(op, "least one instance using it")
872
873 def testEnabledDiskTemplatesWithoutVgName(self):
874 enabled_disk_templates = [constants.DT_PLAIN]
875 self.cluster.volume_group_name = None
876 op = opcodes.OpClusterSetParams(
877 enabled_disk_templates=enabled_disk_templates)
878 self.ExecOpCodeExpectOpPrereqError(op, "specify a volume group")
879
880 def testDisableDiskTemplateWithExistingInstance(self):
881 enabled_disk_templates = [constants.DT_DISKLESS]
882 self.cfg.AddNewInstance(
883 disks=[self.cfg.CreateDisk(dev_type=constants.DT_PLAIN)])
884 op = opcodes.OpClusterSetParams(
885 enabled_disk_templates=enabled_disk_templates,
886 ipolicy={constants.IPOLICY_DTS: enabled_disk_templates})
887 self.ExecOpCodeExpectOpPrereqError(op, "Cannot disable disk template")
888
889 def testVgNameNoLvmDiskTemplateEnabled(self):
890 vg_name = "test_vg"
891 self.cfg.SetEnabledDiskTemplates([constants.DT_DISKLESS])
892 op = opcodes.OpClusterSetParams(vg_name=vg_name)
893 self.ExecOpCode(op)
894
895 self.assertEqual(vg_name, self.cluster.volume_group_name)
896 self.mcpu.assertLogIsEmpty()
897
898 def testUnsetVgNameWithLvmDiskTemplateEnabled(self):
899 vg_name = ""
900 self.cluster.enabled_disk_templates = [constants.DT_PLAIN]
901 op = opcodes.OpClusterSetParams(vg_name=vg_name)
902 self.ExecOpCodeExpectOpPrereqError(op, "Cannot unset volume group")
903
904 def testUnsetVgNameWithLvmInstance(self):
905 vg_name = ""
906 self.cfg.AddNewInstance(
907 disks=[self.cfg.CreateDisk(dev_type=constants.DT_PLAIN)])
908 op = opcodes.OpClusterSetParams(vg_name=vg_name)
909 self.ExecOpCodeExpectOpPrereqError(op, "Cannot unset volume group")
910
911 def testUnsetVgNameWithNoLvmDiskTemplateEnabled(self):
912 vg_name = ""
913 self.cfg.SetEnabledDiskTemplates([constants.DT_DISKLESS])
914 op = opcodes.OpClusterSetParams(vg_name=vg_name)
915 self.ExecOpCode(op)
916
917 self.assertEqual(None, self.cluster.volume_group_name)
918
919 def testVgNameToOldName(self):
920 vg_name = self.cluster.volume_group_name
921 op = opcodes.OpClusterSetParams(vg_name=vg_name)
922 self.ExecOpCode(op)
923
924 self.mcpu.assertLogContainsRegex("already in desired state")
925
926 def testVgNameWithFailingNode(self):
927 vg_name = "test_vg"
928 op = opcodes.OpClusterSetParams(vg_name=vg_name)
929 self.rpc.call_vg_list.return_value = \
930 self.RpcResultsBuilder() \
931 .AddFailedNode(self.master) \
932 .Build()
933 self.ExecOpCode(op)
934
935 self.mcpu.assertLogContainsRegex("Error while gathering data on node")
936
937 def testVgNameWithValidNode(self):
938 vg_name = "test_vg"
939 op = opcodes.OpClusterSetParams(vg_name=vg_name)
940 self.rpc.call_vg_list.return_value = \
941 self.RpcResultsBuilder() \
942 .AddSuccessfulNode(self.master, {vg_name: 1024 * 1024}) \
943 .Build()
944 self.ExecOpCode(op)
945
946 def testVgNameWithTooSmallNode(self):
947 vg_name = "test_vg"
948 op = opcodes.OpClusterSetParams(vg_name=vg_name)
949 self.rpc.call_vg_list.return_value = \
950 self.RpcResultsBuilder() \
951 .AddSuccessfulNode(self.master, {vg_name: 1}) \
952 .Build()
953 self.ExecOpCodeExpectOpPrereqError(op, "too small")
954
955 def testMiscParameters(self):
956 op = opcodes.OpClusterSetParams(candidate_pool_size=123,
957 maintain_node_health=True,
958 modify_etc_hosts=True,
959 prealloc_wipe_disks=True,
960 reserved_lvs=["/dev/mock_lv"],
961 use_external_mip_script=True)
962 self.ExecOpCode(op)
963
964 self.mcpu.assertLogIsEmpty()
965 self.assertEqual(123, self.cluster.candidate_pool_size)
966 self.assertEqual(True, self.cluster.maintain_node_health)
967 self.assertEqual(True, self.cluster.modify_etc_hosts)
968 self.assertEqual(True, self.cluster.prealloc_wipe_disks)
969 self.assertEqual(["/dev/mock_lv"], self.cluster.reserved_lvs)
970 self.assertEqual(True, self.cluster.use_external_mip_script)
971
972 def testAddHiddenOs(self):
973 self.cluster.hidden_os = ["hidden1", "hidden2"]
974 op = opcodes.OpClusterSetParams(hidden_os=[(constants.DDM_ADD, "hidden2"),
975 (constants.DDM_ADD, "hidden3")])
976 self.ExecOpCode(op)
977
978 self.assertEqual(["hidden1", "hidden2", "hidden3"], self.cluster.hidden_os)
979 self.mcpu.assertLogContainsRegex("OS hidden2 already")
980
981 def testRemoveBlacklistedOs(self):
982 self.cluster.blacklisted_os = ["blisted1", "blisted2"]
983 op = opcodes.OpClusterSetParams(blacklisted_os=[
984 (constants.DDM_REMOVE, "blisted2"),
985 (constants.DDM_REMOVE, "blisted3")])
986 self.ExecOpCode(op)
987
988 self.assertEqual(["blisted1"], self.cluster.blacklisted_os)
989 self.mcpu.assertLogContainsRegex("OS blisted3 not found")
990
991 def testMasterNetdev(self):
992 master_netdev = "test_dev"
993 op = opcodes.OpClusterSetParams(master_netdev=master_netdev)
994 self.ExecOpCode(op)
995
996 self.assertEqual(master_netdev, self.cluster.master_netdev)
997
998 def testMasterNetdevFailNoForce(self):
999 master_netdev = "test_dev"
1000 op = opcodes.OpClusterSetParams(master_netdev=master_netdev)
1001 self.rpc.call_node_deactivate_master_ip.return_value = \
1002 self.RpcResultsBuilder() \
1003 .CreateFailedNodeResult(self.master)
1004 self.ExecOpCodeExpectOpExecError(op, "Could not disable the master ip")
1005
1006 def testMasterNetdevFailForce(self):
1007 master_netdev = "test_dev"
1008 op = opcodes.OpClusterSetParams(master_netdev=master_netdev,
1009 force=True)
1010 self.rpc.call_node_deactivate_master_ip.return_value = \
1011 self.RpcResultsBuilder() \
1012 .CreateFailedNodeResult(self.master)
1013 self.ExecOpCode(op)
1014
1015 self.mcpu.assertLogContainsRegex("Could not disable the master ip")
1016
1017 def testCompressionToolSuccess(self):
1018 compression_tools = ["certainly_not_a_default", "gzip"]
1019 op = opcodes.OpClusterSetParams(compression_tools=compression_tools)
1020 self.ExecOpCode(op)
1021 self.assertEqual(compression_tools, self.cluster.compression_tools)
1022
1023 def testCompressionToolCompatibility(self):
1024 compression_tools = ["not_gzip", "not_not_not_gzip"]
1025 op = opcodes.OpClusterSetParams(compression_tools=compression_tools)
1026 self.ExecOpCodeExpectOpPrereqError(op, ".*the gzip utility must be.*")
1027
1028 def testCompressionToolForbiddenValues(self):
1029 for value in ["none", "\"rm -rf all.all\"", "ls$IFS-la"]:
1030 compression_tools = [value, "gzip"]
1031 op = opcodes.OpClusterSetParams(compression_tools=compression_tools)
1032 self.ExecOpCodeExpectOpPrereqError(op, re.escape(value))
1033
1034
1035 class TestLUClusterVerify(CmdlibTestCase):
1036 def testVerifyAllGroups(self):
1037 op = opcodes.OpClusterVerify()
1038 result = self.ExecOpCode(op)
1039
1040 self.assertEqual(2, len(result["jobs"]))
1041
1042 def testVerifyDefaultGroups(self):
1043 op = opcodes.OpClusterVerify(group_name="default")
1044 result = self.ExecOpCode(op)
1045
1046 self.assertEqual(1, len(result["jobs"]))
1047
1048
1049 class TestLUClusterVerifyConfig(CmdlibTestCase):
1050
1051 def setUp(self):
1052 super(TestLUClusterVerifyConfig, self).setUp()
1053
1054 self._load_cert_patcher = testutils \
1055 .patch_object(OpenSSL.crypto, "load_certificate")
1056 self._load_cert_mock = self._load_cert_patcher.start()
1057 self._verify_cert_patcher = testutils \
1058 .patch_object(utils, "VerifyCertificate")
1059 self._verify_cert_mock = self._verify_cert_patcher.start()
1060 self._read_file_patcher = testutils.patch_object(utils, "ReadFile")
1061 self._read_file_mock = self._read_file_patcher.start()
1062 self._can_read_patcher = testutils.patch_object(utils, "CanRead")
1063 self._can_read_mock = self._can_read_patcher.start()
1064
1065 self._can_read_mock.return_value = True
1066 self._read_file_mock.return_value = True
1067 self._verify_cert_mock.return_value = (None, "")
1068 self._load_cert_mock.return_value = True
1069
1070 def tearDown(self):
1071 super(TestLUClusterVerifyConfig, self).tearDown()
1072
1073 self._can_read_patcher.stop()
1074 self._read_file_patcher.stop()
1075 self._verify_cert_patcher.stop()
1076 self._load_cert_patcher.stop()
1077
1078 def testSuccessfulRun(self):
1079 self.cfg.AddNewInstance()
1080 op = opcodes.OpClusterVerifyConfig()
1081 result = self.ExecOpCode(op)
1082 self.assertTrue(result)
1083
1084 def testDanglingNode(self):
1085 node = self.cfg.AddNewNode()
1086 self.cfg.AddNewInstance(primary_node=node)
1087 node.group = "invalid"
1088 op = opcodes.OpClusterVerifyConfig()
1089 result = self.ExecOpCode(op)
1090
1091 self.mcpu.assertLogContainsRegex(
1092 "following nodes \(and their instances\) belong to a non existing group")
1093 self.assertFalse(result)
1094
1095 def testDanglingInstance(self):
1096 inst = self.cfg.AddNewInstance()
1097 inst.primary_node = "invalid"
1098 op = opcodes.OpClusterVerifyConfig()
1099 result = self.ExecOpCode(op)
1100
1101 self.mcpu.assertLogContainsRegex(
1102 "following instances have a non-existing primary-node")
1103 self.assertFalse(result)
1104
1105
1106 class TestLUClusterVerifyGroup(CmdlibTestCase):
1107 def testEmptyNodeGroup(self):
1108 group = self.cfg.AddNewNodeGroup()
1109 op = opcodes.OpClusterVerifyGroup(group_name=group.name, verbose=True)
1110
1111 result = self.ExecOpCode(op)
1112
1113 self.assertTrue(result)
1114 self.mcpu.assertLogContainsRegex("Empty node group, skipping verification")
1115
1116 def testSimpleInvocation(self):
1117 op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1118
1119 self.ExecOpCode(op)
1120
1121 def testSimpleInvocationWithInstance(self):
1122 self.cfg.AddNewInstance(disks=[])
1123 op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1124
1125 self.ExecOpCode(op)
1126
1127 def testGhostNode(self):
1128 group = self.cfg.AddNewNodeGroup()
1129 node = self.cfg.AddNewNode(group=group.uuid, offline=True)
1130 self.master.offline = True
1131 self.cfg.AddNewInstance(disk_template=constants.DT_DRBD8,
1132 primary_node=self.master,
1133 secondary_node=node)
1134
1135 self.rpc.call_blockdev_getmirrorstatus_multi.return_value = \
1136 RpcResultsBuilder() \
1137 .AddOfflineNode(self.master) \
1138 .Build()
1139
1140 op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1141
1142 self.ExecOpCode(op)
1143
1144 def testValidRpcResult(self):
1145 self.cfg.AddNewInstance(disks=[])
1146
1147 self.rpc.call_node_verify.return_value = \
1148 RpcResultsBuilder() \
1149 .AddSuccessfulNode(self.master, {}) \
1150 .Build()
1151
1152 op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1153
1154 self.ExecOpCode(op)
1155
1156
1157 class TestLUClusterVerifyClientCerts(CmdlibTestCase):
1158
1159 def _AddNormalNode(self):
1160 self.normalnode = copy.deepcopy(self.master)
1161 self.normalnode.master_candidate = False
1162 self.normalnode.uuid = "normal-node-uuid"
1163 self.cfg.AddNode(self.normalnode, None)
1164
1165 def testVerifyMasterCandidate(self):
1166 client_cert = "client-cert-digest"
1167 self.cluster.candidate_certs = {self.master.uuid: client_cert}
1168 self.rpc.call_node_verify.return_value = \
1169 RpcResultsBuilder() \
1170 .AddSuccessfulNode(self.master,
1171 {constants.NV_CLIENT_CERT: (None, client_cert)}) \
1172 .Build()
1173 op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1174 self.ExecOpCode(op)
1175
1176 def testVerifyMasterCandidateInvalid(self):
1177 client_cert = "client-cert-digest"
1178 self.cluster.candidate_certs = {self.master.uuid: client_cert}
1179 self.rpc.call_node_verify.return_value = \
1180 RpcResultsBuilder() \
1181 .AddSuccessfulNode(self.master,
1182 {constants.NV_CLIENT_CERT: (666, "Invalid Certificate")}) \
1183 .Build()
1184 op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1185 self.ExecOpCode(op)
1186 self.mcpu.assertLogContainsRegex("Client certificate")
1187 self.mcpu.assertLogContainsRegex("failed validation")
1188
1189 def testVerifyNoMasterCandidateMap(self):
1190 client_cert = "client-cert-digest"
1191 self.cluster.candidate_certs = {}
1192 self.rpc.call_node_verify.return_value = \
1193 RpcResultsBuilder() \
1194 .AddSuccessfulNode(self.master,
1195 {constants.NV_CLIENT_CERT: (None, client_cert)}) \
1196 .Build()
1197 op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1198 self.ExecOpCode(op)
1199 self.mcpu.assertLogContainsRegex(
1200 "list of master candidate certificates is empty")
1201
1202 def testVerifyNoSharingMasterCandidates(self):
1203 client_cert = "client-cert-digest"
1204 self.cluster.candidate_certs = {
1205 self.master.uuid: client_cert,
1206 "some-other-master-candidate-uuid": client_cert}
1207 self.rpc.call_node_verify.return_value = \
1208 RpcResultsBuilder() \
1209 .AddSuccessfulNode(self.master,
1210 {constants.NV_CLIENT_CERT: (None, client_cert)}) \
1211 .Build()
1212 op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1213 self.ExecOpCode(op)
1214 self.mcpu.assertLogContainsRegex(
1215 "two master candidates configured to use the same")
1216
1217 def testVerifyMasterCandidateCertMismatch(self):
1218 client_cert = "client-cert-digest"
1219 self.cluster.candidate_certs = {self.master.uuid: "different-cert-digest"}
1220 self.rpc.call_node_verify.return_value = \
1221 RpcResultsBuilder() \
1222 .AddSuccessfulNode(self.master,
1223 {constants.NV_CLIENT_CERT: (None, client_cert)}) \
1224 .Build()
1225 op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1226 self.ExecOpCode(op)
1227 self.mcpu.assertLogContainsRegex("does not match its entry")
1228
1229 def testVerifyMasterCandidateUnregistered(self):
1230 client_cert = "client-cert-digest"
1231 self.cluster.candidate_certs = {"other-node-uuid": "different-cert-digest"}
1232 self.rpc.call_node_verify.return_value = \
1233 RpcResultsBuilder() \
1234 .AddSuccessfulNode(self.master,
1235 {constants.NV_CLIENT_CERT: (None, client_cert)}) \
1236 .Build()
1237 op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1238 self.ExecOpCode(op)
1239 self.mcpu.assertLogContainsRegex("does not have an entry")
1240
1241 def testVerifyMasterCandidateOtherNodesCert(self):
1242 client_cert = "client-cert-digest"
1243 self.cluster.candidate_certs = {"other-node-uuid": client_cert}
1244 self.rpc.call_node_verify.return_value = \
1245 RpcResultsBuilder() \
1246 .AddSuccessfulNode(self.master,
1247 {constants.NV_CLIENT_CERT: (None, client_cert)}) \
1248 .Build()
1249 op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1250 self.ExecOpCode(op)
1251 self.mcpu.assertLogContainsRegex("using a certificate of another node")
1252
1253 def testNormalNodeStillInList(self):
1254 self._AddNormalNode()
1255 client_cert_master = "client-cert-digest-master"
1256 client_cert_normal = "client-cert-digest-normal"
1257 self.cluster.candidate_certs = {
1258 self.normalnode.uuid: client_cert_normal,
1259 self.master.uuid: client_cert_master}
1260 self.rpc.call_node_verify.return_value = \
1261 RpcResultsBuilder() \
1262 .AddSuccessfulNode(self.normalnode,
1263 {constants.NV_CLIENT_CERT: (None, client_cert_normal)}) \
1264 .AddSuccessfulNode(self.master,
1265 {constants.NV_CLIENT_CERT: (None, client_cert_master)}) \
1266 .Build()
1267 op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1268 self.ExecOpCode(op)
1269 self.mcpu.assertLogContainsRegex("not a master candidate")
1270 self.mcpu.assertLogContainsRegex("still listed")
1271
1272 def testNormalNodeStealingMasterCandidateCert(self):
1273 self._AddNormalNode()
1274 client_cert_master = "client-cert-digest-master"
1275 self.cluster.candidate_certs = {
1276 self.master.uuid: client_cert_master}
1277 self.rpc.call_node_verify.return_value = \
1278 RpcResultsBuilder() \
1279 .AddSuccessfulNode(self.normalnode,
1280 {constants.NV_CLIENT_CERT: (None, client_cert_master)}) \
1281 .AddSuccessfulNode(self.master,
1282 {constants.NV_CLIENT_CERT: (None, client_cert_master)}) \
1283 .Build()
1284 op = opcodes.OpClusterVerifyGroup(group_name="default", verbose=True)
1285 self.ExecOpCode(op)
1286 self.mcpu.assertLogContainsRegex("not a master candidate")
1287 self.mcpu.assertLogContainsRegex(
1288 "certificate of another node which is master candidate")
1289
1290
1291 class TestLUClusterVerifyGroupMethods(CmdlibTestCase):
1292 """Base class for testing individual methods in LUClusterVerifyGroup.
1293
1294 """
1295 def setUp(self):
1296 super(TestLUClusterVerifyGroupMethods, self).setUp()
1297 self.op = opcodes.OpClusterVerifyGroup(group_name="default")
1298
1299 def PrepareLU(self, lu):
1300 lu._exclusive_storage = False
1301 lu.master_node = self.master_uuid
1302 lu.group_info = self.group
1303 cluster.LUClusterVerifyGroup.all_node_info = \
1304 property(fget=lambda _: self.cfg.GetAllNodesInfo())
1305
1306
1307 class TestLUClusterVerifyGroupVerifyNode(TestLUClusterVerifyGroupMethods):
1308 @withLockedLU
1309 def testInvalidNodeResult(self, lu):
1310 self.assertFalse(lu._VerifyNode(self.master, None))
1311 self.assertFalse(lu._VerifyNode(self.master, ""))
1312
1313 @withLockedLU
1314 def testInvalidVersion(self, lu):
1315 self.assertFalse(lu._VerifyNode(self.master, {"version": None}))
1316 self.assertFalse(lu._VerifyNode(self.master, {"version": ""}))
1317 self.assertFalse(lu._VerifyNode(self.master, {
1318 "version": (constants.PROTOCOL_VERSION - 1, constants.RELEASE_VERSION)
1319 }))
1320
1321 self.mcpu.ClearLogMessages()
1322 self.assertTrue(lu._VerifyNode(self.master, {
1323 "version": (constants.PROTOCOL_VERSION, constants.RELEASE_VERSION + "x")
1324 }))
1325 self.mcpu.assertLogContainsRegex("software version mismatch")
1326
1327 def _GetValidNodeResult(self, additional_fields):
1328 ret = {
1329 "version": (constants.PROTOCOL_VERSION, constants.RELEASE_VERSION),
1330 constants.NV_NODESETUP: []
1331 }
1332 ret.update(additional_fields)
1333 return ret
1334
1335 @withLockedLU
1336 def testHypervisor(self, lu):
1337 lu._VerifyNode(self.master, self._GetValidNodeResult({
1338 constants.NV_HYPERVISOR: {
1339 constants.HT_XEN_PVM: None,
1340 constants.HT_XEN_HVM: "mock error"
1341 }
1342 }))
1343 self.mcpu.assertLogContainsRegex(constants.HT_XEN_HVM)
1344 self.mcpu.assertLogContainsRegex("mock error")
1345
1346 @withLockedLU
1347 def testHvParams(self, lu):
1348 lu._VerifyNode(self.master, self._GetValidNodeResult({
1349 constants.NV_HVPARAMS: [("mock item", constants.HT_XEN_HVM, "mock error")]
1350 }))
1351 self.mcpu.assertLogContainsRegex(constants.HT_XEN_HVM)
1352 self.mcpu.assertLogContainsRegex("mock item")
1353 self.mcpu.assertLogContainsRegex("mock error")
1354
1355 @withLockedLU
1356 def testSuccessfulResult(self, lu):
1357 self.assertTrue(lu._VerifyNode(self.master, self._GetValidNodeResult({})))
1358 self.mcpu.assertLogIsEmpty()
1359
1360
1361 class TestLUClusterVerifyGroupVerifyNodeTime(TestLUClusterVerifyGroupMethods):
1362 @withLockedLU
1363 def testInvalidNodeResult(self, lu):
1364 for ndata in [{}, {constants.NV_TIME: "invalid"}]:
1365 self.mcpu.ClearLogMessages()
1366 lu._VerifyNodeTime(self.master, ndata, None, None)
1367
1368 self.mcpu.assertLogContainsRegex("Node returned invalid time")
1369
1370 @withLockedLU
1371 def testNodeDiverges(self, lu):
1372 for ntime in [(0, 0), (2000, 0)]:
1373 self.mcpu.ClearLogMessages()
1374 lu._VerifyNodeTime(self.master, {constants.NV_TIME: ntime}, 1000, 1005)
1375
1376 self.mcpu.assertLogContainsRegex("Node time diverges")
1377
1378 @withLockedLU
1379 def testSuccessfulResult(self, lu):
1380 lu._VerifyNodeTime(self.master, {constants.NV_TIME: (0, 0)}, 0, 5)
1381 self.mcpu.assertLogIsEmpty()
1382
1383
1384 class TestLUClusterVerifyGroupUpdateVerifyNodeLVM(
1385 TestLUClusterVerifyGroupMethods):
1386 def setUp(self):
1387 super(TestLUClusterVerifyGroupUpdateVerifyNodeLVM, self).setUp()
1388 self.VALID_NRESULT = {
1389 constants.NV_VGLIST: {"mock_vg": 30000},
1390 constants.NV_PVLIST: [
1391 {
1392 "name": "mock_pv",
1393 "vg_name": "mock_vg",
1394 "size": 5000,
1395 "free": 2500,
1396 "attributes": [],
1397 "lv_list": []
1398 }
1399 ]
1400 }
1401
1402 @withLockedLU
1403 def testNoVgName(self, lu):
1404 lu._UpdateVerifyNodeLVM(self.master, {}, None, None)
1405 self.mcpu.assertLogIsEmpty()
1406
1407 @withLockedLU
1408 def testEmptyNodeResult(self, lu):
1409 lu._UpdateVerifyNodeLVM(self.master, {}, "mock_vg", None)
1410 self.mcpu.assertLogContainsRegex("unable to check volume groups")
1411 self.mcpu.assertLogContainsRegex("Can't get PV list from node")
1412
1413 @withLockedLU
1414 def testValidNodeResult(self, lu):
1415 lu._UpdateVerifyNodeLVM(self.master, self.VALID_NRESULT, "mock_vg", None)
1416 self.mcpu.assertLogIsEmpty()
1417
1418 @withLockedLU
1419 def testValidNodeResultExclusiveStorage(self, lu):
1420 lu._exclusive_storage = True
1421 lu._UpdateVerifyNodeLVM(self.master, self.VALID_NRESULT, "mock_vg",
1422 cluster.LUClusterVerifyGroup.NodeImage())
1423 self.mcpu.assertLogIsEmpty()
1424
1425
1426 class TestLUClusterVerifyGroupVerifyGroupDRBDVersion(
1427 TestLUClusterVerifyGroupMethods):
1428 @withLockedLU
1429 def testEmptyNodeResult(self, lu):
1430 lu._VerifyGroupDRBDVersion({})
1431 self.mcpu.assertLogIsEmpty()
1432
1433 @withLockedLU
1434 def testValidNodeResult(self, lu):
1435 lu._VerifyGroupDRBDVersion(
1436 RpcResultsBuilder()
1437 .AddSuccessfulNode(self.master, {
1438 constants.NV_DRBDVERSION: "8.3.0"
1439 })
1440 .Build())
1441 self.mcpu.assertLogIsEmpty()
1442
1443 @withLockedLU
1444 def testDifferentVersions(self, lu):
1445 node1 = self.cfg.AddNewNode()
1446 lu._VerifyGroupDRBDVersion(
1447 RpcResultsBuilder()
1448 .AddSuccessfulNode(self.master, {
1449 constants.NV_DRBDVERSION: "8.3.0"
1450 })
1451 .AddSuccessfulNode(node1, {
1452 constants.NV_DRBDVERSION: "8.4.0"
1453 })
1454 .Build())
1455 self.mcpu.assertLogContainsRegex("DRBD version mismatch: 8.3.0")
1456 self.mcpu.assertLogContainsRegex("DRBD version mismatch: 8.4.0")
1457
1458
1459 class TestLUClusterVerifyGroupVerifyGroupLVM(TestLUClusterVerifyGroupMethods):
1460 @withLockedLU
1461 def testNoVgName(self, lu):
1462 lu._VerifyGroupLVM(None, None)
1463 self.mcpu.assertLogIsEmpty()
1464
1465 @withLockedLU
1466 def testNoExclusiveStorage(self, lu):
1467 lu._VerifyGroupLVM(None, "mock_vg")
1468 self.mcpu.assertLogIsEmpty()
1469
1470 @withLockedLU
1471 def testNoPvInfo(self, lu):
1472 lu._exclusive_storage = True
1473 nimg = cluster.LUClusterVerifyGroup.NodeImage()
1474 lu._VerifyGroupLVM({self.master.uuid: nimg}, "mock_vg")
1475 self.mcpu.assertLogIsEmpty()
1476
1477 @withLockedLU
1478 def testValidPvInfos(self, lu):
1479 lu._exclusive_storage = True
1480 node2 = self.cfg.AddNewNode()
1481 nimg1 = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master.uuid)
1482 nimg1.pv_min = 10000
1483 nimg1.pv_max = 10010
1484 nimg2 = cluster.LUClusterVerifyGroup.NodeImage(uuid=node2.uuid)
1485 nimg2.pv_min = 9998
1486 nimg2.pv_max = 10005
1487 lu._VerifyGroupLVM({self.master.uuid: nimg1, node2.uuid: nimg2}, "mock_vg")
1488 self.mcpu.assertLogIsEmpty()
1489
1490
1491 class TestLUClusterVerifyGroupVerifyNodeBridges(
1492 TestLUClusterVerifyGroupMethods):
1493 @withLockedLU
1494 def testNoBridges(self, lu):
1495 lu._VerifyNodeBridges(None, None, None)
1496 self.mcpu.assertLogIsEmpty()
1497
1498 @withLockedLU
1499 def testInvalidBridges(self, lu):
1500 for ndata in [{}, {constants.NV_BRIDGES: ""}]:
1501 self.mcpu.ClearLogMessages()
1502 lu._VerifyNodeBridges(self.master, ndata, ["mock_bridge"])
1503 self.mcpu.assertLogContainsRegex("not return valid bridge information")
1504
1505 self.mcpu.ClearLogMessages()
1506 lu._VerifyNodeBridges(self.master, {constants.NV_BRIDGES: ["mock_bridge"]},
1507 ["mock_bridge"])
1508 self.mcpu.assertLogContainsRegex("missing bridge")
1509
1510
1511 class TestLUClusterVerifyGroupVerifyNodeUserScripts(
1512 TestLUClusterVerifyGroupMethods):
1513 @withLockedLU
1514 def testNoUserScripts(self, lu):
1515 lu._VerifyNodeUserScripts(self.master, {})
1516 self.mcpu.assertLogContainsRegex("did not return user scripts information")
1517
1518 @withLockedLU
1519 def testBrokenUserScripts(self, lu):
1520 lu._VerifyNodeUserScripts(self.master,
1521 {constants.NV_USERSCRIPTS: ["script"]})
1522 self.mcpu.assertLogContainsRegex("scripts not present or not executable")
1523
1524
1525 class TestLUClusterVerifyGroupVerifyNodeNetwork(
1526 TestLUClusterVerifyGroupMethods):
1527
1528 def setUp(self):
1529 super(TestLUClusterVerifyGroupVerifyNodeNetwork, self).setUp()
1530 self.VALID_NRESULT = {
1531 constants.NV_NODELIST: {},
1532 constants.NV_NODENETTEST: {},
1533 constants.NV_MASTERIP: True
1534 }
1535
1536 @withLockedLU
1537 def testEmptyNodeResult(self, lu):
1538 lu._VerifyNodeNetwork(self.master, {})
1539 self.mcpu.assertLogContainsRegex(
1540 "node hasn't returned node ssh connectivity data")
1541 self.mcpu.assertLogContainsRegex(
1542 "node hasn't returned node tcp connectivity data")
1543 self.mcpu.assertLogContainsRegex(
1544 "node hasn't returned node master IP reachability data")
1545
1546 @withLockedLU
1547 def testValidResult(self, lu):
1548 lu._VerifyNodeNetwork(self.master, self.VALID_NRESULT)
1549 self.mcpu.assertLogIsEmpty()
1550
1551 @withLockedLU
1552 def testSshProblem(self, lu):
1553 self.VALID_NRESULT.update({
1554 constants.NV_NODELIST: {
1555 "mock_node": "mock_error"
1556 }
1557 })
1558 lu._VerifyNodeNetwork(self.master, self.VALID_NRESULT)
1559 self.mcpu.assertLogContainsRegex("ssh communication with node 'mock_node'")
1560
1561 @withLockedLU
1562 def testTcpProblem(self, lu):
1563 self.VALID_NRESULT.update({
1564 constants.NV_NODENETTEST: {
1565 "mock_node": "mock_error"
1566 }
1567 })
1568 lu._VerifyNodeNetwork(self.master, self.VALID_NRESULT)
1569 self.mcpu.assertLogContainsRegex("tcp communication with node 'mock_node'")
1570
1571 @withLockedLU
1572 def testMasterIpNotReachable(self, lu):
1573 self.VALID_NRESULT.update({
1574 constants.NV_MASTERIP: False
1575 })
1576 node1 = self.cfg.AddNewNode()
1577 lu._VerifyNodeNetwork(self.master, self.VALID_NRESULT)
1578 self.mcpu.assertLogContainsRegex(
1579 "the master node cannot reach the master IP")
1580
1581 self.mcpu.ClearLogMessages()
1582 lu._VerifyNodeNetwork(node1, self.VALID_NRESULT)
1583 self.mcpu.assertLogContainsRegex("cannot reach the master IP")
1584
1585
1586 class TestLUClusterVerifyGroupVerifyInstance(TestLUClusterVerifyGroupMethods):
1587 def setUp(self):
1588 super(TestLUClusterVerifyGroupVerifyInstance, self).setUp()
1589
1590 self.node1 = self.cfg.AddNewNode()
1591 self.drbd_inst = self.cfg.AddNewInstance(
1592 disks=[self.cfg.CreateDisk(dev_type=constants.DT_DRBD8,
1593 primary_node=self.master,
1594 secondary_node=self.node1)])
1595 self.running_inst = self.cfg.AddNewInstance(
1596 admin_state=constants.ADMINST_UP, disks_active=True)
1597 self.diskless_inst = self.cfg.AddNewInstance(disks=[])
1598
1599 self.master_img = \
1600 cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
1601 self.master_img.volumes = ["/".join(disk.logical_id)
1602 for inst in [self.running_inst,
1603 self.diskless_inst]
1604 for disk in
1605 self.cfg.GetInstanceDisks(inst.uuid)]
1606 drbd_inst_disks = self.cfg.GetInstanceDisks(self.drbd_inst.uuid)
1607 self.master_img.volumes.extend(
1608 ["/".join(disk.logical_id) for disk in drbd_inst_disks[0].children])
1609 self.master_img.instances = [self.running_inst.uuid]
1610 self.node1_img = \
1611 cluster.LUClusterVerifyGroup.NodeImage(uuid=self.node1.uuid)
1612 self.node1_img.volumes = \
1613 ["/".join(disk.logical_id) for disk in drbd_inst_disks[0].children]
1614 self.node_imgs = {
1615 self.master_uuid: self.master_img,
1616 self.node1.uuid: self.node1_img
1617 }
1618 running_inst_disks = self.cfg.GetInstanceDisks(self.running_inst.uuid)
1619 self.diskstatus = {
1620 self.master_uuid: [
1621 (True, objects.BlockDevStatus(ldisk_status=constants.LDS_OKAY))
1622 for _ in running_inst_disks
1623 ]
1624 }
1625
1626 @withLockedLU
1627 def testDisklessInst(self, lu):
1628 lu._VerifyInstance(self.diskless_inst, self.node_imgs, {})
1629 self.mcpu.assertLogIsEmpty()
1630
1631 @withLockedLU
1632 def testOfflineNode(self, lu):
1633 self.master_img.offline = True
1634 lu._VerifyInstance(self.drbd_inst, self.node_imgs, {})
1635 self.mcpu.assertLogIsEmpty()
1636
1637 @withLockedLU
1638 def testRunningOnOfflineNode(self, lu):
1639 self.master_img.offline = True
1640 lu._VerifyInstance(self.running_inst, self.node_imgs, {})
1641 self.mcpu.assertLogContainsRegex(
1642 "instance is marked as running and lives on offline node")
1643
1644 @withLockedLU
1645 def testMissingVolume(self, lu):
1646 self.master_img.volumes = []
1647 lu._VerifyInstance(self.running_inst, self.node_imgs, {})
1648 self.mcpu.assertLogContainsRegex("volume .* missing")
1649
1650 @withLockedLU
1651 def testRunningInstanceOnWrongNode(self, lu):
1652 self.master_img.instances = []
1653 self.diskless_inst.admin_state = constants.ADMINST_UP
1654 lu._VerifyInstance(self.running_inst, self.node_imgs, {})
1655 self.mcpu.assertLogContainsRegex("instance not running on its primary node")
1656
1657 @withLockedLU
1658 def testRunningInstanceOnRightNode(self, lu):
1659 self.master_img.instances = [self.running_inst.uuid]
1660 lu._VerifyInstance(self.running_inst, self.node_imgs, {})
1661 self.mcpu.assertLogIsEmpty()
1662
1663 @withLockedLU
1664 def testValidDiskStatus(self, lu):
1665 lu._VerifyInstance(self.running_inst, self.node_imgs, self.diskstatus)
1666 self.mcpu.assertLogIsEmpty()
1667
1668 @withLockedLU
1669 def testDegradedDiskStatus(self, lu):
1670 self.diskstatus[self.master_uuid][0][1].is_degraded = True
1671 lu._VerifyInstance(self.running_inst, self.node_imgs, self.diskstatus)
1672 self.mcpu.assertLogContainsRegex("instance .* is degraded")
1673
1674 @withLockedLU
1675 def testNotOkayDiskStatus(self, lu):
1676 self.diskstatus[self.master_uuid][0][1].ldisk_status = constants.LDS_FAULTY
1677 lu._VerifyInstance(self.running_inst, self.node_imgs, self.diskstatus)
1678 self.mcpu.assertLogContainsRegex("instance .* state is 'faulty'")
1679
1680 @withLockedLU
1681 def testExclusiveStorageWithInvalidInstance(self, lu):
1682 self.master.ndparams[constants.ND_EXCLUSIVE_STORAGE] = True
1683 lu._VerifyInstance(self.drbd_inst, self.node_imgs, self.diskstatus)
1684 self.mcpu.assertLogContainsRegex(
1685 "instance has template drbd, which is not supported")
1686
1687 @withLockedLU
1688 def testExclusiveStorageWithValidInstance(self, lu):
1689 self.master.ndparams[constants.ND_EXCLUSIVE_STORAGE] = True
1690 running_inst_disks = self.cfg.GetInstanceDisks(self.running_inst.uuid)
1691 running_inst_disks[0].spindles = 1
1692 feedback_fn = lambda _: None
1693 self.cfg.Update(running_inst_disks[0], feedback_fn)
1694 lu._VerifyInstance(self.running_inst, self.node_imgs, self.diskstatus)
1695 self.mcpu.assertLogIsEmpty()
1696
1697 @withLockedLU
1698 def testDrbdInTwoGroups(self, lu):
1699 group = self.cfg.AddNewNodeGroup()
1700 self.node1.group = group.uuid
1701 lu._VerifyInstance(self.drbd_inst, self.node_imgs, self.diskstatus)
1702 self.mcpu.assertLogContainsRegex(
1703 "instance has primary and secondary nodes in different groups")
1704
1705 @withLockedLU
1706 def testOfflineSecondary(self, lu):
1707 self.node1_img.offline = True
1708 lu._VerifyInstance(self.drbd_inst, self.node_imgs, self.diskstatus)
1709 self.mcpu.assertLogContainsRegex("instance has offline secondary node\(s\)")
1710
1711
1712 class TestLUClusterVerifyGroupVerifyOrphanVolumes(
1713 TestLUClusterVerifyGroupMethods):
1714 @withLockedLU
1715 def testOrphanedVolume(self, lu):
1716 master_img = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
1717 master_img.volumes = [
1718 "mock_vg/disk_0", # Required, present, no error
1719 "mock_vg/disk_1", # Unknown, present, orphan
1720 "mock_vg/disk_2", # Reserved, present, no error
1721 "other_vg/disk_0", # Required, present, no error
1722 "other_vg/disk_1", # Unknown, present, no error
1723 ]
1724 node_imgs = {
1725 self.master_uuid: master_img
1726 }
1727 node_vol_should = {
1728 self.master_uuid: ["mock_vg/disk_0", "other_vg/disk_0", "other_vg/disk_1"]
1729 }
1730
1731 lu._VerifyOrphanVolumes("mock_vg", node_vol_should, node_imgs,
1732 utils.FieldSet("mock_vg/disk_2"))
1733 self.mcpu.assertLogContainsRegex("volume mock_vg/disk_1 is unknown")
1734 self.mcpu.assertLogDoesNotContainRegex("volume mock_vg/disk_0 is unknown")
1735 self.mcpu.assertLogDoesNotContainRegex("volume mock_vg/disk_2 is unknown")
1736 self.mcpu.assertLogDoesNotContainRegex("volume other_vg/disk_0 is unknown")
1737 self.mcpu.assertLogDoesNotContainRegex("volume other_vg/disk_1 is unknown")
1738
1739
1740 class TestLUClusterVerifyGroupVerifyNPlusOneMemory(
1741 TestLUClusterVerifyGroupMethods):
1742 @withLockedLU
1743 def testN1Failure(self, lu):
1744 group1 = self.cfg.AddNewNodeGroup()
1745
1746 node1 = self.cfg.AddNewNode()
1747 node2 = self.cfg.AddNewNode(group=group1)
1748 node3 = self.cfg.AddNewNode()
1749
1750 inst1 = self.cfg.AddNewInstance()
1751 inst2 = self.cfg.AddNewInstance()
1752 inst3 = self.cfg.AddNewInstance()
1753
1754 node1_img = cluster.LUClusterVerifyGroup.NodeImage(uuid=node1.uuid)
1755 node1_img.sbp = {
1756 self.master_uuid: [inst1.uuid, inst2.uuid, inst3.uuid]
1757 }
1758
1759 node2_img = cluster.LUClusterVerifyGroup.NodeImage(uuid=node2.uuid)
1760
1761 node3_img = cluster.LUClusterVerifyGroup.NodeImage(uuid=node3.uuid)
1762 node3_img.offline = True
1763
1764 node_imgs = {
1765 node1.uuid: node1_img,
1766 node2.uuid: node2_img,
1767 node3.uuid: node3_img
1768 }
1769
1770 lu._VerifyNPlusOneMemory(node_imgs, self.cfg.GetAllInstancesInfo())
1771 self.mcpu.assertLogContainsRegex(
1772 "not enough memory to accomodate instance failovers")
1773
1774 self.mcpu.ClearLogMessages()
1775 node1_img.mfree = 1000
1776 lu._VerifyNPlusOneMemory(node_imgs, self.cfg.GetAllInstancesInfo())
1777 self.mcpu.assertLogIsEmpty()
1778
1779
1780 class TestLUClusterVerifyGroupVerifyFiles(TestLUClusterVerifyGroupMethods):
1781 @withLockedLU
1782 def test(self, lu):
1783 node1 = self.cfg.AddNewNode(master_candidate=False, offline=False,
1784 vm_capable=True)
1785 node2 = self.cfg.AddNewNode(master_candidate=True, vm_capable=False)
1786 node3 = self.cfg.AddNewNode(master_candidate=False, offline=False,
1787 vm_capable=True)
1788 node4 = self.cfg.AddNewNode(master_candidate=False, offline=False,
1789 vm_capable=True)
1790 node5 = self.cfg.AddNewNode(master_candidate=False, offline=True)
1791
1792 nodeinfo = [self.master, node1, node2, node3, node4, node5]
1793 files_all = set([
1794 pathutils.CLUSTER_DOMAIN_SECRET_FILE,
1795 pathutils.RAPI_CERT_FILE,
1796 pathutils.RAPI_USERS_FILE,
1797 ])
1798 files_opt = set([
1799 pathutils.RAPI_USERS_FILE,
1800 hv_xen.XL_CONFIG_FILE,
1801 pathutils.VNC_PASSWORD_FILE,
1802 ])
1803 files_mc = set([
1804 pathutils.CLUSTER_CONF_FILE,
1805 ])
1806 files_vm = set([
1807 hv_xen.XEND_CONFIG_FILE,
1808 hv_xen.XL_CONFIG_FILE,
1809 pathutils.VNC_PASSWORD_FILE,
1810 ])
1811 nvinfo = RpcResultsBuilder() \
1812 .AddSuccessfulNode(self.master, {
1813 constants.NV_FILELIST: {
1814 pathutils.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
1815 pathutils.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
1816 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
1817 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
1818 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
1819 }}) \
1820 .AddSuccessfulNode(node1, {
1821 constants.NV_FILELIST: {
1822 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
1823 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
1824 }
1825 }) \
1826 .AddSuccessfulNode(node2, {
1827 constants.NV_FILELIST: {
1828 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
1829 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
1830 }
1831 }) \
1832 .AddSuccessfulNode(node3, {
1833 constants.NV_FILELIST: {
1834 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
1835 pathutils.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
1836 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
1837 pathutils.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
1838 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
1839 }
1840 }) \
1841 .AddSuccessfulNode(node4, {}) \
1842 .AddOfflineNode(node5) \
1843 .Build()
1844 assert set(nvinfo.keys()) == set(map(operator.attrgetter("uuid"), nodeinfo))
1845
1846 lu._VerifyFiles(nodeinfo, self.master_uuid, nvinfo,
1847 (files_all, files_opt, files_mc, files_vm))
1848
1849 expected_msgs = [
1850 "File %s found with 2 different checksums (variant 1 on"
1851 " %s, %s, %s; variant 2 on %s)" %
1852 (pathutils.RAPI_CERT_FILE, node1.name, node2.name, node3.name,
1853 self.master.name),
1854 "File %s is missing from node(s) %s" %
1855 (pathutils.CLUSTER_DOMAIN_SECRET_FILE, node1.name),
1856 "File %s should not exist on node(s) %s" %
1857 (pathutils.CLUSTER_CONF_FILE, node3.name),
1858 "File %s is missing from node(s) %s" %
1859 (hv_xen.XEND_CONFIG_FILE, node3.name),
1860 "File %s is missing from node(s) %s" %
1861 (pathutils.CLUSTER_CONF_FILE, node2.name),
1862 "File %s found with 2 different checksums (variant 1 on"
1863 " %s; variant 2 on %s)" %
1864 (pathutils.CLUSTER_CONF_FILE, self.master.name, node3.name),
1865 "File %s is optional, but it must exist on all or no nodes (not"
1866 " found on %s, %s, %s)" %
1867 (pathutils.RAPI_USERS_FILE, self.master.name, node1.name, node2.name),
1868 "File %s is optional, but it must exist on all or no nodes (not"
1869 " found on %s)" % (hv_xen.XL_CONFIG_FILE, node1.name),
1870 "Node did not return file checksum data",
1871 ]
1872
1873 self.assertEqual(len(self.mcpu.GetLogMessages()), len(expected_msgs))
1874 for expected_msg in expected_msgs:
1875 self.mcpu.assertLogContainsInLine(expected_msg)
1876
1877
1878 class TestLUClusterVerifyGroupVerifyNodeOs(TestLUClusterVerifyGroupMethods):
1879 @withLockedLU
1880 def testUpdateNodeOsInvalidNodeResult(self, lu):
1881 for ndata in [{}, {constants.NV_OSLIST: ""}, {constants.NV_OSLIST: [""]},
1882 {constants.NV_OSLIST: [["1", "2"]]}]:
1883 self.mcpu.ClearLogMessages()
1884 nimage = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
1885 lu._UpdateNodeOS(self.master, ndata, nimage)
1886 self.mcpu.assertLogContainsRegex("node hasn't returned valid OS data")
1887
1888 @withLockedLU
1889 def testUpdateNodeOsValidNodeResult(self, lu):
1890 ndata = {
1891 constants.NV_OSLIST: [
1892 ["mock_OS", "/mocked/path", True, "", ["default"], [],
1893 [constants.OS_API_V20], True],
1894 ["Another_Mock", "/random", True, "", ["var1", "var2"],
1895 [{"param1": "val1"}, {"param2": "val2"}], constants.OS_API_VERSIONS,
1896 True]
1897 ]
1898 }
1899 nimage = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
1900 lu._UpdateNodeOS(self.master, ndata, nimage)
1901 self.mcpu.assertLogIsEmpty()
1902
1903 @withLockedLU
1904 def testVerifyNodeOs(self, lu):
1905 node = self.cfg.AddNewNode()
1906 nimg_root = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
1907 nimg = cluster.LUClusterVerifyGroup.NodeImage(uuid=node.uuid)
1908
1909 nimg_root.os_fail = False
1910 nimg_root.oslist = {
1911 "mock_os": [("/mocked/path", True, "", set(["default"]), set(),
1912 set([constants.OS_API_V20]), True)],
1913 "broken_base_os": [("/broken", False, "", set(), set(),
1914 set([constants.OS_API_V20]), True)],
1915 "only_on_root": [("/random", True, "", set(), set(), set(), True)],
1916 "diffing_os": [("/pinky", True, "", set(["var1", "var2"]),
1917 set([("param1", "val1"), ("param2", "val2")]),
1918 set([constants.OS_API_V20]), True)],
1919 "trust_os": [("/trust/mismatch", True, "", set(), set(), set(), True)],
1920 }
1921 nimg.os_fail = False
1922 nimg.oslist = {
1923 "mock_os": [("/mocked/path", True, "", set(["default"]), set(),
1924 set([constants.OS_API_V20]), True)],
1925 "only_on_test": [("/random", True, "", set(), set(), set(), True)],
1926 "diffing_os": [("/bunny", True, "", set(["var1", "var3"]),
1927 set([("param1", "val1"), ("param3", "val3")]),
1928 set([constants.OS_API_V15]), True)],
1929 "broken_os": [("/broken", False, "", set(), set(),
1930 set([constants.OS_API_V20]), True)],
1931 "multi_entries": [
1932 ("/multi1", True, "", set(), set(), set([constants.OS_API_V20]), True),
1933 ("/multi2", True, "", set(), set(), set([constants.OS_API_V20]), True)],
1934 "trust_os": [("/trust/mismatch", True, "", set(), set(), set(), False)],
1935 }
1936
1937 lu._VerifyNodeOS(node, nimg, nimg_root)
1938
1939 expected_msgs = [
1940 "Extra OS only_on_test not present on reference node",
1941 "OSes present on reference node .* but missing on this node:" +
1942 " only_on_root",
1943 "OS API version for diffing_os differs",
1944 "OS variants list for diffing_os differs",
1945 "OS parameters for diffing_os differs",
1946 "Invalid OS broken_os",
1947 "Extra OS broken_os not present on reference node",
1948 "OS 'multi_entries' has multiple entries",
1949 "Extra OS multi_entries not present on reference node",
1950 "OS trusted for trust_os differs from reference node "
1951 ]
1952
1953 self.assertEqual(len(expected_msgs), len(self.mcpu.GetLogMessages()))
1954 for expected_msg in expected_msgs:
1955 self.mcpu.assertLogContainsRegex(expected_msg)
1956
1957
1958 class TestLUClusterVerifyGroupVerifyAcceptedFileStoragePaths(
1959 TestLUClusterVerifyGroupMethods):
1960 @withLockedLU
1961 def testNotMaster(self, lu):
1962 lu._VerifyAcceptedFileStoragePaths(self.master, {}, False)
1963 self.mcpu.assertLogIsEmpty()
1964
1965 @withLockedLU
1966 def testNotMasterButRetunedValue(self, lu):
1967 lu._VerifyAcceptedFileStoragePaths(
1968 self.master, {constants.NV_ACCEPTED_STORAGE_PATHS: []}, False)
1969 self.mcpu.assertLogContainsRegex(
1970 "Node should not have returned forbidden file storage paths")
1971
1972 @withLockedLU
1973 def testMasterInvalidNodeResult(self, lu):
1974 lu._VerifyAcceptedFileStoragePaths(self.master, {}, True)
1975 self.mcpu.assertLogContainsRegex(
1976 "Node did not return forbidden file storage paths")
1977
1978 @withLockedLU
1979 def testMasterForbiddenPaths(self, lu):
1980 lu._VerifyAcceptedFileStoragePaths(
1981 self.master, {constants.NV_ACCEPTED_STORAGE_PATHS: ["/forbidden"]}, True)
1982 self.mcpu.assertLogContainsRegex("Found forbidden file storage paths")
1983
1984 @withLockedLU
1985 def testMasterSuccess(self, lu):
1986 lu._VerifyAcceptedFileStoragePaths(
1987 self.master, {constants.NV_ACCEPTED_STORAGE_PATHS: []}, True)
1988 self.mcpu.assertLogIsEmpty()
1989
1990
1991 class TestLUClusterVerifyGroupVerifyStoragePaths(
1992 TestLUClusterVerifyGroupMethods):
1993 @withLockedLU
1994 def testVerifyFileStoragePathsSuccess(self, lu):
1995 lu._VerifyFileStoragePaths(self.master, {})
1996 self.mcpu.assertLogIsEmpty()
1997
1998 @withLockedLU
1999 def testVerifyFileStoragePathsFailure(self, lu):
2000 lu._VerifyFileStoragePaths(self.master,
2001 {constants.NV_FILE_STORAGE_PATH: "/fail/path"})
2002 self.mcpu.assertLogContainsRegex(
2003 "The configured file storage path is unusable")
2004
2005 @withLockedLU
2006 def testVerifySharedFileStoragePathsSuccess(self, lu):
2007 lu._VerifySharedFileStoragePaths(self.master, {})
2008 self.mcpu.assertLogIsEmpty()
2009
2010 @withLockedLU
2011 def testVerifySharedFileStoragePathsFailure(self, lu):
2012 lu._VerifySharedFileStoragePaths(
2013 self.master, {constants.NV_SHARED_FILE_STORAGE_PATH: "/fail/path"})
2014 self.mcpu.assertLogContainsRegex(
2015 "The configured sharedfile storage path is unusable")
2016
2017
2018 class TestLUClusterVerifyGroupVerifyOob(TestLUClusterVerifyGroupMethods):
2019 @withLockedLU
2020 def testEmptyResult(self, lu):
2021 lu._VerifyOob(self.master, {})
2022 self.mcpu.assertLogIsEmpty()
2023
2024 @withLockedLU
2025 def testErrorResults(self, lu):
2026 lu._VerifyOob(self.master, {constants.NV_OOB_PATHS: ["path1", "path2"]})
2027 self.mcpu.assertLogContainsRegex("path1")
2028 self.mcpu.assertLogContainsRegex("path2")
2029
2030
2031 class TestLUClusterVerifyGroupUpdateNodeVolumes(
2032 TestLUClusterVerifyGroupMethods):
2033 def setUp(self):
2034 super(TestLUClusterVerifyGroupUpdateNodeVolumes, self).setUp()
2035 self.nimg = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
2036
2037 @withLockedLU
2038 def testNoVgName(self, lu):
2039 lu._UpdateNodeVolumes(self.master, {}, self.nimg, None)
2040 self.mcpu.assertLogIsEmpty()
2041 self.assertTrue(self.nimg.lvm_fail)
2042
2043 @withLockedLU
2044 def testErrorMessage(self, lu):
2045 lu._UpdateNodeVolumes(self.master, {constants.NV_LVLIST: "mock error"},
2046 self.nimg, "mock_vg")
2047 self.mcpu.assertLogContainsRegex("LVM problem on node: mock error")
2048 self.assertTrue(self.nimg.lvm_fail)
2049
2050 @withLockedLU
2051 def testInvalidNodeResult(self, lu):
2052 lu._UpdateNodeVolumes(self.master, {constants.NV_LVLIST: [1, 2, 3]},
2053 self.nimg, "mock_vg")
2054 self.mcpu.assertLogContainsRegex("rpc call to node failed")
2055 self.assertTrue(self.nimg.lvm_fail)
2056
2057 @withLockedLU
2058 def testValidNodeResult(self, lu):
2059 lu._UpdateNodeVolumes(self.master, {constants.NV_LVLIST: {}},
2060 self.nimg, "mock_vg")
2061 self.mcpu.assertLogIsEmpty()
2062 self.assertFalse(self.nimg.lvm_fail)
2063
2064
2065 class TestLUClusterVerifyGroupUpdateNodeInstances(
2066 TestLUClusterVerifyGroupMethods):
2067 def setUp(self):
2068 super(TestLUClusterVerifyGroupUpdateNodeInstances, self).setUp()
2069 self.nimg = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
2070
2071 @withLockedLU
2072 def testInvalidNodeResult(self, lu):
2073 lu._UpdateNodeInstances(self.master, {}, self.nimg)
2074 self.mcpu.assertLogContainsRegex("rpc call to node failed")
2075
2076 @withLockedLU
2077 def testValidNodeResult(self, lu):
2078 inst = self.cfg.AddNewInstance()
2079 lu._UpdateNodeInstances(self.master,
2080 {constants.NV_INSTANCELIST: [inst.name]},
2081 self.nimg)
2082 self.mcpu.assertLogIsEmpty()
2083
2084
2085 class TestLUClusterVerifyGroupUpdateNodeInfo(TestLUClusterVerifyGroupMethods):
2086 def setUp(self):
2087 super(TestLUClusterVerifyGroupUpdateNodeInfo, self).setUp()
2088 self.nimg = cluster.LUClusterVerifyGroup.NodeImage(uuid=self.master_uuid)
2089 self.valid_hvresult = {constants.NV_HVINFO: {"memory_free": 1024}}
2090
2091 @withLockedLU
2092 def testInvalidHvNodeResult(self, lu):
2093 for ndata in [{}, {constants.NV_HVINFO: ""}]:
2094 self.mcpu.ClearLogMessages()
2095 lu._UpdateNodeInfo(self.master, ndata, self.nimg, None)
2096 self.mcpu.assertLogContainsRegex("rpc call to node failed")
2097
2098 @withLockedLU
2099 def testInvalidMemoryFreeHvNodeResult(self, lu):
2100 lu._UpdateNodeInfo(self.master,
2101 {constants.NV_HVINFO: {"memory_free": "abc"}},
2102 self.nimg, None)
2103 self.mcpu.assertLogContainsRegex(
2104 "node returned invalid nodeinfo, check hypervisor")
2105
2106 @withLockedLU
2107 def testValidHvNodeResult(self, lu):
2108 lu._UpdateNodeInfo(self.master, self.valid_hvresult, self.nimg, None)
2109 self.mcpu.assertLogIsEmpty()
2110
2111 @withLockedLU
2112 def testInvalidVgNodeResult(self, lu):
2113 for vgdata in [[], ""]:
2114 self.mcpu.ClearLogMessages()
2115 ndata = {constants.NV_VGLIST: vgdata}
2116 ndata.update(self.valid_hvresult)
2117 lu._UpdateNodeInfo(self.master, ndata, self.nimg, "mock_vg")
2118 self.mcpu.assertLogContainsRegex(
2119 "node didn't return data for the volume group 'mock_vg'")
2120
2121 @withLockedLU
2122 def testInvalidDiskFreeVgNodeResult(self, lu):
2123 self.valid_hvresult.update({
2124 constants.NV_VGLIST: {"mock_vg": "abc"}
2125 })
2126 lu._UpdateNodeInfo(self.master, self.valid_hvresult, self.nimg, "mock_vg")
2127 self.mcpu.assertLogContainsRegex(
2128 "node returned invalid LVM info, check LVM status")
2129
2130 @withLockedLU
2131 def testValidVgNodeResult(self, lu):
2132 self.valid_hvresult.update({
2133 constants.NV_VGLIST: {"mock_vg": 10000}
2134 })
2135 lu._UpdateNodeInfo(self.master, self.valid_hvresult, self.nimg, "mock_vg")
2136 self.mcpu.assertLogIsEmpty()
2137
2138
2139 class TestLUClusterVerifyGroupCollectDiskInfo(TestLUClusterVerifyGroupMethods):
2140 def setUp(self):
2141 super(TestLUClusterVerifyGroupCollectDiskInfo, self).setUp()
2142
2143 self.node1 = self.cfg.AddNewNode()
2144 self.node2 = self.cfg.AddNewNode()
2145 self.node3 = self.cfg.AddNewNode()
2146
2147 self.diskless_inst = \
2148 self.cfg.AddNewInstance(primary_node=self.node1,
2149 disk_template=constants.DT_DISKLESS)
2150 self.plain_inst = \
2151 self.cfg.AddNewInstance(primary_node=self.node2,
2152 disk_template=constants.DT_PLAIN)
2153 self.drbd_inst = \
2154 self.cfg.AddNewInstance(primary_node=self.node3,
2155 secondary_node=self.node2,
2156 disk_template=constants.DT_DRBD8)
2157
2158 self.node1_img = cluster.LUClusterVerifyGroup.NodeImage(
2159 uuid=self.node1.uuid)
2160 self.node1_img.pinst = [self.diskless_inst.uuid]
2161 self.node1_img.sinst = []
2162 self.node2_img = cluster.LUClusterVerifyGroup.NodeImage(
2163 uuid=self.node2.uuid)
2164 self.node2_img.pinst = [self.plain_inst.uuid]
2165 self.node2_img.sinst = [self.drbd_inst.uuid]
2166 self.node3_img = cluster.LUClusterVerifyGroup.NodeImage(
2167 uuid=self.node3.uuid)
2168 self.node3_img.pinst = [self.drbd_inst.uuid]
2169 self.node3_img.sinst = []
2170
2171 self.node_images = {
2172 self.node1.uuid: self.node1_img,
2173 self.node2.uuid: self.node2_img,
2174 self.node3.uuid: self.node3_img
2175 }
2176
2177 self.node_uuids = [self.node1.uuid, self.node2.uuid, self.node3.uuid]
2178
2179 @withLockedLU
2180 def testSuccessfulRun(self, lu):
2181 self.rpc.call_blockdev_getmirrorstatus_multi.return_value = \
2182 RpcResultsBuilder() \
2183 .AddSuccessfulNode(self.node2, [(True, ""), (True, "")]) \
2184 .AddSuccessfulNode(self.node3, [(True, "")]) \
2185 .Build()
2186
2187 lu._CollectDiskInfo(self.node_uuids, self.node_images,
2188 self.cfg.GetAllInstancesInfo())
2189
2190 self.mcpu.assertLogIsEmpty()
2191
2192 @withLockedLU
2193 def testOfflineAndFailingNodes(self, lu):
2194 self.rpc.call_blockdev_getmirrorstatus_multi.return_value = \
2195 RpcResultsBuilder() \
2196 .AddOfflineNode(self.node2) \
2197 .AddFailedNode(self.node3) \
2198 .Build()
2199
2200 lu._CollectDiskInfo(self.node_uuids, self.node_images,
2201 self.cfg.GetAllInstancesInfo())
2202
2203 self.mcpu.assertLogContainsRegex("while getting disk information")
2204
2205 @withLockedLU
2206 def testInvalidNodeResult(self, lu):
2207 self.rpc.call_blockdev_getmirrorstatus_multi.return_value = \
2208 RpcResultsBuilder() \
2209 .AddSuccessfulNode(self.node2, [(True,), (False,)]) \
2210 .AddSuccessfulNode(self.node3, [""]) \
2211 .Build()
2212
2213 lu._CollectDiskInfo(self.node_uuids, self.node_images,
2214 self.cfg.GetAllInstancesInfo())
2215 # logging is not performed through mcpu
2216 self.mcpu.assertLogIsEmpty()
2217
2218
2219 class TestLUClusterVerifyGroupHooksCallBack(TestLUClusterVerifyGroupMethods):
2220 def setUp(self):
2221 super(TestLUClusterVerifyGroupHooksCallBack, self).setUp()
2222
2223 self.feedback_fn = lambda _: None
2224
2225 def PrepareLU(self, lu):
2226 super(TestLUClusterVerifyGroupHooksCallBack, self).PrepareLU(lu)
2227
2228 lu.my_node_uuids = list(self.cfg.GetAllNodesInfo().keys())
2229
2230 @withLockedLU
2231 def testEmptyGroup(self, lu):
2232 lu.my_node_uuids = []
2233 lu.HooksCallBack(constants.HOOKS_PHASE_POST, None, self.feedback_fn, None)
2234
2235 @withLockedLU
2236 def testFailedResult(self, lu):
2237 lu.HooksCallBack(constants.HOOKS_PHASE_POST,
2238 RpcResultsBuilder(use_node_names=True)
2239 .AddFailedNode(self.master).Build(),
2240 self.feedback_fn,
2241 None)
2242 self.mcpu.assertLogContainsRegex("Communication failure in hooks execution")
2243
2244 @withLockedLU
2245 def testOfflineNode(self, lu):
2246 lu.HooksCallBack(constants.HOOKS_PHASE_POST,
2247 RpcResultsBuilder(use_node_names=True)
2248 .AddOfflineNode(self.master).Build(),
2249 self.feedback_fn,
2250 None)
2251
2252 @withLockedLU
2253 def testValidResult(self, lu):
2254 lu.HooksCallBack(constants.HOOKS_PHASE_POST,
2255 RpcResultsBuilder(use_node_names=True)
2256 .AddSuccessfulNode(self.master,
2257 [("mock_script",
2258 constants.HKR_SUCCESS,
2259 "mock output")])
2260 .Build(),
2261 self.feedback_fn,
2262 None)
2263
2264 @withLockedLU
2265 def testFailedScriptResult(self, lu):
2266 lu.HooksCallBack(constants.HOOKS_PHASE_POST,
2267 RpcResultsBuilder(use_node_names=True)
2268 .AddSuccessfulNode(self.master,
2269 [("mock_script",
2270 constants.HKR_FAIL,
2271 "mock output")])
2272 .Build(),
2273 self.feedback_fn,
2274 None)
2275 self.mcpu.assertLogContainsRegex("Script mock_script failed")
2276
2277
2278 class TestLUClusterVerifyDisks(CmdlibTestCase):
2279 def testVerifyDisks(self):
2280 op = opcodes.OpClusterVerifyDisks()
2281 result = self.ExecOpCode(op)
2282
2283 self.assertEqual(1, len(result["jobs"]))
2284
2285
2286 class TestLUClusterRenewCrypto(CmdlibTestCase):
2287
2288 def setUp(self):
2289 super(TestLUClusterRenewCrypto, self).setUp()
2290 self._node_cert = self._CreateTempFile()
2291 shutil.copy(testutils.TestDataFilename("cert1.pem"), self._node_cert)
2292 self._client_node_cert = self._CreateTempFile()
2293 shutil.copy(testutils.TestDataFilename("cert2.pem"), self._client_node_cert)
2294 self._client_node_cert_digest = \
2295 "BF:24:F7:57:50:60:43:87:83:E3:0D:7E:EF:DD:14:6C:13:43:20:4E"
2296
2297 def tearDown(self):
2298 super(TestLUClusterRenewCrypto, self).tearDown()
2299
2300 def _GetFakeDigest(self, uuid):
2301 """Creates a fake SSL digest depending on the UUID of a node.
2302
2303 @type uuid: string
2304 @param uuid: node UUID
2305 @returns: a string impersonating a SSL digest
2306
2307 """
2308 return "FA:KE:%s:%s:%s:%s" % (uuid[0:2], uuid[2:4], uuid[4:6], uuid[6:8])
2309
2310 def _InitPathutils(self, pathutils):
2311 """Patch pathutils to point to temporary files.
2312
2313 """
2314 pathutils.NODED_CERT_FILE = self._node_cert
2315 pathutils.NODED_CLIENT_CERT_FILE = self._client_node_cert
2316
2317 def _AssertCertFiles(self, pathutils):
2318 """Check if the correct certificates exist and don't exist on the master.
2319
2320 """
2321 self.assertTrue(os.path.exists(pathutils.NODED_CERT_FILE))
2322 self.assertTrue(os.path.exists(pathutils.NODED_CLIENT_CERT_FILE))
2323
2324 def _CompletelySuccessfulRpc(self, node_uuid, _):
2325 """Fake RPC call which always returns successfully.
2326
2327 """
2328 return self.RpcResultsBuilder() \
2329 .CreateSuccessfulNodeResult(node_uuid,
2330 [(constants.CRYPTO_TYPE_SSL_DIGEST,
2331 self._GetFakeDigest(node_uuid))])
2332
2333 @patchPathutils("cluster")
2334 def testSuccessfulCase(self, pathutils):
2335 self._InitPathutils(pathutils)
2336
2337 # create a few non-master, online nodes
2338 num_nodes = 3
2339 for _ in range(num_nodes):
2340 self.cfg.AddNewNode()
2341 self.rpc.call_node_crypto_tokens = self._CompletelySuccessfulRpc
2342
2343 op = opcodes.OpClusterRenewCrypto()
2344 self.ExecOpCode(op)
2345
2346 self._AssertCertFiles(pathutils)
2347
2348 # Check if we have the correct digests in the configuration
2349 cluster = self.cfg.GetClusterInfo()
2350 self.assertEqual(num_nodes + 1, len(cluster.candidate_certs))
2351 nodes = self.cfg.GetAllNodesInfo()
2352 master_uuid = self.cfg.GetMasterNode()
2353 for (node_uuid, _) in nodes.items():
2354 if node_uuid == master_uuid:
2355 # The master digest is from the actual test certificate.
2356 self.assertEqual(self._client_node_cert_digest,
2357 cluster.candidate_certs[node_uuid])
2358 else:
2359 # The non-master nodes have the fake digest from the
2360 # mock RPC.
2361 expected_digest = self._GetFakeDigest(node_uuid)
2362 self.assertEqual(expected_digest, cluster.candidate_certs[node_uuid])
2363
2364 def _partiallyFailingRpc(self, node_uuid, _):
2365 if node_uuid == self._failed_node:
2366 return self.RpcResultsBuilder() \
2367 .CreateFailedNodeResult(node_uuid)
2368 else:
2369 return self.RpcResultsBuilder() \
2370 .CreateSuccessfulNodeResult(node_uuid,
2371 [(constants.CRYPTO_TYPE_SSL_DIGEST, self._GetFakeDigest(node_uuid))])
2372
2373 @patchPathutils("cluster")
2374 def testNonMasterFails(self, pathutils):
2375 self._InitPathutils(pathutils)
2376
2377 # create a few non-master, online nodes
2378 num_nodes = 3
2379 for _ in range(num_nodes):
2380 self.cfg.AddNewNode()
2381 nodes = self.cfg.GetAllNodesInfo()
2382
2383 # pick one node as the failing one
2384 master_uuid = self.cfg.GetMasterNode()
2385 self._failed_node = [node_uuid for node_uuid in nodes
2386 if node_uuid != master_uuid][1]
2387 self.rpc.call_node_crypto_tokens = self._partiallyFailingRpc
2388
2389 op = opcodes.OpClusterRenewCrypto()
2390 self.ExecOpCode(op)
2391
2392 self._AssertCertFiles(pathutils)
2393
2394 # Check if we have the correct digests in the configuration
2395 cluster = self.cfg.GetClusterInfo()
2396 # There should be one digest missing.
2397 self.assertEqual(num_nodes, len(cluster.candidate_certs))
2398 nodes = self.cfg.GetAllNodesInfo()
2399 for (node_uuid, _) in nodes.items():
2400 if node_uuid == self._failed_node:
2401 self.assertTrue(node_uuid not in cluster.candidate_certs)
2402 else:
2403 self.assertTrue(node_uuid in cluster.candidate_certs)
2404
2405 @patchPathutils("cluster")
2406 def testOfflineNodes(self, pathutils):
2407 self._InitPathutils(pathutils)
2408
2409 # create a few non-master, online nodes
2410 num_nodes = 3
2411 offline_index = 1
2412 for i in range(num_nodes):
2413 # Pick one node to be offline.
2414 self.cfg.AddNewNode(offline=(i==offline_index))
2415 self.rpc.call_node_crypto_tokens = self._CompletelySuccessfulRpc
2416
2417 op = opcodes.OpClusterRenewCrypto()
2418 self.ExecOpCode(op)
2419
2420 self._AssertCertFiles(pathutils)
2421
2422 # Check if we have the correct digests in the configuration
2423 cluster = self.cfg.GetClusterInfo()
2424 # There should be one digest missing.
2425 self.assertEqual(num_nodes, len(cluster.candidate_certs))
2426 nodes = self.cfg.GetAllNodesInfo()
2427 for (node_uuid, node_info) in nodes.items():
2428 if node_info.offline == True:
2429 self.assertTrue(node_uuid not in cluster.candidate_certs)
2430 else:
2431 self.assertTrue(node_uuid in cluster.candidate_certs)
2432
2433 def _RpcSuccessfulAfterRetries(self, node_uuid, _):
2434 if self._retries < self._max_retries:
2435 self._retries += 1
2436 return self.RpcResultsBuilder() \
2437 .CreateFailedNodeResult(node_uuid)
2438 else:
2439 return self.RpcResultsBuilder() \
2440 .CreateSuccessfulNodeResult(node_uuid,
2441 [(constants.CRYPTO_TYPE_SSL_DIGEST, self._GetFakeDigest(node_uuid))])
2442
2443 def _RpcSuccessfulAfterRetriesNonMaster(self, node_uuid, _):
2444 if self._retries < self._max_retries and node_uuid != self._master_uuid:
2445 self._retries += 1
2446 return self.RpcResultsBuilder() \
2447 .CreateFailedNodeResult(node_uuid)
2448 else:
2449 return self.RpcResultsBuilder() \
2450 .CreateSuccessfulNodeResult(node_uuid,
2451 [(constants.CRYPTO_TYPE_SSL_DIGEST, self._GetFakeDigest(node_uuid))])
2452
2453 def _NonMasterRetries(self, pathutils, max_retries):
2454 self._InitPathutils(pathutils)
2455
2456 self._master_uuid = self.cfg.GetMasterNode()
2457 self._max_retries = max_retries
2458 self._retries = 0
2459 self.rpc.call_node_crypto_tokens = self._RpcSuccessfulAfterRetriesNonMaster
2460
2461 # Add one non-master node
2462 self.cfg.AddNewNode()
2463
2464 op = opcodes.OpClusterRenewCrypto()
2465 self.ExecOpCode(op)
2466
2467 self._AssertCertFiles(pathutils)
2468
2469 return self.cfg.GetClusterInfo()
2470
2471 @patchPathutils("cluster")
2472 def testNonMasterRetriesSuccess(self, pathutils):
2473 cluster = self._NonMasterRetries(pathutils, 2)
2474 self.assertEqual(2, len(cluster.candidate_certs.values()))
2475
2476 @patchPathutils("cluster")
2477 def testNonMasterRetriesFail(self, pathutils):
2478 cluster = self._NonMasterRetries(pathutils, 5)
2479
2480 # Only the master digest should be in the cert list
2481 self.assertEqual(1, len(cluster.candidate_certs.values()))
2482 self.assertTrue(self._master_uuid in cluster.candidate_certs)
2483
2484
2485 if __name__ == "__main__":
2486 testutils.GanetiTestProgram()