Fix shelltests for ganeti-*
[ganeti-local] / test / ganeti.cmdlib_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2008, 2011, 2012 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 import opcodes
38 from ganeti import errors
39 from ganeti import utils
40 from ganeti import luxi
41 from ganeti import ht
42 from ganeti import objects
43 from ganeti import compat
44 from ganeti import rpc
45 from ganeti import pathutils
46 from ganeti.masterd import iallocator
47 from ganeti.hypervisor import hv_xen
48
49 import testutils
50 import mocks
51
52
53 class TestCertVerification(testutils.GanetiTestCase):
54   def setUp(self):
55     testutils.GanetiTestCase.setUp(self)
56
57     self.tmpdir = tempfile.mkdtemp()
58
59   def tearDown(self):
60     shutil.rmtree(self.tmpdir)
61
62   def testVerifyCertificate(self):
63     cmdlib._VerifyCertificate(self._TestDataFilename("cert1.pem"))
64
65     nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
66
67     (errcode, msg) = cmdlib._VerifyCertificate(nonexist_filename)
68     self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
69
70     # Try to load non-certificate file
71     invalid_cert = self._TestDataFilename("bdev-net.txt")
72     (errcode, msg) = cmdlib._VerifyCertificate(invalid_cert)
73     self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
74
75
76 class TestOpcodeParams(testutils.GanetiTestCase):
77   def testParamsStructures(self):
78     for op in sorted(mcpu.Processor.DISPATCH_TABLE):
79       lu = mcpu.Processor.DISPATCH_TABLE[op]
80       lu_name = lu.__name__
81       self.failIf(hasattr(lu, "_OP_REQP"),
82                   msg=("LU '%s' has old-style _OP_REQP" % lu_name))
83       self.failIf(hasattr(lu, "_OP_DEFS"),
84                   msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
85       self.failIf(hasattr(lu, "_OP_PARAMS"),
86                   msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
87
88
89 class TestIAllocatorChecks(testutils.GanetiTestCase):
90   def testFunction(self):
91     class TestLU(object):
92       def __init__(self, opcode):
93         self.cfg = mocks.FakeConfig()
94         self.op = opcode
95
96     class OpTest(opcodes.OpCode):
97        OP_PARAMS = [
98         ("iallocator", None, ht.NoType, None),
99         ("node", None, ht.NoType, None),
100         ]
101
102     default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
103     other_iallocator = default_iallocator + "_not"
104
105     op = OpTest()
106     lu = TestLU(op)
107
108     c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
109
110     # Neither node nor iallocator given
111     op.iallocator = None
112     op.node = None
113     c_i()
114     self.assertEqual(lu.op.iallocator, default_iallocator)
115     self.assertEqual(lu.op.node, None)
116
117     # Both, iallocator and node given
118     op.iallocator = "test"
119     op.node = "test"
120     self.assertRaises(errors.OpPrereqError, c_i)
121
122     # Only iallocator given
123     op.iallocator = other_iallocator
124     op.node = None
125     c_i()
126     self.assertEqual(lu.op.iallocator, other_iallocator)
127     self.assertEqual(lu.op.node, None)
128
129     # Only node given
130     op.iallocator = None
131     op.node = "node"
132     c_i()
133     self.assertEqual(lu.op.iallocator, None)
134     self.assertEqual(lu.op.node, "node")
135
136     # No node, iallocator or default iallocator
137     op.iallocator = None
138     op.node = None
139     lu.cfg.GetDefaultIAllocator = lambda: None
140     self.assertRaises(errors.OpPrereqError, c_i)
141
142
143 class TestLUTestJqueue(unittest.TestCase):
144   def test(self):
145     self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
146                  (luxi.WFJC_TIMEOUT * 0.75),
147                  msg=("Client timeout too high, might not notice bugs"
148                       " in WaitForJobChange"))
149
150
151 class TestLUQuery(unittest.TestCase):
152   def test(self):
153     self.assertEqual(sorted(cmdlib._QUERY_IMPL.keys()),
154                      sorted(constants.QR_VIA_OP))
155
156     assert constants.QR_NODE in constants.QR_VIA_OP
157     assert constants.QR_INSTANCE in constants.QR_VIA_OP
158
159     for i in constants.QR_VIA_OP:
160       self.assert_(cmdlib._GetQueryImplementation(i))
161
162     self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation, "")
163     self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation,
164                       "xyz")
165
166
167 class TestLUGroupAssignNodes(unittest.TestCase):
168
169   def testCheckAssignmentForSplitInstances(self):
170     node_data = dict((name, objects.Node(name=name, group=group))
171                      for (name, group) in [("n1a", "g1"), ("n1b", "g1"),
172                                            ("n2a", "g2"), ("n2b", "g2"),
173                                            ("n3a", "g3"), ("n3b", "g3"),
174                                            ("n3c", "g3"),
175                                            ])
176
177     def Instance(name, pnode, snode):
178       if snode is None:
179         disks = []
180         disk_template = constants.DT_DISKLESS
181       else:
182         disks = [objects.Disk(dev_type=constants.LD_DRBD8,
183                               logical_id=[pnode, snode, 1, 17, 17])]
184         disk_template = constants.DT_DRBD8
185
186       return objects.Instance(name=name, primary_node=pnode, disks=disks,
187                               disk_template=disk_template)
188
189     instance_data = dict((name, Instance(name, pnode, snode))
190                          for name, pnode, snode in [("inst1a", "n1a", "n1b"),
191                                                     ("inst1b", "n1b", "n1a"),
192                                                     ("inst2a", "n2a", "n2b"),
193                                                     ("inst3a", "n3a", None),
194                                                     ("inst3b", "n3b", "n1b"),
195                                                     ("inst3c", "n3b", "n2b"),
196                                                     ])
197
198     # Test first with the existing state.
199     (new, prev) = \
200       cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
201                                                                  node_data,
202                                                                  instance_data)
203
204     self.assertEqual([], new)
205     self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
206
207     # And now some changes.
208     (new, prev) = \
209       cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
210                                                                    "g3")],
211                                                                  node_data,
212                                                                  instance_data)
213
214     self.assertEqual(set(["inst1a", "inst1b"]), set(new))
215     self.assertEqual(set(["inst3c"]), set(prev))
216
217
218 class TestClusterVerifySsh(unittest.TestCase):
219   def testMultipleGroups(self):
220     fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
221     mygroupnodes = [
222       objects.Node(name="node20", group="my", offline=False),
223       objects.Node(name="node21", group="my", offline=False),
224       objects.Node(name="node22", group="my", offline=False),
225       objects.Node(name="node23", group="my", offline=False),
226       objects.Node(name="node24", group="my", offline=False),
227       objects.Node(name="node25", group="my", offline=False),
228       objects.Node(name="node26", group="my", offline=True),
229       ]
230     nodes = [
231       objects.Node(name="node1", group="g1", offline=True),
232       objects.Node(name="node2", group="g1", offline=False),
233       objects.Node(name="node3", group="g1", offline=False),
234       objects.Node(name="node4", group="g1", offline=True),
235       objects.Node(name="node5", group="g1", offline=False),
236       objects.Node(name="node10", group="xyz", offline=False),
237       objects.Node(name="node11", group="xyz", offline=False),
238       objects.Node(name="node40", group="alloff", offline=True),
239       objects.Node(name="node41", group="alloff", offline=True),
240       objects.Node(name="node50", group="aaa", offline=False),
241       ] + mygroupnodes
242     assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
243
244     (online, perhost) = fn(mygroupnodes, "my", nodes)
245     self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
246     self.assertEqual(set(perhost.keys()), set(online))
247
248     self.assertEqual(perhost, {
249       "node20": ["node10", "node2", "node50"],
250       "node21": ["node11", "node3", "node50"],
251       "node22": ["node10", "node5", "node50"],
252       "node23": ["node11", "node2", "node50"],
253       "node24": ["node10", "node3", "node50"],
254       "node25": ["node11", "node5", "node50"],
255       })
256
257   def testSingleGroup(self):
258     fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
259     nodes = [
260       objects.Node(name="node1", group="default", offline=True),
261       objects.Node(name="node2", group="default", offline=False),
262       objects.Node(name="node3", group="default", offline=False),
263       objects.Node(name="node4", group="default", offline=True),
264       ]
265     assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
266
267     (online, perhost) = fn(nodes, "default", nodes)
268     self.assertEqual(online, ["node2", "node3"])
269     self.assertEqual(set(perhost.keys()), set(online))
270
271     self.assertEqual(perhost, {
272       "node2": [],
273       "node3": [],
274       })
275
276
277 class TestClusterVerifyFiles(unittest.TestCase):
278   @staticmethod
279   def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
280     assert ((ecode == constants.CV_ENODEFILECHECK and
281              ht.TNonEmptyString(item)) or
282             (ecode == constants.CV_ECLUSTERFILECHECK and
283              item is None))
284
285     if args:
286       msg = msg % args
287
288     if cond:
289       errors.append((item, msg))
290
291   _VerifyFiles = cmdlib.LUClusterVerifyGroup._VerifyFiles
292
293   def test(self):
294     errors = []
295     master_name = "master.example.com"
296     nodeinfo = [
297       objects.Node(name=master_name, offline=False, vm_capable=True),
298       objects.Node(name="node2.example.com", offline=False, vm_capable=True),
299       objects.Node(name="node3.example.com", master_candidate=True,
300                    vm_capable=False),
301       objects.Node(name="node4.example.com", offline=False, vm_capable=True),
302       objects.Node(name="nodata.example.com", offline=False, vm_capable=True),
303       objects.Node(name="offline.example.com", offline=True),
304       ]
305     cluster = objects.Cluster(modify_etc_hosts=True,
306                               enabled_hypervisors=[constants.HT_XEN_HVM])
307     files_all = set([
308       pathutils.CLUSTER_DOMAIN_SECRET_FILE,
309       pathutils.RAPI_CERT_FILE,
310       pathutils.RAPI_USERS_FILE,
311       ])
312     files_opt = set([
313       pathutils.RAPI_USERS_FILE,
314       hv_xen.XL_CONFIG_FILE,
315       pathutils.VNC_PASSWORD_FILE,
316       ])
317     files_mc = set([
318       pathutils.CLUSTER_CONF_FILE,
319       ])
320     files_vm = set([
321       hv_xen.XEND_CONFIG_FILE,
322       hv_xen.XL_CONFIG_FILE,
323       pathutils.VNC_PASSWORD_FILE,
324       ])
325     nvinfo = {
326       master_name: rpc.RpcResult(data=(True, {
327         constants.NV_FILELIST: {
328           pathutils.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
329           pathutils.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
330           pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
331           hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
332           hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
333         }})),
334       "node2.example.com": rpc.RpcResult(data=(True, {
335         constants.NV_FILELIST: {
336           pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
337           hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
338           }
339         })),
340       "node3.example.com": rpc.RpcResult(data=(True, {
341         constants.NV_FILELIST: {
342           pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
343           pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
344           }
345         })),
346       "node4.example.com": rpc.RpcResult(data=(True, {
347         constants.NV_FILELIST: {
348           pathutils.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
349           pathutils.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
350           pathutils.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
351           pathutils.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
352           hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
353           }
354         })),
355       "nodata.example.com": rpc.RpcResult(data=(True, {})),
356       "offline.example.com": rpc.RpcResult(offline=True),
357       }
358     assert set(nvinfo.keys()) == set(map(operator.attrgetter("name"), nodeinfo))
359
360     self._VerifyFiles(compat.partial(self._FakeErrorIf, errors), nodeinfo,
361                       master_name, nvinfo,
362                       (files_all, files_opt, files_mc, files_vm))
363     self.assertEqual(sorted(errors), sorted([
364       (None, ("File %s found with 2 different checksums (variant 1 on"
365               " node2.example.com, node3.example.com, node4.example.com;"
366               " variant 2 on master.example.com)" % pathutils.RAPI_CERT_FILE)),
367       (None, ("File %s is missing from node(s) node2.example.com" %
368               pathutils.CLUSTER_DOMAIN_SECRET_FILE)),
369       (None, ("File %s should not exist on node(s) node4.example.com" %
370               pathutils.CLUSTER_CONF_FILE)),
371       (None, ("File %s is missing from node(s) node4.example.com" %
372               hv_xen.XEND_CONFIG_FILE)),
373       (None, ("File %s is missing from node(s) node3.example.com" %
374               pathutils.CLUSTER_CONF_FILE)),
375       (None, ("File %s found with 2 different checksums (variant 1 on"
376               " master.example.com; variant 2 on node4.example.com)" %
377               pathutils.CLUSTER_CONF_FILE)),
378       (None, ("File %s is optional, but it must exist on all or no nodes (not"
379               " found on master.example.com, node2.example.com,"
380               " node3.example.com)" % pathutils.RAPI_USERS_FILE)),
381       (None, ("File %s is optional, but it must exist on all or no nodes (not"
382               " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
383       ("nodata.example.com", "Node did not return file checksum data"),
384       ]))
385
386
387 class _FakeLU:
388   def __init__(self, cfg=NotImplemented, proc=NotImplemented):
389     self.warning_log = []
390     self.info_log = []
391     self.cfg = cfg
392     self.proc = proc
393
394   def LogWarning(self, text, *args):
395     self.warning_log.append((text, args))
396
397   def LogInfo(self, text, *args):
398     self.info_log.append((text, args))
399
400
401 class TestLoadNodeEvacResult(unittest.TestCase):
402   def testSuccess(self):
403     for moved in [[], [
404       ("inst20153.example.com", "grp2", ["nodeA4509", "nodeB2912"]),
405       ]]:
406       for early_release in [False, True]:
407         for use_nodes in [False, True]:
408           jobs = [
409             [opcodes.OpInstanceReplaceDisks().__getstate__()],
410             [opcodes.OpInstanceMigrate().__getstate__()],
411             ]
412
413           alloc_result = (moved, [], jobs)
414           assert iallocator._NEVAC_RESULT(alloc_result)
415
416           lu = _FakeLU()
417           result = cmdlib._LoadNodeEvacResult(lu, alloc_result,
418                                               early_release, use_nodes)
419
420           if moved:
421             (_, (info_args, )) = lu.info_log.pop(0)
422             for (instname, instgroup, instnodes) in moved:
423               self.assertTrue(instname in info_args)
424               if use_nodes:
425                 for i in instnodes:
426                   self.assertTrue(i in info_args)
427               else:
428                 self.assertTrue(instgroup in info_args)
429
430           self.assertFalse(lu.info_log)
431           self.assertFalse(lu.warning_log)
432
433           for op in itertools.chain(*result):
434             if hasattr(op.__class__, "early_release"):
435               self.assertEqual(op.early_release, early_release)
436             else:
437               self.assertFalse(hasattr(op, "early_release"))
438
439   def testFailed(self):
440     alloc_result = ([], [
441       ("inst5191.example.com", "errormsg21178"),
442       ], [])
443     assert iallocator._NEVAC_RESULT(alloc_result)
444
445     lu = _FakeLU()
446     self.assertRaises(errors.OpExecError, cmdlib._LoadNodeEvacResult,
447                       lu, alloc_result, False, False)
448     self.assertFalse(lu.info_log)
449     (_, (args, )) = lu.warning_log.pop(0)
450     self.assertTrue("inst5191.example.com" in args)
451     self.assertTrue("errormsg21178" in args)
452     self.assertFalse(lu.warning_log)
453
454
455 class TestUpdateAndVerifySubDict(unittest.TestCase):
456   def setUp(self):
457     self.type_check = {
458         "a": constants.VTYPE_INT,
459         "b": constants.VTYPE_STRING,
460         "c": constants.VTYPE_BOOL,
461         "d": constants.VTYPE_STRING,
462         }
463
464   def test(self):
465     old_test = {
466       "foo": {
467         "d": "blubb",
468         "a": 321,
469         },
470       "baz": {
471         "a": 678,
472         "b": "678",
473         "c": True,
474         },
475       }
476     test = {
477       "foo": {
478         "a": 123,
479         "b": "123",
480         "c": True,
481         },
482       "bar": {
483         "a": 321,
484         "b": "321",
485         "c": False,
486         },
487       }
488
489     mv = {
490       "foo": {
491         "a": 123,
492         "b": "123",
493         "c": True,
494         "d": "blubb"
495         },
496       "bar": {
497         "a": 321,
498         "b": "321",
499         "c": False,
500         },
501       "baz": {
502         "a": 678,
503         "b": "678",
504         "c": True,
505         },
506       }
507
508     verified = cmdlib._UpdateAndVerifySubDict(old_test, test, self.type_check)
509     self.assertEqual(verified, mv)
510
511   def testWrong(self):
512     test = {
513       "foo": {
514         "a": "blubb",
515         "b": "123",
516         "c": True,
517         },
518       "bar": {
519         "a": 321,
520         "b": "321",
521         "c": False,
522         },
523       }
524
525     self.assertRaises(errors.TypeEnforcementError,
526                       cmdlib._UpdateAndVerifySubDict, {}, test, self.type_check)
527
528
529 class TestHvStateHelper(unittest.TestCase):
530   def testWithoutOpData(self):
531     self.assertEqual(cmdlib._MergeAndVerifyHvState(None, NotImplemented), None)
532
533   def testWithoutOldData(self):
534     new = {
535       constants.HT_XEN_PVM: {
536         constants.HVST_MEMORY_TOTAL: 4096,
537         },
538       }
539     self.assertEqual(cmdlib._MergeAndVerifyHvState(new, None), new)
540
541   def testWithWrongHv(self):
542     new = {
543       "i-dont-exist": {
544         constants.HVST_MEMORY_TOTAL: 4096,
545         },
546       }
547     self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyHvState, new,
548                       None)
549
550 class TestDiskStateHelper(unittest.TestCase):
551   def testWithoutOpData(self):
552     self.assertEqual(cmdlib._MergeAndVerifyDiskState(None, NotImplemented),
553                      None)
554
555   def testWithoutOldData(self):
556     new = {
557       constants.LD_LV: {
558         "xenvg": {
559           constants.DS_DISK_RESERVED: 1024,
560           },
561         },
562       }
563     self.assertEqual(cmdlib._MergeAndVerifyDiskState(new, None), new)
564
565   def testWithWrongStorageType(self):
566     new = {
567       "i-dont-exist": {
568         "xenvg": {
569           constants.DS_DISK_RESERVED: 1024,
570           },
571         },
572       }
573     self.assertRaises(errors.OpPrereqError, cmdlib._MergeAndVerifyDiskState,
574                       new, None)
575
576
577 class TestComputeMinMaxSpec(unittest.TestCase):
578   def setUp(self):
579     self.ipolicy = {
580       constants.ISPECS_MAX: {
581         constants.ISPEC_MEM_SIZE: 512,
582         constants.ISPEC_DISK_SIZE: 1024,
583         },
584       constants.ISPECS_MIN: {
585         constants.ISPEC_MEM_SIZE: 128,
586         constants.ISPEC_DISK_COUNT: 1,
587         },
588       }
589
590   def testNoneValue(self):
591     self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
592                                               self.ipolicy, None) is None)
593
594   def testAutoValue(self):
595     self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_MEM_SIZE, None,
596                                               self.ipolicy,
597                                               constants.VALUE_AUTO) is None)
598
599   def testNotDefined(self):
600     self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_NIC_COUNT, None,
601                                               self.ipolicy, 3) is None)
602
603   def testNoMinDefined(self):
604     self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_SIZE, None,
605                                               self.ipolicy, 128) is None)
606
607   def testNoMaxDefined(self):
608     self.assertTrue(cmdlib._ComputeMinMaxSpec(constants.ISPEC_DISK_COUNT, None,
609                                                 self.ipolicy, 16) is None)
610
611   def testOutOfRange(self):
612     for (name, val) in ((constants.ISPEC_MEM_SIZE, 64),
613                         (constants.ISPEC_MEM_SIZE, 768),
614                         (constants.ISPEC_DISK_SIZE, 4096),
615                         (constants.ISPEC_DISK_COUNT, 0)):
616       min_v = self.ipolicy[constants.ISPECS_MIN].get(name, val)
617       max_v = self.ipolicy[constants.ISPECS_MAX].get(name, val)
618       self.assertEqual(cmdlib._ComputeMinMaxSpec(name, None,
619                                                  self.ipolicy, val),
620                        "%s value %s is not in range [%s, %s]" %
621                        (name, val,min_v, max_v))
622       self.assertEqual(cmdlib._ComputeMinMaxSpec(name, "1",
623                                                  self.ipolicy, val),
624                        "%s/1 value %s is not in range [%s, %s]" %
625                        (name, val,min_v, max_v))
626
627   def test(self):
628     for (name, val) in ((constants.ISPEC_MEM_SIZE, 256),
629                         (constants.ISPEC_MEM_SIZE, 128),
630                         (constants.ISPEC_MEM_SIZE, 512),
631                         (constants.ISPEC_DISK_SIZE, 1024),
632                         (constants.ISPEC_DISK_SIZE, 0),
633                         (constants.ISPEC_DISK_COUNT, 1),
634                         (constants.ISPEC_DISK_COUNT, 5)):
635       self.assertTrue(cmdlib._ComputeMinMaxSpec(name, None, self.ipolicy, val)
636                       is None)
637
638
639 def _ValidateComputeMinMaxSpec(name, *_):
640   assert name in constants.ISPECS_PARAMETERS
641   return None
642
643
644 class _SpecWrapper:
645   def __init__(self, spec):
646     self.spec = spec
647
648   def ComputeMinMaxSpec(self, *args):
649     return self.spec.pop(0)
650
651
652 class TestComputeIPolicySpecViolation(unittest.TestCase):
653   def test(self):
654     compute_fn = _ValidateComputeMinMaxSpec
655     ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
656                                               [1024], 1, _compute_fn=compute_fn)
657     self.assertEqual(ret, [])
658
659   def testInvalidArguments(self):
660     self.assertRaises(AssertionError, cmdlib._ComputeIPolicySpecViolation,
661                       NotImplemented, 1024, 1, 1, 1, [], 1)
662
663   def testInvalidSpec(self):
664     spec = _SpecWrapper([None, False, "foo", None, "bar", None])
665     compute_fn = spec.ComputeMinMaxSpec
666     ret = cmdlib._ComputeIPolicySpecViolation(NotImplemented, 1024, 1, 1, 1,
667                                               [1024], 1, _compute_fn=compute_fn)
668     self.assertEqual(ret, ["foo", "bar"])
669     self.assertFalse(spec.spec)
670
671
672 class _StubComputeIPolicySpecViolation:
673   def __init__(self, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
674                spindle_use):
675     self.mem_size = mem_size
676     self.cpu_count = cpu_count
677     self.disk_count = disk_count
678     self.nic_count = nic_count
679     self.disk_sizes = disk_sizes
680     self.spindle_use = spindle_use
681
682   def __call__(self, _, mem_size, cpu_count, disk_count, nic_count, disk_sizes,
683                spindle_use):
684     assert self.mem_size == mem_size
685     assert self.cpu_count == cpu_count
686     assert self.disk_count == disk_count
687     assert self.nic_count == nic_count
688     assert self.disk_sizes == disk_sizes
689     assert self.spindle_use == spindle_use
690
691     return []
692
693
694 class TestComputeIPolicyInstanceViolation(unittest.TestCase):
695   def test(self):
696     beparams = {
697       constants.BE_MAXMEM: 2048,
698       constants.BE_VCPUS: 2,
699       constants.BE_SPINDLE_USE: 4,
700       }
701     disks = [objects.Disk(size=512)]
702     instance = objects.Instance(beparams=beparams, disks=disks, nics=[])
703     stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 4)
704     ret = cmdlib._ComputeIPolicyInstanceViolation(NotImplemented, instance,
705                                                   _compute_fn=stub)
706     self.assertEqual(ret, [])
707
708
709 class TestComputeIPolicyInstanceSpecViolation(unittest.TestCase):
710   def test(self):
711     ispec = {
712       constants.ISPEC_MEM_SIZE: 2048,
713       constants.ISPEC_CPU_COUNT: 2,
714       constants.ISPEC_DISK_COUNT: 1,
715       constants.ISPEC_DISK_SIZE: [512],
716       constants.ISPEC_NIC_COUNT: 0,
717       constants.ISPEC_SPINDLE_USE: 1,
718       }
719     stub = _StubComputeIPolicySpecViolation(2048, 2, 1, 0, [512], 1)
720     ret = cmdlib._ComputeIPolicyInstanceSpecViolation(NotImplemented, ispec,
721                                                       _compute_fn=stub)
722     self.assertEqual(ret, [])
723
724
725 class _CallRecorder:
726   def __init__(self, return_value=None):
727     self.called = False
728     self.return_value = return_value
729
730   def __call__(self, *args):
731     self.called = True
732     return self.return_value
733
734
735 class TestComputeIPolicyNodeViolation(unittest.TestCase):
736   def setUp(self):
737     self.recorder = _CallRecorder(return_value=[])
738
739   def testSameGroup(self):
740     ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
741                                               "foo", "foo",
742                                               _compute_fn=self.recorder)
743     self.assertFalse(self.recorder.called)
744     self.assertEqual(ret, [])
745
746   def testDifferentGroup(self):
747     ret = cmdlib._ComputeIPolicyNodeViolation(NotImplemented, NotImplemented,
748                                               "foo", "bar",
749                                               _compute_fn=self.recorder)
750     self.assertTrue(self.recorder.called)
751     self.assertEqual(ret, [])
752
753
754 class _FakeConfigForTargetNodeIPolicy:
755   def __init__(self, node_info=NotImplemented):
756     self._node_info = node_info
757
758   def GetNodeInfo(self, _):
759     return self._node_info
760
761
762 class TestCheckTargetNodeIPolicy(unittest.TestCase):
763   def setUp(self):
764     self.instance = objects.Instance(primary_node="blubb")
765     self.target_node = objects.Node(group="bar")
766     node_info = objects.Node(group="foo")
767     fake_cfg = _FakeConfigForTargetNodeIPolicy(node_info=node_info)
768     self.lu = _FakeLU(cfg=fake_cfg)
769
770   def testNoViolation(self):
771     compute_recoder = _CallRecorder(return_value=[])
772     cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
773                                    self.target_node,
774                                    _compute_fn=compute_recoder)
775     self.assertTrue(compute_recoder.called)
776     self.assertEqual(self.lu.warning_log, [])
777
778   def testNoIgnore(self):
779     compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
780     self.assertRaises(errors.OpPrereqError, cmdlib._CheckTargetNodeIPolicy,
781                       self.lu, NotImplemented, self.instance, self.target_node,
782                       _compute_fn=compute_recoder)
783     self.assertTrue(compute_recoder.called)
784     self.assertEqual(self.lu.warning_log, [])
785
786   def testIgnoreViolation(self):
787     compute_recoder = _CallRecorder(return_value=["mem_size not in range"])
788     cmdlib._CheckTargetNodeIPolicy(self.lu, NotImplemented, self.instance,
789                                    self.target_node, ignore=True,
790                                    _compute_fn=compute_recoder)
791     self.assertTrue(compute_recoder.called)
792     msg = ("Instance does not meet target node group's (bar) instance policy:"
793            " mem_size not in range")
794     self.assertEqual(self.lu.warning_log, [(msg, ())])
795
796
797 class TestApplyContainerMods(unittest.TestCase):
798   def testEmptyContainer(self):
799     container = []
800     chgdesc = []
801     cmdlib.ApplyContainerMods("test", container, chgdesc, [], None, None, None)
802     self.assertEqual(container, [])
803     self.assertEqual(chgdesc, [])
804
805   def testAdd(self):
806     container = []
807     chgdesc = []
808     mods = cmdlib.PrepareContainerMods([
809       (constants.DDM_ADD, -1, "Hello"),
810       (constants.DDM_ADD, -1, "World"),
811       (constants.DDM_ADD, 0, "Start"),
812       (constants.DDM_ADD, -1, "End"),
813       ], None)
814     cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
815                               None, None, None)
816     self.assertEqual(container, ["Start", "Hello", "World", "End"])
817     self.assertEqual(chgdesc, [])
818
819     mods = cmdlib.PrepareContainerMods([
820       (constants.DDM_ADD, 0, "zero"),
821       (constants.DDM_ADD, 3, "Added"),
822       (constants.DDM_ADD, 5, "four"),
823       (constants.DDM_ADD, 7, "xyz"),
824       ], None)
825     cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
826                               None, None, None)
827     self.assertEqual(container,
828                      ["zero", "Start", "Hello", "Added", "World", "four",
829                       "End", "xyz"])
830     self.assertEqual(chgdesc, [])
831
832     for idx in [-2, len(container) + 1]:
833       mods = cmdlib.PrepareContainerMods([
834         (constants.DDM_ADD, idx, "error"),
835         ], None)
836       self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
837                         "test", container, None, mods, None, None, None)
838
839   def testRemoveError(self):
840     for idx in [0, 1, 2, 100, -1, -4]:
841       mods = cmdlib.PrepareContainerMods([
842         (constants.DDM_REMOVE, idx, None),
843         ], None)
844       self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
845                         "test", [], None, mods, None, None, None)
846
847     mods = cmdlib.PrepareContainerMods([
848       (constants.DDM_REMOVE, 0, object()),
849       ], None)
850     self.assertRaises(AssertionError, cmdlib.ApplyContainerMods,
851                       "test", [""], None, mods, None, None, None)
852
853   def testAddError(self):
854     for idx in range(-100, -1) + [100]:
855       mods = cmdlib.PrepareContainerMods([
856         (constants.DDM_ADD, idx, None),
857         ], None)
858       self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
859                         "test", [], None, mods, None, None, None)
860
861   def testRemove(self):
862     container = ["item 1", "item 2"]
863     mods = cmdlib.PrepareContainerMods([
864       (constants.DDM_ADD, -1, "aaa"),
865       (constants.DDM_REMOVE, -1, None),
866       (constants.DDM_ADD, -1, "bbb"),
867       ], None)
868     chgdesc = []
869     cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
870                               None, None, None)
871     self.assertEqual(container, ["item 1", "item 2", "bbb"])
872     self.assertEqual(chgdesc, [
873       ("test/2", "remove"),
874       ])
875
876   def testModify(self):
877     container = ["item 1", "item 2"]
878     mods = cmdlib.PrepareContainerMods([
879       (constants.DDM_MODIFY, -1, "a"),
880       (constants.DDM_MODIFY, 0, "b"),
881       (constants.DDM_MODIFY, 1, "c"),
882       ], None)
883     chgdesc = []
884     cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
885                               None, None, None)
886     self.assertEqual(container, ["item 1", "item 2"])
887     self.assertEqual(chgdesc, [])
888
889     for idx in [-2, len(container) + 1]:
890       mods = cmdlib.PrepareContainerMods([
891         (constants.DDM_MODIFY, idx, "error"),
892         ], None)
893       self.assertRaises(IndexError, cmdlib.ApplyContainerMods,
894                         "test", container, None, mods, None, None, None)
895
896   class _PrivateData:
897     def __init__(self):
898       self.data = None
899
900   @staticmethod
901   def _CreateTestFn(idx, params, private):
902     private.data = ("add", idx, params)
903     return ((100 * idx, params), [
904       ("test/%s" % idx, hex(idx)),
905       ])
906
907   @staticmethod
908   def _ModifyTestFn(idx, item, params, private):
909     private.data = ("modify", idx, params)
910     return [
911       ("test/%s" % idx, "modify %s" % params),
912       ]
913
914   @staticmethod
915   def _RemoveTestFn(idx, item, private):
916     private.data = ("remove", idx, item)
917
918   def testAddWithCreateFunction(self):
919     container = []
920     chgdesc = []
921     mods = cmdlib.PrepareContainerMods([
922       (constants.DDM_ADD, -1, "Hello"),
923       (constants.DDM_ADD, -1, "World"),
924       (constants.DDM_ADD, 0, "Start"),
925       (constants.DDM_ADD, -1, "End"),
926       (constants.DDM_REMOVE, 2, None),
927       (constants.DDM_MODIFY, -1, "foobar"),
928       (constants.DDM_REMOVE, 2, None),
929       (constants.DDM_ADD, 1, "More"),
930       ], self._PrivateData)
931     cmdlib.ApplyContainerMods("test", container, chgdesc, mods,
932       self._CreateTestFn, self._ModifyTestFn, self._RemoveTestFn)
933     self.assertEqual(container, [
934       (000, "Start"),
935       (100, "More"),
936       (000, "Hello"),
937       ])
938     self.assertEqual(chgdesc, [
939       ("test/0", "0x0"),
940       ("test/1", "0x1"),
941       ("test/0", "0x0"),
942       ("test/3", "0x3"),
943       ("test/2", "remove"),
944       ("test/2", "modify foobar"),
945       ("test/2", "remove"),
946       ("test/1", "0x1")
947       ])
948     self.assertTrue(compat.all(op == private.data[0]
949                                for (op, _, _, private) in mods))
950     self.assertEqual([private.data for (op, _, _, private) in mods], [
951       ("add", 0, "Hello"),
952       ("add", 1, "World"),
953       ("add", 0, "Start"),
954       ("add", 3, "End"),
955       ("remove", 2, (100, "World")),
956       ("modify", 2, "foobar"),
957       ("remove", 2, (300, "End")),
958       ("add", 1, "More"),
959       ])
960
961
962 class _FakeConfigForGenDiskTemplate:
963   def __init__(self):
964     self._unique_id = itertools.count()
965     self._drbd_minor = itertools.count(20)
966     self._port = itertools.count(constants.FIRST_DRBD_PORT)
967     self._secret = itertools.count()
968
969   def GetVGName(self):
970     return "testvg"
971
972   def GenerateUniqueID(self, ec_id):
973     return "ec%s-uq%s" % (ec_id, self._unique_id.next())
974
975   def AllocateDRBDMinor(self, nodes, instance):
976     return [self._drbd_minor.next()
977             for _ in nodes]
978
979   def AllocatePort(self):
980     return self._port.next()
981
982   def GenerateDRBDSecret(self, ec_id):
983     return "ec%s-secret%s" % (ec_id, self._secret.next())
984
985   def GetInstanceInfo(self, _):
986     return "foobar"
987
988
989 class _FakeProcForGenDiskTemplate:
990   def GetECId(self):
991     return 0
992
993
994 class TestGenerateDiskTemplate(unittest.TestCase):
995   def setUp(self):
996     nodegroup = objects.NodeGroup(name="ng")
997     nodegroup.UpgradeConfig()
998
999     cfg = _FakeConfigForGenDiskTemplate()
1000     proc = _FakeProcForGenDiskTemplate()
1001
1002     self.lu = _FakeLU(cfg=cfg, proc=proc)
1003     self.nodegroup = nodegroup
1004
1005   @staticmethod
1006   def GetDiskParams():
1007     return copy.deepcopy(constants.DISK_DT_DEFAULTS)
1008
1009   def testWrongDiskTemplate(self):
1010     gdt = cmdlib._GenerateDiskTemplate
1011     disk_template = "##unknown##"
1012
1013     assert disk_template not in constants.DISK_TEMPLATES
1014
1015     self.assertRaises(errors.ProgrammerError, gdt, self.lu, disk_template,
1016                       "inst26831.example.com", "node30113.example.com", [], [],
1017                       NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1018                       self.GetDiskParams())
1019
1020   def testDiskless(self):
1021     gdt = cmdlib._GenerateDiskTemplate
1022
1023     result = gdt(self.lu, constants.DT_DISKLESS, "inst27734.example.com",
1024                  "node30113.example.com", [], [],
1025                  NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1026                  self.GetDiskParams())
1027     self.assertEqual(result, [])
1028
1029   def _TestTrivialDisk(self, template, disk_info, base_index, exp_dev_type,
1030                        file_storage_dir=NotImplemented,
1031                        file_driver=NotImplemented,
1032                        req_file_storage=NotImplemented,
1033                        req_shr_file_storage=NotImplemented):
1034     gdt = cmdlib._GenerateDiskTemplate
1035
1036     map(lambda params: utils.ForceDictType(params,
1037                                            constants.IDISK_PARAMS_TYPES),
1038         disk_info)
1039
1040     # Check if non-empty list of secondaries is rejected
1041     self.assertRaises(errors.ProgrammerError, gdt, self.lu,
1042                       template, "inst25088.example.com",
1043                       "node185.example.com", ["node323.example.com"], [],
1044                       NotImplemented, NotImplemented, base_index,
1045                       self.lu.LogInfo, self.GetDiskParams(),
1046                       _req_file_storage=req_file_storage,
1047                       _req_shr_file_storage=req_shr_file_storage)
1048
1049     result = gdt(self.lu, template, "inst21662.example.com",
1050                  "node21741.example.com", [],
1051                  disk_info, file_storage_dir, file_driver, base_index,
1052                  self.lu.LogInfo, self.GetDiskParams(),
1053                  _req_file_storage=req_file_storage,
1054                  _req_shr_file_storage=req_shr_file_storage)
1055
1056     for (idx, disk) in enumerate(result):
1057       self.assertTrue(isinstance(disk, objects.Disk))
1058       self.assertEqual(disk.dev_type, exp_dev_type)
1059       self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1060       self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1061       self.assertTrue(disk.children is None)
1062
1063     self._CheckIvNames(result, base_index, base_index + len(disk_info))
1064     cmdlib._UpdateIvNames(base_index, result)
1065     self._CheckIvNames(result, base_index, base_index + len(disk_info))
1066
1067     return result
1068
1069   def _CheckIvNames(self, disks, base_index, end_index):
1070     self.assertEqual(map(operator.attrgetter("iv_name"), disks),
1071                      ["disk/%s" % i for i in range(base_index, end_index)])
1072
1073   def testPlain(self):
1074     disk_info = [{
1075       constants.IDISK_SIZE: 1024,
1076       constants.IDISK_MODE: constants.DISK_RDWR,
1077       }, {
1078       constants.IDISK_SIZE: 4096,
1079       constants.IDISK_VG: "othervg",
1080       constants.IDISK_MODE: constants.DISK_RDWR,
1081       }]
1082
1083     result = self._TestTrivialDisk(constants.DT_PLAIN, disk_info, 3,
1084                                    constants.LD_LV)
1085
1086     self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1087       ("testvg", "ec0-uq0.disk3"),
1088       ("othervg", "ec0-uq1.disk4"),
1089       ])
1090
1091   @staticmethod
1092   def _AllowFileStorage():
1093     pass
1094
1095   @staticmethod
1096   def _ForbidFileStorage():
1097     raise errors.OpPrereqError("Disallowed in test")
1098
1099   def testFile(self):
1100     self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1101                       constants.DT_FILE, [], 0, NotImplemented,
1102                       req_file_storage=self._ForbidFileStorage)
1103     self.assertRaises(errors.OpPrereqError, self._TestTrivialDisk,
1104                       constants.DT_SHARED_FILE, [], 0, NotImplemented,
1105                       req_shr_file_storage=self._ForbidFileStorage)
1106
1107     for disk_template in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1108       disk_info = [{
1109         constants.IDISK_SIZE: 80 * 1024,
1110         constants.IDISK_MODE: constants.DISK_RDONLY,
1111         }, {
1112         constants.IDISK_SIZE: 4096,
1113         constants.IDISK_MODE: constants.DISK_RDWR,
1114         }, {
1115         constants.IDISK_SIZE: 6 * 1024,
1116         constants.IDISK_MODE: constants.DISK_RDWR,
1117         }]
1118
1119       result = self._TestTrivialDisk(disk_template, disk_info, 2,
1120         constants.LD_FILE, file_storage_dir="/tmp",
1121         file_driver=constants.FD_BLKTAP,
1122         req_file_storage=self._AllowFileStorage,
1123         req_shr_file_storage=self._AllowFileStorage)
1124
1125       self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1126         (constants.FD_BLKTAP, "/tmp/disk2"),
1127         (constants.FD_BLKTAP, "/tmp/disk3"),
1128         (constants.FD_BLKTAP, "/tmp/disk4"),
1129         ])
1130
1131   def testBlock(self):
1132     disk_info = [{
1133       constants.IDISK_SIZE: 8 * 1024,
1134       constants.IDISK_MODE: constants.DISK_RDWR,
1135       constants.IDISK_ADOPT: "/tmp/some/block/dev",
1136       }]
1137
1138     result = self._TestTrivialDisk(constants.DT_BLOCK, disk_info, 10,
1139                                    constants.LD_BLOCKDEV)
1140
1141     self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1142       (constants.BLOCKDEV_DRIVER_MANUAL, "/tmp/some/block/dev"),
1143       ])
1144
1145   def testRbd(self):
1146     disk_info = [{
1147       constants.IDISK_SIZE: 8 * 1024,
1148       constants.IDISK_MODE: constants.DISK_RDONLY,
1149       }, {
1150       constants.IDISK_SIZE: 100 * 1024,
1151       constants.IDISK_MODE: constants.DISK_RDWR,
1152       }]
1153
1154     result = self._TestTrivialDisk(constants.DT_RBD, disk_info, 0,
1155                                    constants.LD_RBD)
1156
1157     self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1158       ("rbd", "ec0-uq0.rbd.disk0"),
1159       ("rbd", "ec0-uq1.rbd.disk1"),
1160       ])
1161
1162   def testDrbd8(self):
1163     gdt = cmdlib._GenerateDiskTemplate
1164     drbd8_defaults = constants.DISK_LD_DEFAULTS[constants.LD_DRBD8]
1165     drbd8_default_metavg = drbd8_defaults[constants.LDP_DEFAULT_METAVG]
1166
1167     disk_info = [{
1168       constants.IDISK_SIZE: 1024,
1169       constants.IDISK_MODE: constants.DISK_RDWR,
1170       }, {
1171       constants.IDISK_SIZE: 100 * 1024,
1172       constants.IDISK_MODE: constants.DISK_RDONLY,
1173       constants.IDISK_METAVG: "metavg",
1174       }, {
1175       constants.IDISK_SIZE: 4096,
1176       constants.IDISK_MODE: constants.DISK_RDWR,
1177       constants.IDISK_VG: "vgxyz",
1178       },
1179       ]
1180
1181     exp_logical_ids = [[
1182       (self.lu.cfg.GetVGName(), "ec0-uq0.disk0_data"),
1183       (drbd8_default_metavg, "ec0-uq0.disk0_meta"),
1184       ], [
1185       (self.lu.cfg.GetVGName(), "ec0-uq1.disk1_data"),
1186       ("metavg", "ec0-uq1.disk1_meta"),
1187       ], [
1188       ("vgxyz", "ec0-uq2.disk2_data"),
1189       (drbd8_default_metavg, "ec0-uq2.disk2_meta"),
1190       ]]
1191
1192     assert len(exp_logical_ids) == len(disk_info)
1193
1194     map(lambda params: utils.ForceDictType(params,
1195                                            constants.IDISK_PARAMS_TYPES),
1196         disk_info)
1197
1198     # Check if empty list of secondaries is rejected
1199     self.assertRaises(errors.ProgrammerError, gdt, self.lu, constants.DT_DRBD8,
1200                       "inst827.example.com", "node1334.example.com", [],
1201                       disk_info, NotImplemented, NotImplemented, 0,
1202                       self.lu.LogInfo, self.GetDiskParams())
1203
1204     result = gdt(self.lu, constants.DT_DRBD8, "inst827.example.com",
1205                  "node1334.example.com", ["node12272.example.com"],
1206                  disk_info, NotImplemented, NotImplemented, 0, self.lu.LogInfo,
1207                  self.GetDiskParams())
1208
1209     for (idx, disk) in enumerate(result):
1210       self.assertTrue(isinstance(disk, objects.Disk))
1211       self.assertEqual(disk.dev_type, constants.LD_DRBD8)
1212       self.assertEqual(disk.size, disk_info[idx][constants.IDISK_SIZE])
1213       self.assertEqual(disk.mode, disk_info[idx][constants.IDISK_MODE])
1214
1215       for child in disk.children:
1216         self.assertTrue(isinstance(disk, objects.Disk))
1217         self.assertEqual(child.dev_type, constants.LD_LV)
1218         self.assertTrue(child.children is None)
1219
1220       self.assertEqual(map(operator.attrgetter("logical_id"), disk.children),
1221                        exp_logical_ids[idx])
1222
1223       self.assertEqual(len(disk.children), 2)
1224       self.assertEqual(disk.children[0].size, disk.size)
1225       self.assertEqual(disk.children[1].size, constants.DRBD_META_SIZE)
1226
1227     self._CheckIvNames(result, 0, len(disk_info))
1228     cmdlib._UpdateIvNames(0, result)
1229     self._CheckIvNames(result, 0, len(disk_info))
1230
1231     self.assertEqual(map(operator.attrgetter("logical_id"), result), [
1232       ("node1334.example.com", "node12272.example.com",
1233        constants.FIRST_DRBD_PORT, 20, 21, "ec0-secret0"),
1234       ("node1334.example.com", "node12272.example.com",
1235        constants.FIRST_DRBD_PORT + 1, 22, 23, "ec0-secret1"),
1236       ("node1334.example.com", "node12272.example.com",
1237        constants.FIRST_DRBD_PORT + 2, 24, 25, "ec0-secret2"),
1238       ])
1239
1240
1241 if __name__ == "__main__":
1242   testutils.GanetiTestProgram()