root / test / py / qa.qa_config_unittest.py @ e80edd3b
History | View | Annotate | Download (11.1 kB)
1 |
#!/usr/bin/python
|
---|---|
2 |
#
|
3 |
|
4 |
# Copyright (C) 2012, 2013 Google Inc.
|
5 |
#
|
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.
|
10 |
#
|
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.
|
15 |
#
|
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
|
19 |
# 02110-1301, USA.
|
20 |
|
21 |
|
22 |
"""Script for testing qa.qa_config"""
|
23 |
|
24 |
import unittest |
25 |
import tempfile |
26 |
import shutil |
27 |
import os |
28 |
import operator |
29 |
|
30 |
from ganeti import utils |
31 |
from ganeti import serializer |
32 |
from ganeti import constants |
33 |
from ganeti import compat |
34 |
|
35 |
from qa import qa_config |
36 |
from qa import qa_error |
37 |
|
38 |
import testutils |
39 |
|
40 |
|
41 |
class TestTestEnabled(unittest.TestCase): |
42 |
def testSimple(self): |
43 |
for name in ["test", ["foobar"], ["a", "b"]]: |
44 |
self.assertTrue(qa_config.TestEnabled(name, _cfg={}))
|
45 |
|
46 |
for default in [False, True]: |
47 |
self.assertFalse(qa_config.TestEnabled("foo", _cfg={ |
48 |
"tests": {
|
49 |
"default": default,
|
50 |
"foo": False, |
51 |
}, |
52 |
})) |
53 |
|
54 |
self.assertTrue(qa_config.TestEnabled("bar", _cfg={ |
55 |
"tests": {
|
56 |
"default": default,
|
57 |
"bar": True, |
58 |
}, |
59 |
})) |
60 |
|
61 |
def testEitherWithDefault(self): |
62 |
names = qa_config.Either("one")
|
63 |
|
64 |
self.assertTrue(qa_config.TestEnabled(names, _cfg={
|
65 |
"tests": {
|
66 |
"default": True, |
67 |
}, |
68 |
})) |
69 |
|
70 |
self.assertFalse(qa_config.TestEnabled(names, _cfg={
|
71 |
"tests": {
|
72 |
"default": False, |
73 |
}, |
74 |
})) |
75 |
|
76 |
def testEither(self): |
77 |
names = [qa_config.Either(["one", "two"]), |
78 |
qa_config.Either("foo"),
|
79 |
"hello",
|
80 |
["bar", "baz"]] |
81 |
|
82 |
self.assertTrue(qa_config.TestEnabled(names, _cfg={
|
83 |
"tests": {
|
84 |
"default": True, |
85 |
}, |
86 |
})) |
87 |
|
88 |
self.assertFalse(qa_config.TestEnabled(names, _cfg={
|
89 |
"tests": {
|
90 |
"default": False, |
91 |
}, |
92 |
})) |
93 |
|
94 |
for name in ["foo", "bar", "baz", "hello"]: |
95 |
self.assertFalse(qa_config.TestEnabled(names, _cfg={
|
96 |
"tests": {
|
97 |
"default": True, |
98 |
name: False,
|
99 |
}, |
100 |
})) |
101 |
|
102 |
self.assertFalse(qa_config.TestEnabled(names, _cfg={
|
103 |
"tests": {
|
104 |
"default": True, |
105 |
"one": False, |
106 |
"two": False, |
107 |
}, |
108 |
})) |
109 |
|
110 |
self.assertTrue(qa_config.TestEnabled(names, _cfg={
|
111 |
"tests": {
|
112 |
"default": True, |
113 |
"one": False, |
114 |
"two": True, |
115 |
}, |
116 |
})) |
117 |
|
118 |
self.assertFalse(qa_config.TestEnabled(names, _cfg={
|
119 |
"tests": {
|
120 |
"default": True, |
121 |
"one": True, |
122 |
"two": True, |
123 |
"foo": False, |
124 |
}, |
125 |
})) |
126 |
|
127 |
def testEitherNestedWithAnd(self): |
128 |
names = qa_config.Either([["one", "two"], "foo"]) |
129 |
|
130 |
self.assertTrue(qa_config.TestEnabled(names, _cfg={
|
131 |
"tests": {
|
132 |
"default": True, |
133 |
}, |
134 |
})) |
135 |
|
136 |
for name in ["one", "two"]: |
137 |
self.assertFalse(qa_config.TestEnabled(names, _cfg={
|
138 |
"tests": {
|
139 |
"default": True, |
140 |
"foo": False, |
141 |
name: False,
|
142 |
}, |
143 |
})) |
144 |
|
145 |
|
146 |
class TestQaConfigLoad(unittest.TestCase): |
147 |
def setUp(self): |
148 |
self.tmpdir = tempfile.mkdtemp()
|
149 |
|
150 |
def tearDown(self): |
151 |
shutil.rmtree(self.tmpdir)
|
152 |
|
153 |
def testLoadNonExistent(self): |
154 |
filename = utils.PathJoin(self.tmpdir, "does.not.exist") |
155 |
self.assertRaises(EnvironmentError, qa_config._QaConfig.Load, filename) |
156 |
|
157 |
@staticmethod
|
158 |
def _WriteConfig(filename, data): |
159 |
utils.WriteFile(filename, data=serializer.DumpJson(data)) |
160 |
|
161 |
def _CheckLoadError(self, filename, data, expected): |
162 |
self._WriteConfig(filename, data)
|
163 |
|
164 |
try:
|
165 |
qa_config._QaConfig.Load(filename) |
166 |
except qa_error.Error, err:
|
167 |
self.assertTrue(str(err).startswith(expected)) |
168 |
else:
|
169 |
self.fail("Exception was not raised") |
170 |
|
171 |
def testFailsValidation(self): |
172 |
filename = utils.PathJoin(self.tmpdir, "qa.json") |
173 |
testconfig = {} |
174 |
|
175 |
check_fn = compat.partial(self._CheckLoadError, filename, testconfig)
|
176 |
|
177 |
# No cluster name
|
178 |
check_fn("Cluster name is required")
|
179 |
|
180 |
testconfig["name"] = "cluster.example.com" |
181 |
|
182 |
# No nodes
|
183 |
check_fn("Need at least one node")
|
184 |
|
185 |
testconfig["nodes"] = [
|
186 |
{ |
187 |
"primary": "xen-test-0", |
188 |
"secondary": "192.0.2.1", |
189 |
}, |
190 |
] |
191 |
|
192 |
# No instances
|
193 |
check_fn("Need at least one instance")
|
194 |
|
195 |
testconfig["instances"] = [
|
196 |
{ |
197 |
"name": "xen-test-inst1", |
198 |
}, |
199 |
] |
200 |
|
201 |
# Missing "disk" and "disk-growth"
|
202 |
check_fn("Config options 'disk' and 'disk-growth' ")
|
203 |
|
204 |
testconfig["disk"] = []
|
205 |
testconfig["disk-growth"] = testconfig["disk"] |
206 |
|
207 |
# Minimal accepted configuration
|
208 |
self._WriteConfig(filename, testconfig)
|
209 |
result = qa_config._QaConfig.Load(filename) |
210 |
self.assertTrue(result.get("nodes")) |
211 |
|
212 |
# Non-existent instance check script
|
213 |
testconfig[qa_config._INSTANCE_CHECK_KEY] = \ |
214 |
utils.PathJoin(self.tmpdir, "instcheck") |
215 |
check_fn("Can't find instance check script")
|
216 |
del testconfig[qa_config._INSTANCE_CHECK_KEY]
|
217 |
|
218 |
# No enabled hypervisor
|
219 |
testconfig[qa_config._ENABLED_HV_KEY] = None
|
220 |
check_fn("No hypervisor is enabled")
|
221 |
|
222 |
# Unknown hypervisor
|
223 |
testconfig[qa_config._ENABLED_HV_KEY] = ["#unknownhv#"]
|
224 |
check_fn("Unknown hypervisor(s) enabled:")
|
225 |
|
226 |
|
227 |
class TestQaConfigWithSampleConfig(unittest.TestCase): |
228 |
"""Tests using C{qa-sample.json}.
|
229 |
|
230 |
This test case serves two purposes:
|
231 |
|
232 |
- Ensure shipped C{qa-sample.json} file is considered a valid QA
|
233 |
configuration
|
234 |
- Test some functions of L{qa_config._QaConfig} without having to
|
235 |
mock a whole configuration file
|
236 |
|
237 |
"""
|
238 |
def setUp(self): |
239 |
filename = "%s/qa/qa-sample.json" % testutils.GetSourceDir()
|
240 |
|
241 |
self.config = qa_config._QaConfig.Load(filename)
|
242 |
|
243 |
def testGetEnabledHypervisors(self): |
244 |
self.assertEqual(self.config.GetEnabledHypervisors(), |
245 |
[constants.DEFAULT_ENABLED_HYPERVISOR]) |
246 |
|
247 |
def testGetDefaultHypervisor(self): |
248 |
self.assertEqual(self.config.GetDefaultHypervisor(), |
249 |
constants.DEFAULT_ENABLED_HYPERVISOR) |
250 |
|
251 |
def testGetInstanceCheckScript(self): |
252 |
self.assertTrue(self.config.GetInstanceCheckScript() is None) |
253 |
|
254 |
def testGetAndGetItem(self): |
255 |
self.assertEqual(self.config["nodes"], self.config.get("nodes")) |
256 |
|
257 |
def testGetMasterNode(self): |
258 |
self.assertEqual(self.config.GetMasterNode(), self.config["nodes"][0]) |
259 |
|
260 |
|
261 |
class TestQaConfig(unittest.TestCase): |
262 |
def setUp(self): |
263 |
filename = \ |
264 |
testutils.TestDataFilename("qa-minimal-nodes-instances-only.json")
|
265 |
|
266 |
self.config = qa_config._QaConfig.Load(filename)
|
267 |
|
268 |
def testExclusiveStorage(self): |
269 |
self.assertRaises(AssertionError, self.config.GetExclusiveStorage) |
270 |
|
271 |
for value in [False, True, 0, 1, 30804, ""]: |
272 |
self.config.SetExclusiveStorage(value)
|
273 |
self.assertEqual(self.config.GetExclusiveStorage(), bool(value)) |
274 |
|
275 |
for template in constants.DISK_TEMPLATES: |
276 |
if value and template not in constants.DTS_EXCL_STORAGE: |
277 |
self.assertFalse(self.config.IsTemplateSupported(template)) |
278 |
else:
|
279 |
self.assertTrue(self.config.IsTemplateSupported(template)) |
280 |
|
281 |
def testInstanceConversion(self): |
282 |
self.assertTrue(isinstance(self.config["instances"][0], |
283 |
qa_config._QaInstance)) |
284 |
|
285 |
def testNodeConversion(self): |
286 |
self.assertTrue(isinstance(self.config["nodes"][0], |
287 |
qa_config._QaNode)) |
288 |
|
289 |
def testAcquireAndReleaseInstance(self): |
290 |
self.assertFalse(compat.any(map(operator.attrgetter("used"), |
291 |
self.config["instances"]))) |
292 |
|
293 |
inst = qa_config.AcquireInstance(_cfg=self.config)
|
294 |
self.assertTrue(inst.used)
|
295 |
self.assertTrue(inst.disk_template is None) |
296 |
|
297 |
inst.Release() |
298 |
|
299 |
self.assertFalse(inst.used)
|
300 |
self.assertTrue(inst.disk_template is None) |
301 |
|
302 |
self.assertFalse(compat.any(map(operator.attrgetter("used"), |
303 |
self.config["instances"]))) |
304 |
|
305 |
def testAcquireInstanceTooMany(self): |
306 |
# Acquire all instances
|
307 |
for _ in range(len(self.config["instances"])): |
308 |
inst = qa_config.AcquireInstance(_cfg=self.config)
|
309 |
self.assertTrue(inst.used)
|
310 |
self.assertTrue(inst.disk_template is None) |
311 |
|
312 |
# The next acquisition must fail
|
313 |
self.assertRaises(qa_error.OutOfInstancesError,
|
314 |
qa_config.AcquireInstance, _cfg=self.config)
|
315 |
|
316 |
def testAcquireNodeNoneAdded(self): |
317 |
self.assertFalse(compat.any(map(operator.attrgetter("added"), |
318 |
self.config["nodes"]))) |
319 |
|
320 |
# First call must return master node
|
321 |
node = qa_config.AcquireNode(_cfg=self.config)
|
322 |
self.assertEqual(node, self.config.GetMasterNode()) |
323 |
|
324 |
# Next call with exclusion list fails
|
325 |
self.assertRaises(qa_error.OutOfNodesError, qa_config.AcquireNode,
|
326 |
exclude=[node], _cfg=self.config)
|
327 |
|
328 |
def testAcquireNodeTooMany(self): |
329 |
# Mark all nodes as marked (master excluded)
|
330 |
for node in self.config["nodes"]: |
331 |
if node != self.config.GetMasterNode(): |
332 |
node.MarkAdded() |
333 |
|
334 |
nodecount = len(self.config["nodes"]) |
335 |
|
336 |
self.assertTrue(nodecount > 1) |
337 |
|
338 |
acquired = [] |
339 |
|
340 |
for _ in range(nodecount): |
341 |
node = qa_config.AcquireNode(exclude=acquired, _cfg=self.config)
|
342 |
if node == self.config.GetMasterNode(): |
343 |
self.assertFalse(node.added)
|
344 |
else:
|
345 |
self.assertTrue(node.added)
|
346 |
self.assertEqual(node.use_count, 1) |
347 |
acquired.append(node) |
348 |
|
349 |
self.assertRaises(qa_error.OutOfNodesError, qa_config.AcquireNode,
|
350 |
exclude=acquired, _cfg=self.config)
|
351 |
|
352 |
|
353 |
class TestRepresentation(unittest.TestCase): |
354 |
def _Check(self, target, part): |
355 |
self.assertTrue(part in repr(target).split()) |
356 |
|
357 |
def testQaInstance(self): |
358 |
inst = qa_config._QaInstance("inst1.example.com", [])
|
359 |
self._Check(inst, "name=inst1.example.com") |
360 |
self._Check(inst, "nicmac=[]") |
361 |
|
362 |
# Default values
|
363 |
self._Check(inst, "disk_template=None") |
364 |
self._Check(inst, "used=None") |
365 |
|
366 |
# Use instance
|
367 |
inst.Use() |
368 |
self._Check(inst, "used=True") |
369 |
|
370 |
# Disk template
|
371 |
inst.SetDiskTemplate(constants.DT_DRBD8) |
372 |
self._Check(inst, "disk_template=%s" % constants.DT_DRBD8) |
373 |
|
374 |
# Release instance
|
375 |
inst.Release() |
376 |
self._Check(inst, "used=False") |
377 |
self._Check(inst, "disk_template=None") |
378 |
|
379 |
def testQaNode(self): |
380 |
node = qa_config._QaNode("primary.example.com", "192.0.2.1") |
381 |
self._Check(node, "primary=primary.example.com") |
382 |
self._Check(node, "secondary=192.0.2.1") |
383 |
self._Check(node, "added=False") |
384 |
self._Check(node, "use_count=0") |
385 |
|
386 |
# Mark as added
|
387 |
node.MarkAdded() |
388 |
self._Check(node, "added=True") |
389 |
|
390 |
# Use node
|
391 |
for i in range(1, 5): |
392 |
node.Use() |
393 |
self._Check(node, "use_count=%s" % i) |
394 |
|
395 |
# Release node
|
396 |
for i in reversed(range(1, 5)): |
397 |
node.Release() |
398 |
self._Check(node, "use_count=%s" % (i - 1)) |
399 |
|
400 |
self._Check(node, "use_count=0") |
401 |
|
402 |
# Mark as added
|
403 |
node.MarkRemoved() |
404 |
self._Check(node, "added=False") |
405 |
|
406 |
|
407 |
if __name__ == "__main__": |
408 |
testutils.GanetiTestProgram() |