Move python test files to test/py
[ganeti-local] / test / py / ganeti.config_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2006, 2007, 2010, 2011, 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 config module"""
23
24
25 import unittest
26 import os
27 import time
28 import tempfile
29 import os.path
30 import socket
31 import operator
32 import itertools
33
34 from ganeti import bootstrap
35 from ganeti import config
36 from ganeti import constants
37 from ganeti import errors
38 from ganeti import objects
39 from ganeti import utils
40 from ganeti import netutils
41 from ganeti import compat
42 from ganeti import cmdlib
43
44 from ganeti.config import TemporaryReservationManager
45
46 import testutils
47 import mocks
48
49
50 def _StubGetEntResolver():
51   return mocks.FakeGetentResolver()
52
53
54 class TestConfigRunner(unittest.TestCase):
55   """Testing case for HooksRunner"""
56   def setUp(self):
57     fd, self.cfg_file = tempfile.mkstemp()
58     os.close(fd)
59     self._init_cluster(self.cfg_file)
60
61   def tearDown(self):
62     try:
63       os.unlink(self.cfg_file)
64     except OSError:
65       pass
66
67   def _get_object(self):
68     """Returns a instance of ConfigWriter"""
69     cfg = config.ConfigWriter(cfg_file=self.cfg_file, offline=True,
70                               _getents=_StubGetEntResolver)
71     return cfg
72
73   def _init_cluster(self, cfg):
74     """Initializes the cfg object"""
75     me = netutils.Hostname()
76     ip = constants.IP4_ADDRESS_LOCALHOST
77
78     cluster_config = objects.Cluster(
79       serial_no=1,
80       rsahostkeypub="",
81       highest_used_port=(constants.FIRST_DRBD_PORT - 1),
82       mac_prefix="aa:00:00",
83       volume_group_name="xenvg",
84       drbd_usermode_helper="/bin/true",
85       nicparams={constants.PP_DEFAULT: constants.NICC_DEFAULTS},
86       ndparams=constants.NDC_DEFAULTS,
87       tcpudp_port_pool=set(),
88       enabled_hypervisors=[constants.HT_FAKE],
89       master_node=me.name,
90       master_ip="127.0.0.1",
91       master_netdev=constants.DEFAULT_BRIDGE,
92       cluster_name="cluster.local",
93       file_storage_dir="/tmp",
94       uid_pool=[],
95       )
96
97     master_node_config = objects.Node(name=me.name,
98                                       primary_ip=me.ip,
99                                       secondary_ip=ip,
100                                       serial_no=1,
101                                       master_candidate=True)
102
103     bootstrap.InitConfig(constants.CONFIG_VERSION,
104                          cluster_config, master_node_config, self.cfg_file)
105
106   def _create_instance(self):
107     """Create and return an instance object"""
108     inst = objects.Instance(name="test.example.com", disks=[], nics=[],
109                             disk_template=constants.DT_DISKLESS,
110                             primary_node=self._get_object().GetMasterNode())
111     return inst
112
113   def testEmpty(self):
114     """Test instantiate config object"""
115     self._get_object()
116
117   def testInit(self):
118     """Test initialize the config file"""
119     cfg = self._get_object()
120     self.failUnlessEqual(1, len(cfg.GetNodeList()))
121     self.failUnlessEqual(0, len(cfg.GetInstanceList()))
122
123   def testUpdateCluster(self):
124     """Test updates on the cluster object"""
125     cfg = self._get_object()
126     # construct a fake cluster object
127     fake_cl = objects.Cluster()
128     # fail if we didn't read the config
129     self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_cl, None)
130
131     cl = cfg.GetClusterInfo()
132     # first pass, must not fail
133     cfg.Update(cl, None)
134     # second pass, also must not fail (after the config has been written)
135     cfg.Update(cl, None)
136     # but the fake_cl update should still fail
137     self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_cl, None)
138
139   def testUpdateNode(self):
140     """Test updates on one node object"""
141     cfg = self._get_object()
142     # construct a fake node
143     fake_node = objects.Node()
144     # fail if we didn't read the config
145     self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_node,
146                           None)
147
148     node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
149     # first pass, must not fail
150     cfg.Update(node, None)
151     # second pass, also must not fail (after the config has been written)
152     cfg.Update(node, None)
153     # but the fake_node update should still fail
154     self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_node,
155                           None)
156
157   def testUpdateInstance(self):
158     """Test updates on one instance object"""
159     cfg = self._get_object()
160     # construct a fake instance
161     inst = self._create_instance()
162     fake_instance = objects.Instance()
163     # fail if we didn't read the config
164     self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_instance,
165                           None)
166
167     cfg.AddInstance(inst, "my-job")
168     instance = cfg.GetInstanceInfo(cfg.GetInstanceList()[0])
169     # first pass, must not fail
170     cfg.Update(instance, None)
171     # second pass, also must not fail (after the config has been written)
172     cfg.Update(instance, None)
173     # but the fake_instance update should still fail
174     self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_instance,
175                           None)
176
177   def testNICParameterSyntaxCheck(self):
178     """Test the NIC's CheckParameterSyntax function"""
179     mode = constants.NIC_MODE
180     link = constants.NIC_LINK
181     m_bridged = constants.NIC_MODE_BRIDGED
182     m_routed = constants.NIC_MODE_ROUTED
183     CheckSyntax = objects.NIC.CheckParameterSyntax
184
185     CheckSyntax(constants.NICC_DEFAULTS)
186     CheckSyntax({mode: m_bridged, link: "br1"})
187     CheckSyntax({mode: m_routed, link: "default"})
188     self.assertRaises(errors.ConfigurationError,
189                       CheckSyntax, {mode: "000invalid", link: "any"})
190     self.assertRaises(errors.ConfigurationError,
191                       CheckSyntax, {mode: m_bridged, link: None})
192     self.assertRaises(errors.ConfigurationError,
193                       CheckSyntax, {mode: m_bridged, link: ""})
194
195   def testGetNdParamsDefault(self):
196     cfg = self._get_object()
197     node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
198     self.assertEqual(cfg.GetNdParams(node), constants.NDC_DEFAULTS)
199
200   def testGetNdParamsModifiedNode(self):
201     my_ndparams = {
202         constants.ND_OOB_PROGRAM: "/bin/node-oob",
203         constants.ND_SPINDLE_COUNT: 1,
204         constants.ND_EXCLUSIVE_STORAGE: False,
205         }
206
207     cfg = self._get_object()
208     node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
209     node.ndparams = my_ndparams
210     cfg.Update(node, None)
211     self.assertEqual(cfg.GetNdParams(node), my_ndparams)
212
213   def testGetNdParamsInheritance(self):
214     node_ndparams = {
215       constants.ND_OOB_PROGRAM: "/bin/node-oob",
216       }
217     group_ndparams = {
218       constants.ND_SPINDLE_COUNT: 10,
219       }
220     expected_ndparams = {
221       constants.ND_OOB_PROGRAM: "/bin/node-oob",
222       constants.ND_SPINDLE_COUNT: 10,
223       constants.ND_EXCLUSIVE_STORAGE:
224         constants.NDC_DEFAULTS[constants.ND_EXCLUSIVE_STORAGE],
225       }
226     cfg = self._get_object()
227     node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
228     node.ndparams = node_ndparams
229     cfg.Update(node, None)
230     group = cfg.GetNodeGroup(node.group)
231     group.ndparams = group_ndparams
232     cfg.Update(group, None)
233     self.assertEqual(cfg.GetNdParams(node), expected_ndparams)
234
235   def testAddGroupFillsFieldsIfMissing(self):
236     cfg = self._get_object()
237     group = objects.NodeGroup(name="test", members=[])
238     cfg.AddNodeGroup(group, "my-job")
239     self.assert_(utils.UUID_RE.match(group.uuid))
240     self.assertEqual(constants.ALLOC_POLICY_PREFERRED, group.alloc_policy)
241
242   def testAddGroupPreservesFields(self):
243     cfg = self._get_object()
244     group = objects.NodeGroup(name="test", members=[],
245                               alloc_policy=constants.ALLOC_POLICY_LAST_RESORT)
246     cfg.AddNodeGroup(group, "my-job")
247     self.assertEqual(constants.ALLOC_POLICY_LAST_RESORT, group.alloc_policy)
248
249   def testAddGroupDoesNotPreserveFields(self):
250     cfg = self._get_object()
251     group = objects.NodeGroup(name="test", members=[],
252                               serial_no=17, ctime=123, mtime=456)
253     cfg.AddNodeGroup(group, "my-job")
254     self.assertEqual(1, group.serial_no)
255     self.assert_(group.ctime > 1200000000)
256     self.assert_(group.mtime > 1200000000)
257
258   def testAddGroupCanSkipUUIDCheck(self):
259     cfg = self._get_object()
260     uuid = cfg.GenerateUniqueID("my-job")
261     group = objects.NodeGroup(name="test", members=[], uuid=uuid,
262                               serial_no=17, ctime=123, mtime=456)
263
264     self.assertRaises(errors.ConfigurationError,
265                       cfg.AddNodeGroup, group, "my-job")
266
267     cfg.AddNodeGroup(group, "my-job", check_uuid=False) # Does not raise.
268     self.assertEqual(uuid, group.uuid)
269
270   def testAssignGroupNodes(self):
271     me = netutils.Hostname()
272     cfg = self._get_object()
273
274     # Create two groups
275     grp1 = objects.NodeGroup(name="grp1", members=[],
276                              uuid="2f2fadf7-2a70-4a23-9ab5-2568c252032c")
277     grp1_serial = 1
278     cfg.AddNodeGroup(grp1, "job")
279
280     grp2 = objects.NodeGroup(name="grp2", members=[],
281                              uuid="798d0de3-680f-4a0e-b29a-0f54f693b3f1")
282     grp2_serial = 1
283     cfg.AddNodeGroup(grp2, "job")
284     self.assertEqual(set(map(operator.attrgetter("name"),
285                              cfg.GetAllNodeGroupsInfo().values())),
286                      set(["grp1", "grp2", constants.INITIAL_NODE_GROUP_NAME]))
287
288     # No-op
289     cluster_serial = cfg.GetClusterInfo().serial_no
290     cfg.AssignGroupNodes([])
291     cluster_serial += 1
292
293     # Create two nodes
294     node1 = objects.Node(name="node1", group=grp1.uuid, ndparams={})
295     node1_serial = 1
296     node2 = objects.Node(name="node2", group=grp2.uuid, ndparams={})
297     node2_serial = 1
298     cfg.AddNode(node1, "job")
299     cfg.AddNode(node2, "job")
300     cluster_serial += 2
301     self.assertEqual(set(cfg.GetNodeList()), set(["node1", "node2", me.name]))
302
303     def _VerifySerials():
304       self.assertEqual(cfg.GetClusterInfo().serial_no, cluster_serial)
305       self.assertEqual(node1.serial_no, node1_serial)
306       self.assertEqual(node2.serial_no, node2_serial)
307       self.assertEqual(grp1.serial_no, grp1_serial)
308       self.assertEqual(grp2.serial_no, grp2_serial)
309
310     _VerifySerials()
311
312     self.assertEqual(set(grp1.members), set(["node1"]))
313     self.assertEqual(set(grp2.members), set(["node2"]))
314
315     # Check invalid nodes and groups
316     self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
317       ("unknown.node.example.com", grp2.uuid),
318       ])
319     self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
320       (node1.name, "unknown-uuid"),
321       ])
322
323     self.assertEqual(node1.group, grp1.uuid)
324     self.assertEqual(node2.group, grp2.uuid)
325     self.assertEqual(set(grp1.members), set(["node1"]))
326     self.assertEqual(set(grp2.members), set(["node2"]))
327
328     # Another no-op
329     cfg.AssignGroupNodes([])
330     cluster_serial += 1
331     _VerifySerials()
332
333     # Assign to the same group (should be a no-op)
334     self.assertEqual(node2.group, grp2.uuid)
335     cfg.AssignGroupNodes([
336       (node2.name, grp2.uuid),
337       ])
338     cluster_serial += 1
339     self.assertEqual(node2.group, grp2.uuid)
340     _VerifySerials()
341     self.assertEqual(set(grp1.members), set(["node1"]))
342     self.assertEqual(set(grp2.members), set(["node2"]))
343
344     # Assign node 2 to group 1
345     self.assertEqual(node2.group, grp2.uuid)
346     cfg.AssignGroupNodes([
347       (node2.name, grp1.uuid),
348       ])
349     cluster_serial += 1
350     node2_serial += 1
351     grp1_serial += 1
352     grp2_serial += 1
353     self.assertEqual(node2.group, grp1.uuid)
354     _VerifySerials()
355     self.assertEqual(set(grp1.members), set(["node1", "node2"]))
356     self.assertFalse(grp2.members)
357
358     # And assign both nodes to group 2
359     self.assertEqual(node1.group, grp1.uuid)
360     self.assertEqual(node2.group, grp1.uuid)
361     self.assertNotEqual(grp1.uuid, grp2.uuid)
362     cfg.AssignGroupNodes([
363       (node1.name, grp2.uuid),
364       (node2.name, grp2.uuid),
365       ])
366     cluster_serial += 1
367     node1_serial += 1
368     node2_serial += 1
369     grp1_serial += 1
370     grp2_serial += 1
371     self.assertEqual(node1.group, grp2.uuid)
372     self.assertEqual(node2.group, grp2.uuid)
373     _VerifySerials()
374     self.assertFalse(grp1.members)
375     self.assertEqual(set(grp2.members), set(["node1", "node2"]))
376
377     # Destructive tests
378     orig_group = node2.group
379     try:
380       other_uuid = "68b3d087-6ea5-491c-b81f-0a47d90228c5"
381       assert compat.all(node.group != other_uuid
382                         for node in cfg.GetAllNodesInfo().values())
383       node2.group = "68b3d087-6ea5-491c-b81f-0a47d90228c5"
384       self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
385         ("node2", grp2.uuid),
386         ])
387       _VerifySerials()
388     finally:
389       node2.group = orig_group
390
391
392 class TestTRM(unittest.TestCase):
393   EC_ID = 1
394
395   def testEmpty(self):
396     t = TemporaryReservationManager()
397     t.Reserve(self.EC_ID, "a")
398     self.assertFalse(t.Reserved(self.EC_ID))
399     self.assertTrue(t.Reserved("a"))
400     self.assertEqual(len(t.GetReserved()), 1)
401
402   def testDuplicate(self):
403     t = TemporaryReservationManager()
404     t.Reserve(self.EC_ID, "a")
405     self.assertRaises(errors.ReservationError, t.Reserve, 2, "a")
406     t.DropECReservations(self.EC_ID)
407     self.assertFalse(t.Reserved("a"))
408
409
410 class TestCheckInstanceDiskIvNames(unittest.TestCase):
411   @staticmethod
412   def _MakeDisks(names):
413     return [objects.Disk(iv_name=name) for name in names]
414
415   def testNoError(self):
416     disks = self._MakeDisks(["disk/0", "disk/1"])
417     self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
418     cmdlib._UpdateIvNames(0, disks)
419     self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
420
421   def testWrongNames(self):
422     disks = self._MakeDisks(["disk/1", "disk/3", "disk/2"])
423     self.assertEqual(config._CheckInstanceDiskIvNames(disks), [
424       (0, "disk/0", "disk/1"),
425       (1, "disk/1", "disk/3"),
426       ])
427
428     # Fix names
429     cmdlib._UpdateIvNames(0, disks)
430     self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
431
432
433 if __name__ == "__main__":
434   testutils.GanetiTestProgram()