Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.objects_unittest.py @ ba5c6c6b

History | View | Annotate | Download (19.1 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2010, 2012 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 unittesting the objects module"""
23

    
24

    
25
import unittest
26

    
27
from ganeti import constants
28
from ganeti import objects
29
from ganeti import errors
30

    
31
import testutils
32

    
33

    
34
class SimpleObject(objects.ConfigObject):
35
  __slots__ = ["a", "b"]
36

    
37

    
38
class TestDictState(unittest.TestCase):
39
  """Simple dict tansformation tests"""
40

    
41
  def testSimpleObjectToDict(self):
42
    o1 = SimpleObject(a="1")
43
    self.assertEquals(o1.ToDict(), {"a": "1"})
44
    self.assertEquals(o1.__getstate__(), {"a": "1"})
45
    self.assertEquals(o1.__getstate__(), o1.ToDict())
46
    o1.a = 2
47
    o1.b = 5
48
    self.assertEquals(o1.ToDict(), {"a": 2, "b": 5})
49
    o2 = SimpleObject.FromDict(o1.ToDict())
50
    self.assertEquals(o1.ToDict(), {"a": 2, "b": 5})
51

    
52

    
53
class TestClusterObject(unittest.TestCase):
54
  """Tests done on a L{objects.Cluster}"""
55

    
56
  def setUp(self):
57
    hvparams = {
58
      constants.HT_FAKE: {
59
        "foo": "bar",
60
        "bar": "foo",
61
        "foobar": "barfoo",
62
        },
63
      }
64
    os_hvp = {
65
      "lenny-image": {
66
        constants.HT_FAKE: {
67
          "foo": "baz",
68
          "foobar": "foobar",
69
          "blah": "blibb",
70
          "blubb": "blah",
71
          },
72
        constants.HT_XEN_PVM: {
73
          "root_path": "/dev/sda5",
74
          "foo": "foobar",
75
          },
76
        },
77
      "ubuntu-hardy": {
78
        },
79
      }
80
    ndparams = {
81
        constants.ND_OOB_PROGRAM: "/bin/cluster-oob",
82
        constants.ND_SPINDLE_COUNT: 1,
83
        constants.ND_EXCLUSIVE_STORAGE: False,
84
        }
85

    
86
    self.fake_cl = objects.Cluster(hvparams=hvparams, os_hvp=os_hvp,
87
                                   ndparams=ndparams)
88
    self.fake_cl.UpgradeConfig()
89

    
90
  def testGetHVDefaults(self):
91
    cl = self.fake_cl
92
    self.failUnlessEqual(cl.GetHVDefaults(constants.HT_FAKE),
93
                         cl.hvparams[constants.HT_FAKE])
94
    self.failUnlessEqual(cl.GetHVDefaults(None), {})
95
    self.failUnlessEqual(cl.GetHVDefaults(constants.HT_XEN_PVM,
96
                                          os_name="lenny-image"),
97
                         cl.os_hvp["lenny-image"][constants.HT_XEN_PVM])
98

    
99

    
100
  def testFillHvFullMerge(self):
101
    inst_hvparams = {
102
      "blah": "blubb",
103
      }
104

    
105
    fake_dict = {
106
      "foo": "baz",
107
      "bar": "foo",
108
      "foobar": "foobar",
109
      "blah": "blubb",
110
      "blubb": "blah",
111
      }
112
    fake_inst = objects.Instance(name="foobar",
113
                                 os="lenny-image",
114
                                 hypervisor=constants.HT_FAKE,
115
                                 hvparams=inst_hvparams)
116
    self.assertEqual(fake_dict, self.fake_cl.FillHV(fake_inst))
117

    
118
  def testFillHvGlobalParams(self):
119
    fake_inst = objects.Instance(name="foobar",
120
                                 os="ubuntu-hardy",
121
                                 hypervisor=constants.HT_FAKE,
122
                                 hvparams={})
123
    self.assertEqual(self.fake_cl.hvparams[constants.HT_FAKE],
124
                     self.fake_cl.FillHV(fake_inst))
125

    
126
  def testFillHvInstParams(self):
127
    inst_hvparams = {
128
      "blah": "blubb",
129
      }
130
    fake_inst = objects.Instance(name="foobar",
131
                                 os="ubuntu-hardy",
132
                                 hypervisor=constants.HT_XEN_PVM,
133
                                 hvparams=inst_hvparams)
134
    self.assertEqual(inst_hvparams, self.fake_cl.FillHV(fake_inst))
135

    
136
  def testFillHvEmptyParams(self):
137
    fake_inst = objects.Instance(name="foobar",
138
                                 os="ubuntu-hardy",
139
                                 hypervisor=constants.HT_XEN_PVM,
140
                                 hvparams={})
141
    self.assertEqual({}, self.fake_cl.FillHV(fake_inst))
142

    
143
  def testFillHvPartialParams(self):
144
    os = "lenny-image"
145
    fake_inst = objects.Instance(name="foobar",
146
                                 os=os,
147
                                 hypervisor=constants.HT_XEN_PVM,
148
                                 hvparams={})
149
    self.assertEqual(self.fake_cl.os_hvp[os][constants.HT_XEN_PVM],
150
                     self.fake_cl.FillHV(fake_inst))
151

    
152
  def testFillNdParamsCluster(self):
153
    fake_node = objects.Node(name="test",
154
                             ndparams={},
155
                             group="testgroup")
156
    fake_group = objects.NodeGroup(name="testgroup",
157
                                   ndparams={})
158
    self.assertEqual(self.fake_cl.ndparams,
159
                     self.fake_cl.FillND(fake_node, fake_group))
160

    
161
  def testFillNdParamsNodeGroup(self):
162
    fake_node = objects.Node(name="test",
163
                             ndparams={},
164
                             group="testgroup")
165
    group_ndparams = {
166
        constants.ND_OOB_PROGRAM: "/bin/group-oob",
167
        constants.ND_SPINDLE_COUNT: 10,
168
        constants.ND_EXCLUSIVE_STORAGE: True,
169
        }
170
    fake_group = objects.NodeGroup(name="testgroup",
171
                                   ndparams=group_ndparams)
172
    self.assertEqual(group_ndparams,
173
                     self.fake_cl.FillND(fake_node, fake_group))
174

    
175
  def testFillNdParamsNode(self):
176
    node_ndparams = {
177
        constants.ND_OOB_PROGRAM: "/bin/node-oob",
178
        constants.ND_SPINDLE_COUNT: 2,
179
        constants.ND_EXCLUSIVE_STORAGE: True,
180
        }
181
    fake_node = objects.Node(name="test",
182
                             ndparams=node_ndparams,
183
                             group="testgroup")
184
    fake_group = objects.NodeGroup(name="testgroup",
185
                                   ndparams={})
186
    self.assertEqual(node_ndparams,
187
                     self.fake_cl.FillND(fake_node, fake_group))
188

    
189
  def testFillNdParamsAll(self):
190
    node_ndparams = {
191
        constants.ND_OOB_PROGRAM: "/bin/node-oob",
192
        constants.ND_SPINDLE_COUNT: 5,
193
        constants.ND_EXCLUSIVE_STORAGE: True,
194
        }
195
    fake_node = objects.Node(name="test",
196
                             ndparams=node_ndparams,
197
                             group="testgroup")
198
    group_ndparams = {
199
        constants.ND_OOB_PROGRAM: "/bin/group-oob",
200
        constants.ND_SPINDLE_COUNT: 4,
201
        }
202
    fake_group = objects.NodeGroup(name="testgroup",
203
                                   ndparams=group_ndparams)
204
    self.assertEqual(node_ndparams,
205
                     self.fake_cl.FillND(fake_node, fake_group))
206

    
207
  def testPrimaryHypervisor(self):
208
    assert self.fake_cl.enabled_hypervisors is None
209
    self.fake_cl.enabled_hypervisors = [constants.HT_XEN_HVM]
210
    self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_XEN_HVM)
211

    
212
    self.fake_cl.enabled_hypervisors = [constants.HT_XEN_PVM, constants.HT_KVM]
213
    self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_XEN_PVM)
214

    
215
    self.fake_cl.enabled_hypervisors = sorted(constants.HYPER_TYPES)
216
    self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_CHROOT)
217

    
218
  def testUpgradeConfig(self):
219
    # FIXME: This test is incomplete
220
    cluster = objects.Cluster()
221
    cluster.UpgradeConfig()
222
    cluster = objects.Cluster(ipolicy={"unknown_key": None})
223
    self.assertRaises(errors.ConfigurationError, cluster.UpgradeConfig)
224

    
225

    
226
class TestOS(unittest.TestCase):
227
  ALL_DATA = [
228
    "debootstrap",
229
    "debootstrap+default",
230
    "debootstrap++default",
231
    ]
232

    
233
  def testSplitNameVariant(self):
234
    for name in self.ALL_DATA:
235
      self.assertEqual(len(objects.OS.SplitNameVariant(name)), 2)
236

    
237
  def testVariant(self):
238
    self.assertEqual(objects.OS.GetVariant("debootstrap"), "")
239
    self.assertEqual(objects.OS.GetVariant("debootstrap+default"), "default")
240

    
241

    
242
class TestInstance(unittest.TestCase):
243
  def _GenericCheck(self, inst):
244
    for i in [inst.all_nodes, inst.secondary_nodes]:
245
      self.assertTrue(isinstance(inst.all_nodes, (list, tuple)),
246
                      msg="Data type doesn't guarantee order")
247

    
248
    self.assertTrue(inst.primary_node not in inst.secondary_nodes)
249
    self.assertEqual(inst.all_nodes[0], inst.primary_node,
250
                     msg="Primary node not first node in list")
251

    
252
  def testNodesNoDisks(self):
253
    inst = objects.Instance(name="fakeinst.example.com",
254
      primary_node="pnode.example.com",
255
      disks=[
256
        ])
257

    
258
    self._GenericCheck(inst)
259
    self.assertEqual(len(inst.secondary_nodes), 0)
260
    self.assertEqual(set(inst.all_nodes), set([inst.primary_node]))
261
    self.assertEqual(inst.MapLVsByNode(), {
262
      inst.primary_node: [],
263
      })
264

    
265
  def testNodesPlainDisks(self):
266
    inst = objects.Instance(name="fakeinstplain.example.com",
267
      primary_node="node3.example.com",
268
      disks=[
269
        objects.Disk(dev_type=constants.LD_LV, size=128,
270
                     logical_id=("myxenvg", "disk25494")),
271
        objects.Disk(dev_type=constants.LD_LV, size=512,
272
                     logical_id=("myxenvg", "disk29071")),
273
        ])
274

    
275
    self._GenericCheck(inst)
276
    self.assertEqual(len(inst.secondary_nodes), 0)
277
    self.assertEqual(set(inst.all_nodes), set([inst.primary_node]))
278
    self.assertEqual(inst.MapLVsByNode(), {
279
      inst.primary_node: ["myxenvg/disk25494", "myxenvg/disk29071"],
280
      })
281

    
282
  def testNodesDrbdDisks(self):
283
    inst = objects.Instance(name="fakeinstdrbd.example.com",
284
      primary_node="node10.example.com",
285
      disks=[
286
        objects.Disk(dev_type=constants.LD_DRBD8, size=786432,
287
          logical_id=("node10.example.com", "node15.example.com",
288
                      12300, 0, 0, "secret"),
289
          children=[
290
            objects.Disk(dev_type=constants.LD_LV, size=786432,
291
                         logical_id=("myxenvg", "disk0")),
292
            objects.Disk(dev_type=constants.LD_LV, size=128,
293
                         logical_id=("myxenvg", "meta0"))
294
          ],
295
          iv_name="disk/0")
296
        ])
297

    
298
    self._GenericCheck(inst)
299
    self.assertEqual(set(inst.secondary_nodes), set(["node15.example.com"]))
300
    self.assertEqual(set(inst.all_nodes),
301
                     set([inst.primary_node, "node15.example.com"]))
302
    self.assertEqual(inst.MapLVsByNode(), {
303
      inst.primary_node: ["myxenvg/disk0", "myxenvg/meta0"],
304
      "node15.example.com": ["myxenvg/disk0", "myxenvg/meta0"],
305
      })
306

    
307
    self.assertEqual(inst.FindDisk(0), inst.disks[0])
308
    self.assertRaises(errors.OpPrereqError, inst.FindDisk, "hello")
309
    self.assertRaises(errors.OpPrereqError, inst.FindDisk, 100)
310
    self.assertRaises(errors.OpPrereqError, inst.FindDisk, 1)
311

    
312

    
313
class TestNode(unittest.TestCase):
314
  def testEmpty(self):
315
    self.assertEqual(objects.Node().ToDict(), {})
316
    self.assertTrue(isinstance(objects.Node.FromDict({}), objects.Node))
317

    
318
  def testHvState(self):
319
    node = objects.Node(name="node18157.example.com", hv_state={
320
      constants.HT_XEN_HVM: objects.NodeHvState(cpu_total=64),
321
      constants.HT_KVM: objects.NodeHvState(cpu_node=1),
322
      })
323

    
324
    node2 = objects.Node.FromDict(node.ToDict())
325

    
326
    # Make sure nothing can reference it anymore
327
    del node
328

    
329
    self.assertEqual(node2.name, "node18157.example.com")
330
    self.assertEqual(frozenset(node2.hv_state), frozenset([
331
      constants.HT_XEN_HVM,
332
      constants.HT_KVM,
333
      ]))
334
    self.assertEqual(node2.hv_state[constants.HT_KVM].cpu_node, 1)
335
    self.assertEqual(node2.hv_state[constants.HT_XEN_HVM].cpu_total, 64)
336

    
337
  def testDiskState(self):
338
    node = objects.Node(name="node32087.example.com", disk_state={
339
      constants.LD_LV: {
340
        "lv32352": objects.NodeDiskState(total=128),
341
        "lv2082": objects.NodeDiskState(total=512),
342
        },
343
      })
344

    
345
    node2 = objects.Node.FromDict(node.ToDict())
346

    
347
    # Make sure nothing can reference it anymore
348
    del node
349

    
350
    self.assertEqual(node2.name, "node32087.example.com")
351
    self.assertEqual(frozenset(node2.disk_state), frozenset([
352
      constants.LD_LV,
353
      ]))
354
    self.assertEqual(frozenset(node2.disk_state[constants.LD_LV]), frozenset([
355
      "lv32352",
356
      "lv2082",
357
      ]))
358
    self.assertEqual(node2.disk_state[constants.LD_LV]["lv2082"].total, 512)
359
    self.assertEqual(node2.disk_state[constants.LD_LV]["lv32352"].total, 128)
360

    
361
  def testFilterEsNdp(self):
362
    node1 = objects.Node(name="node11673.example.com", ndparams={
363
      constants.ND_EXCLUSIVE_STORAGE: True,
364
      })
365
    node2 = objects.Node(name="node11674.example.com", ndparams={
366
      constants.ND_SPINDLE_COUNT: 3,
367
      constants.ND_EXCLUSIVE_STORAGE: False,
368
      })
369
    self.assertTrue(constants.ND_EXCLUSIVE_STORAGE in node1.ndparams)
370
    node1.UpgradeConfig()
371
    self.assertFalse(constants.ND_EXCLUSIVE_STORAGE in node1.ndparams)
372
    self.assertTrue(constants.ND_EXCLUSIVE_STORAGE in node2.ndparams)
373
    self.assertTrue(constants.ND_SPINDLE_COUNT in node2.ndparams)
374
    node2.UpgradeConfig()
375
    self.assertFalse(constants.ND_EXCLUSIVE_STORAGE in node2.ndparams)
376
    self.assertTrue(constants.ND_SPINDLE_COUNT in node2.ndparams)
377

    
378

    
379
class TestInstancePolicy(unittest.TestCase):
380
  def setUp(self):
381
    # Policies are big, and we want to see the difference in case of an error
382
    self.maxDiff = None
383

    
384
  def _AssertIPolicyIsFull(self, policy):
385
    self.assertEqual(frozenset(policy.keys()), constants.IPOLICY_ALL_KEYS)
386
    for key in constants.IPOLICY_ISPECS:
387
      spec = policy[key]
388
      self.assertEqual(frozenset(spec.keys()), constants.ISPECS_PARAMETERS)
389

    
390
  def testDefaultIPolicy(self):
391
    objects.InstancePolicy.CheckParameterSyntax(constants.IPOLICY_DEFAULTS,
392
                                                True)
393
    self._AssertIPolicyIsFull(constants.IPOLICY_DEFAULTS)
394

    
395
  def testCheckISpecSyntax(self):
396
    par = "my_parameter"
397
    for check_std in [True, False]:
398
      if check_std:
399
        allkeys = constants.IPOLICY_ISPECS
400
      else:
401
        allkeys = constants.IPOLICY_ISPECS - frozenset([constants.ISPECS_STD])
402
      # Only one policy limit
403
      for key in allkeys:
404
        policy = dict((k, {}) for k in allkeys)
405
        policy[key][par] = 11
406
        objects.InstancePolicy.CheckISpecSyntax(policy, par, check_std)
407
      # Min and max only
408
      good_values = [(11, 11), (11, 40), (0, 0)]
409
      for (mn, mx) in good_values:
410
        policy = dict((k, {}) for k in allkeys)
411
        policy[constants.ISPECS_MIN][par] = mn
412
        policy[constants.ISPECS_MAX][par] = mx
413
        objects.InstancePolicy.CheckISpecSyntax(policy, par, check_std)
414
      policy = dict((k, {}) for k in allkeys)
415
      policy[constants.ISPECS_MIN][par] = 11
416
      policy[constants.ISPECS_MAX][par] = 5
417
      self.assertRaises(errors.ConfigurationError,
418
                        objects.InstancePolicy.CheckISpecSyntax,
419
                        policy, par, check_std)
420
    # Min, std, max
421
    good_values = [
422
      (11, 11, 11),
423
      (11, 11, 40),
424
      (11, 40, 40),
425
      ]
426
    for (mn, st, mx) in good_values:
427
      policy = {
428
        constants.ISPECS_MIN: {par: mn},
429
        constants.ISPECS_STD: {par: st},
430
        constants.ISPECS_MAX: {par: mx},
431
        }
432
      objects.InstancePolicy.CheckISpecSyntax(policy, par, True)
433
    bad_values = [
434
      (11, 11,  5),
435
      (40, 11, 11),
436
      (11, 80, 40),
437
      (11,  5, 40),
438
      (11,  5,  5),
439
      (40, 40, 11),
440
      ]
441
    for (mn, st, mx) in bad_values:
442
      policy = {
443
        constants.ISPECS_MIN: {par: mn},
444
        constants.ISPECS_STD: {par: st},
445
        constants.ISPECS_MAX: {par: mx},
446
        }
447
      self.assertRaises(errors.ConfigurationError,
448
                        objects.InstancePolicy.CheckISpecSyntax,
449
                        policy, par, True)
450

    
451
  def testCheckDiskTemplates(self):
452
    invalid = "this_is_not_a_good_template"
453
    for dt in constants.DISK_TEMPLATES:
454
      objects.InstancePolicy.CheckDiskTemplates([dt])
455
    objects.InstancePolicy.CheckDiskTemplates(list(constants.DISK_TEMPLATES))
456
    bad_examples = [
457
      [invalid],
458
      [constants.DT_DRBD8, invalid],
459
      list(constants.DISK_TEMPLATES) + [invalid],
460
      [],
461
      None,
462
      ]
463
    for dtl in bad_examples:
464
      self.assertRaises(errors.ConfigurationError,
465
                        objects.InstancePolicy.CheckDiskTemplates,
466
                        dtl)
467

    
468
  def testCheckParameterSyntax(self):
469
    invalid = "this_key_shouldnt_be_here"
470
    for check_std in [True, False]:
471
      self.assertRaises(KeyError,
472
                        objects.InstancePolicy.CheckParameterSyntax,
473
                        {}, check_std)
474
      policy = objects.MakeEmptyIPolicy()
475
      policy[invalid] = None
476
      self.assertRaises(errors.ConfigurationError,
477
                        objects.InstancePolicy.CheckParameterSyntax,
478
                        policy, check_std)
479
      for par in constants.IPOLICY_PARAMETERS:
480
        policy = objects.MakeEmptyIPolicy()
481
        for val in ("blah", None, {}, [42]):
482
          policy[par] = val
483
          self.assertRaises(errors.ConfigurationError,
484
                            objects.InstancePolicy.CheckParameterSyntax,
485
                            policy, check_std)
486

    
487
  def testFillIPolicyEmpty(self):
488
    policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, {})
489
    objects.InstancePolicy.CheckParameterSyntax(policy, True)
490
    self.assertEqual(policy, constants.IPOLICY_DEFAULTS)
491

    
492
  def _AssertISpecsMerged(self, default_spec, diff_spec, merged_spec):
493
    for (param, value) in merged_spec.items():
494
      if param in diff_spec:
495
        self.assertEqual(value, diff_spec[param])
496
      else:
497
        self.assertEqual(value, default_spec[param])
498

    
499
  def _AssertIPolicyMerged(self, default_pol, diff_pol, merged_pol):
500
    for (key, value) in merged_pol.items():
501
      if key in diff_pol:
502
        if key in constants.IPOLICY_ISPECS:
503
          self._AssertISpecsMerged(default_pol[key], diff_pol[key], value)
504
        else:
505
          self.assertEqual(value, diff_pol[key])
506
      else:
507
        self.assertEqual(value, default_pol[key])
508

    
509
  def testFillIPolicy(self):
510
    partial_policies = [
511
      {constants.IPOLICY_VCPU_RATIO: 3.14},
512
      {constants.IPOLICY_SPINDLE_RATIO: 2.72},
513
      {constants.IPOLICY_DTS: [constants.DT_FILE]},
514
      ]
515
    for diff_pol in partial_policies:
516
      policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, diff_pol)
517
      objects.InstancePolicy.CheckParameterSyntax(policy, True)
518
      self._AssertIPolicyIsFull(policy)
519
      self._AssertIPolicyMerged(constants.IPOLICY_DEFAULTS, diff_pol, policy)
520

    
521
  def testFillIPolicySpecs(self):
522
    partial_policies = [
523
      {constants.ISPECS_MIN: {constants.ISPEC_MEM_SIZE: 32},
524
       constants.ISPECS_MAX: {constants.ISPEC_CPU_COUNT: 1024}},
525
      {constants.ISPECS_STD: {constants.ISPEC_DISK_SIZE: 2048},
526
       constants.ISPECS_MAX: {
527
          constants.ISPEC_DISK_COUNT: constants.MAX_DISKS - 1,
528
          constants.ISPEC_NIC_COUNT: constants.MAX_NICS - 1,
529
          }},
530
      {constants.ISPECS_STD: {constants.ISPEC_SPINDLE_USE: 3}},
531
      ]
532
    for diff_pol in partial_policies:
533
      policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, diff_pol)
534
      objects.InstancePolicy.CheckParameterSyntax(policy, True)
535
      self._AssertIPolicyIsFull(policy)
536
      self._AssertIPolicyMerged(constants.IPOLICY_DEFAULTS, diff_pol, policy)
537

    
538

    
539
if __name__ == "__main__":
540
  testutils.GanetiTestProgram()