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."""