Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / group.py @ 31d3b918

History | View | Annotate | Download (31.3 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 utils
32 f380d53c Thomas Thrainer
from ganeti.masterd import iallocator
33 081dc516 Helga Velroyen
from ganeti.cmdlib.base import LogicalUnit, NoHooksLU, ResultWithJobs
34 5eacbcae Thomas Thrainer
from ganeti.cmdlib.common import MergeAndVerifyHvState, \
35 5eacbcae Thomas Thrainer
  MergeAndVerifyDiskState, GetWantedNodes, GetUpdatedParams, \
36 5eacbcae Thomas Thrainer
  CheckNodeGroupInstances, GetUpdatedIPolicy, \
37 5eacbcae Thomas Thrainer
  ComputeNewInstanceViolations, GetDefaultIAllocator, ShareAll, \
38 702243ec Helga Velroyen
  CheckInstancesNodeGroups, LoadNodeEvacResult, MapInstanceLvsToNodes, \
39 294254b1 Raffa Santi
  CheckIpolicyVsDiskTemplates, CheckDiskAccessModeValidity, \
40 294254b1 Raffa Santi
  CheckDiskAccessModeConsistency
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 702243ec Helga Velroyen
  def _CheckIpolicy(self):
62 702243ec Helga Velroyen
    """Checks the group's ipolicy for consistency and validity.
63 702243ec Helga Velroyen

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

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

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

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

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

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

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

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

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

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

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

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

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

262 f380d53c Thomas Thrainer
    """
263 1c3231aa Thomas Thrainer
    changed_nodes = dict((uuid, group) for uuid, group in changes
264 1c3231aa Thomas Thrainer
                         if node_data[uuid].group != group)
265 f380d53c Thomas Thrainer
266 f380d53c Thomas Thrainer
    all_split_instances = set()
267 f380d53c Thomas Thrainer
    previously_split_instances = set()
268 f380d53c Thomas Thrainer
269 f380d53c Thomas Thrainer
    for inst in instance_data.values():
270 f380d53c Thomas Thrainer
      if inst.disk_template not in constants.DTS_INT_MIRROR:
271 f380d53c Thomas Thrainer
        continue
272 f380d53c Thomas Thrainer
273 1c3231aa Thomas Thrainer
      if len(set(node_data[node_uuid].group
274 1c3231aa Thomas Thrainer
                 for node_uuid in inst.all_nodes)) > 1:
275 da4a52a3 Thomas Thrainer
        previously_split_instances.add(inst.uuid)
276 f380d53c Thomas Thrainer
277 1c3231aa Thomas Thrainer
      if len(set(changed_nodes.get(node_uuid, node_data[node_uuid].group)
278 1c3231aa Thomas Thrainer
                 for node_uuid in inst.all_nodes)) > 1:
279 da4a52a3 Thomas Thrainer
        all_split_instances.add(inst.uuid)
280 f380d53c Thomas Thrainer
281 f380d53c Thomas Thrainer
    return (list(all_split_instances - previously_split_instances),
282 f380d53c Thomas Thrainer
            list(previously_split_instances & all_split_instances))
283 f380d53c Thomas Thrainer
284 f380d53c Thomas Thrainer
285 f380d53c Thomas Thrainer
class LUGroupSetParams(LogicalUnit):
286 f380d53c Thomas Thrainer
  """Modifies the parameters of a node group.
287 f380d53c Thomas Thrainer

288 f380d53c Thomas Thrainer
  """
289 f380d53c Thomas Thrainer
  HPATH = "group-modify"
290 f380d53c Thomas Thrainer
  HTYPE = constants.HTYPE_GROUP
291 f380d53c Thomas Thrainer
  REQ_BGL = False
292 f380d53c Thomas Thrainer
293 f380d53c Thomas Thrainer
  def CheckArguments(self):
294 f380d53c Thomas Thrainer
    all_changes = [
295 f380d53c Thomas Thrainer
      self.op.ndparams,
296 f380d53c Thomas Thrainer
      self.op.diskparams,
297 f380d53c Thomas Thrainer
      self.op.alloc_policy,
298 f380d53c Thomas Thrainer
      self.op.hv_state,
299 f380d53c Thomas Thrainer
      self.op.disk_state,
300 f380d53c Thomas Thrainer
      self.op.ipolicy,
301 f380d53c Thomas Thrainer
      ]
302 f380d53c Thomas Thrainer
303 f380d53c Thomas Thrainer
    if all_changes.count(None) == len(all_changes):
304 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Please pass at least one modification",
305 f380d53c Thomas Thrainer
                                 errors.ECODE_INVAL)
306 f380d53c Thomas Thrainer
307 294254b1 Raffa Santi
    if self.op.diskparams:
308 294254b1 Raffa Santi
      CheckDiskAccessModeValidity(self.op.diskparams)
309 294254b1 Raffa Santi
310 f380d53c Thomas Thrainer
  def ExpandNames(self):
311 f380d53c Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
312 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
313 f380d53c Thomas Thrainer
314 f380d53c Thomas Thrainer
    self.needed_locks = {
315 f380d53c Thomas Thrainer
      locking.LEVEL_INSTANCE: [],
316 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
317 f380d53c Thomas Thrainer
      }
318 f380d53c Thomas Thrainer
319 f380d53c Thomas Thrainer
    self.share_locks[locking.LEVEL_INSTANCE] = 1
320 f380d53c Thomas Thrainer
321 f380d53c Thomas Thrainer
  def DeclareLocks(self, level):
322 f380d53c Thomas Thrainer
    if level == locking.LEVEL_INSTANCE:
323 f380d53c Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_INSTANCE]
324 f380d53c Thomas Thrainer
325 f380d53c Thomas Thrainer
      # Lock instances optimistically, needs verification once group lock has
326 f380d53c Thomas Thrainer
      # been acquired
327 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_INSTANCE] = \
328 da4a52a3 Thomas Thrainer
        self.cfg.GetInstanceNames(
329 da4a52a3 Thomas Thrainer
          self.cfg.GetNodeGroupInstances(self.group_uuid))
330 f380d53c Thomas Thrainer
331 f380d53c Thomas Thrainer
  @staticmethod
332 f380d53c Thomas Thrainer
  def _UpdateAndVerifyDiskParams(old, new):
333 f380d53c Thomas Thrainer
    """Updates and verifies disk parameters.
334 f380d53c Thomas Thrainer

335 f380d53c Thomas Thrainer
    """
336 5eacbcae Thomas Thrainer
    new_params = GetUpdatedParams(old, new)
337 f380d53c Thomas Thrainer
    utils.ForceDictType(new_params, constants.DISK_DT_TYPES)
338 f380d53c Thomas Thrainer
    return new_params
339 f380d53c Thomas Thrainer
340 702243ec Helga Velroyen
  def _CheckIpolicy(self, cluster, owned_instance_names):
341 702243ec Helga Velroyen
    """Sanity checks for the ipolicy.
342 702243ec Helga Velroyen

343 702243ec Helga Velroyen
    @type cluster: C{objects.Cluster}
344 702243ec Helga Velroyen
    @param cluster: the cluster's configuration
345 702243ec Helga Velroyen
    @type owned_instance_names: list of string
346 702243ec Helga Velroyen
    @param owned_instance_names: list of instances
347 702243ec Helga Velroyen

348 702243ec Helga Velroyen
    """
349 702243ec Helga Velroyen
    if self.op.ipolicy:
350 702243ec Helga Velroyen
      self.new_ipolicy = GetUpdatedIPolicy(self.group.ipolicy,
351 702243ec Helga Velroyen
                                           self.op.ipolicy,
352 702243ec Helga Velroyen
                                           group_policy=True)
353 702243ec Helga Velroyen
354 702243ec Helga Velroyen
      new_ipolicy = cluster.SimpleFillIPolicy(self.new_ipolicy)
355 702243ec Helga Velroyen
      CheckIpolicyVsDiskTemplates(new_ipolicy,
356 702243ec Helga Velroyen
                                  cluster.enabled_disk_templates)
357 bbe0f264 Thomas Thrainer
      instances = \
358 bbe0f264 Thomas Thrainer
        dict(self.cfg.GetMultiInstanceInfoByName(owned_instance_names))
359 702243ec Helga Velroyen
      gmi = ganeti.masterd.instance
360 702243ec Helga Velroyen
      violations = \
361 702243ec Helga Velroyen
          ComputeNewInstanceViolations(gmi.CalculateGroupIPolicy(cluster,
362 702243ec Helga Velroyen
                                                                 self.group),
363 bbe0f264 Thomas Thrainer
                                       new_ipolicy, instances.values(),
364 bbe0f264 Thomas Thrainer
                                       self.cfg)
365 702243ec Helga Velroyen
366 702243ec Helga Velroyen
      if violations:
367 702243ec Helga Velroyen
        self.LogWarning("After the ipolicy change the following instances"
368 702243ec Helga Velroyen
                        " violate them: %s",
369 702243ec Helga Velroyen
                        utils.CommaJoin(violations))
370 702243ec Helga Velroyen
371 f380d53c Thomas Thrainer
  def CheckPrereq(self):
372 f380d53c Thomas Thrainer
    """Check prerequisites.
373 f380d53c Thomas Thrainer

374 f380d53c Thomas Thrainer
    """
375 da4a52a3 Thomas Thrainer
    owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
376 f380d53c Thomas Thrainer
377 f380d53c Thomas Thrainer
    # Check if locked instances are still correct
378 da4a52a3 Thomas Thrainer
    CheckNodeGroupInstances(self.cfg, self.group_uuid, owned_instance_names)
379 f380d53c Thomas Thrainer
380 f380d53c Thomas Thrainer
    self.group = self.cfg.GetNodeGroup(self.group_uuid)
381 f380d53c Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
382 f380d53c Thomas Thrainer
383 f380d53c Thomas Thrainer
    if self.group is None:
384 f380d53c Thomas Thrainer
      raise errors.OpExecError("Could not retrieve group '%s' (UUID: %s)" %
385 f380d53c Thomas Thrainer
                               (self.op.group_name, self.group_uuid))
386 f380d53c Thomas Thrainer
387 f380d53c Thomas Thrainer
    if self.op.ndparams:
388 5eacbcae Thomas Thrainer
      new_ndparams = GetUpdatedParams(self.group.ndparams, self.op.ndparams)
389 f380d53c Thomas Thrainer
      utils.ForceDictType(new_ndparams, constants.NDS_PARAMETER_TYPES)
390 f380d53c Thomas Thrainer
      self.new_ndparams = new_ndparams
391 f380d53c Thomas Thrainer
392 f380d53c Thomas Thrainer
    if self.op.diskparams:
393 f380d53c Thomas Thrainer
      diskparams = self.group.diskparams
394 f380d53c Thomas Thrainer
      uavdp = self._UpdateAndVerifyDiskParams
395 f380d53c Thomas Thrainer
      # For each disktemplate subdict update and verify the values
396 f380d53c Thomas Thrainer
      new_diskparams = dict((dt,
397 f380d53c Thomas Thrainer
                             uavdp(diskparams.get(dt, {}),
398 f380d53c Thomas Thrainer
                                   self.op.diskparams[dt]))
399 f380d53c Thomas Thrainer
                            for dt in constants.DISK_TEMPLATES
400 f380d53c Thomas Thrainer
                            if dt in self.op.diskparams)
401 f380d53c Thomas Thrainer
      # As we've all subdicts of diskparams ready, lets merge the actual
402 f380d53c Thomas Thrainer
      # dict with all updated subdicts
403 f380d53c Thomas Thrainer
      self.new_diskparams = objects.FillDict(diskparams, new_diskparams)
404 294254b1 Raffa Santi
405 f380d53c Thomas Thrainer
      try:
406 f380d53c Thomas Thrainer
        utils.VerifyDictOptions(self.new_diskparams, constants.DISK_DT_DEFAULTS)
407 294254b1 Raffa Santi
        CheckDiskAccessModeConsistency(self.new_diskparams, self.cfg,
408 294254b1 Raffa Santi
                                       group=self.group)
409 f380d53c Thomas Thrainer
      except errors.OpPrereqError, err:
410 f380d53c Thomas Thrainer
        raise errors.OpPrereqError("While verify diskparams options: %s" % err,
411 f380d53c Thomas Thrainer
                                   errors.ECODE_INVAL)
412 f380d53c Thomas Thrainer
413 f380d53c Thomas Thrainer
    if self.op.hv_state:
414 5eacbcae Thomas Thrainer
      self.new_hv_state = MergeAndVerifyHvState(self.op.hv_state,
415 5eacbcae Thomas Thrainer
                                                self.group.hv_state_static)
416 f380d53c Thomas Thrainer
417 f380d53c Thomas Thrainer
    if self.op.disk_state:
418 f380d53c Thomas Thrainer
      self.new_disk_state = \
419 5eacbcae Thomas Thrainer
        MergeAndVerifyDiskState(self.op.disk_state,
420 5eacbcae Thomas Thrainer
                                self.group.disk_state_static)
421 f380d53c Thomas Thrainer
422 702243ec Helga Velroyen
    self._CheckIpolicy(cluster, owned_instance_names)
423 f380d53c Thomas Thrainer
424 f380d53c Thomas Thrainer
  def BuildHooksEnv(self):
425 f380d53c Thomas Thrainer
    """Build hooks env.
426 f380d53c Thomas Thrainer

427 f380d53c Thomas Thrainer
    """
428 f380d53c Thomas Thrainer
    return {
429 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
430 f380d53c Thomas Thrainer
      "NEW_ALLOC_POLICY": self.op.alloc_policy,
431 f380d53c Thomas Thrainer
      }
432 f380d53c Thomas Thrainer
433 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
434 f380d53c Thomas Thrainer
    """Build hooks nodes.
435 f380d53c Thomas Thrainer

436 f380d53c Thomas Thrainer
    """
437 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
438 f380d53c Thomas Thrainer
    return ([mn], [mn])
439 f380d53c Thomas Thrainer
440 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
441 f380d53c Thomas Thrainer
    """Modifies the node group.
442 f380d53c Thomas Thrainer

443 f380d53c Thomas Thrainer
    """
444 f380d53c Thomas Thrainer
    result = []
445 f380d53c Thomas Thrainer
446 f380d53c Thomas Thrainer
    if self.op.ndparams:
447 f380d53c Thomas Thrainer
      self.group.ndparams = self.new_ndparams
448 f380d53c Thomas Thrainer
      result.append(("ndparams", str(self.group.ndparams)))
449 f380d53c Thomas Thrainer
450 f380d53c Thomas Thrainer
    if self.op.diskparams:
451 f380d53c Thomas Thrainer
      self.group.diskparams = self.new_diskparams
452 f380d53c Thomas Thrainer
      result.append(("diskparams", str(self.group.diskparams)))
453 f380d53c Thomas Thrainer
454 f380d53c Thomas Thrainer
    if self.op.alloc_policy:
455 f380d53c Thomas Thrainer
      self.group.alloc_policy = self.op.alloc_policy
456 f380d53c Thomas Thrainer
457 f380d53c Thomas Thrainer
    if self.op.hv_state:
458 f380d53c Thomas Thrainer
      self.group.hv_state_static = self.new_hv_state
459 f380d53c Thomas Thrainer
460 f380d53c Thomas Thrainer
    if self.op.disk_state:
461 f380d53c Thomas Thrainer
      self.group.disk_state_static = self.new_disk_state
462 f380d53c Thomas Thrainer
463 f380d53c Thomas Thrainer
    if self.op.ipolicy:
464 f380d53c Thomas Thrainer
      self.group.ipolicy = self.new_ipolicy
465 f380d53c Thomas Thrainer
466 f380d53c Thomas Thrainer
    self.cfg.Update(self.group, feedback_fn)
467 f380d53c Thomas Thrainer
    return result
468 f380d53c Thomas Thrainer
469 f380d53c Thomas Thrainer
470 f380d53c Thomas Thrainer
class LUGroupRemove(LogicalUnit):
471 f380d53c Thomas Thrainer
  HPATH = "group-remove"
472 f380d53c Thomas Thrainer
  HTYPE = constants.HTYPE_GROUP
473 f380d53c Thomas Thrainer
  REQ_BGL = False
474 f380d53c Thomas Thrainer
475 f380d53c Thomas Thrainer
  def ExpandNames(self):
476 f380d53c Thomas Thrainer
    # This will raises errors.OpPrereqError on its own:
477 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
478 f380d53c Thomas Thrainer
    self.needed_locks = {
479 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
480 f380d53c Thomas Thrainer
      }
481 f380d53c Thomas Thrainer
482 f380d53c Thomas Thrainer
  def CheckPrereq(self):
483 f380d53c Thomas Thrainer
    """Check prerequisites.
484 f380d53c Thomas Thrainer

485 f380d53c Thomas Thrainer
    This checks that the given group name exists as a node group, that is
486 f380d53c Thomas Thrainer
    empty (i.e., contains no nodes), and that is not the last group of the
487 f380d53c Thomas Thrainer
    cluster.
488 f380d53c Thomas Thrainer

489 f380d53c Thomas Thrainer
    """
490 f380d53c Thomas Thrainer
    # Verify that the group is empty.
491 1c3231aa Thomas Thrainer
    group_nodes = [node.uuid
492 f380d53c Thomas Thrainer
                   for node in self.cfg.GetAllNodesInfo().values()
493 f380d53c Thomas Thrainer
                   if node.group == self.group_uuid]
494 f380d53c Thomas Thrainer
495 f380d53c Thomas Thrainer
    if group_nodes:
496 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Group '%s' not empty, has the following"
497 f380d53c Thomas Thrainer
                                 " nodes: %s" %
498 f380d53c Thomas Thrainer
                                 (self.op.group_name,
499 f380d53c Thomas Thrainer
                                  utils.CommaJoin(utils.NiceSort(group_nodes))),
500 f380d53c Thomas Thrainer
                                 errors.ECODE_STATE)
501 f380d53c Thomas Thrainer
502 f380d53c Thomas Thrainer
    # Verify the cluster would not be left group-less.
503 f380d53c Thomas Thrainer
    if len(self.cfg.GetNodeGroupList()) == 1:
504 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Group '%s' is the only group, cannot be"
505 f380d53c Thomas Thrainer
                                 " removed" % self.op.group_name,
506 f380d53c Thomas Thrainer
                                 errors.ECODE_STATE)
507 f380d53c Thomas Thrainer
508 f380d53c Thomas Thrainer
  def BuildHooksEnv(self):
509 f380d53c Thomas Thrainer
    """Build hooks env.
510 f380d53c Thomas Thrainer

511 f380d53c Thomas Thrainer
    """
512 f380d53c Thomas Thrainer
    return {
513 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
514 f380d53c Thomas Thrainer
      }
515 f380d53c Thomas Thrainer
516 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
517 f380d53c Thomas Thrainer
    """Build hooks nodes.
518 f380d53c Thomas Thrainer

519 f380d53c Thomas Thrainer
    """
520 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
521 f380d53c Thomas Thrainer
    return ([mn], [mn])
522 f380d53c Thomas Thrainer
523 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
524 f380d53c Thomas Thrainer
    """Remove the node group.
525 f380d53c Thomas Thrainer

526 f380d53c Thomas Thrainer
    """
527 f380d53c Thomas Thrainer
    try:
528 f380d53c Thomas Thrainer
      self.cfg.RemoveNodeGroup(self.group_uuid)
529 f380d53c Thomas Thrainer
    except errors.ConfigurationError:
530 f380d53c Thomas Thrainer
      raise errors.OpExecError("Group '%s' with UUID %s disappeared" %
531 f380d53c Thomas Thrainer
                               (self.op.group_name, self.group_uuid))
532 f380d53c Thomas Thrainer
533 f380d53c Thomas Thrainer
    self.remove_locks[locking.LEVEL_NODEGROUP] = self.group_uuid
534 f380d53c Thomas Thrainer
535 f380d53c Thomas Thrainer
536 f380d53c Thomas Thrainer
class LUGroupRename(LogicalUnit):
537 f380d53c Thomas Thrainer
  HPATH = "group-rename"
538 f380d53c Thomas Thrainer
  HTYPE = constants.HTYPE_GROUP
539 f380d53c Thomas Thrainer
  REQ_BGL = False
540 f380d53c Thomas Thrainer
541 f380d53c Thomas Thrainer
  def ExpandNames(self):
542 f380d53c Thomas Thrainer
    # This raises errors.OpPrereqError on its own:
543 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
544 f380d53c Thomas Thrainer
545 f380d53c Thomas Thrainer
    self.needed_locks = {
546 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: [self.group_uuid],
547 f380d53c Thomas Thrainer
      }
548 f380d53c Thomas Thrainer
549 f380d53c Thomas Thrainer
  def CheckPrereq(self):
550 f380d53c Thomas Thrainer
    """Check prerequisites.
551 f380d53c Thomas Thrainer

552 f380d53c Thomas Thrainer
    Ensures requested new name is not yet used.
553 f380d53c Thomas Thrainer

554 f380d53c Thomas Thrainer
    """
555 f380d53c Thomas Thrainer
    try:
556 f380d53c Thomas Thrainer
      new_name_uuid = self.cfg.LookupNodeGroup(self.op.new_name)
557 f380d53c Thomas Thrainer
    except errors.OpPrereqError:
558 f380d53c Thomas Thrainer
      pass
559 f380d53c Thomas Thrainer
    else:
560 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Desired new name '%s' clashes with existing"
561 f380d53c Thomas Thrainer
                                 " node group (UUID: %s)" %
562 f380d53c Thomas Thrainer
                                 (self.op.new_name, new_name_uuid),
563 f380d53c Thomas Thrainer
                                 errors.ECODE_EXISTS)
564 f380d53c Thomas Thrainer
565 f380d53c Thomas Thrainer
  def BuildHooksEnv(self):
566 f380d53c Thomas Thrainer
    """Build hooks env.
567 f380d53c Thomas Thrainer

568 f380d53c Thomas Thrainer
    """
569 f380d53c Thomas Thrainer
    return {
570 f380d53c Thomas Thrainer
      "OLD_NAME": self.op.group_name,
571 f380d53c Thomas Thrainer
      "NEW_NAME": self.op.new_name,
572 f380d53c Thomas Thrainer
      }
573 f380d53c Thomas Thrainer
574 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
575 f380d53c Thomas Thrainer
    """Build hooks nodes.
576 f380d53c Thomas Thrainer

577 f380d53c Thomas Thrainer
    """
578 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
579 f380d53c Thomas Thrainer
580 f380d53c Thomas Thrainer
    all_nodes = self.cfg.GetAllNodesInfo()
581 f380d53c Thomas Thrainer
    all_nodes.pop(mn, None)
582 f380d53c Thomas Thrainer
583 f380d53c Thomas Thrainer
    run_nodes = [mn]
584 1c3231aa Thomas Thrainer
    run_nodes.extend(node.uuid for node in all_nodes.values()
585 f380d53c Thomas Thrainer
                     if node.group == self.group_uuid)
586 f380d53c Thomas Thrainer
587 f380d53c Thomas Thrainer
    return (run_nodes, run_nodes)
588 f380d53c Thomas Thrainer
589 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
590 f380d53c Thomas Thrainer
    """Rename the node group.
591 f380d53c Thomas Thrainer

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

716 f380d53c Thomas Thrainer
    """
717 f380d53c Thomas Thrainer
    return {
718 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
719 f380d53c Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
720 f380d53c Thomas Thrainer
      }
721 f380d53c Thomas Thrainer
722 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
723 f380d53c Thomas Thrainer
    """Build hooks nodes.
724 f380d53c Thomas Thrainer

725 f380d53c Thomas Thrainer
    """
726 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
727 f380d53c Thomas Thrainer
728 f380d53c Thomas Thrainer
    assert self.group_uuid in self.owned_locks(locking.LEVEL_NODEGROUP)
729 f380d53c Thomas Thrainer
730 f380d53c Thomas Thrainer
    run_nodes = [mn] + self.cfg.GetNodeGroup(self.group_uuid).members
731 f380d53c Thomas Thrainer
732 f380d53c Thomas Thrainer
    return (run_nodes, run_nodes)
733 f380d53c Thomas Thrainer
734 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
735 da4a52a3 Thomas Thrainer
    inst_names = list(self.owned_locks(locking.LEVEL_INSTANCE))
736 f380d53c Thomas Thrainer
737 f380d53c Thomas Thrainer
    assert self.group_uuid not in self.target_uuids
738 f380d53c Thomas Thrainer
739 da4a52a3 Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=inst_names,
740 f380d53c Thomas Thrainer
                                      target_groups=self.target_uuids)
741 f380d53c Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
742 f380d53c Thomas Thrainer
743 f380d53c Thomas Thrainer
    ial.Run(self.op.iallocator)
744 f380d53c Thomas Thrainer
745 f380d53c Thomas Thrainer
    if not ial.success:
746 f380d53c Thomas Thrainer
      raise errors.OpPrereqError("Can't compute group evacuation using"
747 f380d53c Thomas Thrainer
                                 " iallocator '%s': %s" %
748 f380d53c Thomas Thrainer
                                 (self.op.iallocator, ial.info),
749 f380d53c Thomas Thrainer
                                 errors.ECODE_NORES)
750 f380d53c Thomas Thrainer
751 5eacbcae Thomas Thrainer
    jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
752 f380d53c Thomas Thrainer
753 f380d53c Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for evacuating node group %s",
754 f380d53c Thomas Thrainer
                 len(jobs), self.op.group_name)
755 f380d53c Thomas Thrainer
756 f380d53c Thomas Thrainer
    return ResultWithJobs(jobs)
757 f380d53c Thomas Thrainer
758 f380d53c Thomas Thrainer
759 f380d53c Thomas Thrainer
class LUGroupVerifyDisks(NoHooksLU):
760 f380d53c Thomas Thrainer
  """Verifies the status of all disks in a node group.
761 f380d53c Thomas Thrainer

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

894 235a6b29 Thomas Thrainer
    @rtype: tuple of three items
895 235a6b29 Thomas Thrainer
    @return: a tuple of (dict of node-to-node_error, list of instances
896 235a6b29 Thomas Thrainer
        which need activate-disks, dict of instance: (node, volume) for
897 235a6b29 Thomas Thrainer
        missing volumes
898 235a6b29 Thomas Thrainer

899 235a6b29 Thomas Thrainer
    """
900 235a6b29 Thomas Thrainer
    node_errors = {}
901 235a6b29 Thomas Thrainer
    offline_disk_instance_names = set()
902 235a6b29 Thomas Thrainer
    missing_disks = {}
903 235a6b29 Thomas Thrainer
904 235a6b29 Thomas Thrainer
    self._VerifyInstanceLvs(node_errors, offline_disk_instance_names,
905 235a6b29 Thomas Thrainer
                            missing_disks)
906 235a6b29 Thomas Thrainer
    self._VerifyDrbdStates(node_errors, offline_disk_instance_names)
907 f380d53c Thomas Thrainer
908 235a6b29 Thomas Thrainer
    return (node_errors, list(offline_disk_instance_names), missing_disks)