Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.locking_unittest.py @ d4803c24

History | View | Annotate | Download (29.1 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
    self.assertEquals(self.ls._list_owned(), set(['one', 'two', 'three']))
397
    self.assertEquals(self.ls._is_owned(), True)
398
    self.assertEquals(self.ls._names(), set(['one', 'two', 'three']))
399
    # I can still add/remove elements...
400
    self.assertEquals(self.ls.remove(['two', 'three']), ['two', 'three'])
401
    self.assert_(self.ls.add('six'))
402
    self.ls.release()
403
    # share the set-lock
404
    self.assertEquals(self.ls.acquire(None, shared=1), set(['one', 'six']))
405
    # adding new elements is not possible
406
    self.assertRaises(AssertionError, self.ls.add, 'five')
407
    self.ls.release()
408

    
409
  def testAcquireWithRepetitions(self):
410
    self.assertEquals(self.ls.acquire(['two', 'two', 'three'], shared=1),
411
                      set(['two', 'two', 'three']))
412
    self.ls.release(['two', 'two'])
413
    self.assertEquals(self.ls._list_owned(), set(['three']))
414

    
415
  def testEmptyAcquire(self):
416
    # Acquire an empty list of locks...
417
    self.assertEquals(self.ls.acquire([]), set())
418
    self.assertEquals(self.ls._list_owned(), set())
419
    # New locks can still be addded
420
    self.assert_(self.ls.add('six'))
421
    # "re-acquiring" is not an issue, since we had really acquired nothing
422
    self.assertEquals(self.ls.acquire([], shared=1), set())
423
    self.assertEquals(self.ls._list_owned(), set())
424
    # We haven't really acquired anything, so we cannot release
425
    self.assertRaises(AssertionError, self.ls.release)
426

    
427
  def _doLockSet(self, set, shared):
428
    try:
429
      self.ls.acquire(set, shared=shared)
430
      self.done.put('DONE')
431
      self.ls.release()
432
    except errors.LockError:
433
      self.done.put('ERR')
434

    
435
  def _doAddSet(self, set):
436
    try:
437
      self.ls.add(set, acquired=1)
438
      self.done.put('DONE')
439
      self.ls.release()
440
    except errors.LockError:
441
      self.done.put('ERR')
442

    
443
  def _doRemoveSet(self, set):
444
    self.done.put(self.ls.remove(set))
445

    
446
  def testConcurrentSharedAcquire(self):
447
    self.ls.acquire(['one', 'two'], shared=1)
448
    Thread(target=self._doLockSet, args=(['one', 'two'], 1)).start()
449
    self.assertEqual(self.done.get(True, 1), 'DONE')
450
    Thread(target=self._doLockSet, args=(['one', 'two', 'three'], 1)).start()
451
    self.assertEqual(self.done.get(True, 1), 'DONE')
452
    Thread(target=self._doLockSet, args=('three', 1)).start()
453
    self.assertEqual(self.done.get(True, 1), 'DONE')
454
    Thread(target=self._doLockSet, args=(['one', 'two'], 0)).start()
455
    Thread(target=self._doLockSet, args=(['two', 'three'], 0)).start()
456
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
457
    self.ls.release()
458
    self.assertEqual(self.done.get(True, 1), 'DONE')
459
    self.assertEqual(self.done.get(True, 1), 'DONE')
460

    
461
  def testConcurrentExclusiveAcquire(self):
462
    self.ls.acquire(['one', 'two'])
463
    Thread(target=self._doLockSet, args=('three', 1)).start()
464
    self.assertEqual(self.done.get(True, 1), 'DONE')
465
    Thread(target=self._doLockSet, args=('three', 0)).start()
466
    self.assertEqual(self.done.get(True, 1), 'DONE')
467
    Thread(target=self._doLockSet, args=(['one', 'two'], 0)).start()
468
    Thread(target=self._doLockSet, args=(['one', 'two'], 1)).start()
469
    Thread(target=self._doLockSet, args=('one', 0)).start()
470
    Thread(target=self._doLockSet, args=('one', 1)).start()
471
    Thread(target=self._doLockSet, args=(['two', 'three'], 0)).start()
472
    Thread(target=self._doLockSet, args=(['two', 'three'], 1)).start()
473
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
474
    self.ls.release()
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
    self.assertEqual(self.done.get(True, 1), 'DONE')
479
    self.assertEqual(self.done.get(True, 1), 'DONE')
480
    self.assertEqual(self.done.get(True, 1), 'DONE')
481

    
482
  def testConcurrentRemove(self):
483
    self.ls.add('four')
484
    self.ls.acquire(['one', 'two', 'four'])
485
    Thread(target=self._doLockSet, args=(['one', 'four'], 0)).start()
486
    Thread(target=self._doLockSet, args=(['one', 'four'], 1)).start()
487
    Thread(target=self._doLockSet, args=(['one', 'two'], 0)).start()
488
    Thread(target=self._doLockSet, args=(['one', 'two'], 1)).start()
489
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
490
    self.ls.remove('one')
491
    self.ls.release()
492
    self.assertEqual(self.done.get(True, 1), 'ERR')
493
    self.assertEqual(self.done.get(True, 1), 'ERR')
494
    self.assertEqual(self.done.get(True, 1), 'ERR')
495
    self.assertEqual(self.done.get(True, 1), 'ERR')
496
    self.ls.add(['five', 'six'], acquired=1)
497
    Thread(target=self._doLockSet, args=(['three', 'six'], 1)).start()
498
    Thread(target=self._doLockSet, args=(['three', 'six'], 0)).start()
499
    Thread(target=self._doLockSet, args=(['four', 'six'], 1)).start()
500
    Thread(target=self._doLockSet, args=(['four', 'six'], 0)).start()
501
    self.ls.remove('five')
502
    self.ls.release()
503
    self.assertEqual(self.done.get(True, 1), 'DONE')
504
    self.assertEqual(self.done.get(True, 1), 'DONE')
505
    self.assertEqual(self.done.get(True, 1), 'DONE')
506
    self.assertEqual(self.done.get(True, 1), 'DONE')
507
    self.ls.acquire(['three', 'four'])
508
    Thread(target=self._doRemoveSet, args=(['four', 'six'], )).start()
509
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
510
    self.ls.remove('four')
511
    self.assertEqual(self.done.get(True, 1), ['six'])
512
    Thread(target=self._doRemoveSet, args=(['two'])).start()
513
    self.assertEqual(self.done.get(True, 1), ['two'])
514
    self.ls.release()
515

    
516
  def testConcurrentSharedSetLock(self):
517
    # share the set-lock...
518
    self.ls.acquire(None, shared=1)
519
    # ...another thread can share it too
520
    Thread(target=self._doLockSet, args=(None, 1)).start()
521
    self.assertEqual(self.done.get(True, 1), 'DONE')
522
    # ...or just share some elements
523
    Thread(target=self._doLockSet, args=(['one', 'three'], 1)).start()
524
    self.assertEqual(self.done.get(True, 1), 'DONE')
525
    # ...but not add new ones or remove any
526
    Thread(target=self._doAddSet, args=(['nine'])).start()
527
    Thread(target=self._doRemoveSet, args=(['two'], )).start()
528
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
529
    # this just releases the set-lock
530
    self.ls.release([])
531
    self.assertEqual(self.done.get(True, 1), 'DONE')
532
    # release the lock on the actual elements so remove() can proceed too
533
    self.ls.release()
534
    self.assertEqual(self.done.get(True, 1), ['two'])
535

    
536
  def testConcurrentExclusiveSetLock(self):
537
    # acquire the set-lock...
538
    self.ls.acquire(None, shared=0)
539
    # ...no one can do anything else
540
    Thread(target=self._doLockSet, args=(None, 1)).start()
541
    Thread(target=self._doLockSet, args=(None, 0)).start()
542
    Thread(target=self._doLockSet, args=(['three'], 0)).start()
543
    Thread(target=self._doLockSet, args=(['two'], 1)).start()
544
    Thread(target=self._doAddSet, args=(['nine'])).start()
545
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
546
    self.ls.release()
547
    self.assertEqual(self.done.get(True, 1), 'DONE')
548
    self.assertEqual(self.done.get(True, 1), 'DONE')
549
    self.assertEqual(self.done.get(True, 1), 'DONE')
550
    self.assertEqual(self.done.get(True, 1), 'DONE')
551
    self.assertEqual(self.done.get(True, 1), 'DONE')
552

    
553
  def testEmptyLockSet(self):
554
    # get the set-lock
555
    self.assertEqual(self.ls.acquire(None), set(['one', 'two', 'three']))
556
    # now empty it...
557
    self.ls.remove(['one', 'two', 'three'])
558
    # and adds/locks by another thread still wait
559
    Thread(target=self._doAddSet, args=(['nine'])).start()
560
    Thread(target=self._doLockSet, args=(None, 1)).start()
561
    Thread(target=self._doLockSet, args=(None, 0)).start()
562
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
563
    self.ls.release()
564
    self.assertEqual(self.done.get(True, 1), 'DONE')
565
    self.assertEqual(self.done.get(True, 1), 'DONE')
566
    self.assertEqual(self.done.get(True, 1), 'DONE')
567
    # empty it again...
568
    self.assertEqual(self.ls.remove(['nine']), ['nine'])
569
    # now share it...
570
    self.assertEqual(self.ls.acquire(None, shared=1), set())
571
    # other sharers can go, adds still wait
572
    Thread(target=self._doLockSet, args=(None, 1)).start()
573
    self.assertEqual(self.done.get(True, 1), 'DONE')
574
    Thread(target=self._doAddSet, args=(['nine'])).start()
575
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
576
    self.ls.release()
577
    self.assertEqual(self.done.get(True, 1), 'DONE')
578

    
579

    
580
class TestGanetiLockManager(unittest.TestCase):
581

    
582
  def setUp(self):
583
    self.nodes=['n1', 'n2']
584
    self.instances=['i1', 'i2', 'i3']
585
    self.GL = locking.GanetiLockManager(nodes=self.nodes,
586
                                        instances=self.instances)
587
    self.done = Queue.Queue(0)
588

    
589
  def tearDown(self):
590
    # Don't try this at home...
591
    locking.GanetiLockManager._instance = None
592

    
593
  def testLockingConstants(self):
594
    # The locking library internally cheats by assuming its constants have some
595
    # relationships with each other. Check those hold true.
596
    # This relationship is also used in the Processor to recursively acquire
597
    # the right locks. Again, please don't break it.
598
    for i in range(len(locking.LEVELS)):
599
      self.assertEqual(i, locking.LEVELS[i])
600

    
601
  def testDoubleGLFails(self):
602
    self.assertRaises(AssertionError, locking.GanetiLockManager)
603

    
604
  def testLockNames(self):
605
    self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
606
    self.assertEqual(self.GL._names(locking.LEVEL_NODE), set(self.nodes))
607
    self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE),
608
                     set(self.instances))
609

    
610
  def testInitAndResources(self):
611
    locking.GanetiLockManager._instance = None
612
    self.GL = locking.GanetiLockManager()
613
    self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
614
    self.assertEqual(self.GL._names(locking.LEVEL_NODE), set())
615
    self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set())
616

    
617
    locking.GanetiLockManager._instance = None
618
    self.GL = locking.GanetiLockManager(nodes=self.nodes)
619
    self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
620
    self.assertEqual(self.GL._names(locking.LEVEL_NODE), set(self.nodes))
621
    self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set())
622

    
623
    locking.GanetiLockManager._instance = None
624
    self.GL = locking.GanetiLockManager(instances=self.instances)
625
    self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
626
    self.assertEqual(self.GL._names(locking.LEVEL_NODE), set())
627
    self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE),
628
                     set(self.instances))
629

    
630
  def testAcquireRelease(self):
631
    self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
632
    self.assertEquals(self.GL._list_owned(locking.LEVEL_CLUSTER), set(['BGL']))
633
    self.GL.acquire(locking.LEVEL_INSTANCE, ['i1'])
634
    self.GL.acquire(locking.LEVEL_NODE, ['n1', 'n2'], shared=1)
635
    self.GL.release(locking.LEVEL_NODE, ['n2'])
636
    self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE), set(['n1']))
637
    self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE), set(['i1']))
638
    self.GL.release(locking.LEVEL_NODE)
639
    self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE), set())
640
    self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE), set(['i1']))
641
    self.GL.release(locking.LEVEL_INSTANCE)
642
    self.assertRaises(errors.LockError, self.GL.acquire,
643
                      locking.LEVEL_INSTANCE, ['i5'])
644
    self.GL.acquire(locking.LEVEL_INSTANCE, ['i3'], shared=1)
645
    self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE), set(['i3']))
646

    
647
  def testAcquireWholeSets(self):
648
    self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
649
    self.assertEquals(self.GL.acquire(locking.LEVEL_INSTANCE, None),
650
                      set(self.instances))
651
    self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE),
652
                      set(self.instances))
653
    self.assertEquals(self.GL.acquire(locking.LEVEL_NODE, None, shared=1),
654
                      set(self.nodes))
655
    self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE),
656
                      set(self.nodes))
657
    self.GL.release(locking.LEVEL_NODE)
658
    self.GL.release(locking.LEVEL_INSTANCE)
659
    self.GL.release(locking.LEVEL_CLUSTER)
660

    
661
  def testAcquireWholeAndPartial(self):
662
    self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
663
    self.assertEquals(self.GL.acquire(locking.LEVEL_INSTANCE, None),
664
                      set(self.instances))
665
    self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE),
666
                      set(self.instances))
667
    self.assertEquals(self.GL.acquire(locking.LEVEL_NODE, ['n2'], shared=1),
668
                      set(['n2']))
669
    self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE),
670
                      set(['n2']))
671
    self.GL.release(locking.LEVEL_NODE)
672
    self.GL.release(locking.LEVEL_INSTANCE)
673
    self.GL.release(locking.LEVEL_CLUSTER)
674

    
675
  def testBGLDependency(self):
676
    self.assertRaises(AssertionError, self.GL.acquire,
677
                      locking.LEVEL_NODE, ['n1', 'n2'])
678
    self.assertRaises(AssertionError, self.GL.acquire,
679
                      locking.LEVEL_INSTANCE, ['i3'])
680
    self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
681
    self.GL.acquire(locking.LEVEL_NODE, ['n1'])
682
    self.assertRaises(AssertionError, self.GL.release,
683
                      locking.LEVEL_CLUSTER, ['BGL'])
684
    self.assertRaises(AssertionError, self.GL.release,
685
                      locking.LEVEL_CLUSTER)
686
    self.GL.release(locking.LEVEL_NODE)
687
    self.GL.acquire(locking.LEVEL_INSTANCE, ['i1', 'i2'])
688
    self.assertRaises(AssertionError, self.GL.release,
689
                      locking.LEVEL_CLUSTER, ['BGL'])
690
    self.assertRaises(AssertionError, self.GL.release,
691
                      locking.LEVEL_CLUSTER)
692
    self.GL.release(locking.LEVEL_INSTANCE)
693

    
694
  def testWrongOrder(self):
695
    self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
696
    self.GL.acquire(locking.LEVEL_NODE, ['n2'])
697
    self.assertRaises(AssertionError, self.GL.acquire,
698
                      locking.LEVEL_NODE, ['n1'])
699
    self.assertRaises(AssertionError, self.GL.acquire,
700
                      locking.LEVEL_INSTANCE, ['i2'])
701

    
702
  # Helper function to run as a thread that shared the BGL and then acquires
703
  # some locks at another level.
704
  def _doLock(self, level, names, shared):
705
    try:
706
      self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
707
      self.GL.acquire(level, names, shared=shared)
708
      self.done.put('DONE')
709
      self.GL.release(level)
710
      self.GL.release(locking.LEVEL_CLUSTER)
711
    except errors.LockError:
712
      self.done.put('ERR')
713

    
714
  def testConcurrency(self):
715
    self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
716
    Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i1', 1)).start()
717
    self.assertEqual(self.done.get(True, 1), 'DONE')
718
    self.GL.acquire(locking.LEVEL_INSTANCE, ['i3'])
719
    Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i1', 1)).start()
720
    self.assertEqual(self.done.get(True, 1), 'DONE')
721
    Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i3', 1)).start()
722
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
723
    self.GL.release(locking.LEVEL_INSTANCE)
724
    self.assertEqual(self.done.get(True, 1), 'DONE')
725
    self.GL.acquire(locking.LEVEL_INSTANCE, ['i2'], shared=1)
726
    Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i2', 1)).start()
727
    self.assertEqual(self.done.get(True, 1), 'DONE')
728
    Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i2', 0)).start()
729
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
730
    self.GL.release(locking.LEVEL_INSTANCE)
731
    self.assertEqual(self.done.get(True, 1), 'DONE')
732

    
733

    
734
if __name__ == '__main__':
735
  unittest.main()
736
  #suite = unittest.TestLoader().loadTestsFromTestCase(TestSharedLock)
737
  #unittest.TextTestRunner(verbosity=2).run(suite)