Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / group.py @ 1c4910f7

History | View | Annotate | Download (32.6 kB)

1 f380d53c Thomas Thrainer
#
2 f380d53c Thomas Thrainer
#
3 f380d53c Thomas Thrainer
4 39e27230 Jose A. Lopes
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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 4b75f8a4 Jose A. Lopes
  CheckDiskAccessModeConsistency, ConnectInstanceCommunicationNetworkOp
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 39e27230 Jose A. Lopes
  @staticmethod
138 39e27230 Jose A. Lopes
  def _ConnectInstanceCommunicationNetwork(cfg, group_uuid, network_name):
139 39e27230 Jose A. Lopes
    """Connect a node group to the instance communication network.
140 39e27230 Jose A. Lopes

141 39e27230 Jose A. Lopes
    The group is connected to the instance communication network via
142 39e27230 Jose A. Lopes
    the Opcode 'OpNetworkConnect'.
143 39e27230 Jose A. Lopes

144 39e27230 Jose A. Lopes
    @type cfg: L{ganeti.config.ConfigWriter}
145 39e27230 Jose A. Lopes
    @param cfg: Ganeti configuration
146 39e27230 Jose A. Lopes

147 39e27230 Jose A. Lopes
    @type group_uuid: string
148 39e27230 Jose A. Lopes
    @param group_uuid: UUID of the group to connect
149 39e27230 Jose A. Lopes

150 39e27230 Jose A. Lopes
    @type network_name: string
151 39e27230 Jose A. Lopes
    @param network_name: name of the network to connect to
152 39e27230 Jose A. Lopes

153 39e27230 Jose A. Lopes
    @rtype: L{ganeti.cmdlib.ResultWithJobs} or L{None}
154 39e27230 Jose A. Lopes
    @return: L{ganeti.cmdlib.ResultWithJobs} if the group needs to be
155 39e27230 Jose A. Lopes
             connected, otherwise (the group is already connected)
156 39e27230 Jose A. Lopes
             L{None}
157 39e27230 Jose A. Lopes

158 39e27230 Jose A. Lopes
    """
159 39e27230 Jose A. Lopes
    try:
160 39e27230 Jose A. Lopes
      cfg.LookupNetwork(network_name)
161 39e27230 Jose A. Lopes
      network_exists = True
162 39e27230 Jose A. Lopes
    except errors.OpPrereqError:
163 39e27230 Jose A. Lopes
      network_exists = False
164 39e27230 Jose A. Lopes
165 39e27230 Jose A. Lopes
    if network_exists:
166 4b75f8a4 Jose A. Lopes
      op = ConnectInstanceCommunicationNetworkOp(group_uuid, network_name)
167 2ff6426b Jose A. Lopes
      return ResultWithJobs([[op]])
168 39e27230 Jose A. Lopes
    else:
169 39e27230 Jose A. Lopes
      return None
170 39e27230 Jose A. Lopes
171 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
172 f380d53c Thomas Thrainer
    """Add the node group to the cluster.
173 f380d53c Thomas Thrainer

174 f380d53c Thomas Thrainer
    """
175 f380d53c Thomas Thrainer
    group_obj = objects.NodeGroup(name=self.op.group_name, members=[],
176 f380d53c Thomas Thrainer
                                  uuid=self.group_uuid,
177 f380d53c Thomas Thrainer
                                  alloc_policy=self.op.alloc_policy,
178 f380d53c Thomas Thrainer
                                  ndparams=self.op.ndparams,
179 f380d53c Thomas Thrainer
                                  diskparams=self.new_diskparams,
180 f380d53c Thomas Thrainer
                                  ipolicy=self.op.ipolicy,
181 f380d53c Thomas Thrainer
                                  hv_state_static=self.new_hv_state,
182 f380d53c Thomas Thrainer
                                  disk_state_static=self.new_disk_state)
183 f380d53c Thomas Thrainer
184 f380d53c Thomas Thrainer
    self.cfg.AddNodeGroup(group_obj, self.proc.GetECId(), check_uuid=False)
185 f380d53c Thomas Thrainer
    del self.remove_locks[locking.LEVEL_NODEGROUP]
186 f380d53c Thomas Thrainer
187 39e27230 Jose A. Lopes
    network_name = self.cfg.GetClusterInfo().instance_communication_network
188 39e27230 Jose A. Lopes
    if network_name:
189 39e27230 Jose A. Lopes
      return self._ConnectInstanceCommunicationNetwork(self.cfg,
190 39e27230 Jose A. Lopes
                                                       self.group_uuid,
191 39e27230 Jose A. Lopes
                                                       network_name)
192 39e27230 Jose A. Lopes
193 f380d53c Thomas Thrainer
194 f380d53c Thomas Thrainer
class LUGroupAssignNodes(NoHooksLU):
195 f380d53c Thomas Thrainer
  """Logical unit for assigning nodes to groups.
196 f380d53c Thomas Thrainer

197 f380d53c Thomas Thrainer
  """
198 f380d53c Thomas Thrainer
  REQ_BGL = False
199 f380d53c Thomas Thrainer
200 f380d53c Thomas Thrainer
  def ExpandNames(self):
201 f380d53c Thomas Thrainer
    # These raise errors.OpPrereqError on their own:
202 f380d53c Thomas Thrainer
    self.group_uuid = self.cfg.LookupNodeGroup(self.op.group_name)
203 1c3231aa Thomas Thrainer
    (self.op.node_uuids, self.op.nodes) = GetWantedNodes(self, self.op.nodes)
204 f380d53c Thomas Thrainer
205 f380d53c Thomas Thrainer
    # We want to lock all the affected nodes and groups. We have readily
206 f380d53c Thomas Thrainer
    # available the list of nodes, and the *destination* group. To gather the
207 f380d53c Thomas Thrainer
    # list of "source" groups, we need to fetch node information later on.
208 f380d53c Thomas Thrainer
    self.needed_locks = {
209 f380d53c Thomas Thrainer
      locking.LEVEL_NODEGROUP: set([self.group_uuid]),
210 1c3231aa Thomas Thrainer
      locking.LEVEL_NODE: self.op.node_uuids,
211 f380d53c Thomas Thrainer
      }
212 f380d53c Thomas Thrainer
213 f380d53c Thomas Thrainer
  def DeclareLocks(self, level):
214 f380d53c Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
215 f380d53c Thomas Thrainer
      assert len(self.needed_locks[locking.LEVEL_NODEGROUP]) == 1
216 f380d53c Thomas Thrainer
217 f380d53c Thomas Thrainer
      # Try to get all affected nodes' groups without having the group or node
218 f380d53c Thomas Thrainer
      # lock yet. Needs verification later in the code flow.
219 1c3231aa Thomas Thrainer
      groups = self.cfg.GetNodeGroupsFromNodes(self.op.node_uuids)
220 f380d53c Thomas Thrainer
221 f380d53c Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP].update(groups)
222 f380d53c Thomas Thrainer
223 f380d53c Thomas Thrainer
  def CheckPrereq(self):
224 f380d53c Thomas Thrainer
    """Check prerequisites.
225 f380d53c Thomas Thrainer

226 f380d53c Thomas Thrainer
    """
227 f380d53c Thomas Thrainer
    assert self.needed_locks[locking.LEVEL_NODEGROUP]
228 f380d53c Thomas Thrainer
    assert (frozenset(self.owned_locks(locking.LEVEL_NODE)) ==
229 1c3231aa Thomas Thrainer
            frozenset(self.op.node_uuids))
230 f380d53c Thomas Thrainer
231 f380d53c Thomas Thrainer
    expected_locks = (set([self.group_uuid]) |
232 1c3231aa Thomas Thrainer
                      self.cfg.GetNodeGroupsFromNodes(self.op.node_uuids))
233 f380d53c Thomas Thrainer
    actual_locks = self.owned_locks(locking.LEVEL_NODEGROUP)
234 f380d53c Thomas Thrainer
    if actual_locks != expected_locks:
235 f380d53c Thomas Thrainer
      raise errors.OpExecError("Nodes changed groups since locks were acquired,"
236 f380d53c Thomas Thrainer
                               " current groups are '%s', used to be '%s'" %
237 f380d53c Thomas Thrainer
                               (utils.CommaJoin(expected_locks),
238 f380d53c Thomas Thrainer
                                utils.CommaJoin(actual_locks)))
239 f380d53c Thomas Thrainer
240 f380d53c Thomas Thrainer
    self.node_data = self.cfg.GetAllNodesInfo()
241 f380d53c Thomas Thrainer
    self.group = self.cfg.GetNodeGroup(self.group_uuid)
242 f380d53c Thomas Thrainer
    instance_data = self.cfg.GetAllInstancesInfo()
243 f380d53c Thomas Thrainer
244 f380d53c Thomas Thrainer
    if self.group is None:
245 f380d53c Thomas Thrainer
      raise errors.OpExecError("Could not retrieve group '%s' (UUID: %s)" %
246 f380d53c Thomas Thrainer
                               (self.op.group_name, self.group_uuid))
247 f380d53c Thomas Thrainer
248 f380d53c Thomas Thrainer
    (new_splits, previous_splits) = \
249 1c3231aa Thomas Thrainer
      self.CheckAssignmentForSplitInstances([(uuid, self.group_uuid)
250 1c3231aa Thomas Thrainer
                                             for uuid in self.op.node_uuids],
251 f380d53c Thomas Thrainer
                                            self.node_data, instance_data)
252 f380d53c Thomas Thrainer
253 f380d53c Thomas Thrainer
    if new_splits:
254 da4a52a3 Thomas Thrainer
      fmt_new_splits = utils.CommaJoin(utils.NiceSort(
255 da4a52a3 Thomas Thrainer
                         self.cfg.GetInstanceNames(new_splits)))
256 f380d53c Thomas Thrainer
257 f380d53c Thomas Thrainer
      if not self.op.force:
258 f380d53c Thomas Thrainer
        raise errors.OpExecError("The following instances get split by this"
259 f380d53c Thomas Thrainer
                                 " change and --force was not given: %s" %
260 f380d53c Thomas Thrainer
                                 fmt_new_splits)
261 f380d53c Thomas Thrainer
      else:
262 f380d53c Thomas Thrainer
        self.LogWarning("This operation will split the following instances: %s",
263 f380d53c Thomas Thrainer
                        fmt_new_splits)
264 f380d53c Thomas Thrainer
265 f380d53c Thomas Thrainer
        if previous_splits:
266 f380d53c Thomas Thrainer
          self.LogWarning("In addition, these already-split instances continue"
267 f380d53c Thomas Thrainer
                          " to be split across groups: %s",
268 da4a52a3 Thomas Thrainer
                          utils.CommaJoin(utils.NiceSort(
269 da4a52a3 Thomas Thrainer
                            self.cfg.GetInstanceNames(previous_splits))))
270 f380d53c Thomas Thrainer
271 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
272 f380d53c Thomas Thrainer
    """Assign nodes to a new group.
273 f380d53c Thomas Thrainer

274 f380d53c Thomas Thrainer
    """
275 1c3231aa Thomas Thrainer
    mods = [(node_uuid, self.group_uuid) for node_uuid in self.op.node_uuids]
276 f380d53c Thomas Thrainer
277 f380d53c Thomas Thrainer
    self.cfg.AssignGroupNodes(mods)
278 f380d53c Thomas Thrainer
279 f380d53c Thomas Thrainer
  @staticmethod
280 f380d53c Thomas Thrainer
  def CheckAssignmentForSplitInstances(changes, node_data, instance_data):
281 f380d53c Thomas Thrainer
    """Check for split instances after a node assignment.
282 f380d53c Thomas Thrainer

283 f380d53c Thomas Thrainer
    This method considers a series of node assignments as an atomic operation,
284 f380d53c Thomas Thrainer
    and returns information about split instances after applying the set of
285 f380d53c Thomas Thrainer
    changes.
286 f380d53c Thomas Thrainer

287 f380d53c Thomas Thrainer
    In particular, it returns information about newly split instances, and
288 f380d53c Thomas Thrainer
    instances that were already split, and remain so after the change.
289 f380d53c Thomas Thrainer

290 f380d53c Thomas Thrainer
    Only instances whose disk template is listed in constants.DTS_INT_MIRROR are
291 f380d53c Thomas Thrainer
    considered.
292 f380d53c Thomas Thrainer

293 1c3231aa Thomas Thrainer
    @type changes: list of (node_uuid, new_group_uuid) pairs.
294 f380d53c Thomas Thrainer
    @param changes: list of node assignments to consider.
295 f380d53c Thomas Thrainer
    @param node_data: a dict with data for all nodes
296 f380d53c Thomas Thrainer
    @param instance_data: a dict with all instances to consider
297 f380d53c Thomas Thrainer
    @rtype: a two-tuple
298 f380d53c Thomas Thrainer
    @return: a list of instances that were previously okay and result split as a
299 f380d53c Thomas Thrainer
      consequence of this change, and a list of instances that were previously
300 f380d53c Thomas Thrainer
      split and this change does not fix.
301 f380d53c Thomas Thrainer

302 f380d53c Thomas Thrainer
    """
303 1c3231aa Thomas Thrainer
    changed_nodes = dict((uuid, group) for uuid, group in changes
304 1c3231aa Thomas Thrainer
                         if node_data[uuid].group != group)
305 f380d53c Thomas Thrainer
306 f380d53c Thomas Thrainer
    all_split_instances = set()
307 f380d53c Thomas Thrainer
    previously_split_instances = set()
308 f380d53c Thomas Thrainer
309 f380d53c Thomas Thrainer
    for inst in instance_data.values():
310 f380d53c Thomas Thrainer
      if inst.disk_template not in constants.DTS_INT_MIRROR:
311 f380d53c Thomas Thrainer
        continue
312 f380d53c Thomas Thrainer
313 1c3231aa Thomas Thrainer
      if len(set(node_data[node_uuid].group
314 1c3231aa Thomas Thrainer
                 for node_uuid in inst.all_nodes)) > 1:
315 da4a52a3 Thomas Thrainer
        previously_split_instances.add(inst.uuid)
316 f380d53c Thomas Thrainer
317 1c3231aa Thomas Thrainer
      if len(set(changed_nodes.get(node_uuid, node_data[node_uuid].group)
318 1c3231aa Thomas Thrainer
                 for node_uuid in inst.all_nodes)) > 1:
319 da4a52a3 Thomas Thrainer
        all_split_instances.add(inst.uuid)
320 f380d53c Thomas Thrainer
321 f380d53c Thomas Thrainer
    return (list(all_split_instances - previously_split_instances),
322 f380d53c Thomas Thrainer
            list(previously_split_instances & all_split_instances))
323 f380d53c Thomas Thrainer
324 f380d53c Thomas Thrainer
325 f380d53c Thomas Thrainer
class LUGroupSetParams(LogicalUnit):
326 f380d53c Thomas Thrainer
  """Modifies the parameters of a node group.
327 f380d53c Thomas Thrainer

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

375 f380d53c Thomas Thrainer
    """
376 5eacbcae Thomas Thrainer
    new_params = GetUpdatedParams(old, new)
377 f380d53c Thomas Thrainer
    utils.ForceDictType(new_params, constants.DISK_DT_TYPES)
378 f380d53c Thomas Thrainer
    return new_params
379 f380d53c Thomas Thrainer
380 702243ec Helga Velroyen
  def _CheckIpolicy(self, cluster, owned_instance_names):
381 702243ec Helga Velroyen
    """Sanity checks for the ipolicy.
382 702243ec Helga Velroyen

383 702243ec Helga Velroyen
    @type cluster: C{objects.Cluster}
384 702243ec Helga Velroyen
    @param cluster: the cluster's configuration
385 702243ec Helga Velroyen
    @type owned_instance_names: list of string
386 702243ec Helga Velroyen
    @param owned_instance_names: list of instances
387 702243ec Helga Velroyen

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

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

467 f380d53c Thomas Thrainer
    """
468 f380d53c Thomas Thrainer
    return {
469 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
470 f380d53c Thomas Thrainer
      "NEW_ALLOC_POLICY": self.op.alloc_policy,
471 f380d53c Thomas Thrainer
      }
472 f380d53c Thomas Thrainer
473 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
474 f380d53c Thomas Thrainer
    """Build hooks nodes.
475 f380d53c Thomas Thrainer

476 f380d53c Thomas Thrainer
    """
477 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
478 f380d53c Thomas Thrainer
    return ([mn], [mn])
479 f380d53c Thomas Thrainer
480 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
481 f380d53c Thomas Thrainer
    """Modifies the node group.
482 f380d53c Thomas Thrainer

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

525 f380d53c Thomas Thrainer
    This checks that the given group name exists as a node group, that is
526 f380d53c Thomas Thrainer
    empty (i.e., contains no nodes), and that is not the last group of the
527 f380d53c Thomas Thrainer
    cluster.
528 f380d53c Thomas Thrainer

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

551 f380d53c Thomas Thrainer
    """
552 f380d53c Thomas Thrainer
    return {
553 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
554 f380d53c Thomas Thrainer
      }
555 f380d53c Thomas Thrainer
556 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
557 f380d53c Thomas Thrainer
    """Build hooks nodes.
558 f380d53c Thomas Thrainer

559 f380d53c Thomas Thrainer
    """
560 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
561 f380d53c Thomas Thrainer
    return ([mn], [mn])
562 f380d53c Thomas Thrainer
563 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
564 f380d53c Thomas Thrainer
    """Remove the node group.
565 f380d53c Thomas Thrainer

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

592 f380d53c Thomas Thrainer
    Ensures requested new name is not yet used.
593 f380d53c Thomas Thrainer

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

608 f380d53c Thomas Thrainer
    """
609 f380d53c Thomas Thrainer
    return {
610 f380d53c Thomas Thrainer
      "OLD_NAME": self.op.group_name,
611 f380d53c Thomas Thrainer
      "NEW_NAME": self.op.new_name,
612 f380d53c Thomas Thrainer
      }
613 f380d53c Thomas Thrainer
614 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
615 f380d53c Thomas Thrainer
    """Build hooks nodes.
616 f380d53c Thomas Thrainer

617 f380d53c Thomas Thrainer
    """
618 f380d53c Thomas Thrainer
    mn = self.cfg.GetMasterNode()
619 f380d53c Thomas Thrainer
620 f380d53c Thomas Thrainer
    all_nodes = self.cfg.GetAllNodesInfo()
621 f380d53c Thomas Thrainer
    all_nodes.pop(mn, None)
622 f380d53c Thomas Thrainer
623 f380d53c Thomas Thrainer
    run_nodes = [mn]
624 1c3231aa Thomas Thrainer
    run_nodes.extend(node.uuid for node in all_nodes.values()
625 f380d53c Thomas Thrainer
                     if node.group == self.group_uuid)
626 f380d53c Thomas Thrainer
627 f380d53c Thomas Thrainer
    return (run_nodes, run_nodes)
628 f380d53c Thomas Thrainer
629 f380d53c Thomas Thrainer
  def Exec(self, feedback_fn):
630 f380d53c Thomas Thrainer
    """Rename the node group.
631 f380d53c Thomas Thrainer

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

756 f380d53c Thomas Thrainer
    """
757 f380d53c Thomas Thrainer
    return {
758 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
759 f380d53c Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
760 f380d53c Thomas Thrainer
      }
761 f380d53c Thomas Thrainer
762 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
763 f380d53c Thomas Thrainer
    """Build hooks nodes.
764 f380d53c Thomas Thrainer

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

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

934 235a6b29 Thomas Thrainer
    @rtype: tuple of three items
935 235a6b29 Thomas Thrainer
    @return: a tuple of (dict of node-to-node_error, list of instances
936 235a6b29 Thomas Thrainer
        which need activate-disks, dict of instance: (node, volume) for
937 235a6b29 Thomas Thrainer
        missing volumes
938 235a6b29 Thomas Thrainer

939 235a6b29 Thomas Thrainer
    """
940 235a6b29 Thomas Thrainer
    node_errors = {}
941 235a6b29 Thomas Thrainer
    offline_disk_instance_names = set()
942 235a6b29 Thomas Thrainer
    missing_disks = {}
943 235a6b29 Thomas Thrainer
944 235a6b29 Thomas Thrainer
    self._VerifyInstanceLvs(node_errors, offline_disk_instance_names,
945 235a6b29 Thomas Thrainer
                            missing_disks)
946 235a6b29 Thomas Thrainer
    self._VerifyDrbdStates(node_errors, offline_disk_instance_names)
947 f380d53c Thomas Thrainer
948 235a6b29 Thomas Thrainer
    return (node_errors, list(offline_disk_instance_names), missing_disks)