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 default_value = aval()
195 self.assertFalse(callable(default_value),
196 msg="Default value returned by function is callable")
200 if aval is not ht.NoDefault and test is not ht.NoType:
201 self.assertTrue(test(default_value),
202 msg=("Default value of '%s.%s' does not verify" %
203 (cls.OP_ID, attr_name)))
205 # If any parameter has documentation, all others need to have it as well
206 has_doc = [doc is not None for (_, _, _, doc) in cls.OP_PARAMS]
207 self.assertTrue(not compat.any(has_doc) or compat.all(has_doc),
208 msg="%s does not document all parameters" % cls)
210 def testValidateNoModification(self):
211 class OpTest(opcodes.OpCode):
213 ("nodef", ht.NoDefault, ht.TMaybeString, None),
214 ("wdef", "default", ht.TMaybeString, None),
215 ("number", 0, ht.TInt, None),
216 ("notype", None, ht.NoType, None),
219 # Missing required parameter "nodef"
221 before = op.__getstate__()
222 self.assertRaises(errors.OpPrereqError, op.Validate, False)
223 self.assertFalse(hasattr(op, "nodef"))
224 self.assertFalse(hasattr(op, "wdef"))
225 self.assertFalse(hasattr(op, "number"))
226 self.assertFalse(hasattr(op, "notype"))
227 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
229 # Required parameter "nodef" is provided
230 op = OpTest(nodef="foo")
231 before = op.__getstate__()
233 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
234 self.assertEqual(op.nodef, "foo")
235 self.assertFalse(hasattr(op, "wdef"))
236 self.assertFalse(hasattr(op, "number"))
237 self.assertFalse(hasattr(op, "notype"))
239 # Missing required parameter "nodef"
240 op = OpTest(wdef="hello", number=999)
241 before = op.__getstate__()
242 self.assertRaises(errors.OpPrereqError, op.Validate, False)
243 self.assertFalse(hasattr(op, "nodef"))
244 self.assertFalse(hasattr(op, "notype"))
245 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
247 # Wrong type for "nodef"
248 op = OpTest(nodef=987)
249 before = op.__getstate__()
250 self.assertRaises(errors.OpPrereqError, op.Validate, False)
251 self.assertEqual(op.nodef, 987)
252 self.assertFalse(hasattr(op, "notype"))
253 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
255 # Testing different types for "notype"
256 op = OpTest(nodef="foo", notype=[1, 2, 3])
257 before = op.__getstate__()
259 self.assertEqual(op.nodef, "foo")
260 self.assertEqual(op.notype, [1, 2, 3])
261 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
263 op = OpTest(nodef="foo", notype="Hello World")
264 before = op.__getstate__()
266 self.assertEqual(op.nodef, "foo")
267 self.assertEqual(op.notype, "Hello World")
268 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
270 def testValidateSetDefaults(self):
271 class OpTest(opcodes.OpCode):
273 # Static default value
274 ("value1", "default", ht.TMaybeString, None),
276 # Default value callback
277 ("value2", lambda: "result", ht.TMaybeString, None),
281 before = op.__getstate__()
283 self.assertNotEqual(op.__getstate__(), before,
284 msg="Opcode was not modified")
285 self.assertEqual(op.value1, "default")
286 self.assertEqual(op.value2, "result")
287 self.assert_(op.dry_run is None)
288 self.assert_(op.debug_level is None)
289 self.assertEqual(op.priority, constants.OP_PRIO_DEFAULT)
291 op = OpTest(value1="hello", value2="world", debug_level=123)
292 before = op.__getstate__()
294 self.assertNotEqual(op.__getstate__(), before,
295 msg="Opcode was not modified")
296 self.assertEqual(op.value1, "hello")
297 self.assertEqual(op.value2, "world")
298 self.assertEqual(op.debug_level, 123)
301 def testOpInstanceMultiAlloc(self):
302 inst = dict([(name, []) for name in opcodes.OpInstanceCreate.GetAllSlots()])
303 inst_op = opcodes.OpInstanceCreate(**inst)
304 inst_state = inst_op.__getstate__()
306 multialloc = opcodes.OpInstanceMultiAlloc(instances=[inst_op])
307 state = multialloc.__getstate__()
308 self.assertEquals(state["instances"], [inst_state])
309 loaded_multialloc = opcodes.OpCode.LoadOpCode(state)
310 (loaded_inst,) = loaded_multialloc.instances
311 self.assertNotEquals(loaded_inst, inst_op)
312 self.assertEquals(loaded_inst.__getstate__(), inst_state)
315 class TestOpcodeDepends(unittest.TestCase):
317 check_relative = opcodes._BuildJobDepCheck(True)
318 check_norelative = opcodes.TNoRelativeJobDependencies
320 for fn in [check_relative, check_norelative]:
321 self.assertTrue(fn(None))
322 self.assertTrue(fn([]))
323 self.assertTrue(fn([(1, [])]))
324 self.assertTrue(fn([(719833, [])]))
325 self.assertTrue(fn([("24879", [])]))
326 self.assertTrue(fn([(2028, [constants.JOB_STATUS_ERROR])]))
328 (2028, [constants.JOB_STATUS_ERROR]),
330 (5063, [constants.JOB_STATUS_SUCCESS, constants.JOB_STATUS_ERROR]),
333 self.assertFalse(fn(1))
334 self.assertFalse(fn([(9, )]))
335 self.assertFalse(fn([(15194, constants.JOB_STATUS_ERROR)]))
339 [(-27740, [constants.JOB_STATUS_CANCELED, constants.JOB_STATUS_ERROR]),
340 (-1, [constants.JOB_STATUS_ERROR]),
343 self.assertTrue(check_relative(i))
344 self.assertFalse(check_norelative(i))
347 class TestResultChecks(unittest.TestCase):
348 def testJobIdList(self):
349 for i in [[], [(False, "error")], [(False, "")],
350 [(True, 123), (True, "999")]]:
351 self.assertTrue(opcodes.TJobIdList(i))
353 for i in ["", [("x", 1)], [[], []], [[False, "", None], [True, 123]]]:
354 self.assertFalse(opcodes.TJobIdList(i))
356 def testJobIdListOnly(self):
357 self.assertTrue(opcodes.TJobIdListOnly({
358 constants.JOB_IDS_KEY: [],
360 self.assertTrue(opcodes.TJobIdListOnly({
361 constants.JOB_IDS_KEY: [(True, "9282")],
364 self.assertFalse(opcodes.TJobIdListOnly({
367 self.assertFalse(opcodes.TJobIdListOnly({
368 constants.JOB_IDS_KEY: [],
371 self.assertFalse(opcodes.TJobIdListOnly({
372 constants.JOB_IDS_KEY: [("foo", "bar")],
374 self.assertFalse(opcodes.TJobIdListOnly({
375 constants.JOB_IDS_KEY: [("one", "two", "three")],
379 class TestClusterOsList(unittest.TestCase):
384 [(constants.DDM_ADD, "dos"),
385 (constants.DDM_REMOVE, "linux")],
389 self.assertTrue(opcodes._TestClusterOsList(i))
391 wrong = ["", 0, "xy", ["Hello World"], object(),
394 [[constants.DDM_ADD]],
395 [(constants.DDM_ADD, "")],
396 [(constants.DDM_REMOVE, "")],
397 [(constants.DDM_ADD, None)],
398 [(constants.DDM_REMOVE, None)],
402 self.assertFalse(opcodes._TestClusterOsList(i))
405 class TestOpInstanceSetParams(unittest.TestCase):
406 def _GenericTests(self, fn):
407 self.assertTrue(fn([]))
408 self.assertTrue(fn([(constants.DDM_ADD, {})]))
409 self.assertTrue(fn([(constants.DDM_REMOVE, {})]))
410 for i in [0, 1, 2, 3, 9, 10, 1024]:
411 self.assertTrue(fn([(i, {})]))
413 self.assertFalse(fn(None))
414 self.assertFalse(fn({}))
415 self.assertFalse(fn(""))
416 self.assertFalse(fn(0))
417 self.assertFalse(fn([(-100, {})]))
418 self.assertFalse(fn([(constants.DDM_ADD, 2, 3)]))
419 self.assertFalse(fn([[constants.DDM_ADD]]))
421 def testNicModifications(self):
422 fn = opcodes.OpInstanceSetParams.TestNicModifications
423 self._GenericTests(fn)
425 for param in constants.INIC_PARAMS:
426 self.assertTrue(fn([[constants.DDM_ADD, {param: None}]]))
427 self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
429 def testDiskModifications(self):
430 fn = opcodes.OpInstanceSetParams.TestDiskModifications
431 self._GenericTests(fn)
433 for param in constants.IDISK_PARAMS:
434 self.assertTrue(fn([[constants.DDM_ADD, {param: 0}]]))
435 self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
438 if __name__ == "__main__":
439 testutils.GanetiTestProgram()