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 mods = cmdlib.PrepareContainerMods([
806 (constants.DDM_ADD, 0, "zero"),
807 (constants.DDM_ADD, 3, "Added"),
808 (constants.DDM_ADD, 5, "four"),
809 (constants.DDM_ADD, 7, "xyz"),
811 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
813 self.assertEqual(container,
814 ["zero", "Start", "Hello", "Added", "World", "four",
816 self.assertEqual(chgdesc, [])
818 for idx in [-2, len(container) + 1]:
819 mods = cmdlib.PrepareContainerMods([
820 (constants.DDM_ADD, idx, "error"),
822 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
823 "test", container, None, mods, None, None, None)
825 def testRemoveError(self):
826 for idx in [0, 1, 2, 100, -1, -4]:
827 mods = cmdlib.PrepareContainerMods([
828 (constants.DDM_REMOVE, idx, None),
830 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
831 "test", [], None, mods, None, None, None)
833 mods = cmdlib.PrepareContainerMods([
834 (constants.DDM_REMOVE, 0, object()),
836 self.assertRaises(AssertionError, cmdlib.ApplyContainerMods,
837 "test", [""], None, mods, None, None, None)
839 def testAddError(self):
840 for idx in range(-100, -1) + [100]:
841 mods = cmdlib.PrepareContainerMods([
842 (constants.DDM_ADD, idx, None),
844 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
845 "test", [], None, mods, None, None, None)
847 def testRemove(self):
848 container = ["item 1", "item 2"]
849 mods = cmdlib.PrepareContainerMods([
850 (constants.DDM_ADD, -1, "aaa"),
851 (constants.DDM_REMOVE, -1, None),
852 (constants.DDM_ADD, -1, "bbb"),
855 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
857 self.assertEqual(container, ["item 1", "item 2", "bbb"])
858 self.assertEqual(chgdesc, [
859 ("test/2", "remove"),
862 def testModify(self):
863 container = ["item 1", "item 2"]
864 mods = cmdlib.PrepareContainerMods([
865 (constants.DDM_MODIFY, -1, "a"),
866 (constants.DDM_MODIFY, 0, "b"),
867 (constants.DDM_MODIFY, 1, "c"),
870 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
872 self.assertEqual(container, ["item 1", "item 2"])
873 self.assertEqual(chgdesc, [])
875 for idx in [-2, len(container) + 1]:
876 mods = cmdlib.PrepareContainerMods([
877 (constants.DDM_MODIFY, idx, "error"),
879 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
880 "test", container, None, mods, None, None, None)
887 def _CreateTestFn(idx, params, private):
888 private.data = ("add", idx, params)
889 return ((100 * idx, params), [
890 ("test/%s" % idx, hex(idx)),
894 def _ModifyTestFn(idx, item, params, private):
895 private.data = ("modify", idx, params)
897 ("test/%s" % idx, "modify %s" % params),
901 def _RemoveTestFn(idx, item, private):
902 private.data = ("remove", idx, item)
904 def testAddWithCreateFunction(self):
907 mods = cmdlib.PrepareContainerMods([
908 (constants.DDM_ADD, -1, "Hello"),
909 (constants.DDM_ADD, -1, "World"),
910 (constants.DDM_ADD, 0, "Start"),
911 (constants.DDM_ADD, -1, "End"),
912 (constants.DDM_REMOVE, 2, None),
913 (constants.DDM_MODIFY, -1, "foobar"),
914 (constants.DDM_REMOVE, 2, None),
915 (constants.DDM_ADD, 1, "More"),
916 ], self._PrivateData)
917 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
918 self._CreateTestFn, self._ModifyTestFn, self._RemoveTestFn)
919 self.assertEqual(container, [
924 self.assertEqual(chgdesc, [
929 ("test/2", "remove"),
930 ("test/2", "modify foobar"),
931 ("test/2", "remove"),
934 self.assertTrue(compat.all(op == private.data[0]
935 for (op, _, _, private) in mods))
936 self.assertEqual([private.data for (op, _, _, private) in mods], [
941 ("remove", 2, (100, "World")),
942 ("modify", 2, "foobar"),
943 ("remove", 2, (300, "End")),
948 class _FakeConfigForGenDiskTemplate:
950 self._unique_id = itertools.count()
951 self._drbd_minor = itertools.count(20)
952 self._port = itertools.count(constants.FIRST_DRBD_PORT)
953 self._secret = itertools.count()
958 def GenerateUniqueID(self, ec_id):
959 return "ec%s-uq%s" % (ec_id, self._unique_id.next())
961 def AllocateDRBDMinor(self, nodes, instance):
962 return [self._drbd_minor.next()
965 def AllocatePort(self):
966 return self._port.next()
968 def GenerateDRBDSecret(self, ec_id):
969 return "ec%s-secret%s" % (ec_id, self._secret.next())
972 class _FakeProcForGenDiskTemplate:
977 class TestGenerateDiskTemplate(unittest.TestCase):
979 nodegroup = objects.NodeGroup(name="ng")
980 nodegroup.UpgradeConfig()
982 cfg = _FakeConfigForGenDiskTemplate()
983 proc = _FakeProcForGenDiskTemplate()
985 self.lu = _FakeLU(cfg=cfg, proc=proc)
986 self.nodegroup = nodegroup
988 def testWrongDiskTemplate(self):
989 gdt = cmdlib._GenerateDiskTemplate
990 disk_template = "##unknown##"
992 assert disk_template not in constants.DISK_TEMPLATES
994 self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
995 "inst26831.example.com", "node30113.example.com", [], [],
996 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
997 self.nodegroup.diskparams)
999 def testDiskless(self):
1000 gdt = cmdlib._GenerateDiskTemplate
1002 result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1003 "node30113.example.com", [], [],
1004 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1005 self.nodegroup.diskparams)
1006 self.assertEqual(result, [])
1008 def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1009 file_storage_dir=NotImplemented,
1010 file_driver=NotImplemented,
1011 req_file_storage=NotImplemented,
1012 req_shr_file_storage=NotImplemented):
1013 gdt = cmdlib._GenerateDiskTemplate
1015 map(lambda params: utils.ForceDictType(params,
1016 constants.IDISK_PARAMS_TYPES),
1019 # Check if non-empty list of secondaries is rejected
1020 self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1021 template, "inst25088.example.com",
1022 "node185.example.com", ["node323.example.com"], [],
1023 NotImplemented, NotImplemented, base_index,
1024 self.lu.LogInfo, self.nodegroup.diskparams,
1025 _req_file_storage=req_file_storage,
1026 _req_shr_file_storage=req_shr_file_storage)
1028 result = gdt(self.lu, template, "inst21662.example.com",
1029 "node21741.example.com", [],
1030 disk_info, file_storage_dir, file_driver, base_index,
1031 self.lu.LogInfo, self.nodegroup.diskparams,
1032 _req_file_storage=req_file_storage,
1033 _req_shr_file_storage=req_shr_file_storage)
1035 for (idx, disk) in enumerate(result):
1036 self.assertTrue(isinstance(disk, objects.Disk))
1037 self.assertEqual(disk.dev_type, exp_dev_type)
1038 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1039 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1040 self.assertTrue(disk.children is None)
1042 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1043 cmdlib._UpdateIvNames(base_index, result)
1044 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1048 def _CheckIvNames(self, disks, base_index, end_index):
1049 self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1050 ["disk/%s" % i for i in range(base_index, end_index)])
1052 def testPlain(self):
1054 constants.IDISK_SIZE: 1024,
1055 constants.IDISK_MODE: constants.DISK_RDWR,
1057 constants.IDISK_SIZE: 4096,
1058 constants.IDISK_VG: "othervg",
1059 constants.IDISK_MODE: constants.DISK_RDWR,
1062 result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1065 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1066 ("testvg", "ec0-uq0.disk3"),
1067 ("othervg", "ec0-uq1.disk4"),
1071 def _AllowFileStorage():
1075 def _ForbidFileStorage():
1076 raise errors.OpPrereqError("Disallowed in test")
1079 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1080 constants.DT_FILE, [], 0, NotImplemented,
1081 req_file_storage=self._ForbidFileStorage)
1082 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1083 constants.DT_SHARED_FILE, [], 0, NotImplemented,
1084 req_shr_file_storage=self._ForbidFileStorage)
1086 for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1088 constants.IDISK_SIZE: 80 * 1024,
1089 constants.IDISK_MODE: constants.DISK_RDONLY,
1091 constants.IDISK_SIZE: 4096,
1092 constants.IDISK_MODE: constants.DISK_RDWR,
1094 constants.IDISK_SIZE: 6 * 1024,
1095 constants.IDISK_MODE: constants.DISK_RDWR,
1098 result = self._TestTrivialDisk(disk_template, disk_info, 2,
1099 constants.LD_FILE, file_storage_dir="/tmp",
1100 file_driver=constants.FD_BLKTAP,
1101 req_file_storage=self._AllowFileStorage,
1102 req_shr_file_storage=self._AllowFileStorage)
1104 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1105 (constants.FD_BLKTAP, "/tmp/disk2"),
1106 (constants.FD_BLKTAP, "/tmp/disk3"),
1107 (constants.FD_BLKTAP, "/tmp/disk4"),
1110 def testBlock(self):
1112 constants.IDISK_SIZE: 8 * 1024,
1113 constants.IDISK_MODE: constants.DISK_RDWR,
1114 constants.IDISK_ADOPT: "/tmp/some/block/dev",
1117 result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1118 constants.LD_BLOCKDEV)
1120 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1121 (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1126 constants.IDISK_SIZE: 8 * 1024,
1127 constants.IDISK_MODE: constants.DISK_RDONLY,
1129 constants.IDISK_SIZE: 100 * 1024,
1130 constants.IDISK_MODE: constants.DISK_RDWR,
1133 result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1136 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1137 ("rbd", "ec0-uq0.rbd.disk0"),
1138 ("rbd", "ec0-uq1.rbd.disk1"),
1141 def testDrbd8(self):
1142 gdt = cmdlib._GenerateDiskTemplate
1143 drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1144 drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1147 constants.IDISK_SIZE: 1024,
1148 constants.IDISK_MODE: constants.DISK_RDWR,
1150 constants.IDISK_SIZE: 100 * 1024,
1151 constants.IDISK_MODE: constants.DISK_RDONLY,
1152 constants.IDISK_METAVG: "metavg",
1154 constants.IDISK_SIZE: 4096,
1155 constants.IDISK_MODE: constants.DISK_RDWR,
1156 constants.IDISK_VG: "vgxyz",
1160 exp_logical_ids = [[
1161 (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1162 (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1164 (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1165 ("metavg", "ec0-uq1.disk1_meta"),
1167 ("vgxyz", "ec0-uq2.disk2_data"),
1168 (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1171 assert len(exp_logical_ids) == len(disk_info)
1173 map(lambda params: utils.ForceDictType(params,
1174 constants.IDISK_PARAMS_TYPES),
1177 # Check if empty list of secondaries is rejected
1178 self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1179 "inst827.example.com", "node1334.example.com", [],
1180 disk_info, NotImplemented, NotImplemented, 0,
1181 self.lu.LogInfo, self.nodegroup.diskparams)
1183 result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1184 "node1334.example.com", ["node12272.example.com"],
1185 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1186 self.nodegroup.diskparams)
1188 for (idx, disk) in enumerate(result):
1189 self.assertTrue(isinstance(disk, objects.Disk))
1190 self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1191 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1192 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1194 for child in disk.children:
1195 self.assertTrue(isinstance(disk, objects.Disk))
1196 self.assertEqual(child.dev_type, constants.LD_LV)
1197 self.assertTrue(child.children is None)
1199 self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1200 exp_logical_ids[idx])
1202 self.assertEqual(len(disk.children), 2)
1203 self.assertEqual(disk.children[0].size, disk.size)
1204 self.assertEqual(disk.children[1].size, cmdlib.DRBD_META_SIZE)
1206 self._CheckIvNames(result, 0, len(disk_info))
1207 cmdlib._UpdateIvNames(0, result)
1208 self._CheckIvNames(result, 0, len(disk_info))
1210 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1211 ("node1334.example.com", "node12272.example.com",
1212 constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1213 ("node1334.example.com", "node12272.example.com",
1214 constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1215 ("node1334.example.com", "node12272.example.com",
1216 constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1220 if __name__ == "__main__":
1221 testutils.GanetiTestProgram()