Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.config_unittest.py @ 5b798711

History | View | Annotate | Download (26.4 kB)

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

    
4
# Copyright (C) 2006, 2007, 2010, 2011, 2012, 2013 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 tempfile
28
import operator
29

    
30
from ganeti import bootstrap
31
from ganeti import config
32
from ganeti import constants
33
from ganeti import errors
34
from ganeti import objects
35
from ganeti import utils
36
from ganeti import netutils
37
from ganeti import compat
38
from ganeti import serializer
39
from ganeti.cmdlib import instance
40

    
41
from ganeti.config import TemporaryReservationManager
42

    
43
import testutils
44
import mocks
45
import mock
46

    
47

    
48
def _StubGetEntResolver():
49
  return mocks.FakeGetentResolver()
50

    
51

    
52
class TestConfigRunner(unittest.TestCase):
53
  """Testing case for HooksRunner"""
54
  def setUp(self):
55
    fd, self.cfg_file = tempfile.mkstemp()
56
    os.close(fd)
57
    self._init_cluster(self.cfg_file)
58

    
59
  def tearDown(self):
60
    try:
61
      os.unlink(self.cfg_file)
62
    except OSError:
63
      pass
64

    
65
  def _get_object(self):
66
    """Returns an instance of ConfigWriter"""
67
    cfg = config.ConfigWriter(cfg_file=self.cfg_file, offline=True,
68
                              _getents=_StubGetEntResolver)
69
    return cfg
70

    
71
  def _init_cluster(self, cfg):
72
    """Initializes the cfg object"""
73
    me = netutils.Hostname()
74
    ip = constants.IP4_ADDRESS_LOCALHOST
75
    # master_ip must not conflict with the node ip address
76
    master_ip = "127.0.0.2"
77

    
78
    cluster_config = objects.Cluster(
79
      serial_no=1,
80
      rsahostkeypub="",
81
      dsahostkeypub="",
82
      highest_used_port=(constants.FIRST_DRBD_PORT - 1),
83
      mac_prefix="aa:00:00",
84
      volume_group_name="xenvg",
85
      drbd_usermode_helper="/bin/true",
86
      nicparams={constants.PP_DEFAULT: constants.NICC_DEFAULTS},
87
      ndparams=constants.NDC_DEFAULTS,
88
      tcpudp_port_pool=set(),
89
      enabled_hypervisors=[constants.HT_FAKE],
90
      master_node=me.name,
91
      master_ip=master_ip,
92
      master_netdev=constants.DEFAULT_BRIDGE,
93
      cluster_name="cluster.local",
94
      file_storage_dir="/tmp",
95
      uid_pool=[],
96
      )
97

    
98
    master_node_config = objects.Node(name=me.name,
99
                                      primary_ip=me.ip,
100
                                      secondary_ip=ip,
101
                                      serial_no=1,
102
                                      master_candidate=True)
103

    
104
    bootstrap.InitConfig(constants.CONFIG_VERSION,
105
                         cluster_config, master_node_config, self.cfg_file)
106

    
107
  def _create_instance(self, cfg):
108
    """Create and return an instance object"""
109
    inst = objects.Instance(name="test.example.com",
110
                            uuid="test-uuid",
111
                            disks=[], nics=[],
112
                            disk_template=constants.DT_DISKLESS,
113
                            primary_node=cfg.GetMasterNode(),
114
                            osparams_private=serializer.PrivateDict())
115
    return inst
116

    
117
  def testEmpty(self):
118
    """Test instantiate config object"""
119
    self._get_object()
120

    
121
  def testInit(self):
122
    """Test initialize the config file"""
123
    cfg = self._get_object()
124
    self.failUnlessEqual(1, len(cfg.GetNodeList()))
125
    self.failUnlessEqual(0, len(cfg.GetInstanceList()))
126

    
127
  def _GenericNodesCheck(self, inst, all_nodes, secondary_nodes):
128
    for i in [all_nodes, secondary_nodes]:
129
      self.assertTrue(isinstance(i, (list, tuple)),
130
                      msg="Data type doesn't guarantee order")
131

    
132
    self.assertTrue(inst.primary_node not in secondary_nodes)
133
    self.assertEqual(all_nodes[0], inst.primary_node,
134
                     msg="Primary node not first node in list")
135

    
136
  def testInstNodesNoDisks(self):
137
    """Test all_nodes/secondary_nodes when there are no disks"""
138
    # construct instance
139
    cfg = self._get_object()
140
    inst = self._create_instance(cfg)
141
    cfg.AddInstance(inst, "my-job")
142

    
143
    # No disks
144
    all_nodes = cfg.GetInstanceNodes(inst)
145
    secondary_nodes = cfg.GetInstanceSecondaryNodes(inst)
146
    self._GenericNodesCheck(inst, all_nodes, secondary_nodes)
147
    self.assertEqual(len(secondary_nodes), 0)
148
    self.assertEqual(set(all_nodes), set([inst.primary_node]))
149
    self.assertEqual(cfg.GetInstanceLVsByNode(inst), {
150
      inst.primary_node: [],
151
      })
152

    
153
  def testInstNodesPlainDisks(self):
154
    # construct instance
155
    cfg = self._get_object()
156
    inst = self._create_instance(cfg)
157
    disks = [
158
      objects.Disk(dev_type=constants.DT_PLAIN, size=128,
159
                   logical_id=("myxenvg", "disk25494")),
160
      objects.Disk(dev_type=constants.DT_PLAIN, size=512,
161
                   logical_id=("myxenvg", "disk29071")),
162
      ]
163
    inst.disks = disks
164
    cfg.AddInstance(inst, "my-job")
165

    
166
    # Plain disks
167
    all_nodes = cfg.GetInstanceNodes(inst)
168
    secondary_nodes = cfg.GetInstanceSecondaryNodes(inst)
169
    self._GenericNodesCheck(inst, all_nodes, secondary_nodes)
170
    self.assertEqual(len(secondary_nodes), 0)
171
    self.assertEqual(set(all_nodes), set([inst.primary_node]))
172
    self.assertEqual(cfg.GetInstanceLVsByNode(inst), {
173
      inst.primary_node: ["myxenvg/disk25494", "myxenvg/disk29071"],
174
      })
175

    
176
  def testInstNodesDrbdDisks(self):
177
    # construct a second node
178
    cfg = self._get_object()
179
    node_group = cfg.LookupNodeGroup(None)
180
    master_uuid = cfg.GetMasterNode()
181
    node2 = objects.Node(name="node2.example.com", group=node_group,
182
                         ndparams={}, uuid="node2-uuid")
183
    cfg.AddNode(node2, "my-job")
184

    
185
    # construct instance
186
    inst = self._create_instance(cfg)
187
    disks = [
188
      objects.Disk(dev_type=constants.DT_DRBD8, size=786432,
189
                   logical_id=(master_uuid, node2.uuid,
190
                               12300, 0, 0, "secret"),
191
                   children=[
192
                     objects.Disk(dev_type=constants.DT_PLAIN, size=786432,
193
                                  logical_id=("myxenvg", "disk0")),
194
                     objects.Disk(dev_type=constants.DT_PLAIN, size=128,
195
                                  logical_id=("myxenvg", "meta0"))
196
                   ],
197
                   iv_name="disk/0")
198
      ]
199
    inst.disks = disks
200
    cfg.AddInstance(inst, "my-job")
201

    
202
    # Drbd Disks
203
    all_nodes = cfg.GetInstanceNodes(inst)
204
    secondary_nodes = cfg.GetInstanceSecondaryNodes(inst)
205
    self._GenericNodesCheck(inst, all_nodes, secondary_nodes)
206
    self.assertEqual(set(secondary_nodes), set([node2.uuid]))
207
    self.assertEqual(set(all_nodes),
208
                     set([inst.primary_node, node2.uuid]))
209
    self.assertEqual(cfg.GetInstanceLVsByNode(inst), {
210
      master_uuid: ["myxenvg/disk0", "myxenvg/meta0"],
211
      node2.uuid: ["myxenvg/disk0", "myxenvg/meta0"],
212
      })
213

    
214
  def testUpdateCluster(self):
215
    """Test updates on the cluster object"""
216
    cfg = self._get_object()
217
    # construct a fake cluster object
218
    fake_cl = objects.Cluster()
219
    # fail if we didn't read the config
220
    self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_cl, None)
221

    
222
    cl = cfg.GetClusterInfo()
223
    # first pass, must not fail
224
    cfg.Update(cl, None)
225
    # second pass, also must not fail (after the config has been written)
226
    cfg.Update(cl, None)
227
    # but the fake_cl update should still fail
228
    self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_cl, None)
229

    
230
  def testUpdateNode(self):
231
    """Test updates on one node object"""
232
    cfg = self._get_object()
233
    # construct a fake node
234
    fake_node = objects.Node()
235
    # fail if we didn't read the config
236
    self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_node,
237
                          None)
238

    
239
    node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
240
    # first pass, must not fail
241
    cfg.Update(node, None)
242
    # second pass, also must not fail (after the config has been written)
243
    cfg.Update(node, None)
244
    # but the fake_node update should still fail
245
    self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_node,
246
                          None)
247

    
248
  def testUpdateInstance(self):
249
    """Test updates on one instance object"""
250
    cfg = self._get_object()
251
    # construct a fake instance
252
    inst = self._create_instance(cfg)
253
    fake_instance = objects.Instance()
254
    # fail if we didn't read the config
255
    self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_instance,
256
                          None)
257

    
258
    cfg.AddInstance(inst, "my-job")
259
    instance = cfg.GetInstanceInfo(cfg.GetInstanceList()[0])
260
    # first pass, must not fail
261
    cfg.Update(instance, None)
262
    # second pass, also must not fail (after the config has been written)
263
    cfg.Update(instance, None)
264
    # but the fake_instance update should still fail
265
    self.failUnlessRaises(errors.ConfigurationError, cfg.Update, fake_instance,
266
                          None)
267

    
268
  def testUpgradeSave(self):
269
    """Test that any modification done during upgrading is saved back"""
270
    cfg = self._get_object()
271

    
272
    # Remove an element, run upgrade, and check if the element is
273
    # back and the file upgraded
274
    node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
275
    # For a ConfigObject, None is the same as a missing field
276
    node.ndparams = None
277
    oldsaved = utils.ReadFile(self.cfg_file)
278
    cfg._UpgradeConfig()
279
    self.assertTrue(node.ndparams is not None)
280
    newsaved = utils.ReadFile(self.cfg_file)
281
    # We rely on the fact that at least the serial number changes
282
    self.assertNotEqual(oldsaved, newsaved)
283

    
284
    # Add something that should not be there this time
285
    key = list(constants.NDC_GLOBALS)[0]
286
    node.ndparams[key] = constants.NDC_DEFAULTS[key]
287
    cfg._WriteConfig(None)
288
    oldsaved = utils.ReadFile(self.cfg_file)
289
    cfg._UpgradeConfig()
290
    self.assertTrue(node.ndparams.get(key) is None)
291
    newsaved = utils.ReadFile(self.cfg_file)
292
    self.assertNotEqual(oldsaved, newsaved)
293

    
294
    # Do the upgrade again, this time there should be no update
295
    oldsaved = newsaved
296
    cfg._UpgradeConfig()
297
    newsaved = utils.ReadFile(self.cfg_file)
298
    self.assertEqual(oldsaved, newsaved)
299

    
300
    # Reload the configuration again: it shouldn't change the file
301
    oldsaved = newsaved
302
    self._get_object()
303
    newsaved = utils.ReadFile(self.cfg_file)
304
    self.assertEqual(oldsaved, newsaved)
305

    
306
  def testNICParameterSyntaxCheck(self):
307
    """Test the NIC's CheckParameterSyntax function"""
308
    mode = constants.NIC_MODE
309
    link = constants.NIC_LINK
310
    m_bridged = constants.NIC_MODE_BRIDGED
311
    m_routed = constants.NIC_MODE_ROUTED
312
    CheckSyntax = objects.NIC.CheckParameterSyntax
313

    
314
    CheckSyntax(constants.NICC_DEFAULTS)
315
    CheckSyntax({mode: m_bridged, link: "br1"})
316
    CheckSyntax({mode: m_routed, link: "default"})
317
    self.assertRaises(errors.ConfigurationError,
318
                      CheckSyntax, {mode: "000invalid", link: "any"})
319
    self.assertRaises(errors.ConfigurationError,
320
                      CheckSyntax, {mode: m_bridged, link: None})
321
    self.assertRaises(errors.ConfigurationError,
322
                      CheckSyntax, {mode: m_bridged, link: ""})
323

    
324
  def testGetNdParamsDefault(self):
325
    cfg = self._get_object()
326
    node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
327
    self.assertEqual(cfg.GetNdParams(node), constants.NDC_DEFAULTS)
328

    
329
  def testGetNdParamsModifiedNode(self):
330
    my_ndparams = {
331
        constants.ND_OOB_PROGRAM: "/bin/node-oob",
332
        constants.ND_SPINDLE_COUNT: 1,
333
        constants.ND_EXCLUSIVE_STORAGE: False,
334
        constants.ND_OVS: True,
335
        constants.ND_OVS_NAME: "openvswitch",
336
        constants.ND_OVS_LINK: "eth1",
337
        constants.ND_SSH_PORT: 22,
338
        }
339

    
340
    cfg = self._get_object()
341
    node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
342
    node.ndparams = my_ndparams
343
    cfg.Update(node, None)
344
    self.assertEqual(cfg.GetNdParams(node), my_ndparams)
345

    
346
  def testGetNdParamsInheritance(self):
347
    node_ndparams = {
348
      constants.ND_OOB_PROGRAM: "/bin/node-oob",
349
      constants.ND_OVS_LINK: "eth3"
350
      }
351
    group_ndparams = {
352
      constants.ND_SPINDLE_COUNT: 10,
353
      constants.ND_OVS: True,
354
      constants.ND_OVS_NAME: "openvswitch",
355
      constants.ND_SSH_PORT: 222,
356
      }
357
    expected_ndparams = {
358
      constants.ND_OOB_PROGRAM: "/bin/node-oob",
359
      constants.ND_SPINDLE_COUNT: 10,
360
      constants.ND_EXCLUSIVE_STORAGE:
361
        constants.NDC_DEFAULTS[constants.ND_EXCLUSIVE_STORAGE],
362
      constants.ND_OVS: True,
363
      constants.ND_OVS_NAME: "openvswitch",
364
      constants.ND_OVS_LINK: "eth3",
365
      constants.ND_SSH_PORT: 222,
366
      }
367
    cfg = self._get_object()
368
    node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
369
    node.ndparams = node_ndparams
370
    cfg.Update(node, None)
371
    group = cfg.GetNodeGroup(node.group)
372
    group.ndparams = group_ndparams
373
    cfg.Update(group, None)
374
    self.assertEqual(cfg.GetNdParams(node), expected_ndparams)
375

    
376
  def testAddGroupFillsFieldsIfMissing(self):
377
    cfg = self._get_object()
378
    group = objects.NodeGroup(name="test", members=[])
379
    cfg.AddNodeGroup(group, "my-job")
380
    self.assert_(utils.UUID_RE.match(group.uuid))
381
    self.assertEqual(constants.ALLOC_POLICY_PREFERRED, group.alloc_policy)
382

    
383
  def testAddGroupPreservesFields(self):
384
    cfg = self._get_object()
385
    group = objects.NodeGroup(name="test", members=[],
386
                              alloc_policy=constants.ALLOC_POLICY_LAST_RESORT)
387
    cfg.AddNodeGroup(group, "my-job")
388
    self.assertEqual(constants.ALLOC_POLICY_LAST_RESORT, group.alloc_policy)
389

    
390
  def testAddGroupDoesNotPreserveFields(self):
391
    cfg = self._get_object()
392
    group = objects.NodeGroup(name="test", members=[],
393
                              serial_no=17, ctime=123, mtime=456)
394
    cfg.AddNodeGroup(group, "my-job")
395
    self.assertEqual(1, group.serial_no)
396
    self.assert_(group.ctime > 1200000000)
397
    self.assert_(group.mtime > 1200000000)
398

    
399
  def testAddGroupCanSkipUUIDCheck(self):
400
    cfg = self._get_object()
401
    uuid = cfg.GenerateUniqueID("my-job")
402
    group = objects.NodeGroup(name="test", members=[], uuid=uuid,
403
                              serial_no=17, ctime=123, mtime=456)
404

    
405
    self.assertRaises(errors.ConfigurationError,
406
                      cfg.AddNodeGroup, group, "my-job")
407

    
408
    cfg.AddNodeGroup(group, "my-job", check_uuid=False) # Does not raise.
409
    self.assertEqual(uuid, group.uuid)
410

    
411
  def testAssignGroupNodes(self):
412
    me = netutils.Hostname()
413
    cfg = self._get_object()
414

    
415
    # Create two groups
416
    grp1 = objects.NodeGroup(name="grp1", members=[],
417
                             uuid="2f2fadf7-2a70-4a23-9ab5-2568c252032c")
418
    grp1_serial = 1
419
    cfg.AddNodeGroup(grp1, "job")
420

    
421
    grp2 = objects.NodeGroup(name="grp2", members=[],
422
                             uuid="798d0de3-680f-4a0e-b29a-0f54f693b3f1")
423
    grp2_serial = 1
424
    cfg.AddNodeGroup(grp2, "job")
425
    self.assertEqual(set(map(operator.attrgetter("name"),
426
                             cfg.GetAllNodeGroupsInfo().values())),
427
                     set(["grp1", "grp2", constants.INITIAL_NODE_GROUP_NAME]))
428

    
429
    # No-op
430
    cluster_serial = cfg.GetClusterInfo().serial_no
431
    cfg.AssignGroupNodes([])
432
    cluster_serial += 1
433

    
434
    # Create two nodes
435
    node1 = objects.Node(name="node1", group=grp1.uuid, ndparams={},
436
                         uuid="node1-uuid")
437
    node1_serial = 1
438
    node2 = objects.Node(name="node2", group=grp2.uuid, ndparams={},
439
                         uuid="node2-uuid")
440
    node2_serial = 1
441
    cfg.AddNode(node1, "job")
442
    cfg.AddNode(node2, "job")
443
    cluster_serial += 2
444
    self.assertEqual(set(cfg.GetNodeList()),
445
                     set(["node1-uuid", "node2-uuid",
446
                          cfg.GetNodeInfoByName(me.name).uuid]))
447

    
448
    def _VerifySerials():
449
      self.assertEqual(cfg.GetClusterInfo().serial_no, cluster_serial)
450
      self.assertEqual(node1.serial_no, node1_serial)
451
      self.assertEqual(node2.serial_no, node2_serial)
452
      self.assertEqual(grp1.serial_no, grp1_serial)
453
      self.assertEqual(grp2.serial_no, grp2_serial)
454

    
455
    _VerifySerials()
456

    
457
    self.assertEqual(set(grp1.members), set(["node1-uuid"]))
458
    self.assertEqual(set(grp2.members), set(["node2-uuid"]))
459

    
460
    # Check invalid nodes and groups
461
    self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
462
      ("unknown.node.example.com", grp2.uuid),
463
      ])
464
    self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
465
      (node1.name, "unknown-uuid"),
466
      ])
467

    
468
    self.assertEqual(node1.group, grp1.uuid)
469
    self.assertEqual(node2.group, grp2.uuid)
470
    self.assertEqual(set(grp1.members), set(["node1-uuid"]))
471
    self.assertEqual(set(grp2.members), set(["node2-uuid"]))
472

    
473
    # Another no-op
474
    cfg.AssignGroupNodes([])
475
    cluster_serial += 1
476
    _VerifySerials()
477

    
478
    # Assign to the same group (should be a no-op)
479
    self.assertEqual(node2.group, grp2.uuid)
480
    cfg.AssignGroupNodes([
481
      (node2.uuid, grp2.uuid),
482
      ])
483
    cluster_serial += 1
484
    self.assertEqual(node2.group, grp2.uuid)
485
    _VerifySerials()
486
    self.assertEqual(set(grp1.members), set(["node1-uuid"]))
487
    self.assertEqual(set(grp2.members), set(["node2-uuid"]))
488

    
489
    # Assign node 2 to group 1
490
    self.assertEqual(node2.group, grp2.uuid)
491
    cfg.AssignGroupNodes([
492
      (node2.uuid, grp1.uuid),
493
      ])
494
    cluster_serial += 1
495
    node2_serial += 1
496
    grp1_serial += 1
497
    grp2_serial += 1
498
    self.assertEqual(node2.group, grp1.uuid)
499
    _VerifySerials()
500
    self.assertEqual(set(grp1.members), set(["node1-uuid", "node2-uuid"]))
501
    self.assertFalse(grp2.members)
502

    
503
    # And assign both nodes to group 2
504
    self.assertEqual(node1.group, grp1.uuid)
505
    self.assertEqual(node2.group, grp1.uuid)
506
    self.assertNotEqual(grp1.uuid, grp2.uuid)
507
    cfg.AssignGroupNodes([
508
      (node1.uuid, grp2.uuid),
509
      (node2.uuid, grp2.uuid),
510
      ])
511
    cluster_serial += 1
512
    node1_serial += 1
513
    node2_serial += 1
514
    grp1_serial += 1
515
    grp2_serial += 1
516
    self.assertEqual(node1.group, grp2.uuid)
517
    self.assertEqual(node2.group, grp2.uuid)
518
    _VerifySerials()
519
    self.assertFalse(grp1.members)
520
    self.assertEqual(set(grp2.members), set(["node1-uuid", "node2-uuid"]))
521

    
522
    # Destructive tests
523
    orig_group = node2.group
524
    try:
525
      other_uuid = "68b3d087-6ea5-491c-b81f-0a47d90228c5"
526
      assert compat.all(node.group != other_uuid
527
                        for node in cfg.GetAllNodesInfo().values())
528
      node2.group = "68b3d087-6ea5-491c-b81f-0a47d90228c5"
529
      self.assertRaises(errors.ConfigurationError, cfg.AssignGroupNodes, [
530
        (node2.uuid, grp2.uuid),
531
        ])
532
      _VerifySerials()
533
    finally:
534
      node2.group = orig_group
535

    
536
  def _TestVerifyConfigIPolicy(self, ipolicy, ipowner, cfg, isgroup):
537
    INVALID_KEY = "this_key_cannot_exist"
538

    
539
    ipolicy[INVALID_KEY] = None
540
    # A call to cluster.SimpleFillIPolicy causes different kinds of error
541
    # depending on the owner (cluster or group)
542
    if isgroup:
543
      errs = cfg.VerifyConfig()
544
      self.assertTrue(len(errs) >= 1)
545
      errstr = "%s has invalid instance policy" % ipowner
546
      self.assertTrue(_IsErrorInList(errstr, errs))
547
    else:
548
      self.assertRaises(AssertionError, cfg.VerifyConfig)
549
    del ipolicy[INVALID_KEY]
550
    errs = cfg.VerifyConfig()
551
    self.assertFalse(errs)
552

    
553
    key = list(constants.IPOLICY_PARAMETERS)[0]
554
    hasoldv = (key in ipolicy)
555
    if hasoldv:
556
      oldv = ipolicy[key]
557
    ipolicy[key] = "blah"
558
    errs = cfg.VerifyConfig()
559
    self.assertTrue(len(errs) >= 1)
560
    self.assertTrue(_IsErrorInList("%s has invalid instance policy" % ipowner,
561
                                   errs))
562
    if hasoldv:
563
      ipolicy[key] = oldv
564
    else:
565
      del ipolicy[key]
566

    
567
    ispeclist = []
568
    if constants.ISPECS_MINMAX in ipolicy:
569
      for k in range(len(ipolicy[constants.ISPECS_MINMAX])):
570
        ispeclist.extend([
571
            (ipolicy[constants.ISPECS_MINMAX][k][constants.ISPECS_MIN],
572
             "%s[%s]/%s" % (constants.ISPECS_MINMAX, k, constants.ISPECS_MIN)),
573
            (ipolicy[constants.ISPECS_MINMAX][k][constants.ISPECS_MAX],
574
             "%s[%s]/%s" % (constants.ISPECS_MINMAX, k, constants.ISPECS_MAX)),
575
            ])
576
    if constants.ISPECS_STD in ipolicy:
577
      ispeclist.append((ipolicy[constants.ISPECS_STD], constants.ISPECS_STD))
578

    
579
    for (ispec, ispecpath) in ispeclist:
580
      ispec[INVALID_KEY] = None
581
      errs = cfg.VerifyConfig()
582
      self.assertTrue(len(errs) >= 1)
583
      self.assertTrue(_IsErrorInList(("%s has invalid ipolicy/%s" %
584
                                      (ipowner, ispecpath)), errs))
585
      del ispec[INVALID_KEY]
586
      errs = cfg.VerifyConfig()
587
      self.assertFalse(errs)
588

    
589
      for par in constants.ISPECS_PARAMETERS:
590
        hasoldv = par in ispec
591
        if hasoldv:
592
          oldv = ispec[par]
593
        ispec[par] = "blah"
594
        errs = cfg.VerifyConfig()
595
        self.assertTrue(len(errs) >= 1)
596
        self.assertTrue(_IsErrorInList(("%s has invalid ipolicy/%s" %
597
                                        (ipowner, ispecpath)), errs))
598
        if hasoldv:
599
          ispec[par] = oldv
600
        else:
601
          del ispec[par]
602
        errs = cfg.VerifyConfig()
603
        self.assertFalse(errs)
604

    
605
    if constants.ISPECS_MINMAX in ipolicy:
606
      # Test partial minmax specs
607
      for minmax in ipolicy[constants.ISPECS_MINMAX]:
608
        for key in constants.ISPECS_MINMAX_KEYS:
609
          self.assertTrue(key in minmax)
610
          ispec = minmax[key]
611
          del minmax[key]
612
          errs = cfg.VerifyConfig()
613
          self.assertTrue(len(errs) >= 1)
614
          self.assertTrue(_IsErrorInList("Missing instance specification",
615
                                         errs))
616
          minmax[key] = ispec
617
          for par in constants.ISPECS_PARAMETERS:
618
            oldv = ispec[par]
619
            del ispec[par]
620
            errs = cfg.VerifyConfig()
621
            self.assertTrue(len(errs) >= 1)
622
            self.assertTrue(_IsErrorInList("Missing instance specs parameters",
623
                                           errs))
624
            ispec[par] = oldv
625
      errs = cfg.VerifyConfig()
626
      self.assertFalse(errs)
627

    
628
  def _TestVerifyConfigGroupIPolicy(self, groupinfo, cfg):
629
    old_ipolicy = groupinfo.ipolicy
630
    ipolicy = cfg.GetClusterInfo().SimpleFillIPolicy({})
631
    groupinfo.ipolicy = ipolicy
632
    # Test partial policies
633
    for key in constants.IPOLICY_ALL_KEYS:
634
      self.assertTrue(key in ipolicy)
635
      oldv = ipolicy[key]
636
      del ipolicy[key]
637
      errs = cfg.VerifyConfig()
638
      self.assertFalse(errs)
639
      ipolicy[key] = oldv
640
    groupinfo.ipolicy = old_ipolicy
641

    
642
  def _TestVerifyConfigClusterIPolicy(self, ipolicy, cfg):
643
    # Test partial policies
644
    for key in constants.IPOLICY_ALL_KEYS:
645
      self.assertTrue(key in ipolicy)
646
      oldv = ipolicy[key]
647
      del ipolicy[key]
648
      self.assertRaises(AssertionError, cfg.VerifyConfig)
649
      ipolicy[key] = oldv
650
    errs = cfg.VerifyConfig()
651
    self.assertFalse(errs)
652
    # Partial standard specs
653
    ispec = ipolicy[constants.ISPECS_STD]
654
    for par in constants.ISPECS_PARAMETERS:
655
      oldv = ispec[par]
656
      del ispec[par]
657
      errs = cfg.VerifyConfig()
658
      self.assertTrue(len(errs) >= 1)
659
      self.assertTrue(_IsErrorInList("Missing instance specs parameters",
660
                                     errs))
661
      ispec[par] = oldv
662
    errs = cfg.VerifyConfig()
663
    self.assertFalse(errs)
664

    
665
  def testVerifyConfig(self):
666
    cfg = self._get_object()
667

    
668
    errs = cfg.VerifyConfig()
669
    self.assertFalse(errs)
670

    
671
    node = cfg.GetNodeInfo(cfg.GetNodeList()[0])
672
    key = list(constants.NDC_GLOBALS)[0]
673
    node.ndparams[key] = constants.NDC_DEFAULTS[key]
674
    errs = cfg.VerifyConfig()
675
    self.assertTrue(len(errs) >= 1)
676
    self.assertTrue(_IsErrorInList("has some global parameters set", errs))
677

    
678
    del node.ndparams[key]
679
    errs = cfg.VerifyConfig()
680
    self.assertFalse(errs)
681

    
682
    cluster = cfg.GetClusterInfo()
683
    nodegroup = cfg.GetNodeGroup(cfg.GetNodeGroupList()[0])
684
    self._TestVerifyConfigIPolicy(cluster.ipolicy, "cluster", cfg, False)
685
    self._TestVerifyConfigClusterIPolicy(cluster.ipolicy, cfg)
686
    self._TestVerifyConfigIPolicy(nodegroup.ipolicy, nodegroup.name, cfg, True)
687
    self._TestVerifyConfigGroupIPolicy(nodegroup, cfg)
688
    nodegroup.ipolicy = cluster.SimpleFillIPolicy(nodegroup.ipolicy)
689
    self._TestVerifyConfigIPolicy(nodegroup.ipolicy, nodegroup.name, cfg, True)
690

    
691
  # Tests for Ssconf helper functions
692
  def testUnlockedGetHvparamsString(self):
693
    hvparams = {"a": "A", "b": "B", "c": "C"}
694
    hvname = "myhv"
695
    cfg_writer = self._get_object()
696
    cfg_writer._config_data = mock.Mock()
697
    cfg_writer._config_data.cluster = mock.Mock()
698
    cfg_writer._config_data.cluster.hvparams = {hvname: hvparams}
699

    
700
    result = cfg_writer._UnlockedGetHvparamsString(hvname)
701

    
702
    self.assertTrue("a=A" in result)
703
    lines = [line for line in result.split('\n') if line != '']
704
    self.assertEqual(len(hvparams.keys()), len(lines))
705

    
706
  def testExtendByAllHvparamsStrings(self):
707
    all_hvparams = {constants.HT_XEN_PVM: "foo"}
708
    ssconf_values = {}
709
    cfg_writer = self._get_object()
710

    
711
    cfg_writer._ExtendByAllHvparamsStrings(ssconf_values, all_hvparams)
712

    
713
    expected_key = constants.SS_HVPARAMS_PREF + constants.HT_XEN_PVM
714
    self.assertTrue(expected_key in ssconf_values)
715

    
716

    
717
def _IsErrorInList(err_str, err_list):
718
  return any(map(lambda e: err_str in e, err_list))
719

    
720

    
721
class TestTRM(unittest.TestCase):
722
  EC_ID = 1
723

    
724
  def testEmpty(self):
725
    t = TemporaryReservationManager()
726
    t.Reserve(self.EC_ID, "a")
727
    self.assertFalse(t.Reserved(self.EC_ID))
728
    self.assertTrue(t.Reserved("a"))
729
    self.assertEqual(len(t.GetReserved()), 1)
730

    
731
  def testDuplicate(self):
732
    t = TemporaryReservationManager()
733
    t.Reserve(self.EC_ID, "a")
734
    self.assertRaises(errors.ReservationError, t.Reserve, 2, "a")
735
    t.DropECReservations(self.EC_ID)
736
    self.assertFalse(t.Reserved("a"))
737

    
738

    
739
class TestCheckInstanceDiskIvNames(unittest.TestCase):
740
  @staticmethod
741
  def _MakeDisks(names):
742
    return [objects.Disk(iv_name=name) for name in names]
743

    
744
  def testNoError(self):
745
    disks = self._MakeDisks(["disk/0", "disk/1"])
746
    self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
747
    instance._UpdateIvNames(0, disks)
748
    self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
749

    
750
  def testWrongNames(self):
751
    disks = self._MakeDisks(["disk/1", "disk/3", "disk/2"])
752
    self.assertEqual(config._CheckInstanceDiskIvNames(disks), [
753
      (0, "disk/0", "disk/1"),
754
      (1, "disk/1", "disk/3"),
755
      ])
756

    
757
    # Fix names
758
    instance._UpdateIvNames(0, disks)
759
    self.assertEqual(config._CheckInstanceDiskIvNames(disks), [])
760

    
761

    
762
if __name__ == "__main__":
763
  testutils.GanetiTestProgram()