Merge branch 'stable-2.8' into stable-2.9
[ganeti-local] / test / py / ganeti.objects_unittest.py
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         }
171     fake_group = objects.NodeGroup(name="testgroup",
172                                    ndparams=group_ndparams)
173     self.assertEqual(group_ndparams,
174                      self.fake_cl.FillND(fake_node, fake_group))
175
176   def testFillNdParamsNode(self):
177     node_ndparams = {
178         constants.ND_OOB_PROGRAM: "/bin/node-oob",
179         constants.ND_SPINDLE_COUNT: 2,
180         constants.ND_EXCLUSIVE_STORAGE: True,
181         }
182     fake_node = objects.Node(name="test",
183                              ndparams=node_ndparams,
184                              group="testgroup")
185     fake_group = objects.NodeGroup(name="testgroup",
186                                    ndparams={})
187     self.assertEqual(node_ndparams,
188                      self.fake_cl.FillND(fake_node, fake_group))
189
190   def testFillNdParamsAll(self):
191     node_ndparams = {
192         constants.ND_OOB_PROGRAM: "/bin/node-oob",
193         constants.ND_SPINDLE_COUNT: 5,
194         constants.ND_EXCLUSIVE_STORAGE: True,
195         }
196     fake_node = objects.Node(name="test",
197                              ndparams=node_ndparams,
198                              group="testgroup")
199     group_ndparams = {
200         constants.ND_OOB_PROGRAM: "/bin/group-oob",
201         constants.ND_SPINDLE_COUNT: 4,
202         }
203     fake_group = objects.NodeGroup(name="testgroup",
204                                    ndparams=group_ndparams)
205     self.assertEqual(node_ndparams,
206                      self.fake_cl.FillND(fake_node, fake_group))
207
208   def testPrimaryHypervisor(self):
209     assert self.fake_cl.enabled_hypervisors is None
210     self.fake_cl.enabled_hypervisors = [constants.HT_XEN_HVM]
211     self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_XEN_HVM)
212
213     self.fake_cl.enabled_hypervisors = [constants.HT_XEN_PVM, constants.HT_KVM]
214     self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_XEN_PVM)
215
216     self.fake_cl.enabled_hypervisors = sorted(constants.HYPER_TYPES)
217     self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_CHROOT)
218
219   def testUpgradeConfig(self):
220     # FIXME: This test is incomplete
221     cluster = objects.Cluster()
222     cluster.UpgradeConfig()
223     cluster = objects.Cluster(ipolicy={"unknown_key": None})
224     self.assertRaises(errors.ConfigurationError, cluster.UpgradeConfig)
225
226   def testUpgradeEnabledDiskTemplates(self):
227     cfg = objects.ConfigData()
228     cfg.cluster = objects.Cluster()
229     cfg.cluster.volume_group_name = "myvg"
230     instance1 = objects.Instance()
231     instance1.disk_template = constants.DT_DISKLESS
232     instance2 = objects.Instance()
233     instance2.disk_template = constants.DT_RBD
234     cfg.instances = { "myinstance1": instance1, "myinstance2": instance2 }
235     nodegroup = objects.NodeGroup()
236     nodegroup.ipolicy = {}
237     nodegroup.ipolicy[constants.IPOLICY_DTS] = [instance1.disk_template, \
238       constants.DT_BLOCK]
239     cfg.cluster.ipolicy = {}
240     cfg.cluster.ipolicy[constants.IPOLICY_DTS] = \
241       [constants.DT_EXT, constants.DT_DISKLESS]
242     cfg.nodegroups = { "mynodegroup": nodegroup }
243     cfg._UpgradeEnabledDiskTemplates()
244     expected_disk_templates = [constants.DT_DRBD8,
245                                constants.DT_PLAIN,
246                                instance1.disk_template,
247                                instance2.disk_template]
248     self.assertEqual(set(expected_disk_templates),
249                      set(cfg.cluster.enabled_disk_templates))
250     self.assertEqual(set([instance1.disk_template]),
251                      set(cfg.cluster.ipolicy[constants.IPOLICY_DTS]))
252
253
254 class TestClusterObjectTcpUdpPortPool(unittest.TestCase):
255   def testNewCluster(self):
256     self.assertTrue(objects.Cluster().tcpudp_port_pool is None)
257
258   def testSerializingEmpty(self):
259     self.assertEqual(objects.Cluster().ToDict(), {
260       "tcpudp_port_pool": [],
261       })
262
263   def testSerializing(self):
264     cluster = objects.Cluster.FromDict({})
265     self.assertEqual(cluster.tcpudp_port_pool, set())
266
267     cluster.tcpudp_port_pool.add(3546)
268     cluster.tcpudp_port_pool.add(62511)
269
270     data = cluster.ToDict()
271     self.assertEqual(data.keys(), ["tcpudp_port_pool"])
272     self.assertEqual(sorted(data["tcpudp_port_pool"]), sorted([3546, 62511]))
273
274   def testDeserializingEmpty(self):
275     cluster = objects.Cluster.FromDict({})
276     self.assertEqual(cluster.tcpudp_port_pool, set())
277
278   def testDeserialize(self):
279     cluster = objects.Cluster.FromDict({
280       "tcpudp_port_pool": [26214, 10039, 267],
281       })
282     self.assertEqual(cluster.tcpudp_port_pool, set([26214, 10039, 267]))
283
284
285 class TestOS(unittest.TestCase):
286   ALL_DATA = [
287     "debootstrap",
288     "debootstrap+default",
289     "debootstrap++default",
290     ]
291
292   def testSplitNameVariant(self):
293     for name in self.ALL_DATA:
294       self.assertEqual(len(objects.OS.SplitNameVariant(name)), 2)
295
296   def testVariant(self):
297     self.assertEqual(objects.OS.GetVariant("debootstrap"), "")
298     self.assertEqual(objects.OS.GetVariant("debootstrap+default"), "default")
299
300
301 class TestInstance(unittest.TestCase):
302   def _GenericCheck(self, inst):
303     for i in [inst.all_nodes, inst.secondary_nodes]:
304       self.assertTrue(isinstance(inst.all_nodes, (list, tuple)),
305                       msg="Data type doesn't guarantee order")
306
307     self.assertTrue(inst.primary_node not in inst.secondary_nodes)
308     self.assertEqual(inst.all_nodes[0], inst.primary_node,
309                      msg="Primary node not first node in list")
310
311   def testNodesNoDisks(self):
312     inst = objects.Instance(name="fakeinst.example.com",
313       primary_node="pnode.example.com",
314       disks=[
315         ])
316
317     self._GenericCheck(inst)
318     self.assertEqual(len(inst.secondary_nodes), 0)
319     self.assertEqual(set(inst.all_nodes), set([inst.primary_node]))
320     self.assertEqual(inst.MapLVsByNode(), {
321       inst.primary_node: [],
322       })
323
324   def testNodesPlainDisks(self):
325     inst = objects.Instance(name="fakeinstplain.example.com",
326       primary_node="node3.example.com",
327       disks=[
328         objects.Disk(dev_type=constants.DT_PLAIN, size=128,
329                      logical_id=("myxenvg", "disk25494")),
330         objects.Disk(dev_type=constants.DT_PLAIN, size=512,
331                      logical_id=("myxenvg", "disk29071")),
332         ])
333
334     self._GenericCheck(inst)
335     self.assertEqual(len(inst.secondary_nodes), 0)
336     self.assertEqual(set(inst.all_nodes), set([inst.primary_node]))
337     self.assertEqual(inst.MapLVsByNode(), {
338       inst.primary_node: ["myxenvg/disk25494", "myxenvg/disk29071"],
339       })
340
341   def testNodesDrbdDisks(self):
342     inst = objects.Instance(name="fakeinstdrbd.example.com",
343       primary_node="node10.example.com",
344       disks=[
345         objects.Disk(dev_type=constants.DT_DRBD8, size=786432,
346           logical_id=("node10.example.com", "node15.example.com",
347                       12300, 0, 0, "secret"),
348           children=[
349             objects.Disk(dev_type=constants.DT_PLAIN, size=786432,
350                          logical_id=("myxenvg", "disk0")),
351             objects.Disk(dev_type=constants.DT_PLAIN, size=128,
352                          logical_id=("myxenvg", "meta0"))
353           ],
354           iv_name="disk/0")
355         ])
356
357     self._GenericCheck(inst)
358     self.assertEqual(set(inst.secondary_nodes), set(["node15.example.com"]))
359     self.assertEqual(set(inst.all_nodes),
360                      set([inst.primary_node, "node15.example.com"]))
361     self.assertEqual(inst.MapLVsByNode(), {
362       inst.primary_node: ["myxenvg/disk0", "myxenvg/meta0"],
363       "node15.example.com": ["myxenvg/disk0", "myxenvg/meta0"],
364       })
365
366     self.assertEqual(inst.FindDisk(0), inst.disks[0])
367     self.assertRaises(errors.OpPrereqError, inst.FindDisk, "hello")
368     self.assertRaises(errors.OpPrereqError, inst.FindDisk, 100)
369     self.assertRaises(errors.OpPrereqError, inst.FindDisk, 1)
370
371
372 class TestNode(unittest.TestCase):
373   def testEmpty(self):
374     self.assertEqual(objects.Node().ToDict(), {})
375     self.assertTrue(isinstance(objects.Node.FromDict({}), objects.Node))
376
377   def testHvState(self):
378     node = objects.Node(name="node18157.example.com", hv_state={
379       constants.HT_XEN_HVM: objects.NodeHvState(cpu_total=64),
380       constants.HT_KVM: objects.NodeHvState(cpu_node=1),
381       })
382
383     node2 = objects.Node.FromDict(node.ToDict())
384
385     # Make sure nothing can reference it anymore
386     del node
387
388     self.assertEqual(node2.name, "node18157.example.com")
389     self.assertEqual(frozenset(node2.hv_state), frozenset([
390       constants.HT_XEN_HVM,
391       constants.HT_KVM,
392       ]))
393     self.assertEqual(node2.hv_state[constants.HT_KVM].cpu_node, 1)
394     self.assertEqual(node2.hv_state[constants.HT_XEN_HVM].cpu_total, 64)
395
396   def testDiskState(self):
397     node = objects.Node(name="node32087.example.com", disk_state={
398       constants.DT_PLAIN: {
399         "lv32352": objects.NodeDiskState(total=128),
400         "lv2082": objects.NodeDiskState(total=512),
401         },
402       })
403
404     node2 = objects.Node.FromDict(node.ToDict())
405
406     # Make sure nothing can reference it anymore
407     del node
408
409     self.assertEqual(node2.name, "node32087.example.com")
410     self.assertEqual(frozenset(node2.disk_state), frozenset([
411       constants.DT_PLAIN,
412       ]))
413     self.assertEqual(frozenset(node2.disk_state[constants.DT_PLAIN]),
414                      frozenset(["lv32352", "lv2082"]))
415     self.assertEqual(node2.disk_state[constants.DT_PLAIN]["lv2082"].total, 512)
416     self.assertEqual(node2.disk_state[constants.DT_PLAIN]["lv32352"].total, 128)
417
418   def testFilterEsNdp(self):
419     node1 = objects.Node(name="node11673.example.com", ndparams={
420       constants.ND_EXCLUSIVE_STORAGE: True,
421       })
422     node2 = objects.Node(name="node11674.example.com", ndparams={
423       constants.ND_SPINDLE_COUNT: 3,
424       constants.ND_EXCLUSIVE_STORAGE: False,
425       })
426     self.assertTrue(constants.ND_EXCLUSIVE_STORAGE in node1.ndparams)
427     node1.UpgradeConfig()
428     self.assertFalse(constants.ND_EXCLUSIVE_STORAGE in node1.ndparams)
429     self.assertTrue(constants.ND_EXCLUSIVE_STORAGE in node2.ndparams)
430     self.assertTrue(constants.ND_SPINDLE_COUNT in node2.ndparams)
431     node2.UpgradeConfig()
432     self.assertFalse(constants.ND_EXCLUSIVE_STORAGE in node2.ndparams)
433     self.assertTrue(constants.ND_SPINDLE_COUNT in node2.ndparams)
434
435
436 class TestInstancePolicy(unittest.TestCase):
437   def setUp(self):
438     # Policies are big, and we want to see the difference in case of an error
439     self.maxDiff = None
440
441   def _AssertIPolicyIsFull(self, policy):
442     self.assertEqual(frozenset(policy.keys()), constants.IPOLICY_ALL_KEYS)
443     self.assertTrue(len(policy[constants.ISPECS_MINMAX]) > 0)
444     for minmax in policy[constants.ISPECS_MINMAX]:
445       self.assertEqual(frozenset(minmax.keys()), constants.ISPECS_MINMAX_KEYS)
446       for key in constants.ISPECS_MINMAX_KEYS:
447         self.assertEqual(frozenset(minmax[key].keys()),
448                          constants.ISPECS_PARAMETERS)
449     self.assertEqual(frozenset(policy[constants.ISPECS_STD].keys()),
450                      constants.ISPECS_PARAMETERS)
451
452   def testDefaultIPolicy(self):
453     objects.InstancePolicy.CheckParameterSyntax(constants.IPOLICY_DEFAULTS,
454                                                 True)
455     self._AssertIPolicyIsFull(constants.IPOLICY_DEFAULTS)
456
457   def _AssertPolicyIsBad(self, ipolicy, do_check_std=None):
458     if do_check_std is None:
459       check_std_vals = [False, True]
460     else:
461       check_std_vals = [do_check_std]
462     for check_std in check_std_vals:
463       self.assertRaises(errors.ConfigurationError,
464                         objects.InstancePolicy.CheckISpecSyntax,
465                         ipolicy, check_std)
466
467   def testCheckISpecSyntax(self):
468     default_stdspec = constants.IPOLICY_DEFAULTS[constants.ISPECS_STD]
469     incomplete_ipolicies = [
470       {
471          constants.ISPECS_MINMAX: [],
472          constants.ISPECS_STD: default_stdspec,
473          },
474       {
475          constants.ISPECS_MINMAX: [{}],
476          constants.ISPECS_STD: default_stdspec,
477          },
478       {
479         constants.ISPECS_MINMAX: [{
480           constants.ISPECS_MIN: NotImplemented,
481           }],
482         constants.ISPECS_STD: default_stdspec,
483         },
484       {
485         constants.ISPECS_MINMAX: [{
486           constants.ISPECS_MAX: NotImplemented,
487           }],
488         constants.ISPECS_STD: default_stdspec,
489         },
490       {
491         constants.ISPECS_MINMAX: [{
492           constants.ISPECS_MIN: NotImplemented,
493           constants.ISPECS_MAX: NotImplemented,
494           }],
495         },
496       ]
497     for ipol in incomplete_ipolicies:
498       self.assertRaises(errors.ConfigurationError,
499                         objects.InstancePolicy.CheckISpecSyntax,
500                         ipol, True)
501       oldminmax = ipol[constants.ISPECS_MINMAX]
502       if oldminmax:
503         # Prepending valid specs shouldn't change the error
504         ipol[constants.ISPECS_MINMAX] = ([constants.ISPECS_MINMAX_DEFAULTS] +
505                                          oldminmax)
506         self.assertRaises(errors.ConfigurationError,
507                           objects.InstancePolicy.CheckISpecSyntax,
508                           ipol, True)
509
510     good_ipolicy = {
511       constants.ISPECS_MINMAX: [
512         {
513           constants.ISPECS_MIN: {
514             constants.ISPEC_MEM_SIZE: 64,
515             constants.ISPEC_CPU_COUNT: 1,
516             constants.ISPEC_DISK_COUNT: 2,
517             constants.ISPEC_DISK_SIZE: 64,
518             constants.ISPEC_NIC_COUNT: 1,
519             constants.ISPEC_SPINDLE_USE: 1,
520             },
521           constants.ISPECS_MAX: {
522             constants.ISPEC_MEM_SIZE: 16384,
523             constants.ISPEC_CPU_COUNT: 5,
524             constants.ISPEC_DISK_COUNT: 12,
525             constants.ISPEC_DISK_SIZE: 1024,
526             constants.ISPEC_NIC_COUNT: 9,
527             constants.ISPEC_SPINDLE_USE: 18,
528             },
529           },
530         {
531           constants.ISPECS_MIN: {
532             constants.ISPEC_MEM_SIZE: 32768,
533             constants.ISPEC_CPU_COUNT: 8,
534             constants.ISPEC_DISK_COUNT: 1,
535             constants.ISPEC_DISK_SIZE: 1024,
536             constants.ISPEC_NIC_COUNT: 1,
537             constants.ISPEC_SPINDLE_USE: 1,
538             },
539           constants.ISPECS_MAX: {
540             constants.ISPEC_MEM_SIZE: 65536,
541             constants.ISPEC_CPU_COUNT: 10,
542             constants.ISPEC_DISK_COUNT: 5,
543             constants.ISPEC_DISK_SIZE: 1024 * 1024,
544             constants.ISPEC_NIC_COUNT: 3,
545             constants.ISPEC_SPINDLE_USE: 12,
546             },
547           },
548         ],
549       }
550     good_ipolicy[constants.ISPECS_STD] = copy.deepcopy(
551       good_ipolicy[constants.ISPECS_MINMAX][0][constants.ISPECS_MAX])
552     # Check that it's really good before making it bad
553     objects.InstancePolicy.CheckISpecSyntax(good_ipolicy, True)
554
555     bad_ipolicy = copy.deepcopy(good_ipolicy)
556     for minmax in bad_ipolicy[constants.ISPECS_MINMAX]:
557       for (key, spec) in minmax.items():
558         for param in spec:
559           oldv = spec[param]
560           del spec[param]
561           self._AssertPolicyIsBad(bad_ipolicy)
562           if key == constants.ISPECS_MIN:
563             spec[param] = minmax[constants.ISPECS_MAX][param] + 1
564           self._AssertPolicyIsBad(bad_ipolicy)
565           spec[param] = oldv
566     assert bad_ipolicy == good_ipolicy
567
568     stdspec = bad_ipolicy[constants.ISPECS_STD]
569     for param in stdspec:
570       oldv = stdspec[param]
571       del stdspec[param]
572       self._AssertPolicyIsBad(bad_ipolicy, True)
573       # Note that std spec is the same as a max spec
574       stdspec[param] = oldv + 1
575       self._AssertPolicyIsBad(bad_ipolicy, True)
576       stdspec[param] = oldv
577     assert bad_ipolicy == good_ipolicy
578
579     for minmax in good_ipolicy[constants.ISPECS_MINMAX]:
580       for spec in minmax.values():
581         good_ipolicy[constants.ISPECS_STD] = spec
582         objects.InstancePolicy.CheckISpecSyntax(good_ipolicy, True)
583
584   def testCheckISpecParamSyntax(self):
585     par = "my_parameter"
586     for check_std in [True, False]:
587       # Min and max only
588       good_values = [(11, 11), (11, 40), (0, 0)]
589       for (mn, mx) in good_values:
590         minmax = dict((k, {}) for k in constants.ISPECS_MINMAX_KEYS)
591         minmax[constants.ISPECS_MIN][par] = mn
592         minmax[constants.ISPECS_MAX][par] = mx
593         objects.InstancePolicy._CheckISpecParamSyntax(minmax, {}, par,
594                                                      check_std)
595       minmax = dict((k, {}) for k in constants.ISPECS_MINMAX_KEYS)
596       minmax[constants.ISPECS_MIN][par] = 11
597       minmax[constants.ISPECS_MAX][par] = 5
598       self.assertRaises(errors.ConfigurationError,
599                         objects.InstancePolicy._CheckISpecParamSyntax,
600                         minmax, {}, par, check_std)
601     # Min, std, max
602     good_values = [
603       (11, 11, 11),
604       (11, 11, 40),
605       (11, 40, 40),
606       ]
607     for (mn, st, mx) in good_values:
608       minmax = {
609         constants.ISPECS_MIN: {par: mn},
610         constants.ISPECS_MAX: {par: mx},
611         }
612       stdspec = {par: st}
613       objects.InstancePolicy._CheckISpecParamSyntax(minmax, stdspec, par, True)
614     bad_values = [
615       (11, 11,  5, True),
616       (40, 11, 11, True),
617       (11, 80, 40, False),
618       (11,  5, 40, False,),
619       (11,  5,  5, True),
620       (40, 40, 11, True),
621       ]
622     for (mn, st, mx, excp) in bad_values:
623       minmax = {
624         constants.ISPECS_MIN: {par: mn},
625         constants.ISPECS_MAX: {par: mx},
626         }
627       stdspec = {par: st}
628       if excp:
629         self.assertRaises(errors.ConfigurationError,
630                           objects.InstancePolicy._CheckISpecParamSyntax,
631                           minmax, stdspec, par, True)
632       else:
633         ret = objects.InstancePolicy._CheckISpecParamSyntax(minmax, stdspec,
634                                                             par, True)
635         self.assertFalse(ret)
636
637   def testCheckDiskTemplates(self):
638     invalid = "this_is_not_a_good_template"
639     for dt in constants.DISK_TEMPLATES:
640       objects.InstancePolicy.CheckDiskTemplates([dt])
641     objects.InstancePolicy.CheckDiskTemplates(list(constants.DISK_TEMPLATES))
642     bad_examples = [
643       [invalid],
644       [constants.DT_DRBD8, invalid],
645       list(constants.DISK_TEMPLATES) + [invalid],
646       [],
647       None,
648       ]
649     for dtl in bad_examples:
650       self.assertRaises(errors.ConfigurationError,
651                         objects.InstancePolicy.CheckDiskTemplates,
652                         dtl)
653
654   def testCheckParameterSyntax(self):
655     invalid = "this_key_shouldnt_be_here"
656     for check_std in [True, False]:
657       objects.InstancePolicy.CheckParameterSyntax({}, check_std)
658       policy = {invalid: None}
659       self.assertRaises(errors.ConfigurationError,
660                         objects.InstancePolicy.CheckParameterSyntax,
661                         policy, check_std)
662       for par in constants.IPOLICY_PARAMETERS:
663         for val in ("blah", None, {}, [42]):
664           policy = {par: val}
665           self.assertRaises(errors.ConfigurationError,
666                             objects.InstancePolicy.CheckParameterSyntax,
667                             policy, check_std)
668
669   def testFillIPolicyEmpty(self):
670     policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, {})
671     objects.InstancePolicy.CheckParameterSyntax(policy, True)
672     self.assertEqual(policy, constants.IPOLICY_DEFAULTS)
673
674   def _AssertISpecsMerged(self, default_spec, diff_spec, merged_spec):
675     for (param, value) in merged_spec.items():
676       if param in diff_spec:
677         self.assertEqual(value, diff_spec[param])
678       else:
679         self.assertEqual(value, default_spec[param])
680
681   def _AssertIPolicyMerged(self, default_pol, diff_pol, merged_pol):
682     for (key, value) in merged_pol.items():
683       if key in diff_pol:
684         if key == constants.ISPECS_STD:
685           self._AssertISpecsMerged(default_pol[key], diff_pol[key], value)
686         else:
687           self.assertEqual(value, diff_pol[key])
688       else:
689         self.assertEqual(value, default_pol[key])
690
691   def testFillIPolicy(self):
692     partial_policies = [
693       {constants.IPOLICY_VCPU_RATIO: 3.14},
694       {constants.IPOLICY_SPINDLE_RATIO: 2.72},
695       {constants.IPOLICY_DTS: [constants.DT_FILE]},
696       {constants.ISPECS_STD: {constants.ISPEC_DISK_COUNT: 3}},
697       {constants.ISPECS_MINMAX: [constants.ISPECS_MINMAX_DEFAULTS,
698                                  constants.ISPECS_MINMAX_DEFAULTS]}
699       ]
700     for diff_pol in partial_policies:
701       policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, diff_pol)
702       objects.InstancePolicy.CheckParameterSyntax(policy, True)
703       self._AssertIPolicyIsFull(policy)
704       self._AssertIPolicyMerged(constants.IPOLICY_DEFAULTS, diff_pol, policy)
705
706   def testFillIPolicyKeepsUnknown(self):
707     INVALID_KEY = "invalid_ipolicy_key"
708     diff_pol = {
709       INVALID_KEY: None,
710       }
711     policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, diff_pol)
712     self.assertTrue(INVALID_KEY in policy)
713
714
715 class TestDisk(unittest.TestCase):
716   def addChild(self, disk):
717     """Adds a child of the same device type as the parent."""
718     disk.children = []
719     child = objects.Disk()
720     child.dev_type = disk.dev_type
721     disk.children.append(child)
722
723   def testUpgradeConfigDevTypeLegacy(self):
724     for old, new in [("drbd8", constants.DT_DRBD8),
725                      ("lvm", constants.DT_PLAIN)]:
726       disk = objects.Disk()
727       disk.dev_type = old
728       self.addChild(disk)
729       disk.UpgradeConfig()
730       self.assertEqual(new, disk.dev_type)
731       self.assertEqual(new, disk.children[0].dev_type)
732
733   def testUpgradeConfigDevTypeLegacyUnchanged(self):
734     dev_types = [constants.DT_FILE, constants.DT_SHARED_FILE,
735                  constants.DT_BLOCK, constants.DT_EXT,
736                  constants.DT_RBD]
737     for dev_type in dev_types:
738       disk = objects.Disk()
739       disk.dev_type = dev_type
740       self.addChild(disk)
741       disk.UpgradeConfig()
742       self.assertEqual(dev_type, disk.dev_type)
743       self.assertEqual(dev_type, disk.children[0].dev_type)
744
745
746 if __name__ == "__main__":
747   testutils.GanetiTestProgram()