Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.cmdlib_unittest.py @ cb44e3db

History | View | Annotate | Download (62.4 kB)

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

    
4
# Copyright (C) 2008, 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 cmdlib module"""
23

    
24

    
25
import os
26
import unittest
27
import time
28
import tempfile
29
import shutil
30
import operator
31
import itertools
32
import copy
33

    
34
from ganeti import constants
35
from ganeti import mcpu
36
from ganeti import cmdlib
37
from ganeti import opcodes
38
from ganeti import errors
39
from ganeti import utils
40
from ganeti import luxi
41
from ganeti import ht
42
from ganeti import objects
43
from ganeti import compat
44
from ganeti import rpc
45
from ganeti import locking
46
from ganeti import pathutils
47
from ganeti.masterd import iallocator
48
from ganeti.hypervisor import hv_xen
49

    
50
import testutils
51
import mocks
52

    
53

    
54
class TestCertVerification(testutils.GanetiTestCase):
55
  def setUp(self):
56
    testutils.GanetiTestCase.setUp(self)
57

    
58
    self.tmpdir = tempfile.mkdtemp()
59

    
60
  def tearDown(self):
61
    shutil.rmtree(self.tmpdir)
62

    
63
  def testVerifyCertificate(self):
64
    cmdlib._VerifyCertificate(testutils.TestDataFilename("cert1.pem"))
65

    
66
    nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
67

    
68
    (errcode, msg) = cmdlib._VerifyCertificate(nonexist_filename)
69
    self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
70

    
71
    # Try to load non-certificate file
72
    invalid_cert = testutils.TestDataFilename("bdev-net.txt")
73
    (errcode, msg) = cmdlib._VerifyCertificate(invalid_cert)
74
    self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
75

    
76

    
77
class TestOpcodeParams(testutils.GanetiTestCase):
78
  def testParamsStructures(self):
79
    for op in sorted(mcpu.Processor.DISPATCH_TABLE):
80
      lu = mcpu.Processor.DISPATCH_TABLE[op]
81
      lu_name = lu.__name__
82
      self.failIf(hasattr(lu, "_OP_REQP"),
83
                  msg=("LU '%s' has old-style _OP_REQP" % lu_name))
84
      self.failIf(hasattr(lu, "_OP_DEFS"),
85
                  msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
86
      self.failIf(hasattr(lu, "_OP_PARAMS"),
87
                  msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
88

    
89

    
90
class TestIAllocatorChecks(testutils.GanetiTestCase):
91
  def testFunction(self):
92
    class TestLU(object):
93
      def __init__(self, opcode):
94
        self.cfg = mocks.FakeConfig()
95
        self.op = opcode
96

    
97
    class OpTest(opcodes.OpCode):
98
       OP_PARAMS = [
99
        ("iallocator", None, ht.NoType, None),
100
        ("node", None, ht.NoType, None),
101
        ]
102

    
103
    default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
104
    other_iallocator = default_iallocator + "_not"
105

    
106
    op = OpTest()
107
    lu = TestLU(op)
108

    
109
    c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
110

    
111
    # Neither node nor iallocator given
112
    for n in (None, []):
113
      op.iallocator = None
114
      op.node = n
115
      c_i()
116
      self.assertEqual(lu.op.iallocator, default_iallocator)
117
      self.assertEqual(lu.op.node, n)
118

    
119
    # Both, iallocator and node given
120
    for a in ("test", constants.DEFAULT_IALLOCATOR_SHORTCUT):
121
      op.iallocator = a
122
      op.node = "test"
123
      self.assertRaises(errors.OpPrereqError, c_i)
124

    
125
    # Only iallocator given
126
    for n in (None, []):
127
      op.iallocator = other_iallocator
128
      op.node = n
129
      c_i()
130
      self.assertEqual(lu.op.iallocator, other_iallocator)
131
      self.assertEqual(lu.op.node, n)
132

    
133
    # Only node given
134
    op.iallocator = None
135
    op.node = "node"
136
    c_i()
137
    self.assertEqual(lu.op.iallocator, None)
138
    self.assertEqual(lu.op.node, "node")
139

    
140
    # Asked for default iallocator, no node given
141
    op.iallocator = constants.DEFAULT_IALLOCATOR_SHORTCUT
142
    op.node = None
143
    c_i()
144
    self.assertEqual(lu.op.iallocator, default_iallocator)
145
    self.assertEqual(lu.op.node, None)
146

    
147
    # No node, iallocator or default iallocator
148
    op.iallocator = None
149
    op.node = None
150
    lu.cfg.GetDefaultIAllocator = lambda: None
151
    self.assertRaises(errors.OpPrereqError, c_i)
152

    
153

    
154
class TestLUTestJqueue(unittest.TestCase):
155
  def test(self):
156
    self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
157
                 (luxi.WFJC_TIMEOUT * 0.75),
158
                 msg=("Client timeout too high, might not notice bugs"
159
                      " in WaitForJobChange"))
160

    
161

    
162
class TestLUQuery(unittest.TestCase):
163
  def test(self):
164
    self.assertEqual(sorted(cmdlib._QUERY_IMPL.keys()),
165
                     sorted(constants.QR_VIA_OP))
166

    
167
    assert constants.QR_NODE in constants.QR_VIA_OP
168
    assert constants.QR_INSTANCE in constants.QR_VIA_OP
169

    
170
    for i in constants.QR_VIA_OP:
171
      self.assert_(cmdlib._GetQueryImplementation(i))
172

    
173
    self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation, "")
174
    self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation,
175
                      "xyz")
176

    
177

    
178
class TestLUGroupAssignNodes(unittest.TestCase):
179

    
180
  def testCheckAssignmentForSplitInstances(self):
181
    node_data = dict((name, objects.Node(name=name, group=group))
182
                     for (name, group) in [("n1a", "g1"), ("n1b", "g1"),
183
                                           ("n2a", "g2"), ("n2b", "g2"),
184
                                           ("n3a", "g3"), ("n3b", "g3"),
185
                                           ("n3c", "g3"),
186
                                           ])
187

    
188
    def Instance(name, pnode, snode):
189
      if snode is None:
190
        disks = []
191
        disk_template = constants.DT_DISKLESS
192
      else:
193
        disks = [objects.Disk(dev_type=constants.LD_DRBD8,
194
                              logical_id=[pnode, snode, 1, 17, 17])]
195
        disk_template = constants.DT_DRBD8
196

    
197
      return objects.Instance(name=name, primary_node=pnode, disks=disks,
198
                              disk_template=disk_template)
199

    
200
    instance_data = dict((name, Instance(name, pnode, snode))
201
                         for name, pnode, snode in [("inst1a", "n1a", "n1b"),
202
                                                    ("inst1b", "n1b", "n1a"),
203
                                                    ("inst2a", "n2a", "n2b"),
204
                                                    ("inst3a", "n3a", None),
205
                                                    ("inst3b", "n3b", "n1b"),
206
                                                    ("inst3c", "n3b", "n2b"),
207
                                                    ])
208

    
209
    # Test first with the existing state.
210
    (new, prev) = \
211
      cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
212
                                                                 node_data,
213
                                                                 instance_data)
214

    
215
    self.assertEqual([], new)
216
    self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
217

    
218
    # And now some changes.
219
    (new, prev) = \
220
      cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
221
                                                                   "g3")],
222
                                                                 node_data,
223
                                                                 instance_data)
224

    
225
    self.assertEqual(set(["inst1a", "inst1b"]), set(new))
226
    self.assertEqual(set(["inst3c"]), set(prev))
227

    
228

    
229
class TestClusterVerifySsh(unittest.TestCase):
230
  def testMultipleGroups(self):
231
    fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
232
    mygroupnodes = [
233
      objects.Node(name="node20", group="my", offline=False),
234
      objects.Node(name="node21", group="my", offline=False),
235
      objects.Node(name="node22", group="my", offline=False),
236
      objects.Node(name="node23", group="my", offline=False),
237
      objects.Node(name="node24", group="my", offline=False),
238
      objects.Node(name="node25", group="my", offline=False),
239
      objects.Node(name="node26", group="my", offline=True),
240
      ]
241
    nodes = [
242
      objects.Node(name="node1", group="g1", offline=True),
243
      objects.Node(name="node2", group="g1", offline=False),
244
      objects.Node(name="node3", group="g1", offline=False),
245
      objects.Node(name="node4", group="g1", offline=True),
246
      objects.Node(name="node5", group="g1", offline=False),
247
      objects.Node(name="node10", group="xyz", offline=False),
248
      objects.Node(name="node11", group="xyz", offline=False),
249
      objects.Node(name="node40", group="alloff", offline=True),
250
      objects.Node(name="node41", group="alloff", offline=True),
251
      objects.Node(name="node50", group="aaa", offline=False),
252
      ] + mygroupnodes
253
    assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
254

    
255
    (online, perhost) = fn(mygroupnodes, "my", nodes)
256
    self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
257
    self.assertEqual(set(perhost.keys()), set(online))
258

    
259
    self.assertEqual(perhost, {
260
      "node20": ["node10", "node2", "node50"],
261
      "node21": ["node11", "node3", "node50"],
262
      "node22": ["node10", "node5", "node50"],
263
      "node23": ["node11", "node2", "node50"],
264
      "node24": ["node10", "node3", "node50"],
265
      "node25": ["node11", "node5", "node50"],
266
      })
267

    
268
  def testSingleGroup(self):
269
    fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
270
    nodes = [
271
      objects.Node(name="node1", group="default", offline=True),
272
      objects.Node(name="node2", group="default", offline=False),
273
      objects.Node(name="node3", group="default", offline=False),
274
      objects.Node(name="node4", group="default", offline=True),
275
      ]
276
    assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
277

    
278
    (online, perhost) = fn(nodes, "default", nodes)
279
    self.assertEqual(online, ["node2", "node3"])
280
    self.assertEqual(set(perhost.keys()), set(online))
281

    
282
    self.assertEqual(perhost, {
283
      "node2": [],
284
      "node3": [],
285
      })
286

    
287

    
288
class TestClusterVerifyFiles(unittest.TestCase):
289
  @staticmethod
290
  def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
291
    assert ((ecode == constants.CV_ENODEFILECHECK and
292
             ht.TNonEmptyString(item)) or
293
            (ecode == constants.CV_ECLUSTERFILECHECK and
294
             item is None))
295

    
296
    if args:
297
      msg = msg % args
298

    
299
    if cond:
300
      errors.append((item, msg))
301

    
302
  _VerifyFiles = cmdlib.LUClusterVerifyGroup._VerifyFiles
303

    
304
  def test(self):
305
    errors = []
306
    master_name = "master.example.com"
307
    nodeinfo = [
308
      objects.Node(name=master_name, offline=False, vm_capable=True),
309
      objects.Node(name="node2.example.com", offline=False, vm_capable=True),
310
      objects.Node(name="node3.example.com", master_candidate=True,
311
                   vm_capable=False),
312
      objects.Node(name="node4.example.com", offline=False, vm_capable=True),
313
      objects.Node(name="nodata.example.com", offline=False, vm_capable=True),
314
      objects.Node(name="offline.example.com", offline=True),
315
      ]
316
    cluster = objects.Cluster(modify_etc_hosts=True,
317
                              enabled_hypervisors=[constants.HT_XEN_HVM])
318
    files_all = set([
319
      pathutils.CLUSTER_DOMAIN_SECRET_FILE,
320
      pathutils.RAPI_CERT_FILE,
321
      pathutils.RAPI_USERS_FILE,
322
      ])
323
    files_opt = set([
324
      pathutils.RAPI_USERS_FILE,
325
      hv_xen.XL_CONFIG_FILE,
326
      pathutils.VNC_PASSWORD_FILE,
327
      ])
328
    files_mc = set([
329
      pathutils.CLUSTER_CONF_FILE,
330
      ])
331
    files_vm = set([
332
      hv_xen.XEND_CONFIG_FILE,
333
      hv_xen.XL_CONFIG_FILE,
334
      pathutils.VNC_PASSWORD_FILE,
335
      ])
336
    nvinfo = {
337
      master_name: rpc.RpcResult(data=(True, {
338
        constants.NV_FILELIST: {
339
          pathutils.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
340
          pathutils.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
341
          pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
342
          hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
343
          hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
344
        }})),
345
      "node2.example.com": rpc.RpcResult(data=(True, {
346
        constants.NV_FILELIST: {
347
          pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
348
          hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
349
          }
350
        })),
351
      "node3.example.com": rpc.RpcResult(data=(True, {
352
        constants.NV_FILELIST: {
353
          pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
354
          pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
355
          }
356
        })),
357
      "node4.example.com": rpc.RpcResult(data=(True, {
358
        constants.NV_FILELIST: {
359
          pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
360
          pathutils.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
361
          pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
362
          pathutils.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
363
          hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
364
          }
365
        })),
366
      "nodata.example.com": rpc.RpcResult(data=(True, {})),
367
      "offline.example.com": rpc.RpcResult(offline=True),
368
      }
369
    assert set(nvinfo.keys()) == set(map(operator.attrgetter("name"), nodeinfo))
370

    
371
    self._VerifyFiles(compat.partial(self._FakeErrorIf, errors), nodeinfo,
372
                      master_name, nvinfo,
373
                      (files_all, files_opt, files_mc, files_vm))
374
    self.assertEqual(sorted(errors), sorted([
375
      (None, ("File %s found with 2 different checksums (variant 1 on"
376
              " node2.example.com, node3.example.com, node4.example.com;"
377
              " variant 2 on master.example.com)" % pathutils.RAPI_CERT_FILE)),
378
      (None, ("File %s is missing from node(s) node2.example.com" %
379
              pathutils.CLUSTER_DOMAIN_SECRET_FILE)),
380
      (None, ("File %s should not exist on node(s) node4.example.com" %
381
              pathutils.CLUSTER_CONF_FILE)),
382
      (None, ("File %s is missing from node(s) node4.example.com" %
383
              hv_xen.XEND_CONFIG_FILE)),
384
      (None, ("File %s is missing from node(s) node3.example.com" %
385
              pathutils.CLUSTER_CONF_FILE)),
386
      (None, ("File %s found with 2 different checksums (variant 1 on"
387
              " master.example.com; variant 2 on node4.example.com)" %
388
              pathutils.CLUSTER_CONF_FILE)),
389
      (None, ("File %s is optional, but it must exist on all or no nodes (not"
390
              " found on master.example.com, node2.example.com,"
391
              " node3.example.com)" % pathutils.RAPI_USERS_FILE)),
392
      (None, ("File %s is optional, but it must exist on all or no nodes (not"
393
              " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
394
      ("nodata.example.com", "Node did not return file checksum data"),
395
      ]))
396

    
397

    
398
class _FakeLU:
399
  def __init__(self, cfg=NotImplemented, proc=NotImplemented,
400
               rpc=NotImplemented):
401
    self.warning_log = []
402
    self.info_log = []
403
    self.cfg = cfg
404
    self.proc = proc
405
    self.rpc = rpc
406

    
407
  def LogWarning(self, text, *args):
408
    self.warning_log.append((text, args))
409

    
410
  def LogInfo(self, text, *args):
411
    self.info_log.append((text, args))
412

    
413

    
414
class TestLoadNodeEvacResult(unittest.TestCase):
415
  def testSuccess(self):
416
    for moved in [[], [
417
      ("inst20153.example.com", "grp2", ["nodeA4509", "nodeB2912"]),
418
      ]]:
419
      for early_release in [False, True]:
420
        for use_nodes in [False, True]:
421
          jobs = [
422
            [opcodes.OpInstanceReplaceDisks().__getstate__()],
423
            [opcodes.OpInstanceMigrate().__getstate__()],
424
            ]
425

    
426
          alloc_result = (moved, [], jobs)
427
          assert iallocator._NEVAC_RESULT(alloc_result)
428

    
429
          lu = _FakeLU()
430
          result = cmdlib._LoadNodeEvacResult(lu, alloc_result,
431
                                              early_release, use_nodes)
432

    
433
          if moved:
434
            (_, (info_args, )) = lu.info_log.pop(0)
435
            for (instname, instgroup, instnodes) in moved:
436
              self.assertTrue(instname in info_args)
437
              if use_nodes:
438
                for i in instnodes:
439
                  self.assertTrue(i in info_args)
440
              else:
441
                self.assertTrue(instgroup in info_args)
442

    
443
          self.assertFalse(lu.info_log)
444
          self.assertFalse(lu.warning_log)
445

    
446
          for op in itertools.chain(*result):
447
            if hasattr(op.__class__, "early_release"):
448
              self.assertEqual(op.early_release, early_release)
449
            else:
450
              self.assertFalse(hasattr(op, "early_release"))
451

    
452
  def testFailed(self):
453
    alloc_result = ([], [
454
      ("inst5191.example.com", "errormsg21178"),
455
      ], [])
456
    assert iallocator._NEVAC_RESULT(alloc_result)
457

    
458
    lu = _FakeLU()
459
    self.assertRaises(errors.OpExecError, cmdlib._LoadNodeEvacResult,
460
                      lu, alloc_result, False, False)
461
    self.assertFalse(lu.info_log)
462
    (_, (args, )) = lu.warning_log.pop(0)
463
    self.assertTrue("inst5191.example.com" in args)
464
    self.assertTrue("errormsg21178" in args)
465
    self.assertFalse(lu.warning_log)
466

    
467

    
468
class TestUpdateAndVerifySubDict(unittest.TestCase):
469
  def setUp(self):
470
    self.type_check = {
471
        "a": constants.VTYPE_INT,
472
        "b": constants.VTYPE_STRING,
473
        "c": constants.VTYPE_BOOL,
474
        "d": constants.VTYPE_STRING,
475
        }
476

    
477
  def test(self):
478
    old_test = {
479
      "foo": {
480
        "d": "blubb",
481
        "a": 321,
482
        },
483
      "baz": {
484
        "a": 678,
485
        "b": "678",
486
        "c": True,
487
        },
488
      }
489
    test = {
490
      "foo": {
491
        "a": 123,
492
        "b": "123",
493
        "c": True,
494
        },
495
      "bar": {
496
        "a": 321,
497
        "b": "321",
498
        "c": False,
499
        },
500
      }
501

    
502
    mv = {
503
      "foo": {
504
        "a": 123,
505
        "b": "123",
506
        "c": True,
507
        "d": "blubb"
508
        },
509
      "bar": {
510
        "a": 321,
511
        "b": "321",
512
        "c": False,
513
        },
514
      "baz": {
515
        "a": 678,
516
        "b": "678",
517
        "c": True,
518
        },
519
      }
520

    
521
    verified = cmdlib._UpdateAndVerifySubDict(old_test, test, self.type_check)
522
    self.assertEqual(verified, mv)
523

    
524
  def testWrong(self):
525
    test = {
526
      "foo": {
527
        "a": "blubb",
528
        "b": "123",
529
        "c": True,
530
        },
531
      "bar": {
532
        "a": 321,
533
        "b": "321",
534
        "c": False,
535
        },
536
      }
537

    
538
    self.assertRaises(errors.TypeEnforcementError,
539
                      cmdlib._UpdateAndVerifySubDict, {}, test, self.type_check)
540

    
541

    
542
class TestHvStateHelper(unittest.TestCase):
543
  def testWithoutOpData(self):
544
    self.assertEqual(cmdlib._MergeAndVerifyHvState(None, NotImplemented), None)
545

    
546
  def testWithoutOldData(self):
547
    new = {
548
      constants.HT_XEN_PVM: {
549
        constants.HVST_MEMORY_TOTAL: 4096,
550
        },
551
      }
552
    self.assertEqual(cmdlib._MergeAndVerifyHvState(new, None), new)
553

    
554
  def testWithWrongHv(self):
555
    new = {
556
      "i-dont-exist": {
557
        constants.HVST_MEMORY_TOTAL: 4096,
558
        },
559
      }
560
    self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyHvState, new,
561
                      None)
562

    
563
class TestDiskStateHelper(unittest.TestCase):
564
  def testWithoutOpData(self):
565
    self.assertEqual(cmdlib._MergeAndVerifyDiskState(None, NotImplemented),
566
                     None)
567

    
568
  def testWithoutOldData(self):
569
    new = {
570
      constants.LD_LV: {
571
        "xenvg": {
572
          constants.DS_DISK_RESERVED: 1024,
573
          },
574
        },
575
      }
576
    self.assertEqual(cmdlib._MergeAndVerifyDiskState(new, None), new)
577

    
578
  def testWithWrongStorageType(self):
579
    new = {
580
      "i-dont-exist": {
581
        "xenvg": {
582
          constants.DS_DISK_RESERVED: 1024,
583
          },
584
        },
585
      }
586
    self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyDiskState,
587
                      new, None)
588

    
589

    
590
class TestComputeMinMaxSpec(unittest.TestCase):
591
  def setUp(self):
592
    self.ipolicy = {
593
      constants.ISPECS_MAX: {
594
        constants.ISPEC_MEM_SIZE: 512,
595
        constants.ISPEC_DISK_SIZE: 1024,
596
        },
597
      constants.ISPECS_MIN: {
598
        constants.ISPEC_MEM_SIZE: 128,
599
        constants.ISPEC_DISK_COUNT: 1,
600
        },
601
      }
602

    
603
  def testNoneValue(self):
604
    self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
605
                                              self.ipolicy, None) is None)
606

    
607
  def testAutoValue(self):
608
    self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
609
                                              self.ipolicy,
610
                                              constants.VALUE_AUTO) is None)
611

    
612
  def testNotDefined(self):
613
    self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT, None,
614
                                              self.ipolicy, 3) is None)
615

    
616
  def testNoMinDefined(self):
617
    self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE, None,
618
                                              self.ipolicy, 128) is None)
619

    
620
  def testNoMaxDefined(self):
621
    self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT, None,
622
                                                self.ipolicy, 16) is None)
623

    
624
  def testOutOfRange(self):
625
    for (name, val) in ((constants.ISPEC_MEM_SIZE, 64),
626
                        (constants.ISPEC_MEM_SIZE, 768),
627
                        (constants.ISPEC_DISK_SIZE, 4096),
628
                        (constants.ISPEC_DISK_COUNT, 0)):
629
      min_v = self.ipolicy[constants.ISPECS_MIN].get(name, val)
630
      max_v = self.ipolicy[constants.ISPECS_MAX].get(name, val)
631
      self.assertEqual(cmdlib._ComputeMinMaxSpec(name, None,
632
                                                 self.ipolicy, val),
633
                       "%s value %s is not in range [%s, %s]" %
634
                       (name, val,min_v, max_v))
635
      self.assertEqual(cmdlib._ComputeMinMaxSpec(name, "1",
636
                                                 self.ipolicy, val),
637
                       "%s/1 value %s is not in range [%s, %s]" %
638
                       (name, val,min_v, max_v))
639

    
640
  def test(self):
641
    for (name, val) in ((constants.ISPEC_MEM_SIZE, 256),
642
                        (constants.ISPEC_MEM_SIZE, 128),
643
                        (constants.ISPEC_MEM_SIZE, 512),
644
                        (constants.ISPEC_DISK_SIZE, 1024),
645
                        (constants.ISPEC_DISK_SIZE, 0),
646
                        (constants.ISPEC_DISK_COUNT, 1),
647
                        (constants.ISPEC_DISK_COUNT, 5)):
648
      self.assertTrue(cmdlib._ComputeMinMaxSpec(name, None, self.ipolicy, val)
649
                      is None)
650

    
651

    
652
def _ValidateComputeMinMaxSpec(name, *_):
653
  assert name in constants.ISPECS_PARAMETERS
654
  return None
655

    
656

    
657
def _NoDiskComputeMinMaxSpec(name, *_):
658
  if name == constants.ISPEC_DISK_COUNT:
659
    return name
660
  else:
661
    return None
662

    
663

    
664
class _SpecWrapper:
665
  def __init__(self, spec):
666
    self.spec = spec
667

    
668
  def ComputeMinMaxSpec(self, *args):
669
    return self.spec.pop(0)
670

    
671

    
672
class TestComputeIPolicySpecViolation(unittest.TestCase):
673
  # Minimal policy accepted by _ComputeIPolicySpecViolation()
674
  _MICRO_IPOL = {
675
    constants.IPOLICY_DTS: [constants.DT_PLAIN, constants.DT_DISKLESS],
676
    }
677

    
678
  def test(self):
679
    compute_fn = _ValidateComputeMinMaxSpec
680
    ret = cmdlib._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
681
                                              [1024], 1, constants.DT_PLAIN,
682
                                              _compute_fn=compute_fn)
683
    self.assertEqual(ret, [])
684

    
685
  def testDiskFull(self):
686
    compute_fn = _NoDiskComputeMinMaxSpec
687
    ret = cmdlib._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
688
                                              [1024], 1, constants.DT_PLAIN,
689
                                              _compute_fn=compute_fn)
690
    self.assertEqual(ret, [constants.ISPEC_DISK_COUNT])
691

    
692
  def testDiskLess(self):
693
    compute_fn = _NoDiskComputeMinMaxSpec
694
    ret = cmdlib._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
695
                                              [1024], 1, constants.DT_DISKLESS,
696
                                              _compute_fn=compute_fn)
697
    self.assertEqual(ret, [])
698

    
699
  def testWrongTemplates(self):
700
    compute_fn = _ValidateComputeMinMaxSpec
701
    ret = cmdlib._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
702
                                              [1024], 1, constants.DT_DRBD8,
703
                                              _compute_fn=compute_fn)
704
    self.assertEqual(len(ret), 1)
705
    self.assertTrue("Disk template" in ret[0])
706

    
707
  def testInvalidArguments(self):
708
    self.assertRaises(AssertionError, cmdlib._ComputeIPolicySpecViolation,
709
                      self._MICRO_IPOL, 1024, 1, 1, 1, [], 1,
710
                      constants.DT_PLAIN,)
711

    
712
  def testInvalidSpec(self):
713
    spec = _SpecWrapper([None, False, "foo", None, "bar", None])
714
    compute_fn = spec.ComputeMinMaxSpec
715
    ret = cmdlib._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
716
                                              [1024], 1, constants.DT_PLAIN,
717
                                              _compute_fn=compute_fn)
718
    self.assertEqual(ret, ["foo", "bar"])
719
    self.assertFalse(spec.spec)
720

    
721

    
722
class _StubComputeIPolicySpecViolation:
723
  def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
724
               spindle_use, disk_template):
725
    self.mem_size = mem_size
726
    self.cpu_count = cpu_count
727
    self.disk_count = disk_count
728
    self.nic_count = nic_count
729
    self.disk_sizes = disk_sizes
730
    self.spindle_use = spindle_use
731
    self.disk_template = disk_template
732

    
733
  def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
734
               spindle_use, disk_template):
735
    assert self.mem_size == mem_size
736
    assert self.cpu_count == cpu_count
737
    assert self.disk_count == disk_count
738
    assert self.nic_count == nic_count
739
    assert self.disk_sizes == disk_sizes
740
    assert self.spindle_use == spindle_use
741
    assert self.disk_template == disk_template
742

    
743
    return []
744

    
745

    
746
class _FakeConfigForComputeIPolicyInstanceViolation:
747
  def __init__(self, be):
748
    self.cluster = objects.Cluster(beparams={"default": be})
749

    
750
  def GetClusterInfo(self):
751
    return self.cluster
752

    
753

    
754
class TestComputeIPolicyInstanceViolation(unittest.TestCase):
755
  def test(self):
756
    beparams = {
757
      constants.BE_MAXMEM: 2048,
758
      constants.BE_VCPUS: 2,
759
      constants.BE_SPINDLE_USE: 4,
760
      }
761
    disks = [objects.Disk(size=512)]
762
    cfg = _FakeConfigForComputeIPolicyInstanceViolation(beparams)
763
    instance = objects.Instance(beparams=beparams, disks=disks, nics=[],
764
                                disk_template=constants.DT_PLAIN)
765
    stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 4,
766
                                            constants.DT_PLAIN)
767
    ret = cmdlib._ComputeIPolicyInstanceViolation(NotImplemented, instance,
768
                                                  cfg, _compute_fn=stub)
769
    self.assertEqual(ret, [])
770
    instance2 = objects.Instance(beparams={}, disks=disks, nics=[],
771
                                 disk_template=constants.DT_PLAIN)
772
    ret = cmdlib._ComputeIPolicyInstanceViolation(NotImplemented, instance2,
773
                                                  cfg, _compute_fn=stub)
774
    self.assertEqual(ret, [])
775

    
776

    
777
class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
778
  def test(self):
779
    ispec = {
780
      constants.ISPEC_MEM_SIZE: 2048,
781
      constants.ISPEC_CPU_COUNT: 2,
782
      constants.ISPEC_DISK_COUNT: 1,
783
      constants.ISPEC_DISK_SIZE: [512],
784
      constants.ISPEC_NIC_COUNT: 0,
785
      constants.ISPEC_SPINDLE_USE: 1,
786
      }
787
    stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 1,
788
                                            constants.DT_PLAIN)
789
    ret = cmdlib._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
790
                                                      constants.DT_PLAIN,
791
                                                      _compute_fn=stub)
792
    self.assertEqual(ret, [])
793

    
794

    
795
class _CallRecorder:
796
  def __init__(self, return_value=None):
797
    self.called = False
798
    self.return_value = return_value
799

    
800
  def __call__(self, *args):
801
    self.called = True
802
    return self.return_value
803

    
804

    
805
class TestComputeIPolicyNodeViolation(unittest.TestCase):
806
  def setUp(self):
807
    self.recorder = _CallRecorder(return_value=[])
808

    
809
  def testSameGroup(self):
810
    ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
811
                                              "foo", "foo", NotImplemented,
812
                                              _compute_fn=self.recorder)
813
    self.assertFalse(self.recorder.called)
814
    self.assertEqual(ret, [])
815

    
816
  def testDifferentGroup(self):
817
    ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
818
                                              "foo", "bar", NotImplemented,
819
                                              _compute_fn=self.recorder)
820
    self.assertTrue(self.recorder.called)
821
    self.assertEqual(ret, [])
822

    
823

    
824
class _FakeConfigForTargetNodeIPolicy:
825
  def __init__(self, node_info=NotImplemented):
826
    self._node_info = node_info
827

    
828
  def GetNodeInfo(self, _):
829
    return self._node_info
830

    
831

    
832
class TestCheckTargetNodeIPolicy(unittest.TestCase):
833
  def setUp(self):
834
    self.instance = objects.Instance(primary_node="blubb")
835
    self.target_node = objects.Node(group="bar")
836
    node_info = objects.Node(group="foo")
837
    fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
838
    self.lu = _FakeLU(cfg=fake_cfg)
839

    
840
  def testNoViolation(self):
841
    compute_recoder = _CallRecorder(return_value=[])
842
    cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
843
                                   self.target_node, NotImplemented,
844
                                   _compute_fn=compute_recoder)
845
    self.assertTrue(compute_recoder.called)
846
    self.assertEqual(self.lu.warning_log, [])
847

    
848
  def testNoIgnore(self):
849
    compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
850
    self.assertRaises(errors.OpPrereqError, cmdlib._CheckTargetNodeIPolicy,
851
                      self.lu, NotImplemented, self.instance, self.target_node,
852
                      NotImplemented, _compute_fn=compute_recoder)
853
    self.assertTrue(compute_recoder.called)
854
    self.assertEqual(self.lu.warning_log, [])
855

    
856
  def testIgnoreViolation(self):
857
    compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
858
    cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
859
                                   self.target_node, NotImplemented,
860
                                   ignore=True, _compute_fn=compute_recoder)
861
    self.assertTrue(compute_recoder.called)
862
    msg = ("Instance does not meet target node group's (bar) instance policy:"
863
           " mem_size not in range")
864
    self.assertEqual(self.lu.warning_log, [(msg, ())])
865

    
866

    
867
class TestApplyContainerMods(unittest.TestCase):
868
  def testEmptyContainer(self):
869
    container = []
870
    chgdesc = []
871
    cmdlib.ApplyContainerMods("test", container, chgdesc, [], None, None, None)
872
    self.assertEqual(container, [])
873
    self.assertEqual(chgdesc, [])
874

    
875
  def testAdd(self):
876
    container = []
877
    chgdesc = []
878
    mods = cmdlib.PrepareContainerMods([
879
      (constants.DDM_ADD, -1, "Hello"),
880
      (constants.DDM_ADD, -1, "World"),
881
      (constants.DDM_ADD, 0, "Start"),
882
      (constants.DDM_ADD, -1, "End"),
883
      ], None)
884
    cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
885
                              None, None, None)
886
    self.assertEqual(container, ["Start", "Hello", "World", "End"])
887
    self.assertEqual(chgdesc, [])
888

    
889
    mods = cmdlib.PrepareContainerMods([
890
      (constants.DDM_ADD, 0, "zero"),
891
      (constants.DDM_ADD, 3, "Added"),
892
      (constants.DDM_ADD, 5, "four"),
893
      (constants.DDM_ADD, 7, "xyz"),
894
      ], None)
895
    cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
896
                              None, None, None)
897
    self.assertEqual(container,
898
                     ["zero", "Start", "Hello", "Added", "World", "four",
899
                      "End", "xyz"])
900
    self.assertEqual(chgdesc, [])
901

    
902
    for idx in [-2, len(container) + 1]:
903
      mods = cmdlib.PrepareContainerMods([
904
        (constants.DDM_ADD, idx, "error"),
905
        ], None)
906
      self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
907
                        "test", container, None, mods, None, None, None)
908

    
909
  def testRemoveError(self):
910
    for idx in [0, 1, 2, 100, -1, -4]:
911
      mods = cmdlib.PrepareContainerMods([
912
        (constants.DDM_REMOVE, idx, None),
913
        ], None)
914
      self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
915
                        "test", [], None, mods, None, None, None)
916

    
917
    mods = cmdlib.PrepareContainerMods([
918
      (constants.DDM_REMOVE, 0, object()),
919
      ], None)
920
    self.assertRaises(AssertionError, cmdlib.ApplyContainerMods,
921
                      "test", [""], None, mods, None, None, None)
922

    
923
  def testAddError(self):
924
    for idx in range(-100, -1) + [100]:
925
      mods = cmdlib.PrepareContainerMods([
926
        (constants.DDM_ADD, idx, None),
927
        ], None)
928
      self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
929
                        "test", [], None, mods, None, None, None)
930

    
931
  def testRemove(self):
932
    container = ["item 1", "item 2"]
933
    mods = cmdlib.PrepareContainerMods([
934
      (constants.DDM_ADD, -1, "aaa"),
935
      (constants.DDM_REMOVE, -1, None),
936
      (constants.DDM_ADD, -1, "bbb"),
937
      ], None)
938
    chgdesc = []
939
    cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
940
                              None, None, None)
941
    self.assertEqual(container, ["item 1", "item 2", "bbb"])
942
    self.assertEqual(chgdesc, [
943
      ("test/2", "remove"),
944
      ])
945

    
946
  def testModify(self):
947
    container = ["item 1", "item 2"]
948
    mods = cmdlib.PrepareContainerMods([
949
      (constants.DDM_MODIFY, -1, "a"),
950
      (constants.DDM_MODIFY, 0, "b"),
951
      (constants.DDM_MODIFY, 1, "c"),
952
      ], None)
953
    chgdesc = []
954
    cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
955
                              None, None, None)
956
    self.assertEqual(container, ["item 1", "item 2"])
957
    self.assertEqual(chgdesc, [])
958

    
959
    for idx in [-2, len(container) + 1]:
960
      mods = cmdlib.PrepareContainerMods([
961
        (constants.DDM_MODIFY, idx, "error"),
962
        ], None)
963
      self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
964
                        "test", container, None, mods, None, None, None)
965

    
966
  class _PrivateData:
967
    def __init__(self):
968
      self.data = None
969

    
970
  @staticmethod
971
  def _CreateTestFn(idx, params, private):
972
    private.data = ("add", idx, params)
973
    return ((100 * idx, params), [
974
      ("test/%s" % idx, hex(idx)),
975
      ])
976

    
977
  @staticmethod
978
  def _ModifyTestFn(idx, item, params, private):
979
    private.data = ("modify", idx, params)
980
    return [
981
      ("test/%s" % idx, "modify %s" % params),
982
      ]
983

    
984
  @staticmethod
985
  def _RemoveTestFn(idx, item, private):
986
    private.data = ("remove", idx, item)
987

    
988
  def testAddWithCreateFunction(self):
989
    container = []
990
    chgdesc = []
991
    mods = cmdlib.PrepareContainerMods([
992
      (constants.DDM_ADD, -1, "Hello"),
993
      (constants.DDM_ADD, -1, "World"),
994
      (constants.DDM_ADD, 0, "Start"),
995
      (constants.DDM_ADD, -1, "End"),
996
      (constants.DDM_REMOVE, 2, None),
997
      (constants.DDM_MODIFY, -1, "foobar"),
998
      (constants.DDM_REMOVE, 2, None),
999
      (constants.DDM_ADD, 1, "More"),
1000
      ], self._PrivateData)
1001
    cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
1002
      self._CreateTestFn, self._ModifyTestFn, self._RemoveTestFn)
1003
    self.assertEqual(container, [
1004
      (000, "Start"),
1005
      (100, "More"),
1006
      (000, "Hello"),
1007
      ])
1008
    self.assertEqual(chgdesc, [
1009
      ("test/0", "0x0"),
1010
      ("test/1", "0x1"),
1011
      ("test/0", "0x0"),
1012
      ("test/3", "0x3"),
1013
      ("test/2", "remove"),
1014
      ("test/2", "modify foobar"),
1015
      ("test/2", "remove"),
1016
      ("test/1", "0x1")
1017
      ])
1018
    self.assertTrue(compat.all(op == private.data[0]
1019
                               for (op, _, _, private) in mods))
1020
    self.assertEqual([private.data for (op, _, _, private) in mods], [
1021
      ("add", 0, "Hello"),
1022
      ("add", 1, "World"),
1023
      ("add", 0, "Start"),
1024
      ("add", 3, "End"),
1025
      ("remove", 2, (100, "World")),
1026
      ("modify", 2, "foobar"),
1027
      ("remove", 2, (300, "End")),
1028
      ("add", 1, "More"),
1029
      ])
1030

    
1031

    
1032
class _FakeConfigForGenDiskTemplate:
1033
  def __init__(self):
1034
    self._unique_id = itertools.count()
1035
    self._drbd_minor = itertools.count(20)
1036
    self._port = itertools.count(constants.FIRST_DRBD_PORT)
1037
    self._secret = itertools.count()
1038

    
1039
  def GetVGName(self):
1040
    return "testvg"
1041

    
1042
  def GenerateUniqueID(self, ec_id):
1043
    return "ec%s-uq%s" % (ec_id, self._unique_id.next())
1044

    
1045
  def AllocateDRBDMinor(self, nodes, instance):
1046
    return [self._drbd_minor.next()
1047
            for _ in nodes]
1048

    
1049
  def AllocatePort(self):
1050
    return self._port.next()
1051

    
1052
  def GenerateDRBDSecret(self, ec_id):
1053
    return "ec%s-secret%s" % (ec_id, self._secret.next())
1054

    
1055
  def GetInstanceInfo(self, _):
1056
    return "foobar"
1057

    
1058

    
1059
class _FakeProcForGenDiskTemplate:
1060
  def GetECId(self):
1061
    return 0
1062

    
1063

    
1064
class TestGenerateDiskTemplate(unittest.TestCase):
1065
  def setUp(self):
1066
    nodegroup = objects.NodeGroup(name="ng")
1067
    nodegroup.UpgradeConfig()
1068

    
1069
    cfg = _FakeConfigForGenDiskTemplate()
1070
    proc = _FakeProcForGenDiskTemplate()
1071

    
1072
    self.lu = _FakeLU(cfg=cfg, proc=proc)
1073
    self.nodegroup = nodegroup
1074

    
1075
  @staticmethod
1076
  def GetDiskParams():
1077
    return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1078

    
1079
  def testWrongDiskTemplate(self):
1080
    gdt = cmdlib._GenerateDiskTemplate
1081
    disk_template = "##unknown##"
1082

    
1083
    assert disk_template not in constants.DISK_TEMPLATES
1084

    
1085
    self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
1086
                      "inst26831.example.com", "node30113.example.com", [], [],
1087
                      NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1088
                      self.GetDiskParams())
1089

    
1090
  def testDiskless(self):
1091
    gdt = cmdlib._GenerateDiskTemplate
1092

    
1093
    result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1094
                 "node30113.example.com", [], [],
1095
                 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1096
                 self.GetDiskParams())
1097
    self.assertEqual(result, [])
1098

    
1099
  def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1100
                       file_storage_dir=NotImplemented,
1101
                       file_driver=NotImplemented,
1102
                       req_file_storage=NotImplemented,
1103
                       req_shr_file_storage=NotImplemented):
1104
    gdt = cmdlib._GenerateDiskTemplate
1105

    
1106
    map(lambda params: utils.ForceDictType(params,
1107
                                           constants.IDISK_PARAMS_TYPES),
1108
        disk_info)
1109

    
1110
    # Check if non-empty list of secondaries is rejected
1111
    self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1112
                      template, "inst25088.example.com",
1113
                      "node185.example.com", ["node323.example.com"], [],
1114
                      NotImplemented, NotImplemented, base_index,
1115
                      self.lu.LogInfo, self.GetDiskParams(),
1116
                      _req_file_storage=req_file_storage,
1117
                      _req_shr_file_storage=req_shr_file_storage)
1118

    
1119
    result = gdt(self.lu, template, "inst21662.example.com",
1120
                 "node21741.example.com", [],
1121
                 disk_info, file_storage_dir, file_driver, base_index,
1122
                 self.lu.LogInfo, self.GetDiskParams(),
1123
                 _req_file_storage=req_file_storage,
1124
                 _req_shr_file_storage=req_shr_file_storage)
1125

    
1126
    for (idx, disk) in enumerate(result):
1127
      self.assertTrue(isinstance(disk, objects.Disk))
1128
      self.assertEqual(disk.dev_type, exp_dev_type)
1129
      self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1130
      self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1131
      self.assertTrue(disk.children is None)
1132

    
1133
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1134
    cmdlib._UpdateIvNames(base_index, result)
1135
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1136

    
1137
    return result
1138

    
1139
  def _CheckIvNames(self, disks, base_index, end_index):
1140
    self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1141
                     ["disk/%s" % i for i in range(base_index, end_index)])
1142

    
1143
  def testPlain(self):
1144
    disk_info = [{
1145
      constants.IDISK_SIZE: 1024,
1146
      constants.IDISK_MODE: constants.DISK_RDWR,
1147
      }, {
1148
      constants.IDISK_SIZE: 4096,
1149
      constants.IDISK_VG: "othervg",
1150
      constants.IDISK_MODE: constants.DISK_RDWR,
1151
      }]
1152

    
1153
    result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1154
                                   constants.LD_LV)
1155

    
1156
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1157
      ("testvg", "ec0-uq0.disk3"),
1158
      ("othervg", "ec0-uq1.disk4"),
1159
      ])
1160

    
1161
  @staticmethod
1162
  def _AllowFileStorage():
1163
    pass
1164

    
1165
  @staticmethod
1166
  def _ForbidFileStorage():
1167
    raise errors.OpPrereqError("Disallowed in test")
1168

    
1169
  def testFile(self):
1170
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1171
                      constants.DT_FILE, [], 0, NotImplemented,
1172
                      req_file_storage=self._ForbidFileStorage)
1173
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1174
                      constants.DT_SHARED_FILE, [], 0, NotImplemented,
1175
                      req_shr_file_storage=self._ForbidFileStorage)
1176

    
1177
    for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1178
      disk_info = [{
1179
        constants.IDISK_SIZE: 80 * 1024,
1180
        constants.IDISK_MODE: constants.DISK_RDONLY,
1181
        }, {
1182
        constants.IDISK_SIZE: 4096,
1183
        constants.IDISK_MODE: constants.DISK_RDWR,
1184
        }, {
1185
        constants.IDISK_SIZE: 6 * 1024,
1186
        constants.IDISK_MODE: constants.DISK_RDWR,
1187
        }]
1188

    
1189
      result = self._TestTrivialDisk(disk_template, disk_info, 2,
1190
        constants.LD_FILE, file_storage_dir="/tmp",
1191
        file_driver=constants.FD_BLKTAP,
1192
        req_file_storage=self._AllowFileStorage,
1193
        req_shr_file_storage=self._AllowFileStorage)
1194

    
1195
      self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1196
        (constants.FD_BLKTAP, "/tmp/disk2"),
1197
        (constants.FD_BLKTAP, "/tmp/disk3"),
1198
        (constants.FD_BLKTAP, "/tmp/disk4"),
1199
        ])
1200

    
1201
  def testBlock(self):
1202
    disk_info = [{
1203
      constants.IDISK_SIZE: 8 * 1024,
1204
      constants.IDISK_MODE: constants.DISK_RDWR,
1205
      constants.IDISK_ADOPT: "/tmp/some/block/dev",
1206
      }]
1207

    
1208
    result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1209
                                   constants.LD_BLOCKDEV)
1210

    
1211
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1212
      (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1213
      ])
1214

    
1215
  def testRbd(self):
1216
    disk_info = [{
1217
      constants.IDISK_SIZE: 8 * 1024,
1218
      constants.IDISK_MODE: constants.DISK_RDONLY,
1219
      }, {
1220
      constants.IDISK_SIZE: 100 * 1024,
1221
      constants.IDISK_MODE: constants.DISK_RDWR,
1222
      }]
1223

    
1224
    result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1225
                                   constants.LD_RBD)
1226

    
1227
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1228
      ("rbd", "ec0-uq0.rbd.disk0"),
1229
      ("rbd", "ec0-uq1.rbd.disk1"),
1230
      ])
1231

    
1232
  def testDrbd8(self):
1233
    gdt = cmdlib._GenerateDiskTemplate
1234
    drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1235
    drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1236

    
1237
    disk_info = [{
1238
      constants.IDISK_SIZE: 1024,
1239
      constants.IDISK_MODE: constants.DISK_RDWR,
1240
      }, {
1241
      constants.IDISK_SIZE: 100 * 1024,
1242
      constants.IDISK_MODE: constants.DISK_RDONLY,
1243
      constants.IDISK_METAVG: "metavg",
1244
      }, {
1245
      constants.IDISK_SIZE: 4096,
1246
      constants.IDISK_MODE: constants.DISK_RDWR,
1247
      constants.IDISK_VG: "vgxyz",
1248
      },
1249
      ]
1250

    
1251
    exp_logical_ids = [[
1252
      (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1253
      (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1254
      ], [
1255
      (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1256
      ("metavg", "ec0-uq1.disk1_meta"),
1257
      ], [
1258
      ("vgxyz", "ec0-uq2.disk2_data"),
1259
      (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1260
      ]]
1261

    
1262
    assert len(exp_logical_ids) == len(disk_info)
1263

    
1264
    map(lambda params: utils.ForceDictType(params,
1265
                                           constants.IDISK_PARAMS_TYPES),
1266
        disk_info)
1267

    
1268
    # Check if empty list of secondaries is rejected
1269
    self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1270
                      "inst827.example.com", "node1334.example.com", [],
1271
                      disk_info, NotImplemented, NotImplemented, 0,
1272
                      self.lu.LogInfo, self.GetDiskParams())
1273

    
1274
    result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1275
                 "node1334.example.com", ["node12272.example.com"],
1276
                 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1277
                 self.GetDiskParams())
1278

    
1279
    for (idx, disk) in enumerate(result):
1280
      self.assertTrue(isinstance(disk, objects.Disk))
1281
      self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1282
      self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1283
      self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1284

    
1285
      for child in disk.children:
1286
        self.assertTrue(isinstance(disk, objects.Disk))
1287
        self.assertEqual(child.dev_type, constants.LD_LV)
1288
        self.assertTrue(child.children is None)
1289

    
1290
      self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1291
                       exp_logical_ids[idx])
1292

    
1293
      self.assertEqual(len(disk.children), 2)
1294
      self.assertEqual(disk.children[0].size, disk.size)
1295
      self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1296

    
1297
    self._CheckIvNames(result, 0, len(disk_info))
1298
    cmdlib._UpdateIvNames(0, result)
1299
    self._CheckIvNames(result, 0, len(disk_info))
1300

    
1301
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1302
      ("node1334.example.com", "node12272.example.com",
1303
       constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1304
      ("node1334.example.com", "node12272.example.com",
1305
       constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1306
      ("node1334.example.com", "node12272.example.com",
1307
       constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1308
      ])
1309

    
1310

    
1311
class _ConfigForDiskWipe:
1312
  def __init__(self, exp_node):
1313
    self._exp_node = exp_node
1314

    
1315
  def SetDiskID(self, device, node):
1316
    assert isinstance(device, objects.Disk)
1317
    assert node == self._exp_node
1318

    
1319

    
1320
class _RpcForDiskWipe:
1321
  def __init__(self, exp_node, pause_cb, wipe_cb):
1322
    self._exp_node = exp_node
1323
    self._pause_cb = pause_cb
1324
    self._wipe_cb = wipe_cb
1325

    
1326
  def call_blockdev_pause_resume_sync(self, node, disks, pause):
1327
    assert node == self._exp_node
1328
    return rpc.RpcResult(data=self._pause_cb(disks, pause))
1329

    
1330
  def call_blockdev_wipe(self, node, bdev, offset, size):
1331
    assert node == self._exp_node
1332
    return rpc.RpcResult(data=self._wipe_cb(bdev, offset, size))
1333

    
1334

    
1335
class _DiskPauseTracker:
1336
  def __init__(self):
1337
    self.history = []
1338

    
1339
  def __call__(self, (disks, instance), pause):
1340
    assert not (set(disks) - set(instance.disks))
1341

    
1342
    self.history.extend((i.logical_id, i.size, pause)
1343
                        for i in disks)
1344

    
1345
    return (True, [True] * len(disks))
1346

    
1347

    
1348
class _DiskWipeProgressTracker:
1349
  def __init__(self, start_offset):
1350
    self._start_offset = start_offset
1351
    self.progress = {}
1352

    
1353
  def __call__(self, (disk, _), offset, size):
1354
    assert isinstance(offset, (long, int))
1355
    assert isinstance(size, (long, int))
1356

    
1357
    max_chunk_size = (disk.size / 100.0 * constants.MIN_WIPE_CHUNK_PERCENT)
1358

    
1359
    assert offset >= self._start_offset
1360
    assert (offset + size) <= disk.size
1361

    
1362
    assert size > 0
1363
    assert size <= constants.MAX_WIPE_CHUNK
1364
    assert size <= max_chunk_size
1365

    
1366
    assert offset == self._start_offset or disk.logical_id in self.progress
1367

    
1368
    # Keep track of progress
1369
    cur_progress = self.progress.setdefault(disk.logical_id, self._start_offset)
1370

    
1371
    assert cur_progress == offset
1372

    
1373
    # Record progress
1374
    self.progress[disk.logical_id] += size
1375

    
1376
    return (True, None)
1377

    
1378

    
1379
class TestWipeDisks(unittest.TestCase):
1380
  def _FailingPauseCb(self, (disks, _), pause):
1381
    self.assertEqual(len(disks), 3)
1382
    self.assertTrue(pause)
1383
    # Simulate an RPC error
1384
    return (False, "error")
1385

    
1386
  def testPauseFailure(self):
1387
    node_name = "node1372.example.com"
1388

    
1389
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, self._FailingPauseCb,
1390
                                     NotImplemented),
1391
                 cfg=_ConfigForDiskWipe(node_name))
1392

    
1393
    disks = [
1394
      objects.Disk(dev_type=constants.LD_LV),
1395
      objects.Disk(dev_type=constants.LD_LV),
1396
      objects.Disk(dev_type=constants.LD_LV),
1397
      ]
1398

    
1399
    instance = objects.Instance(name="inst21201",
1400
                                primary_node=node_name,
1401
                                disk_template=constants.DT_PLAIN,
1402
                                disks=disks)
1403

    
1404
    self.assertRaises(errors.OpExecError, cmdlib._WipeDisks, lu, instance)
1405

    
1406
  def _FailingWipeCb(self, (disk, _), offset, size):
1407
    # This should only ever be called for the first disk
1408
    self.assertEqual(disk.logical_id, "disk0")
1409
    return (False, None)
1410

    
1411
  def testFailingWipe(self):
1412
    node_name = "node13445.example.com"
1413
    pt = _DiskPauseTracker()
1414

    
1415
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pt, self._FailingWipeCb),
1416
                 cfg=_ConfigForDiskWipe(node_name))
1417

    
1418
    disks = [
1419
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1420
                   size=100 * 1024),
1421
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1422
                   size=500 * 1024),
1423
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=256),
1424
      ]
1425

    
1426
    instance = objects.Instance(name="inst562",
1427
                                primary_node=node_name,
1428
                                disk_template=constants.DT_PLAIN,
1429
                                disks=disks)
1430

    
1431
    try:
1432
      cmdlib._WipeDisks(lu, instance)
1433
    except errors.OpExecError, err:
1434
      self.assertTrue(str(err), "Could not wipe disk 0 at offset 0 ")
1435
    else:
1436
      self.fail("Did not raise exception")
1437

    
1438
    # Check if all disks were paused and resumed
1439
    self.assertEqual(pt.history, [
1440
      ("disk0", 100 * 1024, True),
1441
      ("disk1", 500 * 1024, True),
1442
      ("disk2", 256, True),
1443
      ("disk0", 100 * 1024, False),
1444
      ("disk1", 500 * 1024, False),
1445
      ("disk2", 256, False),
1446
      ])
1447

    
1448
  def _PrepareWipeTest(self, start_offset, disks):
1449
    node_name = "node-with-offset%s.example.com" % start_offset
1450
    pauset = _DiskPauseTracker()
1451
    progresst = _DiskWipeProgressTracker(start_offset)
1452

    
1453
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pauset, progresst),
1454
                 cfg=_ConfigForDiskWipe(node_name))
1455

    
1456
    instance = objects.Instance(name="inst3560",
1457
                                primary_node=node_name,
1458
                                disk_template=constants.DT_PLAIN,
1459
                                disks=disks)
1460

    
1461
    return (lu, instance, pauset, progresst)
1462

    
1463
  def testNormalWipe(self):
1464
    disks = [
1465
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk0", size=1024),
1466
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1467
                   size=500 * 1024),
1468
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=128),
1469
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk3",
1470
                   size=constants.MAX_WIPE_CHUNK),
1471
      ]
1472

    
1473
    (lu, instance, pauset, progresst) = self._PrepareWipeTest(0, disks)
1474

    
1475
    cmdlib._WipeDisks(lu, instance)
1476

    
1477
    self.assertEqual(pauset.history, [
1478
      ("disk0", 1024, True),
1479
      ("disk1", 500 * 1024, True),
1480
      ("disk2", 128, True),
1481
      ("disk3", constants.MAX_WIPE_CHUNK, True),
1482
      ("disk0", 1024, False),
1483
      ("disk1", 500 * 1024, False),
1484
      ("disk2", 128, False),
1485
      ("disk3", constants.MAX_WIPE_CHUNK, False),
1486
      ])
1487

    
1488
    # Ensure the complete disk has been wiped
1489
    self.assertEqual(progresst.progress,
1490
                     dict((i.logical_id, i.size) for i in disks))
1491

    
1492
  def testWipeWithStartOffset(self):
1493
    for start_offset in [0, 280, 8895, 1563204]:
1494
      disks = [
1495
        objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1496
                     size=128),
1497
        objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1498
                     size=start_offset + (100 * 1024)),
1499
        ]
1500

    
1501
      (lu, instance, pauset, progresst) = \
1502
        self._PrepareWipeTest(start_offset, disks)
1503

    
1504
      # Test start offset with only one disk
1505
      cmdlib._WipeDisks(lu, instance,
1506
                        disks=[(1, disks[1], start_offset)])
1507

    
1508
      # Only the second disk may have been paused and wiped
1509
      self.assertEqual(pauset.history, [
1510
        ("disk1", start_offset + (100 * 1024), True),
1511
        ("disk1", start_offset + (100 * 1024), False),
1512
        ])
1513
      self.assertEqual(progresst.progress, {
1514
        "disk1": disks[1].size,
1515
        })
1516

    
1517

    
1518
class TestDiskSizeInBytesToMebibytes(unittest.TestCase):
1519
  def testLessThanOneMebibyte(self):
1520
    for i in [1, 2, 7, 512, 1000, 1023]:
1521
      lu = _FakeLU()
1522
      result = cmdlib._DiskSizeInBytesToMebibytes(lu, i)
1523
      self.assertEqual(result, 1)
1524
      self.assertEqual(len(lu.warning_log), 1)
1525
      self.assertEqual(len(lu.warning_log[0]), 2)
1526
      (_, (warnsize, )) = lu.warning_log[0]
1527
      self.assertEqual(warnsize, (1024 * 1024) - i)
1528

    
1529
  def testEven(self):
1530
    for i in [1, 2, 7, 512, 1000, 1023]:
1531
      lu = _FakeLU()
1532
      result = cmdlib._DiskSizeInBytesToMebibytes(lu, i * 1024 * 1024)
1533
      self.assertEqual(result, i)
1534
      self.assertFalse(lu.warning_log)
1535

    
1536
  def testLargeNumber(self):
1537
    for i in [1, 2, 7, 512, 1000, 1023, 2724, 12420]:
1538
      for j in [1, 2, 486, 326, 986, 1023]:
1539
        lu = _FakeLU()
1540
        size = (1024 * 1024 * i) + j
1541
        result = cmdlib._DiskSizeInBytesToMebibytes(lu, size)
1542
        self.assertEqual(result, i + 1, msg="Amount was not rounded up")
1543
        self.assertEqual(len(lu.warning_log), 1)
1544
        self.assertEqual(len(lu.warning_log[0]), 2)
1545
        (_, (warnsize, )) = lu.warning_log[0]
1546
        self.assertEqual(warnsize, (1024 * 1024) - j)
1547

    
1548

    
1549
class TestCopyLockList(unittest.TestCase):
1550
  def test(self):
1551
    self.assertEqual(cmdlib._CopyLockList([]), [])
1552
    self.assertEqual(cmdlib._CopyLockList(None), None)
1553
    self.assertEqual(cmdlib._CopyLockList(locking.ALL_SET), locking.ALL_SET)
1554

    
1555
    names = ["foo", "bar"]
1556
    output = cmdlib._CopyLockList(names)
1557
    self.assertEqual(names, output)
1558
    self.assertNotEqual(id(names), id(output), msg="List was not copied")
1559

    
1560

    
1561
class TestCheckOpportunisticLocking(unittest.TestCase):
1562
  class OpTest(opcodes.OpCode):
1563
    OP_PARAMS = [
1564
      opcodes._POpportunisticLocking,
1565
      opcodes._PIAllocFromDesc(""),
1566
      ]
1567

    
1568
  @classmethod
1569
  def _MakeOp(cls, **kwargs):
1570
    op = cls.OpTest(**kwargs)
1571
    op.Validate(True)
1572
    return op
1573

    
1574
  def testMissingAttributes(self):
1575
    self.assertRaises(AttributeError, cmdlib._CheckOpportunisticLocking,
1576
                      object())
1577

    
1578
  def testDefaults(self):
1579
    op = self._MakeOp()
1580
    cmdlib._CheckOpportunisticLocking(op)
1581

    
1582
  def test(self):
1583
    for iallocator in [None, "something", "other"]:
1584
      for opplock in [False, True]:
1585
        op = self._MakeOp(iallocator=iallocator, opportunistic_locking=opplock)
1586
        if opplock and not iallocator:
1587
          self.assertRaises(errors.OpPrereqError,
1588
                            cmdlib._CheckOpportunisticLocking, op)
1589
        else:
1590
          cmdlib._CheckOpportunisticLocking(op)
1591

    
1592

    
1593
class _OpTestVerifyErrors(opcodes.OpCode):
1594
  OP_PARAMS = [
1595
    opcodes._PDebugSimulateErrors,
1596
    opcodes._PErrorCodes,
1597
    opcodes._PIgnoreErrors,
1598
    ]
1599

    
1600

    
1601
class _LuTestVerifyErrors(cmdlib._VerifyErrors):
1602
  def __init__(self, **kwargs):
1603
    cmdlib._VerifyErrors.__init__(self)
1604
    self.op = _OpTestVerifyErrors(**kwargs)
1605
    self.op.Validate(True)
1606
    self.msglist = []
1607
    self._feedback_fn = self.msglist.append
1608
    self.bad = False
1609

    
1610
  def DispatchCallError(self, which, *args, **kwargs):
1611
    if which:
1612
      self._Error(*args, **kwargs)
1613
    else:
1614
      self._ErrorIf(True, *args, **kwargs)
1615

    
1616
  def CallErrorIf(self, c, *args, **kwargs):
1617
    self._ErrorIf(c, *args, **kwargs)
1618

    
1619

    
1620
class TestVerifyErrors(unittest.TestCase):
1621
  # Fake cluster-verify error code structures; we use two arbitary real error
1622
  # codes to pass validation of ignore_errors
1623
  (_, _ERR1ID, _) = constants.CV_ECLUSTERCFG
1624
  _NODESTR = "node"
1625
  _NODENAME = "mynode"
1626
  _ERR1CODE = (_NODESTR, _ERR1ID, "Error one")
1627
  (_, _ERR2ID, _) = constants.CV_ECLUSTERCERT
1628
  _INSTSTR = "instance"
1629
  _INSTNAME = "myinstance"
1630
  _ERR2CODE = (_INSTSTR, _ERR2ID, "Error two")
1631
  # Arguments used to call _Error() or _ErrorIf()
1632
  _ERR1ARGS = (_ERR1CODE, _NODENAME, "Error1 is %s", "an error")
1633
  _ERR2ARGS = (_ERR2CODE, _INSTNAME, "Error2 has no argument")
1634
  # Expected error messages
1635
  _ERR1MSG = _ERR1ARGS[2] % _ERR1ARGS[3]
1636
  _ERR2MSG = _ERR2ARGS[2]
1637

    
1638
  def testNoError(self):
1639
    lu = _LuTestVerifyErrors()
1640
    lu.CallErrorIf(False, self._ERR1CODE, *self._ERR1ARGS)
1641
    self.assertFalse(lu.bad)
1642
    self.assertFalse(lu.msglist)
1643

    
1644
  def _InitTest(self, **kwargs):
1645
    self.lu1 = _LuTestVerifyErrors(**kwargs)
1646
    self.lu2 = _LuTestVerifyErrors(**kwargs)
1647

    
1648
  def _CallError(self, *args, **kwargs):
1649
    # Check that _Error() and _ErrorIf() produce the same results
1650
    self.lu1.DispatchCallError(True, *args, **kwargs)
1651
    self.lu2.DispatchCallError(False, *args, **kwargs)
1652
    self.assertEqual(self.lu1.bad, self.lu2.bad)
1653
    self.assertEqual(self.lu1.msglist, self.lu2.msglist)
1654
    # Test-specific checks are made on one LU
1655
    return self.lu1
1656

    
1657
  def _checkMsgCommon(self, logstr, errmsg, itype, item, warning):
1658
    self.assertTrue(errmsg in logstr)
1659
    if warning:
1660
      self.assertTrue("WARNING" in logstr)
1661
    else:
1662
      self.assertTrue("ERROR" in logstr)
1663
    self.assertTrue(itype in logstr)
1664
    self.assertTrue(item in logstr)
1665

    
1666
  def _checkMsg1(self, logstr, warning=False):
1667
    self._checkMsgCommon(logstr, self._ERR1MSG, self._NODESTR,
1668
                         self._NODENAME, warning)
1669

    
1670
  def _checkMsg2(self, logstr, warning=False):
1671
    self._checkMsgCommon(logstr, self._ERR2MSG, self._INSTSTR,
1672
                         self._INSTNAME, warning)
1673

    
1674
  def testPlain(self):
1675
    self._InitTest()
1676
    lu = self._CallError(*self._ERR1ARGS)
1677
    self.assertTrue(lu.bad)
1678
    self.assertEqual(len(lu.msglist), 1)
1679
    self._checkMsg1(lu.msglist[0])
1680

    
1681
  def testMultiple(self):
1682
    self._InitTest()
1683
    self._CallError(*self._ERR1ARGS)
1684
    lu = self._CallError(*self._ERR2ARGS)
1685
    self.assertTrue(lu.bad)
1686
    self.assertEqual(len(lu.msglist), 2)
1687
    self._checkMsg1(lu.msglist[0])
1688
    self._checkMsg2(lu.msglist[1])
1689

    
1690
  def testIgnore(self):
1691
    self._InitTest(ignore_errors=[self._ERR1ID])
1692
    lu = self._CallError(*self._ERR1ARGS)
1693
    self.assertFalse(lu.bad)
1694
    self.assertEqual(len(lu.msglist), 1)
1695
    self._checkMsg1(lu.msglist[0], warning=True)
1696

    
1697
  def testWarning(self):
1698
    self._InitTest()
1699
    lu = self._CallError(*self._ERR1ARGS,
1700
                         code=_LuTestVerifyErrors.ETYPE_WARNING)
1701
    self.assertFalse(lu.bad)
1702
    self.assertEqual(len(lu.msglist), 1)
1703
    self._checkMsg1(lu.msglist[0], warning=True)
1704

    
1705
  def testWarning2(self):
1706
    self._InitTest()
1707
    self._CallError(*self._ERR1ARGS)
1708
    lu = self._CallError(*self._ERR2ARGS,
1709
                         code=_LuTestVerifyErrors.ETYPE_WARNING)
1710
    self.assertTrue(lu.bad)
1711
    self.assertEqual(len(lu.msglist), 2)
1712
    self._checkMsg1(lu.msglist[0])
1713
    self._checkMsg2(lu.msglist[1], warning=True)
1714

    
1715
  def testDebugSimulate(self):
1716
    lu = _LuTestVerifyErrors(debug_simulate_errors=True)
1717
    lu.CallErrorIf(False, *self._ERR1ARGS)
1718
    self.assertTrue(lu.bad)
1719
    self.assertEqual(len(lu.msglist), 1)
1720
    self._checkMsg1(lu.msglist[0])
1721

    
1722
  def testErrCodes(self):
1723
    self._InitTest(error_codes=True)
1724
    lu = self._CallError(*self._ERR1ARGS)
1725
    self.assertTrue(lu.bad)
1726
    self.assertEqual(len(lu.msglist), 1)
1727
    self._checkMsg1(lu.msglist[0])
1728
    self.assertTrue(self._ERR1ID in lu.msglist[0])
1729

    
1730

    
1731
class TestGetUpdatedIPolicy(unittest.TestCase):
1732
  """Tests for cmdlib._GetUpdatedIPolicy()"""
1733
  _OLD_CLUSTER_POLICY = {
1734
    constants.IPOLICY_VCPU_RATIO: 1.5,
1735
    constants.ISPECS_MIN: {
1736
      constants.ISPEC_MEM_SIZE: 20,
1737
      constants.ISPEC_CPU_COUNT: 2,
1738
      },
1739
    constants.ISPECS_MAX: {},
1740
    constants.ISPECS_STD: {},
1741
    }
1742

    
1743
  _OLD_GROUP_POLICY = {
1744
    constants.IPOLICY_SPINDLE_RATIO: 2.5,
1745
    constants.ISPECS_MIN: {
1746
      constants.ISPEC_DISK_SIZE: 20,
1747
      constants.ISPEC_NIC_COUNT: 2,
1748
      },
1749
    constants.ISPECS_MAX: {},
1750
    }
1751

    
1752
  def _TestSetSpecs(self, old_policy, isgroup):
1753
    ispec_key = constants.ISPECS_MIN
1754
    diff_ispec = {
1755
      constants.ISPEC_MEM_SIZE: 50,
1756
      constants.ISPEC_DISK_SIZE: 30,
1757
      }
1758
    diff_policy = {
1759
      ispec_key: diff_ispec
1760
      }
1761
    new_policy = cmdlib._GetUpdatedIPolicy(old_policy, diff_policy,
1762
                                           group_policy=isgroup)
1763
    new_ispec = new_policy[ispec_key]
1764
    for key in diff_ispec:
1765
      self.assertTrue(key in new_ispec)
1766
      self.assertEqual(new_ispec[key], diff_ispec[key])
1767
    for key in old_policy:
1768
      if not key in diff_policy:
1769
        self.assertTrue(key in new_policy)
1770
        self.assertEqual(new_policy[key], old_policy[key])
1771
    old_ispec = old_policy[ispec_key]
1772
    for key in old_ispec:
1773
      if not key in diff_ispec:
1774
        self.assertTrue(key in new_ispec)
1775
        self.assertEqual(new_ispec[key], old_ispec[key])
1776

    
1777
  def _TestSet(self, old_policy, isgroup):
1778
    diff_policy = {
1779
      constants.IPOLICY_VCPU_RATIO: 3,
1780
      constants.IPOLICY_SPINDLE_RATIO: 1.9,
1781
      }
1782
    new_policy = cmdlib._GetUpdatedIPolicy(old_policy, diff_policy,
1783
                                           group_policy=isgroup)
1784
    for key in diff_policy:
1785
      self.assertTrue(key in new_policy)
1786
      self.assertEqual(new_policy[key], diff_policy[key])
1787
    for key in old_policy:
1788
      if not key in diff_policy:
1789
        self.assertTrue(key in new_policy)
1790
        self.assertEqual(new_policy[key], old_policy[key])
1791

    
1792
  def testSet(self):
1793
    self._TestSet(self._OLD_GROUP_POLICY, True)
1794
    self._TestSetSpecs(self._OLD_GROUP_POLICY, True)
1795
    self._TestSet(self._OLD_CLUSTER_POLICY, False)
1796
    self._TestSetSpecs(self._OLD_CLUSTER_POLICY, False)
1797

    
1798
  def testUnset(self):
1799
    old_policy = self._OLD_GROUP_POLICY
1800
    diff_policy = {
1801
      constants.IPOLICY_SPINDLE_RATIO: constants.VALUE_DEFAULT,
1802
      }
1803
    new_policy = cmdlib._GetUpdatedIPolicy(old_policy, diff_policy,
1804
                                           group_policy=True)
1805
    for key in diff_policy:
1806
      self.assertFalse(key in new_policy)
1807
    for key in old_policy:
1808
      if not key in diff_policy:
1809
        self.assertTrue(key in new_policy)
1810
        self.assertEqual(new_policy[key], old_policy[key])
1811

    
1812
  def _TestInvalidKeys(self, old_policy, isgroup):
1813
    INVALID_DICT = {
1814
      "this_key_shouldnt_be_allowed": 3,
1815
      }
1816
    invalid_policy = INVALID_DICT
1817
    self.assertRaises(errors.OpPrereqError, cmdlib._GetUpdatedIPolicy,
1818
                      old_policy, invalid_policy, group_policy=isgroup)
1819
    for key in constants.IPOLICY_ISPECS:
1820
      invalid_ispec = {
1821
        key: INVALID_DICT,
1822
        }
1823
      self.assertRaises(errors.TypeEnforcementError, cmdlib._GetUpdatedIPolicy,
1824
                        old_policy, invalid_ispec, group_policy=isgroup)
1825

    
1826
  def testInvalidKeys(self):
1827
    self._TestInvalidKeys(self._OLD_GROUP_POLICY, True)
1828
    self._TestInvalidKeys(self._OLD_CLUSTER_POLICY, False)
1829

    
1830

    
1831
if __name__ == "__main__":
1832
  testutils.GanetiTestProgram()