Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (58.3 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 28756f80 Helga Velroyen
  FindFaultyInstanceDisks, CheckStorageTypeEnabled, CreateNewClientCert, \
46 28756f80 Helga Velroyen
  AddNodeCertToCandidateCerts, RemoveNodeCertFromCandidateCerts
47 31b836b8 Thomas Thrainer
48 31b836b8 Thomas Thrainer
49 31b836b8 Thomas Thrainer
def _DecideSelfPromotion(lu, exceptions=None):
50 31b836b8 Thomas Thrainer
  """Decide whether I should promote myself as a master candidate.
51 31b836b8 Thomas Thrainer

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

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

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

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

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

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

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

147 31b836b8 Thomas Thrainer
    This checks:
148 31b836b8 Thomas Thrainer
     - the new node is not already in the config
149 31b836b8 Thomas Thrainer
     - it is resolvable
150 31b836b8 Thomas Thrainer
     - its parameters (single/dual homed) matches the cluster
151 31b836b8 Thomas Thrainer

152 31b836b8 Thomas Thrainer
    Any errors are signaled by raising errors.OpPrereqError.
153 31b836b8 Thomas Thrainer

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

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

438 31b836b8 Thomas Thrainer
  @cvar _F2R: a dictionary from tuples of flags (mc, drained, offline)
439 31b836b8 Thomas Thrainer
      to the node role (as _ROLE_*)
440 31b836b8 Thomas Thrainer
  @cvar _R2F: a dictionary from node role to tuples of flags
441 31b836b8 Thomas Thrainer
  @cvar _FLAGS: a list of attribute names corresponding to the flags
442 31b836b8 Thomas Thrainer

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

490 31b836b8 Thomas Thrainer
    """
491 31b836b8 Thomas Thrainer
    return (instance.disk_template in constants.DTS_INT_MIRROR and
492 1c3231aa Thomas Thrainer
            self.op.node_uuid in instance.all_nodes)
493 31b836b8 Thomas Thrainer
494 31b836b8 Thomas Thrainer
  def ExpandNames(self):
495 31b836b8 Thomas Thrainer
    if self.lock_all:
496 31b836b8 Thomas Thrainer
      self.needed_locks = {
497 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE: locking.ALL_SET,
498 31b836b8 Thomas Thrainer
499 31b836b8 Thomas Thrainer
        # Block allocations when all nodes are locked
500 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
501 31b836b8 Thomas Thrainer
        }
502 31b836b8 Thomas Thrainer
    else:
503 31b836b8 Thomas Thrainer
      self.needed_locks = {
504 1c3231aa Thomas Thrainer
        locking.LEVEL_NODE: self.op.node_uuid,
505 31b836b8 Thomas Thrainer
        }
506 31b836b8 Thomas Thrainer
507 31b836b8 Thomas Thrainer
    # Since modifying a node can have severe effects on currently running
508 31b836b8 Thomas Thrainer
    # operations the resource lock is at least acquired in shared mode
509 31b836b8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = \
510 31b836b8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE]
511 31b836b8 Thomas Thrainer
512 31b836b8 Thomas Thrainer
    # Get all locks except nodes in shared mode; they are not used for anything
513 31b836b8 Thomas Thrainer
    # but read-only access
514 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
515 31b836b8 Thomas Thrainer
    self.share_locks[locking.LEVEL_NODE] = 0
516 31b836b8 Thomas Thrainer
    self.share_locks[locking.LEVEL_NODE_RES] = 0
517 31b836b8 Thomas Thrainer
    self.share_locks[locking.LEVEL_NODE_ALLOC] = 0
518 31b836b8 Thomas Thrainer
519 31b836b8 Thomas Thrainer
    if self.lock_instances:
520 31b836b8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_INSTANCE] = \
521 da4a52a3 Thomas Thrainer
        self.cfg.GetInstanceNames(
522 da4a52a3 Thomas Thrainer
          self.cfg.GetInstancesInfoByFilter(self._InstanceFilter).keys())
523 31b836b8 Thomas Thrainer
524 31b836b8 Thomas Thrainer
  def BuildHooksEnv(self):
525 31b836b8 Thomas Thrainer
    """Build hooks env.
526 31b836b8 Thomas Thrainer

527 31b836b8 Thomas Thrainer
    This runs on the master node.
528 31b836b8 Thomas Thrainer

529 31b836b8 Thomas Thrainer
    """
530 31b836b8 Thomas Thrainer
    return {
531 31b836b8 Thomas Thrainer
      "OP_TARGET": self.op.node_name,
532 31b836b8 Thomas Thrainer
      "MASTER_CANDIDATE": str(self.op.master_candidate),
533 31b836b8 Thomas Thrainer
      "OFFLINE": str(self.op.offline),
534 31b836b8 Thomas Thrainer
      "DRAINED": str(self.op.drained),
535 31b836b8 Thomas Thrainer
      "MASTER_CAPABLE": str(self.op.master_capable),
536 31b836b8 Thomas Thrainer
      "VM_CAPABLE": str(self.op.vm_capable),
537 31b836b8 Thomas Thrainer
      }
538 31b836b8 Thomas Thrainer
539 31b836b8 Thomas Thrainer
  def BuildHooksNodes(self):
540 31b836b8 Thomas Thrainer
    """Build hooks nodes.
541 31b836b8 Thomas Thrainer

542 31b836b8 Thomas Thrainer
    """
543 1c3231aa Thomas Thrainer
    nl = [self.cfg.GetMasterNode(), self.op.node_uuid]
544 31b836b8 Thomas Thrainer
    return (nl, nl)
545 31b836b8 Thomas Thrainer
546 31b836b8 Thomas Thrainer
  def CheckPrereq(self):
547 31b836b8 Thomas Thrainer
    """Check prerequisites.
548 31b836b8 Thomas Thrainer

549 31b836b8 Thomas Thrainer
    This only checks the instance list against the existing names.
550 31b836b8 Thomas Thrainer

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

750 31b836b8 Thomas Thrainer
    """
751 1c3231aa Thomas Thrainer
    node = self.cfg.GetNodeInfo(self.op.node_uuid)
752 31b836b8 Thomas Thrainer
    result = []
753 31b836b8 Thomas Thrainer
754 31b836b8 Thomas Thrainer
    if self.op.ndparams:
755 31b836b8 Thomas Thrainer
      node.ndparams = self.new_ndparams
756 31b836b8 Thomas Thrainer
757 31b836b8 Thomas Thrainer
    if self.op.powered is not None:
758 31b836b8 Thomas Thrainer
      node.powered = self.op.powered
759 31b836b8 Thomas Thrainer
760 31b836b8 Thomas Thrainer
    if self.op.hv_state:
761 31b836b8 Thomas Thrainer
      node.hv_state_static = self.new_hv_state
762 31b836b8 Thomas Thrainer
763 31b836b8 Thomas Thrainer
    if self.op.disk_state:
764 31b836b8 Thomas Thrainer
      node.disk_state_static = self.new_disk_state
765 31b836b8 Thomas Thrainer
766 31b836b8 Thomas Thrainer
    for attr in ["master_capable", "vm_capable"]:
767 31b836b8 Thomas Thrainer
      val = getattr(self.op, attr)
768 31b836b8 Thomas Thrainer
      if val is not None:
769 31b836b8 Thomas Thrainer
        setattr(node, attr, val)
770 31b836b8 Thomas Thrainer
        result.append((attr, str(val)))
771 31b836b8 Thomas Thrainer
772 d0d7d7cf Thomas Thrainer
    if self.new_role != self.old_role:
773 31b836b8 Thomas Thrainer
      # Tell the node to demote itself, if no longer MC and not offline
774 d0d7d7cf Thomas Thrainer
      if self.old_role == self._ROLE_CANDIDATE and \
775 d0d7d7cf Thomas Thrainer
          self.new_role != self._ROLE_OFFLINE:
776 31b836b8 Thomas Thrainer
        msg = self.rpc.call_node_demote_from_mc(node.name).fail_msg
777 31b836b8 Thomas Thrainer
        if msg:
778 31b836b8 Thomas Thrainer
          self.LogWarning("Node failed to demote itself: %s", msg)
779 31b836b8 Thomas Thrainer
780 d0d7d7cf Thomas Thrainer
      new_flags = self._R2F[self.new_role]
781 31b836b8 Thomas Thrainer
      for of, nf, desc in zip(self.old_flags, new_flags, self._FLAGS):
782 31b836b8 Thomas Thrainer
        if of != nf:
783 31b836b8 Thomas Thrainer
          result.append((desc, str(nf)))
784 31b836b8 Thomas Thrainer
      (node.master_candidate, node.drained, node.offline) = new_flags
785 31b836b8 Thomas Thrainer
786 31b836b8 Thomas Thrainer
      # we locked all nodes, we adjust the CP before updating this node
787 31b836b8 Thomas Thrainer
      if self.lock_all:
788 c1410048 Helga Velroyen
        AdjustCandidatePool(self, [node.uuid], feedback_fn)
789 31b836b8 Thomas Thrainer
790 28756f80 Helga Velroyen
      cluster = self.cfg.GetClusterInfo()
791 28756f80 Helga Velroyen
      # if node gets promoted, grant RPC priviledges
792 28756f80 Helga Velroyen
      if self.new_role == self._ROLE_CANDIDATE:
793 28756f80 Helga Velroyen
        AddNodeCertToCandidateCerts(self, node.uuid, cluster)
794 28756f80 Helga Velroyen
      # if node is demoted, revoke RPC priviledges
795 28756f80 Helga Velroyen
      if self.old_role == self._ROLE_CANDIDATE:
796 28756f80 Helga Velroyen
        RemoveNodeCertFromCandidateCerts(node.uuid, cluster)
797 28756f80 Helga Velroyen
798 31b836b8 Thomas Thrainer
    if self.op.secondary_ip:
799 31b836b8 Thomas Thrainer
      node.secondary_ip = self.op.secondary_ip
800 31b836b8 Thomas Thrainer
      result.append(("secondary_ip", self.op.secondary_ip))
801 31b836b8 Thomas Thrainer
802 31b836b8 Thomas Thrainer
    # this will trigger configuration file update, if needed
803 31b836b8 Thomas Thrainer
    self.cfg.Update(node, feedback_fn)
804 31b836b8 Thomas Thrainer
805 31b836b8 Thomas Thrainer
    # this will trigger job queue propagation or cleanup if the mc
806 31b836b8 Thomas Thrainer
    # flag changed
807 d0d7d7cf Thomas Thrainer
    if [self.old_role, self.new_role].count(self._ROLE_CANDIDATE) == 1:
808 31b836b8 Thomas Thrainer
      self.context.ReaddNode(node)
809 31b836b8 Thomas Thrainer
810 31b836b8 Thomas Thrainer
    return result
811 31b836b8 Thomas Thrainer
812 31b836b8 Thomas Thrainer
813 31b836b8 Thomas Thrainer
class LUNodePowercycle(NoHooksLU):
814 31b836b8 Thomas Thrainer
  """Powercycles a node.
815 31b836b8 Thomas Thrainer

816 31b836b8 Thomas Thrainer
  """
817 31b836b8 Thomas Thrainer
  REQ_BGL = False
818 31b836b8 Thomas Thrainer
819 31b836b8 Thomas Thrainer
  def CheckArguments(self):
820 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
821 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
822 1c3231aa Thomas Thrainer
823 1c3231aa Thomas Thrainer
    if self.op.node_uuid == self.cfg.GetMasterNode() and not self.op.force:
824 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("The node is the master and the force"
825 31b836b8 Thomas Thrainer
                                 " parameter was not set",
826 31b836b8 Thomas Thrainer
                                 errors.ECODE_INVAL)
827 31b836b8 Thomas Thrainer
828 31b836b8 Thomas Thrainer
  def ExpandNames(self):
829 31b836b8 Thomas Thrainer
    """Locking for PowercycleNode.
830 31b836b8 Thomas Thrainer

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

834 31b836b8 Thomas Thrainer
    """
835 31b836b8 Thomas Thrainer
    self.needed_locks = {}
836 31b836b8 Thomas Thrainer
837 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
838 31b836b8 Thomas Thrainer
    """Reboots a node.
839 31b836b8 Thomas Thrainer

840 31b836b8 Thomas Thrainer
    """
841 8ef418bb Helga Velroyen
    default_hypervisor = self.cfg.GetHypervisorType()
842 8ef418bb Helga Velroyen
    hvparams = self.cfg.GetClusterInfo().hvparams[default_hypervisor]
843 1c3231aa Thomas Thrainer
    result = self.rpc.call_node_powercycle(self.op.node_uuid,
844 8ef418bb Helga Velroyen
                                           default_hypervisor,
845 8ef418bb Helga Velroyen
                                           hvparams)
846 31b836b8 Thomas Thrainer
    result.Raise("Failed to schedule the reboot")
847 31b836b8 Thomas Thrainer
    return result.payload
848 31b836b8 Thomas Thrainer
849 31b836b8 Thomas Thrainer
850 31b836b8 Thomas Thrainer
def _GetNodeInstancesInner(cfg, fn):
851 31b836b8 Thomas Thrainer
  return [i for i in cfg.GetAllInstancesInfo().values() if fn(i)]
852 31b836b8 Thomas Thrainer
853 31b836b8 Thomas Thrainer
854 1c3231aa Thomas Thrainer
def _GetNodePrimaryInstances(cfg, node_uuid):
855 31b836b8 Thomas Thrainer
  """Returns primary instances on a node.
856 31b836b8 Thomas Thrainer

857 31b836b8 Thomas Thrainer
  """
858 31b836b8 Thomas Thrainer
  return _GetNodeInstancesInner(cfg,
859 1c3231aa Thomas Thrainer
                                lambda inst: node_uuid == inst.primary_node)
860 31b836b8 Thomas Thrainer
861 31b836b8 Thomas Thrainer
862 1c3231aa Thomas Thrainer
def _GetNodeSecondaryInstances(cfg, node_uuid):
863 31b836b8 Thomas Thrainer
  """Returns secondary instances on a node.
864 31b836b8 Thomas Thrainer

865 31b836b8 Thomas Thrainer
  """
866 31b836b8 Thomas Thrainer
  return _GetNodeInstancesInner(cfg,
867 1c3231aa Thomas Thrainer
                                lambda inst: node_uuid in inst.secondary_nodes)
868 31b836b8 Thomas Thrainer
869 31b836b8 Thomas Thrainer
870 1c3231aa Thomas Thrainer
def _GetNodeInstances(cfg, node_uuid):
871 31b836b8 Thomas Thrainer
  """Returns a list of all primary and secondary instances on a node.
872 31b836b8 Thomas Thrainer

873 31b836b8 Thomas Thrainer
  """
874 31b836b8 Thomas Thrainer
875 1c3231aa Thomas Thrainer
  return _GetNodeInstancesInner(cfg, lambda inst: node_uuid in inst.all_nodes)
876 31b836b8 Thomas Thrainer
877 31b836b8 Thomas Thrainer
878 31b836b8 Thomas Thrainer
class LUNodeEvacuate(NoHooksLU):
879 31b836b8 Thomas Thrainer
  """Evacuates instances off a list of nodes.
880 31b836b8 Thomas Thrainer

881 31b836b8 Thomas Thrainer
  """
882 31b836b8 Thomas Thrainer
  REQ_BGL = False
883 31b836b8 Thomas Thrainer
884 31b836b8 Thomas Thrainer
  def CheckArguments(self):
885 5eacbcae Thomas Thrainer
    CheckIAllocatorOrNode(self, "iallocator", "remote_node")
886 31b836b8 Thomas Thrainer
887 31b836b8 Thomas Thrainer
  def ExpandNames(self):
888 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
889 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
890 31b836b8 Thomas Thrainer
891 31b836b8 Thomas Thrainer
    if self.op.remote_node is not None:
892 1c3231aa Thomas Thrainer
      (self.op.remote_node_uuid, self.op.remote_node) = \
893 1c3231aa Thomas Thrainer
        ExpandNodeUuidAndName(self.cfg, self.op.remote_node_uuid,
894 1c3231aa Thomas Thrainer
                              self.op.remote_node)
895 31b836b8 Thomas Thrainer
      assert self.op.remote_node
896 31b836b8 Thomas Thrainer
897 1c3231aa Thomas Thrainer
      if self.op.node_uuid == self.op.remote_node_uuid:
898 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Can not use evacuated node as a new"
899 31b836b8 Thomas Thrainer
                                   " secondary node", errors.ECODE_INVAL)
900 31b836b8 Thomas Thrainer
901 31b836b8 Thomas Thrainer
      if self.op.mode != constants.NODE_EVAC_SEC:
902 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Without the use of an iallocator only"
903 31b836b8 Thomas Thrainer
                                   " secondary instances can be evacuated",
904 31b836b8 Thomas Thrainer
                                   errors.ECODE_INVAL)
905 31b836b8 Thomas Thrainer
906 31b836b8 Thomas Thrainer
    # Declare locks
907 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
908 31b836b8 Thomas Thrainer
    self.needed_locks = {
909 31b836b8 Thomas Thrainer
      locking.LEVEL_INSTANCE: [],
910 31b836b8 Thomas Thrainer
      locking.LEVEL_NODEGROUP: [],
911 31b836b8 Thomas Thrainer
      locking.LEVEL_NODE: [],
912 31b836b8 Thomas Thrainer
      }
913 31b836b8 Thomas Thrainer
914 31b836b8 Thomas Thrainer
    # Determine nodes (via group) optimistically, needs verification once locks
915 31b836b8 Thomas Thrainer
    # have been acquired
916 31b836b8 Thomas Thrainer
    self.lock_nodes = self._DetermineNodes()
917 31b836b8 Thomas Thrainer
918 31b836b8 Thomas Thrainer
  def _DetermineNodes(self):
919 1c3231aa Thomas Thrainer
    """Gets the list of node UUIDs to operate on.
920 31b836b8 Thomas Thrainer

921 31b836b8 Thomas Thrainer
    """
922 31b836b8 Thomas Thrainer
    if self.op.remote_node is None:
923 31b836b8 Thomas Thrainer
      # Iallocator will choose any node(s) in the same group
924 1c3231aa Thomas Thrainer
      group_nodes = self.cfg.GetNodeGroupMembersByNodes([self.op.node_uuid])
925 31b836b8 Thomas Thrainer
    else:
926 1c3231aa Thomas Thrainer
      group_nodes = frozenset([self.op.remote_node_uuid])
927 31b836b8 Thomas Thrainer
928 31b836b8 Thomas Thrainer
    # Determine nodes to be locked
929 1c3231aa Thomas Thrainer
    return set([self.op.node_uuid]) | group_nodes
930 31b836b8 Thomas Thrainer
931 31b836b8 Thomas Thrainer
  def _DetermineInstances(self):
932 31b836b8 Thomas Thrainer
    """Builds list of instances to operate on.
933 31b836b8 Thomas Thrainer

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

1073 31b836b8 Thomas Thrainer
  """
1074 31b836b8 Thomas Thrainer
  HPATH = "node-migrate"
1075 31b836b8 Thomas Thrainer
  HTYPE = constants.HTYPE_NODE
1076 31b836b8 Thomas Thrainer
  REQ_BGL = False
1077 31b836b8 Thomas Thrainer
1078 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1079 31b836b8 Thomas Thrainer
    pass
1080 31b836b8 Thomas Thrainer
1081 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1082 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
1083 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
1084 31b836b8 Thomas Thrainer
1085 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1086 31b836b8 Thomas Thrainer
    self.needed_locks = {
1087 1c3231aa Thomas Thrainer
      locking.LEVEL_NODE: [self.op.node_uuid],
1088 31b836b8 Thomas Thrainer
      }
1089 31b836b8 Thomas Thrainer
1090 31b836b8 Thomas Thrainer
  def BuildHooksEnv(self):
1091 31b836b8 Thomas Thrainer
    """Build hooks env.
1092 31b836b8 Thomas Thrainer

1093 31b836b8 Thomas Thrainer
    This runs on the master, the primary and all the secondaries.
1094 31b836b8 Thomas Thrainer

1095 31b836b8 Thomas Thrainer
    """
1096 31b836b8 Thomas Thrainer
    return {
1097 31b836b8 Thomas Thrainer
      "NODE_NAME": self.op.node_name,
1098 31b836b8 Thomas Thrainer
      "ALLOW_RUNTIME_CHANGES": self.op.allow_runtime_changes,
1099 31b836b8 Thomas Thrainer
      }
1100 31b836b8 Thomas Thrainer
1101 31b836b8 Thomas Thrainer
  def BuildHooksNodes(self):
1102 31b836b8 Thomas Thrainer
    """Build hooks nodes.
1103 31b836b8 Thomas Thrainer

1104 31b836b8 Thomas Thrainer
    """
1105 31b836b8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()]
1106 31b836b8 Thomas Thrainer
    return (nl, nl)
1107 31b836b8 Thomas Thrainer
1108 31b836b8 Thomas Thrainer
  def CheckPrereq(self):
1109 31b836b8 Thomas Thrainer
    pass
1110 31b836b8 Thomas Thrainer
1111 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1112 31b836b8 Thomas Thrainer
    # Prepare jobs for migration instances
1113 31b836b8 Thomas Thrainer
    jobs = [
1114 d0d7d7cf Thomas Thrainer
      [opcodes.OpInstanceMigrate(
1115 d0d7d7cf Thomas Thrainer
        instance_name=inst.name,
1116 d0d7d7cf Thomas Thrainer
        mode=self.op.mode,
1117 d0d7d7cf Thomas Thrainer
        live=self.op.live,
1118 d0d7d7cf Thomas Thrainer
        iallocator=self.op.iallocator,
1119 d0d7d7cf Thomas Thrainer
        target_node=self.op.target_node,
1120 d0d7d7cf Thomas Thrainer
        allow_runtime_changes=self.op.allow_runtime_changes,
1121 d0d7d7cf Thomas Thrainer
        ignore_ipolicy=self.op.ignore_ipolicy)]
1122 1c3231aa Thomas Thrainer
      for inst in _GetNodePrimaryInstances(self.cfg, self.op.node_uuid)]
1123 31b836b8 Thomas Thrainer
1124 31b836b8 Thomas Thrainer
    # TODO: Run iallocator in this opcode and pass correct placement options to
1125 31b836b8 Thomas Thrainer
    # OpInstanceMigrate. Since other jobs can modify the cluster between
1126 31b836b8 Thomas Thrainer
    # running the iallocator and the actual migration, a good consistency model
1127 31b836b8 Thomas Thrainer
    # will have to be found.
1128 31b836b8 Thomas Thrainer
1129 31b836b8 Thomas Thrainer
    assert (frozenset(self.owned_locks(locking.LEVEL_NODE)) ==
1130 5e568fee Thomas Thrainer
            frozenset([self.op.node_uuid]))
1131 31b836b8 Thomas Thrainer
1132 31b836b8 Thomas Thrainer
    return ResultWithJobs(jobs)
1133 31b836b8 Thomas Thrainer
1134 31b836b8 Thomas Thrainer
1135 31b836b8 Thomas Thrainer
def _GetStorageTypeArgs(cfg, storage_type):
1136 31b836b8 Thomas Thrainer
  """Returns the arguments for a storage type.
1137 31b836b8 Thomas Thrainer

1138 31b836b8 Thomas Thrainer
  """
1139 31b836b8 Thomas Thrainer
  # Special case for file storage
1140 31b836b8 Thomas Thrainer
1141 5a904197 Santi Raffa
  if storage_type == constants.ST_FILE:
1142 5a904197 Santi Raffa
    return [[cfg.GetFileStorageDir()]]
1143 5a904197 Santi Raffa
  elif storage_type == constants.ST_SHARED_FILE:
1144 5a904197 Santi Raffa
    dts = cfg.GetClusterInfo().enabled_disk_templates
1145 5a904197 Santi Raffa
    paths = []
1146 5a904197 Santi Raffa
    if constants.DT_SHARED_FILE in dts:
1147 5a904197 Santi Raffa
      paths.append(cfg.GetSharedFileStorageDir())
1148 5a904197 Santi Raffa
    if constants.DT_GLUSTER in dts:
1149 5a904197 Santi Raffa
      paths.append(cfg.GetGlusterStorageDir())
1150 5a904197 Santi Raffa
    return [paths]
1151 5a904197 Santi Raffa
  else:
1152 5a904197 Santi Raffa
    return []
1153 31b836b8 Thomas Thrainer
1154 31b836b8 Thomas Thrainer
1155 31b836b8 Thomas Thrainer
class LUNodeModifyStorage(NoHooksLU):
1156 31b836b8 Thomas Thrainer
  """Logical unit for modifying a storage volume on a node.
1157 31b836b8 Thomas Thrainer

1158 31b836b8 Thomas Thrainer
  """
1159 31b836b8 Thomas Thrainer
  REQ_BGL = False
1160 31b836b8 Thomas Thrainer
1161 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1162 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
1163 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
1164 31b836b8 Thomas Thrainer
1165 31b836b8 Thomas Thrainer
    storage_type = self.op.storage_type
1166 31b836b8 Thomas Thrainer
1167 31b836b8 Thomas Thrainer
    try:
1168 31b836b8 Thomas Thrainer
      modifiable = constants.MODIFIABLE_STORAGE_FIELDS[storage_type]
1169 31b836b8 Thomas Thrainer
    except KeyError:
1170 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Storage units of type '%s' can not be"
1171 31b836b8 Thomas Thrainer
                                 " modified" % storage_type,
1172 31b836b8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1173 31b836b8 Thomas Thrainer
1174 31b836b8 Thomas Thrainer
    diff = set(self.op.changes.keys()) - modifiable
1175 31b836b8 Thomas Thrainer
    if diff:
1176 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("The following fields can not be modified for"
1177 31b836b8 Thomas Thrainer
                                 " storage units of type '%s': %r" %
1178 31b836b8 Thomas Thrainer
                                 (storage_type, list(diff)),
1179 31b836b8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1180 31b836b8 Thomas Thrainer
1181 9d276e93 Helga Velroyen
  def CheckPrereq(self):
1182 9d276e93 Helga Velroyen
    """Check prerequisites.
1183 9d276e93 Helga Velroyen

1184 9d276e93 Helga Velroyen
    """
1185 9d276e93 Helga Velroyen
    CheckStorageTypeEnabled(self.cfg.GetClusterInfo(), self.op.storage_type)
1186 9d276e93 Helga Velroyen
1187 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1188 31b836b8 Thomas Thrainer
    self.needed_locks = {
1189 1c3231aa Thomas Thrainer
      locking.LEVEL_NODE: self.op.node_uuid,
1190 31b836b8 Thomas Thrainer
      }
1191 31b836b8 Thomas Thrainer
1192 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1193 31b836b8 Thomas Thrainer
    """Computes the list of nodes and their attributes.
1194 31b836b8 Thomas Thrainer

1195 31b836b8 Thomas Thrainer
    """
1196 31b836b8 Thomas Thrainer
    st_args = _GetStorageTypeArgs(self.cfg, self.op.storage_type)
1197 1c3231aa Thomas Thrainer
    result = self.rpc.call_storage_modify(self.op.node_uuid,
1198 31b836b8 Thomas Thrainer
                                          self.op.storage_type, st_args,
1199 31b836b8 Thomas Thrainer
                                          self.op.name, self.op.changes)
1200 31b836b8 Thomas Thrainer
    result.Raise("Failed to modify storage unit '%s' on %s" %
1201 31b836b8 Thomas Thrainer
                 (self.op.name, self.op.node_name))
1202 31b836b8 Thomas Thrainer
1203 31b836b8 Thomas Thrainer
1204 6afb9fb4 Jose A. Lopes
def _CheckOutputFields(fields, selected):
1205 6afb9fb4 Jose A. Lopes
  """Checks whether all selected fields are valid according to fields.
1206 31b836b8 Thomas Thrainer

1207 6afb9fb4 Jose A. Lopes
  @type fields: L{utils.FieldSet}
1208 6afb9fb4 Jose A. Lopes
  @param fields: fields set
1209 6afb9fb4 Jose A. Lopes
  @type selected: L{utils.FieldSet}
1210 6afb9fb4 Jose A. Lopes
  @param selected: fields set
1211 31b836b8 Thomas Thrainer

1212 31b836b8 Thomas Thrainer
  """
1213 6afb9fb4 Jose A. Lopes
  delta = fields.NonMatching(selected)
1214 31b836b8 Thomas Thrainer
  if delta:
1215 31b836b8 Thomas Thrainer
    raise errors.OpPrereqError("Unknown output fields selected: %s"
1216 31b836b8 Thomas Thrainer
                               % ",".join(delta), errors.ECODE_INVAL)
1217 31b836b8 Thomas Thrainer
1218 31b836b8 Thomas Thrainer
1219 31b836b8 Thomas Thrainer
class LUNodeQueryvols(NoHooksLU):
1220 31b836b8 Thomas Thrainer
  """Logical unit for getting volumes on node(s).
1221 31b836b8 Thomas Thrainer

1222 31b836b8 Thomas Thrainer
  """
1223 31b836b8 Thomas Thrainer
  REQ_BGL = False
1224 31b836b8 Thomas Thrainer
1225 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1226 b2fbea47 Jose A. Lopes
    _CheckOutputFields(utils.FieldSet(constants.VF_NODE, constants.VF_PHYS,
1227 b2fbea47 Jose A. Lopes
                                      constants.VF_VG, constants.VF_NAME,
1228 b2fbea47 Jose A. Lopes
                                      constants.VF_SIZE, constants.VF_INSTANCE),
1229 6afb9fb4 Jose A. Lopes
                       self.op.output_fields)
1230 31b836b8 Thomas Thrainer
1231 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1232 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1233 31b836b8 Thomas Thrainer
1234 31b836b8 Thomas Thrainer
    if self.op.nodes:
1235 31b836b8 Thomas Thrainer
      self.needed_locks = {
1236 1c3231aa Thomas Thrainer
        locking.LEVEL_NODE: GetWantedNodes(self, self.op.nodes)[0],
1237 31b836b8 Thomas Thrainer
        }
1238 31b836b8 Thomas Thrainer
    else:
1239 31b836b8 Thomas Thrainer
      self.needed_locks = {
1240 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE: locking.ALL_SET,
1241 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1242 31b836b8 Thomas Thrainer
        }
1243 31b836b8 Thomas Thrainer
1244 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1245 31b836b8 Thomas Thrainer
    """Computes the list of nodes and their attributes.
1246 31b836b8 Thomas Thrainer

1247 31b836b8 Thomas Thrainer
    """
1248 1c3231aa Thomas Thrainer
    node_uuids = self.owned_locks(locking.LEVEL_NODE)
1249 1c3231aa Thomas Thrainer
    volumes = self.rpc.call_node_volumes(node_uuids)
1250 31b836b8 Thomas Thrainer
1251 31b836b8 Thomas Thrainer
    ilist = self.cfg.GetAllInstancesInfo()
1252 843094ad Thomas Thrainer
    vol2inst = MapInstanceLvsToNodes(ilist.values())
1253 31b836b8 Thomas Thrainer
1254 31b836b8 Thomas Thrainer
    output = []
1255 1c3231aa Thomas Thrainer
    for node_uuid in node_uuids:
1256 1c3231aa Thomas Thrainer
      nresult = volumes[node_uuid]
1257 31b836b8 Thomas Thrainer
      if nresult.offline:
1258 31b836b8 Thomas Thrainer
        continue
1259 31b836b8 Thomas Thrainer
      msg = nresult.fail_msg
1260 31b836b8 Thomas Thrainer
      if msg:
1261 1c3231aa Thomas Thrainer
        self.LogWarning("Can't compute volume data on node %s: %s",
1262 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
1263 31b836b8 Thomas Thrainer
        continue
1264 31b836b8 Thomas Thrainer
1265 31b836b8 Thomas Thrainer
      node_vols = sorted(nresult.payload,
1266 b2fbea47 Jose A. Lopes
                         key=operator.itemgetter(constants.VF_DEV))
1267 31b836b8 Thomas Thrainer
1268 31b836b8 Thomas Thrainer
      for vol in node_vols:
1269 31b836b8 Thomas Thrainer
        node_output = []
1270 31b836b8 Thomas Thrainer
        for field in self.op.output_fields:
1271 b2fbea47 Jose A. Lopes
          if field == constants.VF_NODE:
1272 1c3231aa Thomas Thrainer
            val = self.cfg.GetNodeName(node_uuid)
1273 b2fbea47 Jose A. Lopes
          elif field == constants.VF_PHYS:
1274 b2fbea47 Jose A. Lopes
            val = vol[constants.VF_DEV]
1275 b2fbea47 Jose A. Lopes
          elif field == constants.VF_VG:
1276 b2fbea47 Jose A. Lopes
            val = vol[constants.VF_VG]
1277 b2fbea47 Jose A. Lopes
          elif field == constants.VF_NAME:
1278 b2fbea47 Jose A. Lopes
            val = vol[constants.VF_NAME]
1279 b2fbea47 Jose A. Lopes
          elif field == constants.VF_SIZE:
1280 b2fbea47 Jose A. Lopes
            val = int(float(vol[constants.VF_SIZE]))
1281 b2fbea47 Jose A. Lopes
          elif field == constants.VF_INSTANCE:
1282 b2fbea47 Jose A. Lopes
            inst = vol2inst.get((node_uuid, vol[constants.VF_VG] + "/" +
1283 b2fbea47 Jose A. Lopes
                                 vol[constants.VF_NAME]), None)
1284 843094ad Thomas Thrainer
            if inst is not None:
1285 843094ad Thomas Thrainer
              val = inst.name
1286 843094ad Thomas Thrainer
            else:
1287 843094ad Thomas Thrainer
              val = "-"
1288 31b836b8 Thomas Thrainer
          else:
1289 31b836b8 Thomas Thrainer
            raise errors.ParameterError(field)
1290 31b836b8 Thomas Thrainer
          node_output.append(str(val))
1291 31b836b8 Thomas Thrainer
1292 31b836b8 Thomas Thrainer
        output.append(node_output)
1293 31b836b8 Thomas Thrainer
1294 31b836b8 Thomas Thrainer
    return output
1295 31b836b8 Thomas Thrainer
1296 31b836b8 Thomas Thrainer
1297 31b836b8 Thomas Thrainer
class LUNodeQueryStorage(NoHooksLU):
1298 31b836b8 Thomas Thrainer
  """Logical unit for getting information on storage units on node(s).
1299 31b836b8 Thomas Thrainer

1300 31b836b8 Thomas Thrainer
  """
1301 31b836b8 Thomas Thrainer
  REQ_BGL = False
1302 31b836b8 Thomas Thrainer
1303 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1304 6afb9fb4 Jose A. Lopes
    _CheckOutputFields(utils.FieldSet(*constants.VALID_STORAGE_FIELDS),
1305 6afb9fb4 Jose A. Lopes
                       self.op.output_fields)
1306 31b836b8 Thomas Thrainer
1307 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1308 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1309 31b836b8 Thomas Thrainer
1310 31b836b8 Thomas Thrainer
    if self.op.nodes:
1311 31b836b8 Thomas Thrainer
      self.needed_locks = {
1312 1c3231aa Thomas Thrainer
        locking.LEVEL_NODE: GetWantedNodes(self, self.op.nodes)[0],
1313 31b836b8 Thomas Thrainer
        }
1314 31b836b8 Thomas Thrainer
    else:
1315 31b836b8 Thomas Thrainer
      self.needed_locks = {
1316 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE: locking.ALL_SET,
1317 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1318 31b836b8 Thomas Thrainer
        }
1319 31b836b8 Thomas Thrainer
1320 4f90370c Helga Velroyen
  def _DetermineStorageType(self):
1321 4f90370c Helga Velroyen
    """Determines the default storage type of the cluster.
1322 4f90370c Helga Velroyen

1323 4f90370c Helga Velroyen
    """
1324 4f90370c Helga Velroyen
    enabled_disk_templates = self.cfg.GetClusterInfo().enabled_disk_templates
1325 4f90370c Helga Velroyen
    default_storage_type = \
1326 4f90370c Helga Velroyen
        constants.MAP_DISK_TEMPLATE_STORAGE_TYPE[enabled_disk_templates[0]]
1327 4f90370c Helga Velroyen
    return default_storage_type
1328 4f90370c Helga Velroyen
1329 9d276e93 Helga Velroyen
  def CheckPrereq(self):
1330 9d276e93 Helga Velroyen
    """Check prerequisites.
1331 9d276e93 Helga Velroyen

1332 9d276e93 Helga Velroyen
    """
1333 4f90370c Helga Velroyen
    if self.op.storage_type:
1334 4f90370c Helga Velroyen
      CheckStorageTypeEnabled(self.cfg.GetClusterInfo(), self.op.storage_type)
1335 4f90370c Helga Velroyen
      self.storage_type = self.op.storage_type
1336 4f90370c Helga Velroyen
    else:
1337 4f90370c Helga Velroyen
      self.storage_type = self._DetermineStorageType()
1338 5a904197 Santi Raffa
      supported_storage_types = constants.STS_REPORT_NODE_STORAGE
1339 5a904197 Santi Raffa
      if self.storage_type not in supported_storage_types:
1340 4f90370c Helga Velroyen
        raise errors.OpPrereqError(
1341 4f90370c Helga Velroyen
            "Storage reporting for storage type '%s' is not supported. Please"
1342 4f90370c Helga Velroyen
            " use the --storage-type option to specify one of the supported"
1343 4f90370c Helga Velroyen
            " storage types (%s) or set the default disk template to one that"
1344 4f90370c Helga Velroyen
            " supports storage reporting." %
1345 5a904197 Santi Raffa
            (self.storage_type, utils.CommaJoin(supported_storage_types)))
1346 9d276e93 Helga Velroyen
1347 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1348 31b836b8 Thomas Thrainer
    """Computes the list of nodes and their attributes.
1349 31b836b8 Thomas Thrainer

1350 31b836b8 Thomas Thrainer
    """
1351 4f90370c Helga Velroyen
    if self.op.storage_type:
1352 4f90370c Helga Velroyen
      self.storage_type = self.op.storage_type
1353 4f90370c Helga Velroyen
    else:
1354 4f90370c Helga Velroyen
      self.storage_type = self._DetermineStorageType()
1355 4f90370c Helga Velroyen
1356 1c3231aa Thomas Thrainer
    self.node_uuids = self.owned_locks(locking.LEVEL_NODE)
1357 31b836b8 Thomas Thrainer
1358 31b836b8 Thomas Thrainer
    # Always get name to sort by
1359 31b836b8 Thomas Thrainer
    if constants.SF_NAME in self.op.output_fields:
1360 31b836b8 Thomas Thrainer
      fields = self.op.output_fields[:]
1361 31b836b8 Thomas Thrainer
    else:
1362 31b836b8 Thomas Thrainer
      fields = [constants.SF_NAME] + self.op.output_fields
1363 31b836b8 Thomas Thrainer
1364 31b836b8 Thomas Thrainer
    # Never ask for node or type as it's only known to the LU
1365 31b836b8 Thomas Thrainer
    for extra in [constants.SF_NODE, constants.SF_TYPE]:
1366 31b836b8 Thomas Thrainer
      while extra in fields:
1367 31b836b8 Thomas Thrainer
        fields.remove(extra)
1368 31b836b8 Thomas Thrainer
1369 31b836b8 Thomas Thrainer
    field_idx = dict([(name, idx) for (idx, name) in enumerate(fields)])
1370 31b836b8 Thomas Thrainer
    name_idx = field_idx[constants.SF_NAME]
1371 31b836b8 Thomas Thrainer
1372 4f90370c Helga Velroyen
    st_args = _GetStorageTypeArgs(self.cfg, self.storage_type)
1373 1c3231aa Thomas Thrainer
    data = self.rpc.call_storage_list(self.node_uuids,
1374 4f90370c Helga Velroyen
                                      self.storage_type, st_args,
1375 31b836b8 Thomas Thrainer
                                      self.op.name, fields)
1376 31b836b8 Thomas Thrainer
1377 31b836b8 Thomas Thrainer
    result = []
1378 31b836b8 Thomas Thrainer
1379 1c3231aa Thomas Thrainer
    for node_uuid in utils.NiceSort(self.node_uuids):
1380 1c3231aa Thomas Thrainer
      node_name = self.cfg.GetNodeName(node_uuid)
1381 1c3231aa Thomas Thrainer
      nresult = data[node_uuid]
1382 31b836b8 Thomas Thrainer
      if nresult.offline:
1383 31b836b8 Thomas Thrainer
        continue
1384 31b836b8 Thomas Thrainer
1385 31b836b8 Thomas Thrainer
      msg = nresult.fail_msg
1386 31b836b8 Thomas Thrainer
      if msg:
1387 1c3231aa Thomas Thrainer
        self.LogWarning("Can't get storage data from node %s: %s",
1388 1c3231aa Thomas Thrainer
                        node_name, msg)
1389 31b836b8 Thomas Thrainer
        continue
1390 31b836b8 Thomas Thrainer
1391 31b836b8 Thomas Thrainer
      rows = dict([(row[name_idx], row) for row in nresult.payload])
1392 31b836b8 Thomas Thrainer
1393 31b836b8 Thomas Thrainer
      for name in utils.NiceSort(rows.keys()):
1394 31b836b8 Thomas Thrainer
        row = rows[name]
1395 31b836b8 Thomas Thrainer
1396 31b836b8 Thomas Thrainer
        out = []
1397 31b836b8 Thomas Thrainer
1398 31b836b8 Thomas Thrainer
        for field in self.op.output_fields:
1399 31b836b8 Thomas Thrainer
          if field == constants.SF_NODE:
1400 1c3231aa Thomas Thrainer
            val = node_name
1401 31b836b8 Thomas Thrainer
          elif field == constants.SF_TYPE:
1402 4f90370c Helga Velroyen
            val = self.storage_type
1403 31b836b8 Thomas Thrainer
          elif field in field_idx:
1404 31b836b8 Thomas Thrainer
            val = row[field_idx[field]]
1405 31b836b8 Thomas Thrainer
          else:
1406 31b836b8 Thomas Thrainer
            raise errors.ParameterError(field)
1407 31b836b8 Thomas Thrainer
1408 31b836b8 Thomas Thrainer
          out.append(val)
1409 31b836b8 Thomas Thrainer
1410 31b836b8 Thomas Thrainer
        result.append(out)
1411 31b836b8 Thomas Thrainer
1412 31b836b8 Thomas Thrainer
    return result
1413 31b836b8 Thomas Thrainer
1414 31b836b8 Thomas Thrainer
1415 31b836b8 Thomas Thrainer
class LUNodeRemove(LogicalUnit):
1416 31b836b8 Thomas Thrainer
  """Logical unit for removing a node.
1417 31b836b8 Thomas Thrainer

1418 31b836b8 Thomas Thrainer
  """
1419 31b836b8 Thomas Thrainer
  HPATH = "node-remove"
1420 31b836b8 Thomas Thrainer
  HTYPE = constants.HTYPE_NODE
1421 31b836b8 Thomas Thrainer
1422 31b836b8 Thomas Thrainer
  def BuildHooksEnv(self):
1423 31b836b8 Thomas Thrainer
    """Build hooks env.
1424 31b836b8 Thomas Thrainer

1425 31b836b8 Thomas Thrainer
    """
1426 31b836b8 Thomas Thrainer
    return {
1427 31b836b8 Thomas Thrainer
      "OP_TARGET": self.op.node_name,
1428 31b836b8 Thomas Thrainer
      "NODE_NAME": self.op.node_name,
1429 31b836b8 Thomas Thrainer
      }
1430 31b836b8 Thomas Thrainer
1431 31b836b8 Thomas Thrainer
  def BuildHooksNodes(self):
1432 31b836b8 Thomas Thrainer
    """Build hooks nodes.
1433 31b836b8 Thomas Thrainer

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

1437 31b836b8 Thomas Thrainer
    """
1438 31b836b8 Thomas Thrainer
    all_nodes = self.cfg.GetNodeList()
1439 31b836b8 Thomas Thrainer
    try:
1440 1c3231aa Thomas Thrainer
      all_nodes.remove(self.op.node_uuid)
1441 31b836b8 Thomas Thrainer
    except ValueError:
1442 31b836b8 Thomas Thrainer
      pass
1443 31b836b8 Thomas Thrainer
    return (all_nodes, all_nodes)
1444 31b836b8 Thomas Thrainer
1445 31b836b8 Thomas Thrainer
  def CheckPrereq(self):
1446 31b836b8 Thomas Thrainer
    """Check prerequisites.
1447 31b836b8 Thomas Thrainer

1448 31b836b8 Thomas Thrainer
    This checks:
1449 31b836b8 Thomas Thrainer
     - the node exists in the configuration
1450 31b836b8 Thomas Thrainer
     - it does not have primary or secondary instances
1451 31b836b8 Thomas Thrainer
     - it's not the master
1452 31b836b8 Thomas Thrainer

1453 31b836b8 Thomas Thrainer
    Any errors are signaled by raising errors.OpPrereqError.
1454 31b836b8 Thomas Thrainer

1455 31b836b8 Thomas Thrainer
    """
1456 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
1457 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
1458 1c3231aa Thomas Thrainer
    node = self.cfg.GetNodeInfo(self.op.node_uuid)
1459 31b836b8 Thomas Thrainer
    assert node is not None
1460 31b836b8 Thomas Thrainer
1461 31b836b8 Thomas Thrainer
    masternode = self.cfg.GetMasterNode()
1462 1c3231aa Thomas Thrainer
    if node.uuid == masternode:
1463 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Node is the master node, failover to another"
1464 31b836b8 Thomas Thrainer
                                 " node is required", errors.ECODE_INVAL)
1465 31b836b8 Thomas Thrainer
1466 da4a52a3 Thomas Thrainer
    for _, instance in self.cfg.GetAllInstancesInfo().items():
1467 1c3231aa Thomas Thrainer
      if node.uuid in instance.all_nodes:
1468 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s is still running on the node,"
1469 da4a52a3 Thomas Thrainer
                                   " please remove first" % instance.name,
1470 31b836b8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1471 31b836b8 Thomas Thrainer
    self.op.node_name = node.name
1472 31b836b8 Thomas Thrainer
    self.node = node
1473 31b836b8 Thomas Thrainer
1474 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1475 31b836b8 Thomas Thrainer
    """Removes the node from the cluster.
1476 31b836b8 Thomas Thrainer

1477 31b836b8 Thomas Thrainer
    """
1478 31b836b8 Thomas Thrainer
    logging.info("Stopping the node daemon and removing configs from node %s",
1479 d0d7d7cf Thomas Thrainer
                 self.node.name)
1480 31b836b8 Thomas Thrainer
1481 31b836b8 Thomas Thrainer
    modify_ssh_setup = self.cfg.GetClusterInfo().modify_ssh_setup
1482 31b836b8 Thomas Thrainer
1483 31b836b8 Thomas Thrainer
    assert locking.BGL in self.owned_locks(locking.LEVEL_CLUSTER), \
1484 31b836b8 Thomas Thrainer
      "Not owning BGL"
1485 31b836b8 Thomas Thrainer
1486 31b836b8 Thomas Thrainer
    # Promote nodes to master candidate as needed
1487 c1410048 Helga Velroyen
    AdjustCandidatePool(self, [self.node.uuid], feedback_fn)
1488 d0d7d7cf Thomas Thrainer
    self.context.RemoveNode(self.node)
1489 31b836b8 Thomas Thrainer
1490 31b836b8 Thomas Thrainer
    # Run post hooks on the node before it's removed
1491 d0d7d7cf Thomas Thrainer
    RunPostHook(self, self.node.name)
1492 31b836b8 Thomas Thrainer
1493 1c3231aa Thomas Thrainer
    # we have to call this by name rather than by UUID, as the node is no longer
1494 1c3231aa Thomas Thrainer
    # in the config
1495 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_node_leave_cluster(self.node.name, modify_ssh_setup)
1496 31b836b8 Thomas Thrainer
    msg = result.fail_msg
1497 31b836b8 Thomas Thrainer
    if msg:
1498 31b836b8 Thomas Thrainer
      self.LogWarning("Errors encountered on the remote node while leaving"
1499 31b836b8 Thomas Thrainer
                      " the cluster: %s", msg)
1500 31b836b8 Thomas Thrainer
1501 840ad2ab Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
1502 840ad2ab Helga Velroyen
1503 840ad2ab Helga Velroyen
    # Remove node from candidate certificate list
1504 840ad2ab Helga Velroyen
    if self.node.master_candidate:
1505 840ad2ab Helga Velroyen
      utils.RemoveNodeFromCandidateCerts(self.node.uuid,
1506 840ad2ab Helga Velroyen
                                         cluster.candidate_certs)
1507 840ad2ab Helga Velroyen
      self.cfg.Update(cluster, feedback_fn)
1508 840ad2ab Helga Velroyen
1509 31b836b8 Thomas Thrainer
    # Remove node from our /etc/hosts
1510 840ad2ab Helga Velroyen
    if cluster.modify_etc_hosts:
1511 1c3231aa Thomas Thrainer
      master_node_uuid = self.cfg.GetMasterNode()
1512 1c3231aa Thomas Thrainer
      result = self.rpc.call_etc_hosts_modify(master_node_uuid,
1513 31b836b8 Thomas Thrainer
                                              constants.ETC_HOSTS_REMOVE,
1514 d0d7d7cf Thomas Thrainer
                                              self.node.name, None)
1515 31b836b8 Thomas Thrainer
      result.Raise("Can't update hosts file with new host data")
1516 5eacbcae Thomas Thrainer
      RedistributeAncillaryFiles(self)
1517 31b836b8 Thomas Thrainer
1518 31b836b8 Thomas Thrainer
1519 31b836b8 Thomas Thrainer
class LURepairNodeStorage(NoHooksLU):
1520 31b836b8 Thomas Thrainer
  """Repairs the volume group on a node.
1521 31b836b8 Thomas Thrainer

1522 31b836b8 Thomas Thrainer
  """
1523 31b836b8 Thomas Thrainer
  REQ_BGL = False
1524 31b836b8 Thomas Thrainer
1525 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1526 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
1527 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
1528 31b836b8 Thomas Thrainer
1529 31b836b8 Thomas Thrainer
    storage_type = self.op.storage_type
1530 31b836b8 Thomas Thrainer
1531 31b836b8 Thomas Thrainer
    if (constants.SO_FIX_CONSISTENCY not in
1532 31b836b8 Thomas Thrainer
        constants.VALID_STORAGE_OPERATIONS.get(storage_type, [])):
1533 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Storage units of type '%s' can not be"
1534 31b836b8 Thomas Thrainer
                                 " repaired" % storage_type,
1535 31b836b8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1536 31b836b8 Thomas Thrainer
1537 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1538 31b836b8 Thomas Thrainer
    self.needed_locks = {
1539 1c3231aa Thomas Thrainer
      locking.LEVEL_NODE: [self.op.node_uuid],
1540 31b836b8 Thomas Thrainer
      }
1541 31b836b8 Thomas Thrainer
1542 1c3231aa Thomas Thrainer
  def _CheckFaultyDisks(self, instance, node_uuid):
1543 31b836b8 Thomas Thrainer
    """Ensure faulty disks abort the opcode or at least warn."""
1544 31b836b8 Thomas Thrainer
    try:
1545 5eacbcae Thomas Thrainer
      if FindFaultyInstanceDisks(self.cfg, self.rpc, instance,
1546 1c3231aa Thomas Thrainer
                                 node_uuid, True):
1547 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Instance '%s' has faulty disks on"
1548 1c3231aa Thomas Thrainer
                                   " node '%s'" %
1549 1c3231aa Thomas Thrainer
                                   (instance.name,
1550 1c3231aa Thomas Thrainer
                                    self.cfg.GetNodeName(node_uuid)),
1551 31b836b8 Thomas Thrainer
                                   errors.ECODE_STATE)
1552 31b836b8 Thomas Thrainer
    except errors.OpPrereqError, err:
1553 31b836b8 Thomas Thrainer
      if self.op.ignore_consistency:
1554 31b836b8 Thomas Thrainer
        self.LogWarning(str(err.args[0]))
1555 31b836b8 Thomas Thrainer
      else:
1556 31b836b8 Thomas Thrainer
        raise
1557 31b836b8 Thomas Thrainer
1558 31b836b8 Thomas Thrainer
  def CheckPrereq(self):
1559 31b836b8 Thomas Thrainer
    """Check prerequisites.
1560 31b836b8 Thomas Thrainer

1561 31b836b8 Thomas Thrainer
    """
1562 9d276e93 Helga Velroyen
    CheckStorageTypeEnabled(self.cfg.GetClusterInfo(), self.op.storage_type)
1563 9d276e93 Helga Velroyen
1564 31b836b8 Thomas Thrainer
    # Check whether any instance on this node has faulty disks
1565 1c3231aa Thomas Thrainer
    for inst in _GetNodeInstances(self.cfg, self.op.node_uuid):
1566 1d4a4b26 Thomas Thrainer
      if not inst.disks_active:
1567 31b836b8 Thomas Thrainer
        continue
1568 31b836b8 Thomas Thrainer
      check_nodes = set(inst.all_nodes)
1569 1c3231aa Thomas Thrainer
      check_nodes.discard(self.op.node_uuid)
1570 1c3231aa Thomas Thrainer
      for inst_node_uuid in check_nodes:
1571 1c3231aa Thomas Thrainer
        self._CheckFaultyDisks(inst, inst_node_uuid)
1572 31b836b8 Thomas Thrainer
1573 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1574 31b836b8 Thomas Thrainer
    feedback_fn("Repairing storage unit '%s' on %s ..." %
1575 31b836b8 Thomas Thrainer
                (self.op.name, self.op.node_name))
1576 31b836b8 Thomas Thrainer
1577 31b836b8 Thomas Thrainer
    st_args = _GetStorageTypeArgs(self.cfg, self.op.storage_type)
1578 1c3231aa Thomas Thrainer
    result = self.rpc.call_storage_execute(self.op.node_uuid,
1579 31b836b8 Thomas Thrainer
                                           self.op.storage_type, st_args,
1580 31b836b8 Thomas Thrainer
                                           self.op.name,
1581 31b836b8 Thomas Thrainer
                                           constants.SO_FIX_CONSISTENCY)
1582 31b836b8 Thomas Thrainer
    result.Raise("Failed to repair storage unit '%s' on %s" %
1583 31b836b8 Thomas Thrainer
                 (self.op.name, self.op.node_name))