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