Statistics
| Branch: | Tag: | Revision:

root / test / py / cmdlib / cmdlib_unittest.py @ 9a8f9634

History | View | Annotate | Download (34.5 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 unittest
26
import itertools
27
import copy
28

    
29
from ganeti import constants
30
from ganeti import mcpu
31
from ganeti import cmdlib
32
from ganeti.cmdlib import cluster
33
from ganeti.cmdlib import instance_storage
34
from ganeti.cmdlib import instance_utils
35
from ganeti.cmdlib import common
36
from ganeti.cmdlib import query
37
from ganeti import opcodes
38
from ganeti import errors
39
from ganeti import luxi
40
from ganeti import ht
41
from ganeti import objects
42
from ganeti import locking
43
from ganeti.masterd import iallocator
44

    
45
import testutils
46
import mocks
47

    
48

    
49
class TestOpcodeParams(testutils.GanetiTestCase):
50
  def testParamsStructures(self):
51
    for op in sorted(mcpu.Processor.DISPATCH_TABLE):
52
      lu = mcpu.Processor.DISPATCH_TABLE[op]
53
      lu_name = lu.__name__
54
      self.failIf(hasattr(lu, "_OP_REQP"),
55
                  msg=("LU '%s' has old-style _OP_REQP" % lu_name))
56
      self.failIf(hasattr(lu, "_OP_DEFS"),
57
                  msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
58
      self.failIf(hasattr(lu, "_OP_PARAMS"),
59
                  msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
60

    
61

    
62
class TestIAllocatorChecks(testutils.GanetiTestCase):
63
  def testFunction(self):
64
    class TestLU(object):
65
      def __init__(self, opcode):
66
        self.cfg = mocks.FakeConfig()
67
        self.op = opcode
68

    
69
    class OpTest(opcodes.OpCode):
70
       OP_PARAMS = [
71
        ("iallocator", None, ht.TAny, None),
72
        ("node", None, ht.TAny, None),
73
        ]
74

    
75
    default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
76
    other_iallocator = default_iallocator + "_not"
77

    
78
    op = OpTest()
79
    lu = TestLU(op)
80

    
81
    c_i = lambda: common.CheckIAllocatorOrNode(lu, "iallocator", "node")
82

    
83
    # Neither node nor iallocator given
84
    for n in (None, []):
85
      op.iallocator = None
86
      op.node = n
87
      c_i()
88
      self.assertEqual(lu.op.iallocator, default_iallocator)
89
      self.assertEqual(lu.op.node, n)
90

    
91
    # Both, iallocator and node given
92
    for a in ("test", constants.DEFAULT_IALLOCATOR_SHORTCUT):
93
      op.iallocator = a
94
      op.node = "test"
95
      self.assertRaises(errors.OpPrereqError, c_i)
96

    
97
    # Only iallocator given
98
    for n in (None, []):
99
      op.iallocator = other_iallocator
100
      op.node = n
101
      c_i()
102
      self.assertEqual(lu.op.iallocator, other_iallocator)
103
      self.assertEqual(lu.op.node, n)
104

    
105
    # Only node given
106
    op.iallocator = None
107
    op.node = "node"
108
    c_i()
109
    self.assertEqual(lu.op.iallocator, None)
110
    self.assertEqual(lu.op.node, "node")
111

    
112
    # Asked for default iallocator, no node given
113
    op.iallocator = constants.DEFAULT_IALLOCATOR_SHORTCUT
114
    op.node = None
115
    c_i()
116
    self.assertEqual(lu.op.iallocator, default_iallocator)
117
    self.assertEqual(lu.op.node, None)
118

    
119
    # No node, iallocator or default iallocator
120
    op.iallocator = None
121
    op.node = None
122
    lu.cfg.GetDefaultIAllocator = lambda: None
123
    self.assertRaises(errors.OpPrereqError, c_i)
124

    
125

    
126
class TestLUTestJqueue(unittest.TestCase):
127
  def test(self):
128
    self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
129
                 (luxi.WFJC_TIMEOUT * 0.75),
130
                 msg=("Client timeout too high, might not notice bugs"
131
                      " in WaitForJobChange"))
132

    
133

    
134
class TestLUQuery(unittest.TestCase):
135
  def test(self):
136
    self.assertEqual(sorted(query._QUERY_IMPL.keys()),
137
                     sorted(constants.QR_VIA_OP))
138

    
139
    assert constants.QR_NODE in constants.QR_VIA_LUXI
140
    assert constants.QR_INSTANCE in constants.QR_VIA_LUXI
141

    
142
    for i in constants.QR_VIA_OP:
143
      self.assert_(query._GetQueryImplementation(i))
144

    
145
    self.assertRaises(errors.OpPrereqError, query._GetQueryImplementation,
146
                      "")
147
    self.assertRaises(errors.OpPrereqError, query._GetQueryImplementation,
148
                      "xyz")
149

    
150

    
151
class _FakeLU:
152
  def __init__(self, cfg=NotImplemented, proc=NotImplemented,
153
               rpc=NotImplemented):
154
    self.warning_log = []
155
    self.info_log = []
156
    self.cfg = cfg
157
    self.proc = proc
158
    self.rpc = rpc
159

    
160
  def LogWarning(self, text, *args):
161
    self.warning_log.append((text, args))
162

    
163
  def LogInfo(self, text, *args):
164
    self.info_log.append((text, args))
165

    
166

    
167
class TestLoadNodeEvacResult(unittest.TestCase):
168
  def testSuccess(self):
169
    for moved in [[], [
170
      ("inst20153.example.com", "grp2", ["nodeA4509", "nodeB2912"]),
171
      ]]:
172
      for early_release in [False, True]:
173
        for use_nodes in [False, True]:
174
          jobs = [
175
            [opcodes.OpInstanceReplaceDisks().__getstate__()],
176
            [opcodes.OpInstanceMigrate().__getstate__()],
177
            ]
178

    
179
          alloc_result = (moved, [], jobs)
180
          assert iallocator._NEVAC_RESULT(alloc_result)
181

    
182
          lu = _FakeLU()
183
          result = common.LoadNodeEvacResult(lu, alloc_result,
184
                                             early_release, use_nodes)
185

    
186
          if moved:
187
            (_, (info_args, )) = lu.info_log.pop(0)
188
            for (instname, instgroup, instnodes) in moved:
189
              self.assertTrue(instname in info_args)
190
              if use_nodes:
191
                for i in instnodes:
192
                  self.assertTrue(i in info_args)
193
              else:
194
                self.assertTrue(instgroup in info_args)
195

    
196
          self.assertFalse(lu.info_log)
197
          self.assertFalse(lu.warning_log)
198

    
199
          for op in itertools.chain(*result):
200
            if hasattr(op.__class__, "early_release"):
201
              self.assertEqual(op.early_release, early_release)
202
            else:
203
              self.assertFalse(hasattr(op, "early_release"))
204

    
205
  def testFailed(self):
206
    alloc_result = ([], [
207
      ("inst5191.example.com", "errormsg21178"),
208
      ], [])
209
    assert iallocator._NEVAC_RESULT(alloc_result)
210

    
211
    lu = _FakeLU()
212
    self.assertRaises(errors.OpExecError, common.LoadNodeEvacResult,
213
                      lu, alloc_result, False, False)
214
    self.assertFalse(lu.info_log)
215
    (_, (args, )) = lu.warning_log.pop(0)
216
    self.assertTrue("inst5191.example.com" in args)
217
    self.assertTrue("errormsg21178" in args)
218
    self.assertFalse(lu.warning_log)
219

    
220

    
221
class TestUpdateAndVerifySubDict(unittest.TestCase):
222
  def setUp(self):
223
    self.type_check = {
224
        "a": constants.VTYPE_INT,
225
        "b": constants.VTYPE_STRING,
226
        "c": constants.VTYPE_BOOL,
227
        "d": constants.VTYPE_STRING,
228
        }
229

    
230
  def test(self):
231
    old_test = {
232
      "foo": {
233
        "d": "blubb",
234
        "a": 321,
235
        },
236
      "baz": {
237
        "a": 678,
238
        "b": "678",
239
        "c": True,
240
        },
241
      }
242
    test = {
243
      "foo": {
244
        "a": 123,
245
        "b": "123",
246
        "c": True,
247
        },
248
      "bar": {
249
        "a": 321,
250
        "b": "321",
251
        "c": False,
252
        },
253
      }
254

    
255
    mv = {
256
      "foo": {
257
        "a": 123,
258
        "b": "123",
259
        "c": True,
260
        "d": "blubb"
261
        },
262
      "bar": {
263
        "a": 321,
264
        "b": "321",
265
        "c": False,
266
        },
267
      "baz": {
268
        "a": 678,
269
        "b": "678",
270
        "c": True,
271
        },
272
      }
273

    
274
    verified = common._UpdateAndVerifySubDict(old_test, test, self.type_check)
275
    self.assertEqual(verified, mv)
276

    
277
  def testWrong(self):
278
    test = {
279
      "foo": {
280
        "a": "blubb",
281
        "b": "123",
282
        "c": True,
283
        },
284
      "bar": {
285
        "a": 321,
286
        "b": "321",
287
        "c": False,
288
        },
289
      }
290

    
291
    self.assertRaises(errors.TypeEnforcementError,
292
                      common._UpdateAndVerifySubDict, {}, test,
293
                      self.type_check)
294

    
295

    
296
class TestHvStateHelper(unittest.TestCase):
297
  def testWithoutOpData(self):
298
    self.assertEqual(common.MergeAndVerifyHvState(None, NotImplemented),
299
                     None)
300

    
301
  def testWithoutOldData(self):
302
    new = {
303
      constants.HT_XEN_PVM: {
304
        constants.HVST_MEMORY_TOTAL: 4096,
305
        },
306
      }
307
    self.assertEqual(common.MergeAndVerifyHvState(new, None), new)
308

    
309
  def testWithWrongHv(self):
310
    new = {
311
      "i-dont-exist": {
312
        constants.HVST_MEMORY_TOTAL: 4096,
313
        },
314
      }
315
    self.assertRaises(errors.OpPrereqError, common.MergeAndVerifyHvState,
316
                      new, None)
317

    
318
class TestDiskStateHelper(unittest.TestCase):
319
  def testWithoutOpData(self):
320
    self.assertEqual(common.MergeAndVerifyDiskState(None, NotImplemented),
321
                     None)
322

    
323
  def testWithoutOldData(self):
324
    new = {
325
      constants.DT_PLAIN: {
326
        "xenvg": {
327
          constants.DS_DISK_RESERVED: 1024,
328
          },
329
        },
330
      }
331
    self.assertEqual(common.MergeAndVerifyDiskState(new, None), new)
332

    
333
  def testWithWrongStorageType(self):
334
    new = {
335
      "i-dont-exist": {
336
        "xenvg": {
337
          constants.DS_DISK_RESERVED: 1024,
338
          },
339
        },
340
      }
341
    self.assertRaises(errors.OpPrereqError, common.MergeAndVerifyDiskState,
342
                      new, None)
343

    
344

    
345
class TestComputeMinMaxSpec(unittest.TestCase):
346
  def setUp(self):
347
    self.ispecs = {
348
      constants.ISPECS_MAX: {
349
        constants.ISPEC_MEM_SIZE: 512,
350
        constants.ISPEC_DISK_SIZE: 1024,
351
        },
352
      constants.ISPECS_MIN: {
353
        constants.ISPEC_MEM_SIZE: 128,
354
        constants.ISPEC_DISK_COUNT: 1,
355
        },
356
      }
357

    
358
  def testNoneValue(self):
359
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
360
                                              self.ispecs, None) is None)
361

    
362
  def testAutoValue(self):
363
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
364
                                              self.ispecs,
365
                                              constants.VALUE_AUTO) is None)
366

    
367
  def testNotDefined(self):
368
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT, None,
369
                                              self.ispecs, 3) is None)
370

    
371
  def testNoMinDefined(self):
372
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE, None,
373
                                              self.ispecs, 128) is None)
374

    
375
  def testNoMaxDefined(self):
376
    self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT,
377
                                              None, self.ispecs, 16) is None)
378

    
379
  def testOutOfRange(self):
380
    for (name, val) in ((constants.ISPEC_MEM_SIZE, 64),
381
                        (constants.ISPEC_MEM_SIZE, 768),
382
                        (constants.ISPEC_DISK_SIZE, 4096),
383
                        (constants.ISPEC_DISK_COUNT, 0)):
384
      min_v = self.ispecs[constants.ISPECS_MIN].get(name, val)
385
      max_v = self.ispecs[constants.ISPECS_MAX].get(name, val)
386
      self.assertEqual(common._ComputeMinMaxSpec(name, None,
387
                                                 self.ispecs, val),
388
                       "%s value %s is not in range [%s, %s]" %
389
                       (name, val,min_v, max_v))
390
      self.assertEqual(common._ComputeMinMaxSpec(name, "1",
391
                                                 self.ispecs, val),
392
                       "%s/1 value %s is not in range [%s, %s]" %
393
                       (name, val,min_v, max_v))
394

    
395
  def test(self):
396
    for (name, val) in ((constants.ISPEC_MEM_SIZE, 256),
397
                        (constants.ISPEC_MEM_SIZE, 128),
398
                        (constants.ISPEC_MEM_SIZE, 512),
399
                        (constants.ISPEC_DISK_SIZE, 1024),
400
                        (constants.ISPEC_DISK_SIZE, 0),
401
                        (constants.ISPEC_DISK_COUNT, 1),
402
                        (constants.ISPEC_DISK_COUNT, 5)):
403
      self.assertTrue(common._ComputeMinMaxSpec(name, None, self.ispecs, val)
404
                      is None)
405

    
406

    
407
def _ValidateComputeMinMaxSpec(name, *_):
408
  assert name in constants.ISPECS_PARAMETERS
409
  return None
410

    
411

    
412
def _NoDiskComputeMinMaxSpec(name, *_):
413
  if name == constants.ISPEC_DISK_COUNT:
414
    return name
415
  else:
416
    return None
417

    
418

    
419
class _SpecWrapper:
420
  def __init__(self, spec):
421
    self.spec = spec
422

    
423
  def ComputeMinMaxSpec(self, *args):
424
    return self.spec.pop(0)
425

    
426

    
427
class TestComputeIPolicySpecViolation(unittest.TestCase):
428
  # Minimal policy accepted by _ComputeIPolicySpecViolation()
429
  _MICRO_IPOL = {
430
    constants.IPOLICY_DTS: [constants.DT_PLAIN, constants.DT_DISKLESS],
431
    constants.ISPECS_MINMAX: [NotImplemented],
432
    }
433

    
434
  def test(self):
435
    compute_fn = _ValidateComputeMinMaxSpec
436
    ret = common.ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
437
                                             [1024], 1, constants.DT_PLAIN,
438
                                             _compute_fn=compute_fn)
439
    self.assertEqual(ret, [])
440

    
441
  def testDiskFull(self):
442
    compute_fn = _NoDiskComputeMinMaxSpec
443
    ret = common.ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
444
                                             [1024], 1, constants.DT_PLAIN,
445
                                             _compute_fn=compute_fn)
446
    self.assertEqual(ret, [constants.ISPEC_DISK_COUNT])
447

    
448
  def testDiskLess(self):
449
    compute_fn = _NoDiskComputeMinMaxSpec
450
    ret = common.ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
451
                                             [1024], 1, constants.DT_DISKLESS,
452
                                             _compute_fn=compute_fn)
453
    self.assertEqual(ret, [])
454

    
455
  def testWrongTemplates(self):
456
    compute_fn = _ValidateComputeMinMaxSpec
457
    ret = common.ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
458
                                             [1024], 1, constants.DT_DRBD8,
459
                                             _compute_fn=compute_fn)
460
    self.assertEqual(len(ret), 1)
461
    self.assertTrue("Disk template" in ret[0])
462

    
463
  def testInvalidArguments(self):
464
    self.assertRaises(AssertionError, common.ComputeIPolicySpecViolation,
465
                      self._MICRO_IPOL, 1024, 1, 1, 1, [], 1,
466
                      constants.DT_PLAIN,)
467

    
468
  def testInvalidSpec(self):
469
    spec = _SpecWrapper([None, False, "foo", None, "bar", None])
470
    compute_fn = spec.ComputeMinMaxSpec
471
    ret = common.ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
472
                                             [1024], 1, constants.DT_PLAIN,
473
                                             _compute_fn=compute_fn)
474
    self.assertEqual(ret, ["foo", "bar"])
475
    self.assertFalse(spec.spec)
476

    
477
  def testWithIPolicy(self):
478
    mem_size = 2048
479
    cpu_count = 2
480
    disk_count = 1
481
    disk_sizes = [512]
482
    nic_count = 1
483
    spindle_use = 4
484
    disk_template = "mytemplate"
485
    ispec = {
486
      constants.ISPEC_MEM_SIZE: mem_size,
487
      constants.ISPEC_CPU_COUNT: cpu_count,
488
      constants.ISPEC_DISK_COUNT: disk_count,
489
      constants.ISPEC_DISK_SIZE: disk_sizes[0],
490
      constants.ISPEC_NIC_COUNT: nic_count,
491
      constants.ISPEC_SPINDLE_USE: spindle_use,
492
      }
493
    ipolicy1 = {
494
      constants.ISPECS_MINMAX: [{
495
        constants.ISPECS_MIN: ispec,
496
        constants.ISPECS_MAX: ispec,
497
        }],
498
      constants.IPOLICY_DTS: [disk_template],
499
      }
500
    ispec_copy = copy.deepcopy(ispec)
501
    ipolicy2 = {
502
      constants.ISPECS_MINMAX: [
503
        {
504
          constants.ISPECS_MIN: ispec_copy,
505
          constants.ISPECS_MAX: ispec_copy,
506
          },
507
        {
508
          constants.ISPECS_MIN: ispec,
509
          constants.ISPECS_MAX: ispec,
510
          },
511
        ],
512
      constants.IPOLICY_DTS: [disk_template],
513
      }
514
    ipolicy3 = {
515
      constants.ISPECS_MINMAX: [
516
        {
517
          constants.ISPECS_MIN: ispec,
518
          constants.ISPECS_MAX: ispec,
519
          },
520
        {
521
          constants.ISPECS_MIN: ispec_copy,
522
          constants.ISPECS_MAX: ispec_copy,
523
          },
524
        ],
525
      constants.IPOLICY_DTS: [disk_template],
526
      }
527
    def AssertComputeViolation(ipolicy, violations):
528
      ret = common.ComputeIPolicySpecViolation(ipolicy, mem_size, cpu_count,
529
                                               disk_count, nic_count,
530
                                               disk_sizes, spindle_use,
531
                                               disk_template)
532
      self.assertEqual(len(ret), violations)
533

    
534
    AssertComputeViolation(ipolicy1, 0)
535
    AssertComputeViolation(ipolicy2, 0)
536
    AssertComputeViolation(ipolicy3, 0)
537
    for par in constants.ISPECS_PARAMETERS:
538
      ispec[par] += 1
539
      AssertComputeViolation(ipolicy1, 1)
540
      AssertComputeViolation(ipolicy2, 0)
541
      AssertComputeViolation(ipolicy3, 0)
542
      ispec[par] -= 2
543
      AssertComputeViolation(ipolicy1, 1)
544
      AssertComputeViolation(ipolicy2, 0)
545
      AssertComputeViolation(ipolicy3, 0)
546
      ispec[par] += 1 # Restore
547
    ipolicy1[constants.IPOLICY_DTS] = ["another_template"]
548
    AssertComputeViolation(ipolicy1, 1)
549

    
550

    
551
class _StubComputeIPolicySpecViolation:
552
  def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
553
               spindle_use, disk_template):
554
    self.mem_size = mem_size
555
    self.cpu_count = cpu_count
556
    self.disk_count = disk_count
557
    self.nic_count = nic_count
558
    self.disk_sizes = disk_sizes
559
    self.spindle_use = spindle_use
560
    self.disk_template = disk_template
561

    
562
  def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
563
               spindle_use, disk_template):
564
    assert self.mem_size == mem_size
565
    assert self.cpu_count == cpu_count
566
    assert self.disk_count == disk_count
567
    assert self.nic_count == nic_count
568
    assert self.disk_sizes == disk_sizes
569
    assert self.spindle_use == spindle_use
570
    assert self.disk_template == disk_template
571

    
572
    return []
573

    
574

    
575
class _FakeConfigForComputeIPolicyInstanceViolation:
576
  def __init__(self, be, excl_stor):
577
    self.cluster = objects.Cluster(beparams={"default": be})
578
    self.excl_stor = excl_stor
579

    
580
  def GetClusterInfo(self):
581
    return self.cluster
582

    
583
  def GetNodeInfo(self, _):
584
    return {}
585

    
586
  def GetNdParams(self, _):
587
    return {
588
      constants.ND_EXCLUSIVE_STORAGE: self.excl_stor,
589
      }
590

    
591
  def GetInstanceNodes(self, instance_uuid):
592
    return ("pnode_uuid", )
593

    
594
  def GetInstanceDisks(self, _):
595
    disks = [objects.Disk(size=512, spindles=13, uuid="disk_uuid")]
596
    return disks
597

    
598

    
599
class TestComputeIPolicyInstanceViolation(unittest.TestCase):
600
  def test(self):
601
    beparams = {
602
      constants.BE_MAXMEM: 2048,
603
      constants.BE_VCPUS: 2,
604
      constants.BE_SPINDLE_USE: 4,
605
      }
606
    cfg = _FakeConfigForComputeIPolicyInstanceViolation(beparams, False)
607
    instance = objects.Instance(beparams=beparams, disks=["disk_uuid"],
608
                                nics=[], primary_node="pnode_uuid",
609
                                disk_template=constants.DT_PLAIN)
610
    stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 4,
611
                                            constants.DT_PLAIN)
612
    ret = common.ComputeIPolicyInstanceViolation(NotImplemented, instance,
613
                                                 cfg, _compute_fn=stub)
614
    self.assertEqual(ret, [])
615
    instance2 = objects.Instance(beparams={}, disks=["disk_uuid"],
616
                                 nics=[], primary_node="pnode_uuid",
617
                                 disk_template=constants.DT_PLAIN)
618
    ret = common.ComputeIPolicyInstanceViolation(NotImplemented, instance2,
619
                                                 cfg, _compute_fn=stub)
620
    self.assertEqual(ret, [])
621
    cfg_es = _FakeConfigForComputeIPolicyInstanceViolation(beparams, True)
622
    stub_es = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 13,
623
                                               constants.DT_PLAIN)
624
    ret = common.ComputeIPolicyInstanceViolation(NotImplemented, instance,
625
                                                 cfg_es, _compute_fn=stub_es)
626
    self.assertEqual(ret, [])
627
    ret = common.ComputeIPolicyInstanceViolation(NotImplemented, instance2,
628
                                                 cfg_es, _compute_fn=stub_es)
629
    self.assertEqual(ret, [])
630

    
631

    
632
class _CallRecorder:
633
  def __init__(self, return_value=None):
634
    self.called = False
635
    self.return_value = return_value
636

    
637
  def __call__(self, *args):
638
    self.called = True
639
    return self.return_value
640

    
641

    
642
class TestComputeIPolicyNodeViolation(unittest.TestCase):
643
  def setUp(self):
644
    self.recorder = _CallRecorder(return_value=[])
645

    
646
  def testSameGroup(self):
647
    ret = instance_utils._ComputeIPolicyNodeViolation(
648
      NotImplemented,
649
      NotImplemented,
650
      "foo", "foo", NotImplemented,
651
      _compute_fn=self.recorder)
652
    self.assertFalse(self.recorder.called)
653
    self.assertEqual(ret, [])
654

    
655
  def testDifferentGroup(self):
656
    ret = instance_utils._ComputeIPolicyNodeViolation(
657
      NotImplemented,
658
      NotImplemented,
659
      "foo", "bar", NotImplemented,
660
      _compute_fn=self.recorder)
661
    self.assertTrue(self.recorder.called)
662
    self.assertEqual(ret, [])
663

    
664

    
665
class TestDiskSizeInBytesToMebibytes(unittest.TestCase):
666
  def testLessThanOneMebibyte(self):
667
    for i in [1, 2, 7, 512, 1000, 1023]:
668
      lu = _FakeLU()
669
      result = instance_storage._DiskSizeInBytesToMebibytes(lu, i)
670
      self.assertEqual(result, 1)
671
      self.assertEqual(len(lu.warning_log), 1)
672
      self.assertEqual(len(lu.warning_log[0]), 2)
673
      (_, (warnsize, )) = lu.warning_log[0]
674
      self.assertEqual(warnsize, (1024 * 1024) - i)
675

    
676
  def testEven(self):
677
    for i in [1, 2, 7, 512, 1000, 1023]:
678
      lu = _FakeLU()
679
      result = instance_storage._DiskSizeInBytesToMebibytes(lu,
680
                                                            i * 1024 * 1024)
681
      self.assertEqual(result, i)
682
      self.assertFalse(lu.warning_log)
683

    
684
  def testLargeNumber(self):
685
    for i in [1, 2, 7, 512, 1000, 1023, 2724, 12420]:
686
      for j in [1, 2, 486, 326, 986, 1023]:
687
        lu = _FakeLU()
688
        size = (1024 * 1024 * i) + j
689
        result = instance_storage._DiskSizeInBytesToMebibytes(lu, size)
690
        self.assertEqual(result, i + 1, msg="Amount was not rounded up")
691
        self.assertEqual(len(lu.warning_log), 1)
692
        self.assertEqual(len(lu.warning_log[0]), 2)
693
        (_, (warnsize, )) = lu.warning_log[0]
694
        self.assertEqual(warnsize, (1024 * 1024) - j)
695

    
696

    
697
class _OpTestVerifyErrors(opcodes.OpCode):
698
  OP_PARAMS = [
699
    ("debug_simulate_errors", False, ht.TBool, ""),
700
    ("error_codes", False, ht.TBool, ""),
701
    ("ignore_errors",
702
     [],
703
     ht.TListOf(ht.TElemOf(constants.CV_ALL_ECODES_STRINGS)),
704
     "")
705
    ]
706

    
707

    
708
class _LuTestVerifyErrors(cluster._VerifyErrors):
709
  def __init__(self, **kwargs):
710
    cluster._VerifyErrors.__init__(self)
711
    self.op = _OpTestVerifyErrors(**kwargs)
712
    self.op.Validate(True)
713
    self.msglist = []
714
    self._feedback_fn = self.msglist.append
715
    self.bad = False
716

    
717
  def DispatchCallError(self, which, *args, **kwargs):
718
    if which:
719
      self._Error(*args, **kwargs)
720
    else:
721
      self._ErrorIf(True, *args, **kwargs)
722

    
723
  def CallErrorIf(self, c, *args, **kwargs):
724
    self._ErrorIf(c, *args, **kwargs)
725

    
726

    
727
class TestVerifyErrors(unittest.TestCase):
728
  # Fake cluster-verify error code structures; we use two arbitary real error
729
  # codes to pass validation of ignore_errors
730
  (_, _ERR1ID, _) = constants.CV_ECLUSTERCFG
731
  _NODESTR = "node"
732
  _NODENAME = "mynode"
733
  _ERR1CODE = (_NODESTR, _ERR1ID, "Error one")
734
  (_, _ERR2ID, _) = constants.CV_ECLUSTERCERT
735
  _INSTSTR = "instance"
736
  _INSTNAME = "myinstance"
737
  _ERR2CODE = (_INSTSTR, _ERR2ID, "Error two")
738
  # Arguments used to call _Error() or _ErrorIf()
739
  _ERR1ARGS = (_ERR1CODE, _NODENAME, "Error1 is %s", "an error")
740
  _ERR2ARGS = (_ERR2CODE, _INSTNAME, "Error2 has no argument")
741
  # Expected error messages
742
  _ERR1MSG = _ERR1ARGS[2] % _ERR1ARGS[3]
743
  _ERR2MSG = _ERR2ARGS[2]
744

    
745
  def testNoError(self):
746
    lu = _LuTestVerifyErrors()
747
    lu.CallErrorIf(False, self._ERR1CODE, *self._ERR1ARGS)
748
    self.assertFalse(lu.bad)
749
    self.assertFalse(lu.msglist)
750

    
751
  def _InitTest(self, **kwargs):
752
    self.lu1 = _LuTestVerifyErrors(**kwargs)
753
    self.lu2 = _LuTestVerifyErrors(**kwargs)
754

    
755
  def _CallError(self, *args, **kwargs):
756
    # Check that _Error() and _ErrorIf() produce the same results
757
    self.lu1.DispatchCallError(True, *args, **kwargs)
758
    self.lu2.DispatchCallError(False, *args, **kwargs)
759
    self.assertEqual(self.lu1.bad, self.lu2.bad)
760
    self.assertEqual(self.lu1.msglist, self.lu2.msglist)
761
    # Test-specific checks are made on one LU
762
    return self.lu1
763

    
764
  def _checkMsgCommon(self, logstr, errmsg, itype, item, warning):
765
    self.assertTrue(errmsg in logstr)
766
    if warning:
767
      self.assertTrue("WARNING" in logstr)
768
    else:
769
      self.assertTrue("ERROR" in logstr)
770
    self.assertTrue(itype in logstr)
771
    self.assertTrue(item in logstr)
772

    
773
  def _checkMsg1(self, logstr, warning=False):
774
    self._checkMsgCommon(logstr, self._ERR1MSG, self._NODESTR,
775
                         self._NODENAME, warning)
776

    
777
  def _checkMsg2(self, logstr, warning=False):
778
    self._checkMsgCommon(logstr, self._ERR2MSG, self._INSTSTR,
779
                         self._INSTNAME, warning)
780

    
781
  def testPlain(self):
782
    self._InitTest()
783
    lu = self._CallError(*self._ERR1ARGS)
784
    self.assertTrue(lu.bad)
785
    self.assertEqual(len(lu.msglist), 1)
786
    self._checkMsg1(lu.msglist[0])
787

    
788
  def testMultiple(self):
789
    self._InitTest()
790
    self._CallError(*self._ERR1ARGS)
791
    lu = self._CallError(*self._ERR2ARGS)
792
    self.assertTrue(lu.bad)
793
    self.assertEqual(len(lu.msglist), 2)
794
    self._checkMsg1(lu.msglist[0])
795
    self._checkMsg2(lu.msglist[1])
796

    
797
  def testIgnore(self):
798
    self._InitTest(ignore_errors=[self._ERR1ID])
799
    lu = self._CallError(*self._ERR1ARGS)
800
    self.assertFalse(lu.bad)
801
    self.assertEqual(len(lu.msglist), 1)
802
    self._checkMsg1(lu.msglist[0], warning=True)
803

    
804
  def testWarning(self):
805
    self._InitTest()
806
    lu = self._CallError(*self._ERR1ARGS,
807
                         code=_LuTestVerifyErrors.ETYPE_WARNING)
808
    self.assertFalse(lu.bad)
809
    self.assertEqual(len(lu.msglist), 1)
810
    self._checkMsg1(lu.msglist[0], warning=True)
811

    
812
  def testWarning2(self):
813
    self._InitTest()
814
    self._CallError(*self._ERR1ARGS)
815
    lu = self._CallError(*self._ERR2ARGS,
816
                         code=_LuTestVerifyErrors.ETYPE_WARNING)
817
    self.assertTrue(lu.bad)
818
    self.assertEqual(len(lu.msglist), 2)
819
    self._checkMsg1(lu.msglist[0])
820
    self._checkMsg2(lu.msglist[1], warning=True)
821

    
822
  def testDebugSimulate(self):
823
    lu = _LuTestVerifyErrors(debug_simulate_errors=True)
824
    lu.CallErrorIf(False, *self._ERR1ARGS)
825
    self.assertTrue(lu.bad)
826
    self.assertEqual(len(lu.msglist), 1)
827
    self._checkMsg1(lu.msglist[0])
828

    
829
  def testErrCodes(self):
830
    self._InitTest(error_codes=True)
831
    lu = self._CallError(*self._ERR1ARGS)
832
    self.assertTrue(lu.bad)
833
    self.assertEqual(len(lu.msglist), 1)
834
    self._checkMsg1(lu.msglist[0])
835
    self.assertTrue(self._ERR1ID in lu.msglist[0])
836

    
837

    
838
class TestGetUpdatedIPolicy(unittest.TestCase):
839
  """Tests for cmdlib._GetUpdatedIPolicy()"""
840
  _OLD_CLUSTER_POLICY = {
841
    constants.IPOLICY_VCPU_RATIO: 1.5,
842
    constants.ISPECS_MINMAX: [
843
      {
844
        constants.ISPECS_MIN: {
845
          constants.ISPEC_MEM_SIZE: 32768,
846
          constants.ISPEC_CPU_COUNT: 8,
847
          constants.ISPEC_DISK_COUNT: 1,
848
          constants.ISPEC_DISK_SIZE: 1024,
849
          constants.ISPEC_NIC_COUNT: 1,
850
          constants.ISPEC_SPINDLE_USE: 1,
851
          },
852
        constants.ISPECS_MAX: {
853
          constants.ISPEC_MEM_SIZE: 65536,
854
          constants.ISPEC_CPU_COUNT: 10,
855
          constants.ISPEC_DISK_COUNT: 5,
856
          constants.ISPEC_DISK_SIZE: 1024 * 1024,
857
          constants.ISPEC_NIC_COUNT: 3,
858
          constants.ISPEC_SPINDLE_USE: 12,
859
          },
860
        },
861
      constants.ISPECS_MINMAX_DEFAULTS,
862
      ],
863
    constants.ISPECS_STD: constants.IPOLICY_DEFAULTS[constants.ISPECS_STD],
864
    }
865
  _OLD_GROUP_POLICY = {
866
    constants.IPOLICY_SPINDLE_RATIO: 2.5,
867
    constants.ISPECS_MINMAX: [{
868
      constants.ISPECS_MIN: {
869
        constants.ISPEC_MEM_SIZE: 128,
870
        constants.ISPEC_CPU_COUNT: 1,
871
        constants.ISPEC_DISK_COUNT: 1,
872
        constants.ISPEC_DISK_SIZE: 1024,
873
        constants.ISPEC_NIC_COUNT: 1,
874
        constants.ISPEC_SPINDLE_USE: 1,
875
        },
876
      constants.ISPECS_MAX: {
877
        constants.ISPEC_MEM_SIZE: 32768,
878
        constants.ISPEC_CPU_COUNT: 8,
879
        constants.ISPEC_DISK_COUNT: 5,
880
        constants.ISPEC_DISK_SIZE: 1024 * 1024,
881
        constants.ISPEC_NIC_COUNT: 3,
882
        constants.ISPEC_SPINDLE_USE: 12,
883
        },
884
      }],
885
    }
886

    
887
  def _TestSetSpecs(self, old_policy, isgroup):
888
    diff_minmax = [{
889
      constants.ISPECS_MIN: {
890
        constants.ISPEC_MEM_SIZE: 64,
891
        constants.ISPEC_CPU_COUNT: 1,
892
        constants.ISPEC_DISK_COUNT: 2,
893
        constants.ISPEC_DISK_SIZE: 64,
894
        constants.ISPEC_NIC_COUNT: 1,
895
        constants.ISPEC_SPINDLE_USE: 1,
896
        },
897
      constants.ISPECS_MAX: {
898
        constants.ISPEC_MEM_SIZE: 16384,
899
        constants.ISPEC_CPU_COUNT: 10,
900
        constants.ISPEC_DISK_COUNT: 12,
901
        constants.ISPEC_DISK_SIZE: 1024,
902
        constants.ISPEC_NIC_COUNT: 9,
903
        constants.ISPEC_SPINDLE_USE: 18,
904
        },
905
      }]
906
    diff_std = {
907
        constants.ISPEC_DISK_COUNT: 10,
908
        constants.ISPEC_DISK_SIZE: 512,
909
        }
910
    diff_policy = {
911
      constants.ISPECS_MINMAX: diff_minmax
912
      }
913
    if not isgroup:
914
      diff_policy[constants.ISPECS_STD] = diff_std
915
    new_policy = common.GetUpdatedIPolicy(old_policy, diff_policy,
916
                                          group_policy=isgroup)
917

    
918
    self.assertTrue(constants.ISPECS_MINMAX in new_policy)
919
    self.assertEqual(new_policy[constants.ISPECS_MINMAX], diff_minmax)
920
    for key in old_policy:
921
      if not key in diff_policy:
922
        self.assertTrue(key in new_policy)
923
        self.assertEqual(new_policy[key], old_policy[key])
924

    
925
    if not isgroup:
926
      new_std = new_policy[constants.ISPECS_STD]
927
      for key in diff_std:
928
        self.assertTrue(key in new_std)
929
        self.assertEqual(new_std[key], diff_std[key])
930
      old_std = old_policy.get(constants.ISPECS_STD, {})
931
      for key in old_std:
932
        self.assertTrue(key in new_std)
933
        if key not in diff_std:
934
          self.assertEqual(new_std[key], old_std[key])
935

    
936
  def _TestSet(self, old_policy, diff_policy, isgroup):
937
    new_policy = common.GetUpdatedIPolicy(old_policy, diff_policy,
938
                                           group_policy=isgroup)
939
    for key in diff_policy:
940
      self.assertTrue(key in new_policy)
941
      self.assertEqual(new_policy[key], diff_policy[key])
942
    for key in old_policy:
943
      if not key in diff_policy:
944
        self.assertTrue(key in new_policy)
945
        self.assertEqual(new_policy[key], old_policy[key])
946

    
947
  def testSet(self):
948
    diff_policy = {
949
      constants.IPOLICY_VCPU_RATIO: 3,
950
      constants.IPOLICY_DTS: [constants.DT_FILE],
951
      }
952
    self._TestSet(self._OLD_GROUP_POLICY, diff_policy, True)
953
    self._TestSetSpecs(self._OLD_GROUP_POLICY, True)
954
    self._TestSet({}, diff_policy, True)
955
    self._TestSetSpecs({}, True)
956
    self._TestSet(self._OLD_CLUSTER_POLICY, diff_policy, False)
957
    self._TestSetSpecs(self._OLD_CLUSTER_POLICY, False)
958

    
959
  def testUnset(self):
960
    old_policy = self._OLD_GROUP_POLICY
961
    diff_policy = {
962
      constants.IPOLICY_SPINDLE_RATIO: constants.VALUE_DEFAULT,
963
      }
964
    new_policy = common.GetUpdatedIPolicy(old_policy, diff_policy,
965
                                          group_policy=True)
966
    for key in diff_policy:
967
      self.assertFalse(key in new_policy)
968
    for key in old_policy:
969
      if not key in diff_policy:
970
        self.assertTrue(key in new_policy)
971
        self.assertEqual(new_policy[key], old_policy[key])
972

    
973
    self.assertRaises(errors.OpPrereqError, common.GetUpdatedIPolicy,
974
                      old_policy, diff_policy, group_policy=False)
975

    
976
  def testUnsetEmpty(self):
977
    old_policy = {}
978
    for key in constants.IPOLICY_ALL_KEYS:
979
      diff_policy = {
980
        key: constants.VALUE_DEFAULT,
981
        }
982
    new_policy = common.GetUpdatedIPolicy(old_policy, diff_policy,
983
                                          group_policy=True)
984
    self.assertEqual(new_policy, old_policy)
985

    
986
  def _TestInvalidKeys(self, old_policy, isgroup):
987
    INVALID_KEY = "this_key_shouldnt_be_allowed"
988
    INVALID_DICT = {
989
      INVALID_KEY: 3,
990
      }
991
    invalid_policy = INVALID_DICT
992
    self.assertRaises(errors.OpPrereqError, common.GetUpdatedIPolicy,
993
                      old_policy, invalid_policy, group_policy=isgroup)
994
    invalid_ispecs = {
995
      constants.ISPECS_MINMAX: [INVALID_DICT],
996
      }
997
    self.assertRaises(errors.TypeEnforcementError, common.GetUpdatedIPolicy,
998
                      old_policy, invalid_ispecs, group_policy=isgroup)
999
    if isgroup:
1000
      invalid_for_group = {
1001
        constants.ISPECS_STD: constants.IPOLICY_DEFAULTS[constants.ISPECS_STD],
1002
        }
1003
      self.assertRaises(errors.OpPrereqError, common.GetUpdatedIPolicy,
1004
                        old_policy, invalid_for_group, group_policy=isgroup)
1005
    good_ispecs = self._OLD_CLUSTER_POLICY[constants.ISPECS_MINMAX]
1006
    invalid_ispecs = copy.deepcopy(good_ispecs)
1007
    invalid_policy = {
1008
      constants.ISPECS_MINMAX: invalid_ispecs,
1009
      }
1010
    for minmax in invalid_ispecs:
1011
      for key in constants.ISPECS_MINMAX_KEYS:
1012
        ispec = minmax[key]
1013
        ispec[INVALID_KEY] = None
1014
        self.assertRaises(errors.TypeEnforcementError,
1015
                          common.GetUpdatedIPolicy, old_policy,
1016
                          invalid_policy, group_policy=isgroup)
1017
        del ispec[INVALID_KEY]
1018
        for par in constants.ISPECS_PARAMETERS:
1019
          oldv = ispec[par]
1020
          ispec[par] = "this_is_not_good"
1021
          self.assertRaises(errors.TypeEnforcementError,
1022
                            common.GetUpdatedIPolicy,
1023
                            old_policy, invalid_policy, group_policy=isgroup)
1024
          ispec[par] = oldv
1025
    # This is to make sure that no two errors were present during the tests
1026
    common.GetUpdatedIPolicy(old_policy, invalid_policy,
1027
                             group_policy=isgroup)
1028

    
1029
  def testInvalidKeys(self):
1030
    self._TestInvalidKeys(self._OLD_GROUP_POLICY, True)
1031
    self._TestInvalidKeys(self._OLD_CLUSTER_POLICY, False)
1032

    
1033
  def testInvalidValues(self):
1034
    for par in (constants.IPOLICY_PARAMETERS |
1035
                frozenset([constants.IPOLICY_DTS])):
1036
      bad_policy = {
1037
        par: "invalid_value",
1038
        }
1039
      self.assertRaises(errors.OpPrereqError, common.GetUpdatedIPolicy, {},
1040
                        bad_policy, group_policy=True)
1041

    
1042

    
1043
class TestCopyLockList(unittest.TestCase):
1044
  def test(self):
1045
    self.assertEqual(instance_utils.CopyLockList([]), [])
1046
    self.assertEqual(instance_utils.CopyLockList(None), None)
1047
    self.assertEqual(instance_utils.CopyLockList(locking.ALL_SET),
1048
                     locking.ALL_SET)
1049

    
1050
    names = ["foo", "bar"]
1051
    output = instance_utils.CopyLockList(names)
1052
    self.assertEqual(names, output)
1053
    self.assertNotEqual(id(names), id(output), msg="List was not copied")
1054

    
1055

    
1056
if __name__ == "__main__":
1057
  testutils.GanetiTestProgram()