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, False) for name in cls._all_slots()])
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._all_slots()
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._all_slots()
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 class TestOpcodeDepends(unittest.TestCase):
295 check_relative = opcodes._BuildJobDepCheck(True)
296 check_norelative = opcodes.TNoRelativeJobDependencies
298 for fn in [check_relative, check_norelative]:
299 self.assertTrue(fn(None))
300 self.assertTrue(fn([]))
301 self.assertTrue(fn([(1, [])]))
302 self.assertTrue(fn([(719833, [])]))
303 self.assertTrue(fn([("24879", [])]))
304 self.assertTrue(fn([(2028, [constants.JOB_STATUS_ERROR])]))
306 (2028, [constants.JOB_STATUS_ERROR]),
308 (5063, [constants.JOB_STATUS_SUCCESS, constants.JOB_STATUS_ERROR]),
311 self.assertFalse(fn(1))
312 self.assertFalse(fn([(9, )]))
313 self.assertFalse(fn([(15194, constants.JOB_STATUS_ERROR)]))
317 [(-27740, [constants.JOB_STATUS_CANCELED, constants.JOB_STATUS_ERROR]),
318 (-1, [constants.JOB_STATUS_ERROR]),
321 self.assertTrue(check_relative(i))
322 self.assertFalse(check_norelative(i))
325 class TestResultChecks(unittest.TestCase):
326 def testJobIdList(self):
327 for i in [[], [(False, "error")], [(False, "")],
328 [(True, 123), (True, "999")]]:
329 self.assertTrue(opcodes.TJobIdList(i))
331 for i in ["", [("x", 1)], [[], []], [[False, "", None], [True, 123]]]:
332 self.assertFalse(opcodes.TJobIdList(i))
334 def testJobIdListOnly(self):
335 self.assertTrue(opcodes.TJobIdListOnly({
336 constants.JOB_IDS_KEY: [],
338 self.assertTrue(opcodes.TJobIdListOnly({
339 constants.JOB_IDS_KEY: [(True, "9282")],
342 self.assertFalse(opcodes.TJobIdListOnly({
345 self.assertFalse(opcodes.TJobIdListOnly({
346 constants.JOB_IDS_KEY: [],
349 self.assertFalse(opcodes.TJobIdListOnly({
350 constants.JOB_IDS_KEY: [("foo", "bar")],
352 self.assertFalse(opcodes.TJobIdListOnly({
353 constants.JOB_IDS_KEY: [("one", "two", "three")],
357 class TestClusterOsList(unittest.TestCase):
362 [(constants.DDM_ADD, "dos"),
363 (constants.DDM_REMOVE, "linux")],
367 self.assertTrue(opcodes._TestClusterOsList(i))
369 wrong = ["", 0, "xy", ["Hello World"], object(),
372 [[constants.DDM_ADD]],
373 [(constants.DDM_ADD, "")],
374 [(constants.DDM_REMOVE, "")],
375 [(constants.DDM_ADD, None)],
376 [(constants.DDM_REMOVE, None)],
380 self.assertFalse(opcodes._TestClusterOsList(i))
383 class TestOpInstanceSetParams(unittest.TestCase):
384 def _GenericTests(self, fn):
385 self.assertTrue(fn([]))
386 self.assertTrue(fn([(constants.DDM_ADD, {})]))
387 self.assertTrue(fn([(constants.DDM_REMOVE, {})]))
388 for i in [0, 1, 2, 3, 9, 10, 1024]:
389 self.assertTrue(fn([(i, {})]))
391 self.assertFalse(fn(None))
392 self.assertFalse(fn({}))
393 self.assertFalse(fn(""))
394 self.assertFalse(fn(0))
395 self.assertFalse(fn([(-100, {})]))
396 self.assertFalse(fn([(constants.DDM_ADD, 2, 3)]))
397 self.assertFalse(fn([[constants.DDM_ADD]]))
399 def testNicModifications(self):
400 fn = opcodes.OpInstanceSetParams.TestNicModifications
401 self._GenericTests(fn)
403 for param in constants.INIC_PARAMS:
404 self.assertTrue(fn([[constants.DDM_ADD, {param: None}]]))
405 self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
407 def testDiskModifications(self):
408 fn = opcodes.OpInstanceSetParams.TestDiskModifications
409 self._GenericTests(fn)
411 for param in constants.IDISK_PARAMS:
412 self.assertTrue(fn([[constants.DDM_ADD, {param: 0}]]))
413 self.assertTrue(fn([[constants.DDM_ADD, {param: param}]]))
416 if __name__ == "__main__":
417 testutils.GanetiTestProgram()