Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.config_unittest.py @ 82c54b5b

History | View | Annotate | Download (13.4 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2006, 2007, 2010, 2011 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
        }
204

    
205
    cfg = self._get_object()
206
    node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
207
    node.ndparams = my_ndparams
208
    cfg.Update(node, None)
209
    self.assertEqual(cfg.GetNdParams(node), my_ndparams)
210

    
211
  def testAddGroupFillsFieldsIfMissing(self):
212
    cfg = self._get_object()
213
    group = objects.NodeGroup(name="test", members=[])
214
    cfg.AddNodeGroup(group, "my-job")
215
    self.assert_(utils.UUID_RE.match(group.uuid))
216
    self.assertEqual(constants.ALLOC_POLICY_PREFERRED, group.alloc_policy)
217

    
218
  def testAddGroupPreservesFields(self):
219
    cfg = self._get_object()
220
    group = objects.NodeGroup(name="test", members=[],
221
                              alloc_policy=constants.ALLOC_POLICY_LAST_RESORT)
222
    cfg.AddNodeGroup(group, "my-job")
223
    self.assertEqual(constants.ALLOC_POLICY_LAST_RESORT, group.alloc_policy)
224

    
225
  def testAddGroupDoesNotPreserveFields(self):
226
    cfg = self._get_object()
227
    group = objects.NodeGroup(name="test", members=[],
228
                              serial_no=17, ctime=123, mtime=456)
229
    cfg.AddNodeGroup(group, "my-job")
230
    self.assertEqual(1, group.serial_no)
231
    self.assert_(group.ctime > 1200000000)
232
    self.assert_(group.mtime > 1200000000)
233

    
234
  def testAddGroupCanSkipUUIDCheck(self):
235
    cfg = self._get_object()
236
    uuid = cfg.GenerateUniqueID("my-job")
237
    group = objects.NodeGroup(name="test", members=[], uuid=uuid,
238
                              serial_no=17, ctime=123, mtime=456)
239

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

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

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

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

    
256
    grp2 = objects.NodeGroup(name="grp2", members=[],
257
                             uuid="798d0de3-680f-4a0e-b29a-0f54f693b3f1")
258
    grp2_serial = 1
259
    cfg.AddNodeGroup(grp2, "job")
260
    self.assertEqual(set(map(operator.attrgetter("name"),
261
                             cfg.GetAllNodeGroupsInfo().values())),
262
                     set(["grp1", "grp2", constants.INITIAL_NODE_GROUP_NAME]))
263

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

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

    
279
    def _VerifySerials():
280
      self.assertEqual(cfg.GetClusterInfo().serial_no, cluster_serial)
281
      self.assertEqual(node1.serial_no, node1_serial)
282
      self.assertEqual(node2.serial_no, node2_serial)
283
      self.assertEqual(grp1.serial_no, grp1_serial)
284
      self.assertEqual(grp2.serial_no, grp2_serial)
285

    
286
    _VerifySerials()
287

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

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

    
299
    self.assertEqual(node1.group, grp1.uuid)
300
    self.assertEqual(node2.group, grp2.uuid)
301
    self.assertEqual(set(grp1.members), set(["node1"]))
302
    self.assertEqual(set(grp2.members), set(["node2"]))
303

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

    
309
    # Assign to the same group (should be a no-op)
310
    self.assertEqual(node2.group, grp2.uuid)
311
    cfg.AssignGroupNodes([
312
      (node2.name, grp2.uuid),
313
      ])
314
    cluster_serial += 1
315
    self.assertEqual(node2.group, grp2.uuid)
316
    _VerifySerials()
317
    self.assertEqual(set(grp1.members), set(["node1"]))
318
    self.assertEqual(set(grp2.members), set(["node2"]))
319

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

    
334
    # And assign both nodes to group 2
335
    self.assertEqual(node1.group, grp1.uuid)
336
    self.assertEqual(node2.group, grp1.uuid)
337
    self.assertNotEqual(grp1.uuid, grp2.uuid)
338
    cfg.AssignGroupNodes([
339
      (node1.name, grp2.uuid),
340
      (node2.name, grp2.uuid),
341
      ])
342
    cluster_serial += 1
343
    node1_serial += 1
344
    node2_serial += 1
345
    grp1_serial += 1
346
    grp2_serial += 1
347
    self.assertEqual(node1.group, grp2.uuid)
348
    self.assertEqual(node2.group, grp2.uuid)
349
    _VerifySerials()
350
    self.assertFalse(grp1.members)
351
    self.assertEqual(set(grp2.members), set(["node1", "node2"]))
352

    
353
    # Destructive tests
354
    orig_group = node2.group
355
    try:
356
      other_uuid = "68b3d087-6ea5-491c-b81f-0a47d90228c5"
357
      assert compat.all(node.group != other_uuid
358
                        for node in cfg.GetAllNodesInfo().values())
359
      node2.group = "68b3d087-6ea5-491c-b81f-0a47d90228c5"
360
      self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
361
        ("node2", grp2.uuid),
362
        ])
363
      _VerifySerials()
364
    finally:
365
      node2.group = orig_group
366

    
367

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

    
371
  def testEmpty(self):
372
    t = TemporaryReservationManager()
373
    t.Reserve(self.EC_ID, "a")
374
    self.assertFalse(t.Reserved(self.EC_ID))
375
    self.assertTrue(t.Reserved("a"))
376
    self.assertEqual(len(t.GetReserved()), 1)
377

    
378
  def testDuplicate(self):
379
    t = TemporaryReservationManager()
380
    t.Reserve(self.EC_ID, "a")
381
    self.assertRaises(errors.ReservationError, t.Reserve, 2, "a")
382
    t.DropECReservations(self.EC_ID)
383
    self.assertFalse(t.Reserved("a"))
384

    
385

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

    
391
  def testNoError(self):
392
    disks = self._MakeDisks(["disk/0", "disk/1"])
393
    self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
394
    cmdlib._UpdateIvNames(0, disks)
395
    self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
396

    
397
  def testWrongNames(self):
398
    disks = self._MakeDisks(["disk/1", "disk/3", "disk/2"])
399
    self.assertEqual(config._CheckInstanceDiskIvNames(disks), [
400
      (0, "disk/0", "disk/1"),
401
      (1, "disk/1", "disk/3"),
402
      ])
403

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

    
408

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