Statistics
| Branch: | Tag: | Revision:

root / test / py / qa.qa_config_unittest.py @ 02cff8aa

History | View | Annotate | Download (13.8 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
  def testCallable(self):
146
    self.assertTrue(qa_config.TestEnabled([lambda: True], _cfg={}))
147

    
148
    for value in [None, False, "", 0]:
149
      self.assertFalse(qa_config.TestEnabled(lambda: value, _cfg={}))
150

    
151

    
152
class TestQaConfigLoad(unittest.TestCase):
153
  def setUp(self):
154
    self.tmpdir = tempfile.mkdtemp()
155

    
156
  def tearDown(self):
157
    shutil.rmtree(self.tmpdir)
158

    
159
  def testLoadNonExistent(self):
160
    filename = utils.PathJoin(self.tmpdir, "does.not.exist")
161
    self.assertRaises(EnvironmentError, qa_config._QaConfig.Load, filename)
162

    
163
  @staticmethod
164
  def _WriteConfig(filename, data):
165
    utils.WriteFile(filename, data=serializer.DumpJson(data))
166

    
167
  def _CheckLoadError(self, filename, data, expected):
168
    self._WriteConfig(filename, data)
169

    
170
    try:
171
      qa_config._QaConfig.Load(filename)
172
    except qa_error.Error, err:
173
      self.assertTrue(str(err).startswith(expected))
174
    else:
175
      self.fail("Exception was not raised")
176

    
177
  def testFailsValidation(self):
178
    filename = utils.PathJoin(self.tmpdir, "qa.json")
179
    testconfig = {}
180

    
181
    check_fn = compat.partial(self._CheckLoadError, filename, testconfig)
182

    
183
    # No cluster name
184
    check_fn("Cluster name is required")
185

    
186
    testconfig["name"] = "cluster.example.com"
187

    
188
    # No nodes
189
    check_fn("Need at least one node")
190

    
191
    testconfig["nodes"] = [
192
      {
193
        "primary": "xen-test-0",
194
        "secondary": "192.0.2.1",
195
        },
196
      ]
197

    
198
    # No instances
199
    check_fn("Need at least one instance")
200

    
201
    testconfig["instances"] = [
202
      {
203
        "name": "xen-test-inst1",
204
        },
205
      ]
206

    
207
    # Missing "disk" and "disk-growth"
208
    check_fn("Config options 'disk' and 'disk-growth' ")
209

    
210
    testconfig["disk"] = []
211
    testconfig["disk-growth"] = testconfig["disk"]
212

    
213
    # Minimal accepted configuration
214
    self._WriteConfig(filename, testconfig)
215
    result = qa_config._QaConfig.Load(filename)
216
    self.assertTrue(result.get("nodes"))
217

    
218
    # Non-existent instance check script
219
    testconfig[qa_config._INSTANCE_CHECK_KEY] = \
220
      utils.PathJoin(self.tmpdir, "instcheck")
221
    check_fn("Can't find instance check script")
222
    del testconfig[qa_config._INSTANCE_CHECK_KEY]
223

    
224
    # No enabled hypervisor
225
    testconfig[qa_config._ENABLED_HV_KEY] = None
226
    check_fn("No hypervisor is enabled")
227

    
228
    # Unknown hypervisor
229
    testconfig[qa_config._ENABLED_HV_KEY] = ["#unknownhv#"]
230
    check_fn("Unknown hypervisor(s) enabled:")
231
    del testconfig[qa_config._ENABLED_HV_KEY]
232

    
233
    # Invalid path for virtual cluster base directory
234
    testconfig[qa_config._VCLUSTER_MASTER_KEY] = "value"
235
    testconfig[qa_config._VCLUSTER_BASEDIR_KEY] = "./not//normalized/"
236
    check_fn("Path given in option 'vcluster-basedir' must be")
237

    
238
    # Inconsistent virtual cluster settings
239
    testconfig.pop(qa_config._VCLUSTER_MASTER_KEY)
240
    testconfig[qa_config._VCLUSTER_BASEDIR_KEY] = "/tmp"
241
    check_fn("All or none of the")
242

    
243
    testconfig[qa_config._VCLUSTER_MASTER_KEY] = "master.example.com"
244
    testconfig.pop(qa_config._VCLUSTER_BASEDIR_KEY)
245
    check_fn("All or none of the")
246

    
247
    # Accepted virtual cluster settings
248
    testconfig[qa_config._VCLUSTER_MASTER_KEY] = "master.example.com"
249
    testconfig[qa_config._VCLUSTER_BASEDIR_KEY] = "/tmp"
250

    
251
    self._WriteConfig(filename, testconfig)
252
    result = qa_config._QaConfig.Load(filename)
253
    self.assertEqual(result.GetVclusterSettings(),
254
                     ("master.example.com", "/tmp"))
255

    
256

    
257
class TestQaConfigWithSampleConfig(unittest.TestCase):
258
  """Tests using C{qa-sample.json}.
259

260
  This test case serves two purposes:
261

262
    - Ensure shipped C{qa-sample.json} file is considered a valid QA
263
      configuration
264
    - Test some functions of L{qa_config._QaConfig} without having to
265
      mock a whole configuration file
266

267
  """
268
  def setUp(self):
269
    filename = "%s/qa/qa-sample.json" % testutils.GetSourceDir()
270

    
271
    self.config = qa_config._QaConfig.Load(filename)
272

    
273
  def testGetEnabledHypervisors(self):
274
    self.assertEqual(self.config.GetEnabledHypervisors(),
275
                     [constants.DEFAULT_ENABLED_HYPERVISOR])
276

    
277
  def testGetDefaultHypervisor(self):
278
    self.assertEqual(self.config.GetDefaultHypervisor(),
279
                     constants.DEFAULT_ENABLED_HYPERVISOR)
280

    
281
  def testGetInstanceCheckScript(self):
282
    self.assertTrue(self.config.GetInstanceCheckScript() is None)
283

    
284
  def testGetAndGetItem(self):
285
    self.assertEqual(self.config["nodes"], self.config.get("nodes"))
286

    
287
  def testGetMasterNode(self):
288
    self.assertEqual(self.config.GetMasterNode(), self.config["nodes"][0])
289

    
290
  def testGetVclusterSettings(self):
291
    # Shipped default settings should be to not use a virtual cluster
292
    self.assertEqual(self.config.GetVclusterSettings(), (None, None))
293

    
294
    self.assertFalse(qa_config.UseVirtualCluster(_cfg=self.config))
295

    
296

    
297
class TestQaConfig(unittest.TestCase):
298
  def setUp(self):
299
    filename = \
300
      testutils.TestDataFilename("qa-minimal-nodes-instances-only.json")
301

    
302
    self.config = qa_config._QaConfig.Load(filename)
303

    
304
  def testExclusiveStorage(self):
305
    self.assertRaises(AssertionError, self.config.GetExclusiveStorage)
306

    
307
    for value in [False, True, 0, 1, 30804, ""]:
308
      self.config.SetExclusiveStorage(value)
309
      self.assertEqual(self.config.GetExclusiveStorage(), bool(value))
310

    
311
  def testIsTemplateSupported(self):
312
    enabled_dts = self.config.GetEnabledDiskTemplates()
313
    for e_s in [False, True]:
314
      self.config.SetExclusiveStorage(e_s)
315
      for template in constants.DISK_TEMPLATES:
316
        if (template not in enabled_dts or
317
            e_s and template not in constants.DTS_EXCL_STORAGE):
318
          self.assertFalse(self.config.IsTemplateSupported(template))
319
        else:
320
          self.assertTrue(self.config.IsTemplateSupported(template))
321

    
322
  def testInstanceConversion(self):
323
    self.assertTrue(isinstance(self.config["instances"][0],
324
                               qa_config._QaInstance))
325

    
326
  def testNodeConversion(self):
327
    self.assertTrue(isinstance(self.config["nodes"][0],
328
                               qa_config._QaNode))
329

    
330
  def testAcquireAndReleaseInstance(self):
331
    self.assertFalse(compat.any(map(operator.attrgetter("used"),
332
                                    self.config["instances"])))
333

    
334
    inst = qa_config.AcquireInstance(_cfg=self.config)
335
    self.assertTrue(inst.used)
336
    self.assertTrue(inst.disk_template is None)
337

    
338
    inst.Release()
339

    
340
    self.assertFalse(inst.used)
341
    self.assertTrue(inst.disk_template is None)
342

    
343
    self.assertFalse(compat.any(map(operator.attrgetter("used"),
344
                                    self.config["instances"])))
345

    
346
  def testAcquireInstanceTooMany(self):
347
    # Acquire all instances
348
    for _ in range(len(self.config["instances"])):
349
      inst = qa_config.AcquireInstance(_cfg=self.config)
350
      self.assertTrue(inst.used)
351
      self.assertTrue(inst.disk_template is None)
352

    
353
    # The next acquisition must fail
354
    self.assertRaises(qa_error.OutOfInstancesError,
355
                      qa_config.AcquireInstance, _cfg=self.config)
356

    
357
  def testAcquireNodeNoneAdded(self):
358
    self.assertFalse(compat.any(map(operator.attrgetter("added"),
359
                                    self.config["nodes"])))
360

    
361
    # First call must return master node
362
    node = qa_config.AcquireNode(_cfg=self.config)
363
    self.assertEqual(node, self.config.GetMasterNode())
364

    
365
    # Next call with exclusion list fails
366
    self.assertRaises(qa_error.OutOfNodesError, qa_config.AcquireNode,
367
                      exclude=[node], _cfg=self.config)
368

    
369
  def testAcquireNodeTooMany(self):
370
    # Mark all nodes as marked (master excluded)
371
    for node in self.config["nodes"]:
372
      if node != self.config.GetMasterNode():
373
        node.MarkAdded()
374

    
375
    nodecount = len(self.config["nodes"])
376

    
377
    self.assertTrue(nodecount > 1)
378

    
379
    acquired = []
380

    
381
    for _ in range(nodecount):
382
      node = qa_config.AcquireNode(exclude=acquired, _cfg=self.config)
383
      if node == self.config.GetMasterNode():
384
        self.assertFalse(node.added)
385
      else:
386
        self.assertTrue(node.added)
387
      self.assertEqual(node.use_count, 1)
388
      acquired.append(node)
389

    
390
    self.assertRaises(qa_error.OutOfNodesError, qa_config.AcquireNode,
391
                      exclude=acquired, _cfg=self.config)
392

    
393
  def testAcquireNodeOrder(self):
394
    # Mark all nodes as marked (master excluded)
395
    for node in self.config["nodes"]:
396
      if node != self.config.GetMasterNode():
397
        node.MarkAdded()
398

    
399
    nodecount = len(self.config["nodes"])
400

    
401
    for iterations in [0, 1, 3, 100, 127, 7964]:
402
      acquired = []
403

    
404
      for i in range(iterations):
405
        node = qa_config.AcquireNode(_cfg=self.config)
406
        self.assertTrue(node.use_count > 0)
407
        self.assertEqual(node.use_count, (i / nodecount + 1))
408
        acquired.append((node.use_count, node.primary, node))
409

    
410
      # Check if returned nodes were in correct order
411
      key_fn = lambda (a, b, c): (a, utils.NiceSortKey(b), c)
412
      self.assertEqual(acquired, sorted(acquired, key=key_fn))
413

    
414
      # Release previously acquired nodes
415
      qa_config.ReleaseManyNodes(map(operator.itemgetter(2), acquired))
416

    
417
      # Check if nodes were actually released
418
      for node in self.config["nodes"]:
419
        self.assertEqual(node.use_count, 0)
420
        self.assertTrue(node.added or node == self.config.GetMasterNode())
421

    
422

    
423
class TestRepresentation(unittest.TestCase):
424
  def _Check(self, target, part):
425
    self.assertTrue(part in repr(target).split())
426

    
427
  def testQaInstance(self):
428
    inst = qa_config._QaInstance("inst1.example.com", [])
429
    self._Check(inst, "name=inst1.example.com")
430
    self._Check(inst, "nicmac=[]")
431

    
432
    # Default values
433
    self._Check(inst, "disk_template=None")
434
    self._Check(inst, "used=None")
435

    
436
    # Use instance
437
    inst.Use()
438
    self._Check(inst, "used=True")
439

    
440
    # Disk template
441
    inst.SetDiskTemplate(constants.DT_DRBD8)
442
    self._Check(inst, "disk_template=%s" % constants.DT_DRBD8)
443

    
444
    # Release instance
445
    inst.Release()
446
    self._Check(inst, "used=False")
447
    self._Check(inst, "disk_template=None")
448

    
449
  def testQaNode(self):
450
    node = qa_config._QaNode("primary.example.com", "192.0.2.1")
451
    self._Check(node, "primary=primary.example.com")
452
    self._Check(node, "secondary=192.0.2.1")
453
    self._Check(node, "added=False")
454
    self._Check(node, "use_count=0")
455

    
456
    # Mark as added
457
    node.MarkAdded()
458
    self._Check(node, "added=True")
459

    
460
    # Use node
461
    for i in range(1, 5):
462
      node.Use()
463
      self._Check(node, "use_count=%s" % i)
464

    
465
    # Release node
466
    for i in reversed(range(1, 5)):
467
      node.Release()
468
      self._Check(node, "use_count=%s" % (i - 1))
469

    
470
    self._Check(node, "use_count=0")
471

    
472
    # Mark as added
473
    node.MarkRemoved()
474
    self._Check(node, "added=False")
475

    
476

    
477
if __name__ == "__main__":
478
  testutils.GanetiTestProgram()