Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.cmdlib_unittest.py @ 763ad5be

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

    
56
import testutils
57
import mocks
58

    
59

    
60
class TestCertVerification(testutils.GanetiTestCase):
61
  def setUp(self):
62
    testutils.GanetiTestCase.setUp(self)
63

    
64
    self.tmpdir = tempfile.mkdtemp()
65

    
66
  def tearDown(self):
67
    shutil.rmtree(self.tmpdir)
68

    
69
  def testVerifyCertificate(self):
70
    cluster._VerifyCertificate(testutils.TestDataFilename("cert1.pem"))
71

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

    
74
    (errcode, msg) = cluster._VerifyCertificate(nonexist_filename)
75
    self.assertEqual(errcode, cluster.LUClusterVerifyConfig.ETYPE_ERROR)
76

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

    
82

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

    
95

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

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

    
109
    default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
110
    other_iallocator = default_iallocator + "_not"
111

    
112
    op = OpTest()
113
    lu = TestLU(op)
114

    
115
    c_i = lambda: common._CheckIAllocatorOrNode(lu, "iallocator", "node")
116

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

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

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

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

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

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

    
159

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

    
167

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

    
173
    assert constants.QR_NODE in constants.QR_VIA_OP
174
    assert constants.QR_INSTANCE in constants.QR_VIA_OP
175

    
176
    for i in constants.QR_VIA_OP:
177
      self.assert_(query._GetQueryImplementation(i))
178

    
179
    self.assertRaises(errors.OpPrereqError, query._GetQueryImplementation,
180
                      "")
181
    self.assertRaises(errors.OpPrereqError, query._GetQueryImplementation,
182
                      "xyz")
183

    
184

    
185
class TestLUGroupAssignNodes(unittest.TestCase):
186

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

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

    
204
      return objects.Instance(name=name, primary_node=pnode, disks=disks,
205
                              disk_template=disk_template)
206

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

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

    
222
    self.assertEqual([], new)
223
    self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
224

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

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

    
235

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

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

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

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

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

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

    
294

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

    
303
    if args:
304
      msg = msg % args
305

    
306
    if cond:
307
      errors.append((item, msg))
308

    
309
  _VerifyFiles = cluster.LUClusterVerifyGroup._VerifyFiles
310

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

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

    
404

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

    
414
  def LogWarning(self, text, *args):
415
    self.warning_log.append((text, args))
416

    
417
  def LogInfo(self, text, *args):
418
    self.info_log.append((text, args))
419

    
420

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

    
433
          alloc_result = (moved, [], jobs)
434
          assert iallocator._NEVAC_RESULT(alloc_result)
435

    
436
          lu = _FakeLU()
437
          result = common._LoadNodeEvacResult(lu, alloc_result,
438
                                              early_release, use_nodes)
439

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

    
450
          self.assertFalse(lu.info_log)
451
          self.assertFalse(lu.warning_log)
452

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

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

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

    
474

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

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

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

    
528
    verified = common._UpdateAndVerifySubDict(old_test, test, self.type_check)
529
    self.assertEqual(verified, mv)
530

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

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

    
549

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

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

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

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

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

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

    
598

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

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

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

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

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

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

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

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

    
660

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

    
665

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

    
672

    
673
class _SpecWrapper:
674
  def __init__(self, spec):
675
    self.spec = spec
676

    
677
  def ComputeMinMaxSpec(self, *args):
678
    return self.spec.pop(0)
679

    
680

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

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

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

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

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

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

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

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

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

    
804

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

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

    
826
    return []
827

    
828

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

    
833
  def GetClusterInfo(self):
834
    return self.cluster
835

    
836

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

    
859

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

    
877

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

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

    
887

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

    
892
  def testSameGroup(self):
893
    ret = instance_utils._ComputeIPolicyNodeViolation(
894
      NotImplemented,
895
      NotImplemented,
896
      "foo", "foo", NotImplemented,
897
      _compute_fn=self.recorder)
898
    self.assertFalse(self.recorder.called)
899
    self.assertEqual(ret, [])
900

    
901
  def testDifferentGroup(self):
902
    ret = instance_utils._ComputeIPolicyNodeViolation(
903
      NotImplemented,
904
      NotImplemented,
905
      "foo", "bar", NotImplemented,
906
      _compute_fn=self.recorder)
907
    self.assertTrue(self.recorder.called)
908
    self.assertEqual(ret, [])
909

    
910

    
911
class _FakeConfigForTargetNodeIPolicy:
912
  def __init__(self, node_info=NotImplemented):
913
    self._node_info = node_info
914

    
915
  def GetNodeInfo(self, _):
916
    return self._node_info
917

    
918

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

    
927
  def testNoViolation(self):
928
    compute_recoder = _CallRecorder(return_value=[])
929
    instance._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
930
                                     self.target_node, NotImplemented,
931
                                     _compute_fn=compute_recoder)
932
    self.assertTrue(compute_recoder.called)
933
    self.assertEqual(self.lu.warning_log, [])
934

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

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

    
954

    
955
class TestApplyContainerMods(unittest.TestCase):
956
  def testEmptyContainer(self):
957
    container = []
958
    chgdesc = []
959
    instance.ApplyContainerMods("test", container, chgdesc, [], None, None,
960
                                None)
961
    self.assertEqual(container, [])
962
    self.assertEqual(chgdesc, [])
963

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

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

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

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

    
1006
    mods = instance.PrepareContainerMods([
1007
      (constants.DDM_REMOVE, 0, object()),
1008
      ], None)
1009
    self.assertRaises(AssertionError, instance.ApplyContainerMods,
1010
                      "test", [""], None, mods, None, None, None)
1011

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

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

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

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

    
1055
  class _PrivateData:
1056
    def __init__(self):
1057
      self.data = None
1058

    
1059
  @staticmethod
1060
  def _CreateTestFn(idx, params, private):
1061
    private.data = ("add", idx, params)
1062
    return ((100 * idx, params), [
1063
      ("test/%s" % idx, hex(idx)),
1064
      ])
1065

    
1066
  @staticmethod
1067
  def _ModifyTestFn(idx, item, params, private):
1068
    private.data = ("modify", idx, params)
1069
    return [
1070
      ("test/%s" % idx, "modify %s" % params),
1071
      ]
1072

    
1073
  @staticmethod
1074
  def _RemoveTestFn(idx, item, private):
1075
    private.data = ("remove", idx, item)
1076

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

    
1121

    
1122
class _FakeConfigForGenDiskTemplate:
1123
  def __init__(self):
1124
    self._unique_id = itertools.count()
1125
    self._drbd_minor = itertools.count(20)
1126
    self._port = itertools.count(constants.FIRST_DRBD_PORT)
1127
    self._secret = itertools.count()
1128

    
1129
  def GetVGName(self):
1130
    return "testvg"
1131

    
1132
  def GenerateUniqueID(self, ec_id):
1133
    return "ec%s-uq%s" % (ec_id, self._unique_id.next())
1134

    
1135
  def AllocateDRBDMinor(self, nodes, instance):
1136
    return [self._drbd_minor.next()
1137
            for _ in nodes]
1138

    
1139
  def AllocatePort(self):
1140
    return self._port.next()
1141

    
1142
  def GenerateDRBDSecret(self, ec_id):
1143
    return "ec%s-secret%s" % (ec_id, self._secret.next())
1144

    
1145
  def GetInstanceInfo(self, _):
1146
    return "foobar"
1147

    
1148

    
1149
class _FakeProcForGenDiskTemplate:
1150
  def GetECId(self):
1151
    return 0
1152

    
1153

    
1154
class TestGenerateDiskTemplate(unittest.TestCase):
1155
  def setUp(self):
1156
    nodegroup = objects.NodeGroup(name="ng")
1157
    nodegroup.UpgradeConfig()
1158

    
1159
    cfg = _FakeConfigForGenDiskTemplate()
1160
    proc = _FakeProcForGenDiskTemplate()
1161

    
1162
    self.lu = _FakeLU(cfg=cfg, proc=proc)
1163
    self.nodegroup = nodegroup
1164

    
1165
  @staticmethod
1166
  def GetDiskParams():
1167
    return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1168

    
1169
  def testWrongDiskTemplate(self):
1170
    gdt = instance._GenerateDiskTemplate
1171
    disk_template = "##unknown##"
1172

    
1173
    assert disk_template not in constants.DISK_TEMPLATES
1174

    
1175
    self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
1176
                      "inst26831.example.com", "node30113.example.com", [], [],
1177
                      NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1178
                      self.GetDiskParams())
1179

    
1180
  def testDiskless(self):
1181
    gdt = instance._GenerateDiskTemplate
1182

    
1183
    result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1184
                 "node30113.example.com", [], [],
1185
                 NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1186
                 self.GetDiskParams())
1187
    self.assertEqual(result, [])
1188

    
1189
  def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1190
                       file_storage_dir=NotImplemented,
1191
                       file_driver=NotImplemented,
1192
                       req_file_storage=NotImplemented,
1193
                       req_shr_file_storage=NotImplemented):
1194
    gdt = instance._GenerateDiskTemplate
1195

    
1196
    map(lambda params: utils.ForceDictType(params,
1197
                                           constants.IDISK_PARAMS_TYPES),
1198
        disk_info)
1199

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

    
1209
    result = gdt(self.lu, template, "inst21662.example.com",
1210
                 "node21741.example.com", [],
1211
                 disk_info, file_storage_dir, file_driver, base_index,
1212
                 self.lu.LogInfo, self.GetDiskParams(),
1213
                 _req_file_storage=req_file_storage,
1214
                 _req_shr_file_storage=req_shr_file_storage)
1215

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

    
1223
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1224
    instance._UpdateIvNames(base_index, result)
1225
    self._CheckIvNames(result, base_index, base_index + len(disk_info))
1226

    
1227
    return result
1228

    
1229
  def _CheckIvNames(self, disks, base_index, end_index):
1230
    self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1231
                     ["disk/%s" % i for i in range(base_index, end_index)])
1232

    
1233
  def testPlain(self):
1234
    disk_info = [{
1235
      constants.IDISK_SIZE: 1024,
1236
      constants.IDISK_MODE: constants.DISK_RDWR,
1237
      }, {
1238
      constants.IDISK_SIZE: 4096,
1239
      constants.IDISK_VG: "othervg",
1240
      constants.IDISK_MODE: constants.DISK_RDWR,
1241
      }]
1242

    
1243
    result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1244
                                   constants.LD_LV)
1245

    
1246
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1247
      ("testvg", "ec0-uq0.disk3"),
1248
      ("othervg", "ec0-uq1.disk4"),
1249
      ])
1250

    
1251
  @staticmethod
1252
  def _AllowFileStorage():
1253
    pass
1254

    
1255
  @staticmethod
1256
  def _ForbidFileStorage():
1257
    raise errors.OpPrereqError("Disallowed in test")
1258

    
1259
  def testFile(self):
1260
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1261
                      constants.DT_FILE, [], 0, NotImplemented,
1262
                      req_file_storage=self._ForbidFileStorage)
1263
    self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1264
                      constants.DT_SHARED_FILE, [], 0, NotImplemented,
1265
                      req_shr_file_storage=self._ForbidFileStorage)
1266

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

    
1279
      result = self._TestTrivialDisk(disk_template, disk_info, 2,
1280
        constants.LD_FILE, file_storage_dir="/tmp",
1281
        file_driver=constants.FD_BLKTAP,
1282
        req_file_storage=self._AllowFileStorage,
1283
        req_shr_file_storage=self._AllowFileStorage)
1284

    
1285
      self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1286
        (constants.FD_BLKTAP, "/tmp/disk2"),
1287
        (constants.FD_BLKTAP, "/tmp/disk3"),
1288
        (constants.FD_BLKTAP, "/tmp/disk4"),
1289
        ])
1290

    
1291
  def testBlock(self):
1292
    disk_info = [{
1293
      constants.IDISK_SIZE: 8 * 1024,
1294
      constants.IDISK_MODE: constants.DISK_RDWR,
1295
      constants.IDISK_ADOPT: "/tmp/some/block/dev",
1296
      }]
1297

    
1298
    result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1299
                                   constants.LD_BLOCKDEV)
1300

    
1301
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1302
      (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1303
      ])
1304

    
1305
  def testRbd(self):
1306
    disk_info = [{
1307
      constants.IDISK_SIZE: 8 * 1024,
1308
      constants.IDISK_MODE: constants.DISK_RDONLY,
1309
      }, {
1310
      constants.IDISK_SIZE: 100 * 1024,
1311
      constants.IDISK_MODE: constants.DISK_RDWR,
1312
      }]
1313

    
1314
    result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1315
                                   constants.LD_RBD)
1316

    
1317
    self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1318
      ("rbd", "ec0-uq0.rbd.disk0"),
1319
      ("rbd", "ec0-uq1.rbd.disk1"),
1320
      ])
1321

    
1322
  def testDrbd8(self):
1323
    gdt = instance._GenerateDiskTemplate
1324
    drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1325
    drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1326

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

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

    
1352
    assert len(exp_logical_ids) == len(disk_info)
1353

    
1354
    map(lambda params: utils.ForceDictType(params,
1355
                                           constants.IDISK_PARAMS_TYPES),
1356
        disk_info)
1357

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

    
1364
    result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1365
                 "node1334.example.com", ["node12272.example.com"],
1366
                 disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1367
                 self.GetDiskParams())
1368

    
1369
    for (idx, disk) in enumerate(result):
1370
      self.assertTrue(isinstance(disk, objects.Disk))
1371
      self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1372
      self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1373
      self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1374

    
1375
      for child in disk.children:
1376
        self.assertTrue(isinstance(disk, objects.Disk))
1377
        self.assertEqual(child.dev_type, constants.LD_LV)
1378
        self.assertTrue(child.children is None)
1379

    
1380
      self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1381
                       exp_logical_ids[idx])
1382

    
1383
      self.assertEqual(len(disk.children), 2)
1384
      self.assertEqual(disk.children[0].size, disk.size)
1385
      self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1386

    
1387
    self._CheckIvNames(result, 0, len(disk_info))
1388
    instance._UpdateIvNames(0, result)
1389
    self._CheckIvNames(result, 0, len(disk_info))
1390

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

    
1400

    
1401
class _ConfigForDiskWipe:
1402
  def __init__(self, exp_node):
1403
    self._exp_node = exp_node
1404

    
1405
  def SetDiskID(self, device, node):
1406
    assert isinstance(device, objects.Disk)
1407
    assert node == self._exp_node
1408

    
1409

    
1410
class _RpcForDiskWipe:
1411
  def __init__(self, exp_node, pause_cb, wipe_cb):
1412
    self._exp_node = exp_node
1413
    self._pause_cb = pause_cb
1414
    self._wipe_cb = wipe_cb
1415

    
1416
  def call_blockdev_pause_resume_sync(self, node, disks, pause):
1417
    assert node == self._exp_node
1418
    return rpc.RpcResult(data=self._pause_cb(disks, pause))
1419

    
1420
  def call_blockdev_wipe(self, node, bdev, offset, size):
1421
    assert node == self._exp_node
1422
    return rpc.RpcResult(data=self._wipe_cb(bdev, offset, size))
1423

    
1424

    
1425
class _DiskPauseTracker:
1426
  def __init__(self):
1427
    self.history = []
1428

    
1429
  def __call__(self, (disks, instance), pause):
1430
    assert not (set(disks) - set(instance.disks))
1431

    
1432
    self.history.extend((i.logical_id, i.size, pause)
1433
                        for i in disks)
1434

    
1435
    return (True, [True] * len(disks))
1436

    
1437

    
1438
class _DiskWipeProgressTracker:
1439
  def __init__(self, start_offset):
1440
    self._start_offset = start_offset
1441
    self.progress = {}
1442

    
1443
  def __call__(self, (disk, _), offset, size):
1444
    assert isinstance(offset, (long, int))
1445
    assert isinstance(size, (long, int))
1446

    
1447
    max_chunk_size = (disk.size / 100.0 * constants.MIN_WIPE_CHUNK_PERCENT)
1448

    
1449
    assert offset >= self._start_offset
1450
    assert (offset + size) <= disk.size
1451

    
1452
    assert size > 0
1453
    assert size <= constants.MAX_WIPE_CHUNK
1454
    assert size <= max_chunk_size
1455

    
1456
    assert offset == self._start_offset or disk.logical_id in self.progress
1457

    
1458
    # Keep track of progress
1459
    cur_progress = self.progress.setdefault(disk.logical_id, self._start_offset)
1460

    
1461
    assert cur_progress == offset
1462

    
1463
    # Record progress
1464
    self.progress[disk.logical_id] += size
1465

    
1466
    return (True, None)
1467

    
1468

    
1469
class TestWipeDisks(unittest.TestCase):
1470
  def _FailingPauseCb(self, (disks, _), pause):
1471
    self.assertEqual(len(disks), 3)
1472
    self.assertTrue(pause)
1473
    # Simulate an RPC error
1474
    return (False, "error")
1475

    
1476
  def testPauseFailure(self):
1477
    node_name = "node1372.example.com"
1478

    
1479
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, self._FailingPauseCb,
1480
                                     NotImplemented),
1481
                 cfg=_ConfigForDiskWipe(node_name))
1482

    
1483
    disks = [
1484
      objects.Disk(dev_type=constants.LD_LV),
1485
      objects.Disk(dev_type=constants.LD_LV),
1486
      objects.Disk(dev_type=constants.LD_LV),
1487
      ]
1488

    
1489
    inst = objects.Instance(name="inst21201",
1490
                            primary_node=node_name,
1491
                            disk_template=constants.DT_PLAIN,
1492
                            disks=disks)
1493

    
1494
    self.assertRaises(errors.OpExecError, instance._WipeDisks, lu, inst)
1495

    
1496
  def _FailingWipeCb(self, (disk, _), offset, size):
1497
    # This should only ever be called for the first disk
1498
    self.assertEqual(disk.logical_id, "disk0")
1499
    return (False, None)
1500

    
1501
  def testFailingWipe(self):
1502
    node_name = "node13445.example.com"
1503
    pt = _DiskPauseTracker()
1504

    
1505
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pt, self._FailingWipeCb),
1506
                 cfg=_ConfigForDiskWipe(node_name))
1507

    
1508
    disks = [
1509
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1510
                   size=100 * 1024),
1511
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1512
                   size=500 * 1024),
1513
      objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=256),
1514
      ]
1515

    
1516
    inst = objects.Instance(name="inst562",
1517
                            primary_node=node_name,
1518
                            disk_template=constants.DT_PLAIN,
1519
                            disks=disks)
1520

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

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

    
1538
  def _PrepareWipeTest(self, start_offset, disks):
1539
    node_name = "node-with-offset%s.example.com" % start_offset
1540
    pauset = _DiskPauseTracker()
1541
    progresst = _DiskWipeProgressTracker(start_offset)
1542

    
1543
    lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pauset, progresst),
1544
                 cfg=_ConfigForDiskWipe(node_name))
1545

    
1546
    instance = objects.Instance(name="inst3560",
1547
                                primary_node=node_name,
1548
                                disk_template=constants.DT_PLAIN,
1549
                                disks=disks)
1550

    
1551
    return (lu, instance, pauset, progresst)
1552

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

    
1563
    (lu, inst, pauset, progresst) = self._PrepareWipeTest(0, disks)
1564

    
1565
    instance._WipeDisks(lu, inst)
1566

    
1567
    self.assertEqual(pauset.history, [
1568
      ("disk0", 1024, True),
1569
      ("disk1", 500 * 1024, True),
1570
      ("disk2", 128, True),
1571
      ("disk3", constants.MAX_WIPE_CHUNK, True),
1572
      ("disk0", 1024, False),
1573
      ("disk1", 500 * 1024, False),
1574
      ("disk2", 128, False),
1575
      ("disk3", constants.MAX_WIPE_CHUNK, False),
1576
      ])
1577

    
1578
    # Ensure the complete disk has been wiped
1579
    self.assertEqual(progresst.progress,
1580
                     dict((i.logical_id, i.size) for i in disks))
1581

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

    
1591
      (lu, inst, pauset, progresst) = \
1592
        self._PrepareWipeTest(start_offset, disks)
1593

    
1594
      # Test start offset with only one disk
1595
      instance._WipeDisks(lu, inst,
1596
                          disks=[(1, disks[1], start_offset)])
1597

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

    
1607

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

    
1619
  def testEven(self):
1620
    for i in [1, 2, 7, 512, 1000, 1023]:
1621
      lu = _FakeLU()
1622
      result = instance_storage._DiskSizeInBytesToMebibytes(lu,
1623
                                                            i * 1024 * 1024)
1624
      self.assertEqual(result, i)
1625
      self.assertFalse(lu.warning_log)
1626

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

    
1639

    
1640
class TestCopyLockList(unittest.TestCase):
1641
  def test(self):
1642
    self.assertEqual(instance._CopyLockList([]), [])
1643
    self.assertEqual(instance._CopyLockList(None), None)
1644
    self.assertEqual(instance._CopyLockList(locking.ALL_SET), locking.ALL_SET)
1645

    
1646
    names = ["foo", "bar"]
1647
    output = instance._CopyLockList(names)
1648
    self.assertEqual(names, output)
1649
    self.assertNotEqual(id(names), id(output), msg="List was not copied")
1650

    
1651

    
1652
class TestCheckOpportunisticLocking(unittest.TestCase):
1653
  class OpTest(opcodes.OpCode):
1654
    OP_PARAMS = [
1655
      opcodes._POpportunisticLocking,
1656
      opcodes._PIAllocFromDesc(""),
1657
      ]
1658

    
1659
  @classmethod
1660
  def _MakeOp(cls, **kwargs):
1661
    op = cls.OpTest(**kwargs)
1662
    op.Validate(True)
1663
    return op
1664

    
1665
  def testMissingAttributes(self):
1666
    self.assertRaises(AttributeError, instance._CheckOpportunisticLocking,
1667
                      object())
1668

    
1669
  def testDefaults(self):
1670
    op = self._MakeOp()
1671
    instance._CheckOpportunisticLocking(op)
1672

    
1673
  def test(self):
1674
    for iallocator in [None, "something", "other"]:
1675
      for opplock in [False, True]:
1676
        op = self._MakeOp(iallocator=iallocator,
1677
                          opportunistic_locking=opplock)
1678
        if opplock and not iallocator:
1679
          self.assertRaises(errors.OpPrereqError,
1680
                            instance._CheckOpportunisticLocking, op)
1681
        else:
1682
          instance._CheckOpportunisticLocking(op)
1683

    
1684

    
1685
class _OpTestVerifyErrors(opcodes.OpCode):
1686
  OP_PARAMS = [
1687
    opcodes._PDebugSimulateErrors,
1688
    opcodes._PErrorCodes,
1689
    opcodes._PIgnoreErrors,
1690
    ]
1691

    
1692

    
1693
class _LuTestVerifyErrors(cluster._VerifyErrors):
1694
  def __init__(self, **kwargs):
1695
    cluster._VerifyErrors.__init__(self)
1696
    self.op = _OpTestVerifyErrors(**kwargs)
1697
    self.op.Validate(True)
1698
    self.msglist = []
1699
    self._feedback_fn = self.msglist.append
1700
    self.bad = False
1701

    
1702
  def DispatchCallError(self, which, *args, **kwargs):
1703
    if which:
1704
      self._Error(*args, **kwargs)
1705
    else:
1706
      self._ErrorIf(True, *args, **kwargs)
1707

    
1708
  def CallErrorIf(self, c, *args, **kwargs):
1709
    self._ErrorIf(c, *args, **kwargs)
1710

    
1711

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

    
1730
  def testNoError(self):
1731
    lu = _LuTestVerifyErrors()
1732
    lu.CallErrorIf(False, self._ERR1CODE, *self._ERR1ARGS)
1733
    self.assertFalse(lu.bad)
1734
    self.assertFalse(lu.msglist)
1735

    
1736
  def _InitTest(self, **kwargs):
1737
    self.lu1 = _LuTestVerifyErrors(**kwargs)
1738
    self.lu2 = _LuTestVerifyErrors(**kwargs)
1739

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

    
1749
  def _checkMsgCommon(self, logstr, errmsg, itype, item, warning):
1750
    self.assertTrue(errmsg in logstr)
1751
    if warning:
1752
      self.assertTrue("WARNING" in logstr)
1753
    else:
1754
      self.assertTrue("ERROR" in logstr)
1755
    self.assertTrue(itype in logstr)
1756
    self.assertTrue(item in logstr)
1757

    
1758
  def _checkMsg1(self, logstr, warning=False):
1759
    self._checkMsgCommon(logstr, self._ERR1MSG, self._NODESTR,
1760
                         self._NODENAME, warning)
1761

    
1762
  def _checkMsg2(self, logstr, warning=False):
1763
    self._checkMsgCommon(logstr, self._ERR2MSG, self._INSTSTR,
1764
                         self._INSTNAME, warning)
1765

    
1766
  def testPlain(self):
1767
    self._InitTest()
1768
    lu = self._CallError(*self._ERR1ARGS)
1769
    self.assertTrue(lu.bad)
1770
    self.assertEqual(len(lu.msglist), 1)
1771
    self._checkMsg1(lu.msglist[0])
1772

    
1773
  def testMultiple(self):
1774
    self._InitTest()
1775
    self._CallError(*self._ERR1ARGS)
1776
    lu = self._CallError(*self._ERR2ARGS)
1777
    self.assertTrue(lu.bad)
1778
    self.assertEqual(len(lu.msglist), 2)
1779
    self._checkMsg1(lu.msglist[0])
1780
    self._checkMsg2(lu.msglist[1])
1781

    
1782
  def testIgnore(self):
1783
    self._InitTest(ignore_errors=[self._ERR1ID])
1784
    lu = self._CallError(*self._ERR1ARGS)
1785
    self.assertFalse(lu.bad)
1786
    self.assertEqual(len(lu.msglist), 1)
1787
    self._checkMsg1(lu.msglist[0], warning=True)
1788

    
1789
  def testWarning(self):
1790
    self._InitTest()
1791
    lu = self._CallError(*self._ERR1ARGS,
1792
                         code=_LuTestVerifyErrors.ETYPE_WARNING)
1793
    self.assertFalse(lu.bad)
1794
    self.assertEqual(len(lu.msglist), 1)
1795
    self._checkMsg1(lu.msglist[0], warning=True)
1796

    
1797
  def testWarning2(self):
1798
    self._InitTest()
1799
    self._CallError(*self._ERR1ARGS)
1800
    lu = self._CallError(*self._ERR2ARGS,
1801
                         code=_LuTestVerifyErrors.ETYPE_WARNING)
1802
    self.assertTrue(lu.bad)
1803
    self.assertEqual(len(lu.msglist), 2)
1804
    self._checkMsg1(lu.msglist[0])
1805
    self._checkMsg2(lu.msglist[1], warning=True)
1806

    
1807
  def testDebugSimulate(self):
1808
    lu = _LuTestVerifyErrors(debug_simulate_errors=True)
1809
    lu.CallErrorIf(False, *self._ERR1ARGS)
1810
    self.assertTrue(lu.bad)
1811
    self.assertEqual(len(lu.msglist), 1)
1812
    self._checkMsg1(lu.msglist[0])
1813

    
1814
  def testErrCodes(self):
1815
    self._InitTest(error_codes=True)
1816
    lu = self._CallError(*self._ERR1ARGS)
1817
    self.assertTrue(lu.bad)
1818
    self.assertEqual(len(lu.msglist), 1)
1819
    self._checkMsg1(lu.msglist[0])
1820
    self.assertTrue(self._ERR1ID in lu.msglist[0])
1821

    
1822

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

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

    
1903
    self.assertTrue(constants.ISPECS_MINMAX in new_policy)
1904
    self.assertEqual(new_policy[constants.ISPECS_MINMAX], diff_minmax)
1905
    for key in old_policy:
1906
      if not key in diff_policy:
1907
        self.assertTrue(key in new_policy)
1908
        self.assertEqual(new_policy[key], old_policy[key])
1909

    
1910
    if not isgroup:
1911
      new_std = new_policy[constants.ISPECS_STD]
1912
      for key in diff_std:
1913
        self.assertTrue(key in new_std)
1914
        self.assertEqual(new_std[key], diff_std[key])
1915
      old_std = old_policy.get(constants.ISPECS_STD, {})
1916
      for key in old_std:
1917
        self.assertTrue(key in new_std)
1918
        if key not in diff_std:
1919
          self.assertEqual(new_std[key], old_std[key])
1920

    
1921
  def _TestSet(self, old_policy, diff_policy, isgroup):
1922
    new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1923
                                           group_policy=isgroup)
1924
    for key in diff_policy:
1925
      self.assertTrue(key in new_policy)
1926
      self.assertEqual(new_policy[key], diff_policy[key])
1927
    for key in old_policy:
1928
      if not key in diff_policy:
1929
        self.assertTrue(key in new_policy)
1930
        self.assertEqual(new_policy[key], old_policy[key])
1931

    
1932
  def testSet(self):
1933
    diff_policy = {
1934
      constants.IPOLICY_VCPU_RATIO: 3,
1935
      constants.IPOLICY_DTS: [constants.DT_FILE],
1936
      }
1937
    self._TestSet(self._OLD_GROUP_POLICY, diff_policy, True)
1938
    self._TestSetSpecs(self._OLD_GROUP_POLICY, True)
1939
    self._TestSet({}, diff_policy, True)
1940
    self._TestSetSpecs({}, True)
1941
    self._TestSet(self._OLD_CLUSTER_POLICY, diff_policy, False)
1942
    self._TestSetSpecs(self._OLD_CLUSTER_POLICY, False)
1943

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

    
1958
    self.assertRaises(errors.OpPrereqError, common._GetUpdatedIPolicy,
1959
                      old_policy, diff_policy, group_policy=False)
1960

    
1961
  def testUnsetEmpty(self):
1962
    old_policy = {}
1963
    for key in constants.IPOLICY_ALL_KEYS:
1964
      diff_policy = {
1965
        key: constants.VALUE_DEFAULT,
1966
        }
1967
    new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1968
                                           group_policy=True)
1969
    self.assertEqual(new_policy, old_policy)
1970

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

    
2014
  def testInvalidKeys(self):
2015
    self._TestInvalidKeys(self._OLD_GROUP_POLICY, True)
2016
    self._TestInvalidKeys(self._OLD_CLUSTER_POLICY, False)
2017

    
2018
  def testInvalidValues(self):
2019
    for par in (constants.IPOLICY_PARAMETERS |
2020
                frozenset([constants.IPOLICY_DTS])):
2021
      bad_policy = {
2022
        par: "invalid_value",
2023
        }
2024
      self.assertRaises(errors.OpPrereqError, common._GetUpdatedIPolicy, {},
2025
                        bad_policy, group_policy=True)
2026

    
2027
if __name__ == "__main__":
2028
  testutils.GanetiTestProgram()