Statistics
| Branch: | Tag: | Revision:

root / test / py / cmdlib / cmdlib_unittest.py @ 4e7f986e

History | View | Annotate | Download (34.4 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):
592
    return tuple(instance.primary_node)
593

    
594

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

    
628

    
629
class _CallRecorder:
630
  def __init__(self, return_value=None):
631
    self.called = False
632
    self.return_value = return_value
633

    
634
  def __call__(self, *args):
635
    self.called = True
636
    return self.return_value
637

    
638

    
639
class TestComputeIPolicyNodeViolation(unittest.TestCase):
640
  def setUp(self):
641
    self.recorder = _CallRecorder(return_value=[])
642

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

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

    
661

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

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

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

    
693

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

    
704

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

    
714
  def DispatchCallError(self, which, *args, **kwargs):
715
    if which:
716
      self._Error(*args, **kwargs)
717
    else:
718
      self._ErrorIf(True, *args, **kwargs)
719

    
720
  def CallErrorIf(self, c, *args, **kwargs):
721
    self._ErrorIf(c, *args, **kwargs)
722

    
723

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

    
742
  def testNoError(self):
743
    lu = _LuTestVerifyErrors()
744
    lu.CallErrorIf(False, self._ERR1CODE, *self._ERR1ARGS)
745
    self.assertFalse(lu.bad)
746
    self.assertFalse(lu.msglist)
747

    
748
  def _InitTest(self, **kwargs):
749
    self.lu1 = _LuTestVerifyErrors(**kwargs)
750
    self.lu2 = _LuTestVerifyErrors(**kwargs)
751

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

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

    
770
  def _checkMsg1(self, logstr, warning=False):
771
    self._checkMsgCommon(logstr, self._ERR1MSG, self._NODESTR,
772
                         self._NODENAME, warning)
773

    
774
  def _checkMsg2(self, logstr, warning=False):
775
    self._checkMsgCommon(logstr, self._ERR2MSG, self._INSTSTR,
776
                         self._INSTNAME, warning)
777

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

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

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

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

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

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

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

    
834

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

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

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

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

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

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

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

    
970
    self.assertRaises(errors.OpPrereqError, common.GetUpdatedIPolicy,
971
                      old_policy, diff_policy, group_policy=False)
972

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

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

    
1026
  def testInvalidKeys(self):
1027
    self._TestInvalidKeys(self._OLD_GROUP_POLICY, True)
1028
    self._TestInvalidKeys(self._OLD_CLUSTER_POLICY, False)
1029

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

    
1039

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

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

    
1052

    
1053
if __name__ == "__main__":
1054
  testutils.GanetiTestProgram()