Revision 96276ae7

b/lib/cmdlib.py
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
b/lib/opcodes.py
1027 1027
    ]
1028 1028

  
1029 1029

  
1030
class OpAssignGroupNodes(OpCode):
1031
  """Assign nodes to a node group."""
1032
  OP_ID = "OP_ASSIGN_NODES"
1033
  OP_DSC_FIELD = "group_name"
1034
  OP_PARAMS = [
1035
    _PGroupName,
1036
    _PForce,
1037
    ("nodes", ht.NoDefault, ht.TListOf(ht.TNonEmptyString)),
1038
    ]
1039

  
1040

  
1030 1041
class OpQueryGroups(OpCode):
1031 1042
  """Compute the list of node groups."""
1032 1043
  OP_ID = "OP_GROUP_QUERY"

Also available in: Unified diff