4 # Copyright (C) 2010, 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 testing ganeti.backend"""
28 from ganeti import utils
29 from ganeti import opcodes
31 from ganeti import constants
32 from ganeti import errors
33 from ganeti import compat
38 #: Unless an opcode is included in the following list it must have a result
40 MISSING_RESULT_CHECK = frozenset([
41 opcodes.OpTestAllocator,
48 class TestOpcodes(unittest.TestCase):
50 self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, None)
51 self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, "")
52 self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {})
53 self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {"OP_ID": ""})
55 for cls in opcodes.OP_MAPPING.values():
56 self.assert_(cls.OP_ID.startswith("OP_"))
57 self.assert_(len(cls.OP_ID) > 3)
58 self.assertEqual(cls.OP_ID, cls.OP_ID.upper())
59 self.assertEqual(cls.OP_ID, opcodes._NameToId(cls.__name__))
60 self.assertFalse(compat.any(cls.OP_ID.startswith(prefix)
61 for prefix in opcodes._SUMMARY_PREFIX.keys()))
62 if cls in MISSING_RESULT_CHECK:
63 self.assertTrue(cls.OP_RESULT is None,
64 msg=("%s is listed to not have a result check" %
67 self.assertTrue(callable(cls.OP_RESULT),
68 msg=("%s should have a result check" % cls.OP_ID))
70 self.assertRaises(TypeError, cls, unsupported_parameter="some value")
76 # Variables supported by all opcodes
77 {"dry_run": False, "debug_level": 0, },
80 dict([(name, []) for name in cls.GetAllSlots()])
86 self.assertEqual(op.OP_ID, cls.OP_ID)
87 self._checkSummary(op)
90 state = op.__getstate__()
91 self.assert_(isinstance(state, dict))
93 restored = opcodes.OpCode.LoadOpCode(state)
94 self.assert_(isinstance(restored, cls))
95 self._checkSummary(restored)
97 for name in ["x_y_z", "hello_world"]:
98 assert name not in cls.GetAllSlots()
99 for value in [None, True, False, [], "Hello World"]:
100 self.assertRaises(AttributeError, setattr, op, name, value)
102 def _checkSummary(self, op):
103 summary = op.Summary()
105 if hasattr(op, "OP_DSC_FIELD"):
106 self.assert_(("OP_%s" % summary).startswith("%s(" % op.OP_ID))
107 self.assert_(summary.endswith(")"))
109 self.assertEqual("OP_%s" % summary, op.OP_ID)
111 def testSummary(self):
112 class OpTest(opcodes.OpCode):
113 OP_DSC_FIELD = "data"
115 ("data", ht.NoDefault, ht.TString, None),
118 self.assertEqual(OpTest(data="").Summary(), "TEST()")
119 self.assertEqual(OpTest(data="Hello World").Summary(),
121 self.assertEqual(OpTest(data="node1.example.com").Summary(),
122 "TEST(node1.example.com)")
124 def testTinySummary(self):
125 self.assertFalse(utils.FindDuplicates(opcodes._SUMMARY_PREFIX.values()))
126 self.assertTrue(compat.all(prefix.endswith("_") and supplement.endswith("_")
127 for (prefix, supplement) in
128 opcodes._SUMMARY_PREFIX.items()))
130 self.assertEqual(opcodes.OpClusterPostInit().TinySummary(), "C_POST_INIT")
131 self.assertEqual(opcodes.OpNodeRemove().TinySummary(), "N_REMOVE")
132 self.assertEqual(opcodes.OpInstanceMigrate().TinySummary(), "I_MIGRATE")
133 self.assertEqual(opcodes.OpGroupQuery().TinySummary(), "G_QUERY")
134 self.assertEqual(opcodes.OpTestJqueue().TinySummary(), "TEST_JQUEUE")
136 def testListSummary(self):
137 class OpTest(opcodes.OpCode):
138 OP_DSC_FIELD = "data"
140 ("data", ht.NoDefault, ht.TList, None),
143 self.assertEqual(OpTest(data=["a", "b", "c"]).Summary(),
145 self.assertEqual(OpTest(data=["a", None, "c"]).Summary(),
147 self.assertEqual(OpTest(data=[1, 2, 3, 4]).Summary(), "TEST(1,2,3,4)")
150 self.assertFalse(utils.FindDuplicates(cls.OP_ID
151 for cls in opcodes._GetOpList()))
152 self.assertEqual(len(opcodes._GetOpList()), len(opcodes.OP_MAPPING))
154 def testParams(self):
155 supported_by_all = set(["debug_level", "dry_run", "priority"])
157 self.assertTrue(opcodes.BaseOpCode not in opcodes.OP_MAPPING.values())
158 self.assertTrue(opcodes.OpCode not in opcodes.OP_MAPPING.values())
160 for cls in opcodes.OP_MAPPING.values() + [opcodes.OpCode]:
161 all_slots = cls.GetAllSlots()
163 self.assertEqual(len(set(all_slots) & supported_by_all), 3,
164 msg=("Opcode %s doesn't support all base"
165 " parameters (%r)" % (cls.OP_ID, supported_by_all)))
167 # All opcodes must have OP_PARAMS
168 self.assert_(hasattr(cls, "OP_PARAMS"),
169 msg="%s doesn't have OP_PARAMS" % cls.OP_ID)
171 param_names = [name for (name, _, _, _) in cls.GetAllParams()]
173 self.assertEqual(all_slots, param_names)
175 # Without inheritance
176 self.assertEqual(cls.__slots__,
177 [name for (name, _, _, _) in cls.OP_PARAMS])
179 # This won't work if parameters are converted to a dictionary
180 duplicates = utils.FindDuplicates(param_names)
181 self.assertFalse(duplicates,
182 msg=("Found duplicate parameters %r in %s" %
183 (duplicates, cls.OP_ID)))
185 # Check parameter definitions
186 for attr_name, aval, test, doc in cls.GetAllParams():
187 self.assert_(attr_name)
188 self.assert_(test is None or test is ht.NoType or callable(test),
189 msg=("Invalid type check for %s.%s" %
190 (cls.OP_ID, attr_name)))
191 self.assertTrue(doc is None or isinstance(doc, basestring))
194 self.assertFalse(callable(aval()),
195 msg="Default value returned by function is callable")
197 # If any parameter has documentation, all others need to have it as well
198 has_doc = [doc is not None for (_, _, _, doc) in cls.OP_PARAMS]
199 self.assertTrue(not compat.any(has_doc) or compat.all(has_doc),
200 msg="%s does not document all parameters" % cls)
202 def testValidateNoModification(self):
203 class OpTest(opcodes.OpCode):
205 ("nodef", ht.NoDefault, ht.TMaybeString, None),
206 ("wdef", "default", ht.TMaybeString, None),
207 ("number", 0, ht.TInt, None),
208 ("notype", None, ht.NoType, None),
211 # Missing required parameter "nodef"
213 before = op.__getstate__()
214 self.assertRaises(errors.OpPrereqError, op.Validate, False)
215 self.assertFalse(hasattr(op, "nodef"))
216 self.assertFalse(hasattr(op, "wdef"))
217 self.assertFalse(hasattr(op, "number"))
218 self.assertFalse(hasattr(op, "notype"))
219 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
221 # Required parameter "nodef" is provided
222 op = OpTest(nodef="foo")
223 before = op.__getstate__()
225 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
226 self.assertEqual(op.nodef, "foo")
227 self.assertFalse(hasattr(op, "wdef"))
228 self.assertFalse(hasattr(op, "number"))
229 self.assertFalse(hasattr(op, "notype"))
231 # Missing required parameter "nodef"
232 op = OpTest(wdef="hello", number=999)
233 before = op.__getstate__()
234 self.assertRaises(errors.OpPrereqError, op.Validate, False)
235 self.assertFalse(hasattr(op, "nodef"))
236 self.assertFalse(hasattr(op, "notype"))
237 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
239 # Wrong type for "nodef"
240 op = OpTest(nodef=987)
241 before = op.__getstate__()
242 self.assertRaises(errors.OpPrereqError, op.Validate, False)
243 self.assertEqual(op.nodef, 987)
244 self.assertFalse(hasattr(op, "notype"))
245 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
247 # Testing different types for "notype"
248 op = OpTest(nodef="foo", notype=[1, 2, 3])
249 before = op.__getstate__()
251 self.assertEqual(op.nodef, "foo")
252 self.assertEqual(op.notype, [1, 2, 3])
253 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
255 op = OpTest(nodef="foo", notype="Hello World")
256 before = op.__getstate__()
258 self.assertEqual(op.nodef, "foo")
259 self.assertEqual(op.notype, "Hello World")
260 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
262 def testValidateSetDefaults(self):
263 class OpTest(opcodes.OpCode):
265 # Static default value
266 ("value1", "default", ht.TMaybeString, None),
268 # Default value callback
269 ("value2", lambda: "result", ht.TMaybeString, None),
273 before = op.__getstate__()
275 self.assertNotEqual(op.__getstate__(), before,
276 msg="Opcode was not modified")
277 self.assertEqual(op.value1, "default")
278 self.assertEqual(op.value2, "result")
279 self.assert_(op.dry_run is None)
280 self.assert_(op.debug_level is None)
281 self.assertEqual(op.priority, constants.OP_PRIO_DEFAULT)
283 op = OpTest(value1="hello", value2="world", debug_level=123)
284 before = op.__getstate__()
286 self.assertNotEqual(op.__getstate__(), before,
287 msg="Opcode was not modified")
288 self.assertEqual(op.value1, "hello")
289 self.assertEqual(op.value2, "world")
290 self.assertEqual(op.debug_level, 123)
293 def testOpInstanceMultiAlloc(self):
294 inst = dict([(name, []) for name in opcodes.OpInstanceCreate.GetAllSlots()])
295 inst_op = opcodes.OpInstanceCreate(**inst)
296 inst_state = inst_op.__getstate__()
298 multialloc = opcodes.OpInstanceMultiAlloc(instances=[inst_op])
299 state = multialloc.__getstate__()
300 self.assertEquals(state["instances"], [inst_state])
301 loaded_multialloc = opcodes.OpCode.LoadOpCode(state)
302 (loaded_inst,) = loaded_multialloc.instances
303 self.assertNotEquals(loaded_inst, inst_op)
304 self.assertEquals(loaded_inst.__getstate__(), inst_state)
307 class TestOpcodeDepends(unittest.TestCase):
309 check_relative = opcodes._BuildJobDepCheck(True)
310 check_norelative = opcodes.TNoRelativeJobDependencies
312 for fn in [check_relative, check_norelative]:
313 self.assertTrue(fn(None))
314 self.assertTrue(fn([]))
315 self.assertTrue(fn([(1, [])]))
316 self.assertTrue(fn([(719833, [])]))
317 self.assertTrue(fn([("24879", [])]))
318 self.assertTrue(fn([(2028, [constants.JOB_STATUS_ERROR])]))
320 (2028, [constants.JOB_STATUS_ERROR]),
322 (5063, [constants.JOB_STATUS_SUCCESS, constants.JOB_STATUS_ERROR]),
325 self.assertFalse(fn(1))
326 self.assertFalse(fn([(9, )]))
327 self.assertFalse(fn([(15194, constants.JOB_STATUS_ERROR)]))
331 [(-27740, [constants.JOB_STATUS_CANCELED, constants.JOB_STATUS_ERROR]),
332 (-1, [constants.JOB_STATUS_ERROR]),
335 self.assertTrue(check_relative(i))
336 self.assertFalse(check_norelative(i))
339 class TestResultChecks(unittest.TestCase):
340 def testJobIdList(self):
341 for i in [[], [(False, "error")], [(False, "")],
342 [(True, 123), (True, "999")]]:
343 self.assertTrue(opcodes.TJobIdList(i))
345 for i in ["", [("x", 1)], [[], []], [[False, "", None], [True, 123]]]:
346 self.assertFalse(opcodes.TJobIdList(i))
348 def testJobIdListOnly(self):
349 self.assertTrue(opcodes.TJobIdListOnly({
350 constants.JOB_IDS_KEY: [],
352 self.assertTrue(opcodes.TJobIdListOnly({
353 constants.JOB_IDS_KEY: [(True, "9282")],
356 self.assertFalse(opcodes.TJobIdListOnly({
359 self.assertFalse(opcodes.TJobIdListOnly({
360 constants.JOB_IDS_KEY: [],
363 self.assertFalse(opcodes.TJobIdListOnly({
364 constants.JOB_IDS_KEY: [("foo", "bar")],
366 self.assertFalse(opcodes.TJobIdListOnly({
367 constants.JOB_IDS_KEY: [("one", "two", "three")],
371 class TestClusterOsList(unittest.TestCase):
376 [(constants.DDM_ADD, "dos"),
377 (constants.DDM_REMOVE, "linux")],
381 self.assertTrue(opcodes._TestClusterOsList(i))
383 wrong = ["", 0, "xy", ["Hello World"], object(),
386 [[constants.DDM_ADD]],
387 [(constants.DDM_ADD, "")],
388 [(constants.DDM_REMOVE, "")],
389 [(constants.DDM_ADD, None)],
390 [(constants.DDM_REMOVE, None)],
394 self.assertFalse(opcodes._TestClusterOsList(i))
397 class TestOpInstanceSetParams(unittest.TestCase):
398 def _GenericTests(self, fn):
399 self.assertTrue(fn([]))
400 self.assertTrue(fn([(constants.DDM_ADD, {})]))
401 self.assertTrue(fn([(constants.DDM_REMOVE, {})]))
402 for i in [0, 1, 2, 3, 9, 10, 1024]:
403 self.assertTrue(fn([(i, {})]))
405 self.assertFalse(fn(None))
406 self.assertFalse(fn({}))
407 self.assertFalse(fn(""))
408 self.assertFalse(fn(0))
409 self.assertFalse(fn([(-100, {})]))
410 self.assertFalse(fn([(constants.DDM_ADD, 2, 3)]))
411 self.assertFalse(fn([[constants.DDM_ADD]]))
413 def testNicModifications(self):
414 fn = opcodes.OpInstanceSetParams.TestNicModifications
415 self._GenericTests(fn)
417 for param in constants.INIC_PARAMS:
418 self.assertTrue(fn([[constants.DDM_ADD, {param: None}]]))
419 self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
421 def testDiskModifications(self):
422 fn = opcodes.OpInstanceSetParams.TestDiskModifications
423 self._GenericTests(fn)
425 for param in constants.IDISK_PARAMS:
426 self.assertTrue(fn([[constants.DDM_ADD, {param: 0}]]))
427 self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
430 if __name__ == "__main__":
431 testutils.GanetiTestProgram()