4 # Copyright (C) 2008, 2011, 2012 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Script for unittesting the cmdlib module"""
34 from ganeti import constants
35 from ganeti import mcpu
36 from ganeti import cmdlib
37 from ganeti import opcodes
38 from ganeti import errors
39 from ganeti import utils
40 from ganeti import luxi
42 from ganeti import objects
43 from ganeti import compat
44 from ganeti import rpc
45 from ganeti import pathutils
46 from ganeti.masterd import iallocator
47 from ganeti.hypervisor import hv_xen
53 class TestCertVerification(testutils.GanetiTestCase):
55 testutils.GanetiTestCase.setUp(self)
57 self.tmpdir = tempfile.mkdtemp()
60 shutil.rmtree(self.tmpdir)
62 def testVerifyCertificate(self):
63 cmdlib._VerifyCertificate(self._TestDataFilename("cert1.pem"))
65 nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
67 (errcode, msg) = cmdlib._VerifyCertificate(nonexist_filename)
68 self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
70 # Try to load non-certificate file
71 invalid_cert = self._TestDataFilename("bdev-net.txt")
72 (errcode, msg) = cmdlib._VerifyCertificate(invalid_cert)
73 self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
76 class TestOpcodeParams(testutils.GanetiTestCase):
77 def testParamsStructures(self):
78 for op in sorted(mcpu.Processor.DISPATCH_TABLE):
79 lu = mcpu.Processor.DISPATCH_TABLE[op]
81 self.failIf(hasattr(lu, "_OP_REQP"),
82 msg=("LU '%s' has old-style _OP_REQP" % lu_name))
83 self.failIf(hasattr(lu, "_OP_DEFS"),
84 msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
85 self.failIf(hasattr(lu, "_OP_PARAMS"),
86 msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
89 class TestIAllocatorChecks(testutils.GanetiTestCase):
90 def testFunction(self):
92 def __init__(self, opcode):
93 self.cfg = mocks.FakeConfig()
96 class OpTest(opcodes.OpCode):
98 ("iallocator", None, ht.NoType, None),
99 ("node", None, ht.NoType, None),
102 default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
103 other_iallocator = default_iallocator + "_not"
108 c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
110 # Neither node nor iallocator given
114 self.assertEqual(lu.op.iallocator, default_iallocator)
115 self.assertEqual(lu.op.node, None)
117 # Both, iallocator and node given
118 op.iallocator = "test"
120 self.assertRaises(errors.OpPrereqError, c_i)
122 # Only iallocator given
123 op.iallocator = other_iallocator
126 self.assertEqual(lu.op.iallocator, other_iallocator)
127 self.assertEqual(lu.op.node, None)
133 self.assertEqual(lu.op.iallocator, None)
134 self.assertEqual(lu.op.node, "node")
136 # No node, iallocator or default iallocator
139 lu.cfg.GetDefaultIAllocator = lambda: None
140 self.assertRaises(errors.OpPrereqError, c_i)
143 class TestLUTestJqueue(unittest.TestCase):
145 self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
146 (luxi.WFJC_TIMEOUT * 0.75),
147 msg=("Client timeout too high, might not notice bugs"
148 " in WaitForJobChange"))
151 class TestLUQuery(unittest.TestCase):
153 self.assertEqual(sorted(cmdlib._QUERY_IMPL.keys()),
154 sorted(constants.QR_VIA_OP))
156 assert constants.QR_NODE in constants.QR_VIA_OP
157 assert constants.QR_INSTANCE in constants.QR_VIA_OP
159 for i in constants.QR_VIA_OP:
160 self.assert_(cmdlib._GetQueryImplementation(i))
162 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation, "")
163 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation,
167 class TestLUGroupAssignNodes(unittest.TestCase):
169 def testCheckAssignmentForSplitInstances(self):
170 node_data = dict((name, objects.Node(name=name, group=group))
171 for (name, group) in [("n1a", "g1"), ("n1b", "g1"),
172 ("n2a", "g2"), ("n2b", "g2"),
173 ("n3a", "g3"), ("n3b", "g3"),
177 def Instance(name, pnode, snode):
180 disk_template = constants.DT_DISKLESS
182 disks = [objects.Disk(dev_type=constants.LD_DRBD8,
183 logical_id=[pnode, snode, 1, 17, 17])]
184 disk_template = constants.DT_DRBD8
186 return objects.Instance(name=name, primary_node=pnode, disks=disks,
187 disk_template=disk_template)
189 instance_data = dict((name, Instance(name, pnode, snode))
190 for name, pnode, snode in [("inst1a", "n1a", "n1b"),
191 ("inst1b", "n1b", "n1a"),
192 ("inst2a", "n2a", "n2b"),
193 ("inst3a", "n3a", None),
194 ("inst3b", "n3b", "n1b"),
195 ("inst3c", "n3b", "n2b"),
198 # Test first with the existing state.
200 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
204 self.assertEqual([], new)
205 self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
207 # And now some changes.
209 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
214 self.assertEqual(set(["inst1a", "inst1b"]), set(new))
215 self.assertEqual(set(["inst3c"]), set(prev))
218 class TestClusterVerifySsh(unittest.TestCase):
219 def testMultipleGroups(self):
220 fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
222 objects.Node(name="node20", group="my", offline=False),
223 objects.Node(name="node21", group="my", offline=False),
224 objects.Node(name="node22", group="my", offline=False),
225 objects.Node(name="node23", group="my", offline=False),
226 objects.Node(name="node24", group="my", offline=False),
227 objects.Node(name="node25", group="my", offline=False),
228 objects.Node(name="node26", group="my", offline=True),
231 objects.Node(name="node1", group="g1", offline=True),
232 objects.Node(name="node2", group="g1", offline=False),
233 objects.Node(name="node3", group="g1", offline=False),
234 objects.Node(name="node4", group="g1", offline=True),
235 objects.Node(name="node5", group="g1", offline=False),
236 objects.Node(name="node10", group="xyz", offline=False),
237 objects.Node(name="node11", group="xyz", offline=False),
238 objects.Node(name="node40", group="alloff", offline=True),
239 objects.Node(name="node41", group="alloff", offline=True),
240 objects.Node(name="node50", group="aaa", offline=False),
242 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
244 (online, perhost) = fn(mygroupnodes, "my", nodes)
245 self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
246 self.assertEqual(set(perhost.keys()), set(online))
248 self.assertEqual(perhost, {
249 "node20": ["node10", "node2", "node50"],
250 "node21": ["node11", "node3", "node50"],
251 "node22": ["node10", "node5", "node50"],
252 "node23": ["node11", "node2", "node50"],
253 "node24": ["node10", "node3", "node50"],
254 "node25": ["node11", "node5", "node50"],
257 def testSingleGroup(self):
258 fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
260 objects.Node(name="node1", group="default", offline=True),
261 objects.Node(name="node2", group="default", offline=False),
262 objects.Node(name="node3", group="default", offline=False),
263 objects.Node(name="node4", group="default", offline=True),
265 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
267 (online, perhost) = fn(nodes, "default", nodes)
268 self.assertEqual(online, ["node2", "node3"])
269 self.assertEqual(set(perhost.keys()), set(online))
271 self.assertEqual(perhost, {
277 class TestClusterVerifyFiles(unittest.TestCase):
279 def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
280 assert ((ecode == constants.CV_ENODEFILECHECK and
281 ht.TNonEmptyString(item)) or
282 (ecode == constants.CV_ECLUSTERFILECHECK and
289 errors.append((item, msg))
291 _VerifyFiles = cmdlib.LUClusterVerifyGroup._VerifyFiles
295 master_name = "master.example.com"
297 objects.Node(name=master_name, offline=False, vm_capable=True),
298 objects.Node(name="node2.example.com", offline=False, vm_capable=True),
299 objects.Node(name="node3.example.com", master_candidate=True,
301 objects.Node(name="node4.example.com", offline=False, vm_capable=True),
302 objects.Node(name="nodata.example.com", offline=False, vm_capable=True),
303 objects.Node(name="offline.example.com", offline=True),
305 cluster = objects.Cluster(modify_etc_hosts=True,
306 enabled_hypervisors=[constants.HT_XEN_HVM])
308 pathutils.CLUSTER_DOMAIN_SECRET_FILE,
309 pathutils.RAPI_CERT_FILE,
310 pathutils.RAPI_USERS_FILE,
313 pathutils.RAPI_USERS_FILE,
314 hv_xen.XL_CONFIG_FILE,
315 pathutils.VNC_PASSWORD_FILE,
318 pathutils.CLUSTER_CONF_FILE,
321 hv_xen.XEND_CONFIG_FILE,
322 hv_xen.XL_CONFIG_FILE,
323 pathutils.VNC_PASSWORD_FILE,
326 master_name: rpc.RpcResult(data=(True, {
327 constants.NV_FILELIST: {
328 pathutils.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
329 pathutils.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
330 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
331 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
332 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
334 "node2.example.com": rpc.RpcResult(data=(True, {
335 constants.NV_FILELIST: {
336 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
337 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
340 "node3.example.com": rpc.RpcResult(data=(True, {
341 constants.NV_FILELIST: {
342 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
343 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
346 "node4.example.com": rpc.RpcResult(data=(True, {
347 constants.NV_FILELIST: {
348 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
349 pathutils.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
350 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
351 pathutils.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
352 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
355 "nodata.example.com": rpc.RpcResult(data=(True, {})),
356 "offline.example.com": rpc.RpcResult(offline=True),
358 assert set(nvinfo.keys()) == set(map(operator.attrgetter("name"), nodeinfo))
360 self._VerifyFiles(compat.partial(self._FakeErrorIf, errors), nodeinfo,
362 (files_all, files_opt, files_mc, files_vm))
363 self.assertEqual(sorted(errors), sorted([
364 (None, ("File %s found with 2 different checksums (variant 1 on"
365 " node2.example.com, node3.example.com, node4.example.com;"
366 " variant 2 on master.example.com)" % pathutils.RAPI_CERT_FILE)),
367 (None, ("File %s is missing from node(s) node2.example.com" %
368 pathutils.CLUSTER_DOMAIN_SECRET_FILE)),
369 (None, ("File %s should not exist on node(s) node4.example.com" %
370 pathutils.CLUSTER_CONF_FILE)),
371 (None, ("File %s is missing from node(s) node4.example.com" %
372 hv_xen.XEND_CONFIG_FILE)),
373 (None, ("File %s is missing from node(s) node3.example.com" %
374 pathutils.CLUSTER_CONF_FILE)),
375 (None, ("File %s found with 2 different checksums (variant 1 on"
376 " master.example.com; variant 2 on node4.example.com)" %
377 pathutils.CLUSTER_CONF_FILE)),
378 (None, ("File %s is optional, but it must exist on all or no nodes (not"
379 " found on master.example.com, node2.example.com,"
380 " node3.example.com)" % pathutils.RAPI_USERS_FILE)),
381 (None, ("File %s is optional, but it must exist on all or no nodes (not"
382 " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
383 ("nodata.example.com", "Node did not return file checksum data"),
388 def __init__(self, cfg=NotImplemented, proc=NotImplemented,
390 self.warning_log = []
396 def LogWarning(self, text, *args):
397 self.warning_log.append((text, args))
399 def LogInfo(self, text, *args):
400 self.info_log.append((text, args))
403 class TestLoadNodeEvacResult(unittest.TestCase):
404 def testSuccess(self):
406 ("inst20153.example.com", "grp2", ["nodeA4509", "nodeB2912"]),
408 for early_release in [False, True]:
409 for use_nodes in [False, True]:
411 [opcodes.OpInstanceReplaceDisks().__getstate__()],
412 [opcodes.OpInstanceMigrate().__getstate__()],
415 alloc_result = (moved, [], jobs)
416 assert iallocator._NEVAC_RESULT(alloc_result)
419 result = cmdlib._LoadNodeEvacResult(lu, alloc_result,
420 early_release, use_nodes)
423 (_, (info_args, )) = lu.info_log.pop(0)
424 for (instname, instgroup, instnodes) in moved:
425 self.assertTrue(instname in info_args)
428 self.assertTrue(i in info_args)
430 self.assertTrue(instgroup in info_args)
432 self.assertFalse(lu.info_log)
433 self.assertFalse(lu.warning_log)
435 for op in itertools.chain(*result):
436 if hasattr(op.__class__, "early_release"):
437 self.assertEqual(op.early_release, early_release)
439 self.assertFalse(hasattr(op, "early_release"))
441 def testFailed(self):
442 alloc_result = ([], [
443 ("inst5191.example.com", "errormsg21178"),
445 assert iallocator._NEVAC_RESULT(alloc_result)
448 self.assertRaises(errors.OpExecError, cmdlib._LoadNodeEvacResult,
449 lu, alloc_result, False, False)
450 self.assertFalse(lu.info_log)
451 (_, (args, )) = lu.warning_log.pop(0)
452 self.assertTrue("inst5191.example.com" in args)
453 self.assertTrue("errormsg21178" in args)
454 self.assertFalse(lu.warning_log)
457 class TestUpdateAndVerifySubDict(unittest.TestCase):
460 "a": constants.VTYPE_INT,
461 "b": constants.VTYPE_STRING,
462 "c": constants.VTYPE_BOOL,
463 "d": constants.VTYPE_STRING,
510 verified = cmdlib._UpdateAndVerifySubDict(old_test, test, self.type_check)
511 self.assertEqual(verified, mv)
527 self.assertRaises(errors.TypeEnforcementError,
528 cmdlib._UpdateAndVerifySubDict, {}, test, self.type_check)
531 class TestHvStateHelper(unittest.TestCase):
532 def testWithoutOpData(self):
533 self.assertEqual(cmdlib._MergeAndVerifyHvState(None, NotImplemented), None)
535 def testWithoutOldData(self):
537 constants.HT_XEN_PVM: {
538 constants.HVST_MEMORY_TOTAL: 4096,
541 self.assertEqual(cmdlib._MergeAndVerifyHvState(new, None), new)
543 def testWithWrongHv(self):
546 constants.HVST_MEMORY_TOTAL: 4096,
549 self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyHvState, new,
552 class TestDiskStateHelper(unittest.TestCase):
553 def testWithoutOpData(self):
554 self.assertEqual(cmdlib._MergeAndVerifyDiskState(None, NotImplemented),
557 def testWithoutOldData(self):
561 constants.DS_DISK_RESERVED: 1024,
565 self.assertEqual(cmdlib._MergeAndVerifyDiskState(new, None), new)
567 def testWithWrongStorageType(self):
571 constants.DS_DISK_RESERVED: 1024,
575 self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyDiskState,
579 class TestComputeMinMaxSpec(unittest.TestCase):
582 constants.ISPECS_MAX: {
583 constants.ISPEC_MEM_SIZE: 512,
584 constants.ISPEC_DISK_SIZE: 1024,
586 constants.ISPECS_MIN: {
587 constants.ISPEC_MEM_SIZE: 128,
588 constants.ISPEC_DISK_COUNT: 1,
592 def testNoneValue(self):
593 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
594 self.ipolicy, None) is None)
596 def testAutoValue(self):
597 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
599 constants.VALUE_AUTO) is None)
601 def testNotDefined(self):
602 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT, None,
603 self.ipolicy, 3) is None)
605 def testNoMinDefined(self):
606 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE, None,
607 self.ipolicy, 128) is None)
609 def testNoMaxDefined(self):
610 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT, None,
611 self.ipolicy, 16) is None)
613 def testOutOfRange(self):
614 for (name, val) in ((constants.ISPEC_MEM_SIZE, 64),
615 (constants.ISPEC_MEM_SIZE, 768),
616 (constants.ISPEC_DISK_SIZE, 4096),
617 (constants.ISPEC_DISK_COUNT, 0)):
618 min_v = self.ipolicy[constants.ISPECS_MIN].get(name, val)
619 max_v = self.ipolicy[constants.ISPECS_MAX].get(name, val)
620 self.assertEqual(cmdlib._ComputeMinMaxSpec(name, None,
622 "%s value %s is not in range [%s, %s]" %
623 (name, val,min_v, max_v))
624 self.assertEqual(cmdlib._ComputeMinMaxSpec(name, "1",
626 "%s/1 value %s is not in range [%s, %s]" %
627 (name, val,min_v, max_v))
630 for (name, val) in ((constants.ISPEC_MEM_SIZE, 256),
631 (constants.ISPEC_MEM_SIZE, 128),
632 (constants.ISPEC_MEM_SIZE, 512),
633 (constants.ISPEC_DISK_SIZE, 1024),
634 (constants.ISPEC_DISK_SIZE, 0),
635 (constants.ISPEC_DISK_COUNT, 1),
636 (constants.ISPEC_DISK_COUNT, 5)):
637 self.assertTrue(cmdlib._ComputeMinMaxSpec(name, None, self.ipolicy, val)
641 def _ValidateComputeMinMaxSpec(name, *_):
642 assert name in constants.ISPECS_PARAMETERS
647 def __init__(self, spec):
650 def ComputeMinMaxSpec(self, *args):
651 return self.spec.pop(0)
654 class TestComputeIPolicySpecViolation(unittest.TestCase):
656 compute_fn = _ValidateComputeMinMaxSpec
657 ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
658 [1024], 1, _compute_fn=compute_fn)
659 self.assertEqual(ret, [])
661 def testInvalidArguments(self):
662 self.assertRaises(AssertionError, cmdlib._ComputeIPolicySpecViolation,
663 NotImplemented, 1024, 1, 1, 1, [], 1)
665 def testInvalidSpec(self):
666 spec = _SpecWrapper([None, False, "foo", None, "bar", None])
667 compute_fn = spec.ComputeMinMaxSpec
668 ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
669 [1024], 1, _compute_fn=compute_fn)
670 self.assertEqual(ret, ["foo", "bar"])
671 self.assertFalse(spec.spec)
674 class _StubComputeIPolicySpecViolation:
675 def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
677 self.mem_size = mem_size
678 self.cpu_count = cpu_count
679 self.disk_count = disk_count
680 self.nic_count = nic_count
681 self.disk_sizes = disk_sizes
682 self.spindle_use = spindle_use
684 def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
686 assert self.mem_size == mem_size
687 assert self.cpu_count == cpu_count
688 assert self.disk_count == disk_count
689 assert self.nic_count == nic_count
690 assert self.disk_sizes == disk_sizes
691 assert self.spindle_use == spindle_use
696 class TestComputeIPolicyInstanceViolation(unittest.TestCase):
699 constants.BE_MAXMEM: 2048,
700 constants.BE_VCPUS: 2,
701 constants.BE_SPINDLE_USE: 4,
703 disks = [objects.Disk(size=512)]
704 instance = objects.Instance(beparams=beparams, disks=disks, nics=[])
705 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 4)
706 ret = cmdlib._ComputeIPolicyInstanceViolation(NotImplemented, instance,
708 self.assertEqual(ret, [])
711 class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
714 constants.ISPEC_MEM_SIZE: 2048,
715 constants.ISPEC_CPU_COUNT: 2,
716 constants.ISPEC_DISK_COUNT: 1,
717 constants.ISPEC_DISK_SIZE: [512],
718 constants.ISPEC_NIC_COUNT: 0,
719 constants.ISPEC_SPINDLE_USE: 1,
721 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 1)
722 ret = cmdlib._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
724 self.assertEqual(ret, [])
728 def __init__(self, return_value=None):
730 self.return_value = return_value
732 def __call__(self, *args):
734 return self.return_value
737 class TestComputeIPolicyNodeViolation(unittest.TestCase):
739 self.recorder = _CallRecorder(return_value=[])
741 def testSameGroup(self):
742 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
744 _compute_fn=self.recorder)
745 self.assertFalse(self.recorder.called)
746 self.assertEqual(ret, [])
748 def testDifferentGroup(self):
749 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
751 _compute_fn=self.recorder)
752 self.assertTrue(self.recorder.called)
753 self.assertEqual(ret, [])
756 class _FakeConfigForTargetNodeIPolicy:
757 def __init__(self, node_info=NotImplemented):
758 self._node_info = node_info
760 def GetNodeInfo(self, _):
761 return self._node_info
764 class TestCheckTargetNodeIPolicy(unittest.TestCase):
766 self.instance = objects.Instance(primary_node="blubb")
767 self.target_node = objects.Node(group="bar")
768 node_info = objects.Node(group="foo")
769 fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
770 self.lu = _FakeLU(cfg=fake_cfg)
772 def testNoViolation(self):
773 compute_recoder = _CallRecorder(return_value=[])
774 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
776 _compute_fn=compute_recoder)
777 self.assertTrue(compute_recoder.called)
778 self.assertEqual(self.lu.warning_log, [])
780 def testNoIgnore(self):
781 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
782 self.assertRaises(errors.OpPrereqError, cmdlib._CheckTargetNodeIPolicy,
783 self.lu, NotImplemented, self.instance, self.target_node,
784 _compute_fn=compute_recoder)
785 self.assertTrue(compute_recoder.called)
786 self.assertEqual(self.lu.warning_log, [])
788 def testIgnoreViolation(self):
789 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
790 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
791 self.target_node, ignore=True,
792 _compute_fn=compute_recoder)
793 self.assertTrue(compute_recoder.called)
794 msg = ("Instance does not meet target node group's (bar) instance policy:"
795 " mem_size not in range")
796 self.assertEqual(self.lu.warning_log, [(msg, ())])
799 class TestApplyContainerMods(unittest.TestCase):
800 def testEmptyContainer(self):
803 cmdlib.ApplyContainerMods("test", container, chgdesc, [], None, None, None)
804 self.assertEqual(container, [])
805 self.assertEqual(chgdesc, [])
810 mods = cmdlib.PrepareContainerMods([
811 (constants.DDM_ADD, -1, "Hello"),
812 (constants.DDM_ADD, -1, "World"),
813 (constants.DDM_ADD, 0, "Start"),
814 (constants.DDM_ADD, -1, "End"),
816 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
818 self.assertEqual(container, ["Start", "Hello", "World", "End"])
819 self.assertEqual(chgdesc, [])
821 mods = cmdlib.PrepareContainerMods([
822 (constants.DDM_ADD, 0, "zero"),
823 (constants.DDM_ADD, 3, "Added"),
824 (constants.DDM_ADD, 5, "four"),
825 (constants.DDM_ADD, 7, "xyz"),
827 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
829 self.assertEqual(container,
830 ["zero", "Start", "Hello", "Added", "World", "four",
832 self.assertEqual(chgdesc, [])
834 for idx in [-2, len(container) + 1]:
835 mods = cmdlib.PrepareContainerMods([
836 (constants.DDM_ADD, idx, "error"),
838 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
839 "test", container, None, mods, None, None, None)
841 def testRemoveError(self):
842 for idx in [0, 1, 2, 100, -1, -4]:
843 mods = cmdlib.PrepareContainerMods([
844 (constants.DDM_REMOVE, idx, None),
846 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
847 "test", [], None, mods, None, None, None)
849 mods = cmdlib.PrepareContainerMods([
850 (constants.DDM_REMOVE, 0, object()),
852 self.assertRaises(AssertionError, cmdlib.ApplyContainerMods,
853 "test", [""], None, mods, None, None, None)
855 def testAddError(self):
856 for idx in range(-100, -1) + [100]:
857 mods = cmdlib.PrepareContainerMods([
858 (constants.DDM_ADD, idx, None),
860 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
861 "test", [], None, mods, None, None, None)
863 def testRemove(self):
864 container = ["item 1", "item 2"]
865 mods = cmdlib.PrepareContainerMods([
866 (constants.DDM_ADD, -1, "aaa"),
867 (constants.DDM_REMOVE, -1, None),
868 (constants.DDM_ADD, -1, "bbb"),
871 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
873 self.assertEqual(container, ["item 1", "item 2", "bbb"])
874 self.assertEqual(chgdesc, [
875 ("test/2", "remove"),
878 def testModify(self):
879 container = ["item 1", "item 2"]
880 mods = cmdlib.PrepareContainerMods([
881 (constants.DDM_MODIFY, -1, "a"),
882 (constants.DDM_MODIFY, 0, "b"),
883 (constants.DDM_MODIFY, 1, "c"),
886 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
888 self.assertEqual(container, ["item 1", "item 2"])
889 self.assertEqual(chgdesc, [])
891 for idx in [-2, len(container) + 1]:
892 mods = cmdlib.PrepareContainerMods([
893 (constants.DDM_MODIFY, idx, "error"),
895 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
896 "test", container, None, mods, None, None, None)
903 def _CreateTestFn(idx, params, private):
904 private.data = ("add", idx, params)
905 return ((100 * idx, params), [
906 ("test/%s" % idx, hex(idx)),
910 def _ModifyTestFn(idx, item, params, private):
911 private.data = ("modify", idx, params)
913 ("test/%s" % idx, "modify %s" % params),
917 def _RemoveTestFn(idx, item, private):
918 private.data = ("remove", idx, item)
920 def testAddWithCreateFunction(self):
923 mods = cmdlib.PrepareContainerMods([
924 (constants.DDM_ADD, -1, "Hello"),
925 (constants.DDM_ADD, -1, "World"),
926 (constants.DDM_ADD, 0, "Start"),
927 (constants.DDM_ADD, -1, "End"),
928 (constants.DDM_REMOVE, 2, None),
929 (constants.DDM_MODIFY, -1, "foobar"),
930 (constants.DDM_REMOVE, 2, None),
931 (constants.DDM_ADD, 1, "More"),
932 ], self._PrivateData)
933 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
934 self._CreateTestFn, self._ModifyTestFn, self._RemoveTestFn)
935 self.assertEqual(container, [
940 self.assertEqual(chgdesc, [
945 ("test/2", "remove"),
946 ("test/2", "modify foobar"),
947 ("test/2", "remove"),
950 self.assertTrue(compat.all(op == private.data[0]
951 for (op, _, _, private) in mods))
952 self.assertEqual([private.data for (op, _, _, private) in mods], [
957 ("remove", 2, (100, "World")),
958 ("modify", 2, "foobar"),
959 ("remove", 2, (300, "End")),
964 class _FakeConfigForGenDiskTemplate:
966 self._unique_id = itertools.count()
967 self._drbd_minor = itertools.count(20)
968 self._port = itertools.count(constants.FIRST_DRBD_PORT)
969 self._secret = itertools.count()
974 def GenerateUniqueID(self, ec_id):
975 return "ec%s-uq%s" % (ec_id, self._unique_id.next())
977 def AllocateDRBDMinor(self, nodes, instance):
978 return [self._drbd_minor.next()
981 def AllocatePort(self):
982 return self._port.next()
984 def GenerateDRBDSecret(self, ec_id):
985 return "ec%s-secret%s" % (ec_id, self._secret.next())
987 def GetInstanceInfo(self, _):
991 class _FakeProcForGenDiskTemplate:
996 class TestGenerateDiskTemplate(unittest.TestCase):
998 nodegroup = objects.NodeGroup(name="ng")
999 nodegroup.UpgradeConfig()
1001 cfg = _FakeConfigForGenDiskTemplate()
1002 proc = _FakeProcForGenDiskTemplate()
1004 self.lu = _FakeLU(cfg=cfg, proc=proc)
1005 self.nodegroup = nodegroup
1008 def GetDiskParams():
1009 return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1011 def testWrongDiskTemplate(self):
1012 gdt = cmdlib._GenerateDiskTemplate
1013 disk_template = "##unknown##"
1015 assert disk_template not in constants.DISK_TEMPLATES
1017 self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
1018 "inst26831.example.com", "node30113.example.com", [], [],
1019 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1020 self.GetDiskParams())
1022 def testDiskless(self):
1023 gdt = cmdlib._GenerateDiskTemplate
1025 result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1026 "node30113.example.com", [], [],
1027 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1028 self.GetDiskParams())
1029 self.assertEqual(result, [])
1031 def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1032 file_storage_dir=NotImplemented,
1033 file_driver=NotImplemented,
1034 req_file_storage=NotImplemented,
1035 req_shr_file_storage=NotImplemented):
1036 gdt = cmdlib._GenerateDiskTemplate
1038 map(lambda params: utils.ForceDictType(params,
1039 constants.IDISK_PARAMS_TYPES),
1042 # Check if non-empty list of secondaries is rejected
1043 self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1044 template, "inst25088.example.com",
1045 "node185.example.com", ["node323.example.com"], [],
1046 NotImplemented, NotImplemented, base_index,
1047 self.lu.LogInfo, self.GetDiskParams(),
1048 _req_file_storage=req_file_storage,
1049 _req_shr_file_storage=req_shr_file_storage)
1051 result = gdt(self.lu, template, "inst21662.example.com",
1052 "node21741.example.com", [],
1053 disk_info, file_storage_dir, file_driver, base_index,
1054 self.lu.LogInfo, self.GetDiskParams(),
1055 _req_file_storage=req_file_storage,
1056 _req_shr_file_storage=req_shr_file_storage)
1058 for (idx, disk) in enumerate(result):
1059 self.assertTrue(isinstance(disk, objects.Disk))
1060 self.assertEqual(disk.dev_type, exp_dev_type)
1061 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1062 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1063 self.assertTrue(disk.children is None)
1065 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1066 cmdlib._UpdateIvNames(base_index, result)
1067 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1071 def _CheckIvNames(self, disks, base_index, end_index):
1072 self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1073 ["disk/%s" % i for i in range(base_index, end_index)])
1075 def testPlain(self):
1077 constants.IDISK_SIZE: 1024,
1078 constants.IDISK_MODE: constants.DISK_RDWR,
1080 constants.IDISK_SIZE: 4096,
1081 constants.IDISK_VG: "othervg",
1082 constants.IDISK_MODE: constants.DISK_RDWR,
1085 result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1088 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1089 ("testvg", "ec0-uq0.disk3"),
1090 ("othervg", "ec0-uq1.disk4"),
1094 def _AllowFileStorage():
1098 def _ForbidFileStorage():
1099 raise errors.OpPrereqError("Disallowed in test")
1102 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1103 constants.DT_FILE, [], 0, NotImplemented,
1104 req_file_storage=self._ForbidFileStorage)
1105 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1106 constants.DT_SHARED_FILE, [], 0, NotImplemented,
1107 req_shr_file_storage=self._ForbidFileStorage)
1109 for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1111 constants.IDISK_SIZE: 80 * 1024,
1112 constants.IDISK_MODE: constants.DISK_RDONLY,
1114 constants.IDISK_SIZE: 4096,
1115 constants.IDISK_MODE: constants.DISK_RDWR,
1117 constants.IDISK_SIZE: 6 * 1024,
1118 constants.IDISK_MODE: constants.DISK_RDWR,
1121 result = self._TestTrivialDisk(disk_template, disk_info, 2,
1122 constants.LD_FILE, file_storage_dir="/tmp",
1123 file_driver=constants.FD_BLKTAP,
1124 req_file_storage=self._AllowFileStorage,
1125 req_shr_file_storage=self._AllowFileStorage)
1127 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1128 (constants.FD_BLKTAP, "/tmp/disk2"),
1129 (constants.FD_BLKTAP, "/tmp/disk3"),
1130 (constants.FD_BLKTAP, "/tmp/disk4"),
1133 def testBlock(self):
1135 constants.IDISK_SIZE: 8 * 1024,
1136 constants.IDISK_MODE: constants.DISK_RDWR,
1137 constants.IDISK_ADOPT: "/tmp/some/block/dev",
1140 result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1141 constants.LD_BLOCKDEV)
1143 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1144 (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1149 constants.IDISK_SIZE: 8 * 1024,
1150 constants.IDISK_MODE: constants.DISK_RDONLY,
1152 constants.IDISK_SIZE: 100 * 1024,
1153 constants.IDISK_MODE: constants.DISK_RDWR,
1156 result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1159 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1160 ("rbd", "ec0-uq0.rbd.disk0"),
1161 ("rbd", "ec0-uq1.rbd.disk1"),
1164 def testDrbd8(self):
1165 gdt = cmdlib._GenerateDiskTemplate
1166 drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1167 drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1170 constants.IDISK_SIZE: 1024,
1171 constants.IDISK_MODE: constants.DISK_RDWR,
1173 constants.IDISK_SIZE: 100 * 1024,
1174 constants.IDISK_MODE: constants.DISK_RDONLY,
1175 constants.IDISK_METAVG: "metavg",
1177 constants.IDISK_SIZE: 4096,
1178 constants.IDISK_MODE: constants.DISK_RDWR,
1179 constants.IDISK_VG: "vgxyz",
1183 exp_logical_ids = [[
1184 (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1185 (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1187 (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1188 ("metavg", "ec0-uq1.disk1_meta"),
1190 ("vgxyz", "ec0-uq2.disk2_data"),
1191 (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1194 assert len(exp_logical_ids) == len(disk_info)
1196 map(lambda params: utils.ForceDictType(params,
1197 constants.IDISK_PARAMS_TYPES),
1200 # Check if empty list of secondaries is rejected
1201 self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1202 "inst827.example.com", "node1334.example.com", [],
1203 disk_info, NotImplemented, NotImplemented, 0,
1204 self.lu.LogInfo, self.GetDiskParams())
1206 result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1207 "node1334.example.com", ["node12272.example.com"],
1208 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1209 self.GetDiskParams())
1211 for (idx, disk) in enumerate(result):
1212 self.assertTrue(isinstance(disk, objects.Disk))
1213 self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1214 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1215 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1217 for child in disk.children:
1218 self.assertTrue(isinstance(disk, objects.Disk))
1219 self.assertEqual(child.dev_type, constants.LD_LV)
1220 self.assertTrue(child.children is None)
1222 self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1223 exp_logical_ids[idx])
1225 self.assertEqual(len(disk.children), 2)
1226 self.assertEqual(disk.children[0].size, disk.size)
1227 self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1229 self._CheckIvNames(result, 0, len(disk_info))
1230 cmdlib._UpdateIvNames(0, result)
1231 self._CheckIvNames(result, 0, len(disk_info))
1233 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1234 ("node1334.example.com", "node12272.example.com",
1235 constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1236 ("node1334.example.com", "node12272.example.com",
1237 constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1238 ("node1334.example.com", "node12272.example.com",
1239 constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1243 class _ConfigForDiskWipe:
1244 def SetDiskID(self, device, node):
1245 assert isinstance(device, objects.Disk)
1246 assert node == "node1.example.com"
1249 class _RpcForDiskWipe:
1250 def __init__(self, pause_cb, wipe_cb):
1251 self._pause_cb = pause_cb
1252 self._wipe_cb = wipe_cb
1254 def call_blockdev_pause_resume_sync(self, node, disks, pause):
1255 assert node == "node1.example.com"
1256 return rpc.RpcResult(data=self._pause_cb(disks, pause))
1258 def call_blockdev_wipe(self, node, bdev, offset, size):
1259 assert node == "node1.example.com"
1260 return rpc.RpcResult(data=self._wipe_cb(bdev, offset, size))
1263 class _DiskPauseTracker:
1267 def __call__(self, (disks, instance), pause):
1268 assert instance.disks == disks
1270 self.history.extend((i.logical_id, i.size, pause)
1273 return (True, [True] * len(disks))
1276 class TestWipeDisks(unittest.TestCase):
1277 def testPauseFailure(self):
1278 def _FailPause((disks, _), pause):
1279 self.assertEqual(len(disks), 3)
1280 self.assertTrue(pause)
1281 return (False, "error")
1283 lu = _FakeLU(rpc=_RpcForDiskWipe(_FailPause, NotImplemented),
1284 cfg=_ConfigForDiskWipe())
1287 objects.Disk(dev_type=constants.LD_LV),
1288 objects.Disk(dev_type=constants.LD_LV),
1289 objects.Disk(dev_type=constants.LD_LV),
1292 instance = objects.Instance(name="inst21201",
1293 primary_node="node1.example.com",
1294 disk_template=constants.DT_PLAIN,
1297 self.assertRaises(errors.OpExecError, cmdlib._WipeDisks, lu, instance)
1299 def testFailingWipe(self):
1300 pt = _DiskPauseTracker()
1302 def _WipeCb((disk, _), offset, size):
1303 assert disk.logical_id == "disk0"
1304 return (False, None)
1306 lu = _FakeLU(rpc=_RpcForDiskWipe(pt, _WipeCb),
1307 cfg=_ConfigForDiskWipe())
1310 objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1312 objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1314 objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=256),
1317 instance = objects.Instance(name="inst562",
1318 primary_node="node1.example.com",
1319 disk_template=constants.DT_PLAIN,
1323 cmdlib._WipeDisks(lu, instance)
1324 except errors.OpExecError, err:
1325 self.assertTrue(str(err), "Could not wipe disk 0 at offset 0 ")
1327 self.fail("Did not raise exception")
1329 self.assertEqual(pt.history, [
1330 ("disk0", 100 * 1024, True),
1331 ("disk1", 500 * 1024, True),
1332 ("disk2", 256, True),
1333 ("disk0", 100 * 1024, False),
1334 ("disk1", 500 * 1024, False),
1335 ("disk2", 256, False),
1338 def testNormalWipe(self):
1339 pt = _DiskPauseTracker()
1343 def _WipeCb((disk, _), offset, size):
1344 assert isinstance(offset, (long, int))
1345 assert isinstance(size, (long, int))
1347 max_chunk_size = (disk.size / 100.0 * constants.MIN_WIPE_CHUNK_PERCENT)
1349 self.assertTrue(offset >= 0)
1350 self.assertTrue((offset + size) <= disk.size)
1352 self.assertTrue(size > 0)
1353 self.assertTrue(size <= constants.MAX_WIPE_CHUNK)
1354 self.assertTrue(size <= max_chunk_size)
1356 self.assertTrue(offset == 0 or disk.logical_id in progress)
1358 # Keep track of progress
1359 cur_progress = progress.setdefault(disk.logical_id, 0)
1360 self.assertEqual(cur_progress, offset)
1362 progress[disk.logical_id] += size
1366 lu = _FakeLU(rpc=_RpcForDiskWipe(pt, _WipeCb),
1367 cfg=_ConfigForDiskWipe())
1370 objects.Disk(dev_type=constants.LD_LV, logical_id="disk0", size=1024),
1371 objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1373 objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=128),
1374 objects.Disk(dev_type=constants.LD_LV, logical_id="disk3",
1375 size=constants.MAX_WIPE_CHUNK),
1378 instance = objects.Instance(name="inst3560",
1379 primary_node="node1.example.com",
1380 disk_template=constants.DT_PLAIN,
1383 cmdlib._WipeDisks(lu, instance)
1385 self.assertEqual(pt.history, [
1386 ("disk0", 1024, True),
1387 ("disk1", 500 * 1024, True),
1388 ("disk2", 128, True),
1389 ("disk3", constants.MAX_WIPE_CHUNK, True),
1390 ("disk0", 1024, False),
1391 ("disk1", 500 * 1024, False),
1392 ("disk2", 128, False),
1393 ("disk3", constants.MAX_WIPE_CHUNK, False),
1396 # Ensure the complete disk has been wiped
1397 self.assertEqual(progress, dict((i.logical_id, i.size) for i in disks))
1400 if __name__ == "__main__":
1401 testutils.GanetiTestProgram()