4 # Copyright (C) 2008, 2011 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"""
33 from ganeti import constants
34 from ganeti import mcpu
35 from ganeti import cmdlib
36 from ganeti import opcodes
37 from ganeti import errors
38 from ganeti import utils
39 from ganeti import luxi
41 from ganeti import objects
42 from ganeti import compat
43 from ganeti import rpc
44 from ganeti.hypervisor import hv_xen
50 class TestCertVerification(testutils.GanetiTestCase):
52 testutils.GanetiTestCase.setUp(self)
54 self.tmpdir = tempfile.mkdtemp()
57 shutil.rmtree(self.tmpdir)
59 def testVerifyCertificate(self):
60 cmdlib._VerifyCertificate(self._TestDataFilename("cert1.pem"))
62 nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
64 (errcode, msg) = cmdlib._VerifyCertificate(nonexist_filename)
65 self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
67 # Try to load non-certificate file
68 invalid_cert = self._TestDataFilename("bdev-net.txt")
69 (errcode, msg) = cmdlib._VerifyCertificate(invalid_cert)
70 self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
73 class TestOpcodeParams(testutils.GanetiTestCase):
74 def testParamsStructures(self):
75 for op in sorted(mcpu.Processor.DISPATCH_TABLE):
76 lu = mcpu.Processor.DISPATCH_TABLE[op]
78 self.failIf(hasattr(lu, "_OP_REQP"),
79 msg=("LU '%s' has old-style _OP_REQP" % lu_name))
80 self.failIf(hasattr(lu, "_OP_DEFS"),
81 msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
82 self.failIf(hasattr(lu, "_OP_PARAMS"),
83 msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
86 class TestIAllocatorChecks(testutils.GanetiTestCase):
87 def testFunction(self):
89 def __init__(self, opcode):
90 self.cfg = mocks.FakeConfig()
93 class OpTest(opcodes.OpCode):
95 ("iallocator", None, ht.NoType, None),
96 ("node", None, ht.NoType, None),
99 default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
100 other_iallocator = default_iallocator + "_not"
105 c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
107 # Neither node nor iallocator given
111 self.assertEqual(lu.op.iallocator, default_iallocator)
112 self.assertEqual(lu.op.node, None)
114 # Both, iallocator and node given
115 op.iallocator = "test"
117 self.assertRaises(errors.OpPrereqError, c_i)
119 # Only iallocator given
120 op.iallocator = other_iallocator
123 self.assertEqual(lu.op.iallocator, other_iallocator)
124 self.assertEqual(lu.op.node, None)
130 self.assertEqual(lu.op.iallocator, None)
131 self.assertEqual(lu.op.node, "node")
133 # No node, iallocator or default iallocator
136 lu.cfg.GetDefaultIAllocator = lambda: None
137 self.assertRaises(errors.OpPrereqError, c_i)
140 class TestLUTestJqueue(unittest.TestCase):
142 self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
143 (luxi.WFJC_TIMEOUT * 0.75),
144 msg=("Client timeout too high, might not notice bugs"
145 " in WaitForJobChange"))
148 class TestLUQuery(unittest.TestCase):
150 self.assertEqual(sorted(cmdlib._QUERY_IMPL.keys()),
151 sorted(constants.QR_VIA_OP))
153 assert constants.QR_NODE in constants.QR_VIA_OP
154 assert constants.QR_INSTANCE in constants.QR_VIA_OP
156 for i in constants.QR_VIA_OP:
157 self.assert_(cmdlib._GetQueryImplementation(i))
159 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation, "")
160 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation,
164 class TestLUGroupAssignNodes(unittest.TestCase):
166 def testCheckAssignmentForSplitInstances(self):
167 node_data = dict((name, objects.Node(name=name, group=group))
168 for (name, group) in [("n1a", "g1"), ("n1b", "g1"),
169 ("n2a", "g2"), ("n2b", "g2"),
170 ("n3a", "g3"), ("n3b", "g3"),
174 def Instance(name, pnode, snode):
177 disk_template = constants.DT_DISKLESS
179 disks = [objects.Disk(dev_type=constants.LD_DRBD8,
180 logical_id=[pnode, snode, 1, 17, 17])]
181 disk_template = constants.DT_DRBD8
183 return objects.Instance(name=name, primary_node=pnode, disks=disks,
184 disk_template=disk_template)
186 instance_data = dict((name, Instance(name, pnode, snode))
187 for name, pnode, snode in [("inst1a", "n1a", "n1b"),
188 ("inst1b", "n1b", "n1a"),
189 ("inst2a", "n2a", "n2b"),
190 ("inst3a", "n3a", None),
191 ("inst3b", "n3b", "n1b"),
192 ("inst3c", "n3b", "n2b"),
195 # Test first with the existing state.
197 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
201 self.assertEqual([], new)
202 self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
204 # And now some changes.
206 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
211 self.assertEqual(set(["inst1a", "inst1b"]), set(new))
212 self.assertEqual(set(["inst3c"]), set(prev))
215 class TestClusterVerifySsh(unittest.TestCase):
216 def testMultipleGroups(self):
217 fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
219 objects.Node(name="node20", group="my", offline=False),
220 objects.Node(name="node21", group="my", offline=False),
221 objects.Node(name="node22", group="my", offline=False),
222 objects.Node(name="node23", group="my", offline=False),
223 objects.Node(name="node24", group="my", offline=False),
224 objects.Node(name="node25", group="my", offline=False),
225 objects.Node(name="node26", group="my", offline=True),
228 objects.Node(name="node1", group="g1", offline=True),
229 objects.Node(name="node2", group="g1", offline=False),
230 objects.Node(name="node3", group="g1", offline=False),
231 objects.Node(name="node4", group="g1", offline=True),
232 objects.Node(name="node5", group="g1", offline=False),
233 objects.Node(name="node10", group="xyz", offline=False),
234 objects.Node(name="node11", group="xyz", offline=False),
235 objects.Node(name="node40", group="alloff", offline=True),
236 objects.Node(name="node41", group="alloff", offline=True),
237 objects.Node(name="node50", group="aaa", offline=False),
239 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
241 (online, perhost) = fn(mygroupnodes, "my", nodes)
242 self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
243 self.assertEqual(set(perhost.keys()), set(online))
245 self.assertEqual(perhost, {
246 "node20": ["node10", "node2", "node50"],
247 "node21": ["node11", "node3", "node50"],
248 "node22": ["node10", "node5", "node50"],
249 "node23": ["node11", "node2", "node50"],
250 "node24": ["node10", "node3", "node50"],
251 "node25": ["node11", "node5", "node50"],
254 def testSingleGroup(self):
255 fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
257 objects.Node(name="node1", group="default", offline=True),
258 objects.Node(name="node2", group="default", offline=False),
259 objects.Node(name="node3", group="default", offline=False),
260 objects.Node(name="node4", group="default", offline=True),
262 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
264 (online, perhost) = fn(nodes, "default", nodes)
265 self.assertEqual(online, ["node2", "node3"])
266 self.assertEqual(set(perhost.keys()), set(online))
268 self.assertEqual(perhost, {
274 class TestClusterVerifyFiles(unittest.TestCase):
276 def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
277 assert ((ecode == constants.CV_ENODEFILECHECK and
278 ht.TNonEmptyString(item)) or
279 (ecode == constants.CV_ECLUSTERFILECHECK and
286 errors.append((item, msg))
288 _VerifyFiles = cmdlib.LUClusterVerifyGroup._VerifyFiles
292 master_name = "master.example.com"
294 objects.Node(name=master_name, offline=False, vm_capable=True),
295 objects.Node(name="node2.example.com", offline=False, vm_capable=True),
296 objects.Node(name="node3.example.com", master_candidate=True,
298 objects.Node(name="node4.example.com", offline=False, vm_capable=True),
299 objects.Node(name="nodata.example.com", offline=False, vm_capable=True),
300 objects.Node(name="offline.example.com", offline=True),
302 cluster = objects.Cluster(modify_etc_hosts=True,
303 enabled_hypervisors=[constants.HT_XEN_HVM])
305 constants.CLUSTER_DOMAIN_SECRET_FILE,
306 constants.RAPI_CERT_FILE,
307 constants.RAPI_USERS_FILE,
310 constants.RAPI_USERS_FILE,
311 hv_xen.XL_CONFIG_FILE,
312 constants.VNC_PASSWORD_FILE,
315 constants.CLUSTER_CONF_FILE,
318 hv_xen.XEND_CONFIG_FILE,
319 hv_xen.XL_CONFIG_FILE,
320 constants.VNC_PASSWORD_FILE,
323 master_name: rpc.RpcResult(data=(True, {
324 constants.NV_FILELIST: {
325 constants.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
326 constants.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
327 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
328 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
329 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
331 "node2.example.com": rpc.RpcResult(data=(True, {
332 constants.NV_FILELIST: {
333 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
334 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
337 "node3.example.com": rpc.RpcResult(data=(True, {
338 constants.NV_FILELIST: {
339 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
340 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
343 "node4.example.com": rpc.RpcResult(data=(True, {
344 constants.NV_FILELIST: {
345 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
346 constants.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
347 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
348 constants.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
349 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
352 "nodata.example.com": rpc.RpcResult(data=(True, {})),
353 "offline.example.com": rpc.RpcResult(offline=True),
355 assert set(nvinfo.keys()) == set(map(operator.attrgetter("name"), nodeinfo))
357 self._VerifyFiles(compat.partial(self._FakeErrorIf, errors), nodeinfo,
359 (files_all, files_opt, files_mc, files_vm))
360 self.assertEqual(sorted(errors), sorted([
361 (None, ("File %s found with 2 different checksums (variant 1 on"
362 " node2.example.com, node3.example.com, node4.example.com;"
363 " variant 2 on master.example.com)" % constants.RAPI_CERT_FILE)),
364 (None, ("File %s is missing from node(s) node2.example.com" %
365 constants.CLUSTER_DOMAIN_SECRET_FILE)),
366 (None, ("File %s should not exist on node(s) node4.example.com" %
367 constants.CLUSTER_CONF_FILE)),
368 (None, ("File %s is missing from node(s) node4.example.com" %
369 hv_xen.XEND_CONFIG_FILE)),
370 (None, ("File %s is missing from node(s) node3.example.com" %
371 constants.CLUSTER_CONF_FILE)),
372 (None, ("File %s found with 2 different checksums (variant 1 on"
373 " master.example.com; variant 2 on node4.example.com)" %
374 constants.CLUSTER_CONF_FILE)),
375 (None, ("File %s is optional, but it must exist on all or no nodes (not"
376 " found on master.example.com, node2.example.com,"
377 " node3.example.com)" % constants.RAPI_USERS_FILE)),
378 (None, ("File %s is optional, but it must exist on all or no nodes (not"
379 " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
380 ("nodata.example.com", "Node did not return file checksum data"),
385 def __init__(self, cfg=NotImplemented, proc=NotImplemented):
386 self.warning_log = []
391 def LogWarning(self, text, *args):
392 self.warning_log.append((text, args))
394 def LogInfo(self, text, *args):
395 self.info_log.append((text, args))
398 class TestLoadNodeEvacResult(unittest.TestCase):
399 def testSuccess(self):
401 ("inst20153.example.com", "grp2", ["nodeA4509", "nodeB2912"]),
403 for early_release in [False, True]:
404 for use_nodes in [False, True]:
406 [opcodes.OpInstanceReplaceDisks().__getstate__()],
407 [opcodes.OpInstanceMigrate().__getstate__()],
410 alloc_result = (moved, [], jobs)
411 assert cmdlib.IAllocator._NEVAC_RESULT(alloc_result)
414 result = cmdlib._LoadNodeEvacResult(lu, alloc_result,
415 early_release, use_nodes)
418 (_, (info_args, )) = lu.info_log.pop(0)
419 for (instname, instgroup, instnodes) in moved:
420 self.assertTrue(instname in info_args)
423 self.assertTrue(i in info_args)
425 self.assertTrue(instgroup in info_args)
427 self.assertFalse(lu.info_log)
428 self.assertFalse(lu.warning_log)
430 for op in itertools.chain(*result):
431 if hasattr(op.__class__, "early_release"):
432 self.assertEqual(op.early_release, early_release)
434 self.assertFalse(hasattr(op, "early_release"))
436 def testFailed(self):
437 alloc_result = ([], [
438 ("inst5191.example.com", "errormsg21178"),
440 assert cmdlib.IAllocator._NEVAC_RESULT(alloc_result)
443 self.assertRaises(errors.OpExecError, cmdlib._LoadNodeEvacResult,
444 lu, alloc_result, False, False)
445 self.assertFalse(lu.info_log)
446 (_, (args, )) = lu.warning_log.pop(0)
447 self.assertTrue("inst5191.example.com" in args)
448 self.assertTrue("errormsg21178" in args)
449 self.assertFalse(lu.warning_log)
452 class TestUpdateAndVerifySubDict(unittest.TestCase):
455 "a": constants.VTYPE_INT,
456 "b": constants.VTYPE_STRING,
457 "c": constants.VTYPE_BOOL,
458 "d": constants.VTYPE_STRING,
505 verified = cmdlib._UpdateAndVerifySubDict(old_test, test, self.type_check)
506 self.assertEqual(verified, mv)
522 self.assertRaises(errors.TypeEnforcementError,
523 cmdlib._UpdateAndVerifySubDict, {}, test, self.type_check)
526 class TestHvStateHelper(unittest.TestCase):
527 def testWithoutOpData(self):
528 self.assertEqual(cmdlib._MergeAndVerifyHvState(None, NotImplemented), None)
530 def testWithoutOldData(self):
532 constants.HT_XEN_PVM: {
533 constants.HVST_MEMORY_TOTAL: 4096,
536 self.assertEqual(cmdlib._MergeAndVerifyHvState(new, None), new)
538 def testWithWrongHv(self):
541 constants.HVST_MEMORY_TOTAL: 4096,
544 self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyHvState, new,
547 class TestDiskStateHelper(unittest.TestCase):
548 def testWithoutOpData(self):
549 self.assertEqual(cmdlib._MergeAndVerifyDiskState(None, NotImplemented),
552 def testWithoutOldData(self):
556 constants.DS_DISK_RESERVED: 1024,
560 self.assertEqual(cmdlib._MergeAndVerifyDiskState(new, None), new)
562 def testWithWrongStorageType(self):
566 constants.DS_DISK_RESERVED: 1024,
570 self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyDiskState,
574 class TestComputeMinMaxSpec(unittest.TestCase):
577 constants.ISPECS_MAX: {
578 constants.ISPEC_MEM_SIZE: 512,
579 constants.ISPEC_DISK_SIZE: 1024,
581 constants.ISPECS_MIN: {
582 constants.ISPEC_MEM_SIZE: 128,
583 constants.ISPEC_DISK_COUNT: 1,
587 def testNoneValue(self):
588 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE,
589 self.ipolicy, None) is None)
591 def testAutoValue(self):
592 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE,
594 constants.VALUE_AUTO) is None)
596 def testNotDefined(self):
597 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT,
598 self.ipolicy, 3) is None)
600 def testNoMinDefined(self):
601 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE,
602 self.ipolicy, 128) is None)
604 def testNoMaxDefined(self):
605 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT,
606 self.ipolicy, 16) is None)
608 def testOutOfRange(self):
609 for (name, val) in ((constants.ISPEC_MEM_SIZE, 64),
610 (constants.ISPEC_MEM_SIZE, 768),
611 (constants.ISPEC_DISK_SIZE, 4096),
612 (constants.ISPEC_DISK_COUNT, 0)):
613 min_v = self.ipolicy[constants.ISPECS_MIN].get(name, val)
614 max_v = self.ipolicy[constants.ISPECS_MAX].get(name, val)
615 self.assertEqual(cmdlib._ComputeMinMaxSpec(name, self.ipolicy, val),
616 "%s value %s is not in range [%s, %s]" %
617 (name, val,min_v, max_v))
620 for (name, val) in ((constants.ISPEC_MEM_SIZE, 256),
621 (constants.ISPEC_MEM_SIZE, 128),
622 (constants.ISPEC_MEM_SIZE, 512),
623 (constants.ISPEC_DISK_SIZE, 1024),
624 (constants.ISPEC_DISK_SIZE, 0),
625 (constants.ISPEC_DISK_COUNT, 1),
626 (constants.ISPEC_DISK_COUNT, 5)):
627 self.assertTrue(cmdlib._ComputeMinMaxSpec(name, self.ipolicy, val)
631 def _ValidateComputeMinMaxSpec(name, *_):
632 assert name in constants.ISPECS_PARAMETERS
637 def __init__(self, spec):
640 def ComputeMinMaxSpec(self, *args):
641 return self.spec.pop(0)
644 class TestComputeIPolicySpecViolation(unittest.TestCase):
646 compute_fn = _ValidateComputeMinMaxSpec
647 ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
648 [1024], _compute_fn=compute_fn)
649 self.assertEqual(ret, [])
651 def testInvalidArguments(self):
652 self.assertRaises(AssertionError, cmdlib._ComputeIPolicySpecViolation,
653 NotImplemented, 1024, 1, 1, 1, [])
655 def testInvalidSpec(self):
656 spec = _SpecWrapper([None, False, "foo", None, "bar"])
657 compute_fn = spec.ComputeMinMaxSpec
658 ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
659 [1024], _compute_fn=compute_fn)
660 self.assertEqual(ret, ["foo", "bar"])
661 self.assertFalse(spec.spec)
664 class _StubComputeIPolicySpecViolation:
665 def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes):
666 self.mem_size = mem_size
667 self.cpu_count = cpu_count
668 self.disk_count = disk_count
669 self.nic_count = nic_count
670 self.disk_sizes = disk_sizes
672 def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes):
673 assert self.mem_size == mem_size
674 assert self.cpu_count == cpu_count
675 assert self.disk_count == disk_count
676 assert self.nic_count == nic_count
677 assert self.disk_sizes == disk_sizes
682 class TestComputeIPolicyInstanceViolation(unittest.TestCase):
685 constants.BE_MAXMEM: 2048,
686 constants.BE_VCPUS: 2,
688 disks = [objects.Disk(size=512)]
689 instance = objects.Instance(beparams=beparams, disks=disks, nics=[])
690 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512])
691 ret = cmdlib._ComputeIPolicyInstanceViolation(NotImplemented, instance,
693 self.assertEqual(ret, [])
696 class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
699 constants.ISPEC_MEM_SIZE: 2048,
700 constants.ISPEC_CPU_COUNT: 2,
701 constants.ISPEC_DISK_COUNT: 1,
702 constants.ISPEC_DISK_SIZE: [512],
703 constants.ISPEC_NIC_COUNT: 0,
705 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512])
706 ret = cmdlib._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
708 self.assertEqual(ret, [])
712 def __init__(self, return_value=None):
714 self.return_value = return_value
716 def __call__(self, *args):
718 return self.return_value
721 class TestComputeIPolicyNodeViolation(unittest.TestCase):
723 self.recorder = _CallRecorder(return_value=[])
725 def testSameGroup(self):
726 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
728 _compute_fn=self.recorder)
729 self.assertFalse(self.recorder.called)
730 self.assertEqual(ret, [])
732 def testDifferentGroup(self):
733 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
735 _compute_fn=self.recorder)
736 self.assertTrue(self.recorder.called)
737 self.assertEqual(ret, [])
740 class _FakeConfigForTargetNodeIPolicy:
741 def __init__(self, node_info=NotImplemented):
742 self._node_info = node_info
744 def GetNodeInfo(self, _):
745 return self._node_info
748 class TestCheckTargetNodeIPolicy(unittest.TestCase):
750 self.instance = objects.Instance(primary_node="blubb")
751 self.target_node = objects.Node(group="bar")
752 node_info = objects.Node(group="foo")
753 fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
754 self.lu = _FakeLU(cfg=fake_cfg)
756 def testNoViolation(self):
757 compute_recoder = _CallRecorder(return_value=[])
758 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
760 _compute_fn=compute_recoder)
761 self.assertTrue(compute_recoder.called)
762 self.assertEqual(self.lu.warning_log, [])
764 def testNoIgnore(self):
765 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
766 self.assertRaises(errors.OpPrereqError, cmdlib._CheckTargetNodeIPolicy,
767 self.lu, NotImplemented, self.instance, self.target_node,
768 _compute_fn=compute_recoder)
769 self.assertTrue(compute_recoder.called)
770 self.assertEqual(self.lu.warning_log, [])
772 def testIgnoreViolation(self):
773 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
774 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
775 self.target_node, ignore=True,
776 _compute_fn=compute_recoder)
777 self.assertTrue(compute_recoder.called)
778 msg = ("Instance does not meet target node group's (bar) instance policy:"
779 " mem_size not in range")
780 self.assertEqual(self.lu.warning_log, [(msg, ())])
783 class TestApplyContainerMods(unittest.TestCase):
784 def testEmptyContainer(self):
787 cmdlib.ApplyContainerMods("test", container, chgdesc, [], None, None, None)
788 self.assertEqual(container, [])
789 self.assertEqual(chgdesc, [])
794 mods = cmdlib.PrepareContainerMods([
795 (constants.DDM_ADD, -1, "Hello"),
796 (constants.DDM_ADD, -1, "World"),
797 (constants.DDM_ADD, 0, "Start"),
798 (constants.DDM_ADD, -1, "End"),
800 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
802 self.assertEqual(container, ["Start", "Hello", "World", "End"])
803 self.assertEqual(chgdesc, [])
805 def testRemoveError(self):
806 for idx in [0, 1, 2, 100, -1, -4]:
807 mods = cmdlib.PrepareContainerMods([
808 (constants.DDM_REMOVE, idx, None),
810 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
811 "test", [], None, mods, None, None, None)
813 mods = cmdlib.PrepareContainerMods([
814 (constants.DDM_REMOVE, 0, object()),
816 self.assertRaises(AssertionError, cmdlib.ApplyContainerMods,
817 "test", [""], None, mods, None, None, None)
819 def testAddError(self):
820 for idx in range(-100, -1):
821 mods = cmdlib.PrepareContainerMods([
822 (constants.DDM_ADD, idx, None),
824 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
825 "test", [], None, mods, None, None, None)
827 def testRemove(self):
828 container = ["item 1", "item 2"]
829 mods = cmdlib.PrepareContainerMods([
830 (constants.DDM_ADD, -1, "aaa"),
831 (constants.DDM_REMOVE, -1, None),
832 (constants.DDM_ADD, -1, "bbb"),
835 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
837 self.assertEqual(container, ["item 1", "item 2", "bbb"])
838 self.assertEqual(chgdesc, [
839 ("test/2", "remove"),
847 def _CreateTestFn(idx, params, private):
848 private.data = ("add", idx, params)
849 return ((100 * idx, params), [
850 ("test/%s" % idx, hex(idx)),
854 def _ModifyTestFn(idx, item, params, private):
855 private.data = ("modify", idx, params)
857 ("test/%s" % idx, "modify %s" % params),
861 def _RemoveTestFn(idx, item, private):
862 private.data = ("remove", idx, item)
864 def testAddWithCreateFunction(self):
867 mods = cmdlib.PrepareContainerMods([
868 (constants.DDM_ADD, -1, "Hello"),
869 (constants.DDM_ADD, -1, "World"),
870 (constants.DDM_ADD, 0, "Start"),
871 (constants.DDM_ADD, -1, "End"),
872 (constants.DDM_REMOVE, 2, None),
873 (constants.DDM_MODIFY, -1, "foobar"),
874 (constants.DDM_REMOVE, 2, None),
875 (constants.DDM_ADD, 1, "More"),
876 ], self._PrivateData)
877 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
878 self._CreateTestFn, self._ModifyTestFn, self._RemoveTestFn)
879 self.assertEqual(container, [
884 self.assertEqual(chgdesc, [
889 ("test/2", "remove"),
890 ("test/2", "modify foobar"),
891 ("test/2", "remove"),
894 self.assertTrue(compat.all(op == private.data[0]
895 for (op, _, _, private) in mods))
896 self.assertEqual([private.data for (op, _, _, private) in mods], [
901 ("remove", 2, (100, "World")),
902 ("modify", 2, "foobar"),
903 ("remove", 2, (300, "End")),
908 class _FakeConfigForGenDiskTemplate:
910 self._unique_id = itertools.count()
911 self._drbd_minor = itertools.count(20)
912 self._port = itertools.count(constants.FIRST_DRBD_PORT)
913 self._secret = itertools.count()
918 def GenerateUniqueID(self, ec_id):
919 return "ec%s-uq%s" % (ec_id, self._unique_id.next())
921 def AllocateDRBDMinor(self, nodes, instance):
922 return [self._drbd_minor.next()
925 def AllocatePort(self):
926 return self._port.next()
928 def GenerateDRBDSecret(self, ec_id):
929 return "ec%s-secret%s" % (ec_id, self._secret.next())
932 class _FakeProcForGenDiskTemplate:
937 class TestGenerateDiskTemplate(unittest.TestCase):
939 nodegroup = objects.NodeGroup(name="ng")
940 nodegroup.UpgradeConfig()
942 cfg = _FakeConfigForGenDiskTemplate()
943 proc = _FakeProcForGenDiskTemplate()
945 self.lu = _FakeLU(cfg=cfg, proc=proc)
946 self.nodegroup = nodegroup
948 def testWrongDiskTemplate(self):
949 gdt = cmdlib._GenerateDiskTemplate
950 disk_template = "##unknown##"
952 assert disk_template not in constants.DISK_TEMPLATES
954 self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
955 "inst26831.example.com", "node30113.example.com", [], [],
956 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
957 self.nodegroup.diskparams)
959 def testDiskless(self):
960 gdt = cmdlib._GenerateDiskTemplate
962 result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
963 "node30113.example.com", [], [],
964 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
965 self.nodegroup.diskparams)
966 self.assertEqual(result, [])
968 def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
969 file_storage_dir=NotImplemented,
970 file_driver=NotImplemented,
971 req_file_storage=NotImplemented,
972 req_shr_file_storage=NotImplemented):
973 gdt = cmdlib._GenerateDiskTemplate
975 map(lambda params: utils.ForceDictType(params,
976 constants.IDISK_PARAMS_TYPES),
979 # Check if non-empty list of secondaries is rejected
980 self.assertRaises(errors.ProgrammerError, gdt, self.lu,
981 template, "inst25088.example.com",
982 "node185.example.com", ["node323.example.com"], [],
983 NotImplemented, NotImplemented, base_index,
984 self.lu.LogInfo, self.nodegroup.diskparams,
985 _req_file_storage=req_file_storage,
986 _req_shr_file_storage=req_shr_file_storage)
988 result = gdt(self.lu, template, "inst21662.example.com",
989 "node21741.example.com", [],
990 disk_info, file_storage_dir, file_driver, base_index,
991 self.lu.LogInfo, self.nodegroup.diskparams,
992 _req_file_storage=req_file_storage,
993 _req_shr_file_storage=req_shr_file_storage)
995 for (idx, disk) in enumerate(result):
996 self.assertTrue(isinstance(disk, objects.Disk))
997 self.assertEqual(disk.dev_type, exp_dev_type)
998 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
999 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1000 self.assertTrue(disk.children is None)
1002 self.assertEqual(map(operator.attrgetter("iv_name"), result),
1003 ["disk/%s" % i for i in range(base_index, base_index + len(disk_info))])
1007 def testPlain(self):
1009 constants.IDISK_SIZE: 1024,
1010 constants.IDISK_MODE: constants.DISK_RDWR,
1012 constants.IDISK_SIZE: 4096,
1013 constants.IDISK_VG: "othervg",
1014 constants.IDISK_MODE: constants.DISK_RDWR,
1017 result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1020 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1021 ("testvg", "ec0-uq0.disk3"),
1022 ("othervg", "ec0-uq1.disk4"),
1026 def _AllowFileStorage():
1030 def _ForbidFileStorage():
1031 raise errors.OpPrereqError("Disallowed in test")
1034 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1035 constants.DT_FILE, [], 0, NotImplemented,
1036 req_file_storage=self._ForbidFileStorage)
1037 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1038 constants.DT_SHARED_FILE, [], 0, NotImplemented,
1039 req_shr_file_storage=self._ForbidFileStorage)
1041 for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1043 constants.IDISK_SIZE: 80 * 1024,
1044 constants.IDISK_MODE: constants.DISK_RDONLY,
1046 constants.IDISK_SIZE: 4096,
1047 constants.IDISK_MODE: constants.DISK_RDWR,
1049 constants.IDISK_SIZE: 6 * 1024,
1050 constants.IDISK_MODE: constants.DISK_RDWR,
1053 result = self._TestTrivialDisk(disk_template, disk_info, 2,
1054 constants.LD_FILE, file_storage_dir="/tmp",
1055 file_driver=constants.FD_BLKTAP,
1056 req_file_storage=self._AllowFileStorage,
1057 req_shr_file_storage=self._AllowFileStorage)
1059 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1060 (constants.FD_BLKTAP, "/tmp/disk2"),
1061 (constants.FD_BLKTAP, "/tmp/disk3"),
1062 (constants.FD_BLKTAP, "/tmp/disk4"),
1065 def testBlock(self):
1067 constants.IDISK_SIZE: 8 * 1024,
1068 constants.IDISK_MODE: constants.DISK_RDWR,
1069 constants.IDISK_ADOPT: "/tmp/some/block/dev",
1072 result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1073 constants.LD_BLOCKDEV)
1075 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1076 (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1081 constants.IDISK_SIZE: 8 * 1024,
1082 constants.IDISK_MODE: constants.DISK_RDONLY,
1084 constants.IDISK_SIZE: 100 * 1024,
1085 constants.IDISK_MODE: constants.DISK_RDWR,
1088 result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1091 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1092 ("rbd", "ec0-uq0.rbd.disk0"),
1093 ("rbd", "ec0-uq1.rbd.disk1"),
1096 def testDrbd8(self):
1097 gdt = cmdlib._GenerateDiskTemplate
1098 drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1099 drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1102 constants.IDISK_SIZE: 1024,
1103 constants.IDISK_MODE: constants.DISK_RDWR,
1105 constants.IDISK_SIZE: 100 * 1024,
1106 constants.IDISK_MODE: constants.DISK_RDONLY,
1107 constants.IDISK_METAVG: "metavg",
1109 constants.IDISK_SIZE: 4096,
1110 constants.IDISK_MODE: constants.DISK_RDWR,
1111 constants.IDISK_VG: "vgxyz",
1115 exp_logical_ids = [[
1116 (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1117 (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1119 (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1120 ("metavg", "ec0-uq1.disk1_meta"),
1122 ("vgxyz", "ec0-uq2.disk2_data"),
1123 (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1126 assert len(exp_logical_ids) == len(disk_info)
1128 map(lambda params: utils.ForceDictType(params,
1129 constants.IDISK_PARAMS_TYPES),
1132 # Check if empty list of secondaries is rejected
1133 self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1134 "inst827.example.com", "node1334.example.com", [],
1135 disk_info, NotImplemented, NotImplemented, 0,
1136 self.lu.LogInfo, self.nodegroup.diskparams)
1138 result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1139 "node1334.example.com", ["node12272.example.com"],
1140 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1141 self.nodegroup.diskparams)
1143 for (idx, disk) in enumerate(result):
1144 self.assertTrue(isinstance(disk, objects.Disk))
1145 self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1146 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1147 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1149 for child in disk.children:
1150 self.assertTrue(isinstance(disk, objects.Disk))
1151 self.assertEqual(child.dev_type, constants.LD_LV)
1152 self.assertTrue(child.children is None)
1154 self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1155 exp_logical_ids[idx])
1157 self.assertEqual(len(disk.children), 2)
1158 self.assertEqual(disk.children[0].size, disk.size)
1159 self.assertEqual(disk.children[1].size, cmdlib.DRBD_META_SIZE)
1161 self.assertEqual(map(operator.attrgetter("iv_name"), result),
1162 ["disk/0", "disk/1", "disk/2"])
1164 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1165 ("node1334.example.com", "node12272.example.com",
1166 constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1167 ("node1334.example.com", "node12272.example.com",
1168 constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1169 ("node1334.example.com", "node12272.example.com",
1170 constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1174 if __name__ == "__main__":
1175 testutils.GanetiTestProgram()