9929 |
9929 |
del self.remove_locks[locking.LEVEL_NODEGROUP]
|
9930 |
9930 |
|
9931 |
9931 |
|
|
9932 |
class LUAssignGroupNodes(NoHooksLU):
|
|
9933 |
"""Logical unit for assigning nodes to groups.
|
|
9934 |
|
|
9935 |
"""
|
|
9936 |
REQ_BGL = False
|
|
9937 |
|
|
9938 |
def ExpandNames(self):
|
|
9939 |
# These raise errors.OpPrereqError on their own:
|
|
9940 |
self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
|
|
9941 |
self.op.nodes = _GetWantedNodes(self, self.op.nodes)
|
|
9942 |
|
|
9943 |
# We want to lock all the affected nodes and groups. We have readily
|
|
9944 |
# available the list of nodes, and the *destination* group. To gather the
|
|
9945 |
# list of "source" groups, we need to fetch node information.
|
|
9946 |
self.node_data = self.cfg.GetAllNodesInfo()
|
|
9947 |
affected_groups = set(self.node_data[node].group for node in self.op.nodes)
|
|
9948 |
affected_groups.add(self.group_uuid)
|
|
9949 |
|
|
9950 |
self.needed_locks = {
|
|
9951 |
locking.LEVEL_NODEGROUP: list(affected_groups),
|
|
9952 |
locking.LEVEL_NODE: self.op.nodes,
|
|
9953 |
}
|
|
9954 |
|
|
9955 |
def CheckPrereq(self):
|
|
9956 |
"""Check prerequisites.
|
|
9957 |
|
|
9958 |
"""
|
|
9959 |
self.group = self.cfg.GetNodeGroup(self.group_uuid)
|
|
9960 |
instance_data = self.cfg.GetAllInstancesInfo()
|
|
9961 |
|
|
9962 |
if self.group is None:
|
|
9963 |
raise errors.OpExecError("Could not retrieve group '%s' (UUID: %s)" %
|
|
9964 |
(self.op.group_name, self.group_uuid))
|
|
9965 |
|
|
9966 |
(new_splits, previous_splits) = \
|
|
9967 |
self.CheckAssignmentForSplitInstances([(node, self.group_uuid)
|
|
9968 |
for node in self.op.nodes],
|
|
9969 |
self.node_data, instance_data)
|
|
9970 |
|
|
9971 |
if new_splits:
|
|
9972 |
fmt_new_splits = utils.CommaJoin(utils.NiceSort(new_splits))
|
|
9973 |
|
|
9974 |
if not self.op.force:
|
|
9975 |
raise errors.OpExecError("The following instances get split by this"
|
|
9976 |
" change and --force was not given: %s" %
|
|
9977 |
fmt_new_splits)
|
|
9978 |
else:
|
|
9979 |
self.LogWarning("This operation will split the following instances: %s",
|
|
9980 |
fmt_new_splits)
|
|
9981 |
|
|
9982 |
if previous_splits:
|
|
9983 |
self.LogWarning("In addition, these already-split instances continue"
|
|
9984 |
" to be spit across groups: %s",
|
|
9985 |
utils.CommaJoin(utils.NiceSort(previous_splits)))
|
|
9986 |
|
|
9987 |
def Exec(self, feedback_fn):
|
|
9988 |
"""Assign nodes to a new group.
|
|
9989 |
|
|
9990 |
"""
|
|
9991 |
for node in self.op.nodes:
|
|
9992 |
self.node_data[node].group = self.group_uuid
|
|
9993 |
|
|
9994 |
self.cfg.Update(self.group, feedback_fn) # Saves all modified nodes.
|
|
9995 |
|
|
9996 |
@staticmethod
|
|
9997 |
def CheckAssignmentForSplitInstances(changes, node_data, instance_data):
|
|
9998 |
"""Check for split instances after a node assignment.
|
|
9999 |
|
|
10000 |
This method considers a series of node assignments as an atomic operation,
|
|
10001 |
and returns information about split instances after applying the set of
|
|
10002 |
changes.
|
|
10003 |
|
|
10004 |
In particular, it returns information about newly split instances, and
|
|
10005 |
instances that were already split, and remain so after the change.
|
|
10006 |
|
|
10007 |
Only instances whose disk template is listed in constants.DTS_NET_MIRROR are
|
|
10008 |
considered.
|
|
10009 |
|
|
10010 |
@type changes: list of (node_name, new_group_uuid) pairs.
|
|
10011 |
@param changes: list of node assignments to consider.
|
|
10012 |
@param node_data: a dict with data for all nodes
|
|
10013 |
@param instance_data: a dict with all instances to consider
|
|
10014 |
@rtype: a two-tuple
|
|
10015 |
@return: a list of instances that were previously okay and result split as a
|
|
10016 |
consequence of this change, and a list of instances that were previously
|
|
10017 |
split and this change does not fix.
|
|
10018 |
|
|
10019 |
"""
|
|
10020 |
changed_nodes = dict((node, group) for node, group in changes
|
|
10021 |
if node_data[node].group != group)
|
|
10022 |
|
|
10023 |
all_split_instances = set()
|
|
10024 |
previously_split_instances = set()
|
|
10025 |
|
|
10026 |
def InstanceNodes(instance):
|
|
10027 |
return [instance.primary_node] + list(instance.secondary_nodes)
|
|
10028 |
|
|
10029 |
for inst in instance_data.values():
|
|
10030 |
if inst.disk_template not in constants.DTS_NET_MIRROR:
|
|
10031 |
continue
|
|
10032 |
|
|
10033 |
instance_nodes = InstanceNodes(inst)
|
|
10034 |
|
|
10035 |
if len(set(node_data[node].group for node in instance_nodes)) > 1:
|
|
10036 |
previously_split_instances.add(inst.name)
|
|
10037 |
|
|
10038 |
if len(set(changed_nodes.get(node, node_data[node].group)
|
|
10039 |
for node in instance_nodes)) > 1:
|
|
10040 |
all_split_instances.add(inst.name)
|
|
10041 |
|
|
10042 |
return (list(all_split_instances - previously_split_instances),
|
|
10043 |
list(previously_split_instances & all_split_instances))
|
|
10044 |
|
|
10045 |
|
9932 |
10046 |
class _GroupQuery(_QueryBase):
|
9933 |
10047 |
|
9934 |
10048 |
FIELDS = query.GROUP_FIELDS
|