Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / group.py @ 5cbf7832

History | View | Annotate | Download (33.7 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 qlang
32 f380d53c Thomas Thrainer
from ganeti import query
33 f380d53c Thomas Thrainer
from ganeti import utils
34 f380d53c Thomas Thrainer
from ganeti.masterd import iallocator
35 5eacbcae Thomas Thrainer
from ganeti.cmdlib.base import LogicalUnit, NoHooksLU, QueryBase, \
36 f380d53c Thomas Thrainer
  ResultWithJobs
37 5eacbcae Thomas Thrainer
from ganeti.cmdlib.common import MergeAndVerifyHvState, \
38 5eacbcae Thomas Thrainer
  MergeAndVerifyDiskState, GetWantedNodes, GetUpdatedParams, \
39 5eacbcae Thomas Thrainer
  CheckNodeGroupInstances, GetUpdatedIPolicy, \
40 5eacbcae Thomas Thrainer
  ComputeNewInstanceViolations, GetDefaultIAllocator, ShareAll, \
41 843094ad Thomas Thrainer
  CheckInstancesNodeGroups, LoadNodeEvacResult, MapInstanceLvsToNodes
42 f380d53c Thomas Thrainer
43 f380d53c Thomas Thrainer
import ganeti.masterd.instance
44 f380d53c Thomas Thrainer
45 f380d53c Thomas Thrainer
46 f380d53c Thomas Thrainer
class LUGroupAdd(LogicalUnit):
47 f380d53c Thomas Thrainer
  """Logical unit for creating node groups.
48 f380d53c Thomas Thrainer

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

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

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

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

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

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

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

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

227 f380d53c Thomas Thrainer
    """
228 1c3231aa Thomas Thrainer
    mods = [(node_uuid, self.group_uuid) for node_uuid in self.op.node_uuids]
229 f380d53c Thomas Thrainer
230 f380d53c Thomas Thrainer
    self.cfg.AssignGroupNodes(mods)
231 f380d53c Thomas Thrainer
232 f380d53c Thomas Thrainer
  @staticmethod
233 f380d53c Thomas Thrainer
  def CheckAssignmentForSplitInstances(changes, node_data, instance_data):
234 f380d53c Thomas Thrainer
    """Check for split instances after a node assignment.
235 f380d53c Thomas Thrainer

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

240 f380d53c Thomas Thrainer
    In particular, it returns information about newly split instances, and
241 f380d53c Thomas Thrainer
    instances that were already split, and remain so after the change.
242 f380d53c Thomas Thrainer

243 f380d53c Thomas Thrainer
    Only instances whose disk template is listed in constants.DTS_INT_MIRROR are
244 f380d53c Thomas Thrainer
    considered.
245 f380d53c Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

788 f380d53c Thomas Thrainer
    """
789 f380d53c Thomas Thrainer
    return {
790 f380d53c Thomas Thrainer
      "GROUP_NAME": self.op.group_name,
791 f380d53c Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
792 f380d53c Thomas Thrainer
      }
793 f380d53c Thomas Thrainer
794 f380d53c Thomas Thrainer
  def BuildHooksNodes(self):
795 f380d53c Thomas Thrainer
    """Build hooks nodes.
796 f380d53c Thomas Thrainer

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

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

969 235a6b29 Thomas Thrainer
    @rtype: tuple of three items
970 235a6b29 Thomas Thrainer
    @return: a tuple of (dict of node-to-node_error, list of instances
971 235a6b29 Thomas Thrainer
        which need activate-disks, dict of instance: (node, volume) for
972 235a6b29 Thomas Thrainer
        missing volumes
973 235a6b29 Thomas Thrainer

974 235a6b29 Thomas Thrainer
    """
975 235a6b29 Thomas Thrainer
    node_errors = {}
976 235a6b29 Thomas Thrainer
    offline_disk_instance_names = set()
977 235a6b29 Thomas Thrainer
    missing_disks = {}
978 235a6b29 Thomas Thrainer
979 235a6b29 Thomas Thrainer
    self._VerifyInstanceLvs(node_errors, offline_disk_instance_names,
980 235a6b29 Thomas Thrainer
                            missing_disks)
981 235a6b29 Thomas Thrainer
    self._VerifyDrbdStates(node_errors, offline_disk_instance_names)
982 f380d53c Thomas Thrainer
983 235a6b29 Thomas Thrainer
    return (node_errors, list(offline_disk_instance_names), missing_disks)