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