diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 0630397806970c3648e4ff98be35dff8232cfaea..a13a8beb39ab0141b8f8bca0b2257e8e0be8e184 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -3790,11 +3790,14 @@ class LUAddNode(LogicalUnit): self.new_node = self.cfg.GetNodeInfo(node) assert self.new_node is not None, "Can't retrieve locked node %s" % node else: + # TODO: process an arbitrary non-default nodegroup + nodegroup = cfg.LookupNodeGroup(None) self.new_node = objects.Node(name=node, primary_ip=primary_ip, secondary_ip=secondary_ip, master_candidate=self.master_candidate, - offline=False, drained=False) + offline=False, drained=False, + nodegroup=nodegroup) def Exec(self, feedback_fn): """Adds the new node to the cluster. diff --git a/lib/config.py b/lib/config.py index 1383b773f6c32f0a16412627b1cbdbeef2a30a8b..f955b10bebd3b3688b7cab141ed33b6b39cf00c2 100644 --- a/lib/config.py +++ b/lib/config.py @@ -1065,6 +1065,7 @@ class ConfigWriter: node.serial_no = 1 node.ctime = node.mtime = time.time() + self._UnlockedAddNodeToGroup(node.name, node.nodegroup) self._config_data.nodes[node.name] = node self._config_data.cluster.serial_no += 1 self._WriteConfig() @@ -1079,6 +1080,7 @@ class ConfigWriter: if node_name not in self._config_data.nodes: raise errors.ConfigurationError("Unknown node '%s'" % node_name) + self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_name]) del self._config_data.nodes[node_name] self._config_data.cluster.serial_no += 1 self._WriteConfig() @@ -1239,6 +1241,34 @@ class ConfigWriter: return mod_list + def _UnlockedAddNodeToGroup(self, node_name, nodegroup_uuid): + """Add a given node to the specified group. + + """ + if nodegroup_uuid not in self._config_data.nodegroups: + # This can happen if a node group gets deleted between its lookup and + # when we're adding the first node to it, since we don't keep a lock in + # the meantime. It's ok though, as we'll fail cleanly if the node group + # is not found anymore. + raise errors.OpExecError("Unknown nodegroup: %s" % nodegroup_uuid) + if node_name not in self._config_data.nodegroups[nodegroup_uuid].members: + self._config_data.nodegroups[nodegroup_uuid].members.append(node_name) + + def _UnlockedRemoveNodeFromGroup(self, node): + """Remove a given node from its group. + + """ + nodegroup = node.nodegroup + if nodegroup not in self._config_data.nodegroups: + logging.warning("Warning: node '%s' has a non-existing nodegroup '%s'" + " (while being removed from it)", node.name, nodegroup) + nodegroup_obj = self._config_data.nodegroups[nodegroup] + if node.name not in nodegroup_obj.members: + logging.warning("Warning: node '%s' not a member of its nodegroup '%s'" + " (while being removed from it)", node.name, nodegroup) + else: + nodegroup_obj.members.remove(node.name) + def _BumpSerialNo(self): """Bump up the serial number of the config. @@ -1312,6 +1342,15 @@ class ConfigWriter: ) self._config_data.nodegroups[default_nodegroup_uuid] = default_nodegroup modified = True + for node in self._config_data.nodes.values(): + if not node.nodegroup: + node.nodegroup = self.LookupNodeGroup(None) + modified = True + # This is technically *not* an upgrade, but needs to be done both when + # nodegroups are being added, and upon normally loading the config, + # because the members list of a node group is discarded upon + # serializing/deserializing the object. + self._UnlockedAddNodeToGroup(node.name, node.nodegroup) if modified: self._WriteConfig() # This is ok even if it acquires the internal lock, as _UpgradeConfig is diff --git a/lib/objects.py b/lib/objects.py index 9cbbbee4142c180559b859459b1567f0716a1f5e..fe4775886c927143b312c6e9ddc21c5cf30caf8f 100644 --- a/lib/objects.py +++ b/lib/objects.py @@ -901,6 +901,7 @@ class Node(TaggableObject): "master_candidate", "offline", "drained", + "nodegroup", ] + _TIMESTAMPS + _UUID