Processor: acquire the BGL for LUs requiring it
authorGuido Trotter <ultrotter@google.com>
Tue, 1 Jul 2008 10:43:42 +0000 (10:43 +0000)
committerGuido Trotter <ultrotter@google.com>
Tue, 1 Jul 2008 10:43:42 +0000 (10:43 +0000)
If a LU required the BGL (all LUs do, right now, by default) we'll
acquire it in the Processor before starting them. For LUs that don't
we'll still acquire it, but in a shared fashion, so that they cannot run
together with LUs that do.

We'll also note down whether we own the BGL exclusively, and if we don't
and we try to chain a LU that does, we'll fail.

More work will need to be done, of course, to convert LUs not to require
the BGL, but this basic infrastructure should guarantee the coexistance
of the old and new world for the time being.

Reviewed-by: iustinp

lib/mcpu.py

index 4e9659d..7c87c59 100644 (file)
@@ -37,6 +37,7 @@ from ganeti import cmdlib
 from ganeti import config
 from ganeti import ssconf
 from ganeti import logger
+from ganeti import locking
 
 
 class Processor(object):
@@ -98,6 +99,7 @@ class Processor(object):
     """
     self.context = context
     self._feedback_fn = feedback
+    self.exclusive_BGL = False
 
   def ExecOpCode(self, op):
     """Execute an opcode.
@@ -120,24 +122,31 @@ class Processor(object):
       sstore = ssconf.SimpleStore()
 
     write_count = self.context.cfg.write_count
-    lu = lu_class(self, op, self.context.cfg, sstore)
-    lu.CheckPrereq()
-    hm = HooksMaster(rpc.call_hooks_runner, self, lu)
-    h_results = hm.RunPhase(constants.HOOKS_PHASE_PRE)
-    lu.HooksCallBack(constants.HOOKS_PHASE_PRE,
-                     h_results, self._feedback_fn, None)
+
+    # Acquire the Big Ganeti Lock exclusively if this LU requires it, and in a
+    # shared fashion otherwise (to prevent concurrent run with an exclusive LU.
+    self.context.GLM.acquire(locking.LEVEL_CLUSTER, [locking.BGL],
+                             shared=not lu_class.REQ_BGL)
     try:
-      result = lu.Exec(self._feedback_fn)
-      h_results = hm.RunPhase(constants.HOOKS_PHASE_POST)
-      result = lu.HooksCallBack(constants.HOOKS_PHASE_POST,
-                       h_results, self._feedback_fn, result)
-    finally:
-      if lu.cfg is not None:
-        # we use lu.cfg and not self.cfg as for init cluster, self.cfg
-        # is None but lu.cfg has been recently initialized in the
-        # lu.Exec method
-        if write_count != lu.cfg.write_count:
+      self.exclusive_BGL = lu_class.REQ_BGL
+      lu = lu_class(self, op, self.context.cfg, sstore)
+      lu.CheckPrereq()
+      hm = HooksMaster(rpc.call_hooks_runner, self, lu)
+      h_results = hm.RunPhase(constants.HOOKS_PHASE_PRE)
+      lu.HooksCallBack(constants.HOOKS_PHASE_PRE, h_results,
+                       self._feedback_fn, None)
+      try:
+        result = lu.Exec(self._feedback_fn)
+        h_results = hm.RunPhase(constants.HOOKS_PHASE_POST)
+        result = lu.HooksCallBack(constants.HOOKS_PHASE_POST, h_results,
+                                  self._feedback_fn, result)
+      finally:
+        # FIXME: This needs locks if not lu_class.REQ_BGL
+        if write_count != self.context.cfg.write_count:
           hm.RunConfigUpdate()
+    finally:
+      self.context.GLM.release(locking.LEVEL_CLUSTER)
+      self.exclusive_BGL = False
 
     return result
 
@@ -158,6 +167,10 @@ class Processor(object):
     if lu_class is None:
       raise errors.OpCodeUnknown("Unknown opcode")
 
+    if lu_class.REQ_BGL and not self.exclusive_BGL:
+      raise errors.ProgrammerError("LUs which require the BGL cannot"
+                                   " be chained to granular ones.")
+
     if lu_class.REQ_WSSTORE:
       sstore = ssconf.WritableSimpleStore()
     else: