A few more locking unit tests
[ganeti-local] / test / ganeti.locking_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2006, 2007 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 # 0.0510-1301, USA.
20
21
22 """Script for unittesting the locking module"""
23
24
25 import os
26 import unittest
27 import time
28 import Queue
29
30 from ganeti import locking
31 from ganeti import errors
32 from threading import Thread
33
34
35 # This is used to test the ssynchronize decorator.
36 # Since it's passed as input to a decorator it must be declared as a global.
37 _decoratorlock = locking.SharedLock()
38
39
40 class TestSharedLock(unittest.TestCase):
41   """SharedLock tests"""
42
43   def setUp(self):
44     self.sl = locking.SharedLock()
45     # helper threads use the 'done' queue to tell the master they finished.
46     self.done = Queue.Queue(0)
47
48   def testSequenceAndOwnership(self):
49     self.assert_(not self.sl._is_owned())
50     self.sl.acquire(shared=1)
51     self.assert_(self.sl._is_owned())
52     self.assert_(self.sl._is_owned(shared=1))
53     self.assert_(not self.sl._is_owned(shared=0))
54     self.sl.release()
55     self.assert_(not self.sl._is_owned())
56     self.sl.acquire()
57     self.assert_(self.sl._is_owned())
58     self.assert_(not self.sl._is_owned(shared=1))
59     self.assert_(self.sl._is_owned(shared=0))
60     self.sl.release()
61     self.assert_(not self.sl._is_owned())
62     self.sl.acquire(shared=1)
63     self.assert_(self.sl._is_owned())
64     self.assert_(self.sl._is_owned(shared=1))
65     self.assert_(not self.sl._is_owned(shared=0))
66     self.sl.release()
67     self.assert_(not self.sl._is_owned())
68
69   def testBooleanValue(self):
70     # semaphores are supposed to return a true value on a successful acquire
71     self.assert_(self.sl.acquire(shared=1))
72     self.sl.release()
73     self.assert_(self.sl.acquire())
74     self.sl.release()
75
76   def testDoubleLockingStoE(self):
77     self.sl.acquire(shared=1)
78     self.assertRaises(AssertionError, self.sl.acquire)
79
80   def testDoubleLockingEtoS(self):
81     self.sl.acquire()
82     self.assertRaises(AssertionError, self.sl.acquire, shared=1)
83
84   def testDoubleLockingStoS(self):
85     self.sl.acquire(shared=1)
86     self.assertRaises(AssertionError, self.sl.acquire, shared=1)
87
88   def testDoubleLockingEtoE(self):
89     self.sl.acquire()
90     self.assertRaises(AssertionError, self.sl.acquire)
91
92   # helper functions: called in a separate thread they acquire the lock, send
93   # their identifier on the done queue, then release it.
94   def _doItSharer(self):
95     try:
96       self.sl.acquire(shared=1)
97       self.done.put('SHR')
98       self.sl.release()
99     except errors.LockError:
100       self.done.put('ERR')
101
102   def _doItExclusive(self):
103     try:
104       self.sl.acquire()
105       self.done.put('EXC')
106       self.sl.release()
107     except errors.LockError:
108       self.done.put('ERR')
109
110   def _doItDelete(self):
111     try:
112       self.sl.delete()
113       self.done.put('DEL')
114     except errors.LockError:
115       self.done.put('ERR')
116
117   def testSharersCanCoexist(self):
118     self.sl.acquire(shared=1)
119     Thread(target=self._doItSharer).start()
120     self.assert_(self.done.get(True, 1))
121     self.sl.release()
122
123   def testExclusiveBlocksExclusive(self):
124     self.sl.acquire()
125     Thread(target=self._doItExclusive).start()
126     # give it a bit of time to check that it's not actually doing anything
127     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
128     self.sl.release()
129     self.assert_(self.done.get(True, 1))
130
131   def testExclusiveBlocksDelete(self):
132     self.sl.acquire()
133     Thread(target=self._doItDelete).start()
134     # give it a bit of time to check that it's not actually doing anything
135     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
136     self.sl.release()
137     self.assert_(self.done.get(True, 1))
138
139   def testExclusiveBlocksSharer(self):
140     self.sl.acquire()
141     Thread(target=self._doItSharer).start()
142     time.sleep(0.05)
143     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
144     self.sl.release()
145     self.assert_(self.done.get(True, 1))
146
147   def testSharerBlocksExclusive(self):
148     self.sl.acquire(shared=1)
149     Thread(target=self._doItExclusive).start()
150     time.sleep(0.05)
151     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
152     self.sl.release()
153     self.assert_(self.done.get(True, 1))
154
155   def testSharerBlocksDelete(self):
156     self.sl.acquire(shared=1)
157     Thread(target=self._doItDelete).start()
158     time.sleep(0.05)
159     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
160     self.sl.release()
161     self.assert_(self.done.get(True, 1))
162
163   def testWaitingExclusiveBlocksSharer(self):
164     self.sl.acquire(shared=1)
165     # the lock is acquired in shared mode...
166     Thread(target=self._doItExclusive).start()
167     # ...but now an exclusive is waiting...
168     time.sleep(0.05)
169     Thread(target=self._doItSharer).start()
170     # ...so the sharer should be blocked as well
171     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
172     self.sl.release()
173     # The exclusive passed before
174     self.assertEqual(self.done.get(True, 1), 'EXC')
175     self.assertEqual(self.done.get(True, 1), 'SHR')
176
177   def testWaitingSharerBlocksExclusive(self):
178     self.sl.acquire()
179     # the lock is acquired in exclusive mode...
180     Thread(target=self._doItSharer).start()
181     # ...but now a sharer is waiting...
182     time.sleep(0.05)
183     Thread(target=self._doItExclusive).start()
184     # ...the exclusive is waiting too...
185     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
186     self.sl.release()
187     # The sharer passed before
188     self.assertEqual(self.done.get(True, 1), 'SHR')
189     self.assertEqual(self.done.get(True, 1), 'EXC')
190
191   def testNoNonBlocking(self):
192     self.assertRaises(NotImplementedError, self.sl.acquire, blocking=0)
193     self.assertRaises(NotImplementedError, self.sl.delete, blocking=0)
194     self.sl.acquire()
195     self.sl.delete(blocking=0) # Fine, because the lock is already acquired
196
197   def testDelete(self):
198     self.sl.delete()
199     self.assertRaises(errors.LockError, self.sl.acquire)
200     self.assertRaises(errors.LockError, self.sl.acquire, shared=1)
201     self.assertRaises(errors.LockError, self.sl.delete)
202
203   def testNoDeleteIfSharer(self):
204     self.sl.acquire(shared=1)
205     self.assertRaises(AssertionError, self.sl.delete)
206
207   def testDeletePendingSharersExclusiveDelete(self):
208     self.sl.acquire()
209     Thread(target=self._doItSharer).start()
210     Thread(target=self._doItSharer).start()
211     time.sleep(0.05)
212     Thread(target=self._doItExclusive).start()
213     Thread(target=self._doItDelete).start()
214     time.sleep(0.05)
215     self.sl.delete()
216     # The two threads who were pending return both ERR
217     self.assertEqual(self.done.get(True, 1), 'ERR')
218     self.assertEqual(self.done.get(True, 1), 'ERR')
219     self.assertEqual(self.done.get(True, 1), 'ERR')
220     self.assertEqual(self.done.get(True, 1), 'ERR')
221
222   def testDeletePendingDeleteExclusiveSharers(self):
223     self.sl.acquire()
224     Thread(target=self._doItDelete).start()
225     Thread(target=self._doItExclusive).start()
226     time.sleep(0.05)
227     Thread(target=self._doItSharer).start()
228     Thread(target=self._doItSharer).start()
229     time.sleep(0.05)
230     self.sl.delete()
231     # The two threads who were pending return both ERR
232     self.assertEqual(self.done.get(True, 1), 'ERR')
233     self.assertEqual(self.done.get(True, 1), 'ERR')
234     self.assertEqual(self.done.get(True, 1), 'ERR')
235     self.assertEqual(self.done.get(True, 1), 'ERR')
236
237
238 class TestSSynchronizedDecorator(unittest.TestCase):
239   """Shared Lock Synchronized decorator test"""
240
241   def setUp(self):
242     # helper threads use the 'done' queue to tell the master they finished.
243     self.done = Queue.Queue(0)
244
245   @locking.ssynchronized(_decoratorlock)
246   def _doItExclusive(self):
247     self.assert_(_decoratorlock._is_owned())
248     self.done.put('EXC')
249
250   @locking.ssynchronized(_decoratorlock, shared=1)
251   def _doItSharer(self):
252     self.assert_(_decoratorlock._is_owned(shared=1))
253     self.done.put('SHR')
254
255   def testDecoratedFunctions(self):
256     self._doItExclusive()
257     self.assert_(not _decoratorlock._is_owned())
258     self._doItSharer()
259     self.assert_(not _decoratorlock._is_owned())
260
261   def testSharersCanCoexist(self):
262     _decoratorlock.acquire(shared=1)
263     Thread(target=self._doItSharer).start()
264     self.assert_(self.done.get(True, 1))
265     _decoratorlock.release()
266
267   def testExclusiveBlocksExclusive(self):
268     _decoratorlock.acquire()
269     Thread(target=self._doItExclusive).start()
270     # give it a bit of time to check that it's not actually doing anything
271     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
272     _decoratorlock.release()
273     self.assert_(self.done.get(True, 1))
274
275   def testExclusiveBlocksSharer(self):
276     _decoratorlock.acquire()
277     Thread(target=self._doItSharer).start()
278     time.sleep(0.05)
279     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
280     _decoratorlock.release()
281     self.assert_(self.done.get(True, 1))
282
283   def testSharerBlocksExclusive(self):
284     _decoratorlock.acquire(shared=1)
285     Thread(target=self._doItExclusive).start()
286     time.sleep(0.05)
287     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
288     _decoratorlock.release()
289     self.assert_(self.done.get(True, 1))
290
291
292 class TestLockSet(unittest.TestCase):
293   """LockSet tests"""
294
295   def setUp(self):
296     self.resources = ['one', 'two', 'three']
297     self.ls = locking.LockSet(members=self.resources)
298     # helper threads use the 'done' queue to tell the master they finished.
299     self.done = Queue.Queue(0)
300
301   def testResources(self):
302     self.assertEquals(self.ls._names(), set(self.resources))
303     newls = locking.LockSet()
304     self.assertEquals(newls._names(), set())
305
306   def testAcquireRelease(self):
307     self.assert_(self.ls.acquire('one'))
308     self.assertEquals(self.ls._list_owned(), set(['one']))
309     self.ls.release()
310     self.assertEquals(self.ls._list_owned(), set())
311     self.assertEquals(self.ls.acquire(['one']), set(['one']))
312     self.assertEquals(self.ls._list_owned(), set(['one']))
313     self.ls.release()
314     self.assertEquals(self.ls._list_owned(), set())
315     self.ls.acquire(['one', 'two', 'three'])
316     self.assertEquals(self.ls._list_owned(), set(['one', 'two', 'three']))
317     self.ls.release('one')
318     self.assertEquals(self.ls._list_owned(), set(['two', 'three']))
319     self.ls.release(['three'])
320     self.assertEquals(self.ls._list_owned(), set(['two']))
321     self.ls.release()
322     self.assertEquals(self.ls._list_owned(), set())
323     self.assertEquals(self.ls.acquire(['one', 'three']), set(['one', 'three']))
324     self.assertEquals(self.ls._list_owned(), set(['one', 'three']))
325     self.ls.release()
326     self.assertEquals(self.ls._list_owned(), set())
327
328   def testNoDoubleAcquire(self):
329     self.ls.acquire('one')
330     self.assertRaises(AssertionError, self.ls.acquire, 'one')
331     self.assertRaises(AssertionError, self.ls.acquire, ['two'])
332     self.assertRaises(AssertionError, self.ls.acquire, ['two', 'three'])
333     self.ls.release()
334     self.ls.acquire(['one', 'three'])
335     self.ls.release('one')
336     self.assertRaises(AssertionError, self.ls.acquire, ['two'])
337     self.ls.release('three')
338
339   def testNoWrongRelease(self):
340     self.assertRaises(AssertionError, self.ls.release)
341     self.ls.acquire('one')
342     self.assertRaises(AssertionError, self.ls.release, 'two')
343
344   def testAddRemove(self):
345     self.ls.add('four')
346     self.assertEquals(self.ls._list_owned(), set())
347     self.assert_('four' in self.ls._names())
348     self.ls.add(['five', 'six', 'seven'], acquired=1)
349     self.assert_('five' in self.ls._names())
350     self.assert_('six' in self.ls._names())
351     self.assert_('seven' in self.ls._names())
352     self.assertEquals(self.ls._list_owned(), set(['five', 'six', 'seven']))
353     self.assertEquals(self.ls.remove(['five', 'six']), ['five', 'six'])
354     self.assert_('five' not in self.ls._names())
355     self.assert_('six' not in self.ls._names())
356     self.assertEquals(self.ls._list_owned(), set(['seven']))
357     self.ls.add('eight', acquired=1, shared=1)
358     self.assert_('eight' in self.ls._names())
359     self.assertEquals(self.ls._list_owned(), set(['seven', 'eight']))
360     self.ls.remove('seven')
361     self.assert_('seven' not in self.ls._names())
362     self.assertEquals(self.ls._list_owned(), set(['eight']))
363     self.ls.release()
364     self.ls.remove(['two'])
365     self.assert_('two' not in self.ls._names())
366     self.ls.acquire('three')
367     self.assertEquals(self.ls.remove(['three']), ['three'])
368     self.assert_('three' not in self.ls._names())
369     self.assertEquals(self.ls.remove('three'), [])
370     self.assertEquals(self.ls.remove(['one', 'three', 'six']), ['one'])
371     self.assert_('one' not in self.ls._names())
372
373   def testRemoveNonBlocking(self):
374     self.assertRaises(NotImplementedError, self.ls.remove, 'one', blocking=0)
375     self.ls.acquire('one')
376     self.assertEquals(self.ls.remove('one', blocking=0), ['one'])
377     self.ls.acquire(['two', 'three'])
378     self.assertEquals(self.ls.remove(['two', 'three'], blocking=0),
379                       ['two', 'three'])
380
381   def testNoDoubleAdd(self):
382     self.assertRaises(errors.LockError, self.ls.add, 'two')
383     self.ls.add('four')
384     self.assertRaises(errors.LockError, self.ls.add, 'four')
385
386   def testNoWrongRemoves(self):
387     self.ls.acquire(['one', 'three'], shared=1)
388     # Cannot remove 'two' while holding something which is not a superset
389     self.assertRaises(AssertionError, self.ls.remove, 'two')
390     # Cannot remove 'three' as we are sharing it
391     self.assertRaises(AssertionError, self.ls.remove, 'three')
392
393   def testAcquireSetLock(self):
394     # acquire the set-lock exclusively
395     self.assertEquals(self.ls.acquire(None), set(['one', 'two', 'three']))
396     # I can still add/remove elements...
397     self.assertEquals(self.ls.remove(['two', 'three']), ['two', 'three'])
398     self.assert_(self.ls.add('six'))
399     self.ls.release()
400     # share the set-lock
401     self.assertEquals(self.ls.acquire(None, shared=1), set(['one', 'six']))
402     # adding new elements is not possible
403     self.assertRaises(AssertionError, self.ls.add, 'five')
404     self.ls.release()
405
406   def testAcquireWithRepetitions(self):
407     self.assertEquals(self.ls.acquire(['two', 'two', 'three'], shared=1),
408                       set(['two', 'two', 'three']))
409     self.ls.release(['two', 'two'])
410     self.assertEquals(self.ls._list_owned(), set(['three']))
411
412   def testEmptyAcquire(self):
413     # Acquire an empty list of locks...
414     self.assertEquals(self.ls.acquire([]), set())
415     self.assertEquals(self.ls._list_owned(), set())
416     # New locks can still be addded
417     self.assert_(self.ls.add('six'))
418     # "re-acquiring" is not an issue, since we had really acquired nothing
419     self.assertEquals(self.ls.acquire([], shared=1), set())
420     self.assertEquals(self.ls._list_owned(), set())
421     # We haven't really acquired anything, so we cannot release
422     self.assertRaises(AssertionError, self.ls.release)
423
424   def _doLockSet(self, set, shared):
425     try:
426       self.ls.acquire(set, shared=shared)
427       self.done.put('DONE')
428       self.ls.release()
429     except errors.LockError:
430       self.done.put('ERR')
431
432   def _doAddSet(self, set):
433     try:
434       self.ls.add(set, acquired=1)
435       self.done.put('DONE')
436       self.ls.release()
437     except errors.LockError:
438       self.done.put('ERR')
439
440   def _doRemoveSet(self, set):
441     self.done.put(self.ls.remove(set))
442
443   def testConcurrentSharedAcquire(self):
444     self.ls.acquire(['one', 'two'], shared=1)
445     Thread(target=self._doLockSet, args=(['one', 'two'], 1)).start()
446     self.assertEqual(self.done.get(True, 1), 'DONE')
447     Thread(target=self._doLockSet, args=(['one', 'two', 'three'], 1)).start()
448     self.assertEqual(self.done.get(True, 1), 'DONE')
449     Thread(target=self._doLockSet, args=('three', 1)).start()
450     self.assertEqual(self.done.get(True, 1), 'DONE')
451     Thread(target=self._doLockSet, args=(['one', 'two'], 0)).start()
452     Thread(target=self._doLockSet, args=(['two', 'three'], 0)).start()
453     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
454     self.ls.release()
455     self.assertEqual(self.done.get(True, 1), 'DONE')
456     self.assertEqual(self.done.get(True, 1), 'DONE')
457
458   def testConcurrentExclusiveAcquire(self):
459     self.ls.acquire(['one', 'two'])
460     Thread(target=self._doLockSet, args=('three', 1)).start()
461     self.assertEqual(self.done.get(True, 1), 'DONE')
462     Thread(target=self._doLockSet, args=('three', 0)).start()
463     self.assertEqual(self.done.get(True, 1), 'DONE')
464     Thread(target=self._doLockSet, args=(['one', 'two'], 0)).start()
465     Thread(target=self._doLockSet, args=(['one', 'two'], 1)).start()
466     Thread(target=self._doLockSet, args=('one', 0)).start()
467     Thread(target=self._doLockSet, args=('one', 1)).start()
468     Thread(target=self._doLockSet, args=(['two', 'three'], 0)).start()
469     Thread(target=self._doLockSet, args=(['two', 'three'], 1)).start()
470     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
471     self.ls.release()
472     self.assertEqual(self.done.get(True, 1), 'DONE')
473     self.assertEqual(self.done.get(True, 1), 'DONE')
474     self.assertEqual(self.done.get(True, 1), 'DONE')
475     self.assertEqual(self.done.get(True, 1), 'DONE')
476     self.assertEqual(self.done.get(True, 1), 'DONE')
477     self.assertEqual(self.done.get(True, 1), 'DONE')
478
479   def testConcurrentRemove(self):
480     self.ls.add('four')
481     self.ls.acquire(['one', 'two', 'four'])
482     Thread(target=self._doLockSet, args=(['one', 'four'], 0)).start()
483     Thread(target=self._doLockSet, args=(['one', 'four'], 1)).start()
484     Thread(target=self._doLockSet, args=(['one', 'two'], 0)).start()
485     Thread(target=self._doLockSet, args=(['one', 'two'], 1)).start()
486     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
487     self.ls.remove('one')
488     self.ls.release()
489     self.assertEqual(self.done.get(True, 1), 'ERR')
490     self.assertEqual(self.done.get(True, 1), 'ERR')
491     self.assertEqual(self.done.get(True, 1), 'ERR')
492     self.assertEqual(self.done.get(True, 1), 'ERR')
493     self.ls.add(['five', 'six'], acquired=1)
494     Thread(target=self._doLockSet, args=(['three', 'six'], 1)).start()
495     Thread(target=self._doLockSet, args=(['three', 'six'], 0)).start()
496     Thread(target=self._doLockSet, args=(['four', 'six'], 1)).start()
497     Thread(target=self._doLockSet, args=(['four', 'six'], 0)).start()
498     self.ls.remove('five')
499     self.ls.release()
500     self.assertEqual(self.done.get(True, 1), 'DONE')
501     self.assertEqual(self.done.get(True, 1), 'DONE')
502     self.assertEqual(self.done.get(True, 1), 'DONE')
503     self.assertEqual(self.done.get(True, 1), 'DONE')
504     self.ls.acquire(['three', 'four'])
505     Thread(target=self._doRemoveSet, args=(['four', 'six'], )).start()
506     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
507     self.ls.remove('four')
508     self.assertEqual(self.done.get(True, 1), ['six'])
509     Thread(target=self._doRemoveSet, args=(['two'])).start()
510     self.assertEqual(self.done.get(True, 1), ['two'])
511     self.ls.release()
512
513   def testConcurrentSharedSetLock(self):
514     # share the set-lock...
515     self.ls.acquire(None, shared=1)
516     # ...another thread can share it too
517     Thread(target=self._doLockSet, args=(None, 1)).start()
518     self.assertEqual(self.done.get(True, 1), 'DONE')
519     # ...or just share some elements
520     Thread(target=self._doLockSet, args=(['one', 'three'], 1)).start()
521     self.assertEqual(self.done.get(True, 1), 'DONE')
522     # ...but not add new ones or remove any
523     Thread(target=self._doAddSet, args=(['nine'])).start()
524     Thread(target=self._doRemoveSet, args=(['two'], )).start()
525     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
526     # this just releases the set-lock
527     self.ls.release([])
528     self.assertEqual(self.done.get(True, 1), 'DONE')
529     # release the lock on the actual elements so remove() can proceed too
530     self.ls.release()
531     self.assertEqual(self.done.get(True, 1), ['two'])
532
533   def testConcurrentExclusiveSetLock(self):
534     # acquire the set-lock...
535     self.ls.acquire(None, shared=0)
536     # ...no one can do anything else
537     Thread(target=self._doLockSet, args=(None, 1)).start()
538     Thread(target=self._doLockSet, args=(None, 0)).start()
539     Thread(target=self._doLockSet, args=(['three'], 0)).start()
540     Thread(target=self._doLockSet, args=(['two'], 1)).start()
541     Thread(target=self._doAddSet, args=(['nine'])).start()
542     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
543     self.ls.release()
544     self.assertEqual(self.done.get(True, 1), 'DONE')
545     self.assertEqual(self.done.get(True, 1), 'DONE')
546     self.assertEqual(self.done.get(True, 1), 'DONE')
547     self.assertEqual(self.done.get(True, 1), 'DONE')
548     self.assertEqual(self.done.get(True, 1), 'DONE')
549
550   def testEmptyLockSet(self):
551     # get the set-lock
552     self.assertEqual(self.ls.acquire(None), set(['one', 'two', 'three']))
553     # now empty it...
554     self.ls.remove(['one', 'two', 'three'])
555     # and adds/locks by another thread still wait
556     Thread(target=self._doAddSet, args=(['nine'])).start()
557     Thread(target=self._doLockSet, args=(None, 1)).start()
558     Thread(target=self._doLockSet, args=(None, 0)).start()
559     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
560     self.ls.release()
561     self.assertEqual(self.done.get(True, 1), 'DONE')
562     self.assertEqual(self.done.get(True, 1), 'DONE')
563     self.assertEqual(self.done.get(True, 1), 'DONE')
564     # empty it again...
565     self.assertEqual(self.ls.remove(['nine']), ['nine'])
566     # now share it...
567     self.assertEqual(self.ls.acquire(None, shared=1), set())
568     # other sharers can go, adds still wait
569     Thread(target=self._doLockSet, args=(None, 1)).start()
570     self.assertEqual(self.done.get(True, 1), 'DONE')
571     Thread(target=self._doAddSet, args=(['nine'])).start()
572     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
573     self.ls.release()
574     self.assertEqual(self.done.get(True, 1), 'DONE')
575
576
577 class TestGanetiLockManager(unittest.TestCase):
578
579   def setUp(self):
580     self.nodes=['n1', 'n2']
581     self.instances=['i1', 'i2', 'i3']
582     self.GL = locking.GanetiLockManager(nodes=self.nodes,
583                                         instances=self.instances)
584     self.done = Queue.Queue(0)
585
586   def tearDown(self):
587     # Don't try this at home...
588     locking.GanetiLockManager._instance = None
589
590   def testLockingConstants(self):
591     # The locking library internally cheats by assuming its constants have some
592     # relationships with each other. Check those hold true.
593     # This relationship is also used in the Processor to recursively acquire
594     # the right locks. Again, please don't break it.
595     for i in range(len(locking.LEVELS)):
596       self.assertEqual(i, locking.LEVELS[i])
597
598   def testDoubleGLFails(self):
599     self.assertRaises(AssertionError, locking.GanetiLockManager)
600
601   def testLockNames(self):
602     self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
603     self.assertEqual(self.GL._names(locking.LEVEL_NODE), set(self.nodes))
604     self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE),
605                      set(self.instances))
606
607   def testInitAndResources(self):
608     locking.GanetiLockManager._instance = None
609     self.GL = locking.GanetiLockManager()
610     self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
611     self.assertEqual(self.GL._names(locking.LEVEL_NODE), set())
612     self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set())
613
614     locking.GanetiLockManager._instance = None
615     self.GL = locking.GanetiLockManager(nodes=self.nodes)
616     self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
617     self.assertEqual(self.GL._names(locking.LEVEL_NODE), set(self.nodes))
618     self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set())
619
620     locking.GanetiLockManager._instance = None
621     self.GL = locking.GanetiLockManager(instances=self.instances)
622     self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
623     self.assertEqual(self.GL._names(locking.LEVEL_NODE), set())
624     self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE),
625                      set(self.instances))
626
627   def testAcquireRelease(self):
628     self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
629     self.assertEquals(self.GL._list_owned(locking.LEVEL_CLUSTER), set(['BGL']))
630     self.GL.acquire(locking.LEVEL_INSTANCE, ['i1'])
631     self.GL.acquire(locking.LEVEL_NODE, ['n1', 'n2'], shared=1)
632     self.GL.release(locking.LEVEL_NODE, ['n2'])
633     self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE), set(['n1']))
634     self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE), set(['i1']))
635     self.GL.release(locking.LEVEL_NODE)
636     self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE), set())
637     self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE), set(['i1']))
638     self.GL.release(locking.LEVEL_INSTANCE)
639     self.assertRaises(errors.LockError, self.GL.acquire,
640                       locking.LEVEL_INSTANCE, ['i5'])
641     self.GL.acquire(locking.LEVEL_INSTANCE, ['i3'], shared=1)
642     self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE), set(['i3']))
643
644   def testAcquireWholeSets(self):
645     self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
646     self.assertEquals(self.GL.acquire(locking.LEVEL_INSTANCE, None),
647                       set(self.instances))
648     self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE),
649                       set(self.instances))
650     self.assertEquals(self.GL.acquire(locking.LEVEL_NODE, None, shared=1),
651                       set(self.nodes))
652     self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE),
653                       set(self.nodes))
654     self.GL.release(locking.LEVEL_NODE)
655     self.GL.release(locking.LEVEL_INSTANCE)
656     self.GL.release(locking.LEVEL_CLUSTER)
657
658   def testAcquireWholeAndPartial(self):
659     self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
660     self.assertEquals(self.GL.acquire(locking.LEVEL_INSTANCE, None),
661                       set(self.instances))
662     self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE),
663                       set(self.instances))
664     self.assertEquals(self.GL.acquire(locking.LEVEL_NODE, ['n2'], shared=1),
665                       set(['n2']))
666     self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE),
667                       set(['n2']))
668     self.GL.release(locking.LEVEL_NODE)
669     self.GL.release(locking.LEVEL_INSTANCE)
670     self.GL.release(locking.LEVEL_CLUSTER)
671
672   def testBGLDependency(self):
673     self.assertRaises(AssertionError, self.GL.acquire,
674                       locking.LEVEL_NODE, ['n1', 'n2'])
675     self.assertRaises(AssertionError, self.GL.acquire,
676                       locking.LEVEL_INSTANCE, ['i3'])
677     self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
678     self.GL.acquire(locking.LEVEL_NODE, ['n1'])
679     self.assertRaises(AssertionError, self.GL.release,
680                       locking.LEVEL_CLUSTER, ['BGL'])
681     self.assertRaises(AssertionError, self.GL.release,
682                       locking.LEVEL_CLUSTER)
683     self.GL.release(locking.LEVEL_NODE)
684     self.GL.acquire(locking.LEVEL_INSTANCE, ['i1', 'i2'])
685     self.assertRaises(AssertionError, self.GL.release,
686                       locking.LEVEL_CLUSTER, ['BGL'])
687     self.assertRaises(AssertionError, self.GL.release,
688                       locking.LEVEL_CLUSTER)
689     self.GL.release(locking.LEVEL_INSTANCE)
690
691   def testWrongOrder(self):
692     self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
693     self.GL.acquire(locking.LEVEL_NODE, ['n2'])
694     self.assertRaises(AssertionError, self.GL.acquire,
695                       locking.LEVEL_NODE, ['n1'])
696     self.assertRaises(AssertionError, self.GL.acquire,
697                       locking.LEVEL_INSTANCE, ['i2'])
698
699   # Helper function to run as a thread that shared the BGL and then acquires
700   # some locks at another level.
701   def _doLock(self, level, names, shared):
702     try:
703       self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
704       self.GL.acquire(level, names, shared=shared)
705       self.done.put('DONE')
706       self.GL.release(level)
707       self.GL.release(locking.LEVEL_CLUSTER)
708     except errors.LockError:
709       self.done.put('ERR')
710
711   def testConcurrency(self):
712     self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
713     Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i1', 1)).start()
714     self.assertEqual(self.done.get(True, 1), 'DONE')
715     self.GL.acquire(locking.LEVEL_INSTANCE, ['i3'])
716     Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i1', 1)).start()
717     self.assertEqual(self.done.get(True, 1), 'DONE')
718     Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i3', 1)).start()
719     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
720     self.GL.release(locking.LEVEL_INSTANCE)
721     self.assertEqual(self.done.get(True, 1), 'DONE')
722     self.GL.acquire(locking.LEVEL_INSTANCE, ['i2'], shared=1)
723     Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i2', 1)).start()
724     self.assertEqual(self.done.get(True, 1), 'DONE')
725     Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i2', 0)).start()
726     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
727     self.GL.release(locking.LEVEL_INSTANCE)
728     self.assertEqual(self.done.get(True, 1), 'DONE')
729
730
731 if __name__ == '__main__':
732   unittest.main()
733   #suite = unittest.TestLoader().loadTestsFromTestCase(TestSharedLock)
734   #unittest.TextTestRunner(verbosity=2).run(suite)