4 # Copyright (C) 2010, 2011, 2012 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 = compat.UniqueFrozenset([
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 testSummaryFormatter(self):
125 class OpTest(opcodes.OpCode):
126 OP_DSC_FIELD = "data"
127 OP_DSC_FORMATTER = lambda _, v: "a"
129 ("data", ht.NoDefault, ht.TString, None),
131 self.assertEqual(OpTest(data="").Summary(), "TEST(a)")
132 self.assertEqual(OpTest(data="b").Summary(), "TEST(a)")
134 def testTinySummary(self):
135 self.assertFalse(utils.FindDuplicates(opcodes._SUMMARY_PREFIX.values()))
136 self.assertTrue(compat.all(prefix.endswith("_") and supplement.endswith("_")
137 for (prefix, supplement) in
138 opcodes._SUMMARY_PREFIX.items()))
140 self.assertEqual(opcodes.OpClusterPostInit().TinySummary(), "C_POST_INIT")
141 self.assertEqual(opcodes.OpNodeRemove().TinySummary(), "N_REMOVE")
142 self.assertEqual(opcodes.OpInstanceMigrate().TinySummary(), "I_MIGRATE")
143 self.assertEqual(opcodes.OpGroupQuery().TinySummary(), "G_QUERY")
144 self.assertEqual(opcodes.OpTestJqueue().TinySummary(), "TEST_JQUEUE")
146 def testListSummary(self):
147 class OpTest(opcodes.OpCode):
148 OP_DSC_FIELD = "data"
150 ("data", ht.NoDefault, ht.TList, None),
153 self.assertEqual(OpTest(data=["a", "b", "c"]).Summary(),
155 self.assertEqual(OpTest(data=["a", None, "c"]).Summary(),
157 self.assertEqual(OpTest(data=[1, 2, 3, 4]).Summary(), "TEST(1,2,3,4)")
160 self.assertFalse(utils.FindDuplicates(cls.OP_ID
161 for cls in opcodes._GetOpList()))
162 self.assertEqual(len(opcodes._GetOpList()), len(opcodes.OP_MAPPING))
164 def testParams(self):
165 supported_by_all = set(["debug_level", "dry_run", "priority"])
167 self.assertTrue(opcodes.BaseOpCode not in opcodes.OP_MAPPING.values())
168 self.assertTrue(opcodes.OpCode not in opcodes.OP_MAPPING.values())
170 for cls in opcodes.OP_MAPPING.values() + [opcodes.OpCode]:
171 all_slots = cls.GetAllSlots()
173 self.assertEqual(len(set(all_slots) & supported_by_all), 3,
174 msg=("Opcode %s doesn't support all base"
175 " parameters (%r)" % (cls.OP_ID, supported_by_all)))
177 # All opcodes must have OP_PARAMS
178 self.assert_(hasattr(cls, "OP_PARAMS"),
179 msg="%s doesn't have OP_PARAMS" % cls.OP_ID)
181 param_names = [name for (name, _, _, _) in cls.GetAllParams()]
183 self.assertEqual(all_slots, param_names)
185 # Without inheritance
186 self.assertEqual(cls.__slots__,
187 [name for (name, _, _, _) in cls.OP_PARAMS])
189 # This won't work if parameters are converted to a dictionary
190 duplicates = utils.FindDuplicates(param_names)
191 self.assertFalse(duplicates,
192 msg=("Found duplicate parameters %r in %s" %
193 (duplicates, cls.OP_ID)))
195 # Check parameter definitions
196 for attr_name, aval, test, doc in cls.GetAllParams():
197 self.assert_(attr_name)
198 self.assert_(test is None or test is ht.NoType or callable(test),
199 msg=("Invalid type check for %s.%s" %
200 (cls.OP_ID, attr_name)))
201 self.assertTrue(doc is None or isinstance(doc, basestring))
204 default_value = aval()
205 self.assertFalse(callable(default_value),
206 msg=("Default value of %s.%s returned by function"
207 " is callable" % (cls.OP_ID, attr_name)))
209 self.assertFalse(isinstance(aval, (list, dict, set)),
210 msg=("Default value of %s.%s is mutable (%s)" %
211 (cls.OP_ID, attr_name, repr(aval))))
215 if aval is not ht.NoDefault and test is not ht.NoType:
216 self.assertTrue(test(default_value),
217 msg=("Default value of %s.%s does not verify" %
218 (cls.OP_ID, attr_name)))
220 # If any parameter has documentation, all others need to have it as well
221 has_doc = [doc is not None for (_, _, _, doc) in cls.OP_PARAMS]
222 self.assertTrue(not compat.any(has_doc) or compat.all(has_doc),
223 msg="%s does not document all parameters" % cls)
225 def testValidateNoModification(self):
226 class OpTest(opcodes.OpCode):
228 ("nodef", ht.NoDefault, ht.TMaybeString, None),
229 ("wdef", "default", ht.TMaybeString, None),
230 ("number", 0, ht.TInt, None),
231 ("notype", None, ht.NoType, None),
234 # Missing required parameter "nodef"
236 before = op.__getstate__()
237 self.assertRaises(errors.OpPrereqError, op.Validate, False)
238 self.assertFalse(hasattr(op, "nodef"))
239 self.assertFalse(hasattr(op, "wdef"))
240 self.assertFalse(hasattr(op, "number"))
241 self.assertFalse(hasattr(op, "notype"))
242 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
244 # Required parameter "nodef" is provided
245 op = OpTest(nodef="foo")
246 before = op.__getstate__()
248 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
249 self.assertEqual(op.nodef, "foo")
250 self.assertFalse(hasattr(op, "wdef"))
251 self.assertFalse(hasattr(op, "number"))
252 self.assertFalse(hasattr(op, "notype"))
254 # Missing required parameter "nodef"
255 op = OpTest(wdef="hello", number=999)
256 before = op.__getstate__()
257 self.assertRaises(errors.OpPrereqError, op.Validate, False)
258 self.assertFalse(hasattr(op, "nodef"))
259 self.assertFalse(hasattr(op, "notype"))
260 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
262 # Wrong type for "nodef"
263 op = OpTest(nodef=987)
264 before = op.__getstate__()
265 self.assertRaises(errors.OpPrereqError, op.Validate, False)
266 self.assertEqual(op.nodef, 987)
267 self.assertFalse(hasattr(op, "notype"))
268 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
270 # Testing different types for "notype"
271 op = OpTest(nodef="foo", notype=[1, 2, 3])
272 before = op.__getstate__()
274 self.assertEqual(op.nodef, "foo")
275 self.assertEqual(op.notype, [1, 2, 3])
276 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
278 op = OpTest(nodef="foo", notype="Hello World")
279 before = op.__getstate__()
281 self.assertEqual(op.nodef, "foo")
282 self.assertEqual(op.notype, "Hello World")
283 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
285 def testValidateSetDefaults(self):
286 class OpTest(opcodes.OpCode):
288 # Static default value
289 ("value1", "default", ht.TMaybeString, None),
291 # Default value callback
292 ("value2", lambda: "result", ht.TMaybeString, None),
296 before = op.__getstate__()
298 self.assertNotEqual(op.__getstate__(), before,
299 msg="Opcode was not modified")
300 self.assertEqual(op.value1, "default")
301 self.assertEqual(op.value2, "result")
302 self.assert_(op.dry_run is None)
303 self.assert_(op.debug_level is None)
304 self.assertEqual(op.priority, constants.OP_PRIO_DEFAULT)
306 op = OpTest(value1="hello", value2="world", debug_level=123)
307 before = op.__getstate__()
309 self.assertNotEqual(op.__getstate__(), before,
310 msg="Opcode was not modified")
311 self.assertEqual(op.value1, "hello")
312 self.assertEqual(op.value2, "world")
313 self.assertEqual(op.debug_level, 123)
315 def testOpInstanceMultiAlloc(self):
316 inst = dict([(name, []) for name in opcodes.OpInstanceCreate.GetAllSlots()])
317 inst_op = opcodes.OpInstanceCreate(**inst)
318 inst_state = inst_op.__getstate__()
320 multialloc = opcodes.OpInstanceMultiAlloc(instances=[inst_op])
321 state = multialloc.__getstate__()
322 self.assertEquals(state["instances"], [inst_state])
323 loaded_multialloc = opcodes.OpCode.LoadOpCode(state)
324 (loaded_inst,) = loaded_multialloc.instances
325 self.assertNotEquals(loaded_inst, inst_op)
326 self.assertEquals(loaded_inst.__getstate__(), inst_state)
329 class TestOpcodeDepends(unittest.TestCase):
331 check_relative = opcodes._BuildJobDepCheck(True)
332 check_norelative = opcodes.TNoRelativeJobDependencies
334 for fn in [check_relative, check_norelative]:
335 self.assertTrue(fn(None))
336 self.assertTrue(fn([]))
337 self.assertTrue(fn([(1, [])]))
338 self.assertTrue(fn([(719833, [])]))
339 self.assertTrue(fn([("24879", [])]))
340 self.assertTrue(fn([(2028, [constants.JOB_STATUS_ERROR])]))
342 (2028, [constants.JOB_STATUS_ERROR]),
344 (5063, [constants.JOB_STATUS_SUCCESS, constants.JOB_STATUS_ERROR]),
347 self.assertFalse(fn(1))
348 self.assertFalse(fn([(9, )]))
349 self.assertFalse(fn([(15194, constants.JOB_STATUS_ERROR)]))
353 [(-27740, [constants.JOB_STATUS_CANCELED, constants.JOB_STATUS_ERROR]),
354 (-1, [constants.JOB_STATUS_ERROR]),
357 self.assertTrue(check_relative(i))
358 self.assertFalse(check_norelative(i))
361 class TestResultChecks(unittest.TestCase):
362 def testJobIdList(self):
363 for i in [[], [(False, "error")], [(False, "")],
364 [(True, 123), (True, "999")]]:
365 self.assertTrue(opcodes.TJobIdList(i))
367 for i in ["", [("x", 1)], [[], []], [[False, "", None], [True, 123]]]:
368 self.assertFalse(opcodes.TJobIdList(i))
370 def testJobIdListOnly(self):
371 self.assertTrue(opcodes.TJobIdListOnly({
372 constants.JOB_IDS_KEY: [],
374 self.assertTrue(opcodes.TJobIdListOnly({
375 constants.JOB_IDS_KEY: [(True, "9282")],
378 self.assertFalse(opcodes.TJobIdListOnly({
381 self.assertFalse(opcodes.TJobIdListOnly({
382 constants.JOB_IDS_KEY: [],
385 self.assertFalse(opcodes.TJobIdListOnly({
386 constants.JOB_IDS_KEY: [("foo", "bar")],
388 self.assertFalse(opcodes.TJobIdListOnly({
389 constants.JOB_IDS_KEY: [("one", "two", "three")],
393 class TestClusterOsList(unittest.TestCase):
398 [(constants.DDM_ADD, "dos"),
399 (constants.DDM_REMOVE, "linux")],
403 self.assertTrue(opcodes._TestClusterOsList(i))
405 wrong = ["", 0, "xy", ["Hello World"], object(),
408 [[constants.DDM_ADD]],
409 [(constants.DDM_ADD, "")],
410 [(constants.DDM_REMOVE, "")],
411 [(constants.DDM_ADD, None)],
412 [(constants.DDM_REMOVE, None)],
416 self.assertFalse(opcodes._TestClusterOsList(i))
419 class TestOpInstanceSetParams(unittest.TestCase):
420 def _GenericTests(self, fn):
421 self.assertTrue(fn([]))
422 self.assertTrue(fn([(constants.DDM_ADD, {})]))
423 self.assertTrue(fn([(constants.DDM_REMOVE, {})]))
424 for i in [0, 1, 2, 3, 9, 10, 1024]:
425 self.assertTrue(fn([(i, {})]))
427 self.assertFalse(fn(None))
428 self.assertFalse(fn({}))
429 self.assertFalse(fn(""))
430 self.assertFalse(fn(0))
431 self.assertFalse(fn([(-100, {})]))
432 self.assertFalse(fn([(constants.DDM_ADD, 2, 3)]))
433 self.assertFalse(fn([[constants.DDM_ADD]]))
435 def testNicModifications(self):
436 fn = opcodes.OpInstanceSetParams.TestNicModifications
437 self._GenericTests(fn)
439 for param in constants.INIC_PARAMS:
440 self.assertTrue(fn([[constants.DDM_ADD, {param: None}]]))
441 self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
443 def testDiskModifications(self):
444 fn = opcodes.OpInstanceSetParams.TestDiskModifications
445 self._GenericTests(fn)
447 for param in constants.IDISK_PARAMS:
448 self.assertTrue(fn([[constants.DDM_ADD, {param: 0}]]))
449 self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
452 if __name__ == "__main__":
453 testutils.GanetiTestProgram()