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