4 # Copyright (C) 2012, 2013 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 qa.qa_config"""
30 from ganeti import utils
31 from ganeti import serializer
32 from ganeti import constants
33 from ganeti import compat
35 from qa import qa_config
36 from qa import qa_error
41 class TestTestEnabled(unittest.TestCase):
43 for name in ["test", ["foobar"], ["a", "b"]]:
44 self.assertTrue(qa_config.TestEnabled(name, _cfg={}))
46 for default in [False, True]:
47 self.assertFalse(qa_config.TestEnabled("foo", _cfg={
54 self.assertTrue(qa_config.TestEnabled("bar", _cfg={
61 def testEitherWithDefault(self):
62 names = qa_config.Either("one")
64 self.assertTrue(qa_config.TestEnabled(names, _cfg={
70 self.assertFalse(qa_config.TestEnabled(names, _cfg={
77 names = [qa_config.Either(["one", "two"]),
78 qa_config.Either("foo"),
82 self.assertTrue(qa_config.TestEnabled(names, _cfg={
88 self.assertFalse(qa_config.TestEnabled(names, _cfg={
94 for name in ["foo", "bar", "baz", "hello"]:
95 self.assertFalse(qa_config.TestEnabled(names, _cfg={
102 self.assertFalse(qa_config.TestEnabled(names, _cfg={
110 self.assertTrue(qa_config.TestEnabled(names, _cfg={
118 self.assertFalse(qa_config.TestEnabled(names, _cfg={
127 def testEitherNestedWithAnd(self):
128 names = qa_config.Either([["one", "two"], "foo"])
130 self.assertTrue(qa_config.TestEnabled(names, _cfg={
136 for name in ["one", "two"]:
137 self.assertFalse(qa_config.TestEnabled(names, _cfg={
145 def testCallable(self):
146 self.assertTrue(qa_config.TestEnabled([lambda: True], _cfg={}))
148 for value in [None, False, "", 0]:
149 self.assertFalse(qa_config.TestEnabled(lambda: value, _cfg={}))
152 class TestQaConfigLoad(unittest.TestCase):
154 self.tmpdir = tempfile.mkdtemp()
157 shutil.rmtree(self.tmpdir)
159 def testLoadNonExistent(self):
160 filename = utils.PathJoin(self.tmpdir, "does.not.exist")
161 self.assertRaises(EnvironmentError, qa_config._QaConfig.Load, filename)
164 def _WriteConfig(filename, data):
165 utils.WriteFile(filename, data=serializer.DumpJson(data))
167 def _CheckLoadError(self, filename, data, expected):
168 self._WriteConfig(filename, data)
171 qa_config._QaConfig.Load(filename)
172 except qa_error.Error, err:
173 self.assertTrue(str(err).startswith(expected))
175 self.fail("Exception was not raised")
177 def testFailsValidation(self):
178 filename = utils.PathJoin(self.tmpdir, "qa.json")
181 check_fn = compat.partial(self._CheckLoadError, filename, testconfig)
184 check_fn("Cluster name is required")
186 testconfig["name"] = "cluster.example.com"
189 check_fn("Need at least one node")
191 testconfig["nodes"] = [
193 "primary": "xen-test-0",
194 "secondary": "192.0.2.1",
199 check_fn("Need at least one instance")
201 testconfig["instances"] = [
203 "name": "xen-test-inst1",
207 # Missing "disk" and "disk-growth"
208 check_fn("Config option 'disks'")
210 testconfig["disks"] = []
212 # Minimal accepted configuration
213 self._WriteConfig(filename, testconfig)
214 result = qa_config._QaConfig.Load(filename)
215 self.assertTrue(result.get("nodes"))
217 # Non-existent instance check script
218 testconfig[qa_config._INSTANCE_CHECK_KEY] = \
219 utils.PathJoin(self.tmpdir, "instcheck")
220 check_fn("Can't find instance check script")
221 del testconfig[qa_config._INSTANCE_CHECK_KEY]
223 # No enabled hypervisor
224 testconfig[qa_config._ENABLED_HV_KEY] = None
225 check_fn("No hypervisor is enabled")
228 testconfig[qa_config._ENABLED_HV_KEY] = ["#unknownhv#"]
229 check_fn("Unknown hypervisor(s) enabled:")
230 del testconfig[qa_config._ENABLED_HV_KEY]
232 # Invalid path for virtual cluster base directory
233 testconfig[qa_config._VCLUSTER_MASTER_KEY] = "value"
234 testconfig[qa_config._VCLUSTER_BASEDIR_KEY] = "./not//normalized/"
235 check_fn("Path given in option 'vcluster-basedir' must be")
237 # Inconsistent virtual cluster settings
238 testconfig.pop(qa_config._VCLUSTER_MASTER_KEY)
239 testconfig[qa_config._VCLUSTER_BASEDIR_KEY] = "/tmp"
240 check_fn("All or none of the")
242 testconfig[qa_config._VCLUSTER_MASTER_KEY] = "master.example.com"
243 testconfig.pop(qa_config._VCLUSTER_BASEDIR_KEY)
244 check_fn("All or none of the")
246 # Accepted virtual cluster settings
247 testconfig[qa_config._VCLUSTER_MASTER_KEY] = "master.example.com"
248 testconfig[qa_config._VCLUSTER_BASEDIR_KEY] = "/tmp"
250 self._WriteConfig(filename, testconfig)
251 result = qa_config._QaConfig.Load(filename)
252 self.assertEqual(result.GetVclusterSettings(),
253 ("master.example.com", "/tmp"))
256 class TestQaConfigWithSampleConfig(unittest.TestCase):
257 """Tests using C{qa-sample.json}.
259 This test case serves two purposes:
261 - Ensure shipped C{qa-sample.json} file is considered a valid QA
263 - Test some functions of L{qa_config._QaConfig} without having to
264 mock a whole configuration file
268 filename = "%s/qa/qa-sample.json" % testutils.GetSourceDir()
270 self.config = qa_config._QaConfig.Load(filename)
272 def testGetEnabledHypervisors(self):
273 self.assertEqual(self.config.GetEnabledHypervisors(),
274 [constants.DEFAULT_ENABLED_HYPERVISOR])
276 def testGetDefaultHypervisor(self):
277 self.assertEqual(self.config.GetDefaultHypervisor(),
278 constants.DEFAULT_ENABLED_HYPERVISOR)
280 def testGetInstanceCheckScript(self):
281 self.assertTrue(self.config.GetInstanceCheckScript() is None)
283 def testGetAndGetItem(self):
284 self.assertEqual(self.config["nodes"], self.config.get("nodes"))
286 def testGetMasterNode(self):
287 self.assertEqual(self.config.GetMasterNode(), self.config["nodes"][0])
289 def testGetVclusterSettings(self):
290 # Shipped default settings should be to not use a virtual cluster
291 self.assertEqual(self.config.GetVclusterSettings(), (None, None))
293 self.assertFalse(qa_config.UseVirtualCluster(_cfg=self.config))
296 class TestQaConfig(unittest.TestCase):
299 testutils.TestDataFilename("qa-minimal-nodes-instances-only.json")
301 self.config = qa_config._QaConfig.Load(filename)
303 def testExclusiveStorage(self):
304 self.assertRaises(AssertionError, self.config.GetExclusiveStorage)
306 for value in [False, True, 0, 1, 30804, ""]:
307 self.config.SetExclusiveStorage(value)
308 self.assertEqual(self.config.GetExclusiveStorage(), bool(value))
310 def testIsTemplateSupported(self):
311 enabled_dts = self.config.GetEnabledDiskTemplates()
312 for e_s in [False, True]:
313 self.config.SetExclusiveStorage(e_s)
314 for template in constants.DISK_TEMPLATES:
315 if (template not in enabled_dts or
316 e_s and template not in constants.DTS_EXCL_STORAGE):
317 self.assertFalse(self.config.IsTemplateSupported(template))
319 self.assertTrue(self.config.IsTemplateSupported(template))
321 def testInstanceConversion(self):
322 self.assertTrue(isinstance(self.config["instances"][0],
323 qa_config._QaInstance))
325 def testNodeConversion(self):
326 self.assertTrue(isinstance(self.config["nodes"][0],
329 def testAcquireAndReleaseInstance(self):
330 self.assertFalse(compat.any(map(operator.attrgetter("used"),
331 self.config["instances"])))
333 inst = qa_config.AcquireInstance(_cfg=self.config)
334 self.assertTrue(inst.used)
335 self.assertTrue(inst.disk_template is None)
339 self.assertFalse(inst.used)
340 self.assertTrue(inst.disk_template is None)
342 self.assertFalse(compat.any(map(operator.attrgetter("used"),
343 self.config["instances"])))
345 def testAcquireInstanceTooMany(self):
346 # Acquire all instances
347 for _ in range(len(self.config["instances"])):
348 inst = qa_config.AcquireInstance(_cfg=self.config)
349 self.assertTrue(inst.used)
350 self.assertTrue(inst.disk_template is None)
352 # The next acquisition must fail
353 self.assertRaises(qa_error.OutOfInstancesError,
354 qa_config.AcquireInstance, _cfg=self.config)
356 def testAcquireNodeNoneAdded(self):
357 self.assertFalse(compat.any(map(operator.attrgetter("added"),
358 self.config["nodes"])))
360 # First call must return master node
361 node = qa_config.AcquireNode(_cfg=self.config)
362 self.assertEqual(node, self.config.GetMasterNode())
364 # Next call with exclusion list fails
365 self.assertRaises(qa_error.OutOfNodesError, qa_config.AcquireNode,
366 exclude=[node], _cfg=self.config)
368 def testAcquireNodeTooMany(self):
369 # Mark all nodes as marked (master excluded)
370 for node in self.config["nodes"]:
371 if node != self.config.GetMasterNode():
374 nodecount = len(self.config["nodes"])
376 self.assertTrue(nodecount > 1)
380 for _ in range(nodecount):
381 node = qa_config.AcquireNode(exclude=acquired, _cfg=self.config)
382 if node == self.config.GetMasterNode():
383 self.assertFalse(node.added)
385 self.assertTrue(node.added)
386 self.assertEqual(node.use_count, 1)
387 acquired.append(node)
389 self.assertRaises(qa_error.OutOfNodesError, qa_config.AcquireNode,
390 exclude=acquired, _cfg=self.config)
392 def testAcquireNodeOrder(self):
393 # Mark all nodes as marked (master excluded)
394 for node in self.config["nodes"]:
395 if node != self.config.GetMasterNode():
398 nodecount = len(self.config["nodes"])
400 for iterations in [0, 1, 3, 100, 127, 7964]:
403 for i in range(iterations):
404 node = qa_config.AcquireNode(_cfg=self.config)
405 self.assertTrue(node.use_count > 0)
406 self.assertEqual(node.use_count, (i / nodecount + 1))
407 acquired.append((node.use_count, node.primary, node))
409 # Check if returned nodes were in correct order
410 key_fn = lambda (a, b, c): (a, utils.NiceSortKey(b), c)
411 self.assertEqual(acquired, sorted(acquired, key=key_fn))
413 # Release previously acquired nodes
414 qa_config.ReleaseManyNodes(map(operator.itemgetter(2), acquired))
416 # Check if nodes were actually released
417 for node in self.config["nodes"]:
418 self.assertEqual(node.use_count, 0)
419 self.assertTrue(node.added or node == self.config.GetMasterNode())
422 class TestRepresentation(unittest.TestCase):
423 def _Check(self, target, part):
424 self.assertTrue(part in repr(target).split())
426 def testQaInstance(self):
427 inst = qa_config._QaInstance("inst1.example.com", [])
428 self._Check(inst, "name=inst1.example.com")
429 self._Check(inst, "nicmac=[]")
432 self._Check(inst, "disk_template=None")
433 self._Check(inst, "used=None")
437 self._Check(inst, "used=True")
440 inst.SetDiskTemplate(constants.DT_DRBD8)
441 self._Check(inst, "disk_template=%s" % constants.DT_DRBD8)
445 self._Check(inst, "used=False")
446 self._Check(inst, "disk_template=None")
448 def testQaNode(self):
449 node = qa_config._QaNode("primary.example.com", "192.0.2.1")
450 self._Check(node, "primary=primary.example.com")
451 self._Check(node, "secondary=192.0.2.1")
452 self._Check(node, "added=False")
453 self._Check(node, "use_count=0")
457 self._Check(node, "added=True")
460 for i in range(1, 5):
462 self._Check(node, "use_count=%s" % i)
465 for i in reversed(range(1, 5)):
467 self._Check(node, "use_count=%s" % (i - 1))
469 self._Check(node, "use_count=0")
473 self._Check(node, "added=False")
476 if __name__ == "__main__":
477 testutils.GanetiTestProgram()