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"""
32 from ganeti import constants
33 from ganeti import mcpu
34 from ganeti import cmdlib
35 from ganeti import opcodes
36 from ganeti import errors
37 from ganeti import utils
38 from ganeti import luxi
40 from ganeti import objects
41 from ganeti import compat
42 from ganeti import rpc
43 from ganeti.hypervisor import hv_xen
49 class TestCertVerification(testutils.GanetiTestCase):
51 testutils.GanetiTestCase.setUp(self)
53 self.tmpdir = tempfile.mkdtemp()
56 shutil.rmtree(self.tmpdir)
58 def testVerifyCertificate(self):
59 cmdlib._VerifyCertificate(self._TestDataFilename("cert1.pem"))
61 nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
63 (errcode, msg) = cmdlib._VerifyCertificate(nonexist_filename)
64 self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
66 # Try to load non-certificate file
67 invalid_cert = self._TestDataFilename("bdev-net.txt")
68 (errcode, msg) = cmdlib._VerifyCertificate(invalid_cert)
69 self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
72 class TestOpcodeParams(testutils.GanetiTestCase):
73 def testParamsStructures(self):
74 for op in sorted(mcpu.Processor.DISPATCH_TABLE):
75 lu = mcpu.Processor.DISPATCH_TABLE[op]
77 self.failIf(hasattr(lu, "_OP_REQP"),
78 msg=("LU '%s' has old-style _OP_REQP" % lu_name))
79 self.failIf(hasattr(lu, "_OP_DEFS"),
80 msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
81 self.failIf(hasattr(lu, "_OP_PARAMS"),
82 msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
85 class TestIAllocatorChecks(testutils.GanetiTestCase):
86 def testFunction(self):
88 def __init__(self, opcode):
89 self.cfg = mocks.FakeConfig()
92 class OpTest(opcodes.OpCode):
94 ("iallocator", None, ht.NoType, None),
95 ("node", None, ht.NoType, None),
98 default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
99 other_iallocator = default_iallocator + "_not"
104 c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
106 # Neither node nor iallocator given
110 self.assertEqual(lu.op.iallocator, default_iallocator)
111 self.assertEqual(lu.op.node, None)
113 # Both, iallocator and node given
114 op.iallocator = "test"
116 self.assertRaises(errors.OpPrereqError, c_i)
118 # Only iallocator given
119 op.iallocator = other_iallocator
122 self.assertEqual(lu.op.iallocator, other_iallocator)
123 self.assertEqual(lu.op.node, None)
129 self.assertEqual(lu.op.iallocator, None)
130 self.assertEqual(lu.op.node, "node")
132 # No node, iallocator or default iallocator
135 lu.cfg.GetDefaultIAllocator = lambda: None
136 self.assertRaises(errors.OpPrereqError, c_i)
139 class TestLUTestJqueue(unittest.TestCase):
141 self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
142 (luxi.WFJC_TIMEOUT * 0.75),
143 msg=("Client timeout too high, might not notice bugs"
144 " in WaitForJobChange"))
147 class TestLUQuery(unittest.TestCase):
149 self.assertEqual(sorted(cmdlib._QUERY_IMPL.keys()),
150 sorted(constants.QR_VIA_OP))
152 assert constants.QR_NODE in constants.QR_VIA_OP
153 assert constants.QR_INSTANCE in constants.QR_VIA_OP
155 for i in constants.QR_VIA_OP:
156 self.assert_(cmdlib._GetQueryImplementation(i))
158 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation, "")
159 self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation,
163 class TestLUGroupAssignNodes(unittest.TestCase):
165 def testCheckAssignmentForSplitInstances(self):
166 node_data = dict((name, objects.Node(name=name, group=group))
167 for (name, group) in [("n1a", "g1"), ("n1b", "g1"),
168 ("n2a", "g2"), ("n2b", "g2"),
169 ("n3a", "g3"), ("n3b", "g3"),
173 def Instance(name, pnode, snode):
176 disk_template = constants.DT_DISKLESS
178 disks = [objects.Disk(dev_type=constants.LD_DRBD8,
179 logical_id=[pnode, snode, 1, 17, 17])]
180 disk_template = constants.DT_DRBD8
182 return objects.Instance(name=name, primary_node=pnode, disks=disks,
183 disk_template=disk_template)
185 instance_data = dict((name, Instance(name, pnode, snode))
186 for name, pnode, snode in [("inst1a", "n1a", "n1b"),
187 ("inst1b", "n1b", "n1a"),
188 ("inst2a", "n2a", "n2b"),
189 ("inst3a", "n3a", None),
190 ("inst3b", "n3b", "n1b"),
191 ("inst3c", "n3b", "n2b"),
194 # Test first with the existing state.
196 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
200 self.assertEqual([], new)
201 self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
203 # And now some changes.
205 cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
210 self.assertEqual(set(["inst1a", "inst1b"]), set(new))
211 self.assertEqual(set(["inst3c"]), set(prev))
214 class TestClusterVerifySsh(unittest.TestCase):
215 def testMultipleGroups(self):
216 fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
218 objects.Node(name="node20", group="my", offline=False),
219 objects.Node(name="node21", group="my", offline=False),
220 objects.Node(name="node22", group="my", offline=False),
221 objects.Node(name="node23", group="my", offline=False),
222 objects.Node(name="node24", group="my", offline=False),
223 objects.Node(name="node25", group="my", offline=False),
224 objects.Node(name="node26", group="my", offline=True),
227 objects.Node(name="node1", group="g1", offline=True),
228 objects.Node(name="node2", group="g1", offline=False),
229 objects.Node(name="node3", group="g1", offline=False),
230 objects.Node(name="node4", group="g1", offline=True),
231 objects.Node(name="node5", group="g1", offline=False),
232 objects.Node(name="node10", group="xyz", offline=False),
233 objects.Node(name="node11", group="xyz", offline=False),
234 objects.Node(name="node40", group="alloff", offline=True),
235 objects.Node(name="node41", group="alloff", offline=True),
236 objects.Node(name="node50", group="aaa", offline=False),
238 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
240 (online, perhost) = fn(mygroupnodes, "my", nodes)
241 self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
242 self.assertEqual(set(perhost.keys()), set(online))
244 self.assertEqual(perhost, {
245 "node20": ["node10", "node2", "node50"],
246 "node21": ["node11", "node3", "node50"],
247 "node22": ["node10", "node5", "node50"],
248 "node23": ["node11", "node2", "node50"],
249 "node24": ["node10", "node3", "node50"],
250 "node25": ["node11", "node5", "node50"],
253 def testSingleGroup(self):
254 fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
256 objects.Node(name="node1", group="default", offline=True),
257 objects.Node(name="node2", group="default", offline=False),
258 objects.Node(name="node3", group="default", offline=False),
259 objects.Node(name="node4", group="default", offline=True),
261 assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
263 (online, perhost) = fn(nodes, "default", nodes)
264 self.assertEqual(online, ["node2", "node3"])
265 self.assertEqual(set(perhost.keys()), set(online))
267 self.assertEqual(perhost, {
273 class TestClusterVerifyFiles(unittest.TestCase):
275 def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
276 assert ((ecode == cmdlib.LUClusterVerifyGroup.ENODEFILECHECK and
277 ht.TNonEmptyString(item)) or
278 (ecode == cmdlib.LUClusterVerifyGroup.ECLUSTERFILECHECK and
285 errors.append((item, msg))
287 _VerifyFiles = cmdlib.LUClusterVerifyGroup._VerifyFiles
291 master_name = "master.example.com"
293 objects.Node(name=master_name, offline=False, vm_capable=True),
294 objects.Node(name="node2.example.com", offline=False, vm_capable=True),
295 objects.Node(name="node3.example.com", master_candidate=True,
297 objects.Node(name="node4.example.com", offline=False, vm_capable=True),
298 objects.Node(name="nodata.example.com", offline=False, vm_capable=True),
299 objects.Node(name="offline.example.com", offline=True),
301 cluster = objects.Cluster(modify_etc_hosts=True,
302 enabled_hypervisors=[constants.HT_XEN_HVM])
304 constants.CLUSTER_DOMAIN_SECRET_FILE,
305 constants.RAPI_CERT_FILE,
306 constants.RAPI_USERS_FILE,
309 constants.RAPI_USERS_FILE,
310 hv_xen.XL_CONFIG_FILE,
311 constants.VNC_PASSWORD_FILE,
314 constants.CLUSTER_CONF_FILE,
317 hv_xen.XEND_CONFIG_FILE,
318 hv_xen.XL_CONFIG_FILE,
319 constants.VNC_PASSWORD_FILE,
322 master_name: rpc.RpcResult(data=(True, {
323 constants.NV_FILELIST: {
324 constants.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
325 constants.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
326 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
327 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
328 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
330 "node2.example.com": rpc.RpcResult(data=(True, {
331 constants.NV_FILELIST: {
332 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
333 hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
336 "node3.example.com": rpc.RpcResult(data=(True, {
337 constants.NV_FILELIST: {
338 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
339 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
342 "node4.example.com": rpc.RpcResult(data=(True, {
343 constants.NV_FILELIST: {
344 constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
345 constants.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
346 constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
347 constants.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
348 hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
351 "nodata.example.com": rpc.RpcResult(data=(True, {})),
352 "offline.example.com": rpc.RpcResult(offline=True),
354 assert set(nvinfo.keys()) == set(map(operator.attrgetter("name"), nodeinfo))
356 self._VerifyFiles(compat.partial(self._FakeErrorIf, errors), nodeinfo,
358 (files_all, files_opt, files_mc, files_vm))
359 self.assertEqual(sorted(errors), sorted([
360 (None, ("File %s found with 2 different checksums (variant 1 on"
361 " node2.example.com, node3.example.com, node4.example.com;"
362 " variant 2 on master.example.com)" % constants.RAPI_CERT_FILE)),
363 (None, ("File %s is missing from node(s) node2.example.com" %
364 constants.CLUSTER_DOMAIN_SECRET_FILE)),
365 (None, ("File %s should not exist on node(s) node4.example.com" %
366 constants.CLUSTER_CONF_FILE)),
367 (None, ("File %s is missing from node(s) node4.example.com" %
368 hv_xen.XEND_CONFIG_FILE)),
369 (None, ("File %s is missing from node(s) node3.example.com" %
370 constants.CLUSTER_CONF_FILE)),
371 (None, ("File %s found with 2 different checksums (variant 1 on"
372 " master.example.com; variant 2 on node4.example.com)" %
373 constants.CLUSTER_CONF_FILE)),
374 (None, ("File %s is optional, but it must exist on all or no nodes (not"
375 " found on master.example.com, node2.example.com,"
376 " node3.example.com)" % constants.RAPI_USERS_FILE)),
377 (None, ("File %s is optional, but it must exist on all or no nodes (not"
378 " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
379 ("nodata.example.com", "Node did not return file checksum data"),
383 if __name__ == "__main__":
384 testutils.GanetiTestProgram()