commit 87ed6b798c0c436c1f4754d5811d45850fef1e06
Author: Klaus Aehlig <aehlig@google.com>
Date:   Mon Mar 10 10:01:14 2014 +0100

    Make mcpu acquire WConfD locks
    
    So far, the mcpu acquires locks that live in memory
    of masterd. This design does not fit with our jobs-as-processes
    goal. So make mcpu acquire the corresponding locks in WConfD
    instead.
    
    Note that this implies changes in various other files that call
    to the Ganeti Lock Manager directly.
    
    Signed-off-by: Klaus Aehlig <aehlig@google.com>
    Reviewed-by: Petr Pudlak <pudlak@google.com>

diff --git a/lib/cmdlib/base.py b/lib/cmdlib/base.py
index c735a36..33f2313 100644
--- a/lib/cmdlib/base.py
+++ b/lib/cmdlib/base.py
@@ -53,6 +53,33 @@ class ResultWithJobs:
     self.other = kwargs
 
 
+class LUWConfdClient(object):
+  """Wrapper class for wconfd client calls from LUs.
+
+  Correctly updates the cache of the LU's owned locks
+  when leaving. Also transparently adds the context
+  for resource requests.
+
+  """
+  def __init__(self, lu):
+    self.lu = lu
+
+  def TryUpdateLocks(self, req):
+    jid, livelockfile = self.lu.wconfdcontext
+    self.lu.wconfd.Client().TryUpdateLocks(jid, livelockfile, req)
+    self.lu.wconfdlocks = self.lu.wconfd.Client().ListLocks(jid, livelockfile)
+
+  def DownGradeLocksLevel(self, level):
+    jid, livelockfile = self.lu.wconfdcontext
+    self.lu.wconfd.Client().DownGradeLocksLevel(jid, livelockfile, level)
+    self.lu.wconfdlocks = self.lu.wconfd.Client().ListLocks(jid, livelockfile)
+
+  def FreeLocksLevel(self, level):
+    jid, livelockfile = self.lu.wconfdcontext
+    self.lu.wconfd.Client().FreeLocksLevel(jid, livelockfile, level)
+    self.lu.wconfdlocks = self.lu.wconfd.Client().ListLocks(jid, livelockfile)
+
+
 class LogicalUnit(object):
   """Logical Unit base class.
 
@@ -76,21 +103,28 @@ class LogicalUnit(object):
   HTYPE = None
   REQ_BGL = True
 
-  def __init__(self, processor, op, context, rpc_runner):
+  def __init__(self, processor, op, context, rpc_runner, wconfdcontext, wconfd):
     """Constructor for LogicalUnit.
 
     This needs to be overridden in derived classes in order to check op
     validity.
 
+    @type wconfdcontext: (int, string)
+    @param wconfdcontext: the identity of the logical unit to represent itself
+        to wconfd when asking for resources; it is given as job id and livelock
+        file.
+    @param wconfd: the wconfd class to use; dependency injection to allow
+        testability.
+
     """
     self.proc = processor
     self.op = op
     self.cfg = context.cfg
-    self.glm = context.glm
-    # readability alias
-    self.owned_locks = context.glm.list_owned
+    self.wconfdlocks = []
+    self.wconfdcontext = wconfdcontext
     self.context = context
     self.rpc = rpc_runner
+    self.wconfd = wconfd # wconfd module to use, for testing
 
     # Dictionaries used to declare locking needs to mcpu
     self.needed_locks = None
@@ -123,6 +157,60 @@ class LogicalUnit(object):
 
     self.CheckArguments()
 
+  def WConfdClient(self):
+    return LUWConfdClient(self)
+
+  def owned_locks(self, level):
+    """Return the list of locks owned by the LU at a given level.
+
+    This method assumes that is field wconfdlocks is set correctly
+    by mcpu.
+
+    """
+    levelprefix = "%s/" % (locking.LEVEL_NAMES[level],)
+    locks = set([lock[0][len(levelprefix):]
+                for lock in self.wconfdlocks
+                if lock[0].startswith(levelprefix)])
+    expand_fns = {
+      locking.LEVEL_CLUSTER: (lambda: [locking.BGL]),
+      locking.LEVEL_INSTANCE: self.cfg.GetInstanceList,
+      locking.LEVEL_NODE_ALLOC: (lambda: [locking.NAL]),
+      locking.LEVEL_NODEGROUP: self.cfg.GetNodeGroupList,
+      locking.LEVEL_NODE: self.cfg.GetNodeList,
+      locking.LEVEL_NODE_RES: self.cfg.GetNodeList,
+      locking.LEVEL_NETWORK: self.cfg.GetNetworkList,
+      }
+    if locking.LOCKSET_NAME in locks:
+      return expand_fns[level]()
+    else:
+      return locks
+
+  def release_request(self, level, names):
+    """Return a request to release the specified locks of the given level.
+
+    Correctly break up the group lock to do so.
+
+    """
+    levelprefix = "%s/" % (locking.LEVEL_NAMES[level],)
+    release = [[levelprefix + lock, "release"] for lock in names]
+
+    # if we break up the set-lock, make sure we ask for the rest of it.
+    setlock = levelprefix + locking.LOCKSET_NAME
+    if [setlock, "exclusive"] in self.wconfdlocks:
+      owned = self.owned_locks(level)
+      request = [[levelprefix + lock, "exclusive"]
+                 for lock in owned
+                 if lock not in names]
+    elif [setlock, "shared"] in self.wconfdlocks:
+      owned = self.owned_locks(level)
+      request = [[levelprefix + lock, "shared"]
+                 for lock in owned
+                 if lock not in names]
+    else:
+      request = []
+
+    return release + [[setlock, "release"]] + request
+
   def CheckArguments(self):
     """Check syntactic validity for the opcode arguments.
 
@@ -515,7 +603,6 @@ class QueryBase:
 
     # caller specified names and we must keep the same order
     assert self.names
-    assert not self.do_locking or lu.glm.is_owned(lock_level)
 
     missing = set(self.wanted).difference(names)
     if missing:
