Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (69.2 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 tempfile
28
import shutil
29
import operator
30
import itertools
31
import copy
32

    
33
from ganeti import constants
34
from ganeti import mcpu
35
from ganeti import cmdlib
36
from ganeti.cmdlib import cluster
37
from ganeti.cmdlib import group
38
from ganeti.cmdlib import instance
39
from ganeti.cmdlib import common
40
from ganeti.cmdlib import query
41
from ganeti import opcodes
42
from ganeti import errors
43
from ganeti import utils
44
from ganeti import luxi
45
from ganeti import ht
46
from ganeti import objects
47
from ganeti import compat
48
from ganeti import rpc
49
from ganeti import locking
50
from ganeti import pathutils
51
from ganeti.masterd import iallocator
52
from ganeti.hypervisor import hv_xen
53

    
54
import testutils
55
import mocks
56

    
57

    
58
class TestCertVerification(testutils.GanetiTestCase):
59
  def setUp(self):
60
    testutils.GanetiTestCase.setUp(self)
61

    
62
    self.tmpdir = tempfile.mkdtemp()
63

    
64
  def tearDown(self):
65
    shutil.rmtree(self.tmpdir)
66

    
67
  def testVerifyCertificate(self):
68
    cluster._VerifyCertificate(testutils.TestDataFilename("cert1.pem"))
69

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

    
72
    (errcode, msg) = cluster._VerifyCertificate(nonexist_filename)
73
    self.assertEqual(errcode, cluster.LUClusterVerifyConfig.ETYPE_ERROR)
74

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

    
80

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

    
93

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

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

    
107
    default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
108
    other_iallocator = default_iallocator + "_not"
109

    
110
    op = OpTest()
111
    lu = TestLU(op)
112

    
113
    c_i = lambda: common._CheckIAllocatorOrNode(lu, "iallocator", "node")
114

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

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

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

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

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

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

    
157

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

    
165

    
166
class TestLUQuery(unittest.TestCase):
167
  def test(self):
168
    self.assertEqual(sorted(query._QUERY_IMPL.keys()),
169
                     sorted(constants.QR_VIA_OP))
170

    
171
    assert constants.QR_NODE in constants.QR_VIA_OP
172
    assert constants.QR_INSTANCE in constants.QR_VIA_OP
173

    
174
    for i in constants.QR_VIA_OP:
175
      self.assert_(query._GetQueryImplementation(i))
176

    
177
    self.assertRaises(errors.OpPrereqError, query._GetQueryImplementation,
178
                      "")
179
    self.assertRaises(errors.OpPrereqError, query._GetQueryImplementation,
180
                      "xyz")
181

    
182

    
183
class TestLUGroupAssignNodes(unittest.TestCase):
184

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

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

    
202
      return objects.Instance(name=name, primary_node=pnode, disks=disks,
203
                              disk_template=disk_template)
204

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

    
214
    # Test first with the existing state.
215
    (new, prev) = \
216
      group.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
217
                                                                node_data,
218
                                                                instance_data)
219

    
220
    self.assertEqual([], new)
221
    self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
222

    
223
    # And now some changes.
224
    (new, prev) = \
225
      group.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
226
                                                                  "g3")],
227
                                                                node_data,
228
                                                                instance_data)
229

    
230
    self.assertEqual(set(["inst1a", "inst1b"]), set(new))
231
    self.assertEqual(set(["inst3c"]), set(prev))
232

    
233

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

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

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

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

    
283
    (online, perhost) = fn(nodes, "default", nodes)
284
    self.assertEqual(online, ["node2", "node3"])
285
    self.assertEqual(set(perhost.keys()), set(online))
286

    
287
    self.assertEqual(perhost, {
288
      "node2": [],
289
      "node3": [],
290
      })
291

    
292

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

    
301
    if args:
302
      msg = msg % args
303

    
304
    if cond:
305
      errors.append((item, msg))
306

    
307
  _VerifyFiles = cluster.LUClusterVerifyGroup._VerifyFiles
308

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

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

    
402

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

    
412
  def LogWarning(self, text, *args):
413
    self.warning_log.append((text, args))
414

    
415
  def LogInfo(self, text, *args):
416
    self.info_log.append((text, args))
417

    
418

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

    
431
          alloc_result = (moved, [], jobs)
432
          assert iallocator._NEVAC_RESULT(alloc_result)
433

    
434
          lu = _FakeLU()
435
          result = common._LoadNodeEvacResult(lu, alloc_result,
436
                                              early_release, use_nodes)
437

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

    
448
          self.assertFalse(lu.info_log)
449
          self.assertFalse(lu.warning_log)
450

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

    
457
  def testFailed(self):
458
    alloc_result = ([], [
459
      ("inst5191.example.com", "errormsg21178"),
460
      ], [])
461
    assert iallocator._NEVAC_RESULT(alloc_result)
462

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

    
472

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

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

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

    
526
    verified = common._UpdateAndVerifySubDict(old_test, test, self.type_check)
527
    self.assertEqual(verified, mv)
528

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

    
543
    self.assertRaises(errors.TypeEnforcementError,
544
                      common._UpdateAndVerifySubDict, {}, test,
545
                      self.type_check)
546

    
547

    
548
class TestHvStateHelper(unittest.TestCase):
549
  def testWithoutOpData(self):
550
    self.assertEqual(common._MergeAndVerifyHvState(None, NotImplemented),
551
                     None)
552

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

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

    
570
class TestDiskStateHelper(unittest.TestCase):
571
  def testWithoutOpData(self):
572
    self.assertEqual(common._MergeAndVerifyDiskState(None, NotImplemented),
573
                     None)
574

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

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

    
596

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

    
610
  def testNoneValue(self):
611
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
612
                                              self.ispecs, None) is None)
613

    
614
  def testAutoValue(self):
615
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
616
                                              self.ispecs,
617
                                              constants.VALUE_AUTO) is None)
618

    
619
  def testNotDefined(self):
620
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT, None,
621
                                              self.ispecs, 3) is None)
622

    
623
  def testNoMinDefined(self):
624
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE, None,
625
                                              self.ispecs, 128) is None)
626

    
627
  def testNoMaxDefined(self):
628
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT,
629
                                              None, self.ispecs, 16) is None)
630

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

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

    
658

    
659
def _ValidateComputeMinMaxSpec(name, *_):
660
  assert name in constants.ISPECS_PARAMETERS
661
  return None
662

    
663

    
664
def _NoDiskComputeMinMaxSpec(name, *_):
665
  if name == constants.ISPEC_DISK_COUNT:
666
    return name
667
  else:
668
    return None
669

    
670

    
671
class _SpecWrapper:
672
  def __init__(self, spec):
673
    self.spec = spec
674

    
675
  def ComputeMinMaxSpec(self, *args):
676
    return self.spec.pop(0)
677

    
678

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

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

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

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

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

    
715
  def testInvalidArguments(self):
716
    self.assertRaises(AssertionError, common._ComputeIPolicySpecViolation,
717
                      self._MICRO_IPOL, 1024, 1, 1, 1, [], 1,
718
                      constants.DT_PLAIN,)
719

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

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

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

    
802

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

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

    
824
    return []
825

    
826

    
827
class _FakeConfigForComputeIPolicyInstanceViolation:
828
  def __init__(self, be):
829
    self.cluster = objects.Cluster(beparams={"default": be})
830

    
831
  def GetClusterInfo(self):
832
    return self.cluster
833

    
834

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

    
857

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

    
875

    
876
class _CallRecorder:
877
  def __init__(self, return_value=None):
878
    self.called = False
879
    self.return_value = return_value
880

    
881
  def __call__(self, *args):
882
    self.called = True
883
    return self.return_value
884

    
885

    
886
class TestComputeIPolicyNodeViolation(unittest.TestCase):
887
  def setUp(self):
888
    self.recorder = _CallRecorder(return_value=[])
889

    
890
  def testSameGroup(self):
891
    ret = instance._ComputeIPolicyNodeViolation(NotImplemented,
892
                                                NotImplemented,
893
                                                "foo", "foo", NotImplemented,
894
                                                _compute_fn=self.recorder)
895
    self.assertFalse(self.recorder.called)
896
    self.assertEqual(ret, [])
897

    
898
  def testDifferentGroup(self):
899
    ret = instance._ComputeIPolicyNodeViolation(NotImplemented,
900
                                                NotImplemented,
901
                                                "foo", "bar", NotImplemented,
902
                                                _compute_fn=self.recorder)
903
    self.assertTrue(self.recorder.called)
904
    self.assertEqual(ret, [])
905

    
906

    
907
class _FakeConfigForTargetNodeIPolicy:
908
  def __init__(self, node_info=NotImplemented):
909
    self._node_info = node_info
910

    
911
  def GetNodeInfo(self, _):
912
    return self._node_info
913

    
914

    
915
class TestCheckTargetNodeIPolicy(unittest.TestCase):
916
  def setUp(self):
917
    self.instance = objects.Instance(primary_node="blubb")
918
    self.target_node = objects.Node(group="bar")
919
    node_info = objects.Node(group="foo")
920
    fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
921
    self.lu = _FakeLU(cfg=fake_cfg)
922

    
923
  def testNoViolation(self):
924
    compute_recoder = _CallRecorder(return_value=[])
925
    instance._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
926
                                     self.target_node, NotImplemented,
927
                                     _compute_fn=compute_recoder)
928
    self.assertTrue(compute_recoder.called)
929
    self.assertEqual(self.lu.warning_log, [])
930

    
931
  def testNoIgnore(self):
932
    compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
933
    self.assertRaises(errors.OpPrereqError, instance._CheckTargetNodeIPolicy,
934
                      self.lu, NotImplemented, self.instance,
935
                      self.target_node, NotImplemented,
936
                      _compute_fn=compute_recoder)
937
    self.assertTrue(compute_recoder.called)
938
    self.assertEqual(self.lu.warning_log, [])
939

    
940
  def testIgnoreViolation(self):
941
    compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
942
    instance._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
943
                                     self.target_node, NotImplemented,
944
                                     ignore=True, _compute_fn=compute_recoder)
945
    self.assertTrue(compute_recoder.called)
946
    msg = ("Instance does not meet target node group's (bar) instance policy:"
947
           " mem_size not in range")
948
    self.assertEqual(self.lu.warning_log, [(msg, ())])
949

    
950

    
951
class TestApplyContainerMods(unittest.TestCase):
952
  def testEmptyContainer(self):
953
    container = []
954
    chgdesc = []
955
    instance.ApplyContainerMods("test", container, chgdesc, [], None, None,
956
                                None)
957
    self.assertEqual(container, [])
958
    self.assertEqual(chgdesc, [])
959

    
960
  def testAdd(self):
961
    container = []
962
    chgdesc = []
963
    mods = instance.PrepareContainerMods([
964
      (constants.DDM_ADD, -1, "Hello"),
965
      (constants.DDM_ADD, -1, "World"),
966
      (constants.DDM_ADD, 0, "Start"),
967
      (constants.DDM_ADD, -1, "End"),
968
      ], None)
969
    instance.ApplyContainerMods("test", container, chgdesc, mods,
970
                                None, None, None)
971
    self.assertEqual(container, ["Start", "Hello", "World", "End"])
972
    self.assertEqual(chgdesc, [])
973

    
974
    mods = instance.PrepareContainerMods([
975
      (constants.DDM_ADD, 0, "zero"),
976
      (constants.DDM_ADD, 3, "Added"),
977
      (constants.DDM_ADD, 5, "four"),
978
      (constants.DDM_ADD, 7, "xyz"),
979
      ], None)
980
    instance.ApplyContainerMods("test", container, chgdesc, mods,
981
                                None, None, None)
982
    self.assertEqual(container,
983
                     ["zero", "Start", "Hello", "Added", "World", "four",
984
                      "End", "xyz"])
985
    self.assertEqual(chgdesc, [])
986

    
987
    for idx in [-2, len(container) + 1]:
988
      mods = instance.PrepareContainerMods([
989
        (constants.DDM_ADD, idx, "error"),
990
        ], None)
991
      self.assertRaises(IndexError, instance.ApplyContainerMods,
992
                        "test", container, None, mods, None, None, None)
993

    
994
  def testRemoveError(self):
995
    for idx in [0, 1, 2, 100, -1, -4]:
996
      mods = instance.PrepareContainerMods([
997
        (constants.DDM_REMOVE, idx, None),
998
        ], None)
999
      self.assertRaises(IndexError, instance.ApplyContainerMods,
1000
                        "test", [], None, mods, None, None, None)
1001

    
1002
    mods = instance.PrepareContainerMods([
1003
      (constants.DDM_REMOVE, 0, object()),
1004
      ], None)
1005
    self.assertRaises(AssertionError, instance.ApplyContainerMods,
1006
                      "test", [""], None, mods, None, None, None)
1007

    
1008
  def testAddError(self):
1009
    for idx in range(-100, -1) + [100]:
1010
      mods = instance.PrepareContainerMods([
1011
        (constants.DDM_ADD, idx, None),
1012
        ], None)
1013
      self.assertRaises(IndexError, instance.ApplyContainerMods,
1014
                        "test", [], None, mods, None, None, None)
1015

    
1016
  def testRemove(self):
1017
    container = ["item 1", "item 2"]
1018
    mods = instance.PrepareContainerMods([
1019
      (constants.DDM_ADD, -1, "aaa"),
1020
      (constants.DDM_REMOVE, -1, None),
1021
      (constants.DDM_ADD, -1, "bbb"),
1022
      ], None)
1023
    chgdesc = []
1024
    instance.ApplyContainerMods("test", container, chgdesc, mods,
1025
                                None, None, None)
1026
    self.assertEqual(container, ["item 1", "item 2", "bbb"])
1027
    self.assertEqual(chgdesc, [
1028
      ("test/2", "remove"),
1029
      ])
1030

    
1031
  def testModify(self):
1032
    container = ["item 1", "item 2"]
1033
    mods = instance.PrepareContainerMods([
1034
      (constants.DDM_MODIFY, -1, "a"),
1035
      (constants.DDM_MODIFY, 0, "b"),
1036
      (constants.DDM_MODIFY, 1, "c"),
1037
      ], None)
1038
    chgdesc = []
1039
    instance.ApplyContainerMods("test", container, chgdesc, mods,
1040
                                None, None, None)
1041
    self.assertEqual(container, ["item 1", "item 2"])
1042
    self.assertEqual(chgdesc, [])
1043

    
1044
    for idx in [-2, len(container) + 1]:
1045
      mods = instance.PrepareContainerMods([
1046
        (constants.DDM_MODIFY, idx, "error"),
1047
        ], None)
1048
      self.assertRaises(IndexError, instance.ApplyContainerMods,
1049
                        "test", container, None, mods, None, None, None)
1050

    
1051
  class _PrivateData:
1052
    def __init__(self):
1053
      self.data = None
1054

    
1055
  @staticmethod
1056
  def _CreateTestFn(idx, params, private):
1057
    private.data = ("add", idx, params)
1058
    return ((100 * idx, params), [
1059
      ("test/%s" % idx, hex(idx)),
1060
      ])
1061

    
1062
  @staticmethod
1063
  def _ModifyTestFn(idx, item, params, private):
1064
    private.data = ("modify", idx, params)
1065
    return [
1066
      ("test/%s" % idx, "modify %s" % params),
1067
      ]
1068

    
1069
  @staticmethod
1070
  def _RemoveTestFn(idx, item, private):
1071
    private.data = ("remove", idx, item)
1072

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

    
1117

    
1118
class _FakeConfigForGenDiskTemplate:
1119
  def __init__(self):
1120
    self._unique_id = itertools.count()
1121
    self._drbd_minor = itertools.count(20)
1122
    self._port = itertools.count(constants.FIRST_DRBD_PORT)
1123
    self._secret = itertools.count()
1124

    
1125
  def GetVGName(self):
1126
    return "testvg"
1127

    
1128
  def GenerateUniqueID(self, ec_id):
1129
    return "ec%s-uq%s" % (ec_id, self._unique_id.next())
1130

    
1131
  def AllocateDRBDMinor(self, nodes, instance):
1132
    return [self._drbd_minor.next()
1133
            for _ in nodes]
1134

    
1135
  def AllocatePort(self):
1136
    return self._port.next()
1137

    
1138
  def GenerateDRBDSecret(self, ec_id):
1139
    return "ec%s-secret%s" % (ec_id, self._secret.next())
1140

    
1141
  def GetInstanceInfo(self, _):
1142
    return "foobar"
1143

    
1144

    
1145
class _FakeProcForGenDiskTemplate:
1146
  def GetECId(self):
1147
    return 0
1148

    
1149

    
1150
class TestGenerateDiskTemplate(unittest.TestCase):
1151
  def setUp(self):
1152
    nodegroup = objects.NodeGroup(name="ng")
1153
    nodegroup.UpgradeConfig()
1154

    
1155
    cfg = _FakeConfigForGenDiskTemplate()
1156
    proc = _FakeProcForGenDiskTemplate()
1157

    
1158
    self.lu = _FakeLU(cfg=cfg, proc=proc)
1159
    self.nodegroup = nodegroup
1160

    
1161
  @staticmethod
1162
  def GetDiskParams():
1163
    return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1164

    
1165
  def testWrongDiskTemplate(self):
1166
    gdt = instance._GenerateDiskTemplate
1167
    disk_template = "##unknown##"
1168

    
1169
    assert disk_template not in constants.DISK_TEMPLATES
1170

    
1171
    self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
1172
                      "inst26831.example.com", "node30113.example.com", [], [],
1173
                      NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1174
                      self.GetDiskParams())
1175

    
1176
  def testDiskless(self):
1177
    gdt = instance._GenerateDiskTemplate
1178

    
1179
    result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1180
                 "node30113.example.com", [], [],
1181
                 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1182
                 self.GetDiskParams())
1183
    self.assertEqual(result, [])
1184

    
1185
  def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1186
                       file_storage_dir=NotImplemented,
1187
                       file_driver=NotImplemented,
1188
                       req_file_storage=NotImplemented,
1189
                       req_shr_file_storage=NotImplemented):
1190
    gdt = instance._GenerateDiskTemplate
1191

    
1192
    map(lambda params: utils.ForceDictType(params,
1193
                                           constants.IDISK_PARAMS_TYPES),
1194
        disk_info)
1195

    
1196
    # Check if non-empty list of secondaries is rejected
1197
    self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1198
                      template, "inst25088.example.com",
1199
                      "node185.example.com", ["node323.example.com"], [],
1200
                      NotImplemented, NotImplemented, base_index,
1201
                      self.lu.LogInfo, self.GetDiskParams(),
1202
                      _req_file_storage=req_file_storage,
1203
                      _req_shr_file_storage=req_shr_file_storage)
1204

    
1205
    result = gdt(self.lu, template, "inst21662.example.com",
1206
                 "node21741.example.com", [],
1207
                 disk_info, file_storage_dir, file_driver, base_index,
1208
                 self.lu.LogInfo, self.GetDiskParams(),
1209
                 _req_file_storage=req_file_storage,
1210
                 _req_shr_file_storage=req_shr_file_storage)
1211

    
1212
    for (idx, disk) in enumerate(result):
1213
      self.assertTrue(isinstance(disk, objects.Disk))
1214
      self.assertEqual(disk.dev_type, exp_dev_type)
1215
      self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1216
      self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1217
      self.assertTrue(disk.children is None)
1218

    
1219
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1220
    instance._UpdateIvNames(base_index, result)
1221
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1222

    
1223
    return result
1224

    
1225
  def _CheckIvNames(self, disks, base_index, end_index):
1226
    self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1227
                     ["disk/%s" % i for i in range(base_index, end_index)])
1228

    
1229
  def testPlain(self):
1230
    disk_info = [{
1231
      constants.IDISK_SIZE: 1024,
1232
      constants.IDISK_MODE: constants.DISK_RDWR,
1233
      }, {
1234
      constants.IDISK_SIZE: 4096,
1235
      constants.IDISK_VG: "othervg",
1236
      constants.IDISK_MODE: constants.DISK_RDWR,
1237
      }]
1238

    
1239
    result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1240
                                   constants.LD_LV)
1241

    
1242
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1243
      ("testvg", "ec0-uq0.disk3"),
1244
      ("othervg", "ec0-uq1.disk4"),
1245
      ])
1246

    
1247
  @staticmethod
1248
  def _AllowFileStorage():
1249
    pass
1250

    
1251
  @staticmethod
1252
  def _ForbidFileStorage():
1253
    raise errors.OpPrereqError("Disallowed in test")
1254

    
1255
  def testFile(self):
1256
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1257
                      constants.DT_FILE, [], 0, NotImplemented,
1258
                      req_file_storage=self._ForbidFileStorage)
1259
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1260
                      constants.DT_SHARED_FILE, [], 0, NotImplemented,
1261
                      req_shr_file_storage=self._ForbidFileStorage)
1262

    
1263
    for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1264
      disk_info = [{
1265
        constants.IDISK_SIZE: 80 * 1024,
1266
        constants.IDISK_MODE: constants.DISK_RDONLY,
1267
        }, {
1268
        constants.IDISK_SIZE: 4096,
1269
        constants.IDISK_MODE: constants.DISK_RDWR,
1270
        }, {
1271
        constants.IDISK_SIZE: 6 * 1024,
1272
        constants.IDISK_MODE: constants.DISK_RDWR,
1273
        }]
1274

    
1275
      result = self._TestTrivialDisk(disk_template, disk_info, 2,
1276
        constants.LD_FILE, file_storage_dir="/tmp",
1277
        file_driver=constants.FD_BLKTAP,
1278
        req_file_storage=self._AllowFileStorage,
1279
        req_shr_file_storage=self._AllowFileStorage)
1280

    
1281
      self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1282
        (constants.FD_BLKTAP, "/tmp/disk2"),
1283
        (constants.FD_BLKTAP, "/tmp/disk3"),
1284
        (constants.FD_BLKTAP, "/tmp/disk4"),
1285
        ])
1286

    
1287
  def testBlock(self):
1288
    disk_info = [{
1289
      constants.IDISK_SIZE: 8 * 1024,
1290
      constants.IDISK_MODE: constants.DISK_RDWR,
1291
      constants.IDISK_ADOPT: "/tmp/some/block/dev",
1292
      }]
1293

    
1294
    result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1295
                                   constants.LD_BLOCKDEV)
1296

    
1297
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1298
      (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1299
      ])
1300

    
1301
  def testRbd(self):
1302
    disk_info = [{
1303
      constants.IDISK_SIZE: 8 * 1024,
1304
      constants.IDISK_MODE: constants.DISK_RDONLY,
1305
      }, {
1306
      constants.IDISK_SIZE: 100 * 1024,
1307
      constants.IDISK_MODE: constants.DISK_RDWR,
1308
      }]
1309

    
1310
    result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1311
                                   constants.LD_RBD)
1312

    
1313
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1314
      ("rbd", "ec0-uq0.rbd.disk0"),
1315
      ("rbd", "ec0-uq1.rbd.disk1"),
1316
      ])
1317

    
1318
  def testDrbd8(self):
1319
    gdt = instance._GenerateDiskTemplate
1320
    drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1321
    drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1322

    
1323
    disk_info = [{
1324
      constants.IDISK_SIZE: 1024,
1325
      constants.IDISK_MODE: constants.DISK_RDWR,
1326
      }, {
1327
      constants.IDISK_SIZE: 100 * 1024,
1328
      constants.IDISK_MODE: constants.DISK_RDONLY,
1329
      constants.IDISK_METAVG: "metavg",
1330
      }, {
1331
      constants.IDISK_SIZE: 4096,
1332
      constants.IDISK_MODE: constants.DISK_RDWR,
1333
      constants.IDISK_VG: "vgxyz",
1334
      },
1335
      ]
1336

    
1337
    exp_logical_ids = [[
1338
      (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1339
      (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1340
      ], [
1341
      (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1342
      ("metavg", "ec0-uq1.disk1_meta"),
1343
      ], [
1344
      ("vgxyz", "ec0-uq2.disk2_data"),
1345
      (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1346
      ]]
1347

    
1348
    assert len(exp_logical_ids) == len(disk_info)
1349

    
1350
    map(lambda params: utils.ForceDictType(params,
1351
                                           constants.IDISK_PARAMS_TYPES),
1352
        disk_info)
1353

    
1354
    # Check if empty list of secondaries is rejected
1355
    self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1356
                      "inst827.example.com", "node1334.example.com", [],
1357
                      disk_info, NotImplemented, NotImplemented, 0,
1358
                      self.lu.LogInfo, self.GetDiskParams())
1359

    
1360
    result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1361
                 "node1334.example.com", ["node12272.example.com"],
1362
                 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1363
                 self.GetDiskParams())
1364

    
1365
    for (idx, disk) in enumerate(result):
1366
      self.assertTrue(isinstance(disk, objects.Disk))
1367
      self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1368
      self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1369
      self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1370

    
1371
      for child in disk.children:
1372
        self.assertTrue(isinstance(disk, objects.Disk))
1373
        self.assertEqual(child.dev_type, constants.LD_LV)
1374
        self.assertTrue(child.children is None)
1375

    
1376
      self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1377
                       exp_logical_ids[idx])
1378

    
1379
      self.assertEqual(len(disk.children), 2)
1380
      self.assertEqual(disk.children[0].size, disk.size)
1381
      self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1382

    
1383
    self._CheckIvNames(result, 0, len(disk_info))
1384
    instance._UpdateIvNames(0, result)
1385
    self._CheckIvNames(result, 0, len(disk_info))
1386

    
1387
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1388
      ("node1334.example.com", "node12272.example.com",
1389
       constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1390
      ("node1334.example.com", "node12272.example.com",
1391
       constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1392
      ("node1334.example.com", "node12272.example.com",
1393
       constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1394
      ])
1395

    
1396

    
1397
class _ConfigForDiskWipe:
1398
  def __init__(self, exp_node):
1399
    self._exp_node = exp_node
1400

    
1401
  def SetDiskID(self, device, node):
1402
    assert isinstance(device, objects.Disk)
1403
    assert node == self._exp_node
1404

    
1405

    
1406
class _RpcForDiskWipe:
1407
  def __init__(self, exp_node, pause_cb, wipe_cb):
1408
    self._exp_node = exp_node
1409
    self._pause_cb = pause_cb
1410
    self._wipe_cb = wipe_cb
1411

    
1412
  def call_blockdev_pause_resume_sync(self, node, disks, pause):
1413
    assert node == self._exp_node
1414
    return rpc.RpcResult(data=self._pause_cb(disks, pause))
1415

    
1416
  def call_blockdev_wipe(self, node, bdev, offset, size):
1417
    assert node == self._exp_node
1418
    return rpc.RpcResult(data=self._wipe_cb(bdev, offset, size))
1419

    
1420

    
1421
class _DiskPauseTracker:
1422
  def __init__(self):
1423
    self.history = []
1424

    
1425
  def __call__(self, (disks, instance), pause):
1426
    assert not (set(disks) - set(instance.disks))
1427

    
1428
    self.history.extend((i.logical_id, i.size, pause)
1429
                        for i in disks)
1430

    
1431
    return (True, [True] * len(disks))
1432

    
1433

    
1434
class _DiskWipeProgressTracker:
1435
  def __init__(self, start_offset):
1436
    self._start_offset = start_offset
1437
    self.progress = {}
1438

    
1439
  def __call__(self, (disk, _), offset, size):
1440
    assert isinstance(offset, (long, int))
1441
    assert isinstance(size, (long, int))
1442

    
1443
    max_chunk_size = (disk.size / 100.0 * constants.MIN_WIPE_CHUNK_PERCENT)
1444

    
1445
    assert offset >= self._start_offset
1446
    assert (offset + size) <= disk.size
1447

    
1448
    assert size > 0
1449
    assert size <= constants.MAX_WIPE_CHUNK
1450
    assert size <= max_chunk_size
1451

    
1452
    assert offset == self._start_offset or disk.logical_id in self.progress
1453

    
1454
    # Keep track of progress
1455
    cur_progress = self.progress.setdefault(disk.logical_id, self._start_offset)
1456

    
1457
    assert cur_progress == offset
1458

    
1459
    # Record progress
1460
    self.progress[disk.logical_id] += size
1461

    
1462
    return (True, None)
1463

    
1464

    
1465
class TestWipeDisks(unittest.TestCase):
1466
  def _FailingPauseCb(self, (disks, _), pause):
1467
    self.assertEqual(len(disks), 3)
1468
    self.assertTrue(pause)
1469
    # Simulate an RPC error
1470
    return (False, "error")
1471

    
1472
  def testPauseFailure(self):
1473
    node_name = "node1372.example.com"
1474

    
1475
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, self._FailingPauseCb,
1476
                                     NotImplemented),
1477
                 cfg=_ConfigForDiskWipe(node_name))
1478

    
1479
    disks = [
1480
      objects.Disk(dev_type=constants.LD_LV),
1481
      objects.Disk(dev_type=constants.LD_LV),
1482
      objects.Disk(dev_type=constants.LD_LV),
1483
      ]
1484

    
1485
    inst = objects.Instance(name="inst21201",
1486
                            primary_node=node_name,
1487
                            disk_template=constants.DT_PLAIN,
1488
                            disks=disks)
1489

    
1490
    self.assertRaises(errors.OpExecError, instance._WipeDisks, lu, inst)
1491

    
1492
  def _FailingWipeCb(self, (disk, _), offset, size):
1493
    # This should only ever be called for the first disk
1494
    self.assertEqual(disk.logical_id, "disk0")
1495
    return (False, None)
1496

    
1497
  def testFailingWipe(self):
1498
    node_name = "node13445.example.com"
1499
    pt = _DiskPauseTracker()
1500

    
1501
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pt, self._FailingWipeCb),
1502
                 cfg=_ConfigForDiskWipe(node_name))
1503

    
1504
    disks = [
1505
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1506
                   size=100 * 1024),
1507
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1508
                   size=500 * 1024),
1509
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=256),
1510
      ]
1511

    
1512
    inst = objects.Instance(name="inst562",
1513
                            primary_node=node_name,
1514
                            disk_template=constants.DT_PLAIN,
1515
                            disks=disks)
1516

    
1517
    try:
1518
      instance._WipeDisks(lu, inst)
1519
    except errors.OpExecError, err:
1520
      self.assertTrue(str(err), "Could not wipe disk 0 at offset 0 ")
1521
    else:
1522
      self.fail("Did not raise exception")
1523

    
1524
    # Check if all disks were paused and resumed
1525
    self.assertEqual(pt.history, [
1526
      ("disk0", 100 * 1024, True),
1527
      ("disk1", 500 * 1024, True),
1528
      ("disk2", 256, True),
1529
      ("disk0", 100 * 1024, False),
1530
      ("disk1", 500 * 1024, False),
1531
      ("disk2", 256, False),
1532
      ])
1533

    
1534
  def _PrepareWipeTest(self, start_offset, disks):
1535
    node_name = "node-with-offset%s.example.com" % start_offset
1536
    pauset = _DiskPauseTracker()
1537
    progresst = _DiskWipeProgressTracker(start_offset)
1538

    
1539
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pauset, progresst),
1540
                 cfg=_ConfigForDiskWipe(node_name))
1541

    
1542
    instance = objects.Instance(name="inst3560",
1543
                                primary_node=node_name,
1544
                                disk_template=constants.DT_PLAIN,
1545
                                disks=disks)
1546

    
1547
    return (lu, instance, pauset, progresst)
1548

    
1549
  def testNormalWipe(self):
1550
    disks = [
1551
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk0", size=1024),
1552
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1553
                   size=500 * 1024),
1554
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=128),
1555
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk3",
1556
                   size=constants.MAX_WIPE_CHUNK),
1557
      ]
1558

    
1559
    (lu, inst, pauset, progresst) = self._PrepareWipeTest(0, disks)
1560

    
1561
    instance._WipeDisks(lu, inst)
1562

    
1563
    self.assertEqual(pauset.history, [
1564
      ("disk0", 1024, True),
1565
      ("disk1", 500 * 1024, True),
1566
      ("disk2", 128, True),
1567
      ("disk3", constants.MAX_WIPE_CHUNK, True),
1568
      ("disk0", 1024, False),
1569
      ("disk1", 500 * 1024, False),
1570
      ("disk2", 128, False),
1571
      ("disk3", constants.MAX_WIPE_CHUNK, False),
1572
      ])
1573

    
1574
    # Ensure the complete disk has been wiped
1575
    self.assertEqual(progresst.progress,
1576
                     dict((i.logical_id, i.size) for i in disks))
1577

    
1578
  def testWipeWithStartOffset(self):
1579
    for start_offset in [0, 280, 8895, 1563204]:
1580
      disks = [
1581
        objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1582
                     size=128),
1583
        objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1584
                     size=start_offset + (100 * 1024)),
1585
        ]
1586

    
1587
      (lu, inst, pauset, progresst) = \
1588
        self._PrepareWipeTest(start_offset, disks)
1589

    
1590
      # Test start offset with only one disk
1591
      instance._WipeDisks(lu, inst,
1592
                          disks=[(1, disks[1], start_offset)])
1593

    
1594
      # Only the second disk may have been paused and wiped
1595
      self.assertEqual(pauset.history, [
1596
        ("disk1", start_offset + (100 * 1024), True),
1597
        ("disk1", start_offset + (100 * 1024), False),
1598
        ])
1599
      self.assertEqual(progresst.progress, {
1600
        "disk1": disks[1].size,
1601
        })
1602

    
1603

    
1604
class TestDiskSizeInBytesToMebibytes(unittest.TestCase):
1605
  def testLessThanOneMebibyte(self):
1606
    for i in [1, 2, 7, 512, 1000, 1023]:
1607
      lu = _FakeLU()
1608
      result = instance._DiskSizeInBytesToMebibytes(lu, i)
1609
      self.assertEqual(result, 1)
1610
      self.assertEqual(len(lu.warning_log), 1)
1611
      self.assertEqual(len(lu.warning_log[0]), 2)
1612
      (_, (warnsize, )) = lu.warning_log[0]
1613
      self.assertEqual(warnsize, (1024 * 1024) - i)
1614

    
1615
  def testEven(self):
1616
    for i in [1, 2, 7, 512, 1000, 1023]:
1617
      lu = _FakeLU()
1618
      result = instance._DiskSizeInBytesToMebibytes(lu, i * 1024 * 1024)
1619
      self.assertEqual(result, i)
1620
      self.assertFalse(lu.warning_log)
1621

    
1622
  def testLargeNumber(self):
1623
    for i in [1, 2, 7, 512, 1000, 1023, 2724, 12420]:
1624
      for j in [1, 2, 486, 326, 986, 1023]:
1625
        lu = _FakeLU()
1626
        size = (1024 * 1024 * i) + j
1627
        result = instance._DiskSizeInBytesToMebibytes(lu, size)
1628
        self.assertEqual(result, i + 1, msg="Amount was not rounded up")
1629
        self.assertEqual(len(lu.warning_log), 1)
1630
        self.assertEqual(len(lu.warning_log[0]), 2)
1631
        (_, (warnsize, )) = lu.warning_log[0]
1632
        self.assertEqual(warnsize, (1024 * 1024) - j)
1633

    
1634

    
1635
class TestCopyLockList(unittest.TestCase):
1636
  def test(self):
1637
    self.assertEqual(instance._CopyLockList([]), [])
1638
    self.assertEqual(instance._CopyLockList(None), None)
1639
    self.assertEqual(instance._CopyLockList(locking.ALL_SET), locking.ALL_SET)
1640

    
1641
    names = ["foo", "bar"]
1642
    output = instance._CopyLockList(names)
1643
    self.assertEqual(names, output)
1644
    self.assertNotEqual(id(names), id(output), msg="List was not copied")
1645

    
1646

    
1647
class TestCheckOpportunisticLocking(unittest.TestCase):
1648
  class OpTest(opcodes.OpCode):
1649
    OP_PARAMS = [
1650
      opcodes._POpportunisticLocking,
1651
      opcodes._PIAllocFromDesc(""),
1652
      ]
1653

    
1654
  @classmethod
1655
  def _MakeOp(cls, **kwargs):
1656
    op = cls.OpTest(**kwargs)
1657
    op.Validate(True)
1658
    return op
1659

    
1660
  def testMissingAttributes(self):
1661
    self.assertRaises(AttributeError, instance._CheckOpportunisticLocking,
1662
                      object())
1663

    
1664
  def testDefaults(self):
1665
    op = self._MakeOp()
1666
    instance._CheckOpportunisticLocking(op)
1667

    
1668
  def test(self):
1669
    for iallocator in [None, "something", "other"]:
1670
      for opplock in [False, True]:
1671
        op = self._MakeOp(iallocator=iallocator,
1672
                          opportunistic_locking=opplock)
1673
        if opplock and not iallocator:
1674
          self.assertRaises(errors.OpPrereqError,
1675
                            instance._CheckOpportunisticLocking, op)
1676
        else:
1677
          instance._CheckOpportunisticLocking(op)
1678

    
1679

    
1680
class _OpTestVerifyErrors(opcodes.OpCode):
1681
  OP_PARAMS = [
1682
    opcodes._PDebugSimulateErrors,
1683
    opcodes._PErrorCodes,
1684
    opcodes._PIgnoreErrors,
1685
    ]
1686

    
1687

    
1688
class _LuTestVerifyErrors(cluster._VerifyErrors):
1689
  def __init__(self, **kwargs):
1690
    cluster._VerifyErrors.__init__(self)
1691
    self.op = _OpTestVerifyErrors(**kwargs)
1692
    self.op.Validate(True)
1693
    self.msglist = []
1694
    self._feedback_fn = self.msglist.append
1695
    self.bad = False
1696

    
1697
  def DispatchCallError(self, which, *args, **kwargs):
1698
    if which:
1699
      self._Error(*args, **kwargs)
1700
    else:
1701
      self._ErrorIf(True, *args, **kwargs)
1702

    
1703
  def CallErrorIf(self, c, *args, **kwargs):
1704
    self._ErrorIf(c, *args, **kwargs)
1705

    
1706

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

    
1725
  def testNoError(self):
1726
    lu = _LuTestVerifyErrors()
1727
    lu.CallErrorIf(False, self._ERR1CODE, *self._ERR1ARGS)
1728
    self.assertFalse(lu.bad)
1729
    self.assertFalse(lu.msglist)
1730

    
1731
  def _InitTest(self, **kwargs):
1732
    self.lu1 = _LuTestVerifyErrors(**kwargs)
1733
    self.lu2 = _LuTestVerifyErrors(**kwargs)
1734

    
1735
  def _CallError(self, *args, **kwargs):
1736
    # Check that _Error() and _ErrorIf() produce the same results
1737
    self.lu1.DispatchCallError(True, *args, **kwargs)
1738
    self.lu2.DispatchCallError(False, *args, **kwargs)
1739
    self.assertEqual(self.lu1.bad, self.lu2.bad)
1740
    self.assertEqual(self.lu1.msglist, self.lu2.msglist)
1741
    # Test-specific checks are made on one LU
1742
    return self.lu1
1743

    
1744
  def _checkMsgCommon(self, logstr, errmsg, itype, item, warning):
1745
    self.assertTrue(errmsg in logstr)
1746
    if warning:
1747
      self.assertTrue("WARNING" in logstr)
1748
    else:
1749
      self.assertTrue("ERROR" in logstr)
1750
    self.assertTrue(itype in logstr)
1751
    self.assertTrue(item in logstr)
1752

    
1753
  def _checkMsg1(self, logstr, warning=False):
1754
    self._checkMsgCommon(logstr, self._ERR1MSG, self._NODESTR,
1755
                         self._NODENAME, warning)
1756

    
1757
  def _checkMsg2(self, logstr, warning=False):
1758
    self._checkMsgCommon(logstr, self._ERR2MSG, self._INSTSTR,
1759
                         self._INSTNAME, warning)
1760

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

    
1768
  def testMultiple(self):
1769
    self._InitTest()
1770
    self._CallError(*self._ERR1ARGS)
1771
    lu = self._CallError(*self._ERR2ARGS)
1772
    self.assertTrue(lu.bad)
1773
    self.assertEqual(len(lu.msglist), 2)
1774
    self._checkMsg1(lu.msglist[0])
1775
    self._checkMsg2(lu.msglist[1])
1776

    
1777
  def testIgnore(self):
1778
    self._InitTest(ignore_errors=[self._ERR1ID])
1779
    lu = self._CallError(*self._ERR1ARGS)
1780
    self.assertFalse(lu.bad)
1781
    self.assertEqual(len(lu.msglist), 1)
1782
    self._checkMsg1(lu.msglist[0], warning=True)
1783

    
1784
  def testWarning(self):
1785
    self._InitTest()
1786
    lu = self._CallError(*self._ERR1ARGS,
1787
                         code=_LuTestVerifyErrors.ETYPE_WARNING)
1788
    self.assertFalse(lu.bad)
1789
    self.assertEqual(len(lu.msglist), 1)
1790
    self._checkMsg1(lu.msglist[0], warning=True)
1791

    
1792
  def testWarning2(self):
1793
    self._InitTest()
1794
    self._CallError(*self._ERR1ARGS)
1795
    lu = self._CallError(*self._ERR2ARGS,
1796
                         code=_LuTestVerifyErrors.ETYPE_WARNING)
1797
    self.assertTrue(lu.bad)
1798
    self.assertEqual(len(lu.msglist), 2)
1799
    self._checkMsg1(lu.msglist[0])
1800
    self._checkMsg2(lu.msglist[1], warning=True)
1801

    
1802
  def testDebugSimulate(self):
1803
    lu = _LuTestVerifyErrors(debug_simulate_errors=True)
1804
    lu.CallErrorIf(False, *self._ERR1ARGS)
1805
    self.assertTrue(lu.bad)
1806
    self.assertEqual(len(lu.msglist), 1)
1807
    self._checkMsg1(lu.msglist[0])
1808

    
1809
  def testErrCodes(self):
1810
    self._InitTest(error_codes=True)
1811
    lu = self._CallError(*self._ERR1ARGS)
1812
    self.assertTrue(lu.bad)
1813
    self.assertEqual(len(lu.msglist), 1)
1814
    self._checkMsg1(lu.msglist[0])
1815
    self.assertTrue(self._ERR1ID in lu.msglist[0])
1816

    
1817

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

    
1867
  def _TestSetSpecs(self, old_policy, isgroup):
1868
    diff_minmax = [{
1869
      constants.ISPECS_MIN: {
1870
        constants.ISPEC_MEM_SIZE: 64,
1871
        constants.ISPEC_CPU_COUNT: 1,
1872
        constants.ISPEC_DISK_COUNT: 2,
1873
        constants.ISPEC_DISK_SIZE: 64,
1874
        constants.ISPEC_NIC_COUNT: 1,
1875
        constants.ISPEC_SPINDLE_USE: 1,
1876
        },
1877
      constants.ISPECS_MAX: {
1878
        constants.ISPEC_MEM_SIZE: 16384,
1879
        constants.ISPEC_CPU_COUNT: 10,
1880
        constants.ISPEC_DISK_COUNT: 12,
1881
        constants.ISPEC_DISK_SIZE: 1024,
1882
        constants.ISPEC_NIC_COUNT: 9,
1883
        constants.ISPEC_SPINDLE_USE: 18,
1884
        },
1885
      }]
1886
    diff_std = {
1887
        constants.ISPEC_DISK_COUNT: 10,
1888
        constants.ISPEC_DISK_SIZE: 512,
1889
        }
1890
    diff_policy = {
1891
      constants.ISPECS_MINMAX: diff_minmax
1892
      }
1893
    if not isgroup:
1894
      diff_policy[constants.ISPECS_STD] = diff_std
1895
    new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1896
                                           group_policy=isgroup)
1897

    
1898
    self.assertTrue(constants.ISPECS_MINMAX in new_policy)
1899
    self.assertEqual(new_policy[constants.ISPECS_MINMAX], diff_minmax)
1900
    for key in old_policy:
1901
      if not key in diff_policy:
1902
        self.assertTrue(key in new_policy)
1903
        self.assertEqual(new_policy[key], old_policy[key])
1904

    
1905
    if not isgroup:
1906
      new_std = new_policy[constants.ISPECS_STD]
1907
      for key in diff_std:
1908
        self.assertTrue(key in new_std)
1909
        self.assertEqual(new_std[key], diff_std[key])
1910
      old_std = old_policy.get(constants.ISPECS_STD, {})
1911
      for key in old_std:
1912
        self.assertTrue(key in new_std)
1913
        if key not in diff_std:
1914
          self.assertEqual(new_std[key], old_std[key])
1915

    
1916
  def _TestSet(self, old_policy, diff_policy, isgroup):
1917
    new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1918
                                           group_policy=isgroup)
1919
    for key in diff_policy:
1920
      self.assertTrue(key in new_policy)
1921
      self.assertEqual(new_policy[key], diff_policy[key])
1922
    for key in old_policy:
1923
      if not key in diff_policy:
1924
        self.assertTrue(key in new_policy)
1925
        self.assertEqual(new_policy[key], old_policy[key])
1926

    
1927
  def testSet(self):
1928
    diff_policy = {
1929
      constants.IPOLICY_VCPU_RATIO: 3,
1930
      constants.IPOLICY_DTS: [constants.DT_FILE],
1931
      }
1932
    self._TestSet(self._OLD_GROUP_POLICY, diff_policy, True)
1933
    self._TestSetSpecs(self._OLD_GROUP_POLICY, True)
1934
    self._TestSet({}, diff_policy, True)
1935
    self._TestSetSpecs({}, True)
1936
    self._TestSet(self._OLD_CLUSTER_POLICY, diff_policy, False)
1937
    self._TestSetSpecs(self._OLD_CLUSTER_POLICY, False)
1938

    
1939
  def testUnset(self):
1940
    old_policy = self._OLD_GROUP_POLICY
1941
    diff_policy = {
1942
      constants.IPOLICY_SPINDLE_RATIO: constants.VALUE_DEFAULT,
1943
      }
1944
    new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1945
                                           group_policy=True)
1946
    for key in diff_policy:
1947
      self.assertFalse(key in new_policy)
1948
    for key in old_policy:
1949
      if not key in diff_policy:
1950
        self.assertTrue(key in new_policy)
1951
        self.assertEqual(new_policy[key], old_policy[key])
1952

    
1953
    self.assertRaises(errors.OpPrereqError, common._GetUpdatedIPolicy,
1954
                      old_policy, diff_policy, group_policy=False)
1955

    
1956
  def testUnsetEmpty(self):
1957
    old_policy = {}
1958
    for key in constants.IPOLICY_ALL_KEYS:
1959
      diff_policy = {
1960
        key: constants.VALUE_DEFAULT,
1961
        }
1962
    new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1963
                                           group_policy=True)
1964
    self.assertEqual(new_policy, old_policy)
1965

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

    
2009
  def testInvalidKeys(self):
2010
    self._TestInvalidKeys(self._OLD_GROUP_POLICY, True)
2011
    self._TestInvalidKeys(self._OLD_CLUSTER_POLICY, False)
2012

    
2013
  def testInvalidValues(self):
2014
    for par in (constants.IPOLICY_PARAMETERS |
2015
                frozenset([constants.IPOLICY_DTS])):
2016
      bad_policy = {
2017
        par: "invalid_value",
2018
        }
2019
      self.assertRaises(errors.OpPrereqError, common._GetUpdatedIPolicy, {},
2020
                        bad_policy, group_policy=True)
2021

    
2022
if __name__ == "__main__":
2023
  testutils.GanetiTestProgram()