Add a more comment lines to testLockingConstants
[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 testEmptyAcquire(self):
407     # Acquire an empty list of locks...
408     self.assertEquals(self.ls.acquire([]), set())
409     self.assertEquals(self.ls._list_owned(), set())
410     # New locks can still be addded
411     self.assert_(self.ls.add('six'))
412     # "re-acquiring" is not an issue, since we had really acquired nothing
413     self.assertEquals(self.ls.acquire([], shared=1), set())
414     self.assertEquals(self.ls._list_owned(), set())
415     # We haven't really acquired anything, so we cannot release
416     self.assertRaises(AssertionError, self.ls.release)
417
418   def _doLockSet(self, set, shared):
419     try:
420       self.ls.acquire(set, shared=shared)
421       self.done.put('DONE')
422       self.ls.release()
423     except errors.LockError:
424       self.done.put('ERR')
425
426   def _doAddSet(self, set):
427     try:
428       self.ls.add(set, acquired=1)
429       self.done.put('DONE')
430       self.ls.release()
431     except errors.LockError:
432       self.done.put('ERR')
433
434   def _doRemoveSet(self, set):
435     self.done.put(self.ls.remove(set))
436
437   def testConcurrentSharedAcquire(self):
438     self.ls.acquire(['one', 'two'], shared=1)
439     Thread(target=self._doLockSet, args=(['one', 'two'], 1)).start()
440     self.assertEqual(self.done.get(True, 1), 'DONE')
441     Thread(target=self._doLockSet, args=(['one', 'two', 'three'], 1)).start()
442     self.assertEqual(self.done.get(True, 1), 'DONE')
443     Thread(target=self._doLockSet, args=('three', 1)).start()
444     self.assertEqual(self.done.get(True, 1), 'DONE')
445     Thread(target=self._doLockSet, args=(['one', 'two'], 0)).start()
446     Thread(target=self._doLockSet, args=(['two', 'three'], 0)).start()
447     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
448     self.ls.release()
449     self.assertEqual(self.done.get(True, 1), 'DONE')
450     self.assertEqual(self.done.get(True, 1), 'DONE')
451
452   def testConcurrentExclusiveAcquire(self):
453     self.ls.acquire(['one', 'two'])
454     Thread(target=self._doLockSet, args=('three', 1)).start()
455     self.assertEqual(self.done.get(True, 1), 'DONE')
456     Thread(target=self._doLockSet, args=('three', 0)).start()
457     self.assertEqual(self.done.get(True, 1), 'DONE')
458     Thread(target=self._doLockSet, args=(['one', 'two'], 0)).start()
459     Thread(target=self._doLockSet, args=(['one', 'two'], 1)).start()
460     Thread(target=self._doLockSet, args=('one', 0)).start()
461     Thread(target=self._doLockSet, args=('one', 1)).start()
462     Thread(target=self._doLockSet, args=(['two', 'three'], 0)).start()
463     Thread(target=self._doLockSet, args=(['two', 'three'], 1)).start()
464     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
465     self.ls.release()
466     self.assertEqual(self.done.get(True, 1), 'DONE')
467     self.assertEqual(self.done.get(True, 1), 'DONE')
468     self.assertEqual(self.done.get(True, 1), 'DONE')
469     self.assertEqual(self.done.get(True, 1), 'DONE')
470     self.assertEqual(self.done.get(True, 1), 'DONE')
471     self.assertEqual(self.done.get(True, 1), 'DONE')
472
473   def testConcurrentRemove(self):
474     self.ls.add('four')
475     self.ls.acquire(['one', 'two', 'four'])
476     Thread(target=self._doLockSet, args=(['one', 'four'], 0)).start()
477     Thread(target=self._doLockSet, args=(['one', 'four'], 1)).start()
478     Thread(target=self._doLockSet, args=(['one', 'two'], 0)).start()
479     Thread(target=self._doLockSet, args=(['one', 'two'], 1)).start()
480     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
481     self.ls.remove('one')
482     self.ls.release()
483     self.assertEqual(self.done.get(True, 1), 'ERR')
484     self.assertEqual(self.done.get(True, 1), 'ERR')
485     self.assertEqual(self.done.get(True, 1), 'ERR')
486     self.assertEqual(self.done.get(True, 1), 'ERR')
487     self.ls.add(['five', 'six'], acquired=1)
488     Thread(target=self._doLockSet, args=(['three', 'six'], 1)).start()
489     Thread(target=self._doLockSet, args=(['three', 'six'], 0)).start()
490     Thread(target=self._doLockSet, args=(['four', 'six'], 1)).start()
491     Thread(target=self._doLockSet, args=(['four', 'six'], 0)).start()
492     self.ls.remove('five')
493     self.ls.release()
494     self.assertEqual(self.done.get(True, 1), 'DONE')
495     self.assertEqual(self.done.get(True, 1), 'DONE')
496     self.assertEqual(self.done.get(True, 1), 'DONE')
497     self.assertEqual(self.done.get(True, 1), 'DONE')
498     self.ls.acquire(['three', 'four'])
499     Thread(target=self._doRemoveSet, args=(['four', 'six'], )).start()
500     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
501     self.ls.remove('four')
502     self.assertEqual(self.done.get(True, 1), ['six'])
503     Thread(target=self._doRemoveSet, args=(['two'])).start()
504     self.assertEqual(self.done.get(True, 1), ['two'])
505     self.ls.release()
506
507   def testConcurrentSharedSetLock(self):
508     # share the set-lock...
509     self.ls.acquire(None, shared=1)
510     # ...another thread can share it too
511     Thread(target=self._doLockSet, args=(None, 1)).start()
512     self.assertEqual(self.done.get(True, 1), 'DONE')
513     # ...or just share some elements
514     Thread(target=self._doLockSet, args=(['one', 'three'], 1)).start()
515     self.assertEqual(self.done.get(True, 1), 'DONE')
516     # ...but not add new ones or remove any
517     Thread(target=self._doAddSet, args=(['nine'])).start()
518     Thread(target=self._doRemoveSet, args=(['two'], )).start()
519     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
520     # this just releases the set-lock
521     self.ls.release([])
522     self.assertEqual(self.done.get(True, 1), 'DONE')
523     # release the lock on the actual elements so remove() can proceed too
524     self.ls.release()
525     self.assertEqual(self.done.get(True, 1), ['two'])
526
527   def testConcurrentExclusiveSetLock(self):
528     # acquire the set-lock...
529     self.ls.acquire(None, shared=0)
530     # ...no one can do anything else
531     Thread(target=self._doLockSet, args=(None, 1)).start()
532     Thread(target=self._doLockSet, args=(None, 0)).start()
533     Thread(target=self._doLockSet, args=(['three'], 0)).start()
534     Thread(target=self._doLockSet, args=(['two'], 1)).start()
535     Thread(target=self._doAddSet, args=(['nine'])).start()
536     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
537     self.ls.release()
538     self.assertEqual(self.done.get(True, 1), 'DONE')
539     self.assertEqual(self.done.get(True, 1), 'DONE')
540     self.assertEqual(self.done.get(True, 1), 'DONE')
541     self.assertEqual(self.done.get(True, 1), 'DONE')
542     self.assertEqual(self.done.get(True, 1), 'DONE')
543
544   def testEmptyLockSet(self):
545     # get the set-lock
546     self.assertEqual(self.ls.acquire(None), set(['one', 'two', 'three']))
547     # now empty it...
548     self.ls.remove(['one', 'two', 'three'])
549     # and adds/locks by another thread still wait
550     Thread(target=self._doAddSet, args=(['nine'])).start()
551     Thread(target=self._doLockSet, args=(None, 1)).start()
552     Thread(target=self._doLockSet, args=(None, 0)).start()
553     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
554     self.ls.release()
555     self.assertEqual(self.done.get(True, 1), 'DONE')
556     self.assertEqual(self.done.get(True, 1), 'DONE')
557     self.assertEqual(self.done.get(True, 1), 'DONE')
558     # empty it again...
559     self.assertEqual(self.ls.remove(['nine']), ['nine'])
560     # now share it...
561     self.assertEqual(self.ls.acquire(None, shared=1), set())
562     # other sharers can go, adds still wait
563     Thread(target=self._doLockSet, args=(None, 1)).start()
564     self.assertEqual(self.done.get(True, 1), 'DONE')
565     Thread(target=self._doAddSet, args=(['nine'])).start()
566     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
567     self.ls.release()
568     self.assertEqual(self.done.get(True, 1), 'DONE')
569
570
571 class TestGanetiLockManager(unittest.TestCase):
572
573   def setUp(self):
574     self.nodes=['n1', 'n2']
575     self.instances=['i1', 'i2', 'i3']
576     self.GL = locking.GanetiLockManager(nodes=self.nodes,
577                                         instances=self.instances)
578     self.done = Queue.Queue(0)
579
580   def tearDown(self):
581     # Don't try this at home...
582     locking.GanetiLockManager._instance = None
583
584   def testLockingConstants(self):
585     # The locking library internally cheats by assuming its constants have some
586     # relationships with each other. Check those hold true.
587     # This relationship is also used in the Processor to recursively acquire
588     # the right locks. Again, please don't break it.
589     for i in range(len(locking.LEVELS)):
590       self.assertEqual(i, locking.LEVELS[i])
591
592   def testDoubleGLFails(self):
593     self.assertRaises(AssertionError, locking.GanetiLockManager)
594
595   def testLockNames(self):
596     self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
597     self.assertEqual(self.GL._names(locking.LEVEL_NODE), set(self.nodes))
598     self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE),
599                      set(self.instances))
600
601   def testInitAndResources(self):
602     locking.GanetiLockManager._instance = None
603     self.GL = locking.GanetiLockManager()
604     self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
605     self.assertEqual(self.GL._names(locking.LEVEL_NODE), set())
606     self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set())
607
608     locking.GanetiLockManager._instance = None
609     self.GL = locking.GanetiLockManager(nodes=self.nodes)
610     self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
611     self.assertEqual(self.GL._names(locking.LEVEL_NODE), set(self.nodes))
612     self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set())
613
614     locking.GanetiLockManager._instance = None
615     self.GL = locking.GanetiLockManager(instances=self.instances)
616     self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
617     self.assertEqual(self.GL._names(locking.LEVEL_NODE), set())
618     self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE),
619                      set(self.instances))
620
621   def testAcquireRelease(self):
622     self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
623     self.assertEquals(self.GL._list_owned(locking.LEVEL_CLUSTER), set(['BGL']))
624     self.GL.acquire(locking.LEVEL_NODE, ['n1', 'n2'], shared=1)
625     self.GL.release(locking.LEVEL_NODE)
626     self.GL.acquire(locking.LEVEL_NODE, ['n1'])
627     self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE), set(['n1']))
628     self.GL.acquire(locking.LEVEL_INSTANCE, ['i1', 'i2'])
629     self.GL.release(locking.LEVEL_INSTANCE, ['i2'])
630     self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE), set(['i1']))
631     self.GL.release(locking.LEVEL_NODE)
632     self.GL.release(locking.LEVEL_INSTANCE)
633     self.assertRaises(errors.LockError, self.GL.acquire,
634                       locking.LEVEL_INSTANCE, ['i5'])
635     self.GL.acquire(locking.LEVEL_INSTANCE, ['i3'], shared=1)
636     self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE), set(['i3']))
637
638   def testBGLDependency(self):
639     self.assertRaises(AssertionError, self.GL.acquire,
640                       locking.LEVEL_NODE, ['n1', 'n2'])
641     self.assertRaises(AssertionError, self.GL.acquire,
642                       locking.LEVEL_INSTANCE, ['i3'])
643     self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
644     self.GL.acquire(locking.LEVEL_NODE, ['n1'])
645     self.assertRaises(AssertionError, self.GL.release,
646                       locking.LEVEL_CLUSTER, ['BGL'])
647     self.assertRaises(AssertionError, self.GL.release,
648                       locking.LEVEL_CLUSTER)
649     self.GL.release(locking.LEVEL_NODE)
650     self.GL.acquire(locking.LEVEL_INSTANCE, ['i1', 'i2'])
651     self.assertRaises(AssertionError, self.GL.release,
652                       locking.LEVEL_CLUSTER, ['BGL'])
653     self.assertRaises(AssertionError, self.GL.release,
654                       locking.LEVEL_CLUSTER)
655     self.GL.release(locking.LEVEL_INSTANCE)
656
657   def testWrongOrder(self):
658     self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
659     self.GL.acquire(locking.LEVEL_INSTANCE, ['i3'])
660     self.assertRaises(AssertionError, self.GL.acquire,
661                       locking.LEVEL_NODE, ['n1'])
662     self.assertRaises(AssertionError, self.GL.acquire,
663                       locking.LEVEL_INSTANCE, ['i2'])
664
665   # Helper function to run as a thread that shared the BGL and then acquires
666   # some locks at another level.
667   def _doLock(self, level, names, shared):
668     try:
669       self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
670       self.GL.acquire(level, names, shared=shared)
671       self.done.put('DONE')
672       self.GL.release(level)
673       self.GL.release(locking.LEVEL_CLUSTER)
674     except errors.LockError:
675       self.done.put('ERR')
676
677   def testConcurrency(self):
678     self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
679     Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i1', 1)).start()
680     self.assertEqual(self.done.get(True, 1), 'DONE')
681     self.GL.acquire(locking.LEVEL_NODE, ['n1'])
682     self.GL.acquire(locking.LEVEL_INSTANCE, ['i3'])
683     Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i1', 1)).start()
684     self.assertEqual(self.done.get(True, 1), 'DONE')
685     Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i3', 1)).start()
686     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
687     self.GL.release(locking.LEVEL_INSTANCE)
688     self.assertEqual(self.done.get(True, 1), 'DONE')
689     self.GL.acquire(locking.LEVEL_INSTANCE, ['i2'], shared=1)
690     Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i2', 1)).start()
691     self.assertEqual(self.done.get(True, 1), 'DONE')
692     Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i2', 0)).start()
693     self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
694     self.GL.release(locking.LEVEL_INSTANCE)
695     self.assertEqual(self.done.get(True, 1), 'DONE')
696
697
698 if __name__ == '__main__':
699   unittest.main()
700   #suite = unittest.TestLoader().loadTestsFromTestCase(TestSharedLock)
701   #unittest.TextTestRunner(verbosity=2).run(suite)