Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.cmdlib_unittest.py @ 41044e04

History | View | Annotate | Download (68.7 kB)

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

    
4
# Copyright (C) 2008, 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 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.ispecs = {
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.ispecs, None) is None)
606

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

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

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

    
620
  def testNoMaxDefined(self):
621
    self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT, None,
622
                                                self.ispecs, 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.ispecs[constants.ISPECS_MIN].get(name, val)
630
      max_v = self.ispecs[constants.ISPECS_MAX].get(name, val)
631
      self.assertEqual(cmdlib._ComputeMinMaxSpec(name, None,
632
                                                 self.ispecs, 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.ispecs, 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.ispecs, 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
    constants.ISPECS_MINMAX: [NotImplemented],
677
    }
678

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

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

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

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

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

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

    
722
  def testWithIPolicy(self):
723
    mem_size = 2048
724
    cpu_count = 2
725
    disk_count = 1
726
    disk_sizes = [512]
727
    nic_count = 1
728
    spindle_use = 4
729
    disk_template = "mytemplate"
730
    ispec = {
731
      constants.ISPEC_MEM_SIZE: mem_size,
732
      constants.ISPEC_CPU_COUNT: cpu_count,
733
      constants.ISPEC_DISK_COUNT: disk_count,
734
      constants.ISPEC_DISK_SIZE: disk_sizes[0],
735
      constants.ISPEC_NIC_COUNT: nic_count,
736
      constants.ISPEC_SPINDLE_USE: spindle_use,
737
      }
738
    ipolicy1 = {
739
      constants.ISPECS_MINMAX: [{
740
        constants.ISPECS_MIN: ispec,
741
        constants.ISPECS_MAX: ispec,
742
        }],
743
      constants.IPOLICY_DTS: [disk_template],
744
      }
745
    ispec_copy = copy.deepcopy(ispec)
746
    ipolicy2 = {
747
      constants.ISPECS_MINMAX: [
748
        {
749
          constants.ISPECS_MIN: ispec_copy,
750
          constants.ISPECS_MAX: ispec_copy,
751
          },
752
        {
753
          constants.ISPECS_MIN: ispec,
754
          constants.ISPECS_MAX: ispec,
755
          },
756
        ],
757
      constants.IPOLICY_DTS: [disk_template],
758
      }
759
    ipolicy3 = {
760
      constants.ISPECS_MINMAX: [
761
        {
762
          constants.ISPECS_MIN: ispec,
763
          constants.ISPECS_MAX: ispec,
764
          },
765
        {
766
          constants.ISPECS_MIN: ispec_copy,
767
          constants.ISPECS_MAX: ispec_copy,
768
          },
769
        ],
770
      constants.IPOLICY_DTS: [disk_template],
771
      }
772
    def AssertComputeViolation(ipolicy, violations):
773
      ret = cmdlib._ComputeIPolicySpecViolation(ipolicy, mem_size, cpu_count,
774
                                                disk_count, nic_count,
775
                                                disk_sizes, spindle_use,
776
                                                disk_template)
777
      self.assertEqual(len(ret), violations)
778

    
779
    AssertComputeViolation(ipolicy1, 0)
780
    AssertComputeViolation(ipolicy2, 0)
781
    AssertComputeViolation(ipolicy3, 0)
782
    for par in constants.ISPECS_PARAMETERS:
783
      ispec[par] += 1
784
      AssertComputeViolation(ipolicy1, 1)
785
      AssertComputeViolation(ipolicy2, 0)
786
      AssertComputeViolation(ipolicy3, 0)
787
      ispec[par] -= 2
788
      AssertComputeViolation(ipolicy1, 1)
789
      AssertComputeViolation(ipolicy2, 0)
790
      AssertComputeViolation(ipolicy3, 0)
791
      ispec[par] += 1 # Restore
792
    ipolicy1[constants.IPOLICY_DTS] = ["another_template"]
793
    AssertComputeViolation(ipolicy1, 1)
794

    
795

    
796
class _StubComputeIPolicySpecViolation:
797
  def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
798
               spindle_use, disk_template):
799
    self.mem_size = mem_size
800
    self.cpu_count = cpu_count
801
    self.disk_count = disk_count
802
    self.nic_count = nic_count
803
    self.disk_sizes = disk_sizes
804
    self.spindle_use = spindle_use
805
    self.disk_template = disk_template
806

    
807
  def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
808
               spindle_use, disk_template):
809
    assert self.mem_size == mem_size
810
    assert self.cpu_count == cpu_count
811
    assert self.disk_count == disk_count
812
    assert self.nic_count == nic_count
813
    assert self.disk_sizes == disk_sizes
814
    assert self.spindle_use == spindle_use
815
    assert self.disk_template == disk_template
816

    
817
    return []
818

    
819

    
820
class _FakeConfigForComputeIPolicyInstanceViolation:
821
  def __init__(self, be):
822
    self.cluster = objects.Cluster(beparams={"default": be})
823

    
824
  def GetClusterInfo(self):
825
    return self.cluster
826

    
827

    
828
class TestComputeIPolicyInstanceViolation(unittest.TestCase):
829
  def test(self):
830
    beparams = {
831
      constants.BE_MAXMEM: 2048,
832
      constants.BE_VCPUS: 2,
833
      constants.BE_SPINDLE_USE: 4,
834
      }
835
    disks = [objects.Disk(size=512)]
836
    cfg = _FakeConfigForComputeIPolicyInstanceViolation(beparams)
837
    instance = objects.Instance(beparams=beparams, disks=disks, nics=[],
838
                                disk_template=constants.DT_PLAIN)
839
    stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 4,
840
                                            constants.DT_PLAIN)
841
    ret = cmdlib._ComputeIPolicyInstanceViolation(NotImplemented, instance,
842
                                                  cfg, _compute_fn=stub)
843
    self.assertEqual(ret, [])
844
    instance2 = objects.Instance(beparams={}, disks=disks, nics=[],
845
                                 disk_template=constants.DT_PLAIN)
846
    ret = cmdlib._ComputeIPolicyInstanceViolation(NotImplemented, instance2,
847
                                                  cfg, _compute_fn=stub)
848
    self.assertEqual(ret, [])
849

    
850

    
851
class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
852
  def test(self):
853
    ispec = {
854
      constants.ISPEC_MEM_SIZE: 2048,
855
      constants.ISPEC_CPU_COUNT: 2,
856
      constants.ISPEC_DISK_COUNT: 1,
857
      constants.ISPEC_DISK_SIZE: [512],
858
      constants.ISPEC_NIC_COUNT: 0,
859
      constants.ISPEC_SPINDLE_USE: 1,
860
      }
861
    stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 1,
862
                                            constants.DT_PLAIN)
863
    ret = cmdlib._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
864
                                                      constants.DT_PLAIN,
865
                                                      _compute_fn=stub)
866
    self.assertEqual(ret, [])
867

    
868

    
869
class _CallRecorder:
870
  def __init__(self, return_value=None):
871
    self.called = False
872
    self.return_value = return_value
873

    
874
  def __call__(self, *args):
875
    self.called = True
876
    return self.return_value
877

    
878

    
879
class TestComputeIPolicyNodeViolation(unittest.TestCase):
880
  def setUp(self):
881
    self.recorder = _CallRecorder(return_value=[])
882

    
883
  def testSameGroup(self):
884
    ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
885
                                              "foo", "foo", NotImplemented,
886
                                              _compute_fn=self.recorder)
887
    self.assertFalse(self.recorder.called)
888
    self.assertEqual(ret, [])
889

    
890
  def testDifferentGroup(self):
891
    ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
892
                                              "foo", "bar", NotImplemented,
893
                                              _compute_fn=self.recorder)
894
    self.assertTrue(self.recorder.called)
895
    self.assertEqual(ret, [])
896

    
897

    
898
class _FakeConfigForTargetNodeIPolicy:
899
  def __init__(self, node_info=NotImplemented):
900
    self._node_info = node_info
901

    
902
  def GetNodeInfo(self, _):
903
    return self._node_info
904

    
905

    
906
class TestCheckTargetNodeIPolicy(unittest.TestCase):
907
  def setUp(self):
908
    self.instance = objects.Instance(primary_node="blubb")
909
    self.target_node = objects.Node(group="bar")
910
    node_info = objects.Node(group="foo")
911
    fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
912
    self.lu = _FakeLU(cfg=fake_cfg)
913

    
914
  def testNoViolation(self):
915
    compute_recoder = _CallRecorder(return_value=[])
916
    cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
917
                                   self.target_node, NotImplemented,
918
                                   _compute_fn=compute_recoder)
919
    self.assertTrue(compute_recoder.called)
920
    self.assertEqual(self.lu.warning_log, [])
921

    
922
  def testNoIgnore(self):
923
    compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
924
    self.assertRaises(errors.OpPrereqError, cmdlib._CheckTargetNodeIPolicy,
925
                      self.lu, NotImplemented, self.instance, self.target_node,
926
                      NotImplemented, _compute_fn=compute_recoder)
927
    self.assertTrue(compute_recoder.called)
928
    self.assertEqual(self.lu.warning_log, [])
929

    
930
  def testIgnoreViolation(self):
931
    compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
932
    cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
933
                                   self.target_node, NotImplemented,
934
                                   ignore=True, _compute_fn=compute_recoder)
935
    self.assertTrue(compute_recoder.called)
936
    msg = ("Instance does not meet target node group's (bar) instance policy:"
937
           " mem_size not in range")
938
    self.assertEqual(self.lu.warning_log, [(msg, ())])
939

    
940

    
941
class TestApplyContainerMods(unittest.TestCase):
942
  def testEmptyContainer(self):
943
    container = []
944
    chgdesc = []
945
    cmdlib.ApplyContainerMods("test", container, chgdesc, [], None, None, None)
946
    self.assertEqual(container, [])
947
    self.assertEqual(chgdesc, [])
948

    
949
  def testAdd(self):
950
    container = []
951
    chgdesc = []
952
    mods = cmdlib.PrepareContainerMods([
953
      (constants.DDM_ADD, -1, "Hello"),
954
      (constants.DDM_ADD, -1, "World"),
955
      (constants.DDM_ADD, 0, "Start"),
956
      (constants.DDM_ADD, -1, "End"),
957
      ], None)
958
    cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
959
                              None, None, None)
960
    self.assertEqual(container, ["Start", "Hello", "World", "End"])
961
    self.assertEqual(chgdesc, [])
962

    
963
    mods = cmdlib.PrepareContainerMods([
964
      (constants.DDM_ADD, 0, "zero"),
965
      (constants.DDM_ADD, 3, "Added"),
966
      (constants.DDM_ADD, 5, "four"),
967
      (constants.DDM_ADD, 7, "xyz"),
968
      ], None)
969
    cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
970
                              None, None, None)
971
    self.assertEqual(container,
972
                     ["zero", "Start", "Hello", "Added", "World", "four",
973
                      "End", "xyz"])
974
    self.assertEqual(chgdesc, [])
975

    
976
    for idx in [-2, len(container) + 1]:
977
      mods = cmdlib.PrepareContainerMods([
978
        (constants.DDM_ADD, idx, "error"),
979
        ], None)
980
      self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
981
                        "test", container, None, mods, None, None, None)
982

    
983
  def testRemoveError(self):
984
    for idx in [0, 1, 2, 100, -1, -4]:
985
      mods = cmdlib.PrepareContainerMods([
986
        (constants.DDM_REMOVE, idx, None),
987
        ], None)
988
      self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
989
                        "test", [], None, mods, None, None, None)
990

    
991
    mods = cmdlib.PrepareContainerMods([
992
      (constants.DDM_REMOVE, 0, object()),
993
      ], None)
994
    self.assertRaises(AssertionError, cmdlib.ApplyContainerMods,
995
                      "test", [""], None, mods, None, None, None)
996

    
997
  def testAddError(self):
998
    for idx in range(-100, -1) + [100]:
999
      mods = cmdlib.PrepareContainerMods([
1000
        (constants.DDM_ADD, idx, None),
1001
        ], None)
1002
      self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
1003
                        "test", [], None, mods, None, None, None)
1004

    
1005
  def testRemove(self):
1006
    container = ["item 1", "item 2"]
1007
    mods = cmdlib.PrepareContainerMods([
1008
      (constants.DDM_ADD, -1, "aaa"),
1009
      (constants.DDM_REMOVE, -1, None),
1010
      (constants.DDM_ADD, -1, "bbb"),
1011
      ], None)
1012
    chgdesc = []
1013
    cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
1014
                              None, None, None)
1015
    self.assertEqual(container, ["item 1", "item 2", "bbb"])
1016
    self.assertEqual(chgdesc, [
1017
      ("test/2", "remove"),
1018
      ])
1019

    
1020
  def testModify(self):
1021
    container = ["item 1", "item 2"]
1022
    mods = cmdlib.PrepareContainerMods([
1023
      (constants.DDM_MODIFY, -1, "a"),
1024
      (constants.DDM_MODIFY, 0, "b"),
1025
      (constants.DDM_MODIFY, 1, "c"),
1026
      ], None)
1027
    chgdesc = []
1028
    cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
1029
                              None, None, None)
1030
    self.assertEqual(container, ["item 1", "item 2"])
1031
    self.assertEqual(chgdesc, [])
1032

    
1033
    for idx in [-2, len(container) + 1]:
1034
      mods = cmdlib.PrepareContainerMods([
1035
        (constants.DDM_MODIFY, idx, "error"),
1036
        ], None)
1037
      self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
1038
                        "test", container, None, mods, None, None, None)
1039

    
1040
  class _PrivateData:
1041
    def __init__(self):
1042
      self.data = None
1043

    
1044
  @staticmethod
1045
  def _CreateTestFn(idx, params, private):
1046
    private.data = ("add", idx, params)
1047
    return ((100 * idx, params), [
1048
      ("test/%s" % idx, hex(idx)),
1049
      ])
1050

    
1051
  @staticmethod
1052
  def _ModifyTestFn(idx, item, params, private):
1053
    private.data = ("modify", idx, params)
1054
    return [
1055
      ("test/%s" % idx, "modify %s" % params),
1056
      ]
1057

    
1058
  @staticmethod
1059
  def _RemoveTestFn(idx, item, private):
1060
    private.data = ("remove", idx, item)
1061

    
1062
  def testAddWithCreateFunction(self):
1063
    container = []
1064
    chgdesc = []
1065
    mods = cmdlib.PrepareContainerMods([
1066
      (constants.DDM_ADD, -1, "Hello"),
1067
      (constants.DDM_ADD, -1, "World"),
1068
      (constants.DDM_ADD, 0, "Start"),
1069
      (constants.DDM_ADD, -1, "End"),
1070
      (constants.DDM_REMOVE, 2, None),
1071
      (constants.DDM_MODIFY, -1, "foobar"),
1072
      (constants.DDM_REMOVE, 2, None),
1073
      (constants.DDM_ADD, 1, "More"),
1074
      ], self._PrivateData)
1075
    cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
1076
      self._CreateTestFn, self._ModifyTestFn, self._RemoveTestFn)
1077
    self.assertEqual(container, [
1078
      (000, "Start"),
1079
      (100, "More"),
1080
      (000, "Hello"),
1081
      ])
1082
    self.assertEqual(chgdesc, [
1083
      ("test/0", "0x0"),
1084
      ("test/1", "0x1"),
1085
      ("test/0", "0x0"),
1086
      ("test/3", "0x3"),
1087
      ("test/2", "remove"),
1088
      ("test/2", "modify foobar"),
1089
      ("test/2", "remove"),
1090
      ("test/1", "0x1")
1091
      ])
1092
    self.assertTrue(compat.all(op == private.data[0]
1093
                               for (op, _, _, private) in mods))
1094
    self.assertEqual([private.data for (op, _, _, private) in mods], [
1095
      ("add", 0, "Hello"),
1096
      ("add", 1, "World"),
1097
      ("add", 0, "Start"),
1098
      ("add", 3, "End"),
1099
      ("remove", 2, (100, "World")),
1100
      ("modify", 2, "foobar"),
1101
      ("remove", 2, (300, "End")),
1102
      ("add", 1, "More"),
1103
      ])
1104

    
1105

    
1106
class _FakeConfigForGenDiskTemplate:
1107
  def __init__(self):
1108
    self._unique_id = itertools.count()
1109
    self._drbd_minor = itertools.count(20)
1110
    self._port = itertools.count(constants.FIRST_DRBD_PORT)
1111
    self._secret = itertools.count()
1112

    
1113
  def GetVGName(self):
1114
    return "testvg"
1115

    
1116
  def GenerateUniqueID(self, ec_id):
1117
    return "ec%s-uq%s" % (ec_id, self._unique_id.next())
1118

    
1119
  def AllocateDRBDMinor(self, nodes, instance):
1120
    return [self._drbd_minor.next()
1121
            for _ in nodes]
1122

    
1123
  def AllocatePort(self):
1124
    return self._port.next()
1125

    
1126
  def GenerateDRBDSecret(self, ec_id):
1127
    return "ec%s-secret%s" % (ec_id, self._secret.next())
1128

    
1129
  def GetInstanceInfo(self, _):
1130
    return "foobar"
1131

    
1132

    
1133
class _FakeProcForGenDiskTemplate:
1134
  def GetECId(self):
1135
    return 0
1136

    
1137

    
1138
class TestGenerateDiskTemplate(unittest.TestCase):
1139
  def setUp(self):
1140
    nodegroup = objects.NodeGroup(name="ng")
1141
    nodegroup.UpgradeConfig()
1142

    
1143
    cfg = _FakeConfigForGenDiskTemplate()
1144
    proc = _FakeProcForGenDiskTemplate()
1145

    
1146
    self.lu = _FakeLU(cfg=cfg, proc=proc)
1147
    self.nodegroup = nodegroup
1148

    
1149
  @staticmethod
1150
  def GetDiskParams():
1151
    return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1152

    
1153
  def testWrongDiskTemplate(self):
1154
    gdt = cmdlib._GenerateDiskTemplate
1155
    disk_template = "##unknown##"
1156

    
1157
    assert disk_template not in constants.DISK_TEMPLATES
1158

    
1159
    self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
1160
                      "inst26831.example.com", "node30113.example.com", [], [],
1161
                      NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1162
                      self.GetDiskParams())
1163

    
1164
  def testDiskless(self):
1165
    gdt = cmdlib._GenerateDiskTemplate
1166

    
1167
    result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1168
                 "node30113.example.com", [], [],
1169
                 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1170
                 self.GetDiskParams())
1171
    self.assertEqual(result, [])
1172

    
1173
  def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1174
                       file_storage_dir=NotImplemented,
1175
                       file_driver=NotImplemented,
1176
                       req_file_storage=NotImplemented,
1177
                       req_shr_file_storage=NotImplemented):
1178
    gdt = cmdlib._GenerateDiskTemplate
1179

    
1180
    map(lambda params: utils.ForceDictType(params,
1181
                                           constants.IDISK_PARAMS_TYPES),
1182
        disk_info)
1183

    
1184
    # Check if non-empty list of secondaries is rejected
1185
    self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1186
                      template, "inst25088.example.com",
1187
                      "node185.example.com", ["node323.example.com"], [],
1188
                      NotImplemented, NotImplemented, base_index,
1189
                      self.lu.LogInfo, self.GetDiskParams(),
1190
                      _req_file_storage=req_file_storage,
1191
                      _req_shr_file_storage=req_shr_file_storage)
1192

    
1193
    result = gdt(self.lu, template, "inst21662.example.com",
1194
                 "node21741.example.com", [],
1195
                 disk_info, file_storage_dir, file_driver, base_index,
1196
                 self.lu.LogInfo, self.GetDiskParams(),
1197
                 _req_file_storage=req_file_storage,
1198
                 _req_shr_file_storage=req_shr_file_storage)
1199

    
1200
    for (idx, disk) in enumerate(result):
1201
      self.assertTrue(isinstance(disk, objects.Disk))
1202
      self.assertEqual(disk.dev_type, exp_dev_type)
1203
      self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1204
      self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1205
      self.assertTrue(disk.children is None)
1206

    
1207
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1208
    cmdlib._UpdateIvNames(base_index, result)
1209
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1210

    
1211
    return result
1212

    
1213
  def _CheckIvNames(self, disks, base_index, end_index):
1214
    self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1215
                     ["disk/%s" % i for i in range(base_index, end_index)])
1216

    
1217
  def testPlain(self):
1218
    disk_info = [{
1219
      constants.IDISK_SIZE: 1024,
1220
      constants.IDISK_MODE: constants.DISK_RDWR,
1221
      }, {
1222
      constants.IDISK_SIZE: 4096,
1223
      constants.IDISK_VG: "othervg",
1224
      constants.IDISK_MODE: constants.DISK_RDWR,
1225
      }]
1226

    
1227
    result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1228
                                   constants.LD_LV)
1229

    
1230
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1231
      ("testvg", "ec0-uq0.disk3"),
1232
      ("othervg", "ec0-uq1.disk4"),
1233
      ])
1234

    
1235
  @staticmethod
1236
  def _AllowFileStorage():
1237
    pass
1238

    
1239
  @staticmethod
1240
  def _ForbidFileStorage():
1241
    raise errors.OpPrereqError("Disallowed in test")
1242

    
1243
  def testFile(self):
1244
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1245
                      constants.DT_FILE, [], 0, NotImplemented,
1246
                      req_file_storage=self._ForbidFileStorage)
1247
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1248
                      constants.DT_SHARED_FILE, [], 0, NotImplemented,
1249
                      req_shr_file_storage=self._ForbidFileStorage)
1250

    
1251
    for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1252
      disk_info = [{
1253
        constants.IDISK_SIZE: 80 * 1024,
1254
        constants.IDISK_MODE: constants.DISK_RDONLY,
1255
        }, {
1256
        constants.IDISK_SIZE: 4096,
1257
        constants.IDISK_MODE: constants.DISK_RDWR,
1258
        }, {
1259
        constants.IDISK_SIZE: 6 * 1024,
1260
        constants.IDISK_MODE: constants.DISK_RDWR,
1261
        }]
1262

    
1263
      result = self._TestTrivialDisk(disk_template, disk_info, 2,
1264
        constants.LD_FILE, file_storage_dir="/tmp",
1265
        file_driver=constants.FD_BLKTAP,
1266
        req_file_storage=self._AllowFileStorage,
1267
        req_shr_file_storage=self._AllowFileStorage)
1268

    
1269
      self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1270
        (constants.FD_BLKTAP, "/tmp/disk2"),
1271
        (constants.FD_BLKTAP, "/tmp/disk3"),
1272
        (constants.FD_BLKTAP, "/tmp/disk4"),
1273
        ])
1274

    
1275
  def testBlock(self):
1276
    disk_info = [{
1277
      constants.IDISK_SIZE: 8 * 1024,
1278
      constants.IDISK_MODE: constants.DISK_RDWR,
1279
      constants.IDISK_ADOPT: "/tmp/some/block/dev",
1280
      }]
1281

    
1282
    result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1283
                                   constants.LD_BLOCKDEV)
1284

    
1285
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1286
      (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1287
      ])
1288

    
1289
  def testRbd(self):
1290
    disk_info = [{
1291
      constants.IDISK_SIZE: 8 * 1024,
1292
      constants.IDISK_MODE: constants.DISK_RDONLY,
1293
      }, {
1294
      constants.IDISK_SIZE: 100 * 1024,
1295
      constants.IDISK_MODE: constants.DISK_RDWR,
1296
      }]
1297

    
1298
    result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1299
                                   constants.LD_RBD)
1300

    
1301
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1302
      ("rbd", "ec0-uq0.rbd.disk0"),
1303
      ("rbd", "ec0-uq1.rbd.disk1"),
1304
      ])
1305

    
1306
  def testDrbd8(self):
1307
    gdt = cmdlib._GenerateDiskTemplate
1308
    drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1309
    drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1310

    
1311
    disk_info = [{
1312
      constants.IDISK_SIZE: 1024,
1313
      constants.IDISK_MODE: constants.DISK_RDWR,
1314
      }, {
1315
      constants.IDISK_SIZE: 100 * 1024,
1316
      constants.IDISK_MODE: constants.DISK_RDONLY,
1317
      constants.IDISK_METAVG: "metavg",
1318
      }, {
1319
      constants.IDISK_SIZE: 4096,
1320
      constants.IDISK_MODE: constants.DISK_RDWR,
1321
      constants.IDISK_VG: "vgxyz",
1322
      },
1323
      ]
1324

    
1325
    exp_logical_ids = [[
1326
      (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1327
      (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1328
      ], [
1329
      (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1330
      ("metavg", "ec0-uq1.disk1_meta"),
1331
      ], [
1332
      ("vgxyz", "ec0-uq2.disk2_data"),
1333
      (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1334
      ]]
1335

    
1336
    assert len(exp_logical_ids) == len(disk_info)
1337

    
1338
    map(lambda params: utils.ForceDictType(params,
1339
                                           constants.IDISK_PARAMS_TYPES),
1340
        disk_info)
1341

    
1342
    # Check if empty list of secondaries is rejected
1343
    self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1344
                      "inst827.example.com", "node1334.example.com", [],
1345
                      disk_info, NotImplemented, NotImplemented, 0,
1346
                      self.lu.LogInfo, self.GetDiskParams())
1347

    
1348
    result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1349
                 "node1334.example.com", ["node12272.example.com"],
1350
                 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1351
                 self.GetDiskParams())
1352

    
1353
    for (idx, disk) in enumerate(result):
1354
      self.assertTrue(isinstance(disk, objects.Disk))
1355
      self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1356
      self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1357
      self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1358

    
1359
      for child in disk.children:
1360
        self.assertTrue(isinstance(disk, objects.Disk))
1361
        self.assertEqual(child.dev_type, constants.LD_LV)
1362
        self.assertTrue(child.children is None)
1363

    
1364
      self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1365
                       exp_logical_ids[idx])
1366

    
1367
      self.assertEqual(len(disk.children), 2)
1368
      self.assertEqual(disk.children[0].size, disk.size)
1369
      self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1370

    
1371
    self._CheckIvNames(result, 0, len(disk_info))
1372
    cmdlib._UpdateIvNames(0, result)
1373
    self._CheckIvNames(result, 0, len(disk_info))
1374

    
1375
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1376
      ("node1334.example.com", "node12272.example.com",
1377
       constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1378
      ("node1334.example.com", "node12272.example.com",
1379
       constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1380
      ("node1334.example.com", "node12272.example.com",
1381
       constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1382
      ])
1383

    
1384

    
1385
class _ConfigForDiskWipe:
1386
  def __init__(self, exp_node):
1387
    self._exp_node = exp_node
1388

    
1389
  def SetDiskID(self, device, node):
1390
    assert isinstance(device, objects.Disk)
1391
    assert node == self._exp_node
1392

    
1393

    
1394
class _RpcForDiskWipe:
1395
  def __init__(self, exp_node, pause_cb, wipe_cb):
1396
    self._exp_node = exp_node
1397
    self._pause_cb = pause_cb
1398
    self._wipe_cb = wipe_cb
1399

    
1400
  def call_blockdev_pause_resume_sync(self, node, disks, pause):
1401
    assert node == self._exp_node
1402
    return rpc.RpcResult(data=self._pause_cb(disks, pause))
1403

    
1404
  def call_blockdev_wipe(self, node, bdev, offset, size):
1405
    assert node == self._exp_node
1406
    return rpc.RpcResult(data=self._wipe_cb(bdev, offset, size))
1407

    
1408

    
1409
class _DiskPauseTracker:
1410
  def __init__(self):
1411
    self.history = []
1412

    
1413
  def __call__(self, (disks, instance), pause):
1414
    assert not (set(disks) - set(instance.disks))
1415

    
1416
    self.history.extend((i.logical_id, i.size, pause)
1417
                        for i in disks)
1418

    
1419
    return (True, [True] * len(disks))
1420

    
1421

    
1422
class _DiskWipeProgressTracker:
1423
  def __init__(self, start_offset):
1424
    self._start_offset = start_offset
1425
    self.progress = {}
1426

    
1427
  def __call__(self, (disk, _), offset, size):
1428
    assert isinstance(offset, (long, int))
1429
    assert isinstance(size, (long, int))
1430

    
1431
    max_chunk_size = (disk.size / 100.0 * constants.MIN_WIPE_CHUNK_PERCENT)
1432

    
1433
    assert offset >= self._start_offset
1434
    assert (offset + size) <= disk.size
1435

    
1436
    assert size > 0
1437
    assert size <= constants.MAX_WIPE_CHUNK
1438
    assert size <= max_chunk_size
1439

    
1440
    assert offset == self._start_offset or disk.logical_id in self.progress
1441

    
1442
    # Keep track of progress
1443
    cur_progress = self.progress.setdefault(disk.logical_id, self._start_offset)
1444

    
1445
    assert cur_progress == offset
1446

    
1447
    # Record progress
1448
    self.progress[disk.logical_id] += size
1449

    
1450
    return (True, None)
1451

    
1452

    
1453
class TestWipeDisks(unittest.TestCase):
1454
  def _FailingPauseCb(self, (disks, _), pause):
1455
    self.assertEqual(len(disks), 3)
1456
    self.assertTrue(pause)
1457
    # Simulate an RPC error
1458
    return (False, "error")
1459

    
1460
  def testPauseFailure(self):
1461
    node_name = "node1372.example.com"
1462

    
1463
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, self._FailingPauseCb,
1464
                                     NotImplemented),
1465
                 cfg=_ConfigForDiskWipe(node_name))
1466

    
1467
    disks = [
1468
      objects.Disk(dev_type=constants.LD_LV),
1469
      objects.Disk(dev_type=constants.LD_LV),
1470
      objects.Disk(dev_type=constants.LD_LV),
1471
      ]
1472

    
1473
    instance = objects.Instance(name="inst21201",
1474
                                primary_node=node_name,
1475
                                disk_template=constants.DT_PLAIN,
1476
                                disks=disks)
1477

    
1478
    self.assertRaises(errors.OpExecError, cmdlib._WipeDisks, lu, instance)
1479

    
1480
  def _FailingWipeCb(self, (disk, _), offset, size):
1481
    # This should only ever be called for the first disk
1482
    self.assertEqual(disk.logical_id, "disk0")
1483
    return (False, None)
1484

    
1485
  def testFailingWipe(self):
1486
    node_name = "node13445.example.com"
1487
    pt = _DiskPauseTracker()
1488

    
1489
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pt, self._FailingWipeCb),
1490
                 cfg=_ConfigForDiskWipe(node_name))
1491

    
1492
    disks = [
1493
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1494
                   size=100 * 1024),
1495
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1496
                   size=500 * 1024),
1497
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=256),
1498
      ]
1499

    
1500
    instance = objects.Instance(name="inst562",
1501
                                primary_node=node_name,
1502
                                disk_template=constants.DT_PLAIN,
1503
                                disks=disks)
1504

    
1505
    try:
1506
      cmdlib._WipeDisks(lu, instance)
1507
    except errors.OpExecError, err:
1508
      self.assertTrue(str(err), "Could not wipe disk 0 at offset 0 ")
1509
    else:
1510
      self.fail("Did not raise exception")
1511

    
1512
    # Check if all disks were paused and resumed
1513
    self.assertEqual(pt.history, [
1514
      ("disk0", 100 * 1024, True),
1515
      ("disk1", 500 * 1024, True),
1516
      ("disk2", 256, True),
1517
      ("disk0", 100 * 1024, False),
1518
      ("disk1", 500 * 1024, False),
1519
      ("disk2", 256, False),
1520
      ])
1521

    
1522
  def _PrepareWipeTest(self, start_offset, disks):
1523
    node_name = "node-with-offset%s.example.com" % start_offset
1524
    pauset = _DiskPauseTracker()
1525
    progresst = _DiskWipeProgressTracker(start_offset)
1526

    
1527
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pauset, progresst),
1528
                 cfg=_ConfigForDiskWipe(node_name))
1529

    
1530
    instance = objects.Instance(name="inst3560",
1531
                                primary_node=node_name,
1532
                                disk_template=constants.DT_PLAIN,
1533
                                disks=disks)
1534

    
1535
    return (lu, instance, pauset, progresst)
1536

    
1537
  def testNormalWipe(self):
1538
    disks = [
1539
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk0", size=1024),
1540
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1541
                   size=500 * 1024),
1542
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=128),
1543
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk3",
1544
                   size=constants.MAX_WIPE_CHUNK),
1545
      ]
1546

    
1547
    (lu, instance, pauset, progresst) = self._PrepareWipeTest(0, disks)
1548

    
1549
    cmdlib._WipeDisks(lu, instance)
1550

    
1551
    self.assertEqual(pauset.history, [
1552
      ("disk0", 1024, True),
1553
      ("disk1", 500 * 1024, True),
1554
      ("disk2", 128, True),
1555
      ("disk3", constants.MAX_WIPE_CHUNK, True),
1556
      ("disk0", 1024, False),
1557
      ("disk1", 500 * 1024, False),
1558
      ("disk2", 128, False),
1559
      ("disk3", constants.MAX_WIPE_CHUNK, False),
1560
      ])
1561

    
1562
    # Ensure the complete disk has been wiped
1563
    self.assertEqual(progresst.progress,
1564
                     dict((i.logical_id, i.size) for i in disks))
1565

    
1566
  def testWipeWithStartOffset(self):
1567
    for start_offset in [0, 280, 8895, 1563204]:
1568
      disks = [
1569
        objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1570
                     size=128),
1571
        objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1572
                     size=start_offset + (100 * 1024)),
1573
        ]
1574

    
1575
      (lu, instance, pauset, progresst) = \
1576
        self._PrepareWipeTest(start_offset, disks)
1577

    
1578
      # Test start offset with only one disk
1579
      cmdlib._WipeDisks(lu, instance,
1580
                        disks=[(1, disks[1], start_offset)])
1581

    
1582
      # Only the second disk may have been paused and wiped
1583
      self.assertEqual(pauset.history, [
1584
        ("disk1", start_offset + (100 * 1024), True),
1585
        ("disk1", start_offset + (100 * 1024), False),
1586
        ])
1587
      self.assertEqual(progresst.progress, {
1588
        "disk1": disks[1].size,
1589
        })
1590

    
1591

    
1592
class TestDiskSizeInBytesToMebibytes(unittest.TestCase):
1593
  def testLessThanOneMebibyte(self):
1594
    for i in [1, 2, 7, 512, 1000, 1023]:
1595
      lu = _FakeLU()
1596
      result = cmdlib._DiskSizeInBytesToMebibytes(lu, i)
1597
      self.assertEqual(result, 1)
1598
      self.assertEqual(len(lu.warning_log), 1)
1599
      self.assertEqual(len(lu.warning_log[0]), 2)
1600
      (_, (warnsize, )) = lu.warning_log[0]
1601
      self.assertEqual(warnsize, (1024 * 1024) - i)
1602

    
1603
  def testEven(self):
1604
    for i in [1, 2, 7, 512, 1000, 1023]:
1605
      lu = _FakeLU()
1606
      result = cmdlib._DiskSizeInBytesToMebibytes(lu, i * 1024 * 1024)
1607
      self.assertEqual(result, i)
1608
      self.assertFalse(lu.warning_log)
1609

    
1610
  def testLargeNumber(self):
1611
    for i in [1, 2, 7, 512, 1000, 1023, 2724, 12420]:
1612
      for j in [1, 2, 486, 326, 986, 1023]:
1613
        lu = _FakeLU()
1614
        size = (1024 * 1024 * i) + j
1615
        result = cmdlib._DiskSizeInBytesToMebibytes(lu, size)
1616
        self.assertEqual(result, i + 1, msg="Amount was not rounded up")
1617
        self.assertEqual(len(lu.warning_log), 1)
1618
        self.assertEqual(len(lu.warning_log[0]), 2)
1619
        (_, (warnsize, )) = lu.warning_log[0]
1620
        self.assertEqual(warnsize, (1024 * 1024) - j)
1621

    
1622

    
1623
class TestCopyLockList(unittest.TestCase):
1624
  def test(self):
1625
    self.assertEqual(cmdlib._CopyLockList([]), [])
1626
    self.assertEqual(cmdlib._CopyLockList(None), None)
1627
    self.assertEqual(cmdlib._CopyLockList(locking.ALL_SET), locking.ALL_SET)
1628

    
1629
    names = ["foo", "bar"]
1630
    output = cmdlib._CopyLockList(names)
1631
    self.assertEqual(names, output)
1632
    self.assertNotEqual(id(names), id(output), msg="List was not copied")
1633

    
1634

    
1635
class TestCheckOpportunisticLocking(unittest.TestCase):
1636
  class OpTest(opcodes.OpCode):
1637
    OP_PARAMS = [
1638
      opcodes._POpportunisticLocking,
1639
      opcodes._PIAllocFromDesc(""),
1640
      ]
1641

    
1642
  @classmethod
1643
  def _MakeOp(cls, **kwargs):
1644
    op = cls.OpTest(**kwargs)
1645
    op.Validate(True)
1646
    return op
1647

    
1648
  def testMissingAttributes(self):
1649
    self.assertRaises(AttributeError, cmdlib._CheckOpportunisticLocking,
1650
                      object())
1651

    
1652
  def testDefaults(self):
1653
    op = self._MakeOp()
1654
    cmdlib._CheckOpportunisticLocking(op)
1655

    
1656
  def test(self):
1657
    for iallocator in [None, "something", "other"]:
1658
      for opplock in [False, True]:
1659
        op = self._MakeOp(iallocator=iallocator, opportunistic_locking=opplock)
1660
        if opplock and not iallocator:
1661
          self.assertRaises(errors.OpPrereqError,
1662
                            cmdlib._CheckOpportunisticLocking, op)
1663
        else:
1664
          cmdlib._CheckOpportunisticLocking(op)
1665

    
1666

    
1667
class _OpTestVerifyErrors(opcodes.OpCode):
1668
  OP_PARAMS = [
1669
    opcodes._PDebugSimulateErrors,
1670
    opcodes._PErrorCodes,
1671
    opcodes._PIgnoreErrors,
1672
    ]
1673

    
1674

    
1675
class _LuTestVerifyErrors(cmdlib._VerifyErrors):
1676
  def __init__(self, **kwargs):
1677
    cmdlib._VerifyErrors.__init__(self)
1678
    self.op = _OpTestVerifyErrors(**kwargs)
1679
    self.op.Validate(True)
1680
    self.msglist = []
1681
    self._feedback_fn = self.msglist.append
1682
    self.bad = False
1683

    
1684
  def DispatchCallError(self, which, *args, **kwargs):
1685
    if which:
1686
      self._Error(*args, **kwargs)
1687
    else:
1688
      self._ErrorIf(True, *args, **kwargs)
1689

    
1690
  def CallErrorIf(self, c, *args, **kwargs):
1691
    self._ErrorIf(c, *args, **kwargs)
1692

    
1693

    
1694
class TestVerifyErrors(unittest.TestCase):
1695
  # Fake cluster-verify error code structures; we use two arbitary real error
1696
  # codes to pass validation of ignore_errors
1697
  (_, _ERR1ID, _) = constants.CV_ECLUSTERCFG
1698
  _NODESTR = "node"
1699
  _NODENAME = "mynode"
1700
  _ERR1CODE = (_NODESTR, _ERR1ID, "Error one")
1701
  (_, _ERR2ID, _) = constants.CV_ECLUSTERCERT
1702
  _INSTSTR = "instance"
1703
  _INSTNAME = "myinstance"
1704
  _ERR2CODE = (_INSTSTR, _ERR2ID, "Error two")
1705
  # Arguments used to call _Error() or _ErrorIf()
1706
  _ERR1ARGS = (_ERR1CODE, _NODENAME, "Error1 is %s", "an error")
1707
  _ERR2ARGS = (_ERR2CODE, _INSTNAME, "Error2 has no argument")
1708
  # Expected error messages
1709
  _ERR1MSG = _ERR1ARGS[2] % _ERR1ARGS[3]
1710
  _ERR2MSG = _ERR2ARGS[2]
1711

    
1712
  def testNoError(self):
1713
    lu = _LuTestVerifyErrors()
1714
    lu.CallErrorIf(False, self._ERR1CODE, *self._ERR1ARGS)
1715
    self.assertFalse(lu.bad)
1716
    self.assertFalse(lu.msglist)
1717

    
1718
  def _InitTest(self, **kwargs):
1719
    self.lu1 = _LuTestVerifyErrors(**kwargs)
1720
    self.lu2 = _LuTestVerifyErrors(**kwargs)
1721

    
1722
  def _CallError(self, *args, **kwargs):
1723
    # Check that _Error() and _ErrorIf() produce the same results
1724
    self.lu1.DispatchCallError(True, *args, **kwargs)
1725
    self.lu2.DispatchCallError(False, *args, **kwargs)
1726
    self.assertEqual(self.lu1.bad, self.lu2.bad)
1727
    self.assertEqual(self.lu1.msglist, self.lu2.msglist)
1728
    # Test-specific checks are made on one LU
1729
    return self.lu1
1730

    
1731
  def _checkMsgCommon(self, logstr, errmsg, itype, item, warning):
1732
    self.assertTrue(errmsg in logstr)
1733
    if warning:
1734
      self.assertTrue("WARNING" in logstr)
1735
    else:
1736
      self.assertTrue("ERROR" in logstr)
1737
    self.assertTrue(itype in logstr)
1738
    self.assertTrue(item in logstr)
1739

    
1740
  def _checkMsg1(self, logstr, warning=False):
1741
    self._checkMsgCommon(logstr, self._ERR1MSG, self._NODESTR,
1742
                         self._NODENAME, warning)
1743

    
1744
  def _checkMsg2(self, logstr, warning=False):
1745
    self._checkMsgCommon(logstr, self._ERR2MSG, self._INSTSTR,
1746
                         self._INSTNAME, warning)
1747

    
1748
  def testPlain(self):
1749
    self._InitTest()
1750
    lu = self._CallError(*self._ERR1ARGS)
1751
    self.assertTrue(lu.bad)
1752
    self.assertEqual(len(lu.msglist), 1)
1753
    self._checkMsg1(lu.msglist[0])
1754

    
1755
  def testMultiple(self):
1756
    self._InitTest()
1757
    self._CallError(*self._ERR1ARGS)
1758
    lu = self._CallError(*self._ERR2ARGS)
1759
    self.assertTrue(lu.bad)
1760
    self.assertEqual(len(lu.msglist), 2)
1761
    self._checkMsg1(lu.msglist[0])
1762
    self._checkMsg2(lu.msglist[1])
1763

    
1764
  def testIgnore(self):
1765
    self._InitTest(ignore_errors=[self._ERR1ID])
1766
    lu = self._CallError(*self._ERR1ARGS)
1767
    self.assertFalse(lu.bad)
1768
    self.assertEqual(len(lu.msglist), 1)
1769
    self._checkMsg1(lu.msglist[0], warning=True)
1770

    
1771
  def testWarning(self):
1772
    self._InitTest()
1773
    lu = self._CallError(*self._ERR1ARGS,
1774
                         code=_LuTestVerifyErrors.ETYPE_WARNING)
1775
    self.assertFalse(lu.bad)
1776
    self.assertEqual(len(lu.msglist), 1)
1777
    self._checkMsg1(lu.msglist[0], warning=True)
1778

    
1779
  def testWarning2(self):
1780
    self._InitTest()
1781
    self._CallError(*self._ERR1ARGS)
1782
    lu = self._CallError(*self._ERR2ARGS,
1783
                         code=_LuTestVerifyErrors.ETYPE_WARNING)
1784
    self.assertTrue(lu.bad)
1785
    self.assertEqual(len(lu.msglist), 2)
1786
    self._checkMsg1(lu.msglist[0])
1787
    self._checkMsg2(lu.msglist[1], warning=True)
1788

    
1789
  def testDebugSimulate(self):
1790
    lu = _LuTestVerifyErrors(debug_simulate_errors=True)
1791
    lu.CallErrorIf(False, *self._ERR1ARGS)
1792
    self.assertTrue(lu.bad)
1793
    self.assertEqual(len(lu.msglist), 1)
1794
    self._checkMsg1(lu.msglist[0])
1795

    
1796
  def testErrCodes(self):
1797
    self._InitTest(error_codes=True)
1798
    lu = self._CallError(*self._ERR1ARGS)
1799
    self.assertTrue(lu.bad)
1800
    self.assertEqual(len(lu.msglist), 1)
1801
    self._checkMsg1(lu.msglist[0])
1802
    self.assertTrue(self._ERR1ID in lu.msglist[0])
1803

    
1804

    
1805
class TestGetUpdatedIPolicy(unittest.TestCase):
1806
  """Tests for cmdlib._GetUpdatedIPolicy()"""
1807
  _OLD_CLUSTER_POLICY = {
1808
    constants.IPOLICY_VCPU_RATIO: 1.5,
1809
    constants.ISPECS_MINMAX: [
1810
      {
1811
        constants.ISPECS_MIN: {
1812
          constants.ISPEC_MEM_SIZE: 32768,
1813
          constants.ISPEC_CPU_COUNT: 8,
1814
          constants.ISPEC_DISK_COUNT: 1,
1815
          constants.ISPEC_DISK_SIZE: 1024,
1816
          constants.ISPEC_NIC_COUNT: 1,
1817
          constants.ISPEC_SPINDLE_USE: 1,
1818
          },
1819
        constants.ISPECS_MAX: {
1820
          constants.ISPEC_MEM_SIZE: 65536,
1821
          constants.ISPEC_CPU_COUNT: 10,
1822
          constants.ISPEC_DISK_COUNT: 5,
1823
          constants.ISPEC_DISK_SIZE: 1024 * 1024,
1824
          constants.ISPEC_NIC_COUNT: 3,
1825
          constants.ISPEC_SPINDLE_USE: 12,
1826
          },
1827
        },
1828
      constants.ISPECS_MINMAX_DEFAULTS,
1829
      ],
1830
    constants.ISPECS_STD: constants.IPOLICY_DEFAULTS[constants.ISPECS_STD],
1831
    }
1832
  _OLD_GROUP_POLICY = {
1833
    constants.IPOLICY_SPINDLE_RATIO: 2.5,
1834
    constants.ISPECS_MINMAX: [{
1835
      constants.ISPECS_MIN: {
1836
        constants.ISPEC_MEM_SIZE: 128,
1837
        constants.ISPEC_CPU_COUNT: 1,
1838
        constants.ISPEC_DISK_COUNT: 1,
1839
        constants.ISPEC_DISK_SIZE: 1024,
1840
        constants.ISPEC_NIC_COUNT: 1,
1841
        constants.ISPEC_SPINDLE_USE: 1,
1842
        },
1843
      constants.ISPECS_MAX: {
1844
        constants.ISPEC_MEM_SIZE: 32768,
1845
        constants.ISPEC_CPU_COUNT: 8,
1846
        constants.ISPEC_DISK_COUNT: 5,
1847
        constants.ISPEC_DISK_SIZE: 1024 * 1024,
1848
        constants.ISPEC_NIC_COUNT: 3,
1849
        constants.ISPEC_SPINDLE_USE: 12,
1850
        },
1851
      }],
1852
    }
1853

    
1854
  def _TestSetSpecs(self, old_policy, isgroup):
1855
    diff_minmax = [{
1856
      constants.ISPECS_MIN: {
1857
        constants.ISPEC_MEM_SIZE: 64,
1858
        constants.ISPEC_CPU_COUNT: 1,
1859
        constants.ISPEC_DISK_COUNT: 2,
1860
        constants.ISPEC_DISK_SIZE: 64,
1861
        constants.ISPEC_NIC_COUNT: 1,
1862
        constants.ISPEC_SPINDLE_USE: 1,
1863
        },
1864
      constants.ISPECS_MAX: {
1865
        constants.ISPEC_MEM_SIZE: 16384,
1866
        constants.ISPEC_CPU_COUNT: 10,
1867
        constants.ISPEC_DISK_COUNT: 12,
1868
        constants.ISPEC_DISK_SIZE: 1024,
1869
        constants.ISPEC_NIC_COUNT: 9,
1870
        constants.ISPEC_SPINDLE_USE: 18,
1871
        },
1872
      }]
1873
    diff_std = {
1874
        constants.ISPEC_DISK_COUNT: 10,
1875
        constants.ISPEC_DISK_SIZE: 512,
1876
        }
1877
    diff_policy = {
1878
      constants.ISPECS_MINMAX: diff_minmax
1879
      }
1880
    if not isgroup:
1881
      diff_policy[constants.ISPECS_STD] = diff_std
1882
    new_policy = cmdlib._GetUpdatedIPolicy(old_policy, diff_policy,
1883
                                           group_policy=isgroup)
1884

    
1885
    self.assertTrue(constants.ISPECS_MINMAX in new_policy)
1886
    self.assertEqual(new_policy[constants.ISPECS_MINMAX], diff_minmax)
1887
    for key in old_policy:
1888
      if not key in diff_policy:
1889
        self.assertTrue(key in new_policy)
1890
        self.assertEqual(new_policy[key], old_policy[key])
1891

    
1892
    if not isgroup:
1893
      new_std = new_policy[constants.ISPECS_STD]
1894
      for key in diff_std:
1895
        self.assertTrue(key in new_std)
1896
        self.assertEqual(new_std[key], diff_std[key])
1897
      old_std = old_policy.get(constants.ISPECS_STD, {})
1898
      for key in old_std:
1899
        self.assertTrue(key in new_std)
1900
        if key not in diff_std:
1901
          self.assertEqual(new_std[key], old_std[key])
1902

    
1903
  def _TestSet(self, old_policy, diff_policy, isgroup):
1904
    new_policy = cmdlib._GetUpdatedIPolicy(old_policy, diff_policy,
1905
                                           group_policy=isgroup)
1906
    for key in diff_policy:
1907
      self.assertTrue(key in new_policy)
1908
      self.assertEqual(new_policy[key], diff_policy[key])
1909
    for key in old_policy:
1910
      if not key in diff_policy:
1911
        self.assertTrue(key in new_policy)
1912
        self.assertEqual(new_policy[key], old_policy[key])
1913

    
1914
  def testSet(self):
1915
    diff_policy = {
1916
      constants.IPOLICY_VCPU_RATIO: 3,
1917
      constants.IPOLICY_DTS: [constants.DT_FILE],
1918
      }
1919
    self._TestSet(self._OLD_GROUP_POLICY, diff_policy, True)
1920
    self._TestSetSpecs(self._OLD_GROUP_POLICY, True)
1921
    self._TestSet({}, diff_policy, True)
1922
    self._TestSetSpecs({}, True)
1923
    self._TestSet(self._OLD_CLUSTER_POLICY, diff_policy, False)
1924
    self._TestSetSpecs(self._OLD_CLUSTER_POLICY, False)
1925

    
1926
  def testUnset(self):
1927
    old_policy = self._OLD_GROUP_POLICY
1928
    diff_policy = {
1929
      constants.IPOLICY_SPINDLE_RATIO: constants.VALUE_DEFAULT,
1930
      }
1931
    new_policy = cmdlib._GetUpdatedIPolicy(old_policy, diff_policy,
1932
                                           group_policy=True)
1933
    for key in diff_policy:
1934
      self.assertFalse(key in new_policy)
1935
    for key in old_policy:
1936
      if not key in diff_policy:
1937
        self.assertTrue(key in new_policy)
1938
        self.assertEqual(new_policy[key], old_policy[key])
1939

    
1940
    self.assertRaises(errors.OpPrereqError, cmdlib._GetUpdatedIPolicy,
1941
                      old_policy, diff_policy, group_policy=False)
1942

    
1943
  def testUnsetEmpty(self):
1944
    old_policy = {}
1945
    for key in constants.IPOLICY_ALL_KEYS:
1946
      diff_policy = {
1947
        key: constants.VALUE_DEFAULT,
1948
        }
1949
    new_policy = cmdlib._GetUpdatedIPolicy(old_policy, diff_policy,
1950
                                           group_policy=True)
1951
    self.assertEqual(new_policy, old_policy)
1952

    
1953
  def _TestInvalidKeys(self, old_policy, isgroup):
1954
    INVALID_KEY = "this_key_shouldnt_be_allowed"
1955
    INVALID_DICT = {
1956
      INVALID_KEY: 3,
1957
      }
1958
    invalid_policy = INVALID_DICT
1959
    self.assertRaises(errors.OpPrereqError, cmdlib._GetUpdatedIPolicy,
1960
                      old_policy, invalid_policy, group_policy=isgroup)
1961
    invalid_ispecs = {
1962
      constants.ISPECS_MINMAX: [INVALID_DICT],
1963
      }
1964
    self.assertRaises(errors.TypeEnforcementError, cmdlib._GetUpdatedIPolicy,
1965
                      old_policy, invalid_ispecs, group_policy=isgroup)
1966
    if isgroup:
1967
      invalid_for_group = {
1968
        constants.ISPECS_STD: constants.IPOLICY_DEFAULTS[constants.ISPECS_STD],
1969
        }
1970
      self.assertRaises(errors.OpPrereqError, cmdlib._GetUpdatedIPolicy,
1971
                        old_policy, invalid_for_group, group_policy=isgroup)
1972
    good_ispecs = self._OLD_CLUSTER_POLICY[constants.ISPECS_MINMAX]
1973
    invalid_ispecs = copy.deepcopy(good_ispecs)
1974
    invalid_policy = {
1975
      constants.ISPECS_MINMAX: invalid_ispecs,
1976
      }
1977
    for minmax in invalid_ispecs:
1978
      for key in constants.ISPECS_MINMAX_KEYS:
1979
        ispec = minmax[key]
1980
        ispec[INVALID_KEY] = None
1981
        self.assertRaises(errors.TypeEnforcementError,
1982
                          cmdlib._GetUpdatedIPolicy, old_policy,
1983
                          invalid_policy, group_policy=isgroup)
1984
        del ispec[INVALID_KEY]
1985
        for par in constants.ISPECS_PARAMETERS:
1986
          oldv = ispec[par]
1987
          ispec[par] = "this_is_not_good"
1988
          self.assertRaises(errors.TypeEnforcementError,
1989
                            cmdlib._GetUpdatedIPolicy,
1990
                            old_policy, invalid_policy, group_policy=isgroup)
1991
          ispec[par] = oldv
1992
    # This is to make sure that no two errors were present during the tests
1993
    cmdlib._GetUpdatedIPolicy(old_policy, invalid_policy, group_policy=isgroup)
1994

    
1995
  def testInvalidKeys(self):
1996
    self._TestInvalidKeys(self._OLD_GROUP_POLICY, True)
1997
    self._TestInvalidKeys(self._OLD_CLUSTER_POLICY, False)
1998

    
1999
  def testInvalidValues(self):
2000
    for par in (constants.IPOLICY_PARAMETERS |
2001
                frozenset([constants.IPOLICY_DTS])):
2002
      bad_policy = {
2003
        par: "invalid_value",
2004
        }
2005
      self.assertRaises(errors.OpPrereqError, cmdlib._GetUpdatedIPolicy, {},
2006
                        bad_policy, group_policy=True)
2007

    
2008
if __name__ == "__main__":
2009
  testutils.GanetiTestProgram()