Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.locking_unittest.py @ d4f6a91c

History | View | Annotate | Download (28.9 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 testAcquireWithRepetitions(self):
407
    self.assertEquals(self.ls.acquire(['two', 'two', 'three'], shared=1),
408
                      set(['two', 'two', 'three']))
409
    self.ls.release(['two', 'two'])
410
    self.assertEquals(self.ls._list_owned(), set(['three']))
411

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

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

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

    
440
  def _doRemoveSet(self, set):
441
    self.done.put(self.ls.remove(set))
442

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

    
458
  def testConcurrentExclusiveAcquire(self):
459
    self.ls.acquire(['one', 'two'])
460
    Thread(target=self._doLockSet, args=('three', 1)).start()
461
    self.assertEqual(self.done.get(True, 1), 'DONE')
462
    Thread(target=self._doLockSet, args=('three', 0)).start()
463
    self.assertEqual(self.done.get(True, 1), 'DONE')
464
    Thread(target=self._doLockSet, args=(['one', 'two'], 0)).start()
465
    Thread(target=self._doLockSet, args=(['one', 'two'], 1)).start()
466
    Thread(target=self._doLockSet, args=('one', 0)).start()
467
    Thread(target=self._doLockSet, args=('one', 1)).start()
468
    Thread(target=self._doLockSet, args=(['two', 'three'], 0)).start()
469
    Thread(target=self._doLockSet, args=(['two', 'three'], 1)).start()
470
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
471
    self.ls.release()
472
    self.assertEqual(self.done.get(True, 1), 'DONE')
473
    self.assertEqual(self.done.get(True, 1), 'DONE')
474
    self.assertEqual(self.done.get(True, 1), 'DONE')
475
    self.assertEqual(self.done.get(True, 1), 'DONE')
476
    self.assertEqual(self.done.get(True, 1), 'DONE')
477
    self.assertEqual(self.done.get(True, 1), 'DONE')
478

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

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

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

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

    
576

    
577
class TestGanetiLockManager(unittest.TestCase):
578

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

    
586
  def tearDown(self):
587
    # Don't try this at home...
588
    locking.GanetiLockManager._instance = None
589

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

    
598
  def testDoubleGLFails(self):
599
    self.assertRaises(AssertionError, locking.GanetiLockManager)
600

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

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

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

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

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

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

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

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

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

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

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

    
730

    
731
if __name__ == '__main__':
732
  unittest.main()
733
  #suite = unittest.TestLoader().loadTestsFromTestCase(TestSharedLock)
734
  #unittest.TextTestRunner(verbosity=2).run(suite)