Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.locking_unittest.py @ d2aff862

History | View | Annotate | Download (30.5 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.assertRaises(AssertionError, self.ls.add, 'eight', acquired=1)
358
    self.ls.remove('seven')
359
    self.assert_('seven' not in self.ls._names())
360
    self.assertEquals(self.ls._list_owned(), set([]))
361
    self.ls.acquire(None, shared=1)
362
    self.assertRaises(AssertionError, self.ls.add, 'eight')
363
    self.ls.release()
364
    self.ls.acquire(None)
365
    self.ls.add('eight', acquired=1)
366
    self.assert_('eight' in self.ls._names())
367
    self.assert_('eight' in self.ls._list_owned())
368
    self.ls.add('nine')
369
    self.assert_('nine' in self.ls._names())
370
    self.assert_('nine' not in self.ls._list_owned())
371
    self.ls.release()
372
    self.ls.remove(['two'])
373
    self.assert_('two' not in self.ls._names())
374
    self.ls.acquire('three')
375
    self.assertEquals(self.ls.remove(['three']), ['three'])
376
    self.assert_('three' not in self.ls._names())
377
    self.assertEquals(self.ls.remove('three'), [])
378
    self.assertEquals(self.ls.remove(['one', 'three', 'six']), ['one'])
379
    self.assert_('one' not in self.ls._names())
380

    
381
  def testRemoveNonBlocking(self):
382
    self.assertRaises(NotImplementedError, self.ls.remove, 'one', blocking=0)
383
    self.ls.acquire('one')
384
    self.assertEquals(self.ls.remove('one', blocking=0), ['one'])
385
    self.ls.acquire(['two', 'three'])
386
    self.assertEquals(self.ls.remove(['two', 'three'], blocking=0),
387
                      ['two', 'three'])
388

    
389
  def testNoDoubleAdd(self):
390
    self.assertRaises(errors.LockError, self.ls.add, 'two')
391
    self.ls.add('four')
392
    self.assertRaises(errors.LockError, self.ls.add, 'four')
393

    
394
  def testNoWrongRemoves(self):
395
    self.ls.acquire(['one', 'three'], shared=1)
396
    # Cannot remove 'two' while holding something which is not a superset
397
    self.assertRaises(AssertionError, self.ls.remove, 'two')
398
    # Cannot remove 'three' as we are sharing it
399
    self.assertRaises(AssertionError, self.ls.remove, 'three')
400

    
401
  def testAcquireSetLock(self):
402
    # acquire the set-lock exclusively
403
    self.assertEquals(self.ls.acquire(None), set(['one', 'two', 'three']))
404
    self.assertEquals(self.ls._list_owned(), set(['one', 'two', 'three']))
405
    self.assertEquals(self.ls._is_owned(), True)
406
    self.assertEquals(self.ls._names(), set(['one', 'two', 'three']))
407
    # I can still add/remove elements...
408
    self.assertEquals(self.ls.remove(['two', 'three']), ['two', 'three'])
409
    self.assert_(self.ls.add('six'))
410
    self.ls.release()
411
    # share the set-lock
412
    self.assertEquals(self.ls.acquire(None, shared=1), set(['one', 'six']))
413
    # adding new elements is not possible
414
    self.assertRaises(AssertionError, self.ls.add, 'five')
415
    self.ls.release()
416

    
417
  def testAcquireWithRepetitions(self):
418
    self.assertEquals(self.ls.acquire(['two', 'two', 'three'], shared=1),
419
                      set(['two', 'two', 'three']))
420
    self.ls.release(['two', 'two'])
421
    self.assertEquals(self.ls._list_owned(), set(['three']))
422

    
423
  def testEmptyAcquire(self):
424
    # Acquire an empty list of locks...
425
    self.assertEquals(self.ls.acquire([]), set())
426
    self.assertEquals(self.ls._list_owned(), set())
427
    # New locks can still be addded
428
    self.assert_(self.ls.add('six'))
429
    # "re-acquiring" is not an issue, since we had really acquired nothing
430
    self.assertEquals(self.ls.acquire([], shared=1), set())
431
    self.assertEquals(self.ls._list_owned(), set())
432
    # We haven't really acquired anything, so we cannot release
433
    self.assertRaises(AssertionError, self.ls.release)
434

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

    
443
  def _doAddSet(self, set):
444
    try:
445
      self.ls.add(set, acquired=1)
446
      self.done.put('DONE')
447
      self.ls.release()
448
    except errors.LockError:
449
      self.done.put('ERR')
450

    
451
  def _doRemoveSet(self, set):
452
    self.done.put(self.ls.remove(set))
453

    
454
  def testConcurrentSharedAcquire(self):
455
    self.ls.acquire(['one', 'two'], shared=1)
456
    Thread(target=self._doLockSet, args=(['one', 'two'], 1)).start()
457
    self.assertEqual(self.done.get(True, 1), 'DONE')
458
    Thread(target=self._doLockSet, args=(['one', 'two', 'three'], 1)).start()
459
    self.assertEqual(self.done.get(True, 1), 'DONE')
460
    Thread(target=self._doLockSet, args=('three', 1)).start()
461
    self.assertEqual(self.done.get(True, 1), 'DONE')
462
    Thread(target=self._doLockSet, args=(['one', 'two'], 0)).start()
463
    Thread(target=self._doLockSet, args=(['two', 'three'], 0)).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

    
469
  def testConcurrentExclusiveAcquire(self):
470
    self.ls.acquire(['one', 'two'])
471
    Thread(target=self._doLockSet, args=('three', 1)).start()
472
    self.assertEqual(self.done.get(True, 1), 'DONE')
473
    Thread(target=self._doLockSet, args=('three', 0)).start()
474
    self.assertEqual(self.done.get(True, 1), 'DONE')
475
    Thread(target=self._doLockSet, args=(['one', 'two'], 0)).start()
476
    Thread(target=self._doLockSet, args=(['one', 'two'], 1)).start()
477
    Thread(target=self._doLockSet, args=('one', 0)).start()
478
    Thread(target=self._doLockSet, args=('one', 1)).start()
479
    Thread(target=self._doLockSet, args=(['two', 'three'], 0)).start()
480
    Thread(target=self._doLockSet, args=(['two', 'three'], 1)).start()
481
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
482
    self.ls.release()
483
    self.assertEqual(self.done.get(True, 1), 'DONE')
484
    self.assertEqual(self.done.get(True, 1), 'DONE')
485
    self.assertEqual(self.done.get(True, 1), 'DONE')
486
    self.assertEqual(self.done.get(True, 1), 'DONE')
487
    self.assertEqual(self.done.get(True, 1), 'DONE')
488
    self.assertEqual(self.done.get(True, 1), 'DONE')
489

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

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

    
544
  def testConcurrentExclusiveSetLock(self):
545
    # acquire the set-lock...
546
    self.ls.acquire(None, shared=0)
547
    # ...no one can do anything else
548
    Thread(target=self._doLockSet, args=(None, 1)).start()
549
    Thread(target=self._doLockSet, args=(None, 0)).start()
550
    Thread(target=self._doLockSet, args=(['three'], 0)).start()
551
    Thread(target=self._doLockSet, args=(['two'], 1)).start()
552
    Thread(target=self._doAddSet, args=(['nine'])).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
    self.assertEqual(self.done.get(True, 1), 'DONE')
559
    self.assertEqual(self.done.get(True, 1), 'DONE')
560

    
561
  def testConcurrentSetLockAdd(self):
562
    self.ls.acquire('one')
563
    # Another thread wants the whole SetLock
564
    Thread(target=self._doLockSet, args=(None, 0)).start()
565
    Thread(target=self._doLockSet, args=(None, 1)).start()
566
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
567
    self.assertRaises(AssertionError, self.ls.add, 'four')
568
    self.ls.release()
569
    self.assertEqual(self.done.get(True, 1), 'DONE')
570
    self.assertEqual(self.done.get(True, 1), 'DONE')
571
    self.ls.acquire(None)
572
    Thread(target=self._doLockSet, args=(None, 0)).start()
573
    Thread(target=self._doLockSet, args=(None, 1)).start()
574
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
575
    self.ls.add('four')
576
    self.ls.add('five', acquired=1)
577
    self.ls.add('six', acquired=1, shared=1)
578
    self.assertEquals(self.ls._list_owned(),
579
      set(['one', 'two', 'three', 'five', 'six']))
580
    self.assertEquals(self.ls._is_owned(), True)
581
    self.assertEquals(self.ls._names(),
582
      set(['one', 'two', 'three', 'four', 'five', 'six']))
583
    self.ls.release()
584
    self.assertEqual(self.done.get(True, 1), 'DONE')
585
    self.assertEqual(self.done.get(True, 1), 'DONE')
586

    
587
  def testEmptyLockSet(self):
588
    # get the set-lock
589
    self.assertEqual(self.ls.acquire(None), set(['one', 'two', 'three']))
590
    # now empty it...
591
    self.ls.remove(['one', 'two', 'three'])
592
    # and adds/locks by another thread still wait
593
    Thread(target=self._doAddSet, args=(['nine'])).start()
594
    Thread(target=self._doLockSet, args=(None, 1)).start()
595
    Thread(target=self._doLockSet, args=(None, 0)).start()
596
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
597
    self.ls.release()
598
    self.assertEqual(self.done.get(True, 1), 'DONE')
599
    self.assertEqual(self.done.get(True, 1), 'DONE')
600
    self.assertEqual(self.done.get(True, 1), 'DONE')
601
    # empty it again...
602
    self.assertEqual(self.ls.remove(['nine']), ['nine'])
603
    # now share it...
604
    self.assertEqual(self.ls.acquire(None, shared=1), set())
605
    # other sharers can go, adds still wait
606
    Thread(target=self._doLockSet, args=(None, 1)).start()
607
    self.assertEqual(self.done.get(True, 1), 'DONE')
608
    Thread(target=self._doAddSet, args=(['nine'])).start()
609
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
610
    self.ls.release()
611
    self.assertEqual(self.done.get(True, 1), 'DONE')
612

    
613

    
614
class TestGanetiLockManager(unittest.TestCase):
615

    
616
  def setUp(self):
617
    self.nodes=['n1', 'n2']
618
    self.instances=['i1', 'i2', 'i3']
619
    self.GL = locking.GanetiLockManager(nodes=self.nodes,
620
                                        instances=self.instances)
621
    self.done = Queue.Queue(0)
622

    
623
  def tearDown(self):
624
    # Don't try this at home...
625
    locking.GanetiLockManager._instance = None
626

    
627
  def testLockingConstants(self):
628
    # The locking library internally cheats by assuming its constants have some
629
    # relationships with each other. Check those hold true.
630
    # This relationship is also used in the Processor to recursively acquire
631
    # the right locks. Again, please don't break it.
632
    for i in range(len(locking.LEVELS)):
633
      self.assertEqual(i, locking.LEVELS[i])
634

    
635
  def testDoubleGLFails(self):
636
    self.assertRaises(AssertionError, locking.GanetiLockManager)
637

    
638
  def testLockNames(self):
639
    self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
640
    self.assertEqual(self.GL._names(locking.LEVEL_NODE), set(self.nodes))
641
    self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE),
642
                     set(self.instances))
643

    
644
  def testInitAndResources(self):
645
    locking.GanetiLockManager._instance = None
646
    self.GL = locking.GanetiLockManager()
647
    self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
648
    self.assertEqual(self.GL._names(locking.LEVEL_NODE), set())
649
    self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set())
650

    
651
    locking.GanetiLockManager._instance = None
652
    self.GL = locking.GanetiLockManager(nodes=self.nodes)
653
    self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
654
    self.assertEqual(self.GL._names(locking.LEVEL_NODE), set(self.nodes))
655
    self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE), set())
656

    
657
    locking.GanetiLockManager._instance = None
658
    self.GL = locking.GanetiLockManager(instances=self.instances)
659
    self.assertEqual(self.GL._names(locking.LEVEL_CLUSTER), set(['BGL']))
660
    self.assertEqual(self.GL._names(locking.LEVEL_NODE), set())
661
    self.assertEqual(self.GL._names(locking.LEVEL_INSTANCE),
662
                     set(self.instances))
663

    
664
  def testAcquireRelease(self):
665
    self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
666
    self.assertEquals(self.GL._list_owned(locking.LEVEL_CLUSTER), set(['BGL']))
667
    self.GL.acquire(locking.LEVEL_INSTANCE, ['i1'])
668
    self.GL.acquire(locking.LEVEL_NODE, ['n1', 'n2'], shared=1)
669
    self.GL.release(locking.LEVEL_NODE, ['n2'])
670
    self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE), set(['n1']))
671
    self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE), set(['i1']))
672
    self.GL.release(locking.LEVEL_NODE)
673
    self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE), set())
674
    self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE), set(['i1']))
675
    self.GL.release(locking.LEVEL_INSTANCE)
676
    self.assertRaises(errors.LockError, self.GL.acquire,
677
                      locking.LEVEL_INSTANCE, ['i5'])
678
    self.GL.acquire(locking.LEVEL_INSTANCE, ['i3'], shared=1)
679
    self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE), set(['i3']))
680

    
681
  def testAcquireWholeSets(self):
682
    self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
683
    self.assertEquals(self.GL.acquire(locking.LEVEL_INSTANCE, None),
684
                      set(self.instances))
685
    self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE),
686
                      set(self.instances))
687
    self.assertEquals(self.GL.acquire(locking.LEVEL_NODE, None, shared=1),
688
                      set(self.nodes))
689
    self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE),
690
                      set(self.nodes))
691
    self.GL.release(locking.LEVEL_NODE)
692
    self.GL.release(locking.LEVEL_INSTANCE)
693
    self.GL.release(locking.LEVEL_CLUSTER)
694

    
695
  def testAcquireWholeAndPartial(self):
696
    self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
697
    self.assertEquals(self.GL.acquire(locking.LEVEL_INSTANCE, None),
698
                      set(self.instances))
699
    self.assertEquals(self.GL._list_owned(locking.LEVEL_INSTANCE),
700
                      set(self.instances))
701
    self.assertEquals(self.GL.acquire(locking.LEVEL_NODE, ['n2'], shared=1),
702
                      set(['n2']))
703
    self.assertEquals(self.GL._list_owned(locking.LEVEL_NODE),
704
                      set(['n2']))
705
    self.GL.release(locking.LEVEL_NODE)
706
    self.GL.release(locking.LEVEL_INSTANCE)
707
    self.GL.release(locking.LEVEL_CLUSTER)
708

    
709
  def testBGLDependency(self):
710
    self.assertRaises(AssertionError, self.GL.acquire,
711
                      locking.LEVEL_NODE, ['n1', 'n2'])
712
    self.assertRaises(AssertionError, self.GL.acquire,
713
                      locking.LEVEL_INSTANCE, ['i3'])
714
    self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
715
    self.GL.acquire(locking.LEVEL_NODE, ['n1'])
716
    self.assertRaises(AssertionError, self.GL.release,
717
                      locking.LEVEL_CLUSTER, ['BGL'])
718
    self.assertRaises(AssertionError, self.GL.release,
719
                      locking.LEVEL_CLUSTER)
720
    self.GL.release(locking.LEVEL_NODE)
721
    self.GL.acquire(locking.LEVEL_INSTANCE, ['i1', 'i2'])
722
    self.assertRaises(AssertionError, self.GL.release,
723
                      locking.LEVEL_CLUSTER, ['BGL'])
724
    self.assertRaises(AssertionError, self.GL.release,
725
                      locking.LEVEL_CLUSTER)
726
    self.GL.release(locking.LEVEL_INSTANCE)
727

    
728
  def testWrongOrder(self):
729
    self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
730
    self.GL.acquire(locking.LEVEL_NODE, ['n2'])
731
    self.assertRaises(AssertionError, self.GL.acquire,
732
                      locking.LEVEL_NODE, ['n1'])
733
    self.assertRaises(AssertionError, self.GL.acquire,
734
                      locking.LEVEL_INSTANCE, ['i2'])
735

    
736
  # Helper function to run as a thread that shared the BGL and then acquires
737
  # some locks at another level.
738
  def _doLock(self, level, names, shared):
739
    try:
740
      self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
741
      self.GL.acquire(level, names, shared=shared)
742
      self.done.put('DONE')
743
      self.GL.release(level)
744
      self.GL.release(locking.LEVEL_CLUSTER)
745
    except errors.LockError:
746
      self.done.put('ERR')
747

    
748
  def testConcurrency(self):
749
    self.GL.acquire(locking.LEVEL_CLUSTER, ['BGL'], shared=1)
750
    Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i1', 1)).start()
751
    self.assertEqual(self.done.get(True, 1), 'DONE')
752
    self.GL.acquire(locking.LEVEL_INSTANCE, ['i3'])
753
    Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i1', 1)).start()
754
    self.assertEqual(self.done.get(True, 1), 'DONE')
755
    Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i3', 1)).start()
756
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
757
    self.GL.release(locking.LEVEL_INSTANCE)
758
    self.assertEqual(self.done.get(True, 1), 'DONE')
759
    self.GL.acquire(locking.LEVEL_INSTANCE, ['i2'], shared=1)
760
    Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i2', 1)).start()
761
    self.assertEqual(self.done.get(True, 1), 'DONE')
762
    Thread(target=self._doLock, args=(locking.LEVEL_INSTANCE, 'i2', 0)).start()
763
    self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
764
    self.GL.release(locking.LEVEL_INSTANCE)
765
    self.assertEqual(self.done.get(True, 1), 'DONE')
766

    
767

    
768
if __name__ == '__main__':
769
  unittest.main()
770
  #suite = unittest.TestLoader().loadTestsFromTestCase(TestSharedLock)
771
  #unittest.TextTestRunner(verbosity=2).run(suite)