4 # Copyright (C) 2008, 2011 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Script for unittesting the cmdlib module"""
33 from ganeti import constants
34 from ganeti import mcpu
35 from ganeti import cmdlib
36 from ganeti import opcodes
37 from ganeti import errors
38 from ganeti import utils
39 from ganeti import luxi
41 from ganeti import objects
42 from ganeti import compat
43 from ganeti import rpc
44 from ganeti.hypervisor import hv_xen
50 class TestCertVerification(testutils.GanetiTestCase):
52 testutils.GanetiTestCase.setUp(self)
54 self.tmpdir = tempfile.mkdtemp()
57 shutil.rmtree(self.tmpdir)
59 def testVerifyCertificate(self):
60 cmdlib._VerifyCertificate(self._TestDataFilename("cert1.pem"))
62 nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
64 (errcode, msg) = cmdlib._VerifyCertificate(nonexist_filename)
65 self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
67 # Try to load non-certificate file
68 invalid_cert = self._TestDataFilename("bdev-net.txt")
69 (errcode, msg) = cmdlib._VerifyCertificate(invalid_cert)
70 self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
73 class TestOpcodeParams(testutils.GanetiTestCase):
74 def testParamsStructures(self):
75 for op in sorted(mcpu.Processor.DISPATCH_TABLE):
76 lu = mcpu.Processor.DISPATCH_TABLE[op]
78 self.failIf(hasattr(lu, "_OP_REQP"),
79 msg=("LU '%s' has old-style _OP_REQP" % lu_name))
80 self.failIf(hasattr(lu, "_OP_DEFS"),
81 msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
82 self.failIf(hasattr(lu, "_OP_PARAMS"),
83 msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
86 class TestIAllocatorChecks(testutils.GanetiTestCase):
87 def testFunction(self):
89 def __init__(self, opcode):
90 self.cfg = mocks.FakeConfig()
93 class OpTest(opcodes.OpCode):
95 ("iallocator", None, ht.NoType, None),
96 ("node", None, ht.NoType, None),
99 default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
100 other_iallocator = default_iallocator + "_not"
105 c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
107 # Neither node nor iallocator given
111 self.assertEqual(lu.op.iallocator, default_iallocator)
112 self.assertEqual(lu.op.node, None)
114 # Both, iallocator and node given
115 op.iallocator = "test"
117 self.assertRaises(errors.OpPrereqError, c_i)
119 # Only iallocator given
120 op.iallocator = other_iallocator
123 self.assertEqual(lu.op.iallocator, other_iallocator)
124 self.assertEqual(lu.op.node, None)
130 self.assertEqual(lu.op.iallocator, None)
131 self.assertEqual(lu.op.node, "node")
133 # No node, iallocator or default iallocator
136 lu.cfg.GetDefaultIAllocator = lambda: None
137 self.assertRaises(errors.OpPrereqError, c_i)
140 class TestLUTestJqueue(unittest.TestCase):
142 self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
143 (luxi.WFJC_TIMEOUT * 0.75),
144 msg=("Client timeout too high, might not notice bugs"
145 " in WaitForJobChange"))
148 class TestLUQuery(unittest.TestCase):
150 self.assertEqual(sorted(cmdlib._QUERY_IMPL.keys()),
151 sorted(constants.QR_VIA_OP))
153 assert constants.QR_NODE in constants.QR_VIA_OP
154 assert constants.QR_INSTANCE in constants.QR_VIA_OP
156 for i in constants.QR_VIA_OP:
157 self.assert_(cmdlib._GetQueryImplementation(i))
159 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation, "")
160 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation,
164 class TestLUGroupAssignNodes(unittest.TestCase):
166 def testCheckAssignmentForSplitInstances(self):
167 node_data = dict((name, objects.Node(name=name, group=group))
168 for (name, group) in [("n1a", "g1"), ("n1b", "g1"),
169 ("n2a", "g2"), ("n2b", "g2"),
170 ("n3a", "g3"), ("n3b", "g3"),
174 def Instance(name, pnode, snode):
177 disk_template = constants.DT_DISKLESS
179 disks = [objects.Disk(dev_type=constants.LD_DRBD8,
180 logical_id=[pnode, snode, 1, 17, 17])]
181 disk_template = constants.DT_DRBD8
183 return objects.Instance(name=name, primary_node=pnode, disks=disks,
184 disk_template=disk_template)
186 instance_data = dict((name, Instance(name, pnode, snode))
187 for name, pnode, snode in [("inst1a", "n1a", "n1b"),
188 ("inst1b", "n1b", "n1a"),
189 ("inst2a", "n2a", "n2b"),
190 ("inst3a", "n3a", None),
191 ("inst3b", "n3b", "n1b"),
192 ("inst3c", "n3b", "n2b"),
195 # Test first with the existing state.
197 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
201 self.assertEqual([], new)
202 self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
204 # And now some changes.
206 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
211 self.assertEqual(set(["inst1a", "inst1b"]), set(new))
212 self.assertEqual(set(["inst3c"]), set(prev))
215 class TestClusterVerifySsh(unittest.TestCase):
216 def testMultipleGroups(self):
217 fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
219 objects.Node(name="node20", group="my", offline=False),
220 objects.Node(name="node21", group="my", offline=False),
221 objects.Node(name="node22", group="my", offline=False),
222 objects.Node(name="node23", group="my", offline=False),
223 objects.Node(name="node24", group="my", offline=False),
224 objects.Node(name="node25", group="my", offline=False),
225 objects.Node(name="node26", group="my", offline=True),
228 objects.Node(name="node1", group="g1", offline=True),
229 objects.Node(name="node2", group="g1", offline=False),
230 objects.Node(name="node3", group="g1", offline=False),
231 objects.Node(name="node4", group="g1", offline=True),
232 objects.Node(name="node5", group="g1", offline=False),
233 objects.Node(name="node10", group="xyz", offline=False),
234 objects.Node(name="node11", group="xyz", offline=False),
235 objects.Node(name="node40", group="alloff", offline=True),
236 objects.Node(name="node41", group="alloff", offline=True),
237 objects.Node(name="node50", group="aaa", offline=False),
239 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
241 (online, perhost) = fn(mygroupnodes, "my", nodes)
242 self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
243 self.assertEqual(set(perhost.keys()), set(online))
245 self.assertEqual(perhost, {
246 "node20": ["node10", "node2", "node50"],
247 "node21": ["node11", "node3", "node50"],
248 "node22": ["node10", "node5", "node50"],
249 "node23": ["node11", "node2", "node50"],
250 "node24": ["node10", "node3", "node50"],
251 "node25": ["node11", "node5", "node50"],
254 def testSingleGroup(self):
255 fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
257 objects.Node(name="node1", group="default", offline=True),
258 objects.Node(name="node2", group="default", offline=False),
259 objects.Node(name="node3", group="default", offline=False),
260 objects.Node(name="node4", group="default", offline=True),
262 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
264 (online, perhost) = fn(nodes, "default", nodes)
265 self.assertEqual(online, ["node2", "node3"])
266 self.assertEqual(set(perhost.keys()), set(online))
268 self.assertEqual(perhost, {
274 class TestClusterVerifyFiles(unittest.TestCase):
276 def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
277 assert ((ecode == cmdlib.LUClusterVerifyGroup.ENODEFILECHECK and
278 ht.TNonEmptyString(item)) or
279 (ecode == cmdlib.LUClusterVerifyGroup.ECLUSTERFILECHECK and
286 errors.append((item, msg))
288 _VerifyFiles = cmdlib.LUClusterVerifyGroup._VerifyFiles
292 master_name = "master.example.com"
294 objects.Node(name=master_name, offline=False, vm_capable=True),
295 objects.Node(name="node2.example.com", offline=False, vm_capable=True),
296 objects.Node(name="node3.example.com", master_candidate=True,
298 objects.Node(name="node4.example.com", offline=False, vm_capable=True),
299 objects.Node(name="nodata.example.com", offline=False, vm_capable=True),
300 objects.Node(name="offline.example.com", offline=True),
302 cluster = objects.Cluster(modify_etc_hosts=True,
303 enabled_hypervisors=[constants.HT_XEN_HVM])
305 constants.CLUSTER_DOMAIN_SECRET_FILE,
306 constants.RAPI_CERT_FILE,
307 constants.RAPI_USERS_FILE,
310 constants.RAPI_USERS_FILE,
311 hv_xen.XL_CONFIG_FILE,
312 constants.VNC_PASSWORD_FILE,
315 constants.CLUSTER_CONF_FILE,
318 hv_xen.XEND_CONFIG_FILE,
319 hv_xen.XL_CONFIG_FILE,
320 constants.VNC_PASSWORD_FILE,
323 master_name: rpc.RpcResult(data=(True, {
324 constants.NV_FILELIST: {
325 constants.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
326 constants.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
327 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
328 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
329 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
331 "node2.example.com": rpc.RpcResult(data=(True, {
332 constants.NV_FILELIST: {
333 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
334 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
337 "node3.example.com": rpc.RpcResult(data=(True, {
338 constants.NV_FILELIST: {
339 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
340 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
343 "node4.example.com": rpc.RpcResult(data=(True, {
344 constants.NV_FILELIST: {
345 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
346 constants.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
347 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
348 constants.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
349 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
352 "nodata.example.com": rpc.RpcResult(data=(True, {})),
353 "offline.example.com": rpc.RpcResult(offline=True),
355 assert set(nvinfo.keys()) == set(map(operator.attrgetter("name"), nodeinfo))
357 self._VerifyFiles(compat.partial(self._FakeErrorIf, errors), nodeinfo,
359 (files_all, files_opt, files_mc, files_vm))
360 self.assertEqual(sorted(errors), sorted([
361 (None, ("File %s found with 2 different checksums (variant 1 on"
362 " node2.example.com, node3.example.com, node4.example.com;"
363 " variant 2 on master.example.com)" % constants.RAPI_CERT_FILE)),
364 (None, ("File %s is missing from node(s) node2.example.com" %
365 constants.CLUSTER_DOMAIN_SECRET_FILE)),
366 (None, ("File %s should not exist on node(s) node4.example.com" %
367 constants.CLUSTER_CONF_FILE)),
368 (None, ("File %s is missing from node(s) node4.example.com" %
369 hv_xen.XEND_CONFIG_FILE)),
370 (None, ("File %s is missing from node(s) node3.example.com" %
371 constants.CLUSTER_CONF_FILE)),
372 (None, ("File %s found with 2 different checksums (variant 1 on"
373 " master.example.com; variant 2 on node4.example.com)" %
374 constants.CLUSTER_CONF_FILE)),
375 (None, ("File %s is optional, but it must exist on all or no nodes (not"
376 " found on master.example.com, node2.example.com,"
377 " node3.example.com)" % constants.RAPI_USERS_FILE)),
378 (None, ("File %s is optional, but it must exist on all or no nodes (not"
379 " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
380 ("nodata.example.com", "Node did not return file checksum data"),
386 self.warning_log = []
389 def LogWarning(self, text, *args):
390 self.warning_log.append((text, args))
392 def LogInfo(self, text, *args):
393 self.info_log.append((text, args))
396 class TestLoadNodeEvacResult(unittest.TestCase):
397 def testSuccess(self):
399 ("inst20153.example.com", "grp2", ["nodeA4509", "nodeB2912"]),
401 for early_release in [False, True]:
402 for use_nodes in [False, True]:
404 [opcodes.OpInstanceReplaceDisks().__getstate__()],
405 [opcodes.OpInstanceMigrate().__getstate__()],
408 alloc_result = (moved, [], jobs)
409 assert cmdlib.IAllocator._NEVAC_RESULT(alloc_result)
412 result = cmdlib._LoadNodeEvacResult(lu, alloc_result,
413 early_release, use_nodes)
416 (_, (info_args, )) = lu.info_log.pop(0)
417 for (instname, instgroup, instnodes) in moved:
418 self.assertTrue(instname in info_args)
421 self.assertTrue(i in info_args)
423 self.assertTrue(instgroup in info_args)
425 self.assertFalse(lu.info_log)
426 self.assertFalse(lu.warning_log)
428 for op in itertools.chain(*result):
429 if hasattr(op.__class__, "early_release"):
430 self.assertEqual(op.early_release, early_release)
432 self.assertFalse(hasattr(op, "early_release"))
434 def testFailed(self):
435 alloc_result = ([], [
436 ("inst5191.example.com", "errormsg21178"),
438 assert cmdlib.IAllocator._NEVAC_RESULT(alloc_result)
441 self.assertRaises(errors.OpExecError, cmdlib._LoadNodeEvacResult,
442 lu, alloc_result, False, False)
443 self.assertFalse(lu.info_log)
444 (_, (args, )) = lu.warning_log.pop(0)
445 self.assertTrue("inst5191.example.com" in args)
446 self.assertTrue("errormsg21178" in args)
447 self.assertFalse(lu.warning_log)
450 if __name__ == "__main__":
451 testutils.GanetiTestProgram()