Revision 7ee7c0c7 lib/locking.py

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

  
30 31

  
31 32
class SharedLock:
......
555 556

  
556 557
    return delete_failed
557 558

  
559

  
560
# Locking levels, must be acquired in increasing order.
561
# Current rules are:
562
#   - at level LEVEL_CLUSTER resides the Big Ganeti Lock (BGL) which must be
563
#   acquired before performing any operation, either in shared or in exclusive
564
#   mode. acquiring the BGL in exclusive mode is discouraged and should be
565
#   avoided.
566
#   - at levels LEVEL_NODE and LEVEL_INSTANCE reside node and instance locks.
567
#   If you need more than one node, or more than one instance, acquire them at
568
#   the same time.
569
#  - level LEVEL_CONFIG contains the configuration lock, which you must acquire
570
#  before reading or changing the config file.
571
LEVEL_CLUSTER = 0
572
LEVEL_NODE = 1
573
LEVEL_INSTANCE = 2
574
LEVEL_CONFIG = 3
575

  
576
LEVELS = [LEVEL_CLUSTER,
577
          LEVEL_NODE,
578
          LEVEL_INSTANCE,
579
          LEVEL_CONFIG]
580

  
581
# Lock levels which are modifiable
582
LEVELS_MOD = [LEVEL_NODE, LEVEL_INSTANCE]
583

  
584
# Constant for the big ganeti lock and config lock
585
BGL = 'BGL'
586
CONFIG = 'config'
587

  
588

  
589
class GanetiLockManager:
590
  """The Ganeti Locking Library
591

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

  
597
  """
598
  _instance = None
599

  
600
  def __init__(self, nodes=None, instances=None):
601
    """Constructs a new GanetiLockManager object.
602

  
603
    There should be only a
604
    GanetiLockManager object at any time, so this function raises an error if this
605
    is not the case.
606

  
607
    Args:
608
      nodes: list of node names
609
      instances: list of instance names
610

  
611
    """
612
    assert self.__class__._instance is None, "double GanetiLockManager instance"
613
    self.__class__._instance = self
614

  
615
    # The keyring contains all the locks, at their level and in the correct
616
    # locking order.
617
    self.__keyring = {
618
      LEVEL_CLUSTER: LockSet([BGL]),
619
      LEVEL_NODE: LockSet(nodes),
620
      LEVEL_INSTANCE: LockSet(instances),
621
      LEVEL_CONFIG: LockSet([CONFIG]),
622
    }
623

  
624
  def _names(self, level):
625
    """List the lock names at the given level.
626
    Used for debugging/testing purposes.
627

  
628
    Args:
629
      level: the level whose list of locks to get
630

  
631
    """
632
    assert level in LEVELS, "Invalid locking level %s" % level
633
    return self.__keyring[level]._names()
634

  
635
  def _is_owned(self, level):
636
    """Check whether we are owning locks at the given level
637

  
638
    """
639
    return self.__keyring[level]._is_owned()
640

  
641
  def _list_owned(self, level):
642
    """Get the set of owned locks at the given level
643

  
644
    """
645
    return self.__keyring[level]._list_owned()
646

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

  
650
    """
651
    # This way of checking only works if LEVELS[i] = i, which we check for in
652
    # the test cases.
653
    return utils.any((self._is_owned(l) for l in LEVELS[level + 1:]))
654

  
655
  def _BGL_owned(self):
656
    """Check if the current thread owns the BGL.
657

  
658
    Both an exclusive or a shared acquisition work.
659

  
660
    """
661
    return BGL in self.__keyring[LEVEL_CLUSTER]._list_owned()
662

  
663
  def _contains_BGL(self, level, names):
664
    """Check if acting on the given level and set of names will change the
665
    status of the Big Ganeti Lock.
666

  
667
    """
668
    return level == LEVEL_CLUSTER and (names is None or BGL in names)
669

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

  
673
    Args:
674
      level: the level at which the locks shall be acquired.
675
             It must be a memmber of LEVELS.
676
      names: the names of the locks which shall be acquired.
677
             (special lock names, or instance/node names)
678
      shared: whether to acquire in shared mode. By default an exclusive lock
679
              will be acquired.
680
      blocking: whether to block while trying to acquire or to operate in try-lock mode.
681
                this locking mode is not supported yet.
682

  
683
    """
684
    assert level in LEVELS, "Invalid locking level %s" % level
685

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

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

  
699
    # Acquire the locks in the set.
700
    return self.__keyring[level].acquire(names, shared=shared,
701
                                         blocking=blocking)
702

  
703
  def release(self, level, names=None):
704
    """Release a set of resource locks, at the same level.
705

  
706
    You must have acquired the locks, either in shared or in exclusive mode,
707
    before releasing them.
708

  
709
    Args:
710
      level: the level at which the locks shall be released.
711
             It must be a memmber of LEVELS.
712
      names: the names of the locks which shall be released.
713
             (defaults to all the locks acquired at that level).
714

  
715
    """
716
    assert level in LEVELS, "Invalid locking level %s" % level
717
    assert (not self._contains_BGL(level, names) or
718
            not self._upper_owned(LEVEL_CLUSTER)), (
719
            "Cannot release the Big Ganeti Lock while holding something"
720
            " at upper levels")
721

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

  
725
  def add(self, level, names, acquired=0, shared=0):
726
    """Add locks at the specified level.
727

  
728
    Args:
729
      level: the level at which the locks shall be added.
730
             It must be a memmber of LEVELS_MOD.
731
      names: names of the locks to acquire
732
      acquired: whether to acquire the newly added locks
733
      shared: whether the acquisition will be shared
734
    """
735
    assert level in LEVELS_MOD, "Invalid or immutable level %s" % level
736
    assert self._BGL_owned(), ("You must own the BGL before performing other"
737
           " operations")
738
    assert not self._upper_owned(level), ("Cannot add locks at a level"
739
           " while owning some at a greater one")
740
    return self.__keyring[level].add(names, acquired=acquired, shared=shared)
741

  
742
  def remove(self, level, names, blocking=1):
743
    """Remove locks from the specified level.
744

  
745
    You must either already own the locks you are trying to remove exclusively
746
    or not own any lock at an upper level.
747

  
748
    Args:
749
      level: the level at which the locks shall be removed.
750
             It must be a memmber of LEVELS_MOD.
751
      names: the names of the locks which shall be removed.
752
             (special lock names, or instance/node names)
753
      blocking: whether to block while trying to operate in try-lock mode.
754
                this locking mode is not supported yet.
755

  
756
    """
757
    assert level in LEVELS_MOD, "Invalid or immutable level %s" % level
758
    assert self._BGL_owned(), ("You must own the BGL before performing other"
759
           " operations")
760
    # Check we either own the level or don't own anything from here up.
761
    # LockSet.remove() will check the case in which we don't own all the needed
762
    # resources, or we have a shared ownership.
763
    assert self._is_owned(level) or not self._upper_owned(level), (
764
           "Cannot remove locks at a level while not owning it or"
765
           " owning some at a greater one")
766
    return self.__keyring[level].remove(names, blocking)
767

  

Also available in: Unified diff