Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.cmdlib_unittest.py @ 7352d33b

History | View | Annotate | Download (68.9 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.cmdlib import cluster
38
from ganeti.cmdlib import common
39
from ganeti import opcodes
40
from ganeti import errors
41
from ganeti import utils
42
from ganeti import luxi
43
from ganeti import ht
44
from ganeti import objects
45
from ganeti import compat
46
from ganeti import rpc
47
from ganeti import locking
48
from ganeti import pathutils
49
from ganeti.masterd import iallocator
50
from ganeti.hypervisor import hv_xen
51

    
52
import testutils
53
import mocks
54

    
55

    
56
class TestCertVerification(testutils.GanetiTestCase):
57
  def setUp(self):
58
    testutils.GanetiTestCase.setUp(self)
59

    
60
    self.tmpdir = tempfile.mkdtemp()
61

    
62
  def tearDown(self):
63
    shutil.rmtree(self.tmpdir)
64

    
65
  def testVerifyCertificate(self):
66
    cluster._VerifyCertificate(testutils.TestDataFilename("cert1.pem"))
67

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

    
70
    (errcode, msg) = cluster._VerifyCertificate(nonexist_filename)
71
    self.assertEqual(errcode, cluster.LUClusterVerifyConfig.ETYPE_ERROR)
72

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

    
78

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

    
91

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

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

    
105
    default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
106
    other_iallocator = default_iallocator + "_not"
107

    
108
    op = OpTest()
109
    lu = TestLU(op)
110

    
111
    c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
112

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

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

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

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

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

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

    
155

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

    
163

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

    
169
    assert constants.QR_NODE in constants.QR_VIA_OP
170
    assert constants.QR_INSTANCE in constants.QR_VIA_OP
171

    
172
    for i in constants.QR_VIA_OP:
173
      self.assert_(cmdlib._GetQueryImplementation(i))
174

    
175
    self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation, "")
176
    self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation,
177
                      "xyz")
178

    
179

    
180
class TestLUGroupAssignNodes(unittest.TestCase):
181

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

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

    
199
      return objects.Instance(name=name, primary_node=pnode, disks=disks,
200
                              disk_template=disk_template)
201

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

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

    
217
    self.assertEqual([], new)
218
    self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
219

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

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

    
230

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

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

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

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

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

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

    
289

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

    
298
    if args:
299
      msg = msg % args
300

    
301
    if cond:
302
      errors.append((item, msg))
303

    
304
  _VerifyFiles = cluster.LUClusterVerifyGroup._VerifyFiles
305

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

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

    
399

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

    
409
  def LogWarning(self, text, *args):
410
    self.warning_log.append((text, args))
411

    
412
  def LogInfo(self, text, *args):
413
    self.info_log.append((text, args))
414

    
415

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

    
428
          alloc_result = (moved, [], jobs)
429
          assert iallocator._NEVAC_RESULT(alloc_result)
430

    
431
          lu = _FakeLU()
432
          result = cmdlib._LoadNodeEvacResult(lu, alloc_result,
433
                                              early_release, use_nodes)
434

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

    
445
          self.assertFalse(lu.info_log)
446
          self.assertFalse(lu.warning_log)
447

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

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

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

    
469

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

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

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

    
523
    verified = common._UpdateAndVerifySubDict(old_test, test, self.type_check)
524
    self.assertEqual(verified, mv)
525

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

    
540
    self.assertRaises(errors.TypeEnforcementError,
541
                      common._UpdateAndVerifySubDict, {}, test,
542
                      self.type_check)
543

    
544

    
545
class TestHvStateHelper(unittest.TestCase):
546
  def testWithoutOpData(self):
547
    self.assertEqual(common._MergeAndVerifyHvState(None, NotImplemented),
548
                     None)
549

    
550
  def testWithoutOldData(self):
551
    new = {
552
      constants.HT_XEN_PVM: {
553
        constants.HVST_MEMORY_TOTAL: 4096,
554
        },
555
      }
556
    self.assertEqual(common._MergeAndVerifyHvState(new, None), new)
557

    
558
  def testWithWrongHv(self):
559
    new = {
560
      "i-dont-exist": {
561
        constants.HVST_MEMORY_TOTAL: 4096,
562
        },
563
      }
564
    self.assertRaises(errors.OpPrereqError, common._MergeAndVerifyHvState,
565
                      new, None)
566

    
567
class TestDiskStateHelper(unittest.TestCase):
568
  def testWithoutOpData(self):
569
    self.assertEqual(common._MergeAndVerifyDiskState(None, NotImplemented),
570
                     None)
571

    
572
  def testWithoutOldData(self):
573
    new = {
574
      constants.LD_LV: {
575
        "xenvg": {
576
          constants.DS_DISK_RESERVED: 1024,
577
          },
578
        },
579
      }
580
    self.assertEqual(common._MergeAndVerifyDiskState(new, None), new)
581

    
582
  def testWithWrongStorageType(self):
583
    new = {
584
      "i-dont-exist": {
585
        "xenvg": {
586
          constants.DS_DISK_RESERVED: 1024,
587
          },
588
        },
589
      }
590
    self.assertRaises(errors.OpPrereqError, common._MergeAndVerifyDiskState,
591
                      new, None)
592

    
593

    
594
class TestComputeMinMaxSpec(unittest.TestCase):
595
  def setUp(self):
596
    self.ispecs = {
597
      constants.ISPECS_MAX: {
598
        constants.ISPEC_MEM_SIZE: 512,
599
        constants.ISPEC_DISK_SIZE: 1024,
600
        },
601
      constants.ISPECS_MIN: {
602
        constants.ISPEC_MEM_SIZE: 128,
603
        constants.ISPEC_DISK_COUNT: 1,
604
        },
605
      }
606

    
607
  def testNoneValue(self):
608
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
609
                                              self.ispecs, None) is None)
610

    
611
  def testAutoValue(self):
612
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
613
                                              self.ispecs,
614
                                              constants.VALUE_AUTO) is None)
615

    
616
  def testNotDefined(self):
617
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT, None,
618
                                              self.ispecs, 3) is None)
619

    
620
  def testNoMinDefined(self):
621
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE, None,
622
                                              self.ispecs, 128) is None)
623

    
624
  def testNoMaxDefined(self):
625
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT,
626
                                              None, self.ispecs, 16) is None)
627

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

    
644
  def test(self):
645
    for (name, val) in ((constants.ISPEC_MEM_SIZE, 256),
646
                        (constants.ISPEC_MEM_SIZE, 128),
647
                        (constants.ISPEC_MEM_SIZE, 512),
648
                        (constants.ISPEC_DISK_SIZE, 1024),
649
                        (constants.ISPEC_DISK_SIZE, 0),
650
                        (constants.ISPEC_DISK_COUNT, 1),
651
                        (constants.ISPEC_DISK_COUNT, 5)):
652
      self.assertTrue(common._ComputeMinMaxSpec(name, None, self.ispecs, val)
653
                      is None)
654

    
655

    
656
def _ValidateComputeMinMaxSpec(name, *_):
657
  assert name in constants.ISPECS_PARAMETERS
658
  return None
659

    
660

    
661
def _NoDiskComputeMinMaxSpec(name, *_):
662
  if name == constants.ISPEC_DISK_COUNT:
663
    return name
664
  else:
665
    return None
666

    
667

    
668
class _SpecWrapper:
669
  def __init__(self, spec):
670
    self.spec = spec
671

    
672
  def ComputeMinMaxSpec(self, *args):
673
    return self.spec.pop(0)
674

    
675

    
676
class TestComputeIPolicySpecViolation(unittest.TestCase):
677
  # Minimal policy accepted by _ComputeIPolicySpecViolation()
678
  _MICRO_IPOL = {
679
    constants.IPOLICY_DTS: [constants.DT_PLAIN, constants.DT_DISKLESS],
680
    constants.ISPECS_MINMAX: [NotImplemented],
681
    }
682

    
683
  def test(self):
684
    compute_fn = _ValidateComputeMinMaxSpec
685
    ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
686
                                              [1024], 1, constants.DT_PLAIN,
687
                                              _compute_fn=compute_fn)
688
    self.assertEqual(ret, [])
689

    
690
  def testDiskFull(self):
691
    compute_fn = _NoDiskComputeMinMaxSpec
692
    ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
693
                                              [1024], 1, constants.DT_PLAIN,
694
                                              _compute_fn=compute_fn)
695
    self.assertEqual(ret, [constants.ISPEC_DISK_COUNT])
696

    
697
  def testDiskLess(self):
698
    compute_fn = _NoDiskComputeMinMaxSpec
699
    ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
700
                                              [1024], 1, constants.DT_DISKLESS,
701
                                              _compute_fn=compute_fn)
702
    self.assertEqual(ret, [])
703

    
704
  def testWrongTemplates(self):
705
    compute_fn = _ValidateComputeMinMaxSpec
706
    ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
707
                                              [1024], 1, constants.DT_DRBD8,
708
                                              _compute_fn=compute_fn)
709
    self.assertEqual(len(ret), 1)
710
    self.assertTrue("Disk template" in ret[0])
711

    
712
  def testInvalidArguments(self):
713
    self.assertRaises(AssertionError, common._ComputeIPolicySpecViolation,
714
                      self._MICRO_IPOL, 1024, 1, 1, 1, [], 1,
715
                      constants.DT_PLAIN,)
716

    
717
  def testInvalidSpec(self):
718
    spec = _SpecWrapper([None, False, "foo", None, "bar", None])
719
    compute_fn = spec.ComputeMinMaxSpec
720
    ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
721
                                              [1024], 1, constants.DT_PLAIN,
722
                                              _compute_fn=compute_fn)
723
    self.assertEqual(ret, ["foo", "bar"])
724
    self.assertFalse(spec.spec)
725

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

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

    
799

    
800
class _StubComputeIPolicySpecViolation:
801
  def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
802
               spindle_use, disk_template):
803
    self.mem_size = mem_size
804
    self.cpu_count = cpu_count
805
    self.disk_count = disk_count
806
    self.nic_count = nic_count
807
    self.disk_sizes = disk_sizes
808
    self.spindle_use = spindle_use
809
    self.disk_template = disk_template
810

    
811
  def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
812
               spindle_use, disk_template):
813
    assert self.mem_size == mem_size
814
    assert self.cpu_count == cpu_count
815
    assert self.disk_count == disk_count
816
    assert self.nic_count == nic_count
817
    assert self.disk_sizes == disk_sizes
818
    assert self.spindle_use == spindle_use
819
    assert self.disk_template == disk_template
820

    
821
    return []
822

    
823

    
824
class _FakeConfigForComputeIPolicyInstanceViolation:
825
  def __init__(self, be):
826
    self.cluster = objects.Cluster(beparams={"default": be})
827

    
828
  def GetClusterInfo(self):
829
    return self.cluster
830

    
831

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

    
854

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

    
872

    
873
class _CallRecorder:
874
  def __init__(self, return_value=None):
875
    self.called = False
876
    self.return_value = return_value
877

    
878
  def __call__(self, *args):
879
    self.called = True
880
    return self.return_value
881

    
882

    
883
class TestComputeIPolicyNodeViolation(unittest.TestCase):
884
  def setUp(self):
885
    self.recorder = _CallRecorder(return_value=[])
886

    
887
  def testSameGroup(self):
888
    ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
889
                                              "foo", "foo", NotImplemented,
890
                                              _compute_fn=self.recorder)
891
    self.assertFalse(self.recorder.called)
892
    self.assertEqual(ret, [])
893

    
894
  def testDifferentGroup(self):
895
    ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
896
                                              "foo", "bar", NotImplemented,
897
                                              _compute_fn=self.recorder)
898
    self.assertTrue(self.recorder.called)
899
    self.assertEqual(ret, [])
900

    
901

    
902
class _FakeConfigForTargetNodeIPolicy:
903
  def __init__(self, node_info=NotImplemented):
904
    self._node_info = node_info
905

    
906
  def GetNodeInfo(self, _):
907
    return self._node_info
908

    
909

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

    
918
  def testNoViolation(self):
919
    compute_recoder = _CallRecorder(return_value=[])
920
    cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
921
                                   self.target_node, NotImplemented,
922
                                   _compute_fn=compute_recoder)
923
    self.assertTrue(compute_recoder.called)
924
    self.assertEqual(self.lu.warning_log, [])
925

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

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

    
944

    
945
class TestApplyContainerMods(unittest.TestCase):
946
  def testEmptyContainer(self):
947
    container = []
948
    chgdesc = []
949
    cmdlib.ApplyContainerMods("test", container, chgdesc, [], None, None, None)
950
    self.assertEqual(container, [])
951
    self.assertEqual(chgdesc, [])
952

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

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

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

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

    
995
    mods = cmdlib.PrepareContainerMods([
996
      (constants.DDM_REMOVE, 0, object()),
997
      ], None)
998
    self.assertRaises(AssertionError, cmdlib.ApplyContainerMods,
999
                      "test", [""], None, mods, None, None, None)
1000

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

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

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

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

    
1044
  class _PrivateData:
1045
    def __init__(self):
1046
      self.data = None
1047

    
1048
  @staticmethod
1049
  def _CreateTestFn(idx, params, private):
1050
    private.data = ("add", idx, params)
1051
    return ((100 * idx, params), [
1052
      ("test/%s" % idx, hex(idx)),
1053
      ])
1054

    
1055
  @staticmethod
1056
  def _ModifyTestFn(idx, item, params, private):
1057
    private.data = ("modify", idx, params)
1058
    return [
1059
      ("test/%s" % idx, "modify %s" % params),
1060
      ]
1061

    
1062
  @staticmethod
1063
  def _RemoveTestFn(idx, item, private):
1064
    private.data = ("remove", idx, item)
1065

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

    
1109

    
1110
class _FakeConfigForGenDiskTemplate:
1111
  def __init__(self):
1112
    self._unique_id = itertools.count()
1113
    self._drbd_minor = itertools.count(20)
1114
    self._port = itertools.count(constants.FIRST_DRBD_PORT)
1115
    self._secret = itertools.count()
1116

    
1117
  def GetVGName(self):
1118
    return "testvg"
1119

    
1120
  def GenerateUniqueID(self, ec_id):
1121
    return "ec%s-uq%s" % (ec_id, self._unique_id.next())
1122

    
1123
  def AllocateDRBDMinor(self, nodes, instance):
1124
    return [self._drbd_minor.next()
1125
            for _ in nodes]
1126

    
1127
  def AllocatePort(self):
1128
    return self._port.next()
1129

    
1130
  def GenerateDRBDSecret(self, ec_id):
1131
    return "ec%s-secret%s" % (ec_id, self._secret.next())
1132

    
1133
  def GetInstanceInfo(self, _):
1134
    return "foobar"
1135

    
1136

    
1137
class _FakeProcForGenDiskTemplate:
1138
  def GetECId(self):
1139
    return 0
1140

    
1141

    
1142
class TestGenerateDiskTemplate(unittest.TestCase):
1143
  def setUp(self):
1144
    nodegroup = objects.NodeGroup(name="ng")
1145
    nodegroup.UpgradeConfig()
1146

    
1147
    cfg = _FakeConfigForGenDiskTemplate()
1148
    proc = _FakeProcForGenDiskTemplate()
1149

    
1150
    self.lu = _FakeLU(cfg=cfg, proc=proc)
1151
    self.nodegroup = nodegroup
1152

    
1153
  @staticmethod
1154
  def GetDiskParams():
1155
    return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1156

    
1157
  def testWrongDiskTemplate(self):
1158
    gdt = cmdlib._GenerateDiskTemplate
1159
    disk_template = "##unknown##"
1160

    
1161
    assert disk_template not in constants.DISK_TEMPLATES
1162

    
1163
    self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
1164
                      "inst26831.example.com", "node30113.example.com", [], [],
1165
                      NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1166
                      self.GetDiskParams())
1167

    
1168
  def testDiskless(self):
1169
    gdt = cmdlib._GenerateDiskTemplate
1170

    
1171
    result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1172
                 "node30113.example.com", [], [],
1173
                 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1174
                 self.GetDiskParams())
1175
    self.assertEqual(result, [])
1176

    
1177
  def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1178
                       file_storage_dir=NotImplemented,
1179
                       file_driver=NotImplemented,
1180
                       req_file_storage=NotImplemented,
1181
                       req_shr_file_storage=NotImplemented):
1182
    gdt = cmdlib._GenerateDiskTemplate
1183

    
1184
    map(lambda params: utils.ForceDictType(params,
1185
                                           constants.IDISK_PARAMS_TYPES),
1186
        disk_info)
1187

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

    
1197
    result = gdt(self.lu, template, "inst21662.example.com",
1198
                 "node21741.example.com", [],
1199
                 disk_info, file_storage_dir, file_driver, base_index,
1200
                 self.lu.LogInfo, self.GetDiskParams(),
1201
                 _req_file_storage=req_file_storage,
1202
                 _req_shr_file_storage=req_shr_file_storage)
1203

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

    
1211
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1212
    cmdlib._UpdateIvNames(base_index, result)
1213
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1214

    
1215
    return result
1216

    
1217
  def _CheckIvNames(self, disks, base_index, end_index):
1218
    self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1219
                     ["disk/%s" % i for i in range(base_index, end_index)])
1220

    
1221
  def testPlain(self):
1222
    disk_info = [{
1223
      constants.IDISK_SIZE: 1024,
1224
      constants.IDISK_MODE: constants.DISK_RDWR,
1225
      }, {
1226
      constants.IDISK_SIZE: 4096,
1227
      constants.IDISK_VG: "othervg",
1228
      constants.IDISK_MODE: constants.DISK_RDWR,
1229
      }]
1230

    
1231
    result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1232
                                   constants.LD_LV)
1233

    
1234
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1235
      ("testvg", "ec0-uq0.disk3"),
1236
      ("othervg", "ec0-uq1.disk4"),
1237
      ])
1238

    
1239
  @staticmethod
1240
  def _AllowFileStorage():
1241
    pass
1242

    
1243
  @staticmethod
1244
  def _ForbidFileStorage():
1245
    raise errors.OpPrereqError("Disallowed in test")
1246

    
1247
  def testFile(self):
1248
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1249
                      constants.DT_FILE, [], 0, NotImplemented,
1250
                      req_file_storage=self._ForbidFileStorage)
1251
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1252
                      constants.DT_SHARED_FILE, [], 0, NotImplemented,
1253
                      req_shr_file_storage=self._ForbidFileStorage)
1254

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

    
1267
      result = self._TestTrivialDisk(disk_template, disk_info, 2,
1268
        constants.LD_FILE, file_storage_dir="/tmp",
1269
        file_driver=constants.FD_BLKTAP,
1270
        req_file_storage=self._AllowFileStorage,
1271
        req_shr_file_storage=self._AllowFileStorage)
1272

    
1273
      self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1274
        (constants.FD_BLKTAP, "/tmp/disk2"),
1275
        (constants.FD_BLKTAP, "/tmp/disk3"),
1276
        (constants.FD_BLKTAP, "/tmp/disk4"),
1277
        ])
1278

    
1279
  def testBlock(self):
1280
    disk_info = [{
1281
      constants.IDISK_SIZE: 8 * 1024,
1282
      constants.IDISK_MODE: constants.DISK_RDWR,
1283
      constants.IDISK_ADOPT: "/tmp/some/block/dev",
1284
      }]
1285

    
1286
    result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1287
                                   constants.LD_BLOCKDEV)
1288

    
1289
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1290
      (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1291
      ])
1292

    
1293
  def testRbd(self):
1294
    disk_info = [{
1295
      constants.IDISK_SIZE: 8 * 1024,
1296
      constants.IDISK_MODE: constants.DISK_RDONLY,
1297
      }, {
1298
      constants.IDISK_SIZE: 100 * 1024,
1299
      constants.IDISK_MODE: constants.DISK_RDWR,
1300
      }]
1301

    
1302
    result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1303
                                   constants.LD_RBD)
1304

    
1305
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1306
      ("rbd", "ec0-uq0.rbd.disk0"),
1307
      ("rbd", "ec0-uq1.rbd.disk1"),
1308
      ])
1309

    
1310
  def testDrbd8(self):
1311
    gdt = cmdlib._GenerateDiskTemplate
1312
    drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1313
    drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1314

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

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

    
1340
    assert len(exp_logical_ids) == len(disk_info)
1341

    
1342
    map(lambda params: utils.ForceDictType(params,
1343
                                           constants.IDISK_PARAMS_TYPES),
1344
        disk_info)
1345

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

    
1352
    result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1353
                 "node1334.example.com", ["node12272.example.com"],
1354
                 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1355
                 self.GetDiskParams())
1356

    
1357
    for (idx, disk) in enumerate(result):
1358
      self.assertTrue(isinstance(disk, objects.Disk))
1359
      self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1360
      self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1361
      self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1362

    
1363
      for child in disk.children:
1364
        self.assertTrue(isinstance(disk, objects.Disk))
1365
        self.assertEqual(child.dev_type, constants.LD_LV)
1366
        self.assertTrue(child.children is None)
1367

    
1368
      self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1369
                       exp_logical_ids[idx])
1370

    
1371
      self.assertEqual(len(disk.children), 2)
1372
      self.assertEqual(disk.children[0].size, disk.size)
1373
      self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1374

    
1375
    self._CheckIvNames(result, 0, len(disk_info))
1376
    cmdlib._UpdateIvNames(0, result)
1377
    self._CheckIvNames(result, 0, len(disk_info))
1378

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

    
1388

    
1389
class _ConfigForDiskWipe:
1390
  def __init__(self, exp_node):
1391
    self._exp_node = exp_node
1392

    
1393
  def SetDiskID(self, device, node):
1394
    assert isinstance(device, objects.Disk)
1395
    assert node == self._exp_node
1396

    
1397

    
1398
class _RpcForDiskWipe:
1399
  def __init__(self, exp_node, pause_cb, wipe_cb):
1400
    self._exp_node = exp_node
1401
    self._pause_cb = pause_cb
1402
    self._wipe_cb = wipe_cb
1403

    
1404
  def call_blockdev_pause_resume_sync(self, node, disks, pause):
1405
    assert node == self._exp_node
1406
    return rpc.RpcResult(data=self._pause_cb(disks, pause))
1407

    
1408
  def call_blockdev_wipe(self, node, bdev, offset, size):
1409
    assert node == self._exp_node
1410
    return rpc.RpcResult(data=self._wipe_cb(bdev, offset, size))
1411

    
1412

    
1413
class _DiskPauseTracker:
1414
  def __init__(self):
1415
    self.history = []
1416

    
1417
  def __call__(self, (disks, instance), pause):
1418
    assert not (set(disks) - set(instance.disks))
1419

    
1420
    self.history.extend((i.logical_id, i.size, pause)
1421
                        for i in disks)
1422

    
1423
    return (True, [True] * len(disks))
1424

    
1425

    
1426
class _DiskWipeProgressTracker:
1427
  def __init__(self, start_offset):
1428
    self._start_offset = start_offset
1429
    self.progress = {}
1430

    
1431
  def __call__(self, (disk, _), offset, size):
1432
    assert isinstance(offset, (long, int))
1433
    assert isinstance(size, (long, int))
1434

    
1435
    max_chunk_size = (disk.size / 100.0 * constants.MIN_WIPE_CHUNK_PERCENT)
1436

    
1437
    assert offset >= self._start_offset
1438
    assert (offset + size) <= disk.size
1439

    
1440
    assert size > 0
1441
    assert size <= constants.MAX_WIPE_CHUNK
1442
    assert size <= max_chunk_size
1443

    
1444
    assert offset == self._start_offset or disk.logical_id in self.progress
1445

    
1446
    # Keep track of progress
1447
    cur_progress = self.progress.setdefault(disk.logical_id, self._start_offset)
1448

    
1449
    assert cur_progress == offset
1450

    
1451
    # Record progress
1452
    self.progress[disk.logical_id] += size
1453

    
1454
    return (True, None)
1455

    
1456

    
1457
class TestWipeDisks(unittest.TestCase):
1458
  def _FailingPauseCb(self, (disks, _), pause):
1459
    self.assertEqual(len(disks), 3)
1460
    self.assertTrue(pause)
1461
    # Simulate an RPC error
1462
    return (False, "error")
1463

    
1464
  def testPauseFailure(self):
1465
    node_name = "node1372.example.com"
1466

    
1467
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, self._FailingPauseCb,
1468
                                     NotImplemented),
1469
                 cfg=_ConfigForDiskWipe(node_name))
1470

    
1471
    disks = [
1472
      objects.Disk(dev_type=constants.LD_LV),
1473
      objects.Disk(dev_type=constants.LD_LV),
1474
      objects.Disk(dev_type=constants.LD_LV),
1475
      ]
1476

    
1477
    instance = objects.Instance(name="inst21201",
1478
                                primary_node=node_name,
1479
                                disk_template=constants.DT_PLAIN,
1480
                                disks=disks)
1481

    
1482
    self.assertRaises(errors.OpExecError, cmdlib._WipeDisks, lu, instance)
1483

    
1484
  def _FailingWipeCb(self, (disk, _), offset, size):
1485
    # This should only ever be called for the first disk
1486
    self.assertEqual(disk.logical_id, "disk0")
1487
    return (False, None)
1488

    
1489
  def testFailingWipe(self):
1490
    node_name = "node13445.example.com"
1491
    pt = _DiskPauseTracker()
1492

    
1493
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pt, self._FailingWipeCb),
1494
                 cfg=_ConfigForDiskWipe(node_name))
1495

    
1496
    disks = [
1497
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1498
                   size=100 * 1024),
1499
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1500
                   size=500 * 1024),
1501
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=256),
1502
      ]
1503

    
1504
    instance = objects.Instance(name="inst562",
1505
                                primary_node=node_name,
1506
                                disk_template=constants.DT_PLAIN,
1507
                                disks=disks)
1508

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

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

    
1526
  def _PrepareWipeTest(self, start_offset, disks):
1527
    node_name = "node-with-offset%s.example.com" % start_offset
1528
    pauset = _DiskPauseTracker()
1529
    progresst = _DiskWipeProgressTracker(start_offset)
1530

    
1531
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pauset, progresst),
1532
                 cfg=_ConfigForDiskWipe(node_name))
1533

    
1534
    instance = objects.Instance(name="inst3560",
1535
                                primary_node=node_name,
1536
                                disk_template=constants.DT_PLAIN,
1537
                                disks=disks)
1538

    
1539
    return (lu, instance, pauset, progresst)
1540

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

    
1551
    (lu, instance, pauset, progresst) = self._PrepareWipeTest(0, disks)
1552

    
1553
    cmdlib._WipeDisks(lu, instance)
1554

    
1555
    self.assertEqual(pauset.history, [
1556
      ("disk0", 1024, True),
1557
      ("disk1", 500 * 1024, True),
1558
      ("disk2", 128, True),
1559
      ("disk3", constants.MAX_WIPE_CHUNK, True),
1560
      ("disk0", 1024, False),
1561
      ("disk1", 500 * 1024, False),
1562
      ("disk2", 128, False),
1563
      ("disk3", constants.MAX_WIPE_CHUNK, False),
1564
      ])
1565

    
1566
    # Ensure the complete disk has been wiped
1567
    self.assertEqual(progresst.progress,
1568
                     dict((i.logical_id, i.size) for i in disks))
1569

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

    
1579
      (lu, instance, pauset, progresst) = \
1580
        self._PrepareWipeTest(start_offset, disks)
1581

    
1582
      # Test start offset with only one disk
1583
      cmdlib._WipeDisks(lu, instance,
1584
                        disks=[(1, disks[1], start_offset)])
1585

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

    
1595

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

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

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

    
1626

    
1627
class TestCopyLockList(unittest.TestCase):
1628
  def test(self):
1629
    self.assertEqual(cmdlib._CopyLockList([]), [])
1630
    self.assertEqual(cmdlib._CopyLockList(None), None)
1631
    self.assertEqual(cmdlib._CopyLockList(locking.ALL_SET), locking.ALL_SET)
1632

    
1633
    names = ["foo", "bar"]
1634
    output = cmdlib._CopyLockList(names)
1635
    self.assertEqual(names, output)
1636
    self.assertNotEqual(id(names), id(output), msg="List was not copied")
1637

    
1638

    
1639
class TestCheckOpportunisticLocking(unittest.TestCase):
1640
  class OpTest(opcodes.OpCode):
1641
    OP_PARAMS = [
1642
      opcodes._POpportunisticLocking,
1643
      opcodes._PIAllocFromDesc(""),
1644
      ]
1645

    
1646
  @classmethod
1647
  def _MakeOp(cls, **kwargs):
1648
    op = cls.OpTest(**kwargs)
1649
    op.Validate(True)
1650
    return op
1651

    
1652
  def testMissingAttributes(self):
1653
    self.assertRaises(AttributeError, cmdlib._CheckOpportunisticLocking,
1654
                      object())
1655

    
1656
  def testDefaults(self):
1657
    op = self._MakeOp()
1658
    cmdlib._CheckOpportunisticLocking(op)
1659

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

    
1670

    
1671
class _OpTestVerifyErrors(opcodes.OpCode):
1672
  OP_PARAMS = [
1673
    opcodes._PDebugSimulateErrors,
1674
    opcodes._PErrorCodes,
1675
    opcodes._PIgnoreErrors,
1676
    ]
1677

    
1678

    
1679
class _LuTestVerifyErrors(cluster._VerifyErrors):
1680
  def __init__(self, **kwargs):
1681
    cluster._VerifyErrors.__init__(self)
1682
    self.op = _OpTestVerifyErrors(**kwargs)
1683
    self.op.Validate(True)
1684
    self.msglist = []
1685
    self._feedback_fn = self.msglist.append
1686
    self.bad = False
1687

    
1688
  def DispatchCallError(self, which, *args, **kwargs):
1689
    if which:
1690
      self._Error(*args, **kwargs)
1691
    else:
1692
      self._ErrorIf(True, *args, **kwargs)
1693

    
1694
  def CallErrorIf(self, c, *args, **kwargs):
1695
    self._ErrorIf(c, *args, **kwargs)
1696

    
1697

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

    
1716
  def testNoError(self):
1717
    lu = _LuTestVerifyErrors()
1718
    lu.CallErrorIf(False, self._ERR1CODE, *self._ERR1ARGS)
1719
    self.assertFalse(lu.bad)
1720
    self.assertFalse(lu.msglist)
1721

    
1722
  def _InitTest(self, **kwargs):
1723
    self.lu1 = _LuTestVerifyErrors(**kwargs)
1724
    self.lu2 = _LuTestVerifyErrors(**kwargs)
1725

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

    
1735
  def _checkMsgCommon(self, logstr, errmsg, itype, item, warning):
1736
    self.assertTrue(errmsg in logstr)
1737
    if warning:
1738
      self.assertTrue("WARNING" in logstr)
1739
    else:
1740
      self.assertTrue("ERROR" in logstr)
1741
    self.assertTrue(itype in logstr)
1742
    self.assertTrue(item in logstr)
1743

    
1744
  def _checkMsg1(self, logstr, warning=False):
1745
    self._checkMsgCommon(logstr, self._ERR1MSG, self._NODESTR,
1746
                         self._NODENAME, warning)
1747

    
1748
  def _checkMsg2(self, logstr, warning=False):
1749
    self._checkMsgCommon(logstr, self._ERR2MSG, self._INSTSTR,
1750
                         self._INSTNAME, warning)
1751

    
1752
  def testPlain(self):
1753
    self._InitTest()
1754
    lu = self._CallError(*self._ERR1ARGS)
1755
    self.assertTrue(lu.bad)
1756
    self.assertEqual(len(lu.msglist), 1)
1757
    self._checkMsg1(lu.msglist[0])
1758

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

    
1768
  def testIgnore(self):
1769
    self._InitTest(ignore_errors=[self._ERR1ID])
1770
    lu = self._CallError(*self._ERR1ARGS)
1771
    self.assertFalse(lu.bad)
1772
    self.assertEqual(len(lu.msglist), 1)
1773
    self._checkMsg1(lu.msglist[0], warning=True)
1774

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

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

    
1793
  def testDebugSimulate(self):
1794
    lu = _LuTestVerifyErrors(debug_simulate_errors=True)
1795
    lu.CallErrorIf(False, *self._ERR1ARGS)
1796
    self.assertTrue(lu.bad)
1797
    self.assertEqual(len(lu.msglist), 1)
1798
    self._checkMsg1(lu.msglist[0])
1799

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

    
1808

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

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

    
1889
    self.assertTrue(constants.ISPECS_MINMAX in new_policy)
1890
    self.assertEqual(new_policy[constants.ISPECS_MINMAX], diff_minmax)
1891
    for key in old_policy:
1892
      if not key in diff_policy:
1893
        self.assertTrue(key in new_policy)
1894
        self.assertEqual(new_policy[key], old_policy[key])
1895

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

    
1907
  def _TestSet(self, old_policy, diff_policy, isgroup):
1908
    new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1909
                                           group_policy=isgroup)
1910
    for key in diff_policy:
1911
      self.assertTrue(key in new_policy)
1912
      self.assertEqual(new_policy[key], diff_policy[key])
1913
    for key in old_policy:
1914
      if not key in diff_policy:
1915
        self.assertTrue(key in new_policy)
1916
        self.assertEqual(new_policy[key], old_policy[key])
1917

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

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

    
1944
    self.assertRaises(errors.OpPrereqError, common._GetUpdatedIPolicy,
1945
                      old_policy, diff_policy, group_policy=False)
1946

    
1947
  def testUnsetEmpty(self):
1948
    old_policy = {}
1949
    for key in constants.IPOLICY_ALL_KEYS:
1950
      diff_policy = {
1951
        key: constants.VALUE_DEFAULT,
1952
        }
1953
    new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1954
                                           group_policy=True)
1955
    self.assertEqual(new_policy, old_policy)
1956

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

    
2000
  def testInvalidKeys(self):
2001
    self._TestInvalidKeys(self._OLD_GROUP_POLICY, True)
2002
    self._TestInvalidKeys(self._OLD_CLUSTER_POLICY, False)
2003

    
2004
  def testInvalidValues(self):
2005
    for par in (constants.IPOLICY_PARAMETERS |
2006
                frozenset([constants.IPOLICY_DTS])):
2007
      bad_policy = {
2008
        par: "invalid_value",
2009
        }
2010
      self.assertRaises(errors.OpPrereqError, common._GetUpdatedIPolicy, {},
2011
                        bad_policy, group_policy=True)
2012

    
2013
if __name__ == "__main__":
2014
  testutils.GanetiTestProgram()