Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / node.py @ 355d1f32

History | View | Annotate | Download (60.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 31b836b8 Thomas Thrainer
from ganeti import qlang
34 31b836b8 Thomas Thrainer
from ganeti import query
35 31b836b8 Thomas Thrainer
from ganeti import rpc
36 31b836b8 Thomas Thrainer
from ganeti import utils
37 31b836b8 Thomas Thrainer
from ganeti.masterd import iallocator
38 31b836b8 Thomas Thrainer
39 5eacbcae Thomas Thrainer
from ganeti.cmdlib.base import LogicalUnit, NoHooksLU, QueryBase, \
40 31b836b8 Thomas Thrainer
  ResultWithJobs
41 5eacbcae Thomas Thrainer
from ganeti.cmdlib.common import CheckParamsNotGlobal, \
42 5eacbcae Thomas Thrainer
  MergeAndVerifyHvState, MergeAndVerifyDiskState, \
43 5eacbcae Thomas Thrainer
  IsExclusiveStorageEnabledNode, CheckNodePVs, \
44 1c3231aa Thomas Thrainer
  RedistributeAncillaryFiles, ExpandNodeUuidAndName, ShareAll, SupportsOob, \
45 5eacbcae Thomas Thrainer
  CheckInstanceState, INSTANCE_DOWN, GetUpdatedParams, \
46 5eacbcae Thomas Thrainer
  AdjustCandidatePool, CheckIAllocatorOrNode, LoadNodeEvacResult, \
47 843094ad Thomas Thrainer
  GetWantedNodes, MapInstanceLvsToNodes, RunPostHook, \
48 9d276e93 Helga Velroyen
  FindFaultyInstanceDisks, CheckStorageTypeEnabled
49 31b836b8 Thomas Thrainer
50 31b836b8 Thomas Thrainer
51 31b836b8 Thomas Thrainer
def _DecideSelfPromotion(lu, exceptions=None):
52 31b836b8 Thomas Thrainer
  """Decide whether I should promote myself as a master candidate.
53 31b836b8 Thomas Thrainer

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

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

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

95 31b836b8 Thomas Thrainer
  """
96 31b836b8 Thomas Thrainer
  HPATH = "node-add"
97 31b836b8 Thomas Thrainer
  HTYPE = constants.HTYPE_NODE
98 31b836b8 Thomas Thrainer
  _NFLAGS = ["master_capable", "vm_capable"]
99 31b836b8 Thomas Thrainer
100 31b836b8 Thomas Thrainer
  def CheckArguments(self):
101 31b836b8 Thomas Thrainer
    self.primary_ip_family = self.cfg.GetPrimaryIPFamily()
102 31b836b8 Thomas Thrainer
    # validate/normalize the node name
103 31b836b8 Thomas Thrainer
    self.hostname = netutils.GetHostname(name=self.op.node_name,
104 31b836b8 Thomas Thrainer
                                         family=self.primary_ip_family)
105 31b836b8 Thomas Thrainer
    self.op.node_name = self.hostname.name
106 31b836b8 Thomas Thrainer
107 1c3231aa Thomas Thrainer
    if self.op.readd and self.op.node_name == self.cfg.GetMasterNodeName():
108 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot readd the master node",
109 31b836b8 Thomas Thrainer
                                 errors.ECODE_STATE)
110 31b836b8 Thomas Thrainer
111 31b836b8 Thomas Thrainer
    if self.op.readd and self.op.group:
112 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot pass a node group when a node is"
113 31b836b8 Thomas Thrainer
                                 " being readded", errors.ECODE_INVAL)
114 31b836b8 Thomas Thrainer
115 0d8ce33e Sebastian Gebhard
    if self.op.ndparams:
116 0d8ce33e Sebastian Gebhard
      ovs = self.op.ndparams.get(constants.ND_OVS, None)
117 0d8ce33e Sebastian Gebhard
      ovs_name = self.op.ndparams.get(constants.ND_OVS_NAME, None)
118 0d8ce33e Sebastian Gebhard
      ovs_link = self.op.ndparams.get(constants.ND_OVS_LINK, None)
119 0d8ce33e Sebastian Gebhard
120 0d8ce33e Sebastian Gebhard
      # OpenvSwitch: Warn user if link is missing
121 0d8ce33e Sebastian Gebhard
      if ovs and not ovs_link:
122 0d8ce33e Sebastian Gebhard
        self.LogInfo("No physical interface for OpenvSwitch was given."
123 0d8ce33e Sebastian Gebhard
                     " OpenvSwitch will not have an outside connection. This"
124 0d8ce33e Sebastian Gebhard
                     " might not be what you want.")
125 0d8ce33e Sebastian Gebhard
      # OpenvSwitch: Fail if parameters are given, but OVS is not enabled.
126 0d8ce33e Sebastian Gebhard
      if not ovs and (ovs_name or ovs_link):
127 0d8ce33e Sebastian Gebhard
        raise errors.OpPrereqError("OpenvSwitch name or link were given, but"
128 0d8ce33e Sebastian Gebhard
                                   " OpenvSwitch is not enabled. Please enable"
129 355d1f32 Sebastian Gebhard
                                   " OpenvSwitch with --ovs",
130 355d1f32 Sebastian Gebhard
                                   errors.ECODE_INVAL)
131 8baa9ca7 Sebastian Gebhard
132 31b836b8 Thomas Thrainer
  def BuildHooksEnv(self):
133 31b836b8 Thomas Thrainer
    """Build hooks env.
134 31b836b8 Thomas Thrainer

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

137 31b836b8 Thomas Thrainer
    """
138 31b836b8 Thomas Thrainer
    return {
139 31b836b8 Thomas Thrainer
      "OP_TARGET": self.op.node_name,
140 31b836b8 Thomas Thrainer
      "NODE_NAME": self.op.node_name,
141 31b836b8 Thomas Thrainer
      "NODE_PIP": self.op.primary_ip,
142 31b836b8 Thomas Thrainer
      "NODE_SIP": self.op.secondary_ip,
143 31b836b8 Thomas Thrainer
      "MASTER_CAPABLE": str(self.op.master_capable),
144 31b836b8 Thomas Thrainer
      "VM_CAPABLE": str(self.op.vm_capable),
145 31b836b8 Thomas Thrainer
      }
146 31b836b8 Thomas Thrainer
147 31b836b8 Thomas Thrainer
  def BuildHooksNodes(self):
148 31b836b8 Thomas Thrainer
    """Build hooks nodes.
149 31b836b8 Thomas Thrainer

150 31b836b8 Thomas Thrainer
    """
151 1c3231aa Thomas Thrainer
    hook_nodes = self.cfg.GetNodeList()
152 1c3231aa Thomas Thrainer
    new_node_info = self.cfg.GetNodeInfoByName(self.op.node_name)
153 1c3231aa Thomas Thrainer
    if new_node_info is not None:
154 1c3231aa Thomas Thrainer
      # Exclude added node
155 1c3231aa Thomas Thrainer
      hook_nodes = list(set(hook_nodes) - set([new_node_info.uuid]))
156 31b836b8 Thomas Thrainer
157 1c3231aa Thomas Thrainer
    # add the new node as post hook node by name; it does not have an UUID yet
158 1c3231aa Thomas Thrainer
    return (hook_nodes, hook_nodes, [self.op.node_name, ])
159 31b836b8 Thomas Thrainer
160 31b836b8 Thomas Thrainer
  def CheckPrereq(self):
161 31b836b8 Thomas Thrainer
    """Check prerequisites.
162 31b836b8 Thomas Thrainer

163 31b836b8 Thomas Thrainer
    This checks:
164 31b836b8 Thomas Thrainer
     - the new node is not already in the config
165 31b836b8 Thomas Thrainer
     - it is resolvable
166 31b836b8 Thomas Thrainer
     - its parameters (single/dual homed) matches the cluster
167 31b836b8 Thomas Thrainer

168 31b836b8 Thomas Thrainer
    Any errors are signaled by raising errors.OpPrereqError.
169 31b836b8 Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

906 31b836b8 Thomas Thrainer
    """
907 31b836b8 Thomas Thrainer
    if self.op.remote_node is None:
908 31b836b8 Thomas Thrainer
      # Iallocator will choose any node(s) in the same group
909 1c3231aa Thomas Thrainer
      group_nodes = self.cfg.GetNodeGroupMembersByNodes([self.op.node_uuid])
910 31b836b8 Thomas Thrainer
    else:
911 1c3231aa Thomas Thrainer
      group_nodes = frozenset([self.op.remote_node_uuid])
912 31b836b8 Thomas Thrainer
913 31b836b8 Thomas Thrainer
    # Determine nodes to be locked
914 1c3231aa Thomas Thrainer
    return set([self.op.node_uuid]) | group_nodes
915 31b836b8 Thomas Thrainer
916 31b836b8 Thomas Thrainer
  def _DetermineInstances(self):
917 31b836b8 Thomas Thrainer
    """Builds list of instances to operate on.
918 31b836b8 Thomas Thrainer

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

1059 31b836b8 Thomas Thrainer
  """
1060 31b836b8 Thomas Thrainer
  HPATH = "node-migrate"
1061 31b836b8 Thomas Thrainer
  HTYPE = constants.HTYPE_NODE
1062 31b836b8 Thomas Thrainer
  REQ_BGL = False
1063 31b836b8 Thomas Thrainer
1064 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1065 31b836b8 Thomas Thrainer
    pass
1066 31b836b8 Thomas Thrainer
1067 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1068 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
1069 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
1070 31b836b8 Thomas Thrainer
1071 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1072 31b836b8 Thomas Thrainer
    self.needed_locks = {
1073 1c3231aa Thomas Thrainer
      locking.LEVEL_NODE: [self.op.node_uuid],
1074 31b836b8 Thomas Thrainer
      }
1075 31b836b8 Thomas Thrainer
1076 31b836b8 Thomas Thrainer
  def BuildHooksEnv(self):
1077 31b836b8 Thomas Thrainer
    """Build hooks env.
1078 31b836b8 Thomas Thrainer

1079 31b836b8 Thomas Thrainer
    This runs on the master, the primary and all the secondaries.
1080 31b836b8 Thomas Thrainer

1081 31b836b8 Thomas Thrainer
    """
1082 31b836b8 Thomas Thrainer
    return {
1083 31b836b8 Thomas Thrainer
      "NODE_NAME": self.op.node_name,
1084 31b836b8 Thomas Thrainer
      "ALLOW_RUNTIME_CHANGES": self.op.allow_runtime_changes,
1085 31b836b8 Thomas Thrainer
      }
1086 31b836b8 Thomas Thrainer
1087 31b836b8 Thomas Thrainer
  def BuildHooksNodes(self):
1088 31b836b8 Thomas Thrainer
    """Build hooks nodes.
1089 31b836b8 Thomas Thrainer

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

1124 31b836b8 Thomas Thrainer
  """
1125 31b836b8 Thomas Thrainer
  # Special case for file storage
1126 31b836b8 Thomas Thrainer
  if storage_type == constants.ST_FILE:
1127 31b836b8 Thomas Thrainer
    # storage.FileStorage wants a list of storage directories
1128 31b836b8 Thomas Thrainer
    return [[cfg.GetFileStorageDir(), cfg.GetSharedFileStorageDir()]]
1129 31b836b8 Thomas Thrainer
1130 31b836b8 Thomas Thrainer
  return []
1131 31b836b8 Thomas Thrainer
1132 31b836b8 Thomas Thrainer
1133 31b836b8 Thomas Thrainer
class LUNodeModifyStorage(NoHooksLU):
1134 31b836b8 Thomas Thrainer
  """Logical unit for modifying a storage volume on a node.
1135 31b836b8 Thomas Thrainer

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

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

1173 31b836b8 Thomas Thrainer
    """
1174 31b836b8 Thomas Thrainer
    st_args = _GetStorageTypeArgs(self.cfg, self.op.storage_type)
1175 1c3231aa Thomas Thrainer
    result = self.rpc.call_storage_modify(self.op.node_uuid,
1176 31b836b8 Thomas Thrainer
                                          self.op.storage_type, st_args,
1177 31b836b8 Thomas Thrainer
                                          self.op.name, self.op.changes)
1178 31b836b8 Thomas Thrainer
    result.Raise("Failed to modify storage unit '%s' on %s" %
1179 31b836b8 Thomas Thrainer
                 (self.op.name, self.op.node_name))
1180 31b836b8 Thomas Thrainer
1181 31b836b8 Thomas Thrainer
1182 5eacbcae Thomas Thrainer
class NodeQuery(QueryBase):
1183 31b836b8 Thomas Thrainer
  FIELDS = query.NODE_FIELDS
1184 31b836b8 Thomas Thrainer
1185 31b836b8 Thomas Thrainer
  def ExpandNames(self, lu):
1186 31b836b8 Thomas Thrainer
    lu.needed_locks = {}
1187 5eacbcae Thomas Thrainer
    lu.share_locks = ShareAll()
1188 31b836b8 Thomas Thrainer
1189 31b836b8 Thomas Thrainer
    if self.names:
1190 1c3231aa Thomas Thrainer
      (self.wanted, _) = GetWantedNodes(lu, self.names)
1191 31b836b8 Thomas Thrainer
    else:
1192 31b836b8 Thomas Thrainer
      self.wanted = locking.ALL_SET
1193 31b836b8 Thomas Thrainer
1194 31b836b8 Thomas Thrainer
    self.do_locking = (self.use_locking and
1195 31b836b8 Thomas Thrainer
                       query.NQ_LIVE in self.requested_data)
1196 31b836b8 Thomas Thrainer
1197 31b836b8 Thomas Thrainer
    if self.do_locking:
1198 31b836b8 Thomas Thrainer
      # If any non-static field is requested we need to lock the nodes
1199 31b836b8 Thomas Thrainer
      lu.needed_locks[locking.LEVEL_NODE] = self.wanted
1200 31b836b8 Thomas Thrainer
      lu.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
1201 31b836b8 Thomas Thrainer
1202 31b836b8 Thomas Thrainer
  def DeclareLocks(self, lu, level):
1203 31b836b8 Thomas Thrainer
    pass
1204 31b836b8 Thomas Thrainer
1205 31b836b8 Thomas Thrainer
  def _GetQueryData(self, lu):
1206 31b836b8 Thomas Thrainer
    """Computes the list of nodes and their attributes.
1207 31b836b8 Thomas Thrainer

1208 31b836b8 Thomas Thrainer
    """
1209 31b836b8 Thomas Thrainer
    all_info = lu.cfg.GetAllNodesInfo()
1210 31b836b8 Thomas Thrainer
1211 1c3231aa Thomas Thrainer
    node_uuids = self._GetNames(lu, all_info.keys(), locking.LEVEL_NODE)
1212 31b836b8 Thomas Thrainer
1213 31b836b8 Thomas Thrainer
    # Gather data as requested
1214 31b836b8 Thomas Thrainer
    if query.NQ_LIVE in self.requested_data:
1215 31b836b8 Thomas Thrainer
      # filter out non-vm_capable nodes
1216 1c3231aa Thomas Thrainer
      toquery_node_uuids = [node.uuid for node in all_info.values()
1217 1c3231aa Thomas Thrainer
                            if node.vm_capable and node.uuid in node_uuids]
1218 20faaa74 Helga Velroyen
      lvm_enabled = utils.storage.IsLvmEnabled(
1219 20faaa74 Helga Velroyen
          lu.cfg.GetClusterInfo().enabled_disk_templates)
1220 da803ff1 Helga Velroyen
      # FIXME: this per default asks for storage space information for all
1221 da803ff1 Helga Velroyen
      # enabled disk templates. Fix this by making it possible to specify
1222 da803ff1 Helga Velroyen
      # space report fields for specific disk templates.
1223 da803ff1 Helga Velroyen
      raw_storage_units = utils.storage.GetStorageUnitsOfCluster(
1224 20faaa74 Helga Velroyen
          lu.cfg, include_spindles=lvm_enabled)
1225 da803ff1 Helga Velroyen
      storage_units = rpc.PrepareStorageUnitsForNodes(
1226 da803ff1 Helga Velroyen
          lu.cfg, raw_storage_units, toquery_node_uuids)
1227 a295eb80 Helga Velroyen
      default_hypervisor = lu.cfg.GetHypervisorType()
1228 a295eb80 Helga Velroyen
      hvparams = lu.cfg.GetClusterInfo().hvparams[default_hypervisor]
1229 a295eb80 Helga Velroyen
      hvspecs = [(default_hypervisor, hvparams)]
1230 32389d91 Helga Velroyen
      node_data = lu.rpc.call_node_info(toquery_node_uuids, storage_units,
1231 da803ff1 Helga Velroyen
                                        hvspecs)
1232 32389d91 Helga Velroyen
      live_data = dict(
1233 32389d91 Helga Velroyen
          (uuid, rpc.MakeLegacyNodeInfo(nresult.payload,
1234 20faaa74 Helga Velroyen
                                        require_spindles=lvm_enabled))
1235 32389d91 Helga Velroyen
          for (uuid, nresult) in node_data.items()
1236 32389d91 Helga Velroyen
          if not nresult.fail_msg and nresult.payload)
1237 31b836b8 Thomas Thrainer
    else:
1238 31b836b8 Thomas Thrainer
      live_data = None
1239 31b836b8 Thomas Thrainer
1240 31b836b8 Thomas Thrainer
    if query.NQ_INST in self.requested_data:
1241 1c3231aa Thomas Thrainer
      node_to_primary = dict([(uuid, set()) for uuid in node_uuids])
1242 1c3231aa Thomas Thrainer
      node_to_secondary = dict([(uuid, set()) for uuid in node_uuids])
1243 31b836b8 Thomas Thrainer
1244 31b836b8 Thomas Thrainer
      inst_data = lu.cfg.GetAllInstancesInfo()
1245 da4a52a3 Thomas Thrainer
      inst_uuid_to_inst_name = {}
1246 31b836b8 Thomas Thrainer
1247 31b836b8 Thomas Thrainer
      for inst in inst_data.values():
1248 da4a52a3 Thomas Thrainer
        inst_uuid_to_inst_name[inst.uuid] = inst.name
1249 31b836b8 Thomas Thrainer
        if inst.primary_node in node_to_primary:
1250 da4a52a3 Thomas Thrainer
          node_to_primary[inst.primary_node].add(inst.uuid)
1251 31b836b8 Thomas Thrainer
        for secnode in inst.secondary_nodes:
1252 31b836b8 Thomas Thrainer
          if secnode in node_to_secondary:
1253 da4a52a3 Thomas Thrainer
            node_to_secondary[secnode].add(inst.uuid)
1254 31b836b8 Thomas Thrainer
    else:
1255 31b836b8 Thomas Thrainer
      node_to_primary = None
1256 31b836b8 Thomas Thrainer
      node_to_secondary = None
1257 da4a52a3 Thomas Thrainer
      inst_uuid_to_inst_name = None
1258 31b836b8 Thomas Thrainer
1259 31b836b8 Thomas Thrainer
    if query.NQ_OOB in self.requested_data:
1260 1c3231aa Thomas Thrainer
      oob_support = dict((uuid, bool(SupportsOob(lu.cfg, node)))
1261 1c3231aa Thomas Thrainer
                         for uuid, node in all_info.iteritems())
1262 31b836b8 Thomas Thrainer
    else:
1263 31b836b8 Thomas Thrainer
      oob_support = None
1264 31b836b8 Thomas Thrainer
1265 31b836b8 Thomas Thrainer
    if query.NQ_GROUP in self.requested_data:
1266 31b836b8 Thomas Thrainer
      groups = lu.cfg.GetAllNodeGroupsInfo()
1267 31b836b8 Thomas Thrainer
    else:
1268 31b836b8 Thomas Thrainer
      groups = {}
1269 31b836b8 Thomas Thrainer
1270 1c3231aa Thomas Thrainer
    return query.NodeQueryData([all_info[uuid] for uuid in node_uuids],
1271 31b836b8 Thomas Thrainer
                               live_data, lu.cfg.GetMasterNode(),
1272 da4a52a3 Thomas Thrainer
                               node_to_primary, node_to_secondary,
1273 da4a52a3 Thomas Thrainer
                               inst_uuid_to_inst_name, groups, oob_support,
1274 da4a52a3 Thomas Thrainer
                               lu.cfg.GetClusterInfo())
1275 31b836b8 Thomas Thrainer
1276 31b836b8 Thomas Thrainer
1277 31b836b8 Thomas Thrainer
class LUNodeQuery(NoHooksLU):
1278 31b836b8 Thomas Thrainer
  """Logical unit for querying nodes.
1279 31b836b8 Thomas Thrainer

1280 31b836b8 Thomas Thrainer
  """
1281 31b836b8 Thomas Thrainer
  # pylint: disable=W0142
1282 31b836b8 Thomas Thrainer
  REQ_BGL = False
1283 31b836b8 Thomas Thrainer
1284 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1285 5eacbcae Thomas Thrainer
    self.nq = NodeQuery(qlang.MakeSimpleFilter("name", self.op.names),
1286 31b836b8 Thomas Thrainer
                         self.op.output_fields, self.op.use_locking)
1287 31b836b8 Thomas Thrainer
1288 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1289 31b836b8 Thomas Thrainer
    self.nq.ExpandNames(self)
1290 31b836b8 Thomas Thrainer
1291 31b836b8 Thomas Thrainer
  def DeclareLocks(self, level):
1292 31b836b8 Thomas Thrainer
    self.nq.DeclareLocks(self, level)
1293 31b836b8 Thomas Thrainer
1294 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1295 31b836b8 Thomas Thrainer
    return self.nq.OldStyleQuery(self)
1296 31b836b8 Thomas Thrainer
1297 31b836b8 Thomas Thrainer
1298 6afb9fb4 Jose A. Lopes
def _CheckOutputFields(fields, selected):
1299 6afb9fb4 Jose A. Lopes
  """Checks whether all selected fields are valid according to fields.
1300 31b836b8 Thomas Thrainer

1301 6afb9fb4 Jose A. Lopes
  @type fields: L{utils.FieldSet}
1302 6afb9fb4 Jose A. Lopes
  @param fields: fields set
1303 6afb9fb4 Jose A. Lopes
  @type selected: L{utils.FieldSet}
1304 6afb9fb4 Jose A. Lopes
  @param selected: fields set
1305 31b836b8 Thomas Thrainer

1306 31b836b8 Thomas Thrainer
  """
1307 6afb9fb4 Jose A. Lopes
  delta = fields.NonMatching(selected)
1308 31b836b8 Thomas Thrainer
  if delta:
1309 31b836b8 Thomas Thrainer
    raise errors.OpPrereqError("Unknown output fields selected: %s"
1310 31b836b8 Thomas Thrainer
                               % ",".join(delta), errors.ECODE_INVAL)
1311 31b836b8 Thomas Thrainer
1312 31b836b8 Thomas Thrainer
1313 31b836b8 Thomas Thrainer
class LUNodeQueryvols(NoHooksLU):
1314 31b836b8 Thomas Thrainer
  """Logical unit for getting volumes on node(s).
1315 31b836b8 Thomas Thrainer

1316 31b836b8 Thomas Thrainer
  """
1317 31b836b8 Thomas Thrainer
  REQ_BGL = False
1318 31b836b8 Thomas Thrainer
1319 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1320 b2fbea47 Jose A. Lopes
    _CheckOutputFields(utils.FieldSet(constants.VF_NODE, constants.VF_PHYS,
1321 b2fbea47 Jose A. Lopes
                                      constants.VF_VG, constants.VF_NAME,
1322 b2fbea47 Jose A. Lopes
                                      constants.VF_SIZE, constants.VF_INSTANCE),
1323 6afb9fb4 Jose A. Lopes
                       self.op.output_fields)
1324 31b836b8 Thomas Thrainer
1325 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1326 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1327 31b836b8 Thomas Thrainer
1328 31b836b8 Thomas Thrainer
    if self.op.nodes:
1329 31b836b8 Thomas Thrainer
      self.needed_locks = {
1330 1c3231aa Thomas Thrainer
        locking.LEVEL_NODE: GetWantedNodes(self, self.op.nodes)[0],
1331 31b836b8 Thomas Thrainer
        }
1332 31b836b8 Thomas Thrainer
    else:
1333 31b836b8 Thomas Thrainer
      self.needed_locks = {
1334 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE: locking.ALL_SET,
1335 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1336 31b836b8 Thomas Thrainer
        }
1337 31b836b8 Thomas Thrainer
1338 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1339 31b836b8 Thomas Thrainer
    """Computes the list of nodes and their attributes.
1340 31b836b8 Thomas Thrainer

1341 31b836b8 Thomas Thrainer
    """
1342 1c3231aa Thomas Thrainer
    node_uuids = self.owned_locks(locking.LEVEL_NODE)
1343 1c3231aa Thomas Thrainer
    volumes = self.rpc.call_node_volumes(node_uuids)
1344 31b836b8 Thomas Thrainer
1345 31b836b8 Thomas Thrainer
    ilist = self.cfg.GetAllInstancesInfo()
1346 843094ad Thomas Thrainer
    vol2inst = MapInstanceLvsToNodes(ilist.values())
1347 31b836b8 Thomas Thrainer
1348 31b836b8 Thomas Thrainer
    output = []
1349 1c3231aa Thomas Thrainer
    for node_uuid in node_uuids:
1350 1c3231aa Thomas Thrainer
      nresult = volumes[node_uuid]
1351 31b836b8 Thomas Thrainer
      if nresult.offline:
1352 31b836b8 Thomas Thrainer
        continue
1353 31b836b8 Thomas Thrainer
      msg = nresult.fail_msg
1354 31b836b8 Thomas Thrainer
      if msg:
1355 1c3231aa Thomas Thrainer
        self.LogWarning("Can't compute volume data on node %s: %s",
1356 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
1357 31b836b8 Thomas Thrainer
        continue
1358 31b836b8 Thomas Thrainer
1359 31b836b8 Thomas Thrainer
      node_vols = sorted(nresult.payload,
1360 b2fbea47 Jose A. Lopes
                         key=operator.itemgetter(constants.VF_DEV))
1361 31b836b8 Thomas Thrainer
1362 31b836b8 Thomas Thrainer
      for vol in node_vols:
1363 31b836b8 Thomas Thrainer
        node_output = []
1364 31b836b8 Thomas Thrainer
        for field in self.op.output_fields:
1365 b2fbea47 Jose A. Lopes
          if field == constants.VF_NODE:
1366 1c3231aa Thomas Thrainer
            val = self.cfg.GetNodeName(node_uuid)
1367 b2fbea47 Jose A. Lopes
          elif field == constants.VF_PHYS:
1368 b2fbea47 Jose A. Lopes
            val = vol[constants.VF_DEV]
1369 b2fbea47 Jose A. Lopes
          elif field == constants.VF_VG:
1370 b2fbea47 Jose A. Lopes
            val = vol[constants.VF_VG]
1371 b2fbea47 Jose A. Lopes
          elif field == constants.VF_NAME:
1372 b2fbea47 Jose A. Lopes
            val = vol[constants.VF_NAME]
1373 b2fbea47 Jose A. Lopes
          elif field == constants.VF_SIZE:
1374 b2fbea47 Jose A. Lopes
            val = int(float(vol[constants.VF_SIZE]))
1375 b2fbea47 Jose A. Lopes
          elif field == constants.VF_INSTANCE:
1376 b2fbea47 Jose A. Lopes
            inst = vol2inst.get((node_uuid, vol[constants.VF_VG] + "/" +
1377 b2fbea47 Jose A. Lopes
                                 vol[constants.VF_NAME]), None)
1378 843094ad Thomas Thrainer
            if inst is not None:
1379 843094ad Thomas Thrainer
              val = inst.name
1380 843094ad Thomas Thrainer
            else:
1381 843094ad Thomas Thrainer
              val = "-"
1382 31b836b8 Thomas Thrainer
          else:
1383 31b836b8 Thomas Thrainer
            raise errors.ParameterError(field)
1384 31b836b8 Thomas Thrainer
          node_output.append(str(val))
1385 31b836b8 Thomas Thrainer
1386 31b836b8 Thomas Thrainer
        output.append(node_output)
1387 31b836b8 Thomas Thrainer
1388 31b836b8 Thomas Thrainer
    return output
1389 31b836b8 Thomas Thrainer
1390 31b836b8 Thomas Thrainer
1391 31b836b8 Thomas Thrainer
class LUNodeQueryStorage(NoHooksLU):
1392 31b836b8 Thomas Thrainer
  """Logical unit for getting information on storage units on node(s).
1393 31b836b8 Thomas Thrainer

1394 31b836b8 Thomas Thrainer
  """
1395 31b836b8 Thomas Thrainer
  REQ_BGL = False
1396 31b836b8 Thomas Thrainer
1397 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1398 6afb9fb4 Jose A. Lopes
    _CheckOutputFields(utils.FieldSet(*constants.VALID_STORAGE_FIELDS),
1399 6afb9fb4 Jose A. Lopes
                       self.op.output_fields)
1400 31b836b8 Thomas Thrainer
1401 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1402 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1403 31b836b8 Thomas Thrainer
1404 31b836b8 Thomas Thrainer
    if self.op.nodes:
1405 31b836b8 Thomas Thrainer
      self.needed_locks = {
1406 1c3231aa Thomas Thrainer
        locking.LEVEL_NODE: GetWantedNodes(self, self.op.nodes)[0],
1407 31b836b8 Thomas Thrainer
        }
1408 31b836b8 Thomas Thrainer
    else:
1409 31b836b8 Thomas Thrainer
      self.needed_locks = {
1410 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE: locking.ALL_SET,
1411 31b836b8 Thomas Thrainer
        locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1412 31b836b8 Thomas Thrainer
        }
1413 31b836b8 Thomas Thrainer
1414 9d276e93 Helga Velroyen
  def CheckPrereq(self):
1415 9d276e93 Helga Velroyen
    """Check prerequisites.
1416 9d276e93 Helga Velroyen

1417 9d276e93 Helga Velroyen
    """
1418 9d276e93 Helga Velroyen
    CheckStorageTypeEnabled(self.cfg.GetClusterInfo(), self.op.storage_type)
1419 9d276e93 Helga Velroyen
1420 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1421 31b836b8 Thomas Thrainer
    """Computes the list of nodes and their attributes.
1422 31b836b8 Thomas Thrainer

1423 31b836b8 Thomas Thrainer
    """
1424 1c3231aa Thomas Thrainer
    self.node_uuids = self.owned_locks(locking.LEVEL_NODE)
1425 31b836b8 Thomas Thrainer
1426 31b836b8 Thomas Thrainer
    # Always get name to sort by
1427 31b836b8 Thomas Thrainer
    if constants.SF_NAME in self.op.output_fields:
1428 31b836b8 Thomas Thrainer
      fields = self.op.output_fields[:]
1429 31b836b8 Thomas Thrainer
    else:
1430 31b836b8 Thomas Thrainer
      fields = [constants.SF_NAME] + self.op.output_fields
1431 31b836b8 Thomas Thrainer
1432 31b836b8 Thomas Thrainer
    # Never ask for node or type as it's only known to the LU
1433 31b836b8 Thomas Thrainer
    for extra in [constants.SF_NODE, constants.SF_TYPE]:
1434 31b836b8 Thomas Thrainer
      while extra in fields:
1435 31b836b8 Thomas Thrainer
        fields.remove(extra)
1436 31b836b8 Thomas Thrainer
1437 31b836b8 Thomas Thrainer
    field_idx = dict([(name, idx) for (idx, name) in enumerate(fields)])
1438 31b836b8 Thomas Thrainer
    name_idx = field_idx[constants.SF_NAME]
1439 31b836b8 Thomas Thrainer
1440 31b836b8 Thomas Thrainer
    st_args = _GetStorageTypeArgs(self.cfg, self.op.storage_type)
1441 1c3231aa Thomas Thrainer
    data = self.rpc.call_storage_list(self.node_uuids,
1442 31b836b8 Thomas Thrainer
                                      self.op.storage_type, st_args,
1443 31b836b8 Thomas Thrainer
                                      self.op.name, fields)
1444 31b836b8 Thomas Thrainer
1445 31b836b8 Thomas Thrainer
    result = []
1446 31b836b8 Thomas Thrainer
1447 1c3231aa Thomas Thrainer
    for node_uuid in utils.NiceSort(self.node_uuids):
1448 1c3231aa Thomas Thrainer
      node_name = self.cfg.GetNodeName(node_uuid)
1449 1c3231aa Thomas Thrainer
      nresult = data[node_uuid]
1450 31b836b8 Thomas Thrainer
      if nresult.offline:
1451 31b836b8 Thomas Thrainer
        continue
1452 31b836b8 Thomas Thrainer
1453 31b836b8 Thomas Thrainer
      msg = nresult.fail_msg
1454 31b836b8 Thomas Thrainer
      if msg:
1455 1c3231aa Thomas Thrainer
        self.LogWarning("Can't get storage data from node %s: %s",
1456 1c3231aa Thomas Thrainer
                        node_name, msg)
1457 31b836b8 Thomas Thrainer
        continue
1458 31b836b8 Thomas Thrainer
1459 31b836b8 Thomas Thrainer
      rows = dict([(row[name_idx], row) for row in nresult.payload])
1460 31b836b8 Thomas Thrainer
1461 31b836b8 Thomas Thrainer
      for name in utils.NiceSort(rows.keys()):
1462 31b836b8 Thomas Thrainer
        row = rows[name]
1463 31b836b8 Thomas Thrainer
1464 31b836b8 Thomas Thrainer
        out = []
1465 31b836b8 Thomas Thrainer
1466 31b836b8 Thomas Thrainer
        for field in self.op.output_fields:
1467 31b836b8 Thomas Thrainer
          if field == constants.SF_NODE:
1468 1c3231aa Thomas Thrainer
            val = node_name
1469 31b836b8 Thomas Thrainer
          elif field == constants.SF_TYPE:
1470 31b836b8 Thomas Thrainer
            val = self.op.storage_type
1471 31b836b8 Thomas Thrainer
          elif field in field_idx:
1472 31b836b8 Thomas Thrainer
            val = row[field_idx[field]]
1473 31b836b8 Thomas Thrainer
          else:
1474 31b836b8 Thomas Thrainer
            raise errors.ParameterError(field)
1475 31b836b8 Thomas Thrainer
1476 31b836b8 Thomas Thrainer
          out.append(val)
1477 31b836b8 Thomas Thrainer
1478 31b836b8 Thomas Thrainer
        result.append(out)
1479 31b836b8 Thomas Thrainer
1480 31b836b8 Thomas Thrainer
    return result
1481 31b836b8 Thomas Thrainer
1482 31b836b8 Thomas Thrainer
1483 31b836b8 Thomas Thrainer
class LUNodeRemove(LogicalUnit):
1484 31b836b8 Thomas Thrainer
  """Logical unit for removing a node.
1485 31b836b8 Thomas Thrainer

1486 31b836b8 Thomas Thrainer
  """
1487 31b836b8 Thomas Thrainer
  HPATH = "node-remove"
1488 31b836b8 Thomas Thrainer
  HTYPE = constants.HTYPE_NODE
1489 31b836b8 Thomas Thrainer
1490 31b836b8 Thomas Thrainer
  def BuildHooksEnv(self):
1491 31b836b8 Thomas Thrainer
    """Build hooks env.
1492 31b836b8 Thomas Thrainer

1493 31b836b8 Thomas Thrainer
    """
1494 31b836b8 Thomas Thrainer
    return {
1495 31b836b8 Thomas Thrainer
      "OP_TARGET": self.op.node_name,
1496 31b836b8 Thomas Thrainer
      "NODE_NAME": self.op.node_name,
1497 31b836b8 Thomas Thrainer
      }
1498 31b836b8 Thomas Thrainer
1499 31b836b8 Thomas Thrainer
  def BuildHooksNodes(self):
1500 31b836b8 Thomas Thrainer
    """Build hooks nodes.
1501 31b836b8 Thomas Thrainer

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

1505 31b836b8 Thomas Thrainer
    """
1506 31b836b8 Thomas Thrainer
    all_nodes = self.cfg.GetNodeList()
1507 31b836b8 Thomas Thrainer
    try:
1508 1c3231aa Thomas Thrainer
      all_nodes.remove(self.op.node_uuid)
1509 31b836b8 Thomas Thrainer
    except ValueError:
1510 31b836b8 Thomas Thrainer
      pass
1511 31b836b8 Thomas Thrainer
    return (all_nodes, all_nodes)
1512 31b836b8 Thomas Thrainer
1513 31b836b8 Thomas Thrainer
  def CheckPrereq(self):
1514 31b836b8 Thomas Thrainer
    """Check prerequisites.
1515 31b836b8 Thomas Thrainer

1516 31b836b8 Thomas Thrainer
    This checks:
1517 31b836b8 Thomas Thrainer
     - the node exists in the configuration
1518 31b836b8 Thomas Thrainer
     - it does not have primary or secondary instances
1519 31b836b8 Thomas Thrainer
     - it's not the master
1520 31b836b8 Thomas Thrainer

1521 31b836b8 Thomas Thrainer
    Any errors are signaled by raising errors.OpPrereqError.
1522 31b836b8 Thomas Thrainer

1523 31b836b8 Thomas Thrainer
    """
1524 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
1525 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
1526 1c3231aa Thomas Thrainer
    node = self.cfg.GetNodeInfo(self.op.node_uuid)
1527 31b836b8 Thomas Thrainer
    assert node is not None
1528 31b836b8 Thomas Thrainer
1529 31b836b8 Thomas Thrainer
    masternode = self.cfg.GetMasterNode()
1530 1c3231aa Thomas Thrainer
    if node.uuid == masternode:
1531 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Node is the master node, failover to another"
1532 31b836b8 Thomas Thrainer
                                 " node is required", errors.ECODE_INVAL)
1533 31b836b8 Thomas Thrainer
1534 da4a52a3 Thomas Thrainer
    for _, instance in self.cfg.GetAllInstancesInfo().items():
1535 1c3231aa Thomas Thrainer
      if node.uuid in instance.all_nodes:
1536 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s is still running on the node,"
1537 da4a52a3 Thomas Thrainer
                                   " please remove first" % instance.name,
1538 31b836b8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1539 31b836b8 Thomas Thrainer
    self.op.node_name = node.name
1540 31b836b8 Thomas Thrainer
    self.node = node
1541 31b836b8 Thomas Thrainer
1542 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1543 31b836b8 Thomas Thrainer
    """Removes the node from the cluster.
1544 31b836b8 Thomas Thrainer

1545 31b836b8 Thomas Thrainer
    """
1546 31b836b8 Thomas Thrainer
    logging.info("Stopping the node daemon and removing configs from node %s",
1547 d0d7d7cf Thomas Thrainer
                 self.node.name)
1548 31b836b8 Thomas Thrainer
1549 31b836b8 Thomas Thrainer
    modify_ssh_setup = self.cfg.GetClusterInfo().modify_ssh_setup
1550 31b836b8 Thomas Thrainer
1551 31b836b8 Thomas Thrainer
    assert locking.BGL in self.owned_locks(locking.LEVEL_CLUSTER), \
1552 31b836b8 Thomas Thrainer
      "Not owning BGL"
1553 31b836b8 Thomas Thrainer
1554 31b836b8 Thomas Thrainer
    # Promote nodes to master candidate as needed
1555 d0d7d7cf Thomas Thrainer
    AdjustCandidatePool(self, exceptions=[self.node.uuid])
1556 d0d7d7cf Thomas Thrainer
    self.context.RemoveNode(self.node)
1557 31b836b8 Thomas Thrainer
1558 31b836b8 Thomas Thrainer
    # Run post hooks on the node before it's removed
1559 d0d7d7cf Thomas Thrainer
    RunPostHook(self, self.node.name)
1560 31b836b8 Thomas Thrainer
1561 1c3231aa Thomas Thrainer
    # we have to call this by name rather than by UUID, as the node is no longer
1562 1c3231aa Thomas Thrainer
    # in the config
1563 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_node_leave_cluster(self.node.name, modify_ssh_setup)
1564 31b836b8 Thomas Thrainer
    msg = result.fail_msg
1565 31b836b8 Thomas Thrainer
    if msg:
1566 31b836b8 Thomas Thrainer
      self.LogWarning("Errors encountered on the remote node while leaving"
1567 31b836b8 Thomas Thrainer
                      " the cluster: %s", msg)
1568 31b836b8 Thomas Thrainer
1569 31b836b8 Thomas Thrainer
    # Remove node from our /etc/hosts
1570 31b836b8 Thomas Thrainer
    if self.cfg.GetClusterInfo().modify_etc_hosts:
1571 1c3231aa Thomas Thrainer
      master_node_uuid = self.cfg.GetMasterNode()
1572 1c3231aa Thomas Thrainer
      result = self.rpc.call_etc_hosts_modify(master_node_uuid,
1573 31b836b8 Thomas Thrainer
                                              constants.ETC_HOSTS_REMOVE,
1574 d0d7d7cf Thomas Thrainer
                                              self.node.name, None)
1575 31b836b8 Thomas Thrainer
      result.Raise("Can't update hosts file with new host data")
1576 5eacbcae Thomas Thrainer
      RedistributeAncillaryFiles(self)
1577 31b836b8 Thomas Thrainer
1578 31b836b8 Thomas Thrainer
1579 31b836b8 Thomas Thrainer
class LURepairNodeStorage(NoHooksLU):
1580 31b836b8 Thomas Thrainer
  """Repairs the volume group on a node.
1581 31b836b8 Thomas Thrainer

1582 31b836b8 Thomas Thrainer
  """
1583 31b836b8 Thomas Thrainer
  REQ_BGL = False
1584 31b836b8 Thomas Thrainer
1585 31b836b8 Thomas Thrainer
  def CheckArguments(self):
1586 1c3231aa Thomas Thrainer
    (self.op.node_uuid, self.op.node_name) = \
1587 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.node_uuid, self.op.node_name)
1588 31b836b8 Thomas Thrainer
1589 31b836b8 Thomas Thrainer
    storage_type = self.op.storage_type
1590 31b836b8 Thomas Thrainer
1591 31b836b8 Thomas Thrainer
    if (constants.SO_FIX_CONSISTENCY not in
1592 31b836b8 Thomas Thrainer
        constants.VALID_STORAGE_OPERATIONS.get(storage_type, [])):
1593 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("Storage units of type '%s' can not be"
1594 31b836b8 Thomas Thrainer
                                 " repaired" % storage_type,
1595 31b836b8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1596 31b836b8 Thomas Thrainer
1597 31b836b8 Thomas Thrainer
  def ExpandNames(self):
1598 31b836b8 Thomas Thrainer
    self.needed_locks = {
1599 1c3231aa Thomas Thrainer
      locking.LEVEL_NODE: [self.op.node_uuid],
1600 31b836b8 Thomas Thrainer
      }
1601 31b836b8 Thomas Thrainer
1602 1c3231aa Thomas Thrainer
  def _CheckFaultyDisks(self, instance, node_uuid):
1603 31b836b8 Thomas Thrainer
    """Ensure faulty disks abort the opcode or at least warn."""
1604 31b836b8 Thomas Thrainer
    try:
1605 5eacbcae Thomas Thrainer
      if FindFaultyInstanceDisks(self.cfg, self.rpc, instance,
1606 1c3231aa Thomas Thrainer
                                 node_uuid, True):
1607 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Instance '%s' has faulty disks on"
1608 1c3231aa Thomas Thrainer
                                   " node '%s'" %
1609 1c3231aa Thomas Thrainer
                                   (instance.name,
1610 1c3231aa Thomas Thrainer
                                    self.cfg.GetNodeName(node_uuid)),
1611 31b836b8 Thomas Thrainer
                                   errors.ECODE_STATE)
1612 31b836b8 Thomas Thrainer
    except errors.OpPrereqError, err:
1613 31b836b8 Thomas Thrainer
      if self.op.ignore_consistency:
1614 31b836b8 Thomas Thrainer
        self.LogWarning(str(err.args[0]))
1615 31b836b8 Thomas Thrainer
      else:
1616 31b836b8 Thomas Thrainer
        raise
1617 31b836b8 Thomas Thrainer
1618 31b836b8 Thomas Thrainer
  def CheckPrereq(self):
1619 31b836b8 Thomas Thrainer
    """Check prerequisites.
1620 31b836b8 Thomas Thrainer

1621 31b836b8 Thomas Thrainer
    """
1622 9d276e93 Helga Velroyen
    CheckStorageTypeEnabled(self.cfg.GetClusterInfo(), self.op.storage_type)
1623 9d276e93 Helga Velroyen
1624 31b836b8 Thomas Thrainer
    # Check whether any instance on this node has faulty disks
1625 1c3231aa Thomas Thrainer
    for inst in _GetNodeInstances(self.cfg, self.op.node_uuid):
1626 1d4a4b26 Thomas Thrainer
      if not inst.disks_active:
1627 31b836b8 Thomas Thrainer
        continue
1628 31b836b8 Thomas Thrainer
      check_nodes = set(inst.all_nodes)
1629 1c3231aa Thomas Thrainer
      check_nodes.discard(self.op.node_uuid)
1630 1c3231aa Thomas Thrainer
      for inst_node_uuid in check_nodes:
1631 1c3231aa Thomas Thrainer
        self._CheckFaultyDisks(inst, inst_node_uuid)
1632 31b836b8 Thomas Thrainer
1633 31b836b8 Thomas Thrainer
  def Exec(self, feedback_fn):
1634 31b836b8 Thomas Thrainer
    feedback_fn("Repairing storage unit '%s' on %s ..." %
1635 31b836b8 Thomas Thrainer
                (self.op.name, self.op.node_name))
1636 31b836b8 Thomas Thrainer
1637 31b836b8 Thomas Thrainer
    st_args = _GetStorageTypeArgs(self.cfg, self.op.storage_type)
1638 1c3231aa Thomas Thrainer
    result = self.rpc.call_storage_execute(self.op.node_uuid,
1639 31b836b8 Thomas Thrainer
                                           self.op.storage_type, st_args,
1640 31b836b8 Thomas Thrainer
                                           self.op.name,
1641 31b836b8 Thomas Thrainer
                                           constants.SO_FIX_CONSISTENCY)
1642 31b836b8 Thomas Thrainer
    result.Raise("Failed to repair storage unit '%s' on %s" %
1643 31b836b8 Thomas Thrainer
                 (self.op.name, self.op.node_name))