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], 1, _compute_fn=compute_fn)
649 self.assertEqual(ret, [])
651 def testInvalidArguments(self):
652 self.assertRaises(AssertionError, cmdlib._ComputeIPolicySpecViolation,
653 NotImplemented, 1024, 1, 1, 1, [], 1)
655 def testInvalidSpec(self):
656 spec = _SpecWrapper([None, False, "foo", None, "bar", None])
657 compute_fn = spec.ComputeMinMaxSpec
658 ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
659 [1024], 1, _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,
667 self.mem_size = mem_size
668 self.cpu_count = cpu_count
669 self.disk_count = disk_count
670 self.nic_count = nic_count
671 self.disk_sizes = disk_sizes
672 self.spindle_use = spindle_use
674 def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
676 assert self.mem_size == mem_size
677 assert self.cpu_count == cpu_count
678 assert self.disk_count == disk_count
679 assert self.nic_count == nic_count
680 assert self.disk_sizes == disk_sizes
681 assert self.spindle_use == spindle_use
686 class TestComputeIPolicyInstanceViolation(unittest.TestCase):
689 constants.BE_MAXMEM: 2048,
690 constants.BE_VCPUS: 2,
691 constants.BE_SPINDLE_USE: 4,
693 disks = [objects.Disk(size=512)]
694 instance = objects.Instance(beparams=beparams, disks=disks, nics=[])
695 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 4)
696 ret = cmdlib._ComputeIPolicyInstanceViolation(NotImplemented, instance,
698 self.assertEqual(ret, [])
701 class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
704 constants.ISPEC_MEM_SIZE: 2048,
705 constants.ISPEC_CPU_COUNT: 2,
706 constants.ISPEC_DISK_COUNT: 1,
707 constants.ISPEC_DISK_SIZE: [512],
708 constants.ISPEC_NIC_COUNT: 0,
709 constants.ISPEC_SPINDLE_USE: 1,
711 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 1)
712 ret = cmdlib._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
714 self.assertEqual(ret, [])
718 def __init__(self, return_value=None):
720 self.return_value = return_value
722 def __call__(self, *args):
724 return self.return_value
727 class TestComputeIPolicyNodeViolation(unittest.TestCase):
729 self.recorder = _CallRecorder(return_value=[])
731 def testSameGroup(self):
732 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
734 _compute_fn=self.recorder)
735 self.assertFalse(self.recorder.called)
736 self.assertEqual(ret, [])
738 def testDifferentGroup(self):
739 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
741 _compute_fn=self.recorder)
742 self.assertTrue(self.recorder.called)
743 self.assertEqual(ret, [])
746 class _FakeConfigForTargetNodeIPolicy:
747 def __init__(self, node_info=NotImplemented):
748 self._node_info = node_info
750 def GetNodeInfo(self, _):
751 return self._node_info
754 class TestCheckTargetNodeIPolicy(unittest.TestCase):
756 self.instance = objects.Instance(primary_node="blubb")
757 self.target_node = objects.Node(group="bar")
758 node_info = objects.Node(group="foo")
759 fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
760 self.lu = _FakeLU(cfg=fake_cfg)
762 def testNoViolation(self):
763 compute_recoder = _CallRecorder(return_value=[])
764 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
766 _compute_fn=compute_recoder)
767 self.assertTrue(compute_recoder.called)
768 self.assertEqual(self.lu.warning_log, [])
770 def testNoIgnore(self):
771 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
772 self.assertRaises(errors.OpPrereqError, cmdlib._CheckTargetNodeIPolicy,
773 self.lu, NotImplemented, self.instance, self.target_node,
774 _compute_fn=compute_recoder)
775 self.assertTrue(compute_recoder.called)
776 self.assertEqual(self.lu.warning_log, [])
778 def testIgnoreViolation(self):
779 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
780 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
781 self.target_node, ignore=True,
782 _compute_fn=compute_recoder)
783 self.assertTrue(compute_recoder.called)
784 msg = ("Instance does not meet target node group's (bar) instance policy:"
785 " mem_size not in range")
786 self.assertEqual(self.lu.warning_log, [(msg, ())])
789 class TestApplyContainerMods(unittest.TestCase):
790 def testEmptyContainer(self):
793 cmdlib.ApplyContainerMods("test", container, chgdesc, [], None, None, None)
794 self.assertEqual(container, [])
795 self.assertEqual(chgdesc, [])
800 mods = cmdlib.PrepareContainerMods([
801 (constants.DDM_ADD, -1, "Hello"),
802 (constants.DDM_ADD, -1, "World"),
803 (constants.DDM_ADD, 0, "Start"),
804 (constants.DDM_ADD, -1, "End"),
806 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
808 self.assertEqual(container, ["Start", "Hello", "World", "End"])
809 self.assertEqual(chgdesc, [])
811 mods = cmdlib.PrepareContainerMods([
812 (constants.DDM_ADD, 0, "zero"),
813 (constants.DDM_ADD, 3, "Added"),
814 (constants.DDM_ADD, 5, "four"),
815 (constants.DDM_ADD, 7, "xyz"),
817 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
819 self.assertEqual(container,
820 ["zero", "Start", "Hello", "Added", "World", "four",
822 self.assertEqual(chgdesc, [])
824 for idx in [-2, len(container) + 1]:
825 mods = cmdlib.PrepareContainerMods([
826 (constants.DDM_ADD, idx, "error"),
828 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
829 "test", container, None, mods, None, None, None)
831 def testRemoveError(self):
832 for idx in [0, 1, 2, 100, -1, -4]:
833 mods = cmdlib.PrepareContainerMods([
834 (constants.DDM_REMOVE, idx, None),
836 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
837 "test", [], None, mods, None, None, None)
839 mods = cmdlib.PrepareContainerMods([
840 (constants.DDM_REMOVE, 0, object()),
842 self.assertRaises(AssertionError, cmdlib.ApplyContainerMods,
843 "test", [""], None, mods, None, None, None)
845 def testAddError(self):
846 for idx in range(-100, -1) + [100]:
847 mods = cmdlib.PrepareContainerMods([
848 (constants.DDM_ADD, idx, None),
850 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
851 "test", [], None, mods, None, None, None)
853 def testRemove(self):
854 container = ["item 1", "item 2"]
855 mods = cmdlib.PrepareContainerMods([
856 (constants.DDM_ADD, -1, "aaa"),
857 (constants.DDM_REMOVE, -1, None),
858 (constants.DDM_ADD, -1, "bbb"),
861 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
863 self.assertEqual(container, ["item 1", "item 2", "bbb"])
864 self.assertEqual(chgdesc, [
865 ("test/2", "remove"),
868 def testModify(self):
869 container = ["item 1", "item 2"]
870 mods = cmdlib.PrepareContainerMods([
871 (constants.DDM_MODIFY, -1, "a"),
872 (constants.DDM_MODIFY, 0, "b"),
873 (constants.DDM_MODIFY, 1, "c"),
876 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
878 self.assertEqual(container, ["item 1", "item 2"])
879 self.assertEqual(chgdesc, [])
881 for idx in [-2, len(container) + 1]:
882 mods = cmdlib.PrepareContainerMods([
883 (constants.DDM_MODIFY, idx, "error"),
885 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
886 "test", container, None, mods, None, None, None)
893 def _CreateTestFn(idx, params, private):
894 private.data = ("add", idx, params)
895 return ((100 * idx, params), [
896 ("test/%s" % idx, hex(idx)),
900 def _ModifyTestFn(idx, item, params, private):
901 private.data = ("modify", idx, params)
903 ("test/%s" % idx, "modify %s" % params),
907 def _RemoveTestFn(idx, item, private):
908 private.data = ("remove", idx, item)
910 def testAddWithCreateFunction(self):
913 mods = cmdlib.PrepareContainerMods([
914 (constants.DDM_ADD, -1, "Hello"),
915 (constants.DDM_ADD, -1, "World"),
916 (constants.DDM_ADD, 0, "Start"),
917 (constants.DDM_ADD, -1, "End"),
918 (constants.DDM_REMOVE, 2, None),
919 (constants.DDM_MODIFY, -1, "foobar"),
920 (constants.DDM_REMOVE, 2, None),
921 (constants.DDM_ADD, 1, "More"),
922 ], self._PrivateData)
923 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
924 self._CreateTestFn, self._ModifyTestFn, self._RemoveTestFn)
925 self.assertEqual(container, [
930 self.assertEqual(chgdesc, [
935 ("test/2", "remove"),
936 ("test/2", "modify foobar"),
937 ("test/2", "remove"),
940 self.assertTrue(compat.all(op == private.data[0]
941 for (op, _, _, private) in mods))
942 self.assertEqual([private.data for (op, _, _, private) in mods], [
947 ("remove", 2, (100, "World")),
948 ("modify", 2, "foobar"),
949 ("remove", 2, (300, "End")),
954 class _FakeConfigForGenDiskTemplate:
956 self._unique_id = itertools.count()
957 self._drbd_minor = itertools.count(20)
958 self._port = itertools.count(constants.FIRST_DRBD_PORT)
959 self._secret = itertools.count()
964 def GenerateUniqueID(self, ec_id):
965 return "ec%s-uq%s" % (ec_id, self._unique_id.next())
967 def AllocateDRBDMinor(self, nodes, instance):
968 return [self._drbd_minor.next()
971 def AllocatePort(self):
972 return self._port.next()
974 def GenerateDRBDSecret(self, ec_id):
975 return "ec%s-secret%s" % (ec_id, self._secret.next())
978 class _FakeProcForGenDiskTemplate:
983 class TestGenerateDiskTemplate(unittest.TestCase):
985 nodegroup = objects.NodeGroup(name="ng")
986 nodegroup.UpgradeConfig()
988 cfg = _FakeConfigForGenDiskTemplate()
989 proc = _FakeProcForGenDiskTemplate()
991 self.lu = _FakeLU(cfg=cfg, proc=proc)
992 self.nodegroup = nodegroup
994 def testWrongDiskTemplate(self):
995 gdt = cmdlib._GenerateDiskTemplate
996 disk_template = "##unknown##"
998 assert disk_template not in constants.DISK_TEMPLATES
1000 self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
1001 "inst26831.example.com", "node30113.example.com", [], [],
1002 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1003 self.nodegroup.diskparams)
1005 def testDiskless(self):
1006 gdt = cmdlib._GenerateDiskTemplate
1008 result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1009 "node30113.example.com", [], [],
1010 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1011 self.nodegroup.diskparams)
1012 self.assertEqual(result, [])
1014 def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1015 file_storage_dir=NotImplemented,
1016 file_driver=NotImplemented,
1017 req_file_storage=NotImplemented,
1018 req_shr_file_storage=NotImplemented):
1019 gdt = cmdlib._GenerateDiskTemplate
1021 map(lambda params: utils.ForceDictType(params,
1022 constants.IDISK_PARAMS_TYPES),
1025 # Check if non-empty list of secondaries is rejected
1026 self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1027 template, "inst25088.example.com",
1028 "node185.example.com", ["node323.example.com"], [],
1029 NotImplemented, NotImplemented, base_index,
1030 self.lu.LogInfo, self.nodegroup.diskparams,
1031 _req_file_storage=req_file_storage,
1032 _req_shr_file_storage=req_shr_file_storage)
1034 result = gdt(self.lu, template, "inst21662.example.com",
1035 "node21741.example.com", [],
1036 disk_info, file_storage_dir, file_driver, base_index,
1037 self.lu.LogInfo, self.nodegroup.diskparams,
1038 _req_file_storage=req_file_storage,
1039 _req_shr_file_storage=req_shr_file_storage)
1041 for (idx, disk) in enumerate(result):
1042 self.assertTrue(isinstance(disk, objects.Disk))
1043 self.assertEqual(disk.dev_type, exp_dev_type)
1044 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1045 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1046 self.assertTrue(disk.children is None)
1048 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1049 cmdlib._UpdateIvNames(base_index, result)
1050 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1054 def _CheckIvNames(self, disks, base_index, end_index):
1055 self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1056 ["disk/%s" % i for i in range(base_index, end_index)])
1058 def testPlain(self):
1060 constants.IDISK_SIZE: 1024,
1061 constants.IDISK_MODE: constants.DISK_RDWR,
1063 constants.IDISK_SIZE: 4096,
1064 constants.IDISK_VG: "othervg",
1065 constants.IDISK_MODE: constants.DISK_RDWR,
1068 result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1071 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1072 ("testvg", "ec0-uq0.disk3"),
1073 ("othervg", "ec0-uq1.disk4"),
1077 def _AllowFileStorage():
1081 def _ForbidFileStorage():
1082 raise errors.OpPrereqError("Disallowed in test")
1085 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1086 constants.DT_FILE, [], 0, NotImplemented,
1087 req_file_storage=self._ForbidFileStorage)
1088 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1089 constants.DT_SHARED_FILE, [], 0, NotImplemented,
1090 req_shr_file_storage=self._ForbidFileStorage)
1092 for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1094 constants.IDISK_SIZE: 80 * 1024,
1095 constants.IDISK_MODE: constants.DISK_RDONLY,
1097 constants.IDISK_SIZE: 4096,
1098 constants.IDISK_MODE: constants.DISK_RDWR,
1100 constants.IDISK_SIZE: 6 * 1024,
1101 constants.IDISK_MODE: constants.DISK_RDWR,
1104 result = self._TestTrivialDisk(disk_template, disk_info, 2,
1105 constants.LD_FILE, file_storage_dir="/tmp",
1106 file_driver=constants.FD_BLKTAP,
1107 req_file_storage=self._AllowFileStorage,
1108 req_shr_file_storage=self._AllowFileStorage)
1110 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1111 (constants.FD_BLKTAP, "/tmp/disk2"),
1112 (constants.FD_BLKTAP, "/tmp/disk3"),
1113 (constants.FD_BLKTAP, "/tmp/disk4"),
1116 def testBlock(self):
1118 constants.IDISK_SIZE: 8 * 1024,
1119 constants.IDISK_MODE: constants.DISK_RDWR,
1120 constants.IDISK_ADOPT: "/tmp/some/block/dev",
1123 result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1124 constants.LD_BLOCKDEV)
1126 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1127 (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1132 constants.IDISK_SIZE: 8 * 1024,
1133 constants.IDISK_MODE: constants.DISK_RDONLY,
1135 constants.IDISK_SIZE: 100 * 1024,
1136 constants.IDISK_MODE: constants.DISK_RDWR,
1139 result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1142 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1143 ("rbd", "ec0-uq0.rbd.disk0"),
1144 ("rbd", "ec0-uq1.rbd.disk1"),
1147 def testDrbd8(self):
1148 gdt = cmdlib._GenerateDiskTemplate
1149 drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1150 drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1153 constants.IDISK_SIZE: 1024,
1154 constants.IDISK_MODE: constants.DISK_RDWR,
1156 constants.IDISK_SIZE: 100 * 1024,
1157 constants.IDISK_MODE: constants.DISK_RDONLY,
1158 constants.IDISK_METAVG: "metavg",
1160 constants.IDISK_SIZE: 4096,
1161 constants.IDISK_MODE: constants.DISK_RDWR,
1162 constants.IDISK_VG: "vgxyz",
1166 exp_logical_ids = [[
1167 (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1168 (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1170 (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1171 ("metavg", "ec0-uq1.disk1_meta"),
1173 ("vgxyz", "ec0-uq2.disk2_data"),
1174 (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1177 assert len(exp_logical_ids) == len(disk_info)
1179 map(lambda params: utils.ForceDictType(params,
1180 constants.IDISK_PARAMS_TYPES),
1183 # Check if empty list of secondaries is rejected
1184 self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1185 "inst827.example.com", "node1334.example.com", [],
1186 disk_info, NotImplemented, NotImplemented, 0,
1187 self.lu.LogInfo, self.nodegroup.diskparams)
1189 result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1190 "node1334.example.com", ["node12272.example.com"],
1191 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1192 self.nodegroup.diskparams)
1194 for (idx, disk) in enumerate(result):
1195 self.assertTrue(isinstance(disk, objects.Disk))
1196 self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1197 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1198 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1200 for child in disk.children:
1201 self.assertTrue(isinstance(disk, objects.Disk))
1202 self.assertEqual(child.dev_type, constants.LD_LV)
1203 self.assertTrue(child.children is None)
1205 self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1206 exp_logical_ids[idx])
1208 self.assertEqual(len(disk.children), 2)
1209 self.assertEqual(disk.children[0].size, disk.size)
1210 self.assertEqual(disk.children[1].size, cmdlib.DRBD_META_SIZE)
1212 self._CheckIvNames(result, 0, len(disk_info))
1213 cmdlib._UpdateIvNames(0, result)
1214 self._CheckIvNames(result, 0, len(disk_info))
1216 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1217 ("node1334.example.com", "node12272.example.com",
1218 constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1219 ("node1334.example.com", "node12272.example.com",
1220 constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1221 ("node1334.example.com", "node12272.example.com",
1222 constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1226 if __name__ == "__main__":
1227 testutils.GanetiTestProgram()