commit 8c811986acd5e9298f981182aa23bdb28143977f
Author: Michael Hanselmann <hansmi@google.com>
Date:   Mon Nov 26 06:34:52 2012 +0100

    locking: Method to check if LockSet is fully acquired
    
    A new method is added to check whether the LockSet-internal lock is
    held. This is the case after LockSet.acquire was called with
    locking.ALL_SET.
    
    Unit tests are updated, including one where the list of names must be
    empty.
    
    Signed-off-by: Michael Hanselmann <hansmi@google.com>
    Reviewed-by: Guido Trotter <ultrotter@google.com>

diff --git a/lib/locking.py b/lib/locking.py
index e28f2f0..09c0b4a 100644
--- a/lib/locking.py
+++ b/lib/locking.py
@@ -1033,6 +1033,18 @@ class LockSet:
     else:
       return False
 
+  def owning_all(self):
+    """Checks whether current thread owns internal lock.
+
+    Holding the internal lock is equivalent with holding all locks in the set
+    (the opposite does not necessarily hold as it can not be easily
+    determined). L{add} and L{remove} require the internal lock.
+
+    @rtype: boolean
+
+    """
+    return self.__lock.is_owned()
+
   def _add_owned(self, name=None):
     """Note the current thread owns the given lock"""
     if name is None:
@@ -1620,6 +1632,14 @@ class GanetiLockManager:
     """
     return self.__keyring[level].check_owned(names, shared=shared)
 
+  def owning_all(self, level):
+    """Checks whether current thread owns all locks at a certain level.
+
+    @see: L{LockSet.owning_all}
+
+    """
+    return self.__keyring[level].owning_all()
+
   def _upper_owned(self, level):
     """Check that we don't own any lock at a level greater than the given one.
 
diff --git a/test/ganeti.locking_unittest.py b/test/ganeti.locking_unittest.py
index 04b46fc..6863d6d 100755
--- a/test/ganeti.locking_unittest.py
+++ b/test/ganeti.locking_unittest.py
@@ -1606,6 +1606,7 @@ class TestLockSet(_ThreadedTestCase):
     self.assertEqual(self.ls.acquire(None), set(["one", "two", "three"]))
     # now empty it...
     self.ls.remove(["one", "two", "three"])
+    self.assertFalse(self.ls._names())
     # and adds/locks by another thread still wait
     self._addThread(target=self._doAddSet, args=(["nine"]))
     self._addThread(target=self._doLockSet, args=(None, 1))
@@ -1713,6 +1714,7 @@ class TestLockSet(_ThreadedTestCase):
   def testDowngradeEverything(self):
     self.assertEqual(self.ls.acquire(locking.ALL_SET, shared=0),
                      set(["one", "two", "three"]))
+    self.assertTrue(self.ls.owning_all())
 
     # Ensure all locks are now owned in exclusive mode
     for name in self.ls._names():
@@ -1725,6 +1727,8 @@ class TestLockSet(_ThreadedTestCase):
     for name in self.ls._names():
       self.assertTrue(self.ls.check_owned(name, shared=1))
 
+    self.assertTrue(self.ls.owning_all())
+
   def testPriority(self):
     def _Acquire(prev, next, name, priority, success_fn):
       prev.wait()
@@ -1890,12 +1894,16 @@ class TestGanetiLockManager(_ThreadedTestCase):
                       set(self.nodes))
     self.assertEquals(self.GL.list_owned(locking.LEVEL_NODE),
                       set(self.nodes))
+    self.assertTrue(self.GL.owning_all(locking.LEVEL_INSTANCE))
+    self.assertTrue(self.GL.owning_all(locking.LEVEL_NODEGROUP))
+    self.assertTrue(self.GL.owning_all(locking.LEVEL_NODE))
     self.GL.release(locking.LEVEL_NODE)
     self.GL.release(locking.LEVEL_NODEGROUP)
     self.GL.release(locking.LEVEL_INSTANCE)
     self.GL.release(locking.LEVEL_CLUSTER)
 
   def testAcquireWholeAndPartial(self):
+    self.assertFalse(self.GL.owning_all(locking.LEVEL_INSTANCE))
     self.GL.acquire(locking.LEVEL_CLUSTER, ["BGL"], shared=1)
     self.assertEquals(self.GL.acquire(locking.LEVEL_INSTANCE, None),
                       set(self.instances))
@@ -1905,6 +1913,8 @@ class TestGanetiLockManager(_ThreadedTestCase):
                       set(["n2"]))
     self.assertEquals(self.GL.list_owned(locking.LEVEL_NODE),
                       set(["n2"]))
+    self.assertTrue(self.GL.owning_all(locking.LEVEL_INSTANCE))
+    self.assertFalse(self.GL.owning_all(locking.LEVEL_NODE))
     self.GL.release(locking.LEVEL_NODE)
     self.GL.release(locking.LEVEL_INSTANCE)
     self.GL.release(locking.LEVEL_CLUSTER)
