Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / group.py @ 2a02d6fe

History | View | Annotate | Download (31.9 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 235a6b29 Thomas Thrainer
import itertools
25 f380d53c Thomas Thrainer
import logging
26 f380d53c Thomas Thrainer
27 f380d53c Thomas Thrainer
from ganeti import constants
28 f380d53c Thomas Thrainer
from ganeti import errors
29 f380d53c Thomas Thrainer
from ganeti import locking
30 f380d53c Thomas Thrainer
from ganeti import objects
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 702243ec Helga Velroyen
  CheckInstancesNodeGroups, LoadNodeEvacResult, MapInstanceLvsToNodes, \
41 294254b1 Raffa Santi
  CheckIpolicyVsDiskTemplates, CheckDiskAccessModeValidity, \
42 294254b1 Raffa Santi
  CheckDiskAccessModeConsistency
43 f380d53c Thomas Thrainer
44 f380d53c Thomas Thrainer
import ganeti.masterd.instance
45 f380d53c Thomas Thrainer
46 f380d53c Thomas Thrainer
47 f380d53c Thomas Thrainer
class LUGroupAdd(LogicalUnit):
48 f380d53c Thomas Thrainer
  """Logical unit for creating node groups.
49 f380d53c Thomas Thrainer

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

66 702243ec Helga Velroyen
    """
67 702243ec Helga Velroyen
    if self.op.ipolicy:
68 702243ec Helga Velroyen
      cluster = self.cfg.GetClusterInfo()
69 702243ec Helga Velroyen
      full_ipolicy = cluster.SimpleFillIPolicy(self.op.ipolicy)
70 702243ec Helga Velroyen
      try:
71 702243ec Helga Velroyen
        objects.InstancePolicy.CheckParameterSyntax(full_ipolicy, False)
72 702243ec Helga Velroyen
      except errors.ConfigurationError, err:
73 702243ec Helga Velroyen
        raise errors.OpPrereqError("Invalid instance policy: %s" % err,
74 702243ec Helga Velroyen
                                   errors.ECODE_INVAL)
75 702243ec Helga Velroyen
      CheckIpolicyVsDiskTemplates(full_ipolicy,
76 702243ec Helga Velroyen
                                  cluster.enabled_disk_templates)
77 702243ec Helga Velroyen
78 f380d53c Thomas Thrainer
  def CheckPrereq(self):
79 f380d53c Thomas Thrainer
    """Check prerequisites.
80 f380d53c Thomas Thrainer

81 f380d53c Thomas Thrainer
    This checks that the given group name is not an existing node group
82 f380d53c Thomas Thrainer
    already.
83 f380d53c Thomas Thrainer

84 f380d53c Thomas Thrainer
    """
85 f380d53c Thomas Thrainer
    try:
86 f380d53c Thomas Thrainer
      existing_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
87 f380d53c Thomas Thrainer
    except errors.OpPrereqError:
88 f380d53c Thomas Thrainer
      pass
89 f380d53c Thomas Thrainer
    else:
90 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Desired group name '%s' already exists as a"
91 f380d53c Thomas Thrainer
                                 " node group (UUID: %s)" %
92 f380d53c Thomas Thrainer
                                 (self.op.group_name, existing_uuid),
93 f380d53c Thomas Thrainer
                                 errors.ECODE_EXISTS)
94 f380d53c Thomas Thrainer
95 f380d53c Thomas Thrainer
    if self.op.ndparams:
96 f380d53c Thomas Thrainer
      utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
97 f380d53c Thomas Thrainer
98 f380d53c Thomas Thrainer
    if self.op.hv_state:
99 5eacbcae Thomas Thrainer
      self.new_hv_state = MergeAndVerifyHvState(self.op.hv_state, None)
100 f380d53c Thomas Thrainer
    else:
101 f380d53c Thomas Thrainer
      self.new_hv_state = None
102 f380d53c Thomas Thrainer
103 f380d53c Thomas Thrainer
    if self.op.disk_state:
104 5eacbcae Thomas Thrainer
      self.new_disk_state = MergeAndVerifyDiskState(self.op.disk_state, None)
105 f380d53c Thomas Thrainer
    else:
106 f380d53c Thomas Thrainer
      self.new_disk_state = None
107 f380d53c Thomas Thrainer
108 f380d53c Thomas Thrainer
    if self.op.diskparams:
109 f380d53c Thomas Thrainer
      for templ in constants.DISK_TEMPLATES:
110 f380d53c Thomas Thrainer
        if templ in self.op.diskparams:
111 f380d53c Thomas Thrainer
          utils.ForceDictType(self.op.diskparams[templ],
112 f380d53c Thomas Thrainer
                              constants.DISK_DT_TYPES)
113 f380d53c Thomas Thrainer
      self.new_diskparams = self.op.diskparams
114 f380d53c Thomas Thrainer
      try:
115 f380d53c Thomas Thrainer
        utils.VerifyDictOptions(self.new_diskparams, constants.DISK_DT_DEFAULTS)
116 f380d53c Thomas Thrainer
      except errors.OpPrereqError, err:
117 f380d53c Thomas Thrainer
        raise errors.OpPrereqError("While verify diskparams options: %s" % err,
118 f380d53c Thomas Thrainer
                                   errors.ECODE_INVAL)
119 f380d53c Thomas Thrainer
    else:
120 f380d53c Thomas Thrainer
      self.new_diskparams = {}
121 f380d53c Thomas Thrainer
122 702243ec Helga Velroyen
    self._CheckIpolicy()
123 f380d53c Thomas Thrainer
124 f380d53c Thomas Thrainer
  def BuildHooksEnv(self):
125 f380d53c Thomas Thrainer
    """Build hooks env.
126 f380d53c Thomas Thrainer

127 f380d53c Thomas Thrainer
    """
128 f380d53c Thomas Thrainer
    return {
129 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
130 f380d53c Thomas Thrainer
      }
131 f380d53c Thomas Thrainer
132 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
133 f380d53c Thomas Thrainer
    """Build hooks nodes.
134 f380d53c Thomas Thrainer

135 f380d53c Thomas Thrainer
    """
136 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
137 f380d53c Thomas Thrainer
    return ([mn], [mn])
138 f380d53c Thomas Thrainer
139 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
140 f380d53c Thomas Thrainer
    """Add the node group to the cluster.
141 f380d53c Thomas Thrainer

142 f380d53c Thomas Thrainer
    """
143 f380d53c Thomas Thrainer
    group_obj = objects.NodeGroup(name=self.op.group_name, members=[],
144 f380d53c Thomas Thrainer
                                  uuid=self.group_uuid,
145 f380d53c Thomas Thrainer
                                  alloc_policy=self.op.alloc_policy,
146 f380d53c Thomas Thrainer
                                  ndparams=self.op.ndparams,
147 f380d53c Thomas Thrainer
                                  diskparams=self.new_diskparams,
148 f380d53c Thomas Thrainer
                                  ipolicy=self.op.ipolicy,
149 f380d53c Thomas Thrainer
                                  hv_state_static=self.new_hv_state,
150 f380d53c Thomas Thrainer
                                  disk_state_static=self.new_disk_state)
151 f380d53c Thomas Thrainer
152 f380d53c Thomas Thrainer
    self.cfg.AddNodeGroup(group_obj, self.proc.GetECId(), check_uuid=False)
153 f380d53c Thomas Thrainer
    del self.remove_locks[locking.LEVEL_NODEGROUP]
154 f380d53c Thomas Thrainer
155 f380d53c Thomas Thrainer
156 f380d53c Thomas Thrainer
class LUGroupAssignNodes(NoHooksLU):
157 f380d53c Thomas Thrainer
  """Logical unit for assigning nodes to groups.
158 f380d53c Thomas Thrainer

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

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

236 f380d53c Thomas Thrainer
    """
237 1c3231aa Thomas Thrainer
    mods = [(node_uuid, self.group_uuid) for node_uuid in self.op.node_uuids]
238 f380d53c Thomas Thrainer
239 f380d53c Thomas Thrainer
    self.cfg.AssignGroupNodes(mods)
240 f380d53c Thomas Thrainer
241 f380d53c Thomas Thrainer
  @staticmethod
242 f380d53c Thomas Thrainer
  def CheckAssignmentForSplitInstances(changes, node_data, instance_data):
243 f380d53c Thomas Thrainer
    """Check for split instances after a node assignment.
244 f380d53c Thomas Thrainer

245 f380d53c Thomas Thrainer
    This method considers a series of node assignments as an atomic operation,
246 f380d53c Thomas Thrainer
    and returns information about split instances after applying the set of
247 f380d53c Thomas Thrainer
    changes.
248 f380d53c Thomas Thrainer

249 f380d53c Thomas Thrainer
    In particular, it returns information about newly split instances, and
250 f380d53c Thomas Thrainer
    instances that were already split, and remain so after the change.
251 f380d53c Thomas Thrainer

252 f380d53c Thomas Thrainer
    Only instances whose disk template is listed in constants.DTS_INT_MIRROR are
253 f380d53c Thomas Thrainer
    considered.
254 f380d53c Thomas Thrainer

255 1c3231aa Thomas Thrainer
    @type changes: list of (node_uuid, new_group_uuid) pairs.
256 f380d53c Thomas Thrainer
    @param changes: list of node assignments to consider.
257 f380d53c Thomas Thrainer
    @param node_data: a dict with data for all nodes
258 f380d53c Thomas Thrainer
    @param instance_data: a dict with all instances to consider
259 f380d53c Thomas Thrainer
    @rtype: a two-tuple
260 f380d53c Thomas Thrainer
    @return: a list of instances that were previously okay and result split as a
261 f380d53c Thomas Thrainer
      consequence of this change, and a list of instances that were previously
262 f380d53c Thomas Thrainer
      split and this change does not fix.
263 f380d53c Thomas Thrainer

264 f380d53c Thomas Thrainer
    """
265 1c3231aa Thomas Thrainer
    changed_nodes = dict((uuid, group) for uuid, group in changes
266 1c3231aa Thomas Thrainer
                         if node_data[uuid].group != group)
267 f380d53c Thomas Thrainer
268 f380d53c Thomas Thrainer
    all_split_instances = set()
269 f380d53c Thomas Thrainer
    previously_split_instances = set()
270 f380d53c Thomas Thrainer
271 f380d53c Thomas Thrainer
    for inst in instance_data.values():
272 f380d53c Thomas Thrainer
      if inst.disk_template not in constants.DTS_INT_MIRROR:
273 f380d53c Thomas Thrainer
        continue
274 f380d53c Thomas Thrainer
275 1c3231aa Thomas Thrainer
      if len(set(node_data[node_uuid].group
276 1c3231aa Thomas Thrainer
                 for node_uuid in inst.all_nodes)) > 1:
277 da4a52a3 Thomas Thrainer
        previously_split_instances.add(inst.uuid)
278 f380d53c Thomas Thrainer
279 1c3231aa Thomas Thrainer
      if len(set(changed_nodes.get(node_uuid, node_data[node_uuid].group)
280 1c3231aa Thomas Thrainer
                 for node_uuid in inst.all_nodes)) > 1:
281 da4a52a3 Thomas Thrainer
        all_split_instances.add(inst.uuid)
282 f380d53c Thomas Thrainer
283 f380d53c Thomas Thrainer
    return (list(all_split_instances - previously_split_instances),
284 f380d53c Thomas Thrainer
            list(previously_split_instances & all_split_instances))
285 f380d53c Thomas Thrainer
286 f380d53c Thomas Thrainer
287 5eacbcae Thomas Thrainer
class GroupQuery(QueryBase):
288 f380d53c Thomas Thrainer
  FIELDS = query.GROUP_FIELDS
289 f380d53c Thomas Thrainer
290 f380d53c Thomas Thrainer
  def ExpandNames(self, lu):
291 96431562 Helga Velroyen
    raise NotImplementedError
292 f380d53c Thomas Thrainer
293 f380d53c Thomas Thrainer
  def DeclareLocks(self, lu, level):
294 f380d53c Thomas Thrainer
    pass
295 f380d53c Thomas Thrainer
296 f380d53c Thomas Thrainer
  def _GetQueryData(self, lu):
297 96431562 Helga Velroyen
    raise NotImplementedError
298 f380d53c Thomas Thrainer
299 f380d53c Thomas Thrainer
300 f380d53c Thomas Thrainer
class LUGroupQuery(NoHooksLU):
301 f380d53c Thomas Thrainer
  """Logical unit for querying node groups.
302 f380d53c Thomas Thrainer

303 f380d53c Thomas Thrainer
  """
304 f380d53c Thomas Thrainer
  REQ_BGL = False
305 f380d53c Thomas Thrainer
306 f380d53c Thomas Thrainer
  def CheckArguments(self):
307 96431562 Helga Velroyen
    raise NotImplementedError
308 f380d53c Thomas Thrainer
309 f380d53c Thomas Thrainer
  def ExpandNames(self):
310 96431562 Helga Velroyen
    raise NotImplementedError
311 f380d53c Thomas Thrainer
312 f380d53c Thomas Thrainer
  def DeclareLocks(self, level):
313 96431562 Helga Velroyen
    raise NotImplementedError
314 f380d53c Thomas Thrainer
315 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
316 96431562 Helga Velroyen
    raise NotImplementedError
317 f380d53c Thomas Thrainer
318 f380d53c Thomas Thrainer
319 f380d53c Thomas Thrainer
class LUGroupSetParams(LogicalUnit):
320 f380d53c Thomas Thrainer
  """Modifies the parameters of a node group.
321 f380d53c Thomas Thrainer

322 f380d53c Thomas Thrainer
  """
323 f380d53c Thomas Thrainer
  HPATH = "group-modify"
324 f380d53c Thomas Thrainer
  HTYPE = constants.HTYPE_GROUP
325 f380d53c Thomas Thrainer
  REQ_BGL = False
326 f380d53c Thomas Thrainer
327 f380d53c Thomas Thrainer
  def CheckArguments(self):
328 f380d53c Thomas Thrainer
    all_changes = [
329 f380d53c Thomas Thrainer
      self.op.ndparams,
330 f380d53c Thomas Thrainer
      self.op.diskparams,
331 f380d53c Thomas Thrainer
      self.op.alloc_policy,
332 f380d53c Thomas Thrainer
      self.op.hv_state,
333 f380d53c Thomas Thrainer
      self.op.disk_state,
334 f380d53c Thomas Thrainer
      self.op.ipolicy,
335 f380d53c Thomas Thrainer
      ]
336 f380d53c Thomas Thrainer
337 f380d53c Thomas Thrainer
    if all_changes.count(None) == len(all_changes):
338 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Please pass at least one modification",
339 f380d53c Thomas Thrainer
                                 errors.ECODE_INVAL)
340 f380d53c Thomas Thrainer
341 294254b1 Raffa Santi
    if self.op.diskparams:
342 294254b1 Raffa Santi
      CheckDiskAccessModeValidity(self.op.diskparams)
343 294254b1 Raffa Santi
344 f380d53c Thomas Thrainer
  def ExpandNames(self):
345 f380d53c Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
346 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
347 f380d53c Thomas Thrainer
348 f380d53c Thomas Thrainer
    self.needed_locks = {
349 f380d53c Thomas Thrainer
      locking.LEVEL_INSTANCE: [],
350 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
351 f380d53c Thomas Thrainer
      }
352 f380d53c Thomas Thrainer
353 f380d53c Thomas Thrainer
    self.share_locks[locking.LEVEL_INSTANCE] = 1
354 f380d53c Thomas Thrainer
355 f380d53c Thomas Thrainer
  def DeclareLocks(self, level):
356 f380d53c Thomas Thrainer
    if level == locking.LEVEL_INSTANCE:
357 f380d53c Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_INSTANCE]
358 f380d53c Thomas Thrainer
359 f380d53c Thomas Thrainer
      # Lock instances optimistically, needs verification once group lock has
360 f380d53c Thomas Thrainer
      # been acquired
361 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_INSTANCE] = \
362 da4a52a3 Thomas Thrainer
        self.cfg.GetInstanceNames(
363 da4a52a3 Thomas Thrainer
          self.cfg.GetNodeGroupInstances(self.group_uuid))
364 f380d53c Thomas Thrainer
365 f380d53c Thomas Thrainer
  @staticmethod
366 f380d53c Thomas Thrainer
  def _UpdateAndVerifyDiskParams(old, new):
367 f380d53c Thomas Thrainer
    """Updates and verifies disk parameters.
368 f380d53c Thomas Thrainer

369 f380d53c Thomas Thrainer
    """
370 5eacbcae Thomas Thrainer
    new_params = GetUpdatedParams(old, new)
371 f380d53c Thomas Thrainer
    utils.ForceDictType(new_params, constants.DISK_DT_TYPES)
372 f380d53c Thomas Thrainer
    return new_params
373 f380d53c Thomas Thrainer
374 702243ec Helga Velroyen
  def _CheckIpolicy(self, cluster, owned_instance_names):
375 702243ec Helga Velroyen
    """Sanity checks for the ipolicy.
376 702243ec Helga Velroyen

377 702243ec Helga Velroyen
    @type cluster: C{objects.Cluster}
378 702243ec Helga Velroyen
    @param cluster: the cluster's configuration
379 702243ec Helga Velroyen
    @type owned_instance_names: list of string
380 702243ec Helga Velroyen
    @param owned_instance_names: list of instances
381 702243ec Helga Velroyen

382 702243ec Helga Velroyen
    """
383 702243ec Helga Velroyen
    if self.op.ipolicy:
384 702243ec Helga Velroyen
      self.new_ipolicy = GetUpdatedIPolicy(self.group.ipolicy,
385 702243ec Helga Velroyen
                                           self.op.ipolicy,
386 702243ec Helga Velroyen
                                           group_policy=True)
387 702243ec Helga Velroyen
388 702243ec Helga Velroyen
      new_ipolicy = cluster.SimpleFillIPolicy(self.new_ipolicy)
389 702243ec Helga Velroyen
      CheckIpolicyVsDiskTemplates(new_ipolicy,
390 702243ec Helga Velroyen
                                  cluster.enabled_disk_templates)
391 bbe0f264 Thomas Thrainer
      instances = \
392 bbe0f264 Thomas Thrainer
        dict(self.cfg.GetMultiInstanceInfoByName(owned_instance_names))
393 702243ec Helga Velroyen
      gmi = ganeti.masterd.instance
394 702243ec Helga Velroyen
      violations = \
395 702243ec Helga Velroyen
          ComputeNewInstanceViolations(gmi.CalculateGroupIPolicy(cluster,
396 702243ec Helga Velroyen
                                                                 self.group),
397 bbe0f264 Thomas Thrainer
                                       new_ipolicy, instances.values(),
398 bbe0f264 Thomas Thrainer
                                       self.cfg)
399 702243ec Helga Velroyen
400 702243ec Helga Velroyen
      if violations:
401 702243ec Helga Velroyen
        self.LogWarning("After the ipolicy change the following instances"
402 702243ec Helga Velroyen
                        " violate them: %s",
403 702243ec Helga Velroyen
                        utils.CommaJoin(violations))
404 702243ec Helga Velroyen
405 f380d53c Thomas Thrainer
  def CheckPrereq(self):
406 f380d53c Thomas Thrainer
    """Check prerequisites.
407 f380d53c Thomas Thrainer

408 f380d53c Thomas Thrainer
    """
409 da4a52a3 Thomas Thrainer
    owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
410 f380d53c Thomas Thrainer
411 f380d53c Thomas Thrainer
    # Check if locked instances are still correct
412 da4a52a3 Thomas Thrainer
    CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instance_names)
413 f380d53c Thomas Thrainer
414 f380d53c Thomas Thrainer
    self.group = self.cfg.GetNodeGroup(self.group_uuid)
415 f380d53c Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
416 f380d53c Thomas Thrainer
417 f380d53c Thomas Thrainer
    if self.group is None:
418 f380d53c Thomas Thrainer
      raise errors.OpExecError("Could not retrieve group '%s' (UUID: %s)" %
419 f380d53c Thomas Thrainer
                               (self.op.group_name, self.group_uuid))
420 f380d53c Thomas Thrainer
421 f380d53c Thomas Thrainer
    if self.op.ndparams:
422 5eacbcae Thomas Thrainer
      new_ndparams = GetUpdatedParams(self.group.ndparams, self.op.ndparams)
423 f380d53c Thomas Thrainer
      utils.ForceDictType(new_ndparams, constants.NDS_PARAMETER_TYPES)
424 f380d53c Thomas Thrainer
      self.new_ndparams = new_ndparams
425 f380d53c Thomas Thrainer
426 f380d53c Thomas Thrainer
    if self.op.diskparams:
427 f380d53c Thomas Thrainer
      diskparams = self.group.diskparams
428 f380d53c Thomas Thrainer
      uavdp = self._UpdateAndVerifyDiskParams
429 f380d53c Thomas Thrainer
      # For each disktemplate subdict update and verify the values
430 f380d53c Thomas Thrainer
      new_diskparams = dict((dt,
431 f380d53c Thomas Thrainer
                             uavdp(diskparams.get(dt, {}),
432 f380d53c Thomas Thrainer
                                   self.op.diskparams[dt]))
433 f380d53c Thomas Thrainer
                            for dt in constants.DISK_TEMPLATES
434 f380d53c Thomas Thrainer
                            if dt in self.op.diskparams)
435 f380d53c Thomas Thrainer
      # As we've all subdicts of diskparams ready, lets merge the actual
436 f380d53c Thomas Thrainer
      # dict with all updated subdicts
437 f380d53c Thomas Thrainer
      self.new_diskparams = objects.FillDict(diskparams, new_diskparams)
438 294254b1 Raffa Santi
439 f380d53c Thomas Thrainer
      try:
440 f380d53c Thomas Thrainer
        utils.VerifyDictOptions(self.new_diskparams, constants.DISK_DT_DEFAULTS)
441 294254b1 Raffa Santi
        CheckDiskAccessModeConsistency(self.new_diskparams, self.cfg,
442 294254b1 Raffa Santi
                                       group=self.group)
443 f380d53c Thomas Thrainer
      except errors.OpPrereqError, err:
444 f380d53c Thomas Thrainer
        raise errors.OpPrereqError("While verify diskparams options: %s" % err,
445 f380d53c Thomas Thrainer
                                   errors.ECODE_INVAL)
446 f380d53c Thomas Thrainer
447 f380d53c Thomas Thrainer
    if self.op.hv_state:
448 5eacbcae Thomas Thrainer
      self.new_hv_state = MergeAndVerifyHvState(self.op.hv_state,
449 5eacbcae Thomas Thrainer
                                                self.group.hv_state_static)
450 f380d53c Thomas Thrainer
451 f380d53c Thomas Thrainer
    if self.op.disk_state:
452 f380d53c Thomas Thrainer
      self.new_disk_state = \
453 5eacbcae Thomas Thrainer
        MergeAndVerifyDiskState(self.op.disk_state,
454 5eacbcae Thomas Thrainer
                                self.group.disk_state_static)
455 f380d53c Thomas Thrainer
456 702243ec Helga Velroyen
    self._CheckIpolicy(cluster, owned_instance_names)
457 f380d53c Thomas Thrainer
458 f380d53c Thomas Thrainer
  def BuildHooksEnv(self):
459 f380d53c Thomas Thrainer
    """Build hooks env.
460 f380d53c Thomas Thrainer

461 f380d53c Thomas Thrainer
    """
462 f380d53c Thomas Thrainer
    return {
463 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
464 f380d53c Thomas Thrainer
      "NEW_ALLOC_POLICY": self.op.alloc_policy,
465 f380d53c Thomas Thrainer
      }
466 f380d53c Thomas Thrainer
467 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
468 f380d53c Thomas Thrainer
    """Build hooks nodes.
469 f380d53c Thomas Thrainer

470 f380d53c Thomas Thrainer
    """
471 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
472 f380d53c Thomas Thrainer
    return ([mn], [mn])
473 f380d53c Thomas Thrainer
474 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
475 f380d53c Thomas Thrainer
    """Modifies the node group.
476 f380d53c Thomas Thrainer

477 f380d53c Thomas Thrainer
    """
478 f380d53c Thomas Thrainer
    result = []
479 f380d53c Thomas Thrainer
480 f380d53c Thomas Thrainer
    if self.op.ndparams:
481 f380d53c Thomas Thrainer
      self.group.ndparams = self.new_ndparams
482 f380d53c Thomas Thrainer
      result.append(("ndparams", str(self.group.ndparams)))
483 f380d53c Thomas Thrainer
484 f380d53c Thomas Thrainer
    if self.op.diskparams:
485 f380d53c Thomas Thrainer
      self.group.diskparams = self.new_diskparams
486 f380d53c Thomas Thrainer
      result.append(("diskparams", str(self.group.diskparams)))
487 f380d53c Thomas Thrainer
488 f380d53c Thomas Thrainer
    if self.op.alloc_policy:
489 f380d53c Thomas Thrainer
      self.group.alloc_policy = self.op.alloc_policy
490 f380d53c Thomas Thrainer
491 f380d53c Thomas Thrainer
    if self.op.hv_state:
492 f380d53c Thomas Thrainer
      self.group.hv_state_static = self.new_hv_state
493 f380d53c Thomas Thrainer
494 f380d53c Thomas Thrainer
    if self.op.disk_state:
495 f380d53c Thomas Thrainer
      self.group.disk_state_static = self.new_disk_state
496 f380d53c Thomas Thrainer
497 f380d53c Thomas Thrainer
    if self.op.ipolicy:
498 f380d53c Thomas Thrainer
      self.group.ipolicy = self.new_ipolicy
499 f380d53c Thomas Thrainer
500 f380d53c Thomas Thrainer
    self.cfg.Update(self.group, feedback_fn)
501 f380d53c Thomas Thrainer
    return result
502 f380d53c Thomas Thrainer
503 f380d53c Thomas Thrainer
504 f380d53c Thomas Thrainer
class LUGroupRemove(LogicalUnit):
505 f380d53c Thomas Thrainer
  HPATH = "group-remove"
506 f380d53c Thomas Thrainer
  HTYPE = constants.HTYPE_GROUP
507 f380d53c Thomas Thrainer
  REQ_BGL = False
508 f380d53c Thomas Thrainer
509 f380d53c Thomas Thrainer
  def ExpandNames(self):
510 f380d53c Thomas Thrainer
    # This will raises errors.OpPrereqError on its own:
511 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
512 f380d53c Thomas Thrainer
    self.needed_locks = {
513 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
514 f380d53c Thomas Thrainer
      }
515 f380d53c Thomas Thrainer
516 f380d53c Thomas Thrainer
  def CheckPrereq(self):
517 f380d53c Thomas Thrainer
    """Check prerequisites.
518 f380d53c Thomas Thrainer

519 f380d53c Thomas Thrainer
    This checks that the given group name exists as a node group, that is
520 f380d53c Thomas Thrainer
    empty (i.e., contains no nodes), and that is not the last group of the
521 f380d53c Thomas Thrainer
    cluster.
522 f380d53c Thomas Thrainer

523 f380d53c Thomas Thrainer
    """
524 f380d53c Thomas Thrainer
    # Verify that the group is empty.
525 1c3231aa Thomas Thrainer
    group_nodes = [node.uuid
526 f380d53c Thomas Thrainer
                   for node in self.cfg.GetAllNodesInfo().values()
527 f380d53c Thomas Thrainer
                   if node.group == self.group_uuid]
528 f380d53c Thomas Thrainer
529 f380d53c Thomas Thrainer
    if group_nodes:
530 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Group '%s' not empty, has the following"
531 f380d53c Thomas Thrainer
                                 " nodes: %s" %
532 f380d53c Thomas Thrainer
                                 (self.op.group_name,
533 f380d53c Thomas Thrainer
                                  utils.CommaJoin(utils.NiceSort(group_nodes))),
534 f380d53c Thomas Thrainer
                                 errors.ECODE_STATE)
535 f380d53c Thomas Thrainer
536 f380d53c Thomas Thrainer
    # Verify the cluster would not be left group-less.
537 f380d53c Thomas Thrainer
    if len(self.cfg.GetNodeGroupList()) == 1:
538 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Group '%s' is the only group, cannot be"
539 f380d53c Thomas Thrainer
                                 " removed" % self.op.group_name,
540 f380d53c Thomas Thrainer
                                 errors.ECODE_STATE)
541 f380d53c Thomas Thrainer
542 f380d53c Thomas Thrainer
  def BuildHooksEnv(self):
543 f380d53c Thomas Thrainer
    """Build hooks env.
544 f380d53c Thomas Thrainer

545 f380d53c Thomas Thrainer
    """
546 f380d53c Thomas Thrainer
    return {
547 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
548 f380d53c Thomas Thrainer
      }
549 f380d53c Thomas Thrainer
550 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
551 f380d53c Thomas Thrainer
    """Build hooks nodes.
552 f380d53c Thomas Thrainer

553 f380d53c Thomas Thrainer
    """
554 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
555 f380d53c Thomas Thrainer
    return ([mn], [mn])
556 f380d53c Thomas Thrainer
557 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
558 f380d53c Thomas Thrainer
    """Remove the node group.
559 f380d53c Thomas Thrainer

560 f380d53c Thomas Thrainer
    """
561 f380d53c Thomas Thrainer
    try:
562 f380d53c Thomas Thrainer
      self.cfg.RemoveNodeGroup(self.group_uuid)
563 f380d53c Thomas Thrainer
    except errors.ConfigurationError:
564 f380d53c Thomas Thrainer
      raise errors.OpExecError("Group '%s' with UUID %s disappeared" %
565 f380d53c Thomas Thrainer
                               (self.op.group_name, self.group_uuid))
566 f380d53c Thomas Thrainer
567 f380d53c Thomas Thrainer
    self.remove_locks[locking.LEVEL_NODEGROUP] = self.group_uuid
568 f380d53c Thomas Thrainer
569 f380d53c Thomas Thrainer
570 f380d53c Thomas Thrainer
class LUGroupRename(LogicalUnit):
571 f380d53c Thomas Thrainer
  HPATH = "group-rename"
572 f380d53c Thomas Thrainer
  HTYPE = constants.HTYPE_GROUP
573 f380d53c Thomas Thrainer
  REQ_BGL = False
574 f380d53c Thomas Thrainer
575 f380d53c Thomas Thrainer
  def ExpandNames(self):
576 f380d53c Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
577 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
578 f380d53c Thomas Thrainer
579 f380d53c Thomas Thrainer
    self.needed_locks = {
580 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
581 f380d53c Thomas Thrainer
      }
582 f380d53c Thomas Thrainer
583 f380d53c Thomas Thrainer
  def CheckPrereq(self):
584 f380d53c Thomas Thrainer
    """Check prerequisites.
585 f380d53c Thomas Thrainer

586 f380d53c Thomas Thrainer
    Ensures requested new name is not yet used.
587 f380d53c Thomas Thrainer

588 f380d53c Thomas Thrainer
    """
589 f380d53c Thomas Thrainer
    try:
590 f380d53c Thomas Thrainer
      new_name_uuid = self.cfg.LookupNodeGroup(self.op.new_name)
591 f380d53c Thomas Thrainer
    except errors.OpPrereqError:
592 f380d53c Thomas Thrainer
      pass
593 f380d53c Thomas Thrainer
    else:
594 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Desired new name '%s' clashes with existing"
595 f380d53c Thomas Thrainer
                                 " node group (UUID: %s)" %
596 f380d53c Thomas Thrainer
                                 (self.op.new_name, new_name_uuid),
597 f380d53c Thomas Thrainer
                                 errors.ECODE_EXISTS)
598 f380d53c Thomas Thrainer
599 f380d53c Thomas Thrainer
  def BuildHooksEnv(self):
600 f380d53c Thomas Thrainer
    """Build hooks env.
601 f380d53c Thomas Thrainer

602 f380d53c Thomas Thrainer
    """
603 f380d53c Thomas Thrainer
    return {
604 f380d53c Thomas Thrainer
      "OLD_NAME": self.op.group_name,
605 f380d53c Thomas Thrainer
      "NEW_NAME": self.op.new_name,
606 f380d53c Thomas Thrainer
      }
607 f380d53c Thomas Thrainer
608 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
609 f380d53c Thomas Thrainer
    """Build hooks nodes.
610 f380d53c Thomas Thrainer

611 f380d53c Thomas Thrainer
    """
612 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
613 f380d53c Thomas Thrainer
614 f380d53c Thomas Thrainer
    all_nodes = self.cfg.GetAllNodesInfo()
615 f380d53c Thomas Thrainer
    all_nodes.pop(mn, None)
616 f380d53c Thomas Thrainer
617 f380d53c Thomas Thrainer
    run_nodes = [mn]
618 1c3231aa Thomas Thrainer
    run_nodes.extend(node.uuid for node in all_nodes.values()
619 f380d53c Thomas Thrainer
                     if node.group == self.group_uuid)
620 f380d53c Thomas Thrainer
621 f380d53c Thomas Thrainer
    return (run_nodes, run_nodes)
622 f380d53c Thomas Thrainer
623 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
624 f380d53c Thomas Thrainer
    """Rename the node group.
625 f380d53c Thomas Thrainer

626 f380d53c Thomas Thrainer
    """
627 f380d53c Thomas Thrainer
    group = self.cfg.GetNodeGroup(self.group_uuid)
628 f380d53c Thomas Thrainer
629 f380d53c Thomas Thrainer
    if group is None:
630 f380d53c Thomas Thrainer
      raise errors.OpExecError("Could not retrieve group '%s' (UUID: %s)" %
631 f380d53c Thomas Thrainer
                               (self.op.group_name, self.group_uuid))
632 f380d53c Thomas Thrainer
633 f380d53c Thomas Thrainer
    group.name = self.op.new_name
634 f380d53c Thomas Thrainer
    self.cfg.Update(group, feedback_fn)
635 f380d53c Thomas Thrainer
636 f380d53c Thomas Thrainer
    return self.op.new_name
637 f380d53c Thomas Thrainer
638 f380d53c Thomas Thrainer
639 f380d53c Thomas Thrainer
class LUGroupEvacuate(LogicalUnit):
640 f380d53c Thomas Thrainer
  HPATH = "group-evacuate"
641 f380d53c Thomas Thrainer
  HTYPE = constants.HTYPE_GROUP
642 f380d53c Thomas Thrainer
  REQ_BGL = False
643 f380d53c Thomas Thrainer
644 f380d53c Thomas Thrainer
  def ExpandNames(self):
645 f380d53c Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
646 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
647 f380d53c Thomas Thrainer
648 f380d53c Thomas Thrainer
    if self.op.target_groups:
649 f380d53c Thomas Thrainer
      self.req_target_uuids = map(self.cfg.LookupNodeGroup,
650 f380d53c Thomas Thrainer
                                  self.op.target_groups)
651 f380d53c Thomas Thrainer
    else:
652 f380d53c Thomas Thrainer
      self.req_target_uuids = []
653 f380d53c Thomas Thrainer
654 f380d53c Thomas Thrainer
    if self.group_uuid in self.req_target_uuids:
655 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Group to be evacuated (%s) can not be used"
656 f380d53c Thomas Thrainer
                                 " as a target group (targets are %s)" %
657 f380d53c Thomas Thrainer
                                 (self.group_uuid,
658 f380d53c Thomas Thrainer
                                  utils.CommaJoin(self.req_target_uuids)),
659 f380d53c Thomas Thrainer
                                 errors.ECODE_INVAL)
660 f380d53c Thomas Thrainer
661 5eacbcae Thomas Thrainer
    self.op.iallocator = GetDefaultIAllocator(self.cfg, self.op.iallocator)
662 f380d53c Thomas Thrainer
663 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
664 f380d53c Thomas Thrainer
    self.needed_locks = {
665 f380d53c Thomas Thrainer
      locking.LEVEL_INSTANCE: [],
666 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: [],
667 f380d53c Thomas Thrainer
      locking.LEVEL_NODE: [],
668 f380d53c Thomas Thrainer
      }
669 f380d53c Thomas Thrainer
670 f380d53c Thomas Thrainer
  def DeclareLocks(self, level):
671 f380d53c Thomas Thrainer
    if level == locking.LEVEL_INSTANCE:
672 f380d53c Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_INSTANCE]
673 f380d53c Thomas Thrainer
674 f380d53c Thomas Thrainer
      # Lock instances optimistically, needs verification once node and group
675 f380d53c Thomas Thrainer
      # locks have been acquired
676 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_INSTANCE] = \
677 da4a52a3 Thomas Thrainer
        self.cfg.GetInstanceNames(
678 da4a52a3 Thomas Thrainer
          self.cfg.GetNodeGroupInstances(self.group_uuid))
679 f380d53c Thomas Thrainer
680 f380d53c Thomas Thrainer
    elif level == locking.LEVEL_NODEGROUP:
681 f380d53c Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
682 f380d53c Thomas Thrainer
683 f380d53c Thomas Thrainer
      if self.req_target_uuids:
684 f380d53c Thomas Thrainer
        lock_groups = set([self.group_uuid] + self.req_target_uuids)
685 f380d53c Thomas Thrainer
686 f380d53c Thomas Thrainer
        # Lock all groups used by instances optimistically; this requires going
687 f380d53c Thomas Thrainer
        # via the node before it's locked, requiring verification later on
688 f380d53c Thomas Thrainer
        lock_groups.update(group_uuid
689 f380d53c Thomas Thrainer
                           for instance_name in
690 f380d53c Thomas Thrainer
                             self.owned_locks(locking.LEVEL_INSTANCE)
691 f380d53c Thomas Thrainer
                           for group_uuid in
692 da4a52a3 Thomas Thrainer
                             self.cfg.GetInstanceNodeGroups(
693 da4a52a3 Thomas Thrainer
                               self.cfg.GetInstanceInfoByName(instance_name)
694 da4a52a3 Thomas Thrainer
                                 .uuid))
695 f380d53c Thomas Thrainer
      else:
696 f380d53c Thomas Thrainer
        # No target groups, need to lock all of them
697 f380d53c Thomas Thrainer
        lock_groups = locking.ALL_SET
698 f380d53c Thomas Thrainer
699 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = lock_groups
700 f380d53c Thomas Thrainer
701 f380d53c Thomas Thrainer
    elif level == locking.LEVEL_NODE:
702 f380d53c Thomas Thrainer
      # This will only lock the nodes in the group to be evacuated which
703 f380d53c Thomas Thrainer
      # contain actual instances
704 f380d53c Thomas Thrainer
      self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
705 f380d53c Thomas Thrainer
      self._LockInstancesNodes()
706 f380d53c Thomas Thrainer
707 f380d53c Thomas Thrainer
      # Lock all nodes in group to be evacuated and target groups
708 f380d53c Thomas Thrainer
      owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
709 f380d53c Thomas Thrainer
      assert self.group_uuid in owned_groups
710 1c3231aa Thomas Thrainer
      member_node_uuids = [node_uuid
711 1c3231aa Thomas Thrainer
                           for group in owned_groups
712 1c3231aa Thomas Thrainer
                           for node_uuid in
713 1c3231aa Thomas Thrainer
                             self.cfg.GetNodeGroup(group).members]
714 1c3231aa Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE].extend(member_node_uuids)
715 f380d53c Thomas Thrainer
716 f380d53c Thomas Thrainer
  def CheckPrereq(self):
717 da4a52a3 Thomas Thrainer
    owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
718 f380d53c Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
719 1c3231aa Thomas Thrainer
    owned_node_uuids = frozenset(self.owned_locks(locking.LEVEL_NODE))
720 f380d53c Thomas Thrainer
721 f380d53c Thomas Thrainer
    assert owned_groups.issuperset(self.req_target_uuids)
722 f380d53c Thomas Thrainer
    assert self.group_uuid in owned_groups
723 f380d53c Thomas Thrainer
724 f380d53c Thomas Thrainer
    # Check if locked instances are still correct
725 da4a52a3 Thomas Thrainer
    CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instance_names)
726 f380d53c Thomas Thrainer
727 f380d53c Thomas Thrainer
    # Get instance information
728 da4a52a3 Thomas Thrainer
    self.instances = \
729 da4a52a3 Thomas Thrainer
      dict(self.cfg.GetMultiInstanceInfoByName(owned_instance_names))
730 f380d53c Thomas Thrainer
731 f380d53c Thomas Thrainer
    # Check if node groups for locked instances are still correct
732 5eacbcae Thomas Thrainer
    CheckInstancesNodeGroups(self.cfg, self.instances,
733 1c3231aa Thomas Thrainer
                             owned_groups, owned_node_uuids, self.group_uuid)
734 f380d53c Thomas Thrainer
735 f380d53c Thomas Thrainer
    if self.req_target_uuids:
736 f380d53c Thomas Thrainer
      # User requested specific target groups
737 f380d53c Thomas Thrainer
      self.target_uuids = self.req_target_uuids
738 f380d53c Thomas Thrainer
    else:
739 f380d53c Thomas Thrainer
      # All groups except the one to be evacuated are potential targets
740 f380d53c Thomas Thrainer
      self.target_uuids = [group_uuid for group_uuid in owned_groups
741 f380d53c Thomas Thrainer
                           if group_uuid != self.group_uuid]
742 f380d53c Thomas Thrainer
743 f380d53c Thomas Thrainer
      if not self.target_uuids:
744 f380d53c Thomas Thrainer
        raise errors.OpPrereqError("There are no possible target groups",
745 f380d53c Thomas Thrainer
                                   errors.ECODE_INVAL)
746 f380d53c Thomas Thrainer
747 f380d53c Thomas Thrainer
  def BuildHooksEnv(self):
748 f380d53c Thomas Thrainer
    """Build hooks env.
749 f380d53c Thomas Thrainer

750 f380d53c Thomas Thrainer
    """
751 f380d53c Thomas Thrainer
    return {
752 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
753 f380d53c Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
754 f380d53c Thomas Thrainer
      }
755 f380d53c Thomas Thrainer
756 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
757 f380d53c Thomas Thrainer
    """Build hooks nodes.
758 f380d53c Thomas Thrainer

759 f380d53c Thomas Thrainer
    """
760 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
761 f380d53c Thomas Thrainer
762 f380d53c Thomas Thrainer
    assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP)
763 f380d53c Thomas Thrainer
764 f380d53c Thomas Thrainer
    run_nodes = [mn] + self.cfg.GetNodeGroup(self.group_uuid).members
765 f380d53c Thomas Thrainer
766 f380d53c Thomas Thrainer
    return (run_nodes, run_nodes)
767 f380d53c Thomas Thrainer
768 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
769 da4a52a3 Thomas Thrainer
    inst_names = list(self.owned_locks(locking.LEVEL_INSTANCE))
770 f380d53c Thomas Thrainer
771 f380d53c Thomas Thrainer
    assert self.group_uuid not in self.target_uuids
772 f380d53c Thomas Thrainer
773 da4a52a3 Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=inst_names,
774 f380d53c Thomas Thrainer
                                      target_groups=self.target_uuids)
775 f380d53c Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
776 f380d53c Thomas Thrainer
777 f380d53c Thomas Thrainer
    ial.Run(self.op.iallocator)
778 f380d53c Thomas Thrainer
779 f380d53c Thomas Thrainer
    if not ial.success:
780 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Can't compute group evacuation using"
781 f380d53c Thomas Thrainer
                                 " iallocator '%s': %s" %
782 f380d53c Thomas Thrainer
                                 (self.op.iallocator, ial.info),
783 f380d53c Thomas Thrainer
                                 errors.ECODE_NORES)
784 f380d53c Thomas Thrainer
785 5eacbcae Thomas Thrainer
    jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
786 f380d53c Thomas Thrainer
787 f380d53c Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for evacuating node group %s",
788 f380d53c Thomas Thrainer
                 len(jobs), self.op.group_name)
789 f380d53c Thomas Thrainer
790 f380d53c Thomas Thrainer
    return ResultWithJobs(jobs)
791 f380d53c Thomas Thrainer
792 f380d53c Thomas Thrainer
793 f380d53c Thomas Thrainer
class LUGroupVerifyDisks(NoHooksLU):
794 f380d53c Thomas Thrainer
  """Verifies the status of all disks in a node group.
795 f380d53c Thomas Thrainer

796 f380d53c Thomas Thrainer
  """
797 f380d53c Thomas Thrainer
  REQ_BGL = False
798 f380d53c Thomas Thrainer
799 f380d53c Thomas Thrainer
  def ExpandNames(self):
800 f380d53c Thomas Thrainer
    # Raises errors.OpPrereqError on its own if group can't be found
801 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
802 f380d53c Thomas Thrainer
803 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
804 f380d53c Thomas Thrainer
    self.needed_locks = {
805 f380d53c Thomas Thrainer
      locking.LEVEL_INSTANCE: [],
806 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: [],
807 f380d53c Thomas Thrainer
      locking.LEVEL_NODE: [],
808 f380d53c Thomas Thrainer
809 f380d53c Thomas Thrainer
      # This opcode is acquires all node locks in a group. LUClusterVerifyDisks
810 f380d53c Thomas Thrainer
      # starts one instance of this opcode for every group, which means all
811 f380d53c Thomas Thrainer
      # nodes will be locked for a short amount of time, so it's better to
812 f380d53c Thomas Thrainer
      # acquire the node allocation lock as well.
813 f380d53c Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
814 f380d53c Thomas Thrainer
      }
815 f380d53c Thomas Thrainer
816 f380d53c Thomas Thrainer
  def DeclareLocks(self, level):
817 f380d53c Thomas Thrainer
    if level == locking.LEVEL_INSTANCE:
818 f380d53c Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_INSTANCE]
819 f380d53c Thomas Thrainer
820 f380d53c Thomas Thrainer
      # Lock instances optimistically, needs verification once node and group
821 f380d53c Thomas Thrainer
      # locks have been acquired
822 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_INSTANCE] = \
823 da4a52a3 Thomas Thrainer
        self.cfg.GetInstanceNames(
824 da4a52a3 Thomas Thrainer
          self.cfg.GetNodeGroupInstances(self.group_uuid))
825 f380d53c Thomas Thrainer
826 f380d53c Thomas Thrainer
    elif level == locking.LEVEL_NODEGROUP:
827 f380d53c Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
828 f380d53c Thomas Thrainer
829 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = \
830 f380d53c Thomas Thrainer
        set([self.group_uuid] +
831 f380d53c Thomas Thrainer
            # Lock all groups used by instances optimistically; this requires
832 f380d53c Thomas Thrainer
            # going via the node before it's locked, requiring verification
833 f380d53c Thomas Thrainer
            # later on
834 f380d53c Thomas Thrainer
            [group_uuid
835 f380d53c Thomas Thrainer
             for instance_name in self.owned_locks(locking.LEVEL_INSTANCE)
836 da4a52a3 Thomas Thrainer
             for group_uuid in
837 da4a52a3 Thomas Thrainer
               self.cfg.GetInstanceNodeGroups(
838 da4a52a3 Thomas Thrainer
                 self.cfg.GetInstanceInfoByName(instance_name).uuid)])
839 f380d53c Thomas Thrainer
840 f380d53c Thomas Thrainer
    elif level == locking.LEVEL_NODE:
841 f380d53c Thomas Thrainer
      # This will only lock the nodes in the group to be verified which contain
842 f380d53c Thomas Thrainer
      # actual instances
843 f380d53c Thomas Thrainer
      self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
844 f380d53c Thomas Thrainer
      self._LockInstancesNodes()
845 f380d53c Thomas Thrainer
846 f380d53c Thomas Thrainer
      # Lock all nodes in group to be verified
847 f380d53c Thomas Thrainer
      assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP)
848 1c3231aa Thomas Thrainer
      member_node_uuids = self.cfg.GetNodeGroup(self.group_uuid).members
849 1c3231aa Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE].extend(member_node_uuids)
850 f380d53c Thomas Thrainer
851 f380d53c Thomas Thrainer
  def CheckPrereq(self):
852 da4a52a3 Thomas Thrainer
    owned_inst_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
853 f380d53c Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
854 1c3231aa Thomas Thrainer
    owned_node_uuids = frozenset(self.owned_locks(locking.LEVEL_NODE))
855 f380d53c Thomas Thrainer
856 f380d53c Thomas Thrainer
    assert self.group_uuid in owned_groups
857 f380d53c Thomas Thrainer
858 f380d53c Thomas Thrainer
    # Check if locked instances are still correct
859 da4a52a3 Thomas Thrainer
    CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_inst_names)
860 f380d53c Thomas Thrainer
861 f380d53c Thomas Thrainer
    # Get instance information
862 da4a52a3 Thomas Thrainer
    self.instances = dict(self.cfg.GetMultiInstanceInfoByName(owned_inst_names))
863 f380d53c Thomas Thrainer
864 f380d53c Thomas Thrainer
    # Check if node groups for locked instances are still correct
865 5eacbcae Thomas Thrainer
    CheckInstancesNodeGroups(self.cfg, self.instances,
866 1c3231aa Thomas Thrainer
                             owned_groups, owned_node_uuids, self.group_uuid)
867 f380d53c Thomas Thrainer
868 235a6b29 Thomas Thrainer
  def _VerifyInstanceLvs(self, node_errors, offline_disk_instance_names,
869 235a6b29 Thomas Thrainer
                         missing_disks):
870 843094ad Thomas Thrainer
    node_lv_to_inst = MapInstanceLvsToNodes(
871 1d4a4b26 Thomas Thrainer
      [inst for inst in self.instances.values() if inst.disks_active])
872 843094ad Thomas Thrainer
    if node_lv_to_inst:
873 1c3231aa Thomas Thrainer
      node_uuids = utils.NiceSort(set(self.owned_locks(locking.LEVEL_NODE)) &
874 1c3231aa Thomas Thrainer
                                  set(self.cfg.GetVmCapableNodeList()))
875 f380d53c Thomas Thrainer
876 1c3231aa Thomas Thrainer
      node_lvs = self.rpc.call_lv_list(node_uuids, [])
877 f380d53c Thomas Thrainer
878 1c3231aa Thomas Thrainer
      for (node_uuid, node_res) in node_lvs.items():
879 f380d53c Thomas Thrainer
        if node_res.offline:
880 f380d53c Thomas Thrainer
          continue
881 f380d53c Thomas Thrainer
882 f380d53c Thomas Thrainer
        msg = node_res.fail_msg
883 f380d53c Thomas Thrainer
        if msg:
884 1c3231aa Thomas Thrainer
          logging.warning("Error enumerating LVs on node %s: %s",
885 1c3231aa Thomas Thrainer
                          self.cfg.GetNodeName(node_uuid), msg)
886 843094ad Thomas Thrainer
          node_errors[node_uuid] = msg
887 f380d53c Thomas Thrainer
          continue
888 f380d53c Thomas Thrainer
889 f380d53c Thomas Thrainer
        for lv_name, (_, _, lv_online) in node_res.payload.items():
890 843094ad Thomas Thrainer
          inst = node_lv_to_inst.pop((node_uuid, lv_name), None)
891 843094ad Thomas Thrainer
          if not lv_online and inst is not None:
892 235a6b29 Thomas Thrainer
            offline_disk_instance_names.add(inst.name)
893 f380d53c Thomas Thrainer
894 f380d53c Thomas Thrainer
      # any leftover items in nv_dict are missing LVs, let's arrange the data
895 f380d53c Thomas Thrainer
      # better
896 843094ad Thomas Thrainer
      for key, inst in node_lv_to_inst.iteritems():
897 235a6b29 Thomas Thrainer
        missing_disks.setdefault(inst.name, []).append(list(key))
898 235a6b29 Thomas Thrainer
899 235a6b29 Thomas Thrainer
  def _VerifyDrbdStates(self, node_errors, offline_disk_instance_names):
900 235a6b29 Thomas Thrainer
    node_to_inst = {}
901 235a6b29 Thomas Thrainer
    for inst in self.instances.values():
902 235a6b29 Thomas Thrainer
      if not inst.disks_active or inst.disk_template != constants.DT_DRBD8:
903 235a6b29 Thomas Thrainer
        continue
904 235a6b29 Thomas Thrainer
905 235a6b29 Thomas Thrainer
      for node_uuid in itertools.chain([inst.primary_node],
906 235a6b29 Thomas Thrainer
                                       inst.secondary_nodes):
907 235a6b29 Thomas Thrainer
        node_to_inst.setdefault(node_uuid, []).append(inst)
908 235a6b29 Thomas Thrainer
909 235a6b29 Thomas Thrainer
    for (node_uuid, insts) in node_to_inst.items():
910 235a6b29 Thomas Thrainer
      node_disks = [(inst.disks, inst) for inst in insts]
911 0c3d9c7c Thomas Thrainer
      node_res = self.rpc.call_drbd_needs_activation(node_uuid, node_disks)
912 235a6b29 Thomas Thrainer
      msg = node_res.fail_msg
913 235a6b29 Thomas Thrainer
      if msg:
914 235a6b29 Thomas Thrainer
        logging.warning("Error getting DRBD status on node %s: %s",
915 235a6b29 Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
916 235a6b29 Thomas Thrainer
        node_errors[node_uuid] = msg
917 235a6b29 Thomas Thrainer
        continue
918 235a6b29 Thomas Thrainer
919 235a6b29 Thomas Thrainer
      faulty_disk_uuids = set(node_res.payload)
920 235a6b29 Thomas Thrainer
      for inst in self.instances.values():
921 235a6b29 Thomas Thrainer
        inst_disk_uuids = set([disk.uuid for disk in inst.disks])
922 235a6b29 Thomas Thrainer
        if inst_disk_uuids.intersection(faulty_disk_uuids):
923 235a6b29 Thomas Thrainer
          offline_disk_instance_names.add(inst.name)
924 235a6b29 Thomas Thrainer
925 235a6b29 Thomas Thrainer
  def Exec(self, feedback_fn):
926 235a6b29 Thomas Thrainer
    """Verify integrity of cluster disks.
927 235a6b29 Thomas Thrainer

928 235a6b29 Thomas Thrainer
    @rtype: tuple of three items
929 235a6b29 Thomas Thrainer
    @return: a tuple of (dict of node-to-node_error, list of instances
930 235a6b29 Thomas Thrainer
        which need activate-disks, dict of instance: (node, volume) for
931 235a6b29 Thomas Thrainer
        missing volumes
932 235a6b29 Thomas Thrainer

933 235a6b29 Thomas Thrainer
    """
934 235a6b29 Thomas Thrainer
    node_errors = {}
935 235a6b29 Thomas Thrainer
    offline_disk_instance_names = set()
936 235a6b29 Thomas Thrainer
    missing_disks = {}
937 235a6b29 Thomas Thrainer
938 235a6b29 Thomas Thrainer
    self._VerifyInstanceLvs(node_errors, offline_disk_instance_names,
939 235a6b29 Thomas Thrainer
                            missing_disks)
940 235a6b29 Thomas Thrainer
    self._VerifyDrbdStates(node_errors, offline_disk_instance_names)
941 f380d53c Thomas Thrainer
942 235a6b29 Thomas Thrainer
    return (node_errors, list(offline_disk_instance_names), missing_disks)