Switch the instance_reboot rpc to (status, data)
[ganeti-local] / lib / locking.py
index aaae252..647e14f 100644 (file)
@@ -104,11 +104,10 @@ class SharedLock:
   def _is_owned(self, shared=-1):
     """Is the current thread somehow owning the lock at this time?
 
-    Args:
-      shared:
-        < 0: check for any type of ownership (default)
-        0: check for exclusive ownership
-        > 0: check for shared ownership
+    @param shared:
+        - < 0: check for any type of ownership (default)
+        - 0: check for exclusive ownership
+        - > 0: check for shared ownership
 
     """
     self.__lock.acquire()
@@ -123,8 +122,7 @@ class SharedLock:
     """Wait on the given condition, and raise an exception if the current lock
     is declared deleted in the meantime.
 
-    Args:
-      c: condition to wait on
+    @param c: the condition to wait on
 
     """
     c.wait()
@@ -158,11 +156,10 @@ class SharedLock:
   def acquire(self, blocking=1, shared=0):
     """Acquire a shared lock.
 
-    Args:
-      shared: whether to acquire in shared mode. By default an exclusive lock
-              will be acquired.
-      blocking: whether to block while trying to acquire or to operate in
-                try-lock mode. this locking mode is not supported yet.
+    @param shared: whether to acquire in shared mode; by default an
+        exclusive lock will be acquired
+    @param blocking: whether to block while trying to acquire or to
+        operate in try-lock mode (this locking mode is not supported yet)
 
     """
     if not blocking:
@@ -182,9 +179,10 @@ class SharedLock:
         self.__nwait_shr += 1
         try:
           wait = False
-          # If there is an exclusive holder waiting we have to wait.  We'll
-          # only do this once, though, when we start waiting for the lock. Then
-          # we'll just wait while there are no exclusive holders.
+          # If there is an exclusive holder waiting we have to wait.
+          # We'll only do this once, though, when we start waiting for
+          # the lock. Then we'll just wait while there are no
+          # exclusive holders.
           if self.__nwait_exc > 0:
             # TODO: if !blocking...
             wait = True
@@ -245,8 +243,9 @@ class SharedLock:
         # If there are shared holders waiting (and not just scheduled to pass)
         # there *must* be an exclusive holder waiting as well; otherwise what
         # were they waiting for?
-        assert (self.__nwait_exc > 0 or self.__npass_shr == self.__nwait_shr), \
-               "Lock sharers waiting while no exclusive is queueing"
+        assert (self.__nwait_exc > 0 or
+                self.__npass_shr == self.__nwait_shr), \
+                "Lock sharers waiting while no exclusive is queueing"
 
         # If there are no more shared holders either in or scheduled to pass,
         # and some exclusive holders are waiting let's wake one up.
@@ -268,10 +267,9 @@ class SharedLock:
     acquired in exclusive mode if you don't already own it, then the lock
     will be put in a state where any future and pending acquire() fail.
 
-    Args:
-      blocking: whether to block while trying to acquire or to operate in
-                try-lock mode.  this locking mode is not supported yet unless
-                you are already holding exclusively the lock.
+    @param blocking: whether to block while trying to acquire or to
+        operate in try-lock mode.  this locking mode is not supported
+        yet unless you are already holding exclusively the lock.
 
     """
     self.__lock.acquire()
@@ -298,8 +296,8 @@ class SharedLock:
       self.__lock.release()
 
 
-# Whenever we want to acquire a full LockSet we pass None as the value to acquire.
-# Hide this behing this nicely named constant.
+# Whenever we want to acquire a full LockSet we pass None as the value
+# to acquire.  Hide this behing this nicely named constant.
 ALL_SET = None
 
 
@@ -317,8 +315,7 @@ class LockSet:
   def __init__(self, members=None):
     """Constructs a new LockSet.
 
-    Args:
-      members: initial members of the set
+    @param members: initial members of the set
 
     """
     # Used internally to guarantee coherency.
@@ -390,31 +387,34 @@ class LockSet:
     Used only for debugging purposes.
 
     """
-    self.__lock.acquire(shared=1)
+    # If we don't already own the set-level lock acquired
+    # we'll get it and note we need to release it later.
+    release_lock = False
+    if not self.__lock._is_owned():
+      release_lock = True
+      self.__lock.acquire(shared=1)
     try:
       result = self.__names()
     finally:
-      self.__lock.release()
+      if release_lock:
+        self.__lock.release()
     return set(result)
 
   def acquire(self, names, blocking=1, shared=0):
     """Acquire a set of resource locks.
 
-    Args:
-      names: the names of the locks which shall be acquired.
-             (special lock names, or instance/node names)
-      shared: whether to acquire in shared mode. By default an exclusive lock
-              will be acquired.
-      blocking: whether to block while trying to acquire or to operate in
-                try-lock mode.  this locking mode is not supported yet.
+    @param names: the names of the locks which shall be acquired
+        (special lock names, or instance/node names)
+    @param shared: whether to acquire in shared mode; by default an
+        exclusive lock will be acquired
+    @param blocking: whether to block while trying to acquire or to
+        operate in try-lock mode (this locking mode is not supported yet)
 
-    Returns:
-      True: when all the locks are successfully acquired
+    @return: True when all the locks are successfully acquired
 
-    Raises:
-      errors.LockError: when any lock we try to acquire has been deleted
-      before we succeed. In this case none of the locks requested will be
-      acquired.
+    @raise errors.LockError: when any lock we try to acquire has
+        been deleted before we succeed. In this case none of the
+        locks requested will be acquired.
 
     """
     if not blocking:
@@ -451,7 +451,7 @@ class LockSet:
       if isinstance(names, basestring):
         names = [names]
       else:
-        names.sort()
+        names = sorted(names)
 
       acquire_list = []
       # First we look the locks up on __lockdict. We have no way of being sure
@@ -463,8 +463,8 @@ class LockSet:
           acquire_list.append((lname, lock))
         except (KeyError):
           if self.__lock._is_owned():
-            # We are acquiring all the set, it doesn't matter if this particular
-            # element is not there anymore.
+            # We are acquiring all the set, it doesn't matter if this
+            # particular element is not there anymore.
             continue
           else:
             raise errors.LockError('non-existing lock in set (%s)' % lname)
@@ -483,8 +483,8 @@ class LockSet:
           acquired.add(lname)
         except (errors.LockError):
           if self.__lock._is_owned():
-            # We are acquiring all the set, it doesn't matter if this particular
-            # element is not there anymore.
+            # We are acquiring all the set, it doesn't matter if this
+            # particular element is not there anymore.
             continue
           else:
             name_fail = lname
@@ -514,9 +514,8 @@ class LockSet:
     You must have acquired the locks, either in shared or in exclusive mode,
     before releasing them.
 
-    Args:
-      names: the names of the locks which shall be released.
-             (defaults to all the locks acquired at that level).
+    @param names: the names of the locks which shall be released
+        (defaults to all the locks acquired at that level).
 
     """
     assert self._is_owned(), "release() on lock set while not owner"
@@ -548,21 +547,20 @@ class LockSet:
   def add(self, names, acquired=0, shared=0):
     """Add a new set of elements to the set
 
-    Args:
-      names: names of the new elements to add
-      acquired: pre-acquire the new resource?
-      shared: is the pre-acquisition shared?
+    @param names: names of the new elements to add
+    @param acquired: pre-acquire the new resource?
+    @param shared: is the pre-acquisition shared?
 
     """
-
-    assert not self.__lock._is_owned(shared=1), (
-           "Cannot add new elements while sharing the set-lock")
+    # Check we don't already own locks at this level
+    assert not self._is_owned() or self.__lock._is_owned(shared=0), \
+      "Cannot add locks if the set is only partially owned, or shared"
 
     # Support passing in a single resource to add rather than many
     if isinstance(names, basestring):
       names = [names]
 
-    # If we don't already own the set-level lock acquire it in an exclusive way
+    # If we don't already own the set-level lock acquired in an exclusive way
     # we'll get it and note we need to release it later.
     release_lock = False
     if not self.__lock._is_owned():
@@ -610,15 +608,14 @@ class LockSet:
     You can either not hold anything in the lockset or already hold a superset
     of the elements you want to delete, exclusively.
 
-    Args:
-      names: names of the resource to remove.
-      blocking: whether to block while trying to acquire or to operate in
-                try-lock mode.  this locking mode is not supported yet unless
-                you are already holding exclusively the locks.
+    @param names: names of the resource to remove.
+    @param blocking: whether to block while trying to acquire or to
+        operate in try-lock mode (this locking mode is not supported
+        yet unless you are already holding exclusively the locks)
 
-    Returns:
-      A list of lock which we removed. The list is always equal to the names
-      list if we were holding all the locks exclusively.
+    @return:: a list of locks which we removed; the list is always
+        equal to the names list if we were holding all the locks
+        exclusively
 
     """
     if not blocking and not self._is_owned():
@@ -706,12 +703,13 @@ class GanetiLockManager:
     There should be only a GanetiLockManager object at any time, so this
     function raises an error if this is not the case.
 
-    Args:
-      nodes: list of node names
-      instances: list of instance names
+    @param nodes: list of node names
+    @param instances: list of instance names
 
     """
-    assert self.__class__._instance is None, "double GanetiLockManager instance"
+    assert self.__class__._instance is None, \
+           "double GanetiLockManager instance"
+
     self.__class__._instance = self
 
     # The keyring contains all the locks, at their level and in the correct
@@ -724,10 +722,10 @@ class GanetiLockManager:
 
   def _names(self, level):
     """List the lock names at the given level.
-    Used for debugging/testing purposes.
 
-    Args:
-      level: the level whose list of locks to get
+    This can be used for debugging/testing purposes.
+
+    @param level: the level whose list of locks to get
 
     """
     assert level in LEVELS, "Invalid locking level %s" % level
@@ -739,6 +737,8 @@ class GanetiLockManager:
     """
     return self.__keyring[level]._is_owned()
 
+  is_owned = _is_owned
+
   def _list_owned(self, level):
     """Get the set of owned locks at the given level
 
@@ -762,8 +762,10 @@ class GanetiLockManager:
     return BGL in self.__keyring[LEVEL_CLUSTER]._list_owned()
 
   def _contains_BGL(self, level, names):
-    """Check if acting on the given level and set of names will change the
-    status of the Big Ganeti Lock.
+    """Check if the level contains the BGL.
+
+    Check if acting on the given level and set of names will change
+    the status of the Big Ganeti Lock.
 
     """
     return level == LEVEL_CLUSTER and (names is None or BGL in names)
@@ -771,15 +773,14 @@ class GanetiLockManager:
   def acquire(self, level, names, blocking=1, shared=0):
     """Acquire a set of resource locks, at the same level.
 
-    Args:
-      level: the level at which the locks shall be acquired.
-             It must be a memmber of LEVELS.
-      names: the names of the locks which shall be acquired.
-             (special lock names, or instance/node names)
-      shared: whether to acquire in shared mode. By default an exclusive lock
-              will be acquired.
-      blocking: whether to block while trying to acquire or to operate in
-                try-lock mode.  this locking mode is not supported yet.
+    @param level: the level at which the locks shall be acquired;
+        it must be a memmber of LEVELS.
+    @param names: the names of the locks which shall be acquired
+        (special lock names, or instance/node names)
+    @param shared: whether to acquire in shared mode; by default
+        an exclusive lock will be acquired
+    @param blocking: whether to block while trying to acquire or to
+        operate in try-lock mode (this locking mode is not supported yet)
 
     """
     assert level in LEVELS, "Invalid locking level %s" % level
@@ -804,14 +805,13 @@ class GanetiLockManager:
   def release(self, level, names=None):
     """Release a set of resource locks, at the same level.
 
-    You must have acquired the locks, either in shared or in exclusive mode,
-    before releasing them.
+    You must have acquired the locks, either in shared or in exclusive
+    mode, before releasing them.
 
-    Args:
-      level: the level at which the locks shall be released.
-             It must be a memmber of LEVELS.
-      names: the names of the locks which shall be released.
-             (defaults to all the locks acquired at that level).
+    @param level: the level at which the locks shall be released;
+        it must be a memmber of LEVELS
+    @param names: the names of the locks which shall be released
+        (defaults to all the locks acquired at that level)
 
     """
     assert level in LEVELS, "Invalid locking level %s" % level
@@ -826,12 +826,12 @@ class GanetiLockManager:
   def add(self, level, names, acquired=0, shared=0):
     """Add locks at the specified level.
 
-    Args:
-      level: the level at which the locks shall be added.
-             It must be a memmber of LEVELS_MOD.
-      names: names of the locks to acquire
-      acquired: whether to acquire the newly added locks
-      shared: whether the acquisition will be shared
+    @param level: the level at which the locks shall be added;
+        it must be a memmber of LEVELS_MOD.
+    @param names: names of the locks to acquire
+    @param acquired: whether to acquire the newly added locks
+    @param shared: whether the acquisition will be shared
+
     """
     assert level in LEVELS_MOD, "Invalid or immutable level %s" % level
     assert self._BGL_owned(), ("You must own the BGL before performing other"
@@ -843,24 +843,23 @@ class GanetiLockManager:
   def remove(self, level, names, blocking=1):
     """Remove locks from the specified level.
 
-    You must either already own the locks you are trying to remove exclusively
-    or not own any lock at an upper level.
+    You must either already own the locks you are trying to remove
+    exclusively or not own any lock at an upper level.
 
-    Args:
-      level: the level at which the locks shall be removed.
-             It must be a memmber of LEVELS_MOD.
-      names: the names of the locks which shall be removed.
-             (special lock names, or instance/node names)
-      blocking: whether to block while trying to operate in try-lock mode.
-                this locking mode is not supported yet.
+    @param level: the level at which the locks shall be removed;
+        it must be a member of LEVELS_MOD
+    @param names: the names of the locks which shall be removed
+        (special lock names, or instance/node names)
+    @param blocking: whether to block while trying to operate in
+        try-lock mode (this locking mode is not supported yet)
 
     """
     assert level in LEVELS_MOD, "Invalid or immutable level %s" % level
     assert self._BGL_owned(), ("You must own the BGL before performing other"
            " operations")
-    # Check we either own the level or don't own anything from here up.
-    # LockSet.remove() will check the case in which we don't own all the needed
-    # resources, or we have a shared ownership.
+    # Check we either own the level or don't own anything from here
+    # up. LockSet.remove() will check the case in which we don't own
+    # all the needed resources, or we have a shared ownership.
     assert self._is_owned(level) or not self._upper_owned(level), (
            "Cannot remove locks at a level while not owning it or"
            " owning some at a greater one")