Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (31.9 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 pprint
27
import unittest
28

    
29
from ganeti import constants
30
from ganeti import objects
31
from ganeti import errors
32
from ganeti import serializer
33

    
34
import testutils
35

    
36

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

    
40

    
41
class TestDictState(unittest.TestCase):
42
  """Simple dict tansformation tests"""
43

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

    
55

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

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

    
89
    self.fake_cl = objects.Cluster(hvparams=hvparams, os_hvp=os_hvp,
90
                                   ndparams=ndparams)
91
    self.fake_cl.UpgradeConfig()
92

    
93
  def testGetHVDefaults(self):
94
    cl = self.fake_cl
95
    self.failUnlessEqual(cl.GetHVDefaults(constants.HT_FAKE),
96
                         cl.hvparams[constants.HT_FAKE])
97
    self.failUnlessEqual(cl.GetHVDefaults(None), {})
98
    defaults = cl.GetHVDefaults(constants.HT_XEN_PVM,
99
                                          os_name="lenny-image")
100
    for param, value in cl.os_hvp["lenny-image"][constants.HT_XEN_PVM].items():
101
      self.assertEqual(value, defaults[param])
102

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

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

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

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

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

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

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

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

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

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

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

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

    
238
    self.fake_cl.enabled_hypervisors = sorted(constants.HYPER_TYPES)
239
    self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_CHROOT)
240

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

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

    
275

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

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

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

    
289
    cluster.tcpudp_port_pool.add(3546)
290
    cluster.tcpudp_port_pool.add(62511)
291

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

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

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

    
306

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

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

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

    
322

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

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

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

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

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

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

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

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

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

    
393

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

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

    
405
    node2 = objects.Node.FromDict(node.ToDict())
406

    
407
    # Make sure nothing can reference it anymore
408
    del node
409

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

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

    
426
    node2 = objects.Node.FromDict(node.ToDict())
427

    
428
    # Make sure nothing can reference it anymore
429
    del node
430

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

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

    
457

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
736

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

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

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

    
767

    
768
class TestSimpleFillOS(unittest.TestCase):
769
    # We have to make sure that:
770
    #  * From within the configuration, variants override defaults
771
    #  * Temporary values override configuration
772
    #  * No overlap between public, private and secret dicts is allowed
773
    #
774
    # As a result, here are the actors in this test:
775
    #
776
    # A:  temporary                 public
777
    # B:  temporary                        private
778
    # C:  temporary                                secret
779
    # X:  temporary                 public private secret
780
    # D:            configuration   public                       variant
781
    # E:            configuration   public                  base
782
    # F:            configuration          private               variant
783
    # G:            configuration          private          base
784
    #
785
    # Every time a param is assigned "ERROR", we expect FillOSParams to override
786
    # it. If it doesn't, it's an error.
787
    #
788
    # Every time a param is assigned itself as a value, it's the value we expect
789
    # FillOSParams to give us back.
790

    
791
    def setUp(self):
792
      self.fake_cl = objects.Cluster()
793
      self.fake_cl.UpgradeConfig()
794
      self.fake_cl.osparams = {"os": {"A": "ERROR",
795
                                      "D": "ERROR",
796
                                      "E": "E"},
797
                               "os+a": {"D": "D"}}
798
      self.fake_cl.osparams_private_cluster = {"os": {"B": "ERROR",
799
                                                      "F": "ERROR",
800
                                                      "G": "G"},
801
                                               "os+a": {"F": "F"}}
802

    
803
    def testConflictPublicPrivate(self):
804
      "Make sure we disallow attempts to override params based on visibility."
805
      public_dict = {"A": "A", "X": "X"}
806
      private_dict = {"B": "B", "X": "X"}
807
      secret_dict = {"C": "C"}
808
      dicts_pp = (public_dict, private_dict)
809
      dicts_pps = (public_dict, private_dict, secret_dict)
810

    
811
      # Without secret parameters
812
      self.assertRaises(errors.OpPrereqError,
813
                        lambda: self.fake_cl.SimpleFillOS("os+a", *dicts_pp))
814

    
815
      # but also with those.
816
      self.assertRaises(errors.OpPrereqError,
817
                        lambda: self.fake_cl.SimpleFillOS("os+a", *dicts_pps))
818

    
819
    def testConflictPublicSecret(self):
820
      "Make sure we disallow attempts to override params based on visibility."
821
      public_dict = {"A": "A", "X": "X"}
822
      private_dict = {"B": "B"}
823
      secret_dict = {"C": "C", "X": "X"}
824
      dicts_pps = (public_dict, private_dict, secret_dict)
825

    
826
      self.assertRaises(errors.OpPrereqError,
827
                        lambda: self.fake_cl.SimpleFillOS("os+a", *dicts_pps))
828

    
829
    def testConflictPrivateSecret(self):
830
      "Make sure we disallow attempts to override params based on visibility."
831
      public_dict = {"A": "A"}
832
      private_dict = {"B": "B", "X": "X"}
833
      secret_dict = {"C": "C", "X": "X"}
834
      dicts_pps = (public_dict, private_dict, secret_dict)
835

    
836
      self.assertRaises(errors.OpPrereqError,
837
                        lambda: self.fake_cl.SimpleFillOS("os+a", *dicts_pps))
838

    
839
    def testValidValues(self):
840
      "Make sure we handle all overriding we do allow correctly."
841
      public_dict = {"A": "A"}
842
      private_dict = {"B": "B"}
843
      secret_dict = {"C": "C"}
844
      dicts_p = (public_dict,)
845
      dicts_pp = (public_dict, private_dict)
846
      dicts_pps = (public_dict, private_dict, secret_dict)
847
      expected_keys_p = ("A", "D", "E") # nothing private, secret
848
      expected_keys_pp = ("A", "B", "D", "E", "F", "G") # nothing secret
849
      expected_keys_pps = ("A", "B", "C", "D", "E", "F", "G") # all of them
850

    
851
      for (dicts, expected_keys) in [(dicts_p, expected_keys_p),
852
                                     (dicts_pp, expected_keys_pp),
853
                                     (dicts_pps, expected_keys_pps)]:
854
        result = self.fake_cl.SimpleFillOS("os+a", *dicts)
855
        # Values
856
        for key in result:
857
          if not result[key] == key:
858
            self.fail("Invalid public-private fill with input:\n%s\n%s"
859
                      % (pprint.pformat(dicts), result))
860
        # Completeness
861
        if set(result) != set(expected_keys):
862
          self.fail("Problem with key %s from merge result of:\n%s\n%s"
863
                    % (set(expected_keys) ^ set(result), # symmetric difference
864
                       pprint.pformat(dicts),
865
                       result))
866

    
867
if __name__ == "__main__":
868
  testutils.GanetiTestProgram()