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([
45 opcodes.OpTestAllocator,
52 class TestOpcodes(unittest.TestCase):
54 self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, None)
55 self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, "")
56 self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {})
57 self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {"OP_ID": ""})
59 for cls in opcodes.OP_MAPPING.values():
60 self.assert_(cls.OP_ID.startswith("OP_"))
61 self.assert_(len(cls.OP_ID) > 3)
62 self.assertEqual(cls.OP_ID, cls.OP_ID.upper())
63 self.assertEqual(cls.OP_ID, opcodes._NameToId(cls.__name__))
64 self.assertFalse(compat.any(cls.OP_ID.startswith(prefix)
65 for prefix in opcodes._SUMMARY_PREFIX.keys()))
66 if cls in MISSING_RESULT_CHECK:
67 self.assertTrue(cls.OP_RESULT is None,
68 msg=("%s is listed to not have a result check" %
71 self.assertTrue(callable(cls.OP_RESULT),
72 msg=("%s should have a result check" % cls.OP_ID))
74 self.assertRaises(TypeError, cls, unsupported_parameter="some value")
80 # Variables supported by all opcodes
81 {"dry_run": False, "debug_level": 0, },
84 dict([(name, False) for name in cls._all_slots()])
90 self.assertEqual(op.OP_ID, cls.OP_ID)
91 self._checkSummary(op)
94 state = op.__getstate__()
95 self.assert_(isinstance(state, dict))
97 restored = opcodes.OpCode.LoadOpCode(state)
98 self.assert_(isinstance(restored, cls))
99 self._checkSummary(restored)
101 for name in ["x_y_z", "hello_world"]:
102 assert name not in cls._all_slots()
103 for value in [None, True, False, [], "Hello World"]:
104 self.assertRaises(AttributeError, setattr, op, name, value)
106 def _checkSummary(self, op):
107 summary = op.Summary()
109 if hasattr(op, "OP_DSC_FIELD"):
110 self.assert_(("OP_%s" % summary).startswith("%s(" % op.OP_ID))
111 self.assert_(summary.endswith(")"))
113 self.assertEqual("OP_%s" % summary, op.OP_ID)
115 def testSummary(self):
116 class OpTest(opcodes.OpCode):
117 OP_DSC_FIELD = "data"
119 ("data", ht.NoDefault, ht.TString, None),
122 self.assertEqual(OpTest(data="").Summary(), "TEST()")
123 self.assertEqual(OpTest(data="Hello World").Summary(),
125 self.assertEqual(OpTest(data="node1.example.com").Summary(),
126 "TEST(node1.example.com)")
128 def testTinySummary(self):
129 self.assertFalse(utils.FindDuplicates(opcodes._SUMMARY_PREFIX.values()))
130 self.assertTrue(compat.all(prefix.endswith("_") and supplement.endswith("_")
131 for (prefix, supplement) in
132 opcodes._SUMMARY_PREFIX.items()))
134 self.assertEqual(opcodes.OpClusterPostInit().TinySummary(), "C_POST_INIT")
135 self.assertEqual(opcodes.OpNodeRemove().TinySummary(), "N_REMOVE")
136 self.assertEqual(opcodes.OpInstanceMigrate().TinySummary(), "I_MIGRATE")
137 self.assertEqual(opcodes.OpGroupQuery().TinySummary(), "G_QUERY")
138 self.assertEqual(opcodes.OpTestJqueue().TinySummary(), "TEST_JQUEUE")
140 def testListSummary(self):
141 class OpTest(opcodes.OpCode):
142 OP_DSC_FIELD = "data"
144 ("data", ht.NoDefault, ht.TList, None),
147 self.assertEqual(OpTest(data=["a", "b", "c"]).Summary(),
149 self.assertEqual(OpTest(data=["a", None, "c"]).Summary(),
151 self.assertEqual(OpTest(data=[1, 2, 3, 4]).Summary(), "TEST(1,2,3,4)")
154 self.assertFalse(utils.FindDuplicates(cls.OP_ID
155 for cls in opcodes._GetOpList()))
156 self.assertEqual(len(opcodes._GetOpList()), len(opcodes.OP_MAPPING))
158 def testParams(self):
159 supported_by_all = set(["debug_level", "dry_run", "priority"])
161 self.assertTrue(opcodes.BaseOpCode not in opcodes.OP_MAPPING.values())
162 self.assertTrue(opcodes.OpCode not in opcodes.OP_MAPPING.values())
164 for cls in opcodes.OP_MAPPING.values() + [opcodes.OpCode]:
165 all_slots = cls._all_slots()
167 self.assertEqual(len(set(all_slots) & supported_by_all), 3,
168 msg=("Opcode %s doesn't support all base"
169 " parameters (%r)" % (cls.OP_ID, supported_by_all)))
171 # All opcodes must have OP_PARAMS
172 self.assert_(hasattr(cls, "OP_PARAMS"),
173 msg="%s doesn't have OP_PARAMS" % cls.OP_ID)
175 param_names = [name for (name, _, _, _) in cls.GetAllParams()]
177 self.assertEqual(all_slots, param_names)
179 # Without inheritance
180 self.assertEqual(cls.__slots__,
181 [name for (name, _, _, _) in cls.OP_PARAMS])
183 # This won't work if parameters are converted to a dictionary
184 duplicates = utils.FindDuplicates(param_names)
185 self.assertFalse(duplicates,
186 msg=("Found duplicate parameters %r in %s" %
187 (duplicates, cls.OP_ID)))
189 # Check parameter definitions
190 for attr_name, aval, test, doc in cls.GetAllParams():
191 self.assert_(attr_name)
192 self.assert_(test is None or test is ht.NoType or callable(test),
193 msg=("Invalid type check for %s.%s" %
194 (cls.OP_ID, attr_name)))
195 self.assertTrue(doc is None or isinstance(doc, basestring))
198 self.assertFalse(callable(aval()),
199 msg="Default value returned by function is callable")
201 # If any parameter has documentation, all others need to have it as well
202 has_doc = [doc is not None for (_, _, _, doc) in cls.OP_PARAMS]
203 self.assertTrue(not compat.any(has_doc) or compat.all(has_doc),
204 msg="%s does not document all parameters" % cls)
206 def testValidateNoModification(self):
207 class OpTest(opcodes.OpCode):
209 ("nodef", ht.NoDefault, ht.TMaybeString, None),
210 ("wdef", "default", ht.TMaybeString, None),
211 ("number", 0, ht.TInt, None),
212 ("notype", None, ht.NoType, None),
215 # Missing required parameter "nodef"
217 before = op.__getstate__()
218 self.assertRaises(errors.OpPrereqError, op.Validate, False)
219 self.assertFalse(hasattr(op, "nodef"))
220 self.assertFalse(hasattr(op, "wdef"))
221 self.assertFalse(hasattr(op, "number"))
222 self.assertFalse(hasattr(op, "notype"))
223 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
225 # Required parameter "nodef" is provided
226 op = OpTest(nodef="foo")
227 before = op.__getstate__()
229 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
230 self.assertEqual(op.nodef, "foo")
231 self.assertFalse(hasattr(op, "wdef"))
232 self.assertFalse(hasattr(op, "number"))
233 self.assertFalse(hasattr(op, "notype"))
235 # Missing required parameter "nodef"
236 op = OpTest(wdef="hello", number=999)
237 before = op.__getstate__()
238 self.assertRaises(errors.OpPrereqError, op.Validate, False)
239 self.assertFalse(hasattr(op, "nodef"))
240 self.assertFalse(hasattr(op, "notype"))
241 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
243 # Wrong type for "nodef"
244 op = OpTest(nodef=987)
245 before = op.__getstate__()
246 self.assertRaises(errors.OpPrereqError, op.Validate, False)
247 self.assertEqual(op.nodef, 987)
248 self.assertFalse(hasattr(op, "notype"))
249 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
251 # Testing different types for "notype"
252 op = OpTest(nodef="foo", notype=[1, 2, 3])
253 before = op.__getstate__()
255 self.assertEqual(op.nodef, "foo")
256 self.assertEqual(op.notype, [1, 2, 3])
257 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
259 op = OpTest(nodef="foo", notype="Hello World")
260 before = op.__getstate__()
262 self.assertEqual(op.nodef, "foo")
263 self.assertEqual(op.notype, "Hello World")
264 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
266 def testValidateSetDefaults(self):
267 class OpTest(opcodes.OpCode):
269 # Static default value
270 ("value1", "default", ht.TMaybeString, None),
272 # Default value callback
273 ("value2", lambda: "result", ht.TMaybeString, None),
277 before = op.__getstate__()
279 self.assertNotEqual(op.__getstate__(), before,
280 msg="Opcode was not modified")
281 self.assertEqual(op.value1, "default")
282 self.assertEqual(op.value2, "result")
283 self.assert_(op.dry_run is None)
284 self.assert_(op.debug_level is None)
285 self.assertEqual(op.priority, constants.OP_PRIO_DEFAULT)
287 op = OpTest(value1="hello", value2="world", debug_level=123)
288 before = op.__getstate__()
290 self.assertNotEqual(op.__getstate__(), before,
291 msg="Opcode was not modified")
292 self.assertEqual(op.value1, "hello")
293 self.assertEqual(op.value2, "world")
294 self.assertEqual(op.debug_level, 123)
297 class TestOpcodeDepends(unittest.TestCase):
299 check_relative = opcodes._BuildJobDepCheck(True)
300 check_norelative = opcodes.TNoRelativeJobDependencies
302 for fn in [check_relative, check_norelative]:
303 self.assertTrue(fn(None))
304 self.assertTrue(fn([]))
305 self.assertTrue(fn([(1, [])]))
306 self.assertTrue(fn([(719833, [])]))
307 self.assertTrue(fn([("24879", [])]))
308 self.assertTrue(fn([(2028, [constants.JOB_STATUS_ERROR])]))
310 (2028, [constants.JOB_STATUS_ERROR]),
312 (5063, [constants.JOB_STATUS_SUCCESS, constants.JOB_STATUS_ERROR]),
315 self.assertFalse(fn(1))
316 self.assertFalse(fn([(9, )]))
317 self.assertFalse(fn([(15194, constants.JOB_STATUS_ERROR)]))
321 [(-27740, [constants.JOB_STATUS_CANCELED, constants.JOB_STATUS_ERROR]),
322 (-1, [constants.JOB_STATUS_ERROR]),
325 self.assertTrue(check_relative(i))
326 self.assertFalse(check_norelative(i))
329 class TestResultChecks(unittest.TestCase):
330 def testJobIdList(self):
331 for i in [[], [(False, "error")], [(False, "")],
332 [(True, 123), (True, "999")]]:
333 self.assertTrue(opcodes.TJobIdList(i))
335 for i in ["", [("x", 1)], [[], []], [[False, "", None], [True, 123]]]:
336 self.assertFalse(opcodes.TJobIdList(i))
338 def testJobIdListOnly(self):
339 self.assertTrue(opcodes.TJobIdListOnly({
340 constants.JOB_IDS_KEY: [],
342 self.assertTrue(opcodes.TJobIdListOnly({
343 constants.JOB_IDS_KEY: [(True, "9282")],
346 self.assertFalse(opcodes.TJobIdListOnly({
349 self.assertFalse(opcodes.TJobIdListOnly({
350 constants.JOB_IDS_KEY: [],
353 self.assertFalse(opcodes.TJobIdListOnly({
354 constants.JOB_IDS_KEY: [("foo", "bar")],
356 self.assertFalse(opcodes.TJobIdListOnly({
357 constants.JOB_IDS_KEY: [("one", "two", "three")],
361 class TestClusterOsList(unittest.TestCase):
366 [(constants.DDM_ADD, "dos"),
367 (constants.DDM_REMOVE, "linux")],
371 self.assertTrue(opcodes._TestClusterOsList(i))
373 wrong = ["", 0, "xy", ["Hello World"], object(),
376 [[constants.DDM_ADD]],
377 [(constants.DDM_ADD, "")],
378 [(constants.DDM_REMOVE, "")],
379 [(constants.DDM_ADD, None)],
380 [(constants.DDM_REMOVE, None)],
384 self.assertFalse(opcodes._TestClusterOsList(i))
387 class TestOpInstanceSetParams(unittest.TestCase):
388 def _GenericTests(self, fn):
389 self.assertTrue(fn([]))
390 self.assertTrue(fn([(constants.DDM_ADD, {})]))
391 self.assertTrue(fn([(constants.DDM_REMOVE, {})]))
392 for i in [0, 1, 2, 3, 9, 10, 1024]:
393 self.assertTrue(fn([(i, {})]))
395 self.assertFalse(fn(None))
396 self.assertFalse(fn({}))
397 self.assertFalse(fn(""))
398 self.assertFalse(fn(0))
399 self.assertFalse(fn([(-100, {})]))
400 self.assertFalse(fn([(constants.DDM_ADD, 2, 3)]))
401 self.assertFalse(fn([[constants.DDM_ADD]]))
403 def testNicModifications(self):
404 fn = opcodes.OpInstanceSetParams.TestNicModifications
405 self._GenericTests(fn)
407 for param in constants.INIC_PARAMS:
408 self.assertTrue(fn([[constants.DDM_ADD, {param: None}]]))
409 self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
411 def testDiskModifications(self):
412 fn = opcodes.OpInstanceSetParams.TestDiskModifications
413 self._GenericTests(fn)
415 for param in constants.IDISK_PARAMS:
416 self.assertTrue(fn([[constants.DDM_ADD, {param: 0}]]))
417 self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
420 if __name__ == "__main__":
421 testutils.GanetiTestProgram()