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 class TestOpcodes(unittest.TestCase):
40 self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, None)
41 self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, "")
42 self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {})
43 self.assertRaises(ValueError, opcodes.OpCode.LoadOpCode, {"OP_ID": ""})
45 for cls in opcodes.OP_MAPPING.values():
46 self.assert_(cls.OP_ID.startswith("OP_"))
47 self.assert_(len(cls.OP_ID) > 3)
48 self.assertEqual(cls.OP_ID, cls.OP_ID.upper())
49 self.assertEqual(cls.OP_ID, opcodes._NameToId(cls.__name__))
50 self.assertFalse(compat.any(cls.OP_ID.startswith(prefix)
51 for prefix in opcodes._SUMMARY_PREFIX.keys()))
53 self.assertRaises(TypeError, cls, unsupported_parameter="some value")
59 # Variables supported by all opcodes
60 {"dry_run": False, "debug_level": 0, },
63 dict([(name, False) for name in cls._all_slots()])
69 self.assertEqual(op.OP_ID, cls.OP_ID)
70 self._checkSummary(op)
73 state = op.__getstate__()
74 self.assert_(isinstance(state, dict))
76 restored = opcodes.OpCode.LoadOpCode(state)
77 self.assert_(isinstance(restored, cls))
78 self._checkSummary(restored)
80 for name in ["x_y_z", "hello_world"]:
81 assert name not in cls._all_slots()
82 for value in [None, True, False, [], "Hello World"]:
83 self.assertRaises(AttributeError, setattr, op, name, value)
85 def _checkSummary(self, op):
86 summary = op.Summary()
88 if hasattr(op, "OP_DSC_FIELD"):
89 self.assert_(("OP_%s" % summary).startswith("%s(" % op.OP_ID))
90 self.assert_(summary.endswith(")"))
92 self.assertEqual("OP_%s" % summary, op.OP_ID)
94 def testSummary(self):
95 class OpTest(opcodes.OpCode):
98 ("data", ht.NoDefault, ht.TString, None),
101 self.assertEqual(OpTest(data="").Summary(), "TEST()")
102 self.assertEqual(OpTest(data="Hello World").Summary(),
104 self.assertEqual(OpTest(data="node1.example.com").Summary(),
105 "TEST(node1.example.com)")
107 def testTinySummary(self):
108 self.assertFalse(utils.FindDuplicates(opcodes._SUMMARY_PREFIX.values()))
109 self.assertTrue(compat.all(prefix.endswith("_") and supplement.endswith("_")
110 for (prefix, supplement) in
111 opcodes._SUMMARY_PREFIX.items()))
113 self.assertEqual(opcodes.OpClusterPostInit().TinySummary(), "C_POST_INIT")
114 self.assertEqual(opcodes.OpNodeRemove().TinySummary(), "N_REMOVE")
115 self.assertEqual(opcodes.OpInstanceMigrate().TinySummary(), "I_MIGRATE")
116 self.assertEqual(opcodes.OpGroupQuery().TinySummary(), "G_QUERY")
117 self.assertEqual(opcodes.OpTestJqueue().TinySummary(), "TEST_JQUEUE")
119 def testListSummary(self):
120 class OpTest(opcodes.OpCode):
121 OP_DSC_FIELD = "data"
123 ("data", ht.NoDefault, ht.TList, None),
126 self.assertEqual(OpTest(data=["a", "b", "c"]).Summary(),
128 self.assertEqual(OpTest(data=["a", None, "c"]).Summary(),
130 self.assertEqual(OpTest(data=[1, 2, 3, 4]).Summary(), "TEST(1,2,3,4)")
133 self.assertFalse(utils.FindDuplicates(cls.OP_ID
134 for cls in opcodes._GetOpList()))
135 self.assertEqual(len(opcodes._GetOpList()), len(opcodes.OP_MAPPING))
137 def testParams(self):
138 supported_by_all = set(["debug_level", "dry_run", "priority"])
140 self.assertTrue(opcodes.BaseOpCode not in opcodes.OP_MAPPING.values())
141 self.assertTrue(opcodes.OpCode not in opcodes.OP_MAPPING.values())
143 for cls in opcodes.OP_MAPPING.values() + [opcodes.OpCode]:
144 all_slots = cls._all_slots()
146 self.assertEqual(len(set(all_slots) & supported_by_all), 3,
147 msg=("Opcode %s doesn't support all base"
148 " parameters (%r)" % (cls.OP_ID, supported_by_all)))
150 # All opcodes must have OP_PARAMS
151 self.assert_(hasattr(cls, "OP_PARAMS"),
152 msg="%s doesn't have OP_PARAMS" % cls.OP_ID)
154 param_names = [name for (name, _, _, _) in cls.GetAllParams()]
156 self.assertEqual(all_slots, param_names)
158 # Without inheritance
159 self.assertEqual(cls.__slots__,
160 [name for (name, _, _, _) in cls.OP_PARAMS])
162 # This won't work if parameters are converted to a dictionary
163 duplicates = utils.FindDuplicates(param_names)
164 self.assertFalse(duplicates,
165 msg=("Found duplicate parameters %r in %s" %
166 (duplicates, cls.OP_ID)))
168 # Check parameter definitions
169 for attr_name, aval, test, doc in cls.GetAllParams():
170 self.assert_(attr_name)
171 self.assert_(test is None or test is ht.NoType or callable(test),
172 msg=("Invalid type check for %s.%s" %
173 (cls.OP_ID, attr_name)))
174 self.assertTrue(doc is None or isinstance(doc, basestring))
177 self.assertFalse(callable(aval()),
178 msg="Default value returned by function is callable")
180 # If any parameter has documentation, all others need to have it as well
181 has_doc = [doc is not None for (_, _, _, doc) in cls.OP_PARAMS]
182 self.assertTrue(not compat.any(has_doc) or compat.all(has_doc),
183 msg="%s does not document all parameters" % cls)
185 def testValidateNoModification(self):
186 class OpTest(opcodes.OpCode):
188 ("nodef", ht.NoDefault, ht.TMaybeString, None),
189 ("wdef", "default", ht.TMaybeString, None),
190 ("number", 0, ht.TInt, None),
191 ("notype", None, ht.NoType, None),
194 # Missing required parameter "nodef"
196 before = op.__getstate__()
197 self.assertRaises(errors.OpPrereqError, op.Validate, False)
198 self.assertFalse(hasattr(op, "nodef"))
199 self.assertFalse(hasattr(op, "wdef"))
200 self.assertFalse(hasattr(op, "number"))
201 self.assertFalse(hasattr(op, "notype"))
202 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
204 # Required parameter "nodef" is provided
205 op = OpTest(nodef="foo")
206 before = op.__getstate__()
208 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
209 self.assertEqual(op.nodef, "foo")
210 self.assertFalse(hasattr(op, "wdef"))
211 self.assertFalse(hasattr(op, "number"))
212 self.assertFalse(hasattr(op, "notype"))
214 # Missing required parameter "nodef"
215 op = OpTest(wdef="hello", number=999)
216 before = op.__getstate__()
217 self.assertRaises(errors.OpPrereqError, op.Validate, False)
218 self.assertFalse(hasattr(op, "nodef"))
219 self.assertFalse(hasattr(op, "notype"))
220 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
222 # Wrong type for "nodef"
223 op = OpTest(nodef=987)
224 before = op.__getstate__()
225 self.assertRaises(errors.OpPrereqError, op.Validate, False)
226 self.assertEqual(op.nodef, 987)
227 self.assertFalse(hasattr(op, "notype"))
228 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
230 # Testing different types for "notype"
231 op = OpTest(nodef="foo", notype=[1, 2, 3])
232 before = op.__getstate__()
234 self.assertEqual(op.nodef, "foo")
235 self.assertEqual(op.notype, [1, 2, 3])
236 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
238 op = OpTest(nodef="foo", notype="Hello World")
239 before = op.__getstate__()
241 self.assertEqual(op.nodef, "foo")
242 self.assertEqual(op.notype, "Hello World")
243 self.assertEqual(op.__getstate__(), before, msg="Opcode was modified")
245 def testValidateSetDefaults(self):
246 class OpTest(opcodes.OpCode):
248 # Static default value
249 ("value1", "default", ht.TMaybeString, None),
251 # Default value callback
252 ("value2", lambda: "result", ht.TMaybeString, None),
256 before = op.__getstate__()
258 self.assertNotEqual(op.__getstate__(), before,
259 msg="Opcode was not modified")
260 self.assertEqual(op.value1, "default")
261 self.assertEqual(op.value2, "result")
262 self.assert_(op.dry_run is None)
263 self.assert_(op.debug_level is None)
264 self.assertEqual(op.priority, constants.OP_PRIO_DEFAULT)
266 op = OpTest(value1="hello", value2="world", debug_level=123)
267 before = op.__getstate__()
269 self.assertNotEqual(op.__getstate__(), before,
270 msg="Opcode was not modified")
271 self.assertEqual(op.value1, "hello")
272 self.assertEqual(op.value2, "world")
273 self.assertEqual(op.debug_level, 123)
276 if __name__ == "__main__":
277 testutils.GanetiTestProgram()