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