2c974caaaeda42beebb4f421bf93c19c24791625
[ganeti-local] / test / py / ganeti.cmdlib_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2008, 2011, 2012, 2013 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 """Script for unittesting the cmdlib module"""
23
24
25 import os
26 import unittest
27 import time
28 import tempfile
29 import shutil
30 import operator
31 import itertools
32 import copy
33
34 from ganeti import constants
35 from ganeti import mcpu
36 from ganeti import cmdlib
37 from ganeti.cmdlib import cluster
38 from ganeti.cmdlib import common
39 from ganeti import opcodes
40 from ganeti import errors
41 from ganeti import utils
42 from ganeti import luxi
43 from ganeti import ht
44 from ganeti import objects
45 from ganeti import compat
46 from ganeti import rpc
47 from ganeti import locking
48 from ganeti import pathutils
49 from ganeti.masterd import iallocator
50 from ganeti.hypervisor import hv_xen
51
52 import testutils
53 import mocks
54
55
56 class TestCertVerification(testutils.GanetiTestCase):
57   def setUp(self):
58     testutils.GanetiTestCase.setUp(self)
59
60     self.tmpdir = tempfile.mkdtemp()
61
62   def tearDown(self):
63     shutil.rmtree(self.tmpdir)
64
65   def testVerifyCertificate(self):
66     cluster._VerifyCertificate(testutils.TestDataFilename("cert1.pem"))
67
68     nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
69
70     (errcode, msg) = cluster._VerifyCertificate(nonexist_filename)
71     self.assertEqual(errcode, cluster.LUClusterVerifyConfig.ETYPE_ERROR)
72
73     # Try to load non-certificate file
74     invalid_cert = testutils.TestDataFilename("bdev-net.txt")
75     (errcode, msg) = cluster._VerifyCertificate(invalid_cert)
76     self.assertEqual(errcode, cluster.LUClusterVerifyConfig.ETYPE_ERROR)
77
78
79 class TestOpcodeParams(testutils.GanetiTestCase):
80   def testParamsStructures(self):
81     for op in sorted(mcpu.Processor.DISPATCH_TABLE):
82       lu = mcpu.Processor.DISPATCH_TABLE[op]
83       lu_name = lu.__name__
84       self.failIf(hasattr(lu, "_OP_REQP"),
85                   msg=("LU '%s' has old-style _OP_REQP" % lu_name))
86       self.failIf(hasattr(lu, "_OP_DEFS"),
87                   msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
88       self.failIf(hasattr(lu, "_OP_PARAMS"),
89                   msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
90
91
92 class TestIAllocatorChecks(testutils.GanetiTestCase):
93   def testFunction(self):
94     class TestLU(object):
95       def __init__(self, opcode):
96         self.cfg = mocks.FakeConfig()
97         self.op = opcode
98
99     class OpTest(opcodes.OpCode):
100        OP_PARAMS = [
101         ("iallocator", None, ht.NoType, None),
102         ("node", None, ht.NoType, None),
103         ]
104
105     default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
106     other_iallocator = default_iallocator + "_not"
107
108     op = OpTest()
109     lu = TestLU(op)
110
111     c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
112
113     # Neither node nor iallocator given
114     for n in (None, []):
115       op.iallocator = None
116       op.node = n
117       c_i()
118       self.assertEqual(lu.op.iallocator, default_iallocator)
119       self.assertEqual(lu.op.node, n)
120
121     # Both, iallocator and node given
122     for a in ("test", constants.DEFAULT_IALLOCATOR_SHORTCUT):
123       op.iallocator = a
124       op.node = "test"
125       self.assertRaises(errors.OpPrereqError, c_i)
126
127     # Only iallocator given
128     for n in (None, []):
129       op.iallocator = other_iallocator
130       op.node = n
131       c_i()
132       self.assertEqual(lu.op.iallocator, other_iallocator)
133       self.assertEqual(lu.op.node, n)
134
135     # Only node given
136     op.iallocator = None
137     op.node = "node"
138     c_i()
139     self.assertEqual(lu.op.iallocator, None)
140     self.assertEqual(lu.op.node, "node")
141
142     # Asked for default iallocator, no node given
143     op.iallocator = constants.DEFAULT_IALLOCATOR_SHORTCUT
144     op.node = None
145     c_i()
146     self.assertEqual(lu.op.iallocator, default_iallocator)
147     self.assertEqual(lu.op.node, None)
148
149     # No node, iallocator or default iallocator
150     op.iallocator = None
151     op.node = None
152     lu.cfg.GetDefaultIAllocator = lambda: None
153     self.assertRaises(errors.OpPrereqError, c_i)
154
155
156 class TestLUTestJqueue(unittest.TestCase):
157   def test(self):
158     self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
159                  (luxi.WFJC_TIMEOUT * 0.75),
160                  msg=("Client timeout too high, might not notice bugs"
161                       " in WaitForJobChange"))
162
163
164 class TestLUQuery(unittest.TestCase):
165   def test(self):
166     self.assertEqual(sorted(cmdlib._QUERY_IMPL.keys()),
167                      sorted(constants.QR_VIA_OP))
168
169     assert constants.QR_NODE in constants.QR_VIA_OP
170     assert constants.QR_INSTANCE in constants.QR_VIA_OP
171
172     for i in constants.QR_VIA_OP:
173       self.assert_(cmdlib._GetQueryImplementation(i))
174
175     self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation, "")
176     self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation,
177                       "xyz")
178
179
180 class TestLUGroupAssignNodes(unittest.TestCase):
181
182   def testCheckAssignmentForSplitInstances(self):
183     node_data = dict((name, objects.Node(name=name, group=group))
184                      for (name, group) in [("n1a", "g1"), ("n1b", "g1"),
185                                            ("n2a", "g2"), ("n2b", "g2"),
186                                            ("n3a", "g3"), ("n3b", "g3"),
187                                            ("n3c", "g3"),
188                                            ])
189
190     def Instance(name, pnode, snode):
191       if snode is None:
192         disks = []
193         disk_template = constants.DT_DISKLESS
194       else:
195         disks = [objects.Disk(dev_type=constants.LD_DRBD8,
196                               logical_id=[pnode, snode, 1, 17, 17])]
197         disk_template = constants.DT_DRBD8
198
199       return objects.Instance(name=name, primary_node=pnode, disks=disks,
200                               disk_template=disk_template)
201
202     instance_data = dict((name, Instance(name, pnode, snode))
203                          for name, pnode, snode in [("inst1a", "n1a", "n1b"),
204                                                     ("inst1b", "n1b", "n1a"),
205                                                     ("inst2a", "n2a", "n2b"),
206                                                     ("inst3a", "n3a", None),
207                                                     ("inst3b", "n3b", "n1b"),
208                                                     ("inst3c", "n3b", "n2b"),
209                                                     ])
210
211     # Test first with the existing state.
212     (new, prev) = \
213       cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
214                                                                  node_data,
215                                                                  instance_data)
216
217     self.assertEqual([], new)
218     self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
219
220     # And now some changes.
221     (new, prev) = \
222       cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
223                                                                    "g3")],
224                                                                  node_data,
225                                                                  instance_data)
226
227     self.assertEqual(set(["inst1a", "inst1b"]), set(new))
228     self.assertEqual(set(["inst3c"]), set(prev))
229
230
231 class TestClusterVerifySsh(unittest.TestCase):
232   def testMultipleGroups(self):
233     fn = cluster.LUClusterVerifyGroup._SelectSshCheckNodes
234     mygroupnodes = [
235       objects.Node(name="node20", group="my", offline=False),
236       objects.Node(name="node21", group="my", offline=False),
237       objects.Node(name="node22", group="my", offline=False),
238       objects.Node(name="node23", group="my", offline=False),
239       objects.Node(name="node24", group="my", offline=False),
240       objects.Node(name="node25", group="my", offline=False),
241       objects.Node(name="node26", group="my", offline=True),
242       ]
243     nodes = [
244       objects.Node(name="node1", group="g1", offline=True),
245       objects.Node(name="node2", group="g1", offline=False),
246       objects.Node(name="node3", group="g1", offline=False),
247       objects.Node(name="node4", group="g1", offline=True),
248       objects.Node(name="node5", group="g1", offline=False),
249       objects.Node(name="node10", group="xyz", offline=False),
250       objects.Node(name="node11", group="xyz", offline=False),
251       objects.Node(name="node40", group="alloff", offline=True),
252       objects.Node(name="node41", group="alloff", offline=True),
253       objects.Node(name="node50", group="aaa", offline=False),
254       ] + mygroupnodes
255     assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
256
257     (online, perhost) = fn(mygroupnodes, "my", nodes)
258     self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
259     self.assertEqual(set(perhost.keys()), set(online))
260
261     self.assertEqual(perhost, {
262       "node20": ["node10", "node2", "node50"],
263       "node21": ["node11", "node3", "node50"],
264       "node22": ["node10", "node5", "node50"],
265       "node23": ["node11", "node2", "node50"],
266       "node24": ["node10", "node3", "node50"],
267       "node25": ["node11", "node5", "node50"],
268       })
269
270   def testSingleGroup(self):
271     fn = cluster.LUClusterVerifyGroup._SelectSshCheckNodes
272     nodes = [
273       objects.Node(name="node1", group="default", offline=True),
274       objects.Node(name="node2", group="default", offline=False),
275       objects.Node(name="node3", group="default", offline=False),
276       objects.Node(name="node4", group="default", offline=True),
277       ]
278     assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
279
280     (online, perhost) = fn(nodes, "default", nodes)
281     self.assertEqual(online, ["node2", "node3"])
282     self.assertEqual(set(perhost.keys()), set(online))
283
284     self.assertEqual(perhost, {
285       "node2": [],
286       "node3": [],
287       })
288
289
290 class TestClusterVerifyFiles(unittest.TestCase):
291   @staticmethod
292   def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
293     assert ((ecode == constants.CV_ENODEFILECHECK and
294              ht.TNonEmptyString(item)) or
295             (ecode == constants.CV_ECLUSTERFILECHECK and
296              item is None))
297
298     if args:
299       msg = msg % args
300
301     if cond:
302       errors.append((item, msg))
303
304   _VerifyFiles = cluster.LUClusterVerifyGroup._VerifyFiles
305
306   def test(self):
307     errors = []
308     master_name = "master.example.com"
309     nodeinfo = [
310       objects.Node(name=master_name, offline=False, vm_capable=True),
311       objects.Node(name="node2.example.com", offline=False, vm_capable=True),
312       objects.Node(name="node3.example.com", master_candidate=True,
313                    vm_capable=False),
314       objects.Node(name="node4.example.com", offline=False, vm_capable=True),
315       objects.Node(name="nodata.example.com", offline=False, vm_capable=True),
316       objects.Node(name="offline.example.com", offline=True),
317       ]
318     cluster = objects.Cluster(modify_etc_hosts=True,
319                               enabled_hypervisors=[constants.HT_XEN_HVM])
320     files_all = set([
321       pathutils.CLUSTER_DOMAIN_SECRET_FILE,
322       pathutils.RAPI_CERT_FILE,
323       pathutils.RAPI_USERS_FILE,
324       ])
325     files_opt = set([
326       pathutils.RAPI_USERS_FILE,
327       hv_xen.XL_CONFIG_FILE,
328       pathutils.VNC_PASSWORD_FILE,
329       ])
330     files_mc = set([
331       pathutils.CLUSTER_CONF_FILE,
332       ])
333     files_vm = set([
334       hv_xen.XEND_CONFIG_FILE,
335       hv_xen.XL_CONFIG_FILE,
336       pathutils.VNC_PASSWORD_FILE,
337       ])
338     nvinfo = {
339       master_name: rpc.RpcResult(data=(True, {
340         constants.NV_FILELIST: {
341           pathutils.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
342           pathutils.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
343           pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
344           hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
345           hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
346         }})),
347       "node2.example.com": rpc.RpcResult(data=(True, {
348         constants.NV_FILELIST: {
349           pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
350           hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
351           }
352         })),
353       "node3.example.com": rpc.RpcResult(data=(True, {
354         constants.NV_FILELIST: {
355           pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
356           pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
357           }
358         })),
359       "node4.example.com": rpc.RpcResult(data=(True, {
360         constants.NV_FILELIST: {
361           pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
362           pathutils.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
363           pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
364           pathutils.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
365           hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
366           }
367         })),
368       "nodata.example.com": rpc.RpcResult(data=(True, {})),
369       "offline.example.com": rpc.RpcResult(offline=True),
370       }
371     assert set(nvinfo.keys()) == set(map(operator.attrgetter("name"), nodeinfo))
372
373     self._VerifyFiles(compat.partial(self._FakeErrorIf, errors), nodeinfo,
374                       master_name, nvinfo,
375                       (files_all, files_opt, files_mc, files_vm))
376     self.assertEqual(sorted(errors), sorted([
377       (None, ("File %s found with 2 different checksums (variant 1 on"
378               " node2.example.com, node3.example.com, node4.example.com;"
379               " variant 2 on master.example.com)" % pathutils.RAPI_CERT_FILE)),
380       (None, ("File %s is missing from node(s) node2.example.com" %
381               pathutils.CLUSTER_DOMAIN_SECRET_FILE)),
382       (None, ("File %s should not exist on node(s) node4.example.com" %
383               pathutils.CLUSTER_CONF_FILE)),
384       (None, ("File %s is missing from node(s) node4.example.com" %
385               hv_xen.XEND_CONFIG_FILE)),
386       (None, ("File %s is missing from node(s) node3.example.com" %
387               pathutils.CLUSTER_CONF_FILE)),
388       (None, ("File %s found with 2 different checksums (variant 1 on"
389               " master.example.com; variant 2 on node4.example.com)" %
390               pathutils.CLUSTER_CONF_FILE)),
391       (None, ("File %s is optional, but it must exist on all or no nodes (not"
392               " found on master.example.com, node2.example.com,"
393               " node3.example.com)" % pathutils.RAPI_USERS_FILE)),
394       (None, ("File %s is optional, but it must exist on all or no nodes (not"
395               " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
396       ("nodata.example.com", "Node did not return file checksum data"),
397       ]))
398
399
400 class _FakeLU:
401   def __init__(self, cfg=NotImplemented, proc=NotImplemented,
402                rpc=NotImplemented):
403     self.warning_log = []
404     self.info_log = []
405     self.cfg = cfg
406     self.proc = proc
407     self.rpc = rpc
408
409   def LogWarning(self, text, *args):
410     self.warning_log.append((text, args))
411
412   def LogInfo(self, text, *args):
413     self.info_log.append((text, args))
414
415
416 class TestLoadNodeEvacResult(unittest.TestCase):
417   def testSuccess(self):
418     for moved in [[], [
419       ("inst20153.example.com", "grp2", ["nodeA4509", "nodeB2912"]),
420       ]]:
421       for early_release in [False, True]:
422         for use_nodes in [False, True]:
423           jobs = [
424             [opcodes.OpInstanceReplaceDisks().__getstate__()],
425             [opcodes.OpInstanceMigrate().__getstate__()],
426             ]
427
428           alloc_result = (moved, [], jobs)
429           assert iallocator._NEVAC_RESULT(alloc_result)
430
431           lu = _FakeLU()
432           result = cmdlib._LoadNodeEvacResult(lu, alloc_result,
433                                               early_release, use_nodes)
434
435           if moved:
436             (_, (info_args, )) = lu.info_log.pop(0)
437             for (instname, instgroup, instnodes) in moved:
438               self.assertTrue(instname in info_args)
439               if use_nodes:
440                 for i in instnodes:
441                   self.assertTrue(i in info_args)
442               else:
443                 self.assertTrue(instgroup in info_args)
444
445           self.assertFalse(lu.info_log)
446           self.assertFalse(lu.warning_log)
447
448           for op in itertools.chain(*result):
449             if hasattr(op.__class__, "early_release"):
450               self.assertEqual(op.early_release, early_release)
451             else:
452               self.assertFalse(hasattr(op, "early_release"))
453
454   def testFailed(self):
455     alloc_result = ([], [
456       ("inst5191.example.com", "errormsg21178"),
457       ], [])
458     assert iallocator._NEVAC_RESULT(alloc_result)
459
460     lu = _FakeLU()
461     self.assertRaises(errors.OpExecError, cmdlib._LoadNodeEvacResult,
462                       lu, alloc_result, False, False)
463     self.assertFalse(lu.info_log)
464     (_, (args, )) = lu.warning_log.pop(0)
465     self.assertTrue("inst5191.example.com" in args)
466     self.assertTrue("errormsg21178" in args)
467     self.assertFalse(lu.warning_log)
468
469
470 class TestUpdateAndVerifySubDict(unittest.TestCase):
471   def setUp(self):
472     self.type_check = {
473         "a": constants.VTYPE_INT,
474         "b": constants.VTYPE_STRING,
475         "c": constants.VTYPE_BOOL,
476         "d": constants.VTYPE_STRING,
477         }
478
479   def test(self):
480     old_test = {
481       "foo": {
482         "d": "blubb",
483         "a": 321,
484         },
485       "baz": {
486         "a": 678,
487         "b": "678",
488         "c": True,
489         },
490       }
491     test = {
492       "foo": {
493         "a": 123,
494         "b": "123",
495         "c": True,
496         },
497       "bar": {
498         "a": 321,
499         "b": "321",
500         "c": False,
501         },
502       }
503
504     mv = {
505       "foo": {
506         "a": 123,
507         "b": "123",
508         "c": True,
509         "d": "blubb"
510         },
511       "bar": {
512         "a": 321,
513         "b": "321",
514         "c": False,
515         },
516       "baz": {
517         "a": 678,
518         "b": "678",
519         "c": True,
520         },
521       }
522
523     verified = common._UpdateAndVerifySubDict(old_test, test, self.type_check)
524     self.assertEqual(verified, mv)
525
526   def testWrong(self):
527     test = {
528       "foo": {
529         "a": "blubb",
530         "b": "123",
531         "c": True,
532         },
533       "bar": {
534         "a": 321,
535         "b": "321",
536         "c": False,
537         },
538       }
539
540     self.assertRaises(errors.TypeEnforcementError,
541                       common._UpdateAndVerifySubDict, {}, test,
542                       self.type_check)
543
544
545 class TestHvStateHelper(unittest.TestCase):
546   def testWithoutOpData(self):
547     self.assertEqual(common._MergeAndVerifyHvState(None, NotImplemented),
548                      None)
549
550   def testWithoutOldData(self):
551     new = {
552       constants.HT_XEN_PVM: {
553         constants.HVST_MEMORY_TOTAL: 4096,
554         },
555       }
556     self.assertEqual(common._MergeAndVerifyHvState(new, None), new)
557
558   def testWithWrongHv(self):
559     new = {
560       "i-dont-exist": {
561         constants.HVST_MEMORY_TOTAL: 4096,
562         },
563       }
564     self.assertRaises(errors.OpPrereqError, common._MergeAndVerifyHvState,
565                       new, None)
566
567 class TestDiskStateHelper(unittest.TestCase):
568   def testWithoutOpData(self):
569     self.assertEqual(common._MergeAndVerifyDiskState(None, NotImplemented),
570                      None)
571
572   def testWithoutOldData(self):
573     new = {
574       constants.LD_LV: {
575         "xenvg": {
576           constants.DS_DISK_RESERVED: 1024,
577           },
578         },
579       }
580     self.assertEqual(common._MergeAndVerifyDiskState(new, None), new)
581
582   def testWithWrongStorageType(self):
583     new = {
584       "i-dont-exist": {
585         "xenvg": {
586           constants.DS_DISK_RESERVED: 1024,
587           },
588         },
589       }
590     self.assertRaises(errors.OpPrereqError, common._MergeAndVerifyDiskState,
591                       new, None)
592
593
594 class TestComputeMinMaxSpec(unittest.TestCase):
595   def setUp(self):
596     self.ispecs = {
597       constants.ISPECS_MAX: {
598         constants.ISPEC_MEM_SIZE: 512,
599         constants.ISPEC_DISK_SIZE: 1024,
600         },
601       constants.ISPECS_MIN: {
602         constants.ISPEC_MEM_SIZE: 128,
603         constants.ISPEC_DISK_COUNT: 1,
604         },
605       }
606
607   def testNoneValue(self):
608     self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
609                                               self.ispecs, None) is None)
610
611   def testAutoValue(self):
612     self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
613                                               self.ispecs,
614                                               constants.VALUE_AUTO) is None)
615
616   def testNotDefined(self):
617     self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT, None,
618                                               self.ispecs, 3) is None)
619
620   def testNoMinDefined(self):
621     self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE, None,
622                                               self.ispecs, 128) is None)
623
624   def testNoMaxDefined(self):
625     self.assertTrue(common._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT,
626                                               None, self.ispecs, 16) is None)
627
628   def testOutOfRange(self):
629     for (name, val) in ((constants.ISPEC_MEM_SIZE, 64),
630                         (constants.ISPEC_MEM_SIZE, 768),
631                         (constants.ISPEC_DISK_SIZE, 4096),
632                         (constants.ISPEC_DISK_COUNT, 0)):
633       min_v = self.ispecs[constants.ISPECS_MIN].get(name, val)
634       max_v = self.ispecs[constants.ISPECS_MAX].get(name, val)
635       self.assertEqual(common._ComputeMinMaxSpec(name, None,
636                                                  self.ispecs, val),
637                        "%s value %s is not in range [%s, %s]" %
638                        (name, val,min_v, max_v))
639       self.assertEqual(common._ComputeMinMaxSpec(name, "1",
640                                                  self.ispecs, val),
641                        "%s/1 value %s is not in range [%s, %s]" %
642                        (name, val,min_v, max_v))
643
644   def test(self):
645     for (name, val) in ((constants.ISPEC_MEM_SIZE, 256),
646                         (constants.ISPEC_MEM_SIZE, 128),
647                         (constants.ISPEC_MEM_SIZE, 512),
648                         (constants.ISPEC_DISK_SIZE, 1024),
649                         (constants.ISPEC_DISK_SIZE, 0),
650                         (constants.ISPEC_DISK_COUNT, 1),
651                         (constants.ISPEC_DISK_COUNT, 5)):
652       self.assertTrue(common._ComputeMinMaxSpec(name, None, self.ispecs, val)
653                       is None)
654
655
656 def _ValidateComputeMinMaxSpec(name, *_):
657   assert name in constants.ISPECS_PARAMETERS
658   return None
659
660
661 def _NoDiskComputeMinMaxSpec(name, *_):
662   if name == constants.ISPEC_DISK_COUNT:
663     return name
664   else:
665     return None
666
667
668 class _SpecWrapper:
669   def __init__(self, spec):
670     self.spec = spec
671
672   def ComputeMinMaxSpec(self, *args):
673     return self.spec.pop(0)
674
675
676 class TestComputeIPolicySpecViolation(unittest.TestCase):
677   # Minimal policy accepted by _ComputeIPolicySpecViolation()
678   _MICRO_IPOL = {
679     constants.IPOLICY_DTS: [constants.DT_PLAIN, constants.DT_DISKLESS],
680     constants.ISPECS_MINMAX: [NotImplemented],
681     }
682
683   def test(self):
684     compute_fn = _ValidateComputeMinMaxSpec
685     ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
686                                               [1024], 1, constants.DT_PLAIN,
687                                               _compute_fn=compute_fn)
688     self.assertEqual(ret, [])
689
690   def testDiskFull(self):
691     compute_fn = _NoDiskComputeMinMaxSpec
692     ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
693                                               [1024], 1, constants.DT_PLAIN,
694                                               _compute_fn=compute_fn)
695     self.assertEqual(ret, [constants.ISPEC_DISK_COUNT])
696
697   def testDiskLess(self):
698     compute_fn = _NoDiskComputeMinMaxSpec
699     ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
700                                               [1024], 1, constants.DT_DISKLESS,
701                                               _compute_fn=compute_fn)
702     self.assertEqual(ret, [])
703
704   def testWrongTemplates(self):
705     compute_fn = _ValidateComputeMinMaxSpec
706     ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
707                                               [1024], 1, constants.DT_DRBD8,
708                                               _compute_fn=compute_fn)
709     self.assertEqual(len(ret), 1)
710     self.assertTrue("Disk template" in ret[0])
711
712   def testInvalidArguments(self):
713     self.assertRaises(AssertionError, common._ComputeIPolicySpecViolation,
714                       self._MICRO_IPOL, 1024, 1, 1, 1, [], 1,
715                       constants.DT_PLAIN,)
716
717   def testInvalidSpec(self):
718     spec = _SpecWrapper([None, False, "foo", None, "bar", None])
719     compute_fn = spec.ComputeMinMaxSpec
720     ret = common._ComputeIPolicySpecViolation(self._MICRO_IPOL, 1024, 1, 1, 1,
721                                               [1024], 1, constants.DT_PLAIN,
722                                               _compute_fn=compute_fn)
723     self.assertEqual(ret, ["foo", "bar"])
724     self.assertFalse(spec.spec)
725
726   def testWithIPolicy(self):
727     mem_size = 2048
728     cpu_count = 2
729     disk_count = 1
730     disk_sizes = [512]
731     nic_count = 1
732     spindle_use = 4
733     disk_template = "mytemplate"
734     ispec = {
735       constants.ISPEC_MEM_SIZE: mem_size,
736       constants.ISPEC_CPU_COUNT: cpu_count,
737       constants.ISPEC_DISK_COUNT: disk_count,
738       constants.ISPEC_DISK_SIZE: disk_sizes[0],
739       constants.ISPEC_NIC_COUNT: nic_count,
740       constants.ISPEC_SPINDLE_USE: spindle_use,
741       }
742     ipolicy1 = {
743       constants.ISPECS_MINMAX: [{
744         constants.ISPECS_MIN: ispec,
745         constants.ISPECS_MAX: ispec,
746         }],
747       constants.IPOLICY_DTS: [disk_template],
748       }
749     ispec_copy = copy.deepcopy(ispec)
750     ipolicy2 = {
751       constants.ISPECS_MINMAX: [
752         {
753           constants.ISPECS_MIN: ispec_copy,
754           constants.ISPECS_MAX: ispec_copy,
755           },
756         {
757           constants.ISPECS_MIN: ispec,
758           constants.ISPECS_MAX: ispec,
759           },
760         ],
761       constants.IPOLICY_DTS: [disk_template],
762       }
763     ipolicy3 = {
764       constants.ISPECS_MINMAX: [
765         {
766           constants.ISPECS_MIN: ispec,
767           constants.ISPECS_MAX: ispec,
768           },
769         {
770           constants.ISPECS_MIN: ispec_copy,
771           constants.ISPECS_MAX: ispec_copy,
772           },
773         ],
774       constants.IPOLICY_DTS: [disk_template],
775       }
776     def AssertComputeViolation(ipolicy, violations):
777       ret = common._ComputeIPolicySpecViolation(ipolicy, mem_size, cpu_count,
778                                                 disk_count, nic_count,
779                                                 disk_sizes, spindle_use,
780                                                 disk_template)
781       self.assertEqual(len(ret), violations)
782
783     AssertComputeViolation(ipolicy1, 0)
784     AssertComputeViolation(ipolicy2, 0)
785     AssertComputeViolation(ipolicy3, 0)
786     for par in constants.ISPECS_PARAMETERS:
787       ispec[par] += 1
788       AssertComputeViolation(ipolicy1, 1)
789       AssertComputeViolation(ipolicy2, 0)
790       AssertComputeViolation(ipolicy3, 0)
791       ispec[par] -= 2
792       AssertComputeViolation(ipolicy1, 1)
793       AssertComputeViolation(ipolicy2, 0)
794       AssertComputeViolation(ipolicy3, 0)
795       ispec[par] += 1 # Restore
796     ipolicy1[constants.IPOLICY_DTS] = ["another_template"]
797     AssertComputeViolation(ipolicy1, 1)
798
799
800 class _StubComputeIPolicySpecViolation:
801   def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
802                spindle_use, disk_template):
803     self.mem_size = mem_size
804     self.cpu_count = cpu_count
805     self.disk_count = disk_count
806     self.nic_count = nic_count
807     self.disk_sizes = disk_sizes
808     self.spindle_use = spindle_use
809     self.disk_template = disk_template
810
811   def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
812                spindle_use, disk_template):
813     assert self.mem_size == mem_size
814     assert self.cpu_count == cpu_count
815     assert self.disk_count == disk_count
816     assert self.nic_count == nic_count
817     assert self.disk_sizes == disk_sizes
818     assert self.spindle_use == spindle_use
819     assert self.disk_template == disk_template
820
821     return []
822
823
824 class _FakeConfigForComputeIPolicyInstanceViolation:
825   def __init__(self, be):
826     self.cluster = objects.Cluster(beparams={"default": be})
827
828   def GetClusterInfo(self):
829     return self.cluster
830
831
832 class TestComputeIPolicyInstanceViolation(unittest.TestCase):
833   def test(self):
834     beparams = {
835       constants.BE_MAXMEM: 2048,
836       constants.BE_VCPUS: 2,
837       constants.BE_SPINDLE_USE: 4,
838       }
839     disks = [objects.Disk(size=512)]
840     cfg = _FakeConfigForComputeIPolicyInstanceViolation(beparams)
841     instance = objects.Instance(beparams=beparams, disks=disks, nics=[],
842                                 disk_template=constants.DT_PLAIN)
843     stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 4,
844                                             constants.DT_PLAIN)
845     ret = common._ComputeIPolicyInstanceViolation(NotImplemented, instance,
846                                                   cfg, _compute_fn=stub)
847     self.assertEqual(ret, [])
848     instance2 = objects.Instance(beparams={}, disks=disks, nics=[],
849                                  disk_template=constants.DT_PLAIN)
850     ret = common._ComputeIPolicyInstanceViolation(NotImplemented, instance2,
851                                                   cfg, _compute_fn=stub)
852     self.assertEqual(ret, [])
853
854
855 class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
856   def test(self):
857     ispec = {
858       constants.ISPEC_MEM_SIZE: 2048,
859       constants.ISPEC_CPU_COUNT: 2,
860       constants.ISPEC_DISK_COUNT: 1,
861       constants.ISPEC_DISK_SIZE: [512],
862       constants.ISPEC_NIC_COUNT: 0,
863       constants.ISPEC_SPINDLE_USE: 1,
864       }
865     stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 1,
866                                             constants.DT_PLAIN)
867     ret = cmdlib._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
868                                                       constants.DT_PLAIN,
869                                                       _compute_fn=stub)
870     self.assertEqual(ret, [])
871
872
873 class _CallRecorder:
874   def __init__(self, return_value=None):
875     self.called = False
876     self.return_value = return_value
877
878   def __call__(self, *args):
879     self.called = True
880     return self.return_value
881
882
883 class TestComputeIPolicyNodeViolation(unittest.TestCase):
884   def setUp(self):
885     self.recorder = _CallRecorder(return_value=[])
886
887   def testSameGroup(self):
888     ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
889                                               "foo", "foo", NotImplemented,
890                                               _compute_fn=self.recorder)
891     self.assertFalse(self.recorder.called)
892     self.assertEqual(ret, [])
893
894   def testDifferentGroup(self):
895     ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
896                                               "foo", "bar", NotImplemented,
897                                               _compute_fn=self.recorder)
898     self.assertTrue(self.recorder.called)
899     self.assertEqual(ret, [])
900
901
902 class _FakeConfigForTargetNodeIPolicy:
903   def __init__(self, node_info=NotImplemented):
904     self._node_info = node_info
905
906   def GetNodeInfo(self, _):
907     return self._node_info
908
909
910 class TestCheckTargetNodeIPolicy(unittest.TestCase):
911   def setUp(self):
912     self.instance = objects.Instance(primary_node="blubb")
913     self.target_node = objects.Node(group="bar")
914     node_info = objects.Node(group="foo")
915     fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
916     self.lu = _FakeLU(cfg=fake_cfg)
917
918   def testNoViolation(self):
919     compute_recoder = _CallRecorder(return_value=[])
920     cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
921                                    self.target_node, NotImplemented,
922                                    _compute_fn=compute_recoder)
923     self.assertTrue(compute_recoder.called)
924     self.assertEqual(self.lu.warning_log, [])
925
926   def testNoIgnore(self):
927     compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
928     self.assertRaises(errors.OpPrereqError, cmdlib._CheckTargetNodeIPolicy,
929                       self.lu, NotImplemented, self.instance, self.target_node,
930                       NotImplemented, _compute_fn=compute_recoder)
931     self.assertTrue(compute_recoder.called)
932     self.assertEqual(self.lu.warning_log, [])
933
934   def testIgnoreViolation(self):
935     compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
936     cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
937                                    self.target_node, NotImplemented,
938                                    ignore=True, _compute_fn=compute_recoder)
939     self.assertTrue(compute_recoder.called)
940     msg = ("Instance does not meet target node group's (bar) instance policy:"
941            " mem_size not in range")
942     self.assertEqual(self.lu.warning_log, [(msg, ())])
943
944
945 class TestApplyContainerMods(unittest.TestCase):
946   def testEmptyContainer(self):
947     container = []
948     chgdesc = []
949     cmdlib.ApplyContainerMods("test", container, chgdesc, [], None, None, None)
950     self.assertEqual(container, [])
951     self.assertEqual(chgdesc, [])
952
953   def testAdd(self):
954     container = []
955     chgdesc = []
956     mods = cmdlib.PrepareContainerMods([
957       (constants.DDM_ADD, -1, "Hello"),
958       (constants.DDM_ADD, -1, "World"),
959       (constants.DDM_ADD, 0, "Start"),
960       (constants.DDM_ADD, -1, "End"),
961       ], None)
962     cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
963                               None, None, None)
964     self.assertEqual(container, ["Start", "Hello", "World", "End"])
965     self.assertEqual(chgdesc, [])
966
967     mods = cmdlib.PrepareContainerMods([
968       (constants.DDM_ADD, 0, "zero"),
969       (constants.DDM_ADD, 3, "Added"),
970       (constants.DDM_ADD, 5, "four"),
971       (constants.DDM_ADD, 7, "xyz"),
972       ], None)
973     cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
974                               None, None, None)
975     self.assertEqual(container,
976                      ["zero", "Start", "Hello", "Added", "World", "four",
977                       "End", "xyz"])
978     self.assertEqual(chgdesc, [])
979
980     for idx in [-2, len(container) + 1]:
981       mods = cmdlib.PrepareContainerMods([
982         (constants.DDM_ADD, idx, "error"),
983         ], None)
984       self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
985                         "test", container, None, mods, None, None, None)
986
987   def testRemoveError(self):
988     for idx in [0, 1, 2, 100, -1, -4]:
989       mods = cmdlib.PrepareContainerMods([
990         (constants.DDM_REMOVE, idx, None),
991         ], None)
992       self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
993                         "test", [], None, mods, None, None, None)
994
995     mods = cmdlib.PrepareContainerMods([
996       (constants.DDM_REMOVE, 0, object()),
997       ], None)
998     self.assertRaises(AssertionError, cmdlib.ApplyContainerMods,
999                       "test", [""], None, mods, None, None, None)
1000
1001   def testAddError(self):
1002     for idx in range(-100, -1) + [100]:
1003       mods = cmdlib.PrepareContainerMods([
1004         (constants.DDM_ADD, idx, None),
1005         ], None)
1006       self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
1007                         "test", [], None, mods, None, None, None)
1008
1009   def testRemove(self):
1010     container = ["item 1", "item 2"]
1011     mods = cmdlib.PrepareContainerMods([
1012       (constants.DDM_ADD, -1, "aaa"),
1013       (constants.DDM_REMOVE, -1, None),
1014       (constants.DDM_ADD, -1, "bbb"),
1015       ], None)
1016     chgdesc = []
1017     cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
1018                               None, None, None)
1019     self.assertEqual(container, ["item 1", "item 2", "bbb"])
1020     self.assertEqual(chgdesc, [
1021       ("test/2", "remove"),
1022       ])
1023
1024   def testModify(self):
1025     container = ["item 1", "item 2"]
1026     mods = cmdlib.PrepareContainerMods([
1027       (constants.DDM_MODIFY, -1, "a"),
1028       (constants.DDM_MODIFY, 0, "b"),
1029       (constants.DDM_MODIFY, 1, "c"),
1030       ], None)
1031     chgdesc = []
1032     cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
1033                               None, None, None)
1034     self.assertEqual(container, ["item 1", "item 2"])
1035     self.assertEqual(chgdesc, [])
1036
1037     for idx in [-2, len(container) + 1]:
1038       mods = cmdlib.PrepareContainerMods([
1039         (constants.DDM_MODIFY, idx, "error"),
1040         ], None)
1041       self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
1042                         "test", container, None, mods, None, None, None)
1043
1044   class _PrivateData:
1045     def __init__(self):
1046       self.data = None
1047
1048   @staticmethod
1049   def _CreateTestFn(idx, params, private):
1050     private.data = ("add", idx, params)
1051     return ((100 * idx, params), [
1052       ("test/%s" % idx, hex(idx)),
1053       ])
1054
1055   @staticmethod
1056   def _ModifyTestFn(idx, item, params, private):
1057     private.data = ("modify", idx, params)
1058     return [
1059       ("test/%s" % idx, "modify %s" % params),
1060       ]
1061
1062   @staticmethod
1063   def _RemoveTestFn(idx, item, private):
1064     private.data = ("remove", idx, item)
1065
1066   def testAddWithCreateFunction(self):
1067     container = []
1068     chgdesc = []
1069     mods = cmdlib.PrepareContainerMods([
1070       (constants.DDM_ADD, -1, "Hello"),
1071       (constants.DDM_ADD, -1, "World"),
1072       (constants.DDM_ADD, 0, "Start"),
1073       (constants.DDM_ADD, -1, "End"),
1074       (constants.DDM_REMOVE, 2, None),
1075       (constants.DDM_MODIFY, -1, "foobar"),
1076       (constants.DDM_REMOVE, 2, None),
1077       (constants.DDM_ADD, 1, "More"),
1078       ], self._PrivateData)
1079     cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
1080       self._CreateTestFn, self._ModifyTestFn, self._RemoveTestFn)
1081     self.assertEqual(container, [
1082       (000, "Start"),
1083       (100, "More"),
1084       (000, "Hello"),
1085       ])
1086     self.assertEqual(chgdesc, [
1087       ("test/0", "0x0"),
1088       ("test/1", "0x1"),
1089       ("test/0", "0x0"),
1090       ("test/3", "0x3"),
1091       ("test/2", "remove"),
1092       ("test/2", "modify foobar"),
1093       ("test/2", "remove"),
1094       ("test/1", "0x1")
1095       ])
1096     self.assertTrue(compat.all(op == private.data[0]
1097                                for (op, _, _, private) in mods))
1098     self.assertEqual([private.data for (op, _, _, private) in mods], [
1099       ("add", 0, "Hello"),
1100       ("add", 1, "World"),
1101       ("add", 0, "Start"),
1102       ("add", 3, "End"),
1103       ("remove", 2, (100, "World")),
1104       ("modify", 2, "foobar"),
1105       ("remove", 2, (300, "End")),
1106       ("add", 1, "More"),
1107       ])
1108
1109
1110 class _FakeConfigForGenDiskTemplate:
1111   def __init__(self):
1112     self._unique_id = itertools.count()
1113     self._drbd_minor = itertools.count(20)
1114     self._port = itertools.count(constants.FIRST_DRBD_PORT)
1115     self._secret = itertools.count()
1116
1117   def GetVGName(self):
1118     return "testvg"
1119
1120   def GenerateUniqueID(self, ec_id):
1121     return "ec%s-uq%s" % (ec_id, self._unique_id.next())
1122
1123   def AllocateDRBDMinor(self, nodes, instance):
1124     return [self._drbd_minor.next()
1125             for _ in nodes]
1126
1127   def AllocatePort(self):
1128     return self._port.next()
1129
1130   def GenerateDRBDSecret(self, ec_id):
1131     return "ec%s-secret%s" % (ec_id, self._secret.next())
1132
1133   def GetInstanceInfo(self, _):
1134     return "foobar"
1135
1136
1137 class _FakeProcForGenDiskTemplate:
1138   def GetECId(self):
1139     return 0
1140
1141
1142 class TestGenerateDiskTemplate(unittest.TestCase):
1143   def setUp(self):
1144     nodegroup = objects.NodeGroup(name="ng")
1145     nodegroup.UpgradeConfig()
1146
1147     cfg = _FakeConfigForGenDiskTemplate()
1148     proc = _FakeProcForGenDiskTemplate()
1149
1150     self.lu = _FakeLU(cfg=cfg, proc=proc)
1151     self.nodegroup = nodegroup
1152
1153   @staticmethod
1154   def GetDiskParams():
1155     return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1156
1157   def testWrongDiskTemplate(self):
1158     gdt = cmdlib._GenerateDiskTemplate
1159     disk_template = "##unknown##"
1160
1161     assert disk_template not in constants.DISK_TEMPLATES
1162
1163     self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
1164                       "inst26831.example.com", "node30113.example.com", [], [],
1165                       NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1166                       self.GetDiskParams())
1167
1168   def testDiskless(self):
1169     gdt = cmdlib._GenerateDiskTemplate
1170
1171     result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1172                  "node30113.example.com", [], [],
1173                  NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1174                  self.GetDiskParams())
1175     self.assertEqual(result, [])
1176
1177   def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1178                        file_storage_dir=NotImplemented,
1179                        file_driver=NotImplemented,
1180                        req_file_storage=NotImplemented,
1181                        req_shr_file_storage=NotImplemented):
1182     gdt = cmdlib._GenerateDiskTemplate
1183
1184     map(lambda params: utils.ForceDictType(params,
1185                                            constants.IDISK_PARAMS_TYPES),
1186         disk_info)
1187
1188     # Check if non-empty list of secondaries is rejected
1189     self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1190                       template, "inst25088.example.com",
1191                       "node185.example.com", ["node323.example.com"], [],
1192                       NotImplemented, NotImplemented, base_index,
1193                       self.lu.LogInfo, self.GetDiskParams(),
1194                       _req_file_storage=req_file_storage,
1195                       _req_shr_file_storage=req_shr_file_storage)
1196
1197     result = gdt(self.lu, template, "inst21662.example.com",
1198                  "node21741.example.com", [],
1199                  disk_info, file_storage_dir, file_driver, base_index,
1200                  self.lu.LogInfo, self.GetDiskParams(),
1201                  _req_file_storage=req_file_storage,
1202                  _req_shr_file_storage=req_shr_file_storage)
1203
1204     for (idx, disk) in enumerate(result):
1205       self.assertTrue(isinstance(disk, objects.Disk))
1206       self.assertEqual(disk.dev_type, exp_dev_type)
1207       self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1208       self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1209       self.assertTrue(disk.children is None)
1210
1211     self._CheckIvNames(result, base_index, base_index + len(disk_info))
1212     cmdlib._UpdateIvNames(base_index, result)
1213     self._CheckIvNames(result, base_index, base_index + len(disk_info))
1214
1215     return result
1216
1217   def _CheckIvNames(self, disks, base_index, end_index):
1218     self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1219                      ["disk/%s" % i for i in range(base_index, end_index)])
1220
1221   def testPlain(self):
1222     disk_info = [{
1223       constants.IDISK_SIZE: 1024,
1224       constants.IDISK_MODE: constants.DISK_RDWR,
1225       }, {
1226       constants.IDISK_SIZE: 4096,
1227       constants.IDISK_VG: "othervg",
1228       constants.IDISK_MODE: constants.DISK_RDWR,
1229       }]
1230
1231     result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1232                                    constants.LD_LV)
1233
1234     self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1235       ("testvg", "ec0-uq0.disk3"),
1236       ("othervg", "ec0-uq1.disk4"),
1237       ])
1238
1239   @staticmethod
1240   def _AllowFileStorage():
1241     pass
1242
1243   @staticmethod
1244   def _ForbidFileStorage():
1245     raise errors.OpPrereqError("Disallowed in test")
1246
1247   def testFile(self):
1248     self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1249                       constants.DT_FILE, [], 0, NotImplemented,
1250                       req_file_storage=self._ForbidFileStorage)
1251     self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1252                       constants.DT_SHARED_FILE, [], 0, NotImplemented,
1253                       req_shr_file_storage=self._ForbidFileStorage)
1254
1255     for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1256       disk_info = [{
1257         constants.IDISK_SIZE: 80 * 1024,
1258         constants.IDISK_MODE: constants.DISK_RDONLY,
1259         }, {
1260         constants.IDISK_SIZE: 4096,
1261         constants.IDISK_MODE: constants.DISK_RDWR,
1262         }, {
1263         constants.IDISK_SIZE: 6 * 1024,
1264         constants.IDISK_MODE: constants.DISK_RDWR,
1265         }]
1266
1267       result = self._TestTrivialDisk(disk_template, disk_info, 2,
1268         constants.LD_FILE, file_storage_dir="/tmp",
1269         file_driver=constants.FD_BLKTAP,
1270         req_file_storage=self._AllowFileStorage,
1271         req_shr_file_storage=self._AllowFileStorage)
1272
1273       self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1274         (constants.FD_BLKTAP, "/tmp/disk2"),
1275         (constants.FD_BLKTAP, "/tmp/disk3"),
1276         (constants.FD_BLKTAP, "/tmp/disk4"),
1277         ])
1278
1279   def testBlock(self):
1280     disk_info = [{
1281       constants.IDISK_SIZE: 8 * 1024,
1282       constants.IDISK_MODE: constants.DISK_RDWR,
1283       constants.IDISK_ADOPT: "/tmp/some/block/dev",
1284       }]
1285
1286     result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1287                                    constants.LD_BLOCKDEV)
1288
1289     self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1290       (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1291       ])
1292
1293   def testRbd(self):
1294     disk_info = [{
1295       constants.IDISK_SIZE: 8 * 1024,
1296       constants.IDISK_MODE: constants.DISK_RDONLY,
1297       }, {
1298       constants.IDISK_SIZE: 100 * 1024,
1299       constants.IDISK_MODE: constants.DISK_RDWR,
1300       }]
1301
1302     result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1303                                    constants.LD_RBD)
1304
1305     self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1306       ("rbd", "ec0-uq0.rbd.disk0"),
1307       ("rbd", "ec0-uq1.rbd.disk1"),
1308       ])
1309
1310   def testDrbd8(self):
1311     gdt = cmdlib._GenerateDiskTemplate
1312     drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1313     drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1314
1315     disk_info = [{
1316       constants.IDISK_SIZE: 1024,
1317       constants.IDISK_MODE: constants.DISK_RDWR,
1318       }, {
1319       constants.IDISK_SIZE: 100 * 1024,
1320       constants.IDISK_MODE: constants.DISK_RDONLY,
1321       constants.IDISK_METAVG: "metavg",
1322       }, {
1323       constants.IDISK_SIZE: 4096,
1324       constants.IDISK_MODE: constants.DISK_RDWR,
1325       constants.IDISK_VG: "vgxyz",
1326       },
1327       ]
1328
1329     exp_logical_ids = [[
1330       (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1331       (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1332       ], [
1333       (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1334       ("metavg", "ec0-uq1.disk1_meta"),
1335       ], [
1336       ("vgxyz", "ec0-uq2.disk2_data"),
1337       (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1338       ]]
1339
1340     assert len(exp_logical_ids) == len(disk_info)
1341
1342     map(lambda params: utils.ForceDictType(params,
1343                                            constants.IDISK_PARAMS_TYPES),
1344         disk_info)
1345
1346     # Check if empty list of secondaries is rejected
1347     self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1348                       "inst827.example.com", "node1334.example.com", [],
1349                       disk_info, NotImplemented, NotImplemented, 0,
1350                       self.lu.LogInfo, self.GetDiskParams())
1351
1352     result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1353                  "node1334.example.com", ["node12272.example.com"],
1354                  disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1355                  self.GetDiskParams())
1356
1357     for (idx, disk) in enumerate(result):
1358       self.assertTrue(isinstance(disk, objects.Disk))
1359       self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1360       self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1361       self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1362
1363       for child in disk.children:
1364         self.assertTrue(isinstance(disk, objects.Disk))
1365         self.assertEqual(child.dev_type, constants.LD_LV)
1366         self.assertTrue(child.children is None)
1367
1368       self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1369                        exp_logical_ids[idx])
1370
1371       self.assertEqual(len(disk.children), 2)
1372       self.assertEqual(disk.children[0].size, disk.size)
1373       self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1374
1375     self._CheckIvNames(result, 0, len(disk_info))
1376     cmdlib._UpdateIvNames(0, result)
1377     self._CheckIvNames(result, 0, len(disk_info))
1378
1379     self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1380       ("node1334.example.com", "node12272.example.com",
1381        constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1382       ("node1334.example.com", "node12272.example.com",
1383        constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1384       ("node1334.example.com", "node12272.example.com",
1385        constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1386       ])
1387
1388
1389 class _ConfigForDiskWipe:
1390   def __init__(self, exp_node):
1391     self._exp_node = exp_node
1392
1393   def SetDiskID(self, device, node):
1394     assert isinstance(device, objects.Disk)
1395     assert node == self._exp_node
1396
1397
1398 class _RpcForDiskWipe:
1399   def __init__(self, exp_node, pause_cb, wipe_cb):
1400     self._exp_node = exp_node
1401     self._pause_cb = pause_cb
1402     self._wipe_cb = wipe_cb
1403
1404   def call_blockdev_pause_resume_sync(self, node, disks, pause):
1405     assert node == self._exp_node
1406     return rpc.RpcResult(data=self._pause_cb(disks, pause))
1407
1408   def call_blockdev_wipe(self, node, bdev, offset, size):
1409     assert node == self._exp_node
1410     return rpc.RpcResult(data=self._wipe_cb(bdev, offset, size))
1411
1412
1413 class _DiskPauseTracker:
1414   def __init__(self):
1415     self.history = []
1416
1417   def __call__(self, (disks, instance), pause):
1418     assert not (set(disks) - set(instance.disks))
1419
1420     self.history.extend((i.logical_id, i.size, pause)
1421                         for i in disks)
1422
1423     return (True, [True] * len(disks))
1424
1425
1426 class _DiskWipeProgressTracker:
1427   def __init__(self, start_offset):
1428     self._start_offset = start_offset
1429     self.progress = {}
1430
1431   def __call__(self, (disk, _), offset, size):
1432     assert isinstance(offset, (long, int))
1433     assert isinstance(size, (long, int))
1434
1435     max_chunk_size = (disk.size / 100.0 * constants.MIN_WIPE_CHUNK_PERCENT)
1436
1437     assert offset >= self._start_offset
1438     assert (offset + size) <= disk.size
1439
1440     assert size > 0
1441     assert size <= constants.MAX_WIPE_CHUNK
1442     assert size <= max_chunk_size
1443
1444     assert offset == self._start_offset or disk.logical_id in self.progress
1445
1446     # Keep track of progress
1447     cur_progress = self.progress.setdefault(disk.logical_id, self._start_offset)
1448
1449     assert cur_progress == offset
1450
1451     # Record progress
1452     self.progress[disk.logical_id] += size
1453
1454     return (True, None)
1455
1456
1457 class TestWipeDisks(unittest.TestCase):
1458   def _FailingPauseCb(self, (disks, _), pause):
1459     self.assertEqual(len(disks), 3)
1460     self.assertTrue(pause)
1461     # Simulate an RPC error
1462     return (False, "error")
1463
1464   def testPauseFailure(self):
1465     node_name = "node1372.example.com"
1466
1467     lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, self._FailingPauseCb,
1468                                      NotImplemented),
1469                  cfg=_ConfigForDiskWipe(node_name))
1470
1471     disks = [
1472       objects.Disk(dev_type=constants.LD_LV),
1473       objects.Disk(dev_type=constants.LD_LV),
1474       objects.Disk(dev_type=constants.LD_LV),
1475       ]
1476
1477     instance = objects.Instance(name="inst21201",
1478                                 primary_node=node_name,
1479                                 disk_template=constants.DT_PLAIN,
1480                                 disks=disks)
1481
1482     self.assertRaises(errors.OpExecError, cmdlib._WipeDisks, lu, instance)
1483
1484   def _FailingWipeCb(self, (disk, _), offset, size):
1485     # This should only ever be called for the first disk
1486     self.assertEqual(disk.logical_id, "disk0")
1487     return (False, None)
1488
1489   def testFailingWipe(self):
1490     node_name = "node13445.example.com"
1491     pt = _DiskPauseTracker()
1492
1493     lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pt, self._FailingWipeCb),
1494                  cfg=_ConfigForDiskWipe(node_name))
1495
1496     disks = [
1497       objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1498                    size=100 * 1024),
1499       objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1500                    size=500 * 1024),
1501       objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=256),
1502       ]
1503
1504     instance = objects.Instance(name="inst562",
1505                                 primary_node=node_name,
1506                                 disk_template=constants.DT_PLAIN,
1507                                 disks=disks)
1508
1509     try:
1510       cmdlib._WipeDisks(lu, instance)
1511     except errors.OpExecError, err:
1512       self.assertTrue(str(err), "Could not wipe disk 0 at offset 0 ")
1513     else:
1514       self.fail("Did not raise exception")
1515
1516     # Check if all disks were paused and resumed
1517     self.assertEqual(pt.history, [
1518       ("disk0", 100 * 1024, True),
1519       ("disk1", 500 * 1024, True),
1520       ("disk2", 256, True),
1521       ("disk0", 100 * 1024, False),
1522       ("disk1", 500 * 1024, False),
1523       ("disk2", 256, False),
1524       ])
1525
1526   def _PrepareWipeTest(self, start_offset, disks):
1527     node_name = "node-with-offset%s.example.com" % start_offset
1528     pauset = _DiskPauseTracker()
1529     progresst = _DiskWipeProgressTracker(start_offset)
1530
1531     lu = _FakeLU(rpc=_RpcForDiskWipe(node_name, pauset, progresst),
1532                  cfg=_ConfigForDiskWipe(node_name))
1533
1534     instance = objects.Instance(name="inst3560",
1535                                 primary_node=node_name,
1536                                 disk_template=constants.DT_PLAIN,
1537                                 disks=disks)
1538
1539     return (lu, instance, pauset, progresst)
1540
1541   def testNormalWipe(self):
1542     disks = [
1543       objects.Disk(dev_type=constants.LD_LV, logical_id="disk0", size=1024),
1544       objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1545                    size=500 * 1024),
1546       objects.Disk(dev_type=constants.LD_LV, logical_id="disk2", size=128),
1547       objects.Disk(dev_type=constants.LD_LV, logical_id="disk3",
1548                    size=constants.MAX_WIPE_CHUNK),
1549       ]
1550
1551     (lu, instance, pauset, progresst) = self._PrepareWipeTest(0, disks)
1552
1553     cmdlib._WipeDisks(lu, instance)
1554
1555     self.assertEqual(pauset.history, [
1556       ("disk0", 1024, True),
1557       ("disk1", 500 * 1024, True),
1558       ("disk2", 128, True),
1559       ("disk3", constants.MAX_WIPE_CHUNK, True),
1560       ("disk0", 1024, False),
1561       ("disk1", 500 * 1024, False),
1562       ("disk2", 128, False),
1563       ("disk3", constants.MAX_WIPE_CHUNK, False),
1564       ])
1565
1566     # Ensure the complete disk has been wiped
1567     self.assertEqual(progresst.progress,
1568                      dict((i.logical_id, i.size) for i in disks))
1569
1570   def testWipeWithStartOffset(self):
1571     for start_offset in [0, 280, 8895, 1563204]:
1572       disks = [
1573         objects.Disk(dev_type=constants.LD_LV, logical_id="disk0",
1574                      size=128),
1575         objects.Disk(dev_type=constants.LD_LV, logical_id="disk1",
1576                      size=start_offset + (100 * 1024)),
1577         ]
1578
1579       (lu, instance, pauset, progresst) = \
1580         self._PrepareWipeTest(start_offset, disks)
1581
1582       # Test start offset with only one disk
1583       cmdlib._WipeDisks(lu, instance,
1584                         disks=[(1, disks[1], start_offset)])
1585
1586       # Only the second disk may have been paused and wiped
1587       self.assertEqual(pauset.history, [
1588         ("disk1", start_offset + (100 * 1024), True),
1589         ("disk1", start_offset + (100 * 1024), False),
1590         ])
1591       self.assertEqual(progresst.progress, {
1592         "disk1": disks[1].size,
1593         })
1594
1595
1596 class TestDiskSizeInBytesToMebibytes(unittest.TestCase):
1597   def testLessThanOneMebibyte(self):
1598     for i in [1, 2, 7, 512, 1000, 1023]:
1599       lu = _FakeLU()
1600       result = cmdlib._DiskSizeInBytesToMebibytes(lu, i)
1601       self.assertEqual(result, 1)
1602       self.assertEqual(len(lu.warning_log), 1)
1603       self.assertEqual(len(lu.warning_log[0]), 2)
1604       (_, (warnsize, )) = lu.warning_log[0]
1605       self.assertEqual(warnsize, (1024 * 1024) - i)
1606
1607   def testEven(self):
1608     for i in [1, 2, 7, 512, 1000, 1023]:
1609       lu = _FakeLU()
1610       result = cmdlib._DiskSizeInBytesToMebibytes(lu, i * 1024 * 1024)
1611       self.assertEqual(result, i)
1612       self.assertFalse(lu.warning_log)
1613
1614   def testLargeNumber(self):
1615     for i in [1, 2, 7, 512, 1000, 1023, 2724, 12420]:
1616       for j in [1, 2, 486, 326, 986, 1023]:
1617         lu = _FakeLU()
1618         size = (1024 * 1024 * i) + j
1619         result = cmdlib._DiskSizeInBytesToMebibytes(lu, size)
1620         self.assertEqual(result, i + 1, msg="Amount was not rounded up")
1621         self.assertEqual(len(lu.warning_log), 1)
1622         self.assertEqual(len(lu.warning_log[0]), 2)
1623         (_, (warnsize, )) = lu.warning_log[0]
1624         self.assertEqual(warnsize, (1024 * 1024) - j)
1625
1626
1627 class TestCopyLockList(unittest.TestCase):
1628   def test(self):
1629     self.assertEqual(cmdlib._CopyLockList([]), [])
1630     self.assertEqual(cmdlib._CopyLockList(None), None)
1631     self.assertEqual(cmdlib._CopyLockList(locking.ALL_SET), locking.ALL_SET)
1632
1633     names = ["foo", "bar"]
1634     output = cmdlib._CopyLockList(names)
1635     self.assertEqual(names, output)
1636     self.assertNotEqual(id(names), id(output), msg="List was not copied")
1637
1638
1639 class TestCheckOpportunisticLocking(unittest.TestCase):
1640   class OpTest(opcodes.OpCode):
1641     OP_PARAMS = [
1642       opcodes._POpportunisticLocking,
1643       opcodes._PIAllocFromDesc(""),
1644       ]
1645
1646   @classmethod
1647   def _MakeOp(cls, **kwargs):
1648     op = cls.OpTest(**kwargs)
1649     op.Validate(True)
1650     return op
1651
1652   def testMissingAttributes(self):
1653     self.assertRaises(AttributeError, cmdlib._CheckOpportunisticLocking,
1654                       object())
1655
1656   def testDefaults(self):
1657     op = self._MakeOp()
1658     cmdlib._CheckOpportunisticLocking(op)
1659
1660   def test(self):
1661     for iallocator in [None, "something", "other"]:
1662       for opplock in [False, True]:
1663         op = self._MakeOp(iallocator=iallocator, opportunistic_locking=opplock)
1664         if opplock and not iallocator:
1665           self.assertRaises(errors.OpPrereqError,
1666                             cmdlib._CheckOpportunisticLocking, op)
1667         else:
1668           cmdlib._CheckOpportunisticLocking(op)
1669
1670
1671 class _OpTestVerifyErrors(opcodes.OpCode):
1672   OP_PARAMS = [
1673     opcodes._PDebugSimulateErrors,
1674     opcodes._PErrorCodes,
1675     opcodes._PIgnoreErrors,
1676     ]
1677
1678
1679 class _LuTestVerifyErrors(cluster._VerifyErrors):
1680   def __init__(self, **kwargs):
1681     cluster._VerifyErrors.__init__(self)
1682     self.op = _OpTestVerifyErrors(**kwargs)
1683     self.op.Validate(True)
1684     self.msglist = []
1685     self._feedback_fn = self.msglist.append
1686     self.bad = False
1687
1688   def DispatchCallError(self, which, *args, **kwargs):
1689     if which:
1690       self._Error(*args, **kwargs)
1691     else:
1692       self._ErrorIf(True, *args, **kwargs)
1693
1694   def CallErrorIf(self, c, *args, **kwargs):
1695     self._ErrorIf(c, *args, **kwargs)
1696
1697
1698 class TestVerifyErrors(unittest.TestCase):
1699   # Fake cluster-verify error code structures; we use two arbitary real error
1700   # codes to pass validation of ignore_errors
1701   (_, _ERR1ID, _) = constants.CV_ECLUSTERCFG
1702   _NODESTR = "node"
1703   _NODENAME = "mynode"
1704   _ERR1CODE = (_NODESTR, _ERR1ID, "Error one")
1705   (_, _ERR2ID, _) = constants.CV_ECLUSTERCERT
1706   _INSTSTR = "instance"
1707   _INSTNAME = "myinstance"
1708   _ERR2CODE = (_INSTSTR, _ERR2ID, "Error two")
1709   # Arguments used to call _Error() or _ErrorIf()
1710   _ERR1ARGS = (_ERR1CODE, _NODENAME, "Error1 is %s", "an error")
1711   _ERR2ARGS = (_ERR2CODE, _INSTNAME, "Error2 has no argument")
1712   # Expected error messages
1713   _ERR1MSG = _ERR1ARGS[2] % _ERR1ARGS[3]
1714   _ERR2MSG = _ERR2ARGS[2]
1715
1716   def testNoError(self):
1717     lu = _LuTestVerifyErrors()
1718     lu.CallErrorIf(False, self._ERR1CODE, *self._ERR1ARGS)
1719     self.assertFalse(lu.bad)
1720     self.assertFalse(lu.msglist)
1721
1722   def _InitTest(self, **kwargs):
1723     self.lu1 = _LuTestVerifyErrors(**kwargs)
1724     self.lu2 = _LuTestVerifyErrors(**kwargs)
1725
1726   def _CallError(self, *args, **kwargs):
1727     # Check that _Error() and _ErrorIf() produce the same results
1728     self.lu1.DispatchCallError(True, *args, **kwargs)
1729     self.lu2.DispatchCallError(False, *args, **kwargs)
1730     self.assertEqual(self.lu1.bad, self.lu2.bad)
1731     self.assertEqual(self.lu1.msglist, self.lu2.msglist)
1732     # Test-specific checks are made on one LU
1733     return self.lu1
1734
1735   def _checkMsgCommon(self, logstr, errmsg, itype, item, warning):
1736     self.assertTrue(errmsg in logstr)
1737     if warning:
1738       self.assertTrue("WARNING" in logstr)
1739     else:
1740       self.assertTrue("ERROR" in logstr)
1741     self.assertTrue(itype in logstr)
1742     self.assertTrue(item in logstr)
1743
1744   def _checkMsg1(self, logstr, warning=False):
1745     self._checkMsgCommon(logstr, self._ERR1MSG, self._NODESTR,
1746                          self._NODENAME, warning)
1747
1748   def _checkMsg2(self, logstr, warning=False):
1749     self._checkMsgCommon(logstr, self._ERR2MSG, self._INSTSTR,
1750                          self._INSTNAME, warning)
1751
1752   def testPlain(self):
1753     self._InitTest()
1754     lu = self._CallError(*self._ERR1ARGS)
1755     self.assertTrue(lu.bad)
1756     self.assertEqual(len(lu.msglist), 1)
1757     self._checkMsg1(lu.msglist[0])
1758
1759   def testMultiple(self):
1760     self._InitTest()
1761     self._CallError(*self._ERR1ARGS)
1762     lu = self._CallError(*self._ERR2ARGS)
1763     self.assertTrue(lu.bad)
1764     self.assertEqual(len(lu.msglist), 2)
1765     self._checkMsg1(lu.msglist[0])
1766     self._checkMsg2(lu.msglist[1])
1767
1768   def testIgnore(self):
1769     self._InitTest(ignore_errors=[self._ERR1ID])
1770     lu = self._CallError(*self._ERR1ARGS)
1771     self.assertFalse(lu.bad)
1772     self.assertEqual(len(lu.msglist), 1)
1773     self._checkMsg1(lu.msglist[0], warning=True)
1774
1775   def testWarning(self):
1776     self._InitTest()
1777     lu = self._CallError(*self._ERR1ARGS,
1778                          code=_LuTestVerifyErrors.ETYPE_WARNING)
1779     self.assertFalse(lu.bad)
1780     self.assertEqual(len(lu.msglist), 1)
1781     self._checkMsg1(lu.msglist[0], warning=True)
1782
1783   def testWarning2(self):
1784     self._InitTest()
1785     self._CallError(*self._ERR1ARGS)
1786     lu = self._CallError(*self._ERR2ARGS,
1787                          code=_LuTestVerifyErrors.ETYPE_WARNING)
1788     self.assertTrue(lu.bad)
1789     self.assertEqual(len(lu.msglist), 2)
1790     self._checkMsg1(lu.msglist[0])
1791     self._checkMsg2(lu.msglist[1], warning=True)
1792
1793   def testDebugSimulate(self):
1794     lu = _LuTestVerifyErrors(debug_simulate_errors=True)
1795     lu.CallErrorIf(False, *self._ERR1ARGS)
1796     self.assertTrue(lu.bad)
1797     self.assertEqual(len(lu.msglist), 1)
1798     self._checkMsg1(lu.msglist[0])
1799
1800   def testErrCodes(self):
1801     self._InitTest(error_codes=True)
1802     lu = self._CallError(*self._ERR1ARGS)
1803     self.assertTrue(lu.bad)
1804     self.assertEqual(len(lu.msglist), 1)
1805     self._checkMsg1(lu.msglist[0])
1806     self.assertTrue(self._ERR1ID in lu.msglist[0])
1807
1808
1809 class TestGetUpdatedIPolicy(unittest.TestCase):
1810   """Tests for cmdlib._GetUpdatedIPolicy()"""
1811   _OLD_CLUSTER_POLICY = {
1812     constants.IPOLICY_VCPU_RATIO: 1.5,
1813     constants.ISPECS_MINMAX: [
1814       {
1815         constants.ISPECS_MIN: {
1816           constants.ISPEC_MEM_SIZE: 32768,
1817           constants.ISPEC_CPU_COUNT: 8,
1818           constants.ISPEC_DISK_COUNT: 1,
1819           constants.ISPEC_DISK_SIZE: 1024,
1820           constants.ISPEC_NIC_COUNT: 1,
1821           constants.ISPEC_SPINDLE_USE: 1,
1822           },
1823         constants.ISPECS_MAX: {
1824           constants.ISPEC_MEM_SIZE: 65536,
1825           constants.ISPEC_CPU_COUNT: 10,
1826           constants.ISPEC_DISK_COUNT: 5,
1827           constants.ISPEC_DISK_SIZE: 1024 * 1024,
1828           constants.ISPEC_NIC_COUNT: 3,
1829           constants.ISPEC_SPINDLE_USE: 12,
1830           },
1831         },
1832       constants.ISPECS_MINMAX_DEFAULTS,
1833       ],
1834     constants.ISPECS_STD: constants.IPOLICY_DEFAULTS[constants.ISPECS_STD],
1835     }
1836   _OLD_GROUP_POLICY = {
1837     constants.IPOLICY_SPINDLE_RATIO: 2.5,
1838     constants.ISPECS_MINMAX: [{
1839       constants.ISPECS_MIN: {
1840         constants.ISPEC_MEM_SIZE: 128,
1841         constants.ISPEC_CPU_COUNT: 1,
1842         constants.ISPEC_DISK_COUNT: 1,
1843         constants.ISPEC_DISK_SIZE: 1024,
1844         constants.ISPEC_NIC_COUNT: 1,
1845         constants.ISPEC_SPINDLE_USE: 1,
1846         },
1847       constants.ISPECS_MAX: {
1848         constants.ISPEC_MEM_SIZE: 32768,
1849         constants.ISPEC_CPU_COUNT: 8,
1850         constants.ISPEC_DISK_COUNT: 5,
1851         constants.ISPEC_DISK_SIZE: 1024 * 1024,
1852         constants.ISPEC_NIC_COUNT: 3,
1853         constants.ISPEC_SPINDLE_USE: 12,
1854         },
1855       }],
1856     }
1857
1858   def _TestSetSpecs(self, old_policy, isgroup):
1859     diff_minmax = [{
1860       constants.ISPECS_MIN: {
1861         constants.ISPEC_MEM_SIZE: 64,
1862         constants.ISPEC_CPU_COUNT: 1,
1863         constants.ISPEC_DISK_COUNT: 2,
1864         constants.ISPEC_DISK_SIZE: 64,
1865         constants.ISPEC_NIC_COUNT: 1,
1866         constants.ISPEC_SPINDLE_USE: 1,
1867         },
1868       constants.ISPECS_MAX: {
1869         constants.ISPEC_MEM_SIZE: 16384,
1870         constants.ISPEC_CPU_COUNT: 10,
1871         constants.ISPEC_DISK_COUNT: 12,
1872         constants.ISPEC_DISK_SIZE: 1024,
1873         constants.ISPEC_NIC_COUNT: 9,
1874         constants.ISPEC_SPINDLE_USE: 18,
1875         },
1876       }]
1877     diff_std = {
1878         constants.ISPEC_DISK_COUNT: 10,
1879         constants.ISPEC_DISK_SIZE: 512,
1880         }
1881     diff_policy = {
1882       constants.ISPECS_MINMAX: diff_minmax
1883       }
1884     if not isgroup:
1885       diff_policy[constants.ISPECS_STD] = diff_std
1886     new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1887                                            group_policy=isgroup)
1888
1889     self.assertTrue(constants.ISPECS_MINMAX in new_policy)
1890     self.assertEqual(new_policy[constants.ISPECS_MINMAX], diff_minmax)
1891     for key in old_policy:
1892       if not key in diff_policy:
1893         self.assertTrue(key in new_policy)
1894         self.assertEqual(new_policy[key], old_policy[key])
1895
1896     if not isgroup:
1897       new_std = new_policy[constants.ISPECS_STD]
1898       for key in diff_std:
1899         self.assertTrue(key in new_std)
1900         self.assertEqual(new_std[key], diff_std[key])
1901       old_std = old_policy.get(constants.ISPECS_STD, {})
1902       for key in old_std:
1903         self.assertTrue(key in new_std)
1904         if key not in diff_std:
1905           self.assertEqual(new_std[key], old_std[key])
1906
1907   def _TestSet(self, old_policy, diff_policy, isgroup):
1908     new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1909                                            group_policy=isgroup)
1910     for key in diff_policy:
1911       self.assertTrue(key in new_policy)
1912       self.assertEqual(new_policy[key], diff_policy[key])
1913     for key in old_policy:
1914       if not key in diff_policy:
1915         self.assertTrue(key in new_policy)
1916         self.assertEqual(new_policy[key], old_policy[key])
1917
1918   def testSet(self):
1919     diff_policy = {
1920       constants.IPOLICY_VCPU_RATIO: 3,
1921       constants.IPOLICY_DTS: [constants.DT_FILE],
1922       }
1923     self._TestSet(self._OLD_GROUP_POLICY, diff_policy, True)
1924     self._TestSetSpecs(self._OLD_GROUP_POLICY, True)
1925     self._TestSet({}, diff_policy, True)
1926     self._TestSetSpecs({}, True)
1927     self._TestSet(self._OLD_CLUSTER_POLICY, diff_policy, False)
1928     self._TestSetSpecs(self._OLD_CLUSTER_POLICY, False)
1929
1930   def testUnset(self):
1931     old_policy = self._OLD_GROUP_POLICY
1932     diff_policy = {
1933       constants.IPOLICY_SPINDLE_RATIO: constants.VALUE_DEFAULT,
1934       }
1935     new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1936                                            group_policy=True)
1937     for key in diff_policy:
1938       self.assertFalse(key in new_policy)
1939     for key in old_policy:
1940       if not key in diff_policy:
1941         self.assertTrue(key in new_policy)
1942         self.assertEqual(new_policy[key], old_policy[key])
1943
1944     self.assertRaises(errors.OpPrereqError, common._GetUpdatedIPolicy,
1945                       old_policy, diff_policy, group_policy=False)
1946
1947   def testUnsetEmpty(self):
1948     old_policy = {}
1949     for key in constants.IPOLICY_ALL_KEYS:
1950       diff_policy = {
1951         key: constants.VALUE_DEFAULT,
1952         }
1953     new_policy = common._GetUpdatedIPolicy(old_policy, diff_policy,
1954                                            group_policy=True)
1955     self.assertEqual(new_policy, old_policy)
1956
1957   def _TestInvalidKeys(self, old_policy, isgroup):
1958     INVALID_KEY = "this_key_shouldnt_be_allowed"
1959     INVALID_DICT = {
1960       INVALID_KEY: 3,
1961       }
1962     invalid_policy = INVALID_DICT
1963     self.assertRaises(errors.OpPrereqError, common._GetUpdatedIPolicy,
1964                       old_policy, invalid_policy, group_policy=isgroup)
1965     invalid_ispecs = {
1966       constants.ISPECS_MINMAX: [INVALID_DICT],
1967       }
1968     self.assertRaises(errors.TypeEnforcementError, common._GetUpdatedIPolicy,
1969                       old_policy, invalid_ispecs, group_policy=isgroup)
1970     if isgroup:
1971       invalid_for_group = {
1972         constants.ISPECS_STD: constants.IPOLICY_DEFAULTS[constants.ISPECS_STD],
1973         }
1974       self.assertRaises(errors.OpPrereqError, common._GetUpdatedIPolicy,
1975                         old_policy, invalid_for_group, group_policy=isgroup)
1976     good_ispecs = self._OLD_CLUSTER_POLICY[constants.ISPECS_MINMAX]
1977     invalid_ispecs = copy.deepcopy(good_ispecs)
1978     invalid_policy = {
1979       constants.ISPECS_MINMAX: invalid_ispecs,
1980       }
1981     for minmax in invalid_ispecs:
1982       for key in constants.ISPECS_MINMAX_KEYS:
1983         ispec = minmax[key]
1984         ispec[INVALID_KEY] = None
1985         self.assertRaises(errors.TypeEnforcementError,
1986                           common._GetUpdatedIPolicy, old_policy,
1987                           invalid_policy, group_policy=isgroup)
1988         del ispec[INVALID_KEY]
1989         for par in constants.ISPECS_PARAMETERS:
1990           oldv = ispec[par]
1991           ispec[par] = "this_is_not_good"
1992           self.assertRaises(errors.TypeEnforcementError,
1993                             common._GetUpdatedIPolicy,
1994                             old_policy, invalid_policy, group_policy=isgroup)
1995           ispec[par] = oldv
1996     # This is to make sure that no two errors were present during the tests
1997     common._GetUpdatedIPolicy(old_policy, invalid_policy,
1998                               group_policy=isgroup)
1999
2000   def testInvalidKeys(self):
2001     self._TestInvalidKeys(self._OLD_GROUP_POLICY, True)
2002     self._TestInvalidKeys(self._OLD_CLUSTER_POLICY, False)
2003
2004   def testInvalidValues(self):
2005     for par in (constants.IPOLICY_PARAMETERS |
2006                 frozenset([constants.IPOLICY_DTS])):
2007       bad_policy = {
2008         par: "invalid_value",
2009         }
2010       self.assertRaises(errors.OpPrereqError, common._GetUpdatedIPolicy, {},
2011                         bad_policy, group_policy=True)
2012
2013 if __name__ == "__main__":
2014   testutils.GanetiTestProgram()