Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.objects_unittest.py @ 560ef132

History | View | Annotate | Download (27.5 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
    defaults = cl.GetHVDefaults(constants.HT_XEN_PVM,
97
                                          os_name="lenny-image")
98
    for param, value in cl.os_hvp["lenny-image"][constants.HT_XEN_PVM].items():
99
      self.assertEqual(value, defaults[param])
100

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

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

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

    
128
  def testFillHvInstParams(self):
129
    inst_hvparams = {
130
      "blah": "blubb",
131
      }
132
    fake_inst = objects.Instance(name="foobar",
133
                                 os="ubuntu-hardy",
134
                                 hypervisor=constants.HT_XEN_PVM,
135
                                 hvparams=inst_hvparams)
136
    filled_conf = self.fake_cl.FillHV(fake_inst)
137
    for param, value in constants.HVC_DEFAULTS[constants.HT_XEN_PVM].items():
138
      if param == "blah":
139
        value = "blubb"
140
      self.assertEqual(value, filled_conf[param])
141

    
142
  def testFillHvDefaultParams(self):
143
    fake_inst = objects.Instance(name="foobar",
144
                                 os="ubuntu-hardy",
145
                                 hypervisor=constants.HT_XEN_PVM,
146
                                 hvparams={})
147
    self.assertEqual(constants.HVC_DEFAULTS[constants.HT_XEN_PVM],
148
                     self.fake_cl.FillHV(fake_inst))
149

    
150
  def testFillHvPartialParams(self):
151
    os = "lenny-image"
152
    fake_inst = objects.Instance(name="foobar",
153
                                 os=os,
154
                                 hypervisor=constants.HT_XEN_PVM,
155
                                 hvparams={})
156
    filled_conf = self.fake_cl.FillHV(fake_inst)
157
    for param, value in self.fake_cl.os_hvp[os][constants.HT_XEN_PVM].items():
158
      self.assertEqual(value, filled_conf[param])
159

    
160
  def testFillNdParamsCluster(self):
161
    fake_node = objects.Node(name="test",
162
                             ndparams={},
163
                             group="testgroup")
164
    fake_group = objects.NodeGroup(name="testgroup",
165
                                   ndparams={})
166
    self.assertEqual(self.fake_cl.ndparams,
167
                     self.fake_cl.FillND(fake_node, fake_group))
168

    
169
  def testFillNdParamsNodeGroup(self):
170
    fake_node = objects.Node(name="test",
171
                             ndparams={},
172
                             group="testgroup")
173
    group_ndparams = {
174
        constants.ND_OOB_PROGRAM: "/bin/group-oob",
175
        constants.ND_SPINDLE_COUNT: 10,
176
        constants.ND_EXCLUSIVE_STORAGE: True,
177
        constants.ND_OVS: True,
178
        constants.ND_OVS_LINK: "eth2",
179
        constants.ND_OVS_NAME: "openvswitch",
180
        constants.ND_SSH_PORT: 122,
181
        }
182
    fake_group = objects.NodeGroup(name="testgroup",
183
                                   ndparams=group_ndparams)
184
    self.assertEqual(group_ndparams,
185
                     self.fake_cl.FillND(fake_node, fake_group))
186

    
187
  def testFillNdParamsNode(self):
188
    node_ndparams = {
189
        constants.ND_OOB_PROGRAM: "/bin/node-oob",
190
        constants.ND_SPINDLE_COUNT: 2,
191
        constants.ND_EXCLUSIVE_STORAGE: True,
192
        constants.ND_OVS: True,
193
        constants.ND_OVS_LINK: "eth2",
194
        constants.ND_OVS_NAME: "openvswitch",
195
        constants.ND_SSH_PORT: 222,
196
        }
197
    fake_node = objects.Node(name="test",
198
                             ndparams=node_ndparams,
199
                             group="testgroup")
200
    fake_group = objects.NodeGroup(name="testgroup",
201
                                   ndparams={})
202
    self.assertEqual(node_ndparams,
203
                     self.fake_cl.FillND(fake_node, fake_group))
204

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

    
228
  def testPrimaryHypervisor(self):
229
    assert self.fake_cl.enabled_hypervisors is None
230
    self.fake_cl.enabled_hypervisors = [constants.HT_XEN_HVM]
231
    self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_XEN_HVM)
232

    
233
    self.fake_cl.enabled_hypervisors = [constants.HT_XEN_PVM, constants.HT_KVM]
234
    self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_XEN_PVM)
235

    
236
    self.fake_cl.enabled_hypervisors = sorted(constants.HYPER_TYPES)
237
    self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_CHROOT)
238

    
239
  def testUpgradeConfig(self):
240
    # FIXME: This test is incomplete
241
    cluster = objects.Cluster()
242
    cluster.UpgradeConfig()
243
    cluster = objects.Cluster(ipolicy={"unknown_key": None})
244
    self.assertRaises(errors.ConfigurationError, cluster.UpgradeConfig)
245

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

    
273

    
274
class TestClusterObjectTcpUdpPortPool(unittest.TestCase):
275
  def testNewCluster(self):
276
    self.assertTrue(objects.Cluster().tcpudp_port_pool is None)
277

    
278
  def testSerializingEmpty(self):
279
    self.assertEqual(objects.Cluster().ToDict(), {
280
      "tcpudp_port_pool": [],
281
      })
282

    
283
  def testSerializing(self):
284
    cluster = objects.Cluster.FromDict({})
285
    self.assertEqual(cluster.tcpudp_port_pool, set())
286

    
287
    cluster.tcpudp_port_pool.add(3546)
288
    cluster.tcpudp_port_pool.add(62511)
289

    
290
    data = cluster.ToDict()
291
    self.assertEqual(data.keys(), ["tcpudp_port_pool"])
292
    self.assertEqual(sorted(data["tcpudp_port_pool"]), sorted([3546, 62511]))
293

    
294
  def testDeserializingEmpty(self):
295
    cluster = objects.Cluster.FromDict({})
296
    self.assertEqual(cluster.tcpudp_port_pool, set())
297

    
298
  def testDeserialize(self):
299
    cluster = objects.Cluster.FromDict({
300
      "tcpudp_port_pool": [26214, 10039, 267],
301
      })
302
    self.assertEqual(cluster.tcpudp_port_pool, set([26214, 10039, 267]))
303

    
304

    
305
class TestOS(unittest.TestCase):
306
  ALL_DATA = [
307
    "debootstrap",
308
    "debootstrap+default",
309
    "debootstrap++default",
310
    ]
311

    
312
  def testSplitNameVariant(self):
313
    for name in self.ALL_DATA:
314
      self.assertEqual(len(objects.OS.SplitNameVariant(name)), 2)
315

    
316
  def testVariant(self):
317
    self.assertEqual(objects.OS.GetVariant("debootstrap"), "")
318
    self.assertEqual(objects.OS.GetVariant("debootstrap+default"), "default")
319

    
320

    
321
class TestInstance(unittest.TestCase):
322
  def _GenericCheck(self, inst):
323
    for i in [inst.all_nodes, inst.secondary_nodes]:
324
      self.assertTrue(isinstance(inst.all_nodes, (list, tuple)),
325
                      msg="Data type doesn't guarantee order")
326

    
327
    self.assertTrue(inst.primary_node not in inst.secondary_nodes)
328
    self.assertEqual(inst.all_nodes[0], inst.primary_node,
329
                     msg="Primary node not first node in list")
330

    
331
  def testNodesNoDisks(self):
332
    inst = objects.Instance(name="fakeinst.example.com",
333
      primary_node="pnode.example.com",
334
      disks=[
335
        ])
336

    
337
    self._GenericCheck(inst)
338
    self.assertEqual(len(inst.secondary_nodes), 0)
339
    self.assertEqual(set(inst.all_nodes), set([inst.primary_node]))
340
    self.assertEqual(inst.MapLVsByNode(), {
341
      inst.primary_node: [],
342
      })
343

    
344
  def testNodesPlainDisks(self):
345
    inst = objects.Instance(name="fakeinstplain.example.com",
346
      primary_node="node3.example.com",
347
      disks=[
348
        objects.Disk(dev_type=constants.DT_PLAIN, size=128,
349
                     logical_id=("myxenvg", "disk25494")),
350
        objects.Disk(dev_type=constants.DT_PLAIN, size=512,
351
                     logical_id=("myxenvg", "disk29071")),
352
        ])
353

    
354
    self._GenericCheck(inst)
355
    self.assertEqual(len(inst.secondary_nodes), 0)
356
    self.assertEqual(set(inst.all_nodes), set([inst.primary_node]))
357
    self.assertEqual(inst.MapLVsByNode(), {
358
      inst.primary_node: ["myxenvg/disk25494", "myxenvg/disk29071"],
359
      })
360

    
361
  def testNodesDrbdDisks(self):
362
    inst = objects.Instance(name="fakeinstdrbd.example.com",
363
      primary_node="node10.example.com",
364
      disks=[
365
        objects.Disk(dev_type=constants.DT_DRBD8, size=786432,
366
          logical_id=("node10.example.com", "node15.example.com",
367
                      12300, 0, 0, "secret"),
368
          children=[
369
            objects.Disk(dev_type=constants.DT_PLAIN, size=786432,
370
                         logical_id=("myxenvg", "disk0")),
371
            objects.Disk(dev_type=constants.DT_PLAIN, size=128,
372
                         logical_id=("myxenvg", "meta0"))
373
          ],
374
          iv_name="disk/0")
375
        ])
376

    
377
    self._GenericCheck(inst)
378
    self.assertEqual(set(inst.secondary_nodes), set(["node15.example.com"]))
379
    self.assertEqual(set(inst.all_nodes),
380
                     set([inst.primary_node, "node15.example.com"]))
381
    self.assertEqual(inst.MapLVsByNode(), {
382
      inst.primary_node: ["myxenvg/disk0", "myxenvg/meta0"],
383
      "node15.example.com": ["myxenvg/disk0", "myxenvg/meta0"],
384
      })
385

    
386
    self.assertEqual(inst.FindDisk(0), inst.disks[0])
387
    self.assertRaises(errors.OpPrereqError, inst.FindDisk, "hello")
388
    self.assertRaises(errors.OpPrereqError, inst.FindDisk, 100)
389
    self.assertRaises(errors.OpPrereqError, inst.FindDisk, 1)
390

    
391

    
392
class TestNode(unittest.TestCase):
393
  def testEmpty(self):
394
    self.assertEqual(objects.Node().ToDict(), {})
395
    self.assertTrue(isinstance(objects.Node.FromDict({}), objects.Node))
396

    
397
  def testHvState(self):
398
    node = objects.Node(name="node18157.example.com", hv_state={
399
      constants.HT_XEN_HVM: objects.NodeHvState(cpu_total=64),
400
      constants.HT_KVM: objects.NodeHvState(cpu_node=1),
401
      })
402

    
403
    node2 = objects.Node.FromDict(node.ToDict())
404

    
405
    # Make sure nothing can reference it anymore
406
    del node
407

    
408
    self.assertEqual(node2.name, "node18157.example.com")
409
    self.assertEqual(frozenset(node2.hv_state), frozenset([
410
      constants.HT_XEN_HVM,
411
      constants.HT_KVM,
412
      ]))
413
    self.assertEqual(node2.hv_state[constants.HT_KVM].cpu_node, 1)
414
    self.assertEqual(node2.hv_state[constants.HT_XEN_HVM].cpu_total, 64)
415

    
416
  def testDiskState(self):
417
    node = objects.Node(name="node32087.example.com", disk_state={
418
      constants.DT_PLAIN: {
419
        "lv32352": objects.NodeDiskState(total=128),
420
        "lv2082": objects.NodeDiskState(total=512),
421
        },
422
      })
423

    
424
    node2 = objects.Node.FromDict(node.ToDict())
425

    
426
    # Make sure nothing can reference it anymore
427
    del node
428

    
429
    self.assertEqual(node2.name, "node32087.example.com")
430
    self.assertEqual(frozenset(node2.disk_state), frozenset([
431
      constants.DT_PLAIN,
432
      ]))
433
    self.assertEqual(frozenset(node2.disk_state[constants.DT_PLAIN]),
434
                     frozenset(["lv32352", "lv2082"]))
435
    self.assertEqual(node2.disk_state[constants.DT_PLAIN]["lv2082"].total, 512)
436
    self.assertEqual(node2.disk_state[constants.DT_PLAIN]["lv32352"].total, 128)
437

    
438
  def testFilterEsNdp(self):
439
    node1 = objects.Node(name="node11673.example.com", ndparams={
440
      constants.ND_EXCLUSIVE_STORAGE: True,
441
      })
442
    node2 = objects.Node(name="node11674.example.com", ndparams={
443
      constants.ND_SPINDLE_COUNT: 3,
444
      constants.ND_EXCLUSIVE_STORAGE: False,
445
      })
446
    self.assertTrue(constants.ND_EXCLUSIVE_STORAGE in node1.ndparams)
447
    node1.UpgradeConfig()
448
    self.assertFalse(constants.ND_EXCLUSIVE_STORAGE in node1.ndparams)
449
    self.assertTrue(constants.ND_EXCLUSIVE_STORAGE in node2.ndparams)
450
    self.assertTrue(constants.ND_SPINDLE_COUNT in node2.ndparams)
451
    node2.UpgradeConfig()
452
    self.assertFalse(constants.ND_EXCLUSIVE_STORAGE in node2.ndparams)
453
    self.assertTrue(constants.ND_SPINDLE_COUNT in node2.ndparams)
454

    
455

    
456
class TestInstancePolicy(unittest.TestCase):
457
  def setUp(self):
458
    # Policies are big, and we want to see the difference in case of an error
459
    self.maxDiff = None
460

    
461
  def _AssertIPolicyIsFull(self, policy):
462
    self.assertEqual(frozenset(policy.keys()), constants.IPOLICY_ALL_KEYS)
463
    self.assertTrue(len(policy[constants.ISPECS_MINMAX]) > 0)
464
    for minmax in policy[constants.ISPECS_MINMAX]:
465
      self.assertEqual(frozenset(minmax.keys()), constants.ISPECS_MINMAX_KEYS)
466
      for key in constants.ISPECS_MINMAX_KEYS:
467
        self.assertEqual(frozenset(minmax[key].keys()),
468
                         constants.ISPECS_PARAMETERS)
469
    self.assertEqual(frozenset(policy[constants.ISPECS_STD].keys()),
470
                     constants.ISPECS_PARAMETERS)
471

    
472
  def testDefaultIPolicy(self):
473
    objects.InstancePolicy.CheckParameterSyntax(constants.IPOLICY_DEFAULTS,
474
                                                True)
475
    self._AssertIPolicyIsFull(constants.IPOLICY_DEFAULTS)
476

    
477
  def _AssertPolicyIsBad(self, ipolicy, do_check_std=None):
478
    if do_check_std is None:
479
      check_std_vals = [False, True]
480
    else:
481
      check_std_vals = [do_check_std]
482
    for check_std in check_std_vals:
483
      self.assertRaises(errors.ConfigurationError,
484
                        objects.InstancePolicy.CheckISpecSyntax,
485
                        ipolicy, check_std)
486

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

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

    
575
    bad_ipolicy = copy.deepcopy(good_ipolicy)
576
    for minmax in bad_ipolicy[constants.ISPECS_MINMAX]:
577
      for (key, spec) in minmax.items():
578
        for param in spec:
579
          oldv = spec[param]
580
          del spec[param]
581
          self._AssertPolicyIsBad(bad_ipolicy)
582
          if key == constants.ISPECS_MIN:
583
            spec[param] = minmax[constants.ISPECS_MAX][param] + 1
584
          self._AssertPolicyIsBad(bad_ipolicy)
585
          spec[param] = oldv
586
    assert bad_ipolicy == good_ipolicy
587

    
588
    stdspec = bad_ipolicy[constants.ISPECS_STD]
589
    for param in stdspec:
590
      oldv = stdspec[param]
591
      del stdspec[param]
592
      self._AssertPolicyIsBad(bad_ipolicy, True)
593
      # Note that std spec is the same as a max spec
594
      stdspec[param] = oldv + 1
595
      self._AssertPolicyIsBad(bad_ipolicy, True)
596
      stdspec[param] = oldv
597
    assert bad_ipolicy == good_ipolicy
598

    
599
    for minmax in good_ipolicy[constants.ISPECS_MINMAX]:
600
      for spec in minmax.values():
601
        good_ipolicy[constants.ISPECS_STD] = spec
602
        objects.InstancePolicy.CheckISpecSyntax(good_ipolicy, True)
603

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

    
657
  def testCheckDiskTemplates(self):
658
    invalid = "this_is_not_a_good_template"
659
    for dt in constants.DISK_TEMPLATES:
660
      objects.InstancePolicy.CheckDiskTemplates([dt])
661
    objects.InstancePolicy.CheckDiskTemplates(list(constants.DISK_TEMPLATES))
662
    bad_examples = [
663
      [invalid],
664
      [constants.DT_DRBD8, invalid],
665
      list(constants.DISK_TEMPLATES) + [invalid],
666
      [],
667
      None,
668
      ]
669
    for dtl in bad_examples:
670
      self.assertRaises(errors.ConfigurationError,
671
                        objects.InstancePolicy.CheckDiskTemplates,
672
                        dtl)
673

    
674
  def testCheckParameterSyntax(self):
675
    invalid = "this_key_shouldnt_be_here"
676
    for check_std in [True, False]:
677
      objects.InstancePolicy.CheckParameterSyntax({}, check_std)
678
      policy = {invalid: None}
679
      self.assertRaises(errors.ConfigurationError,
680
                        objects.InstancePolicy.CheckParameterSyntax,
681
                        policy, check_std)
682
      for par in constants.IPOLICY_PARAMETERS:
683
        for val in ("blah", None, {}, [42]):
684
          policy = {par: val}
685
          self.assertRaises(errors.ConfigurationError,
686
                            objects.InstancePolicy.CheckParameterSyntax,
687
                            policy, check_std)
688

    
689
  def testFillIPolicyEmpty(self):
690
    policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, {})
691
    objects.InstancePolicy.CheckParameterSyntax(policy, True)
692
    self.assertEqual(policy, constants.IPOLICY_DEFAULTS)
693

    
694
  def _AssertISpecsMerged(self, default_spec, diff_spec, merged_spec):
695
    for (param, value) in merged_spec.items():
696
      if param in diff_spec:
697
        self.assertEqual(value, diff_spec[param])
698
      else:
699
        self.assertEqual(value, default_spec[param])
700

    
701
  def _AssertIPolicyMerged(self, default_pol, diff_pol, merged_pol):
702
    for (key, value) in merged_pol.items():
703
      if key in diff_pol:
704
        if key == constants.ISPECS_STD:
705
          self._AssertISpecsMerged(default_pol[key], diff_pol[key], value)
706
        else:
707
          self.assertEqual(value, diff_pol[key])
708
      else:
709
        self.assertEqual(value, default_pol[key])
710

    
711
  def testFillIPolicy(self):
712
    partial_policies = [
713
      {constants.IPOLICY_VCPU_RATIO: 3.14},
714
      {constants.IPOLICY_SPINDLE_RATIO: 2.72},
715
      {constants.IPOLICY_DTS: [constants.DT_FILE]},
716
      {constants.ISPECS_STD: {constants.ISPEC_DISK_COUNT: 3}},
717
      {constants.ISPECS_MINMAX: [constants.ISPECS_MINMAX_DEFAULTS,
718
                                 constants.ISPECS_MINMAX_DEFAULTS]}
719
      ]
720
    for diff_pol in partial_policies:
721
      policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, diff_pol)
722
      objects.InstancePolicy.CheckParameterSyntax(policy, True)
723
      self._AssertIPolicyIsFull(policy)
724
      self._AssertIPolicyMerged(constants.IPOLICY_DEFAULTS, diff_pol, policy)
725

    
726
  def testFillIPolicyKeepsUnknown(self):
727
    INVALID_KEY = "invalid_ipolicy_key"
728
    diff_pol = {
729
      INVALID_KEY: None,
730
      }
731
    policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, diff_pol)
732
    self.assertTrue(INVALID_KEY in policy)
733

    
734

    
735
class TestDisk(unittest.TestCase):
736
  def addChild(self, disk):
737
    """Adds a child of the same device type as the parent."""
738
    disk.children = []
739
    child = objects.Disk()
740
    child.dev_type = disk.dev_type
741
    disk.children.append(child)
742

    
743
  def testUpgradeConfigDevTypeLegacy(self):
744
    for old, new in [("drbd8", constants.DT_DRBD8),
745
                     ("lvm", constants.DT_PLAIN)]:
746
      disk = objects.Disk()
747
      disk.dev_type = old
748
      self.addChild(disk)
749
      disk.UpgradeConfig()
750
      self.assertEqual(new, disk.dev_type)
751
      self.assertEqual(new, disk.children[0].dev_type)
752

    
753
  def testUpgradeConfigDevTypeLegacyUnchanged(self):
754
    dev_types = [constants.DT_FILE, constants.DT_SHARED_FILE,
755
                 constants.DT_BLOCK, constants.DT_EXT,
756
                 constants.DT_RBD, constants.DT_GLUSTER]
757
    for dev_type in dev_types:
758
      disk = objects.Disk()
759
      disk.dev_type = dev_type
760
      self.addChild(disk)
761
      disk.UpgradeConfig()
762
      self.assertEqual(dev_type, disk.dev_type)
763
      self.assertEqual(dev_type, disk.children[0].dev_type)
764

    
765

    
766
if __name__ == "__main__":
767
  testutils.GanetiTestProgram()