Replace frozenset with compat.UniqueFrozenset
[ganeti-local] / test / ganeti.mcpu_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2009, 2011 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 """Script for unittesting the mcpu module"""
23
24
25 import unittest
26 import itertools
27
28 from ganeti import compat
29 from ganeti import mcpu
30 from ganeti import opcodes
31 from ganeti import cmdlib
32 from ganeti import locking
33 from ganeti import constants
34 from ganeti.constants import \
35     LOCK_ATTEMPTS_TIMEOUT, \
36     LOCK_ATTEMPTS_MAXWAIT, \
37     LOCK_ATTEMPTS_MINWAIT
38
39 import testutils
40
41
42 REQ_BGL_WHITELIST = compat.UniqueFrozenset([
43   opcodes.OpClusterActivateMasterIp,
44   opcodes.OpClusterDeactivateMasterIp,
45   opcodes.OpClusterDestroy,
46   opcodes.OpClusterPostInit,
47   opcodes.OpClusterRename,
48   opcodes.OpInstanceRename,
49   opcodes.OpNodeAdd,
50   opcodes.OpNodeRemove,
51   opcodes.OpTestAllocator,
52   ])
53
54
55 class TestLockAttemptTimeoutStrategy(unittest.TestCase):
56   def testConstants(self):
57     tpa = mcpu.LockAttemptTimeoutStrategy._TIMEOUT_PER_ATTEMPT
58     self.assert_(len(tpa) > LOCK_ATTEMPTS_TIMEOUT / LOCK_ATTEMPTS_MAXWAIT)
59     self.assert_(sum(tpa) >= LOCK_ATTEMPTS_TIMEOUT)
60
61     self.assertTrue(LOCK_ATTEMPTS_TIMEOUT >= 1800,
62                     msg="Waiting less than half an hour per priority")
63     self.assertTrue(LOCK_ATTEMPTS_TIMEOUT <= 3600,
64                     msg="Waiting more than an hour per priority")
65
66   def testSimple(self):
67     strat = mcpu.LockAttemptTimeoutStrategy(_random_fn=lambda: 0.5,
68                                             _time_fn=lambda: 0.0)
69
70     prev = None
71     for i in range(len(strat._TIMEOUT_PER_ATTEMPT)):
72       timeout = strat.NextAttempt()
73       self.assert_(timeout is not None)
74
75       self.assert_(timeout <= LOCK_ATTEMPTS_MAXWAIT)
76       self.assert_(timeout >= LOCK_ATTEMPTS_MINWAIT)
77       self.assert_(prev is None or timeout >= prev)
78
79       prev = timeout
80
81     for _ in range(10):
82       self.assert_(strat.NextAttempt() is None)
83
84
85 class TestDispatchTable(unittest.TestCase):
86   def test(self):
87     for opcls in opcodes.OP_MAPPING.values():
88       if not opcls.WITH_LU:
89         continue
90       self.assertTrue(opcls in mcpu.Processor.DISPATCH_TABLE,
91                       msg="%s missing handler class" % opcls)
92
93       # Check against BGL whitelist
94       lucls = mcpu.Processor.DISPATCH_TABLE[opcls]
95       if lucls.REQ_BGL:
96         self.assertTrue(opcls in REQ_BGL_WHITELIST,
97                         msg=("%s not whitelisted for BGL" % opcls.OP_ID))
98       else:
99         self.assertFalse(opcls in REQ_BGL_WHITELIST,
100                          msg=("%s whitelisted for BGL, but doesn't use it" %
101                               opcls.OP_ID))
102
103
104 class TestProcessResult(unittest.TestCase):
105   def setUp(self):
106     self._submitted = []
107     self._count = itertools.count(200)
108
109   def _Submit(self, jobs):
110     job_ids = [self._count.next() for _ in jobs]
111     self._submitted.extend(zip(job_ids, jobs))
112     return job_ids
113
114   def testNoJobs(self):
115     for i in [object(), [], False, True, None, 1, 929, {}]:
116       self.assertEqual(mcpu._ProcessResult(NotImplemented, NotImplemented, i),
117                        i)
118
119   def testDefaults(self):
120     src = opcodes.OpTestDummy()
121
122     res = mcpu._ProcessResult(self._Submit, src, cmdlib.ResultWithJobs([[
123       opcodes.OpTestDelay(),
124       opcodes.OpTestDelay(),
125       ], [
126       opcodes.OpTestDelay(),
127       ]]))
128
129     self.assertEqual(res, {
130       constants.JOB_IDS_KEY: [200, 201],
131       })
132
133     (_, (op1, op2)) = self._submitted.pop(0)
134     (_, (op3, )) = self._submitted.pop(0)
135     self.assertRaises(IndexError, self._submitted.pop)
136
137     for op in [op1, op2, op3]:
138       self.assertTrue("OP_TEST_DUMMY" in op.comment)
139       self.assertFalse(hasattr(op, "priority"))
140       self.assertFalse(hasattr(op, "debug_level"))
141
142   def testParams(self):
143     src = opcodes.OpTestDummy(priority=constants.OP_PRIO_HIGH,
144                               debug_level=3)
145
146     res = mcpu._ProcessResult(self._Submit, src, cmdlib.ResultWithJobs([[
147       opcodes.OpTestDelay(priority=constants.OP_PRIO_LOW),
148       ], [
149       opcodes.OpTestDelay(comment="foobar", debug_level=10),
150       ]], other=True, value=range(10)))
151
152     self.assertEqual(res, {
153       constants.JOB_IDS_KEY: [200, 201],
154       "other": True,
155       "value": range(10),
156       })
157
158     (_, (op1, )) = self._submitted.pop(0)
159     (_, (op2, )) = self._submitted.pop(0)
160     self.assertRaises(IndexError, self._submitted.pop)
161
162     self.assertEqual(op1.priority, constants.OP_PRIO_LOW)
163     self.assertTrue("OP_TEST_DUMMY" in op1.comment)
164     self.assertEqual(op1.debug_level, 3)
165
166     self.assertEqual(op2.priority, constants.OP_PRIO_HIGH)
167     self.assertEqual(op2.comment, "foobar")
168     self.assertEqual(op2.debug_level, 3)
169
170
171 class _FakeLuWithLocks:
172   def __init__(self, needed_locks, share_locks):
173     self.needed_locks = needed_locks
174     self.share_locks = share_locks
175
176
177 class _FakeGlm:
178   def __init__(self, owning_nal):
179     self._owning_nal = owning_nal
180
181   def check_owned(self, level, names):
182     assert level == locking.LEVEL_NODE_ALLOC
183     assert names == locking.NAL
184     return self._owning_nal
185
186   def owning_all(self, level):
187     return False
188
189
190 class TestVerifyLocks(unittest.TestCase):
191   def testNoLocks(self):
192     lu = _FakeLuWithLocks({}, {})
193     glm = _FakeGlm(False)
194     mcpu._VerifyLocks(lu, glm,
195                       _mode_whitelist=NotImplemented,
196                       _nal_whitelist=NotImplemented)
197
198   def testNotAllSameMode(self):
199     for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
200       lu = _FakeLuWithLocks({
201         level: ["foo"],
202         }, {
203         level: 0,
204         locking.LEVEL_NODE_ALLOC: 0,
205         })
206       glm = _FakeGlm(False)
207       mcpu._VerifyLocks(lu, glm, _mode_whitelist=[], _nal_whitelist=[])
208
209   def testDifferentMode(self):
210     for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
211       lu = _FakeLuWithLocks({
212         level: ["foo"],
213         }, {
214         level: 0,
215         locking.LEVEL_NODE_ALLOC: 1,
216         })
217       glm = _FakeGlm(False)
218       try:
219         mcpu._VerifyLocks(lu, glm, _mode_whitelist=[], _nal_whitelist=[])
220       except AssertionError, err:
221         self.assertTrue("using the same mode as nodes" in str(err))
222       else:
223         self.fail("Exception not raised")
224
225       # Once more with the whitelist
226       mcpu._VerifyLocks(lu, glm, _mode_whitelist=[_FakeLuWithLocks],
227                         _nal_whitelist=[])
228
229   def testSameMode(self):
230     for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
231       lu = _FakeLuWithLocks({
232         level: ["foo"],
233         locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
234         }, {
235         level: 1,
236         locking.LEVEL_NODE_ALLOC: 1,
237         })
238       glm = _FakeGlm(True)
239
240       try:
241         mcpu._VerifyLocks(lu, glm, _mode_whitelist=[_FakeLuWithLocks],
242                           _nal_whitelist=[])
243       except AssertionError, err:
244         self.assertTrue("whitelisted to use different modes" in str(err))
245       else:
246         self.fail("Exception not raised")
247
248       # Once more without the whitelist
249       mcpu._VerifyLocks(lu, glm, _mode_whitelist=[], _nal_whitelist=[])
250
251   def testAllWithoutAllocLock(self):
252     for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
253       lu = _FakeLuWithLocks({
254         level: locking.ALL_SET,
255         }, {
256         level: 0,
257         locking.LEVEL_NODE_ALLOC: 0,
258         })
259       glm = _FakeGlm(False)
260       try:
261         mcpu._VerifyLocks(lu, glm, _mode_whitelist=[], _nal_whitelist=[])
262       except AssertionError, err:
263         self.assertTrue("allocation lock must be used if" in str(err))
264       else:
265         self.fail("Exception not raised")
266
267       # Once more with the whitelist
268       mcpu._VerifyLocks(lu, glm, _mode_whitelist=[],
269                         _nal_whitelist=[_FakeLuWithLocks])
270
271   def testAllWithAllocLock(self):
272     for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
273       lu = _FakeLuWithLocks({
274         level: locking.ALL_SET,
275         locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
276         }, {
277         level: 0,
278         locking.LEVEL_NODE_ALLOC: 0,
279         })
280       glm = _FakeGlm(True)
281
282       try:
283         mcpu._VerifyLocks(lu, glm, _mode_whitelist=[],
284                           _nal_whitelist=[_FakeLuWithLocks])
285       except AssertionError, err:
286         self.assertTrue("whitelisted for not acquiring" in str(err))
287       else:
288         self.fail("Exception not raised")
289
290       # Once more without the whitelist
291       mcpu._VerifyLocks(lu, glm, _mode_whitelist=[], _nal_whitelist=[])
292
293
294 if __name__ == "__main__":
295   testutils.GanetiTestProgram()