4 # Copyright (C) 2006, 2007, 2010, 2011, 2012 Google Inc.
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.
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.
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
22 """Script for unittesting the config module"""
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
44 from ganeti.config import TemporaryReservationManager
50 def _StubGetEntResolver():
51 return mocks.FakeGetentResolver()
54 class TestConfigRunner(unittest.TestCase):
55 """Testing case for HooksRunner"""
57 fd, self.cfg_file = tempfile.mkstemp()
59 self._init_cluster(self.cfg_file)
63 os.unlink(self.cfg_file)
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)
73 def _init_cluster(self, cfg):
74 """Initializes the cfg object"""
75 me = netutils.Hostname()
76 ip = constants.IP4_ADDRESS_LOCALHOST
78 cluster_config = objects.Cluster(
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],
90 master_ip="127.0.0.1",
91 master_netdev=constants.DEFAULT_BRIDGE,
92 cluster_name="cluster.local",
93 file_storage_dir="/tmp",
97 master_node_config = objects.Node(name=me.name,
101 master_candidate=True)
103 bootstrap.InitConfig(constants.CONFIG_VERSION,
104 cluster_config, master_node_config, self.cfg_file)
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())
114 """Test instantiate config object"""
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()))
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)
131 cl = cfg.GetClusterInfo()
132 # first pass, must not fail
134 # second pass, also must not fail (after the config has been written)
136 # but the fake_cl update should still fail
137 self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_cl, None)
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,
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,
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,
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,
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
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: ''})
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)
200 def testGetNdParamsModifiedNode(self):
202 constants.ND_OOB_PROGRAM: "/bin/node-oob",
203 constants.ND_SPINDLE_COUNT: 1,
206 cfg = self._get_object()
207 node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
208 node.ndparams = my_ndparams
209 cfg.Update(node, None)
210 self.assertEqual(cfg.GetNdParams(node), my_ndparams)
212 def testAddGroupFillsFieldsIfMissing(self):
213 cfg = self._get_object()
214 group = objects.NodeGroup(name="test", members=[])
215 cfg.AddNodeGroup(group, "my-job")
216 self.assert_(utils.UUID_RE.match(group.uuid))
217 self.assertEqual(constants.ALLOC_POLICY_PREFERRED, group.alloc_policy)
219 def testAddGroupPreservesFields(self):
220 cfg = self._get_object()
221 group = objects.NodeGroup(name="test", members=[],
222 alloc_policy=constants.ALLOC_POLICY_LAST_RESORT)
223 cfg.AddNodeGroup(group, "my-job")
224 self.assertEqual(constants.ALLOC_POLICY_LAST_RESORT, group.alloc_policy)
226 def testAddGroupDoesNotPreserveFields(self):
227 cfg = self._get_object()
228 group = objects.NodeGroup(name="test", members=[],
229 serial_no=17, ctime=123, mtime=456)
230 cfg.AddNodeGroup(group, "my-job")
231 self.assertEqual(1, group.serial_no)
232 self.assert_(group.ctime > 1200000000)
233 self.assert_(group.mtime > 1200000000)
235 def testAddGroupCanSkipUUIDCheck(self):
236 cfg = self._get_object()
237 uuid = cfg.GenerateUniqueID("my-job")
238 group = objects.NodeGroup(name="test", members=[], uuid=uuid,
239 serial_no=17, ctime=123, mtime=456)
241 self.assertRaises(errors.ConfigurationError,
242 cfg.AddNodeGroup, group, "my-job")
244 cfg.AddNodeGroup(group, "my-job", check_uuid=False) # Does not raise.
245 self.assertEqual(uuid, group.uuid)
247 def testAssignGroupNodes(self):
248 me = netutils.Hostname()
249 cfg = self._get_object()
252 grp1 = objects.NodeGroup(name="grp1", members=[],
253 uuid="2f2fadf7-2a70-4a23-9ab5-2568c252032c")
255 cfg.AddNodeGroup(grp1, "job")
257 grp2 = objects.NodeGroup(name="grp2", members=[],
258 uuid="798d0de3-680f-4a0e-b29a-0f54f693b3f1")
260 cfg.AddNodeGroup(grp2, "job")
261 self.assertEqual(set(map(operator.attrgetter("name"),
262 cfg.GetAllNodeGroupsInfo().values())),
263 set(["grp1", "grp2", constants.INITIAL_NODE_GROUP_NAME]))
266 cluster_serial = cfg.GetClusterInfo().serial_no
267 cfg.AssignGroupNodes([])
271 node1 = objects.Node(name="node1", group=grp1.uuid, ndparams={})
273 node2 = objects.Node(name="node2", group=grp2.uuid, ndparams={})
275 cfg.AddNode(node1, "job")
276 cfg.AddNode(node2, "job")
278 self.assertEqual(set(cfg.GetNodeList()), set(["node1", "node2", me.name]))
280 def _VerifySerials():
281 self.assertEqual(cfg.GetClusterInfo().serial_no, cluster_serial)
282 self.assertEqual(node1.serial_no, node1_serial)
283 self.assertEqual(node2.serial_no, node2_serial)
284 self.assertEqual(grp1.serial_no, grp1_serial)
285 self.assertEqual(grp2.serial_no, grp2_serial)
289 self.assertEqual(set(grp1.members), set(["node1"]))
290 self.assertEqual(set(grp2.members), set(["node2"]))
292 # Check invalid nodes and groups
293 self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
294 ("unknown.node.example.com", grp2.uuid),
296 self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
297 (node1.name, "unknown-uuid"),
300 self.assertEqual(node1.group, grp1.uuid)
301 self.assertEqual(node2.group, grp2.uuid)
302 self.assertEqual(set(grp1.members), set(["node1"]))
303 self.assertEqual(set(grp2.members), set(["node2"]))
306 cfg.AssignGroupNodes([])
310 # Assign to the same group (should be a no-op)
311 self.assertEqual(node2.group, grp2.uuid)
312 cfg.AssignGroupNodes([
313 (node2.name, grp2.uuid),
316 self.assertEqual(node2.group, grp2.uuid)
318 self.assertEqual(set(grp1.members), set(["node1"]))
319 self.assertEqual(set(grp2.members), set(["node2"]))
321 # Assign node 2 to group 1
322 self.assertEqual(node2.group, grp2.uuid)
323 cfg.AssignGroupNodes([
324 (node2.name, grp1.uuid),
330 self.assertEqual(node2.group, grp1.uuid)
332 self.assertEqual(set(grp1.members), set(["node1", "node2"]))
333 self.assertFalse(grp2.members)
335 # And assign both nodes to group 2
336 self.assertEqual(node1.group, grp1.uuid)
337 self.assertEqual(node2.group, grp1.uuid)
338 self.assertNotEqual(grp1.uuid, grp2.uuid)
339 cfg.AssignGroupNodes([
340 (node1.name, grp2.uuid),
341 (node2.name, grp2.uuid),
348 self.assertEqual(node1.group, grp2.uuid)
349 self.assertEqual(node2.group, grp2.uuid)
351 self.assertFalse(grp1.members)
352 self.assertEqual(set(grp2.members), set(["node1", "node2"]))
355 orig_group = node2.group
357 other_uuid = "68b3d087-6ea5-491c-b81f-0a47d90228c5"
358 assert compat.all(node.group != other_uuid
359 for node in cfg.GetAllNodesInfo().values())
360 node2.group = "68b3d087-6ea5-491c-b81f-0a47d90228c5"
361 self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
362 ("node2", grp2.uuid),
366 node2.group = orig_group
369 class TestTRM(unittest.TestCase):
373 t = TemporaryReservationManager()
374 t.Reserve(self.EC_ID, "a")
375 self.assertFalse(t.Reserved(self.EC_ID))
376 self.assertTrue(t.Reserved("a"))
377 self.assertEqual(len(t.GetReserved()), 1)
379 def testDuplicate(self):
380 t = TemporaryReservationManager()
381 t.Reserve(self.EC_ID, "a")
382 self.assertRaises(errors.ReservationError, t.Reserve, 2, "a")
383 t.DropECReservations(self.EC_ID)
384 self.assertFalse(t.Reserved("a"))
387 class TestCheckInstanceDiskIvNames(unittest.TestCase):
389 def _MakeDisks(names):
390 return [objects.Disk(iv_name=name) for name in names]
392 def testNoError(self):
393 disks = self._MakeDisks(["disk/0", "disk/1"])
394 self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
395 cmdlib._UpdateIvNames(0, disks)
396 self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
398 def testWrongNames(self):
399 disks = self._MakeDisks(["disk/1", "disk/3", "disk/2"])
400 self.assertEqual(config._CheckInstanceDiskIvNames(disks), [
401 (0, "disk/0", "disk/1"),
402 (1, "disk/1", "disk/3"),
406 cmdlib._UpdateIvNames(0, disks)
407 self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
410 if __name__ == '__main__':
411 testutils.GanetiTestProgram()