4 # Copyright (C) 2008, 2011, 2012, 2013 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.cmdlib import cluster
38 from ganeti.cmdlib import common
39 from ganeti import opcodes
40 from ganeti import errors
41 from ganeti import utils
42 from ganeti import luxi
44 from ganeti import objects
45 from ganeti import compat
46 from ganeti import rpc
47 from ganeti import locking
48 from ganeti import pathutils
49 from ganeti.masterd import iallocator
50 from ganeti.hypervisor import hv_xen
56 class TestCertVerification(testutils.GanetiTestCase):
58 testutils.GanetiTestCase.setUp(self)
60 self.tmpdir = tempfile.mkdtemp()
63 shutil.rmtree(self.tmpdir)
65 def testVerifyCertificate(self):
66 cluster._VerifyCertificate(testutils.TestDataFilename("cert1.pem"))
68 nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
70 (errcode, msg) = cluster._VerifyCertificate(nonexist_filename)
71 self.assertEqual(errcode, cluster.LUClusterVerifyConfig.ETYPE_ERROR)
73 # Try to load non-certificate file
74 invalid_cert = testutils.TestDataFilename("bdev-net.txt")
75 (errcode, msg) = cluster._VerifyCertificate(invalid_cert)
76 self.assertEqual(errcode, cluster.LUClusterVerifyConfig.ETYPE_ERROR)
79 class TestOpcodeParams(testutils.GanetiTestCase):
80 def testParamsStructures(self):
81 for op in sorted(mcpu.Processor.DISPATCH_TABLE):
82 lu = mcpu.Processor.DISPATCH_TABLE[op]
84 self.failIf(hasattr(lu, "_OP_REQP"),
85 msg=("LU '%s' has old-style _OP_REQP" % lu_name))
86 self.failIf(hasattr(lu, "_OP_DEFS"),
87 msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
88 self.failIf(hasattr(lu, "_OP_PARAMS"),
89 msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
92 class TestIAllocatorChecks(testutils.GanetiTestCase):
93 def testFunction(self):
95 def __init__(self, opcode):
96 self.cfg = mocks.FakeConfig()
99 class OpTest(opcodes.OpCode):
101 ("iallocator", None, ht.NoType, None),
102 ("node", None, ht.NoType, None),
105 default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
106 other_iallocator = default_iallocator + "_not"
111 c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
113 # Neither node nor iallocator given
118 self.assertEqual(lu.op.iallocator, default_iallocator)
119 self.assertEqual(lu.op.node, n)
121 # Both, iallocator and node given
122 for a in ("test", constants.DEFAULT_IALLOCATOR_SHORTCUT):
125 self.assertRaises(errors.OpPrereqError, c_i)
127 # Only iallocator given
129 op.iallocator = other_iallocator
132 self.assertEqual(lu.op.iallocator, other_iallocator)
133 self.assertEqual(lu.op.node, n)
139 self.assertEqual(lu.op.iallocator, None)
140 self.assertEqual(lu.op.node, "node")
142 # Asked for default iallocator, no node given
143 op.iallocator = constants.DEFAULT_IALLOCATOR_SHORTCUT
146 self.assertEqual(lu.op.iallocator, default_iallocator)
147 self.assertEqual(lu.op.node, None)
149 # No node, iallocator or default iallocator
152 lu.cfg.GetDefaultIAllocator = lambda: None
153 self.assertRaises(errors.OpPrereqError, c_i)
156 class TestLUTestJqueue(unittest.TestCase):
158 self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
159 (luxi.WFJC_TIMEOUT * 0.75),
160 msg=("Client timeout too high, might not notice bugs"
161 " in WaitForJobChange"))
164 class TestLUQuery(unittest.TestCase):
166 self.assertEqual(sorted(cmdlib._QUERY_IMPL.keys()),
167 sorted(constants.QR_VIA_OP))
169 assert constants.QR_NODE in constants.QR_VIA_OP
170 assert constants.QR_INSTANCE in constants.QR_VIA_OP
172 for i in constants.QR_VIA_OP:
173 self.assert_(cmdlib._GetQueryImplementation(i))
175 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation, "")
176 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation,
180 class TestLUGroupAssignNodes(unittest.TestCase):
182 def testCheckAssignmentForSplitInstances(self):
183 node_data = dict((name, objects.Node(name=name, group=group))
184 for (name, group) in [("n1a", "g1"), ("n1b", "g1"),
185 ("n2a", "g2"), ("n2b", "g2"),
186 ("n3a", "g3"), ("n3b", "g3"),
190 def Instance(name, pnode, snode):
193 disk_template = constants.DT_DISKLESS
195 disks = [objects.Disk(dev_type=constants.LD_DRBD8,
196 logical_id=[pnode, snode, 1, 17, 17])]
197 disk_template = constants.DT_DRBD8
199 return objects.Instance(name=name, primary_node=pnode, disks=disks,
200 disk_template=disk_template)
202 instance_data = dict((name, Instance(name, pnode, snode))
203 for name, pnode, snode in [("inst1a", "n1a", "n1b"),
204 ("inst1b", "n1b", "n1a"),
205 ("inst2a", "n2a", "n2b"),
206 ("inst3a", "n3a", None),
207 ("inst3b", "n3b", "n1b"),
208 ("inst3c", "n3b", "n2b"),
211 # Test first with the existing state.
213 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
217 self.assertEqual([], new)
218 self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
220 # And now some changes.
222 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
227 self.assertEqual(set(["inst1a", "inst1b"]), set(new))
228 self.assertEqual(set(["inst3c"]), set(prev))
231 class TestClusterVerifySsh(unittest.TestCase):
232 def testMultipleGroups(self):
233 fn = cluster.LUClusterVerifyGroup._SelectSshCheckNodes
235 objects.Node(name="node20", group="my", offline=False),
236 objects.Node(name="node21", group="my", offline=False),
237 objects.Node(name="node22", group="my", offline=False),
238 objects.Node(name="node23", group="my", offline=False),
239 objects.Node(name="node24", group="my", offline=False),
240 objects.Node(name="node25", group="my", offline=False),
241 objects.Node(name="node26", group="my", offline=True),
244 objects.Node(name="node1", group="g1", offline=True),
245 objects.Node(name="node2", group="g1", offline=False),
246 objects.Node(name="node3", group="g1", offline=False),
247 objects.Node(name="node4", group="g1", offline=True),
248 objects.Node(name="node5", group="g1", offline=False),
249 objects.Node(name="node10", group="xyz", offline=False),
250 objects.Node(name="node11", group="xyz", offline=False),
251 objects.Node(name="node40", group="alloff", offline=True),
252 objects.Node(name="node41", group="alloff", offline=True),
253 objects.Node(name="node50", group="aaa", offline=False),
255 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
257 (online, perhost) = fn(mygroupnodes, "my", nodes)
258 self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
259 self.assertEqual(set(perhost.keys()), set(online))
261 self.assertEqual(perhost, {
262 "node20": ["node10", "node2", "node50"],
263 "node21": ["node11", "node3", "node50"],
264 "node22": ["node10", "node5", "node50"],
265 "node23": ["node11", "node2", "node50"],
266 "node24": ["node10", "node3", "node50"],
267 "node25": ["node11", "node5", "node50"],
270 def testSingleGroup(self):
271 fn = cluster.LUClusterVerifyGroup._SelectSshCheckNodes
273 objects.Node(name="node1", group="default", offline=True),
274 objects.Node(name="node2", group="default", offline=False),
275 objects.Node(name="node3", group="default", offline=False),
276 objects.Node(name="node4", group="default", offline=True),
278 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
280 (online, perhost) = fn(nodes, "default", nodes)
281 self.assertEqual(online, ["node2", "node3"])
282 self.assertEqual(set(perhost.keys()), set(online))
284 self.assertEqual(perhost, {
290 class TestClusterVerifyFiles(unittest.TestCase):
292 def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
293 assert ((ecode == constants.CV_ENODEFILECHECK and
294 ht.TNonEmptyString(item)) or
295 (ecode == constants.CV_ECLUSTERFILECHECK and
302 errors.append((item, msg))
304 _VerifyFiles = cluster.LUClusterVerifyGroup._VerifyFiles
308 master_name = "master.example.com"
310 objects.Node(name=master_name, offline=False, vm_capable=True),
311 objects.Node(name="node2.example.com", offline=False, vm_capable=True),
312 objects.Node(name="node3.example.com", master_candidate=True,
314 objects.Node(name="node4.example.com", offline=False, vm_capable=True),
315 objects.Node(name="nodata.example.com", offline=False, vm_capable=True),
316 objects.Node(name="offline.example.com", offline=True),
318 cluster = objects.Cluster(modify_etc_hosts=True,
319 enabled_hypervisors=[constants.HT_XEN_HVM])
321 pathutils.CLUSTER_DOMAIN_SECRET_FILE,
322 pathutils.RAPI_CERT_FILE,
323 pathutils.RAPI_USERS_FILE,
326 pathutils.RAPI_USERS_FILE,
327 hv_xen.XL_CONFIG_FILE,
328 pathutils.VNC_PASSWORD_FILE,
331 pathutils.CLUSTER_CONF_FILE,
334 hv_xen.XEND_CONFIG_FILE,
335 hv_xen.XL_CONFIG_FILE,
336 pathutils.VNC_PASSWORD_FILE,
339 master_name: rpc.RpcResult(data=(True, {
340 constants.NV_FILELIST: {
341 pathutils.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
342 pathutils.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
343 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
344 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
345 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
347 "node2.example.com": rpc.RpcResult(data=(True, {
348 constants.NV_FILELIST: {
349 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
350 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
353 "node3.example.com": rpc.RpcResult(data=(True, {
354 constants.NV_FILELIST: {
355 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
356 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
359 "node4.example.com": rpc.RpcResult(data=(True, {
360 constants.NV_FILELIST: {
361 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
362 pathutils.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
363 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
364 pathutils.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
365 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
368 "nodata.example.com": rpc.RpcResult(data=(True, {})),
369 "offline.example.com": rpc.RpcResult(offline=True),
371 assert set(nvinfo.keys()) == set(map(operator.attrgetter("name"), nodeinfo))
373 self._VerifyFiles(compat.partial(self._FakeErrorIf, errors), nodeinfo,
375 (files_all, files_opt, files_mc, files_vm))
376 self.assertEqual(sorted(errors), sorted([
377 (None, ("File %s found with 2 different checksums (variant 1 on"
378 " node2.example.com, node3.example.com, node4.example.com;"
379 " variant 2 on master.example.com)" % pathutils.RAPI_CERT_FILE)),
380 (None, ("File %s is missing from node(s) node2.example.com" %
381 pathutils.CLUSTER_DOMAIN_SECRET_FILE)),
382 (None, ("File %s should not exist on node(s) node4.example.com" %
383 pathutils.CLUSTER_CONF_FILE)),
384 (None, ("File %s is missing from node(s) node4.example.com" %
385 hv_xen.XEND_CONFIG_FILE)),
386 (None, ("File %s is missing from node(s) node3.example.com" %
387 pathutils.CLUSTER_CONF_FILE)),
388 (None, ("File %s found with 2 different checksums (variant 1 on"
389 " master.example.com; variant 2 on node4.example.com)" %
390 pathutils.CLUSTER_CONF_FILE)),
391 (None, ("File %s is optional, but it must exist on all or no nodes (not"
392 " found on master.example.com, node2.example.com,"
393 " node3.example.com)" % pathutils.RAPI_USERS_FILE)),
394 (None, ("File %s is optional, but it must exist on all or no nodes (not"
395 " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
396 ("nodata.example.com", "Node did not return file checksum data"),
401 def __init__(self, cfg=NotImplemented, proc=NotImplemented,
403 self.warning_log = []
409 def LogWarning(self, text, *args):
410 self.warning_log.append((text, args))
412 def LogInfo(self, text, *args):
413 self.info_log.append((text, args))
416 class TestLoadNodeEvacResult(unittest.TestCase):
417 def testSuccess(self):
419 ("inst20153.example.com", "grp2", ["nodeA4509", "nodeB2912"]),
421 for early_release in [False, True]:
422 for use_nodes in [False, True]:
424 [opcodes.OpInstanceReplaceDisks().__getstate__()],
425 [opcodes.OpInstanceMigrate().__getstate__()],
428 alloc_result = (moved, [], jobs)
429 assert iallocator._NEVAC_RESULT(alloc_result)
432 result = cmdlib._LoadNodeEvacResult(lu, alloc_result,
433 early_release, use_nodes)
436 (_, (info_args, )) = lu.info_log.pop(0)
437 for (instname, instgroup, instnodes) in moved:
438 self.assertTrue(instname in info_args)
441 self.assertTrue(i in info_args)
443 self.assertTrue(instgroup in info_args)
445 self.assertFalse(lu.info_log)
446 self.assertFalse(lu.warning_log)
448 for op in itertools.chain(*result):
449 if hasattr(op.__class__, "early_release"):
450 self.assertEqual(op.early_release, early_release)
452 self.assertFalse(hasattr(op, "early_release"))
454 def testFailed(self):
455 alloc_result = ([], [
456 ("inst5191.example.com", "errormsg21178"),
458 assert iallocator._NEVAC_RESULT(alloc_result)
461 self.assertRaises(errors.OpExecError, cmdlib._LoadNodeEvacResult,
462 lu, alloc_result, False, False)
463 self.assertFalse(lu.info_log)
464 (_, (args, )) = lu.warning_log.pop(0)
465 self.assertTrue("inst5191.example.com" in args)
466 self.assertTrue("errormsg21178" in args)
467 self.assertFalse(lu.warning_log)
470 class TestUpdateAndVerifySubDict(unittest.TestCase):
473 "a": constants.VTYPE_INT,
474 "b": constants.VTYPE_STRING,
475 "c": constants.VTYPE_BOOL,
476 "d": constants.VTYPE_STRING,
523 verified = common._UpdateAndVerifySubDict(old_test, test, self.type_check)
524 self.assertEqual(verified, mv)
540 self.assertRaises(errors.TypeEnforcementError,
541 common._UpdateAndVerifySubDict, {}, test,
545 class TestHvStateHelper(unittest.TestCase):
546 def testWithoutOpData(self):
547 self.assertEqual(common._MergeAndVerifyHvState(None, NotImplemented),
550 def testWithoutOldData(self):
552 constants.HT_XEN_PVM: {
553 constants.HVST_MEMORY_TOTAL: 4096,
556 self.assertEqual(common._MergeAndVerifyHvState(new, None), new)
558 def testWithWrongHv(self):
561 constants.HVST_MEMORY_TOTAL: 4096,
564 self.assertRaises(errors.OpPrereqError, common._MergeAndVerifyHvState,
567 class TestDiskStateHelper(unittest.TestCase):
568 def testWithoutOpData(self):
569 self.assertEqual(common._MergeAndVerifyDiskState(None, NotImplemented),
572 def testWithoutOldData(self):
576 constants.DS_DISK_RESERVED: 1024,
580 self.assertEqual(common._MergeAndVerifyDiskState(new, None), new)
582 def testWithWrongStorageType(self):
586 constants.DS_DISK_RESERVED: 1024,
590 self.assertRaises(errors.OpPrereqError, common._MergeAndVerifyDiskState,
594 class TestComputeMinMaxSpec(unittest.TestCase):
597 constants.ISPECS_MAX: {
598 constants.ISPEC_MEM_SIZE: 512,
599 constants.ISPEC_DISK_SIZE: 1024,
601 constants.ISPECS_MIN: {
602 constants.ISPEC_MEM_SIZE: 128,
603 constants.ISPEC_DISK_COUNT: 1,
607 def testNoneValue(self):
608 self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
609 self.ispecs, None) is None)
611 def testAutoValue(self):
612 self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
614 constants.VALUE_AUTO) is None)
616 def testNotDefined(self):
617 self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT, None,
618 self.ispecs, 3) is None)
620 def testNoMinDefined(self):
621 self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE, None,
622 self.ispecs, 128) is None)
624 def testNoMaxDefined(self):
625 self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT,
626 None, self.ispecs, 16) is None)
628 def testOutOfRange(self):
629 for (name, val) in ((constants.ISPEC_MEM_SIZE, 64),
630 (constants.ISPEC_MEM_SIZE, 768),
631 (constants.ISPEC_DISK_SIZE, 4096),
632 (constants.ISPEC_DISK_COUNT, 0)):
633 min_v = self.ispecs[constants.ISPECS_MIN].get(name, val)
634 max_v = self.ispecs[constants.ISPECS_MAX].get(name, val)
635 self.assertEqual(common._ComputeMinMaxSpec(name, None,
637 "%s value %s is not in range [%s, %s]" %
638 (name, val,min_v, max_v))
639 self.assertEqual(common._ComputeMinMaxSpec(name, "1",
641 "%s/1 value %s is not in range [%s, %s]" %
642 (name, val,min_v, max_v))
645 for (name, val) in ((constants.ISPEC_MEM_SIZE, 256),
646 (constants.ISPEC_MEM_SIZE, 128),
647 (constants.ISPEC_MEM_SIZE, 512),
648 (constants.ISPEC_DISK_SIZE, 1024),
649 (constants.ISPEC_DISK_SIZE, 0),
650 (constants.ISPEC_DISK_COUNT, 1),
651 (constants.ISPEC_DISK_COUNT, 5)):
652 self.assertTrue(common._ComputeMinMaxSpec(name, None, self.ispecs, val)
656 def _ValidateComputeMinMaxSpec(name, *_):
657 assert name in constants.ISPECS_PARAMETERS
661 def _NoDiskComputeMinMaxSpec(name, *_):
662 if name == constants.ISPEC_DISK_COUNT:
669 def __init__(self, spec):
672 def ComputeMinMaxSpec(self, *args):
673 return self.spec.pop(0)
676 class TestComputeIPolicySpecViolation(unittest.TestCase):
677 # Minimal policy accepted by _ComputeIPolicySpecViolation()
679 constants.IPOLICY_DTS: [constants.DT_PLAIN, constants.DT_DISKLESS],
680 constants.ISPECS_MINMAX: [NotImplemented],
684 compute_fn = _ValidateComputeMinMaxSpec
685 ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
686 [1024], 1, constants.DT_PLAIN,
687 _compute_fn=compute_fn)
688 self.assertEqual(ret, [])
690 def testDiskFull(self):
691 compute_fn = _NoDiskComputeMinMaxSpec
692 ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
693 [1024], 1, constants.DT_PLAIN,
694 _compute_fn=compute_fn)
695 self.assertEqual(ret, [constants.ISPEC_DISK_COUNT])
697 def testDiskLess(self):
698 compute_fn = _NoDiskComputeMinMaxSpec
699 ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
700 [1024], 1, constants.DT_DISKLESS,
701 _compute_fn=compute_fn)
702 self.assertEqual(ret, [])
704 def testWrongTemplates(self):
705 compute_fn = _ValidateComputeMinMaxSpec
706 ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
707 [1024], 1, constants.DT_DRBD8,
708 _compute_fn=compute_fn)
709 self.assertEqual(len(ret), 1)
710 self.assertTrue("Disk template" in ret[0])
712 def testInvalidArguments(self):
713 self.assertRaises(AssertionError, common._ComputeIPolicySpecViolation,
714 self._MICRO_IPOL, 1024, 1, 1, 1, [], 1,
717 def testInvalidSpec(self):
718 spec = _SpecWrapper([None, False, "foo", None, "bar", None])
719 compute_fn = spec.ComputeMinMaxSpec
720 ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
721 [1024], 1, constants.DT_PLAIN,
722 _compute_fn=compute_fn)
723 self.assertEqual(ret, ["foo", "bar"])
724 self.assertFalse(spec.spec)
726 def testWithIPolicy(self):
733 disk_template = "mytemplate"
735 constants.ISPEC_MEM_SIZE: mem_size,
736 constants.ISPEC_CPU_COUNT: cpu_count,
737 constants.ISPEC_DISK_COUNT: disk_count,
738 constants.ISPEC_DISK_SIZE: disk_sizes[0],
739 constants.ISPEC_NIC_COUNT: nic_count,
740 constants.ISPEC_SPINDLE_USE: spindle_use,
743 constants.ISPECS_MINMAX: [{
744 constants.ISPECS_MIN: ispec,
745 constants.ISPECS_MAX: ispec,
747 constants.IPOLICY_DTS: [disk_template],
749 ispec_copy = copy.deepcopy(ispec)
751 constants.ISPECS_MINMAX: [
753 constants.ISPECS_MIN: ispec_copy,
754 constants.ISPECS_MAX: ispec_copy,
757 constants.ISPECS_MIN: ispec,
758 constants.ISPECS_MAX: ispec,
761 constants.IPOLICY_DTS: [disk_template],
764 constants.ISPECS_MINMAX: [
766 constants.ISPECS_MIN: ispec,
767 constants.ISPECS_MAX: ispec,
770 constants.ISPECS_MIN: ispec_copy,
771 constants.ISPECS_MAX: ispec_copy,
774 constants.IPOLICY_DTS: [disk_template],
776 def AssertComputeViolation(ipolicy, violations):
777 ret = common._ComputeIPolicySpecViolation(ipolicy, mem_size, cpu_count,
778 disk_count, nic_count,
779 disk_sizes, spindle_use,
781 self.assertEqual(len(ret), violations)
783 AssertComputeViolation(ipolicy1, 0)
784 AssertComputeViolation(ipolicy2, 0)
785 AssertComputeViolation(ipolicy3, 0)
786 for par in constants.ISPECS_PARAMETERS:
788 AssertComputeViolation(ipolicy1, 1)
789 AssertComputeViolation(ipolicy2, 0)
790 AssertComputeViolation(ipolicy3, 0)
792 AssertComputeViolation(ipolicy1, 1)
793 AssertComputeViolation(ipolicy2, 0)
794 AssertComputeViolation(ipolicy3, 0)
795 ispec[par] += 1 # Restore
796 ipolicy1[constants.IPOLICY_DTS] = ["another_template"]
797 AssertComputeViolation(ipolicy1, 1)
800 class _StubComputeIPolicySpecViolation:
801 def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
802 spindle_use, disk_template):
803 self.mem_size = mem_size
804 self.cpu_count = cpu_count
805 self.disk_count = disk_count
806 self.nic_count = nic_count
807 self.disk_sizes = disk_sizes
808 self.spindle_use = spindle_use
809 self.disk_template = disk_template
811 def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
812 spindle_use, disk_template):
813 assert self.mem_size == mem_size
814 assert self.cpu_count == cpu_count
815 assert self.disk_count == disk_count
816 assert self.nic_count == nic_count
817 assert self.disk_sizes == disk_sizes
818 assert self.spindle_use == spindle_use
819 assert self.disk_template == disk_template
824 class _FakeConfigForComputeIPolicyInstanceViolation:
825 def __init__(self, be):
826 self.cluster = objects.Cluster(beparams={"default": be})
828 def GetClusterInfo(self):
832 class TestComputeIPolicyInstanceViolation(unittest.TestCase):
835 constants.BE_MAXMEM: 2048,
836 constants.BE_VCPUS: 2,
837 constants.BE_SPINDLE_USE: 4,
839 disks = [objects.Disk(size=512)]
840 cfg = _FakeConfigForComputeIPolicyInstanceViolation(beparams)
841 instance = objects.Instance(beparams=beparams, disks=disks, nics=[],
842 disk_template=constants.DT_PLAIN)
843 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 4,
845 ret = common._ComputeIPolicyInstanceViolation(NotImplemented, instance,
846 cfg, _compute_fn=stub)
847 self.assertEqual(ret, [])
848 instance2 = objects.Instance(beparams={}, disks=disks, nics=[],
849 disk_template=constants.DT_PLAIN)
850 ret = common._ComputeIPolicyInstanceViolation(NotImplemented, instance2,
851 cfg, _compute_fn=stub)
852 self.assertEqual(ret, [])
855 class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
858 constants.ISPEC_MEM_SIZE: 2048,
859 constants.ISPEC_CPU_COUNT: 2,
860 constants.ISPEC_DISK_COUNT: 1,
861 constants.ISPEC_DISK_SIZE: [512],
862 constants.ISPEC_NIC_COUNT: 0,
863 constants.ISPEC_SPINDLE_USE: 1,
865 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 1,
867 ret = cmdlib._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
870 self.assertEqual(ret, [])
874 def __init__(self, return_value=None):
876 self.return_value = return_value
878 def __call__(self, *args):
880 return self.return_value
883 class TestComputeIPolicyNodeViolation(unittest.TestCase):
885 self.recorder = _CallRecorder(return_value=[])
887 def testSameGroup(self):
888 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
889 "foo", "foo", NotImplemented,
890 _compute_fn=self.recorder)
891 self.assertFalse(self.recorder.called)
892 self.assertEqual(ret, [])
894 def testDifferentGroup(self):
895 ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
896 "foo", "bar", NotImplemented,
897 _compute_fn=self.recorder)
898 self.assertTrue(self.recorder.called)
899 self.assertEqual(ret, [])
902 class _FakeConfigForTargetNodeIPolicy:
903 def __init__(self, node_info=NotImplemented):
904 self._node_info = node_info
906 def GetNodeInfo(self, _):
907 return self._node_info
910 class TestCheckTargetNodeIPolicy(unittest.TestCase):
912 self.instance = objects.Instance(primary_node="blubb")
913 self.target_node = objects.Node(group="bar")
914 node_info = objects.Node(group="foo")
915 fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
916 self.lu = _FakeLU(cfg=fake_cfg)
918 def testNoViolation(self):
919 compute_recoder = _CallRecorder(return_value=[])
920 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
921 self.target_node, NotImplemented,
922 _compute_fn=compute_recoder)
923 self.assertTrue(compute_recoder.called)
924 self.assertEqual(self.lu.warning_log, [])
926 def testNoIgnore(self):
927 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
928 self.assertRaises(errors.OpPrereqError, cmdlib._CheckTargetNodeIPolicy,
929 self.lu, NotImplemented, self.instance, self.target_node,
930 NotImplemented, _compute_fn=compute_recoder)
931 self.assertTrue(compute_recoder.called)
932 self.assertEqual(self.lu.warning_log, [])
934 def testIgnoreViolation(self):
935 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
936 cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
937 self.target_node, NotImplemented,
938 ignore=True, _compute_fn=compute_recoder)
939 self.assertTrue(compute_recoder.called)
940 msg = ("Instance does not meet target node group's (bar) instance policy:"
941 " mem_size not in range")
942 self.assertEqual(self.lu.warning_log, [(msg, ())])
945 class TestApplyContainerMods(unittest.TestCase):
946 def testEmptyContainer(self):
949 cmdlib.ApplyContainerMods("test", container, chgdesc, [], None, None, None)
950 self.assertEqual(container, [])
951 self.assertEqual(chgdesc, [])
956 mods = cmdlib.PrepareContainerMods([
957 (constants.DDM_ADD, -1, "Hello"),
958 (constants.DDM_ADD, -1, "World"),
959 (constants.DDM_ADD, 0, "Start"),
960 (constants.DDM_ADD, -1, "End"),
962 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
964 self.assertEqual(container, ["Start", "Hello", "World", "End"])
965 self.assertEqual(chgdesc, [])
967 mods = cmdlib.PrepareContainerMods([
968 (constants.DDM_ADD, 0, "zero"),
969 (constants.DDM_ADD, 3, "Added"),
970 (constants.DDM_ADD, 5, "four"),
971 (constants.DDM_ADD, 7, "xyz"),
973 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
975 self.assertEqual(container,
976 ["zero", "Start", "Hello", "Added", "World", "four",
978 self.assertEqual(chgdesc, [])
980 for idx in [-2, len(container) + 1]:
981 mods = cmdlib.PrepareContainerMods([
982 (constants.DDM_ADD, idx, "error"),
984 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
985 "test", container, None, mods, None, None, None)
987 def testRemoveError(self):
988 for idx in [0, 1, 2, 100, -1, -4]:
989 mods = cmdlib.PrepareContainerMods([
990 (constants.DDM_REMOVE, idx, None),
992 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
993 "test", [], None, mods, None, None, None)
995 mods = cmdlib.PrepareContainerMods([
996 (constants.DDM_REMOVE, 0, object()),
998 self.assertRaises(AssertionError, cmdlib.ApplyContainerMods,
999 "test", [""], None, mods, None, None, None)
1001 def testAddError(self):
1002 for idx in range(-100, -1) + [100]:
1003 mods = cmdlib.PrepareContainerMods([
1004 (constants.DDM_ADD, idx, None),
1006 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
1007 "test", [], None, mods, None, None, None)
1009 def testRemove(self):
1010 container = ["item 1", "item 2"]
1011 mods = cmdlib.PrepareContainerMods([
1012 (constants.DDM_ADD, -1, "aaa"),
1013 (constants.DDM_REMOVE, -1, None),
1014 (constants.DDM_ADD, -1, "bbb"),
1017 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
1019 self.assertEqual(container, ["item 1", "item 2", "bbb"])
1020 self.assertEqual(chgdesc, [
1021 ("test/2", "remove"),
1024 def testModify(self):
1025 container = ["item 1", "item 2"]
1026 mods = cmdlib.PrepareContainerMods([
1027 (constants.DDM_MODIFY, -1, "a"),
1028 (constants.DDM_MODIFY, 0, "b"),
1029 (constants.DDM_MODIFY, 1, "c"),
1032 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
1034 self.assertEqual(container, ["item 1", "item 2"])
1035 self.assertEqual(chgdesc, [])
1037 for idx in [-2, len(container) + 1]:
1038 mods = cmdlib.PrepareContainerMods([
1039 (constants.DDM_MODIFY, idx, "error"),
1041 self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
1042 "test", container, None, mods, None, None, None)
1049 def _CreateTestFn(idx, params, private):
1050 private.data = ("add", idx, params)
1051 return ((100 * idx, params), [
1052 ("test/%s" % idx, hex(idx)),
1056 def _ModifyTestFn(idx, item, params, private):
1057 private.data = ("modify", idx, params)
1059 ("test/%s" % idx, "modify %s" % params),
1063 def _RemoveTestFn(idx, item, private):
1064 private.data = ("remove", idx, item)
1066 def testAddWithCreateFunction(self):
1069 mods = cmdlib.PrepareContainerMods([
1070 (constants.DDM_ADD, -1, "Hello"),
1071 (constants.DDM_ADD, -1, "World"),
1072 (constants.DDM_ADD, 0, "Start"),
1073 (constants.DDM_ADD, -1, "End"),
1074 (constants.DDM_REMOVE, 2, None),
1075 (constants.DDM_MODIFY, -1, "foobar"),
1076 (constants.DDM_REMOVE, 2, None),
1077 (constants.DDM_ADD, 1, "More"),
1078 ], self._PrivateData)
1079 cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
1080 self._CreateTestFn, self._ModifyTestFn, self._RemoveTestFn)
1081 self.assertEqual(container, [
1086 self.assertEqual(chgdesc, [
1091 ("test/2", "remove"),
1092 ("test/2", "modify foobar"),
1093 ("test/2", "remove"),
1096 self.assertTrue(compat.all(op == private.data[0]
1097 for (op, _, _, private) in mods))
1098 self.assertEqual([private.data for (op, _, _, private) in mods], [
1099 ("add", 0, "Hello"),
1100 ("add", 1, "World"),
1101 ("add", 0, "Start"),
1103 ("remove", 2, (100, "World")),
1104 ("modify", 2, "foobar"),
1105 ("remove", 2, (300, "End")),
1110 class _FakeConfigForGenDiskTemplate:
1112 self._unique_id = itertools.count()
1113 self._drbd_minor = itertools.count(20)
1114 self._port = itertools.count(constants.FIRST_DRBD_PORT)
1115 self._secret = itertools.count()
1117 def GetVGName(self):
1120 def GenerateUniqueID(self, ec_id):
1121 return "ec%s-uq%s" % (ec_id, self._unique_id.next())
1123 def AllocateDRBDMinor(self, nodes, instance):
1124 return [self._drbd_minor.next()
1127 def AllocatePort(self):
1128 return self._port.next()
1130 def GenerateDRBDSecret(self, ec_id):
1131 return "ec%s-secret%s" % (ec_id, self._secret.next())
1133 def GetInstanceInfo(self, _):
1137 class _FakeProcForGenDiskTemplate:
1142 class TestGenerateDiskTemplate(unittest.TestCase):
1144 nodegroup = objects.NodeGroup(name="ng")
1145 nodegroup.UpgradeConfig()
1147 cfg = _FakeConfigForGenDiskTemplate()
1148 proc = _FakeProcForGenDiskTemplate()
1150 self.lu = _FakeLU(cfg=cfg, proc=proc)
1151 self.nodegroup = nodegroup
1154 def GetDiskParams():
1155 return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1157 def testWrongDiskTemplate(self):
1158 gdt = cmdlib._GenerateDiskTemplate
1159 disk_template = "##unknown##"
1161 assert disk_template not in constants.DISK_TEMPLATES
1163 self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
1164 "inst26831.example.com", "node30113.example.com", [], [],
1165 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1166 self.GetDiskParams())
1168 def testDiskless(self):
1169 gdt = cmdlib._GenerateDiskTemplate
1171 result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1172 "node30113.example.com", [], [],
1173 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1174 self.GetDiskParams())
1175 self.assertEqual(result, [])
1177 def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1178 file_storage_dir=NotImplemented,
1179 file_driver=NotImplemented,
1180 req_file_storage=NotImplemented,
1181 req_shr_file_storage=NotImplemented):
1182 gdt = cmdlib._GenerateDiskTemplate
1184 map(lambda params: utils.ForceDictType(params,
1185 constants.IDISK_PARAMS_TYPES),
1188 # Check if non-empty list of secondaries is rejected
1189 self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1190 template, "inst25088.example.com",
1191 "node185.example.com", ["node323.example.com"], [],
1192 NotImplemented, NotImplemented, base_index,
1193 self.lu.LogInfo, self.GetDiskParams(),
1194 _req_file_storage=req_file_storage,
1195 _req_shr_file_storage=req_shr_file_storage)
1197 result = gdt(self.lu, template, "inst21662.example.com",
1198 "node21741.example.com", [],
1199 disk_info, file_storage_dir, file_driver, base_index,
1200 self.lu.LogInfo, self.GetDiskParams(),
1201 _req_file_storage=req_file_storage,
1202 _req_shr_file_storage=req_shr_file_storage)
1204 for (idx, disk) in enumerate(result):
1205 self.assertTrue(isinstance(disk, objects.Disk))
1206 self.assertEqual(disk.dev_type, exp_dev_type)
1207 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1208 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1209 self.assertTrue(disk.children is None)
1211 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1212 cmdlib._UpdateIvNames(base_index, result)
1213 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1217 def _CheckIvNames(self, disks, base_index, end_index):
1218 self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1219 ["disk/%s" % i for i in range(base_index, end_index)])
1221 def testPlain(self):
1223 constants.IDISK_SIZE: 1024,
1224 constants.IDISK_MODE: constants.DISK_RDWR,
1226 constants.IDISK_SIZE: 4096,
1227 constants.IDISK_VG: "othervg",
1228 constants.IDISK_MODE: constants.DISK_RDWR,
1231 result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1234 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1235 ("testvg", "ec0-uq0.disk3"),
1236 ("othervg", "ec0-uq1.disk4"),
1240 def _AllowFileStorage():
1244 def _ForbidFileStorage():
1245 raise errors.OpPrereqError("Disallowed in test")
1248 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1249 constants.DT_FILE, [], 0, NotImplemented,
1250 req_file_storage=self._ForbidFileStorage)
1251 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1252 constants.DT_SHARED_FILE, [], 0, NotImplemented,
1253 req_shr_file_storage=self._ForbidFileStorage)
1255 for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1257 constants.IDISK_SIZE: 80 * 1024,
1258 constants.IDISK_MODE: constants.DISK_RDONLY,
1260 constants.IDISK_SIZE: 4096,
1261 constants.IDISK_MODE: constants.DISK_RDWR,
1263 constants.IDISK_SIZE: 6 * 1024,
1264 constants.IDISK_MODE: constants.DISK_RDWR,
1267 result = self._TestTrivialDisk(disk_template, disk_info, 2,
1268 constants.LD_FILE, file_storage_dir="/tmp",
1269 file_driver=constants.FD_BLKTAP,
1270 req_file_storage=self._AllowFileStorage,
1271 req_shr_file_storage=self._AllowFileStorage)
1273 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1274 (constants.FD_BLKTAP, "/tmp/disk2"),
1275 (constants.FD_BLKTAP, "/tmp/disk3"),
1276 (constants.FD_BLKTAP, "/tmp/disk4"),
1279 def testBlock(self):
1281 constants.IDISK_SIZE: 8 * 1024,
1282 constants.IDISK_MODE: constants.DISK_RDWR,
1283 constants.IDISK_ADOPT: "/tmp/some/block/dev",
1286 result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1287 constants.LD_BLOCKDEV)
1289 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1290 (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1295 constants.IDISK_SIZE: 8 * 1024,
1296 constants.IDISK_MODE: constants.DISK_RDONLY,
1298 constants.IDISK_SIZE: 100 * 1024,
1299 constants.IDISK_MODE: constants.DISK_RDWR,
1302 result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1305 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1306 ("rbd", "ec0-uq0.rbd.disk0"),
1307 ("rbd", "ec0-uq1.rbd.disk1"),
1310 def testDrbd8(self):
1311 gdt = cmdlib._GenerateDiskTemplate
1312 drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1313 drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1316 constants.IDISK_SIZE: 1024,
1317 constants.IDISK_MODE: constants.DISK_RDWR,
1319 constants.IDISK_SIZE: 100 * 1024,
1320 constants.IDISK_MODE: constants.DISK_RDONLY,
1321 constants.IDISK_METAVG: "metavg",
1323 constants.IDISK_SIZE: 4096,
1324 constants.IDISK_MODE: constants.DISK_RDWR,
1325 constants.IDISK_VG: "vgxyz",
1329 exp_logical_ids = [[
1330 (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1331 (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1333 (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1334 ("metavg", "ec0-uq1.disk1_meta"),
1336 ("vgxyz", "ec0-uq2.disk2_data"),
1337 (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1340 assert len(exp_logical_ids) == len(disk_info)
1342 map(lambda params: utils.ForceDictType(params,
1343 constants.IDISK_PARAMS_TYPES),
1346 # Check if empty list of secondaries is rejected
1347 self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1348 "inst827.example.com", "node1334.example.com", [],
1349 disk_info, NotImplemented, NotImplemented, 0,
1350 self.lu.LogInfo, self.GetDiskParams())
1352 result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1353 "node1334.example.com", ["node12272.example.com"],
1354 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1355 self.GetDiskParams())
1357 for (idx, disk) in enumerate(result):
1358 self.assertTrue(isinstance(disk, objects.Disk))
1359 self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1360 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1361 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1363 for child in disk.children:
1364 self.assertTrue(isinstance(disk, objects.Disk))
1365 self.assertEqual(child.dev_type, constants.LD_LV)
1366 self.assertTrue(child.children is None)
1368 self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1369 exp_logical_ids[idx])
1371 self.assertEqual(len(disk.children), 2)
1372 self.assertEqual(disk.children[0].size, disk.size)
1373 self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1375 self._CheckIvNames(result, 0, len(disk_info))
1376 cmdlib._UpdateIvNames(0, result)
1377 self._CheckIvNames(result, 0, len(disk_info))
1379 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1380 ("node1334.example.com", "node12272.example.com",
1381 constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1382 ("node1334.example.com", "node12272.example.com",
1383 constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1384 ("node1334.example.com", "node12272.example.com",
1385 constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1389 class _ConfigForDiskWipe:
1390 def __init__(self, exp_node):
1391 self._exp_node = exp_node
1393 def SetDiskID(self, device, node):
1394 assert isinstance(device, objects.Disk)
1395 assert node == self._exp_node
1398 class _RpcForDiskWipe:
1399 def __init__(self, exp_node, pause_cb, wipe_cb):
1400 self._exp_node = exp_node
1401 self._pause_cb = pause_cb
1402 self._wipe_cb = wipe_cb
1404 def call_blockdev_pause_resume_sync(self, node, disks, pause):
1405 assert node == self._exp_node
1406 return rpc.RpcResult(data=self._pause_cb(disks, pause))
1408 def call_blockdev_wipe(self, node, bdev, offset, size):
1409 assert node == self._exp_node
1410 return rpc.RpcResult(data=self._wipe_cb(bdev, offset, size))
1413 class _DiskPauseTracker:
1417 def __call__(self, (disks, instance), pause):
1418 assert not (set(disks) - set(instance.disks))
1420 self.history.extend((i.logical_id, i.size, pause)
1423 return (True, [True] * len(disks))
1426 class _DiskWipeProgressTracker:
1427 def __init__(self, start_offset):
1428 self._start_offset = start_offset
1431 def __call__(self, (disk, _), offset, size):
1432 assert isinstance(offset, (long, int))
1433 assert isinstance(size, (long, int))
1435 max_chunk_size = (disk.size / 100.0 * constants.MIN_WIPE_CHUNK_PERCENT)
1437 assert offset >= self._start_offset
1438 assert (offset + size) <= disk.size
1441 assert size <= constants.MAX_WIPE_CHUNK
1442 assert size <= max_chunk_size
1444 assert offset == self._start_offset or disk.logical_id in self.progress
1446 # Keep track of progress
1447 cur_progress = self.progress.setdefault(disk.logical_id, self._start_offset)
1449 assert cur_progress == offset
1452 self.progress[disk.logical_id] += size
1457 class TestWipeDisks(unittest.TestCase):
1458 def _FailingPauseCb(self, (disks, _), pause):
1459 self.assertEqual(len(disks), 3)
1460 self.assertTrue(pause)
1461 # Simulate an RPC error
1462 return (False, "error")
1464 def testPauseFailure(self):
1465 node_name = "node1372.example.com"
1467 lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, self._FailingPauseCb,
1469 cfg=_ConfigForDiskWipe(node_name))
1472 objects.Disk(dev_type=constants.LD_LV),
1473 objects.Disk(dev_type=constants.LD_LV),
1474 objects.Disk(dev_type=constants.LD_LV),
1477 instance = objects.Instance(name="inst21201",
1478 primary_node=node_name,
1479 disk_template=constants.DT_PLAIN,
1482 self.assertRaises(errors.OpExecError, cmdlib._WipeDisks, lu, instance)
1484 def _FailingWipeCb(self, (disk, _), offset, size):
1485 # This should only ever be called for the first disk
1486 self.assertEqual(disk.logical_id, "disk0")
1487 return (False, None)
1489 def testFailingWipe(self):
1490 node_name = "node13445.example.com"
1491 pt = _DiskPauseTracker()
1493 lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pt, self._FailingWipeCb),
1494 cfg=_ConfigForDiskWipe(node_name))
1497 objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1499 objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1501 objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=256),
1504 instance = objects.Instance(name="inst562",
1505 primary_node=node_name,
1506 disk_template=constants.DT_PLAIN,
1510 cmdlib._WipeDisks(lu, instance)
1511 except errors.OpExecError, err:
1512 self.assertTrue(str(err), "Could not wipe disk 0 at offset 0 ")
1514 self.fail("Did not raise exception")
1516 # Check if all disks were paused and resumed
1517 self.assertEqual(pt.history, [
1518 ("disk0", 100 * 1024, True),
1519 ("disk1", 500 * 1024, True),
1520 ("disk2", 256, True),
1521 ("disk0", 100 * 1024, False),
1522 ("disk1", 500 * 1024, False),
1523 ("disk2", 256, False),
1526 def _PrepareWipeTest(self, start_offset, disks):
1527 node_name = "node-with-offset%s.example.com" % start_offset
1528 pauset = _DiskPauseTracker()
1529 progresst = _DiskWipeProgressTracker(start_offset)
1531 lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pauset, progresst),
1532 cfg=_ConfigForDiskWipe(node_name))
1534 instance = objects.Instance(name="inst3560",
1535 primary_node=node_name,
1536 disk_template=constants.DT_PLAIN,
1539 return (lu, instance, pauset, progresst)
1541 def testNormalWipe(self):
1543 objects.Disk(dev_type=constants.LD_LV, logical_id="disk0", size=1024),
1544 objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1546 objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=128),
1547 objects.Disk(dev_type=constants.LD_LV, logical_id="disk3",
1548 size=constants.MAX_WIPE_CHUNK),
1551 (lu, instance, pauset, progresst) = self._PrepareWipeTest(0, disks)
1553 cmdlib._WipeDisks(lu, instance)
1555 self.assertEqual(pauset.history, [
1556 ("disk0", 1024, True),
1557 ("disk1", 500 * 1024, True),
1558 ("disk2", 128, True),
1559 ("disk3", constants.MAX_WIPE_CHUNK, True),
1560 ("disk0", 1024, False),
1561 ("disk1", 500 * 1024, False),
1562 ("disk2", 128, False),
1563 ("disk3", constants.MAX_WIPE_CHUNK, False),
1566 # Ensure the complete disk has been wiped
1567 self.assertEqual(progresst.progress,
1568 dict((i.logical_id, i.size) for i in disks))
1570 def testWipeWithStartOffset(self):
1571 for start_offset in [0, 280, 8895, 1563204]:
1573 objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1575 objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1576 size=start_offset + (100 * 1024)),
1579 (lu, instance, pauset, progresst) = \
1580 self._PrepareWipeTest(start_offset, disks)
1582 # Test start offset with only one disk
1583 cmdlib._WipeDisks(lu, instance,
1584 disks=[(1, disks[1], start_offset)])
1586 # Only the second disk may have been paused and wiped
1587 self.assertEqual(pauset.history, [
1588 ("disk1", start_offset + (100 * 1024), True),
1589 ("disk1", start_offset + (100 * 1024), False),
1591 self.assertEqual(progresst.progress, {
1592 "disk1": disks[1].size,
1596 class TestDiskSizeInBytesToMebibytes(unittest.TestCase):
1597 def testLessThanOneMebibyte(self):
1598 for i in [1, 2, 7, 512, 1000, 1023]:
1600 result = cmdlib._DiskSizeInBytesToMebibytes(lu, i)
1601 self.assertEqual(result, 1)
1602 self.assertEqual(len(lu.warning_log), 1)
1603 self.assertEqual(len(lu.warning_log[0]), 2)
1604 (_, (warnsize, )) = lu.warning_log[0]
1605 self.assertEqual(warnsize, (1024 * 1024) - i)
1608 for i in [1, 2, 7, 512, 1000, 1023]:
1610 result = cmdlib._DiskSizeInBytesToMebibytes(lu, i * 1024 * 1024)
1611 self.assertEqual(result, i)
1612 self.assertFalse(lu.warning_log)
1614 def testLargeNumber(self):
1615 for i in [1, 2, 7, 512, 1000, 1023, 2724, 12420]:
1616 for j in [1, 2, 486, 326, 986, 1023]:
1618 size = (1024 * 1024 * i) + j
1619 result = cmdlib._DiskSizeInBytesToMebibytes(lu, size)
1620 self.assertEqual(result, i + 1, msg="Amount was not rounded up")
1621 self.assertEqual(len(lu.warning_log), 1)
1622 self.assertEqual(len(lu.warning_log[0]), 2)
1623 (_, (warnsize, )) = lu.warning_log[0]
1624 self.assertEqual(warnsize, (1024 * 1024) - j)
1627 class TestCopyLockList(unittest.TestCase):
1629 self.assertEqual(cmdlib._CopyLockList([]), [])
1630 self.assertEqual(cmdlib._CopyLockList(None), None)
1631 self.assertEqual(cmdlib._CopyLockList(locking.ALL_SET), locking.ALL_SET)
1633 names = ["foo", "bar"]
1634 output = cmdlib._CopyLockList(names)
1635 self.assertEqual(names, output)
1636 self.assertNotEqual(id(names), id(output), msg="List was not copied")
1639 class TestCheckOpportunisticLocking(unittest.TestCase):
1640 class OpTest(opcodes.OpCode):
1642 opcodes._POpportunisticLocking,
1643 opcodes._PIAllocFromDesc(""),
1647 def _MakeOp(cls, **kwargs):
1648 op = cls.OpTest(**kwargs)
1652 def testMissingAttributes(self):
1653 self.assertRaises(AttributeError, cmdlib._CheckOpportunisticLocking,
1656 def testDefaults(self):
1658 cmdlib._CheckOpportunisticLocking(op)
1661 for iallocator in [None, "something", "other"]:
1662 for opplock in [False, True]:
1663 op = self._MakeOp(iallocator=iallocator, opportunistic_locking=opplock)
1664 if opplock and not iallocator:
1665 self.assertRaises(errors.OpPrereqError,
1666 cmdlib._CheckOpportunisticLocking, op)
1668 cmdlib._CheckOpportunisticLocking(op)
1671 class _OpTestVerifyErrors(opcodes.OpCode):
1673 opcodes._PDebugSimulateErrors,
1674 opcodes._PErrorCodes,
1675 opcodes._PIgnoreErrors,
1679 class _LuTestVerifyErrors(cluster._VerifyErrors):
1680 def __init__(self, **kwargs):
1681 cluster._VerifyErrors.__init__(self)
1682 self.op = _OpTestVerifyErrors(**kwargs)
1683 self.op.Validate(True)
1685 self._feedback_fn = self.msglist.append
1688 def DispatchCallError(self, which, *args, **kwargs):
1690 self._Error(*args, **kwargs)
1692 self._ErrorIf(True, *args, **kwargs)
1694 def CallErrorIf(self, c, *args, **kwargs):
1695 self._ErrorIf(c, *args, **kwargs)
1698 class TestVerifyErrors(unittest.TestCase):
1699 # Fake cluster-verify error code structures; we use two arbitary real error
1700 # codes to pass validation of ignore_errors
1701 (_, _ERR1ID, _) = constants.CV_ECLUSTERCFG
1703 _NODENAME = "mynode"
1704 _ERR1CODE = (_NODESTR, _ERR1ID, "Error one")
1705 (_, _ERR2ID, _) = constants.CV_ECLUSTERCERT
1706 _INSTSTR = "instance"
1707 _INSTNAME = "myinstance"
1708 _ERR2CODE = (_INSTSTR, _ERR2ID, "Error two")
1709 # Arguments used to call _Error() or _ErrorIf()
1710 _ERR1ARGS = (_ERR1CODE, _NODENAME, "Error1 is %s", "an error")
1711 _ERR2ARGS = (_ERR2CODE, _INSTNAME, "Error2 has no argument")
1712 # Expected error messages
1713 _ERR1MSG = _ERR1ARGS[2] % _ERR1ARGS[3]
1714 _ERR2MSG = _ERR2ARGS[2]
1716 def testNoError(self):
1717 lu = _LuTestVerifyErrors()
1718 lu.CallErrorIf(False, self._ERR1CODE, *self._ERR1ARGS)
1719 self.assertFalse(lu.bad)
1720 self.assertFalse(lu.msglist)
1722 def _InitTest(self, **kwargs):
1723 self.lu1 = _LuTestVerifyErrors(**kwargs)
1724 self.lu2 = _LuTestVerifyErrors(**kwargs)
1726 def _CallError(self, *args, **kwargs):
1727 # Check that _Error() and _ErrorIf() produce the same results
1728 self.lu1.DispatchCallError(True, *args, **kwargs)
1729 self.lu2.DispatchCallError(False, *args, **kwargs)
1730 self.assertEqual(self.lu1.bad, self.lu2.bad)
1731 self.assertEqual(self.lu1.msglist, self.lu2.msglist)
1732 # Test-specific checks are made on one LU
1735 def _checkMsgCommon(self, logstr, errmsg, itype, item, warning):
1736 self.assertTrue(errmsg in logstr)
1738 self.assertTrue("WARNING" in logstr)
1740 self.assertTrue("ERROR" in logstr)
1741 self.assertTrue(itype in logstr)
1742 self.assertTrue(item in logstr)
1744 def _checkMsg1(self, logstr, warning=False):
1745 self._checkMsgCommon(logstr, self._ERR1MSG, self._NODESTR,
1746 self._NODENAME, warning)
1748 def _checkMsg2(self, logstr, warning=False):
1749 self._checkMsgCommon(logstr, self._ERR2MSG, self._INSTSTR,
1750 self._INSTNAME, warning)
1752 def testPlain(self):
1754 lu = self._CallError(*self._ERR1ARGS)
1755 self.assertTrue(lu.bad)
1756 self.assertEqual(len(lu.msglist), 1)
1757 self._checkMsg1(lu.msglist[0])
1759 def testMultiple(self):
1761 self._CallError(*self._ERR1ARGS)
1762 lu = self._CallError(*self._ERR2ARGS)
1763 self.assertTrue(lu.bad)
1764 self.assertEqual(len(lu.msglist), 2)
1765 self._checkMsg1(lu.msglist[0])
1766 self._checkMsg2(lu.msglist[1])
1768 def testIgnore(self):
1769 self._InitTest(ignore_errors=[self._ERR1ID])
1770 lu = self._CallError(*self._ERR1ARGS)
1771 self.assertFalse(lu.bad)
1772 self.assertEqual(len(lu.msglist), 1)
1773 self._checkMsg1(lu.msglist[0], warning=True)
1775 def testWarning(self):
1777 lu = self._CallError(*self._ERR1ARGS,
1778 code=_LuTestVerifyErrors.ETYPE_WARNING)
1779 self.assertFalse(lu.bad)
1780 self.assertEqual(len(lu.msglist), 1)
1781 self._checkMsg1(lu.msglist[0], warning=True)
1783 def testWarning2(self):
1785 self._CallError(*self._ERR1ARGS)
1786 lu = self._CallError(*self._ERR2ARGS,
1787 code=_LuTestVerifyErrors.ETYPE_WARNING)
1788 self.assertTrue(lu.bad)
1789 self.assertEqual(len(lu.msglist), 2)
1790 self._checkMsg1(lu.msglist[0])
1791 self._checkMsg2(lu.msglist[1], warning=True)
1793 def testDebugSimulate(self):
1794 lu = _LuTestVerifyErrors(debug_simulate_errors=True)
1795 lu.CallErrorIf(False, *self._ERR1ARGS)
1796 self.assertTrue(lu.bad)
1797 self.assertEqual(len(lu.msglist), 1)
1798 self._checkMsg1(lu.msglist[0])
1800 def testErrCodes(self):
1801 self._InitTest(error_codes=True)
1802 lu = self._CallError(*self._ERR1ARGS)
1803 self.assertTrue(lu.bad)
1804 self.assertEqual(len(lu.msglist), 1)
1805 self._checkMsg1(lu.msglist[0])
1806 self.assertTrue(self._ERR1ID in lu.msglist[0])
1809 class TestGetUpdatedIPolicy(unittest.TestCase):
1810 """Tests for cmdlib._GetUpdatedIPolicy()"""
1811 _OLD_CLUSTER_POLICY = {
1812 constants.IPOLICY_VCPU_RATIO: 1.5,
1813 constants.ISPECS_MINMAX: [
1815 constants.ISPECS_MIN: {
1816 constants.ISPEC_MEM_SIZE: 32768,
1817 constants.ISPEC_CPU_COUNT: 8,
1818 constants.ISPEC_DISK_COUNT: 1,
1819 constants.ISPEC_DISK_SIZE: 1024,
1820 constants.ISPEC_NIC_COUNT: 1,
1821 constants.ISPEC_SPINDLE_USE: 1,
1823 constants.ISPECS_MAX: {
1824 constants.ISPEC_MEM_SIZE: 65536,
1825 constants.ISPEC_CPU_COUNT: 10,
1826 constants.ISPEC_DISK_COUNT: 5,
1827 constants.ISPEC_DISK_SIZE: 1024 * 1024,
1828 constants.ISPEC_NIC_COUNT: 3,
1829 constants.ISPEC_SPINDLE_USE: 12,
1832 constants.ISPECS_MINMAX_DEFAULTS,
1834 constants.ISPECS_STD: constants.IPOLICY_DEFAULTS[constants.ISPECS_STD],
1836 _OLD_GROUP_POLICY = {
1837 constants.IPOLICY_SPINDLE_RATIO: 2.5,
1838 constants.ISPECS_MINMAX: [{
1839 constants.ISPECS_MIN: {
1840 constants.ISPEC_MEM_SIZE: 128,
1841 constants.ISPEC_CPU_COUNT: 1,
1842 constants.ISPEC_DISK_COUNT: 1,
1843 constants.ISPEC_DISK_SIZE: 1024,
1844 constants.ISPEC_NIC_COUNT: 1,
1845 constants.ISPEC_SPINDLE_USE: 1,
1847 constants.ISPECS_MAX: {
1848 constants.ISPEC_MEM_SIZE: 32768,
1849 constants.ISPEC_CPU_COUNT: 8,
1850 constants.ISPEC_DISK_COUNT: 5,
1851 constants.ISPEC_DISK_SIZE: 1024 * 1024,
1852 constants.ISPEC_NIC_COUNT: 3,
1853 constants.ISPEC_SPINDLE_USE: 12,
1858 def _TestSetSpecs(self, old_policy, isgroup):
1860 constants.ISPECS_MIN: {
1861 constants.ISPEC_MEM_SIZE: 64,
1862 constants.ISPEC_CPU_COUNT: 1,
1863 constants.ISPEC_DISK_COUNT: 2,
1864 constants.ISPEC_DISK_SIZE: 64,
1865 constants.ISPEC_NIC_COUNT: 1,
1866 constants.ISPEC_SPINDLE_USE: 1,
1868 constants.ISPECS_MAX: {
1869 constants.ISPEC_MEM_SIZE: 16384,
1870 constants.ISPEC_CPU_COUNT: 10,
1871 constants.ISPEC_DISK_COUNT: 12,
1872 constants.ISPEC_DISK_SIZE: 1024,
1873 constants.ISPEC_NIC_COUNT: 9,
1874 constants.ISPEC_SPINDLE_USE: 18,
1878 constants.ISPEC_DISK_COUNT: 10,
1879 constants.ISPEC_DISK_SIZE: 512,
1882 constants.ISPECS_MINMAX: diff_minmax
1885 diff_policy[constants.ISPECS_STD] = diff_std
1886 new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1887 group_policy=isgroup)
1889 self.assertTrue(constants.ISPECS_MINMAX in new_policy)
1890 self.assertEqual(new_policy[constants.ISPECS_MINMAX], diff_minmax)
1891 for key in old_policy:
1892 if not key in diff_policy:
1893 self.assertTrue(key in new_policy)
1894 self.assertEqual(new_policy[key], old_policy[key])
1897 new_std = new_policy[constants.ISPECS_STD]
1898 for key in diff_std:
1899 self.assertTrue(key in new_std)
1900 self.assertEqual(new_std[key], diff_std[key])
1901 old_std = old_policy.get(constants.ISPECS_STD, {})
1903 self.assertTrue(key in new_std)
1904 if key not in diff_std:
1905 self.assertEqual(new_std[key], old_std[key])
1907 def _TestSet(self, old_policy, diff_policy, isgroup):
1908 new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1909 group_policy=isgroup)
1910 for key in diff_policy:
1911 self.assertTrue(key in new_policy)
1912 self.assertEqual(new_policy[key], diff_policy[key])
1913 for key in old_policy:
1914 if not key in diff_policy:
1915 self.assertTrue(key in new_policy)
1916 self.assertEqual(new_policy[key], old_policy[key])
1920 constants.IPOLICY_VCPU_RATIO: 3,
1921 constants.IPOLICY_DTS: [constants.DT_FILE],
1923 self._TestSet(self._OLD_GROUP_POLICY, diff_policy, True)
1924 self._TestSetSpecs(self._OLD_GROUP_POLICY, True)
1925 self._TestSet({}, diff_policy, True)
1926 self._TestSetSpecs({}, True)
1927 self._TestSet(self._OLD_CLUSTER_POLICY, diff_policy, False)
1928 self._TestSetSpecs(self._OLD_CLUSTER_POLICY, False)
1930 def testUnset(self):
1931 old_policy = self._OLD_GROUP_POLICY
1933 constants.IPOLICY_SPINDLE_RATIO: constants.VALUE_DEFAULT,
1935 new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1937 for key in diff_policy:
1938 self.assertFalse(key in new_policy)
1939 for key in old_policy:
1940 if not key in diff_policy:
1941 self.assertTrue(key in new_policy)
1942 self.assertEqual(new_policy[key], old_policy[key])
1944 self.assertRaises(errors.OpPrereqError, common._GetUpdatedIPolicy,
1945 old_policy, diff_policy, group_policy=False)
1947 def testUnsetEmpty(self):
1949 for key in constants.IPOLICY_ALL_KEYS:
1951 key: constants.VALUE_DEFAULT,
1953 new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1955 self.assertEqual(new_policy, old_policy)
1957 def _TestInvalidKeys(self, old_policy, isgroup):
1958 INVALID_KEY = "this_key_shouldnt_be_allowed"
1962 invalid_policy = INVALID_DICT
1963 self.assertRaises(errors.OpPrereqError, common._GetUpdatedIPolicy,
1964 old_policy, invalid_policy, group_policy=isgroup)
1966 constants.ISPECS_MINMAX: [INVALID_DICT],
1968 self.assertRaises(errors.TypeEnforcementError, common._GetUpdatedIPolicy,
1969 old_policy, invalid_ispecs, group_policy=isgroup)
1971 invalid_for_group = {
1972 constants.ISPECS_STD: constants.IPOLICY_DEFAULTS[constants.ISPECS_STD],
1974 self.assertRaises(errors.OpPrereqError, common._GetUpdatedIPolicy,
1975 old_policy, invalid_for_group, group_policy=isgroup)
1976 good_ispecs = self._OLD_CLUSTER_POLICY[constants.ISPECS_MINMAX]
1977 invalid_ispecs = copy.deepcopy(good_ispecs)
1979 constants.ISPECS_MINMAX: invalid_ispecs,
1981 for minmax in invalid_ispecs:
1982 for key in constants.ISPECS_MINMAX_KEYS:
1984 ispec[INVALID_KEY] = None
1985 self.assertRaises(errors.TypeEnforcementError,
1986 common._GetUpdatedIPolicy, old_policy,
1987 invalid_policy, group_policy=isgroup)
1988 del ispec[INVALID_KEY]
1989 for par in constants.ISPECS_PARAMETERS:
1991 ispec[par] = "this_is_not_good"
1992 self.assertRaises(errors.TypeEnforcementError,
1993 common._GetUpdatedIPolicy,
1994 old_policy, invalid_policy, group_policy=isgroup)
1996 # This is to make sure that no two errors were present during the tests
1997 common._GetUpdatedIPolicy(old_policy, invalid_policy,
1998 group_policy=isgroup)
2000 def testInvalidKeys(self):
2001 self._TestInvalidKeys(self._OLD_GROUP_POLICY, True)
2002 self._TestInvalidKeys(self._OLD_CLUSTER_POLICY, False)
2004 def testInvalidValues(self):
2005 for par in (constants.IPOLICY_PARAMETERS |
2006 frozenset([constants.IPOLICY_DTS])):
2008 par: "invalid_value",
2010 self.assertRaises(errors.OpPrereqError, common._GetUpdatedIPolicy, {},
2011 bad_policy, group_policy=True)
2013 if __name__ == "__main__":
2014 testutils.GanetiTestProgram()