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.masterd import iallocator
46 from ganeti.hypervisor import hv_xen
52 class TestCertVerification(testutils.GanetiTestCase):
54 testutils.GanetiTestCase.setUp(self)
56 self.tmpdir = tempfile.mkdtemp()
59 shutil.rmtree(self.tmpdir)
61 def testVerifyCertificate(self):
62 cmdlib._VerifyCertificate(self._TestDataFilename("cert1.pem"))
64 nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
66 (errcode, msg) = cmdlib._VerifyCertificate(nonexist_filename)
67 self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
69 # Try to load non-certificate file
70 invalid_cert = self._TestDataFilename("bdev-net.txt")
71 (errcode, msg) = cmdlib._VerifyCertificate(invalid_cert)
72 self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
75 class TestOpcodeParams(testutils.GanetiTestCase):
76 def testParamsStructures(self):
77 for op in sorted(mcpu.Processor.DISPATCH_TABLE):
78 lu = mcpu.Processor.DISPATCH_TABLE[op]
80 self.failIf(hasattr(lu, "_OP_REQP"),
81 msg=("LU '%s' has old-style _OP_REQP" % lu_name))
82 self.failIf(hasattr(lu, "_OP_DEFS"),
83 msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
84 self.failIf(hasattr(lu, "_OP_PARAMS"),
85 msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
88 class TestIAllocatorChecks(testutils.GanetiTestCase):
89 def testFunction(self):
91 def __init__(self, opcode):
92 self.cfg = mocks.FakeConfig()
95 class OpTest(opcodes.OpCode):
97 ("iallocator", None, ht.NoType, None),
98 ("node", None, ht.NoType, None),
101 default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
102 other_iallocator = default_iallocator + "_not"
107 c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
109 # Neither node nor iallocator given
113 self.assertEqual(lu.op.iallocator, default_iallocator)
114 self.assertEqual(lu.op.node, None)
116 # Both, iallocator and node given
117 op.iallocator = "test"
119 self.assertRaises(errors.OpPrereqError, c_i)
121 # Only iallocator given
122 op.iallocator = other_iallocator
125 self.assertEqual(lu.op.iallocator, other_iallocator)
126 self.assertEqual(lu.op.node, None)
132 self.assertEqual(lu.op.iallocator, None)
133 self.assertEqual(lu.op.node, "node")
135 # No node, iallocator or default iallocator
138 lu.cfg.GetDefaultIAllocator = lambda: None
139 self.assertRaises(errors.OpPrereqError, c_i)
142 class TestLUTestJqueue(unittest.TestCase):
144 self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
145 (luxi.WFJC_TIMEOUT * 0.75),
146 msg=("Client timeout too high, might not notice bugs"
147 " in WaitForJobChange"))
150 class TestLUQuery(unittest.TestCase):
152 self.assertEqual(sorted(cmdlib._QUERY_IMPL.keys()),
153 sorted(constants.QR_VIA_OP))
155 assert constants.QR_NODE in constants.QR_VIA_OP
156 assert constants.QR_INSTANCE in constants.QR_VIA_OP
158 for i in constants.QR_VIA_OP:
159 self.assert_(cmdlib._GetQueryImplementation(i))
161 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation, "")
162 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation,
166 class TestLUGroupAssignNodes(unittest.TestCase):
168 def testCheckAssignmentForSplitInstances(self):
169 node_data = dict((name, objects.Node(name=name, group=group))
170 for (name, group) in [("n1a", "g1"), ("n1b", "g1"),
171 ("n2a", "g2"), ("n2b", "g2"),
172 ("n3a", "g3"), ("n3b", "g3"),
176 def Instance(name, pnode, snode):
179 disk_template = constants.DT_DISKLESS
181 disks = [objects.Disk(dev_type=constants.LD_DRBD8,
182 logical_id=[pnode, snode, 1, 17, 17])]
183 disk_template = constants.DT_DRBD8
185 return objects.Instance(name=name, primary_node=pnode, disks=disks,
186 disk_template=disk_template)
188 instance_data = dict((name, Instance(name, pnode, snode))
189 for name, pnode, snode in [("inst1a", "n1a", "n1b"),
190 ("inst1b", "n1b", "n1a"),
191 ("inst2a", "n2a", "n2b"),
192 ("inst3a", "n3a", None),
193 ("inst3b", "n3b", "n1b"),
194 ("inst3c", "n3b", "n2b"),
197 # Test first with the existing state.
199 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
203 self.assertEqual([], new)
204 self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
206 # And now some changes.
208 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
213 self.assertEqual(set(["inst1a", "inst1b"]), set(new))
214 self.assertEqual(set(["inst3c"]), set(prev))
217 class TestClusterVerifySsh(unittest.TestCase):
218 def testMultipleGroups(self):
219 fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
221 objects.Node(name="node20", group="my", offline=False),
222 objects.Node(name="node21", group="my", offline=False),
223 objects.Node(name="node22", group="my", offline=False),
224 objects.Node(name="node23", group="my", offline=False),
225 objects.Node(name="node24", group="my", offline=False),
226 objects.Node(name="node25", group="my", offline=False),
227 objects.Node(name="node26", group="my", offline=True),
230 objects.Node(name="node1", group="g1", offline=True),
231 objects.Node(name="node2", group="g1", offline=False),
232 objects.Node(name="node3", group="g1", offline=False),
233 objects.Node(name="node4", group="g1", offline=True),
234 objects.Node(name="node5", group="g1", offline=False),
235 objects.Node(name="node10", group="xyz", offline=False),
236 objects.Node(name="node11", group="xyz", offline=False),
237 objects.Node(name="node40", group="alloff", offline=True),
238 objects.Node(name="node41", group="alloff", offline=True),
239 objects.Node(name="node50", group="aaa", offline=False),
241 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
243 (online, perhost) = fn(mygroupnodes, "my", nodes)
244 self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
245 self.assertEqual(set(perhost.keys()), set(online))
247 self.assertEqual(perhost, {
248 "node20": ["node10", "node2", "node50"],
249 "node21": ["node11", "node3", "node50"],
250 "node22": ["node10", "node5", "node50"],
251 "node23": ["node11", "node2", "node50"],
252 "node24": ["node10", "node3", "node50"],
253 "node25": ["node11", "node5", "node50"],
256 def testSingleGroup(self):
257 fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
259 objects.Node(name="node1", group="default", offline=True),
260 objects.Node(name="node2", group="default", offline=False),
261 objects.Node(name="node3", group="default", offline=False),
262 objects.Node(name="node4", group="default", offline=True),
264 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
266 (online, perhost) = fn(nodes, "default", nodes)
267 self.assertEqual(online, ["node2", "node3"])
268 self.assertEqual(set(perhost.keys()), set(online))
270 self.assertEqual(perhost, {
276 class TestClusterVerifyFiles(unittest.TestCase):
278 def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
279 assert ((ecode == constants.CV_ENODEFILECHECK and
280 ht.TNonEmptyString(item)) or
281 (ecode == constants.CV_ECLUSTERFILECHECK and
288 errors.append((item, msg))
290 _VerifyFiles = cmdlib.LUClusterVerifyGroup._VerifyFiles
294 master_name = "master.example.com"
296 objects.Node(name=master_name, offline=False, vm_capable=True),
297 objects.Node(name="node2.example.com", offline=False, vm_capable=True),
298 objects.Node(name="node3.example.com", master_candidate=True,
300 objects.Node(name="node4.example.com", offline=False, vm_capable=True),
301 objects.Node(name="nodata.example.com", offline=False, vm_capable=True),
302 objects.Node(name="offline.example.com", offline=True),
304 cluster = objects.Cluster(modify_etc_hosts=True,
305 enabled_hypervisors=[constants.HT_XEN_HVM])
307 constants.CLUSTER_DOMAIN_SECRET_FILE,
308 constants.RAPI_CERT_FILE,
309 constants.RAPI_USERS_FILE,
312 constants.RAPI_USERS_FILE,
313 hv_xen.XL_CONFIG_FILE,
314 constants.VNC_PASSWORD_FILE,
317 constants.CLUSTER_CONF_FILE,
320 hv_xen.XEND_CONFIG_FILE,
321 hv_xen.XL_CONFIG_FILE,
322 constants.VNC_PASSWORD_FILE,
325 master_name: rpc.RpcResult(data=(True, {
326 constants.NV_FILELIST: {
327 constants.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
328 constants.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
329 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
330 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
331 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
333 "node2.example.com": rpc.RpcResult(data=(True, {
334 constants.NV_FILELIST: {
335 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
336 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
339 "node3.example.com": rpc.RpcResult(data=(True, {
340 constants.NV_FILELIST: {
341 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
342 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
345 "node4.example.com": rpc.RpcResult(data=(True, {
346 constants.NV_FILELIST: {
347 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
348 constants.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
349 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
350 constants.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
351 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
354 "nodata.example.com": rpc.RpcResult(data=(True, {})),
355 "offline.example.com": rpc.RpcResult(offline=True),
357 assert set(nvinfo.keys()) == set(map(operator.attrgetter("name"), nodeinfo))
359 self._VerifyFiles(compat.partial(self._FakeErrorIf, errors), nodeinfo,
361 (files_all, files_opt, files_mc, files_vm))
362 self.assertEqual(sorted(errors), sorted([
363 (None, ("File %s found with 2 different checksums (variant 1 on"
364 " node2.example.com, node3.example.com, node4.example.com;"
365 " variant 2 on master.example.com)" % constants.RAPI_CERT_FILE)),
366 (None, ("File %s is missing from node(s) node2.example.com" %
367 constants.CLUSTER_DOMAIN_SECRET_FILE)),
368 (None, ("File %s should not exist on node(s) node4.example.com" %
369 constants.CLUSTER_CONF_FILE)),
370 (None, ("File %s is missing from node(s) node4.example.com" %
371 hv_xen.XEND_CONFIG_FILE)),
372 (None, ("File %s is missing from node(s) node3.example.com" %
373 constants.CLUSTER_CONF_FILE)),
374 (None, ("File %s found with 2 different checksums (variant 1 on"
375 " master.example.com; variant 2 on node4.example.com)" %
376 constants.CLUSTER_CONF_FILE)),
377 (None, ("File %s is optional, but it must exist on all or no nodes (not"
378 " found on master.example.com, node2.example.com,"
379 " node3.example.com)" % constants.RAPI_USERS_FILE)),
380 (None, ("File %s is optional, but it must exist on all or no nodes (not"
381 " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
382 ("nodata.example.com", "Node did not return file checksum data"),
387 def __init__(self, cfg=NotImplemented, proc=NotImplemented):
388 self.warning_log = []
393 def LogWarning(self, text, *args):
394 self.warning_log.append((text, args))
396 def LogInfo(self, text, *args):
397 self.info_log.append((text, args))
400 class TestLoadNodeEvacResult(unittest.TestCase):
401 def testSuccess(self):
403 ("inst20153.example.com", "grp2", ["nodeA4509", "nodeB2912"]),
405 for early_release in [False, True]:
406 for use_nodes in [False, True]:
408 [opcodes.OpInstanceReplaceDisks().__getstate__()],
409 [opcodes.OpInstanceMigrate().__getstate__()],
412 alloc_result = (moved, [], jobs)
413 assert iallocator._NEVAC_RESULT(alloc_result)
416 result = cmdlib._LoadNodeEvacResult(lu, alloc_result,
417 early_release, use_nodes)
420 (_, (info_args, )) = lu.info_log.pop(0)
421 for (instname, instgroup, instnodes) in moved:
422 self.assertTrue(instname in info_args)
425 self.assertTrue(i in info_args)
427 self.assertTrue(instgroup in info_args)
429 self.assertFalse(lu.info_log)
430 self.assertFalse(lu.warning_log)
432 for op in itertools.chain(*result):
433 if hasattr(op.__class__, "early_release"):
434 self.assertEqual(op.early_release, early_release)
436 self.assertFalse(hasattr(op, "early_release"))
438 def testFailed(self):
439 alloc_result = ([], [
440 ("inst5191.example.com", "errormsg21178"),
442 assert iallocator._NEVAC_RESULT(alloc_result)
445 self.assertRaises(errors.OpExecError, cmdlib._LoadNodeEvacResult,
446 lu, alloc_result, False, False)
447 self.assertFalse(lu.info_log)
448 (_, (args, )) = lu.warning_log.pop(0)
449 self.assertTrue("inst5191.example.com" in args)
450 self.assertTrue("errormsg21178" in args)
451 self.assertFalse(lu.warning_log)
454 class TestUpdateAndVerifySubDict(unittest.TestCase):
457 "a": constants.VTYPE_INT,
458 "b": constants.VTYPE_STRING,
459 "c": constants.VTYPE_BOOL,
460 "d": constants.VTYPE_STRING,
507 verified = cmdlib._UpdateAndVerifySubDict(old_test, test, self.type_check)
508 self.assertEqual(verified, mv)
524 self.assertRaises(errors.TypeEnforcementError,
525 cmdlib._UpdateAndVerifySubDict, {}, test, self.type_check)
528 class TestHvStateHelper(unittest.TestCase):
529 def testWithoutOpData(self):
530 self.assertEqual(cmdlib._MergeAndVerifyHvState(None, NotImplemented), None)
532 def testWithoutOldData(self):
534 constants.HT_XEN_PVM: {
535 constants.HVST_MEMORY_TOTAL: 4096,
538 self.assertEqual(cmdlib._MergeAndVerifyHvState(new, None), new)
540 def testWithWrongHv(self):
543 constants.HVST_MEMORY_TOTAL: 4096,
546 self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyHvState, new,
549 class TestDiskStateHelper(unittest.TestCase):
550 def testWithoutOpData(self):
551 self.assertEqual(cmdlib._MergeAndVerifyDiskState(None, NotImplemented),
554 def testWithoutOldData(self):
558 constants.DS_DISK_RESERVED: 1024,
562 self.assertEqual(cmdlib._MergeAndVerifyDiskState(new, None), new)
564 def testWithWrongStorageType(self):
568 constants.DS_DISK_RESERVED: 1024,
572 self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyDiskState,
576 class TestComputeMinMaxSpec(unittest.TestCase):
579 constants.ISPECS_MAX: {
580 constants.ISPEC_MEM_SIZE: 512,
581 constants.ISPEC_DISK_SIZE: 1024,
583 constants.ISPECS_MIN: {
584 constants.ISPEC_MEM_SIZE: 128,
585 constants.ISPEC_DISK_COUNT: 1,
589 def testNoneValue(self):
590 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
591 self.ipolicy, None) is None)
593 def testAutoValue(self):
594 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
596 constants.VALUE_AUTO) is None)
598 def testNotDefined(self):
599 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT, None,
600 self.ipolicy, 3) is None)
602 def testNoMinDefined(self):
603 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE, None,
604 self.ipolicy, 128) is None)
606 def testNoMaxDefined(self):
607 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT, None,
608 self.ipolicy, 16) is None)
610 def testOutOfRange(self):
611 for (name, val) in ((constants.ISPEC_MEM_SIZE, 64),
612 (constants.ISPEC_MEM_SIZE, 768),
613 (constants.ISPEC_DISK_SIZE, 4096),
614 (constants.ISPEC_DISK_COUNT, 0)):
615 min_v = self.ipolicy[constants.ISPECS_MIN].get(name, val)
616 max_v = self.ipolicy[constants.ISPECS_MAX].get(name, val)
617 self.assertEqual(cmdlib._ComputeMinMaxSpec(name, None,
619 "%s value %s is not in range [%s, %s]" %
620 (name, val,min_v, max_v))
621 self.assertEqual(cmdlib._ComputeMinMaxSpec(name, "1",
623 "%s/1 value %s is not in range [%s, %s]" %
624 (name, val,min_v, max_v))
627 for (name, val) in ((constants.ISPEC_MEM_SIZE, 256),
628 (constants.ISPEC_MEM_SIZE, 128),
629 (constants.ISPEC_MEM_SIZE, 512),
630 (constants.ISPEC_DISK_SIZE, 1024),
631 (constants.ISPEC_DISK_SIZE, 0),
632 (constants.ISPEC_DISK_COUNT, 1),
633 (constants.ISPEC_DISK_COUNT, 5)):
634 self.assertTrue(cmdlib._ComputeMinMaxSpec(name, None, self.ipolicy, val)
638 def _ValidateComputeMinMaxSpec(name, *_):
639 assert name in constants.ISPECS_PARAMETERS
644 def __init__(self, spec):
647 def ComputeMinMaxSpec(self, *args):
648 return self.spec.pop(0)
651 class TestComputeIPolicySpecViolation(unittest.TestCase):
653 compute_fn = _ValidateComputeMinMaxSpec
654 ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
655 [1024], 1, _compute_fn=compute_fn)
656 self.assertEqual(ret, [])
658 def testInvalidArguments(self):
659 self.assertRaises(AssertionError, cmdlib._ComputeIPolicySpecViolation,
660 NotImplemented, 1024, 1, 1, 1, [], 1)
662 def testInvalidSpec(self):
663 spec = _SpecWrapper([None, False, "foo", None, "bar", None])
664 compute_fn = spec.ComputeMinMaxSpec
665 ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
666 [1024], 1, _compute_fn=compute_fn)
667 self.assertEqual(ret, ["foo", "bar"])
668 self.assertFalse(spec.spec)
671 class _StubComputeIPolicySpecViolation:
672 def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
674 self.mem_size = mem_size
675 self.cpu_count = cpu_count
676 self.disk_count = disk_count
677 self.nic_count = nic_count
678 self.disk_sizes = disk_sizes
679 self.spindle_use = spindle_use
681 def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
683 assert self.mem_size == mem_size
684 assert self.cpu_count == cpu_count
685 assert self.disk_count == disk_count
686 assert self.nic_count == nic_count
687 assert self.disk_sizes == disk_sizes
688 assert self.spindle_use == spindle_use
693 class TestComputeIPolicyInstanceViolation(unittest.TestCase):
696 constants.BE_MAXMEM: 2048,
697 constants.BE_VCPUS: 2,
698 constants.BE_SPINDLE_USE: 4,
700 disks = [objects.Disk(size=512)]
701 instance = objects.Instance(beparams=beparams, disks=disks, nics=[])
702 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 4)
703 ret = cmdlib._ComputeIPolicyInstanceViolation(NotImplemented, instance,
705 self.assertEqual(ret, [])
708 class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
711 constants.ISPEC_MEM_SIZE: 2048,
712 constants.ISPEC_CPU_COUNT: 2,
713 constants.ISPEC_DISK_COUNT: 1,
714 constants.ISPEC_DISK_SIZE: [512],
715 constants.ISPEC_NIC_COUNT: 0,
716 constants.ISPEC_SPINDLE_USE: 1,
718 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 1)
719 ret = cmdlib._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
721 self.assertEqual(ret, [])
725 def __init__(self, return_value=None):
727 self.return_value = return_value
729 def __call__(self, *args):
731 return self.return_value
734 class TestComputeIPolicyNodeViolation(unittest.TestCase):
736 self.recorder = _CallRecorder(return_value=[])
738 def testSameGroup(self):
739 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
741 _compute_fn=self.recorder)
742 self.assertFalse(self.recorder.called)
743 self.assertEqual(ret, [])
745 def testDifferentGroup(self):
746 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
748 _compute_fn=self.recorder)
749 self.assertTrue(self.recorder.called)
750 self.assertEqual(ret, [])
753 class _FakeConfigForTargetNodeIPolicy:
754 def __init__(self, node_info=NotImplemented):
755 self._node_info = node_info
757 def GetNodeInfo(self, _):
758 return self._node_info
761 class TestCheckTargetNodeIPolicy(unittest.TestCase):
763 self.instance = objects.Instance(primary_node="blubb")
764 self.target_node = objects.Node(group="bar")
765 node_info = objects.Node(group="foo")
766 fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
767 self.lu = _FakeLU(cfg=fake_cfg)
769 def testNoViolation(self):
770 compute_recoder = _CallRecorder(return_value=[])
771 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
773 _compute_fn=compute_recoder)
774 self.assertTrue(compute_recoder.called)
775 self.assertEqual(self.lu.warning_log, [])
777 def testNoIgnore(self):
778 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
779 self.assertRaises(errors.OpPrereqError, cmdlib._CheckTargetNodeIPolicy,
780 self.lu, NotImplemented, self.instance, self.target_node,
781 _compute_fn=compute_recoder)
782 self.assertTrue(compute_recoder.called)
783 self.assertEqual(self.lu.warning_log, [])
785 def testIgnoreViolation(self):
786 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
787 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
788 self.target_node, ignore=True,
789 _compute_fn=compute_recoder)
790 self.assertTrue(compute_recoder.called)
791 msg = ("Instance does not meet target node group's (bar) instance policy:"
792 " mem_size not in range")
793 self.assertEqual(self.lu.warning_log, [(msg, ())])
796 class TestApplyContainerMods(unittest.TestCase):
797 def testEmptyContainer(self):
800 cmdlib.ApplyContainerMods("test", container, chgdesc, [], None, None, None)
801 self.assertEqual(container, [])
802 self.assertEqual(chgdesc, [])
807 mods = cmdlib.PrepareContainerMods([
808 (constants.DDM_ADD, -1, "Hello"),
809 (constants.DDM_ADD, -1, "World"),
810 (constants.DDM_ADD, 0, "Start"),
811 (constants.DDM_ADD, -1, "End"),
813 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
815 self.assertEqual(container, ["Start", "Hello", "World", "End"])
816 self.assertEqual(chgdesc, [])
818 mods = cmdlib.PrepareContainerMods([
819 (constants.DDM_ADD, 0, "zero"),
820 (constants.DDM_ADD, 3, "Added"),
821 (constants.DDM_ADD, 5, "four"),
822 (constants.DDM_ADD, 7, "xyz"),
824 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
826 self.assertEqual(container,
827 ["zero", "Start", "Hello", "Added", "World", "four",
829 self.assertEqual(chgdesc, [])
831 for idx in [-2, len(container) + 1]:
832 mods = cmdlib.PrepareContainerMods([
833 (constants.DDM_ADD, idx, "error"),
835 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
836 "test", container, None, mods, None, None, None)
838 def testRemoveError(self):
839 for idx in [0, 1, 2, 100, -1, -4]:
840 mods = cmdlib.PrepareContainerMods([
841 (constants.DDM_REMOVE, idx, None),
843 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
844 "test", [], None, mods, None, None, None)
846 mods = cmdlib.PrepareContainerMods([
847 (constants.DDM_REMOVE, 0, object()),
849 self.assertRaises(AssertionError, cmdlib.ApplyContainerMods,
850 "test", [""], None, mods, None, None, None)
852 def testAddError(self):
853 for idx in range(-100, -1) + [100]:
854 mods = cmdlib.PrepareContainerMods([
855 (constants.DDM_ADD, idx, None),
857 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
858 "test", [], None, mods, None, None, None)
860 def testRemove(self):
861 container = ["item 1", "item 2"]
862 mods = cmdlib.PrepareContainerMods([
863 (constants.DDM_ADD, -1, "aaa"),
864 (constants.DDM_REMOVE, -1, None),
865 (constants.DDM_ADD, -1, "bbb"),
868 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
870 self.assertEqual(container, ["item 1", "item 2", "bbb"])
871 self.assertEqual(chgdesc, [
872 ("test/2", "remove"),
875 def testModify(self):
876 container = ["item 1", "item 2"]
877 mods = cmdlib.PrepareContainerMods([
878 (constants.DDM_MODIFY, -1, "a"),
879 (constants.DDM_MODIFY, 0, "b"),
880 (constants.DDM_MODIFY, 1, "c"),
883 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
885 self.assertEqual(container, ["item 1", "item 2"])
886 self.assertEqual(chgdesc, [])
888 for idx in [-2, len(container) + 1]:
889 mods = cmdlib.PrepareContainerMods([
890 (constants.DDM_MODIFY, idx, "error"),
892 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
893 "test", container, None, mods, None, None, None)
900 def _CreateTestFn(idx, params, private):
901 private.data = ("add", idx, params)
902 return ((100 * idx, params), [
903 ("test/%s" % idx, hex(idx)),
907 def _ModifyTestFn(idx, item, params, private):
908 private.data = ("modify", idx, params)
910 ("test/%s" % idx, "modify %s" % params),
914 def _RemoveTestFn(idx, item, private):
915 private.data = ("remove", idx, item)
917 def testAddWithCreateFunction(self):
920 mods = cmdlib.PrepareContainerMods([
921 (constants.DDM_ADD, -1, "Hello"),
922 (constants.DDM_ADD, -1, "World"),
923 (constants.DDM_ADD, 0, "Start"),
924 (constants.DDM_ADD, -1, "End"),
925 (constants.DDM_REMOVE, 2, None),
926 (constants.DDM_MODIFY, -1, "foobar"),
927 (constants.DDM_REMOVE, 2, None),
928 (constants.DDM_ADD, 1, "More"),
929 ], self._PrivateData)
930 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
931 self._CreateTestFn, self._ModifyTestFn, self._RemoveTestFn)
932 self.assertEqual(container, [
937 self.assertEqual(chgdesc, [
942 ("test/2", "remove"),
943 ("test/2", "modify foobar"),
944 ("test/2", "remove"),
947 self.assertTrue(compat.all(op == private.data[0]
948 for (op, _, _, private) in mods))
949 self.assertEqual([private.data for (op, _, _, private) in mods], [
954 ("remove", 2, (100, "World")),
955 ("modify", 2, "foobar"),
956 ("remove", 2, (300, "End")),
961 class _FakeConfigForGenDiskTemplate:
963 self._unique_id = itertools.count()
964 self._drbd_minor = itertools.count(20)
965 self._port = itertools.count(constants.FIRST_DRBD_PORT)
966 self._secret = itertools.count()
971 def GenerateUniqueID(self, ec_id):
972 return "ec%s-uq%s" % (ec_id, self._unique_id.next())
974 def AllocateDRBDMinor(self, nodes, instance):
975 return [self._drbd_minor.next()
978 def AllocatePort(self):
979 return self._port.next()
981 def GenerateDRBDSecret(self, ec_id):
982 return "ec%s-secret%s" % (ec_id, self._secret.next())
984 def GetInstanceInfo(self, _):
988 class _FakeProcForGenDiskTemplate:
993 class TestGenerateDiskTemplate(unittest.TestCase):
995 nodegroup = objects.NodeGroup(name="ng")
996 nodegroup.UpgradeConfig()
998 cfg = _FakeConfigForGenDiskTemplate()
999 proc = _FakeProcForGenDiskTemplate()
1001 self.lu = _FakeLU(cfg=cfg, proc=proc)
1002 self.nodegroup = nodegroup
1005 def GetDiskParams():
1006 return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1008 def testWrongDiskTemplate(self):
1009 gdt = cmdlib._GenerateDiskTemplate
1010 disk_template = "##unknown##"
1012 assert disk_template not in constants.DISK_TEMPLATES
1014 self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
1015 "inst26831.example.com", "node30113.example.com", [], [],
1016 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1017 self.GetDiskParams())
1019 def testDiskless(self):
1020 gdt = cmdlib._GenerateDiskTemplate
1022 result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1023 "node30113.example.com", [], [],
1024 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1025 self.GetDiskParams())
1026 self.assertEqual(result, [])
1028 def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1029 file_storage_dir=NotImplemented,
1030 file_driver=NotImplemented,
1031 req_file_storage=NotImplemented,
1032 req_shr_file_storage=NotImplemented):
1033 gdt = cmdlib._GenerateDiskTemplate
1035 map(lambda params: utils.ForceDictType(params,
1036 constants.IDISK_PARAMS_TYPES),
1039 # Check if non-empty list of secondaries is rejected
1040 self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1041 template, "inst25088.example.com",
1042 "node185.example.com", ["node323.example.com"], [],
1043 NotImplemented, NotImplemented, base_index,
1044 self.lu.LogInfo, self.GetDiskParams(),
1045 _req_file_storage=req_file_storage,
1046 _req_shr_file_storage=req_shr_file_storage)
1048 result = gdt(self.lu, template, "inst21662.example.com",
1049 "node21741.example.com", [],
1050 disk_info, file_storage_dir, file_driver, base_index,
1051 self.lu.LogInfo, self.GetDiskParams(),
1052 _req_file_storage=req_file_storage,
1053 _req_shr_file_storage=req_shr_file_storage)
1055 for (idx, disk) in enumerate(result):
1056 self.assertTrue(isinstance(disk, objects.Disk))
1057 self.assertEqual(disk.dev_type, exp_dev_type)
1058 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1059 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1060 self.assertTrue(disk.children is None)
1062 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1063 cmdlib._UpdateIvNames(base_index, result)
1064 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1068 def _CheckIvNames(self, disks, base_index, end_index):
1069 self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1070 ["disk/%s" % i for i in range(base_index, end_index)])
1072 def testPlain(self):
1074 constants.IDISK_SIZE: 1024,
1075 constants.IDISK_MODE: constants.DISK_RDWR,
1077 constants.IDISK_SIZE: 4096,
1078 constants.IDISK_VG: "othervg",
1079 constants.IDISK_MODE: constants.DISK_RDWR,
1082 result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1085 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1086 ("testvg", "ec0-uq0.disk3"),
1087 ("othervg", "ec0-uq1.disk4"),
1091 def _AllowFileStorage():
1095 def _ForbidFileStorage():
1096 raise errors.OpPrereqError("Disallowed in test")
1099 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1100 constants.DT_FILE, [], 0, NotImplemented,
1101 req_file_storage=self._ForbidFileStorage)
1102 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1103 constants.DT_SHARED_FILE, [], 0, NotImplemented,
1104 req_shr_file_storage=self._ForbidFileStorage)
1106 for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1108 constants.IDISK_SIZE: 80 * 1024,
1109 constants.IDISK_MODE: constants.DISK_RDONLY,
1111 constants.IDISK_SIZE: 4096,
1112 constants.IDISK_MODE: constants.DISK_RDWR,
1114 constants.IDISK_SIZE: 6 * 1024,
1115 constants.IDISK_MODE: constants.DISK_RDWR,
1118 result = self._TestTrivialDisk(disk_template, disk_info, 2,
1119 constants.LD_FILE, file_storage_dir="/tmp",
1120 file_driver=constants.FD_BLKTAP,
1121 req_file_storage=self._AllowFileStorage,
1122 req_shr_file_storage=self._AllowFileStorage)
1124 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1125 (constants.FD_BLKTAP, "/tmp/disk2"),
1126 (constants.FD_BLKTAP, "/tmp/disk3"),
1127 (constants.FD_BLKTAP, "/tmp/disk4"),
1130 def testBlock(self):
1132 constants.IDISK_SIZE: 8 * 1024,
1133 constants.IDISK_MODE: constants.DISK_RDWR,
1134 constants.IDISK_ADOPT: "/tmp/some/block/dev",
1137 result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1138 constants.LD_BLOCKDEV)
1140 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1141 (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1146 constants.IDISK_SIZE: 8 * 1024,
1147 constants.IDISK_MODE: constants.DISK_RDONLY,
1149 constants.IDISK_SIZE: 100 * 1024,
1150 constants.IDISK_MODE: constants.DISK_RDWR,
1153 result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1156 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1157 ("rbd", "ec0-uq0.rbd.disk0"),
1158 ("rbd", "ec0-uq1.rbd.disk1"),
1161 def testDrbd8(self):
1162 gdt = cmdlib._GenerateDiskTemplate
1163 drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1164 drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1167 constants.IDISK_SIZE: 1024,
1168 constants.IDISK_MODE: constants.DISK_RDWR,
1170 constants.IDISK_SIZE: 100 * 1024,
1171 constants.IDISK_MODE: constants.DISK_RDONLY,
1172 constants.IDISK_METAVG: "metavg",
1174 constants.IDISK_SIZE: 4096,
1175 constants.IDISK_MODE: constants.DISK_RDWR,
1176 constants.IDISK_VG: "vgxyz",
1180 exp_logical_ids = [[
1181 (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1182 (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1184 (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1185 ("metavg", "ec0-uq1.disk1_meta"),
1187 ("vgxyz", "ec0-uq2.disk2_data"),
1188 (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1191 assert len(exp_logical_ids) == len(disk_info)
1193 map(lambda params: utils.ForceDictType(params,
1194 constants.IDISK_PARAMS_TYPES),
1197 # Check if empty list of secondaries is rejected
1198 self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1199 "inst827.example.com", "node1334.example.com", [],
1200 disk_info, NotImplemented, NotImplemented, 0,
1201 self.lu.LogInfo, self.GetDiskParams())
1203 result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1204 "node1334.example.com", ["node12272.example.com"],
1205 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1206 self.GetDiskParams())
1208 for (idx, disk) in enumerate(result):
1209 self.assertTrue(isinstance(disk, objects.Disk))
1210 self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1211 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1212 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1214 for child in disk.children:
1215 self.assertTrue(isinstance(disk, objects.Disk))
1216 self.assertEqual(child.dev_type, constants.LD_LV)
1217 self.assertTrue(child.children is None)
1219 self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1220 exp_logical_ids[idx])
1222 self.assertEqual(len(disk.children), 2)
1223 self.assertEqual(disk.children[0].size, disk.size)
1224 self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1226 self._CheckIvNames(result, 0, len(disk_info))
1227 cmdlib._UpdateIvNames(0, result)
1228 self._CheckIvNames(result, 0, len(disk_info))
1230 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1231 ("node1334.example.com", "node12272.example.com",
1232 constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1233 ("node1334.example.com", "node12272.example.com",
1234 constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1235 ("node1334.example.com", "node12272.example.com",
1236 constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1240 if __name__ == "__main__":
1241 testutils.GanetiTestProgram()