Statistics
| Branch: | Tag: | Revision:

root / lib / locking.py @ ea3f80bf

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
          # now the lock cannot be deleted, we have it!
417
          self._add_owned(lname)
418
          acquired.add(lname)
419
        except (errors.LockError):
420
          if self.__lock._is_owned():
421
            # We are acquiring all the set, it doesn't matter if this particular
422
            # element is not there anymore.
423
            continue
424
          else:
425
            name_fail = lname
426
            for lname in self._list_owned():
427
              self.__lockdict[lname].release()
428
              self._del_owned(lname)
429
            raise errors.LockError('non-existing lock in set (%s)' % name_fail)
430
        except:
431
          # We shouldn't have problems adding the lock to the owners list, but
432
          # if we did we'll try to release this lock and re-raise exception.
433
          # Of course something is going to be really wrong, after this.
434
          if lock._is_owned():
435
            lock.release()
436
            raise
437

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

    
444
    return acquired
445

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

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

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

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

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

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

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

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

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

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

490
    """
491

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

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

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

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

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

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

    
532
        self.__lockdict[lockname] = lock
533

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

    
539
    return True
540

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

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

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

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

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

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

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

    
572
    removed = []
573

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

    
599
    return removed
600

    
601

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

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

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

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

    
630

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

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

639
  """
640
  _instance = None
641

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

700
    Both an exclusive or a shared acquisition work.
701

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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