diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 1323252932cbc49747a751e5316a19e2cb31e34e..ce2f0b1ec92f5a796cf1842cc65065d3890de491 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -749,6 +749,35 @@ def _GetStorageTypeArgs(cfg, storage_type):
   return []
 
 
+class LUPostInitCluster(LogicalUnit):
+  """Logical unit for running hooks after cluster initialization.
+
+  """
+  HPATH = "cluster-init"
+  HTYPE = constants.HTYPE_CLUSTER
+  _OP_REQP = []
+
+  def BuildHooksEnv(self):
+    """Build hooks env.
+
+    """
+    env = {"OP_TARGET": self.cfg.GetClusterName()}
+    mn = self.cfg.GetMasterNode()
+    return env, [], [mn]
+
+  def CheckPrereq(self):
+    """No prerequisites to check.
+
+    """
+    return True
+
+  def Exec(self, feedback_fn):
+    """Nothing to do.
+
+    """
+    return True
+
+
 class LUDestroyCluster(NoHooksLU):
   """Logical unit for destroying the cluster.
 
diff --git a/lib/mcpu.py b/lib/mcpu.py
index 01d6eb3efc9fe97b44ab2d1ce990b9a6d3aaeac5..59d312e508bff646c2a7e892ca1fbb5264508389 100644
--- a/lib/mcpu.py
+++ b/lib/mcpu.py
@@ -42,6 +42,7 @@ class Processor(object):
   """Object which runs OpCodes"""
   DISPATCH_TABLE = {
     # Cluster
+    opcodes.OpPostInitCluster: cmdlib.LUPostInitCluster,
     opcodes.OpDestroyCluster: cmdlib.LUDestroyCluster,
     opcodes.OpQueryClusterInfo: cmdlib.LUQueryClusterInfo,
     opcodes.OpVerifyCluster: cmdlib.LUVerifyCluster,
diff --git a/lib/opcodes.py b/lib/opcodes.py
index 77497fcac3345a87e7cbd865a9efcb77fda8fecc..b8673f24b82d8eb537a879777227bb1e41fb9c85 100644
--- a/lib/opcodes.py
+++ b/lib/opcodes.py
@@ -170,6 +170,17 @@ class OpCode(BaseOpCode):
 
 # cluster opcodes
 
+class OpPostInitCluster(OpCode):
+  """Post cluster initialization.
+
+  This opcode does not touch the cluster at all. Its purpose is to run hooks
+  after the cluster has been initialized.
+
+  """
+  OP_ID = "OP_CLUSTER_POST_INIT"
+  __slots__ = OpCode.__slots__ + []
+
+
 class OpDestroyCluster(OpCode):
   """Destroy the cluster.