Statistics
| Branch: | Tag: | Revision:

root / lib / locking.py @ 9a39f854

History | View | Annotate | Download (27.4 kB)

1
#
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
# 02110-1301, USA.
20

    
21
"""Module implementing the Ganeti locking code."""
22

    
23
# pylint: disable-msg=W0613,W0201
24

    
25
import threading
26
# Wouldn't it be better to define LockingError in the locking module?
27
# Well, for now that's how the rest of the code does it...
28
from ganeti import errors
29
from ganeti import utils
30

    
31

    
32
class SharedLock:
33
  """Implements a shared lock.
34

35
  Multiple threads can acquire the lock in a shared way, calling
36
  acquire_shared().  In order to acquire the lock in an exclusive way threads
37
  can call acquire_exclusive().
38

39
  The lock prevents starvation but does not guarantee that threads will acquire
40
  the shared lock in the order they queued for it, just that they will
41
  eventually do so.
42

43
  """
44
  def __init__(self):
45
    """Construct a new SharedLock"""
46
    # we have two conditions, c_shr and c_exc, sharing the same lock.
47
    self.__lock = threading.Lock()
48
    self.__turn_shr = threading.Condition(self.__lock)
49
    self.__turn_exc = threading.Condition(self.__lock)
50

    
51
    # current lock holders
52
    self.__shr = set()
53
    self.__exc = None
54

    
55
    # lock waiters
56
    self.__nwait_exc = 0
57
    self.__nwait_shr = 0
58

    
59
    # is this lock in the deleted state?
60
    self.__deleted = False
61

    
62
  def __is_sharer(self):
63
    """Is the current thread sharing the lock at this time?"""
64
    return threading.currentThread() in self.__shr
65

    
66
  def __is_exclusive(self):
67
    """Is the current thread holding the lock exclusively at this time?"""
68
    return threading.currentThread() == self.__exc
69

    
70
  def __is_owned(self, shared=-1):
71
    """Is the current thread somehow owning the lock at this time?
72

73
    This is a private version of the function, which presumes you're holding
74
    the internal lock.
75

76
    """
77
    if shared < 0:
78
      return self.__is_sharer() or self.__is_exclusive()
79
    elif shared:
80
      return self.__is_sharer()
81
    else:
82
      return self.__is_exclusive()
83

    
84
  def _is_owned(self, shared=-1):
85
    """Is the current thread somehow owning the lock at this time?
86

87
    Args:
88
      shared:
89
        < 0: check for any type of ownership (default)
90
        0: check for exclusive ownership
91
        > 0: check for shared ownership
92

93
    """
94
    self.__lock.acquire()
95
    try:
96
      result = self.__is_owned(shared=shared)
97
    finally:
98
      self.__lock.release()
99

    
100
    return result
101

    
102
  def __wait(self, c):
103
    """Wait on the given condition, and raise an exception if the current lock
104
    is declared deleted in the meantime.
105

106
    Args:
107
      c: condition to wait on
108

109
    """
110
    c.wait()
111
    if self.__deleted:
112
      raise errors.LockError('deleted lock')
113

    
114
  def __exclusive_acquire(self):
115
    """Acquire the lock exclusively.
116

117
    This is a private function that presumes you are already holding the
118
    internal lock. It's defined separately to avoid code duplication between
119
    acquire() and delete()
120

121
    """
122
    self.__nwait_exc += 1
123
    try:
124
      # This is to save ourselves from a nasty race condition that could
125
      # theoretically make the sharers starve.
126
      if self.__nwait_shr > 0 or self.__nwait_exc > 1:
127
        self.__wait(self.__turn_exc)
128

    
129
      while len(self.__shr) > 0 or self.__exc is not None:
130
        self.__wait(self.__turn_exc)
131

    
132
      self.__exc = threading.currentThread()
133
    finally:
134
      self.__nwait_exc -= 1
135

    
136
  def acquire(self, blocking=1, shared=0):
137
    """Acquire a shared lock.
138

139
    Args:
140
      shared: whether to acquire in shared mode. By default an exclusive lock
141
              will be acquired.
142
      blocking: whether to block while trying to acquire or to operate in try-lock mode.
143
                this locking mode is not supported yet.
144

145
    """
146
    if not blocking:
147
      # We don't have non-blocking mode for now
148
      raise NotImplementedError
149

    
150
    self.__lock.acquire()
151
    try:
152
      if self.__deleted:
153
        raise errors.LockError('deleted lock')
154

    
155
      # We cannot acquire the lock if we already have it
156
      assert not self.__is_owned(), "double acquire() on a non-recursive lock"
157

    
158
      if shared:
159
        self.__nwait_shr += 1
160
        try:
161
          # If there is an exclusive holder waiting we have to wait.  We'll
162
          # only do this once, though, when we start waiting for the lock. Then
163
          # we'll just wait while there are no exclusive holders.
164
          if self.__nwait_exc > 0:
165
            # TODO: if !blocking...
166
            self.__wait(self.__turn_shr)
167

    
168
          while self.__exc is not None:
169
            # TODO: if !blocking...
170
            self.__wait(self.__turn_shr)
171

    
172
          self.__shr.add(threading.currentThread())
173
        finally:
174
          self.__nwait_shr -= 1
175

    
176
      else:
177
        # TODO: if !blocking...
178
        # (or modify __exclusive_acquire for non-blocking mode)
179
        self.__exclusive_acquire()
180

    
181
    finally:
182
      self.__lock.release()
183

    
184
    return True
185

    
186
  def release(self):
187
    """Release a Shared Lock.
188

189
    You must have acquired the lock, either in shared or in exclusive mode,
190
    before calling this function.
191

192
    """
193
    self.__lock.acquire()
194
    try:
195
      # Autodetect release type
196
      if self.__is_exclusive():
197
        self.__exc = None
198

    
199
        # An exclusive holder has just had the lock, time to put it in shared
200
        # mode if there are shared holders waiting. Otherwise wake up the next
201
        # exclusive holder.
202
        if self.__nwait_shr > 0:
203
          self.__turn_shr.notifyAll()
204
        elif self.__nwait_exc > 0:
205
         self.__turn_exc.notify()
206

    
207
      elif self.__is_sharer():
208
        self.__shr.remove(threading.currentThread())
209

    
210
        # If there are no more shared holders and some exclusive holders are
211
        # waiting let's wake one up.
212
        if len(self.__shr) == 0 and self.__nwait_exc > 0:
213
          self.__turn_exc.notify()
214

    
215
      else:
216
        assert False, "Cannot release non-owned lock"
217

    
218
    finally:
219
      self.__lock.release()
220

    
221
  def delete(self, blocking=1):
222
    """Delete a Shared Lock.
223

224
    This operation will declare the lock for removal. First the lock will be
225
    acquired in exclusive mode if you don't already own it, then the lock
226
    will be put in a state where any future and pending acquire() fail.
227

228
    Args:
229
      blocking: whether to block while trying to acquire or to operate in
230
                try-lock mode.  this locking mode is not supported yet unless
231
                you are already holding exclusively the lock.
232

233
    """
234
    self.__lock.acquire()
235
    try:
236
      assert not self.__is_sharer(), "cannot delete() a lock while sharing it"
237

    
238
      if self.__deleted:
239
        raise errors.LockError('deleted lock')
240

    
241
      if not self.__is_exclusive():
242
        if not blocking:
243
          # We don't have non-blocking mode for now
244
          raise NotImplementedError
245
        self.__exclusive_acquire()
246

    
247
      self.__deleted = True
248
      self.__exc = None
249
      # Wake up everybody, they will fail acquiring the lock and
250
      # raise an exception instead.
251
      self.__turn_exc.notifyAll()
252
      self.__turn_shr.notifyAll()
253

    
254
    finally:
255
      self.__lock.release()
256

    
257

    
258
class LockSet:
259
  """Implements a set of locks.
260

261
  This abstraction implements a set of shared locks for the same resource type,
262
  distinguished by name. The user can lock a subset of the resources and the
263
  LockSet will take care of acquiring the locks always in the same order, thus
264
  preventing deadlock.
265

266
  All the locks needed in the same set must be acquired together, though.
267

268
  """
269
  def __init__(self, members=None):
270
    """Constructs a new LockSet.
271

272
    Args:
273
      members: initial members of the set
274

275
    """
276
    # Used internally to guarantee coherency.
277
    self.__lock = SharedLock()
278

    
279
    # The lockdict indexes the relationship name -> lock
280
    # The order-of-locking is implied by the alphabetical order of names
281
    self.__lockdict = {}
282

    
283
    if members is not None:
284
      for name in members:
285
        self.__lockdict[name] = SharedLock()
286

    
287
    # The owner dict contains the set of locks each thread owns. For
288
    # performance each thread can access its own key without a global lock on
289
    # this structure. It is paramount though that *no* other type of access is
290
    # done to this structure (eg. no looping over its keys). *_owner helper
291
    # function are defined to guarantee access is correct, but in general never
292
    # do anything different than __owners[threading.currentThread()], or there
293
    # will be trouble.
294
    self.__owners = {}
295

    
296
  def _is_owned(self):
297
    """Is the current thread a current level owner?"""
298
    return threading.currentThread() in self.__owners
299

    
300
  def _add_owned(self, name):
301
    """Note the current thread owns the given lock"""
302
    if self._is_owned():
303
      self.__owners[threading.currentThread()].add(name)
304
    else:
305
       self.__owners[threading.currentThread()] = set([name])
306

    
307
  def _del_owned(self, name):
308
    """Note the current thread owns the given lock"""
309
    self.__owners[threading.currentThread()].remove(name)
310

    
311
    if not self.__owners[threading.currentThread()]:
312
      del self.__owners[threading.currentThread()]
313

    
314
  def _list_owned(self):
315
    """Get the set of resource names owned by the current thread"""
316
    if self._is_owned():
317
      return self.__owners[threading.currentThread()].copy()
318
    else:
319
      return set()
320

    
321
  def __names(self):
322
    """Return the current set of names.
323

324
    Only call this function while holding __lock and don't iterate on the
325
    result after releasing the lock.
326

327
    """
328
    return self.__lockdict.keys()
329

    
330
  def _names(self):
331
    """Return a copy of the current set of elements.
332

333
    Used only for debugging purposes.
334

335
    """
336
    self.__lock.acquire(shared=1)
337
    try:
338
      result = self.__names()
339
    finally:
340
      self.__lock.release()
341
    return set(result)
342

    
343
  def acquire(self, names, blocking=1, shared=0):
344
    """Acquire a set of resource locks.
345

346
    Args:
347
      names: the names of the locks which shall be acquired.
348
             (special lock names, or instance/node names)
349
      shared: whether to acquire in shared mode. By default an exclusive lock
350
              will be acquired.
351
      blocking: whether to block while trying to acquire or to operate in try-lock mode.
352
                this locking mode is not supported yet.
353

354
    Returns:
355
      True: when all the locks are successfully acquired
356

357
    Raises:
358
      errors.LockError: when any lock we try to acquire has been deleted
359
      before we succeed. In this case none of the locks requested will be
360
      acquired.
361

362
    """
363
    if not blocking:
364
      # We don't have non-blocking mode for now
365
      raise NotImplementedError
366

    
367
    # Check we don't already own locks at this level
368
    assert not self._is_owned(), "Cannot acquire locks in the same set twice"
369

    
370
    if names is None:
371
      # If no names are given acquire the whole set by not letting new names
372
      # being added before we release, and getting the current list of names.
373
      # Some of them may then be deleted later, but we'll cope with this.
374
      #
375
      # We'd like to acquire this lock in a shared way, as it's nice if
376
      # everybody else can use the instances at the same time. If are acquiring
377
      # them exclusively though they won't be able to do this anyway, though,
378
      # so we'll get the list lock exclusively as well in order to be able to
379
      # do add() on the set while owning it.
380
      self.__lock.acquire(shared=shared)
381

    
382
    try:
383
      # Support passing in a single resource to acquire rather than many
384
      if isinstance(names, basestring):
385
        names = [names]
386
      else:
387
        if names is None:
388
          names = self.__names()
389
        names.sort()
390

    
391
      acquire_list = []
392
      # First we look the locks up on __lockdict. We have no way of being sure
393
      # they will still be there after, but this makes it a lot faster should
394
      # just one of them be the already wrong
395
      for lname in names:
396
        try:
397
          lock = self.__lockdict[lname] # raises KeyError if the lock is not there
398
          acquire_list.append((lname, lock))
399
        except (KeyError):
400
          if self.__lock._is_owned():
401
            # We are acquiring all the set, it doesn't matter if this particular
402
            # element is not there anymore.
403
            continue
404
          else:
405
            raise errors.LockError('non-existing lock in set (%s)' % lname)
406

    
407
      # This will hold the locknames we effectively acquired.
408
      acquired = set()
409
      # Now acquire_list contains a sorted list of resources and locks we want.
410
      # In order to get them we loop on this (private) list and acquire() them.
411
      # We gave no real guarantee they will still exist till this is done but
412
      # .acquire() itself is safe and will alert us if the lock gets deleted.
413
      for (lname, lock) in acquire_list:
414
        try:
415
          lock.acquire(shared=shared) # raises LockError if the lock is deleted
416
          try:
417
            # now the lock cannot be deleted, we have it!
418
            self._add_owned(lname)
419
            acquired.add(lname)
420
          except:
421
            # We shouldn't have problems adding the lock to the owners list, but
422
            # if we did we'll try to release this lock and re-raise exception.
423
            # Of course something is going to be really wrong, after this.
424
            lock.release()
425
            raise
426

    
427
        except (errors.LockError):
428
          if self.__lock._is_owned():
429
            # We are acquiring all the set, it doesn't matter if this particular
430
            # element is not there anymore.
431
            continue
432
          else:
433
            name_fail = lname
434
            for lname in self._list_owned():
435
              self.__lockdict[lname].release()
436
              self._del_owned(lname)
437
            raise errors.LockError('non-existing lock in set (%s)' % name_fail)
438

    
439
    except:
440
      # If something went wrong and we had the set-lock let's release it...
441
      if self.__lock._is_owned():
442
        self.__lock.release()
443
      raise
444

    
445
    return acquired
446

    
447
  def release(self, names=None):
448
    """Release a set of resource locks, at the same level.
449

450
    You must have acquired the locks, either in shared or in exclusive mode,
451
    before releasing them.
452

453
    Args:
454
      names: the names of the locks which shall be released.
455
             (defaults to all the locks acquired at that level).
456

457
    """
458
    assert self._is_owned(), "release() on lock set while not owner"
459

    
460
    # Support passing in a single resource to release rather than many
461
    if isinstance(names, basestring):
462
      names = [names]
463

    
464
    if names is None:
465
      names = self._list_owned()
466
    else:
467
      names = set(names)
468
      assert self._list_owned().issuperset(names), (
469
               "release() on unheld resources %s" %
470
               names.difference(self._list_owned()))
471

    
472
    # First of all let's release the "all elements" lock, if set.
473
    # After this 'add' can work again
474
    if self.__lock._is_owned():
475
      self.__lock.release()
476

    
477
    for lockname in names:
478
      # If we are sure the lock doesn't leave __lockdict without being
479
      # exclusively held we can do this...
480
      self.__lockdict[lockname].release()
481
      self._del_owned(lockname)
482

    
483
  def add(self, names, acquired=0, shared=0):
484
    """Add a new set of elements to the set
485

486
    Args:
487
      names: names of the new elements to add
488
      acquired: pre-acquire the new resource?
489
      shared: is the pre-acquisition shared?
490

491
    """
492

    
493
    assert not self.__lock._is_owned(shared=1), (
494
           "Cannot add new elements while sharing the set-lock")
495

    
496
    # Support passing in a single resource to add rather than many
497
    if isinstance(names, basestring):
498
      names = [names]
499

    
500
    # If we don't already own the set-level lock acquire it in an exclusive way
501
    # we'll get it and note we need to release it later.
502
    release_lock = False
503
    if not self.__lock._is_owned():
504
      release_lock = True
505
      self.__lock.acquire()
506

    
507
    try:
508
      invalid_names = set(self.__names()).intersection(names)
509
      if invalid_names:
510
        # This must be an explicit raise, not an assert, because assert is
511
        # turned off when using optimization, and this can happen because of
512
        # concurrency even if the user doesn't want it.
513
        raise errors.LockError("duplicate add() (%s)" % invalid_names)
514

    
515
      for lockname in names:
516
        lock = SharedLock()
517

    
518
        if acquired:
519
          lock.acquire(shared=shared)
520
          # now the lock cannot be deleted, we have it!
521
          try:
522
            self._add_owned(lockname)
523
          except:
524
            # We shouldn't have problems adding the lock to the owners list,
525
            # but if we did we'll try to release this lock and re-raise
526
            # exception.  Of course something is going to be really wrong,
527
            # after this.  On the other hand the lock hasn't been added to the
528
            # __lockdict yet so no other threads should be pending on it. This
529
            # release is just a safety measure.
530
            lock.release()
531
            raise
532

    
533
        self.__lockdict[lockname] = lock
534

    
535
    finally:
536
      # Only release __lock if we were not holding it previously.
537
      if release_lock:
538
        self.__lock.release()
539

    
540
    return True
541

    
542
  def remove(self, names, blocking=1):
543
    """Remove elements from the lock set.
544

545
    You can either not hold anything in the lockset or already hold a superset
546
    of the elements you want to delete, exclusively.
547

548
    Args:
549
      names: names of the resource to remove.
550
      blocking: whether to block while trying to acquire or to operate in
551
                try-lock mode.  this locking mode is not supported yet unless
552
                you are already holding exclusively the locks.
553

554
    Returns:
555
      A list of lock which we removed. The list is always equal to the names
556
      list if we were holding all the locks exclusively.
557

558
    """
559
    if not blocking and not self._is_owned():
560
      # We don't have non-blocking mode for now
561
      raise NotImplementedError
562

    
563
    # Support passing in a single resource to remove rather than many
564
    if isinstance(names, basestring):
565
      names = [names]
566

    
567
    # If we own any subset of this lock it must be a superset of what we want
568
    # to delete. The ownership must also be exclusive, but that will be checked
569
    # by the lock itself.
570
    assert not self._is_owned() or self._list_owned().issuperset(names), (
571
      "remove() on acquired lockset while not owning all elements")
572

    
573
    removed = []
574

    
575
    for lname in names:
576
      # Calling delete() acquires the lock exclusively if we don't already own
577
      # it, and causes all pending and subsequent lock acquires to fail. It's
578
      # fine to call it out of order because delete() also implies release(),
579
      # and the assertion above guarantees that if we either already hold
580
      # everything we want to delete, or we hold none.
581
      try:
582
        self.__lockdict[lname].delete()
583
        removed.append(lname)
584
      except (KeyError, errors.LockError):
585
        # This cannot happen if we were already holding it, verify:
586
        assert not self._is_owned(), "remove failed while holding lockset"
587
      else:
588
        # If no LockError was raised we are the ones who deleted the lock.
589
        # This means we can safely remove it from lockdict, as any further or
590
        # pending delete() or acquire() will fail (and nobody can have the lock
591
        # since before our call to delete()).
592
        #
593
        # This is done in an else clause because if the exception was thrown
594
        # it's the job of the one who actually deleted it.
595
        del self.__lockdict[lname]
596
        # And let's remove it from our private list if we owned it.
597
        if self._is_owned():
598
          self._del_owned(lname)
599

    
600
    return removed
601

    
602

    
603
# Locking levels, must be acquired in increasing order.
604
# Current rules are:
605
#   - at level LEVEL_CLUSTER resides the Big Ganeti Lock (BGL) which must be
606
#   acquired before performing any operation, either in shared or in exclusive
607
#   mode. acquiring the BGL in exclusive mode is discouraged and should be
608
#   avoided.
609
#   - at levels LEVEL_NODE and LEVEL_INSTANCE reside node and instance locks.
610
#   If you need more than one node, or more than one instance, acquire them at
611
#   the same time.
612
#  - level LEVEL_CONFIG contains the configuration lock, which you must acquire
613
#  before reading or changing the config file.
614
LEVEL_CLUSTER = 0
615
LEVEL_NODE = 1
616
LEVEL_INSTANCE = 2
617
LEVEL_CONFIG = 3
618

    
619
LEVELS = [LEVEL_CLUSTER,
620
          LEVEL_NODE,
621
          LEVEL_INSTANCE,
622
          LEVEL_CONFIG]
623

    
624
# Lock levels which are modifiable
625
LEVELS_MOD = [LEVEL_NODE, LEVEL_INSTANCE]
626

    
627
# Constant for the big ganeti lock and config lock
628
BGL = 'BGL'
629
CONFIG = 'config'
630

    
631

    
632
class GanetiLockManager:
633
  """The Ganeti Locking Library
634

635
  The purpouse of this small library is to manage locking for ganeti clusters
636
  in a central place, while at the same time doing dynamic checks against
637
  possible deadlocks. It will also make it easier to transition to a different
638
  lock type should we migrate away from python threads.
639

640
  """
641
  _instance = None
642

    
643
  def __init__(self, nodes=None, instances=None):
644
    """Constructs a new GanetiLockManager object.
645

646
    There should be only a
647
    GanetiLockManager object at any time, so this function raises an error if this
648
    is not the case.
649

650
    Args:
651
      nodes: list of node names
652
      instances: list of instance names
653

654
    """
655
    assert self.__class__._instance is None, "double GanetiLockManager instance"
656
    self.__class__._instance = self
657

    
658
    # The keyring contains all the locks, at their level and in the correct
659
    # locking order.
660
    self.__keyring = {
661
      LEVEL_CLUSTER: LockSet([BGL]),
662
      LEVEL_NODE: LockSet(nodes),
663
      LEVEL_INSTANCE: LockSet(instances),
664
      LEVEL_CONFIG: LockSet([CONFIG]),
665
    }
666

    
667
  def _names(self, level):
668
    """List the lock names at the given level.
669
    Used for debugging/testing purposes.
670

671
    Args:
672
      level: the level whose list of locks to get
673

674
    """
675
    assert level in LEVELS, "Invalid locking level %s" % level
676
    return self.__keyring[level]._names()
677

    
678
  def _is_owned(self, level):
679
    """Check whether we are owning locks at the given level
680

681
    """
682
    return self.__keyring[level]._is_owned()
683

    
684
  def _list_owned(self, level):
685
    """Get the set of owned locks at the given level
686

687
    """
688
    return self.__keyring[level]._list_owned()
689

    
690
  def _upper_owned(self, level):
691
    """Check that we don't own any lock at a level greater than the given one.
692

693
    """
694
    # This way of checking only works if LEVELS[i] = i, which we check for in
695
    # the test cases.
696
    return utils.any((self._is_owned(l) for l in LEVELS[level + 1:]))
697

    
698
  def _BGL_owned(self):
699
    """Check if the current thread owns the BGL.
700

701
    Both an exclusive or a shared acquisition work.
702

703
    """
704
    return BGL in self.__keyring[LEVEL_CLUSTER]._list_owned()
705

    
706
  def _contains_BGL(self, level, names):
707
    """Check if acting on the given level and set of names will change the
708
    status of the Big Ganeti Lock.
709

710
    """
711
    return level == LEVEL_CLUSTER and (names is None or BGL in names)
712

    
713
  def acquire(self, level, names, blocking=1, shared=0):
714
    """Acquire a set of resource locks, at the same level.
715

716
    Args:
717
      level: the level at which the locks shall be acquired.
718
             It must be a memmber of LEVELS.
719
      names: the names of the locks which shall be acquired.
720
             (special lock names, or instance/node names)
721
      shared: whether to acquire in shared mode. By default an exclusive lock
722
              will be acquired.
723
      blocking: whether to block while trying to acquire or to operate in try-lock mode.
724
                this locking mode is not supported yet.
725

726
    """
727
    assert level in LEVELS, "Invalid locking level %s" % level
728

    
729
    # Check that we are either acquiring the Big Ganeti Lock or we already own
730
    # it. Some "legacy" opcodes need to be sure they are run non-concurrently
731
    # so even if we've migrated we need to at least share the BGL to be
732
    # compatible with them. Of course if we own the BGL exclusively there's no
733
    # point in acquiring any other lock, unless perhaps we are half way through
734
    # the migration of the current opcode.
735
    assert (self._contains_BGL(level, names) or self._BGL_owned()), (
736
            "You must own the Big Ganeti Lock before acquiring any other")
737

    
738
    # Check we don't own locks at the same or upper levels.
739
    assert not self._upper_owned(level), ("Cannot acquire locks at a level" 
740
           " while owning some at a greater one")
741

    
742
    # Acquire the locks in the set.
743
    return self.__keyring[level].acquire(names, shared=shared,
744
                                         blocking=blocking)
745

    
746
  def release(self, level, names=None):
747
    """Release a set of resource locks, at the same level.
748

749
    You must have acquired the locks, either in shared or in exclusive mode,
750
    before releasing them.
751

752
    Args:
753
      level: the level at which the locks shall be released.
754
             It must be a memmber of LEVELS.
755
      names: the names of the locks which shall be released.
756
             (defaults to all the locks acquired at that level).
757

758
    """
759
    assert level in LEVELS, "Invalid locking level %s" % level
760
    assert (not self._contains_BGL(level, names) or
761
            not self._upper_owned(LEVEL_CLUSTER)), (
762
            "Cannot release the Big Ganeti Lock while holding something"
763
            " at upper levels")
764

    
765
    # Release will complain if we don't own the locks already
766
    return self.__keyring[level].release(names)
767

    
768
  def add(self, level, names, acquired=0, shared=0):
769
    """Add locks at the specified level.
770

771
    Args:
772
      level: the level at which the locks shall be added.
773
             It must be a memmber of LEVELS_MOD.
774
      names: names of the locks to acquire
775
      acquired: whether to acquire the newly added locks
776
      shared: whether the acquisition will be shared
777
    """
778
    assert level in LEVELS_MOD, "Invalid or immutable level %s" % level
779
    assert self._BGL_owned(), ("You must own the BGL before performing other"
780
           " operations")
781
    assert not self._upper_owned(level), ("Cannot add locks at a level"
782
           " while owning some at a greater one")
783
    return self.__keyring[level].add(names, acquired=acquired, shared=shared)
784

    
785
  def remove(self, level, names, blocking=1):
786
    """Remove locks from the specified level.
787

788
    You must either already own the locks you are trying to remove exclusively
789
    or not own any lock at an upper level.
790

791
    Args:
792
      level: the level at which the locks shall be removed.
793
             It must be a memmber of LEVELS_MOD.
794
      names: the names of the locks which shall be removed.
795
             (special lock names, or instance/node names)
796
      blocking: whether to block while trying to operate in try-lock mode.
797
                this locking mode is not supported yet.
798

799
    """
800
    assert level in LEVELS_MOD, "Invalid or immutable level %s" % level
801
    assert self._BGL_owned(), ("You must own the BGL before performing other"
802
           " operations")
803
    # Check we either own the level or don't own anything from here up.
804
    # LockSet.remove() will check the case in which we don't own all the needed
805
    # resources, or we have a shared ownership.
806
    assert self._is_owned(level) or not self._upper_owned(level), (
807
           "Cannot remove locks at a level while not owning it or"
808
           " owning some at a greater one")
809
    return self.__keyring[level].remove(names, blocking=blocking)