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 group
39 from ganeti.cmdlib import instance
40 from ganeti.cmdlib import instance_storage
41 from ganeti.cmdlib import instance_utils
42 from ganeti.cmdlib import common
43 from ganeti.cmdlib import query
44 from ganeti import opcodes
45 from ganeti import errors
46 from ganeti import utils
47 from ganeti import luxi
49 from ganeti import objects
50 from ganeti import compat
51 from ganeti import rpc
52 from ganeti import locking
53 from ganeti import pathutils
54 from ganeti.masterd import iallocator
55 from ganeti.hypervisor import hv_xen
61 class TestCertVerification(testutils.GanetiTestCase):
63 testutils.GanetiTestCase.setUp(self)
65 self.tmpdir = tempfile.mkdtemp()
68 shutil.rmtree(self.tmpdir)
70 def testVerifyCertificate(self):
71 cluster._VerifyCertificate(testutils.TestDataFilename("cert1.pem"))
73 nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
75 (errcode, msg) = cluster._VerifyCertificate(nonexist_filename)
76 self.assertEqual(errcode, cluster.LUClusterVerifyConfig.ETYPE_ERROR)
78 # Try to load non-certificate file
79 invalid_cert = testutils.TestDataFilename("bdev-net.txt")
80 (errcode, msg) = cluster._VerifyCertificate(invalid_cert)
81 self.assertEqual(errcode, cluster.LUClusterVerifyConfig.ETYPE_ERROR)
84 class TestOpcodeParams(testutils.GanetiTestCase):
85 def testParamsStructures(self):
86 for op in sorted(mcpu.Processor.DISPATCH_TABLE):
87 lu = mcpu.Processor.DISPATCH_TABLE[op]
89 self.failIf(hasattr(lu, "_OP_REQP"),
90 msg=("LU '%s' has old-style _OP_REQP" % lu_name))
91 self.failIf(hasattr(lu, "_OP_DEFS"),
92 msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
93 self.failIf(hasattr(lu, "_OP_PARAMS"),
94 msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
97 class TestIAllocatorChecks(testutils.GanetiTestCase):
98 def testFunction(self):
100 def __init__(self, opcode):
101 self.cfg = mocks.FakeConfig()
104 class OpTest(opcodes.OpCode):
106 ("iallocator", None, ht.NoType, None),
107 ("node", None, ht.NoType, None),
110 default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
111 other_iallocator = default_iallocator + "_not"
116 c_i = lambda: common.CheckIAllocatorOrNode(lu, "iallocator", "node")
118 # Neither node nor iallocator given
123 self.assertEqual(lu.op.iallocator, default_iallocator)
124 self.assertEqual(lu.op.node, n)
126 # Both, iallocator and node given
127 for a in ("test", constants.DEFAULT_IALLOCATOR_SHORTCUT):
130 self.assertRaises(errors.OpPrereqError, c_i)
132 # Only iallocator given
134 op.iallocator = other_iallocator
137 self.assertEqual(lu.op.iallocator, other_iallocator)
138 self.assertEqual(lu.op.node, n)
144 self.assertEqual(lu.op.iallocator, None)
145 self.assertEqual(lu.op.node, "node")
147 # Asked for default iallocator, no node given
148 op.iallocator = constants.DEFAULT_IALLOCATOR_SHORTCUT
151 self.assertEqual(lu.op.iallocator, default_iallocator)
152 self.assertEqual(lu.op.node, None)
154 # No node, iallocator or default iallocator
157 lu.cfg.GetDefaultIAllocator = lambda: None
158 self.assertRaises(errors.OpPrereqError, c_i)
161 class TestLUTestJqueue(unittest.TestCase):
163 self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
164 (luxi.WFJC_TIMEOUT * 0.75),
165 msg=("Client timeout too high, might not notice bugs"
166 " in WaitForJobChange"))
169 class TestLUQuery(unittest.TestCase):
171 self.assertEqual(sorted(query._QUERY_IMPL.keys()),
172 sorted(constants.QR_VIA_OP))
174 assert constants.QR_NODE in constants.QR_VIA_OP
175 assert constants.QR_INSTANCE in constants.QR_VIA_OP
177 for i in constants.QR_VIA_OP:
178 self.assert_(query._GetQueryImplementation(i))
180 self.assertRaises(errors.OpPrereqError, query._GetQueryImplementation,
182 self.assertRaises(errors.OpPrereqError, query._GetQueryImplementation,
186 class TestLUGroupAssignNodes(unittest.TestCase):
188 def testCheckAssignmentForSplitInstances(self):
189 node_data = dict((n, objects.Node(name=n, group=g))
190 for (n, g) in [("n1a", "g1"), ("n1b", "g1"),
191 ("n2a", "g2"), ("n2b", "g2"),
192 ("n3a", "g3"), ("n3b", "g3"),
196 def Instance(uuid, pnode, snode):
199 disk_template = constants.DT_DISKLESS
201 disks = [objects.Disk(dev_type=constants.DT_DRBD8,
202 logical_id=[pnode, snode, 1, 17, 17])]
203 disk_template = constants.DT_DRBD8
205 return objects.Instance(name="%s-name" % uuid, uuid="%s" % uuid,
206 primary_node=pnode, disks=disks,
207 disk_template=disk_template)
209 instance_data = dict((uuid, Instance(uuid, pnode, snode))
210 for uuid, pnode, snode in [("inst1a", "n1a", "n1b"),
211 ("inst1b", "n1b", "n1a"),
212 ("inst2a", "n2a", "n2b"),
213 ("inst3a", "n3a", None),
214 ("inst3b", "n3b", "n1b"),
215 ("inst3c", "n3b", "n2b"),
218 # Test first with the existing state.
220 group.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
224 self.assertEqual([], new)
225 self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
227 # And now some changes.
229 group.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
234 self.assertEqual(set(["inst1a", "inst1b"]), set(new))
235 self.assertEqual(set(["inst3c"]), set(prev))
238 class TestClusterVerifySsh(unittest.TestCase):
239 def testMultipleGroups(self):
240 fn = cluster.LUClusterVerifyGroup._SelectSshCheckNodes
242 objects.Node(name="node20", group="my", offline=False),
243 objects.Node(name="node21", group="my", offline=False),
244 objects.Node(name="node22", group="my", offline=False),
245 objects.Node(name="node23", group="my", offline=False),
246 objects.Node(name="node24", group="my", offline=False),
247 objects.Node(name="node25", group="my", offline=False),
248 objects.Node(name="node26", group="my", offline=True),
251 objects.Node(name="node1", group="g1", offline=True),
252 objects.Node(name="node2", group="g1", offline=False),
253 objects.Node(name="node3", group="g1", offline=False),
254 objects.Node(name="node4", group="g1", offline=True),
255 objects.Node(name="node5", group="g1", offline=False),
256 objects.Node(name="node10", group="xyz", offline=False),
257 objects.Node(name="node11", group="xyz", offline=False),
258 objects.Node(name="node40", group="alloff", offline=True),
259 objects.Node(name="node41", group="alloff", offline=True),
260 objects.Node(name="node50", group="aaa", offline=False),
262 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
264 (online, perhost) = fn(mygroupnodes, "my", nodes)
265 self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
266 self.assertEqual(set(perhost.keys()), set(online))
268 self.assertEqual(perhost, {
269 "node20": ["node10", "node2", "node50"],
270 "node21": ["node11", "node3", "node50"],
271 "node22": ["node10", "node5", "node50"],
272 "node23": ["node11", "node2", "node50"],
273 "node24": ["node10", "node3", "node50"],
274 "node25": ["node11", "node5", "node50"],
277 def testSingleGroup(self):
278 fn = cluster.LUClusterVerifyGroup._SelectSshCheckNodes
280 objects.Node(name="node1", group="default", offline=True),
281 objects.Node(name="node2", group="default", offline=False),
282 objects.Node(name="node3", group="default", offline=False),
283 objects.Node(name="node4", group="default", offline=True),
285 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
287 (online, perhost) = fn(nodes, "default", nodes)
288 self.assertEqual(online, ["node2", "node3"])
289 self.assertEqual(set(perhost.keys()), set(online))
291 self.assertEqual(perhost, {
297 class TestClusterVerifyFiles(unittest.TestCase):
299 def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
300 assert ((ecode == constants.CV_ENODEFILECHECK and
301 ht.TNonEmptyString(item)) or
302 (ecode == constants.CV_ECLUSTERFILECHECK and
309 errors.append((item, msg))
314 objects.Node(name="master.example.com",
318 objects.Node(name="node2.example.com",
322 objects.Node(name="node3.example.com",
324 master_candidate=True,
326 objects.Node(name="node4.example.com",
330 objects.Node(name="nodata.example.com",
334 objects.Node(name="offline.example.com",
339 pathutils.CLUSTER_DOMAIN_SECRET_FILE,
340 pathutils.RAPI_CERT_FILE,
341 pathutils.RAPI_USERS_FILE,
344 pathutils.RAPI_USERS_FILE,
345 hv_xen.XL_CONFIG_FILE,
346 pathutils.VNC_PASSWORD_FILE,
349 pathutils.CLUSTER_CONF_FILE,
352 hv_xen.XEND_CONFIG_FILE,
353 hv_xen.XL_CONFIG_FILE,
354 pathutils.VNC_PASSWORD_FILE,
357 "master-uuid": rpc.RpcResult(data=(True, {
358 constants.NV_FILELIST: {
359 pathutils.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
360 pathutils.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
361 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
362 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
363 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
365 "node2-uuid": rpc.RpcResult(data=(True, {
366 constants.NV_FILELIST: {
367 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
368 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
371 "node3-uuid": rpc.RpcResult(data=(True, {
372 constants.NV_FILELIST: {
373 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
374 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
377 "node4-uuid": rpc.RpcResult(data=(True, {
378 constants.NV_FILELIST: {
379 pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
380 pathutils.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
381 pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
382 pathutils.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
383 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
386 "nodata-uuid": rpc.RpcResult(data=(True, {})),
387 "offline-uuid": rpc.RpcResult(offline=True),
389 assert set(nvinfo.keys()) == set(map(operator.attrgetter("uuid"), nodeinfo))
391 verify_lu = cluster.LUClusterVerifyGroup(mocks.FakeProc(),
392 opcodes.OpClusterVerify(),
396 verify_lu._ErrorIf = compat.partial(self._FakeErrorIf, errors)
398 # TODO: That's a bit hackish to mock only this single method. We should
399 # build a better FakeConfig which provides such a feature already.
400 def GetNodeName(node_uuid):
401 for node in nodeinfo:
402 if node.uuid == node_uuid:
406 verify_lu.cfg.GetNodeName = GetNodeName
408 verify_lu._VerifyFiles(nodeinfo, "master-uuid", nvinfo,
409 (files_all, files_opt, files_mc, files_vm))
410 self.assertEqual(sorted(errors), sorted([
411 (None, ("File %s found with 2 different checksums (variant 1 on"
412 " node2.example.com, node3.example.com, node4.example.com;"
413 " variant 2 on master.example.com)" % pathutils.RAPI_CERT_FILE)),
414 (None, ("File %s is missing from node(s) node2.example.com" %
415 pathutils.CLUSTER_DOMAIN_SECRET_FILE)),
416 (None, ("File %s should not exist on node(s) node4.example.com" %
417 pathutils.CLUSTER_CONF_FILE)),
418 (None, ("File %s is missing from node(s) node4.example.com" %
419 hv_xen.XEND_CONFIG_FILE)),
420 (None, ("File %s is missing from node(s) node3.example.com" %
421 pathutils.CLUSTER_CONF_FILE)),
422 (None, ("File %s found with 2 different checksums (variant 1 on"
423 " master.example.com; variant 2 on node4.example.com)" %
424 pathutils.CLUSTER_CONF_FILE)),
425 (None, ("File %s is optional, but it must exist on all or no nodes (not"
426 " found on master.example.com, node2.example.com,"
427 " node3.example.com)" % pathutils.RAPI_USERS_FILE)),
428 (None, ("File %s is optional, but it must exist on all or no nodes (not"
429 " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
430 ("nodata.example.com", "Node did not return file checksum data"),
435 def __init__(self, cfg=NotImplemented, proc=NotImplemented,
437 self.warning_log = []
443 def LogWarning(self, text, *args):
444 self.warning_log.append((text, args))
446 def LogInfo(self, text, *args):
447 self.info_log.append((text, args))
450 class TestLoadNodeEvacResult(unittest.TestCase):
451 def testSuccess(self):
453 ("inst20153.example.com", "grp2", ["nodeA4509", "nodeB2912"]),
455 for early_release in [False, True]:
456 for use_nodes in [False, True]:
458 [opcodes.OpInstanceReplaceDisks().__getstate__()],
459 [opcodes.OpInstanceMigrate().__getstate__()],
462 alloc_result = (moved, [], jobs)
463 assert iallocator._NEVAC_RESULT(alloc_result)
466 result = common.LoadNodeEvacResult(lu, alloc_result,
467 early_release, use_nodes)
470 (_, (info_args, )) = lu.info_log.pop(0)
471 for (instname, instgroup, instnodes) in moved:
472 self.assertTrue(instname in info_args)
475 self.assertTrue(i in info_args)
477 self.assertTrue(instgroup in info_args)
479 self.assertFalse(lu.info_log)
480 self.assertFalse(lu.warning_log)
482 for op in itertools.chain(*result):
483 if hasattr(op.__class__, "early_release"):
484 self.assertEqual(op.early_release, early_release)
486 self.assertFalse(hasattr(op, "early_release"))
488 def testFailed(self):
489 alloc_result = ([], [
490 ("inst5191.example.com", "errormsg21178"),
492 assert iallocator._NEVAC_RESULT(alloc_result)
495 self.assertRaises(errors.OpExecError, common.LoadNodeEvacResult,
496 lu, alloc_result, False, False)
497 self.assertFalse(lu.info_log)
498 (_, (args, )) = lu.warning_log.pop(0)
499 self.assertTrue("inst5191.example.com" in args)
500 self.assertTrue("errormsg21178" in args)
501 self.assertFalse(lu.warning_log)
504 class TestUpdateAndVerifySubDict(unittest.TestCase):
507 "a": constants.VTYPE_INT,
508 "b": constants.VTYPE_STRING,
509 "c": constants.VTYPE_BOOL,
510 "d": constants.VTYPE_STRING,
557 verified = common._UpdateAndVerifySubDict(old_test, test, self.type_check)
558 self.assertEqual(verified, mv)
574 self.assertRaises(errors.TypeEnforcementError,
575 common._UpdateAndVerifySubDict, {}, test,
579 class TestHvStateHelper(unittest.TestCase):
580 def testWithoutOpData(self):
581 self.assertEqual(common.MergeAndVerifyHvState(None, NotImplemented),
584 def testWithoutOldData(self):
586 constants.HT_XEN_PVM: {
587 constants.HVST_MEMORY_TOTAL: 4096,
590 self.assertEqual(common.MergeAndVerifyHvState(new, None), new)
592 def testWithWrongHv(self):
595 constants.HVST_MEMORY_TOTAL: 4096,
598 self.assertRaises(errors.OpPrereqError, common.MergeAndVerifyHvState,
601 class TestDiskStateHelper(unittest.TestCase):
602 def testWithoutOpData(self):
603 self.assertEqual(common.MergeAndVerifyDiskState(None, NotImplemented),
606 def testWithoutOldData(self):
608 constants.DT_PLAIN: {
610 constants.DS_DISK_RESERVED: 1024,
614 self.assertEqual(common.MergeAndVerifyDiskState(new, None), new)
616 def testWithWrongStorageType(self):
620 constants.DS_DISK_RESERVED: 1024,
624 self.assertRaises(errors.OpPrereqError, common.MergeAndVerifyDiskState,
628 class TestComputeMinMaxSpec(unittest.TestCase):
631 constants.ISPECS_MAX: {
632 constants.ISPEC_MEM_SIZE: 512,
633 constants.ISPEC_DISK_SIZE: 1024,
635 constants.ISPECS_MIN: {
636 constants.ISPEC_MEM_SIZE: 128,
637 constants.ISPEC_DISK_COUNT: 1,
641 def testNoneValue(self):
642 self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
643 self.ispecs, None) is None)
645 def testAutoValue(self):
646 self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
648 constants.VALUE_AUTO) is None)
650 def testNotDefined(self):
651 self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT, None,
652 self.ispecs, 3) is None)
654 def testNoMinDefined(self):
655 self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE, None,
656 self.ispecs, 128) is None)
658 def testNoMaxDefined(self):
659 self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT,
660 None, self.ispecs, 16) is None)
662 def testOutOfRange(self):
663 for (name, val) in ((constants.ISPEC_MEM_SIZE, 64),
664 (constants.ISPEC_MEM_SIZE, 768),
665 (constants.ISPEC_DISK_SIZE, 4096),
666 (constants.ISPEC_DISK_COUNT, 0)):
667 min_v = self.ispecs[constants.ISPECS_MIN].get(name, val)
668 max_v = self.ispecs[constants.ISPECS_MAX].get(name, val)
669 self.assertEqual(common._ComputeMinMaxSpec(name, None,
671 "%s value %s is not in range [%s, %s]" %
672 (name, val,min_v, max_v))
673 self.assertEqual(common._ComputeMinMaxSpec(name, "1",
675 "%s/1 value %s is not in range [%s, %s]" %
676 (name, val,min_v, max_v))
679 for (name, val) in ((constants.ISPEC_MEM_SIZE, 256),
680 (constants.ISPEC_MEM_SIZE, 128),
681 (constants.ISPEC_MEM_SIZE, 512),
682 (constants.ISPEC_DISK_SIZE, 1024),
683 (constants.ISPEC_DISK_SIZE, 0),
684 (constants.ISPEC_DISK_COUNT, 1),
685 (constants.ISPEC_DISK_COUNT, 5)):
686 self.assertTrue(common._ComputeMinMaxSpec(name, None, self.ispecs, val)
690 def _ValidateComputeMinMaxSpec(name, *_):
691 assert name in constants.ISPECS_PARAMETERS
695 def _NoDiskComputeMinMaxSpec(name, *_):
696 if name == constants.ISPEC_DISK_COUNT:
703 def __init__(self, spec):
706 def ComputeMinMaxSpec(self, *args):
707 return self.spec.pop(0)
710 class TestComputeIPolicySpecViolation(unittest.TestCase):
711 # Minimal policy accepted by _ComputeIPolicySpecViolation()
713 constants.IPOLICY_DTS: [constants.DT_PLAIN, constants.DT_DISKLESS],
714 constants.ISPECS_MINMAX: [NotImplemented],
718 compute_fn = _ValidateComputeMinMaxSpec
719 ret = common.ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
720 [1024], 1, constants.DT_PLAIN,
721 _compute_fn=compute_fn)
722 self.assertEqual(ret, [])
724 def testDiskFull(self):
725 compute_fn = _NoDiskComputeMinMaxSpec
726 ret = common.ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
727 [1024], 1, constants.DT_PLAIN,
728 _compute_fn=compute_fn)
729 self.assertEqual(ret, [constants.ISPEC_DISK_COUNT])
731 def testDiskLess(self):
732 compute_fn = _NoDiskComputeMinMaxSpec
733 ret = common.ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
734 [1024], 1, constants.DT_DISKLESS,
735 _compute_fn=compute_fn)
736 self.assertEqual(ret, [])
738 def testWrongTemplates(self):
739 compute_fn = _ValidateComputeMinMaxSpec
740 ret = common.ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
741 [1024], 1, constants.DT_DRBD8,
742 _compute_fn=compute_fn)
743 self.assertEqual(len(ret), 1)
744 self.assertTrue("Disk template" in ret[0])
746 def testInvalidArguments(self):
747 self.assertRaises(AssertionError, common.ComputeIPolicySpecViolation,
748 self._MICRO_IPOL, 1024, 1, 1, 1, [], 1,
751 def testInvalidSpec(self):
752 spec = _SpecWrapper([None, False, "foo", None, "bar", None])
753 compute_fn = spec.ComputeMinMaxSpec
754 ret = common.ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
755 [1024], 1, constants.DT_PLAIN,
756 _compute_fn=compute_fn)
757 self.assertEqual(ret, ["foo", "bar"])
758 self.assertFalse(spec.spec)
760 def testWithIPolicy(self):
767 disk_template = "mytemplate"
769 constants.ISPEC_MEM_SIZE: mem_size,
770 constants.ISPEC_CPU_COUNT: cpu_count,
771 constants.ISPEC_DISK_COUNT: disk_count,
772 constants.ISPEC_DISK_SIZE: disk_sizes[0],
773 constants.ISPEC_NIC_COUNT: nic_count,
774 constants.ISPEC_SPINDLE_USE: spindle_use,
777 constants.ISPECS_MINMAX: [{
778 constants.ISPECS_MIN: ispec,
779 constants.ISPECS_MAX: ispec,
781 constants.IPOLICY_DTS: [disk_template],
783 ispec_copy = copy.deepcopy(ispec)
785 constants.ISPECS_MINMAX: [
787 constants.ISPECS_MIN: ispec_copy,
788 constants.ISPECS_MAX: ispec_copy,
791 constants.ISPECS_MIN: ispec,
792 constants.ISPECS_MAX: ispec,
795 constants.IPOLICY_DTS: [disk_template],
798 constants.ISPECS_MINMAX: [
800 constants.ISPECS_MIN: ispec,
801 constants.ISPECS_MAX: ispec,
804 constants.ISPECS_MIN: ispec_copy,
805 constants.ISPECS_MAX: ispec_copy,
808 constants.IPOLICY_DTS: [disk_template],
810 def AssertComputeViolation(ipolicy, violations):
811 ret = common.ComputeIPolicySpecViolation(ipolicy, mem_size, cpu_count,
812 disk_count, nic_count,
813 disk_sizes, spindle_use,
815 self.assertEqual(len(ret), violations)
817 AssertComputeViolation(ipolicy1, 0)
818 AssertComputeViolation(ipolicy2, 0)
819 AssertComputeViolation(ipolicy3, 0)
820 for par in constants.ISPECS_PARAMETERS:
822 AssertComputeViolation(ipolicy1, 1)
823 AssertComputeViolation(ipolicy2, 0)
824 AssertComputeViolation(ipolicy3, 0)
826 AssertComputeViolation(ipolicy1, 1)
827 AssertComputeViolation(ipolicy2, 0)
828 AssertComputeViolation(ipolicy3, 0)
829 ispec[par] += 1 # Restore
830 ipolicy1[constants.IPOLICY_DTS] = ["another_template"]
831 AssertComputeViolation(ipolicy1, 1)
834 class _StubComputeIPolicySpecViolation:
835 def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
836 spindle_use, disk_template):
837 self.mem_size = mem_size
838 self.cpu_count = cpu_count
839 self.disk_count = disk_count
840 self.nic_count = nic_count
841 self.disk_sizes = disk_sizes
842 self.spindle_use = spindle_use
843 self.disk_template = disk_template
845 def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
846 spindle_use, disk_template):
847 assert self.mem_size == mem_size
848 assert self.cpu_count == cpu_count
849 assert self.disk_count == disk_count
850 assert self.nic_count == nic_count
851 assert self.disk_sizes == disk_sizes
852 assert self.spindle_use == spindle_use
853 assert self.disk_template == disk_template
858 class _FakeConfigForComputeIPolicyInstanceViolation:
859 def __init__(self, be, excl_stor):
860 self.cluster = objects.Cluster(beparams={"default": be})
861 self.excl_stor = excl_stor
863 def GetClusterInfo(self):
866 def GetNodeInfo(self, _):
869 def GetNdParams(self, _):
871 constants.ND_EXCLUSIVE_STORAGE: self.excl_stor,
875 class TestComputeIPolicyInstanceViolation(unittest.TestCase):
878 constants.BE_MAXMEM: 2048,
879 constants.BE_VCPUS: 2,
880 constants.BE_SPINDLE_USE: 4,
882 disks = [objects.Disk(size=512, spindles=13)]
883 cfg = _FakeConfigForComputeIPolicyInstanceViolation(beparams, False)
884 instance = objects.Instance(beparams=beparams, disks=disks, nics=[],
885 disk_template=constants.DT_PLAIN)
886 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 4,
888 ret = common.ComputeIPolicyInstanceViolation(NotImplemented, instance,
889 cfg, _compute_fn=stub)
890 self.assertEqual(ret, [])
891 instance2 = objects.Instance(beparams={}, disks=disks, nics=[],
892 disk_template=constants.DT_PLAIN)
893 ret = common.ComputeIPolicyInstanceViolation(NotImplemented, instance2,
894 cfg, _compute_fn=stub)
895 self.assertEqual(ret, [])
896 cfg_es = _FakeConfigForComputeIPolicyInstanceViolation(beparams, True)
897 stub_es = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 13,
899 ret = common.ComputeIPolicyInstanceViolation(NotImplemented, instance,
900 cfg_es, _compute_fn=stub_es)
901 self.assertEqual(ret, [])
902 ret = common.ComputeIPolicyInstanceViolation(NotImplemented, instance2,
903 cfg_es, _compute_fn=stub_es)
904 self.assertEqual(ret, [])
907 class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
910 constants.ISPEC_MEM_SIZE: 2048,
911 constants.ISPEC_CPU_COUNT: 2,
912 constants.ISPEC_DISK_COUNT: 1,
913 constants.ISPEC_DISK_SIZE: [512],
914 constants.ISPEC_NIC_COUNT: 0,
915 constants.ISPEC_SPINDLE_USE: 1,
917 stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 1,
919 ret = instance._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
922 self.assertEqual(ret, [])
926 def __init__(self, return_value=None):
928 self.return_value = return_value
930 def __call__(self, *args):
932 return self.return_value
935 class TestComputeIPolicyNodeViolation(unittest.TestCase):
937 self.recorder = _CallRecorder(return_value=[])
939 def testSameGroup(self):
940 ret = instance_utils._ComputeIPolicyNodeViolation(
943 "foo", "foo", NotImplemented,
944 _compute_fn=self.recorder)
945 self.assertFalse(self.recorder.called)
946 self.assertEqual(ret, [])
948 def testDifferentGroup(self):
949 ret = instance_utils._ComputeIPolicyNodeViolation(
952 "foo", "bar", NotImplemented,
953 _compute_fn=self.recorder)
954 self.assertTrue(self.recorder.called)
955 self.assertEqual(ret, [])
958 class _FakeConfigForTargetNodeIPolicy:
959 def __init__(self, node_info=NotImplemented):
960 self._node_info = node_info
962 def GetNodeInfo(self, _):
963 return self._node_info
966 class TestCheckTargetNodeIPolicy(unittest.TestCase):
968 self.instance = objects.Instance(primary_node="blubb")
969 self.target_node = objects.Node(group="bar")
970 node_info = objects.Node(group="foo")
971 fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
972 self.lu = _FakeLU(cfg=fake_cfg)
974 def testNoViolation(self):
975 compute_recoder = _CallRecorder(return_value=[])
976 instance.CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
977 self.target_node, NotImplemented,
978 _compute_fn=compute_recoder)
979 self.assertTrue(compute_recoder.called)
980 self.assertEqual(self.lu.warning_log, [])
982 def testNoIgnore(self):
983 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
984 self.assertRaises(errors.OpPrereqError, instance.CheckTargetNodeIPolicy,
985 self.lu, NotImplemented, self.instance,
986 self.target_node, NotImplemented,
987 _compute_fn=compute_recoder)
988 self.assertTrue(compute_recoder.called)
989 self.assertEqual(self.lu.warning_log, [])
991 def testIgnoreViolation(self):
992 compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
993 instance.CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
994 self.target_node, NotImplemented,
995 ignore=True, _compute_fn=compute_recoder)
996 self.assertTrue(compute_recoder.called)
997 msg = ("Instance does not meet target node group's (bar) instance policy:"
998 " mem_size not in range")
999 self.assertEqual(self.lu.warning_log, [(msg, ())])
1002 class TestApplyContainerMods(unittest.TestCase):
1003 def testEmptyContainer(self):
1006 instance._ApplyContainerMods("test", container, chgdesc, [], None, None,
1008 self.assertEqual(container, [])
1009 self.assertEqual(chgdesc, [])
1014 mods = instance._PrepareContainerMods([
1015 (constants.DDM_ADD, -1, "Hello"),
1016 (constants.DDM_ADD, -1, "World"),
1017 (constants.DDM_ADD, 0, "Start"),
1018 (constants.DDM_ADD, -1, "End"),
1020 instance._ApplyContainerMods("test", container, chgdesc, mods,
1022 self.assertEqual(container, ["Start", "Hello", "World", "End"])
1023 self.assertEqual(chgdesc, [])
1025 mods = instance._PrepareContainerMods([
1026 (constants.DDM_ADD, 0, "zero"),
1027 (constants.DDM_ADD, 3, "Added"),
1028 (constants.DDM_ADD, 5, "four"),
1029 (constants.DDM_ADD, 7, "xyz"),
1031 instance._ApplyContainerMods("test", container, chgdesc, mods,
1033 self.assertEqual(container,
1034 ["zero", "Start", "Hello", "Added", "World", "four",
1036 self.assertEqual(chgdesc, [])
1038 for idx in [-2, len(container) + 1]:
1039 mods = instance._PrepareContainerMods([
1040 (constants.DDM_ADD, idx, "error"),
1042 self.assertRaises(IndexError, instance._ApplyContainerMods,
1043 "test", container, None, mods, None, None, None)
1045 def testRemoveError(self):
1046 for idx in [0, 1, 2, 100, -1, -4]:
1047 mods = instance._PrepareContainerMods([
1048 (constants.DDM_REMOVE, idx, None),
1050 self.assertRaises(IndexError, instance._ApplyContainerMods,
1051 "test", [], None, mods, None, None, None)
1053 mods = instance._PrepareContainerMods([
1054 (constants.DDM_REMOVE, 0, object()),
1056 self.assertRaises(AssertionError, instance._ApplyContainerMods,
1057 "test", [""], None, mods, None, None, None)
1059 def testAddError(self):
1060 for idx in range(-100, -1) + [100]:
1061 mods = instance._PrepareContainerMods([
1062 (constants.DDM_ADD, idx, None),
1064 self.assertRaises(IndexError, instance._ApplyContainerMods,
1065 "test", [], None, mods, None, None, None)
1067 def testRemove(self):
1068 container = ["item 1", "item 2"]
1069 mods = instance._PrepareContainerMods([
1070 (constants.DDM_ADD, -1, "aaa"),
1071 (constants.DDM_REMOVE, -1, None),
1072 (constants.DDM_ADD, -1, "bbb"),
1075 instance._ApplyContainerMods("test", container, chgdesc, mods,
1077 self.assertEqual(container, ["item 1", "item 2", "bbb"])
1078 self.assertEqual(chgdesc, [
1079 ("test/2", "remove"),
1082 def testModify(self):
1083 container = ["item 1", "item 2"]
1084 mods = instance._PrepareContainerMods([
1085 (constants.DDM_MODIFY, -1, "a"),
1086 (constants.DDM_MODIFY, 0, "b"),
1087 (constants.DDM_MODIFY, 1, "c"),
1090 instance._ApplyContainerMods("test", container, chgdesc, mods,
1092 self.assertEqual(container, ["item 1", "item 2"])
1093 self.assertEqual(chgdesc, [])
1095 for idx in [-2, len(container) + 1]:
1096 mods = instance._PrepareContainerMods([
1097 (constants.DDM_MODIFY, idx, "error"),
1099 self.assertRaises(IndexError, instance._ApplyContainerMods,
1100 "test", container, None, mods, None, None, None)
1107 def _CreateTestFn(idx, params, private):
1108 private.data = ("add", idx, params)
1109 return ((100 * idx, params), [
1110 ("test/%s" % idx, hex(idx)),
1114 def _ModifyTestFn(idx, item, params, private):
1115 private.data = ("modify", idx, params)
1117 ("test/%s" % idx, "modify %s" % params),
1121 def _RemoveTestFn(idx, item, private):
1122 private.data = ("remove", idx, item)
1124 def testAddWithCreateFunction(self):
1127 mods = instance._PrepareContainerMods([
1128 (constants.DDM_ADD, -1, "Hello"),
1129 (constants.DDM_ADD, -1, "World"),
1130 (constants.DDM_ADD, 0, "Start"),
1131 (constants.DDM_ADD, -1, "End"),
1132 (constants.DDM_REMOVE, 2, None),
1133 (constants.DDM_MODIFY, -1, "foobar"),
1134 (constants.DDM_REMOVE, 2, None),
1135 (constants.DDM_ADD, 1, "More"),
1136 ], self._PrivateData)
1137 instance._ApplyContainerMods("test", container, chgdesc, mods,
1138 self._CreateTestFn, self._ModifyTestFn,
1140 self.assertEqual(container, [
1145 self.assertEqual(chgdesc, [
1150 ("test/2", "remove"),
1151 ("test/2", "modify foobar"),
1152 ("test/2", "remove"),
1155 self.assertTrue(compat.all(op == private.data[0]
1156 for (op, _, _, private) in mods))
1157 self.assertEqual([private.data for (op, _, _, private) in mods], [
1158 ("add", 0, "Hello"),
1159 ("add", 1, "World"),
1160 ("add", 0, "Start"),
1162 ("remove", 2, (100, "World")),
1163 ("modify", 2, "foobar"),
1164 ("remove", 2, (300, "End")),
1169 class _FakeConfigForGenDiskTemplate:
1170 def __init__(self, enabled_disk_templates):
1171 self._unique_id = itertools.count()
1172 self._drbd_minor = itertools.count(20)
1173 self._port = itertools.count(constants.FIRST_DRBD_PORT)
1174 self._secret = itertools.count()
1175 self._enabled_disk_templates = enabled_disk_templates
1177 def GetVGName(self):
1180 def GenerateUniqueID(self, ec_id):
1181 return "ec%s-uq%s" % (ec_id, self._unique_id.next())
1183 def AllocateDRBDMinor(self, nodes, instance):
1184 return [self._drbd_minor.next()
1187 def AllocatePort(self):
1188 return self._port.next()
1190 def GenerateDRBDSecret(self, ec_id):
1191 return "ec%s-secret%s" % (ec_id, self._secret.next())
1193 def GetInstanceInfo(self, _):
1196 def GetClusterInfo(self):
1197 cluster = objects.Cluster()
1198 cluster.enabled_disk_templates = self._enabled_disk_templates
1202 class _FakeProcForGenDiskTemplate:
1207 class TestGenerateDiskTemplate(unittest.TestCase):
1209 def _SetUpLUWithTemplates(self, enabled_disk_templates):
1210 self._enabled_disk_templates = enabled_disk_templates
1211 cfg = _FakeConfigForGenDiskTemplate(self._enabled_disk_templates)
1212 proc = _FakeProcForGenDiskTemplate()
1214 self.lu = _FakeLU(cfg=cfg, proc=proc)
1217 nodegroup = objects.NodeGroup(name="ng")
1218 nodegroup.UpgradeConfig()
1220 self._enabled_disk_templates = list(constants.DISK_TEMPLATES)
1221 self._SetUpLUWithTemplates(self._enabled_disk_templates)
1222 self.nodegroup = nodegroup
1225 def GetDiskParams():
1226 return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1228 def testWrongDiskTemplate(self):
1229 gdt = instance.GenerateDiskTemplate
1230 disk_template = "##unknown##"
1232 assert disk_template not in constants.DISK_TEMPLATES
1234 self.assertRaises(errors.OpPrereqError, gdt, self.lu, disk_template,
1235 "inst26831.example.com", "node30113.example.com", [], [],
1236 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1237 self.GetDiskParams())
1239 def testDiskless(self):
1240 gdt = instance.GenerateDiskTemplate
1242 result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1243 "node30113.example.com", [], [],
1244 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1245 self.GetDiskParams())
1246 self.assertEqual(result, [])
1248 def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1249 file_storage_dir=NotImplemented,
1250 file_driver=NotImplemented):
1251 gdt = instance.GenerateDiskTemplate
1253 map(lambda params: utils.ForceDictType(params,
1254 constants.IDISK_PARAMS_TYPES),
1257 # Check if non-empty list of secondaries is rejected
1258 self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1259 template, "inst25088.example.com",
1260 "node185.example.com", ["node323.example.com"], [],
1261 NotImplemented, NotImplemented, base_index,
1262 self.lu.LogInfo, self.GetDiskParams())
1264 result = gdt(self.lu, template, "inst21662.example.com",
1265 "node21741.example.com", [],
1266 disk_info, file_storage_dir, file_driver, base_index,
1267 self.lu.LogInfo, self.GetDiskParams())
1269 for (idx, disk) in enumerate(result):
1270 self.assertTrue(isinstance(disk, objects.Disk))
1271 self.assertEqual(disk.dev_type, exp_dev_type)
1272 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1273 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1274 self.assertTrue(disk.children is None)
1276 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1277 instance._UpdateIvNames(base_index, result)
1278 self._CheckIvNames(result, base_index, base_index + len(disk_info))
1282 def _CheckIvNames(self, disks, base_index, end_index):
1283 self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1284 ["disk/%s" % i for i in range(base_index, end_index)])
1286 def testPlain(self):
1288 constants.IDISK_SIZE: 1024,
1289 constants.IDISK_MODE: constants.DISK_RDWR,
1291 constants.IDISK_SIZE: 4096,
1292 constants.IDISK_VG: "othervg",
1293 constants.IDISK_MODE: constants.DISK_RDWR,
1296 result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1299 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1300 ("testvg", "ec0-uq0.disk3"),
1301 ("othervg", "ec0-uq1.disk4"),
1305 # anything != DT_FILE would do here
1306 self._SetUpLUWithTemplates([constants.DT_PLAIN])
1307 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1308 constants.DT_FILE, [], 0, NotImplemented)
1309 self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1310 constants.DT_SHARED_FILE, [], 0, NotImplemented)
1312 for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1314 constants.IDISK_SIZE: 80 * 1024,
1315 constants.IDISK_MODE: constants.DISK_RDONLY,
1317 constants.IDISK_SIZE: 4096,
1318 constants.IDISK_MODE: constants.DISK_RDWR,
1320 constants.IDISK_SIZE: 6 * 1024,
1321 constants.IDISK_MODE: constants.DISK_RDWR,
1324 self._SetUpLUWithTemplates([disk_template])
1325 result = self._TestTrivialDisk(disk_template, disk_info, 2,
1326 disk_template, file_storage_dir="/tmp",
1327 file_driver=constants.FD_BLKTAP)
1329 for (idx, disk) in enumerate(result):
1330 (file_driver, file_storage_dir) = disk.logical_id
1331 dir_fmt = r"^/tmp/.*\.%s\.disk%d$" % (disk_template, idx + 2)
1332 self.assertEqual(file_driver, constants.FD_BLKTAP)
1333 # FIXME: use assertIsNotNone when py 2.7 is minimum supported version
1334 self.assertNotEqual(re.match(dir_fmt, file_storage_dir), None)
1336 def testBlock(self):
1338 constants.IDISK_SIZE: 8 * 1024,
1339 constants.IDISK_MODE: constants.DISK_RDWR,
1340 constants.IDISK_ADOPT: "/tmp/some/block/dev",
1343 result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1346 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1347 (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1352 constants.IDISK_SIZE: 8 * 1024,
1353 constants.IDISK_MODE: constants.DISK_RDONLY,
1355 constants.IDISK_SIZE: 100 * 1024,
1356 constants.IDISK_MODE: constants.DISK_RDWR,
1359 result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1362 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1363 ("rbd", "ec0-uq0.rbd.disk0"),
1364 ("rbd", "ec0-uq1.rbd.disk1"),
1367 def testDrbd8(self):
1368 gdt = instance.GenerateDiskTemplate
1369 drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.DT_DRBD8]
1370 drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1373 constants.IDISK_SIZE: 1024,
1374 constants.IDISK_MODE: constants.DISK_RDWR,
1376 constants.IDISK_SIZE: 100 * 1024,
1377 constants.IDISK_MODE: constants.DISK_RDONLY,
1378 constants.IDISK_METAVG: "metavg",
1380 constants.IDISK_SIZE: 4096,
1381 constants.IDISK_MODE: constants.DISK_RDWR,
1382 constants.IDISK_VG: "vgxyz",
1386 exp_logical_ids = [[
1387 (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1388 (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1390 (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1391 ("metavg", "ec0-uq1.disk1_meta"),
1393 ("vgxyz", "ec0-uq2.disk2_data"),
1394 (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1397 assert len(exp_logical_ids) == len(disk_info)
1399 map(lambda params: utils.ForceDictType(params,
1400 constants.IDISK_PARAMS_TYPES),
1403 # Check if empty list of secondaries is rejected
1404 self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1405 "inst827.example.com", "node1334.example.com", [],
1406 disk_info, NotImplemented, NotImplemented, 0,
1407 self.lu.LogInfo, self.GetDiskParams())
1409 result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1410 "node1334.example.com", ["node12272.example.com"],
1411 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1412 self.GetDiskParams())
1414 for (idx, disk) in enumerate(result):
1415 self.assertTrue(isinstance(disk, objects.Disk))
1416 self.assertEqual(disk.dev_type, constants.DT_DRBD8)
1417 self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1418 self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1420 for child in disk.children:
1421 self.assertTrue(isinstance(disk, objects.Disk))
1422 self.assertEqual(child.dev_type, constants.DT_PLAIN)
1423 self.assertTrue(child.children is None)
1425 self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1426 exp_logical_ids[idx])
1428 self.assertEqual(len(disk.children), 2)
1429 self.assertEqual(disk.children[0].size, disk.size)
1430 self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1432 self._CheckIvNames(result, 0, len(disk_info))
1433 instance._UpdateIvNames(0, result)
1434 self._CheckIvNames(result, 0, len(disk_info))
1436 self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1437 ("node1334.example.com", "node12272.example.com",
1438 constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1439 ("node1334.example.com", "node12272.example.com",
1440 constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1441 ("node1334.example.com", "node12272.example.com",
1442 constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1446 class _ConfigForDiskWipe:
1447 def __init__(self, exp_node_uuid):
1448 self._exp_node_uuid = exp_node_uuid
1450 def SetDiskID(self, device, node_uuid):
1451 assert isinstance(device, objects.Disk)
1452 assert node_uuid == self._exp_node_uuid
1454 def GetNodeName(self, node_uuid):
1455 assert node_uuid == self._exp_node_uuid
1456 return "name.of.expected.node"
1459 class _RpcForDiskWipe:
1460 def __init__(self, exp_node, pause_cb, wipe_cb):
1461 self._exp_node = exp_node
1462 self._pause_cb = pause_cb
1463 self._wipe_cb = wipe_cb
1465 def call_blockdev_pause_resume_sync(self, node, disks, pause):
1466 assert node == self._exp_node
1467 return rpc.RpcResult(data=self._pause_cb(disks, pause))
1469 def call_blockdev_wipe(self, node, bdev, offset, size):
1470 assert node == self._exp_node
1471 return rpc.RpcResult(data=self._wipe_cb(bdev, offset, size))
1474 class _DiskPauseTracker:
1478 def __call__(self, (disks, instance), pause):
1479 assert not (set(disks) - set(instance.disks))
1481 self.history.extend((i.logical_id, i.size, pause)
1484 return (True, [True] * len(disks))
1487 class _DiskWipeProgressTracker:
1488 def __init__(self, start_offset):
1489 self._start_offset = start_offset
1492 def __call__(self, (disk, _), offset, size):
1493 assert isinstance(offset, (long, int))
1494 assert isinstance(size, (long, int))
1496 max_chunk_size = (disk.size / 100.0 * constants.MIN_WIPE_CHUNK_PERCENT)
1498 assert offset >= self._start_offset
1499 assert (offset + size) <= disk.size
1502 assert size <= constants.MAX_WIPE_CHUNK
1503 assert size <= max_chunk_size
1505 assert offset == self._start_offset or disk.logical_id in self.progress
1507 # Keep track of progress
1508 cur_progress = self.progress.setdefault(disk.logical_id, self._start_offset)
1510 assert cur_progress == offset
1513 self.progress[disk.logical_id] += size
1518 class TestWipeDisks(unittest.TestCase):
1519 def _FailingPauseCb(self, (disks, _), pause):
1520 self.assertEqual(len(disks), 3)
1521 self.assertTrue(pause)
1522 # Simulate an RPC error
1523 return (False, "error")
1525 def testPauseFailure(self):
1526 node_name = "node1372.example.com"
1528 lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, self._FailingPauseCb,
1530 cfg=_ConfigForDiskWipe(node_name))
1533 objects.Disk(dev_type=constants.DT_PLAIN),
1534 objects.Disk(dev_type=constants.DT_PLAIN),
1535 objects.Disk(dev_type=constants.DT_PLAIN),
1538 inst = objects.Instance(name="inst21201",
1539 primary_node=node_name,
1540 disk_template=constants.DT_PLAIN,
1543 self.assertRaises(errors.OpExecError, instance.WipeDisks, lu, inst)
1545 def _FailingWipeCb(self, (disk, _), offset, size):
1546 # This should only ever be called for the first disk
1547 self.assertEqual(disk.logical_id, "disk0")
1548 return (False, None)
1550 def testFailingWipe(self):
1551 node_uuid = "node13445-uuid"
1552 pt = _DiskPauseTracker()
1554 lu = _FakeLU(rpc=_RpcForDiskWipe(node_uuid, pt, self._FailingWipeCb),
1555 cfg=_ConfigForDiskWipe(node_uuid))
1558 objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk0",
1560 objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk1",
1562 objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk2", size=256),
1565 inst = objects.Instance(name="inst562",
1566 primary_node=node_uuid,
1567 disk_template=constants.DT_PLAIN,
1571 instance.WipeDisks(lu, inst)
1572 except errors.OpExecError, err:
1573 self.assertTrue(str(err), "Could not wipe disk 0 at offset 0 ")
1575 self.fail("Did not raise exception")
1577 # Check if all disks were paused and resumed
1578 self.assertEqual(pt.history, [
1579 ("disk0", 100 * 1024, True),
1580 ("disk1", 500 * 1024, True),
1581 ("disk2", 256, True),
1582 ("disk0", 100 * 1024, False),
1583 ("disk1", 500 * 1024, False),
1584 ("disk2", 256, False),
1587 def _PrepareWipeTest(self, start_offset, disks):
1588 node_name = "node-with-offset%s.example.com" % start_offset
1589 pauset = _DiskPauseTracker()
1590 progresst = _DiskWipeProgressTracker(start_offset)
1592 lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pauset, progresst),
1593 cfg=_ConfigForDiskWipe(node_name))
1595 instance = objects.Instance(name="inst3560",
1596 primary_node=node_name,
1597 disk_template=constants.DT_PLAIN,
1600 return (lu, instance, pauset, progresst)
1602 def testNormalWipe(self):
1604 objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk0", size=1024),
1605 objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk1",
1607 objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk2", size=128),
1608 objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk3",
1609 size=constants.MAX_WIPE_CHUNK),
1612 (lu, inst, pauset, progresst) = self._PrepareWipeTest(0, disks)
1614 instance.WipeDisks(lu, inst)
1616 self.assertEqual(pauset.history, [
1617 ("disk0", 1024, True),
1618 ("disk1", 500 * 1024, True),
1619 ("disk2", 128, True),
1620 ("disk3", constants.MAX_WIPE_CHUNK, True),
1621 ("disk0", 1024, False),
1622 ("disk1", 500 * 1024, False),
1623 ("disk2", 128, False),
1624 ("disk3", constants.MAX_WIPE_CHUNK, False),
1627 # Ensure the complete disk has been wiped
1628 self.assertEqual(progresst.progress,
1629 dict((i.logical_id, i.size) for i in disks))
1631 def testWipeWithStartOffset(self):
1632 for start_offset in [0, 280, 8895, 1563204]:
1634 objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk0",
1636 objects.Disk(dev_type=constants.DT_PLAIN, logical_id="disk1",
1637 size=start_offset + (100 * 1024)),
1640 (lu, inst, pauset, progresst) = \
1641 self._PrepareWipeTest(start_offset, disks)
1643 # Test start offset with only one disk
1644 instance.WipeDisks(lu, inst,
1645 disks=[(1, disks[1], start_offset)])
1647 # Only the second disk may have been paused and wiped
1648 self.assertEqual(pauset.history, [
1649 ("disk1", start_offset + (100 * 1024), True),
1650 ("disk1", start_offset + (100 * 1024), False),
1652 self.assertEqual(progresst.progress, {
1653 "disk1": disks[1].size,
1657 class TestDiskSizeInBytesToMebibytes(unittest.TestCase):
1658 def testLessThanOneMebibyte(self):
1659 for i in [1, 2, 7, 512, 1000, 1023]:
1661 result = instance_storage._DiskSizeInBytesToMebibytes(lu, i)
1662 self.assertEqual(result, 1)
1663 self.assertEqual(len(lu.warning_log), 1)
1664 self.assertEqual(len(lu.warning_log[0]), 2)
1665 (_, (warnsize, )) = lu.warning_log[0]
1666 self.assertEqual(warnsize, (1024 * 1024) - i)
1669 for i in [1, 2, 7, 512, 1000, 1023]:
1671 result = instance_storage._DiskSizeInBytesToMebibytes(lu,
1673 self.assertEqual(result, i)
1674 self.assertFalse(lu.warning_log)
1676 def testLargeNumber(self):
1677 for i in [1, 2, 7, 512, 1000, 1023, 2724, 12420]:
1678 for j in [1, 2, 486, 326, 986, 1023]:
1680 size = (1024 * 1024 * i) + j
1681 result = instance_storage._DiskSizeInBytesToMebibytes(lu, size)
1682 self.assertEqual(result, i + 1, msg="Amount was not rounded up")
1683 self.assertEqual(len(lu.warning_log), 1)
1684 self.assertEqual(len(lu.warning_log[0]), 2)
1685 (_, (warnsize, )) = lu.warning_log[0]
1686 self.assertEqual(warnsize, (1024 * 1024) - j)
1689 class TestCopyLockList(unittest.TestCase):
1691 self.assertEqual(instance.CopyLockList([]), [])
1692 self.assertEqual(instance.CopyLockList(None), None)
1693 self.assertEqual(instance.CopyLockList(locking.ALL_SET), locking.ALL_SET)
1695 names = ["foo", "bar"]
1696 output = instance.CopyLockList(names)
1697 self.assertEqual(names, output)
1698 self.assertNotEqual(id(names), id(output), msg="List was not copied")
1701 class TestCheckOpportunisticLocking(unittest.TestCase):
1702 class OpTest(opcodes.OpCode):
1704 opcodes._POpportunisticLocking,
1705 opcodes._PIAllocFromDesc(""),
1709 def _MakeOp(cls, **kwargs):
1710 op = cls.OpTest(**kwargs)
1714 def testMissingAttributes(self):
1715 self.assertRaises(AttributeError, instance._CheckOpportunisticLocking,
1718 def testDefaults(self):
1720 instance._CheckOpportunisticLocking(op)
1723 for iallocator in [None, "something", "other"]:
1724 for opplock in [False, True]:
1725 op = self._MakeOp(iallocator=iallocator,
1726 opportunistic_locking=opplock)
1727 if opplock and not iallocator:
1728 self.assertRaises(errors.OpPrereqError,
1729 instance._CheckOpportunisticLocking, op)
1731 instance._CheckOpportunisticLocking(op)
1734 class _OpTestVerifyErrors(opcodes.OpCode):
1736 opcodes._PDebugSimulateErrors,
1737 opcodes._PErrorCodes,
1738 opcodes._PIgnoreErrors,
1742 class _LuTestVerifyErrors(cluster._VerifyErrors):
1743 def __init__(self, **kwargs):
1744 cluster._VerifyErrors.__init__(self)
1745 self.op = _OpTestVerifyErrors(**kwargs)
1746 self.op.Validate(True)
1748 self._feedback_fn = self.msglist.append
1751 def DispatchCallError(self, which, *args, **kwargs):
1753 self._Error(*args, **kwargs)
1755 self._ErrorIf(True, *args, **kwargs)
1757 def CallErrorIf(self, c, *args, **kwargs):
1758 self._ErrorIf(c, *args, **kwargs)
1761 class TestVerifyErrors(unittest.TestCase):
1762 # Fake cluster-verify error code structures; we use two arbitary real error
1763 # codes to pass validation of ignore_errors
1764 (_, _ERR1ID, _) = constants.CV_ECLUSTERCFG
1766 _NODENAME = "mynode"
1767 _ERR1CODE = (_NODESTR, _ERR1ID, "Error one")
1768 (_, _ERR2ID, _) = constants.CV_ECLUSTERCERT
1769 _INSTSTR = "instance"
1770 _INSTNAME = "myinstance"
1771 _ERR2CODE = (_INSTSTR, _ERR2ID, "Error two")
1772 # Arguments used to call _Error() or _ErrorIf()
1773 _ERR1ARGS = (_ERR1CODE, _NODENAME, "Error1 is %s", "an error")
1774 _ERR2ARGS = (_ERR2CODE, _INSTNAME, "Error2 has no argument")
1775 # Expected error messages
1776 _ERR1MSG = _ERR1ARGS[2] % _ERR1ARGS[3]
1777 _ERR2MSG = _ERR2ARGS[2]
1779 def testNoError(self):
1780 lu = _LuTestVerifyErrors()
1781 lu.CallErrorIf(False, self._ERR1CODE, *self._ERR1ARGS)
1782 self.assertFalse(lu.bad)
1783 self.assertFalse(lu.msglist)
1785 def _InitTest(self, **kwargs):
1786 self.lu1 = _LuTestVerifyErrors(**kwargs)
1787 self.lu2 = _LuTestVerifyErrors(**kwargs)
1789 def _CallError(self, *args, **kwargs):
1790 # Check that _Error() and _ErrorIf() produce the same results
1791 self.lu1.DispatchCallError(True, *args, **kwargs)
1792 self.lu2.DispatchCallError(False, *args, **kwargs)
1793 self.assertEqual(self.lu1.bad, self.lu2.bad)
1794 self.assertEqual(self.lu1.msglist, self.lu2.msglist)
1795 # Test-specific checks are made on one LU
1798 def _checkMsgCommon(self, logstr, errmsg, itype, item, warning):
1799 self.assertTrue(errmsg in logstr)
1801 self.assertTrue("WARNING" in logstr)
1803 self.assertTrue("ERROR" in logstr)
1804 self.assertTrue(itype in logstr)
1805 self.assertTrue(item in logstr)
1807 def _checkMsg1(self, logstr, warning=False):
1808 self._checkMsgCommon(logstr, self._ERR1MSG, self._NODESTR,
1809 self._NODENAME, warning)
1811 def _checkMsg2(self, logstr, warning=False):
1812 self._checkMsgCommon(logstr, self._ERR2MSG, self._INSTSTR,
1813 self._INSTNAME, warning)
1815 def testPlain(self):
1817 lu = self._CallError(*self._ERR1ARGS)
1818 self.assertTrue(lu.bad)
1819 self.assertEqual(len(lu.msglist), 1)
1820 self._checkMsg1(lu.msglist[0])
1822 def testMultiple(self):
1824 self._CallError(*self._ERR1ARGS)
1825 lu = self._CallError(*self._ERR2ARGS)
1826 self.assertTrue(lu.bad)
1827 self.assertEqual(len(lu.msglist), 2)
1828 self._checkMsg1(lu.msglist[0])
1829 self._checkMsg2(lu.msglist[1])
1831 def testIgnore(self):
1832 self._InitTest(ignore_errors=[self._ERR1ID])
1833 lu = self._CallError(*self._ERR1ARGS)
1834 self.assertFalse(lu.bad)
1835 self.assertEqual(len(lu.msglist), 1)
1836 self._checkMsg1(lu.msglist[0], warning=True)
1838 def testWarning(self):
1840 lu = self._CallError(*self._ERR1ARGS,
1841 code=_LuTestVerifyErrors.ETYPE_WARNING)
1842 self.assertFalse(lu.bad)
1843 self.assertEqual(len(lu.msglist), 1)
1844 self._checkMsg1(lu.msglist[0], warning=True)
1846 def testWarning2(self):
1848 self._CallError(*self._ERR1ARGS)
1849 lu = self._CallError(*self._ERR2ARGS,
1850 code=_LuTestVerifyErrors.ETYPE_WARNING)
1851 self.assertTrue(lu.bad)
1852 self.assertEqual(len(lu.msglist), 2)
1853 self._checkMsg1(lu.msglist[0])
1854 self._checkMsg2(lu.msglist[1], warning=True)
1856 def testDebugSimulate(self):
1857 lu = _LuTestVerifyErrors(debug_simulate_errors=True)
1858 lu.CallErrorIf(False, *self._ERR1ARGS)
1859 self.assertTrue(lu.bad)
1860 self.assertEqual(len(lu.msglist), 1)
1861 self._checkMsg1(lu.msglist[0])
1863 def testErrCodes(self):
1864 self._InitTest(error_codes=True)
1865 lu = self._CallError(*self._ERR1ARGS)
1866 self.assertTrue(lu.bad)
1867 self.assertEqual(len(lu.msglist), 1)
1868 self._checkMsg1(lu.msglist[0])
1869 self.assertTrue(self._ERR1ID in lu.msglist[0])
1872 class TestGetUpdatedIPolicy(unittest.TestCase):
1873 """Tests for cmdlib._GetUpdatedIPolicy()"""
1874 _OLD_CLUSTER_POLICY = {
1875 constants.IPOLICY_VCPU_RATIO: 1.5,
1876 constants.ISPECS_MINMAX: [
1878 constants.ISPECS_MIN: {
1879 constants.ISPEC_MEM_SIZE: 32768,
1880 constants.ISPEC_CPU_COUNT: 8,
1881 constants.ISPEC_DISK_COUNT: 1,
1882 constants.ISPEC_DISK_SIZE: 1024,
1883 constants.ISPEC_NIC_COUNT: 1,
1884 constants.ISPEC_SPINDLE_USE: 1,
1886 constants.ISPECS_MAX: {
1887 constants.ISPEC_MEM_SIZE: 65536,
1888 constants.ISPEC_CPU_COUNT: 10,
1889 constants.ISPEC_DISK_COUNT: 5,
1890 constants.ISPEC_DISK_SIZE: 1024 * 1024,
1891 constants.ISPEC_NIC_COUNT: 3,
1892 constants.ISPEC_SPINDLE_USE: 12,
1895 constants.ISPECS_MINMAX_DEFAULTS,
1897 constants.ISPECS_STD: constants.IPOLICY_DEFAULTS[constants.ISPECS_STD],
1899 _OLD_GROUP_POLICY = {
1900 constants.IPOLICY_SPINDLE_RATIO: 2.5,
1901 constants.ISPECS_MINMAX: [{
1902 constants.ISPECS_MIN: {
1903 constants.ISPEC_MEM_SIZE: 128,
1904 constants.ISPEC_CPU_COUNT: 1,
1905 constants.ISPEC_DISK_COUNT: 1,
1906 constants.ISPEC_DISK_SIZE: 1024,
1907 constants.ISPEC_NIC_COUNT: 1,
1908 constants.ISPEC_SPINDLE_USE: 1,
1910 constants.ISPECS_MAX: {
1911 constants.ISPEC_MEM_SIZE: 32768,
1912 constants.ISPEC_CPU_COUNT: 8,
1913 constants.ISPEC_DISK_COUNT: 5,
1914 constants.ISPEC_DISK_SIZE: 1024 * 1024,
1915 constants.ISPEC_NIC_COUNT: 3,
1916 constants.ISPEC_SPINDLE_USE: 12,
1921 def _TestSetSpecs(self, old_policy, isgroup):
1923 constants.ISPECS_MIN: {
1924 constants.ISPEC_MEM_SIZE: 64,
1925 constants.ISPEC_CPU_COUNT: 1,
1926 constants.ISPEC_DISK_COUNT: 2,
1927 constants.ISPEC_DISK_SIZE: 64,
1928 constants.ISPEC_NIC_COUNT: 1,
1929 constants.ISPEC_SPINDLE_USE: 1,
1931 constants.ISPECS_MAX: {
1932 constants.ISPEC_MEM_SIZE: 16384,
1933 constants.ISPEC_CPU_COUNT: 10,
1934 constants.ISPEC_DISK_COUNT: 12,
1935 constants.ISPEC_DISK_SIZE: 1024,
1936 constants.ISPEC_NIC_COUNT: 9,
1937 constants.ISPEC_SPINDLE_USE: 18,
1941 constants.ISPEC_DISK_COUNT: 10,
1942 constants.ISPEC_DISK_SIZE: 512,
1945 constants.ISPECS_MINMAX: diff_minmax
1948 diff_policy[constants.ISPECS_STD] = diff_std
1949 new_policy = common.GetUpdatedIPolicy(old_policy, diff_policy,
1950 group_policy=isgroup)
1952 self.assertTrue(constants.ISPECS_MINMAX in new_policy)
1953 self.assertEqual(new_policy[constants.ISPECS_MINMAX], diff_minmax)
1954 for key in old_policy:
1955 if not key in diff_policy:
1956 self.assertTrue(key in new_policy)
1957 self.assertEqual(new_policy[key], old_policy[key])
1960 new_std = new_policy[constants.ISPECS_STD]
1961 for key in diff_std:
1962 self.assertTrue(key in new_std)
1963 self.assertEqual(new_std[key], diff_std[key])
1964 old_std = old_policy.get(constants.ISPECS_STD, {})
1966 self.assertTrue(key in new_std)
1967 if key not in diff_std:
1968 self.assertEqual(new_std[key], old_std[key])
1970 def _TestSet(self, old_policy, diff_policy, isgroup):
1971 new_policy = common.GetUpdatedIPolicy(old_policy, diff_policy,
1972 group_policy=isgroup)
1973 for key in diff_policy:
1974 self.assertTrue(key in new_policy)
1975 self.assertEqual(new_policy[key], diff_policy[key])
1976 for key in old_policy:
1977 if not key in diff_policy:
1978 self.assertTrue(key in new_policy)
1979 self.assertEqual(new_policy[key], old_policy[key])
1983 constants.IPOLICY_VCPU_RATIO: 3,
1984 constants.IPOLICY_DTS: [constants.DT_FILE],
1986 self._TestSet(self._OLD_GROUP_POLICY, diff_policy, True)
1987 self._TestSetSpecs(self._OLD_GROUP_POLICY, True)
1988 self._TestSet({}, diff_policy, True)
1989 self._TestSetSpecs({}, True)
1990 self._TestSet(self._OLD_CLUSTER_POLICY, diff_policy, False)
1991 self._TestSetSpecs(self._OLD_CLUSTER_POLICY, False)
1993 def testUnset(self):
1994 old_policy = self._OLD_GROUP_POLICY
1996 constants.IPOLICY_SPINDLE_RATIO: constants.VALUE_DEFAULT,
1998 new_policy = common.GetUpdatedIPolicy(old_policy, diff_policy,
2000 for key in diff_policy:
2001 self.assertFalse(key in new_policy)
2002 for key in old_policy:
2003 if not key in diff_policy:
2004 self.assertTrue(key in new_policy)
2005 self.assertEqual(new_policy[key], old_policy[key])
2007 self.assertRaises(errors.OpPrereqError, common.GetUpdatedIPolicy,
2008 old_policy, diff_policy, group_policy=False)
2010 def testUnsetEmpty(self):
2012 for key in constants.IPOLICY_ALL_KEYS:
2014 key: constants.VALUE_DEFAULT,
2016 new_policy = common.GetUpdatedIPolicy(old_policy, diff_policy,
2018 self.assertEqual(new_policy, old_policy)
2020 def _TestInvalidKeys(self, old_policy, isgroup):
2021 INVALID_KEY = "this_key_shouldnt_be_allowed"
2025 invalid_policy = INVALID_DICT
2026 self.assertRaises(errors.OpPrereqError, common.GetUpdatedIPolicy,
2027 old_policy, invalid_policy, group_policy=isgroup)
2029 constants.ISPECS_MINMAX: [INVALID_DICT],
2031 self.assertRaises(errors.TypeEnforcementError, common.GetUpdatedIPolicy,
2032 old_policy, invalid_ispecs, group_policy=isgroup)
2034 invalid_for_group = {
2035 constants.ISPECS_STD: constants.IPOLICY_DEFAULTS[constants.ISPECS_STD],
2037 self.assertRaises(errors.OpPrereqError, common.GetUpdatedIPolicy,
2038 old_policy, invalid_for_group, group_policy=isgroup)
2039 good_ispecs = self._OLD_CLUSTER_POLICY[constants.ISPECS_MINMAX]
2040 invalid_ispecs = copy.deepcopy(good_ispecs)
2042 constants.ISPECS_MINMAX: invalid_ispecs,
2044 for minmax in invalid_ispecs:
2045 for key in constants.ISPECS_MINMAX_KEYS:
2047 ispec[INVALID_KEY] = None
2048 self.assertRaises(errors.TypeEnforcementError,
2049 common.GetUpdatedIPolicy, old_policy,
2050 invalid_policy, group_policy=isgroup)
2051 del ispec[INVALID_KEY]
2052 for par in constants.ISPECS_PARAMETERS:
2054 ispec[par] = "this_is_not_good"
2055 self.assertRaises(errors.TypeEnforcementError,
2056 common.GetUpdatedIPolicy,
2057 old_policy, invalid_policy, group_policy=isgroup)
2059 # This is to make sure that no two errors were present during the tests
2060 common.GetUpdatedIPolicy(old_policy, invalid_policy,
2061 group_policy=isgroup)
2063 def testInvalidKeys(self):
2064 self._TestInvalidKeys(self._OLD_GROUP_POLICY, True)
2065 self._TestInvalidKeys(self._OLD_CLUSTER_POLICY, False)
2067 def testInvalidValues(self):
2068 for par in (constants.IPOLICY_PARAMETERS |
2069 frozenset([constants.IPOLICY_DTS])):
2071 par: "invalid_value",
2073 self.assertRaises(errors.OpPrereqError, common.GetUpdatedIPolicy, {},
2074 bad_policy, group_policy=True)
2076 if __name__ == "__main__":
2077 testutils.GanetiTestProgram()