Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / group.py @ 11e90588

History | View | Annotate | Download (31.5 kB)

1 f380d53c Thomas Thrainer
#
2 f380d53c Thomas Thrainer
#
3 f380d53c Thomas Thrainer
4 f380d53c Thomas Thrainer
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5 f380d53c Thomas Thrainer
#
6 f380d53c Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 f380d53c Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 f380d53c Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 f380d53c Thomas Thrainer
# (at your option) any later version.
10 f380d53c Thomas Thrainer
#
11 f380d53c Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 f380d53c Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 f380d53c Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 f380d53c Thomas Thrainer
# General Public License for more details.
15 f380d53c Thomas Thrainer
#
16 f380d53c Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 f380d53c Thomas Thrainer
# along with this program; if not, write to the Free Software
18 f380d53c Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 f380d53c Thomas Thrainer
# 02110-1301, USA.
20 f380d53c Thomas Thrainer
21 f380d53c Thomas Thrainer
22 f380d53c Thomas Thrainer
"""Logical units dealing with node groups."""
23 f380d53c Thomas Thrainer
24 f380d53c Thomas Thrainer
import logging
25 f380d53c Thomas Thrainer
26 f380d53c Thomas Thrainer
from ganeti import constants
27 f380d53c Thomas Thrainer
from ganeti import errors
28 f380d53c Thomas Thrainer
from ganeti import locking
29 f380d53c Thomas Thrainer
from ganeti import objects
30 f380d53c Thomas Thrainer
from ganeti import qlang
31 f380d53c Thomas Thrainer
from ganeti import query
32 f380d53c Thomas Thrainer
from ganeti import utils
33 f380d53c Thomas Thrainer
from ganeti.masterd import iallocator
34 5eacbcae Thomas Thrainer
from ganeti.cmdlib.base import LogicalUnit, NoHooksLU, QueryBase, \
35 f380d53c Thomas Thrainer
  ResultWithJobs
36 5eacbcae Thomas Thrainer
from ganeti.cmdlib.common import MergeAndVerifyHvState, \
37 5eacbcae Thomas Thrainer
  MergeAndVerifyDiskState, GetWantedNodes, GetUpdatedParams, \
38 5eacbcae Thomas Thrainer
  CheckNodeGroupInstances, GetUpdatedIPolicy, \
39 5eacbcae Thomas Thrainer
  ComputeNewInstanceViolations, GetDefaultIAllocator, ShareAll, \
40 5eacbcae Thomas Thrainer
  CheckInstancesNodeGroups, LoadNodeEvacResult, MapInstanceDisksToNodes
41 f380d53c Thomas Thrainer
42 f380d53c Thomas Thrainer
import ganeti.masterd.instance
43 f380d53c Thomas Thrainer
44 f380d53c Thomas Thrainer
45 f380d53c Thomas Thrainer
class LUGroupAdd(LogicalUnit):
46 f380d53c Thomas Thrainer
  """Logical unit for creating node groups.
47 f380d53c Thomas Thrainer

48 f380d53c Thomas Thrainer
  """
49 f380d53c Thomas Thrainer
  HPATH = "group-add"
50 f380d53c Thomas Thrainer
  HTYPE = constants.HTYPE_GROUP
51 f380d53c Thomas Thrainer
  REQ_BGL = False
52 f380d53c Thomas Thrainer
53 f380d53c Thomas Thrainer
  def ExpandNames(self):
54 f380d53c Thomas Thrainer
    # We need the new group's UUID here so that we can create and acquire the
55 f380d53c Thomas Thrainer
    # corresponding lock. Later, in Exec(), we'll indicate to cfg.AddNodeGroup
56 f380d53c Thomas Thrainer
    # that it should not check whether the UUID exists in the configuration.
57 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
58 f380d53c Thomas Thrainer
    self.needed_locks = {}
59 f380d53c Thomas Thrainer
    self.add_locks[locking.LEVEL_NODEGROUP] = self.group_uuid
60 f380d53c Thomas Thrainer
61 f380d53c Thomas Thrainer
  def CheckPrereq(self):
62 f380d53c Thomas Thrainer
    """Check prerequisites.
63 f380d53c Thomas Thrainer

64 f380d53c Thomas Thrainer
    This checks that the given group name is not an existing node group
65 f380d53c Thomas Thrainer
    already.
66 f380d53c Thomas Thrainer

67 f380d53c Thomas Thrainer
    """
68 f380d53c Thomas Thrainer
    try:
69 f380d53c Thomas Thrainer
      existing_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
70 f380d53c Thomas Thrainer
    except errors.OpPrereqError:
71 f380d53c Thomas Thrainer
      pass
72 f380d53c Thomas Thrainer
    else:
73 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Desired group name '%s' already exists as a"
74 f380d53c Thomas Thrainer
                                 " node group (UUID: %s)" %
75 f380d53c Thomas Thrainer
                                 (self.op.group_name, existing_uuid),
76 f380d53c Thomas Thrainer
                                 errors.ECODE_EXISTS)
77 f380d53c Thomas Thrainer
78 f380d53c Thomas Thrainer
    if self.op.ndparams:
79 f380d53c Thomas Thrainer
      utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
80 f380d53c Thomas Thrainer
81 f380d53c Thomas Thrainer
    if self.op.hv_state:
82 5eacbcae Thomas Thrainer
      self.new_hv_state = MergeAndVerifyHvState(self.op.hv_state, None)
83 f380d53c Thomas Thrainer
    else:
84 f380d53c Thomas Thrainer
      self.new_hv_state = None
85 f380d53c Thomas Thrainer
86 f380d53c Thomas Thrainer
    if self.op.disk_state:
87 5eacbcae Thomas Thrainer
      self.new_disk_state = MergeAndVerifyDiskState(self.op.disk_state, None)
88 f380d53c Thomas Thrainer
    else:
89 f380d53c Thomas Thrainer
      self.new_disk_state = None
90 f380d53c Thomas Thrainer
91 f380d53c Thomas Thrainer
    if self.op.diskparams:
92 f380d53c Thomas Thrainer
      for templ in constants.DISK_TEMPLATES:
93 f380d53c Thomas Thrainer
        if templ in self.op.diskparams:
94 f380d53c Thomas Thrainer
          utils.ForceDictType(self.op.diskparams[templ],
95 f380d53c Thomas Thrainer
                              constants.DISK_DT_TYPES)
96 f380d53c Thomas Thrainer
      self.new_diskparams = self.op.diskparams
97 f380d53c Thomas Thrainer
      try:
98 f380d53c Thomas Thrainer
        utils.VerifyDictOptions(self.new_diskparams, constants.DISK_DT_DEFAULTS)
99 f380d53c Thomas Thrainer
      except errors.OpPrereqError, err:
100 f380d53c Thomas Thrainer
        raise errors.OpPrereqError("While verify diskparams options: %s" % err,
101 f380d53c Thomas Thrainer
                                   errors.ECODE_INVAL)
102 f380d53c Thomas Thrainer
    else:
103 f380d53c Thomas Thrainer
      self.new_diskparams = {}
104 f380d53c Thomas Thrainer
105 f380d53c Thomas Thrainer
    if self.op.ipolicy:
106 f380d53c Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
107 f380d53c Thomas Thrainer
      full_ipolicy = cluster.SimpleFillIPolicy(self.op.ipolicy)
108 f380d53c Thomas Thrainer
      try:
109 f380d53c Thomas Thrainer
        objects.InstancePolicy.CheckParameterSyntax(full_ipolicy, False)
110 f380d53c Thomas Thrainer
      except errors.ConfigurationError, err:
111 f380d53c Thomas Thrainer
        raise errors.OpPrereqError("Invalid instance policy: %s" % err,
112 f380d53c Thomas Thrainer
                                   errors.ECODE_INVAL)
113 f380d53c Thomas Thrainer
114 f380d53c Thomas Thrainer
  def BuildHooksEnv(self):
115 f380d53c Thomas Thrainer
    """Build hooks env.
116 f380d53c Thomas Thrainer

117 f380d53c Thomas Thrainer
    """
118 f380d53c Thomas Thrainer
    return {
119 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
120 f380d53c Thomas Thrainer
      }
121 f380d53c Thomas Thrainer
122 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
123 f380d53c Thomas Thrainer
    """Build hooks nodes.
124 f380d53c Thomas Thrainer

125 f380d53c Thomas Thrainer
    """
126 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
127 f380d53c Thomas Thrainer
    return ([mn], [mn])
128 f380d53c Thomas Thrainer
129 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
130 f380d53c Thomas Thrainer
    """Add the node group to the cluster.
131 f380d53c Thomas Thrainer

132 f380d53c Thomas Thrainer
    """
133 f380d53c Thomas Thrainer
    group_obj = objects.NodeGroup(name=self.op.group_name, members=[],
134 f380d53c Thomas Thrainer
                                  uuid=self.group_uuid,
135 f380d53c Thomas Thrainer
                                  alloc_policy=self.op.alloc_policy,
136 f380d53c Thomas Thrainer
                                  ndparams=self.op.ndparams,
137 f380d53c Thomas Thrainer
                                  diskparams=self.new_diskparams,
138 f380d53c Thomas Thrainer
                                  ipolicy=self.op.ipolicy,
139 f380d53c Thomas Thrainer
                                  hv_state_static=self.new_hv_state,
140 f380d53c Thomas Thrainer
                                  disk_state_static=self.new_disk_state)
141 f380d53c Thomas Thrainer
142 f380d53c Thomas Thrainer
    self.cfg.AddNodeGroup(group_obj, self.proc.GetECId(), check_uuid=False)
143 f380d53c Thomas Thrainer
    del self.remove_locks[locking.LEVEL_NODEGROUP]
144 f380d53c Thomas Thrainer
145 f380d53c Thomas Thrainer
146 f380d53c Thomas Thrainer
class LUGroupAssignNodes(NoHooksLU):
147 f380d53c Thomas Thrainer
  """Logical unit for assigning nodes to groups.
148 f380d53c Thomas Thrainer

149 f380d53c Thomas Thrainer
  """
150 f380d53c Thomas Thrainer
  REQ_BGL = False
151 f380d53c Thomas Thrainer
152 f380d53c Thomas Thrainer
  def ExpandNames(self):
153 f380d53c Thomas Thrainer
    # These raise errors.OpPrereqError on their own:
154 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
155 5eacbcae Thomas Thrainer
    self.op.nodes = GetWantedNodes(self, self.op.nodes)
156 f380d53c Thomas Thrainer
157 f380d53c Thomas Thrainer
    # We want to lock all the affected nodes and groups. We have readily
158 f380d53c Thomas Thrainer
    # available the list of nodes, and the *destination* group. To gather the
159 f380d53c Thomas Thrainer
    # list of "source" groups, we need to fetch node information later on.
160 f380d53c Thomas Thrainer
    self.needed_locks = {
161 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: set([self.group_uuid]),
162 f380d53c Thomas Thrainer
      locking.LEVEL_NODE: self.op.nodes,
163 f380d53c Thomas Thrainer
      }
164 f380d53c Thomas Thrainer
165 f380d53c Thomas Thrainer
  def DeclareLocks(self, level):
166 f380d53c Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
167 f380d53c Thomas Thrainer
      assert len(self.needed_locks[locking.LEVEL_NODEGROUP]) == 1
168 f380d53c Thomas Thrainer
169 f380d53c Thomas Thrainer
      # Try to get all affected nodes' groups without having the group or node
170 f380d53c Thomas Thrainer
      # lock yet. Needs verification later in the code flow.
171 f380d53c Thomas Thrainer
      groups = self.cfg.GetNodeGroupsFromNodes(self.op.nodes)
172 f380d53c Thomas Thrainer
173 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP].update(groups)
174 f380d53c Thomas Thrainer
175 f380d53c Thomas Thrainer
  def CheckPrereq(self):
176 f380d53c Thomas Thrainer
    """Check prerequisites.
177 f380d53c Thomas Thrainer

178 f380d53c Thomas Thrainer
    """
179 f380d53c Thomas Thrainer
    assert self.needed_locks[locking.LEVEL_NODEGROUP]
180 f380d53c Thomas Thrainer
    assert (frozenset(self.owned_locks(locking.LEVEL_NODE)) ==
181 f380d53c Thomas Thrainer
            frozenset(self.op.nodes))
182 f380d53c Thomas Thrainer
183 f380d53c Thomas Thrainer
    expected_locks = (set([self.group_uuid]) |
184 f380d53c Thomas Thrainer
                      self.cfg.GetNodeGroupsFromNodes(self.op.nodes))
185 f380d53c Thomas Thrainer
    actual_locks = self.owned_locks(locking.LEVEL_NODEGROUP)
186 f380d53c Thomas Thrainer
    if actual_locks != expected_locks:
187 f380d53c Thomas Thrainer
      raise errors.OpExecError("Nodes changed groups since locks were acquired,"
188 f380d53c Thomas Thrainer
                               " current groups are '%s', used to be '%s'" %
189 f380d53c Thomas Thrainer
                               (utils.CommaJoin(expected_locks),
190 f380d53c Thomas Thrainer
                                utils.CommaJoin(actual_locks)))
191 f380d53c Thomas Thrainer
192 f380d53c Thomas Thrainer
    self.node_data = self.cfg.GetAllNodesInfo()
193 f380d53c Thomas Thrainer
    self.group = self.cfg.GetNodeGroup(self.group_uuid)
194 f380d53c Thomas Thrainer
    instance_data = self.cfg.GetAllInstancesInfo()
195 f380d53c Thomas Thrainer
196 f380d53c Thomas Thrainer
    if self.group is None:
197 f380d53c Thomas Thrainer
      raise errors.OpExecError("Could not retrieve group '%s' (UUID: %s)" %
198 f380d53c Thomas Thrainer
                               (self.op.group_name, self.group_uuid))
199 f380d53c Thomas Thrainer
200 f380d53c Thomas Thrainer
    (new_splits, previous_splits) = \
201 f380d53c Thomas Thrainer
      self.CheckAssignmentForSplitInstances([(node, self.group_uuid)
202 f380d53c Thomas Thrainer
                                             for node in self.op.nodes],
203 f380d53c Thomas Thrainer
                                            self.node_data, instance_data)
204 f380d53c Thomas Thrainer
205 f380d53c Thomas Thrainer
    if new_splits:
206 f380d53c Thomas Thrainer
      fmt_new_splits = utils.CommaJoin(utils.NiceSort(new_splits))
207 f380d53c Thomas Thrainer
208 f380d53c Thomas Thrainer
      if not self.op.force:
209 f380d53c Thomas Thrainer
        raise errors.OpExecError("The following instances get split by this"
210 f380d53c Thomas Thrainer
                                 " change and --force was not given: %s" %
211 f380d53c Thomas Thrainer
                                 fmt_new_splits)
212 f380d53c Thomas Thrainer
      else:
213 f380d53c Thomas Thrainer
        self.LogWarning("This operation will split the following instances: %s",
214 f380d53c Thomas Thrainer
                        fmt_new_splits)
215 f380d53c Thomas Thrainer
216 f380d53c Thomas Thrainer
        if previous_splits:
217 f380d53c Thomas Thrainer
          self.LogWarning("In addition, these already-split instances continue"
218 f380d53c Thomas Thrainer
                          " to be split across groups: %s",
219 f380d53c Thomas Thrainer
                          utils.CommaJoin(utils.NiceSort(previous_splits)))
220 f380d53c Thomas Thrainer
221 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
222 f380d53c Thomas Thrainer
    """Assign nodes to a new group.
223 f380d53c Thomas Thrainer

224 f380d53c Thomas Thrainer
    """
225 f380d53c Thomas Thrainer
    mods = [(node_name, self.group_uuid) for node_name in self.op.nodes]
226 f380d53c Thomas Thrainer
227 f380d53c Thomas Thrainer
    self.cfg.AssignGroupNodes(mods)
228 f380d53c Thomas Thrainer
229 f380d53c Thomas Thrainer
  @staticmethod
230 f380d53c Thomas Thrainer
  def CheckAssignmentForSplitInstances(changes, node_data, instance_data):
231 f380d53c Thomas Thrainer
    """Check for split instances after a node assignment.
232 f380d53c Thomas Thrainer

233 f380d53c Thomas Thrainer
    This method considers a series of node assignments as an atomic operation,
234 f380d53c Thomas Thrainer
    and returns information about split instances after applying the set of
235 f380d53c Thomas Thrainer
    changes.
236 f380d53c Thomas Thrainer

237 f380d53c Thomas Thrainer
    In particular, it returns information about newly split instances, and
238 f380d53c Thomas Thrainer
    instances that were already split, and remain so after the change.
239 f380d53c Thomas Thrainer

240 f380d53c Thomas Thrainer
    Only instances whose disk template is listed in constants.DTS_INT_MIRROR are
241 f380d53c Thomas Thrainer
    considered.
242 f380d53c Thomas Thrainer

243 f380d53c Thomas Thrainer
    @type changes: list of (node_name, new_group_uuid) pairs.
244 f380d53c Thomas Thrainer
    @param changes: list of node assignments to consider.
245 f380d53c Thomas Thrainer
    @param node_data: a dict with data for all nodes
246 f380d53c Thomas Thrainer
    @param instance_data: a dict with all instances to consider
247 f380d53c Thomas Thrainer
    @rtype: a two-tuple
248 f380d53c Thomas Thrainer
    @return: a list of instances that were previously okay and result split as a
249 f380d53c Thomas Thrainer
      consequence of this change, and a list of instances that were previously
250 f380d53c Thomas Thrainer
      split and this change does not fix.
251 f380d53c Thomas Thrainer

252 f380d53c Thomas Thrainer
    """
253 f380d53c Thomas Thrainer
    changed_nodes = dict((node, group) for node, group in changes
254 f380d53c Thomas Thrainer
                         if node_data[node].group != group)
255 f380d53c Thomas Thrainer
256 f380d53c Thomas Thrainer
    all_split_instances = set()
257 f380d53c Thomas Thrainer
    previously_split_instances = set()
258 f380d53c Thomas Thrainer
259 f380d53c Thomas Thrainer
    def InstanceNodes(instance):
260 f380d53c Thomas Thrainer
      return [instance.primary_node] + list(instance.secondary_nodes)
261 f380d53c Thomas Thrainer
262 f380d53c Thomas Thrainer
    for inst in instance_data.values():
263 f380d53c Thomas Thrainer
      if inst.disk_template not in constants.DTS_INT_MIRROR:
264 f380d53c Thomas Thrainer
        continue
265 f380d53c Thomas Thrainer
266 f380d53c Thomas Thrainer
      instance_nodes = InstanceNodes(inst)
267 f380d53c Thomas Thrainer
268 f380d53c Thomas Thrainer
      if len(set(node_data[node].group for node in instance_nodes)) > 1:
269 f380d53c Thomas Thrainer
        previously_split_instances.add(inst.name)
270 f380d53c Thomas Thrainer
271 f380d53c Thomas Thrainer
      if len(set(changed_nodes.get(node, node_data[node].group)
272 f380d53c Thomas Thrainer
                 for node in instance_nodes)) > 1:
273 f380d53c Thomas Thrainer
        all_split_instances.add(inst.name)
274 f380d53c Thomas Thrainer
275 f380d53c Thomas Thrainer
    return (list(all_split_instances - previously_split_instances),
276 f380d53c Thomas Thrainer
            list(previously_split_instances & all_split_instances))
277 f380d53c Thomas Thrainer
278 f380d53c Thomas Thrainer
279 5eacbcae Thomas Thrainer
class GroupQuery(QueryBase):
280 f380d53c Thomas Thrainer
  FIELDS = query.GROUP_FIELDS
281 f380d53c Thomas Thrainer
282 f380d53c Thomas Thrainer
  def ExpandNames(self, lu):
283 f380d53c Thomas Thrainer
    lu.needed_locks = {}
284 f380d53c Thomas Thrainer
285 f380d53c Thomas Thrainer
    self._all_groups = lu.cfg.GetAllNodeGroupsInfo()
286 f380d53c Thomas Thrainer
    self._cluster = lu.cfg.GetClusterInfo()
287 f380d53c Thomas Thrainer
    name_to_uuid = dict((g.name, g.uuid) for g in self._all_groups.values())
288 f380d53c Thomas Thrainer
289 f380d53c Thomas Thrainer
    if not self.names:
290 f380d53c Thomas Thrainer
      self.wanted = [name_to_uuid[name]
291 f380d53c Thomas Thrainer
                     for name in utils.NiceSort(name_to_uuid.keys())]
292 f380d53c Thomas Thrainer
    else:
293 f380d53c Thomas Thrainer
      # Accept names to be either names or UUIDs.
294 f380d53c Thomas Thrainer
      missing = []
295 f380d53c Thomas Thrainer
      self.wanted = []
296 f380d53c Thomas Thrainer
      all_uuid = frozenset(self._all_groups.keys())
297 f380d53c Thomas Thrainer
298 f380d53c Thomas Thrainer
      for name in self.names:
299 f380d53c Thomas Thrainer
        if name in all_uuid:
300 f380d53c Thomas Thrainer
          self.wanted.append(name)
301 f380d53c Thomas Thrainer
        elif name in name_to_uuid:
302 f380d53c Thomas Thrainer
          self.wanted.append(name_to_uuid[name])
303 f380d53c Thomas Thrainer
        else:
304 f380d53c Thomas Thrainer
          missing.append(name)
305 f380d53c Thomas Thrainer
306 f380d53c Thomas Thrainer
      if missing:
307 f380d53c Thomas Thrainer
        raise errors.OpPrereqError("Some groups do not exist: %s" %
308 f380d53c Thomas Thrainer
                                   utils.CommaJoin(missing),
309 f380d53c Thomas Thrainer
                                   errors.ECODE_NOENT)
310 f380d53c Thomas Thrainer
311 f380d53c Thomas Thrainer
  def DeclareLocks(self, lu, level):
312 f380d53c Thomas Thrainer
    pass
313 f380d53c Thomas Thrainer
314 f380d53c Thomas Thrainer
  def _GetQueryData(self, lu):
315 f380d53c Thomas Thrainer
    """Computes the list of node groups and their attributes.
316 f380d53c Thomas Thrainer

317 f380d53c Thomas Thrainer
    """
318 f380d53c Thomas Thrainer
    do_nodes = query.GQ_NODE in self.requested_data
319 f380d53c Thomas Thrainer
    do_instances = query.GQ_INST in self.requested_data
320 f380d53c Thomas Thrainer
321 f380d53c Thomas Thrainer
    group_to_nodes = None
322 f380d53c Thomas Thrainer
    group_to_instances = None
323 f380d53c Thomas Thrainer
324 f380d53c Thomas Thrainer
    # For GQ_NODE, we need to map group->[nodes], and group->[instances] for
325 f380d53c Thomas Thrainer
    # GQ_INST. The former is attainable with just GetAllNodesInfo(), but for the
326 f380d53c Thomas Thrainer
    # latter GetAllInstancesInfo() is not enough, for we have to go through
327 f380d53c Thomas Thrainer
    # instance->node. Hence, we will need to process nodes even if we only need
328 f380d53c Thomas Thrainer
    # instance information.
329 f380d53c Thomas Thrainer
    if do_nodes or do_instances:
330 f380d53c Thomas Thrainer
      all_nodes = lu.cfg.GetAllNodesInfo()
331 f380d53c Thomas Thrainer
      group_to_nodes = dict((uuid, []) for uuid in self.wanted)
332 f380d53c Thomas Thrainer
      node_to_group = {}
333 f380d53c Thomas Thrainer
334 f380d53c Thomas Thrainer
      for node in all_nodes.values():
335 f380d53c Thomas Thrainer
        if node.group in group_to_nodes:
336 f380d53c Thomas Thrainer
          group_to_nodes[node.group].append(node.name)
337 f380d53c Thomas Thrainer
          node_to_group[node.name] = node.group
338 f380d53c Thomas Thrainer
339 f380d53c Thomas Thrainer
      if do_instances:
340 f380d53c Thomas Thrainer
        all_instances = lu.cfg.GetAllInstancesInfo()
341 f380d53c Thomas Thrainer
        group_to_instances = dict((uuid, []) for uuid in self.wanted)
342 f380d53c Thomas Thrainer
343 f380d53c Thomas Thrainer
        for instance in all_instances.values():
344 f380d53c Thomas Thrainer
          node = instance.primary_node
345 f380d53c Thomas Thrainer
          if node in node_to_group:
346 f380d53c Thomas Thrainer
            group_to_instances[node_to_group[node]].append(instance.name)
347 f380d53c Thomas Thrainer
348 f380d53c Thomas Thrainer
        if not do_nodes:
349 f380d53c Thomas Thrainer
          # Do not pass on node information if it was not requested.
350 f380d53c Thomas Thrainer
          group_to_nodes = None
351 f380d53c Thomas Thrainer
352 f380d53c Thomas Thrainer
    return query.GroupQueryData(self._cluster,
353 f380d53c Thomas Thrainer
                                [self._all_groups[uuid]
354 f380d53c Thomas Thrainer
                                 for uuid in self.wanted],
355 f380d53c Thomas Thrainer
                                group_to_nodes, group_to_instances,
356 f380d53c Thomas Thrainer
                                query.GQ_DISKPARAMS in self.requested_data)
357 f380d53c Thomas Thrainer
358 f380d53c Thomas Thrainer
359 f380d53c Thomas Thrainer
class LUGroupQuery(NoHooksLU):
360 f380d53c Thomas Thrainer
  """Logical unit for querying node groups.
361 f380d53c Thomas Thrainer

362 f380d53c Thomas Thrainer
  """
363 f380d53c Thomas Thrainer
  REQ_BGL = False
364 f380d53c Thomas Thrainer
365 f380d53c Thomas Thrainer
  def CheckArguments(self):
366 5eacbcae Thomas Thrainer
    self.gq = GroupQuery(qlang.MakeSimpleFilter("name", self.op.names),
367 f380d53c Thomas Thrainer
                          self.op.output_fields, False)
368 f380d53c Thomas Thrainer
369 f380d53c Thomas Thrainer
  def ExpandNames(self):
370 f380d53c Thomas Thrainer
    self.gq.ExpandNames(self)
371 f380d53c Thomas Thrainer
372 f380d53c Thomas Thrainer
  def DeclareLocks(self, level):
373 f380d53c Thomas Thrainer
    self.gq.DeclareLocks(self, level)
374 f380d53c Thomas Thrainer
375 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
376 f380d53c Thomas Thrainer
    return self.gq.OldStyleQuery(self)
377 f380d53c Thomas Thrainer
378 f380d53c Thomas Thrainer
379 f380d53c Thomas Thrainer
class LUGroupSetParams(LogicalUnit):
380 f380d53c Thomas Thrainer
  """Modifies the parameters of a node group.
381 f380d53c Thomas Thrainer

382 f380d53c Thomas Thrainer
  """
383 f380d53c Thomas Thrainer
  HPATH = "group-modify"
384 f380d53c Thomas Thrainer
  HTYPE = constants.HTYPE_GROUP
385 f380d53c Thomas Thrainer
  REQ_BGL = False
386 f380d53c Thomas Thrainer
387 f380d53c Thomas Thrainer
  def CheckArguments(self):
388 f380d53c Thomas Thrainer
    all_changes = [
389 f380d53c Thomas Thrainer
      self.op.ndparams,
390 f380d53c Thomas Thrainer
      self.op.diskparams,
391 f380d53c Thomas Thrainer
      self.op.alloc_policy,
392 f380d53c Thomas Thrainer
      self.op.hv_state,
393 f380d53c Thomas Thrainer
      self.op.disk_state,
394 f380d53c Thomas Thrainer
      self.op.ipolicy,
395 f380d53c Thomas Thrainer
      ]
396 f380d53c Thomas Thrainer
397 f380d53c Thomas Thrainer
    if all_changes.count(None) == len(all_changes):
398 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Please pass at least one modification",
399 f380d53c Thomas Thrainer
                                 errors.ECODE_INVAL)
400 f380d53c Thomas Thrainer
401 f380d53c Thomas Thrainer
  def ExpandNames(self):
402 f380d53c Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
403 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
404 f380d53c Thomas Thrainer
405 f380d53c Thomas Thrainer
    self.needed_locks = {
406 f380d53c Thomas Thrainer
      locking.LEVEL_INSTANCE: [],
407 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
408 f380d53c Thomas Thrainer
      }
409 f380d53c Thomas Thrainer
410 f380d53c Thomas Thrainer
    self.share_locks[locking.LEVEL_INSTANCE] = 1
411 f380d53c Thomas Thrainer
412 f380d53c Thomas Thrainer
  def DeclareLocks(self, level):
413 f380d53c Thomas Thrainer
    if level == locking.LEVEL_INSTANCE:
414 f380d53c Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_INSTANCE]
415 f380d53c Thomas Thrainer
416 f380d53c Thomas Thrainer
      # Lock instances optimistically, needs verification once group lock has
417 f380d53c Thomas Thrainer
      # been acquired
418 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_INSTANCE] = \
419 f380d53c Thomas Thrainer
          self.cfg.GetNodeGroupInstances(self.group_uuid)
420 f380d53c Thomas Thrainer
421 f380d53c Thomas Thrainer
  @staticmethod
422 f380d53c Thomas Thrainer
  def _UpdateAndVerifyDiskParams(old, new):
423 f380d53c Thomas Thrainer
    """Updates and verifies disk parameters.
424 f380d53c Thomas Thrainer

425 f380d53c Thomas Thrainer
    """
426 5eacbcae Thomas Thrainer
    new_params = GetUpdatedParams(old, new)
427 f380d53c Thomas Thrainer
    utils.ForceDictType(new_params, constants.DISK_DT_TYPES)
428 f380d53c Thomas Thrainer
    return new_params
429 f380d53c Thomas Thrainer
430 f380d53c Thomas Thrainer
  def CheckPrereq(self):
431 f380d53c Thomas Thrainer
    """Check prerequisites.
432 f380d53c Thomas Thrainer

433 f380d53c Thomas Thrainer
    """
434 f380d53c Thomas Thrainer
    owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
435 f380d53c Thomas Thrainer
436 f380d53c Thomas Thrainer
    # Check if locked instances are still correct
437 5eacbcae Thomas Thrainer
    CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
438 f380d53c Thomas Thrainer
439 f380d53c Thomas Thrainer
    self.group = self.cfg.GetNodeGroup(self.group_uuid)
440 f380d53c Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
441 f380d53c Thomas Thrainer
442 f380d53c Thomas Thrainer
    if self.group is None:
443 f380d53c Thomas Thrainer
      raise errors.OpExecError("Could not retrieve group '%s' (UUID: %s)" %
444 f380d53c Thomas Thrainer
                               (self.op.group_name, self.group_uuid))
445 f380d53c Thomas Thrainer
446 f380d53c Thomas Thrainer
    if self.op.ndparams:
447 5eacbcae Thomas Thrainer
      new_ndparams = GetUpdatedParams(self.group.ndparams, self.op.ndparams)
448 f380d53c Thomas Thrainer
      utils.ForceDictType(new_ndparams, constants.NDS_PARAMETER_TYPES)
449 f380d53c Thomas Thrainer
      self.new_ndparams = new_ndparams
450 f380d53c Thomas Thrainer
451 f380d53c Thomas Thrainer
    if self.op.diskparams:
452 f380d53c Thomas Thrainer
      diskparams = self.group.diskparams
453 f380d53c Thomas Thrainer
      uavdp = self._UpdateAndVerifyDiskParams
454 f380d53c Thomas Thrainer
      # For each disktemplate subdict update and verify the values
455 f380d53c Thomas Thrainer
      new_diskparams = dict((dt,
456 f380d53c Thomas Thrainer
                             uavdp(diskparams.get(dt, {}),
457 f380d53c Thomas Thrainer
                                   self.op.diskparams[dt]))
458 f380d53c Thomas Thrainer
                            for dt in constants.DISK_TEMPLATES
459 f380d53c Thomas Thrainer
                            if dt in self.op.diskparams)
460 f380d53c Thomas Thrainer
      # As we've all subdicts of diskparams ready, lets merge the actual
461 f380d53c Thomas Thrainer
      # dict with all updated subdicts
462 f380d53c Thomas Thrainer
      self.new_diskparams = objects.FillDict(diskparams, new_diskparams)
463 f380d53c Thomas Thrainer
      try:
464 f380d53c Thomas Thrainer
        utils.VerifyDictOptions(self.new_diskparams, constants.DISK_DT_DEFAULTS)
465 f380d53c Thomas Thrainer
      except errors.OpPrereqError, err:
466 f380d53c Thomas Thrainer
        raise errors.OpPrereqError("While verify diskparams options: %s" % err,
467 f380d53c Thomas Thrainer
                                   errors.ECODE_INVAL)
468 f380d53c Thomas Thrainer
469 f380d53c Thomas Thrainer
    if self.op.hv_state:
470 5eacbcae Thomas Thrainer
      self.new_hv_state = MergeAndVerifyHvState(self.op.hv_state,
471 5eacbcae Thomas Thrainer
                                                self.group.hv_state_static)
472 f380d53c Thomas Thrainer
473 f380d53c Thomas Thrainer
    if self.op.disk_state:
474 f380d53c Thomas Thrainer
      self.new_disk_state = \
475 5eacbcae Thomas Thrainer
        MergeAndVerifyDiskState(self.op.disk_state,
476 5eacbcae Thomas Thrainer
                                self.group.disk_state_static)
477 f380d53c Thomas Thrainer
478 f380d53c Thomas Thrainer
    if self.op.ipolicy:
479 5eacbcae Thomas Thrainer
      self.new_ipolicy = GetUpdatedIPolicy(self.group.ipolicy,
480 5eacbcae Thomas Thrainer
                                           self.op.ipolicy,
481 5eacbcae Thomas Thrainer
                                           group_policy=True)
482 f380d53c Thomas Thrainer
483 f380d53c Thomas Thrainer
      new_ipolicy = cluster.SimpleFillIPolicy(self.new_ipolicy)
484 f380d53c Thomas Thrainer
      inst_filter = lambda inst: inst.name in owned_instances
485 f380d53c Thomas Thrainer
      instances = self.cfg.GetInstancesInfoByFilter(inst_filter).values()
486 f380d53c Thomas Thrainer
      gmi = ganeti.masterd.instance
487 f380d53c Thomas Thrainer
      violations = \
488 5eacbcae Thomas Thrainer
          ComputeNewInstanceViolations(gmi.CalculateGroupIPolicy(cluster,
489 5eacbcae Thomas Thrainer
                                                                 self.group),
490 5eacbcae Thomas Thrainer
                                       new_ipolicy, instances, self.cfg)
491 f380d53c Thomas Thrainer
492 f380d53c Thomas Thrainer
      if violations:
493 f380d53c Thomas Thrainer
        self.LogWarning("After the ipolicy change the following instances"
494 f380d53c Thomas Thrainer
                        " violate them: %s",
495 f380d53c Thomas Thrainer
                        utils.CommaJoin(violations))
496 f380d53c Thomas Thrainer
497 f380d53c Thomas Thrainer
  def BuildHooksEnv(self):
498 f380d53c Thomas Thrainer
    """Build hooks env.
499 f380d53c Thomas Thrainer

500 f380d53c Thomas Thrainer
    """
501 f380d53c Thomas Thrainer
    return {
502 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
503 f380d53c Thomas Thrainer
      "NEW_ALLOC_POLICY": self.op.alloc_policy,
504 f380d53c Thomas Thrainer
      }
505 f380d53c Thomas Thrainer
506 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
507 f380d53c Thomas Thrainer
    """Build hooks nodes.
508 f380d53c Thomas Thrainer

509 f380d53c Thomas Thrainer
    """
510 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
511 f380d53c Thomas Thrainer
    return ([mn], [mn])
512 f380d53c Thomas Thrainer
513 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
514 f380d53c Thomas Thrainer
    """Modifies the node group.
515 f380d53c Thomas Thrainer

516 f380d53c Thomas Thrainer
    """
517 f380d53c Thomas Thrainer
    result = []
518 f380d53c Thomas Thrainer
519 f380d53c Thomas Thrainer
    if self.op.ndparams:
520 f380d53c Thomas Thrainer
      self.group.ndparams = self.new_ndparams
521 f380d53c Thomas Thrainer
      result.append(("ndparams", str(self.group.ndparams)))
522 f380d53c Thomas Thrainer
523 f380d53c Thomas Thrainer
    if self.op.diskparams:
524 f380d53c Thomas Thrainer
      self.group.diskparams = self.new_diskparams
525 f380d53c Thomas Thrainer
      result.append(("diskparams", str(self.group.diskparams)))
526 f380d53c Thomas Thrainer
527 f380d53c Thomas Thrainer
    if self.op.alloc_policy:
528 f380d53c Thomas Thrainer
      self.group.alloc_policy = self.op.alloc_policy
529 f380d53c Thomas Thrainer
530 f380d53c Thomas Thrainer
    if self.op.hv_state:
531 f380d53c Thomas Thrainer
      self.group.hv_state_static = self.new_hv_state
532 f380d53c Thomas Thrainer
533 f380d53c Thomas Thrainer
    if self.op.disk_state:
534 f380d53c Thomas Thrainer
      self.group.disk_state_static = self.new_disk_state
535 f380d53c Thomas Thrainer
536 f380d53c Thomas Thrainer
    if self.op.ipolicy:
537 f380d53c Thomas Thrainer
      self.group.ipolicy = self.new_ipolicy
538 f380d53c Thomas Thrainer
539 f380d53c Thomas Thrainer
    self.cfg.Update(self.group, feedback_fn)
540 f380d53c Thomas Thrainer
    return result
541 f380d53c Thomas Thrainer
542 f380d53c Thomas Thrainer
543 f380d53c Thomas Thrainer
class LUGroupRemove(LogicalUnit):
544 f380d53c Thomas Thrainer
  HPATH = "group-remove"
545 f380d53c Thomas Thrainer
  HTYPE = constants.HTYPE_GROUP
546 f380d53c Thomas Thrainer
  REQ_BGL = False
547 f380d53c Thomas Thrainer
548 f380d53c Thomas Thrainer
  def ExpandNames(self):
549 f380d53c Thomas Thrainer
    # This will raises errors.OpPrereqError on its own:
550 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
551 f380d53c Thomas Thrainer
    self.needed_locks = {
552 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
553 f380d53c Thomas Thrainer
      }
554 f380d53c Thomas Thrainer
555 f380d53c Thomas Thrainer
  def CheckPrereq(self):
556 f380d53c Thomas Thrainer
    """Check prerequisites.
557 f380d53c Thomas Thrainer

558 f380d53c Thomas Thrainer
    This checks that the given group name exists as a node group, that is
559 f380d53c Thomas Thrainer
    empty (i.e., contains no nodes), and that is not the last group of the
560 f380d53c Thomas Thrainer
    cluster.
561 f380d53c Thomas Thrainer

562 f380d53c Thomas Thrainer
    """
563 f380d53c Thomas Thrainer
    # Verify that the group is empty.
564 f380d53c Thomas Thrainer
    group_nodes = [node.name
565 f380d53c Thomas Thrainer
                   for node in self.cfg.GetAllNodesInfo().values()
566 f380d53c Thomas Thrainer
                   if node.group == self.group_uuid]
567 f380d53c Thomas Thrainer
568 f380d53c Thomas Thrainer
    if group_nodes:
569 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Group '%s' not empty, has the following"
570 f380d53c Thomas Thrainer
                                 " nodes: %s" %
571 f380d53c Thomas Thrainer
                                 (self.op.group_name,
572 f380d53c Thomas Thrainer
                                  utils.CommaJoin(utils.NiceSort(group_nodes))),
573 f380d53c Thomas Thrainer
                                 errors.ECODE_STATE)
574 f380d53c Thomas Thrainer
575 f380d53c Thomas Thrainer
    # Verify the cluster would not be left group-less.
576 f380d53c Thomas Thrainer
    if len(self.cfg.GetNodeGroupList()) == 1:
577 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Group '%s' is the only group, cannot be"
578 f380d53c Thomas Thrainer
                                 " removed" % self.op.group_name,
579 f380d53c Thomas Thrainer
                                 errors.ECODE_STATE)
580 f380d53c Thomas Thrainer
581 f380d53c Thomas Thrainer
  def BuildHooksEnv(self):
582 f380d53c Thomas Thrainer
    """Build hooks env.
583 f380d53c Thomas Thrainer

584 f380d53c Thomas Thrainer
    """
585 f380d53c Thomas Thrainer
    return {
586 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
587 f380d53c Thomas Thrainer
      }
588 f380d53c Thomas Thrainer
589 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
590 f380d53c Thomas Thrainer
    """Build hooks nodes.
591 f380d53c Thomas Thrainer

592 f380d53c Thomas Thrainer
    """
593 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
594 f380d53c Thomas Thrainer
    return ([mn], [mn])
595 f380d53c Thomas Thrainer
596 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
597 f380d53c Thomas Thrainer
    """Remove the node group.
598 f380d53c Thomas Thrainer

599 f380d53c Thomas Thrainer
    """
600 f380d53c Thomas Thrainer
    try:
601 f380d53c Thomas Thrainer
      self.cfg.RemoveNodeGroup(self.group_uuid)
602 f380d53c Thomas Thrainer
    except errors.ConfigurationError:
603 f380d53c Thomas Thrainer
      raise errors.OpExecError("Group '%s' with UUID %s disappeared" %
604 f380d53c Thomas Thrainer
                               (self.op.group_name, self.group_uuid))
605 f380d53c Thomas Thrainer
606 f380d53c Thomas Thrainer
    self.remove_locks[locking.LEVEL_NODEGROUP] = self.group_uuid
607 f380d53c Thomas Thrainer
608 f380d53c Thomas Thrainer
609 f380d53c Thomas Thrainer
class LUGroupRename(LogicalUnit):
610 f380d53c Thomas Thrainer
  HPATH = "group-rename"
611 f380d53c Thomas Thrainer
  HTYPE = constants.HTYPE_GROUP
612 f380d53c Thomas Thrainer
  REQ_BGL = False
613 f380d53c Thomas Thrainer
614 f380d53c Thomas Thrainer
  def ExpandNames(self):
615 f380d53c Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
616 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
617 f380d53c Thomas Thrainer
618 f380d53c Thomas Thrainer
    self.needed_locks = {
619 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
620 f380d53c Thomas Thrainer
      }
621 f380d53c Thomas Thrainer
622 f380d53c Thomas Thrainer
  def CheckPrereq(self):
623 f380d53c Thomas Thrainer
    """Check prerequisites.
624 f380d53c Thomas Thrainer

625 f380d53c Thomas Thrainer
    Ensures requested new name is not yet used.
626 f380d53c Thomas Thrainer

627 f380d53c Thomas Thrainer
    """
628 f380d53c Thomas Thrainer
    try:
629 f380d53c Thomas Thrainer
      new_name_uuid = self.cfg.LookupNodeGroup(self.op.new_name)
630 f380d53c Thomas Thrainer
    except errors.OpPrereqError:
631 f380d53c Thomas Thrainer
      pass
632 f380d53c Thomas Thrainer
    else:
633 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Desired new name '%s' clashes with existing"
634 f380d53c Thomas Thrainer
                                 " node group (UUID: %s)" %
635 f380d53c Thomas Thrainer
                                 (self.op.new_name, new_name_uuid),
636 f380d53c Thomas Thrainer
                                 errors.ECODE_EXISTS)
637 f380d53c Thomas Thrainer
638 f380d53c Thomas Thrainer
  def BuildHooksEnv(self):
639 f380d53c Thomas Thrainer
    """Build hooks env.
640 f380d53c Thomas Thrainer

641 f380d53c Thomas Thrainer
    """
642 f380d53c Thomas Thrainer
    return {
643 f380d53c Thomas Thrainer
      "OLD_NAME": self.op.group_name,
644 f380d53c Thomas Thrainer
      "NEW_NAME": self.op.new_name,
645 f380d53c Thomas Thrainer
      }
646 f380d53c Thomas Thrainer
647 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
648 f380d53c Thomas Thrainer
    """Build hooks nodes.
649 f380d53c Thomas Thrainer

650 f380d53c Thomas Thrainer
    """
651 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
652 f380d53c Thomas Thrainer
653 f380d53c Thomas Thrainer
    all_nodes = self.cfg.GetAllNodesInfo()
654 f380d53c Thomas Thrainer
    all_nodes.pop(mn, None)
655 f380d53c Thomas Thrainer
656 f380d53c Thomas Thrainer
    run_nodes = [mn]
657 f380d53c Thomas Thrainer
    run_nodes.extend(node.name for node in all_nodes.values()
658 f380d53c Thomas Thrainer
                     if node.group == self.group_uuid)
659 f380d53c Thomas Thrainer
660 f380d53c Thomas Thrainer
    return (run_nodes, run_nodes)
661 f380d53c Thomas Thrainer
662 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
663 f380d53c Thomas Thrainer
    """Rename the node group.
664 f380d53c Thomas Thrainer

665 f380d53c Thomas Thrainer
    """
666 f380d53c Thomas Thrainer
    group = self.cfg.GetNodeGroup(self.group_uuid)
667 f380d53c Thomas Thrainer
668 f380d53c Thomas Thrainer
    if group is None:
669 f380d53c Thomas Thrainer
      raise errors.OpExecError("Could not retrieve group '%s' (UUID: %s)" %
670 f380d53c Thomas Thrainer
                               (self.op.group_name, self.group_uuid))
671 f380d53c Thomas Thrainer
672 f380d53c Thomas Thrainer
    group.name = self.op.new_name
673 f380d53c Thomas Thrainer
    self.cfg.Update(group, feedback_fn)
674 f380d53c Thomas Thrainer
675 f380d53c Thomas Thrainer
    return self.op.new_name
676 f380d53c Thomas Thrainer
677 f380d53c Thomas Thrainer
678 f380d53c Thomas Thrainer
class LUGroupEvacuate(LogicalUnit):
679 f380d53c Thomas Thrainer
  HPATH = "group-evacuate"
680 f380d53c Thomas Thrainer
  HTYPE = constants.HTYPE_GROUP
681 f380d53c Thomas Thrainer
  REQ_BGL = False
682 f380d53c Thomas Thrainer
683 f380d53c Thomas Thrainer
  def ExpandNames(self):
684 f380d53c Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
685 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
686 f380d53c Thomas Thrainer
687 f380d53c Thomas Thrainer
    if self.op.target_groups:
688 f380d53c Thomas Thrainer
      self.req_target_uuids = map(self.cfg.LookupNodeGroup,
689 f380d53c Thomas Thrainer
                                  self.op.target_groups)
690 f380d53c Thomas Thrainer
    else:
691 f380d53c Thomas Thrainer
      self.req_target_uuids = []
692 f380d53c Thomas Thrainer
693 f380d53c Thomas Thrainer
    if self.group_uuid in self.req_target_uuids:
694 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Group to be evacuated (%s) can not be used"
695 f380d53c Thomas Thrainer
                                 " as a target group (targets are %s)" %
696 f380d53c Thomas Thrainer
                                 (self.group_uuid,
697 f380d53c Thomas Thrainer
                                  utils.CommaJoin(self.req_target_uuids)),
698 f380d53c Thomas Thrainer
                                 errors.ECODE_INVAL)
699 f380d53c Thomas Thrainer
700 5eacbcae Thomas Thrainer
    self.op.iallocator = GetDefaultIAllocator(self.cfg, self.op.iallocator)
701 f380d53c Thomas Thrainer
702 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
703 f380d53c Thomas Thrainer
    self.needed_locks = {
704 f380d53c Thomas Thrainer
      locking.LEVEL_INSTANCE: [],
705 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: [],
706 f380d53c Thomas Thrainer
      locking.LEVEL_NODE: [],
707 f380d53c Thomas Thrainer
      }
708 f380d53c Thomas Thrainer
709 f380d53c Thomas Thrainer
  def DeclareLocks(self, level):
710 f380d53c Thomas Thrainer
    if level == locking.LEVEL_INSTANCE:
711 f380d53c Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_INSTANCE]
712 f380d53c Thomas Thrainer
713 f380d53c Thomas Thrainer
      # Lock instances optimistically, needs verification once node and group
714 f380d53c Thomas Thrainer
      # locks have been acquired
715 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_INSTANCE] = \
716 f380d53c Thomas Thrainer
        self.cfg.GetNodeGroupInstances(self.group_uuid)
717 f380d53c Thomas Thrainer
718 f380d53c Thomas Thrainer
    elif level == locking.LEVEL_NODEGROUP:
719 f380d53c Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
720 f380d53c Thomas Thrainer
721 f380d53c Thomas Thrainer
      if self.req_target_uuids:
722 f380d53c Thomas Thrainer
        lock_groups = set([self.group_uuid] + self.req_target_uuids)
723 f380d53c Thomas Thrainer
724 f380d53c Thomas Thrainer
        # Lock all groups used by instances optimistically; this requires going
725 f380d53c Thomas Thrainer
        # via the node before it's locked, requiring verification later on
726 f380d53c Thomas Thrainer
        lock_groups.update(group_uuid
727 f380d53c Thomas Thrainer
                           for instance_name in
728 f380d53c Thomas Thrainer
                             self.owned_locks(locking.LEVEL_INSTANCE)
729 f380d53c Thomas Thrainer
                           for group_uuid in
730 f380d53c Thomas Thrainer
                             self.cfg.GetInstanceNodeGroups(instance_name))
731 f380d53c Thomas Thrainer
      else:
732 f380d53c Thomas Thrainer
        # No target groups, need to lock all of them
733 f380d53c Thomas Thrainer
        lock_groups = locking.ALL_SET
734 f380d53c Thomas Thrainer
735 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = lock_groups
736 f380d53c Thomas Thrainer
737 f380d53c Thomas Thrainer
    elif level == locking.LEVEL_NODE:
738 f380d53c Thomas Thrainer
      # This will only lock the nodes in the group to be evacuated which
739 f380d53c Thomas Thrainer
      # contain actual instances
740 f380d53c Thomas Thrainer
      self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
741 f380d53c Thomas Thrainer
      self._LockInstancesNodes()
742 f380d53c Thomas Thrainer
743 f380d53c Thomas Thrainer
      # Lock all nodes in group to be evacuated and target groups
744 f380d53c Thomas Thrainer
      owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
745 f380d53c Thomas Thrainer
      assert self.group_uuid in owned_groups
746 f380d53c Thomas Thrainer
      member_nodes = [node_name
747 f380d53c Thomas Thrainer
                      for group in owned_groups
748 f380d53c Thomas Thrainer
                      for node_name in self.cfg.GetNodeGroup(group).members]
749 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE].extend(member_nodes)
750 f380d53c Thomas Thrainer
751 f380d53c Thomas Thrainer
  def CheckPrereq(self):
752 f380d53c Thomas Thrainer
    owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
753 f380d53c Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
754 f380d53c Thomas Thrainer
    owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE))
755 f380d53c Thomas Thrainer
756 f380d53c Thomas Thrainer
    assert owned_groups.issuperset(self.req_target_uuids)
757 f380d53c Thomas Thrainer
    assert self.group_uuid in owned_groups
758 f380d53c Thomas Thrainer
759 f380d53c Thomas Thrainer
    # Check if locked instances are still correct
760 5eacbcae Thomas Thrainer
    CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
761 f380d53c Thomas Thrainer
762 f380d53c Thomas Thrainer
    # Get instance information
763 f380d53c Thomas Thrainer
    self.instances = dict(self.cfg.GetMultiInstanceInfo(owned_instances))
764 f380d53c Thomas Thrainer
765 f380d53c Thomas Thrainer
    # Check if node groups for locked instances are still correct
766 5eacbcae Thomas Thrainer
    CheckInstancesNodeGroups(self.cfg, self.instances,
767 5eacbcae Thomas Thrainer
                             owned_groups, owned_nodes, self.group_uuid)
768 f380d53c Thomas Thrainer
769 f380d53c Thomas Thrainer
    if self.req_target_uuids:
770 f380d53c Thomas Thrainer
      # User requested specific target groups
771 f380d53c Thomas Thrainer
      self.target_uuids = self.req_target_uuids
772 f380d53c Thomas Thrainer
    else:
773 f380d53c Thomas Thrainer
      # All groups except the one to be evacuated are potential targets
774 f380d53c Thomas Thrainer
      self.target_uuids = [group_uuid for group_uuid in owned_groups
775 f380d53c Thomas Thrainer
                           if group_uuid != self.group_uuid]
776 f380d53c Thomas Thrainer
777 f380d53c Thomas Thrainer
      if not self.target_uuids:
778 f380d53c Thomas Thrainer
        raise errors.OpPrereqError("There are no possible target groups",
779 f380d53c Thomas Thrainer
                                   errors.ECODE_INVAL)
780 f380d53c Thomas Thrainer
781 f380d53c Thomas Thrainer
  def BuildHooksEnv(self):
782 f380d53c Thomas Thrainer
    """Build hooks env.
783 f380d53c Thomas Thrainer

784 f380d53c Thomas Thrainer
    """
785 f380d53c Thomas Thrainer
    return {
786 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
787 f380d53c Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
788 f380d53c Thomas Thrainer
      }
789 f380d53c Thomas Thrainer
790 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
791 f380d53c Thomas Thrainer
    """Build hooks nodes.
792 f380d53c Thomas Thrainer

793 f380d53c Thomas Thrainer
    """
794 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
795 f380d53c Thomas Thrainer
796 f380d53c Thomas Thrainer
    assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP)
797 f380d53c Thomas Thrainer
798 f380d53c Thomas Thrainer
    run_nodes = [mn] + self.cfg.GetNodeGroup(self.group_uuid).members
799 f380d53c Thomas Thrainer
800 f380d53c Thomas Thrainer
    return (run_nodes, run_nodes)
801 f380d53c Thomas Thrainer
802 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
803 f380d53c Thomas Thrainer
    instances = list(self.owned_locks(locking.LEVEL_INSTANCE))
804 f380d53c Thomas Thrainer
805 f380d53c Thomas Thrainer
    assert self.group_uuid not in self.target_uuids
806 f380d53c Thomas Thrainer
807 f380d53c Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=instances,
808 f380d53c Thomas Thrainer
                                      target_groups=self.target_uuids)
809 f380d53c Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
810 f380d53c Thomas Thrainer
811 f380d53c Thomas Thrainer
    ial.Run(self.op.iallocator)
812 f380d53c Thomas Thrainer
813 f380d53c Thomas Thrainer
    if not ial.success:
814 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Can't compute group evacuation using"
815 f380d53c Thomas Thrainer
                                 " iallocator '%s': %s" %
816 f380d53c Thomas Thrainer
                                 (self.op.iallocator, ial.info),
817 f380d53c Thomas Thrainer
                                 errors.ECODE_NORES)
818 f380d53c Thomas Thrainer
819 5eacbcae Thomas Thrainer
    jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
820 f380d53c Thomas Thrainer
821 f380d53c Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for evacuating node group %s",
822 f380d53c Thomas Thrainer
                 len(jobs), self.op.group_name)
823 f380d53c Thomas Thrainer
824 f380d53c Thomas Thrainer
    return ResultWithJobs(jobs)
825 f380d53c Thomas Thrainer
826 f380d53c Thomas Thrainer
827 f380d53c Thomas Thrainer
class LUGroupVerifyDisks(NoHooksLU):
828 f380d53c Thomas Thrainer
  """Verifies the status of all disks in a node group.
829 f380d53c Thomas Thrainer

830 f380d53c Thomas Thrainer
  """
831 f380d53c Thomas Thrainer
  REQ_BGL = False
832 f380d53c Thomas Thrainer
833 f380d53c Thomas Thrainer
  def ExpandNames(self):
834 f380d53c Thomas Thrainer
    # Raises errors.OpPrereqError on its own if group can't be found
835 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
836 f380d53c Thomas Thrainer
837 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
838 f380d53c Thomas Thrainer
    self.needed_locks = {
839 f380d53c Thomas Thrainer
      locking.LEVEL_INSTANCE: [],
840 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: [],
841 f380d53c Thomas Thrainer
      locking.LEVEL_NODE: [],
842 f380d53c Thomas Thrainer
843 f380d53c Thomas Thrainer
      # This opcode is acquires all node locks in a group. LUClusterVerifyDisks
844 f380d53c Thomas Thrainer
      # starts one instance of this opcode for every group, which means all
845 f380d53c Thomas Thrainer
      # nodes will be locked for a short amount of time, so it's better to
846 f380d53c Thomas Thrainer
      # acquire the node allocation lock as well.
847 f380d53c Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
848 f380d53c Thomas Thrainer
      }
849 f380d53c Thomas Thrainer
850 f380d53c Thomas Thrainer
  def DeclareLocks(self, level):
851 f380d53c Thomas Thrainer
    if level == locking.LEVEL_INSTANCE:
852 f380d53c Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_INSTANCE]
853 f380d53c Thomas Thrainer
854 f380d53c Thomas Thrainer
      # Lock instances optimistically, needs verification once node and group
855 f380d53c Thomas Thrainer
      # locks have been acquired
856 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_INSTANCE] = \
857 f380d53c Thomas Thrainer
        self.cfg.GetNodeGroupInstances(self.group_uuid)
858 f380d53c Thomas Thrainer
859 f380d53c Thomas Thrainer
    elif level == locking.LEVEL_NODEGROUP:
860 f380d53c Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
861 f380d53c Thomas Thrainer
862 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = \
863 f380d53c Thomas Thrainer
        set([self.group_uuid] +
864 f380d53c Thomas Thrainer
            # Lock all groups used by instances optimistically; this requires
865 f380d53c Thomas Thrainer
            # going via the node before it's locked, requiring verification
866 f380d53c Thomas Thrainer
            # later on
867 f380d53c Thomas Thrainer
            [group_uuid
868 f380d53c Thomas Thrainer
             for instance_name in self.owned_locks(locking.LEVEL_INSTANCE)
869 f380d53c Thomas Thrainer
             for group_uuid in self.cfg.GetInstanceNodeGroups(instance_name)])
870 f380d53c Thomas Thrainer
871 f380d53c Thomas Thrainer
    elif level == locking.LEVEL_NODE:
872 f380d53c Thomas Thrainer
      # This will only lock the nodes in the group to be verified which contain
873 f380d53c Thomas Thrainer
      # actual instances
874 f380d53c Thomas Thrainer
      self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
875 f380d53c Thomas Thrainer
      self._LockInstancesNodes()
876 f380d53c Thomas Thrainer
877 f380d53c Thomas Thrainer
      # Lock all nodes in group to be verified
878 f380d53c Thomas Thrainer
      assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP)
879 f380d53c Thomas Thrainer
      member_nodes = self.cfg.GetNodeGroup(self.group_uuid).members
880 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE].extend(member_nodes)
881 f380d53c Thomas Thrainer
882 f380d53c Thomas Thrainer
  def CheckPrereq(self):
883 f380d53c Thomas Thrainer
    owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
884 f380d53c Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
885 f380d53c Thomas Thrainer
    owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE))
886 f380d53c Thomas Thrainer
887 f380d53c Thomas Thrainer
    assert self.group_uuid in owned_groups
888 f380d53c Thomas Thrainer
889 f380d53c Thomas Thrainer
    # Check if locked instances are still correct
890 5eacbcae Thomas Thrainer
    CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instances)
891 f380d53c Thomas Thrainer
892 f380d53c Thomas Thrainer
    # Get instance information
893 f380d53c Thomas Thrainer
    self.instances = dict(self.cfg.GetMultiInstanceInfo(owned_instances))
894 f380d53c Thomas Thrainer
895 f380d53c Thomas Thrainer
    # Check if node groups for locked instances are still correct
896 5eacbcae Thomas Thrainer
    CheckInstancesNodeGroups(self.cfg, self.instances,
897 5eacbcae Thomas Thrainer
                             owned_groups, owned_nodes, self.group_uuid)
898 f380d53c Thomas Thrainer
899 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
900 f380d53c Thomas Thrainer
    """Verify integrity of cluster disks.
901 f380d53c Thomas Thrainer

902 f380d53c Thomas Thrainer
    @rtype: tuple of three items
903 f380d53c Thomas Thrainer
    @return: a tuple of (dict of node-to-node_error, list of instances
904 f380d53c Thomas Thrainer
        which need activate-disks, dict of instance: (node, volume) for
905 f380d53c Thomas Thrainer
        missing volumes
906 f380d53c Thomas Thrainer

907 f380d53c Thomas Thrainer
    """
908 f380d53c Thomas Thrainer
    res_nodes = {}
909 f380d53c Thomas Thrainer
    res_instances = set()
910 f380d53c Thomas Thrainer
    res_missing = {}
911 f380d53c Thomas Thrainer
912 5eacbcae Thomas Thrainer
    nv_dict = MapInstanceDisksToNodes(
913 1d4a4b26 Thomas Thrainer
      [inst for inst in self.instances.values() if inst.disks_active])
914 f380d53c Thomas Thrainer
915 f380d53c Thomas Thrainer
    if nv_dict:
916 f380d53c Thomas Thrainer
      nodes = utils.NiceSort(set(self.owned_locks(locking.LEVEL_NODE)) &
917 f380d53c Thomas Thrainer
                             set(self.cfg.GetVmCapableNodeList()))
918 f380d53c Thomas Thrainer
919 f380d53c Thomas Thrainer
      node_lvs = self.rpc.call_lv_list(nodes, [])
920 f380d53c Thomas Thrainer
921 f380d53c Thomas Thrainer
      for (node, node_res) in node_lvs.items():
922 f380d53c Thomas Thrainer
        if node_res.offline:
923 f380d53c Thomas Thrainer
          continue
924 f380d53c Thomas Thrainer
925 f380d53c Thomas Thrainer
        msg = node_res.fail_msg
926 f380d53c Thomas Thrainer
        if msg:
927 f380d53c Thomas Thrainer
          logging.warning("Error enumerating LVs on node %s: %s", node, msg)
928 f380d53c Thomas Thrainer
          res_nodes[node] = msg
929 f380d53c Thomas Thrainer
          continue
930 f380d53c Thomas Thrainer
931 f380d53c Thomas Thrainer
        for lv_name, (_, _, lv_online) in node_res.payload.items():
932 f380d53c Thomas Thrainer
          inst = nv_dict.pop((node, lv_name), None)
933 f380d53c Thomas Thrainer
          if not (lv_online or inst is None):
934 f380d53c Thomas Thrainer
            res_instances.add(inst)
935 f380d53c Thomas Thrainer
936 f380d53c Thomas Thrainer
      # any leftover items in nv_dict are missing LVs, let's arrange the data
937 f380d53c Thomas Thrainer
      # better
938 f380d53c Thomas Thrainer
      for key, inst in nv_dict.iteritems():
939 f380d53c Thomas Thrainer
        res_missing.setdefault(inst, []).append(list(key))
940 f380d53c Thomas Thrainer
941 f380d53c Thomas Thrainer
    return (res_nodes, list(res_instances), res_missing)