4 # Copyright (C) 2008, 2011, 2012 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Script for unittesting the cmdlib module"""
34 from ganeti import constants
35 from ganeti import mcpu
36 from ganeti import cmdlib
37 from ganeti import opcodes
38 from ganeti import errors
39 from ganeti import utils
40 from ganeti import luxi
42 from ganeti import objects
43 from ganeti import compat
44 from ganeti import rpc
45 from ganeti import pathutils
46 from ganeti.masterd import iallocator
47 from ganeti.hypervisor import hv_xen
53 class TestCertVerification(testutils.GanetiTestCase):
55 testutils.GanetiTestCase.setUp(self)
57 self.tmpdir = tempfile.mkdtemp()
60 shutil.rmtree(self.tmpdir)
62 def testVerifyCertificate(self):
63 cmdlib._VerifyCertificate(self._TestDataFilename("cert1.pem"))
65 nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
67 (errcode, msg) = cmdlib._VerifyCertificate(nonexist_filename)
68 self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
70 # Try to load non-certificate file
71 invalid_cert = self._TestDataFilename("bdev-net.txt")
72 (errcode, msg) = cmdlib._VerifyCertificate(invalid_cert)
73 self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
76 class TestOpcodeParams(testutils.GanetiTestCase):
77 def testParamsStructures(self):
78 for op in sorted(mcpu.Processor.DISPATCH_TABLE):
79 lu = mcpu.Processor.DISPATCH_TABLE[op]
81 self.failIf(hasattr(lu, "_OP_REQP"),
82 msg=("LU '%s' has old-style _OP_REQP" % lu_name))
83 self.failIf(hasattr(lu, "_OP_DEFS"),
84 msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
85 self.failIf(hasattr(lu, "_OP_PARAMS"),
86 msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
89 class TestIAllocatorChecks(testutils.GanetiTestCase):
90 def testFunction(self):
92 def __init__(self, opcode):
93 self.cfg = mocks.FakeConfig()
96 class OpTest(opcodes.OpCode):
98 ("iallocator", None, ht.NoType, None),
99 ("node", None, ht.NoType, None),
102 default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
103 other_iallocator = default_iallocator + "_not"
108 c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
110 # Neither node nor iallocator given
114 self.assertEqual(lu.op.iallocator, default_iallocator)
115 self.assertEqual(lu.op.node, None)
117 # Both, iallocator and node given
118 op.iallocator = "test"
120 self.assertRaises(errors.OpPrereqError, c_i)
122 # Only iallocator given
123 op.iallocator = other_iallocator
126 self.assertEqual(lu.op.iallocator, other_iallocator)
127 self.assertEqual(lu.op.node, None)
133 self.assertEqual(lu.op.iallocator, None)
134 self.assertEqual(lu.op.node, "node")
136 # No node, iallocator or default iallocator
139 lu.cfg.GetDefaultIAllocator = lambda: None
140 self.assertRaises(errors.OpPrereqError, c_i)
143 class TestLUTestJqueue(unittest.TestCase):
145 self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
146 (luxi.WFJC_TIMEOUT * 0.75),
147 msg=("Client timeout too high, might not notice bugs"
148 " in WaitForJobChange"))
151 class TestLUQuery(unittest.TestCase):
153 self.assertEqual(sorted(cmdlib._QUERY_IMPL.keys()),
154 sorted(constants.QR_VIA_OP))
156 assert constants.QR_NODE in constants.QR_VIA_OP
157 assert constants.QR_INSTANCE in constants.QR_VIA_OP
159 for i in constants.QR_VIA_OP:
160 self.assert_(cmdlib._GetQueryImplementation(i))
162 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation, "")
163 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation,
167 class TestLUGroupAssignNodes(unittest.TestCase):
169 def testCheckAssignmentForSplitInstances(self):
170 node_data = dict((name, objects.Node(name=name, group=group))
171 for (name, group) in [("n1a", "g1"), ("n1b", "g1"),
172 ("n2a", "g2"), ("n2b", "g2"),
173 ("n3a", "g3"), ("n3b", "g3"),
177 def Instance(name, pnode, snode):
180 disk_template = constants.DT_DISKLESS
182 disks = [objects.Disk(dev_type=constants.LD_DRBD8,
183 logical_id=[pnode, snode, 1, 17, 17])]
184 disk_template = constants.DT_DRBD8
186 return objects.Instance(name=name, primary_node=pnode, disks=disks,
187 disk_template=disk_template)
189 instance_data = dict((name, Instance(name, pnode, snode))
190 for name, pnode, snode in [("inst1a", "n1a", "n1b"),
191 ("inst1b", "n1b", "n1a"),
192 ("inst2a", "n2a", "n2b"),
193 ("inst3a", "n3a", None),
194 ("inst3b", "n3b", "n1b"),
195 ("inst3c", "n3b", "n2b"),
198 # Test first with the existing state.
200 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
204 self.assertEqual([], new)
205 self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
207 # And now some changes.
209 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
214 self.assertEqual(set(["inst1a", "inst1b"]), set(new))
215 self.assertEqual(set(["inst3c"]), set(prev))
218 class TestClusterVerifySsh(unittest.TestCase):
219 def testMultipleGroups(self):
220 fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
222 objects.Node(name="node20", group="my", offline=False),
223 objects.Node(name="node21", group="my", offline=False),
224 objects.Node(name="node22", group="my", offline=False),
225 objects.Node(name="node23", group="my", offline=False),
226 objects.Node(name="node24", group="my", offline=False),
227 objects.Node(name="node25", group="my", offline=False),
228 objects.Node(name="node26", group="my", offline=True),
231 objects.Node(name="node1", group="g1", offline=True),
232 objects.Node(name="node2", group="g1", offline=False),
233 objects.Node(name="node3", group="g1", offline=False),
234 objects.Node(name="node4", group="g1", offline=True),
235 objects.Node(name="node5", group="g1", offline=False),
236 objects.Node(name="node10", group="xyz", offline=False),
237 objects.Node(name="node11", group="xyz", offline=False),
238 objects.Node(name="node40", group="alloff", offline=True),
239 objects.Node(name="node41", group="alloff", offline=True),
240 objects.Node(name="node50", group="aaa", offline=False),
242 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
244 (online, perhost) = fn(mygroupnodes, "my", nodes)
245 self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
246 self.assertEqual(set(perhost.keys()), set(online))
248 self.assertEqual(perhost, {
249 "node20": ["node10", "node2", "node50"],
250 "node21": ["node11", "node3", "node50"],
251 "node22": ["node10", "node5", "node50"],
252 "node23": ["node11", "node2", "node50"],
253 "node24": ["node10", "node3", "node50"],
254 "node25": ["node11", "node5", "node50"],
257 def testSingleGroup(self):
258 fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
260 objects.Node(name="node1", group="default", offline=True),
261 objects.Node(name="node2", group="default", offline=False),
262 objects.Node(name="node3", group="default", offline=False),
263 objects.Node(name="node4", group="default", offline=True),
265 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
267 (online, perhost) = fn(nodes, "default", nodes)
268 self.assertEqual(online, ["node2", "node3"])
269 self.assertEqual(set(perhost.keys()), set(online))
271 self.assertEqual(perhost, {
277 class TestClusterVerifyFiles(unittest.TestCase):
279 def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
280 assert ((ecode == constants.CV_ENODEFILECHECK and
281 ht.TNonEmptyString(item)) or
282 (ecode == constants.CV_ECLUSTERFILECHECK and
289 errors.append((item, msg))
291 _VerifyFiles = cmdlib.LUClusterVerifyGroup._VerifyFiles
295 master_name = "master.example.com"
297 objects.Node(name=master_name, offline=False, vm_capable=True),
298 objects.Node(name="node2.example.com", offline=False, vm_capable=True),
299 objects.Node(name="node3.example.com", master_candidate=True,
301 objects.Node(name="node4.example.com", offline=False, vm_capable=True),
302 objects.Node(name="nodata.example.com", offline=False, vm_capable=True),
303 objects.Node(name="offline.example.com", offline=True),
305 cluster = objects.Cluster(modify_etc_hosts=True,
306 enabled_hypervisors=[constants.HT_XEN_HVM])
308 pathutils.CLUSTER_DOMAIN_SECRET_FILE,
309 pathutils.RAPI_CERT_FILE,
310 pathutils.RAPI_USERS_FILE,
313 pathutils.RAPI_USERS_FILE,
314 hv_xen.XL_CONFIG_FILE,
315 pathutils.VNC_PASSWORD_FILE,
318 pathutils.CLUSTER_CONF_FILE,
321 hv_xen.XEND_CONFIG_FILE,
322 hv_xen.XL_CONFIG_FILE,
323 pathutils.VNC_PASSWORD_FILE,
326 master_name: rpc.RpcResult(data=(True, {
327 constants.NV_FILELIST: {
328 pathutils.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
329 pathutils.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
330 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
331 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
332 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
334 "node2.example.com": rpc.RpcResult(data=(True, {
335 constants.NV_FILELIST: {
336 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
337 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
340 "node3.example.com": rpc.RpcResult(data=(True, {
341 constants.NV_FILELIST: {
342 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
343 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
346 "node4.example.com": rpc.RpcResult(data=(True, {
347 constants.NV_FILELIST: {
348 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
349 pathutils.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
350 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
351 pathutils.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
352 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
355 "nodata.example.com": rpc.RpcResult(data=(True, {})),
356 "offline.example.com": rpc.RpcResult(offline=True),
358 assert set(nvinfo.keys()) == set(map(operator.attrgetter("name"), nodeinfo))
360 self._VerifyFiles(compat.partial(self._FakeErrorIf, errors), nodeinfo,
362 (files_all, files_opt, files_mc, files_vm))
363 self.assertEqual(sorted(errors), sorted([
364 (None, ("File %s found with 2 different checksums (variant 1 on"
365 " node2.example.com, node3.example.com, node4.example.com;"
366 " variant 2 on master.example.com)" % pathutils.RAPI_CERT_FILE)),
367 (None, ("File %s is missing from node(s) node2.example.com" %
368 pathutils.CLUSTER_DOMAIN_SECRET_FILE)),
369 (None, ("File %s should not exist on node(s) node4.example.com" %
370 pathutils.CLUSTER_CONF_FILE)),
371 (None, ("File %s is missing from node(s) node4.example.com" %
372 hv_xen.XEND_CONFIG_FILE)),
373 (None, ("File %s is missing from node(s) node3.example.com" %
374 pathutils.CLUSTER_CONF_FILE)),
375 (None, ("File %s found with 2 different checksums (variant 1 on"
376 " master.example.com; variant 2 on node4.example.com)" %
377 pathutils.CLUSTER_CONF_FILE)),
378 (None, ("File %s is optional, but it must exist on all or no nodes (not"
379 " found on master.example.com, node2.example.com,"
380 " node3.example.com)" % pathutils.RAPI_USERS_FILE)),
381 (None, ("File %s is optional, but it must exist on all or no nodes (not"
382 " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
383 ("nodata.example.com", "Node did not return file checksum data"),
388 def __init__(self, cfg=NotImplemented, proc=NotImplemented):
389 self.warning_log = []
394 def LogWarning(self, text, *args):
395 self.warning_log.append((text, args))
397 def LogInfo(self, text, *args):
398 self.info_log.append((text, args))
401 class TestLoadNodeEvacResult(unittest.TestCase):
402 def testSuccess(self):
404 ("inst20153.example.com", "grp2", ["nodeA4509", "nodeB2912"]),
406 for early_release in [False, True]:
407 for use_nodes in [False, True]:
409 [opcodes.OpInstanceReplaceDisks().__getstate__()],
410 [opcodes.OpInstanceMigrate().__getstate__()],
413 alloc_result = (moved, [], jobs)
414 assert iallocator._NEVAC_RESULT(alloc_result)
417 result = cmdlib._LoadNodeEvacResult(lu, alloc_result,
418 early_release, use_nodes)
421 (_, (info_args, )) = lu.info_log.pop(0)
422 for (instname, instgroup, instnodes) in moved:
423 self.assertTrue(instname in info_args)
426 self.assertTrue(i in info_args)
428 self.assertTrue(instgroup in info_args)
430 self.assertFalse(lu.info_log)
431 self.assertFalse(lu.warning_log)
433 for op in itertools.chain(*result):
434 if hasattr(op.__class__, "early_release"):
435 self.assertEqual(op.early_release, early_release)
437 self.assertFalse(hasattr(op, "early_release"))
439 def testFailed(self):
440 alloc_result = ([], [
441 ("inst5191.example.com", "errormsg21178"),
443 assert iallocator._NEVAC_RESULT(alloc_result)
446 self.assertRaises(errors.OpExecError, cmdlib._LoadNodeEvacResult,
447 lu, alloc_result, False, False)
448 self.assertFalse(lu.info_log)
449 (_, (args, )) = lu.warning_log.pop(0)
450 self.assertTrue("inst5191.example.com" in args)
451 self.assertTrue("errormsg21178" in args)
452 self.assertFalse(lu.warning_log)
455 class TestUpdateAndVerifySubDict(unittest.TestCase):
458 "a": constants.VTYPE_INT,
459 "b": constants.VTYPE_STRING,
460 "c": constants.VTYPE_BOOL,
461 "d": constants.VTYPE_STRING,
508 verified = cmdlib._UpdateAndVerifySubDict(old_test, test, self.type_check)
509 self.assertEqual(verified, mv)
525 self.assertRaises(errors.TypeEnforcementError,
526 cmdlib._UpdateAndVerifySubDict, {}, test, self.type_check)
529 class TestHvStateHelper(unittest.TestCase):
530 def testWithoutOpData(self):
531 self.assertEqual(cmdlib._MergeAndVerifyHvState(None, NotImplemented), None)
533 def testWithoutOldData(self):
535 constants.HT_XEN_PVM: {
536 constants.HVST_MEMORY_TOTAL: 4096,
539 self.assertEqual(cmdlib._MergeAndVerifyHvState(new, None), new)
541 def testWithWrongHv(self):
544 constants.HVST_MEMORY_TOTAL: 4096,
547 self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyHvState, new,
550 class TestDiskStateHelper(unittest.TestCase):
551 def testWithoutOpData(self):
552 self.assertEqual(cmdlib._MergeAndVerifyDiskState(None, NotImplemented),
555 def testWithoutOldData(self):
559 constants.DS_DISK_RESERVED: 1024,
563 self.assertEqual(cmdlib._MergeAndVerifyDiskState(new, None), new)
565 def testWithWrongStorageType(self):
569 constants.DS_DISK_RESERVED: 1024,
573 self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyDiskState,
577 class TestComputeMinMaxSpec(unittest.TestCase):
580 constants.ISPECS_MAX: {
581 constants.ISPEC_MEM_SIZE: 512,
582 constants.ISPEC_DISK_SIZE: 1024,
584 constants.ISPECS_MIN: {
585 constants.ISPEC_MEM_SIZE: 128,
586 constants.ISPEC_DISK_COUNT: 1,
590 def testNoneValue(self):
591 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
592 self.ipolicy, None) is None)
594 def testAutoValue(self):
595 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
597 constants.VALUE_AUTO) is None)
599 def testNotDefined(self):
600 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT, None,
601 self.ipolicy, 3) is None)
603 def testNoMinDefined(self):
604 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE, None,
605 self.ipolicy, 128) is None)
607 def testNoMaxDefined(self):
608 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT, None,
609 self.ipolicy, 16) is None)
611 def testOutOfRange(self):
612 for (name, val) in ((constants.ISPEC_MEM_SIZE, 64),
613 (constants.ISPEC_MEM_SIZE, 768),
614 (constants.ISPEC_DISK_SIZE, 4096),
615 (constants.ISPEC_DISK_COUNT, 0)):
616 min_v = self.ipolicy[constants.ISPECS_MIN].get(name, val)
617 max_v = self.ipolicy[constants.ISPECS_MAX].get(name, val)
618 self.assertEqual(cmdlib._ComputeMinMaxSpec(name, None,
620 "%s value %s is not in range [%s, %s]" %
621 (name, val,min_v, max_v))
622 self.assertEqual(cmdlib._ComputeMinMaxSpec(name, "1",
624 "%s/1 value %s is not in range [%s, %s]" %
625 (name, val,min_v, max_v))
628 for (name, val) in ((constants.ISPEC_MEM_SIZE, 256),
629 (constants.ISPEC_MEM_SIZE, 128),
630 (constants.ISPEC_MEM_SIZE, 512),
631 (constants.ISPEC_DISK_SIZE, 1024),
632 (constants.ISPEC_DISK_SIZE, 0),
633 (constants.ISPEC_DISK_COUNT, 1),
634 (constants.ISPEC_DISK_COUNT, 5)):
635 self.assertTrue(cmdlib._ComputeMinMaxSpec(name, None, self.ipolicy, val)
639 def _ValidateComputeMinMaxSpec(name, *_):
640 assert name in constants.ISPECS_PARAMETERS
645 def __init__(self, spec):
648 def ComputeMinMaxSpec(self, *args):
649 return self.spec.pop(0)
652 class TestComputeIPolicySpecViolation(unittest.TestCase):
654 compute_fn = _ValidateComputeMinMaxSpec
655 ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
656 [1024], 1, _compute_fn=compute_fn)
657 self.assertEqual(ret, [])
659 def testInvalidArguments(self):
660 self.assertRaises(AssertionError, cmdlib._ComputeIPolicySpecViolation,
661 NotImplemented, 1024, 1, 1, 1, [], 1)
663 def testInvalidSpec(self):
664 spec = _SpecWrapper([None, False, "foo", None, "bar", None])
665 compute_fn = spec.ComputeMinMaxSpec
666 ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
667 [1024], 1, _compute_fn=compute_fn)
668 self.assertEqual(ret, ["foo", "bar"])
669 self.assertFalse(spec.spec)
672 class _StubComputeIPolicySpecViolation:
673 def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
675 self.mem_size = mem_size
676 self.cpu_count = cpu_count
677 self.disk_count = disk_count
678 self.nic_count = nic_count
679 self.disk_sizes = disk_sizes
680 self.spindle_use = spindle_use
682 def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
684 assert self.mem_size == mem_size
685 assert self.cpu_count == cpu_count
686 assert self.disk_count == disk_count
687 assert self.nic_count == nic_count
688 assert self.disk_sizes == disk_sizes
689 assert self.spindle_use == spindle_use
694 class TestComputeIPolicyInstanceViolation(unittest.TestCase):
697 constants.BE_MAXMEM: 2048,
698 constants.BE_VCPUS: 2,
699 constants.BE_SPINDLE_USE: 4,
701 disks = [objects.Disk(size=512)]
702 instance = objects.Instance(beparams=beparams, disks=disks, nics=[])
703 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 4)
704 ret = cmdlib._ComputeIPolicyInstanceViolation(NotImplemented, instance,
706 self.assertEqual(ret, [])
709 class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
712 constants.ISPEC_MEM_SIZE: 2048,
713 constants.ISPEC_CPU_COUNT: 2,
714 constants.ISPEC_DISK_COUNT: 1,
715 constants.ISPEC_DISK_SIZE: [512],
716 constants.ISPEC_NIC_COUNT: 0,
717 constants.ISPEC_SPINDLE_USE: 1,
719 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 1)
720 ret = cmdlib._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
722 self.assertEqual(ret, [])
726 def __init__(self, return_value=None):
728 self.return_value = return_value
730 def __call__(self, *args):
732 return self.return_value
735 class TestComputeIPolicyNodeViolation(unittest.TestCase):
737 self.recorder = _CallRecorder(return_value=[])
739 def testSameGroup(self):
740 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
742 _compute_fn=self.recorder)
743 self.assertFalse(self.recorder.called)
744 self.assertEqual(ret, [])
746 def testDifferentGroup(self):
747 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
749 _compute_fn=self.recorder)
750 self.assertTrue(self.recorder.called)
751 self.assertEqual(ret, [])
754 class _FakeConfigForTargetNodeIPolicy:
755 def __init__(self, node_info=NotImplemented):
756 self._node_info = node_info
758 def GetNodeInfo(self, _):
759 return self._node_info
762 class TestCheckTargetNodeIPolicy(unittest.TestCase):
764 self.instance = objects.Instance(primary_node="blubb")
765 self.target_node = objects.Node(group="bar")
766 node_info = objects.Node(group="foo")
767 fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
768 self.lu = _FakeLU(cfg=fake_cfg)
770 def testNoViolation(self):
771 compute_recoder = _CallRecorder(return_value=[])
772 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
774 _compute_fn=compute_recoder)
775 self.assertTrue(compute_recoder.called)
776 self.assertEqual(self.lu.warning_log, [])
778 def testNoIgnore(self):
779 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
780 self.assertRaises(errors.OpPrereqError, cmdlib._CheckTargetNodeIPolicy,
781 self.lu, NotImplemented, self.instance, self.target_node,
782 _compute_fn=compute_recoder)
783 self.assertTrue(compute_recoder.called)
784 self.assertEqual(self.lu.warning_log, [])
786 def testIgnoreViolation(self):
787 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
788 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
789 self.target_node, ignore=True,
790 _compute_fn=compute_recoder)
791 self.assertTrue(compute_recoder.called)
792 msg = ("Instance does not meet target node group's (bar) instance policy:"
793 " mem_size not in range")
794 self.assertEqual(self.lu.warning_log, [(msg, ())])
797 class TestApplyContainerMods(unittest.TestCase):
798 def testEmptyContainer(self):
801 cmdlib.ApplyContainerMods("test", container, chgdesc, [], None, None, None)
802 self.assertEqual(container, [])
803 self.assertEqual(chgdesc, [])
808 mods = cmdlib.PrepareContainerMods([
809 (constants.DDM_ADD, -1, "Hello"),
810 (constants.DDM_ADD, -1, "World"),
811 (constants.DDM_ADD, 0, "Start"),
812 (constants.DDM_ADD, -1, "End"),
814 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
816 self.assertEqual(container, ["Start", "Hello", "World", "End"])
817 self.assertEqual(chgdesc, [])
819 mods = cmdlib.PrepareContainerMods([
820 (constants.DDM_ADD, 0, "zero"),
821 (constants.DDM_ADD, 3, "Added"),
822 (constants.DDM_ADD, 5, "four"),
823 (constants.DDM_ADD, 7, "xyz"),
825 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
827 self.assertEqual(container,
828 ["zero", "Start", "Hello", "Added", "World", "four",
830 self.assertEqual(chgdesc, [])
832 for idx in [-2, len(container) + 1]:
833 mods = cmdlib.PrepareContainerMods([
834 (constants.DDM_ADD, idx, "error"),
836 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
837 "test", container, None, mods, None, None, None)
839 def testRemoveError(self):
840 for idx in [0, 1, 2, 100, -1, -4]:
841 mods = cmdlib.PrepareContainerMods([
842 (constants.DDM_REMOVE, idx, None),
844 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
845 "test", [], None, mods, None, None, None)
847 mods = cmdlib.PrepareContainerMods([
848 (constants.DDM_REMOVE, 0, object()),
850 self.assertRaises(AssertionError, cmdlib.ApplyContainerMods,
851 "test", [""], None, mods, None, None, None)
853 def testAddError(self):
854 for idx in range(-100, -1) + [100]:
855 mods = cmdlib.PrepareContainerMods([
856 (constants.DDM_ADD, idx, None),
858 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
859 "test", [], None, mods, None, None, None)
861 def testRemove(self):
862 container = ["item 1", "item 2"]
863 mods = cmdlib.PrepareContainerMods([
864 (constants.DDM_ADD, -1, "aaa"),
865 (constants.DDM_REMOVE, -1, None),
866 (constants.DDM_ADD, -1, "bbb"),
869 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
871 self.assertEqual(container, ["item 1", "item 2", "bbb"])
872 self.assertEqual(chgdesc, [
873 ("test/2", "remove"),
876 def testModify(self):
877 container = ["item 1", "item 2"]
878 mods = cmdlib.PrepareContainerMods([
879 (constants.DDM_MODIFY, -1, "a"),
880 (constants.DDM_MODIFY, 0, "b"),
881 (constants.DDM_MODIFY, 1, "c"),
884 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
886 self.assertEqual(container, ["item 1", "item 2"])
887 self.assertEqual(chgdesc, [])
889 for idx in [-2, len(container) + 1]:
890 mods = cmdlib.PrepareContainerMods([
891 (constants.DDM_MODIFY, idx, "error"),
893 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
894 "test", container, None, mods, None, None, None)
901 def _CreateTestFn(idx, params, private):
902 private.data = ("add", idx, params)
903 return ((100 * idx, params), [
904 ("test/%s" % idx, hex(idx)),
908 def _ModifyTestFn(idx, item, params, private):
909 private.data = ("modify", idx, params)
911 ("test/%s" % idx, "modify %s" % params),
915 def _RemoveTestFn(idx, item, private):
916 private.data = ("remove", idx, item)
918 def testAddWithCreateFunction(self):
921 mods = cmdlib.PrepareContainerMods([
922 (constants.DDM_ADD, -1, "Hello"),
923 (constants.DDM_ADD, -1, "World"),
924 (constants.DDM_ADD, 0, "Start"),
925 (constants.DDM_ADD, -1, "End"),
926 (constants.DDM_REMOVE, 2, None),
927 (constants.DDM_MODIFY, -1, "foobar"),
928 (constants.DDM_REMOVE, 2, None),
929 (constants.DDM_ADD, 1, "More"),
930 ], self._PrivateData)
931 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
932 self._CreateTestFn, self._ModifyTestFn, self._RemoveTestFn)
933 self.assertEqual(container, [
938 self.assertEqual(chgdesc, [
943 ("test/2", "remove"),
944 ("test/2", "modify foobar"),
945 ("test/2", "remove"),
948 self.assertTrue(compat.all(op == private.data[0]
949 for (op, _, _, private) in mods))
950 self.assertEqual([private.data for (op, _, _, private) in mods], [
955 ("remove", 2, (100, "World")),
956 ("modify", 2, "foobar"),
957 ("remove", 2, (300, "End")),
962 class _FakeConfigForGenDiskTemplate:
964 self._unique_id = itertools.count()
965 self._drbd_minor = itertools.count(20)
966 self._port = itertools.count(constants.FIRST_DRBD_PORT)
967 self._secret = itertools.count()
972 def GenerateUniqueID(self, ec_id):
973 return "ec%s-uq%s" % (ec_id, self._unique_id.next())
975 def AllocateDRBDMinor(self, nodes, instance):
976 return [self._drbd_minor.next()
979 def AllocatePort(self):
980 return self._port.next()
982 def GenerateDRBDSecret(self, ec_id):
983 return "ec%s-secret%s" % (ec_id, self._secret.next())
985 def GetInstanceInfo(self, _):
989 class _FakeProcForGenDiskTemplate:
994 class TestGenerateDiskTemplate(unittest.TestCase):
996 nodegroup = objects.NodeGroup(name="ng")
997 nodegroup.UpgradeConfig()
999 cfg = _FakeConfigForGenDiskTemplate()
1000 proc = _FakeProcForGenDiskTemplate()
1002 self.lu = _FakeLU(cfg=cfg, proc=proc)
1003 self.nodegroup = nodegroup
1006 def GetDiskParams():
1007 return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1009 def testWrongDiskTemplate(self):
1010 gdt = cmdlib._GenerateDiskTemplate
1011 disk_template = "##unknown##"
1013 assert disk_template not in constants.DISK_TEMPLATES
1015 self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
1016 "inst26831.example.com", "node30113.example.com", [], [],
1017 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1018 self.GetDiskParams())
1020 def testDiskless(self):
1021 gdt = cmdlib._GenerateDiskTemplate
1023 result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1024 "node30113.example.com", [], [],
1025 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1026 self.GetDiskParams())
1027 self.assertEqual(result, [])
1029 def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1030 file_storage_dir=NotImplemented,
1031 file_driver=NotImplemented,
1032 req_file_storage=NotImplemented,
1033 req_shr_file_storage=NotImplemented):
1034 gdt = cmdlib._GenerateDiskTemplate
1036 map(lambda params: utils.ForceDictType(params,
1037 constants.IDISK_PARAMS_TYPES),
1040 # Check if non-empty list of secondaries is rejected
1041 self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1042 template, "inst25088.example.com",
1043 "node185.example.com", ["node323.example.com"], [],
1044 NotImplemented, NotImplemented, base_index,
1045 self.lu.LogInfo, self.GetDiskParams(),
1046 _req_file_storage=req_file_storage,
1047 _req_shr_file_storage=req_shr_file_storage)
1049 result = gdt(self.lu, template, "inst21662.example.com",
1050 "node21741.example.com", [],
1051 disk_info, file_storage_dir, file_driver, base_index,
1052 self.lu.LogInfo, self.GetDiskParams(),
1053 _req_file_storage=req_file_storage,
1054 _req_shr_file_storage=req_shr_file_storage)
1056 for (idx, disk) in enumerate(result):
1057 self.assertTrue(isinstance(disk, objects.Disk))
1058 self.assertEqual(disk.dev_type, exp_dev_type)
1059 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1060 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1061 self.assertTrue(disk.children is None)
1063 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1064 cmdlib._UpdateIvNames(base_index, result)
1065 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1069 def _CheckIvNames(self, disks, base_index, end_index):
1070 self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1071 ["disk/%s" % i for i in range(base_index, end_index)])
1073 def testPlain(self):
1075 constants.IDISK_SIZE: 1024,
1076 constants.IDISK_MODE: constants.DISK_RDWR,
1078 constants.IDISK_SIZE: 4096,
1079 constants.IDISK_VG: "othervg",
1080 constants.IDISK_MODE: constants.DISK_RDWR,
1083 result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1086 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1087 ("testvg", "ec0-uq0.disk3"),
1088 ("othervg", "ec0-uq1.disk4"),
1092 def _AllowFileStorage():
1096 def _ForbidFileStorage():
1097 raise errors.OpPrereqError("Disallowed in test")
1100 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1101 constants.DT_FILE, [], 0, NotImplemented,
1102 req_file_storage=self._ForbidFileStorage)
1103 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1104 constants.DT_SHARED_FILE, [], 0, NotImplemented,
1105 req_shr_file_storage=self._ForbidFileStorage)
1107 for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1109 constants.IDISK_SIZE: 80 * 1024,
1110 constants.IDISK_MODE: constants.DISK_RDONLY,
1112 constants.IDISK_SIZE: 4096,
1113 constants.IDISK_MODE: constants.DISK_RDWR,
1115 constants.IDISK_SIZE: 6 * 1024,
1116 constants.IDISK_MODE: constants.DISK_RDWR,
1119 result = self._TestTrivialDisk(disk_template, disk_info, 2,
1120 constants.LD_FILE, file_storage_dir="/tmp",
1121 file_driver=constants.FD_BLKTAP,
1122 req_file_storage=self._AllowFileStorage,
1123 req_shr_file_storage=self._AllowFileStorage)
1125 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1126 (constants.FD_BLKTAP, "/tmp/disk2"),
1127 (constants.FD_BLKTAP, "/tmp/disk3"),
1128 (constants.FD_BLKTAP, "/tmp/disk4"),
1131 def testBlock(self):
1133 constants.IDISK_SIZE: 8 * 1024,
1134 constants.IDISK_MODE: constants.DISK_RDWR,
1135 constants.IDISK_ADOPT: "/tmp/some/block/dev",
1138 result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1139 constants.LD_BLOCKDEV)
1141 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1142 (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1147 constants.IDISK_SIZE: 8 * 1024,
1148 constants.IDISK_MODE: constants.DISK_RDONLY,
1150 constants.IDISK_SIZE: 100 * 1024,
1151 constants.IDISK_MODE: constants.DISK_RDWR,
1154 result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1157 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1158 ("rbd", "ec0-uq0.rbd.disk0"),
1159 ("rbd", "ec0-uq1.rbd.disk1"),
1162 def testDrbd8(self):
1163 gdt = cmdlib._GenerateDiskTemplate
1164 drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1165 drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1168 constants.IDISK_SIZE: 1024,
1169 constants.IDISK_MODE: constants.DISK_RDWR,
1171 constants.IDISK_SIZE: 100 * 1024,
1172 constants.IDISK_MODE: constants.DISK_RDONLY,
1173 constants.IDISK_METAVG: "metavg",
1175 constants.IDISK_SIZE: 4096,
1176 constants.IDISK_MODE: constants.DISK_RDWR,
1177 constants.IDISK_VG: "vgxyz",
1181 exp_logical_ids = [[
1182 (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1183 (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1185 (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1186 ("metavg", "ec0-uq1.disk1_meta"),
1188 ("vgxyz", "ec0-uq2.disk2_data"),
1189 (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1192 assert len(exp_logical_ids) == len(disk_info)
1194 map(lambda params: utils.ForceDictType(params,
1195 constants.IDISK_PARAMS_TYPES),
1198 # Check if empty list of secondaries is rejected
1199 self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1200 "inst827.example.com", "node1334.example.com", [],
1201 disk_info, NotImplemented, NotImplemented, 0,
1202 self.lu.LogInfo, self.GetDiskParams())
1204 result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1205 "node1334.example.com", ["node12272.example.com"],
1206 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1207 self.GetDiskParams())
1209 for (idx, disk) in enumerate(result):
1210 self.assertTrue(isinstance(disk, objects.Disk))
1211 self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1212 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1213 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1215 for child in disk.children:
1216 self.assertTrue(isinstance(disk, objects.Disk))
1217 self.assertEqual(child.dev_type, constants.LD_LV)
1218 self.assertTrue(child.children is None)
1220 self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1221 exp_logical_ids[idx])
1223 self.assertEqual(len(disk.children), 2)
1224 self.assertEqual(disk.children[0].size, disk.size)
1225 self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1227 self._CheckIvNames(result, 0, len(disk_info))
1228 cmdlib._UpdateIvNames(0, result)
1229 self._CheckIvNames(result, 0, len(disk_info))
1231 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1232 ("node1334.example.com", "node12272.example.com",
1233 constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1234 ("node1334.example.com", "node12272.example.com",
1235 constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1236 ("node1334.example.com", "node12272.example.com",
1237 constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1241 if __name__ == "__main__":
1242 testutils.GanetiTestProgram()