Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (27.2 kB)

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

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

    
24

    
25
import copy
26
import unittest
27

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

    
32
import testutils
33

    
34

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

    
38

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

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

    
53

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

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

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

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

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

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

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

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

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

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

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

    
162
  def testFillNdParamsNodeGroup(self):
163
    fake_node = objects.Node(name="test",
164
                             ndparams={},
165
                             group="testgroup")
166
    group_ndparams = {
167
        constants.ND_OOB_PROGRAM: "/bin/group-oob",
168
        constants.ND_SPINDLE_COUNT: 10,
169
        constants.ND_EXCLUSIVE_STORAGE: True,
170
        constants.ND_OVS: True,
171
        constants.ND_OVS_LINK: "eth2",
172
        constants.ND_OVS_NAME: "openvswitch",
173
        constants.ND_SSH_PORT: 122,
174
        }
175
    fake_group = objects.NodeGroup(name="testgroup",
176
                                   ndparams=group_ndparams)
177
    self.assertEqual(group_ndparams,
178
                     self.fake_cl.FillND(fake_node, fake_group))
179

    
180
  def testFillNdParamsNode(self):
181
    node_ndparams = {
182
        constants.ND_OOB_PROGRAM: "/bin/node-oob",
183
        constants.ND_SPINDLE_COUNT: 2,
184
        constants.ND_EXCLUSIVE_STORAGE: True,
185
        constants.ND_OVS: True,
186
        constants.ND_OVS_LINK: "eth2",
187
        constants.ND_OVS_NAME: "openvswitch",
188
        constants.ND_SSH_PORT: 222,
189
        }
190
    fake_node = objects.Node(name="test",
191
                             ndparams=node_ndparams,
192
                             group="testgroup")
193
    fake_group = objects.NodeGroup(name="testgroup",
194
                                   ndparams={})
195
    self.assertEqual(node_ndparams,
196
                     self.fake_cl.FillND(fake_node, fake_group))
197

    
198
  def testFillNdParamsAll(self):
199
    node_ndparams = {
200
        constants.ND_OOB_PROGRAM: "/bin/node-oob",
201
        constants.ND_SPINDLE_COUNT: 5,
202
        constants.ND_EXCLUSIVE_STORAGE: True,
203
        constants.ND_OVS: True,
204
        constants.ND_OVS_LINK: "eth2",
205
        constants.ND_OVS_NAME: "openvswitch",
206
        constants.ND_SSH_PORT: 322,
207
        }
208
    fake_node = objects.Node(name="test",
209
                             ndparams=node_ndparams,
210
                             group="testgroup")
211
    group_ndparams = {
212
        constants.ND_OOB_PROGRAM: "/bin/group-oob",
213
        constants.ND_SPINDLE_COUNT: 4,
214
        constants.ND_SSH_PORT: 422,
215
        }
216
    fake_group = objects.NodeGroup(name="testgroup",
217
                                   ndparams=group_ndparams)
218
    self.assertEqual(node_ndparams,
219
                     self.fake_cl.FillND(fake_node, fake_group))
220

    
221
  def testPrimaryHypervisor(self):
222
    assert self.fake_cl.enabled_hypervisors is None
223
    self.fake_cl.enabled_hypervisors = [constants.HT_XEN_HVM]
224
    self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_XEN_HVM)
225

    
226
    self.fake_cl.enabled_hypervisors = [constants.HT_XEN_PVM, constants.HT_KVM]
227
    self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_XEN_PVM)
228

    
229
    self.fake_cl.enabled_hypervisors = sorted(constants.HYPER_TYPES)
230
    self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_CHROOT)
231

    
232
  def testUpgradeConfig(self):
233
    # FIXME: This test is incomplete
234
    cluster = objects.Cluster()
235
    cluster.UpgradeConfig()
236
    cluster = objects.Cluster(ipolicy={"unknown_key": None})
237
    self.assertRaises(errors.ConfigurationError, cluster.UpgradeConfig)
238

    
239
  def testUpgradeEnabledDiskTemplates(self):
240
    cfg = objects.ConfigData()
241
    cfg.cluster = objects.Cluster()
242
    cfg.cluster.volume_group_name = "myvg"
243
    instance1 = objects.Instance()
244
    instance1.disk_template = constants.DT_DISKLESS
245
    instance2 = objects.Instance()
246
    instance2.disk_template = constants.DT_RBD
247
    cfg.instances = { "myinstance1": instance1, "myinstance2": instance2 }
248
    nodegroup = objects.NodeGroup()
249
    nodegroup.ipolicy = {}
250
    nodegroup.ipolicy[constants.IPOLICY_DTS] = [instance1.disk_template, \
251
      constants.DT_BLOCK]
252
    cfg.cluster.ipolicy = {}
253
    cfg.cluster.ipolicy[constants.IPOLICY_DTS] = \
254
      [constants.DT_EXT, constants.DT_DISKLESS]
255
    cfg.nodegroups = { "mynodegroup": nodegroup }
256
    cfg._UpgradeEnabledDiskTemplates()
257
    expected_disk_templates = [constants.DT_DRBD8,
258
                               constants.DT_PLAIN,
259
                               instance1.disk_template,
260
                               instance2.disk_template]
261
    self.assertEqual(set(expected_disk_templates),
262
                     set(cfg.cluster.enabled_disk_templates))
263
    self.assertEqual(set([instance1.disk_template]),
264
                     set(cfg.cluster.ipolicy[constants.IPOLICY_DTS]))
265

    
266

    
267
class TestClusterObjectTcpUdpPortPool(unittest.TestCase):
268
  def testNewCluster(self):
269
    self.assertTrue(objects.Cluster().tcpudp_port_pool is None)
270

    
271
  def testSerializingEmpty(self):
272
    self.assertEqual(objects.Cluster().ToDict(), {
273
      "tcpudp_port_pool": [],
274
      })
275

    
276
  def testSerializing(self):
277
    cluster = objects.Cluster.FromDict({})
278
    self.assertEqual(cluster.tcpudp_port_pool, set())
279

    
280
    cluster.tcpudp_port_pool.add(3546)
281
    cluster.tcpudp_port_pool.add(62511)
282

    
283
    data = cluster.ToDict()
284
    self.assertEqual(data.keys(), ["tcpudp_port_pool"])
285
    self.assertEqual(sorted(data["tcpudp_port_pool"]), sorted([3546, 62511]))
286

    
287
  def testDeserializingEmpty(self):
288
    cluster = objects.Cluster.FromDict({})
289
    self.assertEqual(cluster.tcpudp_port_pool, set())
290

    
291
  def testDeserialize(self):
292
    cluster = objects.Cluster.FromDict({
293
      "tcpudp_port_pool": [26214, 10039, 267],
294
      })
295
    self.assertEqual(cluster.tcpudp_port_pool, set([26214, 10039, 267]))
296

    
297

    
298
class TestOS(unittest.TestCase):
299
  ALL_DATA = [
300
    "debootstrap",
301
    "debootstrap+default",
302
    "debootstrap++default",
303
    ]
304

    
305
  def testSplitNameVariant(self):
306
    for name in self.ALL_DATA:
307
      self.assertEqual(len(objects.OS.SplitNameVariant(name)), 2)
308

    
309
  def testVariant(self):
310
    self.assertEqual(objects.OS.GetVariant("debootstrap"), "")
311
    self.assertEqual(objects.OS.GetVariant("debootstrap+default"), "default")
312

    
313

    
314
class TestInstance(unittest.TestCase):
315
  def _GenericCheck(self, inst):
316
    for i in [inst.all_nodes, inst.secondary_nodes]:
317
      self.assertTrue(isinstance(inst.all_nodes, (list, tuple)),
318
                      msg="Data type doesn't guarantee order")
319

    
320
    self.assertTrue(inst.primary_node not in inst.secondary_nodes)
321
    self.assertEqual(inst.all_nodes[0], inst.primary_node,
322
                     msg="Primary node not first node in list")
323

    
324
  def testNodesNoDisks(self):
325
    inst = objects.Instance(name="fakeinst.example.com",
326
      primary_node="pnode.example.com",
327
      disks=[
328
        ])
329

    
330
    self._GenericCheck(inst)
331
    self.assertEqual(len(inst.secondary_nodes), 0)
332
    self.assertEqual(set(inst.all_nodes), set([inst.primary_node]))
333
    self.assertEqual(inst.MapLVsByNode(), {
334
      inst.primary_node: [],
335
      })
336

    
337
  def testNodesPlainDisks(self):
338
    inst = objects.Instance(name="fakeinstplain.example.com",
339
      primary_node="node3.example.com",
340
      disks=[
341
        objects.Disk(dev_type=constants.DT_PLAIN, size=128,
342
                     logical_id=("myxenvg", "disk25494")),
343
        objects.Disk(dev_type=constants.DT_PLAIN, size=512,
344
                     logical_id=("myxenvg", "disk29071")),
345
        ])
346

    
347
    self._GenericCheck(inst)
348
    self.assertEqual(len(inst.secondary_nodes), 0)
349
    self.assertEqual(set(inst.all_nodes), set([inst.primary_node]))
350
    self.assertEqual(inst.MapLVsByNode(), {
351
      inst.primary_node: ["myxenvg/disk25494", "myxenvg/disk29071"],
352
      })
353

    
354
  def testNodesDrbdDisks(self):
355
    inst = objects.Instance(name="fakeinstdrbd.example.com",
356
      primary_node="node10.example.com",
357
      disks=[
358
        objects.Disk(dev_type=constants.DT_DRBD8, size=786432,
359
          logical_id=("node10.example.com", "node15.example.com",
360
                      12300, 0, 0, "secret"),
361
          children=[
362
            objects.Disk(dev_type=constants.DT_PLAIN, size=786432,
363
                         logical_id=("myxenvg", "disk0")),
364
            objects.Disk(dev_type=constants.DT_PLAIN, size=128,
365
                         logical_id=("myxenvg", "meta0"))
366
          ],
367
          iv_name="disk/0")
368
        ])
369

    
370
    self._GenericCheck(inst)
371
    self.assertEqual(set(inst.secondary_nodes), set(["node15.example.com"]))
372
    self.assertEqual(set(inst.all_nodes),
373
                     set([inst.primary_node, "node15.example.com"]))
374
    self.assertEqual(inst.MapLVsByNode(), {
375
      inst.primary_node: ["myxenvg/disk0", "myxenvg/meta0"],
376
      "node15.example.com": ["myxenvg/disk0", "myxenvg/meta0"],
377
      })
378

    
379
    self.assertEqual(inst.FindDisk(0), inst.disks[0])
380
    self.assertRaises(errors.OpPrereqError, inst.FindDisk, "hello")
381
    self.assertRaises(errors.OpPrereqError, inst.FindDisk, 100)
382
    self.assertRaises(errors.OpPrereqError, inst.FindDisk, 1)
383

    
384

    
385
class TestNode(unittest.TestCase):
386
  def testEmpty(self):
387
    self.assertEqual(objects.Node().ToDict(), {})
388
    self.assertTrue(isinstance(objects.Node.FromDict({}), objects.Node))
389

    
390
  def testHvState(self):
391
    node = objects.Node(name="node18157.example.com", hv_state={
392
      constants.HT_XEN_HVM: objects.NodeHvState(cpu_total=64),
393
      constants.HT_KVM: objects.NodeHvState(cpu_node=1),
394
      })
395

    
396
    node2 = objects.Node.FromDict(node.ToDict())
397

    
398
    # Make sure nothing can reference it anymore
399
    del node
400

    
401
    self.assertEqual(node2.name, "node18157.example.com")
402
    self.assertEqual(frozenset(node2.hv_state), frozenset([
403
      constants.HT_XEN_HVM,
404
      constants.HT_KVM,
405
      ]))
406
    self.assertEqual(node2.hv_state[constants.HT_KVM].cpu_node, 1)
407
    self.assertEqual(node2.hv_state[constants.HT_XEN_HVM].cpu_total, 64)
408

    
409
  def testDiskState(self):
410
    node = objects.Node(name="node32087.example.com", disk_state={
411
      constants.DT_PLAIN: {
412
        "lv32352": objects.NodeDiskState(total=128),
413
        "lv2082": objects.NodeDiskState(total=512),
414
        },
415
      })
416

    
417
    node2 = objects.Node.FromDict(node.ToDict())
418

    
419
    # Make sure nothing can reference it anymore
420
    del node
421

    
422
    self.assertEqual(node2.name, "node32087.example.com")
423
    self.assertEqual(frozenset(node2.disk_state), frozenset([
424
      constants.DT_PLAIN,
425
      ]))
426
    self.assertEqual(frozenset(node2.disk_state[constants.DT_PLAIN]),
427
                     frozenset(["lv32352", "lv2082"]))
428
    self.assertEqual(node2.disk_state[constants.DT_PLAIN]["lv2082"].total, 512)
429
    self.assertEqual(node2.disk_state[constants.DT_PLAIN]["lv32352"].total, 128)
430

    
431
  def testFilterEsNdp(self):
432
    node1 = objects.Node(name="node11673.example.com", ndparams={
433
      constants.ND_EXCLUSIVE_STORAGE: True,
434
      })
435
    node2 = objects.Node(name="node11674.example.com", ndparams={
436
      constants.ND_SPINDLE_COUNT: 3,
437
      constants.ND_EXCLUSIVE_STORAGE: False,
438
      })
439
    self.assertTrue(constants.ND_EXCLUSIVE_STORAGE in node1.ndparams)
440
    node1.UpgradeConfig()
441
    self.assertFalse(constants.ND_EXCLUSIVE_STORAGE in node1.ndparams)
442
    self.assertTrue(constants.ND_EXCLUSIVE_STORAGE in node2.ndparams)
443
    self.assertTrue(constants.ND_SPINDLE_COUNT in node2.ndparams)
444
    node2.UpgradeConfig()
445
    self.assertFalse(constants.ND_EXCLUSIVE_STORAGE in node2.ndparams)
446
    self.assertTrue(constants.ND_SPINDLE_COUNT in node2.ndparams)
447

    
448

    
449
class TestInstancePolicy(unittest.TestCase):
450
  def setUp(self):
451
    # Policies are big, and we want to see the difference in case of an error
452
    self.maxDiff = None
453

    
454
  def _AssertIPolicyIsFull(self, policy):
455
    self.assertEqual(frozenset(policy.keys()), constants.IPOLICY_ALL_KEYS)
456
    self.assertTrue(len(policy[constants.ISPECS_MINMAX]) > 0)
457
    for minmax in policy[constants.ISPECS_MINMAX]:
458
      self.assertEqual(frozenset(minmax.keys()), constants.ISPECS_MINMAX_KEYS)
459
      for key in constants.ISPECS_MINMAX_KEYS:
460
        self.assertEqual(frozenset(minmax[key].keys()),
461
                         constants.ISPECS_PARAMETERS)
462
    self.assertEqual(frozenset(policy[constants.ISPECS_STD].keys()),
463
                     constants.ISPECS_PARAMETERS)
464

    
465
  def testDefaultIPolicy(self):
466
    objects.InstancePolicy.CheckParameterSyntax(constants.IPOLICY_DEFAULTS,
467
                                                True)
468
    self._AssertIPolicyIsFull(constants.IPOLICY_DEFAULTS)
469

    
470
  def _AssertPolicyIsBad(self, ipolicy, do_check_std=None):
471
    if do_check_std is None:
472
      check_std_vals = [False, True]
473
    else:
474
      check_std_vals = [do_check_std]
475
    for check_std in check_std_vals:
476
      self.assertRaises(errors.ConfigurationError,
477
                        objects.InstancePolicy.CheckISpecSyntax,
478
                        ipolicy, check_std)
479

    
480
  def testCheckISpecSyntax(self):
481
    default_stdspec = constants.IPOLICY_DEFAULTS[constants.ISPECS_STD]
482
    incomplete_ipolicies = [
483
      {
484
         constants.ISPECS_MINMAX: [],
485
         constants.ISPECS_STD: default_stdspec,
486
         },
487
      {
488
         constants.ISPECS_MINMAX: [{}],
489
         constants.ISPECS_STD: default_stdspec,
490
         },
491
      {
492
        constants.ISPECS_MINMAX: [{
493
          constants.ISPECS_MIN: NotImplemented,
494
          }],
495
        constants.ISPECS_STD: default_stdspec,
496
        },
497
      {
498
        constants.ISPECS_MINMAX: [{
499
          constants.ISPECS_MAX: NotImplemented,
500
          }],
501
        constants.ISPECS_STD: default_stdspec,
502
        },
503
      {
504
        constants.ISPECS_MINMAX: [{
505
          constants.ISPECS_MIN: NotImplemented,
506
          constants.ISPECS_MAX: NotImplemented,
507
          }],
508
        },
509
      ]
510
    for ipol in incomplete_ipolicies:
511
      self.assertRaises(errors.ConfigurationError,
512
                        objects.InstancePolicy.CheckISpecSyntax,
513
                        ipol, True)
514
      oldminmax = ipol[constants.ISPECS_MINMAX]
515
      if oldminmax:
516
        # Prepending valid specs shouldn't change the error
517
        ipol[constants.ISPECS_MINMAX] = ([constants.ISPECS_MINMAX_DEFAULTS] +
518
                                         oldminmax)
519
        self.assertRaises(errors.ConfigurationError,
520
                          objects.InstancePolicy.CheckISpecSyntax,
521
                          ipol, True)
522

    
523
    good_ipolicy = {
524
      constants.ISPECS_MINMAX: [
525
        {
526
          constants.ISPECS_MIN: {
527
            constants.ISPEC_MEM_SIZE: 64,
528
            constants.ISPEC_CPU_COUNT: 1,
529
            constants.ISPEC_DISK_COUNT: 2,
530
            constants.ISPEC_DISK_SIZE: 64,
531
            constants.ISPEC_NIC_COUNT: 1,
532
            constants.ISPEC_SPINDLE_USE: 1,
533
            },
534
          constants.ISPECS_MAX: {
535
            constants.ISPEC_MEM_SIZE: 16384,
536
            constants.ISPEC_CPU_COUNT: 5,
537
            constants.ISPEC_DISK_COUNT: 12,
538
            constants.ISPEC_DISK_SIZE: 1024,
539
            constants.ISPEC_NIC_COUNT: 9,
540
            constants.ISPEC_SPINDLE_USE: 18,
541
            },
542
          },
543
        {
544
          constants.ISPECS_MIN: {
545
            constants.ISPEC_MEM_SIZE: 32768,
546
            constants.ISPEC_CPU_COUNT: 8,
547
            constants.ISPEC_DISK_COUNT: 1,
548
            constants.ISPEC_DISK_SIZE: 1024,
549
            constants.ISPEC_NIC_COUNT: 1,
550
            constants.ISPEC_SPINDLE_USE: 1,
551
            },
552
          constants.ISPECS_MAX: {
553
            constants.ISPEC_MEM_SIZE: 65536,
554
            constants.ISPEC_CPU_COUNT: 10,
555
            constants.ISPEC_DISK_COUNT: 5,
556
            constants.ISPEC_DISK_SIZE: 1024 * 1024,
557
            constants.ISPEC_NIC_COUNT: 3,
558
            constants.ISPEC_SPINDLE_USE: 12,
559
            },
560
          },
561
        ],
562
      }
563
    good_ipolicy[constants.ISPECS_STD] = copy.deepcopy(
564
      good_ipolicy[constants.ISPECS_MINMAX][0][constants.ISPECS_MAX])
565
    # Check that it's really good before making it bad
566
    objects.InstancePolicy.CheckISpecSyntax(good_ipolicy, True)
567

    
568
    bad_ipolicy = copy.deepcopy(good_ipolicy)
569
    for minmax in bad_ipolicy[constants.ISPECS_MINMAX]:
570
      for (key, spec) in minmax.items():
571
        for param in spec:
572
          oldv = spec[param]
573
          del spec[param]
574
          self._AssertPolicyIsBad(bad_ipolicy)
575
          if key == constants.ISPECS_MIN:
576
            spec[param] = minmax[constants.ISPECS_MAX][param] + 1
577
          self._AssertPolicyIsBad(bad_ipolicy)
578
          spec[param] = oldv
579
    assert bad_ipolicy == good_ipolicy
580

    
581
    stdspec = bad_ipolicy[constants.ISPECS_STD]
582
    for param in stdspec:
583
      oldv = stdspec[param]
584
      del stdspec[param]
585
      self._AssertPolicyIsBad(bad_ipolicy, True)
586
      # Note that std spec is the same as a max spec
587
      stdspec[param] = oldv + 1
588
      self._AssertPolicyIsBad(bad_ipolicy, True)
589
      stdspec[param] = oldv
590
    assert bad_ipolicy == good_ipolicy
591

    
592
    for minmax in good_ipolicy[constants.ISPECS_MINMAX]:
593
      for spec in minmax.values():
594
        good_ipolicy[constants.ISPECS_STD] = spec
595
        objects.InstancePolicy.CheckISpecSyntax(good_ipolicy, True)
596

    
597
  def testCheckISpecParamSyntax(self):
598
    par = "my_parameter"
599
    for check_std in [True, False]:
600
      # Min and max only
601
      good_values = [(11, 11), (11, 40), (0, 0)]
602
      for (mn, mx) in good_values:
603
        minmax = dict((k, {}) for k in constants.ISPECS_MINMAX_KEYS)
604
        minmax[constants.ISPECS_MIN][par] = mn
605
        minmax[constants.ISPECS_MAX][par] = mx
606
        objects.InstancePolicy._CheckISpecParamSyntax(minmax, {}, par,
607
                                                     check_std)
608
      minmax = dict((k, {}) for k in constants.ISPECS_MINMAX_KEYS)
609
      minmax[constants.ISPECS_MIN][par] = 11
610
      minmax[constants.ISPECS_MAX][par] = 5
611
      self.assertRaises(errors.ConfigurationError,
612
                        objects.InstancePolicy._CheckISpecParamSyntax,
613
                        minmax, {}, par, check_std)
614
    # Min, std, max
615
    good_values = [
616
      (11, 11, 11),
617
      (11, 11, 40),
618
      (11, 40, 40),
619
      ]
620
    for (mn, st, mx) in good_values:
621
      minmax = {
622
        constants.ISPECS_MIN: {par: mn},
623
        constants.ISPECS_MAX: {par: mx},
624
        }
625
      stdspec = {par: st}
626
      objects.InstancePolicy._CheckISpecParamSyntax(minmax, stdspec, par, True)
627
    bad_values = [
628
      (11, 11,  5, True),
629
      (40, 11, 11, True),
630
      (11, 80, 40, False),
631
      (11,  5, 40, False,),
632
      (11,  5,  5, True),
633
      (40, 40, 11, True),
634
      ]
635
    for (mn, st, mx, excp) in bad_values:
636
      minmax = {
637
        constants.ISPECS_MIN: {par: mn},
638
        constants.ISPECS_MAX: {par: mx},
639
        }
640
      stdspec = {par: st}
641
      if excp:
642
        self.assertRaises(errors.ConfigurationError,
643
                          objects.InstancePolicy._CheckISpecParamSyntax,
644
                          minmax, stdspec, par, True)
645
      else:
646
        ret = objects.InstancePolicy._CheckISpecParamSyntax(minmax, stdspec,
647
                                                            par, True)
648
        self.assertFalse(ret)
649

    
650
  def testCheckDiskTemplates(self):
651
    invalid = "this_is_not_a_good_template"
652
    for dt in constants.DISK_TEMPLATES:
653
      objects.InstancePolicy.CheckDiskTemplates([dt])
654
    objects.InstancePolicy.CheckDiskTemplates(list(constants.DISK_TEMPLATES))
655
    bad_examples = [
656
      [invalid],
657
      [constants.DT_DRBD8, invalid],
658
      list(constants.DISK_TEMPLATES) + [invalid],
659
      [],
660
      None,
661
      ]
662
    for dtl in bad_examples:
663
      self.assertRaises(errors.ConfigurationError,
664
                        objects.InstancePolicy.CheckDiskTemplates,
665
                        dtl)
666

    
667
  def testCheckParameterSyntax(self):
668
    invalid = "this_key_shouldnt_be_here"
669
    for check_std in [True, False]:
670
      objects.InstancePolicy.CheckParameterSyntax({}, check_std)
671
      policy = {invalid: None}
672
      self.assertRaises(errors.ConfigurationError,
673
                        objects.InstancePolicy.CheckParameterSyntax,
674
                        policy, check_std)
675
      for par in constants.IPOLICY_PARAMETERS:
676
        for val in ("blah", None, {}, [42]):
677
          policy = {par: val}
678
          self.assertRaises(errors.ConfigurationError,
679
                            objects.InstancePolicy.CheckParameterSyntax,
680
                            policy, check_std)
681

    
682
  def testFillIPolicyEmpty(self):
683
    policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, {})
684
    objects.InstancePolicy.CheckParameterSyntax(policy, True)
685
    self.assertEqual(policy, constants.IPOLICY_DEFAULTS)
686

    
687
  def _AssertISpecsMerged(self, default_spec, diff_spec, merged_spec):
688
    for (param, value) in merged_spec.items():
689
      if param in diff_spec:
690
        self.assertEqual(value, diff_spec[param])
691
      else:
692
        self.assertEqual(value, default_spec[param])
693

    
694
  def _AssertIPolicyMerged(self, default_pol, diff_pol, merged_pol):
695
    for (key, value) in merged_pol.items():
696
      if key in diff_pol:
697
        if key == constants.ISPECS_STD:
698
          self._AssertISpecsMerged(default_pol[key], diff_pol[key], value)
699
        else:
700
          self.assertEqual(value, diff_pol[key])
701
      else:
702
        self.assertEqual(value, default_pol[key])
703

    
704
  def testFillIPolicy(self):
705
    partial_policies = [
706
      {constants.IPOLICY_VCPU_RATIO: 3.14},
707
      {constants.IPOLICY_SPINDLE_RATIO: 2.72},
708
      {constants.IPOLICY_DTS: [constants.DT_FILE]},
709
      {constants.ISPECS_STD: {constants.ISPEC_DISK_COUNT: 3}},
710
      {constants.ISPECS_MINMAX: [constants.ISPECS_MINMAX_DEFAULTS,
711
                                 constants.ISPECS_MINMAX_DEFAULTS]}
712
      ]
713
    for diff_pol in partial_policies:
714
      policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, diff_pol)
715
      objects.InstancePolicy.CheckParameterSyntax(policy, True)
716
      self._AssertIPolicyIsFull(policy)
717
      self._AssertIPolicyMerged(constants.IPOLICY_DEFAULTS, diff_pol, policy)
718

    
719
  def testFillIPolicyKeepsUnknown(self):
720
    INVALID_KEY = "invalid_ipolicy_key"
721
    diff_pol = {
722
      INVALID_KEY: None,
723
      }
724
    policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, diff_pol)
725
    self.assertTrue(INVALID_KEY in policy)
726

    
727

    
728
class TestDisk(unittest.TestCase):
729
  def addChild(self, disk):
730
    """Adds a child of the same device type as the parent."""
731
    disk.children = []
732
    child = objects.Disk()
733
    child.dev_type = disk.dev_type
734
    disk.children.append(child)
735

    
736
  def testUpgradeConfigDevTypeLegacy(self):
737
    for old, new in [("drbd8", constants.DT_DRBD8),
738
                     ("lvm", constants.DT_PLAIN)]:
739
      disk = objects.Disk()
740
      disk.dev_type = old
741
      self.addChild(disk)
742
      disk.UpgradeConfig()
743
      self.assertEqual(new, disk.dev_type)
744
      self.assertEqual(new, disk.children[0].dev_type)
745

    
746
  def testUpgradeConfigDevTypeLegacyUnchanged(self):
747
    dev_types = [constants.DT_FILE, constants.DT_SHARED_FILE,
748
                 constants.DT_BLOCK, constants.DT_EXT,
749
                 constants.DT_RBD, constants.DT_GLUSTER]
750
    for dev_type in dev_types:
751
      disk = objects.Disk()
752
      disk.dev_type = dev_type
753
      self.addChild(disk)
754
      disk.UpgradeConfig()
755
      self.assertEqual(dev_type, disk.dev_type)
756
      self.assertEqual(dev_type, disk.children[0].dev_type)
757

    
758

    
759
if __name__ == "__main__":
760
  testutils.GanetiTestProgram()