Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / node.py @ 4869595d

History | View | Annotate | Download (56.9 kB)

1 31b836b8 Thomas Thrainer
#
2 31b836b8 Thomas Thrainer
#
3 31b836b8 Thomas Thrainer
4 31b836b8 Thomas Thrainer
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5 31b836b8 Thomas Thrainer
#
6 31b836b8 Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 31b836b8 Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 31b836b8 Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 31b836b8 Thomas Thrainer
# (at your option) any later version.
10 31b836b8 Thomas Thrainer
#
11 31b836b8 Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 31b836b8 Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 31b836b8 Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 31b836b8 Thomas Thrainer
# General Public License for more details.
15 31b836b8 Thomas Thrainer
#
16 31b836b8 Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 31b836b8 Thomas Thrainer
# along with this program; if not, write to the Free Software
18 31b836b8 Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 31b836b8 Thomas Thrainer
# 02110-1301, USA.
20 31b836b8 Thomas Thrainer
21 31b836b8 Thomas Thrainer
22 31b836b8 Thomas Thrainer
"""Logical units dealing with nodes."""
23 31b836b8 Thomas Thrainer
24 31b836b8 Thomas Thrainer
import logging
25 31b836b8 Thomas Thrainer
import operator
26 31b836b8 Thomas Thrainer
27 31b836b8 Thomas Thrainer
from ganeti import constants
28 31b836b8 Thomas Thrainer
from ganeti import errors
29 31b836b8 Thomas Thrainer
from ganeti import locking
30 31b836b8 Thomas Thrainer
from ganeti import netutils
31 31b836b8 Thomas Thrainer
from ganeti import objects
32 31b836b8 Thomas Thrainer
from ganeti import opcodes
33 4869595d Petr Pudlak
import ganeti.rpc.node as rpc
34 31b836b8 Thomas Thrainer
from ganeti import utils
35 31b836b8 Thomas Thrainer
from ganeti.masterd import iallocator
36 31b836b8 Thomas Thrainer
37 dde771ba Helga Velroyen
from ganeti.cmdlib.base import LogicalUnit, NoHooksLU, ResultWithJobs
38 5eacbcae Thomas Thrainer
from ganeti.cmdlib.common import CheckParamsNotGlobal, \
39 5eacbcae Thomas Thrainer
  MergeAndVerifyHvState, MergeAndVerifyDiskState, \
40 5eacbcae Thomas Thrainer
  IsExclusiveStorageEnabledNode, CheckNodePVs, \
41 1c3231aa Thomas Thrainer
  RedistributeAncillaryFiles, ExpandNodeUuidAndName, ShareAll, SupportsOob, \
42 5eacbcae Thomas Thrainer
  CheckInstanceState, INSTANCE_DOWN, GetUpdatedParams, \
43 5eacbcae Thomas Thrainer
  AdjustCandidatePool, CheckIAllocatorOrNode, LoadNodeEvacResult, \
44 843094ad Thomas Thrainer
  GetWantedNodes, MapInstanceLvsToNodes, RunPostHook, \
45 9d276e93 Helga Velroyen
  FindFaultyInstanceDisks, CheckStorageTypeEnabled
46 31b836b8 Thomas Thrainer
47 31b836b8 Thomas Thrainer
48 31b836b8 Thomas Thrainer
def _DecideSelfPromotion(lu, exceptions=None):
49 31b836b8 Thomas Thrainer
  """Decide whether I should promote myself as a master candidate.
50 31b836b8 Thomas Thrainer

51 31b836b8 Thomas Thrainer
  """
52 31b836b8 Thomas Thrainer
  cp_size = lu.cfg.GetClusterInfo().candidate_pool_size
53 31b836b8 Thomas Thrainer
  mc_now, mc_should, _ = lu.cfg.GetMasterCandidateStats(exceptions)
54 31b836b8 Thomas Thrainer
  # the new node will increase mc_max with one, so:
55 31b836b8 Thomas Thrainer
  mc_should = min(mc_should + 1, cp_size)
56 31b836b8 Thomas Thrainer
  return mc_now < mc_should
57 31b836b8 Thomas Thrainer
58 31b836b8 Thomas Thrainer
59 31b836b8 Thomas Thrainer
def _CheckNodeHasSecondaryIP(lu, node, secondary_ip, prereq):
60 31b836b8 Thomas Thrainer
  """Ensure that a node has the given secondary ip.
61 31b836b8 Thomas Thrainer

62 31b836b8 Thomas Thrainer
  @type lu: L{LogicalUnit}
63 31b836b8 Thomas Thrainer
  @param lu: the LU on behalf of which we make the check
64 1c3231aa Thomas Thrainer
  @type node: L{objects.Node}
65 31b836b8 Thomas Thrainer
  @param node: the node to check
66 31b836b8 Thomas Thrainer
  @type secondary_ip: string
67 31b836b8 Thomas Thrainer
  @param secondary_ip: the ip to check
68 31b836b8 Thomas Thrainer
  @type prereq: boolean
69 31b836b8 Thomas Thrainer
  @param prereq: whether to throw a prerequisite or an execute error
70 1c3231aa Thomas Thrainer
  @raise errors.OpPrereqError: if the node doesn't have the ip,
71 1c3231aa Thomas Thrainer
  and prereq=True
72 31b836b8 Thomas Thrainer
  @raise errors.OpExecError: if the node doesn't have the ip, and prereq=False
73 31b836b8 Thomas Thrainer

74 31b836b8 Thomas Thrainer
  """
75 1c3231aa Thomas Thrainer
  # this can be called with a new node, which has no UUID yet, so perform the
76 1c3231aa Thomas Thrainer
  # RPC call using its name
77 1c3231aa Thomas Thrainer
  result = lu.rpc.call_node_has_ip_address(node.name, secondary_ip)
78 1c3231aa Thomas Thrainer
  result.Raise("Failure checking secondary ip on node %s" % node.name,
79 31b836b8 Thomas Thrainer
               prereq=prereq, ecode=errors.ECODE_ENVIRON)
80 31b836b8 Thomas Thrainer
  if not result.payload:
81 31b836b8 Thomas Thrainer
    msg = ("Node claims it doesn't have the secondary ip you gave (%s),"
82 31b836b8 Thomas Thrainer
           " please fix and re-run this command" % secondary_ip)
83 31b836b8 Thomas Thrainer
    if prereq:
84 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError(msg, errors.ECODE_ENVIRON)
85 31b836b8 Thomas Thrainer
    else:
86 31b836b8 Thomas Thrainer
      raise errors.OpExecError(msg)
87 31b836b8 Thomas Thrainer
88 31b836b8 Thomas Thrainer
89 31b836b8 Thomas Thrainer
class LUNodeAdd(LogicalUnit):
90 31b836b8 Thomas Thrainer
  """Logical unit for adding node to the cluster.
91 31b836b8 Thomas Thrainer

92 31b836b8 Thomas Thrainer
  """
93 31b836b8 Thomas Thrainer
  HPATH = "node-add"
94 31b836b8 Thomas Thrainer
  HTYPE = constants.HTYPE_NODE
95 31b836b8 Thomas Thrainer
  _NFLAGS = ["master_capable", "vm_capable"]
96 31b836b8 Thomas Thrainer
97 31b836b8 Thomas Thrainer
  def CheckArguments(self):
98 31b836b8 Thomas Thrainer
    self.primary_ip_family = self.cfg.GetPrimaryIPFamily()
99 31b836b8 Thomas Thrainer
    # validate/normalize the node name
100 31b836b8 Thomas Thrainer
    self.hostname = netutils.GetHostname(name=self.op.node_name,
101 31b836b8 Thomas Thrainer
                                         family=self.primary_ip_family)
102 31b836b8 Thomas Thrainer
    self.op.node_name = self.hostname.name
103 31b836b8 Thomas Thrainer
104 1c3231aa Thomas Thrainer
    if self.op.readd and self.op.node_name == self.cfg.GetMasterNodeName():
105 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot readd the master node",
106 31b836b8 Thomas Thrainer
                                 errors.ECODE_STATE)
107 31b836b8 Thomas Thrainer
108 31b836b8 Thomas Thrainer
    if self.op.readd and self.op.group:
109 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot pass a node group when a node is"
110 31b836b8 Thomas Thrainer
                                 " being readded", errors.ECODE_INVAL)
111 31b836b8 Thomas Thrainer
112 31b836b8 Thomas Thrainer
  def BuildHooksEnv(self):
113 31b836b8 Thomas Thrainer
    """Build hooks env.
114 31b836b8 Thomas Thrainer

115 31b836b8 Thomas Thrainer
    This will run on all nodes before, and on all nodes + the new node after.
116 31b836b8 Thomas Thrainer

117 31b836b8 Thomas Thrainer
    """
118 31b836b8 Thomas Thrainer
    return {
119 31b836b8 Thomas Thrainer
      "OP_TARGET": self.op.node_name,
120 31b836b8 Thomas Thrainer
      "NODE_NAME": self.op.node_name,
121 31b836b8 Thomas Thrainer
      "NODE_PIP": self.op.primary_ip,
122 31b836b8 Thomas Thrainer
      "NODE_SIP": self.op.secondary_ip,
123 31b836b8 Thomas Thrainer
      "MASTER_CAPABLE": str(self.op.master_capable),
124 31b836b8 Thomas Thrainer
      "VM_CAPABLE": str(self.op.vm_capable),
125 31b836b8 Thomas Thrainer
      }
126 31b836b8 Thomas Thrainer
127 31b836b8 Thomas Thrainer
  def BuildHooksNodes(self):
128 31b836b8 Thomas Thrainer
    """Build hooks nodes.
129 31b836b8 Thomas Thrainer

130 31b836b8 Thomas Thrainer
    """
131 1c3231aa Thomas Thrainer
    hook_nodes = self.cfg.GetNodeList()
132 1c3231aa Thomas Thrainer
    new_node_info = self.cfg.GetNodeInfoByName(self.op.node_name)
133 1c3231aa Thomas Thrainer
    if new_node_info is not None:
134 1c3231aa Thomas Thrainer
      # Exclude added node
135 1c3231aa Thomas Thrainer
      hook_nodes = list(set(hook_nodes) - set([new_node_info.uuid]))
136 31b836b8 Thomas Thrainer
137 1c3231aa Thomas Thrainer
    # add the new node as post hook node by name; it does not have an UUID yet
138 1c3231aa Thomas Thrainer
    return (hook_nodes, hook_nodes, [self.op.node_name, ])
139 31b836b8 Thomas Thrainer
140 31b836b8 Thomas Thrainer
  def CheckPrereq(self):
141 31b836b8 Thomas Thrainer
    """Check prerequisites.
142 31b836b8 Thomas Thrainer

143 31b836b8 Thomas Thrainer
    This checks:
144 31b836b8 Thomas Thrainer
     - the new node is not already in the config
145 31b836b8 Thomas Thrainer
     - it is resolvable
146 31b836b8 Thomas Thrainer
     - its parameters (single/dual homed) matches the cluster
147 31b836b8 Thomas Thrainer

148 31b836b8 Thomas Thrainer
    Any errors are signaled by raising errors.OpPrereqError.
149 31b836b8 Thomas Thrainer

150 31b836b8 Thomas Thrainer
    """
151 d0d7d7cf Thomas Thrainer
    node_name = self.hostname.name
152 d0d7d7cf Thomas Thrainer
    self.op.primary_ip = self.hostname.ip
153 31b836b8 Thomas Thrainer
    if self.op.secondary_ip is None:
154 31b836b8 Thomas Thrainer
      if self.primary_ip_family == netutils.IP6Address.family:
155 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("When using a IPv6 primary address, a valid"
156 31b836b8 Thomas Thrainer
                                   " IPv4 address must be given as secondary",
157 31b836b8 Thomas Thrainer
                                   errors.ECODE_INVAL)
158 d0d7d7cf Thomas Thrainer
      self.op.secondary_ip = self.op.primary_ip
159 31b836b8 Thomas Thrainer
160 31b836b8 Thomas Thrainer
    secondary_ip = self.op.secondary_ip
161 31b836b8 Thomas Thrainer
    if not netutils.IP4Address.IsValid(secondary_ip):
162 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Secondary IP (%s) needs to be a valid IPv4"
163 31b836b8 Thomas Thrainer
                                 " address" % secondary_ip, errors.ECODE_INVAL)
164 31b836b8 Thomas Thrainer
165 d0d7d7cf Thomas Thrainer
    existing_node_info = self.cfg.GetNodeInfoByName(node_name)
166 1c3231aa Thomas Thrainer
    if not self.op.readd and existing_node_info is not None:
167 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Node %s is already in the configuration" %
168 1c3231aa Thomas Thrainer
                                 node_name, errors.ECODE_EXISTS)
169 1c3231aa Thomas Thrainer
    elif self.op.readd and existing_node_info is None:
170 1c3231aa Thomas Thrainer
      raise errors.OpPrereqError("Node %s is not in the configuration" %
171 1c3231aa Thomas Thrainer
                                 node_name, errors.ECODE_NOENT)
172 31b836b8 Thomas Thrainer
173 31b836b8 Thomas Thrainer
    self.changed_primary_ip = False
174 31b836b8 Thomas Thrainer
175 d0d7d7cf Thomas Thrainer
    for existing_node in self.cfg.GetAllNodesInfo().values():
176 1c3231aa Thomas Thrainer
      if self.op.readd and node_name == existing_node.name:
177 31b836b8 Thomas Thrainer
        if existing_node.secondary_ip != secondary_ip:
178 31b836b8 Thomas Thrainer
          raise errors.OpPrereqError("Readded node doesn't have the same IP"
179 31b836b8 Thomas Thrainer
                                     " address configuration as before",
180 31b836b8 Thomas Thrainer
                                     errors.ECODE_INVAL)
181 d0d7d7cf Thomas Thrainer
        if existing_node.primary_ip != self.op.primary_ip:
182 31b836b8 Thomas Thrainer
          self.changed_primary_ip = True
183 31b836b8 Thomas Thrainer
184 31b836b8 Thomas Thrainer
        continue
185 31b836b8 Thomas Thrainer
186 d0d7d7cf Thomas Thrainer
      if (existing_node.primary_ip == self.op.primary_ip or
187 d0d7d7cf Thomas Thrainer
          existing_node.secondary_ip == self.op.primary_ip or
188 31b836b8 Thomas Thrainer
          existing_node.primary_ip == secondary_ip or
189 31b836b8 Thomas Thrainer
          existing_node.secondary_ip == secondary_ip):
190 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("New node ip address(es) conflict with"
191 31b836b8 Thomas Thrainer
                                   " existing node %s" % existing_node.name,
192 31b836b8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
193 31b836b8 Thomas Thrainer
194 31b836b8 Thomas Thrainer
    # After this 'if' block, None is no longer a valid value for the
195 31b836b8 Thomas Thrainer
    # _capable op attributes
196 31b836b8 Thomas Thrainer
    if self.op.readd:
197 1c3231aa Thomas Thrainer
      assert existing_node_info is not None, \
198 1c3231aa Thomas Thrainer
        "Can't retrieve locked node %s" % node_name
199 31b836b8 Thomas Thrainer
      for attr in self._NFLAGS:
200 31b836b8 Thomas Thrainer
        if getattr(self.op, attr) is None:
201 1c3231aa Thomas Thrainer
          setattr(self.op, attr, getattr(existing_node_info, attr))
202 31b836b8 Thomas Thrainer
    else:
203 31b836b8 Thomas Thrainer
      for attr in self._NFLAGS:
204 31b836b8 Thomas Thrainer
        if getattr(self.op, attr) is None:
205 31b836b8 Thomas Thrainer
          setattr(self.op, attr, True)
206 31b836b8 Thomas Thrainer
207 31b836b8 Thomas Thrainer
    if self.op.readd and not self.op.vm_capable:
208 d0d7d7cf Thomas Thrainer
      pri, sec = self.cfg.GetNodeInstances(existing_node_info.uuid)
209 31b836b8 Thomas Thrainer
      if pri or sec:
210 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Node %s being re-added with vm_capable"
211 31b836b8 Thomas Thrainer
                                   " flag set to false, but it already holds"
212 1c3231aa Thomas Thrainer
                                   " instances" % node_name,
213 31b836b8 Thomas Thrainer
                                   errors.ECODE_STATE)
214 31b836b8 Thomas Thrainer
215 31b836b8 Thomas Thrainer
    # check that the type of the node (single versus dual homed) is the
216 31b836b8 Thomas Thrainer
    # same as for the master
217 6bb43023 Thomas Thrainer
    myself = self.cfg.GetMasterNodeInfo()
218 31b836b8 Thomas Thrainer
    master_singlehomed = myself.secondary_ip == myself.primary_ip
219 d0d7d7cf Thomas Thrainer
    newbie_singlehomed = secondary_ip == self.op.primary_ip
220 31b836b8 Thomas Thrainer
    if master_singlehomed != newbie_singlehomed:
221 31b836b8 Thomas Thrainer
      if master_singlehomed:
222 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("The master has no secondary ip but the"
223 31b836b8 Thomas Thrainer
                                   " new node has one",
224 31b836b8 Thomas Thrainer
                                   errors.ECODE_INVAL)
225 31b836b8 Thomas Thrainer
      else:
226 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("The master has a secondary ip but the"
227 31b836b8 Thomas Thrainer
                                   " new node doesn't have one",
228 31b836b8 Thomas Thrainer
                                   errors.ECODE_INVAL)
229 31b836b8 Thomas Thrainer
230 31b836b8 Thomas Thrainer
    # checks reachability
231 d0d7d7cf Thomas Thrainer
    if not netutils.TcpPing(self.op.primary_ip, constants.DEFAULT_NODED_PORT):
232 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Node not reachable by ping",
233 31b836b8 Thomas Thrainer
                                 errors.ECODE_ENVIRON)
234 31b836b8 Thomas Thrainer
235 31b836b8 Thomas Thrainer
    if not newbie_singlehomed:
236 31b836b8 Thomas Thrainer
      # check reachability from my secondary ip to newbie's secondary ip
237 31b836b8 Thomas Thrainer
      if not netutils.TcpPing(secondary_ip, constants.DEFAULT_NODED_PORT,
238 31b836b8 Thomas Thrainer
                              source=myself.secondary_ip):
239 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Node secondary ip not reachable by TCP"
240 31b836b8 Thomas Thrainer
                                   " based ping to node daemon port",
241 31b836b8 Thomas Thrainer
                                   errors.ECODE_ENVIRON)
242 31b836b8 Thomas Thrainer
243 31b836b8 Thomas Thrainer
    if self.op.readd:
244 1c3231aa Thomas Thrainer
      exceptions = [existing_node_info.uuid]
245 31b836b8 Thomas Thrainer
    else:
246 31b836b8 Thomas Thrainer
      exceptions = []
247 31b836b8 Thomas Thrainer
248 31b836b8 Thomas Thrainer
    if self.op.master_capable:
249 31b836b8 Thomas Thrainer
      self.master_candidate = _DecideSelfPromotion(self, exceptions=exceptions)
250 31b836b8 Thomas Thrainer
    else:
251 31b836b8 Thomas Thrainer
      self.master_candidate = False
252 31b836b8 Thomas Thrainer
253 a9f33339 Petr Pudlak
    node_group = self.cfg.LookupNodeGroup(self.op.group)
254 a9f33339 Petr Pudlak
255 31b836b8 Thomas Thrainer
    if self.op.readd:
256 1c3231aa Thomas Thrainer
      self.new_node = existing_node_info
257 31b836b8 Thomas Thrainer
    else:
258 1c3231aa Thomas Thrainer
      self.new_node = objects.Node(name=node_name,
259 d0d7d7cf Thomas Thrainer
                                   primary_ip=self.op.primary_ip,
260 31b836b8 Thomas Thrainer
                                   secondary_ip=secondary_ip,
261 31b836b8 Thomas Thrainer
                                   master_candidate=self.master_candidate,
262 31b836b8 Thomas Thrainer
                                   offline=False, drained=False,
263 31b836b8 Thomas Thrainer
                                   group=node_group, ndparams={})
264 31b836b8 Thomas Thrainer
265 31b836b8 Thomas Thrainer
    if self.op.ndparams:
266 31b836b8 Thomas Thrainer
      utils.ForceDictType(self.op.ndparams, constants.NDS_PARAMETER_TYPES)
267 5eacbcae Thomas Thrainer
      CheckParamsNotGlobal(self.op.ndparams, constants.NDC_GLOBALS, "node",
268 5eacbcae Thomas Thrainer
                           "node", "cluster or group")
269 31b836b8 Thomas Thrainer
270 31b836b8 Thomas Thrainer
    if self.op.hv_state:
271 5eacbcae Thomas Thrainer
      self.new_hv_state = MergeAndVerifyHvState(self.op.hv_state, None)
272 31b836b8 Thomas Thrainer
273 31b836b8 Thomas Thrainer
    if self.op.disk_state:
274 5eacbcae Thomas Thrainer
      self.new_disk_state = MergeAndVerifyDiskState(self.op.disk_state, None)
275 31b836b8 Thomas Thrainer
276 31b836b8 Thomas Thrainer
    # TODO: If we need to have multiple DnsOnlyRunner we probably should make
277 31b836b8 Thomas Thrainer
    #       it a property on the base class.
278 31b836b8 Thomas Thrainer
    rpcrunner = rpc.DnsOnlyRunner()
279 1c3231aa Thomas Thrainer
    result = rpcrunner.call_version([node_name])[node_name]
280 1c3231aa Thomas Thrainer
    result.Raise("Can't get version information from node %s" % node_name)
281 31b836b8 Thomas Thrainer
    if constants.PROTOCOL_VERSION == result.payload:
282 31b836b8 Thomas Thrainer
      logging.info("Communication to node %s fine, sw version %s match",
283 1c3231aa Thomas Thrainer
                   node_name, result.payload)
284 31b836b8 Thomas Thrainer
    else:
285 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Version mismatch master version %s,"
286 31b836b8 Thomas Thrainer
                                 " node version %s" %
287 31b836b8 Thomas Thrainer
                                 (constants.PROTOCOL_VERSION, result.payload),
288 31b836b8 Thomas Thrainer
                                 errors.ECODE_ENVIRON)
289 31b836b8 Thomas Thrainer
290 d0d7d7cf Thomas Thrainer
    vg_name = self.cfg.GetVGName()
291 31b836b8 Thomas Thrainer
    if vg_name is not None:
292 31b836b8 Thomas Thrainer
      vparams = {constants.NV_PVLIST: [vg_name]}
293 d0d7d7cf Thomas Thrainer
      excl_stor = IsExclusiveStorageEnabledNode(self.cfg, self.new_node)
294 31b836b8 Thomas Thrainer
      cname = self.cfg.GetClusterName()
295 5b0dfcef Helga Velroyen
      result = rpcrunner.call_node_verify_light(
296 d0d7d7cf Thomas Thrainer
          [node_name], vparams, cname,
297 a9f33339 Petr Pudlak
          self.cfg.GetClusterInfo().hvparams,
298 a9f33339 Petr Pudlak
          {node_name: node_group},
299 a9f33339 Petr Pudlak
          self.cfg.GetAllNodeGroupsInfoDict()
300 a9f33339 Petr Pudlak
        )[node_name]
301 5eacbcae Thomas Thrainer
      (errmsgs, _) = CheckNodePVs(result.payload, excl_stor)
302 31b836b8 Thomas Thrainer
      if errmsgs:
303 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Checks on node PVs failed: %s" %
304 31b836b8 Thomas Thrainer
                                   "; ".join(errmsgs), errors.ECODE_ENVIRON)
305 31b836b8 Thomas Thrainer
306 07e68848 Thomas Thrainer
  def _InitOpenVSwitch(self):
307 07e68848 Thomas Thrainer
    filled_ndparams = self.cfg.GetClusterInfo().FillND(
308 07e68848 Thomas Thrainer
      self.new_node, self.cfg.GetNodeGroup(self.new_node.group))
309 07e68848 Thomas Thrainer
310 07e68848 Thomas Thrainer
    ovs = filled_ndparams.get(constants.ND_OVS, None)
311 07e68848 Thomas Thrainer
    ovs_name = filled_ndparams.get(constants.ND_OVS_NAME, None)
312 07e68848 Thomas Thrainer
    ovs_link = filled_ndparams.get(constants.ND_OVS_LINK, None)
313 07e68848 Thomas Thrainer
314 07e68848 Thomas Thrainer
    if ovs:
315 07e68848 Thomas Thrainer
      if not ovs_link:
316 07e68848 Thomas Thrainer
        self.LogInfo("No physical interface for OpenvSwitch was given."
317 07e68848 Thomas Thrainer
                     " OpenvSwitch will not have an outside connection. This"
318 07e68848 Thomas Thrainer
                     " might not be what you want.")
319 07e68848 Thomas Thrainer
320 07e68848 Thomas Thrainer
      result = self.rpc.call_node_configure_ovs(
321 07e68848 Thomas Thrainer
                 self.new_node.name, ovs_name, ovs_link)
322 07e68848 Thomas Thrainer
      result.Raise("Failed to initialize OpenVSwitch on new node")
323 07e68848 Thomas Thrainer
324 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
325 31b836b8 Thomas Thrainer
    """Adds the new node to the cluster.
326 31b836b8 Thomas Thrainer

327 31b836b8 Thomas Thrainer
    """
328 31b836b8 Thomas Thrainer
    assert locking.BGL in self.owned_locks(locking.LEVEL_CLUSTER), \
329 31b836b8 Thomas Thrainer
      "Not owning BGL"
330 31b836b8 Thomas Thrainer
331 31b836b8 Thomas Thrainer
    # We adding a new node so we assume it's powered
332 d0d7d7cf Thomas Thrainer
    self.new_node.powered = True
333 31b836b8 Thomas Thrainer
334 31b836b8 Thomas Thrainer
    # for re-adds, reset the offline/drained/master-candidate flags;
335 31b836b8 Thomas Thrainer
    # we need to reset here, otherwise offline would prevent RPC calls
336 31b836b8 Thomas Thrainer
    # later in the procedure; this also means that if the re-add
337 31b836b8 Thomas Thrainer
    # fails, we are left with a non-offlined, broken node
338 31b836b8 Thomas Thrainer
    if self.op.readd:
339 30796ad6 Klaus Aehlig
      self.new_node.offline = False
340 d0d7d7cf Thomas Thrainer
      self.new_node.drained = False
341 31b836b8 Thomas Thrainer
      self.LogInfo("Readding a node, the offline/drained flags were reset")
342 31b836b8 Thomas Thrainer
      # if we demote the node, we do cleanup later in the procedure
343 d0d7d7cf Thomas Thrainer
      self.new_node.master_candidate = self.master_candidate
344 31b836b8 Thomas Thrainer
      if self.changed_primary_ip:
345 d0d7d7cf Thomas Thrainer
        self.new_node.primary_ip = self.op.primary_ip
346 31b836b8 Thomas Thrainer
347 31b836b8 Thomas Thrainer
    # copy the master/vm_capable flags
348 31b836b8 Thomas Thrainer
    for attr in self._NFLAGS:
349 d0d7d7cf Thomas Thrainer
      setattr(self.new_node, attr, getattr(self.op, attr))
350 31b836b8 Thomas Thrainer
351 31b836b8 Thomas Thrainer
    # notify the user about any possible mc promotion
352 d0d7d7cf Thomas Thrainer
    if self.new_node.master_candidate:
353 31b836b8 Thomas Thrainer
      self.LogInfo("Node will be a master candidate")
354 31b836b8 Thomas Thrainer
355 31b836b8 Thomas Thrainer
    if self.op.ndparams:
356 d0d7d7cf Thomas Thrainer
      self.new_node.ndparams = self.op.ndparams
357 31b836b8 Thomas Thrainer
    else:
358 d0d7d7cf Thomas Thrainer
      self.new_node.ndparams = {}
359 31b836b8 Thomas Thrainer
360 31b836b8 Thomas Thrainer
    if self.op.hv_state:
361 d0d7d7cf Thomas Thrainer
      self.new_node.hv_state_static = self.new_hv_state
362 31b836b8 Thomas Thrainer
363 31b836b8 Thomas Thrainer
    if self.op.disk_state:
364 d0d7d7cf Thomas Thrainer
      self.new_node.disk_state_static = self.new_disk_state
365 31b836b8 Thomas Thrainer
366 31b836b8 Thomas Thrainer
    # Add node to our /etc/hosts, and add key to known_hosts
367 31b836b8 Thomas Thrainer
    if self.cfg.GetClusterInfo().modify_etc_hosts:
368 31b836b8 Thomas Thrainer
      master_node = self.cfg.GetMasterNode()
369 1c3231aa Thomas Thrainer
      result = self.rpc.call_etc_hosts_modify(
370 1c3231aa Thomas Thrainer
                 master_node, constants.ETC_HOSTS_ADD, self.hostname.name,
371 1c3231aa Thomas Thrainer
                 self.hostname.ip)
372 31b836b8 Thomas Thrainer
      result.Raise("Can't update hosts file with new host data")
373 31b836b8 Thomas Thrainer
374 d0d7d7cf Thomas Thrainer
    if self.new_node.secondary_ip != self.new_node.primary_ip:
375 d0d7d7cf Thomas Thrainer
      _CheckNodeHasSecondaryIP(self, self.new_node, self.new_node.secondary_ip,
376 d0d7d7cf Thomas Thrainer
                               False)
377 31b836b8 Thomas Thrainer
378 1c3231aa Thomas Thrainer
    node_verifier_uuids = [self.cfg.GetMasterNode()]
379 31b836b8 Thomas Thrainer
    node_verify_param = {
380 d0d7d7cf Thomas Thrainer
      constants.NV_NODELIST: ([self.new_node.name], {}),
381 31b836b8 Thomas Thrainer
      # TODO: do a node-net-test as well?
382 31b836b8 Thomas Thrainer
    }
383 31b836b8 Thomas Thrainer
384 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_verify(
385 1c3231aa Thomas Thrainer
               node_verifier_uuids, node_verify_param,
386 1c3231aa Thomas Thrainer
               self.cfg.GetClusterName(),
387 a9f33339 Petr Pudlak
               self.cfg.GetClusterInfo().hvparams,
388 a9f33339 Petr Pudlak
               {self.new_node.name: self.cfg.LookupNodeGroup(self.op.group)},
389 a9f33339 Petr Pudlak
               self.cfg.GetAllNodeGroupsInfoDict()
390 a9f33339 Petr Pudlak
               )
391 1c3231aa Thomas Thrainer
    for verifier in node_verifier_uuids:
392 31b836b8 Thomas Thrainer
      result[verifier].Raise("Cannot communicate with node %s" % verifier)
393 31b836b8 Thomas Thrainer
      nl_payload = result[verifier].payload[constants.NV_NODELIST]
394 31b836b8 Thomas Thrainer
      if nl_payload:
395 31b836b8 Thomas Thrainer
        for failed in nl_payload:
396 31b836b8 Thomas Thrainer
          feedback_fn("ssh/hostname verification failed"
397 31b836b8 Thomas Thrainer
                      " (checking from %s): %s" %
398 31b836b8 Thomas Thrainer
                      (verifier, nl_payload[failed]))
399 31b836b8 Thomas Thrainer
        raise errors.OpExecError("ssh/hostname verification failed")
400 31b836b8 Thomas Thrainer
401 07e68848 Thomas Thrainer
    self._InitOpenVSwitch()
402 8baa9ca7 Sebastian Gebhard
403 31b836b8 Thomas Thrainer
    if self.op.readd:
404 d0d7d7cf Thomas Thrainer
      self.context.ReaddNode(self.new_node)
405 1c3231aa Thomas Thrainer
      RedistributeAncillaryFiles(self)
406 31b836b8 Thomas Thrainer
      # make sure we redistribute the config
407 d0d7d7cf Thomas Thrainer
      self.cfg.Update(self.new_node, feedback_fn)
408 31b836b8 Thomas Thrainer
      # and make sure the new node will not have old files around
409 d0d7d7cf Thomas Thrainer
      if not self.new_node.master_candidate:
410 d0d7d7cf Thomas Thrainer
        result = self.rpc.call_node_demote_from_mc(self.new_node.uuid)
411 c7dd65be Klaus Aehlig
        result.Warn("Node failed to demote itself from master candidate status",
412 c7dd65be Klaus Aehlig
                    self.LogWarning)
413 31b836b8 Thomas Thrainer
    else:
414 d0d7d7cf Thomas Thrainer
      self.context.AddNode(self.new_node, self.proc.GetECId())
415 1c3231aa Thomas Thrainer
      RedistributeAncillaryFiles(self)
416 31b836b8 Thomas Thrainer
417 31b836b8 Thomas Thrainer
418 31b836b8 Thomas Thrainer
class LUNodeSetParams(LogicalUnit):
419 31b836b8 Thomas Thrainer
  """Modifies the parameters of a node.
420 31b836b8 Thomas Thrainer

421 31b836b8 Thomas Thrainer
  @cvar _F2R: a dictionary from tuples of flags (mc, drained, offline)
422 31b836b8 Thomas Thrainer
      to the node role (as _ROLE_*)
423 31b836b8 Thomas Thrainer
  @cvar _R2F: a dictionary from node role to tuples of flags
424 31b836b8 Thomas Thrainer
  @cvar _FLAGS: a list of attribute names corresponding to the flags
425 31b836b8 Thomas Thrainer

426 31b836b8 Thomas Thrainer
  """
427 31b836b8 Thomas Thrainer
  HPATH = "node-modify"
428 31b836b8 Thomas Thrainer
  HTYPE = constants.HTYPE_NODE
429 31b836b8 Thomas Thrainer
  REQ_BGL = False
430 31b836b8 Thomas Thrainer
  (_ROLE_CANDIDATE, _ROLE_DRAINED, _ROLE_OFFLINE, _ROLE_REGULAR) = range(4)
431 31b836b8 Thomas Thrainer
  _F2R = {
432 31b836b8 Thomas Thrainer
    (True, False, False): _ROLE_CANDIDATE,
433 31b836b8 Thomas Thrainer
    (False, True, False): _ROLE_DRAINED,
434 31b836b8 Thomas Thrainer
    (False, False, True): _ROLE_OFFLINE,
435 31b836b8 Thomas Thrainer
    (False, False, False): _ROLE_REGULAR,
436 31b836b8 Thomas Thrainer
    }
437 31b836b8 Thomas Thrainer
  _R2F = dict((v, k) for k, v in _F2R.items())
438 31b836b8 Thomas Thrainer
  _FLAGS = ["master_candidate", "drained", "offline"]
439 31b836b8 Thomas Thrainer
440 31b836b8 Thomas Thrainer
  def CheckArguments(self):
441 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
442 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
443 31b836b8 Thomas Thrainer
    all_mods = [self.op.offline, self.op.master_candidate, self.op.drained,
444 31b836b8 Thomas Thrainer
                self.op.master_capable, self.op.vm_capable,
445 31b836b8 Thomas Thrainer
                self.op.secondary_ip, self.op.ndparams, self.op.hv_state,
446 31b836b8 Thomas Thrainer
                self.op.disk_state]
447 31b836b8 Thomas Thrainer
    if all_mods.count(None) == len(all_mods):
448 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Please pass at least one modification",
449 31b836b8 Thomas Thrainer
                                 errors.ECODE_INVAL)
450 31b836b8 Thomas Thrainer
    if all_mods.count(True) > 1:
451 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Can't set the node into more than one"
452 31b836b8 Thomas Thrainer
                                 " state at the same time",
453 31b836b8 Thomas Thrainer
                                 errors.ECODE_INVAL)
454 31b836b8 Thomas Thrainer
455 31b836b8 Thomas Thrainer
    # Boolean value that tells us whether we might be demoting from MC
456 31b836b8 Thomas Thrainer
    self.might_demote = (self.op.master_candidate is False or
457 31b836b8 Thomas Thrainer
                         self.op.offline is True or
458 31b836b8 Thomas Thrainer
                         self.op.drained is True or
459 31b836b8 Thomas Thrainer
                         self.op.master_capable is False)
460 31b836b8 Thomas Thrainer
461 31b836b8 Thomas Thrainer
    if self.op.secondary_ip:
462 31b836b8 Thomas Thrainer
      if not netutils.IP4Address.IsValid(self.op.secondary_ip):
463 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Secondary IP (%s) needs to be a valid IPv4"
464 31b836b8 Thomas Thrainer
                                   " address" % self.op.secondary_ip,
465 31b836b8 Thomas Thrainer
                                   errors.ECODE_INVAL)
466 31b836b8 Thomas Thrainer
467 31b836b8 Thomas Thrainer
    self.lock_all = self.op.auto_promote and self.might_demote
468 31b836b8 Thomas Thrainer
    self.lock_instances = self.op.secondary_ip is not None
469 31b836b8 Thomas Thrainer
470 31b836b8 Thomas Thrainer
  def _InstanceFilter(self, instance):
471 31b836b8 Thomas Thrainer
    """Filter for getting affected instances.
472 31b836b8 Thomas Thrainer

473 31b836b8 Thomas Thrainer
    """
474 31b836b8 Thomas Thrainer
    return (instance.disk_template in constants.DTS_INT_MIRROR and
475 1c3231aa Thomas Thrainer
            self.op.node_uuid in instance.all_nodes)
476 31b836b8 Thomas Thrainer
477 31b836b8 Thomas Thrainer
  def ExpandNames(self):
478 31b836b8 Thomas Thrainer
    if self.lock_all:
479 31b836b8 Thomas Thrainer
      self.needed_locks = {
480 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE: locking.ALL_SET,
481 31b836b8 Thomas Thrainer
482 31b836b8 Thomas Thrainer
        # Block allocations when all nodes are locked
483 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
484 31b836b8 Thomas Thrainer
        }
485 31b836b8 Thomas Thrainer
    else:
486 31b836b8 Thomas Thrainer
      self.needed_locks = {
487 1c3231aa Thomas Thrainer
        locking.LEVEL_NODE: self.op.node_uuid,
488 31b836b8 Thomas Thrainer
        }
489 31b836b8 Thomas Thrainer
490 31b836b8 Thomas Thrainer
    # Since modifying a node can have severe effects on currently running
491 31b836b8 Thomas Thrainer
    # operations the resource lock is at least acquired in shared mode
492 31b836b8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = \
493 31b836b8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE]
494 31b836b8 Thomas Thrainer
495 31b836b8 Thomas Thrainer
    # Get all locks except nodes in shared mode; they are not used for anything
496 31b836b8 Thomas Thrainer
    # but read-only access
497 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
498 31b836b8 Thomas Thrainer
    self.share_locks[locking.LEVEL_NODE] = 0
499 31b836b8 Thomas Thrainer
    self.share_locks[locking.LEVEL_NODE_RES] = 0
500 31b836b8 Thomas Thrainer
    self.share_locks[locking.LEVEL_NODE_ALLOC] = 0
501 31b836b8 Thomas Thrainer
502 31b836b8 Thomas Thrainer
    if self.lock_instances:
503 31b836b8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_INSTANCE] = \
504 da4a52a3 Thomas Thrainer
        self.cfg.GetInstanceNames(
505 da4a52a3 Thomas Thrainer
          self.cfg.GetInstancesInfoByFilter(self._InstanceFilter).keys())
506 31b836b8 Thomas Thrainer
507 31b836b8 Thomas Thrainer
  def BuildHooksEnv(self):
508 31b836b8 Thomas Thrainer
    """Build hooks env.
509 31b836b8 Thomas Thrainer

510 31b836b8 Thomas Thrainer
    This runs on the master node.
511 31b836b8 Thomas Thrainer

512 31b836b8 Thomas Thrainer
    """
513 31b836b8 Thomas Thrainer
    return {
514 31b836b8 Thomas Thrainer
      "OP_TARGET": self.op.node_name,
515 31b836b8 Thomas Thrainer
      "MASTER_CANDIDATE": str(self.op.master_candidate),
516 31b836b8 Thomas Thrainer
      "OFFLINE": str(self.op.offline),
517 31b836b8 Thomas Thrainer
      "DRAINED": str(self.op.drained),
518 31b836b8 Thomas Thrainer
      "MASTER_CAPABLE": str(self.op.master_capable),
519 31b836b8 Thomas Thrainer
      "VM_CAPABLE": str(self.op.vm_capable),
520 31b836b8 Thomas Thrainer
      }
521 31b836b8 Thomas Thrainer
522 31b836b8 Thomas Thrainer
  def BuildHooksNodes(self):
523 31b836b8 Thomas Thrainer
    """Build hooks nodes.
524 31b836b8 Thomas Thrainer

525 31b836b8 Thomas Thrainer
    """
526 1c3231aa Thomas Thrainer
    nl = [self.cfg.GetMasterNode(), self.op.node_uuid]
527 31b836b8 Thomas Thrainer
    return (nl, nl)
528 31b836b8 Thomas Thrainer
529 31b836b8 Thomas Thrainer
  def CheckPrereq(self):
530 31b836b8 Thomas Thrainer
    """Check prerequisites.
531 31b836b8 Thomas Thrainer

532 31b836b8 Thomas Thrainer
    This only checks the instance list against the existing names.
533 31b836b8 Thomas Thrainer

534 31b836b8 Thomas Thrainer
    """
535 1c3231aa Thomas Thrainer
    node = self.cfg.GetNodeInfo(self.op.node_uuid)
536 31b836b8 Thomas Thrainer
    if self.lock_instances:
537 31b836b8 Thomas Thrainer
      affected_instances = \
538 31b836b8 Thomas Thrainer
        self.cfg.GetInstancesInfoByFilter(self._InstanceFilter)
539 31b836b8 Thomas Thrainer
540 31b836b8 Thomas Thrainer
      # Verify instance locks
541 da4a52a3 Thomas Thrainer
      owned_instance_names = self.owned_locks(locking.LEVEL_INSTANCE)
542 da4a52a3 Thomas Thrainer
      wanted_instance_names = frozenset([inst.name for inst in
543 da4a52a3 Thomas Thrainer
                                         affected_instances.values()])
544 da4a52a3 Thomas Thrainer
      if wanted_instance_names - owned_instance_names:
545 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Instances affected by changing node %s's"
546 31b836b8 Thomas Thrainer
                                   " secondary IP address have changed since"
547 31b836b8 Thomas Thrainer
                                   " locks were acquired, wanted '%s', have"
548 31b836b8 Thomas Thrainer
                                   " '%s'; retry the operation" %
549 1c3231aa Thomas Thrainer
                                   (node.name,
550 da4a52a3 Thomas Thrainer
                                    utils.CommaJoin(wanted_instance_names),
551 da4a52a3 Thomas Thrainer
                                    utils.CommaJoin(owned_instance_names)),
552 31b836b8 Thomas Thrainer
                                   errors.ECODE_STATE)
553 31b836b8 Thomas Thrainer
    else:
554 31b836b8 Thomas Thrainer
      affected_instances = None
555 31b836b8 Thomas Thrainer
556 31b836b8 Thomas Thrainer
    if (self.op.master_candidate is not None or
557 31b836b8 Thomas Thrainer
        self.op.drained is not None or
558 31b836b8 Thomas Thrainer
        self.op.offline is not None):
559 31b836b8 Thomas Thrainer
      # we can't change the master's node flags
560 1c3231aa Thomas Thrainer
      if node.uuid == self.cfg.GetMasterNode():
561 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("The master role can be changed"
562 31b836b8 Thomas Thrainer
                                   " only via master-failover",
563 31b836b8 Thomas Thrainer
                                   errors.ECODE_INVAL)
564 31b836b8 Thomas Thrainer
565 31b836b8 Thomas Thrainer
    if self.op.master_candidate and not node.master_capable:
566 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Node %s is not master capable, cannot make"
567 31b836b8 Thomas Thrainer
                                 " it a master candidate" % node.name,
568 31b836b8 Thomas Thrainer
                                 errors.ECODE_STATE)
569 31b836b8 Thomas Thrainer
570 31b836b8 Thomas Thrainer
    if self.op.vm_capable is False:
571 1c3231aa Thomas Thrainer
      (ipri, isec) = self.cfg.GetNodeInstances(node.uuid)
572 31b836b8 Thomas Thrainer
      if ipri or isec:
573 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Node %s hosts instances, cannot unset"
574 31b836b8 Thomas Thrainer
                                   " the vm_capable flag" % node.name,
575 31b836b8 Thomas Thrainer
                                   errors.ECODE_STATE)
576 31b836b8 Thomas Thrainer
577 31b836b8 Thomas Thrainer
    if node.master_candidate and self.might_demote and not self.lock_all:
578 31b836b8 Thomas Thrainer
      assert not self.op.auto_promote, "auto_promote set but lock_all not"
579 31b836b8 Thomas Thrainer
      # check if after removing the current node, we're missing master
580 31b836b8 Thomas Thrainer
      # candidates
581 31b836b8 Thomas Thrainer
      (mc_remaining, mc_should, _) = \
582 1c3231aa Thomas Thrainer
          self.cfg.GetMasterCandidateStats(exceptions=[node.uuid])
583 31b836b8 Thomas Thrainer
      if mc_remaining < mc_should:
584 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Not enough master candidates, please"
585 31b836b8 Thomas Thrainer
                                   " pass auto promote option to allow"
586 31b836b8 Thomas Thrainer
                                   " promotion (--auto-promote or RAPI"
587 31b836b8 Thomas Thrainer
                                   " auto_promote=True)", errors.ECODE_STATE)
588 31b836b8 Thomas Thrainer
589 31b836b8 Thomas Thrainer
    self.old_flags = old_flags = (node.master_candidate,
590 31b836b8 Thomas Thrainer
                                  node.drained, node.offline)
591 31b836b8 Thomas Thrainer
    assert old_flags in self._F2R, "Un-handled old flags %s" % str(old_flags)
592 31b836b8 Thomas Thrainer
    self.old_role = old_role = self._F2R[old_flags]
593 31b836b8 Thomas Thrainer
594 31b836b8 Thomas Thrainer
    # Check for ineffective changes
595 31b836b8 Thomas Thrainer
    for attr in self._FLAGS:
596 1c3231aa Thomas Thrainer
      if getattr(self.op, attr) is False and getattr(node, attr) is False:
597 31b836b8 Thomas Thrainer
        self.LogInfo("Ignoring request to unset flag %s, already unset", attr)
598 31b836b8 Thomas Thrainer
        setattr(self.op, attr, None)
599 31b836b8 Thomas Thrainer
600 31b836b8 Thomas Thrainer
    # Past this point, any flag change to False means a transition
601 31b836b8 Thomas Thrainer
    # away from the respective state, as only real changes are kept
602 31b836b8 Thomas Thrainer
603 31b836b8 Thomas Thrainer
    # TODO: We might query the real power state if it supports OOB
604 5eacbcae Thomas Thrainer
    if SupportsOob(self.cfg, node):
605 31b836b8 Thomas Thrainer
      if self.op.offline is False and not (node.powered or
606 31b836b8 Thomas Thrainer
                                           self.op.powered is True):
607 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError(("Node %s needs to be turned on before its"
608 31b836b8 Thomas Thrainer
                                    " offline status can be reset") %
609 31b836b8 Thomas Thrainer
                                   self.op.node_name, errors.ECODE_STATE)
610 31b836b8 Thomas Thrainer
    elif self.op.powered is not None:
611 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError(("Unable to change powered state for node %s"
612 31b836b8 Thomas Thrainer
                                  " as it does not support out-of-band"
613 31b836b8 Thomas Thrainer
                                  " handling") % self.op.node_name,
614 31b836b8 Thomas Thrainer
                                 errors.ECODE_STATE)
615 31b836b8 Thomas Thrainer
616 31b836b8 Thomas Thrainer
    # If we're being deofflined/drained, we'll MC ourself if needed
617 31b836b8 Thomas Thrainer
    if (self.op.drained is False or self.op.offline is False or
618 31b836b8 Thomas Thrainer
        (self.op.master_capable and not node.master_capable)):
619 31b836b8 Thomas Thrainer
      if _DecideSelfPromotion(self):
620 31b836b8 Thomas Thrainer
        self.op.master_candidate = True
621 31b836b8 Thomas Thrainer
        self.LogInfo("Auto-promoting node to master candidate")
622 31b836b8 Thomas Thrainer
623 31b836b8 Thomas Thrainer
    # If we're no longer master capable, we'll demote ourselves from MC
624 31b836b8 Thomas Thrainer
    if self.op.master_capable is False and node.master_candidate:
625 31b836b8 Thomas Thrainer
      self.LogInfo("Demoting from master candidate")
626 31b836b8 Thomas Thrainer
      self.op.master_candidate = False
627 31b836b8 Thomas Thrainer
628 31b836b8 Thomas Thrainer
    # Compute new role
629 31b836b8 Thomas Thrainer
    assert [getattr(self.op, attr) for attr in self._FLAGS].count(True) <= 1
630 31b836b8 Thomas Thrainer
    if self.op.master_candidate:
631 31b836b8 Thomas Thrainer
      new_role = self._ROLE_CANDIDATE
632 31b836b8 Thomas Thrainer
    elif self.op.drained:
633 31b836b8 Thomas Thrainer
      new_role = self._ROLE_DRAINED
634 31b836b8 Thomas Thrainer
    elif self.op.offline:
635 31b836b8 Thomas Thrainer
      new_role = self._ROLE_OFFLINE
636 31b836b8 Thomas Thrainer
    elif False in [self.op.master_candidate, self.op.drained, self.op.offline]:
637 31b836b8 Thomas Thrainer
      # False is still in new flags, which means we're un-setting (the
638 31b836b8 Thomas Thrainer
      # only) True flag
639 31b836b8 Thomas Thrainer
      new_role = self._ROLE_REGULAR
640 31b836b8 Thomas Thrainer
    else: # no new flags, nothing, keep old role
641 31b836b8 Thomas Thrainer
      new_role = old_role
642 31b836b8 Thomas Thrainer
643 31b836b8 Thomas Thrainer
    self.new_role = new_role
644 31b836b8 Thomas Thrainer
645 31b836b8 Thomas Thrainer
    if old_role == self._ROLE_OFFLINE and new_role != old_role:
646 31b836b8 Thomas Thrainer
      # Trying to transition out of offline status
647 1c3231aa Thomas Thrainer
      result = self.rpc.call_version([node.uuid])[node.uuid]
648 31b836b8 Thomas Thrainer
      if result.fail_msg:
649 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Node %s is being de-offlined but fails"
650 31b836b8 Thomas Thrainer
                                   " to report its version: %s" %
651 31b836b8 Thomas Thrainer
                                   (node.name, result.fail_msg),
652 31b836b8 Thomas Thrainer
                                   errors.ECODE_STATE)
653 31b836b8 Thomas Thrainer
      else:
654 31b836b8 Thomas Thrainer
        self.LogWarning("Transitioning node from offline to online state"
655 31b836b8 Thomas Thrainer
                        " without using re-add. Please make sure the node"
656 31b836b8 Thomas Thrainer
                        " is healthy!")
657 31b836b8 Thomas Thrainer
658 31b836b8 Thomas Thrainer
    # When changing the secondary ip, verify if this is a single-homed to
659 31b836b8 Thomas Thrainer
    # multi-homed transition or vice versa, and apply the relevant
660 31b836b8 Thomas Thrainer
    # restrictions.
661 31b836b8 Thomas Thrainer
    if self.op.secondary_ip:
662 31b836b8 Thomas Thrainer
      # Ok even without locking, because this can't be changed by any LU
663 6bb43023 Thomas Thrainer
      master = self.cfg.GetMasterNodeInfo()
664 31b836b8 Thomas Thrainer
      master_singlehomed = master.secondary_ip == master.primary_ip
665 31b836b8 Thomas Thrainer
      if master_singlehomed and self.op.secondary_ip != node.primary_ip:
666 1c3231aa Thomas Thrainer
        if self.op.force and node.uuid == master.uuid:
667 31b836b8 Thomas Thrainer
          self.LogWarning("Transitioning from single-homed to multi-homed"
668 31b836b8 Thomas Thrainer
                          " cluster; all nodes will require a secondary IP"
669 31b836b8 Thomas Thrainer
                          " address")
670 31b836b8 Thomas Thrainer
        else:
671 31b836b8 Thomas Thrainer
          raise errors.OpPrereqError("Changing the secondary ip on a"
672 31b836b8 Thomas Thrainer
                                     " single-homed cluster requires the"
673 31b836b8 Thomas Thrainer
                                     " --force option to be passed, and the"
674 31b836b8 Thomas Thrainer
                                     " target node to be the master",
675 31b836b8 Thomas Thrainer
                                     errors.ECODE_INVAL)
676 31b836b8 Thomas Thrainer
      elif not master_singlehomed and self.op.secondary_ip == node.primary_ip:
677 1c3231aa Thomas Thrainer
        if self.op.force and node.uuid == master.uuid:
678 31b836b8 Thomas Thrainer
          self.LogWarning("Transitioning from multi-homed to single-homed"
679 31b836b8 Thomas Thrainer
                          " cluster; secondary IP addresses will have to be"
680 31b836b8 Thomas Thrainer
                          " removed")
681 31b836b8 Thomas Thrainer
        else:
682 31b836b8 Thomas Thrainer
          raise errors.OpPrereqError("Cannot set the secondary IP to be the"
683 31b836b8 Thomas Thrainer
                                     " same as the primary IP on a multi-homed"
684 31b836b8 Thomas Thrainer
                                     " cluster, unless the --force option is"
685 31b836b8 Thomas Thrainer
                                     " passed, and the target node is the"
686 31b836b8 Thomas Thrainer
                                     " master", errors.ECODE_INVAL)
687 31b836b8 Thomas Thrainer
688 da4a52a3 Thomas Thrainer
      assert not (set([inst.name for inst in affected_instances.values()]) -
689 31b836b8 Thomas Thrainer
                  self.owned_locks(locking.LEVEL_INSTANCE))
690 31b836b8 Thomas Thrainer
691 31b836b8 Thomas Thrainer
      if node.offline:
692 31b836b8 Thomas Thrainer
        if affected_instances:
693 31b836b8 Thomas Thrainer
          msg = ("Cannot change secondary IP address: offline node has"
694 31b836b8 Thomas Thrainer
                 " instances (%s) configured to use it" %
695 da4a52a3 Thomas Thrainer
                 utils.CommaJoin(
696 da4a52a3 Thomas Thrainer
                   [inst.name for inst in affected_instances.values()]))
697 31b836b8 Thomas Thrainer
          raise errors.OpPrereqError(msg, errors.ECODE_STATE)
698 31b836b8 Thomas Thrainer
      else:
699 31b836b8 Thomas Thrainer
        # On online nodes, check that no instances are running, and that
700 31b836b8 Thomas Thrainer
        # the node has the new ip and we can reach it.
701 31b836b8 Thomas Thrainer
        for instance in affected_instances.values():
702 5eacbcae Thomas Thrainer
          CheckInstanceState(self, instance, INSTANCE_DOWN,
703 5eacbcae Thomas Thrainer
                             msg="cannot change secondary ip")
704 31b836b8 Thomas Thrainer
705 1c3231aa Thomas Thrainer
        _CheckNodeHasSecondaryIP(self, node, self.op.secondary_ip, True)
706 1c3231aa Thomas Thrainer
        if master.uuid != node.uuid:
707 31b836b8 Thomas Thrainer
          # check reachability from master secondary ip to new secondary ip
708 31b836b8 Thomas Thrainer
          if not netutils.TcpPing(self.op.secondary_ip,
709 31b836b8 Thomas Thrainer
                                  constants.DEFAULT_NODED_PORT,
710 31b836b8 Thomas Thrainer
                                  source=master.secondary_ip):
711 31b836b8 Thomas Thrainer
            raise errors.OpPrereqError("Node secondary ip not reachable by TCP"
712 31b836b8 Thomas Thrainer
                                       " based ping to node daemon port",
713 31b836b8 Thomas Thrainer
                                       errors.ECODE_ENVIRON)
714 31b836b8 Thomas Thrainer
715 31b836b8 Thomas Thrainer
    if self.op.ndparams:
716 1c3231aa Thomas Thrainer
      new_ndparams = GetUpdatedParams(node.ndparams, self.op.ndparams)
717 31b836b8 Thomas Thrainer
      utils.ForceDictType(new_ndparams, constants.NDS_PARAMETER_TYPES)
718 5eacbcae Thomas Thrainer
      CheckParamsNotGlobal(self.op.ndparams, constants.NDC_GLOBALS, "node",
719 5eacbcae Thomas Thrainer
                           "node", "cluster or group")
720 31b836b8 Thomas Thrainer
      self.new_ndparams = new_ndparams
721 31b836b8 Thomas Thrainer
722 31b836b8 Thomas Thrainer
    if self.op.hv_state:
723 5eacbcae Thomas Thrainer
      self.new_hv_state = MergeAndVerifyHvState(self.op.hv_state,
724 1c3231aa Thomas Thrainer
                                                node.hv_state_static)
725 31b836b8 Thomas Thrainer
726 31b836b8 Thomas Thrainer
    if self.op.disk_state:
727 31b836b8 Thomas Thrainer
      self.new_disk_state = \
728 1c3231aa Thomas Thrainer
        MergeAndVerifyDiskState(self.op.disk_state, node.disk_state_static)
729 31b836b8 Thomas Thrainer
730 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
731 31b836b8 Thomas Thrainer
    """Modifies a node.
732 31b836b8 Thomas Thrainer

733 31b836b8 Thomas Thrainer
    """
734 1c3231aa Thomas Thrainer
    node = self.cfg.GetNodeInfo(self.op.node_uuid)
735 31b836b8 Thomas Thrainer
    result = []
736 31b836b8 Thomas Thrainer
737 31b836b8 Thomas Thrainer
    if self.op.ndparams:
738 31b836b8 Thomas Thrainer
      node.ndparams = self.new_ndparams
739 31b836b8 Thomas Thrainer
740 31b836b8 Thomas Thrainer
    if self.op.powered is not None:
741 31b836b8 Thomas Thrainer
      node.powered = self.op.powered
742 31b836b8 Thomas Thrainer
743 31b836b8 Thomas Thrainer
    if self.op.hv_state:
744 31b836b8 Thomas Thrainer
      node.hv_state_static = self.new_hv_state
745 31b836b8 Thomas Thrainer
746 31b836b8 Thomas Thrainer
    if self.op.disk_state:
747 31b836b8 Thomas Thrainer
      node.disk_state_static = self.new_disk_state
748 31b836b8 Thomas Thrainer
749 31b836b8 Thomas Thrainer
    for attr in ["master_capable", "vm_capable"]:
750 31b836b8 Thomas Thrainer
      val = getattr(self.op, attr)
751 31b836b8 Thomas Thrainer
      if val is not None:
752 31b836b8 Thomas Thrainer
        setattr(node, attr, val)
753 31b836b8 Thomas Thrainer
        result.append((attr, str(val)))
754 31b836b8 Thomas Thrainer
755 d0d7d7cf Thomas Thrainer
    if self.new_role != self.old_role:
756 31b836b8 Thomas Thrainer
      # Tell the node to demote itself, if no longer MC and not offline
757 d0d7d7cf Thomas Thrainer
      if self.old_role == self._ROLE_CANDIDATE and \
758 d0d7d7cf Thomas Thrainer
          self.new_role != self._ROLE_OFFLINE:
759 31b836b8 Thomas Thrainer
        msg = self.rpc.call_node_demote_from_mc(node.name).fail_msg
760 31b836b8 Thomas Thrainer
        if msg:
761 31b836b8 Thomas Thrainer
          self.LogWarning("Node failed to demote itself: %s", msg)
762 31b836b8 Thomas Thrainer
763 d0d7d7cf Thomas Thrainer
      new_flags = self._R2F[self.new_role]
764 31b836b8 Thomas Thrainer
      for of, nf, desc in zip(self.old_flags, new_flags, self._FLAGS):
765 31b836b8 Thomas Thrainer
        if of != nf:
766 31b836b8 Thomas Thrainer
          result.append((desc, str(nf)))
767 31b836b8 Thomas Thrainer
      (node.master_candidate, node.drained, node.offline) = new_flags
768 31b836b8 Thomas Thrainer
769 31b836b8 Thomas Thrainer
      # we locked all nodes, we adjust the CP before updating this node
770 31b836b8 Thomas Thrainer
      if self.lock_all:
771 1c3231aa Thomas Thrainer
        AdjustCandidatePool(self, [node.uuid])
772 31b836b8 Thomas Thrainer
773 31b836b8 Thomas Thrainer
    if self.op.secondary_ip:
774 31b836b8 Thomas Thrainer
      node.secondary_ip = self.op.secondary_ip
775 31b836b8 Thomas Thrainer
      result.append(("secondary_ip", self.op.secondary_ip))
776 31b836b8 Thomas Thrainer
777 31b836b8 Thomas Thrainer
    # this will trigger configuration file update, if needed
778 31b836b8 Thomas Thrainer
    self.cfg.Update(node, feedback_fn)
779 31b836b8 Thomas Thrainer
780 31b836b8 Thomas Thrainer
    # this will trigger job queue propagation or cleanup if the mc
781 31b836b8 Thomas Thrainer
    # flag changed
782 d0d7d7cf Thomas Thrainer
    if [self.old_role, self.new_role].count(self._ROLE_CANDIDATE) == 1:
783 31b836b8 Thomas Thrainer
      self.context.ReaddNode(node)
784 31b836b8 Thomas Thrainer
785 31b836b8 Thomas Thrainer
    return result
786 31b836b8 Thomas Thrainer
787 31b836b8 Thomas Thrainer
788 31b836b8 Thomas Thrainer
class LUNodePowercycle(NoHooksLU):
789 31b836b8 Thomas Thrainer
  """Powercycles a node.
790 31b836b8 Thomas Thrainer

791 31b836b8 Thomas Thrainer
  """
792 31b836b8 Thomas Thrainer
  REQ_BGL = False
793 31b836b8 Thomas Thrainer
794 31b836b8 Thomas Thrainer
  def CheckArguments(self):
795 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
796 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
797 1c3231aa Thomas Thrainer
798 1c3231aa Thomas Thrainer
    if self.op.node_uuid == self.cfg.GetMasterNode() and not self.op.force:
799 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("The node is the master and the force"
800 31b836b8 Thomas Thrainer
                                 " parameter was not set",
801 31b836b8 Thomas Thrainer
                                 errors.ECODE_INVAL)
802 31b836b8 Thomas Thrainer
803 31b836b8 Thomas Thrainer
  def ExpandNames(self):
804 31b836b8 Thomas Thrainer
    """Locking for PowercycleNode.
805 31b836b8 Thomas Thrainer

806 31b836b8 Thomas Thrainer
    This is a last-resort option and shouldn't block on other
807 31b836b8 Thomas Thrainer
    jobs. Therefore, we grab no locks.
808 31b836b8 Thomas Thrainer

809 31b836b8 Thomas Thrainer
    """
810 31b836b8 Thomas Thrainer
    self.needed_locks = {}
811 31b836b8 Thomas Thrainer
812 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
813 31b836b8 Thomas Thrainer
    """Reboots a node.
814 31b836b8 Thomas Thrainer

815 31b836b8 Thomas Thrainer
    """
816 8ef418bb Helga Velroyen
    default_hypervisor = self.cfg.GetHypervisorType()
817 8ef418bb Helga Velroyen
    hvparams = self.cfg.GetClusterInfo().hvparams[default_hypervisor]
818 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_powercycle(self.op.node_uuid,
819 8ef418bb Helga Velroyen
                                           default_hypervisor,
820 8ef418bb Helga Velroyen
                                           hvparams)
821 31b836b8 Thomas Thrainer
    result.Raise("Failed to schedule the reboot")
822 31b836b8 Thomas Thrainer
    return result.payload
823 31b836b8 Thomas Thrainer
824 31b836b8 Thomas Thrainer
825 31b836b8 Thomas Thrainer
def _GetNodeInstancesInner(cfg, fn):
826 31b836b8 Thomas Thrainer
  return [i for i in cfg.GetAllInstancesInfo().values() if fn(i)]
827 31b836b8 Thomas Thrainer
828 31b836b8 Thomas Thrainer
829 1c3231aa Thomas Thrainer
def _GetNodePrimaryInstances(cfg, node_uuid):
830 31b836b8 Thomas Thrainer
  """Returns primary instances on a node.
831 31b836b8 Thomas Thrainer

832 31b836b8 Thomas Thrainer
  """
833 31b836b8 Thomas Thrainer
  return _GetNodeInstancesInner(cfg,
834 1c3231aa Thomas Thrainer
                                lambda inst: node_uuid == inst.primary_node)
835 31b836b8 Thomas Thrainer
836 31b836b8 Thomas Thrainer
837 1c3231aa Thomas Thrainer
def _GetNodeSecondaryInstances(cfg, node_uuid):
838 31b836b8 Thomas Thrainer
  """Returns secondary instances on a node.
839 31b836b8 Thomas Thrainer

840 31b836b8 Thomas Thrainer
  """
841 31b836b8 Thomas Thrainer
  return _GetNodeInstancesInner(cfg,
842 1c3231aa Thomas Thrainer
                                lambda inst: node_uuid in inst.secondary_nodes)
843 31b836b8 Thomas Thrainer
844 31b836b8 Thomas Thrainer
845 1c3231aa Thomas Thrainer
def _GetNodeInstances(cfg, node_uuid):
846 31b836b8 Thomas Thrainer
  """Returns a list of all primary and secondary instances on a node.
847 31b836b8 Thomas Thrainer

848 31b836b8 Thomas Thrainer
  """
849 31b836b8 Thomas Thrainer
850 1c3231aa Thomas Thrainer
  return _GetNodeInstancesInner(cfg, lambda inst: node_uuid in inst.all_nodes)
851 31b836b8 Thomas Thrainer
852 31b836b8 Thomas Thrainer
853 31b836b8 Thomas Thrainer
class LUNodeEvacuate(NoHooksLU):
854 31b836b8 Thomas Thrainer
  """Evacuates instances off a list of nodes.
855 31b836b8 Thomas Thrainer

856 31b836b8 Thomas Thrainer
  """
857 31b836b8 Thomas Thrainer
  REQ_BGL = False
858 31b836b8 Thomas Thrainer
859 31b836b8 Thomas Thrainer
  def CheckArguments(self):
860 5eacbcae Thomas Thrainer
    CheckIAllocatorOrNode(self, "iallocator", "remote_node")
861 31b836b8 Thomas Thrainer
862 31b836b8 Thomas Thrainer
  def ExpandNames(self):
863 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
864 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
865 31b836b8 Thomas Thrainer
866 31b836b8 Thomas Thrainer
    if self.op.remote_node is not None:
867 1c3231aa Thomas Thrainer
      (self.op.remote_node_uuid, self.op.remote_node) = \
868 1c3231aa Thomas Thrainer
        ExpandNodeUuidAndName(self.cfg, self.op.remote_node_uuid,
869 1c3231aa Thomas Thrainer
                              self.op.remote_node)
870 31b836b8 Thomas Thrainer
      assert self.op.remote_node
871 31b836b8 Thomas Thrainer
872 1c3231aa Thomas Thrainer
      if self.op.node_uuid == self.op.remote_node_uuid:
873 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Can not use evacuated node as a new"
874 31b836b8 Thomas Thrainer
                                   " secondary node", errors.ECODE_INVAL)
875 31b836b8 Thomas Thrainer
876 31b836b8 Thomas Thrainer
      if self.op.mode != constants.NODE_EVAC_SEC:
877 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Without the use of an iallocator only"
878 31b836b8 Thomas Thrainer
                                   " secondary instances can be evacuated",
879 31b836b8 Thomas Thrainer
                                   errors.ECODE_INVAL)
880 31b836b8 Thomas Thrainer
881 31b836b8 Thomas Thrainer
    # Declare locks
882 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
883 31b836b8 Thomas Thrainer
    self.needed_locks = {
884 31b836b8 Thomas Thrainer
      locking.LEVEL_INSTANCE: [],
885 31b836b8 Thomas Thrainer
      locking.LEVEL_NODEGROUP: [],
886 31b836b8 Thomas Thrainer
      locking.LEVEL_NODE: [],
887 31b836b8 Thomas Thrainer
      }
888 31b836b8 Thomas Thrainer
889 31b836b8 Thomas Thrainer
    # Determine nodes (via group) optimistically, needs verification once locks
890 31b836b8 Thomas Thrainer
    # have been acquired
891 31b836b8 Thomas Thrainer
    self.lock_nodes = self._DetermineNodes()
892 31b836b8 Thomas Thrainer
893 31b836b8 Thomas Thrainer
  def _DetermineNodes(self):
894 1c3231aa Thomas Thrainer
    """Gets the list of node UUIDs to operate on.
895 31b836b8 Thomas Thrainer

896 31b836b8 Thomas Thrainer
    """
897 31b836b8 Thomas Thrainer
    if self.op.remote_node is None:
898 31b836b8 Thomas Thrainer
      # Iallocator will choose any node(s) in the same group
899 1c3231aa Thomas Thrainer
      group_nodes = self.cfg.GetNodeGroupMembersByNodes([self.op.node_uuid])
900 31b836b8 Thomas Thrainer
    else:
901 1c3231aa Thomas Thrainer
      group_nodes = frozenset([self.op.remote_node_uuid])
902 31b836b8 Thomas Thrainer
903 31b836b8 Thomas Thrainer
    # Determine nodes to be locked
904 1c3231aa Thomas Thrainer
    return set([self.op.node_uuid]) | group_nodes
905 31b836b8 Thomas Thrainer
906 31b836b8 Thomas Thrainer
  def _DetermineInstances(self):
907 31b836b8 Thomas Thrainer
    """Builds list of instances to operate on.
908 31b836b8 Thomas Thrainer

909 31b836b8 Thomas Thrainer
    """
910 31b836b8 Thomas Thrainer
    assert self.op.mode in constants.NODE_EVAC_MODES
911 31b836b8 Thomas Thrainer
912 31b836b8 Thomas Thrainer
    if self.op.mode == constants.NODE_EVAC_PRI:
913 31b836b8 Thomas Thrainer
      # Primary instances only
914 31b836b8 Thomas Thrainer
      inst_fn = _GetNodePrimaryInstances
915 31b836b8 Thomas Thrainer
      assert self.op.remote_node is None, \
916 31b836b8 Thomas Thrainer
        "Evacuating primary instances requires iallocator"
917 31b836b8 Thomas Thrainer
    elif self.op.mode == constants.NODE_EVAC_SEC:
918 31b836b8 Thomas Thrainer
      # Secondary instances only
919 31b836b8 Thomas Thrainer
      inst_fn = _GetNodeSecondaryInstances
920 31b836b8 Thomas Thrainer
    else:
921 31b836b8 Thomas Thrainer
      # All instances
922 31b836b8 Thomas Thrainer
      assert self.op.mode == constants.NODE_EVAC_ALL
923 31b836b8 Thomas Thrainer
      inst_fn = _GetNodeInstances
924 31b836b8 Thomas Thrainer
      # TODO: In 2.6, change the iallocator interface to take an evacuation mode
925 31b836b8 Thomas Thrainer
      # per instance
926 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Due to an issue with the iallocator"
927 31b836b8 Thomas Thrainer
                                 " interface it is not possible to evacuate"
928 31b836b8 Thomas Thrainer
                                 " all instances at once; specify explicitly"
929 31b836b8 Thomas Thrainer
                                 " whether to evacuate primary or secondary"
930 31b836b8 Thomas Thrainer
                                 " instances",
931 31b836b8 Thomas Thrainer
                                 errors.ECODE_INVAL)
932 31b836b8 Thomas Thrainer
933 1c3231aa Thomas Thrainer
    return inst_fn(self.cfg, self.op.node_uuid)
934 31b836b8 Thomas Thrainer
935 31b836b8 Thomas Thrainer
  def DeclareLocks(self, level):
936 31b836b8 Thomas Thrainer
    if level == locking.LEVEL_INSTANCE:
937 31b836b8 Thomas Thrainer
      # Lock instances optimistically, needs verification once node and group
938 31b836b8 Thomas Thrainer
      # locks have been acquired
939 31b836b8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_INSTANCE] = \
940 31b836b8 Thomas Thrainer
        set(i.name for i in self._DetermineInstances())
941 31b836b8 Thomas Thrainer
942 31b836b8 Thomas Thrainer
    elif level == locking.LEVEL_NODEGROUP:
943 31b836b8 Thomas Thrainer
      # Lock node groups for all potential target nodes optimistically, needs
944 31b836b8 Thomas Thrainer
      # verification once nodes have been acquired
945 31b836b8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = \
946 31b836b8 Thomas Thrainer
        self.cfg.GetNodeGroupsFromNodes(self.lock_nodes)
947 31b836b8 Thomas Thrainer
948 31b836b8 Thomas Thrainer
    elif level == locking.LEVEL_NODE:
949 31b836b8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = self.lock_nodes
950 31b836b8 Thomas Thrainer
951 31b836b8 Thomas Thrainer
  def CheckPrereq(self):
952 31b836b8 Thomas Thrainer
    # Verify locks
953 da4a52a3 Thomas Thrainer
    owned_instance_names = self.owned_locks(locking.LEVEL_INSTANCE)
954 31b836b8 Thomas Thrainer
    owned_nodes = self.owned_locks(locking.LEVEL_NODE)
955 31b836b8 Thomas Thrainer
    owned_groups = self.owned_locks(locking.LEVEL_NODEGROUP)
956 31b836b8 Thomas Thrainer
957 31b836b8 Thomas Thrainer
    need_nodes = self._DetermineNodes()
958 31b836b8 Thomas Thrainer
959 31b836b8 Thomas Thrainer
    if not owned_nodes.issuperset(need_nodes):
960 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Nodes in same group as '%s' changed since"
961 31b836b8 Thomas Thrainer
                                 " locks were acquired, current nodes are"
962 31b836b8 Thomas Thrainer
                                 " are '%s', used to be '%s'; retry the"
963 31b836b8 Thomas Thrainer
                                 " operation" %
964 31b836b8 Thomas Thrainer
                                 (self.op.node_name,
965 31b836b8 Thomas Thrainer
                                  utils.CommaJoin(need_nodes),
966 31b836b8 Thomas Thrainer
                                  utils.CommaJoin(owned_nodes)),
967 31b836b8 Thomas Thrainer
                                 errors.ECODE_STATE)
968 31b836b8 Thomas Thrainer
969 31b836b8 Thomas Thrainer
    wanted_groups = self.cfg.GetNodeGroupsFromNodes(owned_nodes)
970 31b836b8 Thomas Thrainer
    if owned_groups != wanted_groups:
971 31b836b8 Thomas Thrainer
      raise errors.OpExecError("Node groups changed since locks were acquired,"
972 31b836b8 Thomas Thrainer
                               " current groups are '%s', used to be '%s';"
973 31b836b8 Thomas Thrainer
                               " retry the operation" %
974 31b836b8 Thomas Thrainer
                               (utils.CommaJoin(wanted_groups),
975 31b836b8 Thomas Thrainer
                                utils.CommaJoin(owned_groups)))
976 31b836b8 Thomas Thrainer
977 31b836b8 Thomas Thrainer
    # Determine affected instances
978 31b836b8 Thomas Thrainer
    self.instances = self._DetermineInstances()
979 31b836b8 Thomas Thrainer
    self.instance_names = [i.name for i in self.instances]
980 31b836b8 Thomas Thrainer
981 da4a52a3 Thomas Thrainer
    if set(self.instance_names) != owned_instance_names:
982 31b836b8 Thomas Thrainer
      raise errors.OpExecError("Instances on node '%s' changed since locks"
983 31b836b8 Thomas Thrainer
                               " were acquired, current instances are '%s',"
984 31b836b8 Thomas Thrainer
                               " used to be '%s'; retry the operation" %
985 31b836b8 Thomas Thrainer
                               (self.op.node_name,
986 31b836b8 Thomas Thrainer
                                utils.CommaJoin(self.instance_names),
987 da4a52a3 Thomas Thrainer
                                utils.CommaJoin(owned_instance_names)))
988 31b836b8 Thomas Thrainer
989 31b836b8 Thomas Thrainer
    if self.instance_names:
990 31b836b8 Thomas Thrainer
      self.LogInfo("Evacuating instances from node '%s': %s",
991 31b836b8 Thomas Thrainer
                   self.op.node_name,
992 31b836b8 Thomas Thrainer
                   utils.CommaJoin(utils.NiceSort(self.instance_names)))
993 31b836b8 Thomas Thrainer
    else:
994 31b836b8 Thomas Thrainer
      self.LogInfo("No instances to evacuate from node '%s'",
995 31b836b8 Thomas Thrainer
                   self.op.node_name)
996 31b836b8 Thomas Thrainer
997 31b836b8 Thomas Thrainer
    if self.op.remote_node is not None:
998 31b836b8 Thomas Thrainer
      for i in self.instances:
999 1c3231aa Thomas Thrainer
        if i.primary_node == self.op.remote_node_uuid:
1000 31b836b8 Thomas Thrainer
          raise errors.OpPrereqError("Node %s is the primary node of"
1001 31b836b8 Thomas Thrainer
                                     " instance %s, cannot use it as"
1002 31b836b8 Thomas Thrainer
                                     " secondary" %
1003 31b836b8 Thomas Thrainer
                                     (self.op.remote_node, i.name),
1004 31b836b8 Thomas Thrainer
                                     errors.ECODE_INVAL)
1005 31b836b8 Thomas Thrainer
1006 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1007 31b836b8 Thomas Thrainer
    assert (self.op.iallocator is not None) ^ (self.op.remote_node is not None)
1008 31b836b8 Thomas Thrainer
1009 31b836b8 Thomas Thrainer
    if not self.instance_names:
1010 31b836b8 Thomas Thrainer
      # No instances to evacuate
1011 31b836b8 Thomas Thrainer
      jobs = []
1012 31b836b8 Thomas Thrainer
1013 31b836b8 Thomas Thrainer
    elif self.op.iallocator is not None:
1014 31b836b8 Thomas Thrainer
      # TODO: Implement relocation to other group
1015 d4d424fb Jose A. Lopes
      req = iallocator.IAReqNodeEvac(evac_mode=self.op.mode,
1016 31b836b8 Thomas Thrainer
                                     instances=list(self.instance_names))
1017 31b836b8 Thomas Thrainer
      ial = iallocator.IAllocator(self.cfg, self.rpc, req)
1018 31b836b8 Thomas Thrainer
1019 31b836b8 Thomas Thrainer
      ial.Run(self.op.iallocator)
1020 31b836b8 Thomas Thrainer
1021 31b836b8 Thomas Thrainer
      if not ial.success:
1022 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Can't compute node evacuation using"
1023 31b836b8 Thomas Thrainer
                                   " iallocator '%s': %s" %
1024 31b836b8 Thomas Thrainer
                                   (self.op.iallocator, ial.info),
1025 31b836b8 Thomas Thrainer
                                   errors.ECODE_NORES)
1026 31b836b8 Thomas Thrainer
1027 5eacbcae Thomas Thrainer
      jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, True)
1028 31b836b8 Thomas Thrainer
1029 31b836b8 Thomas Thrainer
    elif self.op.remote_node is not None:
1030 31b836b8 Thomas Thrainer
      assert self.op.mode == constants.NODE_EVAC_SEC
1031 31b836b8 Thomas Thrainer
      jobs = [
1032 31b836b8 Thomas Thrainer
        [opcodes.OpInstanceReplaceDisks(instance_name=instance_name,
1033 31b836b8 Thomas Thrainer
                                        remote_node=self.op.remote_node,
1034 31b836b8 Thomas Thrainer
                                        disks=[],
1035 31b836b8 Thomas Thrainer
                                        mode=constants.REPLACE_DISK_CHG,
1036 31b836b8 Thomas Thrainer
                                        early_release=self.op.early_release)]
1037 31b836b8 Thomas Thrainer
        for instance_name in self.instance_names]
1038 31b836b8 Thomas Thrainer
1039 31b836b8 Thomas Thrainer
    else:
1040 31b836b8 Thomas Thrainer
      raise errors.ProgrammerError("No iallocator or remote node")
1041 31b836b8 Thomas Thrainer
1042 31b836b8 Thomas Thrainer
    return ResultWithJobs(jobs)
1043 31b836b8 Thomas Thrainer
1044 31b836b8 Thomas Thrainer
1045 31b836b8 Thomas Thrainer
class LUNodeMigrate(LogicalUnit):
1046 31b836b8 Thomas Thrainer
  """Migrate all instances from a node.
1047 31b836b8 Thomas Thrainer

1048 31b836b8 Thomas Thrainer
  """
1049 31b836b8 Thomas Thrainer
  HPATH = "node-migrate"
1050 31b836b8 Thomas Thrainer
  HTYPE = constants.HTYPE_NODE
1051 31b836b8 Thomas Thrainer
  REQ_BGL = False
1052 31b836b8 Thomas Thrainer
1053 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1054 31b836b8 Thomas Thrainer
    pass
1055 31b836b8 Thomas Thrainer
1056 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1057 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
1058 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
1059 31b836b8 Thomas Thrainer
1060 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1061 31b836b8 Thomas Thrainer
    self.needed_locks = {
1062 1c3231aa Thomas Thrainer
      locking.LEVEL_NODE: [self.op.node_uuid],
1063 31b836b8 Thomas Thrainer
      }
1064 31b836b8 Thomas Thrainer
1065 31b836b8 Thomas Thrainer
  def BuildHooksEnv(self):
1066 31b836b8 Thomas Thrainer
    """Build hooks env.
1067 31b836b8 Thomas Thrainer

1068 31b836b8 Thomas Thrainer
    This runs on the master, the primary and all the secondaries.
1069 31b836b8 Thomas Thrainer

1070 31b836b8 Thomas Thrainer
    """
1071 31b836b8 Thomas Thrainer
    return {
1072 31b836b8 Thomas Thrainer
      "NODE_NAME": self.op.node_name,
1073 31b836b8 Thomas Thrainer
      "ALLOW_RUNTIME_CHANGES": self.op.allow_runtime_changes,
1074 31b836b8 Thomas Thrainer
      }
1075 31b836b8 Thomas Thrainer
1076 31b836b8 Thomas Thrainer
  def BuildHooksNodes(self):
1077 31b836b8 Thomas Thrainer
    """Build hooks nodes.
1078 31b836b8 Thomas Thrainer

1079 31b836b8 Thomas Thrainer
    """
1080 31b836b8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()]
1081 31b836b8 Thomas Thrainer
    return (nl, nl)
1082 31b836b8 Thomas Thrainer
1083 31b836b8 Thomas Thrainer
  def CheckPrereq(self):
1084 31b836b8 Thomas Thrainer
    pass
1085 31b836b8 Thomas Thrainer
1086 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1087 31b836b8 Thomas Thrainer
    # Prepare jobs for migration instances
1088 31b836b8 Thomas Thrainer
    jobs = [
1089 d0d7d7cf Thomas Thrainer
      [opcodes.OpInstanceMigrate(
1090 d0d7d7cf Thomas Thrainer
        instance_name=inst.name,
1091 d0d7d7cf Thomas Thrainer
        mode=self.op.mode,
1092 d0d7d7cf Thomas Thrainer
        live=self.op.live,
1093 d0d7d7cf Thomas Thrainer
        iallocator=self.op.iallocator,
1094 d0d7d7cf Thomas Thrainer
        target_node=self.op.target_node,
1095 d0d7d7cf Thomas Thrainer
        allow_runtime_changes=self.op.allow_runtime_changes,
1096 d0d7d7cf Thomas Thrainer
        ignore_ipolicy=self.op.ignore_ipolicy)]
1097 1c3231aa Thomas Thrainer
      for inst in _GetNodePrimaryInstances(self.cfg, self.op.node_uuid)]
1098 31b836b8 Thomas Thrainer
1099 31b836b8 Thomas Thrainer
    # TODO: Run iallocator in this opcode and pass correct placement options to
1100 31b836b8 Thomas Thrainer
    # OpInstanceMigrate. Since other jobs can modify the cluster between
1101 31b836b8 Thomas Thrainer
    # running the iallocator and the actual migration, a good consistency model
1102 31b836b8 Thomas Thrainer
    # will have to be found.
1103 31b836b8 Thomas Thrainer
1104 31b836b8 Thomas Thrainer
    assert (frozenset(self.owned_locks(locking.LEVEL_NODE)) ==
1105 5e568fee Thomas Thrainer
            frozenset([self.op.node_uuid]))
1106 31b836b8 Thomas Thrainer
1107 31b836b8 Thomas Thrainer
    return ResultWithJobs(jobs)
1108 31b836b8 Thomas Thrainer
1109 31b836b8 Thomas Thrainer
1110 31b836b8 Thomas Thrainer
def _GetStorageTypeArgs(cfg, storage_type):
1111 31b836b8 Thomas Thrainer
  """Returns the arguments for a storage type.
1112 31b836b8 Thomas Thrainer

1113 31b836b8 Thomas Thrainer
  """
1114 31b836b8 Thomas Thrainer
  # Special case for file storage
1115 31b836b8 Thomas Thrainer
1116 5a904197 Santi Raffa
  if storage_type == constants.ST_FILE:
1117 5a904197 Santi Raffa
    return [[cfg.GetFileStorageDir()]]
1118 5a904197 Santi Raffa
  elif storage_type == constants.ST_SHARED_FILE:
1119 5a904197 Santi Raffa
    dts = cfg.GetClusterInfo().enabled_disk_templates
1120 5a904197 Santi Raffa
    paths = []
1121 5a904197 Santi Raffa
    if constants.DT_SHARED_FILE in dts:
1122 5a904197 Santi Raffa
      paths.append(cfg.GetSharedFileStorageDir())
1123 5a904197 Santi Raffa
    if constants.DT_GLUSTER in dts:
1124 5a904197 Santi Raffa
      paths.append(cfg.GetGlusterStorageDir())
1125 5a904197 Santi Raffa
    return [paths]
1126 5a904197 Santi Raffa
  else:
1127 5a904197 Santi Raffa
    return []
1128 31b836b8 Thomas Thrainer
1129 31b836b8 Thomas Thrainer
1130 31b836b8 Thomas Thrainer
class LUNodeModifyStorage(NoHooksLU):
1131 31b836b8 Thomas Thrainer
  """Logical unit for modifying a storage volume on a node.
1132 31b836b8 Thomas Thrainer

1133 31b836b8 Thomas Thrainer
  """
1134 31b836b8 Thomas Thrainer
  REQ_BGL = False
1135 31b836b8 Thomas Thrainer
1136 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1137 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
1138 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
1139 31b836b8 Thomas Thrainer
1140 31b836b8 Thomas Thrainer
    storage_type = self.op.storage_type
1141 31b836b8 Thomas Thrainer
1142 31b836b8 Thomas Thrainer
    try:
1143 31b836b8 Thomas Thrainer
      modifiable = constants.MODIFIABLE_STORAGE_FIELDS[storage_type]
1144 31b836b8 Thomas Thrainer
    except KeyError:
1145 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Storage units of type '%s' can not be"
1146 31b836b8 Thomas Thrainer
                                 " modified" % storage_type,
1147 31b836b8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1148 31b836b8 Thomas Thrainer
1149 31b836b8 Thomas Thrainer
    diff = set(self.op.changes.keys()) - modifiable
1150 31b836b8 Thomas Thrainer
    if diff:
1151 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("The following fields can not be modified for"
1152 31b836b8 Thomas Thrainer
                                 " storage units of type '%s': %r" %
1153 31b836b8 Thomas Thrainer
                                 (storage_type, list(diff)),
1154 31b836b8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1155 31b836b8 Thomas Thrainer
1156 9d276e93 Helga Velroyen
  def CheckPrereq(self):
1157 9d276e93 Helga Velroyen
    """Check prerequisites.
1158 9d276e93 Helga Velroyen

1159 9d276e93 Helga Velroyen
    """
1160 9d276e93 Helga Velroyen
    CheckStorageTypeEnabled(self.cfg.GetClusterInfo(), self.op.storage_type)
1161 9d276e93 Helga Velroyen
1162 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1163 31b836b8 Thomas Thrainer
    self.needed_locks = {
1164 1c3231aa Thomas Thrainer
      locking.LEVEL_NODE: self.op.node_uuid,
1165 31b836b8 Thomas Thrainer
      }
1166 31b836b8 Thomas Thrainer
1167 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1168 31b836b8 Thomas Thrainer
    """Computes the list of nodes and their attributes.
1169 31b836b8 Thomas Thrainer

1170 31b836b8 Thomas Thrainer
    """
1171 31b836b8 Thomas Thrainer
    st_args = _GetStorageTypeArgs(self.cfg, self.op.storage_type)
1172 1c3231aa Thomas Thrainer
    result = self.rpc.call_storage_modify(self.op.node_uuid,
1173 31b836b8 Thomas Thrainer
                                          self.op.storage_type, st_args,
1174 31b836b8 Thomas Thrainer
                                          self.op.name, self.op.changes)
1175 31b836b8 Thomas Thrainer
    result.Raise("Failed to modify storage unit '%s' on %s" %
1176 31b836b8 Thomas Thrainer
                 (self.op.name, self.op.node_name))
1177 31b836b8 Thomas Thrainer
1178 31b836b8 Thomas Thrainer
1179 6afb9fb4 Jose A. Lopes
def _CheckOutputFields(fields, selected):
1180 6afb9fb4 Jose A. Lopes
  """Checks whether all selected fields are valid according to fields.
1181 31b836b8 Thomas Thrainer

1182 6afb9fb4 Jose A. Lopes
  @type fields: L{utils.FieldSet}
1183 6afb9fb4 Jose A. Lopes
  @param fields: fields set
1184 6afb9fb4 Jose A. Lopes
  @type selected: L{utils.FieldSet}
1185 6afb9fb4 Jose A. Lopes
  @param selected: fields set
1186 31b836b8 Thomas Thrainer

1187 31b836b8 Thomas Thrainer
  """
1188 6afb9fb4 Jose A. Lopes
  delta = fields.NonMatching(selected)
1189 31b836b8 Thomas Thrainer
  if delta:
1190 31b836b8 Thomas Thrainer
    raise errors.OpPrereqError("Unknown output fields selected: %s"
1191 31b836b8 Thomas Thrainer
                               % ",".join(delta), errors.ECODE_INVAL)
1192 31b836b8 Thomas Thrainer
1193 31b836b8 Thomas Thrainer
1194 31b836b8 Thomas Thrainer
class LUNodeQueryvols(NoHooksLU):
1195 31b836b8 Thomas Thrainer
  """Logical unit for getting volumes on node(s).
1196 31b836b8 Thomas Thrainer

1197 31b836b8 Thomas Thrainer
  """
1198 31b836b8 Thomas Thrainer
  REQ_BGL = False
1199 31b836b8 Thomas Thrainer
1200 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1201 b2fbea47 Jose A. Lopes
    _CheckOutputFields(utils.FieldSet(constants.VF_NODE, constants.VF_PHYS,
1202 b2fbea47 Jose A. Lopes
                                      constants.VF_VG, constants.VF_NAME,
1203 b2fbea47 Jose A. Lopes
                                      constants.VF_SIZE, constants.VF_INSTANCE),
1204 6afb9fb4 Jose A. Lopes
                       self.op.output_fields)
1205 31b836b8 Thomas Thrainer
1206 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1207 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1208 31b836b8 Thomas Thrainer
1209 31b836b8 Thomas Thrainer
    if self.op.nodes:
1210 31b836b8 Thomas Thrainer
      self.needed_locks = {
1211 1c3231aa Thomas Thrainer
        locking.LEVEL_NODE: GetWantedNodes(self, self.op.nodes)[0],
1212 31b836b8 Thomas Thrainer
        }
1213 31b836b8 Thomas Thrainer
    else:
1214 31b836b8 Thomas Thrainer
      self.needed_locks = {
1215 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE: locking.ALL_SET,
1216 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1217 31b836b8 Thomas Thrainer
        }
1218 31b836b8 Thomas Thrainer
1219 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1220 31b836b8 Thomas Thrainer
    """Computes the list of nodes and their attributes.
1221 31b836b8 Thomas Thrainer

1222 31b836b8 Thomas Thrainer
    """
1223 1c3231aa Thomas Thrainer
    node_uuids = self.owned_locks(locking.LEVEL_NODE)
1224 1c3231aa Thomas Thrainer
    volumes = self.rpc.call_node_volumes(node_uuids)
1225 31b836b8 Thomas Thrainer
1226 31b836b8 Thomas Thrainer
    ilist = self.cfg.GetAllInstancesInfo()
1227 843094ad Thomas Thrainer
    vol2inst = MapInstanceLvsToNodes(ilist.values())
1228 31b836b8 Thomas Thrainer
1229 31b836b8 Thomas Thrainer
    output = []
1230 1c3231aa Thomas Thrainer
    for node_uuid in node_uuids:
1231 1c3231aa Thomas Thrainer
      nresult = volumes[node_uuid]
1232 31b836b8 Thomas Thrainer
      if nresult.offline:
1233 31b836b8 Thomas Thrainer
        continue
1234 31b836b8 Thomas Thrainer
      msg = nresult.fail_msg
1235 31b836b8 Thomas Thrainer
      if msg:
1236 1c3231aa Thomas Thrainer
        self.LogWarning("Can't compute volume data on node %s: %s",
1237 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
1238 31b836b8 Thomas Thrainer
        continue
1239 31b836b8 Thomas Thrainer
1240 31b836b8 Thomas Thrainer
      node_vols = sorted(nresult.payload,
1241 b2fbea47 Jose A. Lopes
                         key=operator.itemgetter(constants.VF_DEV))
1242 31b836b8 Thomas Thrainer
1243 31b836b8 Thomas Thrainer
      for vol in node_vols:
1244 31b836b8 Thomas Thrainer
        node_output = []
1245 31b836b8 Thomas Thrainer
        for field in self.op.output_fields:
1246 b2fbea47 Jose A. Lopes
          if field == constants.VF_NODE:
1247 1c3231aa Thomas Thrainer
            val = self.cfg.GetNodeName(node_uuid)
1248 b2fbea47 Jose A. Lopes
          elif field == constants.VF_PHYS:
1249 b2fbea47 Jose A. Lopes
            val = vol[constants.VF_DEV]
1250 b2fbea47 Jose A. Lopes
          elif field == constants.VF_VG:
1251 b2fbea47 Jose A. Lopes
            val = vol[constants.VF_VG]
1252 b2fbea47 Jose A. Lopes
          elif field == constants.VF_NAME:
1253 b2fbea47 Jose A. Lopes
            val = vol[constants.VF_NAME]
1254 b2fbea47 Jose A. Lopes
          elif field == constants.VF_SIZE:
1255 b2fbea47 Jose A. Lopes
            val = int(float(vol[constants.VF_SIZE]))
1256 b2fbea47 Jose A. Lopes
          elif field == constants.VF_INSTANCE:
1257 b2fbea47 Jose A. Lopes
            inst = vol2inst.get((node_uuid, vol[constants.VF_VG] + "/" +
1258 b2fbea47 Jose A. Lopes
                                 vol[constants.VF_NAME]), None)
1259 843094ad Thomas Thrainer
            if inst is not None:
1260 843094ad Thomas Thrainer
              val = inst.name
1261 843094ad Thomas Thrainer
            else:
1262 843094ad Thomas Thrainer
              val = "-"
1263 31b836b8 Thomas Thrainer
          else:
1264 31b836b8 Thomas Thrainer
            raise errors.ParameterError(field)
1265 31b836b8 Thomas Thrainer
          node_output.append(str(val))
1266 31b836b8 Thomas Thrainer
1267 31b836b8 Thomas Thrainer
        output.append(node_output)
1268 31b836b8 Thomas Thrainer
1269 31b836b8 Thomas Thrainer
    return output
1270 31b836b8 Thomas Thrainer
1271 31b836b8 Thomas Thrainer
1272 31b836b8 Thomas Thrainer
class LUNodeQueryStorage(NoHooksLU):
1273 31b836b8 Thomas Thrainer
  """Logical unit for getting information on storage units on node(s).
1274 31b836b8 Thomas Thrainer

1275 31b836b8 Thomas Thrainer
  """
1276 31b836b8 Thomas Thrainer
  REQ_BGL = False
1277 31b836b8 Thomas Thrainer
1278 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1279 6afb9fb4 Jose A. Lopes
    _CheckOutputFields(utils.FieldSet(*constants.VALID_STORAGE_FIELDS),
1280 6afb9fb4 Jose A. Lopes
                       self.op.output_fields)
1281 31b836b8 Thomas Thrainer
1282 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1283 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1284 31b836b8 Thomas Thrainer
1285 31b836b8 Thomas Thrainer
    if self.op.nodes:
1286 31b836b8 Thomas Thrainer
      self.needed_locks = {
1287 1c3231aa Thomas Thrainer
        locking.LEVEL_NODE: GetWantedNodes(self, self.op.nodes)[0],
1288 31b836b8 Thomas Thrainer
        }
1289 31b836b8 Thomas Thrainer
    else:
1290 31b836b8 Thomas Thrainer
      self.needed_locks = {
1291 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE: locking.ALL_SET,
1292 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1293 31b836b8 Thomas Thrainer
        }
1294 31b836b8 Thomas Thrainer
1295 4f90370c Helga Velroyen
  def _DetermineStorageType(self):
1296 4f90370c Helga Velroyen
    """Determines the default storage type of the cluster.
1297 4f90370c Helga Velroyen

1298 4f90370c Helga Velroyen
    """
1299 4f90370c Helga Velroyen
    enabled_disk_templates = self.cfg.GetClusterInfo().enabled_disk_templates
1300 4f90370c Helga Velroyen
    default_storage_type = \
1301 4f90370c Helga Velroyen
        constants.MAP_DISK_TEMPLATE_STORAGE_TYPE[enabled_disk_templates[0]]
1302 4f90370c Helga Velroyen
    return default_storage_type
1303 4f90370c Helga Velroyen
1304 9d276e93 Helga Velroyen
  def CheckPrereq(self):
1305 9d276e93 Helga Velroyen
    """Check prerequisites.
1306 9d276e93 Helga Velroyen

1307 9d276e93 Helga Velroyen
    """
1308 4f90370c Helga Velroyen
    if self.op.storage_type:
1309 4f90370c Helga Velroyen
      CheckStorageTypeEnabled(self.cfg.GetClusterInfo(), self.op.storage_type)
1310 4f90370c Helga Velroyen
      self.storage_type = self.op.storage_type
1311 4f90370c Helga Velroyen
    else:
1312 4f90370c Helga Velroyen
      self.storage_type = self._DetermineStorageType()
1313 5a904197 Santi Raffa
      supported_storage_types = constants.STS_REPORT_NODE_STORAGE
1314 5a904197 Santi Raffa
      if self.storage_type not in supported_storage_types:
1315 4f90370c Helga Velroyen
        raise errors.OpPrereqError(
1316 4f90370c Helga Velroyen
            "Storage reporting for storage type '%s' is not supported. Please"
1317 4f90370c Helga Velroyen
            " use the --storage-type option to specify one of the supported"
1318 4f90370c Helga Velroyen
            " storage types (%s) or set the default disk template to one that"
1319 4f90370c Helga Velroyen
            " supports storage reporting." %
1320 5a904197 Santi Raffa
            (self.storage_type, utils.CommaJoin(supported_storage_types)))
1321 9d276e93 Helga Velroyen
1322 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1323 31b836b8 Thomas Thrainer
    """Computes the list of nodes and their attributes.
1324 31b836b8 Thomas Thrainer

1325 31b836b8 Thomas Thrainer
    """
1326 4f90370c Helga Velroyen
    if self.op.storage_type:
1327 4f90370c Helga Velroyen
      self.storage_type = self.op.storage_type
1328 4f90370c Helga Velroyen
    else:
1329 4f90370c Helga Velroyen
      self.storage_type = self._DetermineStorageType()
1330 4f90370c Helga Velroyen
1331 1c3231aa Thomas Thrainer
    self.node_uuids = self.owned_locks(locking.LEVEL_NODE)
1332 31b836b8 Thomas Thrainer
1333 31b836b8 Thomas Thrainer
    # Always get name to sort by
1334 31b836b8 Thomas Thrainer
    if constants.SF_NAME in self.op.output_fields:
1335 31b836b8 Thomas Thrainer
      fields = self.op.output_fields[:]
1336 31b836b8 Thomas Thrainer
    else:
1337 31b836b8 Thomas Thrainer
      fields = [constants.SF_NAME] + self.op.output_fields
1338 31b836b8 Thomas Thrainer
1339 31b836b8 Thomas Thrainer
    # Never ask for node or type as it's only known to the LU
1340 31b836b8 Thomas Thrainer
    for extra in [constants.SF_NODE, constants.SF_TYPE]:
1341 31b836b8 Thomas Thrainer
      while extra in fields:
1342 31b836b8 Thomas Thrainer
        fields.remove(extra)
1343 31b836b8 Thomas Thrainer
1344 31b836b8 Thomas Thrainer
    field_idx = dict([(name, idx) for (idx, name) in enumerate(fields)])
1345 31b836b8 Thomas Thrainer
    name_idx = field_idx[constants.SF_NAME]
1346 31b836b8 Thomas Thrainer
1347 4f90370c Helga Velroyen
    st_args = _GetStorageTypeArgs(self.cfg, self.storage_type)
1348 1c3231aa Thomas Thrainer
    data = self.rpc.call_storage_list(self.node_uuids,
1349 4f90370c Helga Velroyen
                                      self.storage_type, st_args,
1350 31b836b8 Thomas Thrainer
                                      self.op.name, fields)
1351 31b836b8 Thomas Thrainer
1352 31b836b8 Thomas Thrainer
    result = []
1353 31b836b8 Thomas Thrainer
1354 1c3231aa Thomas Thrainer
    for node_uuid in utils.NiceSort(self.node_uuids):
1355 1c3231aa Thomas Thrainer
      node_name = self.cfg.GetNodeName(node_uuid)
1356 1c3231aa Thomas Thrainer
      nresult = data[node_uuid]
1357 31b836b8 Thomas Thrainer
      if nresult.offline:
1358 31b836b8 Thomas Thrainer
        continue
1359 31b836b8 Thomas Thrainer
1360 31b836b8 Thomas Thrainer
      msg = nresult.fail_msg
1361 31b836b8 Thomas Thrainer
      if msg:
1362 1c3231aa Thomas Thrainer
        self.LogWarning("Can't get storage data from node %s: %s",
1363 1c3231aa Thomas Thrainer
                        node_name, msg)
1364 31b836b8 Thomas Thrainer
        continue
1365 31b836b8 Thomas Thrainer
1366 31b836b8 Thomas Thrainer
      rows = dict([(row[name_idx], row) for row in nresult.payload])
1367 31b836b8 Thomas Thrainer
1368 31b836b8 Thomas Thrainer
      for name in utils.NiceSort(rows.keys()):
1369 31b836b8 Thomas Thrainer
        row = rows[name]
1370 31b836b8 Thomas Thrainer
1371 31b836b8 Thomas Thrainer
        out = []
1372 31b836b8 Thomas Thrainer
1373 31b836b8 Thomas Thrainer
        for field in self.op.output_fields:
1374 31b836b8 Thomas Thrainer
          if field == constants.SF_NODE:
1375 1c3231aa Thomas Thrainer
            val = node_name
1376 31b836b8 Thomas Thrainer
          elif field == constants.SF_TYPE:
1377 4f90370c Helga Velroyen
            val = self.storage_type
1378 31b836b8 Thomas Thrainer
          elif field in field_idx:
1379 31b836b8 Thomas Thrainer
            val = row[field_idx[field]]
1380 31b836b8 Thomas Thrainer
          else:
1381 31b836b8 Thomas Thrainer
            raise errors.ParameterError(field)
1382 31b836b8 Thomas Thrainer
1383 31b836b8 Thomas Thrainer
          out.append(val)
1384 31b836b8 Thomas Thrainer
1385 31b836b8 Thomas Thrainer
        result.append(out)
1386 31b836b8 Thomas Thrainer
1387 31b836b8 Thomas Thrainer
    return result
1388 31b836b8 Thomas Thrainer
1389 31b836b8 Thomas Thrainer
1390 31b836b8 Thomas Thrainer
class LUNodeRemove(LogicalUnit):
1391 31b836b8 Thomas Thrainer
  """Logical unit for removing a node.
1392 31b836b8 Thomas Thrainer

1393 31b836b8 Thomas Thrainer
  """
1394 31b836b8 Thomas Thrainer
  HPATH = "node-remove"
1395 31b836b8 Thomas Thrainer
  HTYPE = constants.HTYPE_NODE
1396 31b836b8 Thomas Thrainer
1397 31b836b8 Thomas Thrainer
  def BuildHooksEnv(self):
1398 31b836b8 Thomas Thrainer
    """Build hooks env.
1399 31b836b8 Thomas Thrainer

1400 31b836b8 Thomas Thrainer
    """
1401 31b836b8 Thomas Thrainer
    return {
1402 31b836b8 Thomas Thrainer
      "OP_TARGET": self.op.node_name,
1403 31b836b8 Thomas Thrainer
      "NODE_NAME": self.op.node_name,
1404 31b836b8 Thomas Thrainer
      }
1405 31b836b8 Thomas Thrainer
1406 31b836b8 Thomas Thrainer
  def BuildHooksNodes(self):
1407 31b836b8 Thomas Thrainer
    """Build hooks nodes.
1408 31b836b8 Thomas Thrainer

1409 31b836b8 Thomas Thrainer
    This doesn't run on the target node in the pre phase as a failed
1410 31b836b8 Thomas Thrainer
    node would then be impossible to remove.
1411 31b836b8 Thomas Thrainer

1412 31b836b8 Thomas Thrainer
    """
1413 31b836b8 Thomas Thrainer
    all_nodes = self.cfg.GetNodeList()
1414 31b836b8 Thomas Thrainer
    try:
1415 1c3231aa Thomas Thrainer
      all_nodes.remove(self.op.node_uuid)
1416 31b836b8 Thomas Thrainer
    except ValueError:
1417 31b836b8 Thomas Thrainer
      pass
1418 31b836b8 Thomas Thrainer
    return (all_nodes, all_nodes)
1419 31b836b8 Thomas Thrainer
1420 31b836b8 Thomas Thrainer
  def CheckPrereq(self):
1421 31b836b8 Thomas Thrainer
    """Check prerequisites.
1422 31b836b8 Thomas Thrainer

1423 31b836b8 Thomas Thrainer
    This checks:
1424 31b836b8 Thomas Thrainer
     - the node exists in the configuration
1425 31b836b8 Thomas Thrainer
     - it does not have primary or secondary instances
1426 31b836b8 Thomas Thrainer
     - it's not the master
1427 31b836b8 Thomas Thrainer

1428 31b836b8 Thomas Thrainer
    Any errors are signaled by raising errors.OpPrereqError.
1429 31b836b8 Thomas Thrainer

1430 31b836b8 Thomas Thrainer
    """
1431 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
1432 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
1433 1c3231aa Thomas Thrainer
    node = self.cfg.GetNodeInfo(self.op.node_uuid)
1434 31b836b8 Thomas Thrainer
    assert node is not None
1435 31b836b8 Thomas Thrainer
1436 31b836b8 Thomas Thrainer
    masternode = self.cfg.GetMasterNode()
1437 1c3231aa Thomas Thrainer
    if node.uuid == masternode:
1438 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Node is the master node, failover to another"
1439 31b836b8 Thomas Thrainer
                                 " node is required", errors.ECODE_INVAL)
1440 31b836b8 Thomas Thrainer
1441 da4a52a3 Thomas Thrainer
    for _, instance in self.cfg.GetAllInstancesInfo().items():
1442 1c3231aa Thomas Thrainer
      if node.uuid in instance.all_nodes:
1443 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s is still running on the node,"
1444 da4a52a3 Thomas Thrainer
                                   " please remove first" % instance.name,
1445 31b836b8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1446 31b836b8 Thomas Thrainer
    self.op.node_name = node.name
1447 31b836b8 Thomas Thrainer
    self.node = node
1448 31b836b8 Thomas Thrainer
1449 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1450 31b836b8 Thomas Thrainer
    """Removes the node from the cluster.
1451 31b836b8 Thomas Thrainer

1452 31b836b8 Thomas Thrainer
    """
1453 31b836b8 Thomas Thrainer
    logging.info("Stopping the node daemon and removing configs from node %s",
1454 d0d7d7cf Thomas Thrainer
                 self.node.name)
1455 31b836b8 Thomas Thrainer
1456 31b836b8 Thomas Thrainer
    modify_ssh_setup = self.cfg.GetClusterInfo().modify_ssh_setup
1457 31b836b8 Thomas Thrainer
1458 31b836b8 Thomas Thrainer
    assert locking.BGL in self.owned_locks(locking.LEVEL_CLUSTER), \
1459 31b836b8 Thomas Thrainer
      "Not owning BGL"
1460 31b836b8 Thomas Thrainer
1461 31b836b8 Thomas Thrainer
    # Promote nodes to master candidate as needed
1462 d0d7d7cf Thomas Thrainer
    AdjustCandidatePool(self, exceptions=[self.node.uuid])
1463 d0d7d7cf Thomas Thrainer
    self.context.RemoveNode(self.node)
1464 31b836b8 Thomas Thrainer
1465 31b836b8 Thomas Thrainer
    # Run post hooks on the node before it's removed
1466 d0d7d7cf Thomas Thrainer
    RunPostHook(self, self.node.name)
1467 31b836b8 Thomas Thrainer
1468 1c3231aa Thomas Thrainer
    # we have to call this by name rather than by UUID, as the node is no longer
1469 1c3231aa Thomas Thrainer
    # in the config
1470 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_node_leave_cluster(self.node.name, modify_ssh_setup)
1471 31b836b8 Thomas Thrainer
    msg = result.fail_msg
1472 31b836b8 Thomas Thrainer
    if msg:
1473 31b836b8 Thomas Thrainer
      self.LogWarning("Errors encountered on the remote node while leaving"
1474 31b836b8 Thomas Thrainer
                      " the cluster: %s", msg)
1475 31b836b8 Thomas Thrainer
1476 31b836b8 Thomas Thrainer
    # Remove node from our /etc/hosts
1477 31b836b8 Thomas Thrainer
    if self.cfg.GetClusterInfo().modify_etc_hosts:
1478 1c3231aa Thomas Thrainer
      master_node_uuid = self.cfg.GetMasterNode()
1479 1c3231aa Thomas Thrainer
      result = self.rpc.call_etc_hosts_modify(master_node_uuid,
1480 31b836b8 Thomas Thrainer
                                              constants.ETC_HOSTS_REMOVE,
1481 d0d7d7cf Thomas Thrainer
                                              self.node.name, None)
1482 31b836b8 Thomas Thrainer
      result.Raise("Can't update hosts file with new host data")
1483 5eacbcae Thomas Thrainer
      RedistributeAncillaryFiles(self)
1484 31b836b8 Thomas Thrainer
1485 31b836b8 Thomas Thrainer
1486 31b836b8 Thomas Thrainer
class LURepairNodeStorage(NoHooksLU):
1487 31b836b8 Thomas Thrainer
  """Repairs the volume group on a node.
1488 31b836b8 Thomas Thrainer

1489 31b836b8 Thomas Thrainer
  """
1490 31b836b8 Thomas Thrainer
  REQ_BGL = False
1491 31b836b8 Thomas Thrainer
1492 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1493 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
1494 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
1495 31b836b8 Thomas Thrainer
1496 31b836b8 Thomas Thrainer
    storage_type = self.op.storage_type
1497 31b836b8 Thomas Thrainer
1498 31b836b8 Thomas Thrainer
    if (constants.SO_FIX_CONSISTENCY not in
1499 31b836b8 Thomas Thrainer
        constants.VALID_STORAGE_OPERATIONS.get(storage_type, [])):
1500 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Storage units of type '%s' can not be"
1501 31b836b8 Thomas Thrainer
                                 " repaired" % storage_type,
1502 31b836b8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1503 31b836b8 Thomas Thrainer
1504 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1505 31b836b8 Thomas Thrainer
    self.needed_locks = {
1506 1c3231aa Thomas Thrainer
      locking.LEVEL_NODE: [self.op.node_uuid],
1507 31b836b8 Thomas Thrainer
      }
1508 31b836b8 Thomas Thrainer
1509 1c3231aa Thomas Thrainer
  def _CheckFaultyDisks(self, instance, node_uuid):
1510 31b836b8 Thomas Thrainer
    """Ensure faulty disks abort the opcode or at least warn."""
1511 31b836b8 Thomas Thrainer
    try:
1512 5eacbcae Thomas Thrainer
      if FindFaultyInstanceDisks(self.cfg, self.rpc, instance,
1513 1c3231aa Thomas Thrainer
                                 node_uuid, True):
1514 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Instance '%s' has faulty disks on"
1515 1c3231aa Thomas Thrainer
                                   " node '%s'" %
1516 1c3231aa Thomas Thrainer
                                   (instance.name,
1517 1c3231aa Thomas Thrainer
                                    self.cfg.GetNodeName(node_uuid)),
1518 31b836b8 Thomas Thrainer
                                   errors.ECODE_STATE)
1519 31b836b8 Thomas Thrainer
    except errors.OpPrereqError, err:
1520 31b836b8 Thomas Thrainer
      if self.op.ignore_consistency:
1521 31b836b8 Thomas Thrainer
        self.LogWarning(str(err.args[0]))
1522 31b836b8 Thomas Thrainer
      else:
1523 31b836b8 Thomas Thrainer
        raise
1524 31b836b8 Thomas Thrainer
1525 31b836b8 Thomas Thrainer
  def CheckPrereq(self):
1526 31b836b8 Thomas Thrainer
    """Check prerequisites.
1527 31b836b8 Thomas Thrainer

1528 31b836b8 Thomas Thrainer
    """
1529 9d276e93 Helga Velroyen
    CheckStorageTypeEnabled(self.cfg.GetClusterInfo(), self.op.storage_type)
1530 9d276e93 Helga Velroyen
1531 31b836b8 Thomas Thrainer
    # Check whether any instance on this node has faulty disks
1532 1c3231aa Thomas Thrainer
    for inst in _GetNodeInstances(self.cfg, self.op.node_uuid):
1533 1d4a4b26 Thomas Thrainer
      if not inst.disks_active:
1534 31b836b8 Thomas Thrainer
        continue
1535 31b836b8 Thomas Thrainer
      check_nodes = set(inst.all_nodes)
1536 1c3231aa Thomas Thrainer
      check_nodes.discard(self.op.node_uuid)
1537 1c3231aa Thomas Thrainer
      for inst_node_uuid in check_nodes:
1538 1c3231aa Thomas Thrainer
        self._CheckFaultyDisks(inst, inst_node_uuid)
1539 31b836b8 Thomas Thrainer
1540 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1541 31b836b8 Thomas Thrainer
    feedback_fn("Repairing storage unit '%s' on %s ..." %
1542 31b836b8 Thomas Thrainer
                (self.op.name, self.op.node_name))
1543 31b836b8 Thomas Thrainer
1544 31b836b8 Thomas Thrainer
    st_args = _GetStorageTypeArgs(self.cfg, self.op.storage_type)
1545 1c3231aa Thomas Thrainer
    result = self.rpc.call_storage_execute(self.op.node_uuid,
1546 31b836b8 Thomas Thrainer
                                           self.op.storage_type, st_args,
1547 31b836b8 Thomas Thrainer
                                           self.op.name,
1548 31b836b8 Thomas Thrainer
                                           constants.SO_FIX_CONSISTENCY)
1549 31b836b8 Thomas Thrainer
    result.Raise("Failed to repair storage unit '%s' on %s" %
1550 31b836b8 Thomas Thrainer
                 (self.op.name, self.op.node_name))