diff --git a/doc/hooks.rst b/doc/hooks.rst
index 3cfd40498c3567edb1060f4fc776d9b38e3536e8..9d04a839c862ed0b442e1c1453e4412570d03fbd 100644
--- a/doc/hooks.rst
+++ b/doc/hooks.rst
@@ -181,6 +181,18 @@ Adds a node group to the cluster.
 :pre-execution: master node
 :post-execution: master node
 
+OP_REMOVE_GROUP
++++++++++++++++
+
+Removes a node group from the cluster. Since the node group must be
+empty for removal to succeed, the concept of "nodes in the group" does
+not exist, and the hook is only executed in the master node.
+
+:directory: group-remove
+:env. vars: GROUP_NAME
+:pre-execution: master node
+:post-execution: master node
+
 
 Instance operations
 ~~~~~~~~~~~~~~~~~~~
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 24142ece9f347616931f0bb93e1fd4cac03d4173..e28112d3ab4b0045de244db00098aee22c86edcc 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -10466,6 +10466,73 @@ class LUQueryGroups(NoHooksLU):
     return output
 
 
+class LURemoveGroup(LogicalUnit):
+  HPATH = "group-remove"
+  HTYPE = constants.HTYPE_GROUP
+
+  _OP_PARAMS = [
+    _PGroupName,
+    ]
+
+  REQ_BGL = False
+
+  def ExpandNames(self):
+    # This will raises errors.OpPrereqError on its own:
+    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
+    self.needed_locks = {
+      locking.LEVEL_NODEGROUP: [self.group_uuid],
+      }
+
+  def CheckPrereq(self):
+    """Check prerequisites.
+
+    This checks that the given group name exists as a node group, that is
+    empty (i.e., contains no nodes), and that is not the last group of the
+    cluster.
+
+    """
+    # Verify that the group is empty.
+    group_nodes = [node.name
+                   for node in self.cfg.GetAllNodesInfo().values()
+                   if node.group == self.group_uuid]
+
+    if group_nodes:
+      raise errors.OpPrereqError("Group '%s' not empty, has the following"
+                                 " nodes: %s" %
+                                 (self.op.group_name,
+                                  utils.CommaJoin(utils.NiceSort(group_nodes))),
+                                 errors.ECODE_STATE)
+
+    # Verify the cluster would not be left group-less.
+    if len(self.cfg.GetNodeGroupList()) == 1:
+      raise errors.OpPrereqError("Group '%s' is the last group in the cluster,"
+                                 " which cannot be left without at least one"
+                                 " group" % self.op.group_name,
+                                 errors.ECODE_STATE)
+
+  def BuildHooksEnv(self):
+    """Build hooks env.
+
+    """
+    env = {
+      "GROUP_NAME": self.op.group_name,
+      }
+    mn = self.cfg.GetMasterNode()
+    return env, [mn], [mn]
+
+  def Exec(self, feedback_fn):
+    """Remove the node group.
+
+    """
+    try:
+      self.cfg.RemoveNodeGroup(self.group_uuid)
+    except errors.ConfigurationError:
+      raise errors.OpExecError("Group '%s' with UUID %s disappeared" %
+                               (self.op.group_name, self.group_uuid))
+
+    self.remove_locks[locking.LEVEL_NODEGROUP] = self.group_uuid
+
+
 class TagsLU(NoHooksLU): # pylint: disable-msg=W0223
   """Generic tags LU.
 
diff --git a/lib/mcpu.py b/lib/mcpu.py
index 436ce28ca09c53bdbab155b086e8d03c201eb6d7..4dabc6c6a63761877750b3c7eee6dbd78f78af13 100644
--- a/lib/mcpu.py
+++ b/lib/mcpu.py
@@ -191,6 +191,7 @@ class Processor(object):
     # node group lu
     opcodes.OpAddGroup: cmdlib.LUAddGroup,
     opcodes.OpQueryGroups: cmdlib.LUQueryGroups,
+    opcodes.OpRemoveGroup: cmdlib.LURemoveGroup,
     # os lu
     opcodes.OpDiagnoseOS: cmdlib.LUDiagnoseOS,
     # exports lu
diff --git a/lib/opcodes.py b/lib/opcodes.py
index 6d0e8aaf0c173151628532ea4bd76f31f0203931..a546f1e532b0f6017e346c0c598c94b2edea87e9 100644
--- a/lib/opcodes.py
+++ b/lib/opcodes.py
@@ -733,6 +733,13 @@ class OpQueryGroups(OpCode):
   __slots__ = ["output_fields", "names"]
 
 
+class OpRemoveGroup(OpCode):
+  """Remove a node group from the cluster."""
+  OP_ID = "OP_GROUP_REMOVE"
+  OP_DSC_FIELD = "group_name"
+  __slots__ = ["group_name"]
+
+
 # OS opcodes
 class OpDiagnoseOS(OpCode):
   """Compute the list of guest operating systems."""