Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.config_unittest.py @ 45f62156

History | View | Annotate | Download (15.6 kB)

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 testUpgradeSave(self):
178
    """Test that any modification done during upgrading is saved back"""
179
    cfg = self._get_object()
180

    
181
    # Remove an element, run upgrade, and check if the element is
182
    # back and the file upgraded
183
    node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
184
    # For a ConfigObject, None is the same as a missing field
185
    node.ndparams = None
186
    oldsaved = utils.ReadFile(self.cfg_file)
187
    cfg._UpgradeConfig()
188
    self.assertTrue(node.ndparams is not None)
189
    newsaved = utils.ReadFile(self.cfg_file)
190
    # We rely on the fact that at least the serial number changes
191
    self.assertNotEqual(oldsaved, newsaved)
192

    
193
    # Add something that should not be there this time
194
    key = list(constants.NDC_GLOBALS)[0]
195
    node.ndparams[key] = constants.NDC_DEFAULTS[key]
196
    cfg._WriteConfig(None)
197
    oldsaved = utils.ReadFile(self.cfg_file)
198
    cfg._UpgradeConfig()
199
    self.assertTrue(node.ndparams.get(key) is None)
200
    newsaved = utils.ReadFile(self.cfg_file)
201
    self.assertNotEqual(oldsaved, newsaved)
202

    
203
    # Do the upgrade again, this time there should be no update
204
    oldsaved = newsaved
205
    cfg._UpgradeConfig()
206
    newsaved = utils.ReadFile(self.cfg_file)
207
    self.assertEqual(oldsaved, newsaved)
208

    
209
    # Reload the configuration again: it shouldn't change the file
210
    oldsaved = newsaved
211
    self._get_object()
212
    newsaved = utils.ReadFile(self.cfg_file)
213
    self.assertEqual(oldsaved, newsaved)
214

    
215
  def testNICParameterSyntaxCheck(self):
216
    """Test the NIC's CheckParameterSyntax function"""
217
    mode = constants.NIC_MODE
218
    link = constants.NIC_LINK
219
    m_bridged = constants.NIC_MODE_BRIDGED
220
    m_routed = constants.NIC_MODE_ROUTED
221
    CheckSyntax = objects.NIC.CheckParameterSyntax
222

    
223
    CheckSyntax(constants.NICC_DEFAULTS)
224
    CheckSyntax({mode: m_bridged, link: "br1"})
225
    CheckSyntax({mode: m_routed, link: "default"})
226
    self.assertRaises(errors.ConfigurationError,
227
                      CheckSyntax, {mode: "000invalid", link: "any"})
228
    self.assertRaises(errors.ConfigurationError,
229
                      CheckSyntax, {mode: m_bridged, link: None})
230
    self.assertRaises(errors.ConfigurationError,
231
                      CheckSyntax, {mode: m_bridged, link: ""})
232

    
233
  def testGetNdParamsDefault(self):
234
    cfg = self._get_object()
235
    node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
236
    self.assertEqual(cfg.GetNdParams(node), constants.NDC_DEFAULTS)
237

    
238
  def testGetNdParamsModifiedNode(self):
239
    my_ndparams = {
240
        constants.ND_OOB_PROGRAM: "/bin/node-oob",
241
        constants.ND_SPINDLE_COUNT: 1,
242
        constants.ND_EXCLUSIVE_STORAGE: False,
243
        }
244

    
245
    cfg = self._get_object()
246
    node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
247
    node.ndparams = my_ndparams
248
    cfg.Update(node, None)
249
    self.assertEqual(cfg.GetNdParams(node), my_ndparams)
250

    
251
  def testGetNdParamsInheritance(self):
252
    node_ndparams = {
253
      constants.ND_OOB_PROGRAM: "/bin/node-oob",
254
      }
255
    group_ndparams = {
256
      constants.ND_SPINDLE_COUNT: 10,
257
      }
258
    expected_ndparams = {
259
      constants.ND_OOB_PROGRAM: "/bin/node-oob",
260
      constants.ND_SPINDLE_COUNT: 10,
261
      constants.ND_EXCLUSIVE_STORAGE:
262
        constants.NDC_DEFAULTS[constants.ND_EXCLUSIVE_STORAGE],
263
      }
264
    cfg = self._get_object()
265
    node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
266
    node.ndparams = node_ndparams
267
    cfg.Update(node, None)
268
    group = cfg.GetNodeGroup(node.group)
269
    group.ndparams = group_ndparams
270
    cfg.Update(group, None)
271
    self.assertEqual(cfg.GetNdParams(node), expected_ndparams)
272

    
273
  def testAddGroupFillsFieldsIfMissing(self):
274
    cfg = self._get_object()
275
    group = objects.NodeGroup(name="test", members=[])
276
    cfg.AddNodeGroup(group, "my-job")
277
    self.assert_(utils.UUID_RE.match(group.uuid))
278
    self.assertEqual(constants.ALLOC_POLICY_PREFERRED, group.alloc_policy)
279

    
280
  def testAddGroupPreservesFields(self):
281
    cfg = self._get_object()
282
    group = objects.NodeGroup(name="test", members=[],
283
                              alloc_policy=constants.ALLOC_POLICY_LAST_RESORT)
284
    cfg.AddNodeGroup(group, "my-job")
285
    self.assertEqual(constants.ALLOC_POLICY_LAST_RESORT, group.alloc_policy)
286

    
287
  def testAddGroupDoesNotPreserveFields(self):
288
    cfg = self._get_object()
289
    group = objects.NodeGroup(name="test", members=[],
290
                              serial_no=17, ctime=123, mtime=456)
291
    cfg.AddNodeGroup(group, "my-job")
292
    self.assertEqual(1, group.serial_no)
293
    self.assert_(group.ctime > 1200000000)
294
    self.assert_(group.mtime > 1200000000)
295

    
296
  def testAddGroupCanSkipUUIDCheck(self):
297
    cfg = self._get_object()
298
    uuid = cfg.GenerateUniqueID("my-job")
299
    group = objects.NodeGroup(name="test", members=[], uuid=uuid,
300
                              serial_no=17, ctime=123, mtime=456)
301

    
302
    self.assertRaises(errors.ConfigurationError,
303
                      cfg.AddNodeGroup, group, "my-job")
304

    
305
    cfg.AddNodeGroup(group, "my-job", check_uuid=False) # Does not raise.
306
    self.assertEqual(uuid, group.uuid)
307

    
308
  def testAssignGroupNodes(self):
309
    me = netutils.Hostname()
310
    cfg = self._get_object()
311

    
312
    # Create two groups
313
    grp1 = objects.NodeGroup(name="grp1", members=[],
314
                             uuid="2f2fadf7-2a70-4a23-9ab5-2568c252032c")
315
    grp1_serial = 1
316
    cfg.AddNodeGroup(grp1, "job")
317

    
318
    grp2 = objects.NodeGroup(name="grp2", members=[],
319
                             uuid="798d0de3-680f-4a0e-b29a-0f54f693b3f1")
320
    grp2_serial = 1
321
    cfg.AddNodeGroup(grp2, "job")
322
    self.assertEqual(set(map(operator.attrgetter("name"),
323
                             cfg.GetAllNodeGroupsInfo().values())),
324
                     set(["grp1", "grp2", constants.INITIAL_NODE_GROUP_NAME]))
325

    
326
    # No-op
327
    cluster_serial = cfg.GetClusterInfo().serial_no
328
    cfg.AssignGroupNodes([])
329
    cluster_serial += 1
330

    
331
    # Create two nodes
332
    node1 = objects.Node(name="node1", group=grp1.uuid, ndparams={})
333
    node1_serial = 1
334
    node2 = objects.Node(name="node2", group=grp2.uuid, ndparams={})
335
    node2_serial = 1
336
    cfg.AddNode(node1, "job")
337
    cfg.AddNode(node2, "job")
338
    cluster_serial += 2
339
    self.assertEqual(set(cfg.GetNodeList()), set(["node1", "node2", me.name]))
340

    
341
    def _VerifySerials():
342
      self.assertEqual(cfg.GetClusterInfo().serial_no, cluster_serial)
343
      self.assertEqual(node1.serial_no, node1_serial)
344
      self.assertEqual(node2.serial_no, node2_serial)
345
      self.assertEqual(grp1.serial_no, grp1_serial)
346
      self.assertEqual(grp2.serial_no, grp2_serial)
347

    
348
    _VerifySerials()
349

    
350
    self.assertEqual(set(grp1.members), set(["node1"]))
351
    self.assertEqual(set(grp2.members), set(["node2"]))
352

    
353
    # Check invalid nodes and groups
354
    self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
355
      ("unknown.node.example.com", grp2.uuid),
356
      ])
357
    self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
358
      (node1.name, "unknown-uuid"),
359
      ])
360

    
361
    self.assertEqual(node1.group, grp1.uuid)
362
    self.assertEqual(node2.group, grp2.uuid)
363
    self.assertEqual(set(grp1.members), set(["node1"]))
364
    self.assertEqual(set(grp2.members), set(["node2"]))
365

    
366
    # Another no-op
367
    cfg.AssignGroupNodes([])
368
    cluster_serial += 1
369
    _VerifySerials()
370

    
371
    # Assign to the same group (should be a no-op)
372
    self.assertEqual(node2.group, grp2.uuid)
373
    cfg.AssignGroupNodes([
374
      (node2.name, grp2.uuid),
375
      ])
376
    cluster_serial += 1
377
    self.assertEqual(node2.group, grp2.uuid)
378
    _VerifySerials()
379
    self.assertEqual(set(grp1.members), set(["node1"]))
380
    self.assertEqual(set(grp2.members), set(["node2"]))
381

    
382
    # Assign node 2 to group 1
383
    self.assertEqual(node2.group, grp2.uuid)
384
    cfg.AssignGroupNodes([
385
      (node2.name, grp1.uuid),
386
      ])
387
    cluster_serial += 1
388
    node2_serial += 1
389
    grp1_serial += 1
390
    grp2_serial += 1
391
    self.assertEqual(node2.group, grp1.uuid)
392
    _VerifySerials()
393
    self.assertEqual(set(grp1.members), set(["node1", "node2"]))
394
    self.assertFalse(grp2.members)
395

    
396
    # And assign both nodes to group 2
397
    self.assertEqual(node1.group, grp1.uuid)
398
    self.assertEqual(node2.group, grp1.uuid)
399
    self.assertNotEqual(grp1.uuid, grp2.uuid)
400
    cfg.AssignGroupNodes([
401
      (node1.name, grp2.uuid),
402
      (node2.name, grp2.uuid),
403
      ])
404
    cluster_serial += 1
405
    node1_serial += 1
406
    node2_serial += 1
407
    grp1_serial += 1
408
    grp2_serial += 1
409
    self.assertEqual(node1.group, grp2.uuid)
410
    self.assertEqual(node2.group, grp2.uuid)
411
    _VerifySerials()
412
    self.assertFalse(grp1.members)
413
    self.assertEqual(set(grp2.members), set(["node1", "node2"]))
414

    
415
    # Destructive tests
416
    orig_group = node2.group
417
    try:
418
      other_uuid = "68b3d087-6ea5-491c-b81f-0a47d90228c5"
419
      assert compat.all(node.group != other_uuid
420
                        for node in cfg.GetAllNodesInfo().values())
421
      node2.group = "68b3d087-6ea5-491c-b81f-0a47d90228c5"
422
      self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
423
        ("node2", grp2.uuid),
424
        ])
425
      _VerifySerials()
426
    finally:
427
      node2.group = orig_group
428

    
429

    
430
class TestTRM(unittest.TestCase):
431
  EC_ID = 1
432

    
433
  def testEmpty(self):
434
    t = TemporaryReservationManager()
435
    t.Reserve(self.EC_ID, "a")
436
    self.assertFalse(t.Reserved(self.EC_ID))
437
    self.assertTrue(t.Reserved("a"))
438
    self.assertEqual(len(t.GetReserved()), 1)
439

    
440
  def testDuplicate(self):
441
    t = TemporaryReservationManager()
442
    t.Reserve(self.EC_ID, "a")
443
    self.assertRaises(errors.ReservationError, t.Reserve, 2, "a")
444
    t.DropECReservations(self.EC_ID)
445
    self.assertFalse(t.Reserved("a"))
446

    
447

    
448
class TestCheckInstanceDiskIvNames(unittest.TestCase):
449
  @staticmethod
450
  def _MakeDisks(names):
451
    return [objects.Disk(iv_name=name) for name in names]
452

    
453
  def testNoError(self):
454
    disks = self._MakeDisks(["disk/0", "disk/1"])
455
    self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
456
    cmdlib._UpdateIvNames(0, disks)
457
    self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
458

    
459
  def testWrongNames(self):
460
    disks = self._MakeDisks(["disk/1", "disk/3", "disk/2"])
461
    self.assertEqual(config._CheckInstanceDiskIvNames(disks), [
462
      (0, "disk/0", "disk/1"),
463
      (1, "disk/1", "disk/3"),
464
      ])
465

    
466
    # Fix names
467
    cmdlib._UpdateIvNames(0, disks)
468
    self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
469

    
470

    
471
if __name__ == "__main__":
472
  testutils.GanetiTestProgram()