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"""
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.hypervisor import hv_xen
51 class TestCertVerification(testutils.GanetiTestCase):
53 testutils.GanetiTestCase.setUp(self)
55 self.tmpdir = tempfile.mkdtemp()
58 shutil.rmtree(self.tmpdir)
60 def testVerifyCertificate(self):
61 cmdlib._VerifyCertificate(self._TestDataFilename("cert1.pem"))
63 nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
65 (errcode, msg) = cmdlib._VerifyCertificate(nonexist_filename)
66 self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
68 # Try to load non-certificate file
69 invalid_cert = self._TestDataFilename("bdev-net.txt")
70 (errcode, msg) = cmdlib._VerifyCertificate(invalid_cert)
71 self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
74 class TestOpcodeParams(testutils.GanetiTestCase):
75 def testParamsStructures(self):
76 for op in sorted(mcpu.Processor.DISPATCH_TABLE):
77 lu = mcpu.Processor.DISPATCH_TABLE[op]
79 self.failIf(hasattr(lu, "_OP_REQP"),
80 msg=("LU '%s' has old-style _OP_REQP" % lu_name))
81 self.failIf(hasattr(lu, "_OP_DEFS"),
82 msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
83 self.failIf(hasattr(lu, "_OP_PARAMS"),
84 msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
87 class TestIAllocatorChecks(testutils.GanetiTestCase):
88 def testFunction(self):
90 def __init__(self, opcode):
91 self.cfg = mocks.FakeConfig()
94 class OpTest(opcodes.OpCode):
96 ("iallocator", None, ht.NoType, None),
97 ("node", None, ht.NoType, None),
100 default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
101 other_iallocator = default_iallocator + "_not"
106 c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
108 # Neither node nor iallocator given
112 self.assertEqual(lu.op.iallocator, default_iallocator)
113 self.assertEqual(lu.op.node, None)
115 # Both, iallocator and node given
116 op.iallocator = "test"
118 self.assertRaises(errors.OpPrereqError, c_i)
120 # Only iallocator given
121 op.iallocator = other_iallocator
124 self.assertEqual(lu.op.iallocator, other_iallocator)
125 self.assertEqual(lu.op.node, None)
131 self.assertEqual(lu.op.iallocator, None)
132 self.assertEqual(lu.op.node, "node")
134 # No node, iallocator or default iallocator
137 lu.cfg.GetDefaultIAllocator = lambda: None
138 self.assertRaises(errors.OpPrereqError, c_i)
141 class TestLUTestJqueue(unittest.TestCase):
143 self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
144 (luxi.WFJC_TIMEOUT * 0.75),
145 msg=("Client timeout too high, might not notice bugs"
146 " in WaitForJobChange"))
149 class TestLUQuery(unittest.TestCase):
151 self.assertEqual(sorted(cmdlib._QUERY_IMPL.keys()),
152 sorted(constants.QR_VIA_OP))
154 assert constants.QR_NODE in constants.QR_VIA_OP
155 assert constants.QR_INSTANCE in constants.QR_VIA_OP
157 for i in constants.QR_VIA_OP:
158 self.assert_(cmdlib._GetQueryImplementation(i))
160 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation, "")
161 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation,
165 class TestLUGroupAssignNodes(unittest.TestCase):
167 def testCheckAssignmentForSplitInstances(self):
168 node_data = dict((name, objects.Node(name=name, group=group))
169 for (name, group) in [("n1a", "g1"), ("n1b", "g1"),
170 ("n2a", "g2"), ("n2b", "g2"),
171 ("n3a", "g3"), ("n3b", "g3"),
175 def Instance(name, pnode, snode):
178 disk_template = constants.DT_DISKLESS
180 disks = [objects.Disk(dev_type=constants.LD_DRBD8,
181 logical_id=[pnode, snode, 1, 17, 17])]
182 disk_template = constants.DT_DRBD8
184 return objects.Instance(name=name, primary_node=pnode, disks=disks,
185 disk_template=disk_template)
187 instance_data = dict((name, Instance(name, pnode, snode))
188 for name, pnode, snode in [("inst1a", "n1a", "n1b"),
189 ("inst1b", "n1b", "n1a"),
190 ("inst2a", "n2a", "n2b"),
191 ("inst3a", "n3a", None),
192 ("inst3b", "n3b", "n1b"),
193 ("inst3c", "n3b", "n2b"),
196 # Test first with the existing state.
198 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
202 self.assertEqual([], new)
203 self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
205 # And now some changes.
207 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
212 self.assertEqual(set(["inst1a", "inst1b"]), set(new))
213 self.assertEqual(set(["inst3c"]), set(prev))
216 class TestClusterVerifySsh(unittest.TestCase):
217 def testMultipleGroups(self):
218 fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
220 objects.Node(name="node20", group="my", offline=False),
221 objects.Node(name="node21", group="my", offline=False),
222 objects.Node(name="node22", group="my", offline=False),
223 objects.Node(name="node23", group="my", offline=False),
224 objects.Node(name="node24", group="my", offline=False),
225 objects.Node(name="node25", group="my", offline=False),
226 objects.Node(name="node26", group="my", offline=True),
229 objects.Node(name="node1", group="g1", offline=True),
230 objects.Node(name="node2", group="g1", offline=False),
231 objects.Node(name="node3", group="g1", offline=False),
232 objects.Node(name="node4", group="g1", offline=True),
233 objects.Node(name="node5", group="g1", offline=False),
234 objects.Node(name="node10", group="xyz", offline=False),
235 objects.Node(name="node11", group="xyz", offline=False),
236 objects.Node(name="node40", group="alloff", offline=True),
237 objects.Node(name="node41", group="alloff", offline=True),
238 objects.Node(name="node50", group="aaa", offline=False),
240 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
242 (online, perhost) = fn(mygroupnodes, "my", nodes)
243 self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
244 self.assertEqual(set(perhost.keys()), set(online))
246 self.assertEqual(perhost, {
247 "node20": ["node10", "node2", "node50"],
248 "node21": ["node11", "node3", "node50"],
249 "node22": ["node10", "node5", "node50"],
250 "node23": ["node11", "node2", "node50"],
251 "node24": ["node10", "node3", "node50"],
252 "node25": ["node11", "node5", "node50"],
255 def testSingleGroup(self):
256 fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
258 objects.Node(name="node1", group="default", offline=True),
259 objects.Node(name="node2", group="default", offline=False),
260 objects.Node(name="node3", group="default", offline=False),
261 objects.Node(name="node4", group="default", offline=True),
263 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
265 (online, perhost) = fn(nodes, "default", nodes)
266 self.assertEqual(online, ["node2", "node3"])
267 self.assertEqual(set(perhost.keys()), set(online))
269 self.assertEqual(perhost, {
275 class TestClusterVerifyFiles(unittest.TestCase):
277 def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
278 assert ((ecode == constants.CV_ENODEFILECHECK and
279 ht.TNonEmptyString(item)) or
280 (ecode == constants.CV_ECLUSTERFILECHECK and
287 errors.append((item, msg))
289 _VerifyFiles = cmdlib.LUClusterVerifyGroup._VerifyFiles
293 master_name = "master.example.com"
295 objects.Node(name=master_name, offline=False, vm_capable=True),
296 objects.Node(name="node2.example.com", offline=False, vm_capable=True),
297 objects.Node(name="node3.example.com", master_candidate=True,
299 objects.Node(name="node4.example.com", offline=False, vm_capable=True),
300 objects.Node(name="nodata.example.com", offline=False, vm_capable=True),
301 objects.Node(name="offline.example.com", offline=True),
303 cluster = objects.Cluster(modify_etc_hosts=True,
304 enabled_hypervisors=[constants.HT_XEN_HVM])
306 constants.CLUSTER_DOMAIN_SECRET_FILE,
307 constants.RAPI_CERT_FILE,
308 constants.RAPI_USERS_FILE,
311 constants.RAPI_USERS_FILE,
312 hv_xen.XL_CONFIG_FILE,
313 constants.VNC_PASSWORD_FILE,
316 constants.CLUSTER_CONF_FILE,
319 hv_xen.XEND_CONFIG_FILE,
320 hv_xen.XL_CONFIG_FILE,
321 constants.VNC_PASSWORD_FILE,
324 master_name: rpc.RpcResult(data=(True, {
325 constants.NV_FILELIST: {
326 constants.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
327 constants.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
328 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
329 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
330 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
332 "node2.example.com": rpc.RpcResult(data=(True, {
333 constants.NV_FILELIST: {
334 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
335 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
338 "node3.example.com": rpc.RpcResult(data=(True, {
339 constants.NV_FILELIST: {
340 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
341 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
344 "node4.example.com": rpc.RpcResult(data=(True, {
345 constants.NV_FILELIST: {
346 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
347 constants.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
348 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
349 constants.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
350 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
353 "nodata.example.com": rpc.RpcResult(data=(True, {})),
354 "offline.example.com": rpc.RpcResult(offline=True),
356 assert set(nvinfo.keys()) == set(map(operator.attrgetter("name"), nodeinfo))
358 self._VerifyFiles(compat.partial(self._FakeErrorIf, errors), nodeinfo,
360 (files_all, files_opt, files_mc, files_vm))
361 self.assertEqual(sorted(errors), sorted([
362 (None, ("File %s found with 2 different checksums (variant 1 on"
363 " node2.example.com, node3.example.com, node4.example.com;"
364 " variant 2 on master.example.com)" % constants.RAPI_CERT_FILE)),
365 (None, ("File %s is missing from node(s) node2.example.com" %
366 constants.CLUSTER_DOMAIN_SECRET_FILE)),
367 (None, ("File %s should not exist on node(s) node4.example.com" %
368 constants.CLUSTER_CONF_FILE)),
369 (None, ("File %s is missing from node(s) node4.example.com" %
370 hv_xen.XEND_CONFIG_FILE)),
371 (None, ("File %s is missing from node(s) node3.example.com" %
372 constants.CLUSTER_CONF_FILE)),
373 (None, ("File %s found with 2 different checksums (variant 1 on"
374 " master.example.com; variant 2 on node4.example.com)" %
375 constants.CLUSTER_CONF_FILE)),
376 (None, ("File %s is optional, but it must exist on all or no nodes (not"
377 " found on master.example.com, node2.example.com,"
378 " node3.example.com)" % constants.RAPI_USERS_FILE)),
379 (None, ("File %s is optional, but it must exist on all or no nodes (not"
380 " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
381 ("nodata.example.com", "Node did not return file checksum data"),
386 def __init__(self, cfg=NotImplemented, proc=NotImplemented):
387 self.warning_log = []
392 def LogWarning(self, text, *args):
393 self.warning_log.append((text, args))
395 def LogInfo(self, text, *args):
396 self.info_log.append((text, args))
399 class TestLoadNodeEvacResult(unittest.TestCase):
400 def testSuccess(self):
402 ("inst20153.example.com", "grp2", ["nodeA4509", "nodeB2912"]),
404 for early_release in [False, True]:
405 for use_nodes in [False, True]:
407 [opcodes.OpInstanceReplaceDisks().__getstate__()],
408 [opcodes.OpInstanceMigrate().__getstate__()],
411 alloc_result = (moved, [], jobs)
412 assert cmdlib.IAllocator._NEVAC_RESULT(alloc_result)
415 result = cmdlib._LoadNodeEvacResult(lu, alloc_result,
416 early_release, use_nodes)
419 (_, (info_args, )) = lu.info_log.pop(0)
420 for (instname, instgroup, instnodes) in moved:
421 self.assertTrue(instname in info_args)
424 self.assertTrue(i in info_args)
426 self.assertTrue(instgroup in info_args)
428 self.assertFalse(lu.info_log)
429 self.assertFalse(lu.warning_log)
431 for op in itertools.chain(*result):
432 if hasattr(op.__class__, "early_release"):
433 self.assertEqual(op.early_release, early_release)
435 self.assertFalse(hasattr(op, "early_release"))
437 def testFailed(self):
438 alloc_result = ([], [
439 ("inst5191.example.com", "errormsg21178"),
441 assert cmdlib.IAllocator._NEVAC_RESULT(alloc_result)
444 self.assertRaises(errors.OpExecError, cmdlib._LoadNodeEvacResult,
445 lu, alloc_result, False, False)
446 self.assertFalse(lu.info_log)
447 (_, (args, )) = lu.warning_log.pop(0)
448 self.assertTrue("inst5191.example.com" in args)
449 self.assertTrue("errormsg21178" in args)
450 self.assertFalse(lu.warning_log)
453 class TestUpdateAndVerifySubDict(unittest.TestCase):
456 "a": constants.VTYPE_INT,
457 "b": constants.VTYPE_STRING,
458 "c": constants.VTYPE_BOOL,
459 "d": constants.VTYPE_STRING,
506 verified = cmdlib._UpdateAndVerifySubDict(old_test, test, self.type_check)
507 self.assertEqual(verified, mv)
523 self.assertRaises(errors.TypeEnforcementError,
524 cmdlib._UpdateAndVerifySubDict, {}, test, self.type_check)
527 class TestHvStateHelper(unittest.TestCase):
528 def testWithoutOpData(self):
529 self.assertEqual(cmdlib._MergeAndVerifyHvState(None, NotImplemented), None)
531 def testWithoutOldData(self):
533 constants.HT_XEN_PVM: {
534 constants.HVST_MEMORY_TOTAL: 4096,
537 self.assertEqual(cmdlib._MergeAndVerifyHvState(new, None), new)
539 def testWithWrongHv(self):
542 constants.HVST_MEMORY_TOTAL: 4096,
545 self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyHvState, new,
548 class TestDiskStateHelper(unittest.TestCase):
549 def testWithoutOpData(self):
550 self.assertEqual(cmdlib._MergeAndVerifyDiskState(None, NotImplemented),
553 def testWithoutOldData(self):
557 constants.DS_DISK_RESERVED: 1024,
561 self.assertEqual(cmdlib._MergeAndVerifyDiskState(new, None), new)
563 def testWithWrongStorageType(self):
567 constants.DS_DISK_RESERVED: 1024,
571 self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyDiskState,
575 class TestComputeMinMaxSpec(unittest.TestCase):
578 constants.ISPECS_MAX: {
579 constants.ISPEC_MEM_SIZE: 512,
580 constants.ISPEC_DISK_SIZE: 1024,
582 constants.ISPECS_MIN: {
583 constants.ISPEC_MEM_SIZE: 128,
584 constants.ISPEC_DISK_COUNT: 1,
588 def testNoneValue(self):
589 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE,
590 self.ipolicy, None) is None)
592 def testAutoValue(self):
593 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE,
595 constants.VALUE_AUTO) is None)
597 def testNotDefined(self):
598 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT,
599 self.ipolicy, 3) is None)
601 def testNoMinDefined(self):
602 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE,
603 self.ipolicy, 128) is None)
605 def testNoMaxDefined(self):
606 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT,
607 self.ipolicy, 16) is None)
609 def testOutOfRange(self):
610 for (name, val) in ((constants.ISPEC_MEM_SIZE, 64),
611 (constants.ISPEC_MEM_SIZE, 768),
612 (constants.ISPEC_DISK_SIZE, 4096),
613 (constants.ISPEC_DISK_COUNT, 0)):
614 min_v = self.ipolicy[constants.ISPECS_MIN].get(name, val)
615 max_v = self.ipolicy[constants.ISPECS_MAX].get(name, val)
616 self.assertEqual(cmdlib._ComputeMinMaxSpec(name, self.ipolicy, val),
617 "%s value %s is not in range [%s, %s]" %
618 (name, val,min_v, max_v))
621 for (name, val) in ((constants.ISPEC_MEM_SIZE, 256),
622 (constants.ISPEC_MEM_SIZE, 128),
623 (constants.ISPEC_MEM_SIZE, 512),
624 (constants.ISPEC_DISK_SIZE, 1024),
625 (constants.ISPEC_DISK_SIZE, 0),
626 (constants.ISPEC_DISK_COUNT, 1),
627 (constants.ISPEC_DISK_COUNT, 5)):
628 self.assertTrue(cmdlib._ComputeMinMaxSpec(name, self.ipolicy, val)
632 def _ValidateComputeMinMaxSpec(name, *_):
633 assert name in constants.ISPECS_PARAMETERS
638 def __init__(self, spec):
641 def ComputeMinMaxSpec(self, *args):
642 return self.spec.pop(0)
645 class TestComputeIPolicySpecViolation(unittest.TestCase):
647 compute_fn = _ValidateComputeMinMaxSpec
648 ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
649 [1024], 1, _compute_fn=compute_fn)
650 self.assertEqual(ret, [])
652 def testInvalidArguments(self):
653 self.assertRaises(AssertionError, cmdlib._ComputeIPolicySpecViolation,
654 NotImplemented, 1024, 1, 1, 1, [], 1)
656 def testInvalidSpec(self):
657 spec = _SpecWrapper([None, False, "foo", None, "bar", None])
658 compute_fn = spec.ComputeMinMaxSpec
659 ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
660 [1024], 1, _compute_fn=compute_fn)
661 self.assertEqual(ret, ["foo", "bar"])
662 self.assertFalse(spec.spec)
665 class _StubComputeIPolicySpecViolation:
666 def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
668 self.mem_size = mem_size
669 self.cpu_count = cpu_count
670 self.disk_count = disk_count
671 self.nic_count = nic_count
672 self.disk_sizes = disk_sizes
673 self.spindle_use = spindle_use
675 def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
677 assert self.mem_size == mem_size
678 assert self.cpu_count == cpu_count
679 assert self.disk_count == disk_count
680 assert self.nic_count == nic_count
681 assert self.disk_sizes == disk_sizes
682 assert self.spindle_use == spindle_use
687 class TestComputeIPolicyInstanceViolation(unittest.TestCase):
690 constants.BE_MAXMEM: 2048,
691 constants.BE_VCPUS: 2,
692 constants.BE_SPINDLE_USE: 4,
694 disks = [objects.Disk(size=512)]
695 instance = objects.Instance(beparams=beparams, disks=disks, nics=[])
696 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 4)
697 ret = cmdlib._ComputeIPolicyInstanceViolation(NotImplemented, instance,
699 self.assertEqual(ret, [])
702 class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
705 constants.ISPEC_MEM_SIZE: 2048,
706 constants.ISPEC_CPU_COUNT: 2,
707 constants.ISPEC_DISK_COUNT: 1,
708 constants.ISPEC_DISK_SIZE: [512],
709 constants.ISPEC_NIC_COUNT: 0,
710 constants.ISPEC_SPINDLE_USE: 1,
712 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 1)
713 ret = cmdlib._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
715 self.assertEqual(ret, [])
719 def __init__(self, return_value=None):
721 self.return_value = return_value
723 def __call__(self, *args):
725 return self.return_value
728 class TestComputeIPolicyNodeViolation(unittest.TestCase):
730 self.recorder = _CallRecorder(return_value=[])
732 def testSameGroup(self):
733 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
735 _compute_fn=self.recorder)
736 self.assertFalse(self.recorder.called)
737 self.assertEqual(ret, [])
739 def testDifferentGroup(self):
740 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
742 _compute_fn=self.recorder)
743 self.assertTrue(self.recorder.called)
744 self.assertEqual(ret, [])
747 class _FakeConfigForTargetNodeIPolicy:
748 def __init__(self, node_info=NotImplemented):
749 self._node_info = node_info
751 def GetNodeInfo(self, _):
752 return self._node_info
755 class TestCheckTargetNodeIPolicy(unittest.TestCase):
757 self.instance = objects.Instance(primary_node="blubb")
758 self.target_node = objects.Node(group="bar")
759 node_info = objects.Node(group="foo")
760 fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
761 self.lu = _FakeLU(cfg=fake_cfg)
763 def testNoViolation(self):
764 compute_recoder = _CallRecorder(return_value=[])
765 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
767 _compute_fn=compute_recoder)
768 self.assertTrue(compute_recoder.called)
769 self.assertEqual(self.lu.warning_log, [])
771 def testNoIgnore(self):
772 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
773 self.assertRaises(errors.OpPrereqError, cmdlib._CheckTargetNodeIPolicy,
774 self.lu, NotImplemented, self.instance, self.target_node,
775 _compute_fn=compute_recoder)
776 self.assertTrue(compute_recoder.called)
777 self.assertEqual(self.lu.warning_log, [])
779 def testIgnoreViolation(self):
780 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
781 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
782 self.target_node, ignore=True,
783 _compute_fn=compute_recoder)
784 self.assertTrue(compute_recoder.called)
785 msg = ("Instance does not meet target node group's (bar) instance policy:"
786 " mem_size not in range")
787 self.assertEqual(self.lu.warning_log, [(msg, ())])
790 class TestApplyContainerMods(unittest.TestCase):
791 def testEmptyContainer(self):
794 cmdlib.ApplyContainerMods("test", container, chgdesc, [], None, None, None)
795 self.assertEqual(container, [])
796 self.assertEqual(chgdesc, [])
801 mods = cmdlib.PrepareContainerMods([
802 (constants.DDM_ADD, -1, "Hello"),
803 (constants.DDM_ADD, -1, "World"),
804 (constants.DDM_ADD, 0, "Start"),
805 (constants.DDM_ADD, -1, "End"),
807 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
809 self.assertEqual(container, ["Start", "Hello", "World", "End"])
810 self.assertEqual(chgdesc, [])
812 mods = cmdlib.PrepareContainerMods([
813 (constants.DDM_ADD, 0, "zero"),
814 (constants.DDM_ADD, 3, "Added"),
815 (constants.DDM_ADD, 5, "four"),
816 (constants.DDM_ADD, 7, "xyz"),
818 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
820 self.assertEqual(container,
821 ["zero", "Start", "Hello", "Added", "World", "four",
823 self.assertEqual(chgdesc, [])
825 for idx in [-2, len(container) + 1]:
826 mods = cmdlib.PrepareContainerMods([
827 (constants.DDM_ADD, idx, "error"),
829 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
830 "test", container, None, mods, None, None, None)
832 def testRemoveError(self):
833 for idx in [0, 1, 2, 100, -1, -4]:
834 mods = cmdlib.PrepareContainerMods([
835 (constants.DDM_REMOVE, idx, None),
837 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
838 "test", [], None, mods, None, None, None)
840 mods = cmdlib.PrepareContainerMods([
841 (constants.DDM_REMOVE, 0, object()),
843 self.assertRaises(AssertionError, cmdlib.ApplyContainerMods,
844 "test", [""], None, mods, None, None, None)
846 def testAddError(self):
847 for idx in range(-100, -1) + [100]:
848 mods = cmdlib.PrepareContainerMods([
849 (constants.DDM_ADD, idx, None),
851 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
852 "test", [], None, mods, None, None, None)
854 def testRemove(self):
855 container = ["item 1", "item 2"]
856 mods = cmdlib.PrepareContainerMods([
857 (constants.DDM_ADD, -1, "aaa"),
858 (constants.DDM_REMOVE, -1, None),
859 (constants.DDM_ADD, -1, "bbb"),
862 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
864 self.assertEqual(container, ["item 1", "item 2", "bbb"])
865 self.assertEqual(chgdesc, [
866 ("test/2", "remove"),
869 def testModify(self):
870 container = ["item 1", "item 2"]
871 mods = cmdlib.PrepareContainerMods([
872 (constants.DDM_MODIFY, -1, "a"),
873 (constants.DDM_MODIFY, 0, "b"),
874 (constants.DDM_MODIFY, 1, "c"),
877 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
879 self.assertEqual(container, ["item 1", "item 2"])
880 self.assertEqual(chgdesc, [])
882 for idx in [-2, len(container) + 1]:
883 mods = cmdlib.PrepareContainerMods([
884 (constants.DDM_MODIFY, idx, "error"),
886 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
887 "test", container, None, mods, None, None, None)
894 def _CreateTestFn(idx, params, private):
895 private.data = ("add", idx, params)
896 return ((100 * idx, params), [
897 ("test/%s" % idx, hex(idx)),
901 def _ModifyTestFn(idx, item, params, private):
902 private.data = ("modify", idx, params)
904 ("test/%s" % idx, "modify %s" % params),
908 def _RemoveTestFn(idx, item, private):
909 private.data = ("remove", idx, item)
911 def testAddWithCreateFunction(self):
914 mods = cmdlib.PrepareContainerMods([
915 (constants.DDM_ADD, -1, "Hello"),
916 (constants.DDM_ADD, -1, "World"),
917 (constants.DDM_ADD, 0, "Start"),
918 (constants.DDM_ADD, -1, "End"),
919 (constants.DDM_REMOVE, 2, None),
920 (constants.DDM_MODIFY, -1, "foobar"),
921 (constants.DDM_REMOVE, 2, None),
922 (constants.DDM_ADD, 1, "More"),
923 ], self._PrivateData)
924 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
925 self._CreateTestFn, self._ModifyTestFn, self._RemoveTestFn)
926 self.assertEqual(container, [
931 self.assertEqual(chgdesc, [
936 ("test/2", "remove"),
937 ("test/2", "modify foobar"),
938 ("test/2", "remove"),
941 self.assertTrue(compat.all(op == private.data[0]
942 for (op, _, _, private) in mods))
943 self.assertEqual([private.data for (op, _, _, private) in mods], [
948 ("remove", 2, (100, "World")),
949 ("modify", 2, "foobar"),
950 ("remove", 2, (300, "End")),
955 class _FakeConfigForGenDiskTemplate:
957 self._unique_id = itertools.count()
958 self._drbd_minor = itertools.count(20)
959 self._port = itertools.count(constants.FIRST_DRBD_PORT)
960 self._secret = itertools.count()
965 def GenerateUniqueID(self, ec_id):
966 return "ec%s-uq%s" % (ec_id, self._unique_id.next())
968 def AllocateDRBDMinor(self, nodes, instance):
969 return [self._drbd_minor.next()
972 def AllocatePort(self):
973 return self._port.next()
975 def GenerateDRBDSecret(self, ec_id):
976 return "ec%s-secret%s" % (ec_id, self._secret.next())
978 def GetInstanceInfo(self, _):
982 class _FakeProcForGenDiskTemplate:
987 class TestGenerateDiskTemplate(unittest.TestCase):
989 nodegroup = objects.NodeGroup(name="ng")
990 nodegroup.UpgradeConfig()
992 cfg = _FakeConfigForGenDiskTemplate()
993 proc = _FakeProcForGenDiskTemplate()
995 self.lu = _FakeLU(cfg=cfg, proc=proc)
996 self.nodegroup = nodegroup
1000 return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1002 def testWrongDiskTemplate(self):
1003 gdt = cmdlib._GenerateDiskTemplate
1004 disk_template = "##unknown##"
1006 assert disk_template not in constants.DISK_TEMPLATES
1008 self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
1009 "inst26831.example.com", "node30113.example.com", [], [],
1010 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1011 self.GetDiskParams())
1013 def testDiskless(self):
1014 gdt = cmdlib._GenerateDiskTemplate
1016 result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1017 "node30113.example.com", [], [],
1018 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1019 self.GetDiskParams())
1020 self.assertEqual(result, [])
1022 def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1023 file_storage_dir=NotImplemented,
1024 file_driver=NotImplemented,
1025 req_file_storage=NotImplemented,
1026 req_shr_file_storage=NotImplemented):
1027 gdt = cmdlib._GenerateDiskTemplate
1029 map(lambda params: utils.ForceDictType(params,
1030 constants.IDISK_PARAMS_TYPES),
1033 # Check if non-empty list of secondaries is rejected
1034 self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1035 template, "inst25088.example.com",
1036 "node185.example.com", ["node323.example.com"], [],
1037 NotImplemented, NotImplemented, base_index,
1038 self.lu.LogInfo, self.GetDiskParams(),
1039 _req_file_storage=req_file_storage,
1040 _req_shr_file_storage=req_shr_file_storage)
1042 result = gdt(self.lu, template, "inst21662.example.com",
1043 "node21741.example.com", [],
1044 disk_info, file_storage_dir, file_driver, 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 for (idx, disk) in enumerate(result):
1050 self.assertTrue(isinstance(disk, objects.Disk))
1051 self.assertEqual(disk.dev_type, exp_dev_type)
1052 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1053 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1054 self.assertTrue(disk.children is None)
1056 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1057 cmdlib._UpdateIvNames(base_index, result)
1058 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1062 def _CheckIvNames(self, disks, base_index, end_index):
1063 self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1064 ["disk/%s" % i for i in range(base_index, end_index)])
1066 def testPlain(self):
1068 constants.IDISK_SIZE: 1024,
1069 constants.IDISK_MODE: constants.DISK_RDWR,
1071 constants.IDISK_SIZE: 4096,
1072 constants.IDISK_VG: "othervg",
1073 constants.IDISK_MODE: constants.DISK_RDWR,
1076 result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1079 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1080 ("testvg", "ec0-uq0.disk3"),
1081 ("othervg", "ec0-uq1.disk4"),
1085 def _AllowFileStorage():
1089 def _ForbidFileStorage():
1090 raise errors.OpPrereqError("Disallowed in test")
1093 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1094 constants.DT_FILE, [], 0, NotImplemented,
1095 req_file_storage=self._ForbidFileStorage)
1096 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1097 constants.DT_SHARED_FILE, [], 0, NotImplemented,
1098 req_shr_file_storage=self._ForbidFileStorage)
1100 for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1102 constants.IDISK_SIZE: 80 * 1024,
1103 constants.IDISK_MODE: constants.DISK_RDONLY,
1105 constants.IDISK_SIZE: 4096,
1106 constants.IDISK_MODE: constants.DISK_RDWR,
1108 constants.IDISK_SIZE: 6 * 1024,
1109 constants.IDISK_MODE: constants.DISK_RDWR,
1112 result = self._TestTrivialDisk(disk_template, disk_info, 2,
1113 constants.LD_FILE, file_storage_dir="/tmp",
1114 file_driver=constants.FD_BLKTAP,
1115 req_file_storage=self._AllowFileStorage,
1116 req_shr_file_storage=self._AllowFileStorage)
1118 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1119 (constants.FD_BLKTAP, "/tmp/disk2"),
1120 (constants.FD_BLKTAP, "/tmp/disk3"),
1121 (constants.FD_BLKTAP, "/tmp/disk4"),
1124 def testBlock(self):
1126 constants.IDISK_SIZE: 8 * 1024,
1127 constants.IDISK_MODE: constants.DISK_RDWR,
1128 constants.IDISK_ADOPT: "/tmp/some/block/dev",
1131 result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1132 constants.LD_BLOCKDEV)
1134 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1135 (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1140 constants.IDISK_SIZE: 8 * 1024,
1141 constants.IDISK_MODE: constants.DISK_RDONLY,
1143 constants.IDISK_SIZE: 100 * 1024,
1144 constants.IDISK_MODE: constants.DISK_RDWR,
1147 result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1150 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1151 ("rbd", "ec0-uq0.rbd.disk0"),
1152 ("rbd", "ec0-uq1.rbd.disk1"),
1155 def testDrbd8(self):
1156 gdt = cmdlib._GenerateDiskTemplate
1157 drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1158 drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1161 constants.IDISK_SIZE: 1024,
1162 constants.IDISK_MODE: constants.DISK_RDWR,
1164 constants.IDISK_SIZE: 100 * 1024,
1165 constants.IDISK_MODE: constants.DISK_RDONLY,
1166 constants.IDISK_METAVG: "metavg",
1168 constants.IDISK_SIZE: 4096,
1169 constants.IDISK_MODE: constants.DISK_RDWR,
1170 constants.IDISK_VG: "vgxyz",
1174 exp_logical_ids = [[
1175 (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1176 (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1178 (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1179 ("metavg", "ec0-uq1.disk1_meta"),
1181 ("vgxyz", "ec0-uq2.disk2_data"),
1182 (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1185 assert len(exp_logical_ids) == len(disk_info)
1187 map(lambda params: utils.ForceDictType(params,
1188 constants.IDISK_PARAMS_TYPES),
1191 # Check if empty list of secondaries is rejected
1192 self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1193 "inst827.example.com", "node1334.example.com", [],
1194 disk_info, NotImplemented, NotImplemented, 0,
1195 self.lu.LogInfo, self.GetDiskParams())
1197 result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1198 "node1334.example.com", ["node12272.example.com"],
1199 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1200 self.GetDiskParams())
1202 for (idx, disk) in enumerate(result):
1203 self.assertTrue(isinstance(disk, objects.Disk))
1204 self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1205 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1206 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1208 for child in disk.children:
1209 self.assertTrue(isinstance(disk, objects.Disk))
1210 self.assertEqual(child.dev_type, constants.LD_LV)
1211 self.assertTrue(child.children is None)
1213 self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1214 exp_logical_ids[idx])
1216 self.assertEqual(len(disk.children), 2)
1217 self.assertEqual(disk.children[0].size, disk.size)
1218 self.assertEqual(disk.children[1].size, cmdlib.DRBD_META_SIZE)
1220 self._CheckIvNames(result, 0, len(disk_info))
1221 cmdlib._UpdateIvNames(0, result)
1222 self._CheckIvNames(result, 0, len(disk_info))
1224 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1225 ("node1334.example.com", "node12272.example.com",
1226 constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1227 ("node1334.example.com", "node12272.example.com",
1228 constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1229 ("node1334.example.com", "node12272.example.com",
1230 constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1234 if __name__ == "__main__":
1235 testutils.GanetiTestProgram()