Statistics
| Branch: | Tag: | Revision:

root / test / py / cmdlib / cmdlib_unittest.py @ 560ef132

History | View | Annotate | Download (34.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 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

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

    
623

    
624
class _CallRecorder:
625
  def __init__(self, return_value=None):
626
    self.called = False
627
    self.return_value = return_value
628

    
629
  def __call__(self, *args):
630
    self.called = True
631
    return self.return_value
632

    
633

    
634
class TestComputeIPolicyNodeViolation(unittest.TestCase):
635
  def setUp(self):
636
    self.recorder = _CallRecorder(return_value=[])
637

    
638
  def testSameGroup(self):
639
    ret = instance_utils._ComputeIPolicyNodeViolation(
640
      NotImplemented,
641
      NotImplemented,
642
      "foo", "foo", NotImplemented,
643
      _compute_fn=self.recorder)
644
    self.assertFalse(self.recorder.called)
645
    self.assertEqual(ret, [])
646

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

    
656

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

    
668
  def testEven(self):
669
    for i in [1, 2, 7, 512, 1000, 1023]:
670
      lu = _FakeLU()
671
      result = instance_storage._DiskSizeInBytesToMebibytes(lu,
672
                                                            i * 1024 * 1024)
673
      self.assertEqual(result, i)
674
      self.assertFalse(lu.warning_log)
675

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

    
688

    
689
class _OpTestVerifyErrors(opcodes.OpCode):
690
  OP_PARAMS = [
691
    ("debug_simulate_errors", False, ht.TBool, ""),
692
    ("error_codes", False, ht.TBool, ""),
693
    ("ignore_errors",
694
     [],
695
     ht.TListOf(ht.TElemOf(constants.CV_ALL_ECODES_STRINGS)),
696
     "")
697
    ]
698

    
699

    
700
class _LuTestVerifyErrors(cluster._VerifyErrors):
701
  def __init__(self, **kwargs):
702
    cluster._VerifyErrors.__init__(self)
703
    self.op = _OpTestVerifyErrors(**kwargs)
704
    self.op.Validate(True)
705
    self.msglist = []
706
    self._feedback_fn = self.msglist.append
707
    self.bad = False
708

    
709
  def DispatchCallError(self, which, *args, **kwargs):
710
    if which:
711
      self._Error(*args, **kwargs)
712
    else:
713
      self._ErrorIf(True, *args, **kwargs)
714

    
715
  def CallErrorIf(self, c, *args, **kwargs):
716
    self._ErrorIf(c, *args, **kwargs)
717

    
718

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

    
737
  def testNoError(self):
738
    lu = _LuTestVerifyErrors()
739
    lu.CallErrorIf(False, self._ERR1CODE, *self._ERR1ARGS)
740
    self.assertFalse(lu.bad)
741
    self.assertFalse(lu.msglist)
742

    
743
  def _InitTest(self, **kwargs):
744
    self.lu1 = _LuTestVerifyErrors(**kwargs)
745
    self.lu2 = _LuTestVerifyErrors(**kwargs)
746

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

    
756
  def _checkMsgCommon(self, logstr, errmsg, itype, item, warning):
757
    self.assertTrue(errmsg in logstr)
758
    if warning:
759
      self.assertTrue("WARNING" in logstr)
760
    else:
761
      self.assertTrue("ERROR" in logstr)
762
    self.assertTrue(itype in logstr)
763
    self.assertTrue(item in logstr)
764

    
765
  def _checkMsg1(self, logstr, warning=False):
766
    self._checkMsgCommon(logstr, self._ERR1MSG, self._NODESTR,
767
                         self._NODENAME, warning)
768

    
769
  def _checkMsg2(self, logstr, warning=False):
770
    self._checkMsgCommon(logstr, self._ERR2MSG, self._INSTSTR,
771
                         self._INSTNAME, warning)
772

    
773
  def testPlain(self):
774
    self._InitTest()
775
    lu = self._CallError(*self._ERR1ARGS)
776
    self.assertTrue(lu.bad)
777
    self.assertEqual(len(lu.msglist), 1)
778
    self._checkMsg1(lu.msglist[0])
779

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

    
789
  def testIgnore(self):
790
    self._InitTest(ignore_errors=[self._ERR1ID])
791
    lu = self._CallError(*self._ERR1ARGS)
792
    self.assertFalse(lu.bad)
793
    self.assertEqual(len(lu.msglist), 1)
794
    self._checkMsg1(lu.msglist[0], warning=True)
795

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

    
804
  def testWarning2(self):
805
    self._InitTest()
806
    self._CallError(*self._ERR1ARGS)
807
    lu = self._CallError(*self._ERR2ARGS,
808
                         code=_LuTestVerifyErrors.ETYPE_WARNING)
809
    self.assertTrue(lu.bad)
810
    self.assertEqual(len(lu.msglist), 2)
811
    self._checkMsg1(lu.msglist[0])
812
    self._checkMsg2(lu.msglist[1], warning=True)
813

    
814
  def testDebugSimulate(self):
815
    lu = _LuTestVerifyErrors(debug_simulate_errors=True)
816
    lu.CallErrorIf(False, *self._ERR1ARGS)
817
    self.assertTrue(lu.bad)
818
    self.assertEqual(len(lu.msglist), 1)
819
    self._checkMsg1(lu.msglist[0])
820

    
821
  def testErrCodes(self):
822
    self._InitTest(error_codes=True)
823
    lu = self._CallError(*self._ERR1ARGS)
824
    self.assertTrue(lu.bad)
825
    self.assertEqual(len(lu.msglist), 1)
826
    self._checkMsg1(lu.msglist[0])
827
    self.assertTrue(self._ERR1ID in lu.msglist[0])
828

    
829

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

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

    
910
    self.assertTrue(constants.ISPECS_MINMAX in new_policy)
911
    self.assertEqual(new_policy[constants.ISPECS_MINMAX], diff_minmax)
912
    for key in old_policy:
913
      if not key in diff_policy:
914
        self.assertTrue(key in new_policy)
915
        self.assertEqual(new_policy[key], old_policy[key])
916

    
917
    if not isgroup:
918
      new_std = new_policy[constants.ISPECS_STD]
919
      for key in diff_std:
920
        self.assertTrue(key in new_std)
921
        self.assertEqual(new_std[key], diff_std[key])
922
      old_std = old_policy.get(constants.ISPECS_STD, {})
923
      for key in old_std:
924
        self.assertTrue(key in new_std)
925
        if key not in diff_std:
926
          self.assertEqual(new_std[key], old_std[key])
927

    
928
  def _TestSet(self, old_policy, diff_policy, isgroup):
929
    new_policy = common.GetUpdatedIPolicy(old_policy, diff_policy,
930
                                           group_policy=isgroup)
931
    for key in diff_policy:
932
      self.assertTrue(key in new_policy)
933
      self.assertEqual(new_policy[key], diff_policy[key])
934
    for key in old_policy:
935
      if not key in diff_policy:
936
        self.assertTrue(key in new_policy)
937
        self.assertEqual(new_policy[key], old_policy[key])
938

    
939
  def testSet(self):
940
    diff_policy = {
941
      constants.IPOLICY_VCPU_RATIO: 3,
942
      constants.IPOLICY_DTS: [constants.DT_FILE],
943
      }
944
    self._TestSet(self._OLD_GROUP_POLICY, diff_policy, True)
945
    self._TestSetSpecs(self._OLD_GROUP_POLICY, True)
946
    self._TestSet({}, diff_policy, True)
947
    self._TestSetSpecs({}, True)
948
    self._TestSet(self._OLD_CLUSTER_POLICY, diff_policy, False)
949
    self._TestSetSpecs(self._OLD_CLUSTER_POLICY, False)
950

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

    
965
    self.assertRaises(errors.OpPrereqError, common.GetUpdatedIPolicy,
966
                      old_policy, diff_policy, group_policy=False)
967

    
968
  def testUnsetEmpty(self):
969
    old_policy = {}
970
    for key in constants.IPOLICY_ALL_KEYS:
971
      diff_policy = {
972
        key: constants.VALUE_DEFAULT,
973
        }
974
    new_policy = common.GetUpdatedIPolicy(old_policy, diff_policy,
975
                                          group_policy=True)
976
    self.assertEqual(new_policy, old_policy)
977

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

    
1021
  def testInvalidKeys(self):
1022
    self._TestInvalidKeys(self._OLD_GROUP_POLICY, True)
1023
    self._TestInvalidKeys(self._OLD_CLUSTER_POLICY, False)
1024

    
1025
  def testInvalidValues(self):
1026
    for par in (constants.IPOLICY_PARAMETERS |
1027
                frozenset([constants.IPOLICY_DTS])):
1028
      bad_policy = {
1029
        par: "invalid_value",
1030
        }
1031
      self.assertRaises(errors.OpPrereqError, common.GetUpdatedIPolicy, {},
1032
                        bad_policy, group_policy=True)
1033

    
1034

    
1035
class TestCopyLockList(unittest.TestCase):
1036
  def test(self):
1037
    self.assertEqual(instance_utils.CopyLockList([]), [])
1038
    self.assertEqual(instance_utils.CopyLockList(None), None)
1039
    self.assertEqual(instance_utils.CopyLockList(locking.ALL_SET),
1040
                     locking.ALL_SET)
1041

    
1042
    names = ["foo", "bar"]
1043
    output = instance_utils.CopyLockList(names)
1044
    self.assertEqual(names, output)
1045
    self.assertNotEqual(id(names), id(output), msg="List was not copied")
1046

    
1047

    
1048
if __name__ == "__main__":
1049
  testutils.GanetiTestProgram()