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.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, None,
590 self.ipolicy, None) is None)
592 def testAutoValue(self):
593 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
595 constants.VALUE_AUTO) is None)
597 def testNotDefined(self):
598 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT, None,
599 self.ipolicy, 3) is None)
601 def testNoMinDefined(self):
602 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE, None,
603 self.ipolicy, 128) is None)
605 def testNoMaxDefined(self):
606 self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT, None,
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, None,
618 "%s value %s is not in range [%s, %s]" %
619 (name, val,min_v, max_v))
620 self.assertEqual(cmdlib._ComputeMinMaxSpec(name, "1",
622 "%s/1 value %s is not in range [%s, %s]" %
623 (name, val,min_v, max_v))
626 for (name, val) in ((constants.ISPEC_MEM_SIZE, 256),
627 (constants.ISPEC_MEM_SIZE, 128),
628 (constants.ISPEC_MEM_SIZE, 512),
629 (constants.ISPEC_DISK_SIZE, 1024),
630 (constants.ISPEC_DISK_SIZE, 0),
631 (constants.ISPEC_DISK_COUNT, 1),
632 (constants.ISPEC_DISK_COUNT, 5)):
633 self.assertTrue(cmdlib._ComputeMinMaxSpec(name, None, self.ipolicy, val)
637 def _ValidateComputeMinMaxSpec(name, *_):
638 assert name in constants.ISPECS_PARAMETERS
643 def __init__(self, spec):
646 def ComputeMinMaxSpec(self, *args):
647 return self.spec.pop(0)
650 class TestComputeIPolicySpecViolation(unittest.TestCase):
652 compute_fn = _ValidateComputeMinMaxSpec
653 ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
654 [1024], 1, _compute_fn=compute_fn)
655 self.assertEqual(ret, [])
657 def testInvalidArguments(self):
658 self.assertRaises(AssertionError, cmdlib._ComputeIPolicySpecViolation,
659 NotImplemented, 1024, 1, 1, 1, [], 1)
661 def testInvalidSpec(self):
662 spec = _SpecWrapper([None, False, "foo", None, "bar", None])
663 compute_fn = spec.ComputeMinMaxSpec
664 ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
665 [1024], 1, _compute_fn=compute_fn)
666 self.assertEqual(ret, ["foo", "bar"])
667 self.assertFalse(spec.spec)
670 class _StubComputeIPolicySpecViolation:
671 def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
673 self.mem_size = mem_size
674 self.cpu_count = cpu_count
675 self.disk_count = disk_count
676 self.nic_count = nic_count
677 self.disk_sizes = disk_sizes
678 self.spindle_use = spindle_use
680 def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
682 assert self.mem_size == mem_size
683 assert self.cpu_count == cpu_count
684 assert self.disk_count == disk_count
685 assert self.nic_count == nic_count
686 assert self.disk_sizes == disk_sizes
687 assert self.spindle_use == spindle_use
692 class TestComputeIPolicyInstanceViolation(unittest.TestCase):
695 constants.BE_MAXMEM: 2048,
696 constants.BE_VCPUS: 2,
697 constants.BE_SPINDLE_USE: 4,
699 disks = [objects.Disk(size=512)]
700 instance = objects.Instance(beparams=beparams, disks=disks, nics=[])
701 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 4)
702 ret = cmdlib._ComputeIPolicyInstanceViolation(NotImplemented, instance,
704 self.assertEqual(ret, [])
707 class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
710 constants.ISPEC_MEM_SIZE: 2048,
711 constants.ISPEC_CPU_COUNT: 2,
712 constants.ISPEC_DISK_COUNT: 1,
713 constants.ISPEC_DISK_SIZE: [512],
714 constants.ISPEC_NIC_COUNT: 0,
715 constants.ISPEC_SPINDLE_USE: 1,
717 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 1)
718 ret = cmdlib._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
720 self.assertEqual(ret, [])
724 def __init__(self, return_value=None):
726 self.return_value = return_value
728 def __call__(self, *args):
730 return self.return_value
733 class TestComputeIPolicyNodeViolation(unittest.TestCase):
735 self.recorder = _CallRecorder(return_value=[])
737 def testSameGroup(self):
738 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
740 _compute_fn=self.recorder)
741 self.assertFalse(self.recorder.called)
742 self.assertEqual(ret, [])
744 def testDifferentGroup(self):
745 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
747 _compute_fn=self.recorder)
748 self.assertTrue(self.recorder.called)
749 self.assertEqual(ret, [])
752 class _FakeConfigForTargetNodeIPolicy:
753 def __init__(self, node_info=NotImplemented):
754 self._node_info = node_info
756 def GetNodeInfo(self, _):
757 return self._node_info
760 class TestCheckTargetNodeIPolicy(unittest.TestCase):
762 self.instance = objects.Instance(primary_node="blubb")
763 self.target_node = objects.Node(group="bar")
764 node_info = objects.Node(group="foo")
765 fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
766 self.lu = _FakeLU(cfg=fake_cfg)
768 def testNoViolation(self):
769 compute_recoder = _CallRecorder(return_value=[])
770 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
772 _compute_fn=compute_recoder)
773 self.assertTrue(compute_recoder.called)
774 self.assertEqual(self.lu.warning_log, [])
776 def testNoIgnore(self):
777 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
778 self.assertRaises(errors.OpPrereqError, cmdlib._CheckTargetNodeIPolicy,
779 self.lu, NotImplemented, self.instance, self.target_node,
780 _compute_fn=compute_recoder)
781 self.assertTrue(compute_recoder.called)
782 self.assertEqual(self.lu.warning_log, [])
784 def testIgnoreViolation(self):
785 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
786 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
787 self.target_node, ignore=True,
788 _compute_fn=compute_recoder)
789 self.assertTrue(compute_recoder.called)
790 msg = ("Instance does not meet target node group's (bar) instance policy:"
791 " mem_size not in range")
792 self.assertEqual(self.lu.warning_log, [(msg, ())])
795 class TestApplyContainerMods(unittest.TestCase):
796 def testEmptyContainer(self):
799 cmdlib.ApplyContainerMods("test", container, chgdesc, [], None, None, None)
800 self.assertEqual(container, [])
801 self.assertEqual(chgdesc, [])
806 mods = cmdlib.PrepareContainerMods([
807 (constants.DDM_ADD, -1, "Hello"),
808 (constants.DDM_ADD, -1, "World"),
809 (constants.DDM_ADD, 0, "Start"),
810 (constants.DDM_ADD, -1, "End"),
812 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
814 self.assertEqual(container, ["Start", "Hello", "World", "End"])
815 self.assertEqual(chgdesc, [])
817 mods = cmdlib.PrepareContainerMods([
818 (constants.DDM_ADD, 0, "zero"),
819 (constants.DDM_ADD, 3, "Added"),
820 (constants.DDM_ADD, 5, "four"),
821 (constants.DDM_ADD, 7, "xyz"),
823 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
825 self.assertEqual(container,
826 ["zero", "Start", "Hello", "Added", "World", "four",
828 self.assertEqual(chgdesc, [])
830 for idx in [-2, len(container) + 1]:
831 mods = cmdlib.PrepareContainerMods([
832 (constants.DDM_ADD, idx, "error"),
834 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
835 "test", container, None, mods, None, None, None)
837 def testRemoveError(self):
838 for idx in [0, 1, 2, 100, -1, -4]:
839 mods = cmdlib.PrepareContainerMods([
840 (constants.DDM_REMOVE, idx, None),
842 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
843 "test", [], None, mods, None, None, None)
845 mods = cmdlib.PrepareContainerMods([
846 (constants.DDM_REMOVE, 0, object()),
848 self.assertRaises(AssertionError, cmdlib.ApplyContainerMods,
849 "test", [""], None, mods, None, None, None)
851 def testAddError(self):
852 for idx in range(-100, -1) + [100]:
853 mods = cmdlib.PrepareContainerMods([
854 (constants.DDM_ADD, idx, None),
856 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
857 "test", [], None, mods, None, None, None)
859 def testRemove(self):
860 container = ["item 1", "item 2"]
861 mods = cmdlib.PrepareContainerMods([
862 (constants.DDM_ADD, -1, "aaa"),
863 (constants.DDM_REMOVE, -1, None),
864 (constants.DDM_ADD, -1, "bbb"),
867 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
869 self.assertEqual(container, ["item 1", "item 2", "bbb"])
870 self.assertEqual(chgdesc, [
871 ("test/2", "remove"),
874 def testModify(self):
875 container = ["item 1", "item 2"]
876 mods = cmdlib.PrepareContainerMods([
877 (constants.DDM_MODIFY, -1, "a"),
878 (constants.DDM_MODIFY, 0, "b"),
879 (constants.DDM_MODIFY, 1, "c"),
882 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
884 self.assertEqual(container, ["item 1", "item 2"])
885 self.assertEqual(chgdesc, [])
887 for idx in [-2, len(container) + 1]:
888 mods = cmdlib.PrepareContainerMods([
889 (constants.DDM_MODIFY, idx, "error"),
891 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
892 "test", container, None, mods, None, None, None)
899 def _CreateTestFn(idx, params, private):
900 private.data = ("add", idx, params)
901 return ((100 * idx, params), [
902 ("test/%s" % idx, hex(idx)),
906 def _ModifyTestFn(idx, item, params, private):
907 private.data = ("modify", idx, params)
909 ("test/%s" % idx, "modify %s" % params),
913 def _RemoveTestFn(idx, item, private):
914 private.data = ("remove", idx, item)
916 def testAddWithCreateFunction(self):
919 mods = cmdlib.PrepareContainerMods([
920 (constants.DDM_ADD, -1, "Hello"),
921 (constants.DDM_ADD, -1, "World"),
922 (constants.DDM_ADD, 0, "Start"),
923 (constants.DDM_ADD, -1, "End"),
924 (constants.DDM_REMOVE, 2, None),
925 (constants.DDM_MODIFY, -1, "foobar"),
926 (constants.DDM_REMOVE, 2, None),
927 (constants.DDM_ADD, 1, "More"),
928 ], self._PrivateData)
929 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
930 self._CreateTestFn, self._ModifyTestFn, self._RemoveTestFn)
931 self.assertEqual(container, [
936 self.assertEqual(chgdesc, [
941 ("test/2", "remove"),
942 ("test/2", "modify foobar"),
943 ("test/2", "remove"),
946 self.assertTrue(compat.all(op == private.data[0]
947 for (op, _, _, private) in mods))
948 self.assertEqual([private.data for (op, _, _, private) in mods], [
953 ("remove", 2, (100, "World")),
954 ("modify", 2, "foobar"),
955 ("remove", 2, (300, "End")),
960 class _FakeConfigForGenDiskTemplate:
962 self._unique_id = itertools.count()
963 self._drbd_minor = itertools.count(20)
964 self._port = itertools.count(constants.FIRST_DRBD_PORT)
965 self._secret = itertools.count()
970 def GenerateUniqueID(self, ec_id):
971 return "ec%s-uq%s" % (ec_id, self._unique_id.next())
973 def AllocateDRBDMinor(self, nodes, instance):
974 return [self._drbd_minor.next()
977 def AllocatePort(self):
978 return self._port.next()
980 def GenerateDRBDSecret(self, ec_id):
981 return "ec%s-secret%s" % (ec_id, self._secret.next())
983 def GetInstanceInfo(self, _):
987 class _FakeProcForGenDiskTemplate:
992 class TestGenerateDiskTemplate(unittest.TestCase):
994 nodegroup = objects.NodeGroup(name="ng")
995 nodegroup.UpgradeConfig()
997 cfg = _FakeConfigForGenDiskTemplate()
998 proc = _FakeProcForGenDiskTemplate()
1000 self.lu = _FakeLU(cfg=cfg, proc=proc)
1001 self.nodegroup = nodegroup
1004 def GetDiskParams():
1005 return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1007 def testWrongDiskTemplate(self):
1008 gdt = cmdlib._GenerateDiskTemplate
1009 disk_template = "##unknown##"
1011 assert disk_template not in constants.DISK_TEMPLATES
1013 self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
1014 "inst26831.example.com", "node30113.example.com", [], [],
1015 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1016 self.GetDiskParams())
1018 def testDiskless(self):
1019 gdt = cmdlib._GenerateDiskTemplate
1021 result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1022 "node30113.example.com", [], [],
1023 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1024 self.GetDiskParams())
1025 self.assertEqual(result, [])
1027 def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1028 file_storage_dir=NotImplemented,
1029 file_driver=NotImplemented,
1030 req_file_storage=NotImplemented,
1031 req_shr_file_storage=NotImplemented):
1032 gdt = cmdlib._GenerateDiskTemplate
1034 map(lambda params: utils.ForceDictType(params,
1035 constants.IDISK_PARAMS_TYPES),
1038 # Check if non-empty list of secondaries is rejected
1039 self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1040 template, "inst25088.example.com",
1041 "node185.example.com", ["node323.example.com"], [],
1042 NotImplemented, NotImplemented, base_index,
1043 self.lu.LogInfo, self.GetDiskParams(),
1044 _req_file_storage=req_file_storage,
1045 _req_shr_file_storage=req_shr_file_storage)
1047 result = gdt(self.lu, template, "inst21662.example.com",
1048 "node21741.example.com", [],
1049 disk_info, file_storage_dir, file_driver, base_index,
1050 self.lu.LogInfo, self.GetDiskParams(),
1051 _req_file_storage=req_file_storage,
1052 _req_shr_file_storage=req_shr_file_storage)
1054 for (idx, disk) in enumerate(result):
1055 self.assertTrue(isinstance(disk, objects.Disk))
1056 self.assertEqual(disk.dev_type, exp_dev_type)
1057 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1058 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1059 self.assertTrue(disk.children is None)
1061 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1062 cmdlib._UpdateIvNames(base_index, result)
1063 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1067 def _CheckIvNames(self, disks, base_index, end_index):
1068 self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1069 ["disk/%s" % i for i in range(base_index, end_index)])
1071 def testPlain(self):
1073 constants.IDISK_SIZE: 1024,
1074 constants.IDISK_MODE: constants.DISK_RDWR,
1076 constants.IDISK_SIZE: 4096,
1077 constants.IDISK_VG: "othervg",
1078 constants.IDISK_MODE: constants.DISK_RDWR,
1081 result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1084 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1085 ("testvg", "ec0-uq0.disk3"),
1086 ("othervg", "ec0-uq1.disk4"),
1090 def _AllowFileStorage():
1094 def _ForbidFileStorage():
1095 raise errors.OpPrereqError("Disallowed in test")
1098 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1099 constants.DT_FILE, [], 0, NotImplemented,
1100 req_file_storage=self._ForbidFileStorage)
1101 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1102 constants.DT_SHARED_FILE, [], 0, NotImplemented,
1103 req_shr_file_storage=self._ForbidFileStorage)
1105 for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1107 constants.IDISK_SIZE: 80 * 1024,
1108 constants.IDISK_MODE: constants.DISK_RDONLY,
1110 constants.IDISK_SIZE: 4096,
1111 constants.IDISK_MODE: constants.DISK_RDWR,
1113 constants.IDISK_SIZE: 6 * 1024,
1114 constants.IDISK_MODE: constants.DISK_RDWR,
1117 result = self._TestTrivialDisk(disk_template, disk_info, 2,
1118 constants.LD_FILE, file_storage_dir="/tmp",
1119 file_driver=constants.FD_BLKTAP,
1120 req_file_storage=self._AllowFileStorage,
1121 req_shr_file_storage=self._AllowFileStorage)
1123 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1124 (constants.FD_BLKTAP, "/tmp/disk2"),
1125 (constants.FD_BLKTAP, "/tmp/disk3"),
1126 (constants.FD_BLKTAP, "/tmp/disk4"),
1129 def testBlock(self):
1131 constants.IDISK_SIZE: 8 * 1024,
1132 constants.IDISK_MODE: constants.DISK_RDWR,
1133 constants.IDISK_ADOPT: "/tmp/some/block/dev",
1136 result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1137 constants.LD_BLOCKDEV)
1139 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1140 (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1145 constants.IDISK_SIZE: 8 * 1024,
1146 constants.IDISK_MODE: constants.DISK_RDONLY,
1148 constants.IDISK_SIZE: 100 * 1024,
1149 constants.IDISK_MODE: constants.DISK_RDWR,
1152 result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1155 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1156 ("rbd", "ec0-uq0.rbd.disk0"),
1157 ("rbd", "ec0-uq1.rbd.disk1"),
1160 def testDrbd8(self):
1161 gdt = cmdlib._GenerateDiskTemplate
1162 drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1163 drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1166 constants.IDISK_SIZE: 1024,
1167 constants.IDISK_MODE: constants.DISK_RDWR,
1169 constants.IDISK_SIZE: 100 * 1024,
1170 constants.IDISK_MODE: constants.DISK_RDONLY,
1171 constants.IDISK_METAVG: "metavg",
1173 constants.IDISK_SIZE: 4096,
1174 constants.IDISK_MODE: constants.DISK_RDWR,
1175 constants.IDISK_VG: "vgxyz",
1179 exp_logical_ids = [[
1180 (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1181 (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1183 (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1184 ("metavg", "ec0-uq1.disk1_meta"),
1186 ("vgxyz", "ec0-uq2.disk2_data"),
1187 (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1190 assert len(exp_logical_ids) == len(disk_info)
1192 map(lambda params: utils.ForceDictType(params,
1193 constants.IDISK_PARAMS_TYPES),
1196 # Check if empty list of secondaries is rejected
1197 self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1198 "inst827.example.com", "node1334.example.com", [],
1199 disk_info, NotImplemented, NotImplemented, 0,
1200 self.lu.LogInfo, self.GetDiskParams())
1202 result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1203 "node1334.example.com", ["node12272.example.com"],
1204 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1205 self.GetDiskParams())
1207 for (idx, disk) in enumerate(result):
1208 self.assertTrue(isinstance(disk, objects.Disk))
1209 self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1210 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1211 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1213 for child in disk.children:
1214 self.assertTrue(isinstance(disk, objects.Disk))
1215 self.assertEqual(child.dev_type, constants.LD_LV)
1216 self.assertTrue(child.children is None)
1218 self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1219 exp_logical_ids[idx])
1221 self.assertEqual(len(disk.children), 2)
1222 self.assertEqual(disk.children[0].size, disk.size)
1223 self.assertEqual(disk.children[1].size, cmdlib.DRBD_META_SIZE)
1225 self._CheckIvNames(result, 0, len(disk_info))
1226 cmdlib._UpdateIvNames(0, result)
1227 self._CheckIvNames(result, 0, len(disk_info))
1229 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1230 ("node1334.example.com", "node12272.example.com",
1231 constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1232 ("node1334.example.com", "node12272.example.com",
1233 constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1234 ("node1334.example.com", "node12272.example.com",
1235 constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1239 if __name__ == "__main__":
1240 testutils.GanetiTestProgram()