diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index f0b6dba683864212cc52076947c4b6d5d3d6ea0d..97316a2471db71dca25ddf3fe6d029fe8f03ab89 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -3184,6 +3184,75 @@ def _CheckDiskConsistency(lu, dev, node, on_primary, ldisk=False):
   return result
 
 
+class LUOutOfBand(NoHooksLU):
+  """Logical unit for OOB handling.
+
+  """
+  _OP_PARAMS = [
+    _PNodeName,
+    ("command", None, ht.TElemOf(constants.OOB_COMMANDS)),
+    ("timeout", constants.OOB_TIMEOUT, ht.TInt),
+    ]
+  REG_BGL = False
+
+  def CheckPrereq(self):
+    """Check prerequisites.
+
+    This checks:
+     - the node exists in the configuration
+     - OOB is supported
+
+    Any errors are signaled by raising errors.OpPrereqError.
+
+    """
+    self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name)
+    node = self.cfg.GetNodeInfo(self.op.node_name)
+
+    if node is None:
+      raise errors.OpPrereqError("Node %s not found" % self.op.node_name)
+
+    self.oob_program = self.cfg.GetOobProgram(node)
+
+    if not self.oob_program:
+      raise errors.OpPrereqError("OOB is not supported for node %s" %
+                                 self.op.node_name)
+
+    self.op.node_name = node.name
+    self.node = node
+
+  def ExpandNames(self):
+    """Gather locks we need.
+
+    """
+    self.needed_locks = {
+      locking.LEVEL_NODE: [self.op.node_name],
+      }
+
+  def Exec(self, feedback_fn):
+    """Execute OOB and return result if we expect any.
+
+    """
+    master_node = self.cfg.GetMasterNode()
+
+    logging.info("Executing out-of-band command '%s' using '%s' on %s",
+                 self.op.command, self.oob_program, self.op.node_name)
+    result = self.rpc.call_run_oob(master_node, self.oob_program,
+                                   self.op.command, self.op.node_name,
+                                   self.op.timeout)
+
+    result.Raise("An error occurred on execution of OOB helper")
+
+    if self.op.command == constants.OOB_HEALTH:
+      # For health we should log important events
+      for item, status in result.payload:
+        if status in [constants.OOB_STATUS_WARNING,
+                      constants.OOB_STATUS_CRITICAL]:
+          logging.warning("On node '%s' item '%s' has status '%s'",
+                          self.op.node_name, item, status)
+
+    return result.payload
+
+
 class LUDiagnoseOS(NoHooksLU):
   """Logical unit for OS diagnose/query.
 
diff --git a/lib/constants.py b/lib/constants.py
index ffb0dabff5bfe8e9dcee24e92258a61dc9b1030e..083a026ea78f1ad4f3e79ca19e469fe420c4c5c6 100644
--- a/lib/constants.py
+++ b/lib/constants.py
@@ -655,6 +655,20 @@ OOB_HEALTH = "health"
 OOB_COMMANDS = frozenset([OOB_POWER_ON, OOB_POWER_OFF, OOB_POWER_CYCLE,
                           OOB_POWER_STATUS, OOB_HEALTH])
 
+OOB_TIMEOUT = 60 # 60 seconds
+
+OOB_STATUS_OK = "OK"
+OOB_STATUS_WARNING = "WARNING"
+OOB_STATUS_CRITICAL = "CRITICAL"
+OOB_STATUS_UNKNOWN = "UNKNOWN"
+
+OOB_STATUSES = frozenset([
+  OOB_STATUS_OK,
+  OOB_STATUS_WARNING,
+  OOB_STATUS_CRITICAL,
+  OOB_STATUS_UNKNOWN,
+  ])
+
 # Instance Parameters Profile
 PP_DEFAULT = "default"