cmdlib._ComputeMinMaxSpec: Add unittest for this function
[ganeti-local] / test / ganeti.cmdlib_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2008, 2011 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 """Script for unittesting the cmdlib module"""
23
24
25 import os
26 import unittest
27 import time
28 import tempfile
29 import shutil
30 import operator
31 import itertools
32
33 from ganeti import constants
34 from ganeti import mcpu
35 from ganeti import cmdlib
36 from ganeti import opcodes
37 from ganeti import errors
38 from ganeti import utils
39 from ganeti import luxi
40 from ganeti import ht
41 from ganeti import objects
42 from ganeti import compat
43 from ganeti import rpc
44 from ganeti.hypervisor import hv_xen
45
46 import testutils
47 import mocks
48
49
50 class TestCertVerification(testutils.GanetiTestCase):
51   def setUp(self):
52     testutils.GanetiTestCase.setUp(self)
53
54     self.tmpdir = tempfile.mkdtemp()
55
56   def tearDown(self):
57     shutil.rmtree(self.tmpdir)
58
59   def testVerifyCertificate(self):
60     cmdlib._VerifyCertificate(self._TestDataFilename("cert1.pem"))
61
62     nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
63
64     (errcode, msg) = cmdlib._VerifyCertificate(nonexist_filename)
65     self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
66
67     # Try to load non-certificate file
68     invalid_cert = self._TestDataFilename("bdev-net.txt")
69     (errcode, msg) = cmdlib._VerifyCertificate(invalid_cert)
70     self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
71
72
73 class TestOpcodeParams(testutils.GanetiTestCase):
74   def testParamsStructures(self):
75     for op in sorted(mcpu.Processor.DISPATCH_TABLE):
76       lu = mcpu.Processor.DISPATCH_TABLE[op]
77       lu_name = lu.__name__
78       self.failIf(hasattr(lu, "_OP_REQP"),
79                   msg=("LU '%s' has old-style _OP_REQP" % lu_name))
80       self.failIf(hasattr(lu, "_OP_DEFS"),
81                   msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
82       self.failIf(hasattr(lu, "_OP_PARAMS"),
83                   msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
84
85
86 class TestIAllocatorChecks(testutils.GanetiTestCase):
87   def testFunction(self):
88     class TestLU(object):
89       def __init__(self, opcode):
90         self.cfg = mocks.FakeConfig()
91         self.op = opcode
92
93     class OpTest(opcodes.OpCode):
94        OP_PARAMS = [
95         ("iallocator", None, ht.NoType, None),
96         ("node", None, ht.NoType, None),
97         ]
98
99     default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
100     other_iallocator = default_iallocator + "_not"
101
102     op = OpTest()
103     lu = TestLU(op)
104
105     c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
106
107     # Neither node nor iallocator given
108     op.iallocator = None
109     op.node = None
110     c_i()
111     self.assertEqual(lu.op.iallocator, default_iallocator)
112     self.assertEqual(lu.op.node, None)
113
114     # Both, iallocator and node given
115     op.iallocator = "test"
116     op.node = "test"
117     self.assertRaises(errors.OpPrereqError, c_i)
118
119     # Only iallocator given
120     op.iallocator = other_iallocator
121     op.node = None
122     c_i()
123     self.assertEqual(lu.op.iallocator, other_iallocator)
124     self.assertEqual(lu.op.node, None)
125
126     # Only node given
127     op.iallocator = None
128     op.node = "node"
129     c_i()
130     self.assertEqual(lu.op.iallocator, None)
131     self.assertEqual(lu.op.node, "node")
132
133     # No node, iallocator or default iallocator
134     op.iallocator = None
135     op.node = None
136     lu.cfg.GetDefaultIAllocator = lambda: None
137     self.assertRaises(errors.OpPrereqError, c_i)
138
139
140 class TestLUTestJqueue(unittest.TestCase):
141   def test(self):
142     self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
143                  (luxi.WFJC_TIMEOUT * 0.75),
144                  msg=("Client timeout too high, might not notice bugs"
145                       " in WaitForJobChange"))
146
147
148 class TestLUQuery(unittest.TestCase):
149   def test(self):
150     self.assertEqual(sorted(cmdlib._QUERY_IMPL.keys()),
151                      sorted(constants.QR_VIA_OP))
152
153     assert constants.QR_NODE in constants.QR_VIA_OP
154     assert constants.QR_INSTANCE in constants.QR_VIA_OP
155
156     for i in constants.QR_VIA_OP:
157       self.assert_(cmdlib._GetQueryImplementation(i))
158
159     self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation, "")
160     self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation,
161                       "xyz")
162
163
164 class TestLUGroupAssignNodes(unittest.TestCase):
165
166   def testCheckAssignmentForSplitInstances(self):
167     node_data = dict((name, objects.Node(name=name, group=group))
168                      for (name, group) in [("n1a", "g1"), ("n1b", "g1"),
169                                            ("n2a", "g2"), ("n2b", "g2"),
170                                            ("n3a", "g3"), ("n3b", "g3"),
171                                            ("n3c", "g3"),
172                                            ])
173
174     def Instance(name, pnode, snode):
175       if snode is None:
176         disks = []
177         disk_template = constants.DT_DISKLESS
178       else:
179         disks = [objects.Disk(dev_type=constants.LD_DRBD8,
180                               logical_id=[pnode, snode, 1, 17, 17])]
181         disk_template = constants.DT_DRBD8
182
183       return objects.Instance(name=name, primary_node=pnode, disks=disks,
184                               disk_template=disk_template)
185
186     instance_data = dict((name, Instance(name, pnode, snode))
187                          for name, pnode, snode in [("inst1a", "n1a", "n1b"),
188                                                     ("inst1b", "n1b", "n1a"),
189                                                     ("inst2a", "n2a", "n2b"),
190                                                     ("inst3a", "n3a", None),
191                                                     ("inst3b", "n3b", "n1b"),
192                                                     ("inst3c", "n3b", "n2b"),
193                                                     ])
194
195     # Test first with the existing state.
196     (new, prev) = \
197       cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
198                                                                  node_data,
199                                                                  instance_data)
200
201     self.assertEqual([], new)
202     self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
203
204     # And now some changes.
205     (new, prev) = \
206       cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
207                                                                    "g3")],
208                                                                  node_data,
209                                                                  instance_data)
210
211     self.assertEqual(set(["inst1a", "inst1b"]), set(new))
212     self.assertEqual(set(["inst3c"]), set(prev))
213
214
215 class TestClusterVerifySsh(unittest.TestCase):
216   def testMultipleGroups(self):
217     fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
218     mygroupnodes = [
219       objects.Node(name="node20", group="my", offline=False),
220       objects.Node(name="node21", group="my", offline=False),
221       objects.Node(name="node22", group="my", offline=False),
222       objects.Node(name="node23", group="my", offline=False),
223       objects.Node(name="node24", group="my", offline=False),
224       objects.Node(name="node25", group="my", offline=False),
225       objects.Node(name="node26", group="my", offline=True),
226       ]
227     nodes = [
228       objects.Node(name="node1", group="g1", offline=True),
229       objects.Node(name="node2", group="g1", offline=False),
230       objects.Node(name="node3", group="g1", offline=False),
231       objects.Node(name="node4", group="g1", offline=True),
232       objects.Node(name="node5", group="g1", offline=False),
233       objects.Node(name="node10", group="xyz", offline=False),
234       objects.Node(name="node11", group="xyz", offline=False),
235       objects.Node(name="node40", group="alloff", offline=True),
236       objects.Node(name="node41", group="alloff", offline=True),
237       objects.Node(name="node50", group="aaa", offline=False),
238       ] + mygroupnodes
239     assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
240
241     (online, perhost) = fn(mygroupnodes, "my", nodes)
242     self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
243     self.assertEqual(set(perhost.keys()), set(online))
244
245     self.assertEqual(perhost, {
246       "node20": ["node10", "node2", "node50"],
247       "node21": ["node11", "node3", "node50"],
248       "node22": ["node10", "node5", "node50"],
249       "node23": ["node11", "node2", "node50"],
250       "node24": ["node10", "node3", "node50"],
251       "node25": ["node11", "node5", "node50"],
252       })
253
254   def testSingleGroup(self):
255     fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
256     nodes = [
257       objects.Node(name="node1", group="default", offline=True),
258       objects.Node(name="node2", group="default", offline=False),
259       objects.Node(name="node3", group="default", offline=False),
260       objects.Node(name="node4", group="default", offline=True),
261       ]
262     assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
263
264     (online, perhost) = fn(nodes, "default", nodes)
265     self.assertEqual(online, ["node2", "node3"])
266     self.assertEqual(set(perhost.keys()), set(online))
267
268     self.assertEqual(perhost, {
269       "node2": [],
270       "node3": [],
271       })
272
273
274 class TestClusterVerifyFiles(unittest.TestCase):
275   @staticmethod
276   def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
277     assert ((ecode == constants.CV_ENODEFILECHECK and
278              ht.TNonEmptyString(item)) or
279             (ecode == constants.CV_ECLUSTERFILECHECK and
280              item is None))
281
282     if args:
283       msg = msg % args
284
285     if cond:
286       errors.append((item, msg))
287
288   _VerifyFiles = cmdlib.LUClusterVerifyGroup._VerifyFiles
289
290   def test(self):
291     errors = []
292     master_name = "master.example.com"
293     nodeinfo = [
294       objects.Node(name=master_name, offline=False, vm_capable=True),
295       objects.Node(name="node2.example.com", offline=False, vm_capable=True),
296       objects.Node(name="node3.example.com", master_candidate=True,
297                    vm_capable=False),
298       objects.Node(name="node4.example.com", offline=False, vm_capable=True),
299       objects.Node(name="nodata.example.com", offline=False, vm_capable=True),
300       objects.Node(name="offline.example.com", offline=True),
301       ]
302     cluster = objects.Cluster(modify_etc_hosts=True,
303                               enabled_hypervisors=[constants.HT_XEN_HVM])
304     files_all = set([
305       constants.CLUSTER_DOMAIN_SECRET_FILE,
306       constants.RAPI_CERT_FILE,
307       constants.RAPI_USERS_FILE,
308       ])
309     files_opt = set([
310       constants.RAPI_USERS_FILE,
311       hv_xen.XL_CONFIG_FILE,
312       constants.VNC_PASSWORD_FILE,
313       ])
314     files_mc = set([
315       constants.CLUSTER_CONF_FILE,
316       ])
317     files_vm = set([
318       hv_xen.XEND_CONFIG_FILE,
319       hv_xen.XL_CONFIG_FILE,
320       constants.VNC_PASSWORD_FILE,
321       ])
322     nvinfo = {
323       master_name: rpc.RpcResult(data=(True, {
324         constants.NV_FILELIST: {
325           constants.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
326           constants.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
327           constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
328           hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
329           hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
330         }})),
331       "node2.example.com": rpc.RpcResult(data=(True, {
332         constants.NV_FILELIST: {
333           constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
334           hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
335           }
336         })),
337       "node3.example.com": rpc.RpcResult(data=(True, {
338         constants.NV_FILELIST: {
339           constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
340           constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
341           }
342         })),
343       "node4.example.com": rpc.RpcResult(data=(True, {
344         constants.NV_FILELIST: {
345           constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
346           constants.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
347           constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
348           constants.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
349           hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
350           }
351         })),
352       "nodata.example.com": rpc.RpcResult(data=(True, {})),
353       "offline.example.com": rpc.RpcResult(offline=True),
354       }
355     assert set(nvinfo.keys()) == set(map(operator.attrgetter("name"), nodeinfo))
356
357     self._VerifyFiles(compat.partial(self._FakeErrorIf, errors), nodeinfo,
358                       master_name, nvinfo,
359                       (files_all, files_opt, files_mc, files_vm))
360     self.assertEqual(sorted(errors), sorted([
361       (None, ("File %s found with 2 different checksums (variant 1 on"
362               " node2.example.com, node3.example.com, node4.example.com;"
363               " variant 2 on master.example.com)" % constants.RAPI_CERT_FILE)),
364       (None, ("File %s is missing from node(s) node2.example.com" %
365               constants.CLUSTER_DOMAIN_SECRET_FILE)),
366       (None, ("File %s should not exist on node(s) node4.example.com" %
367               constants.CLUSTER_CONF_FILE)),
368       (None, ("File %s is missing from node(s) node4.example.com" %
369               hv_xen.XEND_CONFIG_FILE)),
370       (None, ("File %s is missing from node(s) node3.example.com" %
371               constants.CLUSTER_CONF_FILE)),
372       (None, ("File %s found with 2 different checksums (variant 1 on"
373               " master.example.com; variant 2 on node4.example.com)" %
374               constants.CLUSTER_CONF_FILE)),
375       (None, ("File %s is optional, but it must exist on all or no nodes (not"
376               " found on master.example.com, node2.example.com,"
377               " node3.example.com)" % constants.RAPI_USERS_FILE)),
378       (None, ("File %s is optional, but it must exist on all or no nodes (not"
379               " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
380       ("nodata.example.com", "Node did not return file checksum data"),
381       ]))
382
383
384 class _FakeLU:
385   def __init__(self, cfg=NotImplemented):
386     self.warning_log = []
387     self.info_log = []
388     self.cfg = cfg
389
390   def LogWarning(self, text, *args):
391     self.warning_log.append((text, args))
392
393   def LogInfo(self, text, *args):
394     self.info_log.append((text, args))
395
396
397 class TestLoadNodeEvacResult(unittest.TestCase):
398   def testSuccess(self):
399     for moved in [[], [
400       ("inst20153.example.com", "grp2", ["nodeA4509", "nodeB2912"]),
401       ]]:
402       for early_release in [False, True]:
403         for use_nodes in [False, True]:
404           jobs = [
405             [opcodes.OpInstanceReplaceDisks().__getstate__()],
406             [opcodes.OpInstanceMigrate().__getstate__()],
407             ]
408
409           alloc_result = (moved, [], jobs)
410           assert cmdlib.IAllocator._NEVAC_RESULT(alloc_result)
411
412           lu = _FakeLU()
413           result = cmdlib._LoadNodeEvacResult(lu, alloc_result,
414                                               early_release, use_nodes)
415
416           if moved:
417             (_, (info_args, )) = lu.info_log.pop(0)
418             for (instname, instgroup, instnodes) in moved:
419               self.assertTrue(instname in info_args)
420               if use_nodes:
421                 for i in instnodes:
422                   self.assertTrue(i in info_args)
423               else:
424                 self.assertTrue(instgroup in info_args)
425
426           self.assertFalse(lu.info_log)
427           self.assertFalse(lu.warning_log)
428
429           for op in itertools.chain(*result):
430             if hasattr(op.__class__, "early_release"):
431               self.assertEqual(op.early_release, early_release)
432             else:
433               self.assertFalse(hasattr(op, "early_release"))
434
435   def testFailed(self):
436     alloc_result = ([], [
437       ("inst5191.example.com", "errormsg21178"),
438       ], [])
439     assert cmdlib.IAllocator._NEVAC_RESULT(alloc_result)
440
441     lu = _FakeLU()
442     self.assertRaises(errors.OpExecError, cmdlib._LoadNodeEvacResult,
443                       lu, alloc_result, False, False)
444     self.assertFalse(lu.info_log)
445     (_, (args, )) = lu.warning_log.pop(0)
446     self.assertTrue("inst5191.example.com" in args)
447     self.assertTrue("errormsg21178" in args)
448     self.assertFalse(lu.warning_log)
449
450
451 class TestUpdateAndVerifySubDict(unittest.TestCase):
452   def setUp(self):
453     self.type_check = {
454         "a": constants.VTYPE_INT,
455         "b": constants.VTYPE_STRING,
456         "c": constants.VTYPE_BOOL,
457         "d": constants.VTYPE_STRING,
458         }
459
460   def test(self):
461     old_test = {
462       "foo": {
463         "d": "blubb",
464         "a": 321,
465         },
466       "baz": {
467         "a": 678,
468         "b": "678",
469         "c": True,
470         },
471       }
472     test = {
473       "foo": {
474         "a": 123,
475         "b": "123",
476         "c": True,
477         },
478       "bar": {
479         "a": 321,
480         "b": "321",
481         "c": False,
482         },
483       }
484
485     mv = {
486       "foo": {
487         "a": 123,
488         "b": "123",
489         "c": True,
490         "d": "blubb"
491         },
492       "bar": {
493         "a": 321,
494         "b": "321",
495         "c": False,
496         },
497       "baz": {
498         "a": 678,
499         "b": "678",
500         "c": True,
501         },
502       }
503
504     verified = cmdlib._UpdateAndVerifySubDict(old_test, test, self.type_check)
505     self.assertEqual(verified, mv)
506
507   def testWrong(self):
508     test = {
509       "foo": {
510         "a": "blubb",
511         "b": "123",
512         "c": True,
513         },
514       "bar": {
515         "a": 321,
516         "b": "321",
517         "c": False,
518         },
519       }
520
521     self.assertRaises(errors.TypeEnforcementError,
522                       cmdlib._UpdateAndVerifySubDict, {}, test, self.type_check)
523
524
525 class TestHvStateHelper(unittest.TestCase):
526   def testWithoutOpData(self):
527     self.assertEqual(cmdlib._MergeAndVerifyHvState(None, NotImplemented), None)
528
529   def testWithoutOldData(self):
530     new = {
531       constants.HT_XEN_PVM: {
532         constants.HVST_MEMORY_TOTAL: 4096,
533         },
534       }
535     self.assertEqual(cmdlib._MergeAndVerifyHvState(new, None), new)
536
537   def testWithWrongHv(self):
538     new = {
539       "i-dont-exist": {
540         constants.HVST_MEMORY_TOTAL: 4096,
541         },
542       }
543     self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyHvState, new,
544                       None)
545
546 class TestDiskStateHelper(unittest.TestCase):
547   def testWithoutOpData(self):
548     self.assertEqual(cmdlib._MergeAndVerifyDiskState(None, NotImplemented),
549                      None)
550
551   def testWithoutOldData(self):
552     new = {
553       constants.LD_LV: {
554         "xenvg": {
555           constants.DS_DISK_RESERVED: 1024,
556           },
557         },
558       }
559     self.assertEqual(cmdlib._MergeAndVerifyDiskState(new, None), new)
560
561   def testWithWrongStorageType(self):
562     new = {
563       "i-dont-exist": {
564         "xenvg": {
565           constants.DS_DISK_RESERVED: 1024,
566           },
567         },
568       }
569     self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyDiskState,
570                       new, None)
571
572
573 class TestComputeMinMaxSpec(unittest.TestCase):
574   def setUp(self):
575     self.ipolicy = {
576       constants.ISPECS_MAX: {
577         constants.ISPEC_MEM_SIZE: 512,
578         constants.ISPEC_DISK_SIZE: 1024,
579         },
580       constants.ISPECS_MIN: {
581         constants.ISPEC_MEM_SIZE: 128,
582         constants.ISPEC_DISK_COUNT: 1,
583         },
584       }
585
586   def testNoneValue(self):
587     self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE,
588                                               self.ipolicy, None) is None)
589
590   def testAutoValue(self):
591     self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE,
592                                               self.ipolicy,
593                                               constants.VALUE_AUTO) is None)
594
595   def testNotDefined(self):
596     self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT,
597                                               self.ipolicy, 3) is None)
598
599   def testNoMinDefined(self):
600     self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE,
601                                               self.ipolicy, 128) is None)
602
603   def testNoMaxDefined(self):
604     self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT,
605                                                 self.ipolicy, 16) is None)
606
607   def testOutOfRange(self):
608     for (name, val) in ((constants.ISPEC_MEM_SIZE, 64),
609                         (constants.ISPEC_MEM_SIZE, 768),
610                         (constants.ISPEC_DISK_SIZE, 4096),
611                         (constants.ISPEC_DISK_COUNT, 0)):
612       min_v = self.ipolicy[constants.ISPECS_MIN].get(name, val)
613       max_v = self.ipolicy[constants.ISPECS_MAX].get(name, val)
614       self.assertEqual(cmdlib._ComputeMinMaxSpec(name, self.ipolicy, val),
615                        "%s value %s is not in range [%s, %s]" %
616                        (name, val,min_v, max_v))
617
618   def test(self):
619     for (name, val) in ((constants.ISPEC_MEM_SIZE, 256),
620                         (constants.ISPEC_MEM_SIZE, 128),
621                         (constants.ISPEC_MEM_SIZE, 512),
622                         (constants.ISPEC_DISK_SIZE, 1024),
623                         (constants.ISPEC_DISK_SIZE, 0),
624                         (constants.ISPEC_DISK_COUNT, 1),
625                         (constants.ISPEC_DISK_COUNT, 5)):
626       self.assertTrue(cmdlib._ComputeMinMaxSpec(name, self.ipolicy, val)
627                       is None)
628
629
630 def _ValidateComputeMinMaxSpec(name, *_):
631   assert name in constants.ISPECS_PARAMETERS
632   return None
633
634
635 class _SpecWrapper:
636   def __init__(self, spec):
637     self.spec = spec
638
639   def ComputeMinMaxSpec(self, *args):
640     return self.spec.pop(0)
641
642
643 class TestComputeIPolicySpecViolation(unittest.TestCase):
644   def test(self):
645     compute_fn = _ValidateComputeMinMaxSpec
646     ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
647                                               [1024], _compute_fn=compute_fn)
648     self.assertEqual(ret, [])
649
650   def testInvalidArguments(self):
651     self.assertRaises(AssertionError, cmdlib._ComputeIPolicySpecViolation,
652                       NotImplemented, 1024, 1, 1, 1, [])
653
654   def testInvalidSpec(self):
655     spec = _SpecWrapper([None, False, "foo", None, "bar"])
656     compute_fn = spec.ComputeMinMaxSpec
657     ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
658                                               [1024], _compute_fn=compute_fn)
659     self.assertEqual(ret, ["foo", "bar"])
660     self.assertFalse(spec.spec)
661
662
663 class _StubComputeIPolicySpecViolation:
664   def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes):
665     self.mem_size = mem_size
666     self.cpu_count = cpu_count
667     self.disk_count = disk_count
668     self.nic_count = nic_count
669     self.disk_sizes = disk_sizes
670
671   def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes):
672     assert self.mem_size == mem_size
673     assert self.cpu_count == cpu_count
674     assert self.disk_count == disk_count
675     assert self.nic_count == nic_count
676     assert self.disk_sizes == disk_sizes
677
678     return []
679
680
681 class TestComputeIPolicyInstanceViolation(unittest.TestCase):
682   def test(self):
683     beparams = {
684       constants.BE_MAXMEM: 2048,
685       constants.BE_VCPUS: 2,
686       }
687     disks = [objects.Disk(size=512)]
688     instance = objects.Instance(beparams=beparams, disks=disks, nics=[])
689     stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512])
690     ret = cmdlib._ComputeIPolicyInstanceViolation(NotImplemented, instance,
691                                                   _compute_fn=stub)
692     self.assertEqual(ret, [])
693
694
695 class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
696   def test(self):
697     ispec = {
698       constants.ISPEC_MEM_SIZE: 2048,
699       constants.ISPEC_CPU_COUNT: 2,
700       constants.ISPEC_DISK_COUNT: 1,
701       constants.ISPEC_DISK_SIZE: [512],
702       constants.ISPEC_NIC_COUNT: 0,
703       }
704     stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512])
705     ret = cmdlib._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
706                                                       _compute_fn=stub)
707     self.assertEqual(ret, [])
708
709
710 class _CallRecorder:
711   def __init__(self, return_value=None):
712     self.called = False
713     self.return_value = return_value
714
715   def __call__(self, *args):
716     self.called = True
717     return self.return_value
718
719
720 class TestComputeIPolicyNodeViolation(unittest.TestCase):
721   def setUp(self):
722     self.recorder = _CallRecorder(return_value=[])
723
724   def testSameGroup(self):
725     ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
726                                               "foo", "foo",
727                                               _compute_fn=self.recorder)
728     self.assertFalse(self.recorder.called)
729     self.assertEqual(ret, [])
730
731   def testDifferentGroup(self):
732     ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
733                                               "foo", "bar",
734                                               _compute_fn=self.recorder)
735     self.assertTrue(self.recorder.called)
736     self.assertEqual(ret, [])
737
738
739 class _FakeConfigForTargetNodeIPolicy:
740   def __init__(self, node_info=NotImplemented):
741     self._node_info = node_info
742
743   def GetNodeInfo(self, _):
744     return self._node_info
745
746
747 class TestCheckTargetNodeIPolicy(unittest.TestCase):
748   def setUp(self):
749     self.instance = objects.Instance(primary_node="blubb")
750     self.target_node = objects.Node(group="bar")
751     node_info = objects.Node(group="foo")
752     fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
753     self.lu = _FakeLU(cfg=fake_cfg)
754
755   def testNoViolation(self):
756     compute_recoder = _CallRecorder(return_value=[])
757     cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
758                                    self.target_node,
759                                    _compute_fn=compute_recoder)
760     self.assertTrue(compute_recoder.called)
761     self.assertEqual(self.lu.warning_log, [])
762
763   def testNoIgnore(self):
764     compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
765     self.assertRaises(errors.OpPrereqError, cmdlib._CheckTargetNodeIPolicy,
766                       self.lu, NotImplemented, self.instance, self.target_node,
767                       _compute_fn=compute_recoder)
768     self.assertTrue(compute_recoder.called)
769     self.assertEqual(self.lu.warning_log, [])
770
771   def testIgnoreViolation(self):
772     compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
773     cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
774                                    self.target_node, ignore=True,
775                                    _compute_fn=compute_recoder)
776     self.assertTrue(compute_recoder.called)
777     msg = ("Instance does not meet target node group's (bar) instance policy:"
778            " mem_size not in range")
779     self.assertEqual(self.lu.warning_log, [(msg, ())])
780
781
782 if __name__ == "__main__":
783   testutils.GanetiTestProgram()