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