Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.locking_unittest.py @ c54784d9

History | View | Annotate | Download (27.4 kB)

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_INSTANCE, ['i1'])
625
    self.GL.acquire(locking.LEVEL_NODE, ['n1', 'n2'], shared=1)
626
    self.GL.release(locking.LEVEL_NODE, ['n2'])
627
    self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE), set(['n1']))
628
    self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE), set(['i1']))
629
    self.GL.release(locking.LEVEL_NODE)
630
    self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE), set())
631
    self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE), set(['i1']))
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_NODE, ['n2'])
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_INSTANCE, ['i3'])
682
    Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i1', 1)).start()
683
    self.assertEqual(self.done.get(True, 1), 'DONE')
684
    Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i3', 1)).start()
685
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
686
    self.GL.release(locking.LEVEL_INSTANCE)
687
    self.assertEqual(self.done.get(True, 1), 'DONE')
688
    self.GL.acquire(locking.LEVEL_INSTANCE, ['i2'], shared=1)
689
    Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i2', 1)).start()
690
    self.assertEqual(self.done.get(True, 1), 'DONE')
691
    Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i2', 0)).start()
692
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
693
    self.GL.release(locking.LEVEL_INSTANCE)
694
    self.assertEqual(self.done.get(True, 1), 'DONE')
695

    
696

    
697
if __name__ == '__main__':
698
  unittest.main()
699
  #suite = unittest.TestLoader().loadTestsFromTestCase(TestSharedLock)
700
  #unittest.TextTestRunner(verbosity=2).run(suite)