4 # Copyright (C) 2008, 2011, 2012, 2013 Google Inc.
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
11 # 1. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
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.
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.
31 """Tests for LUCluster*
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
55 from testsupport
import *
60 class TestClusterVerifySsh(unittest
.TestCase
):
61 def testMultipleGroups(self
):
62 fn
= cluster
.LUClusterVerifyGroup
._SelectSshCheckNodes
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),
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),
84 assert not utils
.FindDuplicates(map(operator
.attrgetter("name"), nodes
))
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
))
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"],
99 def testSingleGroup(self
):
100 fn
= cluster
.LUClusterVerifyGroup
._SelectSshCheckNodes
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),
107 assert not utils
.FindDuplicates(map(operator
.attrgetter("name"), nodes
))
109 (online
, perhost
) = fn(nodes
, "default", nodes
)
110 self
.assertEqual(online
, ["node2", "node3"])
111 self
.assertEqual(set(perhost
.keys()), set(online
))
113 self
.assertEqual(perhost
, {
119 class TestLUClusterActivateMasterIp(CmdlibTestCase
):
120 def testSuccess(self
):
121 op
= opcodes
.OpClusterActivateMasterIp()
123 self
.rpc
.call_node_activate_master_ip
.return_value
= \
124 self
.RpcResultsBuilder() \
125 .CreateSuccessfulNodeResult(self
.master
)
129 self
.rpc
.call_node_activate_master_ip
.assert_called_once_with(
130 self
.master_uuid
, self
.cfg
.GetMasterNetworkParameters(), False)
132 def testFailure(self
):
133 op
= opcodes
.OpClusterActivateMasterIp()
135 self
.rpc
.call_node_activate_master_ip
.return_value
= \
136 self
.RpcResultsBuilder() \
137 .CreateFailedNodeResult(self
.master
) \
139 self
.ExecOpCodeExpectOpExecError(op
)
142 class TestLUClusterDeactivateMasterIp(CmdlibTestCase
):
143 def testSuccess(self
):
144 op
= opcodes
.OpClusterDeactivateMasterIp()
146 self
.rpc
.call_node_deactivate_master_ip
.return_value
= \
147 self
.RpcResultsBuilder() \
148 .CreateSuccessfulNodeResult(self
.master
)
152 self
.rpc
.call_node_deactivate_master_ip
.assert_called_once_with(
153 self
.master_uuid
, self
.cfg
.GetMasterNetworkParameters(), False)
155 def testFailure(self
):
156 op
= opcodes
.OpClusterDeactivateMasterIp()
158 self
.rpc
.call_node_deactivate_master_ip
.return_value
= \
159 self
.RpcResultsBuilder() \
160 .CreateFailedNodeResult(self
.master
) \
162 self
.ExecOpCodeExpectOpExecError(op
)
165 class TestLUClusterConfigQuery(CmdlibTestCase
):
166 def testInvalidField(self
):
167 op
= opcodes
.OpClusterConfigQuery(output_fields
=["pinky_bunny"])
169 self
.ExecOpCodeExpectOpPrereqError(op
, "pinky_bunny")
171 def testAllFields(self
):
172 op
= opcodes
.OpClusterConfigQuery(output_fields
=query
.CLUSTER_FIELDS
.keys())
174 self
.rpc
.call_get_watcher_pause
.return_value
= \
175 self
.RpcResultsBuilder() \
176 .CreateSuccessfulNodeResult(self
.master
, -1)
178 ret
= self
.ExecOpCode(op
)
180 self
.assertEqual(1, self
.rpc
.call_get_watcher_pause
.call_count
)
181 self
.assertEqual(len(ret
), len(query
.CLUSTER_FIELDS
))
183 def testEmpytFields(self
):
184 op
= opcodes
.OpClusterConfigQuery(output_fields
=[])
188 self
.assertFalse(self
.rpc
.call_get_watcher_pause
.called
)
191 class TestLUClusterDestroy(CmdlibTestCase
):
192 def testExistingNodes(self
):
193 op
= opcodes
.OpClusterDestroy()
195 self
.cfg
.AddNewNode()
196 self
.cfg
.AddNewNode()
198 self
.ExecOpCodeExpectOpPrereqError(op
, "still 2 node\(s\)")
200 def testExistingInstances(self
):
201 op
= opcodes
.OpClusterDestroy()
203 self
.cfg
.AddNewInstance()
204 self
.cfg
.AddNewInstance()
206 self
.ExecOpCodeExpectOpPrereqError(op
, "still 2 instance\(s\)")
208 def testEmptyCluster(self
):
209 op
= opcodes
.OpClusterDestroy()
213 self
.assertSingleHooksCall([self
.master
.name
],
215 constants
.HOOKS_PHASE_POST
)
218 class TestLUClusterPostInit(CmdlibTestCase
):
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
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()
234 self
.assertSingleHooksCall([self
.master
.uuid
],
236 constants
.HOOKS_PHASE_POST
)
239 class TestLUClusterQuery(CmdlibTestCase
):
240 def testSimpleInvocation(self
):
241 op
= opcodes
.OpClusterQuery()
245 def testIPv6Cluster(self
):
246 op
= opcodes
.OpClusterQuery()
248 self
.cluster
.primary_ip_family
= netutils
.IP6Address
.family
253 class TestLUClusterRedistConf(CmdlibTestCase
):
254 def testSimpleInvocation(self
):
255 op
= opcodes
.OpClusterRedistConf()
260 class TestLUClusterRename(CmdlibTestCase
):
261 NEW_NAME
= "new-name.example.com"
262 NEW_IP
= "203.0.113.100"
264 def testNoChanges(self
):
265 op
= opcodes
.OpClusterRename(name
=self
.cfg
.GetClusterName())
267 self
.ExecOpCodeExpectOpPrereqError(op
, "name nor the IP address")
269 def testReachableIp(self
):
270 op
= opcodes
.OpClusterRename(name
=self
.NEW_NAME
)
272 self
.netutils_mod
.GetHostname
.return_value
= \
273 HostnameMock(self
.NEW_NAME
, self
.NEW_IP
)
274 self
.netutils_mod
.TcpPing
.return_value
= True
276 self
.ExecOpCodeExpectOpPrereqError(op
, "is reachable on the network")
278 def testValidRename(self
):
279 op
= opcodes
.OpClusterRename(name
=self
.NEW_NAME
)
281 self
.netutils_mod
.GetHostname
.return_value
= \
282 HostnameMock(self
.NEW_NAME
, self
.NEW_IP
)
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)
292 def testRenameOfflineMaster(self
):
293 op
= opcodes
.OpClusterRename(name
=self
.NEW_NAME
)
295 self
.master
.offline
= True
296 self
.netutils_mod
.GetHostname
.return_value
= \
297 HostnameMock(self
.NEW_NAME
, self
.NEW_IP
)
302 class TestLUClusterRepairDiskSizes(CmdlibTestCase
):
303 def testNoInstances(self
):
304 op
= opcodes
.OpClusterRepairDiskSizes()
308 def _SetUpInstanceSingleDisk(self
, dev_type
=constants
.DT_PLAIN
):
310 snode
= self
.cfg
.AddNewNode()
312 disk
= self
.cfg
.CreateDisk(dev_type
=dev_type
,
314 secondary_node
=snode
)
315 inst
= self
.cfg
.AddNewInstance(disks
=[disk
])
319 def testSingleInstanceOnFailingNode(self
):
320 (inst
, _
) = self
._SetUpInstanceSingleDisk()
321 op
= opcodes
.OpClusterRepairDiskSizes(instances
=[inst
.name
])
323 self
.rpc
.call_blockdev_getdimensions
.return_value
= \
324 self
.RpcResultsBuilder() \
325 .CreateFailedNodeResult(self
.master
)
329 self
.mcpu
.assertLogContainsRegex("Failure in blockdev_getdimensions")
331 def _ExecOpClusterRepairDiskSizes(self
, node_data
):
332 # not specifying instances repairs all
333 op
= opcodes
.OpClusterRepairDiskSizes()
335 self
.rpc
.call_blockdev_getdimensions
.return_value
= \
336 self
.RpcResultsBuilder() \
337 .CreateSuccessfulNodeResult(self
.master
, node_data
)
339 return self
.ExecOpCode(op
)
341 def testInvalidResultData(self
):
342 for data
in [[], [None], ["invalid"], [("still", "invalid")]]:
345 self
._SetUpInstanceSingleDisk()
346 self
._ExecOpClusterRepairDiskSizes(data
)
348 self
.mcpu
.assertLogContainsRegex("ignoring")
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
))
356 def testWrongSize(self
):
357 self
._SetUpInstanceSingleDisk()
358 changed
= self
._ExecOpClusterRepairDiskSizes([(512 * 1024 * 1024, None)])
359 self
.assertEqual(1, len(changed
))
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
))
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
))
373 def testExclusiveStorageInvalidResultData(self
):
374 self
._SetUpInstanceSingleDisk()
375 self
.master
.ndparams
[constants
.ND_EXCLUSIVE_STORAGE
] = True
376 self
._ExecOpClusterRepairDiskSizes([(1024 * 1024 * 1024, None)])
378 self
.mcpu
.assertLogContainsRegex(
379 "did not return valid spindles information")
381 def testExclusiveStorageCorrectSpindles(self
):
382 (_
, disk
) = self
._SetUpInstanceSingleDisk()
384 self
.master
.ndparams
[constants
.ND_EXCLUSIVE_STORAGE
] = True
385 changed
= self
._ExecOpClusterRepairDiskSizes([(1024 * 1024 * 1024, 1)])
386 self
.assertEqual(0, len(changed
))
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
))
395 class TestLUClusterSetParams(CmdlibTestCase
):
396 UID_POOL
= [(10, 1000)]
398 def testUidPool(self
):
399 op
= opcodes
.OpClusterSetParams(uid_pool
=self
.UID_POOL
)
401 self
.assertEqual(self
.UID_POOL
, self
.cluster
.uid_pool
)
403 def testAddUids(self
):
405 self
.cluster
.uid_pool
= list(old_pool
)
406 op
= opcodes
.OpClusterSetParams(add_uids
=self
.UID_POOL
)
408 self
.assertEqual(set(self
.UID_POOL
+ old_pool
),
409 set(self
.cluster
.uid_pool
))
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
)
416 self
.assertEqual(additional_pool
, self
.cluster
.uid_pool
)
418 def testMacPrefix(self
):
419 mac_prefix
= "aa:01:02"
420 op
= opcodes
.OpClusterSetParams(mac_prefix
=mac_prefix
)
422 self
.assertEqual(mac_prefix
, self
.cluster
.mac_prefix
)
424 def testEmptyMacPrefix(self
):
426 op
= opcodes
.OpClusterSetParams(mac_prefix
=mac_prefix
)
427 self
.ExecOpCodeExpectOpPrereqError(
428 op
, "Parameter 'OP_CLUSTER_SET_PARAMS.mac_prefix' fails validation")
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")
435 def testMasterNetmask(self
):
436 op
= opcodes
.OpClusterSetParams(master_netmask
=26)
438 self
.assertEqual(26, self
.cluster
.master_netmask
)
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"}}]:
445 op
= opcodes
.OpClusterSetParams(diskparams
=diskparams
)
446 self
.ExecOpCodeExpectOpPrereqError(op
, "verify diskparams")
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
)
453 self
.assertEqual(diskparams
[constants
.DT_RBD
],
454 self
.cluster
.diskparams
[constants
.DT_RBD
])
456 def testMinimalDiskparams(self
):
457 diskparams
= {constants
.DT_RBD
: {constants
.RBD_POOL
: "mock_pool"}}
458 self
.cluster
.diskparams
= {}
459 op
= opcodes
.OpClusterSetParams(diskparams
=diskparams
)
461 self
.assertEqual(diskparams
, self
.cluster
.diskparams
)
463 def testValidDiskparamsAccess(self
):
464 for value
in constants
.DISK_VALID_ACCESS_MODES
:
466 op
= opcodes
.OpClusterSetParams(diskparams
={
467 constants
.DT_RBD
: {constants
.RBD_ACCESS
: value
}
470 got
= self
.cluster
.diskparams
[constants
.DT_RBD
][constants
.RBD_ACCESS
]
471 self
.assertEqual(value
, got
)
473 def testInvalidDiskparamsAccess(self
):
474 for value
in ["default", "pinky_bunny"]:
476 op
= opcodes
.OpClusterSetParams(diskparams
={
477 constants
.DT_RBD
: {constants
.RBD_ACCESS
: value
}
479 self
.ExecOpCodeExpectOpPrereqError(op
, "Invalid value of 'rbd:access'")
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")
487 def testFileStorageDir(self
):
488 op
= opcodes
.OpClusterSetParams(file_storage_dir
="/random/path")
490 self
.assertEqual("/random/path", self
.cluster
.file_storage_dir
)
492 def testSetFileStorageDirToCurrentValue(self
):
493 op
= opcodes
.OpClusterSetParams(
494 file_storage_dir
=self
.cluster
.file_storage_dir
)
497 self
.mcpu
.assertLogContainsRegex("file storage dir already set to value")
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")
504 def testUnsetFileStorageDirFileStorageDisabled(self
):
505 self
.cfg
.SetEnabledDiskTemplates([constants
.DT_PLAIN
])
506 op
= opcodes
.OpClusterSetParams(file_storage_dir
='')
509 def testSetFileStorageDirFileStorageDisabled(self
):
510 self
.cfg
.SetEnabledDiskTemplates([constants
.DT_PLAIN
])
511 op
= opcodes
.OpClusterSetParams(file_storage_dir
='/some/path/')
513 self
.mcpu
.assertLogContainsRegex("although file storage is not enabled")
515 def testSharedFileStorageDir(self
):
516 op
= opcodes
.OpClusterSetParams(shared_file_storage_dir
="/random/path")
518 self
.assertEqual("/random/path", self
.cluster
.shared_file_storage_dir
)
520 def testSetSharedFileStorageDirToCurrentValue(self
):
521 op
= opcodes
.OpClusterSetParams(shared_file_storage_dir
="/random/path")
523 op
= opcodes
.OpClusterSetParams(shared_file_storage_dir
="/random/path")
525 self
.mcpu
.assertLogContainsRegex("shared file storage dir already set to"
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")
533 def testUnsetSharedFileStorageDirSharedFileStorageDisabled(self
):
534 self
.cfg
.SetEnabledDiskTemplates([constants
.DT_PLAIN
])
535 op
= opcodes
.OpClusterSetParams(shared_file_storage_dir
='')
538 def testSetSharedFileStorageDirSharedFileStorageDisabled(self
):
539 self
.cfg
.SetEnabledDiskTemplates([constants
.DT_PLAIN
])
540 op
= opcodes
.OpClusterSetParams(shared_file_storage_dir
='/some/path/')
542 self
.mcpu
.assertLogContainsRegex("although sharedfile storage is not"
545 def testValidDrbdHelper(self
):
546 node1
= self
.cfg
.AddNewNode()
548 self
.rpc
.call_drbd_helper
.return_value
= \
549 self
.RpcResultsBuilder() \
550 .AddSuccessfulNode(self
.master
, "/bin/true") \
551 .AddOfflineNode(node1
) \
553 op
= opcodes
.OpClusterSetParams(drbd_helper
="/bin/true")
555 self
.mcpu
.assertLogContainsRegex("Not checking drbd helper on offline node")
557 def testDrbdHelperFailingNode(self
):
558 self
.rpc
.call_drbd_helper
.return_value
= \
559 self
.RpcResultsBuilder() \
560 .AddFailedNode(self
.master
) \
562 op
= opcodes
.OpClusterSetParams(drbd_helper
="/bin/true")
563 self
.ExecOpCodeExpectOpPrereqError(op
, "Error checking drbd helper")
565 def testInvalidDrbdHelper(self
):
566 self
.rpc
.call_drbd_helper
.return_value
= \
567 self
.RpcResultsBuilder() \
568 .AddSuccessfulNode(self
.master
, "/bin/false") \
570 op
= opcodes
.OpClusterSetParams(drbd_helper
="/bin/true")
571 self
.ExecOpCodeExpectOpPrereqError(op
, "drbd helper is /bin/false")
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
) \
580 op
= opcodes
.OpClusterSetParams(drbd_helper
=drbd_helper
)
583 self
.mcpu
.assertLogContainsRegex("but did not enable")
585 def testResetDrbdHelperDrbdDisabled(self
):
587 self
.cfg
.SetEnabledDiskTemplates([constants
.DT_DISKLESS
])
588 op
= opcodes
.OpClusterSetParams(drbd_helper
=drbd_helper
)
591 self
.assertEqual(None, self
.cluster
.drbd_usermode_helper
)
593 def testResetDrbdHelperDrbdEnabled(self
):
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.")
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")
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
) \
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
})
623 self
.assertEqual(drbd_helper
, self
.cluster
.drbd_usermode_helper
)
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") \
631 self
.cfg
.SetEnabledDiskTemplates([constants
.DT_DISKLESS
])
632 op
= opcodes
.OpClusterSetParams(drbd_helper
=drbd_helper
)
635 self
.assertEqual(drbd_helper
, self
.cluster
.drbd_usermode_helper
)
636 self
.mcpu
.assertLogContainsRegex("DRBD helper already in desired state")
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") \
644 self
.cluster
.drbd_usermode_helper
= "/bin/false"
645 self
.cfg
.SetEnabledDiskTemplates([constants
.DT_DRBD8
])
646 op
= opcodes
.OpClusterSetParams(drbd_helper
=drbd_helper
)
649 self
.assertEqual(drbd_helper
, self
.cluster
.drbd_usermode_helper
)
651 def testBeparams(self
):
652 beparams
= {constants
.BE_VCPUS
: 32}
653 op
= opcodes
.OpClusterSetParams(beparams
=beparams
)
655 self
.assertEqual(32, self
.cluster
656 .beparams
[constants
.PP_DEFAULT
][constants
.BE_VCPUS
])
658 def testNdparams(self
):
659 ndparams
= {constants
.ND_EXCLUSIVE_STORAGE
: True}
660 op
= opcodes
.OpClusterSetParams(ndparams
=ndparams
)
662 self
.assertEqual(True, self
.cluster
663 .ndparams
[constants
.ND_EXCLUSIVE_STORAGE
])
665 def testNdparamsResetOobProgram(self
):
666 ndparams
= {constants
.ND_OOB_PROGRAM
: ""}
667 op
= opcodes
.OpClusterSetParams(ndparams
=ndparams
)
669 self
.assertEqual(constants
.NDC_DEFAULTS
[constants
.ND_OOB_PROGRAM
],
670 self
.cluster
.ndparams
[constants
.ND_OOB_PROGRAM
])
672 def testHvState(self
):
673 hv_state
= {constants
.HT_FAKE
: {constants
.HVST_CPU_TOTAL
: 8}}
674 op
= opcodes
.OpClusterSetParams(hv_state
=hv_state
)
676 self
.assertEqual(8, self
.cluster
.hv_state_static
677 [constants
.HT_FAKE
][constants
.HVST_CPU_TOTAL
])
679 def testDiskState(self
):
681 constants
.DT_PLAIN
: {
682 "mock_vg": {constants
.DS_DISK_TOTAL
: 10}
685 op
= opcodes
.OpClusterSetParams(disk_state
=disk_state
)
687 self
.assertEqual(10, self
.cluster
688 .disk_state_static
[constants
.DT_PLAIN
]["mock_vg"]
689 [constants
.DS_DISK_TOTAL
])
691 def testDefaultIPolicy(self
):
692 ipolicy
= constants
.IPOLICY_DEFAULTS
693 op
= opcodes
.OpClusterSetParams(ipolicy
=ipolicy
)
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
702 self
.cfg
.AddNewInstance(beparams
={C
.BE_MINMEM
: 512, C
.BE_MAXMEM
: 512})
703 op
= opcodes
.OpClusterSetParams(ipolicy
=ipolicy
)
706 self
.mcpu
.assertLogContainsRegex("instances violate them")
708 def testNicparamsNoInstance(self
):
710 constants
.NIC_LINK
: "mock_bridge"
712 op
= opcodes
.OpClusterSetParams(nicparams
=nicparams
)
715 self
.assertEqual("mock_bridge",
716 self
.cluster
.nicparams
717 [constants
.PP_DEFAULT
][constants
.NIC_LINK
])
719 def testNicparamsInvalidConf(self
):
721 constants
.NIC_MODE
: constants
.NIC_MODE_BRIDGED
,
722 constants
.NIC_LINK
: ""
724 op
= opcodes
.OpClusterSetParams(nicparams
=nicparams
)
725 self
.ExecOpCodeExpectException(op
, errors
.ConfigurationError
, "NIC link")
727 def testNicparamsInvalidInstanceConf(self
):
729 constants
.NIC_MODE
: constants
.NIC_MODE_BRIDGED
,
730 constants
.NIC_LINK
: "mock_bridge"
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")
737 def testNicparamsMissingIp(self
):
739 constants
.NIC_MODE
: constants
.NIC_MODE_ROUTED
741 self
.cfg
.AddNewInstance()
742 op
= opcodes
.OpClusterSetParams(nicparams
=nicparams
)
743 self
.ExecOpCodeExpectOpPrereqError(op
, "routed NIC with no ip address")
745 def testNicparamsWithInstance(self
):
747 constants
.NIC_LINK
: "mock_bridge"
749 self
.cfg
.AddNewInstance()
750 op
= opcodes
.OpClusterSetParams(nicparams
=nicparams
)
753 def testDefaultHvparams(self
):
754 hvparams
= constants
.HVC_DEFAULTS
755 op
= opcodes
.OpClusterSetParams(hvparams
=hvparams
)
758 self
.assertEqual(hvparams
, self
.cluster
.hvparams
)
760 def testMinimalHvparams(self
):
763 constants
.HV_MIGRATION_MODE
: constants
.HT_MIGRATION_NONLIVE
766 self
.cluster
.hvparams
= {}
767 op
= opcodes
.OpClusterSetParams(hvparams
=hvparams
)
770 self
.assertEqual(hvparams
, self
.cluster
.hvparams
)
776 constants
.HV_MIGRATION_MODE
: constants
.HT_MIGRATION_NONLIVE
779 "other_os": constants
.HVC_DEFAULTS
781 op
= opcodes
.OpClusterSetParams(os_hvp
=os_hvp
)
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"])
789 def testRemoveOsHvp(self
):
790 os_hvp
= {"mocked_os": {constants
.HT_FAKE
: None}}
791 op
= opcodes
.OpClusterSetParams(os_hvp
=os_hvp
)
794 assert constants
.HT_FAKE
not in self
.cluster
.os_hvp
["mocked_os"]
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
)
802 self
.assertEqual(os_hvp
, self
.cluster
.os_hvp
)
804 def testOsparams(self
):
814 self
.cluster
.osparams
= {"other_os": {"param1": "value1"}}
815 self
.cluster
.osparams_private_cluster
= {}
816 op
= opcodes
.OpClusterSetParams(osparams
=osparams
)
819 self
.assertEqual({"mocked_os": {"param1": "value1"}}, self
.cluster
.osparams
)
821 def testEnabledHypervisors(self
):
822 enabled_hypervisors
= [constants
.HT_XEN_HVM
, constants
.HT_XEN_PVM
]
823 op
= opcodes
.OpClusterSetParams(enabled_hypervisors
=enabled_hypervisors
)
826 self
.assertEqual(enabled_hypervisors
, self
.cluster
.enabled_hypervisors
)
828 def testEnabledHypervisorsWithoutHypervisorParams(self
):
829 enabled_hypervisors
= [constants
.HT_FAKE
]
830 self
.cluster
.hvparams
= {}
831 op
= opcodes
.OpClusterSetParams(enabled_hypervisors
=enabled_hypervisors
)
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
])
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
)
845 self
.assertEqual(default_iallocator
, self
.cluster
.default_iallocator
)
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")
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
})
861 self
.assertEqual(enabled_disk_templates
,
862 self
.cluster
.enabled_disk_templates
)
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")
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")
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")
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")
898 def testVgNameNoLvmDiskTemplateEnabled(self
):
900 self
.cfg
.SetEnabledDiskTemplates([constants
.DT_DISKLESS
])
901 op
= opcodes
.OpClusterSetParams(vg_name
=vg_name
)
904 self
.assertEqual(vg_name
, self
.cluster
.volume_group_name
)
905 self
.mcpu
.assertLogIsEmpty()
907 def testUnsetVgNameWithLvmDiskTemplateEnabled(self
):
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")
913 def testUnsetVgNameWithLvmInstance(self
):
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")
920 def testUnsetVgNameWithNoLvmDiskTemplateEnabled(self
):
922 self
.cfg
.SetEnabledDiskTemplates([constants
.DT_DISKLESS
])
923 op
= opcodes
.OpClusterSetParams(vg_name
=vg_name
)
926 self
.assertEqual(None, self
.cluster
.volume_group_name
)
928 def testVgNameToOldName(self
):
929 vg_name
= self
.cluster
.volume_group_name
930 op
= opcodes
.OpClusterSetParams(vg_name
=vg_name
)
933 self
.mcpu
.assertLogContainsRegex("already in desired state")
935 def testVgNameWithFailingNode(self
):
937 op
= opcodes
.OpClusterSetParams(vg_name
=vg_name
)
938 self
.rpc
.call_vg_list
.return_value
= \
939 self
.RpcResultsBuilder() \
940 .AddFailedNode(self
.master
) \
944 self
.mcpu
.assertLogContainsRegex("Error while gathering data on node")
946 def testVgNameWithValidNode(self
):
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}) \
955 def testVgNameWithTooSmallNode(self
):
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}) \
962 self
.ExecOpCodeExpectOpPrereqError(op
, "too small")
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)
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
)
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")])
987 self
.assertEqual(["hidden1", "hidden2", "hidden3"], self
.cluster
.hidden_os
)
988 self
.mcpu
.assertLogContainsRegex("OS hidden2 already")
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")])
997 self
.assertEqual(["blisted1"], self
.cluster
.blacklisted_os
)
998 self
.mcpu
.assertLogContainsRegex("OS blisted3 not found")
1000 def testMasterNetdev(self
):
1001 master_netdev
= "test_dev"
1002 op
= opcodes
.OpClusterSetParams(master_netdev
=master_netdev
)
1005 self
.assertEqual(master_netdev
, self
.cluster
.master_netdev
)
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")
1015 def testMasterNetdevFailForce(self
):
1016 master_netdev
= "test_dev"
1017 op
= opcodes
.OpClusterSetParams(master_netdev
=master_netdev
,
1019 self
.rpc
.call_node_deactivate_master_ip
.return_value
= \
1020 self
.RpcResultsBuilder() \
1021 .CreateFailedNodeResult(self
.master
)
1024 self
.mcpu
.assertLogContainsRegex("Could not disable the master ip")
1026 def testCompressionToolSuccess(self
):
1027 compression_tools
= ["certainly_not_a_default", "gzip"]
1028 op
= opcodes
.OpClusterSetParams(compression_tools
=compression_tools
)
1030 self
.assertEqual(compression_tools
, self
.cluster
.compression_tools
)
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.*")
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
))
1044 class TestLUClusterVerify(CmdlibTestCase
):
1045 def testVerifyAllGroups(self
):
1046 op
= opcodes
.OpClusterVerify()
1047 result
= self
.ExecOpCode(op
)
1049 self
.assertEqual(2, len(result
["jobs"]))
1051 def testVerifyDefaultGroups(self
):
1052 op
= opcodes
.OpClusterVerify(group_name
="default")
1053 result
= self
.ExecOpCode(op
)
1055 self
.assertEqual(1, len(result
["jobs"]))
1058 class TestLUClusterVerifyConfig(CmdlibTestCase
):
1061 super(TestLUClusterVerifyConfig
, self
).setUp()
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()
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
1080 super(TestLUClusterVerifyConfig
, self
).tearDown()
1082 self
._can_read_patcher
.stop()
1083 self
._read_file_patcher
.stop()
1084 self
._verify_cert_patcher
.stop()
1085 self
._load_cert_patcher
.stop()
1087 def testSuccessfulRun(self
):
1088 self
.cfg
.AddNewInstance()
1089 op
= opcodes
.OpClusterVerifyConfig()
1090 result
= self
.ExecOpCode(op
)
1091 self
.assertTrue(result
)
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
)
1100 self
.mcpu
.assertLogContainsRegex(
1101 "following nodes \(and their instances\) belong to a non existing group")
1102 self
.assertFalse(result
)
1104 def testDanglingInstance(self
):
1105 inst
= self
.cfg
.AddNewInstance()
1106 inst
.primary_node
= "invalid"
1107 op
= opcodes
.OpClusterVerifyConfig()
1108 result
= self
.ExecOpCode(op
)
1110 self
.mcpu
.assertLogContainsRegex(
1111 "following instances have a non-existing primary-node")
1112 self
.assertFalse(result
)
1115 class TestLUClusterVerifyGroup(CmdlibTestCase
):
1116 def testEmptyNodeGroup(self
):
1117 group
= self
.cfg
.AddNewNodeGroup()
1118 op
= opcodes
.OpClusterVerifyGroup(group_name
=group
.name
, verbose
=True)
1120 result
= self
.ExecOpCode(op
)
1122 self
.assertTrue(result
)
1123 self
.mcpu
.assertLogContainsRegex("Empty node group, skipping verification")
1125 def testSimpleInvocation(self
):
1126 op
= opcodes
.OpClusterVerifyGroup(group_name
="default", verbose
=True)
1130 def testSimpleInvocationWithInstance(self
):
1131 self
.cfg
.AddNewInstance(disks
=[])
1132 op
= opcodes
.OpClusterVerifyGroup(group_name
="default", verbose
=True)
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
)
1144 self
.rpc
.call_blockdev_getmirrorstatus_multi
.return_value
= \
1145 RpcResultsBuilder() \
1146 .AddOfflineNode(self
.master
) \
1149 op
= opcodes
.OpClusterVerifyGroup(group_name
="default", verbose
=True)
1153 def testValidRpcResult(self
):
1154 self
.cfg
.AddNewInstance(disks
=[])
1156 self
.rpc
.call_node_verify
.return_value
= \
1157 RpcResultsBuilder() \
1158 .AddSuccessfulNode(self
.master
, {}) \
1161 op
= opcodes
.OpClusterVerifyGroup(group_name
="default", verbose
=True)
1166 class TestLUClusterVerifyClientCerts(CmdlibTestCase
):
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)
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
)}) \
1182 op
= opcodes
.OpClusterVerifyGroup(group_name
="default", verbose
=True)
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")}) \
1193 op
= opcodes
.OpClusterVerifyGroup(group_name
="default", verbose
=True)
1195 self
.mcpu
.assertLogContainsRegex("Client certificate")
1196 self
.mcpu
.assertLogContainsRegex("failed validation")
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
)}) \
1206 op
= opcodes
.OpClusterVerifyGroup(group_name
="default", verbose
=True)
1208 self
.mcpu
.assertLogContainsRegex(
1209 "list of master candidate certificates is empty")
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
)}) \
1221 op
= opcodes
.OpClusterVerifyGroup(group_name
="default", verbose
=True)
1223 self
.mcpu
.assertLogContainsRegex(
1224 "two master candidates configured to use the same")
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
)}) \
1234 op
= opcodes
.OpClusterVerifyGroup(group_name
="default", verbose
=True)
1236 self
.mcpu
.assertLogContainsRegex("does not match its entry")
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
)}) \
1246 op
= opcodes
.OpClusterVerifyGroup(group_name
="default", verbose
=True)
1248 self
.mcpu
.assertLogContainsRegex("does not have an entry")
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
)}) \
1258 op
= opcodes
.OpClusterVerifyGroup(group_name
="default", verbose
=True)
1260 self
.mcpu
.assertLogContainsRegex("using a certificate of another node")
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
)}) \
1276 op
= opcodes
.OpClusterVerifyGroup(group_name
="default", verbose
=True)
1278 self
.mcpu
.assertLogContainsRegex("not a master candidate")
1279 self
.mcpu
.assertLogContainsRegex("still listed")
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
)}) \
1293 op
= opcodes
.OpClusterVerifyGroup(group_name
="default", verbose
=True)
1295 self
.mcpu
.assertLogContainsRegex("not a master candidate")
1296 self
.mcpu
.assertLogContainsRegex(
1297 "certificate of another node which is master candidate")
1300 class TestLUClusterVerifyGroupMethods(CmdlibTestCase
):
1301 """Base class for testing individual methods in LUClusterVerifyGroup.
1305 super(TestLUClusterVerifyGroupMethods
, self
).setUp()
1306 self
.op
= opcodes
.OpClusterVerifyGroup(group_name
="default")
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())
1316 class TestLUClusterVerifyGroupVerifyNode(TestLUClusterVerifyGroupMethods
):
1318 def testInvalidNodeResult(self
, lu
):
1319 self
.assertFalse(lu
._VerifyNode(self
.master
, None))
1320 self
.assertFalse(lu
._VerifyNode(self
.master
, ""))
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
)
1330 self
.mcpu
.ClearLogMessages()
1331 self
.assertTrue(lu
._VerifyNode(self
.master
, {
1332 "version": (constants
.PROTOCOL_VERSION
, constants
.RELEASE_VERSION
+ "x")
1334 self
.mcpu
.assertLogContainsRegex("software version mismatch")
1336 def _GetValidNodeResult(self
, additional_fields
):
1338 "version": (constants
.PROTOCOL_VERSION
, constants
.RELEASE_VERSION
),
1339 constants
.NV_NODESETUP
: []
1341 ret
.update(additional_fields
)
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"
1352 self
.mcpu
.assertLogContainsRegex(constants
.HT_XEN_HVM
)
1353 self
.mcpu
.assertLogContainsRegex("mock error")
1356 def testHvParams(self
, lu
):
1357 lu
._VerifyNode(self
.master
, self
._GetValidNodeResult({
1358 constants
.NV_HVPARAMS
: [("mock item", constants
.HT_XEN_HVM
, "mock error")]
1360 self
.mcpu
.assertLogContainsRegex(constants
.HT_XEN_HVM
)
1361 self
.mcpu
.assertLogContainsRegex("mock item")
1362 self
.mcpu
.assertLogContainsRegex("mock error")
1365 def testSuccessfulResult(self
, lu
):
1366 self
.assertTrue(lu
._VerifyNode(self
.master
, self
._GetValidNodeResult({})))
1367 self
.mcpu
.assertLogIsEmpty()
1370 class TestLUClusterVerifyGroupVerifyNodeTime(TestLUClusterVerifyGroupMethods
):
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)
1377 self
.mcpu
.assertLogContainsRegex("Node returned invalid time")
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)
1385 self
.mcpu
.assertLogContainsRegex("Node time diverges")
1388 def testSuccessfulResult(self
, lu
):
1389 lu
._VerifyNodeTime(self
.master
, {constants
.NV_TIME
: (0, 0)}, 0, 5)
1390 self
.mcpu
.assertLogIsEmpty()
1393 class TestLUClusterVerifyGroupUpdateVerifyNodeLVM(
1394 TestLUClusterVerifyGroupMethods
):
1396 super(TestLUClusterVerifyGroupUpdateVerifyNodeLVM
, self
).setUp()
1397 self
.VALID_NRESULT
= {
1398 constants
.NV_VGLIST
: {"mock_vg": 30000},
1399 constants
.NV_PVLIST
: [
1402 "vg_name": "mock_vg",
1412 def testNoVgName(self
, lu
):
1413 lu
._UpdateVerifyNodeLVM(self
.master
, {}, None, None)
1414 self
.mcpu
.assertLogIsEmpty()
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")
1423 def testValidNodeResult(self
, lu
):
1424 lu
._UpdateVerifyNodeLVM(self
.master
, self
.VALID_NRESULT
, "mock_vg", None)
1425 self
.mcpu
.assertLogIsEmpty()
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()
1435 class TestLUClusterVerifyGroupVerifyGroupDRBDVersion(
1436 TestLUClusterVerifyGroupMethods
):
1438 def testEmptyNodeResult(self
, lu
):
1439 lu
._VerifyGroupDRBDVersion({})
1440 self
.mcpu
.assertLogIsEmpty()
1443 def testValidNodeResult(self
, lu
):
1444 lu
._VerifyGroupDRBDVersion(
1446 .AddSuccessfulNode(self
.master
, {
1447 constants
.NV_DRBDVERSION
: "8.3.0"
1450 self
.mcpu
.assertLogIsEmpty()
1453 def testDifferentVersions(self
, lu
):
1454 node1
= self
.cfg
.AddNewNode()
1455 lu
._VerifyGroupDRBDVersion(
1457 .AddSuccessfulNode(self
.master
, {
1458 constants
.NV_DRBDVERSION
: "8.3.0"
1460 .AddSuccessfulNode(node1
, {
1461 constants
.NV_DRBDVERSION
: "8.4.0"
1464 self
.mcpu
.assertLogContainsRegex("DRBD version mismatch: 8.3.0")
1465 self
.mcpu
.assertLogContainsRegex("DRBD version mismatch: 8.4.0")
1468 class TestLUClusterVerifyGroupVerifyGroupLVM(TestLUClusterVerifyGroupMethods
):
1470 def testNoVgName(self
, lu
):
1471 lu
._VerifyGroupLVM(None, None)
1472 self
.mcpu
.assertLogIsEmpty()
1475 def testNoExclusiveStorage(self
, lu
):
1476 lu
._VerifyGroupLVM(None, "mock_vg")
1477 self
.mcpu
.assertLogIsEmpty()
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()
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
)
1495 nimg2
.pv_max
= 10005
1496 lu
._VerifyGroupLVM({self
.master
.uuid
: nimg1
, node2
.uuid
: nimg2
}, "mock_vg")
1497 self
.mcpu
.assertLogIsEmpty()
1500 class TestLUClusterVerifyGroupVerifyNodeBridges(
1501 TestLUClusterVerifyGroupMethods
):
1503 def testNoBridges(self
, lu
):
1504 lu
._VerifyNodeBridges(None, None, None)
1505 self
.mcpu
.assertLogIsEmpty()
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")
1514 self
.mcpu
.ClearLogMessages()
1515 lu
._VerifyNodeBridges(self
.master
, {constants
.NV_BRIDGES
: ["mock_bridge"]},
1517 self
.mcpu
.assertLogContainsRegex("missing bridge")
1520 class TestLUClusterVerifyGroupVerifyNodeUserScripts(
1521 TestLUClusterVerifyGroupMethods
):
1523 def testNoUserScripts(self
, lu
):
1524 lu
._VerifyNodeUserScripts(self
.master
, {})
1525 self
.mcpu
.assertLogContainsRegex("did not return user scripts information")
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")
1534 class TestLUClusterVerifyGroupVerifyNodeNetwork(
1535 TestLUClusterVerifyGroupMethods
):
1538 super(TestLUClusterVerifyGroupVerifyNodeNetwork
, self
).setUp()
1539 self
.VALID_NRESULT
= {
1540 constants
.NV_NODELIST
: {},
1541 constants
.NV_NODENETTEST
: {},
1542 constants
.NV_MASTERIP
: True
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")
1556 def testValidResult(self
, lu
):
1557 lu
._VerifyNodeNetwork(self
.master
, self
.VALID_NRESULT
)
1558 self
.mcpu
.assertLogIsEmpty()
1561 def testSshProblem(self
, lu
):
1562 self
.VALID_NRESULT
.update({
1563 constants
.NV_NODELIST
: {
1564 "mock_node": "mock_error"
1567 lu
._VerifyNodeNetwork(self
.master
, self
.VALID_NRESULT
)
1568 self
.mcpu
.assertLogContainsRegex("ssh communication with node 'mock_node'")
1571 def testTcpProblem(self
, lu
):
1572 self
.VALID_NRESULT
.update({
1573 constants
.NV_NODENETTEST
: {
1574 "mock_node": "mock_error"
1577 lu
._VerifyNodeNetwork(self
.master
, self
.VALID_NRESULT
)
1578 self
.mcpu
.assertLogContainsRegex("tcp communication with node 'mock_node'")
1581 def testMasterIpNotReachable(self
, lu
):
1582 self
.VALID_NRESULT
.update({
1583 constants
.NV_MASTERIP
: False
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")
1590 self
.mcpu
.ClearLogMessages()
1591 lu
._VerifyNodeNetwork(node1
, self
.VALID_NRESULT
)
1592 self
.mcpu
.assertLogContainsRegex("cannot reach the master IP")
1595 class TestLUClusterVerifyGroupVerifyInstance(TestLUClusterVerifyGroupMethods
):
1597 super(TestLUClusterVerifyGroupVerifyInstance
, self
).setUp()
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
=[])
1609 cluster
.LUClusterVerifyGroup
.NodeImage(uuid
=self
.master_uuid
)
1610 self
.master_img
.volumes
= ["/".join(disk
.logical_id
)
1611 for inst
in [self
.running_inst
,
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
]
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
]
1624 self
.master_uuid
: self
.master_img
,
1625 self
.node1
.uuid
: self
.node1_img
1627 running_inst_disks
= self
.cfg
.GetInstanceDisks(self
.running_inst
.uuid
)
1630 (True, objects
.BlockDevStatus(ldisk_status
=constants
.LDS_OKAY
))
1631 for _
in running_inst_disks
1636 def testDisklessInst(self
, lu
):
1637 lu
._VerifyInstance(self
.diskless_inst
, self
.node_imgs
, {})
1638 self
.mcpu
.assertLogIsEmpty()
1641 def testOfflineNode(self
, lu
):
1642 self
.master_img
.offline
= True
1643 lu
._VerifyInstance(self
.drbd_inst
, self
.node_imgs
, {})
1644 self
.mcpu
.assertLogIsEmpty()
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")
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")
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")
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()
1673 def testValidDiskStatus(self
, lu
):
1674 lu
._VerifyInstance(self
.running_inst
, self
.node_imgs
, self
.diskstatus
)
1675 self
.mcpu
.assertLogIsEmpty()
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")
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'")
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")
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()
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")
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\)")
1721 class TestLUClusterVerifyGroupVerifyOrphanVolumes(
1722 TestLUClusterVerifyGroupMethods
):
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
1734 self
.master_uuid
: master_img
1737 self
.master_uuid
: ["mock_vg/disk_0", "other_vg/disk_0", "other_vg/disk_1"]
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")
1749 class TestLUClusterVerifyGroupVerifyNPlusOneMemory(
1750 TestLUClusterVerifyGroupMethods
):
1752 def testN1Failure(self
, lu
):
1753 group1
= self
.cfg
.AddNewNodeGroup()
1755 node1
= self
.cfg
.AddNewNode()
1756 node2
= self
.cfg
.AddNewNode(group
=group1
)
1757 node3
= self
.cfg
.AddNewNode()
1759 inst1
= self
.cfg
.AddNewInstance()
1760 inst2
= self
.cfg
.AddNewInstance()
1761 inst3
= self
.cfg
.AddNewInstance()
1763 node1_img
= cluster
.LUClusterVerifyGroup
.NodeImage(uuid
=node1
.uuid
)
1765 self
.master_uuid
: [inst1
.uuid
, inst2
.uuid
, inst3
.uuid
]
1768 node2_img
= cluster
.LUClusterVerifyGroup
.NodeImage(uuid
=node2
.uuid
)
1770 node3_img
= cluster
.LUClusterVerifyGroup
.NodeImage(uuid
=node3
.uuid
)
1771 node3_img
.offline
= True
1774 node1
.uuid
: node1_img
,
1775 node2
.uuid
: node2_img
,
1776 node3
.uuid
: node3_img
1779 lu
._VerifyNPlusOneMemory(node_imgs
, self
.cfg
.GetAllInstancesInfo())
1780 self
.mcpu
.assertLogContainsRegex(
1781 "not enough memory to accomodate instance failovers")
1783 self
.mcpu
.ClearLogMessages()
1784 node1_img
.mfree
= 1000
1785 lu
._VerifyNPlusOneMemory(node_imgs
, self
.cfg
.GetAllInstancesInfo())
1786 self
.mcpu
.assertLogIsEmpty()
1789 class TestLUClusterVerifyGroupVerifyFiles(TestLUClusterVerifyGroupMethods
):
1792 node1
= self
.cfg
.AddNewNode(master_candidate
=False, offline
=False,
1794 node2
= self
.cfg
.AddNewNode(master_candidate
=True, vm_capable
=False)
1795 node3
= self
.cfg
.AddNewNode(master_candidate
=False, offline
=False,
1797 node4
= self
.cfg
.AddNewNode(master_candidate
=False, offline
=False,
1799 node5
= self
.cfg
.AddNewNode(master_candidate
=False, offline
=True)
1801 nodeinfo
= [self
.master
, node1
, node2
, node3
, node4
, node5
]
1803 pathutils
.CLUSTER_DOMAIN_SECRET_FILE
,
1804 pathutils
.RAPI_CERT_FILE
,
1805 pathutils
.RAPI_USERS_FILE
,
1808 pathutils
.RAPI_USERS_FILE
,
1809 hv_xen
.XL_CONFIG_FILE
,
1810 pathutils
.VNC_PASSWORD_FILE
,
1813 pathutils
.CLUSTER_CONF_FILE
,
1816 hv_xen
.XEND_CONFIG_FILE
,
1817 hv_xen
.XL_CONFIG_FILE
,
1818 pathutils
.VNC_PASSWORD_FILE
,
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"
1829 .AddSuccessfulNode(node1
, {
1830 constants
.NV_FILELIST
: {
1831 pathutils
.RAPI_CERT_FILE
: "97f0356500e866387f4b84233848cc4a",
1832 hv_xen
.XEND_CONFIG_FILE
: "b4a8a824ab3cac3d88839a9adeadf310",
1835 .AddSuccessfulNode(node2
, {
1836 constants
.NV_FILELIST
: {
1837 pathutils
.RAPI_CERT_FILE
: "97f0356500e866387f4b84233848cc4a",
1838 pathutils
.CLUSTER_DOMAIN_SECRET_FILE
: "cds-47b5b3f19202936bb4",
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"
1850 .AddSuccessfulNode(node4
, {}) \
1851 .AddOfflineNode(node5
) \
1853 assert set(nvinfo
.keys()) == set(map(operator
.attrgetter("uuid"), nodeinfo
))
1855 lu
._VerifyFiles(nodeinfo
, self
.master_uuid
, nvinfo
,
1856 (files_all
, files_opt
, files_mc
, files_vm
))
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
,
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",
1882 self
.assertEqual(len(self
.mcpu
.GetLogMessages()), len(expected_msgs
))
1883 for expected_msg
in expected_msgs
:
1884 self
.mcpu
.assertLogContainsInLine(expected_msg
)
1887 class TestLUClusterVerifyGroupVerifyNodeOs(TestLUClusterVerifyGroupMethods
):
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")
1898 def testUpdateNodeOsValidNodeResult(self
, lu
):
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
,
1908 nimage
= cluster
.LUClusterVerifyGroup
.NodeImage(uuid
=self
.master_uuid
)
1909 lu
._UpdateNodeOS(self
.master
, ndata
, nimage
)
1910 self
.mcpu
.assertLogIsEmpty()
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
)
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)],
1930 nimg
.os_fail
= False
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)],
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)],
1946 lu
._VerifyNodeOS(node
, nimg
, nimg_root
)
1949 "Extra OS only_on_test not present on reference node",
1950 "OSes present on reference node .* but missing on this node:" +
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 "
1962 self
.assertEqual(len(expected_msgs
), len(self
.mcpu
.GetLogMessages()))
1963 for expected_msg
in expected_msgs
:
1964 self
.mcpu
.assertLogContainsRegex(expected_msg
)
1967 class TestLUClusterVerifyGroupVerifyAcceptedFileStoragePaths(
1968 TestLUClusterVerifyGroupMethods
):
1970 def testNotMaster(self
, lu
):
1971 lu
._VerifyAcceptedFileStoragePaths(self
.master
, {}, False)
1972 self
.mcpu
.assertLogIsEmpty()
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")
1982 def testMasterInvalidNodeResult(self
, lu
):
1983 lu
._VerifyAcceptedFileStoragePaths(self
.master
, {}, True)
1984 self
.mcpu
.assertLogContainsRegex(
1985 "Node did not return forbidden file storage paths")
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")
1994 def testMasterSuccess(self
, lu
):
1995 lu
._VerifyAcceptedFileStoragePaths(
1996 self
.master
, {constants
.NV_ACCEPTED_STORAGE_PATHS
: []}, True)
1997 self
.mcpu
.assertLogIsEmpty()
2000 class TestLUClusterVerifyGroupVerifyStoragePaths(
2001 TestLUClusterVerifyGroupMethods
):
2003 def testVerifyFileStoragePathsSuccess(self
, lu
):
2004 lu
._VerifyFileStoragePaths(self
.master
, {})
2005 self
.mcpu
.assertLogIsEmpty()
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")
2015 def testVerifySharedFileStoragePathsSuccess(self
, lu
):
2016 lu
._VerifySharedFileStoragePaths(self
.master
, {})
2017 self
.mcpu
.assertLogIsEmpty()
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")
2027 class TestLUClusterVerifyGroupVerifyOob(TestLUClusterVerifyGroupMethods
):
2029 def testEmptyResult(self
, lu
):
2030 lu
._VerifyOob(self
.master
, {})
2031 self
.mcpu
.assertLogIsEmpty()
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")
2040 class TestLUClusterVerifyGroupUpdateNodeVolumes(
2041 TestLUClusterVerifyGroupMethods
):
2043 super(TestLUClusterVerifyGroupUpdateNodeVolumes
, self
).setUp()
2044 self
.nimg
= cluster
.LUClusterVerifyGroup
.NodeImage(uuid
=self
.master_uuid
)
2047 def testNoVgName(self
, lu
):
2048 lu
._UpdateNodeVolumes(self
.master
, {}, self
.nimg
, None)
2049 self
.mcpu
.assertLogIsEmpty()
2050 self
.assertTrue(self
.nimg
.lvm_fail
)
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
)
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
)
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
)
2074 class TestLUClusterVerifyGroupUpdateNodeInstances(
2075 TestLUClusterVerifyGroupMethods
):
2077 super(TestLUClusterVerifyGroupUpdateNodeInstances
, self
).setUp()
2078 self
.nimg
= cluster
.LUClusterVerifyGroup
.NodeImage(uuid
=self
.master_uuid
)
2081 def testInvalidNodeResult(self
, lu
):
2082 lu
._UpdateNodeInstances(self
.master
, {}, self
.nimg
)
2083 self
.mcpu
.assertLogContainsRegex("rpc call to node failed")
2086 def testValidNodeResult(self
, lu
):
2087 inst
= self
.cfg
.AddNewInstance()
2088 lu
._UpdateNodeInstances(self
.master
,
2089 {constants
.NV_INSTANCELIST
: [inst
.name
]},
2091 self
.mcpu
.assertLogIsEmpty()
2094 class TestLUClusterVerifyGroupUpdateNodeInfo(TestLUClusterVerifyGroupMethods
):
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}}
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")
2108 def testInvalidMemoryFreeHvNodeResult(self
, lu
):
2109 lu
._UpdateNodeInfo(self
.master
,
2110 {constants
.NV_HVINFO
: {"memory_free": "abc"}},
2112 self
.mcpu
.assertLogContainsRegex(
2113 "node returned invalid nodeinfo, check hypervisor")
2116 def testValidHvNodeResult(self
, lu
):
2117 lu
._UpdateNodeInfo(self
.master
, self
.valid_hvresult
, self
.nimg
, None)
2118 self
.mcpu
.assertLogIsEmpty()
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'")
2131 def testInvalidDiskFreeVgNodeResult(self
, lu
):
2132 self
.valid_hvresult
.update({
2133 constants
.NV_VGLIST
: {"mock_vg": "abc"}
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")
2140 def testValidVgNodeResult(self
, lu
):
2141 self
.valid_hvresult
.update({
2142 constants
.NV_VGLIST
: {"mock_vg": 10000}
2144 lu
._UpdateNodeInfo(self
.master
, self
.valid_hvresult
, self
.nimg
, "mock_vg")
2145 self
.mcpu
.assertLogIsEmpty()
2148 class TestLUClusterVerifyGroupCollectDiskInfo(TestLUClusterVerifyGroupMethods
):
2150 super(TestLUClusterVerifyGroupCollectDiskInfo
, self
).setUp()
2152 self
.node1
= self
.cfg
.AddNewNode()
2153 self
.node2
= self
.cfg
.AddNewNode()
2154 self
.node3
= self
.cfg
.AddNewNode()
2156 self
.diskless_inst
= \
2157 self
.cfg
.AddNewInstance(primary_node
=self
.node1
,
2158 disk_template
=constants
.DT_DISKLESS
)
2160 self
.cfg
.AddNewInstance(primary_node
=self
.node2
,
2161 disk_template
=constants
.DT_PLAIN
)
2163 self
.cfg
.AddNewInstance(primary_node
=self
.node3
,
2164 secondary_node
=self
.node2
,
2165 disk_template
=constants
.DT_DRBD8
)
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
= []
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
2186 self
.node_uuids
= [self
.node1
.uuid
, self
.node2
.uuid
, self
.node3
.uuid
]
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, "")]) \
2196 lu
._CollectDiskInfo(self
.node_uuids
, self
.node_images
,
2197 self
.cfg
.GetAllInstancesInfo())
2199 self
.mcpu
.assertLogIsEmpty()
2202 def testOfflineAndFailingNodes(self
, lu
):
2203 self
.rpc
.call_blockdev_getmirrorstatus_multi
.return_value
= \
2204 RpcResultsBuilder() \
2205 .AddOfflineNode(self
.node2
) \
2206 .AddFailedNode(self
.node3
) \
2209 lu
._CollectDiskInfo(self
.node_uuids
, self
.node_images
,
2210 self
.cfg
.GetAllInstancesInfo())
2212 self
.mcpu
.assertLogContainsRegex("while getting disk information")
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
, [""]) \
2222 lu
._CollectDiskInfo(self
.node_uuids
, self
.node_images
,
2223 self
.cfg
.GetAllInstancesInfo())
2224 # logging is not performed through mcpu
2225 self
.mcpu
.assertLogIsEmpty()
2228 class TestLUClusterVerifyGroupHooksCallBack(TestLUClusterVerifyGroupMethods
):
2230 super(TestLUClusterVerifyGroupHooksCallBack
, self
).setUp()
2232 self
.feedback_fn
= lambda _
: None
2234 def PrepareLU(self
, lu
):
2235 super(TestLUClusterVerifyGroupHooksCallBack
, self
).PrepareLU(lu
)
2237 lu
.my_node_uuids
= list(self
.cfg
.GetAllNodesInfo().keys())
2240 def testEmptyGroup(self
, lu
):
2241 lu
.my_node_uuids
= []
2242 lu
.HooksCallBack(constants
.HOOKS_PHASE_POST
, None, self
.feedback_fn
, None)
2245 def testFailedResult(self
, lu
):
2246 lu
.HooksCallBack(constants
.HOOKS_PHASE_POST
,
2247 RpcResultsBuilder(use_node_names
=True)
2248 .AddFailedNode(self
.master
).Build(),
2251 self
.mcpu
.assertLogContainsRegex("Communication failure in hooks execution")
2254 def testOfflineNode(self
, lu
):
2255 lu
.HooksCallBack(constants
.HOOKS_PHASE_POST
,
2256 RpcResultsBuilder(use_node_names
=True)
2257 .AddOfflineNode(self
.master
).Build(),
2262 def testValidResult(self
, lu
):
2263 lu
.HooksCallBack(constants
.HOOKS_PHASE_POST
,
2264 RpcResultsBuilder(use_node_names
=True)
2265 .AddSuccessfulNode(self
.master
,
2267 constants
.HKR_SUCCESS
,
2274 def testFailedScriptResult(self
, lu
):
2275 lu
.HooksCallBack(constants
.HOOKS_PHASE_POST
,
2276 RpcResultsBuilder(use_node_names
=True)
2277 .AddSuccessfulNode(self
.master
,
2284 self
.mcpu
.assertLogContainsRegex("Script mock_script failed")
2287 class TestLUClusterVerifyDisks(CmdlibTestCase
):
2288 def testVerifyDisks(self
):
2289 op
= opcodes
.OpClusterVerifyDisks()
2290 result
= self
.ExecOpCode(op
)
2292 self
.assertEqual(1, len(result
["jobs"]))
2295 class TestLUClusterRenewCrypto(CmdlibTestCase
):
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"
2307 super(TestLUClusterRenewCrypto
, self
).tearDown()
2309 def _GetFakeDigest(self
, uuid
):
2310 """Creates a fake SSL digest depending on the UUID of a node.
2313 @param uuid: node UUID
2314 @returns: a string impersonating a SSL digest
2317 return "FA:KE:%s:%s:%s:%s" % (uuid
[0:2], uuid
[2:4], uuid
[4:6], uuid
[6:8])
2319 def _InitPathutils(self
, pathutils
):
2320 """Patch pathutils to point to temporary files.
2323 pathutils
.NODED_CERT_FILE
= self
._node_cert
2324 pathutils
.NODED_CLIENT_CERT_FILE
= self
._client_node_cert
2326 def _AssertCertFiles(self
, pathutils
):
2327 """Check if the correct certificates exist and don't exist on the master.
2330 self
.assertTrue(os
.path
.exists(pathutils
.NODED_CERT_FILE
))
2331 self
.assertTrue(os
.path
.exists(pathutils
.NODED_CLIENT_CERT_FILE
))
2333 def _CompletelySuccessfulRpc(self
, node_uuid
, _
):
2334 """Fake RPC call which always returns successfully.
2337 return self
.RpcResultsBuilder() \
2338 .CreateSuccessfulNodeResult(node_uuid
,
2339 [(constants
.CRYPTO_TYPE_SSL_DIGEST
,
2340 self
._GetFakeDigest(node_uuid
))])
2342 @patchPathutils("cluster")
2343 def testSuccessfulCase(self
, pathutils
):
2344 self
._InitPathutils(pathutils
)
2346 # create a few non-master, online nodes
2348 for _
in range(num_nodes
):
2349 self
.cfg
.AddNewNode()
2350 self
.rpc
.call_node_crypto_tokens
= self
._CompletelySuccessfulRpc
2352 op
= opcodes
.OpClusterRenewCrypto()
2355 self
._AssertCertFiles(pathutils
)
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
])
2368 # The non-master nodes have the fake digest from the
2370 expected_digest
= self
._GetFakeDigest(node_uuid
)
2371 self
.assertEqual(expected_digest
, cluster
.candidate_certs
[node_uuid
])
2373 def _partiallyFailingRpc(self
, node_uuid
, _
):
2374 if node_uuid
== self
._failed_node
:
2375 return self
.RpcResultsBuilder() \
2376 .CreateFailedNodeResult(node_uuid
)
2378 return self
.RpcResultsBuilder() \
2379 .CreateSuccessfulNodeResult(node_uuid
,
2380 [(constants
.CRYPTO_TYPE_SSL_DIGEST
, self
._GetFakeDigest(node_uuid
))])
2382 @patchPathutils("cluster")
2383 def testNonMasterFails(self
, pathutils
):
2384 self
._InitPathutils(pathutils
)
2386 # create a few non-master, online nodes
2388 for _
in range(num_nodes
):
2389 self
.cfg
.AddNewNode()
2390 nodes
= self
.cfg
.GetAllNodesInfo()
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
2398 op
= opcodes
.OpClusterRenewCrypto()
2401 self
._AssertCertFiles(pathutils
)
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
)
2412 self
.assertTrue(node_uuid
in cluster
.candidate_certs
)
2414 @patchPathutils("cluster")
2415 def testOfflineNodes(self
, pathutils
):
2416 self
._InitPathutils(pathutils
)
2418 # create a few non-master, online nodes
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
2426 op
= opcodes
.OpClusterRenewCrypto()
2429 self
._AssertCertFiles(pathutils
)
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
)
2440 self
.assertTrue(node_uuid
in cluster
.candidate_certs
)
2442 def _RpcSuccessfulAfterRetries(self
, node_uuid
, _
):
2443 if self
._retries
< self
._max_retries
:
2445 return self
.RpcResultsBuilder() \
2446 .CreateFailedNodeResult(node_uuid
)
2448 return self
.RpcResultsBuilder() \
2449 .CreateSuccessfulNodeResult(node_uuid
,
2450 [(constants
.CRYPTO_TYPE_SSL_DIGEST
, self
._GetFakeDigest(node_uuid
))])
2452 def _RpcSuccessfulAfterRetriesNonMaster(self
, node_uuid
, _
):
2453 if self
._retries
< self
._max_retries
and node_uuid
!= self
._master_uuid
:
2455 return self
.RpcResultsBuilder() \
2456 .CreateFailedNodeResult(node_uuid
)
2458 return self
.RpcResultsBuilder() \
2459 .CreateSuccessfulNodeResult(node_uuid
,
2460 [(constants
.CRYPTO_TYPE_SSL_DIGEST
, self
._GetFakeDigest(node_uuid
))])
2462 def _NonMasterRetries(self
, pathutils
, max_retries
):
2463 self
._InitPathutils(pathutils
)
2465 self
._master_uuid
= self
.cfg
.GetMasterNode()
2466 self
._max_retries
= max_retries
2468 self
.rpc
.call_node_crypto_tokens
= self
._RpcSuccessfulAfterRetriesNonMaster
2470 # Add one non-master node
2471 self
.cfg
.AddNewNode()
2473 op
= opcodes
.OpClusterRenewCrypto()
2476 self
._AssertCertFiles(pathutils
)
2478 return self
.cfg
.GetClusterInfo()
2480 @patchPathutils("cluster")
2481 def testNonMasterRetriesSuccess(self
, pathutils
):
2482 cluster
= self
._NonMasterRetries(pathutils
, 2)
2483 self
.assertEqual(2, len(cluster
.candidate_certs
.values()))
2485 @patchPathutils("cluster")
2486 def testNonMasterRetriesFail(self
, pathutils
):
2487 cluster
= self
._NonMasterRetries(pathutils
, 5)
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
)
2494 if __name__
== "__main__":
2495 testutils
.GanetiTestProgram()