Statistics
| Branch: | Tag: | Revision:

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()