Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.cmdlib_unittest.py @ 22b7f6f8

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 import opcodes
41
from ganeti import errors
42
from ganeti import utils
43
from ganeti import luxi
44
from ganeti import ht
45
from ganeti import objects
46
from ganeti import compat
47
from ganeti import rpc
48
from ganeti import locking
49
from ganeti import pathutils
50
from ganeti.masterd import iallocator
51
from ganeti.hypervisor import hv_xen
52

    
53
import testutils
54
import mocks
55

    
56

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

    
61
    self.tmpdir = tempfile.mkdtemp()
62

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

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

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

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

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

    
79

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

    
92

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

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

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

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

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

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

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

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

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

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

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

    
156

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

    
164

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

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

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

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

    
181

    
182
class TestLUGroupAssignNodes(unittest.TestCase):
183

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

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

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

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

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

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

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

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

    
232

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

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

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

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

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

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

    
291

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

    
300
    if args:
301
      msg = msg % args
302

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

    
306
  _VerifyFiles = cluster.LUClusterVerifyGroup._VerifyFiles
307

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

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

    
401

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

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

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

    
417

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

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

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

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

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

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

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

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

    
471

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

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

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

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

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

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

    
546

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

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

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

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

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

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

    
595

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

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

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

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

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

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

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

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

    
657

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

    
662

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

    
669

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

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

    
677

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

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

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

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

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

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

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

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

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

    
801

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

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

    
823
    return []
824

    
825

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

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

    
833

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

    
856

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

    
874

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

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

    
884

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

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

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

    
905

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

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

    
913

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

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

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

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

    
949

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1116

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

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

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

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

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

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

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

    
1143

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

    
1148

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

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

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

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

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

    
1168
    assert disk_template not in constants.DISK_TEMPLATES
1169

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

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

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

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

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

    
1195
    # Check if non-empty list of secondaries is rejected
1196
    self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1197
                      template, "inst25088.example.com",
1198
                      "node185.example.com", ["node323.example.com"], [],
1199
                      NotImplemented, NotImplemented, 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
    result = gdt(self.lu, template, "inst21662.example.com",
1205
                 "node21741.example.com", [],
1206
                 disk_info, file_storage_dir, file_driver, base_index,
1207
                 self.lu.LogInfo, self.GetDiskParams(),
1208
                 _req_file_storage=req_file_storage,
1209
                 _req_shr_file_storage=req_shr_file_storage)
1210

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

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

    
1222
    return result
1223

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1395

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

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

    
1404

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

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

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

    
1419

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

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

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

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

    
1432

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

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

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

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

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

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

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

    
1456
    assert cur_progress == offset
1457

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

    
1461
    return (True, None)
1462

    
1463

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1560
    instance._WipeDisks(lu, inst)
1561

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

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

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

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

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

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

    
1602

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

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

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

    
1633

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

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

    
1645

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

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

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

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

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

    
1678

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

    
1686

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

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

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

    
1705

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1816

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

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

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

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

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

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

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

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

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

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

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

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

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