Unit tests for objects.FillIPolicy() + small fix
[ganeti-local] / test / py / ganeti.objects_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2006, 2007, 2008, 2010, 2012 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 unittest
26
27 from ganeti import constants
28 from ganeti import objects
29 from ganeti import errors
30
31 import testutils
32
33
34 class SimpleObject(objects.ConfigObject):
35   __slots__ = ["a", "b"]
36
37
38 class TestDictState(unittest.TestCase):
39   """Simple dict tansformation tests"""
40
41   def testSimpleObjectToDict(self):
42     o1 = SimpleObject(a="1")
43     self.assertEquals(o1.ToDict(), {"a": "1"})
44     self.assertEquals(o1.__getstate__(), {"a": "1"})
45     self.assertEquals(o1.__getstate__(), o1.ToDict())
46     o1.a = 2
47     o1.b = 5
48     self.assertEquals(o1.ToDict(), {"a": 2, "b": 5})
49     o2 = SimpleObject.FromDict(o1.ToDict())
50     self.assertEquals(o1.ToDict(), {"a": 2, "b": 5})
51
52
53 class TestClusterObject(unittest.TestCase):
54   """Tests done on a L{objects.Cluster}"""
55
56   def setUp(self):
57     hvparams = {
58       constants.HT_FAKE: {
59         "foo": "bar",
60         "bar": "foo",
61         "foobar": "barfoo",
62         },
63       }
64     os_hvp = {
65       "lenny-image": {
66         constants.HT_FAKE: {
67           "foo": "baz",
68           "foobar": "foobar",
69           "blah": "blibb",
70           "blubb": "blah",
71           },
72         constants.HT_XEN_PVM: {
73           "root_path": "/dev/sda5",
74           "foo": "foobar",
75           },
76         },
77       "ubuntu-hardy": {
78         },
79       }
80     ndparams = {
81         constants.ND_OOB_PROGRAM: "/bin/cluster-oob",
82         constants.ND_SPINDLE_COUNT: 1,
83         constants.ND_EXCLUSIVE_STORAGE: False,
84         }
85
86     self.fake_cl = objects.Cluster(hvparams=hvparams, os_hvp=os_hvp,
87                                    ndparams=ndparams)
88     self.fake_cl.UpgradeConfig()
89
90   def testGetHVDefaults(self):
91     cl = self.fake_cl
92     self.failUnlessEqual(cl.GetHVDefaults(constants.HT_FAKE),
93                          cl.hvparams[constants.HT_FAKE])
94     self.failUnlessEqual(cl.GetHVDefaults(None), {})
95     self.failUnlessEqual(cl.GetHVDefaults(constants.HT_XEN_PVM,
96                                           os_name="lenny-image"),
97                          cl.os_hvp["lenny-image"][constants.HT_XEN_PVM])
98
99
100   def testFillHvFullMerge(self):
101     inst_hvparams = {
102       "blah": "blubb",
103       }
104
105     fake_dict = {
106       "foo": "baz",
107       "bar": "foo",
108       "foobar": "foobar",
109       "blah": "blubb",
110       "blubb": "blah",
111       }
112     fake_inst = objects.Instance(name="foobar",
113                                  os="lenny-image",
114                                  hypervisor=constants.HT_FAKE,
115                                  hvparams=inst_hvparams)
116     self.assertEqual(fake_dict, self.fake_cl.FillHV(fake_inst))
117
118   def testFillHvGlobalParams(self):
119     fake_inst = objects.Instance(name="foobar",
120                                  os="ubuntu-hardy",
121                                  hypervisor=constants.HT_FAKE,
122                                  hvparams={})
123     self.assertEqual(self.fake_cl.hvparams[constants.HT_FAKE],
124                      self.fake_cl.FillHV(fake_inst))
125
126   def testFillHvInstParams(self):
127     inst_hvparams = {
128       "blah": "blubb",
129       }
130     fake_inst = objects.Instance(name="foobar",
131                                  os="ubuntu-hardy",
132                                  hypervisor=constants.HT_XEN_PVM,
133                                  hvparams=inst_hvparams)
134     self.assertEqual(inst_hvparams, self.fake_cl.FillHV(fake_inst))
135
136   def testFillHvEmptyParams(self):
137     fake_inst = objects.Instance(name="foobar",
138                                  os="ubuntu-hardy",
139                                  hypervisor=constants.HT_XEN_PVM,
140                                  hvparams={})
141     self.assertEqual({}, self.fake_cl.FillHV(fake_inst))
142
143   def testFillHvPartialParams(self):
144     os = "lenny-image"
145     fake_inst = objects.Instance(name="foobar",
146                                  os=os,
147                                  hypervisor=constants.HT_XEN_PVM,
148                                  hvparams={})
149     self.assertEqual(self.fake_cl.os_hvp[os][constants.HT_XEN_PVM],
150                      self.fake_cl.FillHV(fake_inst))
151
152   def testFillNdParamsCluster(self):
153     fake_node = objects.Node(name="test",
154                              ndparams={},
155                              group="testgroup")
156     fake_group = objects.NodeGroup(name="testgroup",
157                                    ndparams={})
158     self.assertEqual(self.fake_cl.ndparams,
159                      self.fake_cl.FillND(fake_node, fake_group))
160
161   def testFillNdParamsNodeGroup(self):
162     fake_node = objects.Node(name="test",
163                              ndparams={},
164                              group="testgroup")
165     group_ndparams = {
166         constants.ND_OOB_PROGRAM: "/bin/group-oob",
167         constants.ND_SPINDLE_COUNT: 10,
168         constants.ND_EXCLUSIVE_STORAGE: True,
169         }
170     fake_group = objects.NodeGroup(name="testgroup",
171                                    ndparams=group_ndparams)
172     self.assertEqual(group_ndparams,
173                      self.fake_cl.FillND(fake_node, fake_group))
174
175   def testFillNdParamsNode(self):
176     node_ndparams = {
177         constants.ND_OOB_PROGRAM: "/bin/node-oob",
178         constants.ND_SPINDLE_COUNT: 2,
179         constants.ND_EXCLUSIVE_STORAGE: True,
180         }
181     fake_node = objects.Node(name="test",
182                              ndparams=node_ndparams,
183                              group="testgroup")
184     fake_group = objects.NodeGroup(name="testgroup",
185                                    ndparams={})
186     self.assertEqual(node_ndparams,
187                      self.fake_cl.FillND(fake_node, fake_group))
188
189   def testFillNdParamsAll(self):
190     node_ndparams = {
191         constants.ND_OOB_PROGRAM: "/bin/node-oob",
192         constants.ND_SPINDLE_COUNT: 5,
193         constants.ND_EXCLUSIVE_STORAGE: True,
194         }
195     fake_node = objects.Node(name="test",
196                              ndparams=node_ndparams,
197                              group="testgroup")
198     group_ndparams = {
199         constants.ND_OOB_PROGRAM: "/bin/group-oob",
200         constants.ND_SPINDLE_COUNT: 4,
201         }
202     fake_group = objects.NodeGroup(name="testgroup",
203                                    ndparams=group_ndparams)
204     self.assertEqual(node_ndparams,
205                      self.fake_cl.FillND(fake_node, fake_group))
206
207   def testPrimaryHypervisor(self):
208     assert self.fake_cl.enabled_hypervisors is None
209     self.fake_cl.enabled_hypervisors = [constants.HT_XEN_HVM]
210     self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_XEN_HVM)
211
212     self.fake_cl.enabled_hypervisors = [constants.HT_XEN_PVM, constants.HT_KVM]
213     self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_XEN_PVM)
214
215     self.fake_cl.enabled_hypervisors = sorted(constants.HYPER_TYPES)
216     self.assertEqual(self.fake_cl.primary_hypervisor, constants.HT_CHROOT)
217
218   def testUpgradeConfig(self):
219     # FIXME: This test is incomplete
220     cluster = objects.Cluster()
221     cluster.UpgradeConfig()
222     cluster = objects.Cluster(ipolicy={"unknown_key": None})
223     self.assertRaises(errors.ConfigurationError, cluster.UpgradeConfig)
224
225
226 class TestOS(unittest.TestCase):
227   ALL_DATA = [
228     "debootstrap",
229     "debootstrap+default",
230     "debootstrap++default",
231     ]
232
233   def testSplitNameVariant(self):
234     for name in self.ALL_DATA:
235       self.assertEqual(len(objects.OS.SplitNameVariant(name)), 2)
236
237   def testVariant(self):
238     self.assertEqual(objects.OS.GetVariant("debootstrap"), "")
239     self.assertEqual(objects.OS.GetVariant("debootstrap+default"), "default")
240
241
242 class TestInstance(unittest.TestCase):
243   def _GenericCheck(self, inst):
244     for i in [inst.all_nodes, inst.secondary_nodes]:
245       self.assertTrue(isinstance(inst.all_nodes, (list, tuple)),
246                       msg="Data type doesn't guarantee order")
247
248     self.assertTrue(inst.primary_node not in inst.secondary_nodes)
249     self.assertEqual(inst.all_nodes[0], inst.primary_node,
250                      msg="Primary node not first node in list")
251
252   def testNodesNoDisks(self):
253     inst = objects.Instance(name="fakeinst.example.com",
254       primary_node="pnode.example.com",
255       disks=[
256         ])
257
258     self._GenericCheck(inst)
259     self.assertEqual(len(inst.secondary_nodes), 0)
260     self.assertEqual(set(inst.all_nodes), set([inst.primary_node]))
261     self.assertEqual(inst.MapLVsByNode(), {
262       inst.primary_node: [],
263       })
264
265   def testNodesPlainDisks(self):
266     inst = objects.Instance(name="fakeinstplain.example.com",
267       primary_node="node3.example.com",
268       disks=[
269         objects.Disk(dev_type=constants.LD_LV, size=128,
270                      logical_id=("myxenvg", "disk25494")),
271         objects.Disk(dev_type=constants.LD_LV, size=512,
272                      logical_id=("myxenvg", "disk29071")),
273         ])
274
275     self._GenericCheck(inst)
276     self.assertEqual(len(inst.secondary_nodes), 0)
277     self.assertEqual(set(inst.all_nodes), set([inst.primary_node]))
278     self.assertEqual(inst.MapLVsByNode(), {
279       inst.primary_node: ["myxenvg/disk25494", "myxenvg/disk29071"],
280       })
281
282   def testNodesDrbdDisks(self):
283     inst = objects.Instance(name="fakeinstdrbd.example.com",
284       primary_node="node10.example.com",
285       disks=[
286         objects.Disk(dev_type=constants.LD_DRBD8, size=786432,
287           logical_id=("node10.example.com", "node15.example.com",
288                       12300, 0, 0, "secret"),
289           children=[
290             objects.Disk(dev_type=constants.LD_LV, size=786432,
291                          logical_id=("myxenvg", "disk0")),
292             objects.Disk(dev_type=constants.LD_LV, size=128,
293                          logical_id=("myxenvg", "meta0"))
294           ],
295           iv_name="disk/0")
296         ])
297
298     self._GenericCheck(inst)
299     self.assertEqual(set(inst.secondary_nodes), set(["node15.example.com"]))
300     self.assertEqual(set(inst.all_nodes),
301                      set([inst.primary_node, "node15.example.com"]))
302     self.assertEqual(inst.MapLVsByNode(), {
303       inst.primary_node: ["myxenvg/disk0", "myxenvg/meta0"],
304       "node15.example.com": ["myxenvg/disk0", "myxenvg/meta0"],
305       })
306
307     self.assertEqual(inst.FindDisk(0), inst.disks[0])
308     self.assertRaises(errors.OpPrereqError, inst.FindDisk, "hello")
309     self.assertRaises(errors.OpPrereqError, inst.FindDisk, 100)
310     self.assertRaises(errors.OpPrereqError, inst.FindDisk, 1)
311
312
313 class TestNode(unittest.TestCase):
314   def testEmpty(self):
315     self.assertEqual(objects.Node().ToDict(), {})
316     self.assertTrue(isinstance(objects.Node.FromDict({}), objects.Node))
317
318   def testHvState(self):
319     node = objects.Node(name="node18157.example.com", hv_state={
320       constants.HT_XEN_HVM: objects.NodeHvState(cpu_total=64),
321       constants.HT_KVM: objects.NodeHvState(cpu_node=1),
322       })
323
324     node2 = objects.Node.FromDict(node.ToDict())
325
326     # Make sure nothing can reference it anymore
327     del node
328
329     self.assertEqual(node2.name, "node18157.example.com")
330     self.assertEqual(frozenset(node2.hv_state), frozenset([
331       constants.HT_XEN_HVM,
332       constants.HT_KVM,
333       ]))
334     self.assertEqual(node2.hv_state[constants.HT_KVM].cpu_node, 1)
335     self.assertEqual(node2.hv_state[constants.HT_XEN_HVM].cpu_total, 64)
336
337   def testDiskState(self):
338     node = objects.Node(name="node32087.example.com", disk_state={
339       constants.LD_LV: {
340         "lv32352": objects.NodeDiskState(total=128),
341         "lv2082": objects.NodeDiskState(total=512),
342         },
343       })
344
345     node2 = objects.Node.FromDict(node.ToDict())
346
347     # Make sure nothing can reference it anymore
348     del node
349
350     self.assertEqual(node2.name, "node32087.example.com")
351     self.assertEqual(frozenset(node2.disk_state), frozenset([
352       constants.LD_LV,
353       ]))
354     self.assertEqual(frozenset(node2.disk_state[constants.LD_LV]), frozenset([
355       "lv32352",
356       "lv2082",
357       ]))
358     self.assertEqual(node2.disk_state[constants.LD_LV]["lv2082"].total, 512)
359     self.assertEqual(node2.disk_state[constants.LD_LV]["lv32352"].total, 128)
360
361   def testFilterEsNdp(self):
362     node1 = objects.Node(name="node11673.example.com", ndparams={
363       constants.ND_EXCLUSIVE_STORAGE: True,
364       })
365     node2 = objects.Node(name="node11674.example.com", ndparams={
366       constants.ND_SPINDLE_COUNT: 3,
367       constants.ND_EXCLUSIVE_STORAGE: False,
368       })
369     self.assertTrue(constants.ND_EXCLUSIVE_STORAGE in node1.ndparams)
370     node1.UpgradeConfig()
371     self.assertFalse(constants.ND_EXCLUSIVE_STORAGE in node1.ndparams)
372     self.assertTrue(constants.ND_EXCLUSIVE_STORAGE in node2.ndparams)
373     self.assertTrue(constants.ND_SPINDLE_COUNT in node2.ndparams)
374     node2.UpgradeConfig()
375     self.assertFalse(constants.ND_EXCLUSIVE_STORAGE in node2.ndparams)
376     self.assertTrue(constants.ND_SPINDLE_COUNT in node2.ndparams)
377
378
379 class TestInstancePolicy(unittest.TestCase):
380   def setUp(self):
381     # Policies are big, and we want to see the difference in case of an error
382     self.maxDiff = None
383
384   def _AssertIPolicyIsFull(self, policy):
385     self.assertEqual(frozenset(policy.keys()), constants.IPOLICY_ALL_KEYS)
386     for key in constants.IPOLICY_ISPECS:
387       spec = policy[key]
388       self.assertEqual(frozenset(spec.keys()), constants.ISPECS_PARAMETERS)
389
390   def testDefaultIPolicy(self):
391     objects.InstancePolicy.CheckParameterSyntax(constants.IPOLICY_DEFAULTS,
392                                                 True)
393     self._AssertIPolicyIsFull(constants.IPOLICY_DEFAULTS)
394
395   def testFillIPolicyEmpty(self):
396     policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, {})
397     objects.InstancePolicy.CheckParameterSyntax(policy, True)
398     self.assertEqual(policy, constants.IPOLICY_DEFAULTS)
399
400   def _AssertISpecsMerged(self, default_spec, diff_spec, merged_spec):
401     for (param, value) in merged_spec.items():
402       if param in diff_spec:
403         self.assertEqual(value, diff_spec[param])
404       else:
405         self.assertEqual(value, default_spec[param])
406
407   def _AssertIPolicyMerged(self, default_pol, diff_pol, merged_pol):
408     for (key, value) in merged_pol.items():
409       if key in diff_pol:
410         if key in constants.IPOLICY_ISPECS:
411           self._AssertISpecsMerged(default_pol[key], diff_pol[key], value)
412         else:
413           self.assertEqual(value, diff_pol[key])
414       else:
415         self.assertEqual(value, default_pol[key])
416
417   def testFillIPolicy(self):
418     partial_policies = [
419       {constants.IPOLICY_VCPU_RATIO: 3.14},
420       {constants.IPOLICY_SPINDLE_RATIO: 2.72},
421       {constants.IPOLICY_DTS: []},
422       {constants.IPOLICY_DTS: [constants.DT_FILE]},
423       ]
424     for diff_pol in partial_policies:
425       policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, diff_pol)
426       objects.InstancePolicy.CheckParameterSyntax(policy, True)
427       self._AssertIPolicyIsFull(policy)
428       self._AssertIPolicyMerged(constants.IPOLICY_DEFAULTS, diff_pol, policy)
429
430   def testFillIPolicySpecs(self):
431     partial_policies = [
432       {constants.ISPECS_MIN: {constants.ISPEC_MEM_SIZE: 32},
433        constants.ISPECS_MAX: {constants.ISPEC_CPU_COUNT: 1024}},
434       {constants.ISPECS_STD: {constants.ISPEC_DISK_SIZE: 2048},
435        constants.ISPECS_MAX: {
436           constants.ISPEC_DISK_COUNT: constants.MAX_DISKS - 1,
437           constants.ISPEC_NIC_COUNT: constants.MAX_NICS - 1,
438           }},
439       {constants.ISPECS_STD: {constants.ISPEC_SPINDLE_USE: 3}},
440       ]
441     for diff_pol in partial_policies:
442       policy = objects.FillIPolicy(constants.IPOLICY_DEFAULTS, diff_pol)
443       objects.InstancePolicy.CheckParameterSyntax(policy, True)
444       self._AssertIPolicyIsFull(policy)
445       self._AssertIPolicyMerged(constants.IPOLICY_DEFAULTS, diff_pol, policy)
446
447
448 if __name__ == "__main__":
449   testutils.GanetiTestProgram()