Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.config_unittest.py @ 432e8e2f

History | View | Annotate | Download (13.5 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 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
        }
205

    
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)
211

    
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)
218

    
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)
225

    
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)
234

    
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)
240

    
241
    self.assertRaises(errors.ConfigurationError,
242
                      cfg.AddNodeGroup, group, "my-job")
243

    
244
    cfg.AddNodeGroup(group, "my-job", check_uuid=False) # Does not raise.
245
    self.assertEqual(uuid, group.uuid)
246

    
247
  def testAssignGroupNodes(self):
248
    me = netutils.Hostname()
249
    cfg = self._get_object()
250

    
251
    # Create two groups
252
    grp1 = objects.NodeGroup(name="grp1", members=[],
253
                             uuid="2f2fadf7-2a70-4a23-9ab5-2568c252032c")
254
    grp1_serial = 1
255
    cfg.AddNodeGroup(grp1, "job")
256

    
257
    grp2 = objects.NodeGroup(name="grp2", members=[],
258
                             uuid="798d0de3-680f-4a0e-b29a-0f54f693b3f1")
259
    grp2_serial = 1
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]))
264

    
265
    # No-op
266
    cluster_serial = cfg.GetClusterInfo().serial_no
267
    cfg.AssignGroupNodes([])
268
    cluster_serial += 1
269

    
270
    # Create two nodes
271
    node1 = objects.Node(name="node1", group=grp1.uuid, ndparams={})
272
    node1_serial = 1
273
    node2 = objects.Node(name="node2", group=grp2.uuid, ndparams={})
274
    node2_serial = 1
275
    cfg.AddNode(node1, "job")
276
    cfg.AddNode(node2, "job")
277
    cluster_serial += 2
278
    self.assertEqual(set(cfg.GetNodeList()), set(["node1", "node2", me.name]))
279

    
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)
286

    
287
    _VerifySerials()
288

    
289
    self.assertEqual(set(grp1.members), set(["node1"]))
290
    self.assertEqual(set(grp2.members), set(["node2"]))
291

    
292
    # Check invalid nodes and groups
293
    self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
294
      ("unknown.node.example.com", grp2.uuid),
295
      ])
296
    self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
297
      (node1.name, "unknown-uuid"),
298
      ])
299

    
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"]))
304

    
305
    # Another no-op
306
    cfg.AssignGroupNodes([])
307
    cluster_serial += 1
308
    _VerifySerials()
309

    
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),
314
      ])
315
    cluster_serial += 1
316
    self.assertEqual(node2.group, grp2.uuid)
317
    _VerifySerials()
318
    self.assertEqual(set(grp1.members), set(["node1"]))
319
    self.assertEqual(set(grp2.members), set(["node2"]))
320

    
321
    # Assign node 2 to group 1
322
    self.assertEqual(node2.group, grp2.uuid)
323
    cfg.AssignGroupNodes([
324
      (node2.name, grp1.uuid),
325
      ])
326
    cluster_serial += 1
327
    node2_serial += 1
328
    grp1_serial += 1
329
    grp2_serial += 1
330
    self.assertEqual(node2.group, grp1.uuid)
331
    _VerifySerials()
332
    self.assertEqual(set(grp1.members), set(["node1", "node2"]))
333
    self.assertFalse(grp2.members)
334

    
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),
342
      ])
343
    cluster_serial += 1
344
    node1_serial += 1
345
    node2_serial += 1
346
    grp1_serial += 1
347
    grp2_serial += 1
348
    self.assertEqual(node1.group, grp2.uuid)
349
    self.assertEqual(node2.group, grp2.uuid)
350
    _VerifySerials()
351
    self.assertFalse(grp1.members)
352
    self.assertEqual(set(grp2.members), set(["node1", "node2"]))
353

    
354
    # Destructive tests
355
    orig_group = node2.group
356
    try:
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),
363
        ])
364
      _VerifySerials()
365
    finally:
366
      node2.group = orig_group
367

    
368

    
369
class TestTRM(unittest.TestCase):
370
  EC_ID = 1
371

    
372
  def testEmpty(self):
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)
378

    
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"))
385

    
386

    
387
class TestCheckInstanceDiskIvNames(unittest.TestCase):
388
  @staticmethod
389
  def _MakeDisks(names):
390
    return [objects.Disk(iv_name=name) for name in names]
391

    
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), [])
397

    
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"),
403
      ])
404

    
405
    # Fix names
406
    cmdlib._UpdateIvNames(0, disks)
407
    self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
408

    
409

    
410
if __name__ == '__main__':
411
  testutils.GanetiTestProgram()