Statistics
| Branch: | Tag: | Revision:

root / test / py / qa.qa_config_unittest.py @ 41be279f

History | View | Annotate | Download (12.2 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
  def testAcquireNodeOrder(self):
353
    # Mark all nodes as marked (master excluded)
354
    for node in self.config["nodes"]:
355
      if node != self.config.GetMasterNode():
356
        node.MarkAdded()
357

    
358
    nodecount = len(self.config["nodes"])
359

    
360
    for iterations in [0, 1, 3, 100, 127, 7964]:
361
      acquired = []
362

    
363
      for i in range(iterations):
364
        node = qa_config.AcquireNode(_cfg=self.config)
365
        self.assertTrue(node.use_count > 0)
366
        self.assertEqual(node.use_count, (i / nodecount + 1))
367
        acquired.append((node.use_count, node.primary, node))
368

    
369
      # Check if returned nodes were in correct order
370
      key_fn = lambda (a, b, c): (a, utils.NiceSortKey(b), c)
371
      self.assertEqual(acquired, sorted(acquired, key=key_fn))
372

    
373
      # Release previously acquired nodes
374
      qa_config.ReleaseManyNodes(map(operator.itemgetter(2), acquired))
375

    
376
      # Check if nodes were actually released
377
      for node in self.config["nodes"]:
378
        self.assertEqual(node.use_count, 0)
379
        self.assertTrue(node.added or node == self.config.GetMasterNode())
380

    
381

    
382
class TestRepresentation(unittest.TestCase):
383
  def _Check(self, target, part):
384
    self.assertTrue(part in repr(target).split())
385

    
386
  def testQaInstance(self):
387
    inst = qa_config._QaInstance("inst1.example.com", [])
388
    self._Check(inst, "name=inst1.example.com")
389
    self._Check(inst, "nicmac=[]")
390

    
391
    # Default values
392
    self._Check(inst, "disk_template=None")
393
    self._Check(inst, "used=None")
394

    
395
    # Use instance
396
    inst.Use()
397
    self._Check(inst, "used=True")
398

    
399
    # Disk template
400
    inst.SetDiskTemplate(constants.DT_DRBD8)
401
    self._Check(inst, "disk_template=%s" % constants.DT_DRBD8)
402

    
403
    # Release instance
404
    inst.Release()
405
    self._Check(inst, "used=False")
406
    self._Check(inst, "disk_template=None")
407

    
408
  def testQaNode(self):
409
    node = qa_config._QaNode("primary.example.com", "192.0.2.1")
410
    self._Check(node, "primary=primary.example.com")
411
    self._Check(node, "secondary=192.0.2.1")
412
    self._Check(node, "added=False")
413
    self._Check(node, "use_count=0")
414

    
415
    # Mark as added
416
    node.MarkAdded()
417
    self._Check(node, "added=True")
418

    
419
    # Use node
420
    for i in range(1, 5):
421
      node.Use()
422
      self._Check(node, "use_count=%s" % i)
423

    
424
    # Release node
425
    for i in reversed(range(1, 5)):
426
      node.Release()
427
      self._Check(node, "use_count=%s" % (i - 1))
428

    
429
    self._Check(node, "use_count=0")
430

    
431
    # Mark as added
432
    node.MarkRemoved()
433
    self._Check(node, "added=False")
434

    
435

    
436
if __name__ == "__main__":
437
  testutils.GanetiTestProgram()