Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib.py @ 6b7d5878

History | View | Annotate | Download (326.5 kB)

1 2f31098c Iustin Pop
#
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 e7c6e02b Michael Hanselmann
# Copyright (C) 2006, 2007, 2008 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 880478f8 Iustin Pop
"""Module implementing the master-side code."""
23 a8083063 Iustin Pop
24 c70d2d9b Iustin Pop
# pylint: disable-msg=W0201
25 c70d2d9b Iustin Pop
26 c70d2d9b Iustin Pop
# W0201 since most LU attributes are defined in CheckPrereq or similar
27 c70d2d9b Iustin Pop
# functions
28 a8083063 Iustin Pop
29 a8083063 Iustin Pop
import os
30 a8083063 Iustin Pop
import os.path
31 a8083063 Iustin Pop
import time
32 a8083063 Iustin Pop
import re
33 a8083063 Iustin Pop
import platform
34 ffa1c0dc Iustin Pop
import logging
35 74409b12 Iustin Pop
import copy
36 b98bf262 Michael Hanselmann
import OpenSSL
37 a8083063 Iustin Pop
38 a8083063 Iustin Pop
from ganeti import ssh
39 a8083063 Iustin Pop
from ganeti import utils
40 a8083063 Iustin Pop
from ganeti import errors
41 a8083063 Iustin Pop
from ganeti import hypervisor
42 6048c986 Guido Trotter
from ganeti import locking
43 a8083063 Iustin Pop
from ganeti import constants
44 a8083063 Iustin Pop
from ganeti import objects
45 8d14b30d Iustin Pop
from ganeti import serializer
46 112f18a5 Iustin Pop
from ganeti import ssconf
47 d61df03e Iustin Pop
48 d61df03e Iustin Pop
49 a8083063 Iustin Pop
class LogicalUnit(object):
50 396e1b78 Michael Hanselmann
  """Logical Unit base class.
51 a8083063 Iustin Pop

52 a8083063 Iustin Pop
  Subclasses must follow these rules:
53 d465bdc8 Guido Trotter
    - implement ExpandNames
54 6fd35c4d Michael Hanselmann
    - implement CheckPrereq (except when tasklets are used)
55 6fd35c4d Michael Hanselmann
    - implement Exec (except when tasklets are used)
56 a8083063 Iustin Pop
    - implement BuildHooksEnv
57 a8083063 Iustin Pop
    - redefine HPATH and HTYPE
58 05f86716 Guido Trotter
    - optionally redefine their run requirements:
59 7e55040e Guido Trotter
        REQ_BGL: the LU needs to hold the Big Ganeti Lock exclusively
60 05f86716 Guido Trotter

61 05f86716 Guido Trotter
  Note that all commands require root permissions.
62 a8083063 Iustin Pop

63 20777413 Iustin Pop
  @ivar dry_run_result: the value (if any) that will be returned to the caller
64 20777413 Iustin Pop
      in dry-run mode (signalled by opcode dry_run parameter)
65 20777413 Iustin Pop

66 a8083063 Iustin Pop
  """
67 a8083063 Iustin Pop
  HPATH = None
68 a8083063 Iustin Pop
  HTYPE = None
69 a8083063 Iustin Pop
  _OP_REQP = []
70 7e55040e Guido Trotter
  REQ_BGL = True
71 a8083063 Iustin Pop
72 72737a7f Iustin Pop
  def __init__(self, processor, op, context, rpc):
73 a8083063 Iustin Pop
    """Constructor for LogicalUnit.
74 a8083063 Iustin Pop

75 5bbd3f7f Michael Hanselmann
    This needs to be overridden in derived classes in order to check op
76 a8083063 Iustin Pop
    validity.
77 a8083063 Iustin Pop

78 a8083063 Iustin Pop
    """
79 5bfac263 Iustin Pop
    self.proc = processor
80 a8083063 Iustin Pop
    self.op = op
81 77b657a3 Guido Trotter
    self.cfg = context.cfg
82 77b657a3 Guido Trotter
    self.context = context
83 72737a7f Iustin Pop
    self.rpc = rpc
84 ca2a79e1 Guido Trotter
    # Dicts used to declare locking needs to mcpu
85 d465bdc8 Guido Trotter
    self.needed_locks = None
86 6683bba2 Guido Trotter
    self.acquired_locks = {}
87 c772d142 Michael Hanselmann
    self.share_locks = dict.fromkeys(locking.LEVELS, 0)
88 ca2a79e1 Guido Trotter
    self.add_locks = {}
89 ca2a79e1 Guido Trotter
    self.remove_locks = {}
90 c4a2fee1 Guido Trotter
    # Used to force good behavior when calling helper functions
91 c4a2fee1 Guido Trotter
    self.recalculate_locks = {}
92 c92b310a Michael Hanselmann
    self.__ssh = None
93 86d9d3bb Iustin Pop
    # logging
94 fe267188 Iustin Pop
    self.LogWarning = processor.LogWarning # pylint: disable-msg=C0103
95 fe267188 Iustin Pop
    self.LogInfo = processor.LogInfo # pylint: disable-msg=C0103
96 d984846d Iustin Pop
    self.LogStep = processor.LogStep # pylint: disable-msg=C0103
97 20777413 Iustin Pop
    # support for dry-run
98 20777413 Iustin Pop
    self.dry_run_result = None
99 ee844e20 Iustin Pop
    # support for generic debug attribute
100 ee844e20 Iustin Pop
    if (not hasattr(self.op, "debug_level") or
101 ee844e20 Iustin Pop
        not isinstance(self.op.debug_level, int)):
102 ee844e20 Iustin Pop
      self.op.debug_level = 0
103 c92b310a Michael Hanselmann
104 6fd35c4d Michael Hanselmann
    # Tasklets
105 3a012b41 Michael Hanselmann
    self.tasklets = None
106 6fd35c4d Michael Hanselmann
107 a8083063 Iustin Pop
    for attr_name in self._OP_REQP:
108 a8083063 Iustin Pop
      attr_val = getattr(op, attr_name, None)
109 a8083063 Iustin Pop
      if attr_val is None:
110 3ecf6786 Iustin Pop
        raise errors.OpPrereqError("Required parameter '%s' missing" %
111 5c983ee5 Iustin Pop
                                   attr_name, errors.ECODE_INVAL)
112 6fd35c4d Michael Hanselmann
113 4be4691d Iustin Pop
    self.CheckArguments()
114 a8083063 Iustin Pop
115 c92b310a Michael Hanselmann
  def __GetSSH(self):
116 c92b310a Michael Hanselmann
    """Returns the SshRunner object
117 c92b310a Michael Hanselmann

118 c92b310a Michael Hanselmann
    """
119 c92b310a Michael Hanselmann
    if not self.__ssh:
120 6b0469d2 Iustin Pop
      self.__ssh = ssh.SshRunner(self.cfg.GetClusterName())
121 c92b310a Michael Hanselmann
    return self.__ssh
122 c92b310a Michael Hanselmann
123 c92b310a Michael Hanselmann
  ssh = property(fget=__GetSSH)
124 c92b310a Michael Hanselmann
125 4be4691d Iustin Pop
  def CheckArguments(self):
126 4be4691d Iustin Pop
    """Check syntactic validity for the opcode arguments.
127 4be4691d Iustin Pop

128 4be4691d Iustin Pop
    This method is for doing a simple syntactic check and ensure
129 4be4691d Iustin Pop
    validity of opcode parameters, without any cluster-related
130 4be4691d Iustin Pop
    checks. While the same can be accomplished in ExpandNames and/or
131 4be4691d Iustin Pop
    CheckPrereq, doing these separate is better because:
132 4be4691d Iustin Pop

133 4be4691d Iustin Pop
      - ExpandNames is left as as purely a lock-related function
134 5bbd3f7f Michael Hanselmann
      - CheckPrereq is run after we have acquired locks (and possible
135 4be4691d Iustin Pop
        waited for them)
136 4be4691d Iustin Pop

137 4be4691d Iustin Pop
    The function is allowed to change the self.op attribute so that
138 4be4691d Iustin Pop
    later methods can no longer worry about missing parameters.
139 4be4691d Iustin Pop

140 4be4691d Iustin Pop
    """
141 4be4691d Iustin Pop
    pass
142 4be4691d Iustin Pop
143 d465bdc8 Guido Trotter
  def ExpandNames(self):
144 d465bdc8 Guido Trotter
    """Expand names for this LU.
145 d465bdc8 Guido Trotter

146 d465bdc8 Guido Trotter
    This method is called before starting to execute the opcode, and it should
147 d465bdc8 Guido Trotter
    update all the parameters of the opcode to their canonical form (e.g. a
148 d465bdc8 Guido Trotter
    short node name must be fully expanded after this method has successfully
149 d465bdc8 Guido Trotter
    completed). This way locking, hooks, logging, ecc. can work correctly.
150 d465bdc8 Guido Trotter

151 d465bdc8 Guido Trotter
    LUs which implement this method must also populate the self.needed_locks
152 d465bdc8 Guido Trotter
    member, as a dict with lock levels as keys, and a list of needed lock names
153 d465bdc8 Guido Trotter
    as values. Rules:
154 e4376078 Iustin Pop

155 e4376078 Iustin Pop
      - use an empty dict if you don't need any lock
156 e4376078 Iustin Pop
      - if you don't need any lock at a particular level omit that level
157 e4376078 Iustin Pop
      - don't put anything for the BGL level
158 e4376078 Iustin Pop
      - if you want all locks at a level use locking.ALL_SET as a value
159 d465bdc8 Guido Trotter

160 3977a4c1 Guido Trotter
    If you need to share locks (rather than acquire them exclusively) at one
161 3977a4c1 Guido Trotter
    level you can modify self.share_locks, setting a true value (usually 1) for
162 3977a4c1 Guido Trotter
    that level. By default locks are not shared.
163 3977a4c1 Guido Trotter

164 6fd35c4d Michael Hanselmann
    This function can also define a list of tasklets, which then will be
165 6fd35c4d Michael Hanselmann
    executed in order instead of the usual LU-level CheckPrereq and Exec
166 6fd35c4d Michael Hanselmann
    functions, if those are not defined by the LU.
167 6fd35c4d Michael Hanselmann

168 e4376078 Iustin Pop
    Examples::
169 e4376078 Iustin Pop

170 e4376078 Iustin Pop
      # Acquire all nodes and one instance
171 e4376078 Iustin Pop
      self.needed_locks = {
172 e4376078 Iustin Pop
        locking.LEVEL_NODE: locking.ALL_SET,
173 e4376078 Iustin Pop
        locking.LEVEL_INSTANCE: ['instance1.example.tld'],
174 e4376078 Iustin Pop
      }
175 e4376078 Iustin Pop
      # Acquire just two nodes
176 e4376078 Iustin Pop
      self.needed_locks = {
177 e4376078 Iustin Pop
        locking.LEVEL_NODE: ['node1.example.tld', 'node2.example.tld'],
178 e4376078 Iustin Pop
      }
179 e4376078 Iustin Pop
      # Acquire no locks
180 e4376078 Iustin Pop
      self.needed_locks = {} # No, you can't leave it to the default value None
181 d465bdc8 Guido Trotter

182 d465bdc8 Guido Trotter
    """
183 d465bdc8 Guido Trotter
    # The implementation of this method is mandatory only if the new LU is
184 d465bdc8 Guido Trotter
    # concurrent, so that old LUs don't need to be changed all at the same
185 d465bdc8 Guido Trotter
    # time.
186 d465bdc8 Guido Trotter
    if self.REQ_BGL:
187 d465bdc8 Guido Trotter
      self.needed_locks = {} # Exclusive LUs don't need locks.
188 d465bdc8 Guido Trotter
    else:
189 d465bdc8 Guido Trotter
      raise NotImplementedError
190 d465bdc8 Guido Trotter
191 fb8dcb62 Guido Trotter
  def DeclareLocks(self, level):
192 fb8dcb62 Guido Trotter
    """Declare LU locking needs for a level
193 fb8dcb62 Guido Trotter

194 fb8dcb62 Guido Trotter
    While most LUs can just declare their locking needs at ExpandNames time,
195 fb8dcb62 Guido Trotter
    sometimes there's the need to calculate some locks after having acquired
196 fb8dcb62 Guido Trotter
    the ones before. This function is called just before acquiring locks at a
197 fb8dcb62 Guido Trotter
    particular level, but after acquiring the ones at lower levels, and permits
198 fb8dcb62 Guido Trotter
    such calculations. It can be used to modify self.needed_locks, and by
199 fb8dcb62 Guido Trotter
    default it does nothing.
200 fb8dcb62 Guido Trotter

201 fb8dcb62 Guido Trotter
    This function is only called if you have something already set in
202 fb8dcb62 Guido Trotter
    self.needed_locks for the level.
203 fb8dcb62 Guido Trotter

204 fb8dcb62 Guido Trotter
    @param level: Locking level which is going to be locked
205 fb8dcb62 Guido Trotter
    @type level: member of ganeti.locking.LEVELS
206 fb8dcb62 Guido Trotter

207 fb8dcb62 Guido Trotter
    """
208 fb8dcb62 Guido Trotter
209 a8083063 Iustin Pop
  def CheckPrereq(self):
210 a8083063 Iustin Pop
    """Check prerequisites for this LU.
211 a8083063 Iustin Pop

212 a8083063 Iustin Pop
    This method should check that the prerequisites for the execution
213 a8083063 Iustin Pop
    of this LU are fulfilled. It can do internode communication, but
214 a8083063 Iustin Pop
    it should be idempotent - no cluster or system changes are
215 a8083063 Iustin Pop
    allowed.
216 a8083063 Iustin Pop

217 a8083063 Iustin Pop
    The method should raise errors.OpPrereqError in case something is
218 a8083063 Iustin Pop
    not fulfilled. Its return value is ignored.
219 a8083063 Iustin Pop

220 a8083063 Iustin Pop
    This method should also update all the parameters of the opcode to
221 d465bdc8 Guido Trotter
    their canonical form if it hasn't been done by ExpandNames before.
222 a8083063 Iustin Pop

223 a8083063 Iustin Pop
    """
224 3a012b41 Michael Hanselmann
    if self.tasklets is not None:
225 b4a9eb66 Michael Hanselmann
      for (idx, tl) in enumerate(self.tasklets):
226 abae1b2b Michael Hanselmann
        logging.debug("Checking prerequisites for tasklet %s/%s",
227 abae1b2b Michael Hanselmann
                      idx + 1, len(self.tasklets))
228 6fd35c4d Michael Hanselmann
        tl.CheckPrereq()
229 6fd35c4d Michael Hanselmann
    else:
230 6fd35c4d Michael Hanselmann
      raise NotImplementedError
231 a8083063 Iustin Pop
232 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
233 a8083063 Iustin Pop
    """Execute the LU.
234 a8083063 Iustin Pop

235 a8083063 Iustin Pop
    This method should implement the actual work. It should raise
236 a8083063 Iustin Pop
    errors.OpExecError for failures that are somewhat dealt with in
237 a8083063 Iustin Pop
    code, or expected.
238 a8083063 Iustin Pop

239 a8083063 Iustin Pop
    """
240 3a012b41 Michael Hanselmann
    if self.tasklets is not None:
241 b4a9eb66 Michael Hanselmann
      for (idx, tl) in enumerate(self.tasklets):
242 abae1b2b Michael Hanselmann
        logging.debug("Executing tasklet %s/%s", idx + 1, len(self.tasklets))
243 6fd35c4d Michael Hanselmann
        tl.Exec(feedback_fn)
244 6fd35c4d Michael Hanselmann
    else:
245 6fd35c4d Michael Hanselmann
      raise NotImplementedError
246 a8083063 Iustin Pop
247 a8083063 Iustin Pop
  def BuildHooksEnv(self):
248 a8083063 Iustin Pop
    """Build hooks environment for this LU.
249 a8083063 Iustin Pop

250 a8083063 Iustin Pop
    This method should return a three-node tuple consisting of: a dict
251 a8083063 Iustin Pop
    containing the environment that will be used for running the
252 a8083063 Iustin Pop
    specific hook for this LU, a list of node names on which the hook
253 a8083063 Iustin Pop
    should run before the execution, and a list of node names on which
254 a8083063 Iustin Pop
    the hook should run after the execution.
255 a8083063 Iustin Pop

256 a8083063 Iustin Pop
    The keys of the dict must not have 'GANETI_' prefixed as this will
257 a8083063 Iustin Pop
    be handled in the hooks runner. Also note additional keys will be
258 a8083063 Iustin Pop
    added by the hooks runner. If the LU doesn't define any
259 a8083063 Iustin Pop
    environment, an empty dict (and not None) should be returned.
260 a8083063 Iustin Pop

261 8a3fe350 Guido Trotter
    No nodes should be returned as an empty list (and not None).
262 a8083063 Iustin Pop

263 a8083063 Iustin Pop
    Note that if the HPATH for a LU class is None, this function will
264 a8083063 Iustin Pop
    not be called.
265 a8083063 Iustin Pop

266 a8083063 Iustin Pop
    """
267 a8083063 Iustin Pop
    raise NotImplementedError
268 a8083063 Iustin Pop
269 1fce5219 Guido Trotter
  def HooksCallBack(self, phase, hook_results, feedback_fn, lu_result):
270 1fce5219 Guido Trotter
    """Notify the LU about the results of its hooks.
271 1fce5219 Guido Trotter

272 1fce5219 Guido Trotter
    This method is called every time a hooks phase is executed, and notifies
273 1fce5219 Guido Trotter
    the Logical Unit about the hooks' result. The LU can then use it to alter
274 1fce5219 Guido Trotter
    its result based on the hooks.  By default the method does nothing and the
275 1fce5219 Guido Trotter
    previous result is passed back unchanged but any LU can define it if it
276 1fce5219 Guido Trotter
    wants to use the local cluster hook-scripts somehow.
277 1fce5219 Guido Trotter

278 e4376078 Iustin Pop
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
279 e4376078 Iustin Pop
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
280 e4376078 Iustin Pop
    @param hook_results: the results of the multi-node hooks rpc call
281 e4376078 Iustin Pop
    @param feedback_fn: function used send feedback back to the caller
282 e4376078 Iustin Pop
    @param lu_result: the previous Exec result this LU had, or None
283 e4376078 Iustin Pop
        in the PRE phase
284 e4376078 Iustin Pop
    @return: the new Exec result, based on the previous result
285 e4376078 Iustin Pop
        and hook results
286 1fce5219 Guido Trotter

287 1fce5219 Guido Trotter
    """
288 2d54e29c Iustin Pop
    # API must be kept, thus we ignore the unused argument and could
289 2d54e29c Iustin Pop
    # be a function warnings
290 2d54e29c Iustin Pop
    # pylint: disable-msg=W0613,R0201
291 1fce5219 Guido Trotter
    return lu_result
292 1fce5219 Guido Trotter
293 43905206 Guido Trotter
  def _ExpandAndLockInstance(self):
294 43905206 Guido Trotter
    """Helper function to expand and lock an instance.
295 43905206 Guido Trotter

296 43905206 Guido Trotter
    Many LUs that work on an instance take its name in self.op.instance_name
297 43905206 Guido Trotter
    and need to expand it and then declare the expanded name for locking. This
298 43905206 Guido Trotter
    function does it, and then updates self.op.instance_name to the expanded
299 43905206 Guido Trotter
    name. It also initializes needed_locks as a dict, if this hasn't been done
300 43905206 Guido Trotter
    before.
301 43905206 Guido Trotter

302 43905206 Guido Trotter
    """
303 43905206 Guido Trotter
    if self.needed_locks is None:
304 43905206 Guido Trotter
      self.needed_locks = {}
305 43905206 Guido Trotter
    else:
306 43905206 Guido Trotter
      assert locking.LEVEL_INSTANCE not in self.needed_locks, \
307 43905206 Guido Trotter
        "_ExpandAndLockInstance called with instance-level locks set"
308 cf26a87a Iustin Pop
    self.op.instance_name = _ExpandInstanceName(self.cfg,
309 cf26a87a Iustin Pop
                                                self.op.instance_name)
310 cf26a87a Iustin Pop
    self.needed_locks[locking.LEVEL_INSTANCE] = self.op.instance_name
311 43905206 Guido Trotter
312 a82ce292 Guido Trotter
  def _LockInstancesNodes(self, primary_only=False):
313 c4a2fee1 Guido Trotter
    """Helper function to declare instances' nodes for locking.
314 c4a2fee1 Guido Trotter

315 c4a2fee1 Guido Trotter
    This function should be called after locking one or more instances to lock
316 c4a2fee1 Guido Trotter
    their nodes. Its effect is populating self.needed_locks[locking.LEVEL_NODE]
317 c4a2fee1 Guido Trotter
    with all primary or secondary nodes for instances already locked and
318 c4a2fee1 Guido Trotter
    present in self.needed_locks[locking.LEVEL_INSTANCE].
319 c4a2fee1 Guido Trotter

320 c4a2fee1 Guido Trotter
    It should be called from DeclareLocks, and for safety only works if
321 c4a2fee1 Guido Trotter
    self.recalculate_locks[locking.LEVEL_NODE] is set.
322 c4a2fee1 Guido Trotter

323 c4a2fee1 Guido Trotter
    In the future it may grow parameters to just lock some instance's nodes, or
324 c4a2fee1 Guido Trotter
    to just lock primaries or secondary nodes, if needed.
325 c4a2fee1 Guido Trotter

326 e4376078 Iustin Pop
    If should be called in DeclareLocks in a way similar to::
327 c4a2fee1 Guido Trotter

328 e4376078 Iustin Pop
      if level == locking.LEVEL_NODE:
329 e4376078 Iustin Pop
        self._LockInstancesNodes()
330 c4a2fee1 Guido Trotter

331 a82ce292 Guido Trotter
    @type primary_only: boolean
332 a82ce292 Guido Trotter
    @param primary_only: only lock primary nodes of locked instances
333 a82ce292 Guido Trotter

334 c4a2fee1 Guido Trotter
    """
335 c4a2fee1 Guido Trotter
    assert locking.LEVEL_NODE in self.recalculate_locks, \
336 c4a2fee1 Guido Trotter
      "_LockInstancesNodes helper function called with no nodes to recalculate"
337 c4a2fee1 Guido Trotter
338 c4a2fee1 Guido Trotter
    # TODO: check if we're really been called with the instance locks held
339 c4a2fee1 Guido Trotter
340 c4a2fee1 Guido Trotter
    # For now we'll replace self.needed_locks[locking.LEVEL_NODE], but in the
341 c4a2fee1 Guido Trotter
    # future we might want to have different behaviors depending on the value
342 c4a2fee1 Guido Trotter
    # of self.recalculate_locks[locking.LEVEL_NODE]
343 c4a2fee1 Guido Trotter
    wanted_nodes = []
344 6683bba2 Guido Trotter
    for instance_name in self.acquired_locks[locking.LEVEL_INSTANCE]:
345 c4a2fee1 Guido Trotter
      instance = self.context.cfg.GetInstanceInfo(instance_name)
346 c4a2fee1 Guido Trotter
      wanted_nodes.append(instance.primary_node)
347 a82ce292 Guido Trotter
      if not primary_only:
348 a82ce292 Guido Trotter
        wanted_nodes.extend(instance.secondary_nodes)
349 9513b6ab Guido Trotter
350 9513b6ab Guido Trotter
    if self.recalculate_locks[locking.LEVEL_NODE] == constants.LOCKS_REPLACE:
351 9513b6ab Guido Trotter
      self.needed_locks[locking.LEVEL_NODE] = wanted_nodes
352 9513b6ab Guido Trotter
    elif self.recalculate_locks[locking.LEVEL_NODE] == constants.LOCKS_APPEND:
353 9513b6ab Guido Trotter
      self.needed_locks[locking.LEVEL_NODE].extend(wanted_nodes)
354 c4a2fee1 Guido Trotter
355 c4a2fee1 Guido Trotter
    del self.recalculate_locks[locking.LEVEL_NODE]
356 c4a2fee1 Guido Trotter
357 a8083063 Iustin Pop
358 fe267188 Iustin Pop
class NoHooksLU(LogicalUnit): # pylint: disable-msg=W0223
359 a8083063 Iustin Pop
  """Simple LU which runs no hooks.
360 a8083063 Iustin Pop

361 a8083063 Iustin Pop
  This LU is intended as a parent for other LogicalUnits which will
362 a8083063 Iustin Pop
  run no hooks, in order to reduce duplicate code.
363 a8083063 Iustin Pop

364 a8083063 Iustin Pop
  """
365 a8083063 Iustin Pop
  HPATH = None
366 a8083063 Iustin Pop
  HTYPE = None
367 a8083063 Iustin Pop
368 fc8a6b8f Iustin Pop
  def BuildHooksEnv(self):
369 fc8a6b8f Iustin Pop
    """Empty BuildHooksEnv for NoHooksLu.
370 fc8a6b8f Iustin Pop

371 fc8a6b8f Iustin Pop
    This just raises an error.
372 fc8a6b8f Iustin Pop

373 fc8a6b8f Iustin Pop
    """
374 fc8a6b8f Iustin Pop
    assert False, "BuildHooksEnv called for NoHooksLUs"
375 fc8a6b8f Iustin Pop
376 a8083063 Iustin Pop
377 9a6800e1 Michael Hanselmann
class Tasklet:
378 9a6800e1 Michael Hanselmann
  """Tasklet base class.
379 9a6800e1 Michael Hanselmann

380 9a6800e1 Michael Hanselmann
  Tasklets are subcomponents for LUs. LUs can consist entirely of tasklets or
381 9a6800e1 Michael Hanselmann
  they can mix legacy code with tasklets. Locking needs to be done in the LU,
382 9a6800e1 Michael Hanselmann
  tasklets know nothing about locks.
383 9a6800e1 Michael Hanselmann

384 9a6800e1 Michael Hanselmann
  Subclasses must follow these rules:
385 9a6800e1 Michael Hanselmann
    - Implement CheckPrereq
386 9a6800e1 Michael Hanselmann
    - Implement Exec
387 9a6800e1 Michael Hanselmann

388 9a6800e1 Michael Hanselmann
  """
389 464243a7 Michael Hanselmann
  def __init__(self, lu):
390 464243a7 Michael Hanselmann
    self.lu = lu
391 464243a7 Michael Hanselmann
392 464243a7 Michael Hanselmann
    # Shortcuts
393 464243a7 Michael Hanselmann
    self.cfg = lu.cfg
394 464243a7 Michael Hanselmann
    self.rpc = lu.rpc
395 464243a7 Michael Hanselmann
396 9a6800e1 Michael Hanselmann
  def CheckPrereq(self):
397 9a6800e1 Michael Hanselmann
    """Check prerequisites for this tasklets.
398 9a6800e1 Michael Hanselmann

399 9a6800e1 Michael Hanselmann
    This method should check whether the prerequisites for the execution of
400 9a6800e1 Michael Hanselmann
    this tasklet are fulfilled. It can do internode communication, but it
401 9a6800e1 Michael Hanselmann
    should be idempotent - no cluster or system changes are allowed.
402 9a6800e1 Michael Hanselmann

403 9a6800e1 Michael Hanselmann
    The method should raise errors.OpPrereqError in case something is not
404 9a6800e1 Michael Hanselmann
    fulfilled. Its return value is ignored.
405 9a6800e1 Michael Hanselmann

406 9a6800e1 Michael Hanselmann
    This method should also update all parameters to their canonical form if it
407 9a6800e1 Michael Hanselmann
    hasn't been done before.
408 9a6800e1 Michael Hanselmann

409 9a6800e1 Michael Hanselmann
    """
410 9a6800e1 Michael Hanselmann
    raise NotImplementedError
411 9a6800e1 Michael Hanselmann
412 9a6800e1 Michael Hanselmann
  def Exec(self, feedback_fn):
413 9a6800e1 Michael Hanselmann
    """Execute the tasklet.
414 9a6800e1 Michael Hanselmann

415 9a6800e1 Michael Hanselmann
    This method should implement the actual work. It should raise
416 9a6800e1 Michael Hanselmann
    errors.OpExecError for failures that are somewhat dealt with in code, or
417 9a6800e1 Michael Hanselmann
    expected.
418 9a6800e1 Michael Hanselmann

419 9a6800e1 Michael Hanselmann
    """
420 9a6800e1 Michael Hanselmann
    raise NotImplementedError
421 9a6800e1 Michael Hanselmann
422 9a6800e1 Michael Hanselmann
423 dcb93971 Michael Hanselmann
def _GetWantedNodes(lu, nodes):
424 a7ba5e53 Iustin Pop
  """Returns list of checked and expanded node names.
425 83120a01 Michael Hanselmann

426 e4376078 Iustin Pop
  @type lu: L{LogicalUnit}
427 e4376078 Iustin Pop
  @param lu: the logical unit on whose behalf we execute
428 e4376078 Iustin Pop
  @type nodes: list
429 e4376078 Iustin Pop
  @param nodes: list of node names or None for all nodes
430 e4376078 Iustin Pop
  @rtype: list
431 e4376078 Iustin Pop
  @return: the list of nodes, sorted
432 083a91c9 Iustin Pop
  @raise errors.ProgrammerError: if the nodes parameter is wrong type
433 83120a01 Michael Hanselmann

434 83120a01 Michael Hanselmann
  """
435 3312b702 Iustin Pop
  if not isinstance(nodes, list):
436 5c983ee5 Iustin Pop
    raise errors.OpPrereqError("Invalid argument type 'nodes'",
437 5c983ee5 Iustin Pop
                               errors.ECODE_INVAL)
438 dcb93971 Michael Hanselmann
439 ea47808a Guido Trotter
  if not nodes:
440 ea47808a Guido Trotter
    raise errors.ProgrammerError("_GetWantedNodes should only be called with a"
441 ea47808a Guido Trotter
      " non-empty list of nodes whose name is to be expanded.")
442 dcb93971 Michael Hanselmann
443 61dabca4 Iustin Pop
  wanted = [_ExpandNodeName(lu.cfg, name) for name in nodes]
444 a7ba5e53 Iustin Pop
  return utils.NiceSort(wanted)
445 3312b702 Iustin Pop
446 3312b702 Iustin Pop
447 3312b702 Iustin Pop
def _GetWantedInstances(lu, instances):
448 a7ba5e53 Iustin Pop
  """Returns list of checked and expanded instance names.
449 3312b702 Iustin Pop

450 e4376078 Iustin Pop
  @type lu: L{LogicalUnit}
451 e4376078 Iustin Pop
  @param lu: the logical unit on whose behalf we execute
452 e4376078 Iustin Pop
  @type instances: list
453 e4376078 Iustin Pop
  @param instances: list of instance names or None for all instances
454 e4376078 Iustin Pop
  @rtype: list
455 e4376078 Iustin Pop
  @return: the list of instances, sorted
456 e4376078 Iustin Pop
  @raise errors.OpPrereqError: if the instances parameter is wrong type
457 e4376078 Iustin Pop
  @raise errors.OpPrereqError: if any of the passed instances is not found
458 3312b702 Iustin Pop

459 3312b702 Iustin Pop
  """
460 3312b702 Iustin Pop
  if not isinstance(instances, list):
461 5c983ee5 Iustin Pop
    raise errors.OpPrereqError("Invalid argument type 'instances'",
462 5c983ee5 Iustin Pop
                               errors.ECODE_INVAL)
463 3312b702 Iustin Pop
464 3312b702 Iustin Pop
  if instances:
465 cf26a87a Iustin Pop
    wanted = [_ExpandInstanceName(lu.cfg, name) for name in instances]
466 3312b702 Iustin Pop
  else:
467 a7f5dc98 Iustin Pop
    wanted = utils.NiceSort(lu.cfg.GetInstanceList())
468 a7f5dc98 Iustin Pop
  return wanted
469 dcb93971 Michael Hanselmann
470 dcb93971 Michael Hanselmann
471 dcb93971 Michael Hanselmann
def _CheckOutputFields(static, dynamic, selected):
472 83120a01 Michael Hanselmann
  """Checks whether all selected fields are valid.
473 83120a01 Michael Hanselmann

474 a2d2e1a7 Iustin Pop
  @type static: L{utils.FieldSet}
475 31bf511f Iustin Pop
  @param static: static fields set
476 a2d2e1a7 Iustin Pop
  @type dynamic: L{utils.FieldSet}
477 31bf511f Iustin Pop
  @param dynamic: dynamic fields set
478 83120a01 Michael Hanselmann

479 83120a01 Michael Hanselmann
  """
480 a2d2e1a7 Iustin Pop
  f = utils.FieldSet()
481 31bf511f Iustin Pop
  f.Extend(static)
482 31bf511f Iustin Pop
  f.Extend(dynamic)
483 dcb93971 Michael Hanselmann
484 31bf511f Iustin Pop
  delta = f.NonMatching(selected)
485 31bf511f Iustin Pop
  if delta:
486 3ecf6786 Iustin Pop
    raise errors.OpPrereqError("Unknown output fields selected: %s"
487 5c983ee5 Iustin Pop
                               % ",".join(delta), errors.ECODE_INVAL)
488 dcb93971 Michael Hanselmann
489 dcb93971 Michael Hanselmann
490 a5961235 Iustin Pop
def _CheckBooleanOpField(op, name):
491 a5961235 Iustin Pop
  """Validates boolean opcode parameters.
492 a5961235 Iustin Pop

493 a5961235 Iustin Pop
  This will ensure that an opcode parameter is either a boolean value,
494 a5961235 Iustin Pop
  or None (but that it always exists).
495 a5961235 Iustin Pop

496 a5961235 Iustin Pop
  """
497 a5961235 Iustin Pop
  val = getattr(op, name, None)
498 a5961235 Iustin Pop
  if not (val is None or isinstance(val, bool)):
499 a5961235 Iustin Pop
    raise errors.OpPrereqError("Invalid boolean parameter '%s' (%s)" %
500 5c983ee5 Iustin Pop
                               (name, str(val)), errors.ECODE_INVAL)
501 a5961235 Iustin Pop
  setattr(op, name, val)
502 a5961235 Iustin Pop
503 a5961235 Iustin Pop
504 7736a5f2 Iustin Pop
def _CheckGlobalHvParams(params):
505 7736a5f2 Iustin Pop
  """Validates that given hypervisor params are not global ones.
506 7736a5f2 Iustin Pop

507 7736a5f2 Iustin Pop
  This will ensure that instances don't get customised versions of
508 7736a5f2 Iustin Pop
  global params.
509 7736a5f2 Iustin Pop

510 7736a5f2 Iustin Pop
  """
511 7736a5f2 Iustin Pop
  used_globals = constants.HVC_GLOBALS.intersection(params)
512 7736a5f2 Iustin Pop
  if used_globals:
513 7736a5f2 Iustin Pop
    msg = ("The following hypervisor parameters are global and cannot"
514 7736a5f2 Iustin Pop
           " be customized at instance level, please modify them at"
515 1f864b60 Iustin Pop
           " cluster level: %s" % utils.CommaJoin(used_globals))
516 7736a5f2 Iustin Pop
    raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
517 7736a5f2 Iustin Pop
518 7736a5f2 Iustin Pop
519 a5961235 Iustin Pop
def _CheckNodeOnline(lu, node):
520 a5961235 Iustin Pop
  """Ensure that a given node is online.
521 a5961235 Iustin Pop

522 a5961235 Iustin Pop
  @param lu: the LU on behalf of which we make the check
523 a5961235 Iustin Pop
  @param node: the node to check
524 733a2b6a Iustin Pop
  @raise errors.OpPrereqError: if the node is offline
525 a5961235 Iustin Pop

526 a5961235 Iustin Pop
  """
527 a5961235 Iustin Pop
  if lu.cfg.GetNodeInfo(node).offline:
528 5c983ee5 Iustin Pop
    raise errors.OpPrereqError("Can't use offline node %s" % node,
529 5c983ee5 Iustin Pop
                               errors.ECODE_INVAL)
530 a5961235 Iustin Pop
531 a5961235 Iustin Pop
532 733a2b6a Iustin Pop
def _CheckNodeNotDrained(lu, node):
533 733a2b6a Iustin Pop
  """Ensure that a given node is not drained.
534 733a2b6a Iustin Pop

535 733a2b6a Iustin Pop
  @param lu: the LU on behalf of which we make the check
536 733a2b6a Iustin Pop
  @param node: the node to check
537 733a2b6a Iustin Pop
  @raise errors.OpPrereqError: if the node is drained
538 733a2b6a Iustin Pop

539 733a2b6a Iustin Pop
  """
540 733a2b6a Iustin Pop
  if lu.cfg.GetNodeInfo(node).drained:
541 5c983ee5 Iustin Pop
    raise errors.OpPrereqError("Can't use drained node %s" % node,
542 5c983ee5 Iustin Pop
                               errors.ECODE_INVAL)
543 733a2b6a Iustin Pop
544 733a2b6a Iustin Pop
545 5d55819e Iustin Pop
def _CheckDiskTemplate(template):
546 5d55819e Iustin Pop
  """Ensure a given disk template is valid.
547 5d55819e Iustin Pop

548 5d55819e Iustin Pop
  """
549 5d55819e Iustin Pop
  if template not in constants.DISK_TEMPLATES:
550 5d55819e Iustin Pop
    msg = ("Invalid disk template name '%s', valid templates are: %s" %
551 5d55819e Iustin Pop
           (template, utils.CommaJoin(constants.DISK_TEMPLATES)))
552 5d55819e Iustin Pop
    raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
553 5d55819e Iustin Pop
554 5d55819e Iustin Pop
555 31624382 Iustin Pop
def _CheckInstanceDown(lu, instance, reason):
556 31624382 Iustin Pop
  """Ensure that an instance is not running."""
557 31624382 Iustin Pop
  if instance.admin_up:
558 31624382 Iustin Pop
    raise errors.OpPrereqError("Instance %s is marked to be up, %s" %
559 31624382 Iustin Pop
                               (instance.name, reason), errors.ECODE_STATE)
560 31624382 Iustin Pop
561 31624382 Iustin Pop
  pnode = instance.primary_node
562 31624382 Iustin Pop
  ins_l = lu.rpc.call_instance_list([pnode], [instance.hypervisor])[pnode]
563 31624382 Iustin Pop
  ins_l.Raise("Can't contact node %s for instance information" % pnode,
564 31624382 Iustin Pop
              prereq=True, ecode=errors.ECODE_ENVIRON)
565 31624382 Iustin Pop
566 31624382 Iustin Pop
  if instance.name in ins_l.payload:
567 31624382 Iustin Pop
    raise errors.OpPrereqError("Instance %s is running, %s" %
568 31624382 Iustin Pop
                               (instance.name, reason), errors.ECODE_STATE)
569 31624382 Iustin Pop
570 31624382 Iustin Pop
571 cf26a87a Iustin Pop
def _ExpandItemName(fn, name, kind):
572 cf26a87a Iustin Pop
  """Expand an item name.
573 cf26a87a Iustin Pop

574 cf26a87a Iustin Pop
  @param fn: the function to use for expansion
575 cf26a87a Iustin Pop
  @param name: requested item name
576 cf26a87a Iustin Pop
  @param kind: text description ('Node' or 'Instance')
577 cf26a87a Iustin Pop
  @return: the resolved (full) name
578 cf26a87a Iustin Pop
  @raise errors.OpPrereqError: if the item is not found
579 cf26a87a Iustin Pop

580 cf26a87a Iustin Pop
  """
581 cf26a87a Iustin Pop
  full_name = fn(name)
582 cf26a87a Iustin Pop
  if full_name is None:
583 cf26a87a Iustin Pop
    raise errors.OpPrereqError("%s '%s' not known" % (kind, name),
584 cf26a87a Iustin Pop
                               errors.ECODE_NOENT)
585 cf26a87a Iustin Pop
  return full_name
586 cf26a87a Iustin Pop
587 cf26a87a Iustin Pop
588 cf26a87a Iustin Pop
def _ExpandNodeName(cfg, name):
589 cf26a87a Iustin Pop
  """Wrapper over L{_ExpandItemName} for nodes."""
590 cf26a87a Iustin Pop
  return _ExpandItemName(cfg.ExpandNodeName, name, "Node")
591 cf26a87a Iustin Pop
592 cf26a87a Iustin Pop
593 cf26a87a Iustin Pop
def _ExpandInstanceName(cfg, name):
594 cf26a87a Iustin Pop
  """Wrapper over L{_ExpandItemName} for instance."""
595 cf26a87a Iustin Pop
  return _ExpandItemName(cfg.ExpandInstanceName, name, "Instance")
596 cf26a87a Iustin Pop
597 cf26a87a Iustin Pop
598 ecb215b5 Michael Hanselmann
def _BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status,
599 67fc3042 Iustin Pop
                          memory, vcpus, nics, disk_template, disks,
600 7c4d6c7b Michael Hanselmann
                          bep, hvp, hypervisor_name):
601 e4376078 Iustin Pop
  """Builds instance related env variables for hooks
602 e4376078 Iustin Pop

603 e4376078 Iustin Pop
  This builds the hook environment from individual variables.
604 e4376078 Iustin Pop

605 e4376078 Iustin Pop
  @type name: string
606 e4376078 Iustin Pop
  @param name: the name of the instance
607 e4376078 Iustin Pop
  @type primary_node: string
608 e4376078 Iustin Pop
  @param primary_node: the name of the instance's primary node
609 e4376078 Iustin Pop
  @type secondary_nodes: list
610 e4376078 Iustin Pop
  @param secondary_nodes: list of secondary nodes as strings
611 e4376078 Iustin Pop
  @type os_type: string
612 e4376078 Iustin Pop
  @param os_type: the name of the instance's OS
613 0d68c45d Iustin Pop
  @type status: boolean
614 0d68c45d Iustin Pop
  @param status: the should_run status of the instance
615 e4376078 Iustin Pop
  @type memory: string
616 e4376078 Iustin Pop
  @param memory: the memory size of the instance
617 e4376078 Iustin Pop
  @type vcpus: string
618 e4376078 Iustin Pop
  @param vcpus: the count of VCPUs the instance has
619 e4376078 Iustin Pop
  @type nics: list
620 5e3d3eb3 Guido Trotter
  @param nics: list of tuples (ip, mac, mode, link) representing
621 5e3d3eb3 Guido Trotter
      the NICs the instance has
622 2c2690c9 Iustin Pop
  @type disk_template: string
623 5bbd3f7f Michael Hanselmann
  @param disk_template: the disk template of the instance
624 2c2690c9 Iustin Pop
  @type disks: list
625 2c2690c9 Iustin Pop
  @param disks: the list of (size, mode) pairs
626 67fc3042 Iustin Pop
  @type bep: dict
627 67fc3042 Iustin Pop
  @param bep: the backend parameters for the instance
628 67fc3042 Iustin Pop
  @type hvp: dict
629 67fc3042 Iustin Pop
  @param hvp: the hypervisor parameters for the instance
630 7c4d6c7b Michael Hanselmann
  @type hypervisor_name: string
631 7c4d6c7b Michael Hanselmann
  @param hypervisor_name: the hypervisor for the instance
632 e4376078 Iustin Pop
  @rtype: dict
633 e4376078 Iustin Pop
  @return: the hook environment for this instance
634 ecb215b5 Michael Hanselmann

635 396e1b78 Michael Hanselmann
  """
636 0d68c45d Iustin Pop
  if status:
637 0d68c45d Iustin Pop
    str_status = "up"
638 0d68c45d Iustin Pop
  else:
639 0d68c45d Iustin Pop
    str_status = "down"
640 396e1b78 Michael Hanselmann
  env = {
641 0e137c28 Iustin Pop
    "OP_TARGET": name,
642 396e1b78 Michael Hanselmann
    "INSTANCE_NAME": name,
643 396e1b78 Michael Hanselmann
    "INSTANCE_PRIMARY": primary_node,
644 396e1b78 Michael Hanselmann
    "INSTANCE_SECONDARIES": " ".join(secondary_nodes),
645 ecb215b5 Michael Hanselmann
    "INSTANCE_OS_TYPE": os_type,
646 0d68c45d Iustin Pop
    "INSTANCE_STATUS": str_status,
647 396e1b78 Michael Hanselmann
    "INSTANCE_MEMORY": memory,
648 396e1b78 Michael Hanselmann
    "INSTANCE_VCPUS": vcpus,
649 2c2690c9 Iustin Pop
    "INSTANCE_DISK_TEMPLATE": disk_template,
650 7c4d6c7b Michael Hanselmann
    "INSTANCE_HYPERVISOR": hypervisor_name,
651 396e1b78 Michael Hanselmann
  }
652 396e1b78 Michael Hanselmann
653 396e1b78 Michael Hanselmann
  if nics:
654 396e1b78 Michael Hanselmann
    nic_count = len(nics)
655 62f0dd02 Guido Trotter
    for idx, (ip, mac, mode, link) in enumerate(nics):
656 396e1b78 Michael Hanselmann
      if ip is None:
657 396e1b78 Michael Hanselmann
        ip = ""
658 396e1b78 Michael Hanselmann
      env["INSTANCE_NIC%d_IP" % idx] = ip
659 2c2690c9 Iustin Pop
      env["INSTANCE_NIC%d_MAC" % idx] = mac
660 62f0dd02 Guido Trotter
      env["INSTANCE_NIC%d_MODE" % idx] = mode
661 62f0dd02 Guido Trotter
      env["INSTANCE_NIC%d_LINK" % idx] = link
662 62f0dd02 Guido Trotter
      if mode == constants.NIC_MODE_BRIDGED:
663 62f0dd02 Guido Trotter
        env["INSTANCE_NIC%d_BRIDGE" % idx] = link
664 396e1b78 Michael Hanselmann
  else:
665 396e1b78 Michael Hanselmann
    nic_count = 0
666 396e1b78 Michael Hanselmann
667 396e1b78 Michael Hanselmann
  env["INSTANCE_NIC_COUNT"] = nic_count
668 396e1b78 Michael Hanselmann
669 2c2690c9 Iustin Pop
  if disks:
670 2c2690c9 Iustin Pop
    disk_count = len(disks)
671 2c2690c9 Iustin Pop
    for idx, (size, mode) in enumerate(disks):
672 2c2690c9 Iustin Pop
      env["INSTANCE_DISK%d_SIZE" % idx] = size
673 2c2690c9 Iustin Pop
      env["INSTANCE_DISK%d_MODE" % idx] = mode
674 2c2690c9 Iustin Pop
  else:
675 2c2690c9 Iustin Pop
    disk_count = 0
676 2c2690c9 Iustin Pop
677 2c2690c9 Iustin Pop
  env["INSTANCE_DISK_COUNT"] = disk_count
678 2c2690c9 Iustin Pop
679 67fc3042 Iustin Pop
  for source, kind in [(bep, "BE"), (hvp, "HV")]:
680 67fc3042 Iustin Pop
    for key, value in source.items():
681 67fc3042 Iustin Pop
      env["INSTANCE_%s_%s" % (kind, key)] = value
682 67fc3042 Iustin Pop
683 396e1b78 Michael Hanselmann
  return env
684 396e1b78 Michael Hanselmann
685 96acbc09 Michael Hanselmann
686 f9b10246 Guido Trotter
def _NICListToTuple(lu, nics):
687 62f0dd02 Guido Trotter
  """Build a list of nic information tuples.
688 62f0dd02 Guido Trotter

689 f9b10246 Guido Trotter
  This list is suitable to be passed to _BuildInstanceHookEnv or as a return
690 f9b10246 Guido Trotter
  value in LUQueryInstanceData.
691 62f0dd02 Guido Trotter

692 62f0dd02 Guido Trotter
  @type lu:  L{LogicalUnit}
693 62f0dd02 Guido Trotter
  @param lu: the logical unit on whose behalf we execute
694 62f0dd02 Guido Trotter
  @type nics: list of L{objects.NIC}
695 62f0dd02 Guido Trotter
  @param nics: list of nics to convert to hooks tuples
696 62f0dd02 Guido Trotter

697 62f0dd02 Guido Trotter
  """
698 62f0dd02 Guido Trotter
  hooks_nics = []
699 62f0dd02 Guido Trotter
  c_nicparams = lu.cfg.GetClusterInfo().nicparams[constants.PP_DEFAULT]
700 62f0dd02 Guido Trotter
  for nic in nics:
701 62f0dd02 Guido Trotter
    ip = nic.ip
702 62f0dd02 Guido Trotter
    mac = nic.mac
703 62f0dd02 Guido Trotter
    filled_params = objects.FillDict(c_nicparams, nic.nicparams)
704 62f0dd02 Guido Trotter
    mode = filled_params[constants.NIC_MODE]
705 62f0dd02 Guido Trotter
    link = filled_params[constants.NIC_LINK]
706 62f0dd02 Guido Trotter
    hooks_nics.append((ip, mac, mode, link))
707 62f0dd02 Guido Trotter
  return hooks_nics
708 396e1b78 Michael Hanselmann
709 96acbc09 Michael Hanselmann
710 338e51e8 Iustin Pop
def _BuildInstanceHookEnvByObject(lu, instance, override=None):
711 ecb215b5 Michael Hanselmann
  """Builds instance related env variables for hooks from an object.
712 ecb215b5 Michael Hanselmann

713 e4376078 Iustin Pop
  @type lu: L{LogicalUnit}
714 e4376078 Iustin Pop
  @param lu: the logical unit on whose behalf we execute
715 e4376078 Iustin Pop
  @type instance: L{objects.Instance}
716 e4376078 Iustin Pop
  @param instance: the instance for which we should build the
717 e4376078 Iustin Pop
      environment
718 e4376078 Iustin Pop
  @type override: dict
719 e4376078 Iustin Pop
  @param override: dictionary with key/values that will override
720 e4376078 Iustin Pop
      our values
721 e4376078 Iustin Pop
  @rtype: dict
722 e4376078 Iustin Pop
  @return: the hook environment dictionary
723 e4376078 Iustin Pop

724 ecb215b5 Michael Hanselmann
  """
725 67fc3042 Iustin Pop
  cluster = lu.cfg.GetClusterInfo()
726 67fc3042 Iustin Pop
  bep = cluster.FillBE(instance)
727 67fc3042 Iustin Pop
  hvp = cluster.FillHV(instance)
728 396e1b78 Michael Hanselmann
  args = {
729 396e1b78 Michael Hanselmann
    'name': instance.name,
730 396e1b78 Michael Hanselmann
    'primary_node': instance.primary_node,
731 396e1b78 Michael Hanselmann
    'secondary_nodes': instance.secondary_nodes,
732 ecb215b5 Michael Hanselmann
    'os_type': instance.os,
733 0d68c45d Iustin Pop
    'status': instance.admin_up,
734 338e51e8 Iustin Pop
    'memory': bep[constants.BE_MEMORY],
735 338e51e8 Iustin Pop
    'vcpus': bep[constants.BE_VCPUS],
736 f9b10246 Guido Trotter
    'nics': _NICListToTuple(lu, instance.nics),
737 2c2690c9 Iustin Pop
    'disk_template': instance.disk_template,
738 2c2690c9 Iustin Pop
    'disks': [(disk.size, disk.mode) for disk in instance.disks],
739 67fc3042 Iustin Pop
    'bep': bep,
740 67fc3042 Iustin Pop
    'hvp': hvp,
741 b0c63e2b Iustin Pop
    'hypervisor_name': instance.hypervisor,
742 396e1b78 Michael Hanselmann
  }
743 396e1b78 Michael Hanselmann
  if override:
744 396e1b78 Michael Hanselmann
    args.update(override)
745 7260cfbe Iustin Pop
  return _BuildInstanceHookEnv(**args) # pylint: disable-msg=W0142
746 396e1b78 Michael Hanselmann
747 396e1b78 Michael Hanselmann
748 44485f49 Guido Trotter
def _AdjustCandidatePool(lu, exceptions):
749 ec0292f1 Iustin Pop
  """Adjust the candidate pool after node operations.
750 ec0292f1 Iustin Pop

751 ec0292f1 Iustin Pop
  """
752 44485f49 Guido Trotter
  mod_list = lu.cfg.MaintainCandidatePool(exceptions)
753 ec0292f1 Iustin Pop
  if mod_list:
754 ec0292f1 Iustin Pop
    lu.LogInfo("Promoted nodes to master candidate role: %s",
755 1f864b60 Iustin Pop
               utils.CommaJoin(node.name for node in mod_list))
756 ec0292f1 Iustin Pop
    for name in mod_list:
757 ec0292f1 Iustin Pop
      lu.context.ReaddNode(name)
758 44485f49 Guido Trotter
  mc_now, mc_max, _ = lu.cfg.GetMasterCandidateStats(exceptions)
759 ec0292f1 Iustin Pop
  if mc_now > mc_max:
760 ec0292f1 Iustin Pop
    lu.LogInfo("Note: more nodes are candidates (%d) than desired (%d)" %
761 ec0292f1 Iustin Pop
               (mc_now, mc_max))
762 ec0292f1 Iustin Pop
763 ec0292f1 Iustin Pop
764 6d7e1f20 Guido Trotter
def _DecideSelfPromotion(lu, exceptions=None):
765 6d7e1f20 Guido Trotter
  """Decide whether I should promote myself as a master candidate.
766 6d7e1f20 Guido Trotter

767 6d7e1f20 Guido Trotter
  """
768 6d7e1f20 Guido Trotter
  cp_size = lu.cfg.GetClusterInfo().candidate_pool_size
769 6d7e1f20 Guido Trotter
  mc_now, mc_should, _ = lu.cfg.GetMasterCandidateStats(exceptions)
770 6d7e1f20 Guido Trotter
  # the new node will increase mc_max with one, so:
771 6d7e1f20 Guido Trotter
  mc_should = min(mc_should + 1, cp_size)
772 6d7e1f20 Guido Trotter
  return mc_now < mc_should
773 6d7e1f20 Guido Trotter
774 6d7e1f20 Guido Trotter
775 b165e77e Guido Trotter
def _CheckNicsBridgesExist(lu, target_nics, target_node,
776 b165e77e Guido Trotter
                               profile=constants.PP_DEFAULT):
777 b165e77e Guido Trotter
  """Check that the brigdes needed by a list of nics exist.
778 b165e77e Guido Trotter

779 b165e77e Guido Trotter
  """
780 b165e77e Guido Trotter
  c_nicparams = lu.cfg.GetClusterInfo().nicparams[profile]
781 b165e77e Guido Trotter
  paramslist = [objects.FillDict(c_nicparams, nic.nicparams)
782 b165e77e Guido Trotter
                for nic in target_nics]
783 b165e77e Guido Trotter
  brlist = [params[constants.NIC_LINK] for params in paramslist
784 b165e77e Guido Trotter
            if params[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED]
785 b165e77e Guido Trotter
  if brlist:
786 b165e77e Guido Trotter
    result = lu.rpc.call_bridges_exist(target_node, brlist)
787 4c4e4e1e Iustin Pop
    result.Raise("Error checking bridges on destination node '%s'" %
788 045dd6d9 Iustin Pop
                 target_node, prereq=True, ecode=errors.ECODE_ENVIRON)
789 b165e77e Guido Trotter
790 b165e77e Guido Trotter
791 b165e77e Guido Trotter
def _CheckInstanceBridgesExist(lu, instance, node=None):
792 bf6929a2 Alexander Schreiber
  """Check that the brigdes needed by an instance exist.
793 bf6929a2 Alexander Schreiber

794 bf6929a2 Alexander Schreiber
  """
795 b165e77e Guido Trotter
  if node is None:
796 29921401 Iustin Pop
    node = instance.primary_node
797 b165e77e Guido Trotter
  _CheckNicsBridgesExist(lu, instance.nics, node)
798 bf6929a2 Alexander Schreiber
799 bf6929a2 Alexander Schreiber
800 c6f1af07 Iustin Pop
def _CheckOSVariant(os_obj, name):
801 f2c05717 Guido Trotter
  """Check whether an OS name conforms to the os variants specification.
802 f2c05717 Guido Trotter

803 c6f1af07 Iustin Pop
  @type os_obj: L{objects.OS}
804 c6f1af07 Iustin Pop
  @param os_obj: OS object to check
805 f2c05717 Guido Trotter
  @type name: string
806 f2c05717 Guido Trotter
  @param name: OS name passed by the user, to check for validity
807 f2c05717 Guido Trotter

808 f2c05717 Guido Trotter
  """
809 c6f1af07 Iustin Pop
  if not os_obj.supported_variants:
810 f2c05717 Guido Trotter
    return
811 f2c05717 Guido Trotter
  try:
812 f2c05717 Guido Trotter
    variant = name.split("+", 1)[1]
813 f2c05717 Guido Trotter
  except IndexError:
814 5c983ee5 Iustin Pop
    raise errors.OpPrereqError("OS name must include a variant",
815 5c983ee5 Iustin Pop
                               errors.ECODE_INVAL)
816 f2c05717 Guido Trotter
817 c6f1af07 Iustin Pop
  if variant not in os_obj.supported_variants:
818 5c983ee5 Iustin Pop
    raise errors.OpPrereqError("Unsupported OS variant", errors.ECODE_INVAL)
819 f2c05717 Guido Trotter
820 f2c05717 Guido Trotter
821 5ba9701d Michael Hanselmann
def _GetNodeInstancesInner(cfg, fn):
822 5ba9701d Michael Hanselmann
  return [i for i in cfg.GetAllInstancesInfo().values() if fn(i)]
823 5ba9701d Michael Hanselmann
824 5ba9701d Michael Hanselmann
825 e9721add Michael Hanselmann
def _GetNodeInstances(cfg, node_name):
826 e9721add Michael Hanselmann
  """Returns a list of all primary and secondary instances on a node.
827 e9721add Michael Hanselmann

828 e9721add Michael Hanselmann
  """
829 e9721add Michael Hanselmann
830 e9721add Michael Hanselmann
  return _GetNodeInstancesInner(cfg, lambda inst: node_name in inst.all_nodes)
831 e9721add Michael Hanselmann
832 e9721add Michael Hanselmann
833 80cb875c Michael Hanselmann
def _GetNodePrimaryInstances(cfg, node_name):
834 80cb875c Michael Hanselmann
  """Returns primary instances on a node.
835 80cb875c Michael Hanselmann

836 80cb875c Michael Hanselmann
  """
837 5ba9701d Michael Hanselmann
  return _GetNodeInstancesInner(cfg,
838 5ba9701d Michael Hanselmann
                                lambda inst: node_name == inst.primary_node)
839 80cb875c Michael Hanselmann
840 80cb875c Michael Hanselmann
841 692738fc Michael Hanselmann
def _GetNodeSecondaryInstances(cfg, node_name):
842 692738fc Michael Hanselmann
  """Returns secondary instances on a node.
843 692738fc Michael Hanselmann

844 692738fc Michael Hanselmann
  """
845 5ba9701d Michael Hanselmann
  return _GetNodeInstancesInner(cfg,
846 5ba9701d Michael Hanselmann
                                lambda inst: node_name in inst.secondary_nodes)
847 692738fc Michael Hanselmann
848 692738fc Michael Hanselmann
849 efb8da02 Michael Hanselmann
def _GetStorageTypeArgs(cfg, storage_type):
850 efb8da02 Michael Hanselmann
  """Returns the arguments for a storage type.
851 efb8da02 Michael Hanselmann

852 efb8da02 Michael Hanselmann
  """
853 efb8da02 Michael Hanselmann
  # Special case for file storage
854 efb8da02 Michael Hanselmann
  if storage_type == constants.ST_FILE:
855 a4d138b7 Michael Hanselmann
    # storage.FileStorage wants a list of storage directories
856 a4d138b7 Michael Hanselmann
    return [[cfg.GetFileStorageDir()]]
857 efb8da02 Michael Hanselmann
858 efb8da02 Michael Hanselmann
  return []
859 efb8da02 Michael Hanselmann
860 efb8da02 Michael Hanselmann
861 2d9005d8 Michael Hanselmann
def _FindFaultyInstanceDisks(cfg, rpc, instance, node_name, prereq):
862 2d9005d8 Michael Hanselmann
  faulty = []
863 2d9005d8 Michael Hanselmann
864 2d9005d8 Michael Hanselmann
  for dev in instance.disks:
865 2d9005d8 Michael Hanselmann
    cfg.SetDiskID(dev, node_name)
866 2d9005d8 Michael Hanselmann
867 2d9005d8 Michael Hanselmann
  result = rpc.call_blockdev_getmirrorstatus(node_name, instance.disks)
868 2d9005d8 Michael Hanselmann
  result.Raise("Failed to get disk status from node %s" % node_name,
869 045dd6d9 Iustin Pop
               prereq=prereq, ecode=errors.ECODE_ENVIRON)
870 2d9005d8 Michael Hanselmann
871 2d9005d8 Michael Hanselmann
  for idx, bdev_status in enumerate(result.payload):
872 2d9005d8 Michael Hanselmann
    if bdev_status and bdev_status.ldisk_status == constants.LDS_FAULTY:
873 2d9005d8 Michael Hanselmann
      faulty.append(idx)
874 2d9005d8 Michael Hanselmann
875 2d9005d8 Michael Hanselmann
  return faulty
876 2d9005d8 Michael Hanselmann
877 2d9005d8 Michael Hanselmann
878 b98bf262 Michael Hanselmann
def _FormatTimestamp(secs):
879 b98bf262 Michael Hanselmann
  """Formats a Unix timestamp with the local timezone.
880 b98bf262 Michael Hanselmann

881 b98bf262 Michael Hanselmann
  """
882 b98bf262 Michael Hanselmann
  return time.strftime("%F %T %Z", time.gmtime(secs))
883 b98bf262 Michael Hanselmann
884 b98bf262 Michael Hanselmann
885 b5f5fae9 Luca Bigliardi
class LUPostInitCluster(LogicalUnit):
886 b5f5fae9 Luca Bigliardi
  """Logical unit for running hooks after cluster initialization.
887 b5f5fae9 Luca Bigliardi

888 b5f5fae9 Luca Bigliardi
  """
889 b5f5fae9 Luca Bigliardi
  HPATH = "cluster-init"
890 b5f5fae9 Luca Bigliardi
  HTYPE = constants.HTYPE_CLUSTER
891 b5f5fae9 Luca Bigliardi
  _OP_REQP = []
892 b5f5fae9 Luca Bigliardi
893 b5f5fae9 Luca Bigliardi
  def BuildHooksEnv(self):
894 b5f5fae9 Luca Bigliardi
    """Build hooks env.
895 b5f5fae9 Luca Bigliardi

896 b5f5fae9 Luca Bigliardi
    """
897 b5f5fae9 Luca Bigliardi
    env = {"OP_TARGET": self.cfg.GetClusterName()}
898 b5f5fae9 Luca Bigliardi
    mn = self.cfg.GetMasterNode()
899 b5f5fae9 Luca Bigliardi
    return env, [], [mn]
900 b5f5fae9 Luca Bigliardi
901 b5f5fae9 Luca Bigliardi
  def CheckPrereq(self):
902 b5f5fae9 Luca Bigliardi
    """No prerequisites to check.
903 b5f5fae9 Luca Bigliardi

904 b5f5fae9 Luca Bigliardi
    """
905 b5f5fae9 Luca Bigliardi
    return True
906 b5f5fae9 Luca Bigliardi
907 b5f5fae9 Luca Bigliardi
  def Exec(self, feedback_fn):
908 b5f5fae9 Luca Bigliardi
    """Nothing to do.
909 b5f5fae9 Luca Bigliardi

910 b5f5fae9 Luca Bigliardi
    """
911 b5f5fae9 Luca Bigliardi
    return True
912 b5f5fae9 Luca Bigliardi
913 b5f5fae9 Luca Bigliardi
914 b2c750a4 Luca Bigliardi
class LUDestroyCluster(LogicalUnit):
915 a8083063 Iustin Pop
  """Logical unit for destroying the cluster.
916 a8083063 Iustin Pop

917 a8083063 Iustin Pop
  """
918 b2c750a4 Luca Bigliardi
  HPATH = "cluster-destroy"
919 b2c750a4 Luca Bigliardi
  HTYPE = constants.HTYPE_CLUSTER
920 a8083063 Iustin Pop
  _OP_REQP = []
921 a8083063 Iustin Pop
922 b2c750a4 Luca Bigliardi
  def BuildHooksEnv(self):
923 b2c750a4 Luca Bigliardi
    """Build hooks env.
924 b2c750a4 Luca Bigliardi

925 b2c750a4 Luca Bigliardi
    """
926 b2c750a4 Luca Bigliardi
    env = {"OP_TARGET": self.cfg.GetClusterName()}
927 b2c750a4 Luca Bigliardi
    return env, [], []
928 b2c750a4 Luca Bigliardi
929 a8083063 Iustin Pop
  def CheckPrereq(self):
930 a8083063 Iustin Pop
    """Check prerequisites.
931 a8083063 Iustin Pop

932 a8083063 Iustin Pop
    This checks whether the cluster is empty.
933 a8083063 Iustin Pop

934 5bbd3f7f Michael Hanselmann
    Any errors are signaled by raising errors.OpPrereqError.
935 a8083063 Iustin Pop

936 a8083063 Iustin Pop
    """
937 d6a02168 Michael Hanselmann
    master = self.cfg.GetMasterNode()
938 a8083063 Iustin Pop
939 a8083063 Iustin Pop
    nodelist = self.cfg.GetNodeList()
940 db915bd1 Michael Hanselmann
    if len(nodelist) != 1 or nodelist[0] != master:
941 3ecf6786 Iustin Pop
      raise errors.OpPrereqError("There are still %d node(s) in"
942 5c983ee5 Iustin Pop
                                 " this cluster." % (len(nodelist) - 1),
943 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
944 db915bd1 Michael Hanselmann
    instancelist = self.cfg.GetInstanceList()
945 db915bd1 Michael Hanselmann
    if instancelist:
946 3ecf6786 Iustin Pop
      raise errors.OpPrereqError("There are still %d instance(s) in"
947 5c983ee5 Iustin Pop
                                 " this cluster." % len(instancelist),
948 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
949 a8083063 Iustin Pop
950 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
951 a8083063 Iustin Pop
    """Destroys the cluster.
952 a8083063 Iustin Pop

953 a8083063 Iustin Pop
    """
954 d6a02168 Michael Hanselmann
    master = self.cfg.GetMasterNode()
955 b989b9d9 Ken Wehr
    modify_ssh_setup = self.cfg.GetClusterInfo().modify_ssh_setup
956 3141ad3b Luca Bigliardi
957 3141ad3b Luca Bigliardi
    # Run post hooks on master node before it's removed
958 3141ad3b Luca Bigliardi
    hm = self.proc.hmclass(self.rpc.call_hooks_runner, self)
959 3141ad3b Luca Bigliardi
    try:
960 3141ad3b Luca Bigliardi
      hm.RunPhase(constants.HOOKS_PHASE_POST, [master])
961 3141ad3b Luca Bigliardi
    except:
962 7260cfbe Iustin Pop
      # pylint: disable-msg=W0702
963 3141ad3b Luca Bigliardi
      self.LogWarning("Errors occurred running hooks on %s" % master)
964 3141ad3b Luca Bigliardi
965 781de953 Iustin Pop
    result = self.rpc.call_node_stop_master(master, False)
966 4c4e4e1e Iustin Pop
    result.Raise("Could not disable the master role")
967 b989b9d9 Ken Wehr
968 b989b9d9 Ken Wehr
    if modify_ssh_setup:
969 b989b9d9 Ken Wehr
      priv_key, pub_key, _ = ssh.GetUserFiles(constants.GANETI_RUNAS)
970 b989b9d9 Ken Wehr
      utils.CreateBackup(priv_key)
971 b989b9d9 Ken Wehr
      utils.CreateBackup(pub_key)
972 b989b9d9 Ken Wehr
973 140aa4a8 Iustin Pop
    return master
974 a8083063 Iustin Pop
975 a8083063 Iustin Pop
976 b98bf262 Michael Hanselmann
def _VerifyCertificateInner(filename, expired, not_before, not_after, now,
977 b98bf262 Michael Hanselmann
                            warn_days=constants.SSL_CERT_EXPIRATION_WARN,
978 b98bf262 Michael Hanselmann
                            error_days=constants.SSL_CERT_EXPIRATION_ERROR):
979 b98bf262 Michael Hanselmann
  """Verifies certificate details for LUVerifyCluster.
980 b98bf262 Michael Hanselmann

981 b98bf262 Michael Hanselmann
  """
982 b98bf262 Michael Hanselmann
  if expired:
983 b98bf262 Michael Hanselmann
    msg = "Certificate %s is expired" % filename
984 b98bf262 Michael Hanselmann
985 b98bf262 Michael Hanselmann
    if not_before is not None and not_after is not None:
986 b98bf262 Michael Hanselmann
      msg += (" (valid from %s to %s)" %
987 b98bf262 Michael Hanselmann
              (_FormatTimestamp(not_before),
988 b98bf262 Michael Hanselmann
               _FormatTimestamp(not_after)))
989 b98bf262 Michael Hanselmann
    elif not_before is not None:
990 b98bf262 Michael Hanselmann
      msg += " (valid from %s)" % _FormatTimestamp(not_before)
991 b98bf262 Michael Hanselmann
    elif not_after is not None:
992 b98bf262 Michael Hanselmann
      msg += " (valid until %s)" % _FormatTimestamp(not_after)
993 b98bf262 Michael Hanselmann
994 b98bf262 Michael Hanselmann
    return (LUVerifyCluster.ETYPE_ERROR, msg)
995 b98bf262 Michael Hanselmann
996 b98bf262 Michael Hanselmann
  elif not_before is not None and not_before > now:
997 b98bf262 Michael Hanselmann
    return (LUVerifyCluster.ETYPE_WARNING,
998 b98bf262 Michael Hanselmann
            "Certificate %s not yet valid (valid from %s)" %
999 b98bf262 Michael Hanselmann
            (filename, _FormatTimestamp(not_before)))
1000 b98bf262 Michael Hanselmann
1001 b98bf262 Michael Hanselmann
  elif not_after is not None:
1002 b98bf262 Michael Hanselmann
    remaining_days = int((not_after - now) / (24 * 3600))
1003 b98bf262 Michael Hanselmann
1004 b98bf262 Michael Hanselmann
    msg = ("Certificate %s expires in %d days" % (filename, remaining_days))
1005 b98bf262 Michael Hanselmann
1006 b98bf262 Michael Hanselmann
    if remaining_days <= error_days:
1007 b98bf262 Michael Hanselmann
      return (LUVerifyCluster.ETYPE_ERROR, msg)
1008 b98bf262 Michael Hanselmann
1009 b98bf262 Michael Hanselmann
    if remaining_days <= warn_days:
1010 b98bf262 Michael Hanselmann
      return (LUVerifyCluster.ETYPE_WARNING, msg)
1011 b98bf262 Michael Hanselmann
1012 b98bf262 Michael Hanselmann
  return (None, None)
1013 b98bf262 Michael Hanselmann
1014 b98bf262 Michael Hanselmann
1015 b98bf262 Michael Hanselmann
def _VerifyCertificate(filename):
1016 b98bf262 Michael Hanselmann
  """Verifies a certificate for LUVerifyCluster.
1017 b98bf262 Michael Hanselmann

1018 b98bf262 Michael Hanselmann
  @type filename: string
1019 b98bf262 Michael Hanselmann
  @param filename: Path to PEM file
1020 b98bf262 Michael Hanselmann

1021 b98bf262 Michael Hanselmann
  """
1022 b98bf262 Michael Hanselmann
  try:
1023 b98bf262 Michael Hanselmann
    cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
1024 b98bf262 Michael Hanselmann
                                           utils.ReadFile(filename))
1025 b98bf262 Michael Hanselmann
  except Exception, err: # pylint: disable-msg=W0703
1026 b98bf262 Michael Hanselmann
    return (LUVerifyCluster.ETYPE_ERROR,
1027 b98bf262 Michael Hanselmann
            "Failed to load X509 certificate %s: %s" % (filename, err))
1028 b98bf262 Michael Hanselmann
1029 b98bf262 Michael Hanselmann
  # Depending on the pyOpenSSL version, this can just return (None, None)
1030 b98bf262 Michael Hanselmann
  (not_before, not_after) = utils.GetX509CertValidity(cert)
1031 b98bf262 Michael Hanselmann
1032 b98bf262 Michael Hanselmann
  return _VerifyCertificateInner(filename, cert.has_expired(),
1033 b98bf262 Michael Hanselmann
                                 not_before, not_after, time.time())
1034 b98bf262 Michael Hanselmann
1035 b98bf262 Michael Hanselmann
1036 d8fff41c Guido Trotter
class LUVerifyCluster(LogicalUnit):
1037 a8083063 Iustin Pop
  """Verifies the cluster status.
1038 a8083063 Iustin Pop

1039 a8083063 Iustin Pop
  """
1040 d8fff41c Guido Trotter
  HPATH = "cluster-verify"
1041 d8fff41c Guido Trotter
  HTYPE = constants.HTYPE_CLUSTER
1042 a0c9776a Iustin Pop
  _OP_REQP = ["skip_checks", "verbose", "error_codes", "debug_simulate_errors"]
1043 d4b9d97f Guido Trotter
  REQ_BGL = False
1044 d4b9d97f Guido Trotter
1045 7c874ee1 Iustin Pop
  TCLUSTER = "cluster"
1046 7c874ee1 Iustin Pop
  TNODE = "node"
1047 7c874ee1 Iustin Pop
  TINSTANCE = "instance"
1048 7c874ee1 Iustin Pop
1049 7c874ee1 Iustin Pop
  ECLUSTERCFG = (TCLUSTER, "ECLUSTERCFG")
1050 b98bf262 Michael Hanselmann
  ECLUSTERCERT = (TCLUSTER, "ECLUSTERCERT")
1051 7c874ee1 Iustin Pop
  EINSTANCEBADNODE = (TINSTANCE, "EINSTANCEBADNODE")
1052 7c874ee1 Iustin Pop
  EINSTANCEDOWN = (TINSTANCE, "EINSTANCEDOWN")
1053 7c874ee1 Iustin Pop
  EINSTANCELAYOUT = (TINSTANCE, "EINSTANCELAYOUT")
1054 7c874ee1 Iustin Pop
  EINSTANCEMISSINGDISK = (TINSTANCE, "EINSTANCEMISSINGDISK")
1055 7c874ee1 Iustin Pop
  EINSTANCEMISSINGDISK = (TINSTANCE, "EINSTANCEMISSINGDISK")
1056 7c874ee1 Iustin Pop
  EINSTANCEWRONGNODE = (TINSTANCE, "EINSTANCEWRONGNODE")
1057 7c874ee1 Iustin Pop
  ENODEDRBD = (TNODE, "ENODEDRBD")
1058 7c874ee1 Iustin Pop
  ENODEFILECHECK = (TNODE, "ENODEFILECHECK")
1059 7c874ee1 Iustin Pop
  ENODEHOOKS = (TNODE, "ENODEHOOKS")
1060 7c874ee1 Iustin Pop
  ENODEHV = (TNODE, "ENODEHV")
1061 7c874ee1 Iustin Pop
  ENODELVM = (TNODE, "ENODELVM")
1062 7c874ee1 Iustin Pop
  ENODEN1 = (TNODE, "ENODEN1")
1063 7c874ee1 Iustin Pop
  ENODENET = (TNODE, "ENODENET")
1064 7c874ee1 Iustin Pop
  ENODEORPHANINSTANCE = (TNODE, "ENODEORPHANINSTANCE")
1065 7c874ee1 Iustin Pop
  ENODEORPHANLV = (TNODE, "ENODEORPHANLV")
1066 7c874ee1 Iustin Pop
  ENODERPC = (TNODE, "ENODERPC")
1067 7c874ee1 Iustin Pop
  ENODESSH = (TNODE, "ENODESSH")
1068 7c874ee1 Iustin Pop
  ENODEVERSION = (TNODE, "ENODEVERSION")
1069 7c0aa8e9 Iustin Pop
  ENODESETUP = (TNODE, "ENODESETUP")
1070 313b2dd4 Michael Hanselmann
  ENODETIME = (TNODE, "ENODETIME")
1071 7c874ee1 Iustin Pop
1072 a0c9776a Iustin Pop
  ETYPE_FIELD = "code"
1073 a0c9776a Iustin Pop
  ETYPE_ERROR = "ERROR"
1074 a0c9776a Iustin Pop
  ETYPE_WARNING = "WARNING"
1075 a0c9776a Iustin Pop
1076 d4b9d97f Guido Trotter
  def ExpandNames(self):
1077 d4b9d97f Guido Trotter
    self.needed_locks = {
1078 d4b9d97f Guido Trotter
      locking.LEVEL_NODE: locking.ALL_SET,
1079 d4b9d97f Guido Trotter
      locking.LEVEL_INSTANCE: locking.ALL_SET,
1080 d4b9d97f Guido Trotter
    }
1081 c772d142 Michael Hanselmann
    self.share_locks = dict.fromkeys(locking.LEVELS, 1)
1082 a8083063 Iustin Pop
1083 7c874ee1 Iustin Pop
  def _Error(self, ecode, item, msg, *args, **kwargs):
1084 7c874ee1 Iustin Pop
    """Format an error message.
1085 7c874ee1 Iustin Pop

1086 7c874ee1 Iustin Pop
    Based on the opcode's error_codes parameter, either format a
1087 7c874ee1 Iustin Pop
    parseable error code, or a simpler error string.
1088 7c874ee1 Iustin Pop

1089 7c874ee1 Iustin Pop
    This must be called only from Exec and functions called from Exec.
1090 7c874ee1 Iustin Pop

1091 7c874ee1 Iustin Pop
    """
1092 a0c9776a Iustin Pop
    ltype = kwargs.get(self.ETYPE_FIELD, self.ETYPE_ERROR)
1093 7c874ee1 Iustin Pop
    itype, etxt = ecode
1094 7c874ee1 Iustin Pop
    # first complete the msg
1095 7c874ee1 Iustin Pop
    if args:
1096 7c874ee1 Iustin Pop
      msg = msg % args
1097 7c874ee1 Iustin Pop
    # then format the whole message
1098 7c874ee1 Iustin Pop
    if self.op.error_codes:
1099 7c874ee1 Iustin Pop
      msg = "%s:%s:%s:%s:%s" % (ltype, etxt, itype, item, msg)
1100 7c874ee1 Iustin Pop
    else:
1101 7c874ee1 Iustin Pop
      if item:
1102 7c874ee1 Iustin Pop
        item = " " + item
1103 7c874ee1 Iustin Pop
      else:
1104 7c874ee1 Iustin Pop
        item = ""
1105 7c874ee1 Iustin Pop
      msg = "%s: %s%s: %s" % (ltype, itype, item, msg)
1106 7c874ee1 Iustin Pop
    # and finally report it via the feedback_fn
1107 7c874ee1 Iustin Pop
    self._feedback_fn("  - %s" % msg)
1108 7c874ee1 Iustin Pop
1109 a0c9776a Iustin Pop
  def _ErrorIf(self, cond, *args, **kwargs):
1110 a0c9776a Iustin Pop
    """Log an error message if the passed condition is True.
1111 a0c9776a Iustin Pop

1112 a0c9776a Iustin Pop
    """
1113 a0c9776a Iustin Pop
    cond = bool(cond) or self.op.debug_simulate_errors
1114 a0c9776a Iustin Pop
    if cond:
1115 a0c9776a Iustin Pop
      self._Error(*args, **kwargs)
1116 a0c9776a Iustin Pop
    # do not mark the operation as failed for WARN cases only
1117 a0c9776a Iustin Pop
    if kwargs.get(self.ETYPE_FIELD, self.ETYPE_ERROR) == self.ETYPE_ERROR:
1118 a0c9776a Iustin Pop
      self.bad = self.bad or cond
1119 a0c9776a Iustin Pop
1120 25361b9a Iustin Pop
  def _VerifyNode(self, nodeinfo, file_list, local_cksum,
1121 7c874ee1 Iustin Pop
                  node_result, master_files, drbd_map, vg_name):
1122 a8083063 Iustin Pop
    """Run multiple tests against a node.
1123 a8083063 Iustin Pop

1124 112f18a5 Iustin Pop
    Test list:
1125 e4376078 Iustin Pop

1126 a8083063 Iustin Pop
      - compares ganeti version
1127 5bbd3f7f Michael Hanselmann
      - checks vg existence and size > 20G
1128 a8083063 Iustin Pop
      - checks config file checksum
1129 a8083063 Iustin Pop
      - checks ssh to other nodes
1130 a8083063 Iustin Pop

1131 112f18a5 Iustin Pop
    @type nodeinfo: L{objects.Node}
1132 112f18a5 Iustin Pop
    @param nodeinfo: the node to check
1133 e4376078 Iustin Pop
    @param file_list: required list of files
1134 e4376078 Iustin Pop
    @param local_cksum: dictionary of local files and their checksums
1135 e4376078 Iustin Pop
    @param node_result: the results from the node
1136 112f18a5 Iustin Pop
    @param master_files: list of files that only masters should have
1137 6d2e83d5 Iustin Pop
    @param drbd_map: the useddrbd minors for this node, in
1138 6d2e83d5 Iustin Pop
        form of minor: (instance, must_exist) which correspond to instances
1139 6d2e83d5 Iustin Pop
        and their running status
1140 cc9e1230 Guido Trotter
    @param vg_name: Ganeti Volume Group (result of self.cfg.GetVGName())
1141 098c0958 Michael Hanselmann

1142 a8083063 Iustin Pop
    """
1143 112f18a5 Iustin Pop
    node = nodeinfo.name
1144 7260cfbe Iustin Pop
    _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103
1145 25361b9a Iustin Pop
1146 25361b9a Iustin Pop
    # main result, node_result should be a non-empty dict
1147 a0c9776a Iustin Pop
    test = not node_result or not isinstance(node_result, dict)
1148 a0c9776a Iustin Pop
    _ErrorIf(test, self.ENODERPC, node,
1149 7c874ee1 Iustin Pop
                  "unable to verify node: no data returned")
1150 a0c9776a Iustin Pop
    if test:
1151 a0c9776a Iustin Pop
      return
1152 25361b9a Iustin Pop
1153 a8083063 Iustin Pop
    # compares ganeti version
1154 a8083063 Iustin Pop
    local_version = constants.PROTOCOL_VERSION
1155 25361b9a Iustin Pop
    remote_version = node_result.get('version', None)
1156 a0c9776a Iustin Pop
    test = not (remote_version and
1157 a0c9776a Iustin Pop
                isinstance(remote_version, (list, tuple)) and
1158 a0c9776a Iustin Pop
                len(remote_version) == 2)
1159 a0c9776a Iustin Pop
    _ErrorIf(test, self.ENODERPC, node,
1160 a0c9776a Iustin Pop
             "connection to node returned invalid data")
1161 a0c9776a Iustin Pop
    if test:
1162 a0c9776a Iustin Pop
      return
1163 a0c9776a Iustin Pop
1164 a0c9776a Iustin Pop
    test = local_version != remote_version[0]
1165 a0c9776a Iustin Pop
    _ErrorIf(test, self.ENODEVERSION, node,
1166 a0c9776a Iustin Pop
             "incompatible protocol versions: master %s,"
1167 a0c9776a Iustin Pop
             " node %s", local_version, remote_version[0])
1168 a0c9776a Iustin Pop
    if test:
1169 a0c9776a Iustin Pop
      return
1170 a8083063 Iustin Pop
1171 e9ce0a64 Iustin Pop
    # node seems compatible, we can actually try to look into its results
1172 a8083063 Iustin Pop
1173 e9ce0a64 Iustin Pop
    # full package version
1174 a0c9776a Iustin Pop
    self._ErrorIf(constants.RELEASE_VERSION != remote_version[1],
1175 a0c9776a Iustin Pop
                  self.ENODEVERSION, node,
1176 7c874ee1 Iustin Pop
                  "software version mismatch: master %s, node %s",
1177 7c874ee1 Iustin Pop
                  constants.RELEASE_VERSION, remote_version[1],
1178 a0c9776a Iustin Pop
                  code=self.ETYPE_WARNING)
1179 e9ce0a64 Iustin Pop
1180 e9ce0a64 Iustin Pop
    # checks vg existence and size > 20G
1181 cc9e1230 Guido Trotter
    if vg_name is not None:
1182 cc9e1230 Guido Trotter
      vglist = node_result.get(constants.NV_VGLIST, None)
1183 a0c9776a Iustin Pop
      test = not vglist
1184 a0c9776a Iustin Pop
      _ErrorIf(test, self.ENODELVM, node, "unable to check volume groups")
1185 a0c9776a Iustin Pop
      if not test:
1186 cc9e1230 Guido Trotter
        vgstatus = utils.CheckVolumeGroupSize(vglist, vg_name,
1187 cc9e1230 Guido Trotter
                                              constants.MIN_VG_SIZE)
1188 a0c9776a Iustin Pop
        _ErrorIf(vgstatus, self.ENODELVM, node, vgstatus)
1189 a8083063 Iustin Pop
1190 a8083063 Iustin Pop
    # checks config file checksum
1191 a8083063 Iustin Pop
1192 25361b9a Iustin Pop
    remote_cksum = node_result.get(constants.NV_FILELIST, None)
1193 a0c9776a Iustin Pop
    test = not isinstance(remote_cksum, dict)
1194 a0c9776a Iustin Pop
    _ErrorIf(test, self.ENODEFILECHECK, node,
1195 a0c9776a Iustin Pop
             "node hasn't returned file checksum data")
1196 a0c9776a Iustin Pop
    if not test:
1197 a8083063 Iustin Pop
      for file_name in file_list:
1198 112f18a5 Iustin Pop
        node_is_mc = nodeinfo.master_candidate
1199 a0c9776a Iustin Pop
        must_have = (file_name not in master_files) or node_is_mc
1200 a0c9776a Iustin Pop
        # missing
1201 a0c9776a Iustin Pop
        test1 = file_name not in remote_cksum
1202 a0c9776a Iustin Pop
        # invalid checksum
1203 a0c9776a Iustin Pop
        test2 = not test1 and remote_cksum[file_name] != local_cksum[file_name]
1204 a0c9776a Iustin Pop
        # existing and good
1205 a0c9776a Iustin Pop
        test3 = not test1 and remote_cksum[file_name] == local_cksum[file_name]
1206 a0c9776a Iustin Pop
        _ErrorIf(test1 and must_have, self.ENODEFILECHECK, node,
1207 a0c9776a Iustin Pop
                 "file '%s' missing", file_name)
1208 a0c9776a Iustin Pop
        _ErrorIf(test2 and must_have, self.ENODEFILECHECK, node,
1209 a0c9776a Iustin Pop
                 "file '%s' has wrong checksum", file_name)
1210 a0c9776a Iustin Pop
        # not candidate and this is not a must-have file
1211 a0c9776a Iustin Pop
        _ErrorIf(test2 and not must_have, self.ENODEFILECHECK, node,
1212 a0c9776a Iustin Pop
                 "file '%s' should not exist on non master"
1213 a0c9776a Iustin Pop
                 " candidates (and the file is outdated)", file_name)
1214 a0c9776a Iustin Pop
        # all good, except non-master/non-must have combination
1215 a0c9776a Iustin Pop
        _ErrorIf(test3 and not must_have, self.ENODEFILECHECK, node,
1216 a0c9776a Iustin Pop
                 "file '%s' should not exist"
1217 a0c9776a Iustin Pop
                 " on non master candidates", file_name)
1218 a8083063 Iustin Pop
1219 25361b9a Iustin Pop
    # checks ssh to any
1220 25361b9a Iustin Pop
1221 a0c9776a Iustin Pop
    test = constants.NV_NODELIST not in node_result
1222 a0c9776a Iustin Pop
    _ErrorIf(test, self.ENODESSH, node,
1223 a0c9776a Iustin Pop
             "node hasn't returned node ssh connectivity data")
1224 a0c9776a Iustin Pop
    if not test:
1225 25361b9a Iustin Pop
      if node_result[constants.NV_NODELIST]:
1226 7c874ee1 Iustin Pop
        for a_node, a_msg in node_result[constants.NV_NODELIST].items():
1227 a0c9776a Iustin Pop
          _ErrorIf(True, self.ENODESSH, node,
1228 a0c9776a Iustin Pop
                   "ssh communication with node '%s': %s", a_node, a_msg)
1229 25361b9a Iustin Pop
1230 a0c9776a Iustin Pop
    test = constants.NV_NODENETTEST not in node_result
1231 a0c9776a Iustin Pop
    _ErrorIf(test, self.ENODENET, node,
1232 a0c9776a Iustin Pop
             "node hasn't returned node tcp connectivity data")
1233 a0c9776a Iustin Pop
    if not test:
1234 25361b9a Iustin Pop
      if node_result[constants.NV_NODENETTEST]:
1235 25361b9a Iustin Pop
        nlist = utils.NiceSort(node_result[constants.NV_NODENETTEST].keys())
1236 7c874ee1 Iustin Pop
        for anode in nlist:
1237 a0c9776a Iustin Pop
          _ErrorIf(True, self.ENODENET, node,
1238 a0c9776a Iustin Pop
                   "tcp communication with node '%s': %s",
1239 a0c9776a Iustin Pop
                   anode, node_result[constants.NV_NODENETTEST][anode])
1240 9d4bfc96 Iustin Pop
1241 25361b9a Iustin Pop
    hyp_result = node_result.get(constants.NV_HYPERVISOR, None)
1242 e69d05fd Iustin Pop
    if isinstance(hyp_result, dict):
1243 e69d05fd Iustin Pop
      for hv_name, hv_result in hyp_result.iteritems():
1244 a0c9776a Iustin Pop
        test = hv_result is not None
1245 a0c9776a Iustin Pop
        _ErrorIf(test, self.ENODEHV, node,
1246 a0c9776a Iustin Pop
                 "hypervisor %s verify failure: '%s'", hv_name, hv_result)
1247 6d2e83d5 Iustin Pop
1248 6d2e83d5 Iustin Pop
    # check used drbd list
1249 cc9e1230 Guido Trotter
    if vg_name is not None:
1250 cc9e1230 Guido Trotter
      used_minors = node_result.get(constants.NV_DRBDLIST, [])
1251 a0c9776a Iustin Pop
      test = not isinstance(used_minors, (tuple, list))
1252 a0c9776a Iustin Pop
      _ErrorIf(test, self.ENODEDRBD, node,
1253 a0c9776a Iustin Pop
               "cannot parse drbd status file: %s", str(used_minors))
1254 a0c9776a Iustin Pop
      if not test:
1255 cc9e1230 Guido Trotter
        for minor, (iname, must_exist) in drbd_map.items():
1256 a0c9776a Iustin Pop
          test = minor not in used_minors and must_exist
1257 a0c9776a Iustin Pop
          _ErrorIf(test, self.ENODEDRBD, node,
1258 a0c9776a Iustin Pop
                   "drbd minor %d of instance %s is not active",
1259 a0c9776a Iustin Pop
                   minor, iname)
1260 cc9e1230 Guido Trotter
        for minor in used_minors:
1261 a0c9776a Iustin Pop
          test = minor not in drbd_map
1262 a0c9776a Iustin Pop
          _ErrorIf(test, self.ENODEDRBD, node,
1263 a0c9776a Iustin Pop
                   "unallocated drbd minor %d is in use", minor)
1264 7c0aa8e9 Iustin Pop
    test = node_result.get(constants.NV_NODESETUP,
1265 7c0aa8e9 Iustin Pop
                           ["Missing NODESETUP results"])
1266 7c0aa8e9 Iustin Pop
    _ErrorIf(test, self.ENODESETUP, node, "node setup error: %s",
1267 7c0aa8e9 Iustin Pop
             "; ".join(test))
1268 a8083063 Iustin Pop
1269 d091393e Iustin Pop
    # check pv names
1270 d091393e Iustin Pop
    if vg_name is not None:
1271 d091393e Iustin Pop
      pvlist = node_result.get(constants.NV_PVLIST, None)
1272 d091393e Iustin Pop
      test = pvlist is None
1273 d091393e Iustin Pop
      _ErrorIf(test, self.ENODELVM, node, "Can't get PV list from node")
1274 d091393e Iustin Pop
      if not test:
1275 d091393e Iustin Pop
        # check that ':' is not present in PV names, since it's a
1276 d091393e Iustin Pop
        # special character for lvcreate (denotes the range of PEs to
1277 d091393e Iustin Pop
        # use on the PV)
1278 1122eb25 Iustin Pop
        for _, pvname, owner_vg in pvlist:
1279 d091393e Iustin Pop
          test = ":" in pvname
1280 d091393e Iustin Pop
          _ErrorIf(test, self.ENODELVM, node, "Invalid character ':' in PV"
1281 d091393e Iustin Pop
                   " '%s' of VG '%s'", pvname, owner_vg)
1282 d091393e Iustin Pop
1283 c5705f58 Guido Trotter
  def _VerifyInstance(self, instance, instanceconfig, node_vol_is,
1284 7c874ee1 Iustin Pop
                      node_instance, n_offline):
1285 a8083063 Iustin Pop
    """Verify an instance.
1286 a8083063 Iustin Pop

1287 a8083063 Iustin Pop
    This function checks to see if the required block devices are
1288 a8083063 Iustin Pop
    available on the instance's node.
1289 a8083063 Iustin Pop

1290 a8083063 Iustin Pop
    """
1291 7260cfbe Iustin Pop
    _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103
1292 a8083063 Iustin Pop
    node_current = instanceconfig.primary_node
1293 a8083063 Iustin Pop
1294 a8083063 Iustin Pop
    node_vol_should = {}
1295 a8083063 Iustin Pop
    instanceconfig.MapLVsByNode(node_vol_should)
1296 a8083063 Iustin Pop
1297 a8083063 Iustin Pop
    for node in node_vol_should:
1298 0a66c968 Iustin Pop
      if node in n_offline:
1299 0a66c968 Iustin Pop
        # ignore missing volumes on offline nodes
1300 0a66c968 Iustin Pop
        continue
1301 a8083063 Iustin Pop
      for volume in node_vol_should[node]:
1302 a0c9776a Iustin Pop
        test = node not in node_vol_is or volume not in node_vol_is[node]
1303 a0c9776a Iustin Pop
        _ErrorIf(test, self.EINSTANCEMISSINGDISK, instance,
1304 a0c9776a Iustin Pop
                 "volume %s missing on node %s", volume, node)
1305 a8083063 Iustin Pop
1306 0d68c45d Iustin Pop
    if instanceconfig.admin_up:
1307 a0c9776a Iustin Pop
      test = ((node_current not in node_instance or
1308 a0c9776a Iustin Pop
               not instance in node_instance[node_current]) and
1309 a0c9776a Iustin Pop
              node_current not in n_offline)
1310 a0c9776a Iustin Pop
      _ErrorIf(test, self.EINSTANCEDOWN, instance,
1311 a0c9776a Iustin Pop
               "instance not running on its primary node %s",
1312 a0c9776a Iustin Pop
               node_current)
1313 a8083063 Iustin Pop
1314 a8083063 Iustin Pop
    for node in node_instance:
1315 a8083063 Iustin Pop
      if (not node == node_current):
1316 a0c9776a Iustin Pop
        test = instance in node_instance[node]
1317 a0c9776a Iustin Pop
        _ErrorIf(test, self.EINSTANCEWRONGNODE, instance,
1318 a0c9776a Iustin Pop
                 "instance should not run on node %s", node)
1319 a8083063 Iustin Pop
1320 7c874ee1 Iustin Pop
  def _VerifyOrphanVolumes(self, node_vol_should, node_vol_is):
1321 a8083063 Iustin Pop
    """Verify if there are any unknown volumes in the cluster.
1322 a8083063 Iustin Pop

1323 a8083063 Iustin Pop
    The .os, .swap and backup volumes are ignored. All other volumes are
1324 a8083063 Iustin Pop
    reported as unknown.
1325 a8083063 Iustin Pop

1326 a8083063 Iustin Pop
    """
1327 a8083063 Iustin Pop
    for node in node_vol_is:
1328 a8083063 Iustin Pop
      for volume in node_vol_is[node]:
1329 a0c9776a Iustin Pop
        test = (node not in node_vol_should or
1330 a0c9776a Iustin Pop
                volume not in node_vol_should[node])
1331 a0c9776a Iustin Pop
        self._ErrorIf(test, self.ENODEORPHANLV, node,
1332 7c874ee1 Iustin Pop
                      "volume %s is unknown", volume)
1333 a8083063 Iustin Pop
1334 7c874ee1 Iustin Pop
  def _VerifyOrphanInstances(self, instancelist, node_instance):
1335 a8083063 Iustin Pop
    """Verify the list of running instances.
1336 a8083063 Iustin Pop

1337 a8083063 Iustin Pop
    This checks what instances are running but unknown to the cluster.
1338 a8083063 Iustin Pop

1339 a8083063 Iustin Pop
    """
1340 a8083063 Iustin Pop
    for node in node_instance:
1341 7c874ee1 Iustin Pop
      for o_inst in node_instance[node]:
1342 a0c9776a Iustin Pop
        test = o_inst not in instancelist
1343 a0c9776a Iustin Pop
        self._ErrorIf(test, self.ENODEORPHANINSTANCE, node,
1344 7c874ee1 Iustin Pop
                      "instance %s on node %s should not exist", o_inst, node)
1345 a8083063 Iustin Pop
1346 7c874ee1 Iustin Pop
  def _VerifyNPlusOneMemory(self, node_info, instance_cfg):
1347 2b3b6ddd Guido Trotter
    """Verify N+1 Memory Resilience.
1348 2b3b6ddd Guido Trotter

1349 2b3b6ddd Guido Trotter
    Check that if one single node dies we can still start all the instances it
1350 2b3b6ddd Guido Trotter
    was primary for.
1351 2b3b6ddd Guido Trotter

1352 2b3b6ddd Guido Trotter
    """
1353 2b3b6ddd Guido Trotter
    for node, nodeinfo in node_info.iteritems():
1354 2b3b6ddd Guido Trotter
      # This code checks that every node which is now listed as secondary has
1355 2b3b6ddd Guido Trotter
      # enough memory to host all instances it is supposed to should a single
1356 2b3b6ddd Guido Trotter
      # other node in the cluster fail.
1357 2b3b6ddd Guido Trotter
      # FIXME: not ready for failover to an arbitrary node
1358 2b3b6ddd Guido Trotter
      # FIXME: does not support file-backed instances
1359 2b3b6ddd Guido Trotter
      # WARNING: we currently take into account down instances as well as up
1360 2b3b6ddd Guido Trotter
      # ones, considering that even if they're down someone might want to start
1361 2b3b6ddd Guido Trotter
      # them even in the event of a node failure.
1362 2b3b6ddd Guido Trotter
      for prinode, instances in nodeinfo['sinst-by-pnode'].iteritems():
1363 2b3b6ddd Guido Trotter
        needed_mem = 0
1364 2b3b6ddd Guido Trotter
        for instance in instances:
1365 338e51e8 Iustin Pop
          bep = self.cfg.GetClusterInfo().FillBE(instance_cfg[instance])
1366 c0f2b229 Iustin Pop
          if bep[constants.BE_AUTO_BALANCE]:
1367 3924700f Iustin Pop
            needed_mem += bep[constants.BE_MEMORY]
1368 a0c9776a Iustin Pop
        test = nodeinfo['mfree'] < needed_mem
1369 a0c9776a Iustin Pop
        self._ErrorIf(test, self.ENODEN1, node,
1370 7c874ee1 Iustin Pop
                      "not enough memory on to accommodate"
1371 7c874ee1 Iustin Pop
                      " failovers should peer node %s fail", prinode)
1372 2b3b6ddd Guido Trotter
1373 a8083063 Iustin Pop
  def CheckPrereq(self):
1374 a8083063 Iustin Pop
    """Check prerequisites.
1375 a8083063 Iustin Pop

1376 e54c4c5e Guido Trotter
    Transform the list of checks we're going to skip into a set and check that
1377 e54c4c5e Guido Trotter
    all its members are valid.
1378 a8083063 Iustin Pop

1379 a8083063 Iustin Pop
    """
1380 e54c4c5e Guido Trotter
    self.skip_set = frozenset(self.op.skip_checks)
1381 e54c4c5e Guido Trotter
    if not constants.VERIFY_OPTIONAL_CHECKS.issuperset(self.skip_set):
1382 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Invalid checks to be skipped specified",
1383 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
1384 a8083063 Iustin Pop
1385 d8fff41c Guido Trotter
  def BuildHooksEnv(self):
1386 d8fff41c Guido Trotter
    """Build hooks env.
1387 d8fff41c Guido Trotter

1388 5bbd3f7f Michael Hanselmann
    Cluster-Verify hooks just ran in the post phase and their failure makes
1389 d8fff41c Guido Trotter
    the output be logged in the verify output and the verification to fail.
1390 d8fff41c Guido Trotter

1391 d8fff41c Guido Trotter
    """
1392 d8fff41c Guido Trotter
    all_nodes = self.cfg.GetNodeList()
1393 35e994e9 Iustin Pop
    env = {
1394 35e994e9 Iustin Pop
      "CLUSTER_TAGS": " ".join(self.cfg.GetClusterInfo().GetTags())
1395 35e994e9 Iustin Pop
      }
1396 35e994e9 Iustin Pop
    for node in self.cfg.GetAllNodesInfo().values():
1397 35e994e9 Iustin Pop
      env["NODE_TAGS_%s" % node.name] = " ".join(node.GetTags())
1398 35e994e9 Iustin Pop
1399 d8fff41c Guido Trotter
    return env, [], all_nodes
1400 d8fff41c Guido Trotter
1401 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
1402 a8083063 Iustin Pop
    """Verify integrity of cluster, performing various test on nodes.
1403 a8083063 Iustin Pop

1404 a8083063 Iustin Pop
    """
1405 a0c9776a Iustin Pop
    self.bad = False
1406 7260cfbe Iustin Pop
    _ErrorIf = self._ErrorIf # pylint: disable-msg=C0103
1407 7c874ee1 Iustin Pop
    verbose = self.op.verbose
1408 7c874ee1 Iustin Pop
    self._feedback_fn = feedback_fn
1409 a8083063 Iustin Pop
    feedback_fn("* Verifying global settings")
1410 8522ceeb Iustin Pop
    for msg in self.cfg.VerifyConfig():
1411 a0c9776a Iustin Pop
      _ErrorIf(True, self.ECLUSTERCFG, None, msg)
1412 a8083063 Iustin Pop
1413 b98bf262 Michael Hanselmann
    # Check the cluster certificates
1414 b98bf262 Michael Hanselmann
    for cert_filename in constants.ALL_CERT_FILES:
1415 b98bf262 Michael Hanselmann
      (errcode, msg) = _VerifyCertificate(cert_filename)
1416 b98bf262 Michael Hanselmann
      _ErrorIf(errcode, self.ECLUSTERCERT, None, msg, code=errcode)
1417 b98bf262 Michael Hanselmann
1418 a8083063 Iustin Pop
    vg_name = self.cfg.GetVGName()
1419 e69d05fd Iustin Pop
    hypervisors = self.cfg.GetClusterInfo().enabled_hypervisors
1420 a8083063 Iustin Pop
    nodelist = utils.NiceSort(self.cfg.GetNodeList())
1421 9d4bfc96 Iustin Pop
    nodeinfo = [self.cfg.GetNodeInfo(nname) for nname in nodelist]
1422 a8083063 Iustin Pop
    instancelist = utils.NiceSort(self.cfg.GetInstanceList())
1423 6d2e83d5 Iustin Pop
    instanceinfo = dict((iname, self.cfg.GetInstanceInfo(iname))
1424 6d2e83d5 Iustin Pop
                        for iname in instancelist)
1425 93e4c50b Guido Trotter
    i_non_redundant = [] # Non redundant instances
1426 3924700f Iustin Pop
    i_non_a_balanced = [] # Non auto-balanced instances
1427 0a66c968 Iustin Pop
    n_offline = [] # List of offline nodes
1428 22f0f71d Iustin Pop
    n_drained = [] # List of nodes being drained
1429 a8083063 Iustin Pop
    node_volume = {}
1430 a8083063 Iustin Pop
    node_instance = {}
1431 9c9c7d30 Guido Trotter
    node_info = {}
1432 26b6af5e Guido Trotter
    instance_cfg = {}
1433 a8083063 Iustin Pop
1434 a8083063 Iustin Pop
    # FIXME: verify OS list
1435 a8083063 Iustin Pop
    # do local checksums
1436 112f18a5 Iustin Pop
    master_files = [constants.CLUSTER_CONF_FILE]
1437 112f18a5 Iustin Pop
1438 112f18a5 Iustin Pop
    file_names = ssconf.SimpleStore().GetFileList()
1439 d3100055 Michael Hanselmann
    file_names.extend(constants.ALL_CERT_FILES)
1440 112f18a5 Iustin Pop
    file_names.extend(master_files)
1441 112f18a5 Iustin Pop
1442 a8083063 Iustin Pop
    local_checksums = utils.FingerprintFiles(file_names)
1443 a8083063 Iustin Pop
1444 a8083063 Iustin Pop
    feedback_fn("* Gathering data (%d nodes)" % len(nodelist))
1445 a8083063 Iustin Pop
    node_verify_param = {
1446 25361b9a Iustin Pop
      constants.NV_FILELIST: file_names,
1447 82e37788 Iustin Pop
      constants.NV_NODELIST: [node.name for node in nodeinfo
1448 82e37788 Iustin Pop
                              if not node.offline],
1449 25361b9a Iustin Pop
      constants.NV_HYPERVISOR: hypervisors,
1450 25361b9a Iustin Pop
      constants.NV_NODENETTEST: [(node.name, node.primary_ip,
1451 82e37788 Iustin Pop
                                  node.secondary_ip) for node in nodeinfo
1452 82e37788 Iustin Pop
                                 if not node.offline],
1453 25361b9a Iustin Pop
      constants.NV_INSTANCELIST: hypervisors,
1454 25361b9a Iustin Pop
      constants.NV_VERSION: None,
1455 25361b9a Iustin Pop
      constants.NV_HVINFO: self.cfg.GetHypervisorType(),
1456 7c0aa8e9 Iustin Pop
      constants.NV_NODESETUP: None,
1457 313b2dd4 Michael Hanselmann
      constants.NV_TIME: None,
1458 a8083063 Iustin Pop
      }
1459 313b2dd4 Michael Hanselmann
1460 cc9e1230 Guido Trotter
    if vg_name is not None:
1461 cc9e1230 Guido Trotter
      node_verify_param[constants.NV_VGLIST] = None
1462 cc9e1230 Guido Trotter
      node_verify_param[constants.NV_LVLIST] = vg_name
1463 d091393e Iustin Pop
      node_verify_param[constants.NV_PVLIST] = [vg_name]
1464 cc9e1230 Guido Trotter
      node_verify_param[constants.NV_DRBDLIST] = None
1465 313b2dd4 Michael Hanselmann
1466 313b2dd4 Michael Hanselmann
    # Due to the way our RPC system works, exact response times cannot be
1467 313b2dd4 Michael Hanselmann
    # guaranteed (e.g. a broken node could run into a timeout). By keeping the
1468 313b2dd4 Michael Hanselmann
    # time before and after executing the request, we can at least have a time
1469 313b2dd4 Michael Hanselmann
    # window.
1470 313b2dd4 Michael Hanselmann
    nvinfo_starttime = time.time()
1471 72737a7f Iustin Pop
    all_nvinfo = self.rpc.call_node_verify(nodelist, node_verify_param,
1472 72737a7f Iustin Pop
                                           self.cfg.GetClusterName())
1473 313b2dd4 Michael Hanselmann
    nvinfo_endtime = time.time()
1474 a8083063 Iustin Pop
1475 3924700f Iustin Pop
    cluster = self.cfg.GetClusterInfo()
1476 112f18a5 Iustin Pop
    master_node = self.cfg.GetMasterNode()
1477 6d2e83d5 Iustin Pop
    all_drbd_map = self.cfg.ComputeDRBDMap()
1478 6d2e83d5 Iustin Pop
1479 7c874ee1 Iustin Pop
    feedback_fn("* Verifying node status")
1480 112f18a5 Iustin Pop
    for node_i in nodeinfo:
1481 112f18a5 Iustin Pop
      node = node_i.name
1482 25361b9a Iustin Pop
1483 0a66c968 Iustin Pop
      if node_i.offline:
1484 7c874ee1 Iustin Pop
        if verbose:
1485 7c874ee1 Iustin Pop
          feedback_fn("* Skipping offline node %s" % (node,))
1486 0a66c968 Iustin Pop
        n_offline.append(node)
1487 0a66c968 Iustin Pop
        continue
1488 0a66c968 Iustin Pop
1489 112f18a5 Iustin Pop
      if node == master_node:
1490 25361b9a Iustin Pop
        ntype = "master"
1491 112f18a5 Iustin Pop
      elif node_i.master_candidate:
1492 25361b9a Iustin Pop
        ntype = "master candidate"
1493 22f0f71d Iustin Pop
      elif node_i.drained:
1494 22f0f71d Iustin Pop
        ntype = "drained"
1495 22f0f71d Iustin Pop
        n_drained.append(node)
1496 112f18a5 Iustin Pop
      else:
1497 25361b9a Iustin Pop
        ntype = "regular"
1498 7c874ee1 Iustin Pop
      if verbose:
1499 7c874ee1 Iustin Pop
        feedback_fn("* Verifying node %s (%s)" % (node, ntype))
1500 25361b9a Iustin Pop
1501 4c4e4e1e Iustin Pop
      msg = all_nvinfo[node].fail_msg
1502 a0c9776a Iustin Pop
      _ErrorIf(msg, self.ENODERPC, node, "while contacting node: %s", msg)
1503 6f68a739 Iustin Pop
      if msg:
1504 25361b9a Iustin Pop
        continue
1505 25361b9a Iustin Pop
1506 6f68a739 Iustin Pop
      nresult = all_nvinfo[node].payload
1507 6d2e83d5 Iustin Pop
      node_drbd = {}
1508 6d2e83d5 Iustin Pop
      for minor, instance in all_drbd_map[node].items():
1509 a0c9776a Iustin Pop
        test = instance not in instanceinfo
1510 a0c9776a Iustin Pop
        _ErrorIf(test, self.ECLUSTERCFG, None,
1511 a0c9776a Iustin Pop
                 "ghost instance '%s' in temporary DRBD map", instance)
1512 c614e5fb Iustin Pop
          # ghost instance should not be running, but otherwise we
1513 c614e5fb Iustin Pop
          # don't give double warnings (both ghost instance and
1514 c614e5fb Iustin Pop
          # unallocated minor in use)
1515 a0c9776a Iustin Pop
        if test:
1516 c614e5fb Iustin Pop
          node_drbd[minor] = (instance, False)
1517 c614e5fb Iustin Pop
        else:
1518 c614e5fb Iustin Pop
          instance = instanceinfo[instance]
1519 c614e5fb Iustin Pop
          node_drbd[minor] = (instance.name, instance.admin_up)
1520 313b2dd4 Michael Hanselmann
1521 a0c9776a Iustin Pop
      self._VerifyNode(node_i, file_names, local_checksums,
1522 a0c9776a Iustin Pop
                       nresult, master_files, node_drbd, vg_name)
1523 a8083063 Iustin Pop
1524 25361b9a Iustin Pop
      lvdata = nresult.get(constants.NV_LVLIST, "Missing LV data")
1525 cc9e1230 Guido Trotter
      if vg_name is None:
1526 cc9e1230 Guido Trotter
        node_volume[node] = {}
1527 cc9e1230 Guido Trotter
      elif isinstance(lvdata, basestring):
1528 a0c9776a Iustin Pop
        _ErrorIf(True, self.ENODELVM, node, "LVM problem on node: %s",
1529 a0c9776a Iustin Pop
                 utils.SafeEncode(lvdata))
1530 b63ed789 Iustin Pop
        node_volume[node] = {}
1531 25361b9a Iustin Pop
      elif not isinstance(lvdata, dict):
1532 a0c9776a Iustin Pop
        _ErrorIf(True, self.ENODELVM, node, "rpc call to node failed (lvlist)")
1533 a8083063 Iustin Pop
        continue
1534 b63ed789 Iustin Pop
      else:
1535 25361b9a Iustin Pop
        node_volume[node] = lvdata
1536 a8083063 Iustin Pop
1537 a8083063 Iustin Pop
      # node_instance
1538 25361b9a Iustin Pop
      idata = nresult.get(constants.NV_INSTANCELIST, None)
1539 a0c9776a Iustin Pop
      test = not isinstance(idata, list)
1540 a0c9776a Iustin Pop
      _ErrorIf(test, self.ENODEHV, node,
1541 0cf5e7f5 Iustin Pop
               "rpc call to node failed (instancelist): %s",
1542 0cf5e7f5 Iustin Pop
               utils.SafeEncode(str(idata)))
1543 a0c9776a Iustin Pop
      if test:
1544 a8083063 Iustin Pop
        continue
1545 a8083063 Iustin Pop
1546 25361b9a Iustin Pop
      node_instance[node] = idata
1547 a8083063 Iustin Pop
1548 9c9c7d30 Guido Trotter
      # node_info
1549 25361b9a Iustin Pop
      nodeinfo = nresult.get(constants.NV_HVINFO, None)
1550 a0c9776a Iustin Pop
      test = not isinstance(nodeinfo, dict)
1551 a0c9776a Iustin Pop
      _ErrorIf(test, self.ENODEHV, node, "rpc call to node failed (hvinfo)")
1552 a0c9776a Iustin Pop
      if test:
1553 9c9c7d30 Guido Trotter
        continue
1554 9c9c7d30 Guido Trotter
1555 313b2dd4 Michael Hanselmann
      # Node time
1556 313b2dd4 Michael Hanselmann
      ntime = nresult.get(constants.NV_TIME, None)
1557 313b2dd4 Michael Hanselmann
      try:
1558 313b2dd4 Michael Hanselmann
        ntime_merged = utils.MergeTime(ntime)
1559 313b2dd4 Michael Hanselmann
      except (ValueError, TypeError):
1560 30bb62ea Michael Hanselmann
        _ErrorIf(True, self.ENODETIME, node, "Node returned invalid time")
1561 313b2dd4 Michael Hanselmann
1562 313b2dd4 Michael Hanselmann
      if ntime_merged < (nvinfo_starttime - constants.NODE_MAX_CLOCK_SKEW):
1563 92c1b5d6 Iustin Pop
        ntime_diff = "%.01fs" % abs(nvinfo_starttime - ntime_merged)
1564 313b2dd4 Michael Hanselmann
      elif ntime_merged > (nvinfo_endtime + constants.NODE_MAX_CLOCK_SKEW):
1565 92c1b5d6 Iustin Pop
        ntime_diff = "%.01fs" % abs(ntime_merged - nvinfo_endtime)
1566 313b2dd4 Michael Hanselmann
      else:
1567 313b2dd4 Michael Hanselmann
        ntime_diff = None
1568 313b2dd4 Michael Hanselmann
1569 313b2dd4 Michael Hanselmann
      _ErrorIf(ntime_diff is not None, self.ENODETIME, node,
1570 92c1b5d6 Iustin Pop
               "Node time diverges by at least %s from master node time",
1571 313b2dd4 Michael Hanselmann
               ntime_diff)
1572 313b2dd4 Michael Hanselmann
1573 313b2dd4 Michael Hanselmann
      if ntime_diff is not None:
1574 313b2dd4 Michael Hanselmann
        continue
1575 313b2dd4 Michael Hanselmann
1576 9c9c7d30 Guido Trotter
      try:
1577 9c9c7d30 Guido Trotter
        node_info[node] = {
1578 9c9c7d30 Guido Trotter
          "mfree": int(nodeinfo['memory_free']),
1579 93e4c50b Guido Trotter
          "pinst": [],
1580 93e4c50b Guido Trotter
          "sinst": [],
1581 36e7da50 Guido Trotter
          # dictionary holding all instances this node is secondary for,
1582 36e7da50 Guido Trotter
          # grouped by their primary node. Each key is a cluster node, and each
1583 36e7da50 Guido Trotter
          # value is a list of instances which have the key as primary and the
1584 36e7da50 Guido Trotter
          # current node as secondary.  this is handy to calculate N+1 memory
1585 36e7da50 Guido Trotter
          # availability if you can only failover from a primary to its
1586 36e7da50 Guido Trotter
          # secondary.
1587 36e7da50 Guido Trotter
          "sinst-by-pnode": {},
1588 9c9c7d30 Guido Trotter
        }
1589 cc9e1230 Guido Trotter
        # FIXME: devise a free space model for file based instances as well
1590 cc9e1230 Guido Trotter
        if vg_name is not None:
1591 a0c9776a Iustin Pop
          test = (constants.NV_VGLIST not in nresult or
1592 a0c9776a Iustin Pop
                  vg_name not in nresult[constants.NV_VGLIST])
1593 a0c9776a Iustin Pop
          _ErrorIf(test, self.ENODELVM, node,
1594 a0c9776a Iustin Pop
                   "node didn't return data for the volume group '%s'"
1595 a0c9776a Iustin Pop
                   " - it is either missing or broken", vg_name)
1596 a0c9776a Iustin Pop
          if test:
1597 9a198532 Iustin Pop
            continue
1598 cc9e1230 Guido Trotter
          node_info[node]["dfree"] = int(nresult[constants.NV_VGLIST][vg_name])
1599 9a198532 Iustin Pop
      except (ValueError, KeyError):
1600 a0c9776a Iustin Pop
        _ErrorIf(True, self.ENODERPC, node,
1601 a0c9776a Iustin Pop
                 "node returned invalid nodeinfo, check lvm/hypervisor")
1602 9c9c7d30 Guido Trotter
        continue
1603 9c9c7d30 Guido Trotter
1604 a8083063 Iustin Pop
    node_vol_should = {}
1605 a8083063 Iustin Pop
1606 7c874ee1 Iustin Pop
    feedback_fn("* Verifying instance status")
1607 a8083063 Iustin Pop
    for instance in instancelist:
1608 7c874ee1 Iustin Pop
      if verbose:
1609 7c874ee1 Iustin Pop
        feedback_fn("* Verifying instance %s" % instance)
1610 6d2e83d5 Iustin Pop
      inst_config = instanceinfo[instance]
1611 a0c9776a Iustin Pop
      self._VerifyInstance(instance, inst_config, node_volume,
1612 a0c9776a Iustin Pop
                           node_instance, n_offline)
1613 832261fd Iustin Pop
      inst_nodes_offline = []
1614 a8083063 Iustin Pop
1615 a8083063 Iustin Pop
      inst_config.MapLVsByNode(node_vol_should)
1616 a8083063 Iustin Pop
1617 26b6af5e Guido Trotter
      instance_cfg[instance] = inst_config
1618 26b6af5e Guido Trotter
1619 93e4c50b Guido Trotter
      pnode = inst_config.primary_node
1620 a0c9776a Iustin Pop
      _ErrorIf(pnode not in node_info and pnode not in n_offline,
1621 a0c9776a Iustin Pop
               self.ENODERPC, pnode, "instance %s, connection to"
1622 a0c9776a Iustin Pop
               " primary node failed", instance)
1623 93e4c50b Guido Trotter
      if pnode in node_info:
1624 93e4c50b Guido Trotter
        node_info[pnode]['pinst'].append(instance)
1625 93e4c50b Guido Trotter
1626 832261fd Iustin Pop
      if pnode in n_offline:
1627 832261fd Iustin Pop
        inst_nodes_offline.append(pnode)
1628 832261fd Iustin Pop
1629 93e4c50b Guido Trotter
      # If the instance is non-redundant we cannot survive losing its primary
1630 93e4c50b Guido Trotter
      # node, so we are not N+1 compliant. On the other hand we have no disk
1631 93e4c50b Guido Trotter
      # templates with more than one secondary so that situation is not well
1632 93e4c50b Guido Trotter
      # supported either.
1633 93e4c50b Guido Trotter
      # FIXME: does not support file-backed instances
1634 93e4c50b Guido Trotter
      if len(inst_config.secondary_nodes) == 0:
1635 93e4c50b Guido Trotter
        i_non_redundant.append(instance)
1636 a0c9776a Iustin Pop
      _ErrorIf(len(inst_config.secondary_nodes) > 1,
1637 a0c9776a Iustin Pop
               self.EINSTANCELAYOUT, instance,
1638 a0c9776a Iustin Pop
               "instance has multiple secondary nodes", code="WARNING")
1639 93e4c50b Guido Trotter
1640 c0f2b229 Iustin Pop
      if not cluster.FillBE(inst_config)[constants.BE_AUTO_BALANCE]:
1641 3924700f Iustin Pop
        i_non_a_balanced.append(instance)
1642 3924700f Iustin Pop
1643 93e4c50b Guido Trotter
      for snode in inst_config.secondary_nodes:
1644 a0c9776a Iustin Pop
        _ErrorIf(snode not in node_info and snode not in n_offline,
1645 a0c9776a Iustin Pop
                 self.ENODERPC, snode,
1646 a0c9776a Iustin Pop
                 "instance %s, connection to secondary node"
1647 0cf5e7f5 Iustin Pop
                 " failed", instance)
1648 a0c9776a Iustin Pop
1649 93e4c50b Guido Trotter
        if snode in node_info:
1650 93e4c50b Guido Trotter
          node_info[snode]['sinst'].append(instance)
1651 36e7da50 Guido Trotter
          if pnode not in node_info[snode]['sinst-by-pnode']:
1652 36e7da50 Guido Trotter
            node_info[snode]['sinst-by-pnode'][pnode] = []
1653 36e7da50 Guido Trotter
          node_info[snode]['sinst-by-pnode'][pnode].append(instance)
1654 a0c9776a Iustin Pop
1655 832261fd Iustin Pop
        if snode in n_offline:
1656 832261fd Iustin Pop
          inst_nodes_offline.append(snode)
1657 832261fd Iustin Pop
1658 a0c9776a Iustin Pop
      # warn that the instance lives on offline nodes
1659 a0c9776a Iustin Pop
      _ErrorIf(inst_nodes_offline, self.EINSTANCEBADNODE, instance,
1660 a0c9776a Iustin Pop
               "instance lives on offline node(s) %s",
1661 1f864b60 Iustin Pop
               utils.CommaJoin(inst_nodes_offline))
1662 93e4c50b Guido Trotter
1663 a8083063 Iustin Pop
    feedback_fn("* Verifying orphan volumes")
1664 a0c9776a Iustin Pop
    self._VerifyOrphanVolumes(node_vol_should, node_volume)
1665 a8083063 Iustin Pop
1666 a8083063 Iustin Pop
    feedback_fn("* Verifying remaining instances")
1667 a0c9776a Iustin Pop
    self._VerifyOrphanInstances(instancelist, node_instance)
1668 a8083063 Iustin Pop
1669 e54c4c5e Guido Trotter
    if constants.VERIFY_NPLUSONE_MEM not in self.skip_set:
1670 e54c4c5e Guido Trotter
      feedback_fn("* Verifying N+1 Memory redundancy")
1671 a0c9776a Iustin Pop
      self._VerifyNPlusOneMemory(node_info, instance_cfg)
1672 2b3b6ddd Guido Trotter
1673 2b3b6ddd Guido Trotter
    feedback_fn("* Other Notes")
1674 2b3b6ddd Guido Trotter
    if i_non_redundant:
1675 2b3b6ddd Guido Trotter
      feedback_fn("  - NOTICE: %d non-redundant instance(s) found."
1676 2b3b6ddd Guido Trotter
                  % len(i_non_redundant))
1677 2b3b6ddd Guido Trotter
1678 3924700f Iustin Pop
    if i_non_a_balanced:
1679 3924700f Iustin Pop
      feedback_fn("  - NOTICE: %d non-auto-balanced instance(s) found."
1680 3924700f Iustin Pop
                  % len(i_non_a_balanced))
1681 3924700f Iustin Pop
1682 0a66c968 Iustin Pop
    if n_offline:
1683 0a66c968 Iustin Pop
      feedback_fn("  - NOTICE: %d offline node(s) found." % len(n_offline))
1684 0a66c968 Iustin Pop
1685 22f0f71d Iustin Pop
    if n_drained:
1686 22f0f71d Iustin Pop
      feedback_fn("  - NOTICE: %d drained node(s) found." % len(n_drained))
1687 22f0f71d Iustin Pop
1688 a0c9776a Iustin Pop
    return not self.bad
1689 a8083063 Iustin Pop
1690 d8fff41c Guido Trotter
  def HooksCallBack(self, phase, hooks_results, feedback_fn, lu_result):
1691 5bbd3f7f Michael Hanselmann
    """Analyze the post-hooks' result
1692 e4376078 Iustin Pop

1693 e4376078 Iustin Pop
    This method analyses the hook result, handles it, and sends some
1694 d8fff41c Guido Trotter
    nicely-formatted feedback back to the user.
1695 d8fff41c Guido Trotter

1696 e4376078 Iustin Pop
    @param phase: one of L{constants.HOOKS_PHASE_POST} or
1697 e4376078 Iustin Pop
        L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
1698 e4376078 Iustin Pop
    @param hooks_results: the results of the multi-node hooks rpc call
1699 e4376078 Iustin Pop
    @param feedback_fn: function used send feedback back to the caller
1700 e4376078 Iustin Pop
    @param lu_result: previous Exec result
1701 e4376078 Iustin Pop
    @return: the new Exec result, based on the previous result
1702 e4376078 Iustin Pop
        and hook results
1703 d8fff41c Guido Trotter

1704 d8fff41c Guido Trotter
    """
1705 38206f3c Iustin Pop
    # We only really run POST phase hooks, and are only interested in
1706 38206f3c Iustin Pop
    # their results
1707 d8fff41c Guido Trotter
    if phase == constants.HOOKS_PHASE_POST:
1708 d8fff41c Guido Trotter
      # Used to change hooks' output to proper indentation
1709 d8fff41c Guido Trotter
      indent_re = re.compile('^', re.M)
1710 d8fff41c Guido Trotter
      feedback_fn("* Hooks Results")
1711 7c874ee1 Iustin Pop
      assert hooks_results, "invalid result from hooks"
1712 7c874ee1 Iustin Pop
1713 7c874ee1 Iustin Pop
      for node_name in hooks_results:
1714 7c874ee1 Iustin Pop
        res = hooks_results[node_name]
1715 7c874ee1 Iustin Pop
        msg = res.fail_msg
1716 a0c9776a Iustin Pop
        test = msg and not res.offline
1717 a0c9776a Iustin Pop
        self._ErrorIf(test, self.ENODEHOOKS, node_name,
1718 7c874ee1 Iustin Pop
                      "Communication failure in hooks execution: %s", msg)
1719 dd9e9f9c Michael Hanselmann
        if res.offline or msg:
1720 dd9e9f9c Michael Hanselmann
          # No need to investigate payload if node is offline or gave an error.
1721 a0c9776a Iustin Pop
          # override manually lu_result here as _ErrorIf only
1722 a0c9776a Iustin Pop
          # overrides self.bad
1723 7c874ee1 Iustin Pop
          lu_result = 1
1724 7c874ee1 Iustin Pop
          continue
1725 7c874ee1 Iustin Pop
        for script, hkr, output in res.payload:
1726 a0c9776a Iustin Pop
          test = hkr == constants.HKR_FAIL
1727 a0c9776a Iustin Pop
          self._ErrorIf(test, self.ENODEHOOKS, node_name,
1728 7c874ee1 Iustin Pop
                        "Script %s failed, output:", script)
1729 a0c9776a Iustin Pop
          if test:
1730 7c874ee1 Iustin Pop
            output = indent_re.sub('      ', output)
1731 7c874ee1 Iustin Pop
            feedback_fn("%s" % output)
1732 6d7b472a Iustin Pop
            lu_result = 0
1733 d8fff41c Guido Trotter
1734 d8fff41c Guido Trotter
      return lu_result
1735 d8fff41c Guido Trotter
1736 a8083063 Iustin Pop
1737 2c95a8d4 Iustin Pop
class LUVerifyDisks(NoHooksLU):
1738 2c95a8d4 Iustin Pop
  """Verifies the cluster disks status.
1739 2c95a8d4 Iustin Pop

1740 2c95a8d4 Iustin Pop
  """
1741 2c95a8d4 Iustin Pop
  _OP_REQP = []
1742 d4b9d97f Guido Trotter
  REQ_BGL = False
1743 d4b9d97f Guido Trotter
1744 d4b9d97f Guido Trotter
  def ExpandNames(self):
1745 d4b9d97f Guido Trotter
    self.needed_locks = {
1746 d4b9d97f Guido Trotter
      locking.LEVEL_NODE: locking.ALL_SET,
1747 d4b9d97f Guido Trotter
      locking.LEVEL_INSTANCE: locking.ALL_SET,
1748 d4b9d97f Guido Trotter
    }
1749 c772d142 Michael Hanselmann
    self.share_locks = dict.fromkeys(locking.LEVELS, 1)
1750 2c95a8d4 Iustin Pop
1751 2c95a8d4 Iustin Pop
  def CheckPrereq(self):
1752 2c95a8d4 Iustin Pop
    """Check prerequisites.
1753 2c95a8d4 Iustin Pop

1754 2c95a8d4 Iustin Pop
    This has no prerequisites.
1755 2c95a8d4 Iustin Pop

1756 2c95a8d4 Iustin Pop
    """
1757 2c95a8d4 Iustin Pop
    pass
1758 2c95a8d4 Iustin Pop
1759 2c95a8d4 Iustin Pop
  def Exec(self, feedback_fn):
1760 2c95a8d4 Iustin Pop
    """Verify integrity of cluster disks.
1761 2c95a8d4 Iustin Pop

1762 29d376ec Iustin Pop
    @rtype: tuple of three items
1763 29d376ec Iustin Pop
    @return: a tuple of (dict of node-to-node_error, list of instances
1764 29d376ec Iustin Pop
        which need activate-disks, dict of instance: (node, volume) for
1765 29d376ec Iustin Pop
        missing volumes
1766 29d376ec Iustin Pop

1767 2c95a8d4 Iustin Pop
    """
1768 29d376ec Iustin Pop
    result = res_nodes, res_instances, res_missing = {}, [], {}
1769 2c95a8d4 Iustin Pop
1770 2c95a8d4 Iustin Pop
    vg_name = self.cfg.GetVGName()
1771 2c95a8d4 Iustin Pop
    nodes = utils.NiceSort(self.cfg.GetNodeList())
1772 2c95a8d4 Iustin Pop
    instances = [self.cfg.GetInstanceInfo(name)
1773 2c95a8d4 Iustin Pop
                 for name in self.cfg.GetInstanceList()]
1774 2c95a8d4 Iustin Pop
1775 2c95a8d4 Iustin Pop
    nv_dict = {}
1776 2c95a8d4 Iustin Pop
    for inst in instances:
1777 2c95a8d4 Iustin Pop
      inst_lvs = {}
1778 0d68c45d Iustin Pop
      if (not inst.admin_up or
1779 2c95a8d4 Iustin Pop
          inst.disk_template not in constants.DTS_NET_MIRROR):
1780 2c95a8d4 Iustin Pop
        continue
1781 2c95a8d4 Iustin Pop
      inst.MapLVsByNode(inst_lvs)
1782 2c95a8d4 Iustin Pop
      # transform { iname: {node: [vol,],},} to {(node, vol): iname}
1783 2c95a8d4 Iustin Pop
      for node, vol_list in inst_lvs.iteritems():
1784 2c95a8d4 Iustin Pop
        for vol in vol_list:
1785 2c95a8d4 Iustin Pop
          nv_dict[(node, vol)] = inst
1786 2c95a8d4 Iustin Pop
1787 2c95a8d4 Iustin Pop
    if not nv_dict:
1788 2c95a8d4 Iustin Pop
      return result
1789 2c95a8d4 Iustin Pop
1790 b2a6ccd4 Iustin Pop
    node_lvs = self.rpc.call_lv_list(nodes, vg_name)
1791 2c95a8d4 Iustin Pop
1792 2c95a8d4 Iustin Pop
    for node in nodes:
1793 2c95a8d4 Iustin Pop
      # node_volume
1794 29d376ec Iustin Pop
      node_res = node_lvs[node]
1795 29d376ec Iustin Pop
      if node_res.offline:
1796 ea9ddc07 Iustin Pop
        continue
1797 4c4e4e1e Iustin Pop
      msg = node_res.fail_msg
1798 29d376ec Iustin Pop
      if msg:
1799 29d376ec Iustin Pop
        logging.warning("Error enumerating LVs on node %s: %s", node, msg)
1800 29d376ec Iustin Pop
        res_nodes[node] = msg
1801 2c95a8d4 Iustin Pop
        continue
1802 2c95a8d4 Iustin Pop
1803 29d376ec Iustin Pop
      lvs = node_res.payload
1804 1122eb25 Iustin Pop
      for lv_name, (_, _, lv_online) in lvs.items():
1805 b63ed789 Iustin Pop
        inst = nv_dict.pop((node, lv_name), None)
1806 b63ed789 Iustin Pop
        if (not lv_online and inst is not None
1807 b63ed789 Iustin Pop
            and inst.name not in res_instances):
1808 b08d5a87 Iustin Pop
          res_instances.append(inst.name)
1809 2c95a8d4 Iustin Pop
1810 b63ed789 Iustin Pop
    # any leftover items in nv_dict are missing LVs, let's arrange the
1811 b63ed789 Iustin Pop
    # data better
1812 b63ed789 Iustin Pop
    for key, inst in nv_dict.iteritems():
1813 b63ed789 Iustin Pop
      if inst.name not in res_missing:
1814 b63ed789 Iustin Pop
        res_missing[inst.name] = []
1815 b63ed789 Iustin Pop
      res_missing[inst.name].append(key)
1816 b63ed789 Iustin Pop
1817 2c95a8d4 Iustin Pop
    return result
1818 2c95a8d4 Iustin Pop
1819 2c95a8d4 Iustin Pop
1820 60975797 Iustin Pop
class LURepairDiskSizes(NoHooksLU):
1821 60975797 Iustin Pop
  """Verifies the cluster disks sizes.
1822 60975797 Iustin Pop

1823 60975797 Iustin Pop
  """
1824 60975797 Iustin Pop
  _OP_REQP = ["instances"]
1825 60975797 Iustin Pop
  REQ_BGL = False
1826 60975797 Iustin Pop
1827 60975797 Iustin Pop
  def ExpandNames(self):
1828 60975797 Iustin Pop
    if not isinstance(self.op.instances, list):
1829 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Invalid argument type 'instances'",
1830 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
1831 60975797 Iustin Pop
1832 60975797 Iustin Pop
    if self.op.instances:
1833 60975797 Iustin Pop
      self.wanted_names = []
1834 60975797 Iustin Pop
      for name in self.op.instances:
1835 cf26a87a Iustin Pop
        full_name = _ExpandInstanceName(self.cfg, name)
1836 60975797 Iustin Pop
        self.wanted_names.append(full_name)
1837 60975797 Iustin Pop
      self.needed_locks = {
1838 60975797 Iustin Pop
        locking.LEVEL_NODE: [],
1839 60975797 Iustin Pop
        locking.LEVEL_INSTANCE: self.wanted_names,
1840 60975797 Iustin Pop
        }
1841 60975797 Iustin Pop
      self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
1842 60975797 Iustin Pop
    else:
1843 60975797 Iustin Pop
      self.wanted_names = None
1844 60975797 Iustin Pop
      self.needed_locks = {
1845 60975797 Iustin Pop
        locking.LEVEL_NODE: locking.ALL_SET,
1846 60975797 Iustin Pop
        locking.LEVEL_INSTANCE: locking.ALL_SET,
1847 60975797 Iustin Pop
        }
1848 60975797 Iustin Pop
    self.share_locks = dict(((i, 1) for i in locking.LEVELS))
1849 60975797 Iustin Pop
1850 60975797 Iustin Pop
  def DeclareLocks(self, level):
1851 60975797 Iustin Pop
    if level == locking.LEVEL_NODE and self.wanted_names is not None:
1852 60975797 Iustin Pop
      self._LockInstancesNodes(primary_only=True)
1853 60975797 Iustin Pop
1854 60975797 Iustin Pop
  def CheckPrereq(self):
1855 60975797 Iustin Pop
    """Check prerequisites.
1856 60975797 Iustin Pop

1857 60975797 Iustin Pop
    This only checks the optional instance list against the existing names.
1858 60975797 Iustin Pop

1859 60975797 Iustin Pop
    """
1860 60975797 Iustin Pop
    if self.wanted_names is None:
1861 60975797 Iustin Pop
      self.wanted_names = self.acquired_locks[locking.LEVEL_INSTANCE]
1862 60975797 Iustin Pop
1863 60975797 Iustin Pop
    self.wanted_instances = [self.cfg.GetInstanceInfo(name) for name
1864 60975797 Iustin Pop
                             in self.wanted_names]
1865 60975797 Iustin Pop
1866 b775c337 Iustin Pop
  def _EnsureChildSizes(self, disk):
1867 b775c337 Iustin Pop
    """Ensure children of the disk have the needed disk size.
1868 b775c337 Iustin Pop

1869 b775c337 Iustin Pop
    This is valid mainly for DRBD8 and fixes an issue where the
1870 b775c337 Iustin Pop
    children have smaller disk size.
1871 b775c337 Iustin Pop

1872 b775c337 Iustin Pop
    @param disk: an L{ganeti.objects.Disk} object
1873 b775c337 Iustin Pop

1874 b775c337 Iustin Pop
    """
1875 b775c337 Iustin Pop
    if disk.dev_type == constants.LD_DRBD8:
1876 b775c337 Iustin Pop
      assert disk.children, "Empty children for DRBD8?"
1877 b775c337 Iustin Pop
      fchild = disk.children[0]
1878 b775c337 Iustin Pop
      mismatch = fchild.size < disk.size
1879 b775c337 Iustin Pop
      if mismatch:
1880 b775c337 Iustin Pop
        self.LogInfo("Child disk has size %d, parent %d, fixing",
1881 b775c337 Iustin Pop
                     fchild.size, disk.size)
1882 b775c337 Iustin Pop
        fchild.size = disk.size
1883 b775c337 Iustin Pop
1884 b775c337 Iustin Pop
      # and we recurse on this child only, not on the metadev
1885 b775c337 Iustin Pop
      return self._EnsureChildSizes(fchild) or mismatch
1886 b775c337 Iustin Pop
    else:
1887 b775c337 Iustin Pop
      return False
1888 b775c337 Iustin Pop
1889 60975797 Iustin Pop
  def Exec(self, feedback_fn):
1890 60975797 Iustin Pop
    """Verify the size of cluster disks.
1891 60975797 Iustin Pop

1892 60975797 Iustin Pop
    """
1893 60975797 Iustin Pop
    # TODO: check child disks too
1894 60975797 Iustin Pop
    # TODO: check differences in size between primary/secondary nodes
1895 60975797 Iustin Pop
    per_node_disks = {}
1896 60975797 Iustin Pop
    for instance in self.wanted_instances:
1897 60975797 Iustin Pop
      pnode = instance.primary_node
1898 60975797 Iustin Pop
      if pnode not in per_node_disks:
1899 60975797 Iustin Pop
        per_node_disks[pnode] = []
1900 60975797 Iustin Pop
      for idx, disk in enumerate(instance.disks):
1901 60975797 Iustin Pop
        per_node_disks[pnode].append((instance, idx, disk))
1902 60975797 Iustin Pop
1903 60975797 Iustin Pop
    changed = []
1904 60975797 Iustin Pop
    for node, dskl in per_node_disks.items():
1905 4d9e6835 Iustin Pop
      newl = [v[2].Copy() for v in dskl]
1906 4d9e6835 Iustin Pop
      for dsk in newl:
1907 4d9e6835 Iustin Pop
        self.cfg.SetDiskID(dsk, node)
1908 4d9e6835 Iustin Pop
      result = self.rpc.call_blockdev_getsizes(node, newl)
1909 3cebe102 Michael Hanselmann
      if result.fail_msg:
1910 60975797 Iustin Pop
        self.LogWarning("Failure in blockdev_getsizes call to node"
1911 60975797 Iustin Pop
                        " %s, ignoring", node)
1912 60975797 Iustin Pop
        continue
1913 60975797 Iustin Pop
      if len(result.data) != len(dskl):
1914 60975797 Iustin Pop
        self.LogWarning("Invalid result from node %s, ignoring node results",
1915 60975797 Iustin Pop
                        node)
1916 60975797 Iustin Pop
        continue
1917 60975797 Iustin Pop
      for ((instance, idx, disk), size) in zip(dskl, result.data):
1918 60975797 Iustin Pop
        if size is None:
1919 60975797 Iustin Pop
          self.LogWarning("Disk %d of instance %s did not return size"
1920 60975797 Iustin Pop
                          " information, ignoring", idx, instance.name)
1921 60975797 Iustin Pop
          continue
1922 60975797 Iustin Pop
        if not isinstance(size, (int, long)):
1923 60975797 Iustin Pop
          self.LogWarning("Disk %d of instance %s did not return valid"
1924 60975797 Iustin Pop
                          " size information, ignoring", idx, instance.name)
1925 60975797 Iustin Pop
          continue
1926 60975797 Iustin Pop
        size = size >> 20
1927 60975797 Iustin Pop
        if size != disk.size:
1928 60975797 Iustin Pop
          self.LogInfo("Disk %d of instance %s has mismatched size,"
1929 60975797 Iustin Pop
                       " correcting: recorded %d, actual %d", idx,
1930 60975797 Iustin Pop
                       instance.name, disk.size, size)
1931 60975797 Iustin Pop
          disk.size = size
1932 a4eae71f Michael Hanselmann
          self.cfg.Update(instance, feedback_fn)
1933 60975797 Iustin Pop
          changed.append((instance.name, idx, size))
1934 b775c337 Iustin Pop
        if self._EnsureChildSizes(disk):
1935 a4eae71f Michael Hanselmann
          self.cfg.Update(instance, feedback_fn)
1936 b775c337 Iustin Pop
          changed.append((instance.name, idx, disk.size))
1937 60975797 Iustin Pop
    return changed
1938 60975797 Iustin Pop
1939 60975797 Iustin Pop
1940 07bd8a51 Iustin Pop
class LURenameCluster(LogicalUnit):
1941 07bd8a51 Iustin Pop
  """Rename the cluster.
1942 07bd8a51 Iustin Pop

1943 07bd8a51 Iustin Pop
  """
1944 07bd8a51 Iustin Pop
  HPATH = "cluster-rename"
1945 07bd8a51 Iustin Pop
  HTYPE = constants.HTYPE_CLUSTER
1946 07bd8a51 Iustin Pop
  _OP_REQP = ["name"]
1947 07bd8a51 Iustin Pop
1948 07bd8a51 Iustin Pop
  def BuildHooksEnv(self):
1949 07bd8a51 Iustin Pop
    """Build hooks env.
1950 07bd8a51 Iustin Pop

1951 07bd8a51 Iustin Pop
    """
1952 07bd8a51 Iustin Pop
    env = {
1953 d6a02168 Michael Hanselmann
      "OP_TARGET": self.cfg.GetClusterName(),
1954 07bd8a51 Iustin Pop
      "NEW_NAME": self.op.name,
1955 07bd8a51 Iustin Pop
      }
1956 d6a02168 Michael Hanselmann
    mn = self.cfg.GetMasterNode()
1957 47a72f18 Iustin Pop
    all_nodes = self.cfg.GetNodeList()
1958 47a72f18 Iustin Pop
    return env, [mn], all_nodes
1959 07bd8a51 Iustin Pop
1960 07bd8a51 Iustin Pop
  def CheckPrereq(self):
1961 07bd8a51 Iustin Pop
    """Verify that the passed name is a valid one.
1962 07bd8a51 Iustin Pop

1963 07bd8a51 Iustin Pop
    """
1964 104f4ca1 Iustin Pop
    hostname = utils.GetHostInfo(self.op.name)
1965 07bd8a51 Iustin Pop
1966 bcf043c9 Iustin Pop
    new_name = hostname.name
1967 bcf043c9 Iustin Pop
    self.ip = new_ip = hostname.ip
1968 d6a02168 Michael Hanselmann
    old_name = self.cfg.GetClusterName()
1969 d6a02168 Michael Hanselmann
    old_ip = self.cfg.GetMasterIP()
1970 07bd8a51 Iustin Pop
    if new_name == old_name and new_ip == old_ip:
1971 07bd8a51 Iustin Pop
      raise errors.OpPrereqError("Neither the name nor the IP address of the"
1972 5c983ee5 Iustin Pop
                                 " cluster has changed",
1973 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
1974 07bd8a51 Iustin Pop
    if new_ip != old_ip:
1975 937f983d Guido Trotter
      if utils.TcpPing(new_ip, constants.DEFAULT_NODED_PORT):
1976 07bd8a51 Iustin Pop
        raise errors.OpPrereqError("The given cluster IP address (%s) is"
1977 07bd8a51 Iustin Pop
                                   " reachable on the network. Aborting." %
1978 5c983ee5 Iustin Pop
                                   new_ip, errors.ECODE_NOTUNIQUE)
1979 07bd8a51 Iustin Pop
1980 07bd8a51 Iustin Pop
    self.op.name = new_name
1981 07bd8a51 Iustin Pop
1982 07bd8a51 Iustin Pop
  def Exec(self, feedback_fn):
1983 07bd8a51 Iustin Pop
    """Rename the cluster.
1984 07bd8a51 Iustin Pop

1985 07bd8a51 Iustin Pop
    """
1986 07bd8a51 Iustin Pop
    clustername = self.op.name
1987 07bd8a51 Iustin Pop
    ip = self.ip
1988 07bd8a51 Iustin Pop
1989 07bd8a51 Iustin Pop
    # shutdown the master IP
1990 d6a02168 Michael Hanselmann
    master = self.cfg.GetMasterNode()
1991 781de953 Iustin Pop
    result = self.rpc.call_node_stop_master(master, False)
1992 4c4e4e1e Iustin Pop
    result.Raise("Could not disable the master role")
1993 07bd8a51 Iustin Pop
1994 07bd8a51 Iustin Pop
    try:
1995 55cf7d83 Iustin Pop
      cluster = self.cfg.GetClusterInfo()
1996 55cf7d83 Iustin Pop
      cluster.cluster_name = clustername
1997 55cf7d83 Iustin Pop
      cluster.master_ip = ip
1998 a4eae71f Michael Hanselmann
      self.cfg.Update(cluster, feedback_fn)
1999 ec85e3d5 Iustin Pop
2000 ec85e3d5 Iustin Pop
      # update the known hosts file
2001 ec85e3d5 Iustin Pop
      ssh.WriteKnownHostsFile(self.cfg, constants.SSH_KNOWN_HOSTS_FILE)
2002 ec85e3d5 Iustin Pop
      node_list = self.cfg.GetNodeList()
2003 ec85e3d5 Iustin Pop
      try:
2004 ec85e3d5 Iustin Pop
        node_list.remove(master)
2005 ec85e3d5 Iustin Pop
      except ValueError:
2006 ec85e3d5 Iustin Pop
        pass
2007 ec85e3d5 Iustin Pop
      result = self.rpc.call_upload_file(node_list,
2008 ec85e3d5 Iustin Pop
                                         constants.SSH_KNOWN_HOSTS_FILE)
2009 ec85e3d5 Iustin Pop
      for to_node, to_result in result.iteritems():
2010 6f7d4e75 Iustin Pop
        msg = to_result.fail_msg
2011 6f7d4e75 Iustin Pop
        if msg:
2012 6f7d4e75 Iustin Pop
          msg = ("Copy of file %s to node %s failed: %s" %
2013 6f7d4e75 Iustin Pop
                 (constants.SSH_KNOWN_HOSTS_FILE, to_node, msg))
2014 6f7d4e75 Iustin Pop
          self.proc.LogWarning(msg)
2015 ec85e3d5 Iustin Pop
2016 07bd8a51 Iustin Pop
    finally:
2017 3583908a Guido Trotter
      result = self.rpc.call_node_start_master(master, False, False)
2018 4c4e4e1e Iustin Pop
      msg = result.fail_msg
2019 b726aff0 Iustin Pop
      if msg:
2020 86d9d3bb Iustin Pop
        self.LogWarning("Could not re-enable the master role on"
2021 b726aff0 Iustin Pop
                        " the master, please restart manually: %s", msg)
2022 07bd8a51 Iustin Pop
2023 07bd8a51 Iustin Pop
2024 8084f9f6 Manuel Franceschini
def _RecursiveCheckIfLVMBased(disk):
2025 8084f9f6 Manuel Franceschini
  """Check if the given disk or its children are lvm-based.
2026 8084f9f6 Manuel Franceschini

2027 e4376078 Iustin Pop
  @type disk: L{objects.Disk}
2028 e4376078 Iustin Pop
  @param disk: the disk to check
2029 5bbd3f7f Michael Hanselmann
  @rtype: boolean
2030 e4376078 Iustin Pop
  @return: boolean indicating whether a LD_LV dev_type was found or not
2031 8084f9f6 Manuel Franceschini

2032 8084f9f6 Manuel Franceschini
  """
2033 8084f9f6 Manuel Franceschini
  if disk.children:
2034 8084f9f6 Manuel Franceschini
    for chdisk in disk.children:
2035 8084f9f6 Manuel Franceschini
      if _RecursiveCheckIfLVMBased(chdisk):
2036 8084f9f6 Manuel Franceschini
        return True
2037 8084f9f6 Manuel Franceschini
  return disk.dev_type == constants.LD_LV
2038 8084f9f6 Manuel Franceschini
2039 8084f9f6 Manuel Franceschini
2040 8084f9f6 Manuel Franceschini
class LUSetClusterParams(LogicalUnit):
2041 8084f9f6 Manuel Franceschini
  """Change the parameters of the cluster.
2042 8084f9f6 Manuel Franceschini

2043 8084f9f6 Manuel Franceschini
  """
2044 8084f9f6 Manuel Franceschini
  HPATH = "cluster-modify"
2045 8084f9f6 Manuel Franceschini
  HTYPE = constants.HTYPE_CLUSTER
2046 8084f9f6 Manuel Franceschini
  _OP_REQP = []
2047 c53279cf Guido Trotter
  REQ_BGL = False
2048 c53279cf Guido Trotter
2049 3994f455 Iustin Pop
  def CheckArguments(self):
2050 4b7735f9 Iustin Pop
    """Check parameters
2051 4b7735f9 Iustin Pop

2052 4b7735f9 Iustin Pop
    """
2053 4b7735f9 Iustin Pop
    if not hasattr(self.op, "candidate_pool_size"):
2054 4b7735f9 Iustin Pop
      self.op.candidate_pool_size = None
2055 4b7735f9 Iustin Pop
    if self.op.candidate_pool_size is not None:
2056 4b7735f9 Iustin Pop
      try:
2057 4b7735f9 Iustin Pop
        self.op.candidate_pool_size = int(self.op.candidate_pool_size)
2058 3994f455 Iustin Pop
      except (ValueError, TypeError), err:
2059 4b7735f9 Iustin Pop
        raise errors.OpPrereqError("Invalid candidate_pool_size value: %s" %
2060 5c983ee5 Iustin Pop
                                   str(err), errors.ECODE_INVAL)
2061 4b7735f9 Iustin Pop
      if self.op.candidate_pool_size < 1:
2062 5c983ee5 Iustin Pop
        raise errors.OpPrereqError("At least one master candidate needed",
2063 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
2064 4b7735f9 Iustin Pop
2065 c53279cf Guido Trotter
  def ExpandNames(self):
2066 c53279cf Guido Trotter
    # FIXME: in the future maybe other cluster params won't require checking on
2067 c53279cf Guido Trotter
    # all nodes to be modified.
2068 c53279cf Guido Trotter
    self.needed_locks = {
2069 c53279cf Guido Trotter
      locking.LEVEL_NODE: locking.ALL_SET,
2070 c53279cf Guido Trotter
    }
2071 c53279cf Guido Trotter
    self.share_locks[locking.LEVEL_NODE] = 1
2072 8084f9f6 Manuel Franceschini
2073 8084f9f6 Manuel Franceschini
  def BuildHooksEnv(self):
2074 8084f9f6 Manuel Franceschini
    """Build hooks env.
2075 8084f9f6 Manuel Franceschini

2076 8084f9f6 Manuel Franceschini
    """
2077 8084f9f6 Manuel Franceschini
    env = {
2078 d6a02168 Michael Hanselmann
      "OP_TARGET": self.cfg.GetClusterName(),
2079 8084f9f6 Manuel Franceschini
      "NEW_VG_NAME": self.op.vg_name,
2080 8084f9f6 Manuel Franceschini
      }
2081 d6a02168 Michael Hanselmann
    mn = self.cfg.GetMasterNode()
2082 8084f9f6 Manuel Franceschini
    return env, [mn], [mn]
2083 8084f9f6 Manuel Franceschini
2084 8084f9f6 Manuel Franceschini
  def CheckPrereq(self):
2085 8084f9f6 Manuel Franceschini
    """Check prerequisites.
2086 8084f9f6 Manuel Franceschini

2087 8084f9f6 Manuel Franceschini
    This checks whether the given params don't conflict and
2088 5f83e263 Iustin Pop
    if the given volume group is valid.
2089 8084f9f6 Manuel Franceschini

2090 8084f9f6 Manuel Franceschini
    """
2091 779c15bb Iustin Pop
    if self.op.vg_name is not None and not self.op.vg_name:
2092 c53279cf Guido Trotter
      instances = self.cfg.GetAllInstancesInfo().values()
2093 8084f9f6 Manuel Franceschini
      for inst in instances:
2094 8084f9f6 Manuel Franceschini
        for disk in inst.disks:
2095 8084f9f6 Manuel Franceschini
          if _RecursiveCheckIfLVMBased(disk):
2096 8084f9f6 Manuel Franceschini
            raise errors.OpPrereqError("Cannot disable lvm storage while"
2097 5c983ee5 Iustin Pop
                                       " lvm-based instances exist",
2098 5c983ee5 Iustin Pop
                                       errors.ECODE_INVAL)
2099 8084f9f6 Manuel Franceschini
2100 779c15bb Iustin Pop
    node_list = self.acquired_locks[locking.LEVEL_NODE]
2101 779c15bb Iustin Pop
2102 8084f9f6 Manuel Franceschini
    # if vg_name not None, checks given volume group on all nodes
2103 8084f9f6 Manuel Franceschini
    if self.op.vg_name:
2104 72737a7f Iustin Pop
      vglist = self.rpc.call_vg_list(node_list)
2105 8084f9f6 Manuel Franceschini
      for node in node_list:
2106 4c4e4e1e Iustin Pop
        msg = vglist[node].fail_msg
2107 e480923b Iustin Pop
        if msg:
2108 781de953 Iustin Pop
          # ignoring down node
2109 e480923b Iustin Pop
          self.LogWarning("Error while gathering data on node %s"
2110 e480923b Iustin Pop
                          " (ignoring node): %s", node, msg)
2111 781de953 Iustin Pop
          continue
2112 e480923b Iustin Pop
        vgstatus = utils.CheckVolumeGroupSize(vglist[node].payload,
2113 781de953 Iustin Pop
                                              self.op.vg_name,
2114 8d1a2a64 Michael Hanselmann
                                              constants.MIN_VG_SIZE)
2115 8084f9f6 Manuel Franceschini
        if vgstatus:
2116 8084f9f6 Manuel Franceschini
          raise errors.OpPrereqError("Error on node '%s': %s" %
2117 5c983ee5 Iustin Pop
                                     (node, vgstatus), errors.ECODE_ENVIRON)
2118 8084f9f6 Manuel Franceschini
2119 779c15bb Iustin Pop
    self.cluster = cluster = self.cfg.GetClusterInfo()
2120 5af3da74 Guido Trotter
    # validate params changes
2121 779c15bb Iustin Pop
    if self.op.beparams:
2122 a5728081 Guido Trotter
      utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
2123 abe609b2 Guido Trotter
      self.new_beparams = objects.FillDict(
2124 4ef7f423 Guido Trotter
        cluster.beparams[constants.PP_DEFAULT], self.op.beparams)
2125 779c15bb Iustin Pop
2126 5af3da74 Guido Trotter
    if self.op.nicparams:
2127 5af3da74 Guido Trotter
      utils.ForceDictType(self.op.nicparams, constants.NICS_PARAMETER_TYPES)
2128 5af3da74 Guido Trotter
      self.new_nicparams = objects.FillDict(
2129 5af3da74 Guido Trotter
        cluster.nicparams[constants.PP_DEFAULT], self.op.nicparams)
2130 5af3da74 Guido Trotter
      objects.NIC.CheckParameterSyntax(self.new_nicparams)
2131 90b704a1 Guido Trotter
      nic_errors = []
2132 90b704a1 Guido Trotter
2133 90b704a1 Guido Trotter
      # check all instances for consistency
2134 90b704a1 Guido Trotter
      for instance in self.cfg.GetAllInstancesInfo().values():
2135 90b704a1 Guido Trotter
        for nic_idx, nic in enumerate(instance.nics):
2136 90b704a1 Guido Trotter
          params_copy = copy.deepcopy(nic.nicparams)
2137 90b704a1 Guido Trotter
          params_filled = objects.FillDict(self.new_nicparams, params_copy)
2138 90b704a1 Guido Trotter
2139 90b704a1 Guido Trotter
          # check parameter syntax
2140 90b704a1 Guido Trotter
          try:
2141 90b704a1 Guido Trotter
            objects.NIC.CheckParameterSyntax(params_filled)
2142 90b704a1 Guido Trotter
          except errors.ConfigurationError, err:
2143 90b704a1 Guido Trotter
            nic_errors.append("Instance %s, nic/%d: %s" %
2144 90b704a1 Guido Trotter
                              (instance.name, nic_idx, err))
2145 90b704a1 Guido Trotter
2146 90b704a1 Guido Trotter
          # if we're moving instances to routed, check that they have an ip
2147 90b704a1 Guido Trotter
          target_mode = params_filled[constants.NIC_MODE]
2148 90b704a1 Guido Trotter
          if target_mode == constants.NIC_MODE_ROUTED and not nic.ip:
2149 90b704a1 Guido Trotter
            nic_errors.append("Instance %s, nic/%d: routed nick with no ip" %
2150 90b704a1 Guido Trotter
                              (instance.name, nic_idx))
2151 90b704a1 Guido Trotter
      if nic_errors:
2152 90b704a1 Guido Trotter
        raise errors.OpPrereqError("Cannot apply the change, errors:\n%s" %
2153 90b704a1 Guido Trotter
                                   "\n".join(nic_errors))
2154 5af3da74 Guido Trotter
2155 779c15bb Iustin Pop
    # hypervisor list/parameters
2156 abe609b2 Guido Trotter
    self.new_hvparams = objects.FillDict(cluster.hvparams, {})
2157 779c15bb Iustin Pop
    if self.op.hvparams:
2158 779c15bb Iustin Pop
      if not isinstance(self.op.hvparams, dict):
2159 5c983ee5 Iustin Pop
        raise errors.OpPrereqError("Invalid 'hvparams' parameter on input",
2160 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
2161 779c15bb Iustin Pop
      for hv_name, hv_dict in self.op.hvparams.items():
2162 779c15bb Iustin Pop
        if hv_name not in self.new_hvparams:
2163 779c15bb Iustin Pop
          self.new_hvparams[hv_name] = hv_dict
2164 779c15bb Iustin Pop
        else:
2165 779c15bb Iustin Pop
          self.new_hvparams[hv_name].update(hv_dict)
2166 779c15bb Iustin Pop
2167 17463d22 René Nussbaumer
    # os hypervisor parameters
2168 17463d22 René Nussbaumer
    self.new_os_hvp = objects.FillDict(cluster.os_hvp, {})
2169 17463d22 René Nussbaumer
    if self.op.os_hvp:
2170 17463d22 René Nussbaumer
      if not isinstance(self.op.os_hvp, dict):
2171 17463d22 René Nussbaumer
        raise errors.OpPrereqError("Invalid 'os_hvp' parameter on input",
2172 17463d22 René Nussbaumer
                                   errors.ECODE_INVAL)
2173 17463d22 René Nussbaumer
      for os_name, hvs in self.op.os_hvp.items():
2174 17463d22 René Nussbaumer
        if not isinstance(hvs, dict):
2175 17463d22 René Nussbaumer
          raise errors.OpPrereqError(("Invalid 'os_hvp' parameter on"
2176 17463d22 René Nussbaumer
                                      " input"), errors.ECODE_INVAL)
2177 17463d22 René Nussbaumer
        if os_name not in self.new_os_hvp:
2178 17463d22 René Nussbaumer
          self.new_os_hvp[os_name] = hvs
2179 17463d22 René Nussbaumer
        else:
2180 17463d22 René Nussbaumer
          for hv_name, hv_dict in hvs.items():
2181 17463d22 René Nussbaumer
            if hv_name not in self.new_os_hvp[os_name]:
2182 17463d22 René Nussbaumer
              self.new_os_hvp[os_name][hv_name] = hv_dict
2183 17463d22 René Nussbaumer
            else:
2184 17463d22 René Nussbaumer
              self.new_os_hvp[os_name][hv_name].update(hv_dict)
2185 17463d22 René Nussbaumer
2186 779c15bb Iustin Pop
    if self.op.enabled_hypervisors is not None:
2187 779c15bb Iustin Pop
      self.hv_list = self.op.enabled_hypervisors
2188 b119bccb Guido Trotter
      if not self.hv_list:
2189 b119bccb Guido Trotter
        raise errors.OpPrereqError("Enabled hypervisors list must contain at"
2190 5c983ee5 Iustin Pop
                                   " least one member",
2191 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
2192 b119bccb Guido Trotter
      invalid_hvs = set(self.hv_list) - constants.HYPER_TYPES
2193 b119bccb Guido Trotter
      if invalid_hvs:
2194 b119bccb Guido Trotter
        raise errors.OpPrereqError("Enabled hypervisors contains invalid"
2195 ab3e6da8 Iustin Pop
                                   " entries: %s" %
2196 ab3e6da8 Iustin Pop
                                   utils.CommaJoin(invalid_hvs),
2197 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
2198 779c15bb Iustin Pop
    else:
2199 779c15bb Iustin Pop
      self.hv_list = cluster.enabled_hypervisors
2200 779c15bb Iustin Pop
2201 779c15bb Iustin Pop
    if self.op.hvparams or self.op.enabled_hypervisors is not None:
2202 779c15bb Iustin Pop
      # either the enabled list has changed, or the parameters have, validate
2203 779c15bb Iustin Pop
      for hv_name, hv_params in self.new_hvparams.items():
2204 779c15bb Iustin Pop
        if ((self.op.hvparams and hv_name in self.op.hvparams) or
2205 779c15bb Iustin Pop
            (self.op.enabled_hypervisors and
2206 779c15bb Iustin Pop
             hv_name in self.op.enabled_hypervisors)):
2207 779c15bb Iustin Pop
          # either this is a new hypervisor, or its parameters have changed
2208 779c15bb Iustin Pop
          hv_class = hypervisor.GetHypervisor(hv_name)
2209 a5728081 Guido Trotter
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
2210 779c15bb Iustin Pop
          hv_class.CheckParameterSyntax(hv_params)
2211 779c15bb Iustin Pop
          _CheckHVParams(self, node_list, hv_name, hv_params)
2212 779c15bb Iustin Pop
2213 cced4c39 Iustin Pop
    if self.op.os_hvp:
2214 cced4c39 Iustin Pop
      # no need to check any newly-enabled hypervisors, since the
2215 cced4c39 Iustin Pop
      # defaults have already been checked in the above code-block
2216 cced4c39 Iustin Pop
      for os_name, os_hvp in self.new_os_hvp.items():
2217 cced4c39 Iustin Pop
        for hv_name, hv_params in os_hvp.items():
2218 cced4c39 Iustin Pop
          utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
2219 cced4c39 Iustin Pop
          # we need to fill in the new os_hvp on top of the actual hv_p
2220 cced4c39 Iustin Pop
          cluster_defaults = self.new_hvparams.get(hv_name, {})
2221 cced4c39 Iustin Pop
          new_osp = objects.FillDict(cluster_defaults, hv_params)
2222 cced4c39 Iustin Pop
          hv_class = hypervisor.GetHypervisor(hv_name)
2223 cced4c39 Iustin Pop
          hv_class.CheckParameterSyntax(new_osp)
2224 cced4c39 Iustin Pop
          _CheckHVParams(self, node_list, hv_name, new_osp)
2225 cced4c39 Iustin Pop
2226 cced4c39 Iustin Pop
2227 8084f9f6 Manuel Franceschini
  def Exec(self, feedback_fn):
2228 8084f9f6 Manuel Franceschini
    """Change the parameters of the cluster.
2229 8084f9f6 Manuel Franceschini

2230 8084f9f6 Manuel Franceschini
    """
2231 779c15bb Iustin Pop
    if self.op.vg_name is not None:
2232 b2482333 Guido Trotter
      new_volume = self.op.vg_name
2233 b2482333 Guido Trotter
      if not new_volume:
2234 b2482333 Guido Trotter
        new_volume = None
2235 b2482333 Guido Trotter
      if new_volume != self.cfg.GetVGName():
2236 b2482333 Guido Trotter
        self.cfg.SetVGName(new_volume)
2237 779c15bb Iustin Pop
      else:
2238 779c15bb Iustin Pop
        feedback_fn("Cluster LVM configuration already in desired"
2239 779c15bb Iustin Pop
                    " state, not changing")
2240 779c15bb Iustin Pop
    if self.op.hvparams:
2241 779c15bb Iustin Pop
      self.cluster.hvparams = self.new_hvparams
2242 17463d22 René Nussbaumer
    if self.op.os_hvp:
2243 17463d22 René Nussbaumer
      self.cluster.os_hvp = self.new_os_hvp
2244 779c15bb Iustin Pop
    if self.op.enabled_hypervisors is not None:
2245 779c15bb Iustin Pop
      self.cluster.enabled_hypervisors = self.op.enabled_hypervisors
2246 779c15bb Iustin Pop
    if self.op.beparams:
2247 4ef7f423 Guido Trotter
      self.cluster.beparams[constants.PP_DEFAULT] = self.new_beparams
2248 5af3da74 Guido Trotter
    if self.op.nicparams:
2249 5af3da74 Guido Trotter
      self.cluster.nicparams[constants.PP_DEFAULT] = self.new_nicparams
2250 5af3da74 Guido Trotter
2251 4b7735f9 Iustin Pop
    if self.op.candidate_pool_size is not None:
2252 4b7735f9 Iustin Pop
      self.cluster.candidate_pool_size = self.op.candidate_pool_size
2253 75e914fb Iustin Pop
      # we need to update the pool size here, otherwise the save will fail
2254 44485f49 Guido Trotter
      _AdjustCandidatePool(self, [])
2255 4b7735f9 Iustin Pop
2256 a4eae71f Michael Hanselmann
    self.cfg.Update(self.cluster, feedback_fn)
2257 8084f9f6 Manuel Franceschini
2258 8084f9f6 Manuel Franceschini
2259 28eddce5 Guido Trotter
def _RedistributeAncillaryFiles(lu, additional_nodes=None):
2260 28eddce5 Guido Trotter
  """Distribute additional files which are part of the cluster configuration.
2261 28eddce5 Guido Trotter

2262 28eddce5 Guido Trotter
  ConfigWriter takes care of distributing the config and ssconf files, but
2263 28eddce5 Guido Trotter
  there are more files which should be distributed to all nodes. This function
2264 28eddce5 Guido Trotter
  makes sure those are copied.
2265 28eddce5 Guido Trotter

2266 28eddce5 Guido Trotter
  @param lu: calling logical unit
2267 28eddce5 Guido Trotter
  @param additional_nodes: list of nodes not in the config to distribute to
2268 28eddce5 Guido Trotter

2269 28eddce5 Guido Trotter
  """
2270 28eddce5 Guido Trotter
  # 1. Gather target nodes
2271 28eddce5 Guido Trotter
  myself = lu.cfg.GetNodeInfo(lu.cfg.GetMasterNode())
2272 6819dc49 Iustin Pop
  dist_nodes = lu.cfg.GetOnlineNodeList()
2273 28eddce5 Guido Trotter
  if additional_nodes is not None:
2274 28eddce5 Guido Trotter
    dist_nodes.extend(additional_nodes)
2275 28eddce5 Guido Trotter
  if myself.name in dist_nodes:
2276 28eddce5 Guido Trotter
    dist_nodes.remove(myself.name)
2277 a4eae71f Michael Hanselmann
2278 28eddce5 Guido Trotter
  # 2. Gather files to distribute
2279 28eddce5 Guido Trotter
  dist_files = set([constants.ETC_HOSTS,
2280 28eddce5 Guido Trotter
                    constants.SSH_KNOWN_HOSTS_FILE,
2281 28eddce5 Guido Trotter
                    constants.RAPI_CERT_FILE,
2282 28eddce5 Guido Trotter
                    constants.RAPI_USERS_FILE,
2283 6b7d5878 Michael Hanselmann
                    constants.CONFD_HMAC_KEY,
2284 28eddce5 Guido Trotter
                   ])
2285 e1b8653f Guido Trotter
2286 e1b8653f Guido Trotter
  enabled_hypervisors = lu.cfg.GetClusterInfo().enabled_hypervisors
2287 e1b8653f Guido Trotter
  for hv_name in enabled_hypervisors:
2288 e1b8653f Guido Trotter
    hv_class = hypervisor.GetHypervisor(hv_name)
2289 e1b8653f Guido Trotter
    dist_files.update(hv_class.GetAncillaryFiles())
2290 e1b8653f Guido Trotter
2291 28eddce5 Guido Trotter
  # 3. Perform the files upload
2292 28eddce5 Guido Trotter
  for fname in dist_files:
2293 28eddce5 Guido Trotter
    if os.path.exists(fname):
2294 28eddce5 Guido Trotter
      result = lu.rpc.call_upload_file(dist_nodes, fname)
2295 28eddce5 Guido Trotter
      for to_node, to_result in result.items():
2296 6f7d4e75 Iustin Pop
        msg = to_result.fail_msg
2297 6f7d4e75 Iustin Pop
        if msg:
2298 6f7d4e75 Iustin Pop
          msg = ("Copy of file %s to node %s failed: %s" %
2299 6f7d4e75 Iustin Pop
                 (fname, to_node, msg))
2300 6f7d4e75 Iustin Pop
          lu.proc.LogWarning(msg)
2301 28eddce5 Guido Trotter
2302 28eddce5 Guido Trotter
2303 afee0879 Iustin Pop
class LURedistributeConfig(NoHooksLU):
2304 afee0879 Iustin Pop
  """Force the redistribution of cluster configuration.
2305 afee0879 Iustin Pop

2306 afee0879 Iustin Pop
  This is a very simple LU.
2307 afee0879 Iustin Pop

2308 afee0879 Iustin Pop
  """
2309 afee0879 Iustin Pop
  _OP_REQP = []
2310 afee0879 Iustin Pop
  REQ_BGL = False
2311 afee0879 Iustin Pop
2312 afee0879 Iustin Pop
  def ExpandNames(self):
2313 afee0879 Iustin Pop
    self.needed_locks = {
2314 afee0879 Iustin Pop
      locking.LEVEL_NODE: locking.ALL_SET,
2315 afee0879 Iustin Pop
    }
2316 afee0879 Iustin Pop
    self.share_locks[locking.LEVEL_NODE] = 1
2317 afee0879 Iustin Pop
2318 afee0879 Iustin Pop
  def CheckPrereq(self):
2319 afee0879 Iustin Pop
    """Check prerequisites.
2320 afee0879 Iustin Pop

2321 afee0879 Iustin Pop
    """
2322 afee0879 Iustin Pop
2323 afee0879 Iustin Pop
  def Exec(self, feedback_fn):
2324 afee0879 Iustin Pop
    """Redistribute the configuration.
2325 afee0879 Iustin Pop

2326 afee0879 Iustin Pop
    """
2327 a4eae71f Michael Hanselmann
    self.cfg.Update(self.cfg.GetClusterInfo(), feedback_fn)
2328 28eddce5 Guido Trotter
    _RedistributeAncillaryFiles(self)
2329 afee0879 Iustin Pop
2330 afee0879 Iustin Pop
2331 b6c07b79 Michael Hanselmann
def _WaitForSync(lu, instance, oneshot=False):
2332 a8083063 Iustin Pop
  """Sleep and poll for an instance's disk to sync.
2333 a8083063 Iustin Pop

2334 a8083063 Iustin Pop
  """
2335 a8083063 Iustin Pop
  if not instance.disks:
2336 a8083063 Iustin Pop
    return True
2337 a8083063 Iustin Pop
2338 a8083063 Iustin Pop
  if not oneshot:
2339 b9bddb6b Iustin Pop
    lu.proc.LogInfo("Waiting for instance %s to sync disks." % instance.name)
2340 a8083063 Iustin Pop
2341 a8083063 Iustin Pop
  node = instance.primary_node
2342 a8083063 Iustin Pop
2343 a8083063 Iustin Pop
  for dev in instance.disks:
2344 b9bddb6b Iustin Pop
    lu.cfg.SetDiskID(dev, node)
2345 a8083063 Iustin Pop
2346 6bcb1446 Michael Hanselmann
  # TODO: Convert to utils.Retry
2347 6bcb1446 Michael Hanselmann
2348 a8083063 Iustin Pop
  retries = 0
2349 fbafd7a8 Iustin Pop
  degr_retries = 10 # in seconds, as we sleep 1 second each time
2350 a8083063 Iustin Pop
  while True:
2351 a8083063 Iustin Pop
    max_time = 0
2352 a8083063 Iustin Pop
    done = True
2353 a8083063 Iustin Pop
    cumul_degraded = False
2354 72737a7f Iustin Pop
    rstats = lu.rpc.call_blockdev_getmirrorstatus(node, instance.disks)
2355 4c4e4e1e Iustin Pop
    msg = rstats.fail_msg
2356 3efa9051 Iustin Pop
    if msg:
2357 3efa9051 Iustin Pop
      lu.LogWarning("Can't get any data from node %s: %s", node, msg)
2358 a8083063 Iustin Pop
      retries += 1
2359 a8083063 Iustin Pop
      if retries >= 10:
2360 3ecf6786 Iustin Pop
        raise errors.RemoteError("Can't contact node %s for mirror data,"
2361 3ecf6786 Iustin Pop
                                 " aborting." % node)
2362 a8083063 Iustin Pop
      time.sleep(6)
2363 a8083063 Iustin Pop
      continue
2364 3efa9051 Iustin Pop
    rstats = rstats.payload
2365 a8083063 Iustin Pop
    retries = 0
2366 1492cca7 Iustin Pop
    for i, mstat in enumerate(rstats):
2367 a8083063 Iustin Pop
      if mstat is None:
2368 86d9d3bb Iustin Pop
        lu.LogWarning("Can't compute data for node %s/%s",
2369 86d9d3bb Iustin Pop
                           node, instance.disks[i].iv_name)
2370 a8083063 Iustin Pop
        continue
2371 36145b12 Michael Hanselmann
2372 36145b12 Michael Hanselmann
      cumul_degraded = (cumul_degraded or
2373 36145b12 Michael Hanselmann
                        (mstat.is_degraded and mstat.sync_percent is None))
2374 36145b12 Michael Hanselmann
      if mstat.sync_percent is not None:
2375 a8083063 Iustin Pop
        done = False
2376 36145b12 Michael Hanselmann
        if mstat.estimated_time is not None:
2377 36145b12 Michael Hanselmann
          rem_time = "%d estimated seconds remaining" % mstat.estimated_time
2378 36145b12 Michael Hanselmann
          max_time = mstat.estimated_time
2379 a8083063 Iustin Pop
        else:
2380 a8083063 Iustin Pop
          rem_time = "no time estimate"
2381 b9bddb6b Iustin Pop
        lu.proc.LogInfo("- device %s: %5.2f%% done, %s" %
2382 4d4a651d Michael Hanselmann
                        (instance.disks[i].iv_name, mstat.sync_percent,
2383 4d4a651d Michael Hanselmann
                         rem_time))
2384 fbafd7a8 Iustin Pop
2385 fbafd7a8 Iustin Pop
    # if we're done but degraded, let's do a few small retries, to
2386 fbafd7a8 Iustin Pop
    # make sure we see a stable and not transient situation; therefore
2387 fbafd7a8 Iustin Pop
    # we force restart of the loop
2388 fbafd7a8 Iustin Pop
    if (done or oneshot) and cumul_degraded and degr_retries > 0:
2389 fbafd7a8 Iustin Pop
      logging.info("Degraded disks found, %d retries left", degr_retries)
2390 fbafd7a8 Iustin Pop
      degr_retries -= 1
2391 fbafd7a8 Iustin Pop
      time.sleep(1)
2392 fbafd7a8 Iustin Pop
      continue
2393 fbafd7a8 Iustin Pop
2394 a8083063 Iustin Pop
    if done or oneshot:
2395 a8083063 Iustin Pop
      break
2396 a8083063 Iustin Pop
2397 d4fa5c23 Iustin Pop
    time.sleep(min(60, max_time))
2398 a8083063 Iustin Pop
2399 a8083063 Iustin Pop
  if done:
2400 b9bddb6b Iustin Pop
    lu.proc.LogInfo("Instance %s's disks are in sync." % instance.name)
2401 a8083063 Iustin Pop
  return not cumul_degraded
2402 a8083063 Iustin Pop
2403 a8083063 Iustin Pop
2404 b9bddb6b Iustin Pop
def _CheckDiskConsistency(lu, dev, node, on_primary, ldisk=False):
2405 a8083063 Iustin Pop
  """Check that mirrors are not degraded.
2406 a8083063 Iustin Pop

2407 0834c866 Iustin Pop
  The ldisk parameter, if True, will change the test from the
2408 0834c866 Iustin Pop
  is_degraded attribute (which represents overall non-ok status for
2409 0834c866 Iustin Pop
  the device(s)) to the ldisk (representing the local storage status).
2410 0834c866 Iustin Pop

2411 a8083063 Iustin Pop
  """
2412 b9bddb6b Iustin Pop
  lu.cfg.SetDiskID(dev, node)
2413 a8083063 Iustin Pop
2414 a8083063 Iustin Pop
  result = True
2415 96acbc09 Michael Hanselmann
2416 a8083063 Iustin Pop
  if on_primary or dev.AssembleOnSecondary():
2417 72737a7f Iustin Pop
    rstats = lu.rpc.call_blockdev_find(node, dev)
2418 4c4e4e1e Iustin Pop
    msg = rstats.fail_msg
2419 23829f6f Iustin Pop
    if msg:
2420 23829f6f Iustin Pop
      lu.LogWarning("Can't find disk on node %s: %s", node, msg)
2421 23829f6f Iustin Pop
      result = False
2422 23829f6f Iustin Pop
    elif not rstats.payload:
2423 23829f6f Iustin Pop
      lu.LogWarning("Can't find disk on node %s", node)
2424 a8083063 Iustin Pop
      result = False
2425 a8083063 Iustin Pop
    else:
2426 96acbc09 Michael Hanselmann
      if ldisk:
2427 f208978a Michael Hanselmann
        result = result and rstats.payload.ldisk_status == constants.LDS_OKAY
2428 96acbc09 Michael Hanselmann
      else:
2429 96acbc09 Michael Hanselmann
        result = result and not rstats.payload.is_degraded
2430 96acbc09 Michael Hanselmann
2431 a8083063 Iustin Pop
  if dev.children:
2432 a8083063 Iustin Pop
    for child in dev.children:
2433 b9bddb6b Iustin Pop
      result = result and _CheckDiskConsistency(lu, child, node, on_primary)
2434 a8083063 Iustin Pop
2435 a8083063 Iustin Pop
  return result
2436 a8083063 Iustin Pop
2437 a8083063 Iustin Pop
2438 a8083063 Iustin Pop
class LUDiagnoseOS(NoHooksLU):
2439 a8083063 Iustin Pop
  """Logical unit for OS diagnose/query.
2440 a8083063 Iustin Pop

2441 a8083063 Iustin Pop
  """
2442 1f9430d6 Iustin Pop
  _OP_REQP = ["output_fields", "names"]
2443 6bf01bbb Guido Trotter
  REQ_BGL = False
2444 a2d2e1a7 Iustin Pop
  _FIELDS_STATIC = utils.FieldSet()
2445 1e288a26 Guido Trotter
  _FIELDS_DYNAMIC = utils.FieldSet("name", "valid", "node_status", "variants")
2446 1e288a26 Guido Trotter
  # Fields that need calculation of global os validity
2447 1e288a26 Guido Trotter
  _FIELDS_NEEDVALID = frozenset(["valid", "variants"])
2448 a8083063 Iustin Pop
2449 6bf01bbb Guido Trotter
  def ExpandNames(self):
2450 1f9430d6 Iustin Pop
    if self.op.names:
2451 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Selective OS query not supported",
2452 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
2453 1f9430d6 Iustin Pop
2454 31bf511f Iustin Pop
    _CheckOutputFields(static=self._FIELDS_STATIC,
2455 31bf511f Iustin Pop
                       dynamic=self._FIELDS_DYNAMIC,
2456 1f9430d6 Iustin Pop
                       selected=self.op.output_fields)
2457 1f9430d6 Iustin Pop
2458 6bf01bbb Guido Trotter
    # Lock all nodes, in shared mode
2459 a6ab004b Iustin Pop
    # Temporary removal of locks, should be reverted later
2460 a6ab004b Iustin Pop
    # TODO: reintroduce locks when they are lighter-weight
2461 6bf01bbb Guido Trotter
    self.needed_locks = {}
2462 a6ab004b Iustin Pop
    #self.share_locks[locking.LEVEL_NODE] = 1
2463 a6ab004b Iustin Pop
    #self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
2464 6bf01bbb Guido Trotter
2465 6bf01bbb Guido Trotter
  def CheckPrereq(self):
2466 6bf01bbb Guido Trotter
    """Check prerequisites.
2467 6bf01bbb Guido Trotter

2468 6bf01bbb Guido Trotter
    """
2469 6bf01bbb Guido Trotter
2470 1f9430d6 Iustin Pop
  @staticmethod
2471 857121ad Iustin Pop
  def _DiagnoseByOS(rlist):
2472 1f9430d6 Iustin Pop
    """Remaps a per-node return list into an a per-os per-node dictionary
2473 1f9430d6 Iustin Pop

2474 e4376078 Iustin Pop
    @param rlist: a map with node names as keys and OS objects as values
2475 1f9430d6 Iustin Pop

2476 e4376078 Iustin Pop
    @rtype: dict
2477 5fcc718f Iustin Pop
    @return: a dictionary with osnames as keys and as value another map, with
2478 255dcebd Iustin Pop
        nodes as keys and tuples of (path, status, diagnose) as values, eg::
2479 e4376078 Iustin Pop

2480 255dcebd Iustin Pop
          {"debian-etch": {"node1": [(/usr/lib/..., True, ""),
2481 255dcebd Iustin Pop
                                     (/srv/..., False, "invalid api")],
2482 255dcebd Iustin Pop
                           "node2": [(/srv/..., True, "")]}
2483 e4376078 Iustin Pop
          }
2484 1f9430d6 Iustin Pop

2485 1f9430d6 Iustin Pop
    """
2486 1f9430d6 Iustin Pop
    all_os = {}
2487 a6ab004b Iustin Pop
    # we build here the list of nodes that didn't fail the RPC (at RPC
2488 a6ab004b Iustin Pop
    # level), so that nodes with a non-responding node daemon don't
2489 a6ab004b Iustin Pop
    # make all OSes invalid
2490 a6ab004b Iustin Pop
    good_nodes = [node_name for node_name in rlist
2491 4c4e4e1e Iustin Pop
                  if not rlist[node_name].fail_msg]
2492 83d92ad8 Iustin Pop
    for node_name, nr in rlist.items():
2493 4c4e4e1e Iustin Pop
      if nr.fail_msg or not nr.payload:
2494 1f9430d6 Iustin Pop
        continue
2495 ba00557a Guido Trotter
      for name, path, status, diagnose, variants in nr.payload:
2496 255dcebd Iustin Pop
        if name not in all_os:
2497 1f9430d6 Iustin Pop
          # build a list of nodes for this os containing empty lists
2498 1f9430d6 Iustin Pop
          # for each node in node_list
2499 255dcebd Iustin Pop
          all_os[name] = {}
2500 a6ab004b Iustin Pop
          for nname in good_nodes:
2501 255dcebd Iustin Pop
            all_os[name][nname] = []
2502 ba00557a Guido Trotter
        all_os[name][node_name].append((path, status, diagnose, variants))
2503 1f9430d6 Iustin Pop
    return all_os
2504 a8083063 Iustin Pop
2505 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
2506 a8083063 Iustin Pop
    """Compute the list of OSes.
2507 a8083063 Iustin Pop

2508 a8083063 Iustin Pop
    """
2509 a6ab004b Iustin Pop
    valid_nodes = [node for node in self.cfg.GetOnlineNodeList()]
2510 94a02bb5 Iustin Pop
    node_data = self.rpc.call_os_diagnose(valid_nodes)
2511 857121ad Iustin Pop
    pol = self._DiagnoseByOS(node_data)
2512 1f9430d6 Iustin Pop
    output = []
2513 1e288a26 Guido Trotter
    calc_valid = self._FIELDS_NEEDVALID.intersection(self.op.output_fields)
2514 1e288a26 Guido Trotter
    calc_variants = "variants" in self.op.output_fields
2515 1e288a26 Guido Trotter
2516 83d92ad8 Iustin Pop
    for os_name, os_data in pol.items():
2517 1f9430d6 Iustin Pop
      row = []
2518 1e288a26 Guido Trotter
      if calc_valid:
2519 1e288a26 Guido Trotter
        valid = True
2520 1e288a26 Guido Trotter
        variants = None
2521 1e288a26 Guido Trotter
        for osl in os_data.values():
2522 1e288a26 Guido Trotter
          valid = valid and osl and osl[0][1]
2523 1e288a26 Guido Trotter
          if not valid:
2524 1e288a26 Guido Trotter
            variants = None
2525 1e288a26 Guido Trotter
            break
2526 1e288a26 Guido Trotter
          if calc_variants:
2527 1e288a26 Guido Trotter
            node_variants = osl[0][3]
2528 1e288a26 Guido Trotter
            if variants is None:
2529 1e288a26 Guido Trotter
              variants = node_variants
2530 1e288a26 Guido Trotter
            else:
2531 1e288a26 Guido Trotter
              variants = [v for v in variants if v in node_variants]
2532 1e288a26 Guido Trotter
2533 1f9430d6 Iustin Pop
      for field in self.op.output_fields:
2534 1f9430d6 Iustin Pop
        if field == "name":
2535 1f9430d6 Iustin Pop
          val = os_name
2536 1f9430d6 Iustin Pop
        elif field == "valid":
2537 1e288a26 Guido Trotter
          val = valid
2538 1f9430d6 Iustin Pop
        elif field == "node_status":
2539 255dcebd Iustin Pop
          # this is just a copy of the dict
2540 1f9430d6 Iustin Pop
          val = {}
2541 255dcebd Iustin Pop
          for node_name, nos_list in os_data.items():
2542 255dcebd Iustin Pop
            val[node_name] = nos_list
2543 1e288a26 Guido Trotter
        elif field == "variants":
2544 1e288a26 Guido Trotter
          val =  variants
2545 1f9430d6 Iustin Pop
        else:
2546 1f9430d6 Iustin Pop
          raise errors.ParameterError(field)
2547 1f9430d6 Iustin Pop
        row.append(val)
2548 1f9430d6 Iustin Pop
      output.append(row)
2549 1f9430d6 Iustin Pop
2550 1f9430d6 Iustin Pop
    return output
2551 a8083063 Iustin Pop
2552 a8083063 Iustin Pop
2553 a8083063 Iustin Pop
class LURemoveNode(LogicalUnit):
2554 a8083063 Iustin Pop
  """Logical unit for removing a node.
2555 a8083063 Iustin Pop

2556 a8083063 Iustin Pop
  """
2557 a8083063 Iustin Pop
  HPATH = "node-remove"
2558 a8083063 Iustin Pop
  HTYPE = constants.HTYPE_NODE
2559 a8083063 Iustin Pop
  _OP_REQP = ["node_name"]
2560 a8083063 Iustin Pop
2561 a8083063 Iustin Pop
  def BuildHooksEnv(self):
2562 a8083063 Iustin Pop
    """Build hooks env.
2563 a8083063 Iustin Pop

2564 a8083063 Iustin Pop
    This doesn't run on the target node in the pre phase as a failed
2565 d08869ee Guido Trotter
    node would then be impossible to remove.
2566 a8083063 Iustin Pop

2567 a8083063 Iustin Pop
    """
2568 396e1b78 Michael Hanselmann
    env = {
2569 0e137c28 Iustin Pop
      "OP_TARGET": self.op.node_name,
2570 396e1b78 Michael Hanselmann
      "NODE_NAME": self.op.node_name,
2571 396e1b78 Michael Hanselmann
      }
2572 a8083063 Iustin Pop
    all_nodes = self.cfg.GetNodeList()
2573 9bb31ea8 Iustin Pop
    try:
2574 cd46f3b4 Luca Bigliardi
      all_nodes.remove(self.op.node_name)
2575 9bb31ea8 Iustin Pop
    except ValueError:
2576 9bb31ea8 Iustin Pop
      logging.warning("Node %s which is about to be removed not found"
2577 9bb31ea8 Iustin Pop
                      " in the all nodes list", self.op.node_name)
2578 396e1b78 Michael Hanselmann
    return env, all_nodes, all_nodes
2579 a8083063 Iustin Pop
2580 a8083063 Iustin Pop
  def CheckPrereq(self):
2581 a8083063 Iustin Pop
    """Check prerequisites.
2582 a8083063 Iustin Pop

2583 a8083063 Iustin Pop
    This checks:
2584 a8083063 Iustin Pop
     - the node exists in the configuration
2585 a8083063 Iustin Pop
     - it does not have primary or secondary instances
2586 a8083063 Iustin Pop
     - it's not the master
2587 a8083063 Iustin Pop

2588 5bbd3f7f Michael Hanselmann
    Any errors are signaled by raising errors.OpPrereqError.
2589 a8083063 Iustin Pop

2590 a8083063 Iustin Pop
    """
2591 cf26a87a Iustin Pop
    self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name)
2592 cf26a87a Iustin Pop
    node = self.cfg.GetNodeInfo(self.op.node_name)
2593 cf26a87a Iustin Pop
    assert node is not None
2594 a8083063 Iustin Pop
2595 a8083063 Iustin Pop
    instance_list = self.cfg.GetInstanceList()
2596 a8083063 Iustin Pop
2597 d6a02168 Michael Hanselmann
    masternode = self.cfg.GetMasterNode()
2598 a8083063 Iustin Pop
    if node.name == masternode:
2599 3ecf6786 Iustin Pop
      raise errors.OpPrereqError("Node is the master node,"
2600 5c983ee5 Iustin Pop
                                 " you need to failover first.",
2601 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
2602 a8083063 Iustin Pop
2603 a8083063 Iustin Pop
    for instance_name in instance_list:
2604 a8083063 Iustin Pop
      instance = self.cfg.GetInstanceInfo(instance_name)
2605 6b12959c Iustin Pop
      if node.name in instance.all_nodes:
2606 6b12959c Iustin Pop
        raise errors.OpPrereqError("Instance %s is still running on the node,"
2607 5c983ee5 Iustin Pop
                                   " please remove first." % instance_name,
2608 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
2609 a8083063 Iustin Pop
    self.op.node_name = node.name
2610 a8083063 Iustin Pop
    self.node = node
2611 a8083063 Iustin Pop
2612 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
2613 a8083063 Iustin Pop
    """Removes the node from the cluster.
2614 a8083063 Iustin Pop

2615 a8083063 Iustin Pop
    """
2616 a8083063 Iustin Pop
    node = self.node
2617 9a4f63d1 Iustin Pop
    logging.info("Stopping the node daemon and removing configs from node %s",
2618 9a4f63d1 Iustin Pop
                 node.name)
2619 a8083063 Iustin Pop
2620 b989b9d9 Ken Wehr
    modify_ssh_setup = self.cfg.GetClusterInfo().modify_ssh_setup
2621 b989b9d9 Ken Wehr
2622 44485f49 Guido Trotter
    # Promote nodes to master candidate as needed
2623 44485f49 Guido Trotter
    _AdjustCandidatePool(self, exceptions=[node.name])
2624 d8470559 Michael Hanselmann
    self.context.RemoveNode(node.name)
2625 a8083063 Iustin Pop
2626 cd46f3b4 Luca Bigliardi
    # Run post hooks on the node before it's removed
2627 cd46f3b4 Luca Bigliardi
    hm = self.proc.hmclass(self.rpc.call_hooks_runner, self)
2628 cd46f3b4 Luca Bigliardi
    try:
2629 1122eb25 Iustin Pop
      hm.RunPhase(constants.HOOKS_PHASE_POST, [node.name])
2630 3cb5c1e3 Luca Bigliardi
    except:
2631 7260cfbe Iustin Pop
      # pylint: disable-msg=W0702
2632 3cb5c1e3 Luca Bigliardi
      self.LogWarning("Errors occurred running hooks on %s" % node.name)
2633 cd46f3b4 Luca Bigliardi
2634 b989b9d9 Ken Wehr
    result = self.rpc.call_node_leave_cluster(node.name, modify_ssh_setup)
2635 4c4e4e1e Iustin Pop
    msg = result.fail_msg
2636 0623d351 Iustin Pop
    if msg:
2637 0623d351 Iustin Pop
      self.LogWarning("Errors encountered on the remote node while leaving"
2638 0623d351 Iustin Pop
                      " the cluster: %s", msg)
2639 c8a0948f Michael Hanselmann
2640 a8083063 Iustin Pop
2641 a8083063 Iustin Pop
class LUQueryNodes(NoHooksLU):
2642 a8083063 Iustin Pop
  """Logical unit for querying nodes.
2643 a8083063 Iustin Pop

2644 a8083063 Iustin Pop
  """
2645 7260cfbe Iustin Pop
  # pylint: disable-msg=W0142
2646 bc8e4a1a Iustin Pop
  _OP_REQP = ["output_fields", "names", "use_locking"]
2647 35705d8f Guido Trotter
  REQ_BGL = False
2648 19bed813 Iustin Pop
2649 19bed813 Iustin Pop
  _SIMPLE_FIELDS = ["name", "serial_no", "ctime", "mtime", "uuid",
2650 19bed813 Iustin Pop
                    "master_candidate", "offline", "drained"]
2651 19bed813 Iustin Pop
2652 a2d2e1a7 Iustin Pop
  _FIELDS_DYNAMIC = utils.FieldSet(
2653 31bf511f Iustin Pop
    "dtotal", "dfree",
2654 31bf511f Iustin Pop
    "mtotal", "mnode", "mfree",
2655 31bf511f Iustin Pop
    "bootid",
2656 0105bad3 Iustin Pop
    "ctotal", "cnodes", "csockets",
2657 31bf511f Iustin Pop
    )
2658 31bf511f Iustin Pop
2659 19bed813 Iustin Pop
  _FIELDS_STATIC = utils.FieldSet(*[
2660 19bed813 Iustin Pop
    "pinst_cnt", "sinst_cnt",
2661 31bf511f Iustin Pop
    "pinst_list", "sinst_list",
2662 31bf511f Iustin Pop
    "pip", "sip", "tags",
2663 0e67cdbe Iustin Pop
    "master",
2664 19bed813 Iustin Pop
    "role"] + _SIMPLE_FIELDS
2665 31bf511f Iustin Pop
    )
2666 a8083063 Iustin Pop
2667 35705d8f Guido Trotter
  def ExpandNames(self):
2668 31bf511f Iustin Pop
    _CheckOutputFields(static=self._FIELDS_STATIC,
2669 31bf511f Iustin Pop
                       dynamic=self._FIELDS_DYNAMIC,
2670 dcb93971 Michael Hanselmann
                       selected=self.op.output_fields)
2671 a8083063 Iustin Pop
2672 35705d8f Guido Trotter
    self.needed_locks = {}
2673 35705d8f Guido Trotter
    self.share_locks[locking.LEVEL_NODE] = 1
2674 c8d8b4c8 Iustin Pop
2675 c8d8b4c8 Iustin Pop
    if self.op.names:
2676 c8d8b4c8 Iustin Pop
      self.wanted = _GetWantedNodes(self, self.op.names)
2677 35705d8f Guido Trotter
    else:
2678 c8d8b4c8 Iustin Pop
      self.wanted = locking.ALL_SET
2679 c8d8b4c8 Iustin Pop
2680 bc8e4a1a Iustin Pop
    self.do_node_query = self._FIELDS_STATIC.NonMatching(self.op.output_fields)
2681 bc8e4a1a Iustin Pop
    self.do_locking = self.do_node_query and self.op.use_locking
2682 c8d8b4c8 Iustin Pop
    if self.do_locking:
2683 c8d8b4c8 Iustin Pop
      # if we don't request only static fields, we need to lock the nodes
2684 c8d8b4c8 Iustin Pop
      self.needed_locks[locking.LEVEL_NODE] = self.wanted
2685 c8d8b4c8 Iustin Pop
2686 35705d8f Guido Trotter
  def CheckPrereq(self):
2687 35705d8f Guido Trotter
    """Check prerequisites.
2688 35705d8f Guido Trotter

2689 35705d8f Guido Trotter
    """
2690 c8d8b4c8 Iustin Pop
    # The validation of the node list is done in the _GetWantedNodes,
2691 c8d8b4c8 Iustin Pop
    # if non empty, and if empty, there's no validation to do
2692 c8d8b4c8 Iustin Pop
    pass
2693 a8083063 Iustin Pop
2694 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
2695 a8083063 Iustin Pop
    """Computes the list of nodes and their attributes.
2696 a8083063 Iustin Pop

2697 a8083063 Iustin Pop
    """
2698 c8d8b4c8 Iustin Pop
    all_info = self.cfg.GetAllNodesInfo()
2699 c8d8b4c8 Iustin Pop
    if self.do_locking:
2700 c8d8b4c8 Iustin Pop
      nodenames = self.acquired_locks[locking.LEVEL_NODE]
2701 3fa93523 Guido Trotter
    elif self.wanted != locking.ALL_SET:
2702 3fa93523 Guido Trotter
      nodenames = self.wanted
2703 3fa93523 Guido Trotter
      missing = set(nodenames).difference(all_info.keys())
2704 3fa93523 Guido Trotter
      if missing:
2705 7b3a8fb5 Iustin Pop
        raise errors.OpExecError(
2706 3fa93523 Guido Trotter
          "Some nodes were removed before retrieving their data: %s" % missing)
2707 c8d8b4c8 Iustin Pop
    else:
2708 c8d8b4c8 Iustin Pop
      nodenames = all_info.keys()
2709 c1f1cbb2 Iustin Pop
2710 c1f1cbb2 Iustin Pop
    nodenames = utils.NiceSort(nodenames)
2711 c8d8b4c8 Iustin Pop
    nodelist = [all_info[name] for name in nodenames]
2712 a8083063 Iustin Pop
2713 a8083063 Iustin Pop
    # begin data gathering
2714 a8083063 Iustin Pop
2715 bc8e4a1a Iustin Pop
    if self.do_node_query:
2716 a8083063 Iustin Pop
      live_data = {}
2717 72737a7f Iustin Pop
      node_data = self.rpc.call_node_info(nodenames, self.cfg.GetVGName(),
2718 72737a7f Iustin Pop
                                          self.cfg.GetHypervisorType())
2719 a8083063 Iustin Pop
      for name in nodenames:
2720 781de953 Iustin Pop
        nodeinfo = node_data[name]
2721 4c4e4e1e Iustin Pop
        if not nodeinfo.fail_msg and nodeinfo.payload:
2722 070e998b Iustin Pop
          nodeinfo = nodeinfo.payload
2723 d599d686 Iustin Pop
          fn = utils.TryConvert
2724 a8083063 Iustin Pop
          live_data[name] = {
2725 d599d686 Iustin Pop
            "mtotal": fn(int, nodeinfo.get('memory_total', None)),
2726 d599d686 Iustin Pop
            "mnode": fn(int, nodeinfo.get('memory_dom0', None)),
2727 d599d686 Iustin Pop
            "mfree": fn(int, nodeinfo.get('memory_free', None)),
2728 d599d686 Iustin Pop
            "dtotal": fn(int, nodeinfo.get('vg_size', None)),
2729 d599d686 Iustin Pop
            "dfree": fn(int, nodeinfo.get('vg_free', None)),
2730 d599d686 Iustin Pop
            "ctotal": fn(int, nodeinfo.get('cpu_total', None)),
2731 d599d686 Iustin Pop
            "bootid": nodeinfo.get('bootid', None),
2732 0105bad3 Iustin Pop
            "cnodes": fn(int, nodeinfo.get('cpu_nodes', None)),
2733 0105bad3 Iustin Pop
            "csockets": fn(int, nodeinfo.get('cpu_sockets', None)),
2734 a8083063 Iustin Pop
            }
2735 a8083063 Iustin Pop
        else:
2736 a8083063 Iustin Pop
          live_data[name] = {}
2737 a8083063 Iustin Pop
    else:
2738 a8083063 Iustin Pop
      live_data = dict.fromkeys(nodenames, {})
2739 a8083063 Iustin Pop
2740 ec223efb Iustin Pop
    node_to_primary = dict([(name, set()) for name in nodenames])
2741 ec223efb Iustin Pop
    node_to_secondary = dict([(name, set()) for name in nodenames])
2742 a8083063 Iustin Pop
2743 ec223efb Iustin Pop
    inst_fields = frozenset(("pinst_cnt", "pinst_list",
2744 ec223efb Iustin Pop
                             "sinst_cnt", "sinst_list"))
2745 ec223efb Iustin Pop
    if inst_fields & frozenset(self.op.output_fields):
2746 4dfd6266 Iustin Pop
      inst_data = self.cfg.GetAllInstancesInfo()
2747 a8083063 Iustin Pop
2748 1122eb25 Iustin Pop
      for inst in inst_data.values():
2749 ec223efb Iustin Pop
        if inst.primary_node in node_to_primary:
2750 ec223efb Iustin Pop
          node_to_primary[inst.primary_node].add(inst.name)
2751 ec223efb Iustin Pop
        for secnode in inst.secondary_nodes:
2752 ec223efb Iustin Pop
          if secnode in node_to_secondary:
2753 ec223efb Iustin Pop
            node_to_secondary[secnode].add(inst.name)
2754 a8083063 Iustin Pop
2755 0e67cdbe Iustin Pop
    master_node = self.cfg.GetMasterNode()
2756 0e67cdbe Iustin Pop
2757 a8083063 Iustin Pop
    # end data gathering
2758 a8083063 Iustin Pop
2759 a8083063 Iustin Pop
    output = []
2760 a8083063 Iustin Pop
    for node in nodelist:
2761 a8083063 Iustin Pop
      node_output = []
2762 a8083063 Iustin Pop
      for field in self.op.output_fields:
2763 19bed813 Iustin Pop
        if field in self._SIMPLE_FIELDS:
2764 19bed813 Iustin Pop
          val = getattr(node, field)
2765 ec223efb Iustin Pop
        elif field == "pinst_list":
2766 ec223efb Iustin Pop
          val = list(node_to_primary[node.name])
2767 ec223efb Iustin Pop
        elif field == "sinst_list":
2768 ec223efb Iustin Pop
          val = list(node_to_secondary[node.name])
2769 ec223efb Iustin Pop
        elif field == "pinst_cnt":
2770 ec223efb Iustin Pop
          val = len(node_to_primary[node.name])
2771 ec223efb Iustin Pop
        elif field == "sinst_cnt":
2772 ec223efb Iustin Pop
          val = len(node_to_secondary[node.name])
2773 a8083063 Iustin Pop
        elif field == "pip":
2774 a8083063 Iustin Pop
          val = node.primary_ip
2775 a8083063 Iustin Pop
        elif field == "sip":
2776 a8083063 Iustin Pop
          val = node.secondary_ip
2777 130a6a6f Iustin Pop
        elif field == "tags":
2778 130a6a6f Iustin Pop
          val = list(node.GetTags())
2779 0e67cdbe Iustin Pop
        elif field == "master":
2780 0e67cdbe Iustin Pop
          val = node.name == master_node
2781 31bf511f Iustin Pop
        elif self._FIELDS_DYNAMIC.Matches(field):
2782 ec223efb Iustin Pop
          val = live_data[node.name].get(field, None)
2783 c120ff34 Iustin Pop
        elif field == "role":
2784 c120ff34 Iustin Pop
          if node.name == master_node:
2785 c120ff34 Iustin Pop
            val = "M"
2786 c120ff34 Iustin Pop
          elif node.master_candidate:
2787 c120ff34 Iustin Pop
            val = "C"
2788 c120ff34 Iustin Pop
          elif node.drained:
2789 c120ff34 Iustin Pop
            val = "D"
2790 c120ff34 Iustin Pop
          elif node.offline:
2791 c120ff34 Iustin Pop
            val = "O"
2792 c120ff34 Iustin Pop
          else:
2793 c120ff34 Iustin Pop
            val = "R"
2794 a8083063 Iustin Pop
        else:
2795 3ecf6786 Iustin Pop
          raise errors.ParameterError(field)
2796 a8083063 Iustin Pop
        node_output.append(val)
2797 a8083063 Iustin Pop
      output.append(node_output)
2798 a8083063 Iustin Pop
2799 a8083063 Iustin Pop
    return output
2800 a8083063 Iustin Pop
2801 a8083063 Iustin Pop
2802 dcb93971 Michael Hanselmann
class LUQueryNodeVolumes(NoHooksLU):
2803 dcb93971 Michael Hanselmann
  """Logical unit for getting volumes on node(s).
2804 dcb93971 Michael Hanselmann

2805 dcb93971 Michael Hanselmann
  """
2806 dcb93971 Michael Hanselmann
  _OP_REQP = ["nodes", "output_fields"]
2807 21a15682 Guido Trotter
  REQ_BGL = False
2808 a2d2e1a7 Iustin Pop
  _FIELDS_DYNAMIC = utils.FieldSet("phys", "vg", "name", "size", "instance")
2809 a2d2e1a7 Iustin Pop
  _FIELDS_STATIC = utils.FieldSet("node")
2810 21a15682 Guido Trotter
2811 21a15682 Guido Trotter
  def ExpandNames(self):
2812 31bf511f Iustin Pop
    _CheckOutputFields(static=self._FIELDS_STATIC,
2813 31bf511f Iustin Pop
                       dynamic=self._FIELDS_DYNAMIC,
2814 21a15682 Guido Trotter
                       selected=self.op.output_fields)
2815 21a15682 Guido Trotter
2816 21a15682 Guido Trotter
    self.needed_locks = {}
2817 21a15682 Guido Trotter
    self.share_locks[locking.LEVEL_NODE] = 1
2818 21a15682 Guido Trotter
    if not self.op.nodes:
2819 e310b019 Guido Trotter
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
2820 21a15682 Guido Trotter
    else:
2821 21a15682 Guido Trotter
      self.needed_locks[locking.LEVEL_NODE] = \
2822 21a15682 Guido Trotter
        _GetWantedNodes(self, self.op.nodes)
2823 dcb93971 Michael Hanselmann
2824 dcb93971 Michael Hanselmann
  def CheckPrereq(self):
2825 dcb93971 Michael Hanselmann
    """Check prerequisites.
2826 dcb93971 Michael Hanselmann

2827 dcb93971 Michael Hanselmann
    This checks that the fields required are valid output fields.
2828 dcb93971 Michael Hanselmann

2829 dcb93971 Michael Hanselmann
    """
2830 21a15682 Guido Trotter
    self.nodes = self.acquired_locks[locking.LEVEL_NODE]
2831 dcb93971 Michael Hanselmann
2832 dcb93971 Michael Hanselmann
  def Exec(self, feedback_fn):
2833 dcb93971 Michael Hanselmann
    """Computes the list of nodes and their attributes.
2834 dcb93971 Michael Hanselmann

2835 dcb93971 Michael Hanselmann
    """
2836 a7ba5e53 Iustin Pop
    nodenames = self.nodes
2837 72737a7f Iustin Pop
    volumes = self.rpc.call_node_volumes(nodenames)
2838 dcb93971 Michael Hanselmann
2839 dcb93971 Michael Hanselmann
    ilist = [self.cfg.GetInstanceInfo(iname) for iname
2840 dcb93971 Michael Hanselmann
             in self.cfg.GetInstanceList()]
2841 dcb93971 Michael Hanselmann
2842 dcb93971 Michael Hanselmann
    lv_by_node = dict([(inst, inst.MapLVsByNode()) for inst in ilist])
2843 dcb93971 Michael Hanselmann
2844 dcb93971 Michael Hanselmann
    output = []
2845 dcb93971 Michael Hanselmann
    for node in nodenames:
2846 10bfe6cb Iustin Pop
      nresult = volumes[node]
2847 10bfe6cb Iustin Pop
      if nresult.offline:
2848 10bfe6cb Iustin Pop
        continue
2849 4c4e4e1e Iustin Pop
      msg = nresult.fail_msg
2850 10bfe6cb Iustin Pop
      if msg:
2851 10bfe6cb Iustin Pop
        self.LogWarning("Can't compute volume data on node %s: %s", node, msg)
2852 37d19eb2 Michael Hanselmann
        continue
2853 37d19eb2 Michael Hanselmann
2854 10bfe6cb Iustin Pop
      node_vols = nresult.payload[:]
2855 dcb93971 Michael Hanselmann
      node_vols.sort(key=lambda vol: vol['dev'])
2856 dcb93971 Michael Hanselmann
2857 dcb93971 Michael Hanselmann
      for vol in node_vols:
2858 dcb93971 Michael Hanselmann
        node_output = []
2859 dcb93971 Michael Hanselmann
        for field in self.op.output_fields:
2860 dcb93971 Michael Hanselmann
          if field == "node":
2861 dcb93971 Michael Hanselmann
            val = node
2862 dcb93971 Michael Hanselmann
          elif field == "phys":
2863 dcb93971 Michael Hanselmann
            val = vol['dev']
2864 dcb93971 Michael Hanselmann
          elif field == "vg":
2865 dcb93971 Michael Hanselmann
            val = vol['vg']
2866 dcb93971 Michael Hanselmann
          elif field == "name":
2867 dcb93971 Michael Hanselmann
            val = vol['name']
2868 dcb93971 Michael Hanselmann
          elif field == "size":
2869 dcb93971 Michael Hanselmann
            val = int(float(vol['size']))
2870 dcb93971 Michael Hanselmann
          elif field == "instance":
2871 dcb93971 Michael Hanselmann
            for inst in ilist:
2872 dcb93971 Michael Hanselmann
              if node not in lv_by_node[inst]:
2873 dcb93971 Michael Hanselmann
                continue
2874 dcb93971 Michael Hanselmann
              if vol['name'] in lv_by_node[inst][node]:
2875 dcb93971 Michael Hanselmann
                val = inst.name
2876 dcb93971 Michael Hanselmann
                break
2877 dcb93971 Michael Hanselmann
            else:
2878 dcb93971 Michael Hanselmann
              val = '-'
2879 dcb93971 Michael Hanselmann
          else:
2880 3ecf6786 Iustin Pop
            raise errors.ParameterError(field)
2881 dcb93971 Michael Hanselmann
          node_output.append(str(val))
2882 dcb93971 Michael Hanselmann
2883 dcb93971 Michael Hanselmann
        output.append(node_output)
2884 dcb93971 Michael Hanselmann
2885 dcb93971 Michael Hanselmann
    return output
2886 dcb93971 Michael Hanselmann
2887 dcb93971 Michael Hanselmann
2888 9e5442ce Michael Hanselmann
class LUQueryNodeStorage(NoHooksLU):
2889 9e5442ce Michael Hanselmann
  """Logical unit for getting information on storage units on node(s).
2890 9e5442ce Michael Hanselmann

2891 9e5442ce Michael Hanselmann
  """
2892 9e5442ce Michael Hanselmann
  _OP_REQP = ["nodes", "storage_type", "output_fields"]
2893 9e5442ce Michael Hanselmann
  REQ_BGL = False
2894 620a85fd Iustin Pop
  _FIELDS_STATIC = utils.FieldSet(constants.SF_NODE)
2895 9e5442ce Michael Hanselmann
2896 9e5442ce Michael Hanselmann
  def ExpandNames(self):
2897 9e5442ce Michael Hanselmann
    storage_type = self.op.storage_type
2898 9e5442ce Michael Hanselmann
2899 620a85fd Iustin Pop
    if storage_type not in constants.VALID_STORAGE_TYPES:
2900 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
2901 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
2902 9e5442ce Michael Hanselmann
2903 9e5442ce Michael Hanselmann
    _CheckOutputFields(static=self._FIELDS_STATIC,
2904 620a85fd Iustin Pop
                       dynamic=utils.FieldSet(*constants.VALID_STORAGE_FIELDS),
2905 9e5442ce Michael Hanselmann
                       selected=self.op.output_fields)
2906 9e5442ce Michael Hanselmann
2907 9e5442ce Michael Hanselmann
    self.needed_locks = {}
2908 9e5442ce Michael Hanselmann
    self.share_locks[locking.LEVEL_NODE] = 1
2909 9e5442ce Michael Hanselmann
2910 9e5442ce Michael Hanselmann
    if self.op.nodes:
2911 9e5442ce Michael Hanselmann
      self.needed_locks[locking.LEVEL_NODE] = \
2912 9e5442ce Michael Hanselmann
        _GetWantedNodes(self, self.op.nodes)
2913 9e5442ce Michael Hanselmann
    else:
2914 9e5442ce Michael Hanselmann
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
2915 9e5442ce Michael Hanselmann
2916 9e5442ce Michael Hanselmann
  def CheckPrereq(self):
2917 9e5442ce Michael Hanselmann
    """Check prerequisites.
2918 9e5442ce Michael Hanselmann

2919 9e5442ce Michael Hanselmann
    This checks that the fields required are valid output fields.
2920 9e5442ce Michael Hanselmann

2921 9e5442ce Michael Hanselmann
    """
2922 9e5442ce Michael Hanselmann
    self.op.name = getattr(self.op, "name", None)
2923 9e5442ce Michael Hanselmann
2924 9e5442ce Michael Hanselmann
    self.nodes = self.acquired_locks[locking.LEVEL_NODE]
2925 9e5442ce Michael Hanselmann
2926 9e5442ce Michael Hanselmann
  def Exec(self, feedback_fn):
2927 9e5442ce Michael Hanselmann
    """Computes the list of nodes and their attributes.
2928 9e5442ce Michael Hanselmann

2929 9e5442ce Michael Hanselmann
    """
2930 9e5442ce Michael Hanselmann
    # Always get name to sort by
2931 9e5442ce Michael Hanselmann
    if constants.SF_NAME in self.op.output_fields:
2932 9e5442ce Michael Hanselmann
      fields = self.op.output_fields[:]
2933 9e5442ce Michael Hanselmann
    else:
2934 9e5442ce Michael Hanselmann
      fields = [constants.SF_NAME] + self.op.output_fields
2935 9e5442ce Michael Hanselmann
2936 620a85fd Iustin Pop
    # Never ask for node or type as it's only known to the LU
2937 620a85fd Iustin Pop
    for extra in [constants.SF_NODE, constants.SF_TYPE]:
2938 620a85fd Iustin Pop
      while extra in fields:
2939 620a85fd Iustin Pop
        fields.remove(extra)
2940 9e5442ce Michael Hanselmann
2941 9e5442ce Michael Hanselmann
    field_idx = dict([(name, idx) for (idx, name) in enumerate(fields)])
2942 9e5442ce Michael Hanselmann
    name_idx = field_idx[constants.SF_NAME]
2943 9e5442ce Michael Hanselmann
2944 efb8da02 Michael Hanselmann
    st_args = _GetStorageTypeArgs(self.cfg, self.op.storage_type)
2945 9e5442ce Michael Hanselmann
    data = self.rpc.call_storage_list(self.nodes,
2946 9e5442ce Michael Hanselmann
                                      self.op.storage_type, st_args,
2947 9e5442ce Michael Hanselmann
                                      self.op.name, fields)
2948 9e5442ce Michael Hanselmann
2949 9e5442ce Michael Hanselmann
    result = []
2950 9e5442ce Michael Hanselmann
2951 9e5442ce Michael Hanselmann
    for node in utils.NiceSort(self.nodes):
2952 9e5442ce Michael Hanselmann
      nresult = data[node]
2953 9e5442ce Michael Hanselmann
      if nresult.offline:
2954 9e5442ce Michael Hanselmann
        continue
2955 9e5442ce Michael Hanselmann
2956 9e5442ce Michael Hanselmann
      msg = nresult.fail_msg
2957 9e5442ce Michael Hanselmann
      if msg:
2958 9e5442ce Michael Hanselmann
        self.LogWarning("Can't get storage data from node %s: %s", node, msg)
2959 9e5442ce Michael Hanselmann
        continue
2960 9e5442ce Michael Hanselmann
2961 9e5442ce Michael Hanselmann
      rows = dict([(row[name_idx], row) for row in nresult.payload])
2962 9e5442ce Michael Hanselmann
2963 9e5442ce Michael Hanselmann
      for name in utils.NiceSort(rows.keys()):
2964 9e5442ce Michael Hanselmann
        row = rows[name]
2965 9e5442ce Michael Hanselmann
2966 9e5442ce Michael Hanselmann
        out = []
2967 9e5442ce Michael Hanselmann
2968 9e5442ce Michael Hanselmann
        for field in self.op.output_fields:
2969 620a85fd Iustin Pop
          if field == constants.SF_NODE:
2970 9e5442ce Michael Hanselmann
            val = node
2971 620a85fd Iustin Pop
          elif field == constants.SF_TYPE:
2972 620a85fd Iustin Pop
            val = self.op.storage_type
2973 9e5442ce Michael Hanselmann
          elif field in field_idx:
2974 9e5442ce Michael Hanselmann
            val = row[field_idx[field]]
2975 9e5442ce Michael Hanselmann
          else:
2976 9e5442ce Michael Hanselmann
            raise errors.ParameterError(field)
2977 9e5442ce Michael Hanselmann
2978 9e5442ce Michael Hanselmann
          out.append(val)
2979 9e5442ce Michael Hanselmann
2980 9e5442ce Michael Hanselmann
        result.append(out)
2981 9e5442ce Michael Hanselmann
2982 9e5442ce Michael Hanselmann
    return result
2983 9e5442ce Michael Hanselmann
2984 9e5442ce Michael Hanselmann
2985 efb8da02 Michael Hanselmann
class LUModifyNodeStorage(NoHooksLU):
2986 efb8da02 Michael Hanselmann
  """Logical unit for modifying a storage volume on a node.
2987 efb8da02 Michael Hanselmann

2988 efb8da02 Michael Hanselmann
  """
2989 efb8da02 Michael Hanselmann
  _OP_REQP = ["node_name", "storage_type", "name", "changes"]
2990 efb8da02 Michael Hanselmann
  REQ_BGL = False
2991 efb8da02 Michael Hanselmann
2992 efb8da02 Michael Hanselmann
  def CheckArguments(self):
2993 cf26a87a Iustin Pop
    self.opnode_name = _ExpandNodeName(self.cfg, self.op.node_name)
2994 efb8da02 Michael Hanselmann
2995 efb8da02 Michael Hanselmann
    storage_type = self.op.storage_type
2996 620a85fd Iustin Pop
    if storage_type not in constants.VALID_STORAGE_TYPES:
2997 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Unknown storage type: %s" % storage_type,
2998 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
2999 efb8da02 Michael Hanselmann
3000 efb8da02 Michael Hanselmann
  def ExpandNames(self):
3001 efb8da02 Michael Hanselmann
    self.needed_locks = {
3002 efb8da02 Michael Hanselmann
      locking.LEVEL_NODE: self.op.node_name,
3003 efb8da02 Michael Hanselmann
      }
3004 efb8da02 Michael Hanselmann
3005 efb8da02 Michael Hanselmann
  def CheckPrereq(self):
3006 efb8da02 Michael Hanselmann
    """Check prerequisites.
3007 efb8da02 Michael Hanselmann

3008 efb8da02 Michael Hanselmann
    """
3009 efb8da02 Michael Hanselmann
    storage_type = self.op.storage_type
3010 efb8da02 Michael Hanselmann
3011 efb8da02 Michael Hanselmann
    try:
3012 efb8da02 Michael Hanselmann
      modifiable = constants.MODIFIABLE_STORAGE_FIELDS[storage_type]
3013 efb8da02 Michael Hanselmann
    except KeyError:
3014 efb8da02 Michael Hanselmann
      raise errors.OpPrereqError("Storage units of type '%s' can not be"
3015 5c983ee5 Iustin Pop
                                 " modified" % storage_type,
3016 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
3017 efb8da02 Michael Hanselmann
3018 efb8da02 Michael Hanselmann
    diff = set(self.op.changes.keys()) - modifiable
3019 efb8da02 Michael Hanselmann
    if diff:
3020 efb8da02 Michael Hanselmann
      raise errors.OpPrereqError("The following fields can not be modified for"
3021 efb8da02 Michael Hanselmann
                                 " storage units of type '%s': %r" %
3022 5c983ee5 Iustin Pop
                                 (storage_type, list(diff)),
3023 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
3024 efb8da02 Michael Hanselmann
3025 efb8da02 Michael Hanselmann
  def Exec(self, feedback_fn):
3026 efb8da02 Michael Hanselmann
    """Computes the list of nodes and their attributes.
3027 efb8da02 Michael Hanselmann

3028 efb8da02 Michael Hanselmann
    """
3029 efb8da02 Michael Hanselmann
    st_args = _GetStorageTypeArgs(self.cfg, self.op.storage_type)
3030 efb8da02 Michael Hanselmann
    result = self.rpc.call_storage_modify(self.op.node_name,
3031 efb8da02 Michael Hanselmann
                                          self.op.storage_type, st_args,
3032 efb8da02 Michael Hanselmann
                                          self.op.name, self.op.changes)
3033 efb8da02 Michael Hanselmann
    result.Raise("Failed to modify storage unit '%s' on %s" %
3034 efb8da02 Michael Hanselmann
                 (self.op.name, self.op.node_name))
3035 efb8da02 Michael Hanselmann
3036 efb8da02 Michael Hanselmann
3037 a8083063 Iustin Pop
class LUAddNode(LogicalUnit):
3038 a8083063 Iustin Pop
  """Logical unit for adding node to the cluster.
3039 a8083063 Iustin Pop

3040 a8083063 Iustin Pop
  """
3041 a8083063 Iustin Pop
  HPATH = "node-add"
3042 a8083063 Iustin Pop
  HTYPE = constants.HTYPE_NODE
3043 a8083063 Iustin Pop
  _OP_REQP = ["node_name"]
3044 a8083063 Iustin Pop
3045 44caf5a8 Iustin Pop
  def CheckArguments(self):
3046 44caf5a8 Iustin Pop
    # validate/normalize the node name
3047 44caf5a8 Iustin Pop
    self.op.node_name = utils.HostInfo.NormalizeName(self.op.node_name)
3048 44caf5a8 Iustin Pop
3049 a8083063 Iustin Pop
  def BuildHooksEnv(self):
3050 a8083063 Iustin Pop
    """Build hooks env.
3051 a8083063 Iustin Pop

3052 a8083063 Iustin Pop
    This will run on all nodes before, and on all nodes + the new node after.
3053 a8083063 Iustin Pop

3054 a8083063 Iustin Pop
    """
3055 a8083063 Iustin Pop
    env = {
3056 0e137c28 Iustin Pop
      "OP_TARGET": self.op.node_name,
3057 a8083063 Iustin Pop
      "NODE_NAME": self.op.node_name,
3058 a8083063 Iustin Pop
      "NODE_PIP": self.op.primary_ip,
3059 a8083063 Iustin Pop
      "NODE_SIP": self.op.secondary_ip,
3060 a8083063 Iustin Pop
      }
3061 a8083063 Iustin Pop
    nodes_0 = self.cfg.GetNodeList()
3062 a8083063 Iustin Pop
    nodes_1 = nodes_0 + [self.op.node_name, ]
3063 a8083063 Iustin Pop
    return env, nodes_0, nodes_1
3064 a8083063 Iustin Pop
3065 a8083063 Iustin Pop
  def CheckPrereq(self):
3066 a8083063 Iustin Pop
    """Check prerequisites.
3067 a8083063 Iustin Pop

3068 a8083063 Iustin Pop
    This checks:
3069 a8083063 Iustin Pop
     - the new node is not already in the config
3070 a8083063 Iustin Pop
     - it is resolvable
3071 a8083063 Iustin Pop
     - its parameters (single/dual homed) matches the cluster
3072 a8083063 Iustin Pop

3073 5bbd3f7f Michael Hanselmann
    Any errors are signaled by raising errors.OpPrereqError.
3074 a8083063 Iustin Pop

3075 a8083063 Iustin Pop
    """
3076 a8083063 Iustin Pop
    node_name = self.op.node_name
3077 a8083063 Iustin Pop
    cfg = self.cfg
3078 a8083063 Iustin Pop
3079 104f4ca1 Iustin Pop
    dns_data = utils.GetHostInfo(node_name)
3080 a8083063 Iustin Pop
3081 bcf043c9 Iustin Pop
    node = dns_data.name
3082 bcf043c9 Iustin Pop
    primary_ip = self.op.primary_ip = dns_data.ip
3083 a8083063 Iustin Pop
    secondary_ip = getattr(self.op, "secondary_ip", None)
3084 a8083063 Iustin Pop
    if secondary_ip is None:
3085 a8083063 Iustin Pop
      secondary_ip = primary_ip
3086 a8083063 Iustin Pop
    if not utils.IsValidIP(secondary_ip):
3087 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Invalid secondary IP given",
3088 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
3089 a8083063 Iustin Pop
    self.op.secondary_ip = secondary_ip
3090 e7c6e02b Michael Hanselmann
3091 a8083063 Iustin Pop
    node_list = cfg.GetNodeList()
3092 e7c6e02b Michael Hanselmann
    if not self.op.readd and node in node_list:
3093 e7c6e02b Michael Hanselmann
      raise errors.OpPrereqError("Node %s is already in the configuration" %
3094 5c983ee5 Iustin Pop
                                 node, errors.ECODE_EXISTS)
3095 e7c6e02b Michael Hanselmann
    elif self.op.readd and node not in node_list:
3096 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Node %s is not in the configuration" % node,
3097 5c983ee5 Iustin Pop
                                 errors.ECODE_NOENT)
3098 a8083063 Iustin Pop
3099 a8083063 Iustin Pop
    for existing_node_name in node_list:
3100 a8083063 Iustin Pop
      existing_node = cfg.GetNodeInfo(existing_node_name)
3101 e7c6e02b Michael Hanselmann
3102 e7c6e02b Michael Hanselmann
      if self.op.readd and node == existing_node_name:
3103 e7c6e02b Michael Hanselmann
        if (existing_node.primary_ip != primary_ip or
3104 e7c6e02b Michael Hanselmann
            existing_node.secondary_ip != secondary_ip):
3105 e7c6e02b Michael Hanselmann
          raise errors.OpPrereqError("Readded node doesn't have the same IP"
3106 5c983ee5 Iustin Pop
                                     " address configuration as before",
3107 5c983ee5 Iustin Pop
                                     errors.ECODE_INVAL)
3108 e7c6e02b Michael Hanselmann
        continue
3109 e7c6e02b Michael Hanselmann
3110 a8083063 Iustin Pop
      if (existing_node.primary_ip == primary_ip or
3111 a8083063 Iustin Pop
          existing_node.secondary_ip == primary_ip or
3112 a8083063 Iustin Pop
          existing_node.primary_ip == secondary_ip or
3113 a8083063 Iustin Pop
          existing_node.secondary_ip == secondary_ip):
3114 3ecf6786 Iustin Pop
        raise errors.OpPrereqError("New node ip address(es) conflict with"
3115 5c983ee5 Iustin Pop
                                   " existing node %s" % existing_node.name,
3116 5c983ee5 Iustin Pop
                                   errors.ECODE_NOTUNIQUE)
3117 a8083063 Iustin Pop
3118 a8083063 Iustin Pop
    # check that the type of the node (single versus dual homed) is the
3119 a8083063 Iustin Pop
    # same as for the master
3120 d6a02168 Michael Hanselmann
    myself = cfg.GetNodeInfo(self.cfg.GetMasterNode())
3121 a8083063 Iustin Pop
    master_singlehomed = myself.secondary_ip == myself.primary_ip
3122 a8083063 Iustin Pop
    newbie_singlehomed = secondary_ip == primary_ip
3123 a8083063 Iustin Pop
    if master_singlehomed != newbie_singlehomed:
3124 a8083063 Iustin Pop
      if master_singlehomed:
3125 3ecf6786 Iustin Pop
        raise errors.OpPrereqError("The master has no private ip but the"
3126 5c983ee5 Iustin Pop
                                   " new node has one",
3127 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
3128 a8083063 Iustin Pop
      else:
3129 3ecf6786 Iustin Pop
        raise errors.OpPrereqError("The master has a private ip but the"
3130 5c983ee5 Iustin Pop
                                   " new node doesn't have one",
3131 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
3132 a8083063 Iustin Pop
3133 5bbd3f7f Michael Hanselmann
    # checks reachability
3134 b15d625f Iustin Pop
    if not utils.TcpPing(primary_ip, constants.DEFAULT_NODED_PORT):
3135 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Node not reachable by ping",
3136 5c983ee5 Iustin Pop
                                 errors.ECODE_ENVIRON)
3137 a8083063 Iustin Pop
3138 a8083063 Iustin Pop
    if not newbie_singlehomed:
3139 a8083063 Iustin Pop
      # check reachability from my secondary ip to newbie's secondary ip
3140 b15d625f Iustin Pop
      if not utils.TcpPing(secondary_ip, constants.DEFAULT_NODED_PORT,
3141 b15d625f Iustin Pop
                           source=myself.secondary_ip):
3142 f4bc1f2c Michael Hanselmann
        raise errors.OpPrereqError("Node secondary ip not reachable by TCP"
3143 5c983ee5 Iustin Pop
                                   " based ping to noded port",
3144 5c983ee5 Iustin Pop
                                   errors.ECODE_ENVIRON)
3145 a8083063 Iustin Pop
3146 a8ae3eb5 Iustin Pop
    if self.op.readd:
3147 a8ae3eb5 Iustin Pop
      exceptions = [node]
3148 a8ae3eb5 Iustin Pop
    else:
3149 a8ae3eb5 Iustin Pop
      exceptions = []
3150 6d7e1f20 Guido Trotter
3151 6d7e1f20 Guido Trotter
    self.master_candidate = _DecideSelfPromotion(self, exceptions=exceptions)
3152 0fff97e9 Guido Trotter
3153 a8ae3eb5 Iustin Pop
    if self.op.readd:
3154 a8ae3eb5 Iustin Pop
      self.new_node = self.cfg.GetNodeInfo(node)
3155 a8ae3eb5 Iustin Pop
      assert self.new_node is not None, "Can't retrieve locked node %s" % node
3156 a8ae3eb5 Iustin Pop
    else:
3157 a8ae3eb5 Iustin Pop
      self.new_node = objects.Node(name=node,
3158 a8ae3eb5 Iustin Pop
                                   primary_ip=primary_ip,
3159 a8ae3eb5 Iustin Pop
                                   secondary_ip=secondary_ip,
3160 a8ae3eb5 Iustin Pop
                                   master_candidate=self.master_candidate,
3161 a8ae3eb5 Iustin Pop
                                   offline=False, drained=False)
3162 a8083063 Iustin Pop
3163 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
3164 a8083063 Iustin Pop
    """Adds the new node to the cluster.
3165 a8083063 Iustin Pop

3166 a8083063 Iustin Pop
    """
3167 a8083063 Iustin Pop
    new_node = self.new_node
3168 a8083063 Iustin Pop
    node = new_node.name
3169 a8083063 Iustin Pop
3170 a8ae3eb5 Iustin Pop
    # for re-adds, reset the offline/drained/master-candidate flags;
3171 a8ae3eb5 Iustin Pop
    # we need to reset here, otherwise offline would prevent RPC calls
3172 a8ae3eb5 Iustin Pop
    # later in the procedure; this also means that if the re-add
3173 a8ae3eb5 Iustin Pop
    # fails, we are left with a non-offlined, broken node
3174 a8ae3eb5 Iustin Pop
    if self.op.readd:
3175 7260cfbe Iustin Pop
      new_node.drained = new_node.offline = False # pylint: disable-msg=W0201
3176 a8ae3eb5 Iustin Pop
      self.LogInfo("Readding a node, the offline/drained flags were reset")
3177 a8ae3eb5 Iustin Pop
      # if we demote the node, we do cleanup later in the procedure
3178 a8ae3eb5 Iustin Pop
      new_node.master_candidate = self.master_candidate
3179 a8ae3eb5 Iustin Pop
3180 a8ae3eb5 Iustin Pop
    # notify the user about any possible mc promotion
3181 a8ae3eb5 Iustin Pop
    if new_node.master_candidate:
3182 a8ae3eb5 Iustin Pop
      self.LogInfo("Node will be a master candidate")
3183 a8ae3eb5 Iustin Pop
3184 a8083063 Iustin Pop
    # check connectivity
3185 72737a7f Iustin Pop
    result = self.rpc.call_version([node])[node]
3186 4c4e4e1e Iustin Pop
    result.Raise("Can't get version information from node %s" % node)
3187 90b54c26 Iustin Pop
    if constants.PROTOCOL_VERSION == result.payload:
3188 90b54c26 Iustin Pop
      logging.info("Communication to node %s fine, sw version %s match",
3189 90b54c26 Iustin Pop
                   node, result.payload)
3190 a8083063 Iustin Pop
    else:
3191 90b54c26 Iustin Pop
      raise errors.OpExecError("Version mismatch master version %s,"
3192 90b54c26 Iustin Pop
                               " node version %s" %
3193 90b54c26 Iustin Pop
                               (constants.PROTOCOL_VERSION, result.payload))
3194 a8083063 Iustin Pop
3195 a8083063 Iustin Pop
    # setup ssh on node
3196 b989b9d9 Ken Wehr
    if self.cfg.GetClusterInfo().modify_ssh_setup:
3197 b989b9d9 Ken Wehr
      logging.info("Copy ssh key to node %s", node)
3198 b989b9d9 Ken Wehr
      priv_key, pub_key, _ = ssh.GetUserFiles(constants.GANETI_RUNAS)
3199 b989b9d9 Ken Wehr
      keyarray = []
3200 b989b9d9 Ken Wehr
      keyfiles = [constants.SSH_HOST_DSA_PRIV, constants.SSH_HOST_DSA_PUB,
3201 b989b9d9 Ken Wehr
                  constants.SSH_HOST_RSA_PRIV, constants.SSH_HOST_RSA_PUB,
3202 b989b9d9 Ken Wehr
                  priv_key, pub_key]
3203 b989b9d9 Ken Wehr
3204 b989b9d9 Ken Wehr
      for i in keyfiles:
3205 b989b9d9 Ken Wehr
        keyarray.append(utils.ReadFile(i))
3206 b989b9d9 Ken Wehr
3207 b989b9d9 Ken Wehr
      result = self.rpc.call_node_add(node, keyarray[0], keyarray[1],
3208 b989b9d9 Ken Wehr
                                      keyarray[2], keyarray[3], keyarray[4],
3209 b989b9d9 Ken Wehr
                                      keyarray[5])
3210 b989b9d9 Ken Wehr
      result.Raise("Cannot transfer ssh keys to the new node")
3211 a8083063 Iustin Pop
3212 a8083063 Iustin Pop
    # Add node to our /etc/hosts, and add key to known_hosts
3213 b86a6bcd Guido Trotter
    if self.cfg.GetClusterInfo().modify_etc_hosts:
3214 b86a6bcd Guido Trotter
      utils.AddHostToEtcHosts(new_node.name)
3215 c8a0948f Michael Hanselmann
3216 a8083063 Iustin Pop
    if new_node.secondary_ip != new_node.primary_ip:
3217 781de953 Iustin Pop
      result = self.rpc.call_node_has_ip_address(new_node.name,
3218 781de953 Iustin Pop
                                                 new_node.secondary_ip)
3219 4c4e4e1e Iustin Pop
      result.Raise("Failure checking secondary ip on node %s" % new_node.name,
3220 045dd6d9 Iustin Pop
                   prereq=True, ecode=errors.ECODE_ENVIRON)
3221 c2fc8250 Iustin Pop
      if not result.payload:
3222 f4bc1f2c Michael Hanselmann
        raise errors.OpExecError("Node claims it doesn't have the secondary ip"
3223 f4bc1f2c Michael Hanselmann
                                 " you gave (%s). Please fix and re-run this"
3224 f4bc1f2c Michael Hanselmann
                                 " command." % new_node.secondary_ip)
3225 a8083063 Iustin Pop
3226 d6a02168 Michael Hanselmann
    node_verify_list = [self.cfg.GetMasterNode()]
3227 5c0527ed Guido Trotter
    node_verify_param = {
3228 f60759f7 Iustin Pop
      constants.NV_NODELIST: [node],
3229 5c0527ed Guido Trotter
      # TODO: do a node-net-test as well?
3230 5c0527ed Guido Trotter
    }
3231 5c0527ed Guido Trotter
3232 72737a7f Iustin Pop
    result = self.rpc.call_node_verify(node_verify_list, node_verify_param,
3233 72737a7f Iustin Pop
                                       self.cfg.GetClusterName())
3234 5c0527ed Guido Trotter
    for verifier in node_verify_list:
3235 4c4e4e1e Iustin Pop
      result[verifier].Raise("Cannot communicate with node %s" % verifier)
3236 f60759f7 Iustin Pop
      nl_payload = result[verifier].payload[constants.NV_NODELIST]
3237 6f68a739 Iustin Pop
      if nl_payload:
3238 6f68a739 Iustin Pop
        for failed in nl_payload:
3239 31821208 Iustin Pop
          feedback_fn("ssh/hostname verification failed"
3240 31821208 Iustin Pop
                      " (checking from %s): %s" %
3241 6f68a739 Iustin Pop
                      (verifier, nl_payload[failed]))
3242 5c0527ed Guido Trotter
        raise errors.OpExecError("ssh/hostname verification failed.")
3243 ff98055b Iustin Pop
3244 d8470559 Michael Hanselmann
    if self.op.readd:
3245 28eddce5 Guido Trotter
      _RedistributeAncillaryFiles(self)
3246 d8470559 Michael Hanselmann
      self.context.ReaddNode(new_node)
3247 a8ae3eb5 Iustin Pop
      # make sure we redistribute the config
3248 a4eae71f Michael Hanselmann
      self.cfg.Update(new_node, feedback_fn)
3249 a8ae3eb5 Iustin Pop
      # and make sure the new node will not have old files around
3250 a8ae3eb5 Iustin Pop
      if not new_node.master_candidate:
3251 a8ae3eb5 Iustin Pop
        result = self.rpc.call_node_demote_from_mc(new_node.name)
3252 3cebe102 Michael Hanselmann
        msg = result.fail_msg
3253 a8ae3eb5 Iustin Pop
        if msg:
3254 a8ae3eb5 Iustin Pop
          self.LogWarning("Node failed to demote itself from master"
3255 a8ae3eb5 Iustin Pop
                          " candidate status: %s" % msg)
3256 d8470559 Michael Hanselmann
    else:
3257 035566e3 Iustin Pop
      _RedistributeAncillaryFiles(self, additional_nodes=[node])
3258 0debfb35 Guido Trotter
      self.context.AddNode(new_node, self.proc.GetECId())
3259 a8083063 Iustin Pop
3260 a8083063 Iustin Pop
3261 b31c8676 Iustin Pop
class LUSetNodeParams(LogicalUnit):
3262 b31c8676 Iustin Pop
  """Modifies the parameters of a node.
3263 b31c8676 Iustin Pop

3264 b31c8676 Iustin Pop
  """
3265 b31c8676 Iustin Pop
  HPATH = "node-modify"
3266 b31c8676 Iustin Pop
  HTYPE = constants.HTYPE_NODE
3267 b31c8676 Iustin Pop
  _OP_REQP = ["node_name"]
3268 b31c8676 Iustin Pop
  REQ_BGL = False
3269 b31c8676 Iustin Pop
3270 b31c8676 Iustin Pop
  def CheckArguments(self):
3271 cf26a87a Iustin Pop
    self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name)
3272 3a5ba66a Iustin Pop
    _CheckBooleanOpField(self.op, 'master_candidate')
3273 3a5ba66a Iustin Pop
    _CheckBooleanOpField(self.op, 'offline')
3274 c9d443ea Iustin Pop
    _CheckBooleanOpField(self.op, 'drained')
3275 601908d0 Iustin Pop
    _CheckBooleanOpField(self.op, 'auto_promote')
3276 c9d443ea Iustin Pop
    all_mods = [self.op.offline, self.op.master_candidate, self.op.drained]
3277 c9d443ea Iustin Pop
    if all_mods.count(None) == 3:
3278 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Please pass at least one modification",
3279 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
3280 c9d443ea Iustin Pop
    if all_mods.count(True) > 1:
3281 c9d443ea Iustin Pop
      raise errors.OpPrereqError("Can't set the node into more than one"
3282 5c983ee5 Iustin Pop
                                 " state at the same time",
3283 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
3284 b31c8676 Iustin Pop
3285 601908d0 Iustin Pop
    # Boolean value that tells us whether we're offlining or draining the node
3286 601908d0 Iustin Pop
    self.offline_or_drain = (self.op.offline == True or
3287 601908d0 Iustin Pop
                             self.op.drained == True)
3288 601908d0 Iustin Pop
    self.deoffline_or_drain = (self.op.offline == False or
3289 601908d0 Iustin Pop
                               self.op.drained == False)
3290 601908d0 Iustin Pop
    self.might_demote = (self.op.master_candidate == False or
3291 601908d0 Iustin Pop
                         self.offline_or_drain)
3292 601908d0 Iustin Pop
3293 601908d0 Iustin Pop
    self.lock_all = self.op.auto_promote and self.might_demote
3294 601908d0 Iustin Pop
3295 601908d0 Iustin Pop
3296 b31c8676 Iustin Pop
  def ExpandNames(self):
3297 601908d0 Iustin Pop
    if self.lock_all:
3298 601908d0 Iustin Pop
      self.needed_locks = {locking.LEVEL_NODE: locking.ALL_SET}
3299 601908d0 Iustin Pop
    else:
3300 601908d0 Iustin Pop
      self.needed_locks = {locking.LEVEL_NODE: self.op.node_name}
3301 b31c8676 Iustin Pop
3302 b31c8676 Iustin Pop
  def BuildHooksEnv(self):
3303 b31c8676 Iustin Pop
    """Build hooks env.
3304 b31c8676 Iustin Pop

3305 b31c8676 Iustin Pop
    This runs on the master node.
3306 b31c8676 Iustin Pop

3307 b31c8676 Iustin Pop
    """
3308 b31c8676 Iustin Pop
    env = {
3309 b31c8676 Iustin Pop
      "OP_TARGET": self.op.node_name,
3310 b31c8676 Iustin Pop
      "MASTER_CANDIDATE": str(self.op.master_candidate),
3311 3a5ba66a Iustin Pop
      "OFFLINE": str(self.op.offline),
3312 c9d443ea Iustin Pop
      "DRAINED": str(self.op.drained),
3313 b31c8676 Iustin Pop
      }
3314 b31c8676 Iustin Pop
    nl = [self.cfg.GetMasterNode(),
3315 b31c8676 Iustin Pop
          self.op.node_name]
3316 b31c8676 Iustin Pop
    return env, nl, nl
3317 b31c8676 Iustin Pop
3318 b31c8676 Iustin Pop
  def CheckPrereq(self):
3319 b31c8676 Iustin Pop
    """Check prerequisites.
3320 b31c8676 Iustin Pop

3321 b31c8676 Iustin Pop
    This only checks the instance list against the existing names.
3322 b31c8676 Iustin Pop

3323 b31c8676 Iustin Pop
    """
3324 3a5ba66a Iustin Pop
    node = self.node = self.cfg.GetNodeInfo(self.op.node_name)
3325 b31c8676 Iustin Pop
3326 97c61d46 Iustin Pop
    if (self.op.master_candidate is not None or
3327 97c61d46 Iustin Pop
        self.op.drained is not None or
3328 97c61d46 Iustin Pop
        self.op.offline is not None):
3329 97c61d46 Iustin Pop
      # we can't change the master's node flags
3330 97c61d46 Iustin Pop
      if self.op.node_name == self.cfg.GetMasterNode():
3331 97c61d46 Iustin Pop
        raise errors.OpPrereqError("The master role can be changed"
3332 5c983ee5 Iustin Pop
                                   " only via masterfailover",
3333 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
3334 97c61d46 Iustin Pop
3335 601908d0 Iustin Pop
3336 601908d0 Iustin Pop
    if node.master_candidate and self.might_demote and not self.lock_all:
3337 601908d0 Iustin Pop
      assert not self.op.auto_promote, "auto-promote set but lock_all not"
3338 601908d0 Iustin Pop
      # check if after removing the current node, we're missing master
3339 601908d0 Iustin Pop
      # candidates
3340 601908d0 Iustin Pop
      (mc_remaining, mc_should, _) = \
3341 601908d0 Iustin Pop
          self.cfg.GetMasterCandidateStats(exceptions=[node.name])
3342 601908d0 Iustin Pop
      if mc_remaining != mc_should:
3343 601908d0 Iustin Pop
        raise errors.OpPrereqError("Not enough master candidates, please"
3344 601908d0 Iustin Pop
                                   " pass auto_promote to allow promotion",
3345 601908d0 Iustin Pop
                                   errors.ECODE_INVAL)
3346 3e83dd48 Iustin Pop
3347 c9d443ea Iustin Pop
    if (self.op.master_candidate == True and
3348 c9d443ea Iustin Pop
        ((node.offline and not self.op.offline == False) or
3349 c9d443ea Iustin Pop
         (node.drained and not self.op.drained == False))):
3350 c9d443ea Iustin Pop
      raise errors.OpPrereqError("Node '%s' is offline or drained, can't set"
3351 5c983ee5 Iustin Pop
                                 " to master_candidate" % node.name,
3352 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
3353 3a5ba66a Iustin Pop
3354 3d9eb52b Guido Trotter
    # If we're being deofflined/drained, we'll MC ourself if needed
3355 601908d0 Iustin Pop
    if (self.deoffline_or_drain and not self.offline_or_drain and not
3356 cea0534a Guido Trotter
        self.op.master_candidate == True and not node.master_candidate):
3357 3d9eb52b Guido Trotter
      self.op.master_candidate = _DecideSelfPromotion(self)
3358 3d9eb52b Guido Trotter
      if self.op.master_candidate:
3359 3d9eb52b Guido Trotter
        self.LogInfo("Autopromoting node to master candidate")
3360 3d9eb52b Guido Trotter
3361 b31c8676 Iustin Pop
    return
3362 b31c8676 Iustin Pop
3363 b31c8676 Iustin Pop
  def Exec(self, feedback_fn):
3364 b31c8676 Iustin Pop
    """Modifies a node.
3365 b31c8676 Iustin Pop

3366 b31c8676 Iustin Pop
    """
3367 3a5ba66a Iustin Pop
    node = self.node
3368 b31c8676 Iustin Pop
3369 b31c8676 Iustin Pop
    result = []
3370 c9d443ea Iustin Pop
    changed_mc = False
3371 b31c8676 Iustin Pop
3372 3a5ba66a Iustin Pop
    if self.op.offline is not None:
3373 3a5ba66a Iustin Pop
      node.offline = self.op.offline
3374 3a5ba66a Iustin Pop
      result.append(("offline", str(self.op.offline)))
3375 c9d443ea Iustin Pop
      if self.op.offline == True:
3376 c9d443ea Iustin Pop
        if node.master_candidate:
3377 c9d443ea Iustin Pop
          node.master_candidate = False
3378 c9d443ea Iustin Pop
          changed_mc = True
3379 c9d443ea Iustin Pop
          result.append(("master_candidate", "auto-demotion due to offline"))
3380 c9d443ea Iustin Pop
        if node.drained:
3381 c9d443ea Iustin Pop
          node.drained = False
3382 c9d443ea Iustin Pop
          result.append(("drained", "clear drained status due to offline"))
3383 3a5ba66a Iustin Pop
3384 b31c8676 Iustin Pop
    if self.op.master_candidate is not None:
3385 b31c8676 Iustin Pop
      node.master_candidate = self.op.master_candidate
3386 c9d443ea Iustin Pop
      changed_mc = True
3387 b31c8676 Iustin Pop
      result.append(("master_candidate", str(self.op.master_candidate)))
3388 56aa9fd5 Iustin Pop
      if self.op.master_candidate == False:
3389 56aa9fd5 Iustin Pop
        rrc = self.rpc.call_node_demote_from_mc(node.name)
3390 4c4e4e1e Iustin Pop
        msg = rrc.fail_msg
3391 0959c824 Iustin Pop
        if msg:
3392 0959c824 Iustin Pop
          self.LogWarning("Node failed to demote itself: %s" % msg)
3393 b31c8676 Iustin Pop
3394 c9d443ea Iustin Pop
    if self.op.drained is not None:
3395 c9d443ea Iustin Pop
      node.drained = self.op.drained
3396 82e12743 Iustin Pop
      result.append(("drained", str(self.op.drained)))
3397 c9d443ea Iustin Pop
      if self.op.drained == True:
3398 c9d443ea Iustin Pop
        if node.master_candidate:
3399 c9d443ea Iustin Pop
          node.master_candidate = False
3400 c9d443ea Iustin Pop
          changed_mc = True
3401 c9d443ea Iustin Pop
          result.append(("master_candidate", "auto-demotion due to drain"))
3402 dec0d9da Iustin Pop
          rrc = self.rpc.call_node_demote_from_mc(node.name)
3403 3cebe102 Michael Hanselmann
          msg = rrc.fail_msg
3404 dec0d9da Iustin Pop
          if msg:
3405 dec0d9da Iustin Pop
            self.LogWarning("Node failed to demote itself: %s" % msg)
3406 c9d443ea Iustin Pop
        if node.offline:
3407 c9d443ea Iustin Pop
          node.offline = False
3408 c9d443ea Iustin Pop
          result.append(("offline", "clear offline status due to drain"))
3409 c9d443ea Iustin Pop
3410 601908d0 Iustin Pop
    # we locked all nodes, we adjust the CP before updating this node
3411 601908d0 Iustin Pop
    if self.lock_all:
3412 601908d0 Iustin Pop
      _AdjustCandidatePool(self, [node.name])
3413 601908d0 Iustin Pop
3414 b31c8676 Iustin Pop
    # this will trigger configuration file update, if needed
3415 a4eae71f Michael Hanselmann
    self.cfg.Update(node, feedback_fn)
3416 601908d0 Iustin Pop
3417 b31c8676 Iustin Pop
    # this will trigger job queue propagation or cleanup
3418 c9d443ea Iustin Pop
    if changed_mc:
3419 3a26773f Iustin Pop
      self.context.ReaddNode(node)
3420 b31c8676 Iustin Pop
3421 b31c8676 Iustin Pop
    return result
3422 b31c8676 Iustin Pop
3423 b31c8676 Iustin Pop
3424 f5118ade Iustin Pop
class LUPowercycleNode(NoHooksLU):
3425 f5118ade Iustin Pop
  """Powercycles a node.
3426 f5118ade Iustin Pop

3427 f5118ade Iustin Pop
  """
3428 f5118ade Iustin Pop
  _OP_REQP = ["node_name", "force"]
3429 f5118ade Iustin Pop
  REQ_BGL = False
3430 f5118ade Iustin Pop
3431 f5118ade Iustin Pop
  def CheckArguments(self):
3432 cf26a87a Iustin Pop
    self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name)
3433 cf26a87a Iustin Pop
    if self.op.node_name == self.cfg.GetMasterNode() and not self.op.force:
3434 f5118ade Iustin Pop
      raise errors.OpPrereqError("The node is the master and the force"
3435 5c983ee5 Iustin Pop
                                 " parameter was not set",
3436 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
3437 f5118ade Iustin Pop
3438 f5118ade Iustin Pop
  def ExpandNames(self):
3439 f5118ade Iustin Pop
    """Locking for PowercycleNode.
3440 f5118ade Iustin Pop

3441 efb8da02 Michael Hanselmann
    This is a last-resort option and shouldn't block on other
3442 f5118ade Iustin Pop
    jobs. Therefore, we grab no locks.
3443 f5118ade Iustin Pop

3444 f5118ade Iustin Pop
    """
3445 f5118ade Iustin Pop
    self.needed_locks = {}
3446 f5118ade Iustin Pop
3447 f5118ade Iustin Pop
  def CheckPrereq(self):
3448 f5118ade Iustin Pop
    """Check prerequisites.
3449 f5118ade Iustin Pop

3450 f5118ade Iustin Pop
    This LU has no prereqs.
3451 f5118ade Iustin Pop

3452 f5118ade Iustin Pop
    """
3453 f5118ade Iustin Pop
    pass
3454 f5118ade Iustin Pop
3455 f5118ade Iustin Pop
  def Exec(self, feedback_fn):
3456 f5118ade Iustin Pop
    """Reboots a node.
3457 f5118ade Iustin Pop

3458 f5118ade Iustin Pop
    """
3459 f5118ade Iustin Pop
    result = self.rpc.call_node_powercycle(self.op.node_name,
3460 f5118ade Iustin Pop
                                           self.cfg.GetHypervisorType())
3461 4c4e4e1e Iustin Pop
    result.Raise("Failed to schedule the reboot")
3462 f5118ade Iustin Pop
    return result.payload
3463 f5118ade Iustin Pop
3464 f5118ade Iustin Pop
3465 a8083063 Iustin Pop
class LUQueryClusterInfo(NoHooksLU):
3466 a8083063 Iustin Pop
  """Query cluster configuration.
3467 a8083063 Iustin Pop

3468 a8083063 Iustin Pop
  """
3469 a8083063 Iustin Pop
  _OP_REQP = []
3470 642339cf Guido Trotter
  REQ_BGL = False
3471 642339cf Guido Trotter
3472 642339cf Guido Trotter
  def ExpandNames(self):
3473 642339cf Guido Trotter
    self.needed_locks = {}
3474 a8083063 Iustin Pop
3475 a8083063 Iustin Pop
  def CheckPrereq(self):
3476 a8083063 Iustin Pop
    """No prerequsites needed for this LU.
3477 a8083063 Iustin Pop

3478 a8083063 Iustin Pop
    """
3479 a8083063 Iustin Pop
    pass
3480 a8083063 Iustin Pop
3481 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
3482 a8083063 Iustin Pop
    """Return cluster config.
3483 a8083063 Iustin Pop

3484 a8083063 Iustin Pop
    """
3485 469f88e1 Iustin Pop
    cluster = self.cfg.GetClusterInfo()
3486 17463d22 René Nussbaumer
    os_hvp = {}
3487 17463d22 René Nussbaumer
3488 17463d22 René Nussbaumer
    # Filter just for enabled hypervisors
3489 17463d22 René Nussbaumer
    for os_name, hv_dict in cluster.os_hvp.items():
3490 17463d22 René Nussbaumer
      os_hvp[os_name] = {}
3491 17463d22 René Nussbaumer
      for hv_name, hv_params in hv_dict.items():
3492 17463d22 René Nussbaumer
        if hv_name in cluster.enabled_hypervisors:
3493 17463d22 René Nussbaumer
          os_hvp[os_name][hv_name] = hv_params
3494 17463d22 René Nussbaumer
3495 a8083063 Iustin Pop
    result = {
3496 a8083063 Iustin Pop
      "software_version": constants.RELEASE_VERSION,
3497 a8083063 Iustin Pop
      "protocol_version": constants.PROTOCOL_VERSION,
3498 a8083063 Iustin Pop
      "config_version": constants.CONFIG_VERSION,
3499 d1a7d66f Guido Trotter
      "os_api_version": max(constants.OS_API_VERSIONS),
3500 a8083063 Iustin Pop
      "export_version": constants.EXPORT_VERSION,
3501 a8083063 Iustin Pop
      "architecture": (platform.architecture()[0], platform.machine()),
3502 469f88e1 Iustin Pop
      "name": cluster.cluster_name,
3503 469f88e1 Iustin Pop
      "master": cluster.master_node,
3504 066f465d Guido Trotter
      "default_hypervisor": cluster.enabled_hypervisors[0],
3505 469f88e1 Iustin Pop
      "enabled_hypervisors": cluster.enabled_hypervisors,
3506 b8810fec Michael Hanselmann
      "hvparams": dict([(hypervisor_name, cluster.hvparams[hypervisor_name])
3507 7c4d6c7b Michael Hanselmann
                        for hypervisor_name in cluster.enabled_hypervisors]),
3508 17463d22 René Nussbaumer
      "os_hvp": os_hvp,
3509 469f88e1 Iustin Pop
      "beparams": cluster.beparams,
3510 1094acda Guido Trotter
      "nicparams": cluster.nicparams,
3511 4b7735f9 Iustin Pop
      "candidate_pool_size": cluster.candidate_pool_size,
3512 7a56b411 Guido Trotter
      "master_netdev": cluster.master_netdev,
3513 7a56b411 Guido Trotter
      "volume_group_name": cluster.volume_group_name,
3514 7a56b411 Guido Trotter
      "file_storage_dir": cluster.file_storage_dir,
3515 90f72445 Iustin Pop
      "ctime": cluster.ctime,
3516 90f72445 Iustin Pop
      "mtime": cluster.mtime,
3517 259578eb Iustin Pop
      "uuid": cluster.uuid,
3518 c118d1f4 Michael Hanselmann
      "tags": list(cluster.GetTags()),
3519 a8083063 Iustin Pop
      }
3520 a8083063 Iustin Pop
3521 a8083063 Iustin Pop
    return result
3522 a8083063 Iustin Pop
3523 a8083063 Iustin Pop
3524 ae5849b5 Michael Hanselmann
class LUQueryConfigValues(NoHooksLU):
3525 ae5849b5 Michael Hanselmann
  """Return configuration values.
3526 a8083063 Iustin Pop

3527 a8083063 Iustin Pop
  """
3528 a8083063 Iustin Pop
  _OP_REQP = []
3529 642339cf Guido Trotter
  REQ_BGL = False
3530 a2d2e1a7 Iustin Pop
  _FIELDS_DYNAMIC = utils.FieldSet()
3531 05e50653 Michael Hanselmann
  _FIELDS_STATIC = utils.FieldSet("cluster_name", "master_node", "drain_flag",
3532 05e50653 Michael Hanselmann
                                  "watcher_pause")
3533 642339cf Guido Trotter
3534 642339cf Guido Trotter
  def ExpandNames(self):
3535 642339cf Guido Trotter
    self.needed_locks = {}
3536 a8083063 Iustin Pop
3537 31bf511f Iustin Pop
    _CheckOutputFields(static=self._FIELDS_STATIC,
3538 31bf511f Iustin Pop
                       dynamic=self._FIELDS_DYNAMIC,
3539 ae5849b5 Michael Hanselmann
                       selected=self.op.output_fields)
3540 ae5849b5 Michael Hanselmann
3541 a8083063 Iustin Pop
  def CheckPrereq(self):
3542 a8083063 Iustin Pop
    """No prerequisites.
3543 a8083063 Iustin Pop

3544 a8083063 Iustin Pop
    """
3545 a8083063 Iustin Pop
    pass
3546 a8083063 Iustin Pop
3547 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
3548 a8083063 Iustin Pop
    """Dump a representation of the cluster config to the standard output.
3549 a8083063 Iustin Pop

3550 a8083063 Iustin Pop
    """
3551 ae5849b5 Michael Hanselmann
    values = []
3552 ae5849b5 Michael Hanselmann
    for field in self.op.output_fields:
3553 ae5849b5 Michael Hanselmann
      if field == "cluster_name":
3554 3ccafd0e Iustin Pop
        entry = self.cfg.GetClusterName()
3555 ae5849b5 Michael Hanselmann
      elif field == "master_node":
3556 3ccafd0e Iustin Pop
        entry = self.cfg.GetMasterNode()
3557 3ccafd0e Iustin Pop
      elif field == "drain_flag":
3558 3ccafd0e Iustin Pop
        entry = os.path.exists(constants.JOB_QUEUE_DRAIN_FILE)
3559 05e50653 Michael Hanselmann
      elif field == "watcher_pause":
3560 cac599f1 Michael Hanselmann
        entry = utils.ReadWatcherPauseFile(constants.WATCHER_PAUSEFILE)
3561 ae5849b5 Michael Hanselmann
      else:
3562 ae5849b5 Michael Hanselmann
        raise errors.ParameterError(field)
3563 3ccafd0e Iustin Pop
      values.append(entry)
3564 ae5849b5 Michael Hanselmann
    return values
3565 a8083063 Iustin Pop
3566 a8083063 Iustin Pop
3567 a8083063 Iustin Pop
class LUActivateInstanceDisks(NoHooksLU):
3568 a8083063 Iustin Pop
  """Bring up an instance's disks.
3569 a8083063 Iustin Pop

3570 a8083063 Iustin Pop
  """
3571 a8083063 Iustin Pop
  _OP_REQP = ["instance_name"]
3572 f22a8ba3 Guido Trotter
  REQ_BGL = False
3573 f22a8ba3 Guido Trotter
3574 f22a8ba3 Guido Trotter
  def ExpandNames(self):
3575 f22a8ba3 Guido Trotter
    self._ExpandAndLockInstance()
3576 f22a8ba3 Guido Trotter
    self.needed_locks[locking.LEVEL_NODE] = []
3577 f22a8ba3 Guido Trotter
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
3578 f22a8ba3 Guido Trotter
3579 f22a8ba3 Guido Trotter
  def DeclareLocks(self, level):
3580 f22a8ba3 Guido Trotter
    if level == locking.LEVEL_NODE:
3581 f22a8ba3 Guido Trotter
      self._LockInstancesNodes()
3582 a8083063 Iustin Pop
3583 a8083063 Iustin Pop
  def CheckPrereq(self):
3584 a8083063 Iustin Pop
    """Check prerequisites.
3585 a8083063 Iustin Pop

3586 a8083063 Iustin Pop
    This checks that the instance is in the cluster.
3587 a8083063 Iustin Pop

3588 a8083063 Iustin Pop
    """
3589 f22a8ba3 Guido Trotter
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
3590 f22a8ba3 Guido Trotter
    assert self.instance is not None, \
3591 f22a8ba3 Guido Trotter
      "Cannot retrieve locked instance %s" % self.op.instance_name
3592 43017d26 Iustin Pop
    _CheckNodeOnline(self, self.instance.primary_node)
3593 b4ec07f8 Iustin Pop
    if not hasattr(self.op, "ignore_size"):
3594 b4ec07f8 Iustin Pop
      self.op.ignore_size = False
3595 a8083063 Iustin Pop
3596 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
3597 a8083063 Iustin Pop
    """Activate the disks.
3598 a8083063 Iustin Pop

3599 a8083063 Iustin Pop
    """
3600 b4ec07f8 Iustin Pop
    disks_ok, disks_info = \
3601 b4ec07f8 Iustin Pop
              _AssembleInstanceDisks(self, self.instance,
3602 b4ec07f8 Iustin Pop
                                     ignore_size=self.op.ignore_size)
3603 a8083063 Iustin Pop
    if not disks_ok:
3604 3ecf6786 Iustin Pop
      raise errors.OpExecError("Cannot activate block devices")
3605 a8083063 Iustin Pop
3606 a8083063 Iustin Pop
    return disks_info
3607 a8083063 Iustin Pop
3608 a8083063 Iustin Pop
3609 e3443b36 Iustin Pop
def _AssembleInstanceDisks(lu, instance, ignore_secondaries=False,
3610 e3443b36 Iustin Pop
                           ignore_size=False):
3611 a8083063 Iustin Pop
  """Prepare the block devices for an instance.
3612 a8083063 Iustin Pop

3613 a8083063 Iustin Pop
  This sets up the block devices on all nodes.
3614 a8083063 Iustin Pop

3615 e4376078 Iustin Pop
  @type lu: L{LogicalUnit}
3616 e4376078 Iustin Pop
  @param lu: the logical unit on whose behalf we execute
3617 e4376078 Iustin Pop
  @type instance: L{objects.Instance}
3618 e4376078 Iustin Pop
  @param instance: the instance for whose disks we assemble
3619 e4376078 Iustin Pop
  @type ignore_secondaries: boolean
3620 e4376078 Iustin Pop
  @param ignore_secondaries: if true, errors on secondary nodes
3621 e4376078 Iustin Pop
      won't result in an error return from the function
3622 e3443b36 Iustin Pop
  @type ignore_size: boolean
3623 e3443b36 Iustin Pop
  @param ignore_size: if true, the current known size of the disk
3624 e3443b36 Iustin Pop
      will not be used during the disk activation, useful for cases
3625 e3443b36 Iustin Pop
      when the size is wrong
3626 e4376078 Iustin Pop
  @return: False if the operation failed, otherwise a list of
3627 e4376078 Iustin Pop
      (host, instance_visible_name, node_visible_name)
3628 e4376078 Iustin Pop
      with the mapping from node devices to instance devices
3629 a8083063 Iustin Pop

3630 a8083063 Iustin Pop
  """
3631 a8083063 Iustin Pop
  device_info = []
3632 a8083063 Iustin Pop
  disks_ok = True
3633 fdbd668d Iustin Pop
  iname = instance.name
3634 fdbd668d Iustin Pop
  # With the two passes mechanism we try to reduce the window of
3635 fdbd668d Iustin Pop
  # opportunity for the race condition of switching DRBD to primary
3636 fdbd668d Iustin Pop
  # before handshaking occured, but we do not eliminate it
3637 fdbd668d Iustin Pop
3638 fdbd668d Iustin Pop
  # The proper fix would be to wait (with some limits) until the
3639 fdbd668d Iustin Pop
  # connection has been made and drbd transitions from WFConnection
3640 fdbd668d Iustin Pop
  # into any other network-connected state (Connected, SyncTarget,
3641 fdbd668d Iustin Pop
  # SyncSource, etc.)
3642 fdbd668d Iustin Pop
3643 fdbd668d Iustin Pop
  # 1st pass, assemble on all nodes in secondary mode
3644 a8083063 Iustin Pop
  for inst_disk in instance.disks:
3645 a8083063 Iustin Pop
    for node, node_disk in inst_disk.ComputeNodeTree(instance.primary_node):
3646 e3443b36 Iustin Pop
      if ignore_size:
3647 e3443b36 Iustin Pop
        node_disk = node_disk.Copy()
3648 e3443b36 Iustin Pop
        node_disk.UnsetSize()
3649 b9bddb6b Iustin Pop
      lu.cfg.SetDiskID(node_disk, node)
3650 72737a7f Iustin Pop
      result = lu.rpc.call_blockdev_assemble(node, node_disk, iname, False)
3651 4c4e4e1e Iustin Pop
      msg = result.fail_msg
3652 53c14ef1 Iustin Pop
      if msg:
3653 86d9d3bb Iustin Pop
        lu.proc.LogWarning("Could not prepare block device %s on node %s"
3654 53c14ef1 Iustin Pop
                           " (is_primary=False, pass=1): %s",
3655 53c14ef1 Iustin Pop
                           inst_disk.iv_name, node, msg)
3656 fdbd668d Iustin Pop
        if not ignore_secondaries:
3657 a8083063 Iustin Pop
          disks_ok = False
3658 fdbd668d Iustin Pop
3659 fdbd668d Iustin Pop
  # FIXME: race condition on drbd migration to primary
3660 fdbd668d Iustin Pop
3661 fdbd668d Iustin Pop
  # 2nd pass, do only the primary node
3662 fdbd668d Iustin Pop
  for inst_disk in instance.disks:
3663 d52ea991 Michael Hanselmann
    dev_path = None
3664 d52ea991 Michael Hanselmann
3665 fdbd668d Iustin Pop
    for node, node_disk in inst_disk.ComputeNodeTree(instance.primary_node):
3666 fdbd668d Iustin Pop
      if node != instance.primary_node:
3667 fdbd668d Iustin Pop
        continue
3668 e3443b36 Iustin Pop
      if ignore_size:
3669 e3443b36 Iustin Pop
        node_disk = node_disk.Copy()
3670 e3443b36 Iustin Pop
        node_disk.UnsetSize()
3671 b9bddb6b Iustin Pop
      lu.cfg.SetDiskID(node_disk, node)
3672 72737a7f Iustin Pop
      result = lu.rpc.call_blockdev_assemble(node, node_disk, iname, True)
3673 4c4e4e1e Iustin Pop
      msg = result.fail_msg
3674 53c14ef1 Iustin Pop
      if msg:
3675 86d9d3bb Iustin Pop
        lu.proc.LogWarning("Could not prepare block device %s on node %s"
3676 53c14ef1 Iustin Pop
                           " (is_primary=True, pass=2): %s",
3677 53c14ef1 Iustin Pop
                           inst_disk.iv_name, node, msg)
3678 fdbd668d Iustin Pop
        disks_ok = False
3679 d52ea991 Michael Hanselmann
      else:
3680 d52ea991 Michael Hanselmann
        dev_path = result.payload
3681 d52ea991 Michael Hanselmann
3682 d52ea991 Michael Hanselmann
    device_info.append((instance.primary_node, inst_disk.iv_name, dev_path))
3683 a8083063 Iustin Pop
3684 b352ab5b Iustin Pop
  # leave the disks configured for the primary node
3685 b352ab5b Iustin Pop
  # this is a workaround that would be fixed better by
3686 b352ab5b Iustin Pop
  # improving the logical/physical id handling
3687 b352ab5b Iustin Pop
  for disk in instance.disks:
3688 b9bddb6b Iustin Pop
    lu.cfg.SetDiskID(disk, instance.primary_node)
3689 b352ab5b Iustin Pop
3690 a8083063 Iustin Pop
  return disks_ok, device_info
3691 a8083063 Iustin Pop
3692 a8083063 Iustin Pop
3693 b9bddb6b Iustin Pop
def _StartInstanceDisks(lu, instance, force):
3694 3ecf6786 Iustin Pop
  """Start the disks of an instance.
3695 3ecf6786 Iustin Pop

3696 3ecf6786 Iustin Pop
  """
3697 7c4d6c7b Michael Hanselmann
  disks_ok, _ = _AssembleInstanceDisks(lu, instance,
3698 fe7b0351 Michael Hanselmann
                                           ignore_secondaries=force)
3699 fe7b0351 Michael Hanselmann
  if not disks_ok:
3700 b9bddb6b Iustin Pop
    _ShutdownInstanceDisks(lu, instance)
3701 fe7b0351 Michael Hanselmann
    if force is not None and not force:
3702 86d9d3bb Iustin Pop
      lu.proc.LogWarning("", hint="If the message above refers to a"
3703 86d9d3bb Iustin Pop
                         " secondary node,"
3704 86d9d3bb Iustin Pop
                         " you can retry the operation using '--force'.")
3705 3ecf6786 Iustin Pop
    raise errors.OpExecError("Disk consistency error")
3706 fe7b0351 Michael Hanselmann
3707 fe7b0351 Michael Hanselmann
3708 a8083063 Iustin Pop
class LUDeactivateInstanceDisks(NoHooksLU):
3709 a8083063 Iustin Pop
  """Shutdown an instance's disks.
3710 a8083063 Iustin Pop

3711 a8083063 Iustin Pop
  """
3712 a8083063 Iustin Pop
  _OP_REQP = ["instance_name"]
3713 f22a8ba3 Guido Trotter
  REQ_BGL = False
3714 f22a8ba3 Guido Trotter
3715 f22a8ba3 Guido Trotter
  def ExpandNames(self):
3716 f22a8ba3 Guido Trotter
    self._ExpandAndLockInstance()
3717 f22a8ba3 Guido Trotter
    self.needed_locks[locking.LEVEL_NODE] = []
3718 f22a8ba3 Guido Trotter
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
3719 f22a8ba3 Guido Trotter
3720 f22a8ba3 Guido Trotter
  def DeclareLocks(self, level):
3721 f22a8ba3 Guido Trotter
    if level == locking.LEVEL_NODE:
3722 f22a8ba3 Guido Trotter
      self._LockInstancesNodes()
3723 a8083063 Iustin Pop
3724 a8083063 Iustin Pop
  def CheckPrereq(self):
3725 a8083063 Iustin Pop
    """Check prerequisites.
3726 a8083063 Iustin Pop

3727 a8083063 Iustin Pop
    This checks that the instance is in the cluster.
3728 a8083063 Iustin Pop

3729 a8083063 Iustin Pop
    """
3730 f22a8ba3 Guido Trotter
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
3731 f22a8ba3 Guido Trotter
    assert self.instance is not None, \
3732 f22a8ba3 Guido Trotter
      "Cannot retrieve locked instance %s" % self.op.instance_name
3733 a8083063 Iustin Pop
3734 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
3735 a8083063 Iustin Pop
    """Deactivate the disks
3736 a8083063 Iustin Pop

3737 a8083063 Iustin Pop
    """
3738 a8083063 Iustin Pop
    instance = self.instance
3739 b9bddb6b Iustin Pop
    _SafeShutdownInstanceDisks(self, instance)
3740 a8083063 Iustin Pop
3741 a8083063 Iustin Pop
3742 b9bddb6b Iustin Pop
def _SafeShutdownInstanceDisks(lu, instance):
3743 155d6c75 Guido Trotter
  """Shutdown block devices of an instance.
3744 155d6c75 Guido Trotter

3745 155d6c75 Guido Trotter
  This function checks if an instance is running, before calling
3746 155d6c75 Guido Trotter
  _ShutdownInstanceDisks.
3747 155d6c75 Guido Trotter

3748 155d6c75 Guido Trotter
  """
3749 31624382 Iustin Pop
  _CheckInstanceDown(lu, instance, "cannot shutdown disks")
3750 b9bddb6b Iustin Pop
  _ShutdownInstanceDisks(lu, instance)
3751 a8083063 Iustin Pop
3752 a8083063 Iustin Pop
3753 b9bddb6b Iustin Pop
def _ShutdownInstanceDisks(lu, instance, ignore_primary=False):
3754 a8083063 Iustin Pop
  """Shutdown block devices of an instance.
3755 a8083063 Iustin Pop

3756 a8083063 Iustin Pop
  This does the shutdown on all nodes of the instance.
3757 a8083063 Iustin Pop

3758 a8083063 Iustin Pop
  If the ignore_primary is false, errors on the primary node are
3759 a8083063 Iustin Pop
  ignored.
3760 a8083063 Iustin Pop

3761 a8083063 Iustin Pop
  """
3762 cacfd1fd Iustin Pop
  all_result = True
3763 a8083063 Iustin Pop
  for disk in instance.disks:
3764 a8083063 Iustin Pop
    for node, top_disk in disk.ComputeNodeTree(instance.primary_node):
3765 b9bddb6b Iustin Pop
      lu.cfg.SetDiskID(top_disk, node)
3766 781de953 Iustin Pop
      result = lu.rpc.call_blockdev_shutdown(node, top_disk)
3767 4c4e4e1e Iustin Pop
      msg = result.fail_msg
3768 cacfd1fd Iustin Pop
      if msg:
3769 cacfd1fd Iustin Pop
        lu.LogWarning("Could not shutdown block device %s on node %s: %s",
3770 cacfd1fd Iustin Pop
                      disk.iv_name, node, msg)
3771 a8083063 Iustin Pop
        if not ignore_primary or node != instance.primary_node:
3772 cacfd1fd Iustin Pop
          all_result = False
3773 cacfd1fd Iustin Pop
  return all_result
3774 a8083063 Iustin Pop
3775 a8083063 Iustin Pop
3776 9ca87a96 Iustin Pop
def _CheckNodeFreeMemory(lu, node, reason, requested, hypervisor_name):
3777 d4f16fd9 Iustin Pop
  """Checks if a node has enough free memory.
3778 d4f16fd9 Iustin Pop

3779 d4f16fd9 Iustin Pop
  This function check if a given node has the needed amount of free
3780 d4f16fd9 Iustin Pop
  memory. In case the node has less memory or we cannot get the
3781 d4f16fd9 Iustin Pop
  information from the node, this function raise an OpPrereqError
3782 d4f16fd9 Iustin Pop
  exception.
3783 d4f16fd9 Iustin Pop

3784 b9bddb6b Iustin Pop
  @type lu: C{LogicalUnit}
3785 b9bddb6b Iustin Pop
  @param lu: a logical unit from which we get configuration data
3786 e69d05fd Iustin Pop
  @type node: C{str}
3787 e69d05fd Iustin Pop
  @param node: the node to check
3788 e69d05fd Iustin Pop
  @type reason: C{str}
3789 e69d05fd Iustin Pop
  @param reason: string to use in the error message
3790 e69d05fd Iustin Pop
  @type requested: C{int}
3791 e69d05fd Iustin Pop
  @param requested: the amount of memory in MiB to check for
3792 9ca87a96 Iustin Pop
  @type hypervisor_name: C{str}
3793 9ca87a96 Iustin Pop
  @param hypervisor_name: the hypervisor to ask for memory stats
3794 e69d05fd Iustin Pop
  @raise errors.OpPrereqError: if the node doesn't have enough memory, or
3795 e69d05fd Iustin Pop
      we cannot check the node
3796 d4f16fd9 Iustin Pop

3797 d4f16fd9 Iustin Pop
  """
3798 9ca87a96 Iustin Pop
  nodeinfo = lu.rpc.call_node_info([node], lu.cfg.GetVGName(), hypervisor_name)
3799 045dd6d9 Iustin Pop
  nodeinfo[node].Raise("Can't get data from node %s" % node,
3800 045dd6d9 Iustin Pop
                       prereq=True, ecode=errors.ECODE_ENVIRON)
3801 070e998b Iustin Pop
  free_mem = nodeinfo[node].payload.get('memory_free', None)
3802 d4f16fd9 Iustin Pop
  if not isinstance(free_mem, int):
3803 d4f16fd9 Iustin Pop
    raise errors.OpPrereqError("Can't compute free memory on node %s, result"
3804 5c983ee5 Iustin Pop
                               " was '%s'" % (node, free_mem),
3805 5c983ee5 Iustin Pop
                               errors.ECODE_ENVIRON)
3806 d4f16fd9 Iustin Pop
  if requested > free_mem:
3807 d4f16fd9 Iustin Pop
    raise errors.OpPrereqError("Not enough memory on node %s for %s:"
3808 070e998b Iustin Pop
                               " needed %s MiB, available %s MiB" %
3809 5c983ee5 Iustin Pop
                               (node, reason, requested, free_mem),
3810 5c983ee5 Iustin Pop
                               errors.ECODE_NORES)
3811 d4f16fd9 Iustin Pop
3812 d4f16fd9 Iustin Pop
3813 701384a9 Iustin Pop
def _CheckNodesFreeDisk(lu, nodenames, requested):
3814 701384a9 Iustin Pop
  """Checks if nodes have enough free disk space in the default VG.
3815 701384a9 Iustin Pop

3816 701384a9 Iustin Pop
  This function check if all given nodes have the needed amount of
3817 701384a9 Iustin Pop
  free disk. In case any node has less disk or we cannot get the
3818 701384a9 Iustin Pop
  information from the node, this function raise an OpPrereqError
3819 701384a9 Iustin Pop
  exception.
3820 701384a9 Iustin Pop

3821 701384a9 Iustin Pop
  @type lu: C{LogicalUnit}
3822 701384a9 Iustin Pop
  @param lu: a logical unit from which we get configuration data
3823 701384a9 Iustin Pop
  @type nodenames: C{list}
3824 701384a9 Iustin Pop
  @param node: the list of node names to check
3825 701384a9 Iustin Pop
  @type requested: C{int}
3826 701384a9 Iustin Pop
  @param requested: the amount of disk in MiB to check for
3827 701384a9 Iustin Pop
  @raise errors.OpPrereqError: if the node doesn't have enough disk, or
3828 701384a9 Iustin Pop
      we cannot check the node
3829 701384a9 Iustin Pop

3830 701384a9 Iustin Pop
  """
3831 701384a9 Iustin Pop
  nodeinfo = lu.rpc.call_node_info(nodenames, lu.cfg.GetVGName(),
3832 701384a9 Iustin Pop
                                   lu.cfg.GetHypervisorType())
3833 701384a9 Iustin Pop
  for node in nodenames:
3834 701384a9 Iustin Pop
    info = nodeinfo[node]
3835 701384a9 Iustin Pop
    info.Raise("Cannot get current information from node %s" % node,
3836 701384a9 Iustin Pop
               prereq=True, ecode=errors.ECODE_ENVIRON)
3837 701384a9 Iustin Pop
    vg_free = info.payload.get("vg_free", None)
3838 701384a9 Iustin Pop
    if not isinstance(vg_free, int):
3839 701384a9 Iustin Pop
      raise errors.OpPrereqError("Can't compute free disk space on node %s,"
3840 701384a9 Iustin Pop
                                 " result was '%s'" % (node, vg_free),
3841 701384a9 Iustin Pop
                                 errors.ECODE_ENVIRON)
3842 701384a9 Iustin Pop
    if requested > vg_free:
3843 701384a9 Iustin Pop
      raise errors.OpPrereqError("Not enough disk space on target node %s:"
3844 701384a9 Iustin Pop
                                 " required %d MiB, available %d MiB" %
3845 701384a9 Iustin Pop
                                 (node, requested, vg_free),
3846 701384a9 Iustin Pop
                                 errors.ECODE_NORES)
3847 701384a9 Iustin Pop
3848 701384a9 Iustin Pop
3849 a8083063 Iustin Pop
class LUStartupInstance(LogicalUnit):
3850 a8083063 Iustin Pop
  """Starts an instance.
3851 a8083063 Iustin Pop

3852 a8083063 Iustin Pop
  """
3853 a8083063 Iustin Pop
  HPATH = "instance-start"
3854 a8083063 Iustin Pop
  HTYPE = constants.HTYPE_INSTANCE
3855 a8083063 Iustin Pop
  _OP_REQP = ["instance_name", "force"]
3856 e873317a Guido Trotter
  REQ_BGL = False
3857 e873317a Guido Trotter
3858 e873317a Guido Trotter
  def ExpandNames(self):
3859 e873317a Guido Trotter
    self._ExpandAndLockInstance()
3860 a8083063 Iustin Pop
3861 a8083063 Iustin Pop
  def BuildHooksEnv(self):
3862 a8083063 Iustin Pop
    """Build hooks env.
3863 a8083063 Iustin Pop

3864 a8083063 Iustin Pop
    This runs on master, primary and secondary nodes of the instance.
3865 a8083063 Iustin Pop

3866 a8083063 Iustin Pop
    """
3867 a8083063 Iustin Pop
    env = {
3868 a8083063 Iustin Pop
      "FORCE": self.op.force,
3869 a8083063 Iustin Pop
      }
3870 338e51e8 Iustin Pop
    env.update(_BuildInstanceHookEnvByObject(self, self.instance))
3871 6b12959c Iustin Pop
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
3872 a8083063 Iustin Pop
    return env, nl, nl
3873 a8083063 Iustin Pop
3874 a8083063 Iustin Pop
  def CheckPrereq(self):
3875 a8083063 Iustin Pop
    """Check prerequisites.
3876 a8083063 Iustin Pop

3877 a8083063 Iustin Pop
    This checks that the instance is in the cluster.
3878 a8083063 Iustin Pop

3879 a8083063 Iustin Pop
    """
3880 e873317a Guido Trotter
    self.instance = instance = self.cfg.GetInstanceInfo(self.op.instance_name)
3881 e873317a Guido Trotter
    assert self.instance is not None, \
3882 e873317a Guido Trotter
      "Cannot retrieve locked instance %s" % self.op.instance_name
3883 a8083063 Iustin Pop
3884 d04aaa2f Iustin Pop
    # extra beparams
3885 d04aaa2f Iustin Pop
    self.beparams = getattr(self.op, "beparams", {})
3886 d04aaa2f Iustin Pop
    if self.beparams:
3887 d04aaa2f Iustin Pop
      if not isinstance(self.beparams, dict):
3888 d04aaa2f Iustin Pop
        raise errors.OpPrereqError("Invalid beparams passed: %s, expected"
3889 5c983ee5 Iustin Pop
                                   " dict" % (type(self.beparams), ),
3890 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
3891 d04aaa2f Iustin Pop
      # fill the beparams dict
3892 d04aaa2f Iustin Pop
      utils.ForceDictType(self.beparams, constants.BES_PARAMETER_TYPES)
3893 d04aaa2f Iustin Pop
      self.op.beparams = self.beparams
3894 d04aaa2f Iustin Pop
3895 d04aaa2f Iustin Pop
    # extra hvparams
3896 d04aaa2f Iustin Pop
    self.hvparams = getattr(self.op, "hvparams", {})
3897 d04aaa2f Iustin Pop
    if self.hvparams:
3898 d04aaa2f Iustin Pop
      if not isinstance(self.hvparams, dict):
3899 d04aaa2f Iustin Pop
        raise errors.OpPrereqError("Invalid hvparams passed: %s, expected"
3900 5c983ee5 Iustin Pop
                                   " dict" % (type(self.hvparams), ),
3901 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
3902 d04aaa2f Iustin Pop
3903 d04aaa2f Iustin Pop
      # check hypervisor parameter syntax (locally)
3904 d04aaa2f Iustin Pop
      cluster = self.cfg.GetClusterInfo()
3905 d04aaa2f Iustin Pop
      utils.ForceDictType(self.hvparams, constants.HVS_PARAMETER_TYPES)
3906 abe609b2 Guido Trotter
      filled_hvp = objects.FillDict(cluster.hvparams[instance.hypervisor],
3907 d04aaa2f Iustin Pop
                                    instance.hvparams)
3908 d04aaa2f Iustin Pop
      filled_hvp.update(self.hvparams)
3909 d04aaa2f Iustin Pop
      hv_type = hypervisor.GetHypervisor(instance.hypervisor)
3910 d04aaa2f Iustin Pop
      hv_type.CheckParameterSyntax(filled_hvp)
3911 d04aaa2f Iustin Pop
      _CheckHVParams(self, instance.all_nodes, instance.hypervisor, filled_hvp)
3912 d04aaa2f Iustin Pop
      self.op.hvparams = self.hvparams
3913 d04aaa2f Iustin Pop
3914 7527a8a4 Iustin Pop
    _CheckNodeOnline(self, instance.primary_node)
3915 7527a8a4 Iustin Pop
3916 338e51e8 Iustin Pop
    bep = self.cfg.GetClusterInfo().FillBE(instance)
3917 5bbd3f7f Michael Hanselmann
    # check bridges existence
3918 b9bddb6b Iustin Pop
    _CheckInstanceBridgesExist(self, instance)
3919 a8083063 Iustin Pop
3920 f1926756 Guido Trotter
    remote_info = self.rpc.call_instance_info(instance.primary_node,
3921 f1926756 Guido Trotter
                                              instance.name,
3922 f1926756 Guido Trotter
                                              instance.hypervisor)
3923 4c4e4e1e Iustin Pop
    remote_info.Raise("Error checking node %s" % instance.primary_node,
3924 045dd6d9 Iustin Pop
                      prereq=True, ecode=errors.ECODE_ENVIRON)
3925 7ad1af4a Iustin Pop
    if not remote_info.payload: # not running already
3926 f1926756 Guido Trotter
      _CheckNodeFreeMemory(self, instance.primary_node,
3927 f1926756 Guido Trotter
                           "starting instance %s" % instance.name,
3928 f1926756 Guido Trotter
                           bep[constants.BE_MEMORY], instance.hypervisor)
3929 d4f16fd9 Iustin Pop
3930 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
3931 a8083063 Iustin Pop
    """Start the instance.
3932 a8083063 Iustin Pop

3933 a8083063 Iustin Pop
    """
3934 a8083063 Iustin Pop
    instance = self.instance
3935 a8083063 Iustin Pop
    force = self.op.force
3936 a8083063 Iustin Pop
3937 fe482621 Iustin Pop
    self.cfg.MarkInstanceUp(instance.name)
3938 fe482621 Iustin Pop
3939 a8083063 Iustin Pop
    node_current = instance.primary_node
3940 a8083063 Iustin Pop
3941 b9bddb6b Iustin Pop
    _StartInstanceDisks(self, instance, force)
3942 a8083063 Iustin Pop
3943 d04aaa2f Iustin Pop
    result = self.rpc.call_instance_start(node_current, instance,
3944 d04aaa2f Iustin Pop
                                          self.hvparams, self.beparams)
3945 4c4e4e1e Iustin Pop
    msg = result.fail_msg
3946 dd279568 Iustin Pop
    if msg:
3947 b9bddb6b Iustin Pop
      _ShutdownInstanceDisks(self, instance)
3948 dd279568 Iustin Pop
      raise errors.OpExecError("Could not start instance: %s" % msg)
3949 a8083063 Iustin Pop
3950 a8083063 Iustin Pop
3951 bf6929a2 Alexander Schreiber
class LURebootInstance(LogicalUnit):
3952 bf6929a2 Alexander Schreiber
  """Reboot an instance.
3953 bf6929a2 Alexander Schreiber

3954 bf6929a2 Alexander Schreiber
  """
3955 bf6929a2 Alexander Schreiber
  HPATH = "instance-reboot"
3956 bf6929a2 Alexander Schreiber
  HTYPE = constants.HTYPE_INSTANCE
3957 bf6929a2 Alexander Schreiber
  _OP_REQP = ["instance_name", "ignore_secondaries", "reboot_type"]
3958 e873317a Guido Trotter
  REQ_BGL = False
3959 e873317a Guido Trotter
3960 17c3f802 Guido Trotter
  def CheckArguments(self):
3961 17c3f802 Guido Trotter
    """Check the arguments.
3962 17c3f802 Guido Trotter

3963 17c3f802 Guido Trotter
    """
3964 17c3f802 Guido Trotter
    self.shutdown_timeout = getattr(self.op, "shutdown_timeout",
3965 17c3f802 Guido Trotter
                                    constants.DEFAULT_SHUTDOWN_TIMEOUT)
3966 17c3f802 Guido Trotter
3967 e873317a Guido Trotter
  def ExpandNames(self):
3968 0fcc5db3 Guido Trotter
    if self.op.reboot_type not in [constants.INSTANCE_REBOOT_SOFT,
3969 0fcc5db3 Guido Trotter
                                   constants.INSTANCE_REBOOT_HARD,
3970 0fcc5db3 Guido Trotter
                                   constants.INSTANCE_REBOOT_FULL]:
3971 0fcc5db3 Guido Trotter
      raise errors.ParameterError("reboot type not in [%s, %s, %s]" %
3972 0fcc5db3 Guido Trotter
                                  (constants.INSTANCE_REBOOT_SOFT,
3973 0fcc5db3 Guido Trotter
                                   constants.INSTANCE_REBOOT_HARD,
3974 0fcc5db3 Guido Trotter
                                   constants.INSTANCE_REBOOT_FULL))
3975 e873317a Guido Trotter
    self._ExpandAndLockInstance()
3976 bf6929a2 Alexander Schreiber
3977 bf6929a2 Alexander Schreiber
  def BuildHooksEnv(self):
3978 bf6929a2 Alexander Schreiber
    """Build hooks env.
3979 bf6929a2 Alexander Schreiber

3980 bf6929a2 Alexander Schreiber
    This runs on master, primary and secondary nodes of the instance.
3981 bf6929a2 Alexander Schreiber

3982 bf6929a2 Alexander Schreiber
    """
3983 bf6929a2 Alexander Schreiber
    env = {
3984 bf6929a2 Alexander Schreiber
      "IGNORE_SECONDARIES": self.op.ignore_secondaries,
3985 2c2690c9 Iustin Pop
      "REBOOT_TYPE": self.op.reboot_type,
3986 17c3f802 Guido Trotter
      "SHUTDOWN_TIMEOUT": self.shutdown_timeout,
3987 bf6929a2 Alexander Schreiber
      }
3988 338e51e8 Iustin Pop
    env.update(_BuildInstanceHookEnvByObject(self, self.instance))
3989 6b12959c Iustin Pop
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
3990 bf6929a2 Alexander Schreiber
    return env, nl, nl
3991 bf6929a2 Alexander Schreiber
3992 bf6929a2 Alexander Schreiber
  def CheckPrereq(self):
3993 bf6929a2 Alexander Schreiber
    """Check prerequisites.
3994 bf6929a2 Alexander Schreiber

3995 bf6929a2 Alexander Schreiber
    This checks that the instance is in the cluster.
3996 bf6929a2 Alexander Schreiber

3997 bf6929a2 Alexander Schreiber
    """
3998 e873317a Guido Trotter
    self.instance = instance = self.cfg.GetInstanceInfo(self.op.instance_name)
3999 e873317a Guido Trotter
    assert self.instance is not None, \
4000 e873317a Guido Trotter
      "Cannot retrieve locked instance %s" % self.op.instance_name
4001 bf6929a2 Alexander Schreiber
4002 7527a8a4 Iustin Pop
    _CheckNodeOnline(self, instance.primary_node)
4003 7527a8a4 Iustin Pop
4004 5bbd3f7f Michael Hanselmann
    # check bridges existence
4005 b9bddb6b Iustin Pop
    _CheckInstanceBridgesExist(self, instance)
4006 bf6929a2 Alexander Schreiber
4007 bf6929a2 Alexander Schreiber
  def Exec(self, feedback_fn):
4008 bf6929a2 Alexander Schreiber
    """Reboot the instance.
4009 bf6929a2 Alexander Schreiber

4010 bf6929a2 Alexander Schreiber
    """
4011 bf6929a2 Alexander Schreiber
    instance = self.instance
4012 bf6929a2 Alexander Schreiber
    ignore_secondaries = self.op.ignore_secondaries
4013 bf6929a2 Alexander Schreiber
    reboot_type = self.op.reboot_type
4014 bf6929a2 Alexander Schreiber
4015 bf6929a2 Alexander Schreiber
    node_current = instance.primary_node
4016 bf6929a2 Alexander Schreiber
4017 bf6929a2 Alexander Schreiber
    if reboot_type in [constants.INSTANCE_REBOOT_SOFT,
4018 bf6929a2 Alexander Schreiber
                       constants.INSTANCE_REBOOT_HARD]:
4019 ae48ac32 Iustin Pop
      for disk in instance.disks:
4020 ae48ac32 Iustin Pop
        self.cfg.SetDiskID(disk, node_current)
4021 781de953 Iustin Pop
      result = self.rpc.call_instance_reboot(node_current, instance,
4022 17c3f802 Guido Trotter
                                             reboot_type,
4023 17c3f802 Guido Trotter
                                             self.shutdown_timeout)
4024 4c4e4e1e Iustin Pop
      result.Raise("Could not reboot instance")
4025 bf6929a2 Alexander Schreiber
    else:
4026 17c3f802 Guido Trotter
      result = self.rpc.call_instance_shutdown(node_current, instance,
4027 17c3f802 Guido Trotter
                                               self.shutdown_timeout)
4028 4c4e4e1e Iustin Pop
      result.Raise("Could not shutdown instance for full reboot")
4029 b9bddb6b Iustin Pop
      _ShutdownInstanceDisks(self, instance)
4030 b9bddb6b Iustin Pop
      _StartInstanceDisks(self, instance, ignore_secondaries)
4031 0eca8e0c Iustin Pop
      result = self.rpc.call_instance_start(node_current, instance, None, None)
4032 4c4e4e1e Iustin Pop
      msg = result.fail_msg
4033 dd279568 Iustin Pop
      if msg:
4034 b9bddb6b Iustin Pop
        _ShutdownInstanceDisks(self, instance)
4035 dd279568 Iustin Pop
        raise errors.OpExecError("Could not start instance for"
4036 dd279568 Iustin Pop
                                 " full reboot: %s" % msg)
4037 bf6929a2 Alexander Schreiber
4038 bf6929a2 Alexander Schreiber
    self.cfg.MarkInstanceUp(instance.name)
4039 bf6929a2 Alexander Schreiber
4040 bf6929a2 Alexander Schreiber
4041 a8083063 Iustin Pop
class LUShutdownInstance(LogicalUnit):
4042 a8083063 Iustin Pop
  """Shutdown an instance.
4043 a8083063 Iustin Pop

4044 a8083063 Iustin Pop
  """
4045 a8083063 Iustin Pop
  HPATH = "instance-stop"
4046 a8083063 Iustin Pop
  HTYPE = constants.HTYPE_INSTANCE
4047 a8083063 Iustin Pop
  _OP_REQP = ["instance_name"]
4048 e873317a Guido Trotter
  REQ_BGL = False
4049 e873317a Guido Trotter
4050 6263189c Guido Trotter
  def CheckArguments(self):
4051 6263189c Guido Trotter
    """Check the arguments.
4052 6263189c Guido Trotter

4053 6263189c Guido Trotter
    """
4054 6263189c Guido Trotter
    self.timeout = getattr(self.op, "timeout",
4055 6263189c Guido Trotter
                           constants.DEFAULT_SHUTDOWN_TIMEOUT)
4056 6263189c Guido Trotter
4057 e873317a Guido Trotter
  def ExpandNames(self):
4058 e873317a Guido Trotter
    self._ExpandAndLockInstance()
4059 a8083063 Iustin Pop
4060 a8083063 Iustin Pop
  def BuildHooksEnv(self):
4061 a8083063 Iustin Pop
    """Build hooks env.
4062 a8083063 Iustin Pop

4063 a8083063 Iustin Pop
    This runs on master, primary and secondary nodes of the instance.
4064 a8083063 Iustin Pop

4065 a8083063 Iustin Pop
    """
4066 338e51e8 Iustin Pop
    env = _BuildInstanceHookEnvByObject(self, self.instance)
4067 6263189c Guido Trotter
    env["TIMEOUT"] = self.timeout
4068 6b12959c Iustin Pop
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
4069 a8083063 Iustin Pop
    return env, nl, nl
4070 a8083063 Iustin Pop
4071 a8083063 Iustin Pop
  def CheckPrereq(self):
4072 a8083063 Iustin Pop
    """Check prerequisites.
4073 a8083063 Iustin Pop

4074 a8083063 Iustin Pop
    This checks that the instance is in the cluster.
4075 a8083063 Iustin Pop

4076 a8083063 Iustin Pop
    """
4077 e873317a Guido Trotter
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
4078 e873317a Guido Trotter
    assert self.instance is not None, \
4079 e873317a Guido Trotter
      "Cannot retrieve locked instance %s" % self.op.instance_name
4080 43017d26 Iustin Pop
    _CheckNodeOnline(self, self.instance.primary_node)
4081 a8083063 Iustin Pop
4082 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
4083 a8083063 Iustin Pop
    """Shutdown the instance.
4084 a8083063 Iustin Pop

4085 a8083063 Iustin Pop
    """
4086 a8083063 Iustin Pop
    instance = self.instance
4087 a8083063 Iustin Pop
    node_current = instance.primary_node
4088 6263189c Guido Trotter
    timeout = self.timeout
4089 fe482621 Iustin Pop
    self.cfg.MarkInstanceDown(instance.name)
4090 6263189c Guido Trotter
    result = self.rpc.call_instance_shutdown(node_current, instance, timeout)
4091 4c4e4e1e Iustin Pop
    msg = result.fail_msg
4092 1fae010f Iustin Pop
    if msg:
4093 1fae010f Iustin Pop
      self.proc.LogWarning("Could not shutdown instance: %s" % msg)
4094 a8083063 Iustin Pop
4095 b9bddb6b Iustin Pop
    _ShutdownInstanceDisks(self, instance)
4096 a8083063 Iustin Pop
4097 a8083063 Iustin Pop
4098 fe7b0351 Michael Hanselmann
class LUReinstallInstance(LogicalUnit):
4099 fe7b0351 Michael Hanselmann
  """Reinstall an instance.
4100 fe7b0351 Michael Hanselmann

4101 fe7b0351 Michael Hanselmann
  """
4102 fe7b0351 Michael Hanselmann
  HPATH = "instance-reinstall"
4103 fe7b0351 Michael Hanselmann
  HTYPE = constants.HTYPE_INSTANCE
4104 fe7b0351 Michael Hanselmann
  _OP_REQP = ["instance_name"]
4105 4e0b4d2d Guido Trotter
  REQ_BGL = False
4106 4e0b4d2d Guido Trotter
4107 4e0b4d2d Guido Trotter
  def ExpandNames(self):
4108 4e0b4d2d Guido Trotter
    self._ExpandAndLockInstance()
4109 fe7b0351 Michael Hanselmann
4110 fe7b0351 Michael Hanselmann
  def BuildHooksEnv(self):
4111 fe7b0351 Michael Hanselmann
    """Build hooks env.
4112 fe7b0351 Michael Hanselmann

4113 fe7b0351 Michael Hanselmann
    This runs on master, primary and secondary nodes of the instance.
4114 fe7b0351 Michael Hanselmann

4115 fe7b0351 Michael Hanselmann
    """
4116 338e51e8 Iustin Pop
    env = _BuildInstanceHookEnvByObject(self, self.instance)
4117 6b12959c Iustin Pop
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
4118 fe7b0351 Michael Hanselmann
    return env, nl, nl
4119 fe7b0351 Michael Hanselmann
4120 fe7b0351 Michael Hanselmann
  def CheckPrereq(self):
4121 fe7b0351 Michael Hanselmann
    """Check prerequisites.
4122 fe7b0351 Michael Hanselmann

4123 fe7b0351 Michael Hanselmann
    This checks that the instance is in the cluster and is not running.
4124 fe7b0351 Michael Hanselmann

4125 fe7b0351 Michael Hanselmann
    """
4126 4e0b4d2d Guido Trotter
    instance = self.cfg.GetInstanceInfo(self.op.instance_name)
4127 4e0b4d2d Guido Trotter
    assert instance is not None, \
4128 4e0b4d2d Guido Trotter
      "Cannot retrieve locked instance %s" % self.op.instance_name
4129 7527a8a4 Iustin Pop
    _CheckNodeOnline(self, instance.primary_node)
4130 4e0b4d2d Guido Trotter
4131 fe7b0351 Michael Hanselmann
    if instance.disk_template == constants.DT_DISKLESS:
4132 3ecf6786 Iustin Pop
      raise errors.OpPrereqError("Instance '%s' has no disks" %
4133 5c983ee5 Iustin Pop
                                 self.op.instance_name,
4134 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
4135 31624382 Iustin Pop
    _CheckInstanceDown(self, instance, "cannot reinstall")
4136 d0834de3 Michael Hanselmann
4137 d0834de3 Michael Hanselmann
    self.op.os_type = getattr(self.op, "os_type", None)
4138 f2c05717 Guido Trotter
    self.op.force_variant = getattr(self.op, "force_variant", False)
4139 d0834de3 Michael Hanselmann
    if self.op.os_type is not None:
4140 d0834de3 Michael Hanselmann
      # OS verification
4141 cf26a87a Iustin Pop
      pnode = _ExpandNodeName(self.cfg, instance.primary_node)
4142 cf26a87a Iustin Pop
      result = self.rpc.call_os_get(pnode, self.op.os_type)
4143 4c4e4e1e Iustin Pop
      result.Raise("OS '%s' not in supported OS list for primary node %s" %
4144 cf26a87a Iustin Pop
                   (self.op.os_type, pnode),
4145 045dd6d9 Iustin Pop
                   prereq=True, ecode=errors.ECODE_INVAL)
4146 f2c05717 Guido Trotter
      if not self.op.force_variant:
4147 f2c05717 Guido Trotter
        _CheckOSVariant(result.payload, self.op.os_type)
4148 d0834de3 Michael Hanselmann
4149 fe7b0351 Michael Hanselmann
    self.instance = instance
4150 fe7b0351 Michael Hanselmann
4151 fe7b0351 Michael Hanselmann
  def Exec(self, feedback_fn):
4152 fe7b0351 Michael Hanselmann
    """Reinstall the instance.
4153 fe7b0351 Michael Hanselmann

4154 fe7b0351 Michael Hanselmann
    """
4155 fe7b0351 Michael Hanselmann
    inst = self.instance
4156 fe7b0351 Michael Hanselmann
4157 d0834de3 Michael Hanselmann
    if self.op.os_type is not None:
4158 d0834de3 Michael Hanselmann
      feedback_fn("Changing OS to '%s'..." % self.op.os_type)
4159 d0834de3 Michael Hanselmann
      inst.os = self.op.os_type
4160 a4eae71f Michael Hanselmann
      self.cfg.Update(inst, feedback_fn)
4161 d0834de3 Michael Hanselmann
4162 b9bddb6b Iustin Pop
    _StartInstanceDisks(self, inst, None)
4163 fe7b0351 Michael Hanselmann
    try:
4164 fe7b0351 Michael Hanselmann
      feedback_fn("Running the instance OS create scripts...")
4165 4a0e011f Iustin Pop
      # FIXME: pass debug option from opcode to backend
4166 dd713605 Iustin Pop
      result = self.rpc.call_instance_os_add(inst.primary_node, inst, True,
4167 dd713605 Iustin Pop
                                             self.op.debug_level)
4168 4c4e4e1e Iustin Pop
      result.Raise("Could not install OS for instance %s on node %s" %
4169 4c4e4e1e Iustin Pop
                   (inst.name, inst.primary_node))
4170 fe7b0351 Michael Hanselmann
    finally:
4171 b9bddb6b Iustin Pop
      _ShutdownInstanceDisks(self, inst)
4172 fe7b0351 Michael Hanselmann
4173 fe7b0351 Michael Hanselmann
4174 bd315bfa Iustin Pop
class LURecreateInstanceDisks(LogicalUnit):
4175 bd315bfa Iustin Pop
  """Recreate an instance's missing disks.
4176 bd315bfa Iustin Pop

4177 bd315bfa Iustin Pop
  """
4178 bd315bfa Iustin Pop
  HPATH = "instance-recreate-disks"
4179 bd315bfa Iustin Pop
  HTYPE = constants.HTYPE_INSTANCE
4180 bd315bfa Iustin Pop
  _OP_REQP = ["instance_name", "disks"]
4181 bd315bfa Iustin Pop
  REQ_BGL = False
4182 bd315bfa Iustin Pop
4183 bd315bfa Iustin Pop
  def CheckArguments(self):
4184 bd315bfa Iustin Pop
    """Check the arguments.
4185 bd315bfa Iustin Pop

4186 bd315bfa Iustin Pop
    """
4187 bd315bfa Iustin Pop
    if not isinstance(self.op.disks, list):
4188 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Invalid disks parameter", errors.ECODE_INVAL)
4189 bd315bfa Iustin Pop
    for item in self.op.disks:
4190 bd315bfa Iustin Pop
      if (not isinstance(item, int) or
4191 bd315bfa Iustin Pop
          item < 0):
4192 bd315bfa Iustin Pop
        raise errors.OpPrereqError("Invalid disk specification '%s'" %
4193 5c983ee5 Iustin Pop
                                   str(item), errors.ECODE_INVAL)
4194 bd315bfa Iustin Pop
4195 bd315bfa Iustin Pop
  def ExpandNames(self):
4196 bd315bfa Iustin Pop
    self._ExpandAndLockInstance()
4197 bd315bfa Iustin Pop
4198 bd315bfa Iustin Pop
  def BuildHooksEnv(self):
4199 bd315bfa Iustin Pop
    """Build hooks env.
4200 bd315bfa Iustin Pop

4201 bd315bfa Iustin Pop
    This runs on master, primary and secondary nodes of the instance.
4202 bd315bfa Iustin Pop

4203 bd315bfa Iustin Pop
    """
4204 bd315bfa Iustin Pop
    env = _BuildInstanceHookEnvByObject(self, self.instance)
4205 bd315bfa Iustin Pop
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
4206 bd315bfa Iustin Pop
    return env, nl, nl
4207 bd315bfa Iustin Pop
4208 bd315bfa Iustin Pop
  def CheckPrereq(self):
4209 bd315bfa Iustin Pop
    """Check prerequisites.
4210 bd315bfa Iustin Pop

4211 bd315bfa Iustin Pop
    This checks that the instance is in the cluster and is not running.
4212 bd315bfa Iustin Pop

4213 bd315bfa Iustin Pop
    """
4214 bd315bfa Iustin Pop
    instance = self.cfg.GetInstanceInfo(self.op.instance_name)
4215 bd315bfa Iustin Pop
    assert instance is not None, \
4216 bd315bfa Iustin Pop
      "Cannot retrieve locked instance %s" % self.op.instance_name
4217 bd315bfa Iustin Pop
    _CheckNodeOnline(self, instance.primary_node)
4218 bd315bfa Iustin Pop
4219 bd315bfa Iustin Pop
    if instance.disk_template == constants.DT_DISKLESS:
4220 bd315bfa Iustin Pop
      raise errors.OpPrereqError("Instance '%s' has no disks" %
4221 5c983ee5 Iustin Pop
                                 self.op.instance_name, errors.ECODE_INVAL)
4222 31624382 Iustin Pop
    _CheckInstanceDown(self, instance, "cannot recreate disks")
4223 bd315bfa Iustin Pop
4224 bd315bfa Iustin Pop
    if not self.op.disks:
4225 bd315bfa Iustin Pop
      self.op.disks = range(len(instance.disks))
4226 bd315bfa Iustin Pop
    else:
4227 bd315bfa Iustin Pop
      for idx in self.op.disks:
4228 bd315bfa Iustin Pop
        if idx >= len(instance.disks):
4229 5c983ee5 Iustin Pop
          raise errors.OpPrereqError("Invalid disk index passed '%s'" % idx,
4230 5c983ee5 Iustin Pop
                                     errors.ECODE_INVAL)
4231 bd315bfa Iustin Pop
4232 bd315bfa Iustin Pop
    self.instance = instance
4233 bd315bfa Iustin Pop
4234 bd315bfa Iustin Pop
  def Exec(self, feedback_fn):
4235 bd315bfa Iustin Pop
    """Recreate the disks.
4236 bd315bfa Iustin Pop

4237 bd315bfa Iustin Pop
    """
4238 bd315bfa Iustin Pop
    to_skip = []
4239 1122eb25 Iustin Pop
    for idx, _ in enumerate(self.instance.disks):
4240 bd315bfa Iustin Pop
      if idx not in self.op.disks: # disk idx has not been passed in
4241 bd315bfa Iustin Pop
        to_skip.append(idx)
4242 bd315bfa Iustin Pop
        continue
4243 bd315bfa Iustin Pop
4244 bd315bfa Iustin Pop
    _CreateDisks(self, self.instance, to_skip=to_skip)
4245 bd315bfa Iustin Pop
4246 bd315bfa Iustin Pop
4247 decd5f45 Iustin Pop
class LURenameInstance(LogicalUnit):
4248 decd5f45 Iustin Pop
  """Rename an instance.
4249 decd5f45 Iustin Pop

4250 decd5f45 Iustin Pop
  """
4251 decd5f45 Iustin Pop
  HPATH = "instance-rename"
4252 decd5f45 Iustin Pop
  HTYPE = constants.HTYPE_INSTANCE
4253 decd5f45 Iustin Pop
  _OP_REQP = ["instance_name", "new_name"]
4254 decd5f45 Iustin Pop
4255 decd5f45 Iustin Pop
  def BuildHooksEnv(self):
4256 decd5f45 Iustin Pop
    """Build hooks env.
4257 decd5f45 Iustin Pop

4258 decd5f45 Iustin Pop
    This runs on master, primary and secondary nodes of the instance.
4259 decd5f45 Iustin Pop

4260 decd5f45 Iustin Pop
    """
4261 338e51e8 Iustin Pop
    env = _BuildInstanceHookEnvByObject(self, self.instance)
4262 decd5f45 Iustin Pop
    env["INSTANCE_NEW_NAME"] = self.op.new_name
4263 6b12959c Iustin Pop
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
4264 decd5f45 Iustin Pop
    return env, nl, nl
4265 decd5f45 Iustin Pop
4266 decd5f45 Iustin Pop
  def CheckPrereq(self):
4267 decd5f45 Iustin Pop
    """Check prerequisites.
4268 decd5f45 Iustin Pop

4269 decd5f45 Iustin Pop
    This checks that the instance is in the cluster and is not running.
4270 decd5f45 Iustin Pop

4271 decd5f45 Iustin Pop
    """
4272 cf26a87a Iustin Pop
    self.op.instance_name = _ExpandInstanceName(self.cfg,
4273 cf26a87a Iustin Pop
                                                self.op.instance_name)
4274 cf26a87a Iustin Pop
    instance = self.cfg.GetInstanceInfo(self.op.instance_name)
4275 cf26a87a Iustin Pop
    assert instance is not None
4276 7527a8a4 Iustin Pop
    _CheckNodeOnline(self, instance.primary_node)
4277 31624382 Iustin Pop
    _CheckInstanceDown(self, instance, "cannot rename")
4278 decd5f45 Iustin Pop
    self.instance = instance
4279 decd5f45 Iustin Pop
4280 decd5f45 Iustin Pop
    # new name verification
4281 104f4ca1 Iustin Pop
    name_info = utils.GetHostInfo(self.op.new_name)
4282 decd5f45 Iustin Pop
4283 89e1fc26 Iustin Pop
    self.op.new_name = new_name = name_info.name
4284 7bde3275 Guido Trotter
    instance_list = self.cfg.GetInstanceList()
4285 7bde3275 Guido Trotter
    if new_name in instance_list:
4286 7bde3275 Guido Trotter
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
4287 5c983ee5 Iustin Pop
                                 new_name, errors.ECODE_EXISTS)
4288 7bde3275 Guido Trotter
4289 decd5f45 Iustin Pop
    if not getattr(self.op, "ignore_ip", False):
4290 937f983d Guido Trotter
      if utils.TcpPing(name_info.ip, constants.DEFAULT_NODED_PORT):
4291 decd5f45 Iustin Pop
        raise errors.OpPrereqError("IP %s of instance %s already in use" %
4292 5c983ee5 Iustin Pop
                                   (name_info.ip, new_name),
4293 5c983ee5 Iustin Pop
                                   errors.ECODE_NOTUNIQUE)
4294 decd5f45 Iustin Pop
4295 decd5f45 Iustin Pop
4296 decd5f45 Iustin Pop
  def Exec(self, feedback_fn):
4297 decd5f45 Iustin Pop
    """Reinstall the instance.
4298 decd5f45 Iustin Pop

4299 decd5f45 Iustin Pop
    """
4300 decd5f45 Iustin Pop
    inst = self.instance
4301 decd5f45 Iustin Pop
    old_name = inst.name
4302 decd5f45 Iustin Pop
4303 b23c4333 Manuel Franceschini
    if inst.disk_template == constants.DT_FILE:
4304 b23c4333 Manuel Franceschini
      old_file_storage_dir = os.path.dirname(inst.disks[0].logical_id[1])
4305 b23c4333 Manuel Franceschini
4306 decd5f45 Iustin Pop
    self.cfg.RenameInstance(inst.name, self.op.new_name)
4307 74b5913f Guido Trotter
    # Change the instance lock. This is definitely safe while we hold the BGL
4308 cb4e8387 Iustin Pop
    self.context.glm.remove(locking.LEVEL_INSTANCE, old_name)
4309 74b5913f Guido Trotter
    self.context.glm.add(locking.LEVEL_INSTANCE, self.op.new_name)
4310 decd5f45 Iustin Pop
4311 decd5f45 Iustin Pop
    # re-read the instance from the configuration after rename
4312 decd5f45 Iustin Pop
    inst = self.cfg.GetInstanceInfo(self.op.new_name)
4313 decd5f45 Iustin Pop
4314 b23c4333 Manuel Franceschini
    if inst.disk_template == constants.DT_FILE:
4315 b23c4333 Manuel Franceschini
      new_file_storage_dir = os.path.dirname(inst.disks[0].logical_id[1])
4316 72737a7f Iustin Pop
      result = self.rpc.call_file_storage_dir_rename(inst.primary_node,
4317 72737a7f Iustin Pop
                                                     old_file_storage_dir,
4318 72737a7f Iustin Pop
                                                     new_file_storage_dir)
4319 4c4e4e1e Iustin Pop
      result.Raise("Could not rename on node %s directory '%s' to '%s'"
4320 4c4e4e1e Iustin Pop
                   " (but the instance has been renamed in Ganeti)" %
4321 4c4e4e1e Iustin Pop
                   (inst.primary_node, old_file_storage_dir,
4322 4c4e4e1e Iustin Pop
                    new_file_storage_dir))
4323 b23c4333 Manuel Franceschini
4324 b9bddb6b Iustin Pop
    _StartInstanceDisks(self, inst, None)
4325 decd5f45 Iustin Pop
    try:
4326 781de953 Iustin Pop
      result = self.rpc.call_instance_run_rename(inst.primary_node, inst,
4327 dd713605 Iustin Pop
                                                 old_name, self.op.debug_level)
4328 4c4e4e1e Iustin Pop
      msg = result.fail_msg
4329 96841384 Iustin Pop
      if msg:
4330 6291574d Alexander Schreiber
        msg = ("Could not run OS rename script for instance %s on node %s"
4331 96841384 Iustin Pop
               " (but the instance has been renamed in Ganeti): %s" %
4332 96841384 Iustin Pop
               (inst.name, inst.primary_node, msg))
4333 86d9d3bb Iustin Pop
        self.proc.LogWarning(msg)
4334 decd5f45 Iustin Pop
    finally:
4335 b9bddb6b Iustin Pop
      _ShutdownInstanceDisks(self, inst)
4336 decd5f45 Iustin Pop
4337 decd5f45 Iustin Pop
4338 a8083063 Iustin Pop
class LURemoveInstance(LogicalUnit):
4339 a8083063 Iustin Pop
  """Remove an instance.
4340 a8083063 Iustin Pop

4341 a8083063 Iustin Pop
  """
4342 a8083063 Iustin Pop
  HPATH = "instance-remove"
4343 a8083063 Iustin Pop
  HTYPE = constants.HTYPE_INSTANCE
4344 5c54b832 Iustin Pop
  _OP_REQP = ["instance_name", "ignore_failures"]
4345 cf472233 Guido Trotter
  REQ_BGL = False
4346 cf472233 Guido Trotter
4347 17c3f802 Guido Trotter
  def CheckArguments(self):
4348 17c3f802 Guido Trotter
    """Check the arguments.
4349 17c3f802 Guido Trotter

4350 17c3f802 Guido Trotter
    """
4351 17c3f802 Guido Trotter
    self.shutdown_timeout = getattr(self.op, "shutdown_timeout",
4352 17c3f802 Guido Trotter
                                    constants.DEFAULT_SHUTDOWN_TIMEOUT)
4353 17c3f802 Guido Trotter
4354 cf472233 Guido Trotter
  def ExpandNames(self):
4355 cf472233 Guido Trotter
    self._ExpandAndLockInstance()
4356 cf472233 Guido Trotter
    self.needed_locks[locking.LEVEL_NODE] = []
4357 cf472233 Guido Trotter
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
4358 cf472233 Guido Trotter
4359 cf472233 Guido Trotter
  def DeclareLocks(self, level):
4360 cf472233 Guido Trotter
    if level == locking.LEVEL_NODE:
4361 cf472233 Guido Trotter
      self._LockInstancesNodes()
4362 a8083063 Iustin Pop
4363 a8083063 Iustin Pop
  def BuildHooksEnv(self):
4364 a8083063 Iustin Pop
    """Build hooks env.
4365 a8083063 Iustin Pop

4366 a8083063 Iustin Pop
    This runs on master, primary and secondary nodes of the instance.
4367 a8083063 Iustin Pop

4368 a8083063 Iustin Pop
    """
4369 338e51e8 Iustin Pop
    env = _BuildInstanceHookEnvByObject(self, self.instance)
4370 17c3f802 Guido Trotter
    env["SHUTDOWN_TIMEOUT"] = self.shutdown_timeout
4371 d6a02168 Michael Hanselmann
    nl = [self.cfg.GetMasterNode()]
4372 abd8e836 Iustin Pop
    nl_post = list(self.instance.all_nodes) + nl
4373 abd8e836 Iustin Pop
    return env, nl, nl_post
4374 a8083063 Iustin Pop
4375 a8083063 Iustin Pop
  def CheckPrereq(self):
4376 a8083063 Iustin Pop
    """Check prerequisites.
4377 a8083063 Iustin Pop

4378 a8083063 Iustin Pop
    This checks that the instance is in the cluster.
4379 a8083063 Iustin Pop

4380 a8083063 Iustin Pop
    """
4381 cf472233 Guido Trotter
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
4382 cf472233 Guido Trotter
    assert self.instance is not None, \
4383 cf472233 Guido Trotter
      "Cannot retrieve locked instance %s" % self.op.instance_name
4384 a8083063 Iustin Pop
4385 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
4386 a8083063 Iustin Pop
    """Remove the instance.
4387 a8083063 Iustin Pop

4388 a8083063 Iustin Pop
    """
4389 a8083063 Iustin Pop
    instance = self.instance
4390 9a4f63d1 Iustin Pop
    logging.info("Shutting down instance %s on node %s",
4391 9a4f63d1 Iustin Pop
                 instance.name, instance.primary_node)
4392 a8083063 Iustin Pop
4393 17c3f802 Guido Trotter
    result = self.rpc.call_instance_shutdown(instance.primary_node, instance,
4394 17c3f802 Guido Trotter
                                             self.shutdown_timeout)
4395 4c4e4e1e Iustin Pop
    msg = result.fail_msg
4396 1fae010f Iustin Pop
    if msg:
4397 1d67656e Iustin Pop
      if self.op.ignore_failures:
4398 1fae010f Iustin Pop
        feedback_fn("Warning: can't shutdown instance: %s" % msg)
4399 1d67656e Iustin Pop
      else:
4400 1fae010f Iustin Pop
        raise errors.OpExecError("Could not shutdown instance %s on"
4401 1fae010f Iustin Pop
                                 " node %s: %s" %
4402 1fae010f Iustin Pop
                                 (instance.name, instance.primary_node, msg))
4403 a8083063 Iustin Pop
4404 9a4f63d1 Iustin Pop
    logging.info("Removing block devices for instance %s", instance.name)
4405 a8083063 Iustin Pop
4406 b9bddb6b Iustin Pop
    if not _RemoveDisks(self, instance):
4407 1d67656e Iustin Pop
      if self.op.ignore_failures:
4408 1d67656e Iustin Pop
        feedback_fn("Warning: can't remove instance's disks")
4409 1d67656e Iustin Pop
      else:
4410 1d67656e Iustin Pop
        raise errors.OpExecError("Can't remove instance's disks")
4411 a8083063 Iustin Pop
4412 9a4f63d1 Iustin Pop
    logging.info("Removing instance %s out of cluster config", instance.name)
4413 a8083063 Iustin Pop
4414 a8083063 Iustin Pop
    self.cfg.RemoveInstance(instance.name)
4415 cf472233 Guido Trotter
    self.remove_locks[locking.LEVEL_INSTANCE] = instance.name
4416 a8083063 Iustin Pop
4417 a8083063 Iustin Pop
4418 a8083063 Iustin Pop
class LUQueryInstances(NoHooksLU):
4419 a8083063 Iustin Pop
  """Logical unit for querying instances.
4420 a8083063 Iustin Pop

4421 a8083063 Iustin Pop
  """
4422 7260cfbe Iustin Pop
  # pylint: disable-msg=W0142
4423 ec79568d Iustin Pop
  _OP_REQP = ["output_fields", "names", "use_locking"]
4424 7eb9d8f7 Guido Trotter
  REQ_BGL = False
4425 19bed813 Iustin Pop
  _SIMPLE_FIELDS = ["name", "os", "network_port", "hypervisor",
4426 19bed813 Iustin Pop
                    "serial_no", "ctime", "mtime", "uuid"]
4427 a2d2e1a7 Iustin Pop
  _FIELDS_STATIC = utils.FieldSet(*["name", "os", "pnode", "snodes",
4428 5b460366 Iustin Pop
                                    "admin_state",
4429 a2d2e1a7 Iustin Pop
                                    "disk_template", "ip", "mac", "bridge",
4430 638c6349 Guido Trotter
                                    "nic_mode", "nic_link",
4431 a2d2e1a7 Iustin Pop
                                    "sda_size", "sdb_size", "vcpus", "tags",
4432 a2d2e1a7 Iustin Pop
                                    "network_port", "beparams",
4433 8aec325c Iustin Pop
                                    r"(disk)\.(size)/([0-9]+)",
4434 8aec325c Iustin Pop
                                    r"(disk)\.(sizes)", "disk_usage",
4435 638c6349 Guido Trotter
                                    r"(nic)\.(mac|ip|mode|link)/([0-9]+)",
4436 638c6349 Guido Trotter
                                    r"(nic)\.(bridge)/([0-9]+)",
4437 638c6349 Guido Trotter
                                    r"(nic)\.(macs|ips|modes|links|bridges)",
4438 8aec325c Iustin Pop
                                    r"(disk|nic)\.(count)",
4439 19bed813 Iustin Pop
                                    "hvparams",
4440 19bed813 Iustin Pop
                                    ] + _SIMPLE_FIELDS +
4441 a2d2e1a7 Iustin Pop
                                  ["hv/%s" % name
4442 7736a5f2 Iustin Pop
                                   for name in constants.HVS_PARAMETERS
4443 7736a5f2 Iustin Pop
                                   if name not in constants.HVC_GLOBALS] +
4444 a2d2e1a7 Iustin Pop
                                  ["be/%s" % name
4445 a2d2e1a7 Iustin Pop
                                   for name in constants.BES_PARAMETERS])
4446 a2d2e1a7 Iustin Pop
  _FIELDS_DYNAMIC = utils.FieldSet("oper_state", "oper_ram", "status")
4447 31bf511f Iustin Pop
4448 a8083063 Iustin Pop
4449 7eb9d8f7 Guido Trotter
  def ExpandNames(self):
4450 31bf511f Iustin Pop
    _CheckOutputFields(static=self._FIELDS_STATIC,
4451 31bf511f Iustin Pop
                       dynamic=self._FIELDS_DYNAMIC,
4452 dcb93971 Michael Hanselmann
                       selected=self.op.output_fields)
4453 a8083063 Iustin Pop
4454 7eb9d8f7 Guido Trotter
    self.needed_locks = {}
4455 7eb9d8f7 Guido Trotter
    self.share_locks[locking.LEVEL_INSTANCE] = 1
4456 7eb9d8f7 Guido Trotter
    self.share_locks[locking.LEVEL_NODE] = 1
4457 7eb9d8f7 Guido Trotter
4458 57a2fb91 Iustin Pop
    if self.op.names:
4459 57a2fb91 Iustin Pop
      self.wanted = _GetWantedInstances(self, self.op.names)
4460 7eb9d8f7 Guido Trotter
    else:
4461 57a2fb91 Iustin Pop
      self.wanted = locking.ALL_SET
4462 7eb9d8f7 Guido Trotter
4463 ec79568d Iustin Pop
    self.do_node_query = self._FIELDS_STATIC.NonMatching(self.op.output_fields)
4464 ec79568d Iustin Pop
    self.do_locking = self.do_node_query and self.op.use_locking
4465 57a2fb91 Iustin Pop
    if self.do_locking:
4466 57a2fb91 Iustin Pop
      self.needed_locks[locking.LEVEL_INSTANCE] = self.wanted
4467 57a2fb91 Iustin Pop
      self.needed_locks[locking.LEVEL_NODE] = []
4468 57a2fb91 Iustin Pop
      self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
4469 7eb9d8f7 Guido Trotter
4470 7eb9d8f7 Guido Trotter
  def DeclareLocks(self, level):
4471 57a2fb91 Iustin Pop
    if level == locking.LEVEL_NODE and self.do_locking:
4472 7eb9d8f7 Guido Trotter
      self._LockInstancesNodes()
4473 7eb9d8f7 Guido Trotter
4474 7eb9d8f7 Guido Trotter
  def CheckPrereq(self):
4475 7eb9d8f7 Guido Trotter
    """Check prerequisites.
4476 7eb9d8f7 Guido Trotter

4477 7eb9d8f7 Guido Trotter
    """
4478 57a2fb91 Iustin Pop
    pass
4479 069dcc86 Iustin Pop
4480 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
4481 a8083063 Iustin Pop
    """Computes the list of nodes and their attributes.
4482 a8083063 Iustin Pop

4483 a8083063 Iustin Pop
    """
4484 7260cfbe Iustin Pop
    # pylint: disable-msg=R0912
4485 7260cfbe Iustin Pop
    # way too many branches here
4486 57a2fb91 Iustin Pop
    all_info = self.cfg.GetAllInstancesInfo()
4487 a7f5dc98 Iustin Pop
    if self.wanted == locking.ALL_SET:
4488 a7f5dc98 Iustin Pop
      # caller didn't specify instance names, so ordering is not important
4489 a7f5dc98 Iustin Pop
      if self.do_locking:
4490 a7f5dc98 Iustin Pop
        instance_names = self.acquired_locks[locking.LEVEL_INSTANCE]
4491 a7f5dc98 Iustin Pop
      else:
4492 a7f5dc98 Iustin Pop
        instance_names = all_info.keys()
4493 a7f5dc98 Iustin Pop
      instance_names = utils.NiceSort(instance_names)
4494 57a2fb91 Iustin Pop
    else:
4495 a7f5dc98 Iustin Pop
      # caller did specify names, so we must keep the ordering
4496 a7f5dc98 Iustin Pop
      if self.do_locking:
4497 a7f5dc98 Iustin Pop
        tgt_set = self.acquired_locks[locking.LEVEL_INSTANCE]
4498 a7f5dc98 Iustin Pop
      else:
4499 a7f5dc98 Iustin Pop
        tgt_set = all_info.keys()
4500 a7f5dc98 Iustin Pop
      missing = set(self.wanted).difference(tgt_set)
4501 a7f5dc98 Iustin Pop
      if missing:
4502 a7f5dc98 Iustin Pop
        raise errors.OpExecError("Some instances were removed before"
4503 a7f5dc98 Iustin Pop
                                 " retrieving their data: %s" % missing)
4504 a7f5dc98 Iustin Pop
      instance_names = self.wanted
4505 c1f1cbb2 Iustin Pop
4506 57a2fb91 Iustin Pop
    instance_list = [all_info[iname] for iname in instance_names]
4507 a8083063 Iustin Pop
4508 a8083063 Iustin Pop
    # begin data gathering
4509 a8083063 Iustin Pop
4510 a8083063 Iustin Pop
    nodes = frozenset([inst.primary_node for inst in instance_list])
4511 e69d05fd Iustin Pop
    hv_list = list(set([inst.hypervisor for inst in instance_list]))
4512 a8083063 Iustin Pop
4513 a8083063 Iustin Pop
    bad_nodes = []
4514 cbfc4681 Iustin Pop
    off_nodes = []
4515 ec79568d Iustin Pop
    if self.do_node_query:
4516 a8083063 Iustin Pop
      live_data = {}
4517 72737a7f Iustin Pop
      node_data = self.rpc.call_all_instances_info(nodes, hv_list)
4518 a8083063 Iustin Pop
      for name in nodes:
4519 a8083063 Iustin Pop
        result = node_data[name]
4520 cbfc4681 Iustin Pop
        if result.offline:
4521 cbfc4681 Iustin Pop
          # offline nodes will be in both lists
4522 cbfc4681 Iustin Pop
          off_nodes.append(name)
4523 3cebe102 Michael Hanselmann
        if result.fail_msg:
4524 a8083063 Iustin Pop
          bad_nodes.append(name)
4525 781de953 Iustin Pop
        else:
4526 2fa74ef4 Iustin Pop
          if result.payload:
4527 2fa74ef4 Iustin Pop
            live_data.update(result.payload)
4528 2fa74ef4 Iustin Pop
          # else no instance is alive
4529 a8083063 Iustin Pop
    else:
4530 a8083063 Iustin Pop
      live_data = dict([(name, {}) for name in instance_names])
4531 a8083063 Iustin Pop
4532 a8083063 Iustin Pop
    # end data gathering
4533 a8083063 Iustin Pop
4534 5018a335 Iustin Pop
    HVPREFIX = "hv/"
4535 338e51e8 Iustin Pop
    BEPREFIX = "be/"
4536 a8083063 Iustin Pop
    output = []
4537 638c6349 Guido Trotter
    cluster = self.cfg.GetClusterInfo()
4538 a8083063 Iustin Pop
    for instance in instance_list:
4539 a8083063 Iustin Pop
      iout = []
4540 7736a5f2 Iustin Pop
      i_hv = cluster.FillHV(instance, skip_globals=True)
4541 638c6349 Guido Trotter
      i_be = cluster.FillBE(instance)
4542 638c6349 Guido Trotter
      i_nicp = [objects.FillDict(cluster.nicparams[constants.PP_DEFAULT],
4543 638c6349 Guido Trotter
                                 nic.nicparams) for nic in instance.nics]
4544 a8083063 Iustin Pop
      for field in self.op.output_fields:
4545 71c1af58 Iustin Pop
        st_match = self._FIELDS_STATIC.Matches(field)
4546 19bed813 Iustin Pop
        if field in self._SIMPLE_FIELDS:
4547 19bed813 Iustin Pop
          val = getattr(instance, field)
4548 a8083063 Iustin Pop
        elif field == "pnode":
4549 a8083063 Iustin Pop
          val = instance.primary_node
4550 a8083063 Iustin Pop
        elif field == "snodes":
4551 8a23d2d3 Iustin Pop
          val = list(instance.secondary_nodes)
4552 a8083063 Iustin Pop
        elif field == "admin_state":
4553 0d68c45d Iustin Pop
          val = instance.admin_up
4554 a8083063 Iustin Pop
        elif field == "oper_state":
4555 a8083063 Iustin Pop
          if instance.primary_node in bad_nodes:
4556 8a23d2d3 Iustin Pop
            val = None
4557 a8083063 Iustin Pop
          else:
4558 8a23d2d3 Iustin Pop
            val = bool(live_data.get(instance.name))
4559 d8052456 Iustin Pop
        elif field == "status":
4560 cbfc4681 Iustin Pop
          if instance.primary_node in off_nodes:
4561 cbfc4681 Iustin Pop
            val = "ERROR_nodeoffline"
4562 cbfc4681 Iustin Pop
          elif instance.primary_node in bad_nodes:
4563 d8052456 Iustin Pop
            val = "ERROR_nodedown"
4564 d8052456 Iustin Pop
          else:
4565 d8052456 Iustin Pop
            running = bool(live_data.get(instance.name))
4566 d8052456 Iustin Pop
            if running:
4567 0d68c45d Iustin Pop
              if instance.admin_up:
4568 d8052456 Iustin Pop
                val = "running"
4569 d8052456 Iustin Pop
              else:
4570 d8052456 Iustin Pop
                val = "ERROR_up"
4571 d8052456 Iustin Pop
            else:
4572 0d68c45d Iustin Pop
              if instance.admin_up:
4573 d8052456 Iustin Pop
                val = "ERROR_down"
4574 d8052456 Iustin Pop
              else:
4575 d8052456 Iustin Pop
                val = "ADMIN_down"
4576 a8083063 Iustin Pop
        elif field == "oper_ram":
4577 a8083063 Iustin Pop
          if instance.primary_node in bad_nodes:
4578 8a23d2d3 Iustin Pop
            val = None
4579 a8083063 Iustin Pop
          elif instance.name in live_data:
4580 a8083063 Iustin Pop
            val = live_data[instance.name].get("memory", "?")
4581 a8083063 Iustin Pop
          else:
4582 a8083063 Iustin Pop
            val = "-"
4583 c1ce76bb Iustin Pop
        elif field == "vcpus":
4584 c1ce76bb Iustin Pop
          val = i_be[constants.BE_VCPUS]
4585 a8083063 Iustin Pop
        elif field == "disk_template":
4586 a8083063 Iustin Pop
          val = instance.disk_template
4587 a8083063 Iustin Pop
        elif field == "ip":
4588 39a02558 Guido Trotter
          if instance.nics:
4589 39a02558 Guido Trotter
            val = instance.nics[0].ip
4590 39a02558 Guido Trotter
          else:
4591 39a02558 Guido Trotter
            val = None
4592 638c6349 Guido Trotter
        elif field == "nic_mode":
4593 638c6349 Guido Trotter
          if instance.nics:
4594 638c6349 Guido Trotter
            val = i_nicp[0][constants.NIC_MODE]
4595 638c6349 Guido Trotter
          else:
4596 638c6349 Guido Trotter
            val = None
4597 638c6349 Guido Trotter
        elif field == "nic_link":
4598 39a02558 Guido Trotter
          if instance.nics:
4599 638c6349 Guido Trotter
            val = i_nicp[0][constants.NIC_LINK]
4600 638c6349 Guido Trotter
          else:
4601 638c6349 Guido Trotter
            val = None
4602 638c6349 Guido Trotter
        elif field == "bridge":
4603 638c6349 Guido Trotter
          if (instance.nics and
4604 638c6349 Guido Trotter
              i_nicp[0][constants.NIC_MODE] == constants.NIC_MODE_BRIDGED):
4605 638c6349 Guido Trotter
            val = i_nicp[0][constants.NIC_LINK]
4606 39a02558 Guido Trotter
          else:
4607 39a02558 Guido Trotter
            val = None
4608 a8083063 Iustin Pop
        elif field == "mac":
4609 39a02558 Guido Trotter
          if instance.nics:
4610 39a02558 Guido Trotter
            val = instance.nics[0].mac
4611 39a02558 Guido Trotter
          else:
4612 39a02558 Guido Trotter
            val = None
4613 644eeef9 Iustin Pop
        elif field == "sda_size" or field == "sdb_size":
4614 ad24e046 Iustin Pop
          idx = ord(field[2]) - ord('a')
4615 ad24e046 Iustin Pop
          try:
4616 ad24e046 Iustin Pop
            val = instance.FindDisk(idx).size
4617 ad24e046 Iustin Pop
          except errors.OpPrereqError:
4618 8a23d2d3 Iustin Pop
            val = None
4619 024e157f Iustin Pop
        elif field == "disk_usage": # total disk usage per node
4620 024e157f Iustin Pop
          disk_sizes = [{'size': disk.size} for disk in instance.disks]
4621 024e157f Iustin Pop
          val = _ComputeDiskSize(instance.disk_template, disk_sizes)
4622 130a6a6f Iustin Pop
        elif field == "tags":
4623 130a6a6f Iustin Pop
          val = list(instance.GetTags())
4624 338e51e8 Iustin Pop
        elif field == "hvparams":
4625 338e51e8 Iustin Pop
          val = i_hv
4626 5018a335 Iustin Pop
        elif (field.startswith(HVPREFIX) and
4627 7736a5f2 Iustin Pop
              field[len(HVPREFIX):] in constants.HVS_PARAMETERS and
4628 7736a5f2 Iustin Pop
              field[len(HVPREFIX):] not in constants.HVC_GLOBALS):
4629 5018a335 Iustin Pop
          val = i_hv.get(field[len(HVPREFIX):], None)
4630 338e51e8 Iustin Pop
        elif field == "beparams":
4631 338e51e8 Iustin Pop
          val = i_be
4632 338e51e8 Iustin Pop
        elif (field.startswith(BEPREFIX) and
4633 338e51e8 Iustin Pop
              field[len(BEPREFIX):] in constants.BES_PARAMETERS):
4634 338e51e8 Iustin Pop
          val = i_be.get(field[len(BEPREFIX):], None)
4635 71c1af58 Iustin Pop
        elif st_match and st_match.groups():
4636 71c1af58 Iustin Pop
          # matches a variable list
4637 71c1af58 Iustin Pop
          st_groups = st_match.groups()
4638 71c1af58 Iustin Pop
          if st_groups and st_groups[0] == "disk":
4639 71c1af58 Iustin Pop
            if st_groups[1] == "count":
4640 71c1af58 Iustin Pop
              val = len(instance.disks)
4641 41a776da Iustin Pop
            elif st_groups[1] == "sizes":
4642 41a776da Iustin Pop
              val = [disk.size for disk in instance.disks]
4643 71c1af58 Iustin Pop
            elif st_groups[1] == "size":
4644 3e0cea06 Iustin Pop
              try:
4645 3e0cea06 Iustin Pop
                val = instance.FindDisk(st_groups[2]).size
4646 3e0cea06 Iustin Pop
              except errors.OpPrereqError:
4647 71c1af58 Iustin Pop
                val = None
4648 71c1af58 Iustin Pop
            else:
4649 71c1af58 Iustin Pop
              assert False, "Unhandled disk parameter"
4650 71c1af58 Iustin Pop
          elif st_groups[0] == "nic":
4651 71c1af58 Iustin Pop
            if st_groups[1] == "count":
4652 71c1af58 Iustin Pop
              val = len(instance.nics)
4653 41a776da Iustin Pop
            elif st_groups[1] == "macs":
4654 41a776da Iustin Pop
              val = [nic.mac for nic in instance.nics]
4655 41a776da Iustin Pop
            elif st_groups[1] == "ips":
4656 41a776da Iustin Pop
              val = [nic.ip for nic in instance.nics]
4657 638c6349 Guido Trotter
            elif st_groups[1] == "modes":
4658 638c6349 Guido Trotter
              val = [nicp[constants.NIC_MODE] for nicp in i_nicp]
4659 638c6349 Guido Trotter
            elif st_groups[1] == "links":
4660 638c6349 Guido Trotter
              val = [nicp[constants.NIC_LINK] for nicp in i_nicp]
4661 41a776da Iustin Pop
            elif st_groups[1] == "bridges":
4662 638c6349 Guido Trotter
              val = []
4663 638c6349 Guido Trotter
              for nicp in i_nicp:
4664 638c6349 Guido Trotter
                if nicp[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
4665 638c6349 Guido Trotter
                  val.append(nicp[constants.NIC_LINK])
4666 638c6349 Guido Trotter
                else:
4667 638c6349 Guido Trotter
                  val.append(None)
4668 71c1af58 Iustin Pop
            else:
4669 71c1af58 Iustin Pop
              # index-based item
4670 71c1af58 Iustin Pop
              nic_idx = int(st_groups[2])
4671 71c1af58 Iustin Pop
              if nic_idx >= len(instance.nics):
4672 71c1af58 Iustin Pop
                val = None
4673 71c1af58 Iustin Pop
              else:
4674 71c1af58 Iustin Pop
                if st_groups[1] == "mac":
4675 71c1af58 Iustin Pop
                  val = instance.nics[nic_idx].mac
4676 71c1af58 Iustin Pop
                elif st_groups[1] == "ip":
4677 71c1af58 Iustin Pop
                  val = instance.nics[nic_idx].ip
4678 638c6349 Guido Trotter
                elif st_groups[1] == "mode":
4679 638c6349 Guido Trotter
                  val = i_nicp[nic_idx][constants.NIC_MODE]
4680 638c6349 Guido Trotter
                elif st_groups[1] == "link":
4681 638c6349 Guido Trotter
                  val = i_nicp[nic_idx][constants.NIC_LINK]
4682 71c1af58 Iustin Pop
                elif st_groups[1] == "bridge":
4683 638c6349 Guido Trotter
                  nic_mode = i_nicp[nic_idx][constants.NIC_MODE]
4684 638c6349 Guido Trotter
                  if nic_mode == constants.NIC_MODE_BRIDGED:
4685 638c6349 Guido Trotter
                    val = i_nicp[nic_idx][constants.NIC_LINK]
4686 638c6349 Guido Trotter
                  else:
4687 638c6349 Guido Trotter
                    val = None
4688 71c1af58 Iustin Pop
                else:
4689 71c1af58 Iustin Pop
                  assert False, "Unhandled NIC parameter"
4690 71c1af58 Iustin Pop
          else:
4691 c1ce76bb Iustin Pop
            assert False, ("Declared but unhandled variable parameter '%s'" %
4692 c1ce76bb Iustin Pop
                           field)
4693 a8083063 Iustin Pop
        else:
4694 c1ce76bb Iustin Pop
          assert False, "Declared but unhandled parameter '%s'" % field
4695 a8083063 Iustin Pop
        iout.append(val)
4696 a8083063 Iustin Pop
      output.append(iout)
4697 a8083063 Iustin Pop
4698 a8083063 Iustin Pop
    return output
4699 a8083063 Iustin Pop
4700 a8083063 Iustin Pop
4701 a8083063 Iustin Pop
class LUFailoverInstance(LogicalUnit):
4702 a8083063 Iustin Pop
  """Failover an instance.
4703 a8083063 Iustin Pop

4704 a8083063 Iustin Pop
  """
4705 a8083063 Iustin Pop
  HPATH = "instance-failover"
4706 a8083063 Iustin Pop
  HTYPE = constants.HTYPE_INSTANCE
4707 a8083063 Iustin Pop
  _OP_REQP = ["instance_name", "ignore_consistency"]
4708 c9e5c064 Guido Trotter
  REQ_BGL = False
4709 c9e5c064 Guido Trotter
4710 17c3f802 Guido Trotter
  def CheckArguments(self):
4711 17c3f802 Guido Trotter
    """Check the arguments.
4712 17c3f802 Guido Trotter

4713 17c3f802 Guido Trotter
    """
4714 17c3f802 Guido Trotter
    self.shutdown_timeout = getattr(self.op, "shutdown_timeout",
4715 17c3f802 Guido Trotter
                                    constants.DEFAULT_SHUTDOWN_TIMEOUT)
4716 17c3f802 Guido Trotter
4717 c9e5c064 Guido Trotter
  def ExpandNames(self):
4718 c9e5c064 Guido Trotter
    self._ExpandAndLockInstance()
4719 c9e5c064 Guido Trotter
    self.needed_locks[locking.LEVEL_NODE] = []
4720 f6d9a522 Guido Trotter
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
4721 c9e5c064 Guido Trotter
4722 c9e5c064 Guido Trotter
  def DeclareLocks(self, level):
4723 c9e5c064 Guido Trotter
    if level == locking.LEVEL_NODE:
4724 c9e5c064 Guido Trotter
      self._LockInstancesNodes()
4725 a8083063 Iustin Pop
4726 a8083063 Iustin Pop
  def BuildHooksEnv(self):
4727 a8083063 Iustin Pop
    """Build hooks env.
4728 a8083063 Iustin Pop

4729 a8083063 Iustin Pop
    This runs on master, primary and secondary nodes of the instance.
4730 a8083063 Iustin Pop

4731 a8083063 Iustin Pop
    """
4732 08eec276 Iustin Pop
    instance = self.instance
4733 08eec276 Iustin Pop
    source_node = instance.primary_node
4734 08eec276 Iustin Pop
    target_node = instance.secondary_nodes[0]
4735 a8083063 Iustin Pop
    env = {
4736 a8083063 Iustin Pop
      "IGNORE_CONSISTENCY": self.op.ignore_consistency,
4737 17c3f802 Guido Trotter
      "SHUTDOWN_TIMEOUT": self.shutdown_timeout,
4738 08eec276 Iustin Pop
      "OLD_PRIMARY": source_node,
4739 08eec276 Iustin Pop
      "OLD_SECONDARY": target_node,
4740 08eec276 Iustin Pop
      "NEW_PRIMARY": target_node,
4741 08eec276 Iustin Pop
      "NEW_SECONDARY": source_node,
4742 a8083063 Iustin Pop
      }
4743 08eec276 Iustin Pop
    env.update(_BuildInstanceHookEnvByObject(self, instance))
4744 08eec276 Iustin Pop
    nl = [self.cfg.GetMasterNode()] + list(instance.secondary_nodes)
4745 abd8e836 Iustin Pop
    nl_post = list(nl)
4746 abd8e836 Iustin Pop
    nl_post.append(source_node)
4747 abd8e836 Iustin Pop
    return env, nl, nl_post
4748 a8083063 Iustin Pop
4749 a8083063 Iustin Pop
  def CheckPrereq(self):
4750 a8083063 Iustin Pop
    """Check prerequisites.
4751 a8083063 Iustin Pop

4752 a8083063 Iustin Pop
    This checks that the instance is in the cluster.
4753 a8083063 Iustin Pop

4754 a8083063 Iustin Pop
    """
4755 c9e5c064 Guido Trotter
    self.instance = instance = self.cfg.GetInstanceInfo(self.op.instance_name)
4756 c9e5c064 Guido Trotter
    assert self.instance is not None, \
4757 c9e5c064 Guido Trotter
      "Cannot retrieve locked instance %s" % self.op.instance_name
4758 a8083063 Iustin Pop
4759 338e51e8 Iustin Pop
    bep = self.cfg.GetClusterInfo().FillBE(instance)
4760 a1f445d3 Iustin Pop
    if instance.disk_template not in constants.DTS_NET_MIRROR:
4761 2a710df1 Michael Hanselmann
      raise errors.OpPrereqError("Instance's disk layout is not"
4762 5c983ee5 Iustin Pop
                                 " network mirrored, cannot failover.",
4763 5c983ee5 Iustin Pop
                                 errors.ECODE_STATE)
4764 2a710df1 Michael Hanselmann
4765 2a710df1 Michael Hanselmann
    secondary_nodes = instance.secondary_nodes
4766 2a710df1 Michael Hanselmann
    if not secondary_nodes:
4767 2a710df1 Michael Hanselmann
      raise errors.ProgrammerError("no secondary node but using "
4768 abdf0113 Iustin Pop
                                   "a mirrored disk template")
4769 2a710df1 Michael Hanselmann
4770 2a710df1 Michael Hanselmann
    target_node = secondary_nodes[0]
4771 7527a8a4 Iustin Pop
    _CheckNodeOnline(self, target_node)
4772 733a2b6a Iustin Pop
    _CheckNodeNotDrained(self, target_node)
4773 d27776f0 Iustin Pop
    if instance.admin_up:
4774 d27776f0 Iustin Pop
      # check memory requirements on the secondary node
4775 d27776f0 Iustin Pop
      _CheckNodeFreeMemory(self, target_node, "failing over instance %s" %
4776 d27776f0 Iustin Pop
                           instance.name, bep[constants.BE_MEMORY],
4777 d27776f0 Iustin Pop
                           instance.hypervisor)
4778 d27776f0 Iustin Pop
    else:
4779 d27776f0 Iustin Pop
      self.LogInfo("Not checking memory on the secondary node as"
4780 d27776f0 Iustin Pop
                   " instance will not be started")
4781 3a7c308e Guido Trotter
4782 a8083063 Iustin Pop
    # check bridge existance
4783 b165e77e Guido Trotter
    _CheckInstanceBridgesExist(self, instance, node=target_node)
4784 a8083063 Iustin Pop
4785 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
4786 a8083063 Iustin Pop
    """Failover an instance.
4787 a8083063 Iustin Pop

4788 a8083063 Iustin Pop
    The failover is done by shutting it down on its present node and
4789 a8083063 Iustin Pop
    starting it on the secondary.
4790 a8083063 Iustin Pop

4791 a8083063 Iustin Pop
    """
4792 a8083063 Iustin Pop
    instance = self.instance
4793 a8083063 Iustin Pop
4794 a8083063 Iustin Pop
    source_node = instance.primary_node
4795 a8083063 Iustin Pop
    target_node = instance.secondary_nodes[0]
4796 a8083063 Iustin Pop
4797 1df79ce6 Michael Hanselmann
    if instance.admin_up:
4798 1df79ce6 Michael Hanselmann
      feedback_fn("* checking disk consistency between source and target")
4799 1df79ce6 Michael Hanselmann
      for dev in instance.disks:
4800 1df79ce6 Michael Hanselmann
        # for drbd, these are drbd over lvm
4801 1df79ce6 Michael Hanselmann
        if not _CheckDiskConsistency(self, dev, target_node, False):
4802 1df79ce6 Michael Hanselmann
          if not self.op.ignore_consistency:
4803 1df79ce6 Michael Hanselmann
            raise errors.OpExecError("Disk %s is degraded on target node,"
4804 1df79ce6 Michael Hanselmann
                                     " aborting failover." % dev.iv_name)
4805 1df79ce6 Michael Hanselmann
    else:
4806 1df79ce6 Michael Hanselmann
      feedback_fn("* not checking disk consistency as instance is not running")
4807 a8083063 Iustin Pop
4808 a8083063 Iustin Pop
    feedback_fn("* shutting down instance on source node")
4809 9a4f63d1 Iustin Pop
    logging.info("Shutting down instance %s on node %s",
4810 9a4f63d1 Iustin Pop
                 instance.name, source_node)
4811 a8083063 Iustin Pop
4812 17c3f802 Guido Trotter
    result = self.rpc.call_instance_shutdown(source_node, instance,
4813 17c3f802 Guido Trotter
                                             self.shutdown_timeout)
4814 4c4e4e1e Iustin Pop
    msg = result.fail_msg
4815 1fae010f Iustin Pop
    if msg:
4816 24a40d57 Iustin Pop
      if self.op.ignore_consistency:
4817 86d9d3bb Iustin Pop
        self.proc.LogWarning("Could not shutdown instance %s on node %s."
4818 1fae010f Iustin Pop
                             " Proceeding anyway. Please make sure node"
4819 1fae010f Iustin Pop
                             " %s is down. Error details: %s",
4820 1fae010f Iustin Pop
                             instance.name, source_node, source_node, msg)
4821 24a40d57 Iustin Pop
      else:
4822 1fae010f Iustin Pop
        raise errors.OpExecError("Could not shutdown instance %s on"
4823 1fae010f Iustin Pop
                                 " node %s: %s" %
4824 1fae010f Iustin Pop
                                 (instance.name, source_node, msg))
4825 a8083063 Iustin Pop
4826 a8083063 Iustin Pop
    feedback_fn("* deactivating the instance's disks on source node")
4827 b9bddb6b Iustin Pop
    if not _ShutdownInstanceDisks(self, instance, ignore_primary=True):
4828 3ecf6786 Iustin Pop
      raise errors.OpExecError("Can't shut down the instance's disks.")
4829 a8083063 Iustin Pop
4830 a8083063 Iustin Pop
    instance.primary_node = target_node
4831 a8083063 Iustin Pop
    # distribute new instance config to the other nodes
4832 a4eae71f Michael Hanselmann
    self.cfg.Update(instance, feedback_fn)
4833 a8083063 Iustin Pop
4834 12a0cfbe Guido Trotter
    # Only start the instance if it's marked as up
4835 0d68c45d Iustin Pop
    if instance.admin_up:
4836 12a0cfbe Guido Trotter
      feedback_fn("* activating the instance's disks on target node")
4837 9a4f63d1 Iustin Pop
      logging.info("Starting instance %s on node %s",
4838 9a4f63d1 Iustin Pop
                   instance.name, target_node)
4839 12a0cfbe Guido Trotter
4840 7c4d6c7b Michael Hanselmann
      disks_ok, _ = _AssembleInstanceDisks(self, instance,
4841 12a0cfbe Guido Trotter
                                               ignore_secondaries=True)
4842 12a0cfbe Guido Trotter
      if not disks_ok:
4843 b9bddb6b Iustin Pop
        _ShutdownInstanceDisks(self, instance)
4844 12a0cfbe Guido Trotter
        raise errors.OpExecError("Can't activate the instance's disks")
4845 a8083063 Iustin Pop
4846 12a0cfbe Guido Trotter
      feedback_fn("* starting the instance on the target node")
4847 0eca8e0c Iustin Pop
      result = self.rpc.call_instance_start(target_node, instance, None, None)
4848 4c4e4e1e Iustin Pop
      msg = result.fail_msg
4849 dd279568 Iustin Pop
      if msg:
4850 b9bddb6b Iustin Pop
        _ShutdownInstanceDisks(self, instance)
4851 dd279568 Iustin Pop
        raise errors.OpExecError("Could not start instance %s on node %s: %s" %
4852 dd279568 Iustin Pop
                                 (instance.name, target_node, msg))
4853 a8083063 Iustin Pop
4854 a8083063 Iustin Pop
4855 53c776b5 Iustin Pop
class LUMigrateInstance(LogicalUnit):
4856 53c776b5 Iustin Pop
  """Migrate an instance.
4857 53c776b5 Iustin Pop

4858 53c776b5 Iustin Pop
  This is migration without shutting down, compared to the failover,
4859 53c776b5 Iustin Pop
  which is done with shutdown.
4860 53c776b5 Iustin Pop

4861 53c776b5 Iustin Pop
  """
4862 53c776b5 Iustin Pop
  HPATH = "instance-migrate"
4863 53c776b5 Iustin Pop
  HTYPE = constants.HTYPE_INSTANCE
4864 53c776b5 Iustin Pop
  _OP_REQP = ["instance_name", "live", "cleanup"]
4865 53c776b5 Iustin Pop
4866 53c776b5 Iustin Pop
  REQ_BGL = False
4867 53c776b5 Iustin Pop
4868 53c776b5 Iustin Pop
  def ExpandNames(self):
4869 53c776b5 Iustin Pop
    self._ExpandAndLockInstance()
4870 3e06e001 Michael Hanselmann
4871 53c776b5 Iustin Pop
    self.needed_locks[locking.LEVEL_NODE] = []
4872 53c776b5 Iustin Pop
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
4873 53c776b5 Iustin Pop
4874 3e06e001 Michael Hanselmann
    self._migrater = TLMigrateInstance(self, self.op.instance_name,
4875 3e06e001 Michael Hanselmann
                                       self.op.live, self.op.cleanup)
4876 3a012b41 Michael Hanselmann
    self.tasklets = [self._migrater]
4877 3e06e001 Michael Hanselmann
4878 53c776b5 Iustin Pop
  def DeclareLocks(self, level):
4879 53c776b5 Iustin Pop
    if level == locking.LEVEL_NODE:
4880 53c776b5 Iustin Pop
      self._LockInstancesNodes()
4881 53c776b5 Iustin Pop
4882 53c776b5 Iustin Pop
  def BuildHooksEnv(self):
4883 53c776b5 Iustin Pop
    """Build hooks env.
4884 53c776b5 Iustin Pop

4885 53c776b5 Iustin Pop
    This runs on master, primary and secondary nodes of the instance.
4886 53c776b5 Iustin Pop

4887 53c776b5 Iustin Pop
    """
4888 3e06e001 Michael Hanselmann
    instance = self._migrater.instance
4889 08eec276 Iustin Pop
    source_node = instance.primary_node
4890 08eec276 Iustin Pop
    target_node = instance.secondary_nodes[0]
4891 3e06e001 Michael Hanselmann
    env = _BuildInstanceHookEnvByObject(self, instance)
4892 2c2690c9 Iustin Pop
    env["MIGRATE_LIVE"] = self.op.live
4893 2c2690c9 Iustin Pop
    env["MIGRATE_CLEANUP"] = self.op.cleanup
4894 08eec276 Iustin Pop
    env.update({
4895 08eec276 Iustin Pop
        "OLD_PRIMARY": source_node,
4896 08eec276 Iustin Pop
        "OLD_SECONDARY": target_node,
4897 08eec276 Iustin Pop
        "NEW_PRIMARY": target_node,
4898 08eec276 Iustin Pop
        "NEW_SECONDARY": source_node,
4899 08eec276 Iustin Pop
        })
4900 3e06e001 Michael Hanselmann
    nl = [self.cfg.GetMasterNode()] + list(instance.secondary_nodes)
4901 abd8e836 Iustin Pop
    nl_post = list(nl)
4902 abd8e836 Iustin Pop
    nl_post.append(source_node)
4903 abd8e836 Iustin Pop
    return env, nl, nl_post
4904 53c776b5 Iustin Pop
4905 3e06e001 Michael Hanselmann
4906 313bcead Iustin Pop
class LUMoveInstance(LogicalUnit):
4907 313bcead Iustin Pop
  """Move an instance by data-copying.
4908 313bcead Iustin Pop

4909 313bcead Iustin Pop
  """
4910 313bcead Iustin Pop
  HPATH = "instance-move"
4911 313bcead Iustin Pop
  HTYPE = constants.HTYPE_INSTANCE
4912 313bcead Iustin Pop
  _OP_REQP = ["instance_name", "target_node"]
4913 313bcead Iustin Pop
  REQ_BGL = False
4914 313bcead Iustin Pop
4915 17c3f802 Guido Trotter
  def CheckArguments(self):
4916 17c3f802 Guido Trotter
    """Check the arguments.
4917 17c3f802 Guido Trotter

4918 17c3f802 Guido Trotter
    """
4919 17c3f802 Guido Trotter
    self.shutdown_timeout = getattr(self.op, "shutdown_timeout",
4920 17c3f802 Guido Trotter
                                    constants.DEFAULT_SHUTDOWN_TIMEOUT)
4921 17c3f802 Guido Trotter
4922 313bcead Iustin Pop
  def ExpandNames(self):
4923 313bcead Iustin Pop
    self._ExpandAndLockInstance()
4924 cf26a87a Iustin Pop
    target_node = _ExpandNodeName(self.cfg, self.op.target_node)
4925 313bcead Iustin Pop
    self.op.target_node = target_node
4926 313bcead Iustin Pop
    self.needed_locks[locking.LEVEL_NODE] = [target_node]
4927 313bcead Iustin Pop
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
4928 313bcead Iustin Pop
4929 313bcead Iustin Pop
  def DeclareLocks(self, level):
4930 313bcead Iustin Pop
    if level == locking.LEVEL_NODE:
4931 313bcead Iustin Pop
      self._LockInstancesNodes(primary_only=True)
4932 313bcead Iustin Pop
4933 313bcead Iustin Pop
  def BuildHooksEnv(self):
4934 313bcead Iustin Pop
    """Build hooks env.
4935 313bcead Iustin Pop

4936 313bcead Iustin Pop
    This runs on master, primary and secondary nodes of the instance.
4937 313bcead Iustin Pop

4938 313bcead Iustin Pop
    """
4939 313bcead Iustin Pop
    env = {
4940 313bcead Iustin Pop
      "TARGET_NODE": self.op.target_node,
4941 17c3f802 Guido Trotter
      "SHUTDOWN_TIMEOUT": self.shutdown_timeout,
4942 313bcead Iustin Pop
      }
4943 313bcead Iustin Pop
    env.update(_BuildInstanceHookEnvByObject(self, self.instance))
4944 313bcead Iustin Pop
    nl = [self.cfg.GetMasterNode()] + [self.instance.primary_node,
4945 313bcead Iustin Pop
                                       self.op.target_node]
4946 313bcead Iustin Pop
    return env, nl, nl
4947 313bcead Iustin Pop
4948 313bcead Iustin Pop
  def CheckPrereq(self):
4949 313bcead Iustin Pop
    """Check prerequisites.
4950 313bcead Iustin Pop

4951 313bcead Iustin Pop
    This checks that the instance is in the cluster.
4952 313bcead Iustin Pop

4953 313bcead Iustin Pop
    """
4954 313bcead Iustin Pop
    self.instance = instance = self.cfg.GetInstanceInfo(self.op.instance_name)
4955 313bcead Iustin Pop
    assert self.instance is not None, \
4956 313bcead Iustin Pop
      "Cannot retrieve locked instance %s" % self.op.instance_name
4957 313bcead Iustin Pop
4958 313bcead Iustin Pop
    node = self.cfg.GetNodeInfo(self.op.target_node)
4959 313bcead Iustin Pop
    assert node is not None, \
4960 313bcead Iustin Pop
      "Cannot retrieve locked node %s" % self.op.target_node
4961 313bcead Iustin Pop
4962 313bcead Iustin Pop
    self.target_node = target_node = node.name
4963 313bcead Iustin Pop
4964 313bcead Iustin Pop
    if target_node == instance.primary_node:
4965 313bcead Iustin Pop
      raise errors.OpPrereqError("Instance %s is already on the node %s" %
4966 5c983ee5 Iustin Pop
                                 (instance.name, target_node),
4967 5c983ee5 Iustin Pop
                                 errors.ECODE_STATE)
4968 313bcead Iustin Pop
4969 313bcead Iustin Pop
    bep = self.cfg.GetClusterInfo().FillBE(instance)
4970 313bcead Iustin Pop
4971 313bcead Iustin Pop
    for idx, dsk in enumerate(instance.disks):
4972 313bcead Iustin Pop
      if dsk.dev_type not in (constants.LD_LV, constants.LD_FILE):
4973 313bcead Iustin Pop
        raise errors.OpPrereqError("Instance disk %d has a complex layout,"
4974 d1b83918 Iustin Pop
                                   " cannot copy" % idx, errors.ECODE_STATE)
4975 313bcead Iustin Pop
4976 313bcead Iustin Pop
    _CheckNodeOnline(self, target_node)
4977 313bcead Iustin Pop
    _CheckNodeNotDrained(self, target_node)
4978 313bcead Iustin Pop
4979 313bcead Iustin Pop
    if instance.admin_up:
4980 313bcead Iustin Pop
      # check memory requirements on the secondary node
4981 313bcead Iustin Pop
      _CheckNodeFreeMemory(self, target_node, "failing over instance %s" %
4982 313bcead Iustin Pop
                           instance.name, bep[constants.BE_MEMORY],
4983 313bcead Iustin Pop
                           instance.hypervisor)
4984 313bcead Iustin Pop
    else:
4985 313bcead Iustin Pop
      self.LogInfo("Not checking memory on the secondary node as"
4986 313bcead Iustin Pop
                   " instance will not be started")
4987 313bcead Iustin Pop
4988 313bcead Iustin Pop
    # check bridge existance
4989 313bcead Iustin Pop
    _CheckInstanceBridgesExist(self, instance, node=target_node)
4990 313bcead Iustin Pop
4991 313bcead Iustin Pop
  def Exec(self, feedback_fn):
4992 313bcead Iustin Pop
    """Move an instance.
4993 313bcead Iustin Pop

4994 313bcead Iustin Pop
    The move is done by shutting it down on its present node, copying
4995 313bcead Iustin Pop
    the data over (slow) and starting it on the new node.
4996 313bcead Iustin Pop

4997 313bcead Iustin Pop
    """
4998 313bcead Iustin Pop
    instance = self.instance
4999 313bcead Iustin Pop
5000 313bcead Iustin Pop
    source_node = instance.primary_node
5001 313bcead Iustin Pop
    target_node = self.target_node
5002 313bcead Iustin Pop
5003 313bcead Iustin Pop
    self.LogInfo("Shutting down instance %s on source node %s",
5004 313bcead Iustin Pop
                 instance.name, source_node)
5005 313bcead Iustin Pop
5006 17c3f802 Guido Trotter
    result = self.rpc.call_instance_shutdown(source_node, instance,
5007 17c3f802 Guido Trotter
                                             self.shutdown_timeout)
5008 313bcead Iustin Pop
    msg = result.fail_msg
5009 313bcead Iustin Pop
    if msg:
5010 313bcead Iustin Pop
      if self.op.ignore_consistency:
5011 313bcead Iustin Pop
        self.proc.LogWarning("Could not shutdown instance %s on node %s."
5012 313bcead Iustin Pop
                             " Proceeding anyway. Please make sure node"
5013 313bcead Iustin Pop
                             " %s is down. Error details: %s",
5014 313bcead Iustin Pop
                             instance.name, source_node, source_node, msg)
5015 313bcead Iustin Pop
      else:
5016 313bcead Iustin Pop
        raise errors.OpExecError("Could not shutdown instance %s on"
5017 313bcead Iustin Pop
                                 " node %s: %s" %
5018 313bcead Iustin Pop
                                 (instance.name, source_node, msg))
5019 313bcead Iustin Pop
5020 313bcead Iustin Pop
    # create the target disks
5021 313bcead Iustin Pop
    try:
5022 313bcead Iustin Pop
      _CreateDisks(self, instance, target_node=target_node)
5023 313bcead Iustin Pop
    except errors.OpExecError:
5024 313bcead Iustin Pop
      self.LogWarning("Device creation failed, reverting...")
5025 313bcead Iustin Pop
      try:
5026 313bcead Iustin Pop
        _RemoveDisks(self, instance, target_node=target_node)
5027 313bcead Iustin Pop
      finally:
5028 313bcead Iustin Pop
        self.cfg.ReleaseDRBDMinors(instance.name)
5029 313bcead Iustin Pop
        raise
5030 313bcead Iustin Pop
5031 313bcead Iustin Pop
    cluster_name = self.cfg.GetClusterInfo().cluster_name
5032 313bcead Iustin Pop
5033 313bcead Iustin Pop
    errs = []
5034 313bcead Iustin Pop
    # activate, get path, copy the data over
5035 313bcead Iustin Pop
    for idx, disk in enumerate(instance.disks):
5036 313bcead Iustin Pop
      self.LogInfo("Copying data for disk %d", idx)
5037 313bcead Iustin Pop
      result = self.rpc.call_blockdev_assemble(target_node, disk,
5038 313bcead Iustin Pop
                                               instance.name, True)
5039 313bcead Iustin Pop
      if result.fail_msg:
5040 313bcead Iustin Pop
        self.LogWarning("Can't assemble newly created disk %d: %s",
5041 313bcead Iustin Pop
                        idx, result.fail_msg)
5042 313bcead Iustin Pop
        errs.append(result.fail_msg)
5043 313bcead Iustin Pop
        break
5044 313bcead Iustin Pop
      dev_path = result.payload
5045 313bcead Iustin Pop
      result = self.rpc.call_blockdev_export(source_node, disk,
5046 313bcead Iustin Pop
                                             target_node, dev_path,
5047 313bcead Iustin Pop
                                             cluster_name)
5048 313bcead Iustin Pop
      if result.fail_msg:
5049 313bcead Iustin Pop
        self.LogWarning("Can't copy data over for disk %d: %s",
5050 313bcead Iustin Pop
                        idx, result.fail_msg)
5051 313bcead Iustin Pop
        errs.append(result.fail_msg)
5052 313bcead Iustin Pop
        break
5053 313bcead Iustin Pop
5054 313bcead Iustin Pop
    if errs:
5055 313bcead Iustin Pop
      self.LogWarning("Some disks failed to copy, aborting")
5056 313bcead Iustin Pop
      try:
5057 313bcead Iustin Pop
        _RemoveDisks(self, instance, target_node=target_node)
5058 313bcead Iustin Pop
      finally:
5059 313bcead Iustin Pop
        self.cfg.ReleaseDRBDMinors(instance.name)
5060 313bcead Iustin Pop
        raise errors.OpExecError("Errors during disk copy: %s" %
5061 313bcead Iustin Pop
                                 (",".join(errs),))
5062 313bcead Iustin Pop
5063 313bcead Iustin Pop
    instance.primary_node = target_node
5064 a4eae71f Michael Hanselmann
    self.cfg.Update(instance, feedback_fn)
5065 313bcead Iustin Pop
5066 313bcead Iustin Pop
    self.LogInfo("Removing the disks on the original node")
5067 313bcead Iustin Pop
    _RemoveDisks(self, instance, target_node=source_node)
5068 313bcead Iustin Pop
5069 313bcead Iustin Pop
    # Only start the instance if it's marked as up
5070 313bcead Iustin Pop
    if instance.admin_up:
5071 313bcead Iustin Pop
      self.LogInfo("Starting instance %s on node %s",
5072 313bcead Iustin Pop
                   instance.name, target_node)
5073 313bcead Iustin Pop
5074 313bcead Iustin Pop
      disks_ok, _ = _AssembleInstanceDisks(self, instance,
5075 313bcead Iustin Pop
                                           ignore_secondaries=True)
5076 313bcead Iustin Pop
      if not disks_ok:
5077 313bcead Iustin Pop
        _ShutdownInstanceDisks(self, instance)
5078 313bcead Iustin Pop
        raise errors.OpExecError("Can't activate the instance's disks")
5079 313bcead Iustin Pop
5080 313bcead Iustin Pop
      result = self.rpc.call_instance_start(target_node, instance, None, None)
5081 313bcead Iustin Pop
      msg = result.fail_msg
5082 313bcead Iustin Pop
      if msg:
5083 313bcead Iustin Pop
        _ShutdownInstanceDisks(self, instance)
5084 313bcead Iustin Pop
        raise errors.OpExecError("Could not start instance %s on node %s: %s" %
5085 313bcead Iustin Pop
                                 (instance.name, target_node, msg))
5086 313bcead Iustin Pop
5087 313bcead Iustin Pop
5088 80cb875c Michael Hanselmann
class LUMigrateNode(LogicalUnit):
5089 80cb875c Michael Hanselmann
  """Migrate all instances from a node.
5090 80cb875c Michael Hanselmann

5091 80cb875c Michael Hanselmann
  """
5092 80cb875c Michael Hanselmann
  HPATH = "node-migrate"
5093 80cb875c Michael Hanselmann
  HTYPE = constants.HTYPE_NODE
5094 80cb875c Michael Hanselmann
  _OP_REQP = ["node_name", "live"]
5095 80cb875c Michael Hanselmann
  REQ_BGL = False
5096 80cb875c Michael Hanselmann
5097 80cb875c Michael Hanselmann
  def ExpandNames(self):
5098 cf26a87a Iustin Pop
    self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name)
5099 80cb875c Michael Hanselmann
5100 80cb875c Michael Hanselmann
    self.needed_locks = {
5101 80cb875c Michael Hanselmann
      locking.LEVEL_NODE: [self.op.node_name],
5102 80cb875c Michael Hanselmann
      }
5103 80cb875c Michael Hanselmann
5104 80cb875c Michael Hanselmann
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
5105 80cb875c Michael Hanselmann
5106 80cb875c Michael Hanselmann
    # Create tasklets for migrating instances for all instances on this node
5107 80cb875c Michael Hanselmann
    names = []
5108 80cb875c Michael Hanselmann
    tasklets = []
5109 80cb875c Michael Hanselmann
5110 80cb875c Michael Hanselmann
    for inst in _GetNodePrimaryInstances(self.cfg, self.op.node_name):
5111 80cb875c Michael Hanselmann
      logging.debug("Migrating instance %s", inst.name)
5112 80cb875c Michael Hanselmann
      names.append(inst.name)
5113 80cb875c Michael Hanselmann
5114 80cb875c Michael Hanselmann
      tasklets.append(TLMigrateInstance(self, inst.name, self.op.live, False))
5115 80cb875c Michael Hanselmann
5116 80cb875c Michael Hanselmann
    self.tasklets = tasklets
5117 80cb875c Michael Hanselmann
5118 80cb875c Michael Hanselmann
    # Declare instance locks
5119 80cb875c Michael Hanselmann
    self.needed_locks[locking.LEVEL_INSTANCE] = names
5120 80cb875c Michael Hanselmann
5121 80cb875c Michael Hanselmann
  def DeclareLocks(self, level):
5122 80cb875c Michael Hanselmann
    if level == locking.LEVEL_NODE:
5123 80cb875c Michael Hanselmann
      self._LockInstancesNodes()
5124 80cb875c Michael Hanselmann
5125 80cb875c Michael Hanselmann
  def BuildHooksEnv(self):
5126 80cb875c Michael Hanselmann
    """Build hooks env.
5127 80cb875c Michael Hanselmann

5128 80cb875c Michael Hanselmann
    This runs on the master, the primary and all the secondaries.
5129 80cb875c Michael Hanselmann

5130 80cb875c Michael Hanselmann
    """
5131 80cb875c Michael Hanselmann
    env = {
5132 80cb875c Michael Hanselmann
      "NODE_NAME": self.op.node_name,
5133 80cb875c Michael Hanselmann
      }
5134 80cb875c Michael Hanselmann
5135 80cb875c Michael Hanselmann
    nl = [self.cfg.GetMasterNode()]
5136 80cb875c Michael Hanselmann
5137 80cb875c Michael Hanselmann
    return (env, nl, nl)
5138 80cb875c Michael Hanselmann
5139 80cb875c Michael Hanselmann
5140 3e06e001 Michael Hanselmann
class TLMigrateInstance(Tasklet):
5141 3e06e001 Michael Hanselmann
  def __init__(self, lu, instance_name, live, cleanup):
5142 3e06e001 Michael Hanselmann
    """Initializes this class.
5143 3e06e001 Michael Hanselmann

5144 3e06e001 Michael Hanselmann
    """
5145 464243a7 Michael Hanselmann
    Tasklet.__init__(self, lu)
5146 464243a7 Michael Hanselmann
5147 3e06e001 Michael Hanselmann
    # Parameters
5148 3e06e001 Michael Hanselmann
    self.instance_name = instance_name
5149 3e06e001 Michael Hanselmann
    self.live = live
5150 3e06e001 Michael Hanselmann
    self.cleanup = cleanup
5151 3e06e001 Michael Hanselmann
5152 53c776b5 Iustin Pop
  def CheckPrereq(self):
5153 53c776b5 Iustin Pop
    """Check prerequisites.
5154 53c776b5 Iustin Pop

5155 53c776b5 Iustin Pop
    This checks that the instance is in the cluster.
5156 53c776b5 Iustin Pop

5157 53c776b5 Iustin Pop
    """
5158 cf26a87a Iustin Pop
    instance_name = _ExpandInstanceName(self.lu.cfg, self.instance_name)
5159 cf26a87a Iustin Pop
    instance = self.cfg.GetInstanceInfo(instance_name)
5160 cf26a87a Iustin Pop
    assert instance is not None
5161 53c776b5 Iustin Pop
5162 53c776b5 Iustin Pop
    if instance.disk_template != constants.DT_DRBD8:
5163 53c776b5 Iustin Pop
      raise errors.OpPrereqError("Instance's disk layout is not"
5164 5c983ee5 Iustin Pop
                                 " drbd8, cannot migrate.", errors.ECODE_STATE)
5165 53c776b5 Iustin Pop
5166 53c776b5 Iustin Pop
    secondary_nodes = instance.secondary_nodes
5167 53c776b5 Iustin Pop
    if not secondary_nodes:
5168 733a2b6a Iustin Pop
      raise errors.ConfigurationError("No secondary node but using"
5169 733a2b6a Iustin Pop
                                      " drbd8 disk template")
5170 53c776b5 Iustin Pop
5171 53c776b5 Iustin Pop
    i_be = self.cfg.GetClusterInfo().FillBE(instance)
5172 53c776b5 Iustin Pop
5173 53c776b5 Iustin Pop
    target_node = secondary_nodes[0]
5174 53c776b5 Iustin Pop
    # check memory requirements on the secondary node
5175 53c776b5 Iustin Pop
    _CheckNodeFreeMemory(self, target_node, "migrating instance %s" %
5176 53c776b5 Iustin Pop
                         instance.name, i_be[constants.BE_MEMORY],
5177 53c776b5 Iustin Pop
                         instance.hypervisor)
5178 53c776b5 Iustin Pop
5179 53c776b5 Iustin Pop
    # check bridge existance
5180 b165e77e Guido Trotter
    _CheckInstanceBridgesExist(self, instance, node=target_node)
5181 53c776b5 Iustin Pop
5182 3e06e001 Michael Hanselmann
    if not self.cleanup:
5183 733a2b6a Iustin Pop
      _CheckNodeNotDrained(self, target_node)
5184 53c776b5 Iustin Pop
      result = self.rpc.call_instance_migratable(instance.primary_node,
5185 53c776b5 Iustin Pop
                                                 instance)
5186 045dd6d9 Iustin Pop
      result.Raise("Can't migrate, please use failover",
5187 045dd6d9 Iustin Pop
                   prereq=True, ecode=errors.ECODE_STATE)
5188 53c776b5 Iustin Pop
5189 53c776b5 Iustin Pop
    self.instance = instance
5190 53c776b5 Iustin Pop
5191 53c776b5 Iustin Pop
  def _WaitUntilSync(self):
5192 53c776b5 Iustin Pop
    """Poll with custom rpc for disk sync.
5193 53c776b5 Iustin Pop

5194 53c776b5 Iustin Pop
    This uses our own step-based rpc call.
5195 53c776b5 Iustin Pop

5196 53c776b5 Iustin Pop
    """
5197 53c776b5 Iustin Pop
    self.feedback_fn("* wait until resync is done")
5198 53c776b5 Iustin Pop
    all_done = False
5199 53c776b5 Iustin Pop
    while not all_done:
5200 53c776b5 Iustin Pop
      all_done = True
5201 53c776b5 Iustin Pop
      result = self.rpc.call_drbd_wait_sync(self.all_nodes,
5202 53c776b5 Iustin Pop
                                            self.nodes_ip,
5203 53c776b5 Iustin Pop
                                            self.instance.disks)
5204 53c776b5 Iustin Pop
      min_percent = 100
5205 53c776b5 Iustin Pop
      for node, nres in result.items():
5206 4c4e4e1e Iustin Pop
        nres.Raise("Cannot resync disks on node %s" % node)
5207 0959c824 Iustin Pop
        node_done, node_percent = nres.payload
5208 53c776b5 Iustin Pop
        all_done = all_done and node_done
5209 53c776b5 Iustin Pop
        if node_percent is not None:
5210 53c776b5 Iustin Pop
          min_percent = min(min_percent, node_percent)
5211 53c776b5 Iustin Pop
      if not all_done:
5212 53c776b5 Iustin Pop
        if min_percent < 100:
5213 53c776b5 Iustin Pop
          self.feedback_fn("   - progress: %.1f%%" % min_percent)
5214 53c776b5 Iustin Pop
        time.sleep(2)
5215 53c776b5 Iustin Pop
5216 53c776b5 Iustin Pop
  def _EnsureSecondary(self, node):
5217 53c776b5 Iustin Pop
    """Demote a node to secondary.
5218 53c776b5 Iustin Pop

5219 53c776b5 Iustin Pop
    """
5220 53c776b5 Iustin Pop
    self.feedback_fn("* switching node %s to secondary mode" % node)
5221 53c776b5 Iustin Pop
5222 53c776b5 Iustin Pop
    for dev in self.instance.disks:
5223 53c776b5 Iustin Pop
      self.cfg.SetDiskID(dev, node)
5224 53c776b5 Iustin Pop
5225 53c776b5 Iustin Pop
    result = self.rpc.call_blockdev_close(node, self.instance.name,
5226 53c776b5 Iustin Pop
                                          self.instance.disks)
5227 4c4e4e1e Iustin Pop
    result.Raise("Cannot change disk to secondary on node %s" % node)
5228 53c776b5 Iustin Pop
5229 53c776b5 Iustin Pop
  def _GoStandalone(self):
5230 53c776b5 Iustin Pop
    """Disconnect from the network.
5231 53c776b5 Iustin Pop

5232 53c776b5 Iustin Pop
    """
5233 53c776b5 Iustin Pop
    self.feedback_fn("* changing into standalone mode")
5234 53c776b5 Iustin Pop
    result = self.rpc.call_drbd_disconnect_net(self.all_nodes, self.nodes_ip,
5235 53c776b5 Iustin Pop
                                               self.instance.disks)
5236 53c776b5 Iustin Pop
    for node, nres in result.items():
5237 4c4e4e1e Iustin Pop
      nres.Raise("Cannot disconnect disks node %s" % node)
5238 53c776b5 Iustin Pop
5239 53c776b5 Iustin Pop
  def _GoReconnect(self, multimaster):
5240 53c776b5 Iustin Pop
    """Reconnect to the network.
5241 53c776b5 Iustin Pop

5242 53c776b5 Iustin Pop
    """
5243 53c776b5 Iustin Pop
    if multimaster:
5244 53c776b5 Iustin Pop
      msg = "dual-master"
5245 53c776b5 Iustin Pop
    else:
5246 53c776b5 Iustin Pop
      msg = "single-master"
5247 53c776b5 Iustin Pop
    self.feedback_fn("* changing disks into %s mode" % msg)
5248 53c776b5 Iustin Pop
    result = self.rpc.call_drbd_attach_net(self.all_nodes, self.nodes_ip,
5249 53c776b5 Iustin Pop
                                           self.instance.disks,
5250 53c776b5 Iustin Pop
                                           self.instance.name, multimaster)
5251 53c776b5 Iustin Pop
    for node, nres in result.items():
5252 4c4e4e1e Iustin Pop
      nres.Raise("Cannot change disks config on node %s" % node)
5253 53c776b5 Iustin Pop
5254 53c776b5 Iustin Pop
  def _ExecCleanup(self):
5255 53c776b5 Iustin Pop
    """Try to cleanup after a failed migration.
5256 53c776b5 Iustin Pop

5257 53c776b5 Iustin Pop
    The cleanup is done by:
5258 53c776b5 Iustin Pop
      - check that the instance is running only on one node
5259 53c776b5 Iustin Pop
        (and update the config if needed)
5260 53c776b5 Iustin Pop
      - change disks on its secondary node to secondary
5261 53c776b5 Iustin Pop
      - wait until disks are fully synchronized
5262 53c776b5 Iustin Pop
      - disconnect from the network
5263 53c776b5 Iustin Pop
      - change disks into single-master mode
5264 53c776b5 Iustin Pop
      - wait again until disks are fully synchronized
5265 53c776b5 Iustin Pop

5266 53c776b5 Iustin Pop
    """
5267 53c776b5 Iustin Pop
    instance = self.instance
5268 53c776b5 Iustin Pop
    target_node = self.target_node
5269 53c776b5 Iustin Pop
    source_node = self.source_node
5270 53c776b5 Iustin Pop
5271 53c776b5 Iustin Pop
    # check running on only one node
5272 53c776b5 Iustin Pop
    self.feedback_fn("* checking where the instance actually runs"
5273 53c776b5 Iustin Pop
                     " (if this hangs, the hypervisor might be in"
5274 53c776b5 Iustin Pop
                     " a bad state)")
5275 53c776b5 Iustin Pop
    ins_l = self.rpc.call_instance_list(self.all_nodes, [instance.hypervisor])
5276 53c776b5 Iustin Pop
    for node, result in ins_l.items():
5277 4c4e4e1e Iustin Pop
      result.Raise("Can't contact node %s" % node)
5278 53c776b5 Iustin Pop
5279 aca13712 Iustin Pop
    runningon_source = instance.name in ins_l[source_node].payload
5280 aca13712 Iustin Pop
    runningon_target = instance.name in ins_l[target_node].payload
5281 53c776b5 Iustin Pop
5282 53c776b5 Iustin Pop
    if runningon_source and runningon_target:
5283 53c776b5 Iustin Pop
      raise errors.OpExecError("Instance seems to be running on two nodes,"
5284 53c776b5 Iustin Pop
                               " or the hypervisor is confused. You will have"
5285 53c776b5 Iustin Pop
                               " to ensure manually that it runs only on one"
5286 53c776b5 Iustin Pop
                               " and restart this operation.")
5287 53c776b5 Iustin Pop
5288 53c776b5 Iustin Pop
    if not (runningon_source or runningon_target):
5289 53c776b5 Iustin Pop
      raise errors.OpExecError("Instance does not seem to be running at all."
5290 53c776b5 Iustin Pop
                               " In this case, it's safer to repair by"
5291 53c776b5 Iustin Pop
                               " running 'gnt-instance stop' to ensure disk"
5292 53c776b5 Iustin Pop
                               " shutdown, and then restarting it.")
5293 53c776b5 Iustin Pop
5294 53c776b5 Iustin Pop
    if runningon_target:
5295 53c776b5 Iustin Pop
      # the migration has actually succeeded, we need to update the config
5296 53c776b5 Iustin Pop
      self.feedback_fn("* instance running on secondary node (%s),"
5297 53c776b5 Iustin Pop
                       " updating config" % target_node)
5298 53c776b5 Iustin Pop
      instance.primary_node = target_node
5299 a4eae71f Michael Hanselmann
      self.cfg.Update(instance, self.feedback_fn)
5300 53c776b5 Iustin Pop
      demoted_node = source_node
5301 53c776b5 Iustin Pop
    else:
5302 53c776b5 Iustin Pop
      self.feedback_fn("* instance confirmed to be running on its"
5303 53c776b5 Iustin Pop
                       " primary node (%s)" % source_node)
5304 53c776b5 Iustin Pop
      demoted_node = target_node
5305 53c776b5 Iustin Pop
5306 53c776b5 Iustin Pop
    self._EnsureSecondary(demoted_node)
5307 53c776b5 Iustin Pop
    try:
5308 53c776b5 Iustin Pop
      self._WaitUntilSync()
5309 53c776b5 Iustin Pop
    except errors.OpExecError:
5310 53c776b5 Iustin Pop
      # we ignore here errors, since if the device is standalone, it
5311 53c776b5 Iustin Pop
      # won't be able to sync
5312 53c776b5 Iustin Pop
      pass
5313 53c776b5 Iustin Pop
    self._GoStandalone()
5314 53c776b5 Iustin Pop
    self._GoReconnect(False)
5315 53c776b5 Iustin Pop
    self._WaitUntilSync()
5316 53c776b5 Iustin Pop
5317 53c776b5 Iustin Pop
    self.feedback_fn("* done")
5318 53c776b5 Iustin Pop
5319 6906a9d8 Guido Trotter
  def _RevertDiskStatus(self):
5320 6906a9d8 Guido Trotter
    """Try to revert the disk status after a failed migration.
5321 6906a9d8 Guido Trotter

5322 6906a9d8 Guido Trotter
    """
5323 6906a9d8 Guido Trotter
    target_node = self.target_node
5324 6906a9d8 Guido Trotter
    try:
5325 6906a9d8 Guido Trotter
      self._EnsureSecondary(target_node)
5326 6906a9d8 Guido Trotter
      self._GoStandalone()
5327 6906a9d8 Guido Trotter
      self._GoReconnect(False)
5328 6906a9d8 Guido Trotter
      self._WaitUntilSync()
5329 6906a9d8 Guido Trotter
    except errors.OpExecError, err:
5330 3e06e001 Michael Hanselmann
      self.lu.LogWarning("Migration failed and I can't reconnect the"
5331 3e06e001 Michael Hanselmann
                         " drives: error '%s'\n"
5332 3e06e001 Michael Hanselmann
                         "Please look and recover the instance status" %
5333 3e06e001 Michael Hanselmann
                         str(err))
5334 6906a9d8 Guido Trotter
5335 6906a9d8 Guido Trotter
  def _AbortMigration(self):
5336 6906a9d8 Guido Trotter
    """Call the hypervisor code to abort a started migration.
5337 6906a9d8 Guido Trotter

5338 6906a9d8 Guido Trotter
    """
5339 6906a9d8 Guido Trotter
    instance = self.instance
5340 6906a9d8 Guido Trotter
    target_node = self.target_node
5341 6906a9d8 Guido Trotter
    migration_info = self.migration_info
5342 6906a9d8 Guido Trotter
5343 6906a9d8 Guido Trotter
    abort_result = self.rpc.call_finalize_migration(target_node,
5344 6906a9d8 Guido Trotter
                                                    instance,
5345 6906a9d8 Guido Trotter
                                                    migration_info,
5346 6906a9d8 Guido Trotter
                                                    False)
5347 4c4e4e1e Iustin Pop
    abort_msg = abort_result.fail_msg
5348 6906a9d8 Guido Trotter
    if abort_msg:
5349 099c52ad Iustin Pop
      logging.error("Aborting migration failed on target node %s: %s",
5350 099c52ad Iustin Pop
                    target_node, abort_msg)
5351 6906a9d8 Guido Trotter
      # Don't raise an exception here, as we stil have to try to revert the
5352 6906a9d8 Guido Trotter
      # disk status, even if this step failed.
5353 6906a9d8 Guido Trotter
5354 53c776b5 Iustin Pop
  def _ExecMigration(self):
5355 53c776b5 Iustin Pop
    """Migrate an instance.
5356 53c776b5 Iustin Pop

5357 53c776b5 Iustin Pop
    The migrate is done by:
5358 53c776b5 Iustin Pop
      - change the disks into dual-master mode
5359 53c776b5 Iustin Pop
      - wait until disks are fully synchronized again
5360 53c776b5 Iustin Pop
      - migrate the instance
5361 53c776b5 Iustin Pop
      - change disks on the new secondary node (the old primary) to secondary
5362 53c776b5 Iustin Pop
      - wait until disks are fully synchronized
5363 53c776b5 Iustin Pop
      - change disks into single-master mode
5364 53c776b5 Iustin Pop

5365 53c776b5 Iustin Pop
    """
5366 53c776b5 Iustin Pop
    instance = self.instance
5367 53c776b5 Iustin Pop
    target_node = self.target_node
5368 53c776b5 Iustin Pop
    source_node = self.source_node
5369 53c776b5 Iustin Pop
5370 53c776b5 Iustin Pop
    self.feedback_fn("* checking disk consistency between source and target")
5371 53c776b5 Iustin Pop
    for dev in instance.disks:
5372 53c776b5 Iustin Pop
      if not _CheckDiskConsistency(self, dev, target_node, False):
5373 53c776b5 Iustin Pop
        raise errors.OpExecError("Disk %s is degraded or not fully"
5374 53c776b5 Iustin Pop
                                 " synchronized on target node,"
5375 53c776b5 Iustin Pop
                                 " aborting migrate." % dev.iv_name)
5376 53c776b5 Iustin Pop
5377 6906a9d8 Guido Trotter
    # First get the migration information from the remote node
5378 6906a9d8 Guido Trotter
    result = self.rpc.call_migration_info(source_node, instance)
5379 4c4e4e1e Iustin Pop
    msg = result.fail_msg
5380 6906a9d8 Guido Trotter
    if msg:
5381 6906a9d8 Guido Trotter
      log_err = ("Failed fetching source migration information from %s: %s" %
5382 0959c824 Iustin Pop
                 (source_node, msg))
5383 6906a9d8 Guido Trotter
      logging.error(log_err)
5384 6906a9d8 Guido Trotter
      raise errors.OpExecError(log_err)
5385 6906a9d8 Guido Trotter
5386 0959c824 Iustin Pop
    self.migration_info = migration_info = result.payload
5387 6906a9d8 Guido Trotter
5388 6906a9d8 Guido Trotter
    # Then switch the disks to master/master mode
5389 53c776b5 Iustin Pop
    self._EnsureSecondary(target_node)
5390 53c776b5 Iustin Pop
    self._GoStandalone()
5391 53c776b5 Iustin Pop
    self._GoReconnect(True)
5392 53c776b5 Iustin Pop
    self._WaitUntilSync()
5393 53c776b5 Iustin Pop
5394 6906a9d8 Guido Trotter
    self.feedback_fn("* preparing %s to accept the instance" % target_node)
5395 6906a9d8 Guido Trotter
    result = self.rpc.call_accept_instance(target_node,
5396 6906a9d8 Guido Trotter
                                           instance,
5397 6906a9d8 Guido Trotter
                                           migration_info,
5398 6906a9d8 Guido Trotter
                                           self.nodes_ip[target_node])
5399 6906a9d8 Guido Trotter
5400 4c4e4e1e Iustin Pop
    msg = result.fail_msg
5401 6906a9d8 Guido Trotter
    if msg:
5402 6906a9d8 Guido Trotter
      logging.error("Instance pre-migration failed, trying to revert"
5403 6906a9d8 Guido Trotter
                    " disk status: %s", msg)
5404 78212a5d Iustin Pop
      self.feedback_fn("Pre-migration failed, aborting")
5405 6906a9d8 Guido Trotter
      self._AbortMigration()
5406 6906a9d8 Guido Trotter
      self._RevertDiskStatus()
5407 6906a9d8 Guido Trotter
      raise errors.OpExecError("Could not pre-migrate instance %s: %s" %
5408 6906a9d8 Guido Trotter
                               (instance.name, msg))
5409 6906a9d8 Guido Trotter
5410 53c776b5 Iustin Pop
    self.feedback_fn("* migrating instance to %s" % target_node)
5411 53c776b5 Iustin Pop
    time.sleep(10)
5412 53c776b5 Iustin Pop
    result = self.rpc.call_instance_migrate(source_node, instance,
5413 53c776b5 Iustin Pop
                                            self.nodes_ip[target_node],
5414 3e06e001 Michael Hanselmann
                                            self.live)
5415 4c4e4e1e Iustin Pop
    msg = result.fail_msg
5416 53c776b5 Iustin Pop
    if msg:
5417 53c776b5 Iustin Pop
      logging.error("Instance migration failed, trying to revert"
5418 53c776b5 Iustin Pop
                    " disk status: %s", msg)
5419 78212a5d Iustin Pop
      self.feedback_fn("Migration failed, aborting")
5420 6906a9d8 Guido Trotter
      self._AbortMigration()
5421 6906a9d8 Guido Trotter
      self._RevertDiskStatus()
5422 53c776b5 Iustin Pop
      raise errors.OpExecError("Could not migrate instance %s: %s" %
5423 53c776b5 Iustin Pop
                               (instance.name, msg))
5424 53c776b5 Iustin Pop
    time.sleep(10)
5425 53c776b5 Iustin Pop
5426 53c776b5 Iustin Pop
    instance.primary_node = target_node
5427 53c776b5 Iustin Pop
    # distribute new instance config to the other nodes
5428 a4eae71f Michael Hanselmann
    self.cfg.Update(instance, self.feedback_fn)
5429 53c776b5 Iustin Pop
5430 6906a9d8 Guido Trotter
    result = self.rpc.call_finalize_migration(target_node,
5431 6906a9d8 Guido Trotter
                                              instance,
5432 6906a9d8 Guido Trotter
                                              migration_info,
5433 6906a9d8 Guido Trotter
                                              True)
5434 4c4e4e1e Iustin Pop
    msg = result.fail_msg
5435 6906a9d8 Guido Trotter
    if msg:
5436 6906a9d8 Guido Trotter
      logging.error("Instance migration succeeded, but finalization failed:"
5437 099c52ad Iustin Pop
                    " %s", msg)
5438 6906a9d8 Guido Trotter
      raise errors.OpExecError("Could not finalize instance migration: %s" %
5439 6906a9d8 Guido Trotter
                               msg)
5440 6906a9d8 Guido Trotter
5441 53c776b5 Iustin Pop
    self._EnsureSecondary(source_node)
5442 53c776b5 Iustin Pop
    self._WaitUntilSync()
5443 53c776b5 Iustin Pop
    self._GoStandalone()
5444 53c776b5 Iustin Pop
    self._GoReconnect(False)
5445 53c776b5 Iustin Pop
    self._WaitUntilSync()
5446 53c776b5 Iustin Pop
5447 53c776b5 Iustin Pop
    self.feedback_fn("* done")
5448 53c776b5 Iustin Pop
5449 53c776b5 Iustin Pop
  def Exec(self, feedback_fn):
5450 53c776b5 Iustin Pop
    """Perform the migration.
5451 53c776b5 Iustin Pop

5452 53c776b5 Iustin Pop
    """
5453 80cb875c Michael Hanselmann
    feedback_fn("Migrating instance %s" % self.instance.name)
5454 80cb875c Michael Hanselmann
5455 53c776b5 Iustin Pop
    self.feedback_fn = feedback_fn
5456 53c776b5 Iustin Pop
5457 53c776b5 Iustin Pop
    self.source_node = self.instance.primary_node
5458 53c776b5 Iustin Pop
    self.target_node = self.instance.secondary_nodes[0]
5459 53c776b5 Iustin Pop
    self.all_nodes = [self.source_node, self.target_node]
5460 53c776b5 Iustin Pop
    self.nodes_ip = {
5461 53c776b5 Iustin Pop
      self.source_node: self.cfg.GetNodeInfo(self.source_node).secondary_ip,
5462 53c776b5 Iustin Pop
      self.target_node: self.cfg.GetNodeInfo(self.target_node).secondary_ip,
5463 53c776b5 Iustin Pop
      }
5464 3e06e001 Michael Hanselmann
5465 3e06e001 Michael Hanselmann
    if self.cleanup:
5466 53c776b5 Iustin Pop
      return self._ExecCleanup()
5467 53c776b5 Iustin Pop
    else:
5468 53c776b5 Iustin Pop
      return self._ExecMigration()
5469 53c776b5 Iustin Pop
5470 53c776b5 Iustin Pop
5471 428958aa Iustin Pop
def _CreateBlockDev(lu, node, instance, device, force_create,
5472 428958aa Iustin Pop
                    info, force_open):
5473 428958aa Iustin Pop
  """Create a tree of block devices on a given node.
5474 a8083063 Iustin Pop

5475 a8083063 Iustin Pop
  If this device type has to be created on secondaries, create it and
5476 a8083063 Iustin Pop
  all its children.
5477 a8083063 Iustin Pop

5478 a8083063 Iustin Pop
  If not, just recurse to children keeping the same 'force' value.
5479 a8083063 Iustin Pop

5480 428958aa Iustin Pop
  @param lu: the lu on whose behalf we execute
5481 428958aa Iustin Pop
  @param node: the node on which to create the device
5482 428958aa Iustin Pop
  @type instance: L{objects.Instance}
5483 428958aa Iustin Pop
  @param instance: the instance which owns the device
5484 428958aa Iustin Pop
  @type device: L{objects.Disk}
5485 428958aa Iustin Pop
  @param device: the device to create
5486 428958aa Iustin Pop
  @type force_create: boolean
5487 428958aa Iustin Pop
  @param force_create: whether to force creation of this device; this
5488 428958aa Iustin Pop
      will be change to True whenever we find a device which has
5489 428958aa Iustin Pop
      CreateOnSecondary() attribute
5490 428958aa Iustin Pop
  @param info: the extra 'metadata' we should attach to the device
5491 428958aa Iustin Pop
      (this will be represented as a LVM tag)
5492 428958aa Iustin Pop
  @type force_open: boolean
5493 428958aa Iustin Pop
  @param force_open: this parameter will be passes to the
5494 821d1bd1 Iustin Pop
      L{backend.BlockdevCreate} function where it specifies
5495 428958aa Iustin Pop
      whether we run on primary or not, and it affects both
5496 428958aa Iustin Pop
      the child assembly and the device own Open() execution
5497 428958aa Iustin Pop

5498 a8083063 Iustin Pop
  """
5499 a8083063 Iustin Pop
  if device.CreateOnSecondary():
5500 428958aa Iustin Pop
    force_create = True
5501 796cab27 Iustin Pop
5502 a8083063 Iustin Pop
  if device.children:
5503 a8083063 Iustin Pop
    for child in device.children:
5504 428958aa Iustin Pop
      _CreateBlockDev(lu, node, instance, child, force_create,
5505 428958aa Iustin Pop
                      info, force_open)
5506 a8083063 Iustin Pop
5507 428958aa Iustin Pop
  if not force_create:
5508 796cab27 Iustin Pop
    return
5509 796cab27 Iustin Pop
5510 de12473a Iustin Pop
  _CreateSingleBlockDev(lu, node, instance, device, info, force_open)
5511 de12473a Iustin Pop
5512 de12473a Iustin Pop
5513 de12473a Iustin Pop
def _CreateSingleBlockDev(lu, node, instance, device, info, force_open):
5514 de12473a Iustin Pop
  """Create a single block device on a given node.
5515 de12473a Iustin Pop

5516 de12473a Iustin Pop
  This will not recurse over children of the device, so they must be
5517 de12473a Iustin Pop
  created in advance.
5518 de12473a Iustin Pop

5519 de12473a Iustin Pop
  @param lu: the lu on whose behalf we execute
5520 de12473a Iustin Pop
  @param node: the node on which to create the device
5521 de12473a Iustin Pop
  @type instance: L{objects.Instance}
5522 de12473a Iustin Pop
  @param instance: the instance which owns the device
5523 de12473a Iustin Pop
  @type device: L{objects.Disk}
5524 de12473a Iustin Pop
  @param device: the device to create
5525 de12473a Iustin Pop
  @param info: the extra 'metadata' we should attach to the device
5526 de12473a Iustin Pop
      (this will be represented as a LVM tag)
5527 de12473a Iustin Pop
  @type force_open: boolean
5528 de12473a Iustin Pop
  @param force_open: this parameter will be passes to the
5529 821d1bd1 Iustin Pop
      L{backend.BlockdevCreate} function where it specifies
5530 de12473a Iustin Pop
      whether we run on primary or not, and it affects both
5531 de12473a Iustin Pop
      the child assembly and the device own Open() execution
5532 de12473a Iustin Pop

5533 de12473a Iustin Pop
  """
5534 b9bddb6b Iustin Pop
  lu.cfg.SetDiskID(device, node)
5535 7d81697f Iustin Pop
  result = lu.rpc.call_blockdev_create(node, device, device.size,
5536 428958aa Iustin Pop
                                       instance.name, force_open, info)
5537 4c4e4e1e Iustin Pop
  result.Raise("Can't create block device %s on"
5538 4c4e4e1e Iustin Pop
               " node %s for instance %s" % (device, node, instance.name))
5539 a8083063 Iustin Pop
  if device.physical_id is None:
5540 0959c824 Iustin Pop
    device.physical_id = result.payload
5541 a8083063 Iustin Pop
5542 a8083063 Iustin Pop
5543 b9bddb6b Iustin Pop
def _GenerateUniqueNames(lu, exts):
5544 923b1523 Iustin Pop
  """Generate a suitable LV name.
5545 923b1523 Iustin Pop

5546 923b1523 Iustin Pop
  This will generate a logical volume name for the given instance.
5547 923b1523 Iustin Pop

5548 923b1523 Iustin Pop
  """
5549 923b1523 Iustin Pop
  results = []
5550 923b1523 Iustin Pop
  for val in exts:
5551 4fae38c5 Guido Trotter
    new_id = lu.cfg.GenerateUniqueID(lu.proc.GetECId())
5552 923b1523 Iustin Pop
    results.append("%s%s" % (new_id, val))
5553 923b1523 Iustin Pop
  return results
5554 923b1523 Iustin Pop
5555 923b1523 Iustin Pop
5556 b9bddb6b Iustin Pop
def _GenerateDRBD8Branch(lu, primary, secondary, size, names, iv_name,
5557 ffa1c0dc Iustin Pop
                         p_minor, s_minor):
5558 a1f445d3 Iustin Pop
  """Generate a drbd8 device complete with its children.
5559 a1f445d3 Iustin Pop

5560 a1f445d3 Iustin Pop
  """
5561 b9bddb6b Iustin Pop
  port = lu.cfg.AllocatePort()
5562 b9bddb6b Iustin Pop
  vgname = lu.cfg.GetVGName()
5563 afa1386e Guido Trotter
  shared_secret = lu.cfg.GenerateDRBDSecret(lu.proc.GetECId())
5564 a1f445d3 Iustin Pop
  dev_data = objects.Disk(dev_type=constants.LD_LV, size=size,
5565 a1f445d3 Iustin Pop
                          logical_id=(vgname, names[0]))
5566 a1f445d3 Iustin Pop
  dev_meta = objects.Disk(dev_type=constants.LD_LV, size=128,
5567 a1f445d3 Iustin Pop
                          logical_id=(vgname, names[1]))
5568 a1f445d3 Iustin Pop
  drbd_dev = objects.Disk(dev_type=constants.LD_DRBD8, size=size,
5569 ffa1c0dc Iustin Pop
                          logical_id=(primary, secondary, port,
5570 f9518d38 Iustin Pop
                                      p_minor, s_minor,
5571 f9518d38 Iustin Pop
                                      shared_secret),
5572 ffa1c0dc Iustin Pop
                          children=[dev_data, dev_meta],
5573 a1f445d3 Iustin Pop
                          iv_name=iv_name)
5574 a1f445d3 Iustin Pop
  return drbd_dev
5575 a1f445d3 Iustin Pop
5576 7c0d6283 Michael Hanselmann
5577 b9bddb6b Iustin Pop
def _GenerateDiskTemplate(lu, template_name,
5578 a8083063 Iustin Pop
                          instance_name, primary_node,
5579 08db7c5c Iustin Pop
                          secondary_nodes, disk_info,
5580 e2a65344 Iustin Pop
                          file_storage_dir, file_driver,
5581 e2a65344 Iustin Pop
                          base_index):
5582 a8083063 Iustin Pop
  """Generate the entire disk layout for a given template type.
5583 a8083063 Iustin Pop

5584 a8083063 Iustin Pop
  """
5585 a8083063 Iustin Pop
  #TODO: compute space requirements
5586 a8083063 Iustin Pop
5587 b9bddb6b Iustin Pop
  vgname = lu.cfg.GetVGName()
5588 08db7c5c Iustin Pop
  disk_count = len(disk_info)
5589 08db7c5c Iustin Pop
  disks = []
5590 3517d9b9 Manuel Franceschini
  if template_name == constants.DT_DISKLESS:
5591 08db7c5c Iustin Pop
    pass
5592 3517d9b9 Manuel Franceschini
  elif template_name == constants.DT_PLAIN:
5593 a8083063 Iustin Pop
    if len(secondary_nodes) != 0:
5594 a8083063 Iustin Pop
      raise errors.ProgrammerError("Wrong template configuration")
5595 923b1523 Iustin Pop
5596 fb4b324b Guido Trotter
    names = _GenerateUniqueNames(lu, [".disk%d" % (base_index + i)
5597 08db7c5c Iustin Pop
                                      for i in range(disk_count)])
5598 08db7c5c Iustin Pop
    for idx, disk in enumerate(disk_info):
5599 e2a65344 Iustin Pop
      disk_index = idx + base_index
5600 08db7c5c Iustin Pop
      disk_dev = objects.Disk(dev_type=constants.LD_LV, size=disk["size"],
5601 08db7c5c Iustin Pop
                              logical_id=(vgname, names[idx]),
5602 6ec66eae Iustin Pop
                              iv_name="disk/%d" % disk_index,
5603 6ec66eae Iustin Pop
                              mode=disk["mode"])
5604 08db7c5c Iustin Pop
      disks.append(disk_dev)
5605 a1f445d3 Iustin Pop
  elif template_name == constants.DT_DRBD8:
5606 a1f445d3 Iustin Pop
    if len(secondary_nodes) != 1:
5607 a1f445d3 Iustin Pop
      raise errors.ProgrammerError("Wrong template configuration")
5608 a1f445d3 Iustin Pop
    remote_node = secondary_nodes[0]
5609 08db7c5c Iustin Pop
    minors = lu.cfg.AllocateDRBDMinor(
5610 08db7c5c Iustin Pop
      [primary_node, remote_node] * len(disk_info), instance_name)
5611 08db7c5c Iustin Pop
5612 e6c1ff2f Iustin Pop
    names = []
5613 fb4b324b Guido Trotter
    for lv_prefix in _GenerateUniqueNames(lu, [".disk%d" % (base_index + i)
5614 e6c1ff2f Iustin Pop
                                               for i in range(disk_count)]):
5615 e6c1ff2f Iustin Pop
      names.append(lv_prefix + "_data")
5616 e6c1ff2f Iustin Pop
      names.append(lv_prefix + "_meta")
5617 08db7c5c Iustin Pop
    for idx, disk in enumerate(disk_info):
5618 112050d9 Iustin Pop
      disk_index = idx + base_index
5619 08db7c5c Iustin Pop
      disk_dev = _GenerateDRBD8Branch(lu, primary_node, remote_node,
5620 08db7c5c Iustin Pop
                                      disk["size"], names[idx*2:idx*2+2],
5621 e2a65344 Iustin Pop
                                      "disk/%d" % disk_index,
5622 08db7c5c Iustin Pop
                                      minors[idx*2], minors[idx*2+1])
5623 6ec66eae Iustin Pop
      disk_dev.mode = disk["mode"]
5624 08db7c5c Iustin Pop
      disks.append(disk_dev)
5625 0f1a06e3 Manuel Franceschini
  elif template_name == constants.DT_FILE:
5626 0f1a06e3 Manuel Franceschini
    if len(secondary_nodes) != 0:
5627 0f1a06e3 Manuel Franceschini
      raise errors.ProgrammerError("Wrong template configuration")
5628 0f1a06e3 Manuel Franceschini
5629 08db7c5c Iustin Pop
    for idx, disk in enumerate(disk_info):
5630 112050d9 Iustin Pop
      disk_index = idx + base_index
5631 08db7c5c Iustin Pop
      disk_dev = objects.Disk(dev_type=constants.LD_FILE, size=disk["size"],
5632 e2a65344 Iustin Pop
                              iv_name="disk/%d" % disk_index,
5633 08db7c5c Iustin Pop
                              logical_id=(file_driver,
5634 08db7c5c Iustin Pop
                                          "%s/disk%d" % (file_storage_dir,
5635 43e99cff Guido Trotter
                                                         disk_index)),
5636 6ec66eae Iustin Pop
                              mode=disk["mode"])
5637 08db7c5c Iustin Pop
      disks.append(disk_dev)
5638 a8083063 Iustin Pop
  else:
5639 a8083063 Iustin Pop
    raise errors.ProgrammerError("Invalid disk template '%s'" % template_name)
5640 a8083063 Iustin Pop
  return disks
5641 a8083063 Iustin Pop
5642 a8083063 Iustin Pop
5643 a0c3fea1 Michael Hanselmann
def _GetInstanceInfoText(instance):
5644 3ecf6786 Iustin Pop
  """Compute that text that should be added to the disk's metadata.
5645 3ecf6786 Iustin Pop

5646 3ecf6786 Iustin Pop
  """
5647 a0c3fea1 Michael Hanselmann
  return "originstname+%s" % instance.name
5648 a0c3fea1 Michael Hanselmann
5649 a0c3fea1 Michael Hanselmann
5650 621b7678 Iustin Pop
def _CreateDisks(lu, instance, to_skip=None, target_node=None):
5651 a8083063 Iustin Pop
  """Create all disks for an instance.
5652 a8083063 Iustin Pop

5653 a8083063 Iustin Pop
  This abstracts away some work from AddInstance.
5654 a8083063 Iustin Pop

5655 e4376078 Iustin Pop
  @type lu: L{LogicalUnit}
5656 e4376078 Iustin Pop
  @param lu: the logical unit on whose behalf we execute
5657 e4376078 Iustin Pop
  @type instance: L{objects.Instance}
5658 e4376078 Iustin Pop
  @param instance: the instance whose disks we should create
5659 bd315bfa Iustin Pop
  @type to_skip: list
5660 bd315bfa Iustin Pop
  @param to_skip: list of indices to skip
5661 621b7678 Iustin Pop
  @type target_node: string
5662 621b7678 Iustin Pop
  @param target_node: if passed, overrides the target node for creation
5663 e4376078 Iustin Pop
  @rtype: boolean
5664 e4376078 Iustin Pop
  @return: the success of the creation
5665 a8083063 Iustin Pop

5666 a8083063 Iustin Pop
  """
5667 a0c3fea1 Michael Hanselmann
  info = _GetInstanceInfoText(instance)
5668 621b7678 Iustin Pop
  if target_node is None:
5669 621b7678 Iustin Pop
    pnode = instance.primary_node
5670 621b7678 Iustin Pop
    all_nodes = instance.all_nodes
5671 621b7678 Iustin Pop
  else:
5672 621b7678 Iustin Pop
    pnode = target_node
5673 621b7678 Iustin Pop
    all_nodes = [pnode]
5674 a0c3fea1 Michael Hanselmann
5675 0f1a06e3 Manuel Franceschini
  if instance.disk_template == constants.DT_FILE:
5676 0f1a06e3 Manuel Franceschini
    file_storage_dir = os.path.dirname(instance.disks[0].logical_id[1])
5677 428958aa Iustin Pop
    result = lu.rpc.call_file_storage_dir_create(pnode, file_storage_dir)
5678 0f1a06e3 Manuel Franceschini
5679 4c4e4e1e Iustin Pop
    result.Raise("Failed to create directory '%s' on"
5680 9b4127eb Guido Trotter
                 " node %s" % (file_storage_dir, pnode))
5681 0f1a06e3 Manuel Franceschini
5682 24991749 Iustin Pop
  # Note: this needs to be kept in sync with adding of disks in
5683 24991749 Iustin Pop
  # LUSetInstanceParams
5684 bd315bfa Iustin Pop
  for idx, device in enumerate(instance.disks):
5685 bd315bfa Iustin Pop
    if to_skip and idx in to_skip:
5686 bd315bfa Iustin Pop
      continue
5687 9a4f63d1 Iustin Pop
    logging.info("Creating volume %s for instance %s",
5688 9a4f63d1 Iustin Pop
                 device.iv_name, instance.name)
5689 a8083063 Iustin Pop
    #HARDCODE
5690 621b7678 Iustin Pop
    for node in all_nodes:
5691 428958aa Iustin Pop
      f_create = node == pnode
5692 428958aa Iustin Pop
      _CreateBlockDev(lu, node, instance, device, f_create, info, f_create)
5693 a8083063 Iustin Pop
5694 a8083063 Iustin Pop
5695 621b7678 Iustin Pop
def _RemoveDisks(lu, instance, target_node=None):
5696 a8083063 Iustin Pop
  """Remove all disks for an instance.
5697 a8083063 Iustin Pop

5698 a8083063 Iustin Pop
  This abstracts away some work from `AddInstance()` and
5699 a8083063 Iustin Pop
  `RemoveInstance()`. Note that in case some of the devices couldn't
5700 1d67656e Iustin Pop
  be removed, the removal will continue with the other ones (compare
5701 a8083063 Iustin Pop
  with `_CreateDisks()`).
5702 a8083063 Iustin Pop

5703 e4376078 Iustin Pop
  @type lu: L{LogicalUnit}
5704 e4376078 Iustin Pop
  @param lu: the logical unit on whose behalf we execute
5705 e4376078 Iustin Pop
  @type instance: L{objects.Instance}
5706 e4376078 Iustin Pop
  @param instance: the instance whose disks we should remove
5707 621b7678 Iustin Pop
  @type target_node: string
5708 621b7678 Iustin Pop
  @param target_node: used to override the node on which to remove the disks
5709 e4376078 Iustin Pop
  @rtype: boolean
5710 e4376078 Iustin Pop
  @return: the success of the removal
5711 a8083063 Iustin Pop

5712 a8083063 Iustin Pop
  """
5713 9a4f63d1 Iustin Pop
  logging.info("Removing block devices for instance %s", instance.name)
5714 a8083063 Iustin Pop
5715 e1bc0878 Iustin Pop
  all_result = True
5716 a8083063 Iustin Pop
  for device in instance.disks:
5717 621b7678 Iustin Pop
    if target_node:
5718 621b7678 Iustin Pop
      edata = [(target_node, device)]
5719 621b7678 Iustin Pop
    else:
5720 621b7678 Iustin Pop
      edata = device.ComputeNodeTree(instance.primary_node)
5721 621b7678 Iustin Pop
    for node, disk in edata:
5722 b9bddb6b Iustin Pop
      lu.cfg.SetDiskID(disk, node)
5723 4c4e4e1e Iustin Pop
      msg = lu.rpc.call_blockdev_remove(node, disk).fail_msg
5724 e1bc0878 Iustin Pop
      if msg:
5725 e1bc0878 Iustin Pop
        lu.LogWarning("Could not remove block device %s on node %s,"
5726 e1bc0878 Iustin Pop
                      " continuing anyway: %s", device.iv_name, node, msg)
5727 e1bc0878 Iustin Pop
        all_result = False
5728 0f1a06e3 Manuel Franceschini
5729 0f1a06e3 Manuel Franceschini
  if instance.disk_template == constants.DT_FILE:
5730 0f1a06e3 Manuel Franceschini
    file_storage_dir = os.path.dirname(instance.disks[0].logical_id[1])
5731 dfc2a24c Guido Trotter
    if target_node:
5732 dfc2a24c Guido Trotter
      tgt = target_node
5733 621b7678 Iustin Pop
    else:
5734 dfc2a24c Guido Trotter
      tgt = instance.primary_node
5735 621b7678 Iustin Pop
    result = lu.rpc.call_file_storage_dir_remove(tgt, file_storage_dir)
5736 621b7678 Iustin Pop
    if result.fail_msg:
5737 b2b8bcce Iustin Pop
      lu.LogWarning("Could not remove directory '%s' on node %s: %s",
5738 621b7678 Iustin Pop
                    file_storage_dir, instance.primary_node, result.fail_msg)
5739 e1bc0878 Iustin Pop
      all_result = False
5740 0f1a06e3 Manuel Franceschini
5741 e1bc0878 Iustin Pop
  return all_result
5742 a8083063 Iustin Pop
5743 a8083063 Iustin Pop
5744 08db7c5c Iustin Pop
def _ComputeDiskSize(disk_template, disks):
5745 e2fe6369 Iustin Pop
  """Compute disk size requirements in the volume group
5746 e2fe6369 Iustin Pop

5747 e2fe6369 Iustin Pop
  """
5748 e2fe6369 Iustin Pop
  # Required free disk space as a function of disk and swap space
5749 e2fe6369 Iustin Pop
  req_size_dict = {
5750 e2fe6369 Iustin Pop
    constants.DT_DISKLESS: None,
5751 08db7c5c Iustin Pop
    constants.DT_PLAIN: sum(d["size"] for d in disks),
5752 08db7c5c Iustin Pop
    # 128 MB are added for drbd metadata for each disk
5753 08db7c5c Iustin Pop
    constants.DT_DRBD8: sum(d["size"] + 128 for d in disks),
5754 e2fe6369 Iustin Pop
    constants.DT_FILE: None,
5755 e2fe6369 Iustin Pop
  }
5756 e2fe6369 Iustin Pop
5757 e2fe6369 Iustin Pop
  if disk_template not in req_size_dict:
5758 e2fe6369 Iustin Pop
    raise errors.ProgrammerError("Disk template '%s' size requirement"
5759 e2fe6369 Iustin Pop
                                 " is unknown" %  disk_template)
5760 e2fe6369 Iustin Pop
5761 e2fe6369 Iustin Pop
  return req_size_dict[disk_template]
5762 e2fe6369 Iustin Pop
5763 e2fe6369 Iustin Pop
5764 74409b12 Iustin Pop
def _CheckHVParams(lu, nodenames, hvname, hvparams):
5765 74409b12 Iustin Pop
  """Hypervisor parameter validation.
5766 74409b12 Iustin Pop

5767 74409b12 Iustin Pop
  This function abstract the hypervisor parameter validation to be
5768 74409b12 Iustin Pop
  used in both instance create and instance modify.
5769 74409b12 Iustin Pop

5770 74409b12 Iustin Pop
  @type lu: L{LogicalUnit}
5771 74409b12 Iustin Pop
  @param lu: the logical unit for which we check
5772 74409b12 Iustin Pop
  @type nodenames: list
5773 74409b12 Iustin Pop
  @param nodenames: the list of nodes on which we should check
5774 74409b12 Iustin Pop
  @type hvname: string
5775 74409b12 Iustin Pop
  @param hvname: the name of the hypervisor we should use
5776 74409b12 Iustin Pop
  @type hvparams: dict
5777 74409b12 Iustin Pop
  @param hvparams: the parameters which we need to check
5778 74409b12 Iustin Pop
  @raise errors.OpPrereqError: if the parameters are not valid
5779 74409b12 Iustin Pop

5780 74409b12 Iustin Pop
  """
5781 74409b12 Iustin Pop
  hvinfo = lu.rpc.call_hypervisor_validate_params(nodenames,
5782 74409b12 Iustin Pop
                                                  hvname,
5783 74409b12 Iustin Pop
                                                  hvparams)
5784 74409b12 Iustin Pop
  for node in nodenames:
5785 781de953 Iustin Pop
    info = hvinfo[node]
5786 68c6f21c Iustin Pop
    if info.offline:
5787 68c6f21c Iustin Pop
      continue
5788 4c4e4e1e Iustin Pop
    info.Raise("Hypervisor parameter validation failed on node %s" % node)
5789 74409b12 Iustin Pop
5790 74409b12 Iustin Pop
5791 a8083063 Iustin Pop
class LUCreateInstance(LogicalUnit):
5792 a8083063 Iustin Pop
  """Create an instance.
5793 a8083063 Iustin Pop

5794 a8083063 Iustin Pop
  """
5795 a8083063 Iustin Pop
  HPATH = "instance-add"
5796 a8083063 Iustin Pop
  HTYPE = constants.HTYPE_INSTANCE
5797 08db7c5c Iustin Pop
  _OP_REQP = ["instance_name", "disks", "disk_template",
5798 08db7c5c Iustin Pop
              "mode", "start",
5799 08db7c5c Iustin Pop
              "wait_for_sync", "ip_check", "nics",
5800 338e51e8 Iustin Pop
              "hvparams", "beparams"]
5801 7baf741d Guido Trotter
  REQ_BGL = False
5802 7baf741d Guido Trotter
5803 5f23e043 Iustin Pop
  def CheckArguments(self):
5804 5f23e043 Iustin Pop
    """Check arguments.
5805 5f23e043 Iustin Pop

5806 5f23e043 Iustin Pop
    """
5807 df4272e5 Iustin Pop
    # set optional parameters to none if they don't exist
5808 df4272e5 Iustin Pop
    for attr in ["pnode", "snode", "iallocator", "hypervisor"]:
5809 df4272e5 Iustin Pop
      if not hasattr(self.op, attr):
5810 df4272e5 Iustin Pop
        setattr(self.op, attr, None)
5811 df4272e5 Iustin Pop
5812 5f23e043 Iustin Pop
    # do not require name_check to ease forward/backward compatibility
5813 5f23e043 Iustin Pop
    # for tools
5814 5f23e043 Iustin Pop
    if not hasattr(self.op, "name_check"):
5815 5f23e043 Iustin Pop
      self.op.name_check = True
5816 44caf5a8 Iustin Pop
    # validate/normalize the instance name
5817 44caf5a8 Iustin Pop
    self.op.instance_name = utils.HostInfo.NormalizeName(self.op.instance_name)
5818 5f23e043 Iustin Pop
    if self.op.ip_check and not self.op.name_check:
5819 5f23e043 Iustin Pop
      # TODO: make the ip check more flexible and not depend on the name check
5820 5f23e043 Iustin Pop
      raise errors.OpPrereqError("Cannot do ip checks without a name check",
5821 5f23e043 Iustin Pop
                                 errors.ECODE_INVAL)
5822 cb7c0198 Iustin Pop
    if (self.op.disk_template == constants.DT_FILE and
5823 cb7c0198 Iustin Pop
        not constants.ENABLE_FILE_STORAGE):
5824 cb7c0198 Iustin Pop
      raise errors.OpPrereqError("File storage disabled at configure time",
5825 cb7c0198 Iustin Pop
                                 errors.ECODE_INVAL)
5826 c3589cf8 Iustin Pop
    # check disk information: either all adopt, or no adopt
5827 c3589cf8 Iustin Pop
    has_adopt = has_no_adopt = False
5828 c3589cf8 Iustin Pop
    for disk in self.op.disks:
5829 c3589cf8 Iustin Pop
      if "adopt" in disk:
5830 c3589cf8 Iustin Pop
        has_adopt = True
5831 c3589cf8 Iustin Pop
      else:
5832 c3589cf8 Iustin Pop
        has_no_adopt = True
5833 c3589cf8 Iustin Pop
    if has_adopt and has_no_adopt:
5834 c3589cf8 Iustin Pop
      raise errors.OpPrereqError("Either all disks have are adoped or none is",
5835 c3589cf8 Iustin Pop
                                 errors.ECODE_INVAL)
5836 c3589cf8 Iustin Pop
    if has_adopt:
5837 c3589cf8 Iustin Pop
      if self.op.disk_template != constants.DT_PLAIN:
5838 c3589cf8 Iustin Pop
        raise errors.OpPrereqError("Disk adoption is only supported for the"
5839 c3589cf8 Iustin Pop
                                   " 'plain' disk template",
5840 c3589cf8 Iustin Pop
                                   errors.ECODE_INVAL)
5841 c3589cf8 Iustin Pop
      if self.op.iallocator is not None:
5842 c3589cf8 Iustin Pop
        raise errors.OpPrereqError("Disk adoption not allowed with an"
5843 c3589cf8 Iustin Pop
                                   " iallocator script", errors.ECODE_INVAL)
5844 c3589cf8 Iustin Pop
      if self.op.mode == constants.INSTANCE_IMPORT:
5845 c3589cf8 Iustin Pop
        raise errors.OpPrereqError("Disk adoption not allowed for"
5846 c3589cf8 Iustin Pop
                                   " instance import", errors.ECODE_INVAL)
5847 c3589cf8 Iustin Pop
5848 c3589cf8 Iustin Pop
    self.adopt_disks = has_adopt
5849 5f23e043 Iustin Pop
5850 7baf741d Guido Trotter
  def ExpandNames(self):
5851 7baf741d Guido Trotter
    """ExpandNames for CreateInstance.
5852 7baf741d Guido Trotter

5853 7baf741d Guido Trotter
    Figure out the right locks for instance creation.
5854 7baf741d Guido Trotter

5855 7baf741d Guido Trotter
    """
5856 7baf741d Guido Trotter
    self.needed_locks = {}
5857 7baf741d Guido Trotter
5858 4b2f38dd Iustin Pop
    # cheap checks, mostly valid constants given
5859 4b2f38dd Iustin Pop
5860 7baf741d Guido Trotter
    # verify creation mode
5861 7baf741d Guido Trotter
    if self.op.mode not in (constants.INSTANCE_CREATE,
5862 7baf741d Guido Trotter
                            constants.INSTANCE_IMPORT):
5863 7baf741d Guido Trotter
      raise errors.OpPrereqError("Invalid instance creation mode '%s'" %
5864 5c983ee5 Iustin Pop
                                 self.op.mode, errors.ECODE_INVAL)
5865 4b2f38dd Iustin Pop
5866 7baf741d Guido Trotter
    # disk template and mirror node verification
5867 5d55819e Iustin Pop
    _CheckDiskTemplate(self.op.disk_template)
5868 7baf741d Guido Trotter
5869 4b2f38dd Iustin Pop
    if self.op.hypervisor is None:
5870 4b2f38dd Iustin Pop
      self.op.hypervisor = self.cfg.GetHypervisorType()
5871 4b2f38dd Iustin Pop
5872 8705eb96 Iustin Pop
    cluster = self.cfg.GetClusterInfo()
5873 8705eb96 Iustin Pop
    enabled_hvs = cluster.enabled_hypervisors
5874 4b2f38dd Iustin Pop
    if self.op.hypervisor not in enabled_hvs:
5875 4b2f38dd Iustin Pop
      raise errors.OpPrereqError("Selected hypervisor (%s) not enabled in the"
5876 4b2f38dd Iustin Pop
                                 " cluster (%s)" % (self.op.hypervisor,
5877 5c983ee5 Iustin Pop
                                  ",".join(enabled_hvs)),
5878 5c983ee5 Iustin Pop
                                 errors.ECODE_STATE)
5879 4b2f38dd Iustin Pop
5880 6785674e Iustin Pop
    # check hypervisor parameter syntax (locally)
5881 a5728081 Guido Trotter
    utils.ForceDictType(self.op.hvparams, constants.HVS_PARAMETER_TYPES)
5882 abe609b2 Guido Trotter
    filled_hvp = objects.FillDict(cluster.hvparams[self.op.hypervisor],
5883 8705eb96 Iustin Pop
                                  self.op.hvparams)
5884 6785674e Iustin Pop
    hv_type = hypervisor.GetHypervisor(self.op.hypervisor)
5885 8705eb96 Iustin Pop
    hv_type.CheckParameterSyntax(filled_hvp)
5886 67fc3042 Iustin Pop
    self.hv_full = filled_hvp
5887 7736a5f2 Iustin Pop
    # check that we don't specify global parameters on an instance
5888 7736a5f2 Iustin Pop
    _CheckGlobalHvParams(self.op.hvparams)
5889 6785674e Iustin Pop
5890 338e51e8 Iustin Pop
    # fill and remember the beparams dict
5891 a5728081 Guido Trotter
    utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
5892 4ef7f423 Guido Trotter
    self.be_full = objects.FillDict(cluster.beparams[constants.PP_DEFAULT],
5893 338e51e8 Iustin Pop
                                    self.op.beparams)
5894 338e51e8 Iustin Pop
5895 7baf741d Guido Trotter
    #### instance parameters check
5896 7baf741d Guido Trotter
5897 7baf741d Guido Trotter
    # instance name verification
5898 5f23e043 Iustin Pop
    if self.op.name_check:
5899 5f23e043 Iustin Pop
      hostname1 = utils.GetHostInfo(self.op.instance_name)
5900 5f23e043 Iustin Pop
      self.op.instance_name = instance_name = hostname1.name
5901 5f23e043 Iustin Pop
      # used in CheckPrereq for ip ping check
5902 5f23e043 Iustin Pop
      self.check_ip = hostname1.ip
5903 5f23e043 Iustin Pop
    else:
5904 5f23e043 Iustin Pop
      instance_name = self.op.instance_name
5905 5f23e043 Iustin Pop
      self.check_ip = None
5906 7baf741d Guido Trotter
5907 7baf741d Guido Trotter
    # this is just a preventive check, but someone might still add this
5908 7baf741d Guido Trotter
    # instance in the meantime, and creation will fail at lock-add time
5909 7baf741d Guido Trotter
    if instance_name in self.cfg.GetInstanceList():
5910 7baf741d Guido Trotter
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
5911 5c983ee5 Iustin Pop
                                 instance_name, errors.ECODE_EXISTS)
5912 7baf741d Guido Trotter
5913 7baf741d Guido Trotter
    self.add_locks[locking.LEVEL_INSTANCE] = instance_name
5914 7baf741d Guido Trotter
5915 08db7c5c Iustin Pop
    # NIC buildup
5916 08db7c5c Iustin Pop
    self.nics = []
5917 9dce4771 Guido Trotter
    for idx, nic in enumerate(self.op.nics):
5918 9dce4771 Guido Trotter
      nic_mode_req = nic.get("mode", None)
5919 9dce4771 Guido Trotter
      nic_mode = nic_mode_req
5920 9dce4771 Guido Trotter
      if nic_mode is None:
5921 9dce4771 Guido Trotter
        nic_mode = cluster.nicparams[constants.PP_DEFAULT][constants.NIC_MODE]
5922 9dce4771 Guido Trotter
5923 9dce4771 Guido Trotter
      # in routed mode, for the first nic, the default ip is 'auto'
5924 9dce4771 Guido Trotter
      if nic_mode == constants.NIC_MODE_ROUTED and idx == 0:
5925 9dce4771 Guido Trotter
        default_ip_mode = constants.VALUE_AUTO
5926 9dce4771 Guido Trotter
      else:
5927 9dce4771 Guido Trotter
        default_ip_mode = constants.VALUE_NONE
5928 9dce4771 Guido Trotter
5929 08db7c5c Iustin Pop
      # ip validity checks
5930 9dce4771 Guido Trotter
      ip = nic.get("ip", default_ip_mode)
5931 9dce4771 Guido Trotter
      if ip is None or ip.lower() == constants.VALUE_NONE:
5932 08db7c5c Iustin Pop
        nic_ip = None
5933 08db7c5c Iustin Pop
      elif ip.lower() == constants.VALUE_AUTO:
5934 5f23e043 Iustin Pop
        if not self.op.name_check:
5935 5f23e043 Iustin Pop
          raise errors.OpPrereqError("IP address set to auto but name checks"
5936 5f23e043 Iustin Pop
                                     " have been skipped. Aborting.",
5937 5f23e043 Iustin Pop
                                     errors.ECODE_INVAL)
5938 08db7c5c Iustin Pop
        nic_ip = hostname1.ip
5939 08db7c5c Iustin Pop
      else:
5940 08db7c5c Iustin Pop
        if not utils.IsValidIP(ip):
5941 08db7c5c Iustin Pop
          raise errors.OpPrereqError("Given IP address '%s' doesn't look"
5942 5c983ee5 Iustin Pop
                                     " like a valid IP" % ip,
5943 5c983ee5 Iustin Pop
                                     errors.ECODE_INVAL)
5944 08db7c5c Iustin Pop
        nic_ip = ip
5945 08db7c5c Iustin Pop
5946 b8716596 Michael Hanselmann
      # TODO: check the ip address for uniqueness
5947 9dce4771 Guido Trotter
      if nic_mode == constants.NIC_MODE_ROUTED and not nic_ip:
5948 5c983ee5 Iustin Pop
        raise errors.OpPrereqError("Routed nic mode requires an ip address",
5949 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
5950 9dce4771 Guido Trotter
5951 08db7c5c Iustin Pop
      # MAC address verification
5952 08db7c5c Iustin Pop
      mac = nic.get("mac", constants.VALUE_AUTO)
5953 08db7c5c Iustin Pop
      if mac not in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
5954 82187135 René Nussbaumer
        mac = utils.NormalizeAndValidateMac(mac)
5955 82187135 René Nussbaumer
5956 82187135 René Nussbaumer
        try:
5957 82187135 René Nussbaumer
          self.cfg.ReserveMAC(mac, self.proc.GetECId())
5958 82187135 René Nussbaumer
        except errors.ReservationError:
5959 82187135 René Nussbaumer
          raise errors.OpPrereqError("MAC address %s already in use"
5960 82187135 René Nussbaumer
                                     " in cluster" % mac,
5961 82187135 René Nussbaumer
                                     errors.ECODE_NOTUNIQUE)
5962 87e43988 Iustin Pop
5963 08db7c5c Iustin Pop
      # bridge verification
5964 9939547b Iustin Pop
      bridge = nic.get("bridge", None)
5965 9dce4771 Guido Trotter
      link = nic.get("link", None)
5966 9dce4771 Guido Trotter
      if bridge and link:
5967 29921401 Iustin Pop
        raise errors.OpPrereqError("Cannot pass 'bridge' and 'link'"
5968 5c983ee5 Iustin Pop
                                   " at the same time", errors.ECODE_INVAL)
5969 9dce4771 Guido Trotter
      elif bridge and nic_mode == constants.NIC_MODE_ROUTED:
5970 5c983ee5 Iustin Pop
        raise errors.OpPrereqError("Cannot pass 'bridge' on a routed nic",
5971 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
5972 9dce4771 Guido Trotter
      elif bridge:
5973 9dce4771 Guido Trotter
        link = bridge
5974 9dce4771 Guido Trotter
5975 9dce4771 Guido Trotter
      nicparams = {}
5976 9dce4771 Guido Trotter
      if nic_mode_req:
5977 9dce4771 Guido Trotter
        nicparams[constants.NIC_MODE] = nic_mode_req
5978 9dce4771 Guido Trotter
      if link:
5979 9dce4771 Guido Trotter
        nicparams[constants.NIC_LINK] = link
5980 9dce4771 Guido Trotter
5981 9dce4771 Guido Trotter
      check_params = objects.FillDict(cluster.nicparams[constants.PP_DEFAULT],
5982 9dce4771 Guido Trotter
                                      nicparams)
5983 9dce4771 Guido Trotter
      objects.NIC.CheckParameterSyntax(check_params)
5984 9dce4771 Guido Trotter
      self.nics.append(objects.NIC(mac=mac, ip=nic_ip, nicparams=nicparams))
5985 08db7c5c Iustin Pop
5986 08db7c5c Iustin Pop
    # disk checks/pre-build
5987 08db7c5c Iustin Pop
    self.disks = []
5988 08db7c5c Iustin Pop
    for disk in self.op.disks:
5989 08db7c5c Iustin Pop
      mode = disk.get("mode", constants.DISK_RDWR)
5990 08db7c5c Iustin Pop
      if mode not in constants.DISK_ACCESS_SET:
5991 08db7c5c Iustin Pop
        raise errors.OpPrereqError("Invalid disk access mode '%s'" %
5992 5c983ee5 Iustin Pop
                                   mode, errors.ECODE_INVAL)
5993 08db7c5c Iustin Pop
      size = disk.get("size", None)
5994 08db7c5c Iustin Pop
      if size is None:
5995 5c983ee5 Iustin Pop
        raise errors.OpPrereqError("Missing disk size", errors.ECODE_INVAL)
5996 08db7c5c Iustin Pop
      try:
5997 08db7c5c Iustin Pop
        size = int(size)
5998 691744c4 Iustin Pop
      except (TypeError, ValueError):
5999 5c983ee5 Iustin Pop
        raise errors.OpPrereqError("Invalid disk size '%s'" % size,
6000 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
6001 c3589cf8 Iustin Pop
      new_disk = {"size": size, "mode": mode}
6002 c3589cf8 Iustin Pop
      if "adopt" in disk:
6003 c3589cf8 Iustin Pop
        new_disk["adopt"] = disk["adopt"]
6004 c3589cf8 Iustin Pop
      self.disks.append(new_disk)
6005 08db7c5c Iustin Pop
6006 7baf741d Guido Trotter
    # file storage checks
6007 7baf741d Guido Trotter
    if (self.op.file_driver and
6008 7baf741d Guido Trotter
        not self.op.file_driver in constants.FILE_DRIVER):
6009 7baf741d Guido Trotter
      raise errors.OpPrereqError("Invalid file driver name '%s'" %
6010 5c983ee5 Iustin Pop
                                 self.op.file_driver, errors.ECODE_INVAL)
6011 7baf741d Guido Trotter
6012 7baf741d Guido Trotter
    if self.op.file_storage_dir and os.path.isabs(self.op.file_storage_dir):
6013 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("File storage directory path not absolute",
6014 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
6015 7baf741d Guido Trotter
6016 7baf741d Guido Trotter
    ### Node/iallocator related checks
6017 7baf741d Guido Trotter
    if [self.op.iallocator, self.op.pnode].count(None) != 1:
6018 7baf741d Guido Trotter
      raise errors.OpPrereqError("One and only one of iallocator and primary"
6019 5c983ee5 Iustin Pop
                                 " node must be given",
6020 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
6021 7baf741d Guido Trotter
6022 7baf741d Guido Trotter
    if self.op.iallocator:
6023 7baf741d Guido Trotter
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
6024 7baf741d Guido Trotter
    else:
6025 cf26a87a Iustin Pop
      self.op.pnode = _ExpandNodeName(self.cfg, self.op.pnode)
6026 7baf741d Guido Trotter
      nodelist = [self.op.pnode]
6027 7baf741d Guido Trotter
      if self.op.snode is not None:
6028 cf26a87a Iustin Pop
        self.op.snode = _ExpandNodeName(self.cfg, self.op.snode)
6029 7baf741d Guido Trotter
        nodelist.append(self.op.snode)
6030 7baf741d Guido Trotter
      self.needed_locks[locking.LEVEL_NODE] = nodelist
6031 7baf741d Guido Trotter
6032 7baf741d Guido Trotter
    # in case of import lock the source node too
6033 7baf741d Guido Trotter
    if self.op.mode == constants.INSTANCE_IMPORT:
6034 7baf741d Guido Trotter
      src_node = getattr(self.op, "src_node", None)
6035 7baf741d Guido Trotter
      src_path = getattr(self.op, "src_path", None)
6036 7baf741d Guido Trotter
6037 b9322a9f Guido Trotter
      if src_path is None:
6038 b9322a9f Guido Trotter
        self.op.src_path = src_path = self.op.instance_name
6039 b9322a9f Guido Trotter
6040 b9322a9f Guido Trotter
      if src_node is None:
6041 b9322a9f Guido Trotter
        self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
6042 b9322a9f Guido Trotter
        self.op.src_node = None
6043 b9322a9f Guido Trotter
        if os.path.isabs(src_path):
6044 b9322a9f Guido Trotter
          raise errors.OpPrereqError("Importing an instance from an absolute"
6045 5c983ee5 Iustin Pop
                                     " path requires a source node option.",
6046 5c983ee5 Iustin Pop
                                     errors.ECODE_INVAL)
6047 b9322a9f Guido Trotter
      else:
6048 cf26a87a Iustin Pop
        self.op.src_node = src_node = _ExpandNodeName(self.cfg, src_node)
6049 b9322a9f Guido Trotter
        if self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET:
6050 b9322a9f Guido Trotter
          self.needed_locks[locking.LEVEL_NODE].append(src_node)
6051 b9322a9f Guido Trotter
        if not os.path.isabs(src_path):
6052 b9322a9f Guido Trotter
          self.op.src_path = src_path = \
6053 c4feafe8 Iustin Pop
            utils.PathJoin(constants.EXPORT_DIR, src_path)
6054 7baf741d Guido Trotter
6055 f2c05717 Guido Trotter
      # On import force_variant must be True, because if we forced it at
6056 f2c05717 Guido Trotter
      # initial install, our only chance when importing it back is that it
6057 f2c05717 Guido Trotter
      # works again!
6058 f2c05717 Guido Trotter
      self.op.force_variant = True
6059 f2c05717 Guido Trotter
6060 7baf741d Guido Trotter
    else: # INSTANCE_CREATE
6061 7baf741d Guido Trotter
      if getattr(self.op, "os_type", None) is None:
6062 5c983ee5 Iustin Pop
        raise errors.OpPrereqError("No guest OS specified",
6063 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
6064 f2c05717 Guido Trotter
      self.op.force_variant = getattr(self.op, "force_variant", False)
6065 a8083063 Iustin Pop
6066 538475ca Iustin Pop
  def _RunAllocator(self):
6067 538475ca Iustin Pop
    """Run the allocator based on input opcode.
6068 538475ca Iustin Pop

6069 538475ca Iustin Pop
    """
6070 08db7c5c Iustin Pop
    nics = [n.ToDict() for n in self.nics]
6071 923ddac0 Michael Hanselmann
    ial = IAllocator(self.cfg, self.rpc,
6072 29859cb7 Iustin Pop
                     mode=constants.IALLOCATOR_MODE_ALLOC,
6073 d1c2dd75 Iustin Pop
                     name=self.op.instance_name,
6074 d1c2dd75 Iustin Pop
                     disk_template=self.op.disk_template,
6075 d1c2dd75 Iustin Pop
                     tags=[],
6076 d1c2dd75 Iustin Pop
                     os=self.op.os_type,
6077 338e51e8 Iustin Pop
                     vcpus=self.be_full[constants.BE_VCPUS],
6078 338e51e8 Iustin Pop
                     mem_size=self.be_full[constants.BE_MEMORY],
6079 08db7c5c Iustin Pop
                     disks=self.disks,
6080 d1c2dd75 Iustin Pop
                     nics=nics,
6081 8cc7e742 Guido Trotter
                     hypervisor=self.op.hypervisor,
6082 29859cb7 Iustin Pop
                     )
6083 d1c2dd75 Iustin Pop
6084 d1c2dd75 Iustin Pop
    ial.Run(self.op.iallocator)
6085 d1c2dd75 Iustin Pop
6086 d1c2dd75 Iustin Pop
    if not ial.success:
6087 538475ca Iustin Pop
      raise errors.OpPrereqError("Can't compute nodes using"
6088 5c983ee5 Iustin Pop
                                 " iallocator '%s': %s" %
6089 5c983ee5 Iustin Pop
                                 (self.op.iallocator, ial.info),
6090 5c983ee5 Iustin Pop
                                 errors.ECODE_NORES)
6091 680f0a89 Iustin Pop
    if len(ial.result) != ial.required_nodes:
6092 538475ca Iustin Pop
      raise errors.OpPrereqError("iallocator '%s' returned invalid number"
6093 538475ca Iustin Pop
                                 " of nodes (%s), required %s" %
6094 680f0a89 Iustin Pop
                                 (self.op.iallocator, len(ial.result),
6095 5c983ee5 Iustin Pop
                                  ial.required_nodes), errors.ECODE_FAULT)
6096 680f0a89 Iustin Pop
    self.op.pnode = ial.result[0]
6097 86d9d3bb Iustin Pop
    self.LogInfo("Selected nodes for instance %s via iallocator %s: %s",
6098 86d9d3bb Iustin Pop
                 self.op.instance_name, self.op.iallocator,
6099 680f0a89 Iustin Pop
                 utils.CommaJoin(ial.result))
6100 27579978 Iustin Pop
    if ial.required_nodes == 2:
6101 680f0a89 Iustin Pop
      self.op.snode = ial.result[1]
6102 538475ca Iustin Pop
6103 a8083063 Iustin Pop
  def BuildHooksEnv(self):
6104 a8083063 Iustin Pop
    """Build hooks env.
6105 a8083063 Iustin Pop

6106 a8083063 Iustin Pop
    This runs on master, primary and secondary nodes of the instance.
6107 a8083063 Iustin Pop

6108 a8083063 Iustin Pop
    """
6109 a8083063 Iustin Pop
    env = {
6110 2c2690c9 Iustin Pop
      "ADD_MODE": self.op.mode,
6111 a8083063 Iustin Pop
      }
6112 a8083063 Iustin Pop
    if self.op.mode == constants.INSTANCE_IMPORT:
6113 2c2690c9 Iustin Pop
      env["SRC_NODE"] = self.op.src_node
6114 2c2690c9 Iustin Pop
      env["SRC_PATH"] = self.op.src_path
6115 2c2690c9 Iustin Pop
      env["SRC_IMAGES"] = self.src_images
6116 396e1b78 Michael Hanselmann
6117 2c2690c9 Iustin Pop
    env.update(_BuildInstanceHookEnv(
6118 2c2690c9 Iustin Pop
      name=self.op.instance_name,
6119 396e1b78 Michael Hanselmann
      primary_node=self.op.pnode,
6120 396e1b78 Michael Hanselmann
      secondary_nodes=self.secondaries,
6121 4978db17 Iustin Pop
      status=self.op.start,
6122 ecb215b5 Michael Hanselmann
      os_type=self.op.os_type,
6123 338e51e8 Iustin Pop
      memory=self.be_full[constants.BE_MEMORY],
6124 338e51e8 Iustin Pop
      vcpus=self.be_full[constants.BE_VCPUS],
6125 f9b10246 Guido Trotter
      nics=_NICListToTuple(self, self.nics),
6126 2c2690c9 Iustin Pop
      disk_template=self.op.disk_template,
6127 2c2690c9 Iustin Pop
      disks=[(d["size"], d["mode"]) for d in self.disks],
6128 67fc3042 Iustin Pop
      bep=self.be_full,
6129 67fc3042 Iustin Pop
      hvp=self.hv_full,
6130 3df6e710 Iustin Pop
      hypervisor_name=self.op.hypervisor,
6131 396e1b78 Michael Hanselmann
    ))
6132 a8083063 Iustin Pop
6133 d6a02168 Michael Hanselmann
    nl = ([self.cfg.GetMasterNode(), self.op.pnode] +
6134 a8083063 Iustin Pop
          self.secondaries)
6135 a8083063 Iustin Pop
    return env, nl, nl
6136 a8083063 Iustin Pop
6137 a8083063 Iustin Pop
6138 a8083063 Iustin Pop
  def CheckPrereq(self):
6139 a8083063 Iustin Pop
    """Check prerequisites.
6140 a8083063 Iustin Pop

6141 a8083063 Iustin Pop
    """
6142 eedc99de Manuel Franceschini
    if (not self.cfg.GetVGName() and
6143 eedc99de Manuel Franceschini
        self.op.disk_template not in constants.DTS_NOT_LVM):
6144 eedc99de Manuel Franceschini
      raise errors.OpPrereqError("Cluster does not support lvm-based"
6145 5c983ee5 Iustin Pop
                                 " instances", errors.ECODE_STATE)
6146 eedc99de Manuel Franceschini
6147 a8083063 Iustin Pop
    if self.op.mode == constants.INSTANCE_IMPORT:
6148 7baf741d Guido Trotter
      src_node = self.op.src_node
6149 7baf741d Guido Trotter
      src_path = self.op.src_path
6150 a8083063 Iustin Pop
6151 c0cbdc67 Guido Trotter
      if src_node is None:
6152 1b7bfbb7 Iustin Pop
        locked_nodes = self.acquired_locks[locking.LEVEL_NODE]
6153 1b7bfbb7 Iustin Pop
        exp_list = self.rpc.call_export_list(locked_nodes)
6154 c0cbdc67 Guido Trotter
        found = False
6155 c0cbdc67 Guido Trotter
        for node in exp_list:
6156 4c4e4e1e Iustin Pop
          if exp_list[node].fail_msg:
6157 1b7bfbb7 Iustin Pop
            continue
6158 1b7bfbb7 Iustin Pop
          if src_path in exp_list[node].payload:
6159 c0cbdc67 Guido Trotter
            found = True
6160 c0cbdc67 Guido Trotter
            self.op.src_node = src_node = node
6161 c4feafe8 Iustin Pop
            self.op.src_path = src_path = utils.PathJoin(constants.EXPORT_DIR,
6162 c4feafe8 Iustin Pop
                                                         src_path)
6163 c0cbdc67 Guido Trotter
            break
6164 c0cbdc67 Guido Trotter
        if not found:
6165 c0cbdc67 Guido Trotter
          raise errors.OpPrereqError("No export found for relative path %s" %
6166 5c983ee5 Iustin Pop
                                      src_path, errors.ECODE_INVAL)
6167 c0cbdc67 Guido Trotter
6168 7527a8a4 Iustin Pop
      _CheckNodeOnline(self, src_node)
6169 781de953 Iustin Pop
      result = self.rpc.call_export_info(src_node, src_path)
6170 4c4e4e1e Iustin Pop
      result.Raise("No export or invalid export found in dir %s" % src_path)
6171 a8083063 Iustin Pop
6172 3eccac06 Iustin Pop
      export_info = objects.SerializableConfigParser.Loads(str(result.payload))
6173 a8083063 Iustin Pop
      if not export_info.has_section(constants.INISECT_EXP):
6174 5c983ee5 Iustin Pop
        raise errors.ProgrammerError("Corrupted export config",
6175 5c983ee5 Iustin Pop
                                     errors.ECODE_ENVIRON)
6176 a8083063 Iustin Pop
6177 a8083063 Iustin Pop
      ei_version = export_info.get(constants.INISECT_EXP, 'version')
6178 a8083063 Iustin Pop
      if (int(ei_version) != constants.EXPORT_VERSION):
6179 3ecf6786 Iustin Pop
        raise errors.OpPrereqError("Wrong export version %s (wanted %d)" %
6180 5c983ee5 Iustin Pop
                                   (ei_version, constants.EXPORT_VERSION),
6181 5c983ee5 Iustin Pop
                                   errors.ECODE_ENVIRON)
6182 a8083063 Iustin Pop
6183 09acf207 Guido Trotter
      # Check that the new instance doesn't have less disks than the export
6184 08db7c5c Iustin Pop
      instance_disks = len(self.disks)
6185 09acf207 Guido Trotter
      export_disks = export_info.getint(constants.INISECT_INS, 'disk_count')
6186 09acf207 Guido Trotter
      if instance_disks < export_disks:
6187 09acf207 Guido Trotter
        raise errors.OpPrereqError("Not enough disks to import."
6188 09acf207 Guido Trotter
                                   " (instance: %d, export: %d)" %
6189 5c983ee5 Iustin Pop
                                   (instance_disks, export_disks),
6190 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
6191 a8083063 Iustin Pop
6192 a8083063 Iustin Pop
      self.op.os_type = export_info.get(constants.INISECT_EXP, 'os')
6193 09acf207 Guido Trotter
      disk_images = []
6194 09acf207 Guido Trotter
      for idx in range(export_disks):
6195 09acf207 Guido Trotter
        option = 'disk%d_dump' % idx
6196 09acf207 Guido Trotter
        if export_info.has_option(constants.INISECT_INS, option):
6197 09acf207 Guido Trotter
          # FIXME: are the old os-es, disk sizes, etc. useful?
6198 09acf207 Guido Trotter
          export_name = export_info.get(constants.INISECT_INS, option)
6199 c4feafe8 Iustin Pop
          image = utils.PathJoin(src_path, export_name)
6200 09acf207 Guido Trotter
          disk_images.append(image)
6201 09acf207 Guido Trotter
        else:
6202 09acf207 Guido Trotter
          disk_images.append(False)
6203 09acf207 Guido Trotter
6204 09acf207 Guido Trotter
      self.src_images = disk_images
6205 901a65c1 Iustin Pop
6206 b4364a6b Guido Trotter
      old_name = export_info.get(constants.INISECT_INS, 'name')
6207 b4364a6b Guido Trotter
      # FIXME: int() here could throw a ValueError on broken exports
6208 b4364a6b Guido Trotter
      exp_nic_count = int(export_info.get(constants.INISECT_INS, 'nic_count'))
6209 b4364a6b Guido Trotter
      if self.op.instance_name == old_name:
6210 b4364a6b Guido Trotter
        for idx, nic in enumerate(self.nics):
6211 b4364a6b Guido Trotter
          if nic.mac == constants.VALUE_AUTO and exp_nic_count >= idx:
6212 b4364a6b Guido Trotter
            nic_mac_ini = 'nic%d_mac' % idx
6213 b4364a6b Guido Trotter
            nic.mac = export_info.get(constants.INISECT_INS, nic_mac_ini)
6214 bc89efc3 Guido Trotter
6215 295728df Guido Trotter
    # ENDIF: self.op.mode == constants.INSTANCE_IMPORT
6216 901a65c1 Iustin Pop
6217 18c8f361 Iustin Pop
    # ip ping checks (we use the same ip that was resolved in ExpandNames)
6218 901a65c1 Iustin Pop
    if self.op.ip_check:
6219 7baf741d Guido Trotter
      if utils.TcpPing(self.check_ip, constants.DEFAULT_NODED_PORT):
6220 901a65c1 Iustin Pop
        raise errors.OpPrereqError("IP %s of instance %s already in use" %
6221 5c983ee5 Iustin Pop
                                   (self.check_ip, self.op.instance_name),
6222 5c983ee5 Iustin Pop
                                   errors.ECODE_NOTUNIQUE)
6223 901a65c1 Iustin Pop
6224 295728df Guido Trotter
    #### mac address generation
6225 295728df Guido Trotter
    # By generating here the mac address both the allocator and the hooks get
6226 295728df Guido Trotter
    # the real final mac address rather than the 'auto' or 'generate' value.
6227 295728df Guido Trotter
    # There is a race condition between the generation and the instance object
6228 295728df Guido Trotter
    # creation, which means that we know the mac is valid now, but we're not
6229 295728df Guido Trotter
    # sure it will be when we actually add the instance. If things go bad
6230 295728df Guido Trotter
    # adding the instance will abort because of a duplicate mac, and the
6231 295728df Guido Trotter
    # creation job will fail.
6232 295728df Guido Trotter
    for nic in self.nics:
6233 295728df Guido Trotter
      if nic.mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
6234 36b66e6e Guido Trotter
        nic.mac = self.cfg.GenerateMAC(self.proc.GetECId())
6235 295728df Guido Trotter
6236 538475ca Iustin Pop
    #### allocator run
6237 538475ca Iustin Pop
6238 538475ca Iustin Pop
    if self.op.iallocator is not None:
6239 538475ca Iustin Pop
      self._RunAllocator()
6240 0f1a06e3 Manuel Franceschini
6241 901a65c1 Iustin Pop
    #### node related checks
6242 901a65c1 Iustin Pop
6243 901a65c1 Iustin Pop
    # check primary node
6244 7baf741d Guido Trotter
    self.pnode = pnode = self.cfg.GetNodeInfo(self.op.pnode)
6245 7baf741d Guido Trotter
    assert self.pnode is not None, \
6246 7baf741d Guido Trotter
      "Cannot retrieve locked node %s" % self.op.pnode
6247 7527a8a4 Iustin Pop
    if pnode.offline:
6248 7527a8a4 Iustin Pop
      raise errors.OpPrereqError("Cannot use offline primary node '%s'" %
6249 5c983ee5 Iustin Pop
                                 pnode.name, errors.ECODE_STATE)
6250 733a2b6a Iustin Pop
    if pnode.drained:
6251 733a2b6a Iustin Pop
      raise errors.OpPrereqError("Cannot use drained primary node '%s'" %
6252 5c983ee5 Iustin Pop
                                 pnode.name, errors.ECODE_STATE)
6253 7527a8a4 Iustin Pop
6254 901a65c1 Iustin Pop
    self.secondaries = []
6255 901a65c1 Iustin Pop
6256 901a65c1 Iustin Pop
    # mirror node verification
6257 a1f445d3 Iustin Pop
    if self.op.disk_template in constants.DTS_NET_MIRROR:
6258 7baf741d Guido Trotter
      if self.op.snode is None:
6259 a1f445d3 Iustin Pop
        raise errors.OpPrereqError("The networked disk templates need"
6260 5c983ee5 Iustin Pop
                                   " a mirror node", errors.ECODE_INVAL)
6261 7baf741d Guido Trotter
      if self.op.snode == pnode.name:
6262 5c983ee5 Iustin Pop
        raise errors.OpPrereqError("The secondary node cannot be the"
6263 5c983ee5 Iustin Pop
                                   " primary node.", errors.ECODE_INVAL)
6264 7527a8a4 Iustin Pop
      _CheckNodeOnline(self, self.op.snode)
6265 733a2b6a Iustin Pop
      _CheckNodeNotDrained(self, self.op.snode)
6266 733a2b6a Iustin Pop
      self.secondaries.append(self.op.snode)
6267 a8083063 Iustin Pop
6268 6785674e Iustin Pop
    nodenames = [pnode.name] + self.secondaries
6269 6785674e Iustin Pop
6270 e2fe6369 Iustin Pop
    req_size = _ComputeDiskSize(self.op.disk_template,
6271 08db7c5c Iustin Pop
                                self.disks)
6272 ed1ebc60 Guido Trotter
6273 c3589cf8 Iustin Pop
    # Check lv size requirements, if not adopting
6274 c3589cf8 Iustin Pop
    if req_size is not None and not self.adopt_disks:
6275 701384a9 Iustin Pop
      _CheckNodesFreeDisk(self, nodenames, req_size)
6276 ed1ebc60 Guido Trotter
6277 c3589cf8 Iustin Pop
    if self.adopt_disks: # instead, we must check the adoption data
6278 c3589cf8 Iustin Pop
      all_lvs = set([i["adopt"] for i in self.disks])
6279 c3589cf8 Iustin Pop
      if len(all_lvs) != len(self.disks):
6280 c3589cf8 Iustin Pop
        raise errors.OpPrereqError("Duplicate volume names given for adoption",
6281 c3589cf8 Iustin Pop
                                   errors.ECODE_INVAL)
6282 c3589cf8 Iustin Pop
      for lv_name in all_lvs:
6283 c3589cf8 Iustin Pop
        try:
6284 c3589cf8 Iustin Pop
          self.cfg.ReserveLV(lv_name, self.proc.GetECId())
6285 c3589cf8 Iustin Pop
        except errors.ReservationError:
6286 c3589cf8 Iustin Pop
          raise errors.OpPrereqError("LV named %s used by another instance" %
6287 c3589cf8 Iustin Pop
                                     lv_name, errors.ECODE_NOTUNIQUE)
6288 c3589cf8 Iustin Pop
6289 c3589cf8 Iustin Pop
      node_lvs = self.rpc.call_lv_list([pnode.name],
6290 c3589cf8 Iustin Pop
                                       self.cfg.GetVGName())[pnode.name]
6291 c3589cf8 Iustin Pop
      node_lvs.Raise("Cannot get LV information from node %s" % pnode.name)
6292 c3589cf8 Iustin Pop
      node_lvs = node_lvs.payload
6293 c3589cf8 Iustin Pop
      delta = all_lvs.difference(node_lvs.keys())
6294 c3589cf8 Iustin Pop
      if delta:
6295 c3589cf8 Iustin Pop
        raise errors.OpPrereqError("Missing logical volume(s): %s" %
6296 c3589cf8 Iustin Pop
                                   utils.CommaJoin(delta),
6297 c3589cf8 Iustin Pop
                                   errors.ECODE_INVAL)
6298 c3589cf8 Iustin Pop
      online_lvs = [lv for lv in all_lvs if node_lvs[lv][2]]
6299 c3589cf8 Iustin Pop
      if online_lvs:
6300 c3589cf8 Iustin Pop
        raise errors.OpPrereqError("Online logical volumes found, cannot"
6301 c3589cf8 Iustin Pop
                                   " adopt: %s" % utils.CommaJoin(online_lvs),
6302 c3589cf8 Iustin Pop
                                   errors.ECODE_STATE)
6303 c3589cf8 Iustin Pop
      # update the size of disk based on what is found
6304 c3589cf8 Iustin Pop
      for dsk in self.disks:
6305 c3589cf8 Iustin Pop
        dsk["size"] = int(float(node_lvs[dsk["adopt"]][0]))
6306 c3589cf8 Iustin Pop
6307 74409b12 Iustin Pop
    _CheckHVParams(self, nodenames, self.op.hypervisor, self.op.hvparams)
6308 6785674e Iustin Pop
6309 a8083063 Iustin Pop
    # os verification
6310 781de953 Iustin Pop
    result = self.rpc.call_os_get(pnode.name, self.op.os_type)
6311 4c4e4e1e Iustin Pop
    result.Raise("OS '%s' not in supported os list for primary node %s" %
6312 045dd6d9 Iustin Pop
                 (self.op.os_type, pnode.name),
6313 045dd6d9 Iustin Pop
                 prereq=True, ecode=errors.ECODE_INVAL)
6314 f2c05717 Guido Trotter
    if not self.op.force_variant:
6315 f2c05717 Guido Trotter
      _CheckOSVariant(result.payload, self.op.os_type)
6316 a8083063 Iustin Pop
6317 b165e77e Guido Trotter
    _CheckNicsBridgesExist(self, self.nics, self.pnode.name)
6318 a8083063 Iustin Pop
6319 49ce1563 Iustin Pop
    # memory check on primary node
6320 49ce1563 Iustin Pop
    if self.op.start:
6321 b9bddb6b Iustin Pop
      _CheckNodeFreeMemory(self, self.pnode.name,
6322 49ce1563 Iustin Pop
                           "creating instance %s" % self.op.instance_name,
6323 338e51e8 Iustin Pop
                           self.be_full[constants.BE_MEMORY],
6324 338e51e8 Iustin Pop
                           self.op.hypervisor)
6325 49ce1563 Iustin Pop
6326 08896026 Iustin Pop
    self.dry_run_result = list(nodenames)
6327 08896026 Iustin Pop
6328 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
6329 a8083063 Iustin Pop
    """Create and add the instance to the cluster.
6330 a8083063 Iustin Pop

6331 a8083063 Iustin Pop
    """
6332 a8083063 Iustin Pop
    instance = self.op.instance_name
6333 a8083063 Iustin Pop
    pnode_name = self.pnode.name
6334 a8083063 Iustin Pop
6335 e69d05fd Iustin Pop
    ht_kind = self.op.hypervisor
6336 2a6469d5 Alexander Schreiber
    if ht_kind in constants.HTS_REQ_PORT:
6337 2a6469d5 Alexander Schreiber
      network_port = self.cfg.AllocatePort()
6338 2a6469d5 Alexander Schreiber
    else:
6339 2a6469d5 Alexander Schreiber
      network_port = None
6340 58acb49d Alexander Schreiber
6341 6785674e Iustin Pop
    ##if self.op.vnc_bind_address is None:
6342 6785674e Iustin Pop
    ##  self.op.vnc_bind_address = constants.VNC_DEFAULT_BIND_ADDRESS
6343 31a853d2 Iustin Pop
6344 2c313123 Manuel Franceschini
    # this is needed because os.path.join does not accept None arguments
6345 2c313123 Manuel Franceschini
    if self.op.file_storage_dir is None:
6346 2c313123 Manuel Franceschini
      string_file_storage_dir = ""
6347 2c313123 Manuel Franceschini
    else:
6348 2c313123 Manuel Franceschini
      string_file_storage_dir = self.op.file_storage_dir
6349 2c313123 Manuel Franceschini
6350 0f1a06e3 Manuel Franceschini
    # build the full file storage dir path
6351 c4feafe8 Iustin Pop
    file_storage_dir = utils.PathJoin(self.cfg.GetFileStorageDir(),
6352 c4feafe8 Iustin Pop
                                      string_file_storage_dir, instance)
6353 0f1a06e3 Manuel Franceschini
6354 0f1a06e3 Manuel Franceschini
6355 b9bddb6b Iustin Pop
    disks = _GenerateDiskTemplate(self,
6356 a8083063 Iustin Pop
                                  self.op.disk_template,
6357 a8083063 Iustin Pop
                                  instance, pnode_name,
6358 08db7c5c Iustin Pop
                                  self.secondaries,
6359 08db7c5c Iustin Pop
                                  self.disks,
6360 0f1a06e3 Manuel Franceschini
                                  file_storage_dir,
6361 e2a65344 Iustin Pop
                                  self.op.file_driver,
6362 e2a65344 Iustin Pop
                                  0)
6363 a8083063 Iustin Pop
6364 a8083063 Iustin Pop
    iobj = objects.Instance(name=instance, os=self.op.os_type,
6365 a8083063 Iustin Pop
                            primary_node=pnode_name,
6366 08db7c5c Iustin Pop
                            nics=self.nics, disks=disks,
6367 a8083063 Iustin Pop
                            disk_template=self.op.disk_template,
6368 4978db17 Iustin Pop
                            admin_up=False,
6369 58acb49d Alexander Schreiber
                            network_port=network_port,
6370 338e51e8 Iustin Pop
                            beparams=self.op.beparams,
6371 6785674e Iustin Pop
                            hvparams=self.op.hvparams,
6372 e69d05fd Iustin Pop
                            hypervisor=self.op.hypervisor,
6373 a8083063 Iustin Pop
                            )
6374 a8083063 Iustin Pop
6375 c3589cf8 Iustin Pop
    if self.adopt_disks:
6376 c3589cf8 Iustin Pop
      # rename LVs to the newly-generated names; we need to construct
6377 c3589cf8 Iustin Pop
      # 'fake' LV disks with the old data, plus the new unique_id
6378 c3589cf8 Iustin Pop
      tmp_disks = [objects.Disk.FromDict(v.ToDict()) for v in disks]
6379 c3589cf8 Iustin Pop
      rename_to = []
6380 c3589cf8 Iustin Pop
      for t_dsk, a_dsk in zip (tmp_disks, self.disks):
6381 c3589cf8 Iustin Pop
        rename_to.append(t_dsk.logical_id)
6382 c3589cf8 Iustin Pop
        t_dsk.logical_id = (t_dsk.logical_id[0], a_dsk["adopt"])
6383 c3589cf8 Iustin Pop
        self.cfg.SetDiskID(t_dsk, pnode_name)
6384 c3589cf8 Iustin Pop
      result = self.rpc.call_blockdev_rename(pnode_name,
6385 c3589cf8 Iustin Pop
                                             zip(tmp_disks, rename_to))
6386 c3589cf8 Iustin Pop
      result.Raise("Failed to rename adoped LVs")
6387 c3589cf8 Iustin Pop
    else:
6388 c3589cf8 Iustin Pop
      feedback_fn("* creating instance disks...")
6389 796cab27 Iustin Pop
      try:
6390 c3589cf8 Iustin Pop
        _CreateDisks(self, iobj)
6391 c3589cf8 Iustin Pop
      except errors.OpExecError:
6392 c3589cf8 Iustin Pop
        self.LogWarning("Device creation failed, reverting...")
6393 c3589cf8 Iustin Pop
        try:
6394 c3589cf8 Iustin Pop
          _RemoveDisks(self, iobj)
6395 c3589cf8 Iustin Pop
        finally:
6396 c3589cf8 Iustin Pop
          self.cfg.ReleaseDRBDMinors(instance)
6397 c3589cf8 Iustin Pop
          raise
6398 a8083063 Iustin Pop
6399 a8083063 Iustin Pop
    feedback_fn("adding instance %s to cluster config" % instance)
6400 a8083063 Iustin Pop
6401 0debfb35 Guido Trotter
    self.cfg.AddInstance(iobj, self.proc.GetECId())
6402 0debfb35 Guido Trotter
6403 7baf741d Guido Trotter
    # Declare that we don't want to remove the instance lock anymore, as we've
6404 7baf741d Guido Trotter
    # added the instance to the config
6405 7baf741d Guido Trotter
    del self.remove_locks[locking.LEVEL_INSTANCE]
6406 e36e96b4 Guido Trotter
    # Unlock all the nodes
6407 9c8971d7 Guido Trotter
    if self.op.mode == constants.INSTANCE_IMPORT:
6408 9c8971d7 Guido Trotter
      nodes_keep = [self.op.src_node]
6409 9c8971d7 Guido Trotter
      nodes_release = [node for node in self.acquired_locks[locking.LEVEL_NODE]
6410 9c8971d7 Guido Trotter
                       if node != self.op.src_node]
6411 9c8971d7 Guido Trotter
      self.context.glm.release(locking.LEVEL_NODE, nodes_release)
6412 9c8971d7 Guido Trotter
      self.acquired_locks[locking.LEVEL_NODE] = nodes_keep
6413 9c8971d7 Guido Trotter
    else:
6414 9c8971d7 Guido Trotter
      self.context.glm.release(locking.LEVEL_NODE)
6415 9c8971d7 Guido Trotter
      del self.acquired_locks[locking.LEVEL_NODE]
6416 a8083063 Iustin Pop
6417 a8083063 Iustin Pop
    if self.op.wait_for_sync:
6418 b9bddb6b Iustin Pop
      disk_abort = not _WaitForSync(self, iobj)
6419 a1f445d3 Iustin Pop
    elif iobj.disk_template in constants.DTS_NET_MIRROR:
6420 a8083063 Iustin Pop
      # make sure the disks are not degraded (still sync-ing is ok)
6421 a8083063 Iustin Pop
      time.sleep(15)
6422 a8083063 Iustin Pop
      feedback_fn("* checking mirrors status")
6423 b9bddb6b Iustin Pop
      disk_abort = not _WaitForSync(self, iobj, oneshot=True)
6424 a8083063 Iustin Pop
    else:
6425 a8083063 Iustin Pop
      disk_abort = False
6426 a8083063 Iustin Pop
6427 a8083063 Iustin Pop
    if disk_abort:
6428 b9bddb6b Iustin Pop
      _RemoveDisks(self, iobj)
6429 a8083063 Iustin Pop
      self.cfg.RemoveInstance(iobj.name)
6430 7baf741d Guido Trotter
      # Make sure the instance lock gets removed
6431 7baf741d Guido Trotter
      self.remove_locks[locking.LEVEL_INSTANCE] = iobj.name
6432 3ecf6786 Iustin Pop
      raise errors.OpExecError("There are some degraded disks for"
6433 3ecf6786 Iustin Pop
                               " this instance")
6434 a8083063 Iustin Pop
6435 c3589cf8 Iustin Pop
    if iobj.disk_template != constants.DT_DISKLESS and not self.adopt_disks:
6436 a8083063 Iustin Pop
      if self.op.mode == constants.INSTANCE_CREATE:
6437 a8083063 Iustin Pop
        feedback_fn("* running the instance OS create scripts...")
6438 4a0e011f Iustin Pop
        # FIXME: pass debug option from opcode to backend
6439 dd713605 Iustin Pop
        result = self.rpc.call_instance_os_add(pnode_name, iobj, False,
6440 dd713605 Iustin Pop
                                               self.op.debug_level)
6441 4c4e4e1e Iustin Pop
        result.Raise("Could not add os for instance %s"
6442 4c4e4e1e Iustin Pop
                     " on node %s" % (instance, pnode_name))
6443 a8083063 Iustin Pop
6444 a8083063 Iustin Pop
      elif self.op.mode == constants.INSTANCE_IMPORT:
6445 a8083063 Iustin Pop
        feedback_fn("* running the instance OS import scripts...")
6446 a8083063 Iustin Pop
        src_node = self.op.src_node
6447 09acf207 Guido Trotter
        src_images = self.src_images
6448 62c9ec92 Iustin Pop
        cluster_name = self.cfg.GetClusterName()
6449 4a0e011f Iustin Pop
        # FIXME: pass debug option from opcode to backend
6450 6c0af70e Guido Trotter
        import_result = self.rpc.call_instance_os_import(pnode_name, iobj,
6451 09acf207 Guido Trotter
                                                         src_node, src_images,
6452 dd713605 Iustin Pop
                                                         cluster_name,
6453 dd713605 Iustin Pop
                                                         self.op.debug_level)
6454 4c4e4e1e Iustin Pop
        msg = import_result.fail_msg
6455 944bf548 Iustin Pop
        if msg:
6456 944bf548 Iustin Pop
          self.LogWarning("Error while importing the disk images for instance"
6457 944bf548 Iustin Pop
                          " %s on node %s: %s" % (instance, pnode_name, msg))
6458 a8083063 Iustin Pop
      else:
6459 a8083063 Iustin Pop
        # also checked in the prereq part
6460 3ecf6786 Iustin Pop
        raise errors.ProgrammerError("Unknown OS initialization mode '%s'"
6461 3ecf6786 Iustin Pop
                                     % self.op.mode)
6462 a8083063 Iustin Pop
6463 a8083063 Iustin Pop
    if self.op.start:
6464 4978db17 Iustin Pop
      iobj.admin_up = True
6465 a4eae71f Michael Hanselmann
      self.cfg.Update(iobj, feedback_fn)
6466 9a4f63d1 Iustin Pop
      logging.info("Starting instance %s on node %s", instance, pnode_name)
6467 a8083063 Iustin Pop
      feedback_fn("* starting instance...")
6468 0eca8e0c Iustin Pop
      result = self.rpc.call_instance_start(pnode_name, iobj, None, None)
6469 4c4e4e1e Iustin Pop
      result.Raise("Could not start instance")
6470 a8083063 Iustin Pop
6471 08896026 Iustin Pop
    return list(iobj.all_nodes)
6472 08896026 Iustin Pop
6473 a8083063 Iustin Pop
6474 a8083063 Iustin Pop
class LUConnectConsole(NoHooksLU):
6475 a8083063 Iustin Pop
  """Connect to an instance's console.
6476 a8083063 Iustin Pop

6477 a8083063 Iustin Pop
  This is somewhat special in that it returns the command line that
6478 a8083063 Iustin Pop
  you need to run on the master node in order to connect to the
6479 a8083063 Iustin Pop
  console.
6480 a8083063 Iustin Pop

6481 a8083063 Iustin Pop
  """
6482 a8083063 Iustin Pop
  _OP_REQP = ["instance_name"]
6483 8659b73e Guido Trotter
  REQ_BGL = False
6484 8659b73e Guido Trotter
6485 8659b73e Guido Trotter
  def ExpandNames(self):
6486 8659b73e Guido Trotter
    self._ExpandAndLockInstance()
6487 a8083063 Iustin Pop
6488 a8083063 Iustin Pop
  def CheckPrereq(self):
6489 a8083063 Iustin Pop
    """Check prerequisites.
6490 a8083063 Iustin Pop

6491 a8083063 Iustin Pop
    This checks that the instance is in the cluster.
6492 a8083063 Iustin Pop

6493 a8083063 Iustin Pop
    """
6494 8659b73e Guido Trotter
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
6495 8659b73e Guido Trotter
    assert self.instance is not None, \
6496 8659b73e Guido Trotter
      "Cannot retrieve locked instance %s" % self.op.instance_name
6497 513e896d Guido Trotter
    _CheckNodeOnline(self, self.instance.primary_node)
6498 a8083063 Iustin Pop
6499 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
6500 a8083063 Iustin Pop
    """Connect to the console of an instance
6501 a8083063 Iustin Pop

6502 a8083063 Iustin Pop
    """
6503 a8083063 Iustin Pop
    instance = self.instance
6504 a8083063 Iustin Pop
    node = instance.primary_node
6505 a8083063 Iustin Pop
6506 72737a7f Iustin Pop
    node_insts = self.rpc.call_instance_list([node],
6507 72737a7f Iustin Pop
                                             [instance.hypervisor])[node]
6508 4c4e4e1e Iustin Pop
    node_insts.Raise("Can't get node information from %s" % node)
6509 a8083063 Iustin Pop
6510 aca13712 Iustin Pop
    if instance.name not in node_insts.payload:
6511 3ecf6786 Iustin Pop
      raise errors.OpExecError("Instance %s is not running." % instance.name)
6512 a8083063 Iustin Pop
6513 9a4f63d1 Iustin Pop
    logging.debug("Connecting to console of %s on %s", instance.name, node)
6514 a8083063 Iustin Pop
6515 e69d05fd Iustin Pop
    hyper = hypervisor.GetHypervisor(instance.hypervisor)
6516 5431b2e4 Guido Trotter
    cluster = self.cfg.GetClusterInfo()
6517 5431b2e4 Guido Trotter
    # beparams and hvparams are passed separately, to avoid editing the
6518 5431b2e4 Guido Trotter
    # instance and then saving the defaults in the instance itself.
6519 5431b2e4 Guido Trotter
    hvparams = cluster.FillHV(instance)
6520 5431b2e4 Guido Trotter
    beparams = cluster.FillBE(instance)
6521 5431b2e4 Guido Trotter
    console_cmd = hyper.GetShellCommandForConsole(instance, hvparams, beparams)
6522 b047857b Michael Hanselmann
6523 82122173 Iustin Pop
    # build ssh cmdline
6524 0a80a26f Michael Hanselmann
    return self.ssh.BuildCmd(node, "root", console_cmd, batch=True, tty=True)
6525 a8083063 Iustin Pop
6526 a8083063 Iustin Pop
6527 a8083063 Iustin Pop
class LUReplaceDisks(LogicalUnit):
6528 a8083063 Iustin Pop
  """Replace the disks of an instance.
6529 a8083063 Iustin Pop

6530 a8083063 Iustin Pop
  """
6531 a8083063 Iustin Pop
  HPATH = "mirrors-replace"
6532 a8083063 Iustin Pop
  HTYPE = constants.HTYPE_INSTANCE
6533 a9e0c397 Iustin Pop
  _OP_REQP = ["instance_name", "mode", "disks"]
6534 efd990e4 Guido Trotter
  REQ_BGL = False
6535 efd990e4 Guido Trotter
6536 7e9366f7 Iustin Pop
  def CheckArguments(self):
6537 efd990e4 Guido Trotter
    if not hasattr(self.op, "remote_node"):
6538 efd990e4 Guido Trotter
      self.op.remote_node = None
6539 7e9366f7 Iustin Pop
    if not hasattr(self.op, "iallocator"):
6540 7e9366f7 Iustin Pop
      self.op.iallocator = None
6541 7ea7bcf6 Iustin Pop
    if not hasattr(self.op, "early_release"):
6542 7ea7bcf6 Iustin Pop
      self.op.early_release = False
6543 7e9366f7 Iustin Pop
6544 c68174b6 Michael Hanselmann
    TLReplaceDisks.CheckArguments(self.op.mode, self.op.remote_node,
6545 c68174b6 Michael Hanselmann
                                  self.op.iallocator)
6546 7e9366f7 Iustin Pop
6547 7e9366f7 Iustin Pop
  def ExpandNames(self):
6548 7e9366f7 Iustin Pop
    self._ExpandAndLockInstance()
6549 7e9366f7 Iustin Pop
6550 7e9366f7 Iustin Pop
    if self.op.iallocator is not None:
6551 efd990e4 Guido Trotter
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
6552 2bb5c911 Michael Hanselmann
6553 efd990e4 Guido Trotter
    elif self.op.remote_node is not None:
6554 cf26a87a Iustin Pop
      remote_node = _ExpandNodeName(self.cfg, self.op.remote_node)
6555 efd990e4 Guido Trotter
      self.op.remote_node = remote_node
6556 2bb5c911 Michael Hanselmann
6557 3b559640 Iustin Pop
      # Warning: do not remove the locking of the new secondary here
6558 3b559640 Iustin Pop
      # unless DRBD8.AddChildren is changed to work in parallel;
6559 3b559640 Iustin Pop
      # currently it doesn't since parallel invocations of
6560 3b559640 Iustin Pop
      # FindUnusedMinor will conflict
6561 efd990e4 Guido Trotter
      self.needed_locks[locking.LEVEL_NODE] = [remote_node]
6562 efd990e4 Guido Trotter
      self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
6563 2bb5c911 Michael Hanselmann
6564 efd990e4 Guido Trotter
    else:
6565 efd990e4 Guido Trotter
      self.needed_locks[locking.LEVEL_NODE] = []
6566 efd990e4 Guido Trotter
      self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
6567 efd990e4 Guido Trotter
6568 c68174b6 Michael Hanselmann
    self.replacer = TLReplaceDisks(self, self.op.instance_name, self.op.mode,
6569 c68174b6 Michael Hanselmann
                                   self.op.iallocator, self.op.remote_node,
6570 7ea7bcf6 Iustin Pop
                                   self.op.disks, False, self.op.early_release)
6571 c68174b6 Michael Hanselmann
6572 3a012b41 Michael Hanselmann
    self.tasklets = [self.replacer]
6573 2bb5c911 Michael Hanselmann
6574 efd990e4 Guido Trotter
  def DeclareLocks(self, level):
6575 efd990e4 Guido Trotter
    # If we're not already locking all nodes in the set we have to declare the
6576 efd990e4 Guido Trotter
    # instance's primary/secondary nodes.
6577 efd990e4 Guido Trotter
    if (level == locking.LEVEL_NODE and
6578 efd990e4 Guido Trotter
        self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET):
6579 efd990e4 Guido Trotter
      self._LockInstancesNodes()
6580 a8083063 Iustin Pop
6581 a8083063 Iustin Pop
  def BuildHooksEnv(self):
6582 a8083063 Iustin Pop
    """Build hooks env.
6583 a8083063 Iustin Pop

6584 a8083063 Iustin Pop
    This runs on the master, the primary and all the secondaries.
6585 a8083063 Iustin Pop

6586 a8083063 Iustin Pop
    """
6587 2bb5c911 Michael Hanselmann
    instance = self.replacer.instance
6588 a8083063 Iustin Pop
    env = {
6589 a9e0c397 Iustin Pop
      "MODE": self.op.mode,
6590 a8083063 Iustin Pop
      "NEW_SECONDARY": self.op.remote_node,
6591 2bb5c911 Michael Hanselmann
      "OLD_SECONDARY": instance.secondary_nodes[0],
6592 a8083063 Iustin Pop
      }
6593 2bb5c911 Michael Hanselmann
    env.update(_BuildInstanceHookEnvByObject(self, instance))
6594 0834c866 Iustin Pop
    nl = [
6595 d6a02168 Michael Hanselmann
      self.cfg.GetMasterNode(),
6596 2bb5c911 Michael Hanselmann
      instance.primary_node,
6597 0834c866 Iustin Pop
      ]
6598 0834c866 Iustin Pop
    if self.op.remote_node is not None:
6599 0834c866 Iustin Pop
      nl.append(self.op.remote_node)
6600 a8083063 Iustin Pop
    return env, nl, nl
6601 a8083063 Iustin Pop
6602 2bb5c911 Michael Hanselmann
6603 7ffc5a86 Michael Hanselmann
class LUEvacuateNode(LogicalUnit):
6604 7ffc5a86 Michael Hanselmann
  """Relocate the secondary instances from a node.
6605 7ffc5a86 Michael Hanselmann

6606 7ffc5a86 Michael Hanselmann
  """
6607 7ffc5a86 Michael Hanselmann
  HPATH = "node-evacuate"
6608 7ffc5a86 Michael Hanselmann
  HTYPE = constants.HTYPE_NODE
6609 7ffc5a86 Michael Hanselmann
  _OP_REQP = ["node_name"]
6610 7ffc5a86 Michael Hanselmann
  REQ_BGL = False
6611 7ffc5a86 Michael Hanselmann
6612 7ffc5a86 Michael Hanselmann
  def CheckArguments(self):
6613 7ffc5a86 Michael Hanselmann
    if not hasattr(self.op, "remote_node"):
6614 7ffc5a86 Michael Hanselmann
      self.op.remote_node = None
6615 7ffc5a86 Michael Hanselmann
    if not hasattr(self.op, "iallocator"):
6616 7ffc5a86 Michael Hanselmann
      self.op.iallocator = None
6617 7ea7bcf6 Iustin Pop
    if not hasattr(self.op, "early_release"):
6618 7ea7bcf6 Iustin Pop
      self.op.early_release = False
6619 7ffc5a86 Michael Hanselmann
6620 7ffc5a86 Michael Hanselmann
    TLReplaceDisks.CheckArguments(constants.REPLACE_DISK_CHG,
6621 7ffc5a86 Michael Hanselmann
                                  self.op.remote_node,
6622 7ffc5a86 Michael Hanselmann
                                  self.op.iallocator)
6623 7ffc5a86 Michael Hanselmann
6624 7ffc5a86 Michael Hanselmann
  def ExpandNames(self):
6625 cf26a87a Iustin Pop
    self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name)
6626 7ffc5a86 Michael Hanselmann
6627 7ffc5a86 Michael Hanselmann
    self.needed_locks = {}
6628 7ffc5a86 Michael Hanselmann
6629 7ffc5a86 Michael Hanselmann
    # Declare node locks
6630 7ffc5a86 Michael Hanselmann
    if self.op.iallocator is not None:
6631 7ffc5a86 Michael Hanselmann
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
6632 7ffc5a86 Michael Hanselmann
6633 7ffc5a86 Michael Hanselmann
    elif self.op.remote_node is not None:
6634 cf26a87a Iustin Pop
      self.op.remote_node = _ExpandNodeName(self.cfg, self.op.remote_node)
6635 7ffc5a86 Michael Hanselmann
6636 7ffc5a86 Michael Hanselmann
      # Warning: do not remove the locking of the new secondary here
6637 7ffc5a86 Michael Hanselmann
      # unless DRBD8.AddChildren is changed to work in parallel;
6638 7ffc5a86 Michael Hanselmann
      # currently it doesn't since parallel invocations of
6639 7ffc5a86 Michael Hanselmann
      # FindUnusedMinor will conflict
6640 cf26a87a Iustin Pop
      self.needed_locks[locking.LEVEL_NODE] = [self.op.remote_node]
6641 7ffc5a86 Michael Hanselmann
      self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
6642 7ffc5a86 Michael Hanselmann
6643 7ffc5a86 Michael Hanselmann
    else:
6644 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Invalid parameters", errors.ECODE_INVAL)
6645 7ffc5a86 Michael Hanselmann
6646 7ffc5a86 Michael Hanselmann
    # Create tasklets for replacing disks for all secondary instances on this
6647 7ffc5a86 Michael Hanselmann
    # node
6648 7ffc5a86 Michael Hanselmann
    names = []
6649 3a012b41 Michael Hanselmann
    tasklets = []
6650 7ffc5a86 Michael Hanselmann
6651 7ffc5a86 Michael Hanselmann
    for inst in _GetNodeSecondaryInstances(self.cfg, self.op.node_name):
6652 7ffc5a86 Michael Hanselmann
      logging.debug("Replacing disks for instance %s", inst.name)
6653 7ffc5a86 Michael Hanselmann
      names.append(inst.name)
6654 7ffc5a86 Michael Hanselmann
6655 7ffc5a86 Michael Hanselmann
      replacer = TLReplaceDisks(self, inst.name, constants.REPLACE_DISK_CHG,
6656 94a1b377 Michael Hanselmann
                                self.op.iallocator, self.op.remote_node, [],
6657 7ea7bcf6 Iustin Pop
                                True, self.op.early_release)
6658 3a012b41 Michael Hanselmann
      tasklets.append(replacer)
6659 7ffc5a86 Michael Hanselmann
6660 3a012b41 Michael Hanselmann
    self.tasklets = tasklets
6661 7ffc5a86 Michael Hanselmann
    self.instance_names = names
6662 7ffc5a86 Michael Hanselmann
6663 7ffc5a86 Michael Hanselmann
    # Declare instance locks
6664 7ffc5a86 Michael Hanselmann
    self.needed_locks[locking.LEVEL_INSTANCE] = self.instance_names
6665 7ffc5a86 Michael Hanselmann
6666 7ffc5a86 Michael Hanselmann
  def DeclareLocks(self, level):
6667 7ffc5a86 Michael Hanselmann
    # If we're not already locking all nodes in the set we have to declare the
6668 7ffc5a86 Michael Hanselmann
    # instance's primary/secondary nodes.
6669 7ffc5a86 Michael Hanselmann
    if (level == locking.LEVEL_NODE and
6670 7ffc5a86 Michael Hanselmann
        self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET):
6671 7ffc5a86 Michael Hanselmann
      self._LockInstancesNodes()
6672 7ffc5a86 Michael Hanselmann
6673 7ffc5a86 Michael Hanselmann
  def BuildHooksEnv(self):
6674 7ffc5a86 Michael Hanselmann
    """Build hooks env.
6675 7ffc5a86 Michael Hanselmann

6676 7ffc5a86 Michael Hanselmann
    This runs on the master, the primary and all the secondaries.
6677 7ffc5a86 Michael Hanselmann

6678 7ffc5a86 Michael Hanselmann
    """
6679 7ffc5a86 Michael Hanselmann
    env = {
6680 7ffc5a86 Michael Hanselmann
      "NODE_NAME": self.op.node_name,
6681 7ffc5a86 Michael Hanselmann
      }
6682 7ffc5a86 Michael Hanselmann
6683 7ffc5a86 Michael Hanselmann
    nl = [self.cfg.GetMasterNode()]
6684 7ffc5a86 Michael Hanselmann
6685 7ffc5a86 Michael Hanselmann
    if self.op.remote_node is not None:
6686 7ffc5a86 Michael Hanselmann
      env["NEW_SECONDARY"] = self.op.remote_node
6687 7ffc5a86 Michael Hanselmann
      nl.append(self.op.remote_node)
6688 7ffc5a86 Michael Hanselmann
6689 7ffc5a86 Michael Hanselmann
    return (env, nl, nl)
6690 7ffc5a86 Michael Hanselmann
6691 7ffc5a86 Michael Hanselmann
6692 c68174b6 Michael Hanselmann
class TLReplaceDisks(Tasklet):
6693 2bb5c911 Michael Hanselmann
  """Replaces disks for an instance.
6694 2bb5c911 Michael Hanselmann

6695 2bb5c911 Michael Hanselmann
  Note: Locking is not within the scope of this class.
6696 2bb5c911 Michael Hanselmann

6697 2bb5c911 Michael Hanselmann
  """
6698 2bb5c911 Michael Hanselmann
  def __init__(self, lu, instance_name, mode, iallocator_name, remote_node,
6699 7ea7bcf6 Iustin Pop
               disks, delay_iallocator, early_release):
6700 2bb5c911 Michael Hanselmann
    """Initializes this class.
6701 2bb5c911 Michael Hanselmann

6702 2bb5c911 Michael Hanselmann
    """
6703 464243a7 Michael Hanselmann
    Tasklet.__init__(self, lu)
6704 464243a7 Michael Hanselmann
6705 2bb5c911 Michael Hanselmann
    # Parameters
6706 2bb5c911 Michael Hanselmann
    self.instance_name = instance_name
6707 2bb5c911 Michael Hanselmann
    self.mode = mode
6708 2bb5c911 Michael Hanselmann
    self.iallocator_name = iallocator_name
6709 2bb5c911 Michael Hanselmann
    self.remote_node = remote_node
6710 2bb5c911 Michael Hanselmann
    self.disks = disks
6711 94a1b377 Michael Hanselmann
    self.delay_iallocator = delay_iallocator
6712 7ea7bcf6 Iustin Pop
    self.early_release = early_release
6713 2bb5c911 Michael Hanselmann
6714 2bb5c911 Michael Hanselmann
    # Runtime data
6715 2bb5c911 Michael Hanselmann
    self.instance = None
6716 2bb5c911 Michael Hanselmann
    self.new_node = None
6717 2bb5c911 Michael Hanselmann
    self.target_node = None
6718 2bb5c911 Michael Hanselmann
    self.other_node = None
6719 2bb5c911 Michael Hanselmann
    self.remote_node_info = None
6720 2bb5c911 Michael Hanselmann
    self.node_secondary_ip = None
6721 2bb5c911 Michael Hanselmann
6722 2bb5c911 Michael Hanselmann
  @staticmethod
6723 2bb5c911 Michael Hanselmann
  def CheckArguments(mode, remote_node, iallocator):
6724 c68174b6 Michael Hanselmann
    """Helper function for users of this class.
6725 c68174b6 Michael Hanselmann

6726 c68174b6 Michael Hanselmann
    """
6727 2bb5c911 Michael Hanselmann
    # check for valid parameter combination
6728 2bb5c911 Michael Hanselmann
    if mode == constants.REPLACE_DISK_CHG:
6729 02a00186 Michael Hanselmann
      if remote_node is None and iallocator is None:
6730 2bb5c911 Michael Hanselmann
        raise errors.OpPrereqError("When changing the secondary either an"
6731 2bb5c911 Michael Hanselmann
                                   " iallocator script must be used or the"
6732 5c983ee5 Iustin Pop
                                   " new node given", errors.ECODE_INVAL)
6733 02a00186 Michael Hanselmann
6734 02a00186 Michael Hanselmann
      if remote_node is not None and iallocator is not None:
6735 2bb5c911 Michael Hanselmann
        raise errors.OpPrereqError("Give either the iallocator or the new"
6736 5c983ee5 Iustin Pop
                                   " secondary, not both", errors.ECODE_INVAL)
6737 02a00186 Michael Hanselmann
6738 02a00186 Michael Hanselmann
    elif remote_node is not None or iallocator is not None:
6739 02a00186 Michael Hanselmann
      # Not replacing the secondary
6740 02a00186 Michael Hanselmann
      raise errors.OpPrereqError("The iallocator and new node options can"
6741 02a00186 Michael Hanselmann
                                 " only be used when changing the"
6742 5c983ee5 Iustin Pop
                                 " secondary node", errors.ECODE_INVAL)
6743 2bb5c911 Michael Hanselmann
6744 2bb5c911 Michael Hanselmann
  @staticmethod
6745 2bb5c911 Michael Hanselmann
  def _RunAllocator(lu, iallocator_name, instance_name, relocate_from):
6746 2bb5c911 Michael Hanselmann
    """Compute a new secondary node using an IAllocator.
6747 2bb5c911 Michael Hanselmann

6748 2bb5c911 Michael Hanselmann
    """
6749 2bb5c911 Michael Hanselmann
    ial = IAllocator(lu.cfg, lu.rpc,
6750 2bb5c911 Michael Hanselmann
                     mode=constants.IALLOCATOR_MODE_RELOC,
6751 2bb5c911 Michael Hanselmann
                     name=instance_name,
6752 2bb5c911 Michael Hanselmann
                     relocate_from=relocate_from)
6753 2bb5c911 Michael Hanselmann
6754 2bb5c911 Michael Hanselmann
    ial.Run(iallocator_name)
6755 2bb5c911 Michael Hanselmann
6756 2bb5c911 Michael Hanselmann
    if not ial.success:
6757 2bb5c911 Michael Hanselmann
      raise errors.OpPrereqError("Can't compute nodes using iallocator '%s':"
6758 5c983ee5 Iustin Pop
                                 " %s" % (iallocator_name, ial.info),
6759 5c983ee5 Iustin Pop
                                 errors.ECODE_NORES)
6760 2bb5c911 Michael Hanselmann
6761 680f0a89 Iustin Pop
    if len(ial.result) != ial.required_nodes:
6762 2bb5c911 Michael Hanselmann
      raise errors.OpPrereqError("iallocator '%s' returned invalid number"
6763 2bb5c911 Michael Hanselmann
                                 " of nodes (%s), required %s" %
6764 d984846d Iustin Pop
                                 (iallocator_name,
6765 680f0a89 Iustin Pop
                                  len(ial.result), ial.required_nodes),
6766 5c983ee5 Iustin Pop
                                 errors.ECODE_FAULT)
6767 2bb5c911 Michael Hanselmann
6768 680f0a89 Iustin Pop
    remote_node_name = ial.result[0]
6769 2bb5c911 Michael Hanselmann
6770 2bb5c911 Michael Hanselmann
    lu.LogInfo("Selected new secondary for instance '%s': %s",
6771 2bb5c911 Michael Hanselmann
               instance_name, remote_node_name)
6772 2bb5c911 Michael Hanselmann
6773 2bb5c911 Michael Hanselmann
    return remote_node_name
6774 2bb5c911 Michael Hanselmann
6775 942be002 Michael Hanselmann
  def _FindFaultyDisks(self, node_name):
6776 2d9005d8 Michael Hanselmann
    return _FindFaultyInstanceDisks(self.cfg, self.rpc, self.instance,
6777 2d9005d8 Michael Hanselmann
                                    node_name, True)
6778 942be002 Michael Hanselmann
6779 2bb5c911 Michael Hanselmann
  def CheckPrereq(self):
6780 2bb5c911 Michael Hanselmann
    """Check prerequisites.
6781 2bb5c911 Michael Hanselmann

6782 2bb5c911 Michael Hanselmann
    This checks that the instance is in the cluster.
6783 2bb5c911 Michael Hanselmann

6784 2bb5c911 Michael Hanselmann
    """
6785 e9022531 Iustin Pop
    self.instance = instance = self.cfg.GetInstanceInfo(self.instance_name)
6786 e9022531 Iustin Pop
    assert instance is not None, \
6787 20eca47d Iustin Pop
      "Cannot retrieve locked instance %s" % self.instance_name
6788 2bb5c911 Michael Hanselmann
6789 e9022531 Iustin Pop
    if instance.disk_template != constants.DT_DRBD8:
6790 7e9366f7 Iustin Pop
      raise errors.OpPrereqError("Can only run replace disks for DRBD8-based"
6791 5c983ee5 Iustin Pop
                                 " instances", errors.ECODE_INVAL)
6792 a8083063 Iustin Pop
6793 e9022531 Iustin Pop
    if len(instance.secondary_nodes) != 1:
6794 3ecf6786 Iustin Pop
      raise errors.OpPrereqError("The instance has a strange layout,"
6795 3ecf6786 Iustin Pop
                                 " expected one secondary but found %d" %
6796 5c983ee5 Iustin Pop
                                 len(instance.secondary_nodes),
6797 5c983ee5 Iustin Pop
                                 errors.ECODE_FAULT)
6798 a8083063 Iustin Pop
6799 94a1b377 Michael Hanselmann
    if not self.delay_iallocator:
6800 94a1b377 Michael Hanselmann
      self._CheckPrereq2()
6801 94a1b377 Michael Hanselmann
6802 94a1b377 Michael Hanselmann
  def _CheckPrereq2(self):
6803 94a1b377 Michael Hanselmann
    """Check prerequisites, second part.
6804 94a1b377 Michael Hanselmann

6805 94a1b377 Michael Hanselmann
    This function should always be part of CheckPrereq. It was separated and is
6806 94a1b377 Michael Hanselmann
    now called from Exec because during node evacuation iallocator was only
6807 94a1b377 Michael Hanselmann
    called with an unmodified cluster model, not taking planned changes into
6808 94a1b377 Michael Hanselmann
    account.
6809 94a1b377 Michael Hanselmann

6810 94a1b377 Michael Hanselmann
    """
6811 94a1b377 Michael Hanselmann
    instance = self.instance
6812 e9022531 Iustin Pop
    secondary_node = instance.secondary_nodes[0]
6813 a9e0c397 Iustin Pop
6814 2bb5c911 Michael Hanselmann
    if self.iallocator_name is None:
6815 2bb5c911 Michael Hanselmann
      remote_node = self.remote_node
6816 2bb5c911 Michael Hanselmann
    else:
6817 2bb5c911 Michael Hanselmann
      remote_node = self._RunAllocator(self.lu, self.iallocator_name,
6818 e9022531 Iustin Pop
                                       instance.name, instance.secondary_nodes)
6819 b6e82a65 Iustin Pop
6820 a9e0c397 Iustin Pop
    if remote_node is not None:
6821 a9e0c397 Iustin Pop
      self.remote_node_info = self.cfg.GetNodeInfo(remote_node)
6822 efd990e4 Guido Trotter
      assert self.remote_node_info is not None, \
6823 efd990e4 Guido Trotter
        "Cannot retrieve locked node %s" % remote_node
6824 a9e0c397 Iustin Pop
    else:
6825 a9e0c397 Iustin Pop
      self.remote_node_info = None
6826 2bb5c911 Michael Hanselmann
6827 2bb5c911 Michael Hanselmann
    if remote_node == self.instance.primary_node:
6828 3ecf6786 Iustin Pop
      raise errors.OpPrereqError("The specified node is the primary node of"
6829 5c983ee5 Iustin Pop
                                 " the instance.", errors.ECODE_INVAL)
6830 2bb5c911 Michael Hanselmann
6831 2bb5c911 Michael Hanselmann
    if remote_node == secondary_node:
6832 7e9366f7 Iustin Pop
      raise errors.OpPrereqError("The specified node is already the"
6833 5c983ee5 Iustin Pop
                                 " secondary node of the instance.",
6834 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
6835 7e9366f7 Iustin Pop
6836 2945fd2d Michael Hanselmann
    if self.disks and self.mode in (constants.REPLACE_DISK_AUTO,
6837 2945fd2d Michael Hanselmann
                                    constants.REPLACE_DISK_CHG):
6838 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Cannot specify disks to be replaced",
6839 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
6840 942be002 Michael Hanselmann
6841 2945fd2d Michael Hanselmann
    if self.mode == constants.REPLACE_DISK_AUTO:
6842 e9022531 Iustin Pop
      faulty_primary = self._FindFaultyDisks(instance.primary_node)
6843 942be002 Michael Hanselmann
      faulty_secondary = self._FindFaultyDisks(secondary_node)
6844 942be002 Michael Hanselmann
6845 942be002 Michael Hanselmann
      if faulty_primary and faulty_secondary:
6846 942be002 Michael Hanselmann
        raise errors.OpPrereqError("Instance %s has faulty disks on more than"
6847 942be002 Michael Hanselmann
                                   " one node and can not be repaired"
6848 5c983ee5 Iustin Pop
                                   " automatically" % self.instance_name,
6849 5c983ee5 Iustin Pop
                                   errors.ECODE_STATE)
6850 942be002 Michael Hanselmann
6851 942be002 Michael Hanselmann
      if faulty_primary:
6852 942be002 Michael Hanselmann
        self.disks = faulty_primary
6853 e9022531 Iustin Pop
        self.target_node = instance.primary_node
6854 942be002 Michael Hanselmann
        self.other_node = secondary_node
6855 942be002 Michael Hanselmann
        check_nodes = [self.target_node, self.other_node]
6856 942be002 Michael Hanselmann
      elif faulty_secondary:
6857 942be002 Michael Hanselmann
        self.disks = faulty_secondary
6858 942be002 Michael Hanselmann
        self.target_node = secondary_node
6859 e9022531 Iustin Pop
        self.other_node = instance.primary_node
6860 942be002 Michael Hanselmann
        check_nodes = [self.target_node, self.other_node]
6861 942be002 Michael Hanselmann
      else:
6862 942be002 Michael Hanselmann
        self.disks = []
6863 942be002 Michael Hanselmann
        check_nodes = []
6864 942be002 Michael Hanselmann
6865 942be002 Michael Hanselmann
    else:
6866 942be002 Michael Hanselmann
      # Non-automatic modes
6867 942be002 Michael Hanselmann
      if self.mode == constants.REPLACE_DISK_PRI:
6868 e9022531 Iustin Pop
        self.target_node = instance.primary_node
6869 942be002 Michael Hanselmann
        self.other_node = secondary_node
6870 942be002 Michael Hanselmann
        check_nodes = [self.target_node, self.other_node]
6871 7e9366f7 Iustin Pop
6872 942be002 Michael Hanselmann
      elif self.mode == constants.REPLACE_DISK_SEC:
6873 942be002 Michael Hanselmann
        self.target_node = secondary_node
6874 e9022531 Iustin Pop
        self.other_node = instance.primary_node
6875 942be002 Michael Hanselmann
        check_nodes = [self.target_node, self.other_node]
6876 a9e0c397 Iustin Pop
6877 942be002 Michael Hanselmann
      elif self.mode == constants.REPLACE_DISK_CHG:
6878 942be002 Michael Hanselmann
        self.new_node = remote_node
6879 e9022531 Iustin Pop
        self.other_node = instance.primary_node
6880 942be002 Michael Hanselmann
        self.target_node = secondary_node
6881 942be002 Michael Hanselmann
        check_nodes = [self.new_node, self.other_node]
6882 54155f52 Iustin Pop
6883 942be002 Michael Hanselmann
        _CheckNodeNotDrained(self.lu, remote_node)
6884 a8083063 Iustin Pop
6885 9af0fa6a Iustin Pop
        old_node_info = self.cfg.GetNodeInfo(secondary_node)
6886 9af0fa6a Iustin Pop
        assert old_node_info is not None
6887 9af0fa6a Iustin Pop
        if old_node_info.offline and not self.early_release:
6888 9af0fa6a Iustin Pop
          # doesn't make sense to delay the release
6889 9af0fa6a Iustin Pop
          self.early_release = True
6890 9af0fa6a Iustin Pop
          self.lu.LogInfo("Old secondary %s is offline, automatically enabling"
6891 9af0fa6a Iustin Pop
                          " early-release mode", secondary_node)
6892 9af0fa6a Iustin Pop
6893 942be002 Michael Hanselmann
      else:
6894 942be002 Michael Hanselmann
        raise errors.ProgrammerError("Unhandled disk replace mode (%s)" %
6895 942be002 Michael Hanselmann
                                     self.mode)
6896 942be002 Michael Hanselmann
6897 942be002 Michael Hanselmann
      # If not specified all disks should be replaced
6898 942be002 Michael Hanselmann
      if not self.disks:
6899 942be002 Michael Hanselmann
        self.disks = range(len(self.instance.disks))
6900 a9e0c397 Iustin Pop
6901 2bb5c911 Michael Hanselmann
    for node in check_nodes:
6902 2bb5c911 Michael Hanselmann
      _CheckNodeOnline(self.lu, node)
6903 e4376078 Iustin Pop
6904 2bb5c911 Michael Hanselmann
    # Check whether disks are valid
6905 2bb5c911 Michael Hanselmann
    for disk_idx in self.disks:
6906 e9022531 Iustin Pop
      instance.FindDisk(disk_idx)
6907 e4376078 Iustin Pop
6908 2bb5c911 Michael Hanselmann
    # Get secondary node IP addresses
6909 2bb5c911 Michael Hanselmann
    node_2nd_ip = {}
6910 e4376078 Iustin Pop
6911 2bb5c911 Michael Hanselmann
    for node_name in [self.target_node, self.other_node, self.new_node]:
6912 2bb5c911 Michael Hanselmann
      if node_name is not None:
6913 2bb5c911 Michael Hanselmann
        node_2nd_ip[node_name] = self.cfg.GetNodeInfo(node_name).secondary_ip
6914 e4376078 Iustin Pop
6915 2bb5c911 Michael Hanselmann
    self.node_secondary_ip = node_2nd_ip
6916 a9e0c397 Iustin Pop
6917 c68174b6 Michael Hanselmann
  def Exec(self, feedback_fn):
6918 2bb5c911 Michael Hanselmann
    """Execute disk replacement.
6919 2bb5c911 Michael Hanselmann

6920 2bb5c911 Michael Hanselmann
    This dispatches the disk replacement to the appropriate handler.
6921 cff90b79 Iustin Pop

6922 a9e0c397 Iustin Pop
    """
6923 94a1b377 Michael Hanselmann
    if self.delay_iallocator:
6924 94a1b377 Michael Hanselmann
      self._CheckPrereq2()
6925 94a1b377 Michael Hanselmann
6926 942be002 Michael Hanselmann
    if not self.disks:
6927 942be002 Michael Hanselmann
      feedback_fn("No disks need replacement")
6928 942be002 Michael Hanselmann
      return
6929 942be002 Michael Hanselmann
6930 942be002 Michael Hanselmann
    feedback_fn("Replacing disk(s) %s for %s" %
6931 1f864b60 Iustin Pop
                (utils.CommaJoin(self.disks), self.instance.name))
6932 7ffc5a86 Michael Hanselmann
6933 2bb5c911 Michael Hanselmann
    activate_disks = (not self.instance.admin_up)
6934 2bb5c911 Michael Hanselmann
6935 2bb5c911 Michael Hanselmann
    # Activate the instance disks if we're replacing them on a down instance
6936 2bb5c911 Michael Hanselmann
    if activate_disks:
6937 2bb5c911 Michael Hanselmann
      _StartInstanceDisks(self.lu, self.instance, True)
6938 2bb5c911 Michael Hanselmann
6939 2bb5c911 Michael Hanselmann
    try:
6940 942be002 Michael Hanselmann
      # Should we replace the secondary node?
6941 942be002 Michael Hanselmann
      if self.new_node is not None:
6942 a4eae71f Michael Hanselmann
        fn = self._ExecDrbd8Secondary
6943 2bb5c911 Michael Hanselmann
      else:
6944 a4eae71f Michael Hanselmann
        fn = self._ExecDrbd8DiskOnly
6945 a4eae71f Michael Hanselmann
6946 a4eae71f Michael Hanselmann
      return fn(feedback_fn)
6947 2bb5c911 Michael Hanselmann
6948 2bb5c911 Michael Hanselmann
    finally:
6949 5c983ee5 Iustin Pop
      # Deactivate the instance disks if we're replacing them on a
6950 5c983ee5 Iustin Pop
      # down instance
6951 2bb5c911 Michael Hanselmann
      if activate_disks:
6952 2bb5c911 Michael Hanselmann
        _SafeShutdownInstanceDisks(self.lu, self.instance)
6953 2bb5c911 Michael Hanselmann
6954 2bb5c911 Michael Hanselmann
  def _CheckVolumeGroup(self, nodes):
6955 2bb5c911 Michael Hanselmann
    self.lu.LogInfo("Checking volume groups")
6956 2bb5c911 Michael Hanselmann
6957 a9e0c397 Iustin Pop
    vgname = self.cfg.GetVGName()
6958 cff90b79 Iustin Pop
6959 2bb5c911 Michael Hanselmann
    # Make sure volume group exists on all involved nodes
6960 2bb5c911 Michael Hanselmann
    results = self.rpc.call_vg_list(nodes)
6961 cff90b79 Iustin Pop
    if not results:
6962 cff90b79 Iustin Pop
      raise errors.OpExecError("Can't list volume groups on the nodes")
6963 2bb5c911 Michael Hanselmann
6964 2bb5c911 Michael Hanselmann
    for node in nodes:
6965 781de953 Iustin Pop
      res = results[node]
6966 4c4e4e1e Iustin Pop
      res.Raise("Error checking node %s" % node)
6967 2bb5c911 Michael Hanselmann
      if vgname not in res.payload:
6968 2bb5c911 Michael Hanselmann
        raise errors.OpExecError("Volume group '%s' not found on node %s" %
6969 2bb5c911 Michael Hanselmann
                                 (vgname, node))
6970 2bb5c911 Michael Hanselmann
6971 2bb5c911 Michael Hanselmann
  def _CheckDisksExistence(self, nodes):
6972 2bb5c911 Michael Hanselmann
    # Check disk existence
6973 2bb5c911 Michael Hanselmann
    for idx, dev in enumerate(self.instance.disks):
6974 2bb5c911 Michael Hanselmann
      if idx not in self.disks:
6975 cff90b79 Iustin Pop
        continue
6976 2bb5c911 Michael Hanselmann
6977 2bb5c911 Michael Hanselmann
      for node in nodes:
6978 2bb5c911 Michael Hanselmann
        self.lu.LogInfo("Checking disk/%d on %s" % (idx, node))
6979 2bb5c911 Michael Hanselmann
        self.cfg.SetDiskID(dev, node)
6980 2bb5c911 Michael Hanselmann
6981 23829f6f Iustin Pop
        result = self.rpc.call_blockdev_find(node, dev)
6982 2bb5c911 Michael Hanselmann
6983 4c4e4e1e Iustin Pop
        msg = result.fail_msg
6984 2bb5c911 Michael Hanselmann
        if msg or not result.payload:
6985 2bb5c911 Michael Hanselmann
          if not msg:
6986 2bb5c911 Michael Hanselmann
            msg = "disk not found"
6987 23829f6f Iustin Pop
          raise errors.OpExecError("Can't find disk/%d on node %s: %s" %
6988 23829f6f Iustin Pop
                                   (idx, node, msg))
6989 cff90b79 Iustin Pop
6990 2bb5c911 Michael Hanselmann
  def _CheckDisksConsistency(self, node_name, on_primary, ldisk):
6991 2bb5c911 Michael Hanselmann
    for idx, dev in enumerate(self.instance.disks):
6992 2bb5c911 Michael Hanselmann
      if idx not in self.disks:
6993 cff90b79 Iustin Pop
        continue
6994 cff90b79 Iustin Pop
6995 2bb5c911 Michael Hanselmann
      self.lu.LogInfo("Checking disk/%d consistency on node %s" %
6996 2bb5c911 Michael Hanselmann
                      (idx, node_name))
6997 2bb5c911 Michael Hanselmann
6998 2bb5c911 Michael Hanselmann
      if not _CheckDiskConsistency(self.lu, dev, node_name, on_primary,
6999 2bb5c911 Michael Hanselmann
                                   ldisk=ldisk):
7000 2bb5c911 Michael Hanselmann
        raise errors.OpExecError("Node %s has degraded storage, unsafe to"
7001 2bb5c911 Michael Hanselmann
                                 " replace disks for instance %s" %
7002 2bb5c911 Michael Hanselmann
                                 (node_name, self.instance.name))
7003 2bb5c911 Michael Hanselmann
7004 2bb5c911 Michael Hanselmann
  def _CreateNewStorage(self, node_name):
7005 2bb5c911 Michael Hanselmann
    vgname = self.cfg.GetVGName()
7006 2bb5c911 Michael Hanselmann
    iv_names = {}
7007 2bb5c911 Michael Hanselmann
7008 2bb5c911 Michael Hanselmann
    for idx, dev in enumerate(self.instance.disks):
7009 2bb5c911 Michael Hanselmann
      if idx not in self.disks:
7010 a9e0c397 Iustin Pop
        continue
7011 2bb5c911 Michael Hanselmann
7012 2bb5c911 Michael Hanselmann
      self.lu.LogInfo("Adding storage on %s for disk/%d" % (node_name, idx))
7013 2bb5c911 Michael Hanselmann
7014 2bb5c911 Michael Hanselmann
      self.cfg.SetDiskID(dev, node_name)
7015 2bb5c911 Michael Hanselmann
7016 2bb5c911 Michael Hanselmann
      lv_names = [".disk%d_%s" % (idx, suffix) for suffix in ["data", "meta"]]
7017 2bb5c911 Michael Hanselmann
      names = _GenerateUniqueNames(self.lu, lv_names)
7018 2bb5c911 Michael Hanselmann
7019 2bb5c911 Michael Hanselmann
      lv_data = objects.Disk(dev_type=constants.LD_LV, size=dev.size,
7020 a9e0c397 Iustin Pop
                             logical_id=(vgname, names[0]))
7021 a9e0c397 Iustin Pop
      lv_meta = objects.Disk(dev_type=constants.LD_LV, size=128,
7022 a9e0c397 Iustin Pop
                             logical_id=(vgname, names[1]))
7023 2bb5c911 Michael Hanselmann
7024 a9e0c397 Iustin Pop
      new_lvs = [lv_data, lv_meta]
7025 a9e0c397 Iustin Pop
      old_lvs = dev.children
7026 a9e0c397 Iustin Pop
      iv_names[dev.iv_name] = (dev, old_lvs, new_lvs)
7027 2bb5c911 Michael Hanselmann
7028 428958aa Iustin Pop
      # we pass force_create=True to force the LVM creation
7029 a9e0c397 Iustin Pop
      for new_lv in new_lvs:
7030 2bb5c911 Michael Hanselmann
        _CreateBlockDev(self.lu, node_name, self.instance, new_lv, True,
7031 2bb5c911 Michael Hanselmann
                        _GetInstanceInfoText(self.instance), False)
7032 2bb5c911 Michael Hanselmann
7033 2bb5c911 Michael Hanselmann
    return iv_names
7034 2bb5c911 Michael Hanselmann
7035 2bb5c911 Michael Hanselmann
  def _CheckDevices(self, node_name, iv_names):
7036 1122eb25 Iustin Pop
    for name, (dev, _, _) in iv_names.iteritems():
7037 2bb5c911 Michael Hanselmann
      self.cfg.SetDiskID(dev, node_name)
7038 2bb5c911 Michael Hanselmann
7039 2bb5c911 Michael Hanselmann
      result = self.rpc.call_blockdev_find(node_name, dev)
7040 2bb5c911 Michael Hanselmann
7041 2bb5c911 Michael Hanselmann
      msg = result.fail_msg
7042 2bb5c911 Michael Hanselmann
      if msg or not result.payload:
7043 2bb5c911 Michael Hanselmann
        if not msg:
7044 2bb5c911 Michael Hanselmann
          msg = "disk not found"
7045 2bb5c911 Michael Hanselmann
        raise errors.OpExecError("Can't find DRBD device %s: %s" %
7046 2bb5c911 Michael Hanselmann
                                 (name, msg))
7047 2bb5c911 Michael Hanselmann
7048 96acbc09 Michael Hanselmann
      if result.payload.is_degraded:
7049 2bb5c911 Michael Hanselmann
        raise errors.OpExecError("DRBD device %s is degraded!" % name)
7050 2bb5c911 Michael Hanselmann
7051 2bb5c911 Michael Hanselmann
  def _RemoveOldStorage(self, node_name, iv_names):
7052 1122eb25 Iustin Pop
    for name, (_, old_lvs, _) in iv_names.iteritems():
7053 2bb5c911 Michael Hanselmann
      self.lu.LogInfo("Remove logical volumes for %s" % name)
7054 2bb5c911 Michael Hanselmann
7055 2bb5c911 Michael Hanselmann
      for lv in old_lvs:
7056 2bb5c911 Michael Hanselmann
        self.cfg.SetDiskID(lv, node_name)
7057 2bb5c911 Michael Hanselmann
7058 2bb5c911 Michael Hanselmann
        msg = self.rpc.call_blockdev_remove(node_name, lv).fail_msg
7059 2bb5c911 Michael Hanselmann
        if msg:
7060 2bb5c911 Michael Hanselmann
          self.lu.LogWarning("Can't remove old LV: %s" % msg,
7061 2bb5c911 Michael Hanselmann
                             hint="remove unused LVs manually")
7062 2bb5c911 Michael Hanselmann
7063 7ea7bcf6 Iustin Pop
  def _ReleaseNodeLock(self, node_name):
7064 7ea7bcf6 Iustin Pop
    """Releases the lock for a given node."""
7065 7ea7bcf6 Iustin Pop
    self.lu.context.glm.release(locking.LEVEL_NODE, node_name)
7066 7ea7bcf6 Iustin Pop
7067 a4eae71f Michael Hanselmann
  def _ExecDrbd8DiskOnly(self, feedback_fn):
7068 2bb5c911 Michael Hanselmann
    """Replace a disk on the primary or secondary for DRBD 8.
7069 2bb5c911 Michael Hanselmann

7070 2bb5c911 Michael Hanselmann
    The algorithm for replace is quite complicated:
7071 2bb5c911 Michael Hanselmann

7072 2bb5c911 Michael Hanselmann
      1. for each disk to be replaced:
7073 2bb5c911 Michael Hanselmann

7074 2bb5c911 Michael Hanselmann
        1. create new LVs on the target node with unique names
7075 2bb5c911 Michael Hanselmann
        1. detach old LVs from the drbd device
7076 2bb5c911 Michael Hanselmann
        1. rename old LVs to name_replaced.<time_t>
7077 2bb5c911 Michael Hanselmann
        1. rename new LVs to old LVs
7078 2bb5c911 Michael Hanselmann
        1. attach the new LVs (with the old names now) to the drbd device
7079 2bb5c911 Michael Hanselmann

7080 2bb5c911 Michael Hanselmann
      1. wait for sync across all devices
7081 2bb5c911 Michael Hanselmann

7082 2bb5c911 Michael Hanselmann
      1. for each modified disk:
7083 2bb5c911 Michael Hanselmann

7084 2bb5c911 Michael Hanselmann
        1. remove old LVs (which have the name name_replaces.<time_t>)
7085 2bb5c911 Michael Hanselmann

7086 2bb5c911 Michael Hanselmann
    Failures are not very well handled.
7087 2bb5c911 Michael Hanselmann

7088 2bb5c911 Michael Hanselmann
    """
7089 2bb5c911 Michael Hanselmann
    steps_total = 6
7090 2bb5c911 Michael Hanselmann
7091 2bb5c911 Michael Hanselmann
    # Step: check device activation
7092 2bb5c911 Michael Hanselmann
    self.lu.LogStep(1, steps_total, "Check device existence")
7093 2bb5c911 Michael Hanselmann
    self._CheckDisksExistence([self.other_node, self.target_node])
7094 2bb5c911 Michael Hanselmann
    self._CheckVolumeGroup([self.target_node, self.other_node])
7095 2bb5c911 Michael Hanselmann
7096 2bb5c911 Michael Hanselmann
    # Step: check other node consistency
7097 2bb5c911 Michael Hanselmann
    self.lu.LogStep(2, steps_total, "Check peer consistency")
7098 2bb5c911 Michael Hanselmann
    self._CheckDisksConsistency(self.other_node,
7099 2bb5c911 Michael Hanselmann
                                self.other_node == self.instance.primary_node,
7100 2bb5c911 Michael Hanselmann
                                False)
7101 2bb5c911 Michael Hanselmann
7102 2bb5c911 Michael Hanselmann
    # Step: create new storage
7103 2bb5c911 Michael Hanselmann
    self.lu.LogStep(3, steps_total, "Allocate new storage")
7104 2bb5c911 Michael Hanselmann
    iv_names = self._CreateNewStorage(self.target_node)
7105 a9e0c397 Iustin Pop
7106 cff90b79 Iustin Pop
    # Step: for each lv, detach+rename*2+attach
7107 2bb5c911 Michael Hanselmann
    self.lu.LogStep(4, steps_total, "Changing drbd configuration")
7108 cff90b79 Iustin Pop
    for dev, old_lvs, new_lvs in iv_names.itervalues():
7109 2bb5c911 Michael Hanselmann
      self.lu.LogInfo("Detaching %s drbd from local storage" % dev.iv_name)
7110 2bb5c911 Michael Hanselmann
7111 4d4a651d Michael Hanselmann
      result = self.rpc.call_blockdev_removechildren(self.target_node, dev,
7112 4d4a651d Michael Hanselmann
                                                     old_lvs)
7113 4c4e4e1e Iustin Pop
      result.Raise("Can't detach drbd from local storage on node"
7114 2bb5c911 Michael Hanselmann
                   " %s for device %s" % (self.target_node, dev.iv_name))
7115 cff90b79 Iustin Pop
      #dev.children = []
7116 cff90b79 Iustin Pop
      #cfg.Update(instance)
7117 a9e0c397 Iustin Pop
7118 a9e0c397 Iustin Pop
      # ok, we created the new LVs, so now we know we have the needed
7119 a9e0c397 Iustin Pop
      # storage; as such, we proceed on the target node to rename
7120 a9e0c397 Iustin Pop
      # old_lv to _old, and new_lv to old_lv; note that we rename LVs
7121 c99a3cc0 Manuel Franceschini
      # using the assumption that logical_id == physical_id (which in
7122 a9e0c397 Iustin Pop
      # turn is the unique_id on that node)
7123 cff90b79 Iustin Pop
7124 cff90b79 Iustin Pop
      # FIXME(iustin): use a better name for the replaced LVs
7125 a9e0c397 Iustin Pop
      temp_suffix = int(time.time())
7126 a9e0c397 Iustin Pop
      ren_fn = lambda d, suff: (d.physical_id[0],
7127 a9e0c397 Iustin Pop
                                d.physical_id[1] + "_replaced-%s" % suff)
7128 2bb5c911 Michael Hanselmann
7129 2bb5c911 Michael Hanselmann
      # Build the rename list based on what LVs exist on the node
7130 2bb5c911 Michael Hanselmann
      rename_old_to_new = []
7131 cff90b79 Iustin Pop
      for to_ren in old_lvs:
7132 2bb5c911 Michael Hanselmann
        result = self.rpc.call_blockdev_find(self.target_node, to_ren)
7133 4c4e4e1e Iustin Pop
        if not result.fail_msg and result.payload:
7134 23829f6f Iustin Pop
          # device exists
7135 2bb5c911 Michael Hanselmann
          rename_old_to_new.append((to_ren, ren_fn(to_ren, temp_suffix)))
7136 cff90b79 Iustin Pop
7137 2bb5c911 Michael Hanselmann
      self.lu.LogInfo("Renaming the old LVs on the target node")
7138 4d4a651d Michael Hanselmann
      result = self.rpc.call_blockdev_rename(self.target_node,
7139 4d4a651d Michael Hanselmann
                                             rename_old_to_new)
7140 2bb5c911 Michael Hanselmann
      result.Raise("Can't rename old LVs on node %s" % self.target_node)
7141 2bb5c911 Michael Hanselmann
7142 2bb5c911 Michael Hanselmann
      # Now we rename the new LVs to the old LVs
7143 2bb5c911 Michael Hanselmann
      self.lu.LogInfo("Renaming the new LVs on the target node")
7144 2bb5c911 Michael Hanselmann
      rename_new_to_old = [(new, old.physical_id)
7145 2bb5c911 Michael Hanselmann
                           for old, new in zip(old_lvs, new_lvs)]
7146 4d4a651d Michael Hanselmann
      result = self.rpc.call_blockdev_rename(self.target_node,
7147 4d4a651d Michael Hanselmann
                                             rename_new_to_old)
7148 2bb5c911 Michael Hanselmann
      result.Raise("Can't rename new LVs on node %s" % self.target_node)
7149 cff90b79 Iustin Pop
7150 cff90b79 Iustin Pop
      for old, new in zip(old_lvs, new_lvs):
7151 cff90b79 Iustin Pop
        new.logical_id = old.logical_id
7152 2bb5c911 Michael Hanselmann
        self.cfg.SetDiskID(new, self.target_node)
7153 a9e0c397 Iustin Pop
7154 cff90b79 Iustin Pop
      for disk in old_lvs:
7155 cff90b79 Iustin Pop
        disk.logical_id = ren_fn(disk, temp_suffix)
7156 2bb5c911 Michael Hanselmann
        self.cfg.SetDiskID(disk, self.target_node)
7157 a9e0c397 Iustin Pop
7158 2bb5c911 Michael Hanselmann
      # Now that the new lvs have the old name, we can add them to the device
7159 2bb5c911 Michael Hanselmann
      self.lu.LogInfo("Adding new mirror component on %s" % self.target_node)
7160 4d4a651d Michael Hanselmann
      result = self.rpc.call_blockdev_addchildren(self.target_node, dev,
7161 4d4a651d Michael Hanselmann
                                                  new_lvs)
7162 4c4e4e1e Iustin Pop
      msg = result.fail_msg
7163 2cc1da8b Iustin Pop
      if msg:
7164 a9e0c397 Iustin Pop
        for new_lv in new_lvs:
7165 4d4a651d Michael Hanselmann
          msg2 = self.rpc.call_blockdev_remove(self.target_node,
7166 4d4a651d Michael Hanselmann
                                               new_lv).fail_msg
7167 4c4e4e1e Iustin Pop
          if msg2:
7168 2bb5c911 Michael Hanselmann
            self.lu.LogWarning("Can't rollback device %s: %s", dev, msg2,
7169 2bb5c911 Michael Hanselmann
                               hint=("cleanup manually the unused logical"
7170 2bb5c911 Michael Hanselmann
                                     "volumes"))
7171 2cc1da8b Iustin Pop
        raise errors.OpExecError("Can't add local storage to drbd: %s" % msg)
7172 a9e0c397 Iustin Pop
7173 a9e0c397 Iustin Pop
      dev.children = new_lvs
7174 a9e0c397 Iustin Pop
7175 a4eae71f Michael Hanselmann
      self.cfg.Update(self.instance, feedback_fn)
7176 a9e0c397 Iustin Pop
7177 7ea7bcf6 Iustin Pop
    cstep = 5
7178 7ea7bcf6 Iustin Pop
    if self.early_release:
7179 7ea7bcf6 Iustin Pop
      self.lu.LogStep(cstep, steps_total, "Removing old storage")
7180 7ea7bcf6 Iustin Pop
      cstep += 1
7181 7ea7bcf6 Iustin Pop
      self._RemoveOldStorage(self.target_node, iv_names)
7182 d5cd389c Iustin Pop
      # WARNING: we release both node locks here, do not do other RPCs
7183 d5cd389c Iustin Pop
      # than WaitForSync to the primary node
7184 d5cd389c Iustin Pop
      self._ReleaseNodeLock([self.target_node, self.other_node])
7185 7ea7bcf6 Iustin Pop
7186 2bb5c911 Michael Hanselmann
    # Wait for sync
7187 2bb5c911 Michael Hanselmann
    # This can fail as the old devices are degraded and _WaitForSync
7188 2bb5c911 Michael Hanselmann
    # does a combined result over all disks, so we don't check its return value
7189 7ea7bcf6 Iustin Pop
    self.lu.LogStep(cstep, steps_total, "Sync devices")
7190 7ea7bcf6 Iustin Pop
    cstep += 1
7191 b6c07b79 Michael Hanselmann
    _WaitForSync(self.lu, self.instance)
7192 a9e0c397 Iustin Pop
7193 2bb5c911 Michael Hanselmann
    # Check all devices manually
7194 2bb5c911 Michael Hanselmann
    self._CheckDevices(self.instance.primary_node, iv_names)
7195 a9e0c397 Iustin Pop
7196 cff90b79 Iustin Pop
    # Step: remove old storage
7197 7ea7bcf6 Iustin Pop
    if not self.early_release:
7198 7ea7bcf6 Iustin Pop
      self.lu.LogStep(cstep, steps_total, "Removing old storage")
7199 7ea7bcf6 Iustin Pop
      cstep += 1
7200 7ea7bcf6 Iustin Pop
      self._RemoveOldStorage(self.target_node, iv_names)
7201 a9e0c397 Iustin Pop
7202 a4eae71f Michael Hanselmann
  def _ExecDrbd8Secondary(self, feedback_fn):
7203 2bb5c911 Michael Hanselmann
    """Replace the secondary node for DRBD 8.
7204 a9e0c397 Iustin Pop

7205 a9e0c397 Iustin Pop
    The algorithm for replace is quite complicated:
7206 a9e0c397 Iustin Pop
      - for all disks of the instance:
7207 a9e0c397 Iustin Pop
        - create new LVs on the new node with same names
7208 a9e0c397 Iustin Pop
        - shutdown the drbd device on the old secondary
7209 a9e0c397 Iustin Pop
        - disconnect the drbd network on the primary
7210 a9e0c397 Iustin Pop
        - create the drbd device on the new secondary
7211 a9e0c397 Iustin Pop
        - network attach the drbd on the primary, using an artifice:
7212 a9e0c397 Iustin Pop
          the drbd code for Attach() will connect to the network if it
7213 a9e0c397 Iustin Pop
          finds a device which is connected to the good local disks but
7214 a9e0c397 Iustin Pop
          not network enabled
7215 a9e0c397 Iustin Pop
      - wait for sync across all devices
7216 a9e0c397 Iustin Pop
      - remove all disks from the old secondary
7217 a9e0c397 Iustin Pop

7218 a9e0c397 Iustin Pop
    Failures are not very well handled.
7219 0834c866 Iustin Pop

7220 a9e0c397 Iustin Pop
    """
7221 0834c866 Iustin Pop
    steps_total = 6
7222 0834c866 Iustin Pop
7223 0834c866 Iustin Pop
    # Step: check device activation
7224 2bb5c911 Michael Hanselmann
    self.lu.LogStep(1, steps_total, "Check device existence")
7225 2bb5c911 Michael Hanselmann
    self._CheckDisksExistence([self.instance.primary_node])
7226 2bb5c911 Michael Hanselmann
    self._CheckVolumeGroup([self.instance.primary_node])
7227 0834c866 Iustin Pop
7228 0834c866 Iustin Pop
    # Step: check other node consistency
7229 2bb5c911 Michael Hanselmann
    self.lu.LogStep(2, steps_total, "Check peer consistency")
7230 2bb5c911 Michael Hanselmann
    self._CheckDisksConsistency(self.instance.primary_node, True, True)
7231 0834c866 Iustin Pop
7232 0834c866 Iustin Pop
    # Step: create new storage
7233 2bb5c911 Michael Hanselmann
    self.lu.LogStep(3, steps_total, "Allocate new storage")
7234 2bb5c911 Michael Hanselmann
    for idx, dev in enumerate(self.instance.disks):
7235 2bb5c911 Michael Hanselmann
      self.lu.LogInfo("Adding new local storage on %s for disk/%d" %
7236 2bb5c911 Michael Hanselmann
                      (self.new_node, idx))
7237 428958aa Iustin Pop
      # we pass force_create=True to force LVM creation
7238 a9e0c397 Iustin Pop
      for new_lv in dev.children:
7239 2bb5c911 Michael Hanselmann
        _CreateBlockDev(self.lu, self.new_node, self.instance, new_lv, True,
7240 2bb5c911 Michael Hanselmann
                        _GetInstanceInfoText(self.instance), False)
7241 a9e0c397 Iustin Pop
7242 468b46f9 Iustin Pop
    # Step 4: dbrd minors and drbd setups changes
7243 a1578d63 Iustin Pop
    # after this, we must manually remove the drbd minors on both the
7244 a1578d63 Iustin Pop
    # error and the success paths
7245 2bb5c911 Michael Hanselmann
    self.lu.LogStep(4, steps_total, "Changing drbd configuration")
7246 4d4a651d Michael Hanselmann
    minors = self.cfg.AllocateDRBDMinor([self.new_node
7247 4d4a651d Michael Hanselmann
                                         for dev in self.instance.disks],
7248 2bb5c911 Michael Hanselmann
                                        self.instance.name)
7249 099c52ad Iustin Pop
    logging.debug("Allocated minors %r", minors)
7250 2bb5c911 Michael Hanselmann
7251 2bb5c911 Michael Hanselmann
    iv_names = {}
7252 2bb5c911 Michael Hanselmann
    for idx, (dev, new_minor) in enumerate(zip(self.instance.disks, minors)):
7253 4d4a651d Michael Hanselmann
      self.lu.LogInfo("activating a new drbd on %s for disk/%d" %
7254 4d4a651d Michael Hanselmann
                      (self.new_node, idx))
7255 a2d59d8b Iustin Pop
      # create new devices on new_node; note that we create two IDs:
7256 a2d59d8b Iustin Pop
      # one without port, so the drbd will be activated without
7257 a2d59d8b Iustin Pop
      # networking information on the new node at this stage, and one
7258 a2d59d8b Iustin Pop
      # with network, for the latter activation in step 4
7259 a2d59d8b Iustin Pop
      (o_node1, o_node2, o_port, o_minor1, o_minor2, o_secret) = dev.logical_id
7260 2bb5c911 Michael Hanselmann
      if self.instance.primary_node == o_node1:
7261 a2d59d8b Iustin Pop
        p_minor = o_minor1
7262 ffa1c0dc Iustin Pop
      else:
7263 1122eb25 Iustin Pop
        assert self.instance.primary_node == o_node2, "Three-node instance?"
7264 a2d59d8b Iustin Pop
        p_minor = o_minor2
7265 a2d59d8b Iustin Pop
7266 4d4a651d Michael Hanselmann
      new_alone_id = (self.instance.primary_node, self.new_node, None,
7267 4d4a651d Michael Hanselmann
                      p_minor, new_minor, o_secret)
7268 4d4a651d Michael Hanselmann
      new_net_id = (self.instance.primary_node, self.new_node, o_port,
7269 4d4a651d Michael Hanselmann
                    p_minor, new_minor, o_secret)
7270 a2d59d8b Iustin Pop
7271 a2d59d8b Iustin Pop
      iv_names[idx] = (dev, dev.children, new_net_id)
7272 a1578d63 Iustin Pop
      logging.debug("Allocated new_minor: %s, new_logical_id: %s", new_minor,
7273 a2d59d8b Iustin Pop
                    new_net_id)
7274 a9e0c397 Iustin Pop
      new_drbd = objects.Disk(dev_type=constants.LD_DRBD8,
7275 a2d59d8b Iustin Pop
                              logical_id=new_alone_id,
7276 8a6c7011 Iustin Pop
                              children=dev.children,
7277 8a6c7011 Iustin Pop
                              size=dev.size)
7278 796cab27 Iustin Pop
      try:
7279 2bb5c911 Michael Hanselmann
        _CreateSingleBlockDev(self.lu, self.new_node, self.instance, new_drbd,
7280 2bb5c911 Michael Hanselmann
                              _GetInstanceInfoText(self.instance), False)
7281 82759cb1 Iustin Pop
      except errors.GenericError:
7282 2bb5c911 Michael Hanselmann
        self.cfg.ReleaseDRBDMinors(self.instance.name)
7283 796cab27 Iustin Pop
        raise
7284 a9e0c397 Iustin Pop
7285 2bb5c911 Michael Hanselmann
    # We have new devices, shutdown the drbd on the old secondary
7286 2bb5c911 Michael Hanselmann
    for idx, dev in enumerate(self.instance.disks):
7287 2bb5c911 Michael Hanselmann
      self.lu.LogInfo("Shutting down drbd for disk/%d on old node" % idx)
7288 2bb5c911 Michael Hanselmann
      self.cfg.SetDiskID(dev, self.target_node)
7289 2bb5c911 Michael Hanselmann
      msg = self.rpc.call_blockdev_shutdown(self.target_node, dev).fail_msg
7290 cacfd1fd Iustin Pop
      if msg:
7291 2bb5c911 Michael Hanselmann
        self.lu.LogWarning("Failed to shutdown drbd for disk/%d on old"
7292 2bb5c911 Michael Hanselmann
                           "node: %s" % (idx, msg),
7293 2bb5c911 Michael Hanselmann
                           hint=("Please cleanup this device manually as"
7294 2bb5c911 Michael Hanselmann
                                 " soon as possible"))
7295 a9e0c397 Iustin Pop
7296 2bb5c911 Michael Hanselmann
    self.lu.LogInfo("Detaching primary drbds from the network (=> standalone)")
7297 4d4a651d Michael Hanselmann
    result = self.rpc.call_drbd_disconnect_net([self.instance.primary_node],
7298 4d4a651d Michael Hanselmann
                                               self.node_secondary_ip,
7299 4d4a651d Michael Hanselmann
                                               self.instance.disks)\
7300 4d4a651d Michael Hanselmann
                                              [self.instance.primary_node]
7301 642445d9 Iustin Pop
7302 4c4e4e1e Iustin Pop
    msg = result.fail_msg
7303 a2d59d8b Iustin Pop
    if msg:
7304 a2d59d8b Iustin Pop
      # detaches didn't succeed (unlikely)
7305 2bb5c911 Michael Hanselmann
      self.cfg.ReleaseDRBDMinors(self.instance.name)
7306 a2d59d8b Iustin Pop
      raise errors.OpExecError("Can't detach the disks from the network on"
7307 a2d59d8b Iustin Pop
                               " old node: %s" % (msg,))
7308 642445d9 Iustin Pop
7309 642445d9 Iustin Pop
    # if we managed to detach at least one, we update all the disks of
7310 642445d9 Iustin Pop
    # the instance to point to the new secondary
7311 2bb5c911 Michael Hanselmann
    self.lu.LogInfo("Updating instance configuration")
7312 468b46f9 Iustin Pop
    for dev, _, new_logical_id in iv_names.itervalues():
7313 468b46f9 Iustin Pop
      dev.logical_id = new_logical_id
7314 2bb5c911 Michael Hanselmann
      self.cfg.SetDiskID(dev, self.instance.primary_node)
7315 2bb5c911 Michael Hanselmann
7316 a4eae71f Michael Hanselmann
    self.cfg.Update(self.instance, feedback_fn)
7317 a9e0c397 Iustin Pop
7318 642445d9 Iustin Pop
    # and now perform the drbd attach
7319 2bb5c911 Michael Hanselmann
    self.lu.LogInfo("Attaching primary drbds to new secondary"
7320 2bb5c911 Michael Hanselmann
                    " (standalone => connected)")
7321 4d4a651d Michael Hanselmann
    result = self.rpc.call_drbd_attach_net([self.instance.primary_node,
7322 4d4a651d Michael Hanselmann
                                            self.new_node],
7323 4d4a651d Michael Hanselmann
                                           self.node_secondary_ip,
7324 4d4a651d Michael Hanselmann
                                           self.instance.disks,
7325 4d4a651d Michael Hanselmann
                                           self.instance.name,
7326 a2d59d8b Iustin Pop
                                           False)
7327 a2d59d8b Iustin Pop
    for to_node, to_result in result.items():
7328 4c4e4e1e Iustin Pop
      msg = to_result.fail_msg
7329 a2d59d8b Iustin Pop
      if msg:
7330 4d4a651d Michael Hanselmann
        self.lu.LogWarning("Can't attach drbd disks on node %s: %s",
7331 4d4a651d Michael Hanselmann
                           to_node, msg,
7332 2bb5c911 Michael Hanselmann
                           hint=("please do a gnt-instance info to see the"
7333 2bb5c911 Michael Hanselmann
                                 " status of disks"))
7334 7ea7bcf6 Iustin Pop
    cstep = 5
7335 7ea7bcf6 Iustin Pop
    if self.early_release:
7336 7ea7bcf6 Iustin Pop
      self.lu.LogStep(cstep, steps_total, "Removing old storage")
7337 7ea7bcf6 Iustin Pop
      cstep += 1
7338 7ea7bcf6 Iustin Pop
      self._RemoveOldStorage(self.target_node, iv_names)
7339 d5cd389c Iustin Pop
      # WARNING: we release all node locks here, do not do other RPCs
7340 d5cd389c Iustin Pop
      # than WaitForSync to the primary node
7341 d5cd389c Iustin Pop
      self._ReleaseNodeLock([self.instance.primary_node,
7342 d5cd389c Iustin Pop
                             self.target_node,
7343 d5cd389c Iustin Pop
                             self.new_node])
7344 a9e0c397 Iustin Pop
7345 2bb5c911 Michael Hanselmann
    # Wait for sync
7346 2bb5c911 Michael Hanselmann
    # This can fail as the old devices are degraded and _WaitForSync
7347 2bb5c911 Michael Hanselmann
    # does a combined result over all disks, so we don't check its return value
7348 7ea7bcf6 Iustin Pop
    self.lu.LogStep(cstep, steps_total, "Sync devices")
7349 7ea7bcf6 Iustin Pop
    cstep += 1
7350 b6c07b79 Michael Hanselmann
    _WaitForSync(self.lu, self.instance)
7351 a9e0c397 Iustin Pop
7352 2bb5c911 Michael Hanselmann
    # Check all devices manually
7353 2bb5c911 Michael Hanselmann
    self._CheckDevices(self.instance.primary_node, iv_names)
7354 22985314 Guido Trotter
7355 2bb5c911 Michael Hanselmann
    # Step: remove old storage
7356 7ea7bcf6 Iustin Pop
    if not self.early_release:
7357 7ea7bcf6 Iustin Pop
      self.lu.LogStep(cstep, steps_total, "Removing old storage")
7358 7ea7bcf6 Iustin Pop
      self._RemoveOldStorage(self.target_node, iv_names)
7359 a9e0c397 Iustin Pop
7360 a8083063 Iustin Pop
7361 76aef8fc Michael Hanselmann
class LURepairNodeStorage(NoHooksLU):
7362 76aef8fc Michael Hanselmann
  """Repairs the volume group on a node.
7363 76aef8fc Michael Hanselmann

7364 76aef8fc Michael Hanselmann
  """
7365 76aef8fc Michael Hanselmann
  _OP_REQP = ["node_name"]
7366 76aef8fc Michael Hanselmann
  REQ_BGL = False
7367 76aef8fc Michael Hanselmann
7368 76aef8fc Michael Hanselmann
  def CheckArguments(self):
7369 cf26a87a Iustin Pop
    self.op.node_name = _ExpandNodeName(self.cfg, self.op.node_name)
7370 76aef8fc Michael Hanselmann
7371 76aef8fc Michael Hanselmann
  def ExpandNames(self):
7372 76aef8fc Michael Hanselmann
    self.needed_locks = {
7373 76aef8fc Michael Hanselmann
      locking.LEVEL_NODE: [self.op.node_name],
7374 76aef8fc Michael Hanselmann
      }
7375 76aef8fc Michael Hanselmann
7376 76aef8fc Michael Hanselmann
  def _CheckFaultyDisks(self, instance, node_name):
7377 7e9c6a78 Iustin Pop
    """Ensure faulty disks abort the opcode or at least warn."""
7378 7e9c6a78 Iustin Pop
    try:
7379 7e9c6a78 Iustin Pop
      if _FindFaultyInstanceDisks(self.cfg, self.rpc, instance,
7380 7e9c6a78 Iustin Pop
                                  node_name, True):
7381 7e9c6a78 Iustin Pop
        raise errors.OpPrereqError("Instance '%s' has faulty disks on"
7382 7e9c6a78 Iustin Pop
                                   " node '%s'" % (instance.name, node_name),
7383 7e9c6a78 Iustin Pop
                                   errors.ECODE_STATE)
7384 7e9c6a78 Iustin Pop
    except errors.OpPrereqError, err:
7385 7e9c6a78 Iustin Pop
      if self.op.ignore_consistency:
7386 7e9c6a78 Iustin Pop
        self.proc.LogWarning(str(err.args[0]))
7387 7e9c6a78 Iustin Pop
      else:
7388 7e9c6a78 Iustin Pop
        raise
7389 76aef8fc Michael Hanselmann
7390 76aef8fc Michael Hanselmann
  def CheckPrereq(self):
7391 76aef8fc Michael Hanselmann
    """Check prerequisites.
7392 76aef8fc Michael Hanselmann

7393 76aef8fc Michael Hanselmann
    """
7394 76aef8fc Michael Hanselmann
    storage_type = self.op.storage_type
7395 76aef8fc Michael Hanselmann
7396 76aef8fc Michael Hanselmann
    if (constants.SO_FIX_CONSISTENCY not in
7397 76aef8fc Michael Hanselmann
        constants.VALID_STORAGE_OPERATIONS.get(storage_type, [])):
7398 76aef8fc Michael Hanselmann
      raise errors.OpPrereqError("Storage units of type '%s' can not be"
7399 5c983ee5 Iustin Pop
                                 " repaired" % storage_type,
7400 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
7401 76aef8fc Michael Hanselmann
7402 76aef8fc Michael Hanselmann
    # Check whether any instance on this node has faulty disks
7403 76aef8fc Michael Hanselmann
    for inst in _GetNodeInstances(self.cfg, self.op.node_name):
7404 7e9c6a78 Iustin Pop
      if not inst.admin_up:
7405 7e9c6a78 Iustin Pop
        continue
7406 76aef8fc Michael Hanselmann
      check_nodes = set(inst.all_nodes)
7407 76aef8fc Michael Hanselmann
      check_nodes.discard(self.op.node_name)
7408 76aef8fc Michael Hanselmann
      for inst_node_name in check_nodes:
7409 76aef8fc Michael Hanselmann
        self._CheckFaultyDisks(inst, inst_node_name)
7410 76aef8fc Michael Hanselmann
7411 76aef8fc Michael Hanselmann
  def Exec(self, feedback_fn):
7412 76aef8fc Michael Hanselmann
    feedback_fn("Repairing storage unit '%s' on %s ..." %
7413 76aef8fc Michael Hanselmann
                (self.op.name, self.op.node_name))
7414 76aef8fc Michael Hanselmann
7415 76aef8fc Michael Hanselmann
    st_args = _GetStorageTypeArgs(self.cfg, self.op.storage_type)
7416 76aef8fc Michael Hanselmann
    result = self.rpc.call_storage_execute(self.op.node_name,
7417 76aef8fc Michael Hanselmann
                                           self.op.storage_type, st_args,
7418 76aef8fc Michael Hanselmann
                                           self.op.name,
7419 76aef8fc Michael Hanselmann
                                           constants.SO_FIX_CONSISTENCY)
7420 76aef8fc Michael Hanselmann
    result.Raise("Failed to repair storage unit '%s' on %s" %
7421 76aef8fc Michael Hanselmann
                 (self.op.name, self.op.node_name))
7422 76aef8fc Michael Hanselmann
7423 76aef8fc Michael Hanselmann
7424 f7e7689f Iustin Pop
class LUNodeEvacuationStrategy(NoHooksLU):
7425 f7e7689f Iustin Pop
  """Computes the node evacuation strategy.
7426 f7e7689f Iustin Pop

7427 f7e7689f Iustin Pop
  """
7428 f7e7689f Iustin Pop
  _OP_REQP = ["nodes"]
7429 f7e7689f Iustin Pop
  REQ_BGL = False
7430 f7e7689f Iustin Pop
7431 f7e7689f Iustin Pop
  def CheckArguments(self):
7432 f7e7689f Iustin Pop
    if not hasattr(self.op, "remote_node"):
7433 f7e7689f Iustin Pop
      self.op.remote_node = None
7434 f7e7689f Iustin Pop
    if not hasattr(self.op, "iallocator"):
7435 f7e7689f Iustin Pop
      self.op.iallocator = None
7436 f7e7689f Iustin Pop
    if self.op.remote_node is not None and self.op.iallocator is not None:
7437 f7e7689f Iustin Pop
      raise errors.OpPrereqError("Give either the iallocator or the new"
7438 f7e7689f Iustin Pop
                                 " secondary, not both", errors.ECODE_INVAL)
7439 f7e7689f Iustin Pop
7440 f7e7689f Iustin Pop
  def ExpandNames(self):
7441 f7e7689f Iustin Pop
    self.op.nodes = _GetWantedNodes(self, self.op.nodes)
7442 f7e7689f Iustin Pop
    self.needed_locks = locks = {}
7443 f7e7689f Iustin Pop
    if self.op.remote_node is None:
7444 f7e7689f Iustin Pop
      locks[locking.LEVEL_NODE] = locking.ALL_SET
7445 f7e7689f Iustin Pop
    else:
7446 f7e7689f Iustin Pop
      self.op.remote_node = _ExpandNodeName(self.cfg, self.op.remote_node)
7447 f7e7689f Iustin Pop
      locks[locking.LEVEL_NODE] = self.op.nodes + [self.op.remote_node]
7448 f7e7689f Iustin Pop
7449 f7e7689f Iustin Pop
  def CheckPrereq(self):
7450 f7e7689f Iustin Pop
    pass
7451 f7e7689f Iustin Pop
7452 f7e7689f Iustin Pop
  def Exec(self, feedback_fn):
7453 f7e7689f Iustin Pop
    if self.op.remote_node is not None:
7454 f7e7689f Iustin Pop
      instances = []
7455 f7e7689f Iustin Pop
      for node in self.op.nodes:
7456 f7e7689f Iustin Pop
        instances.extend(_GetNodeSecondaryInstances(self.cfg, node))
7457 f7e7689f Iustin Pop
      result = []
7458 f7e7689f Iustin Pop
      for i in instances:
7459 f7e7689f Iustin Pop
        if i.primary_node == self.op.remote_node:
7460 f7e7689f Iustin Pop
          raise errors.OpPrereqError("Node %s is the primary node of"
7461 f7e7689f Iustin Pop
                                     " instance %s, cannot use it as"
7462 f7e7689f Iustin Pop
                                     " secondary" %
7463 f7e7689f Iustin Pop
                                     (self.op.remote_node, i.name),
7464 f7e7689f Iustin Pop
                                     errors.ECODE_INVAL)
7465 f7e7689f Iustin Pop
        result.append([i.name, self.op.remote_node])
7466 f7e7689f Iustin Pop
    else:
7467 f7e7689f Iustin Pop
      ial = IAllocator(self.cfg, self.rpc,
7468 f7e7689f Iustin Pop
                       mode=constants.IALLOCATOR_MODE_MEVAC,
7469 f7e7689f Iustin Pop
                       evac_nodes=self.op.nodes)
7470 f7e7689f Iustin Pop
      ial.Run(self.op.iallocator, validate=True)
7471 f7e7689f Iustin Pop
      if not ial.success:
7472 f7e7689f Iustin Pop
        raise errors.OpExecError("No valid evacuation solution: %s" % ial.info,
7473 f7e7689f Iustin Pop
                                 errors.ECODE_NORES)
7474 f7e7689f Iustin Pop
      result = ial.result
7475 f7e7689f Iustin Pop
    return result
7476 f7e7689f Iustin Pop
7477 f7e7689f Iustin Pop
7478 8729e0d7 Iustin Pop
class LUGrowDisk(LogicalUnit):
7479 8729e0d7 Iustin Pop
  """Grow a disk of an instance.
7480 8729e0d7 Iustin Pop

7481 8729e0d7 Iustin Pop
  """
7482 8729e0d7 Iustin Pop
  HPATH = "disk-grow"
7483 8729e0d7 Iustin Pop
  HTYPE = constants.HTYPE_INSTANCE
7484 6605411d Iustin Pop
  _OP_REQP = ["instance_name", "disk", "amount", "wait_for_sync"]
7485 31e63dbf Guido Trotter
  REQ_BGL = False
7486 31e63dbf Guido Trotter
7487 31e63dbf Guido Trotter
  def ExpandNames(self):
7488 31e63dbf Guido Trotter
    self._ExpandAndLockInstance()
7489 31e63dbf Guido Trotter
    self.needed_locks[locking.LEVEL_NODE] = []
7490 f6d9a522 Guido Trotter
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
7491 31e63dbf Guido Trotter
7492 31e63dbf Guido Trotter
  def DeclareLocks(self, level):
7493 31e63dbf Guido Trotter
    if level == locking.LEVEL_NODE:
7494 31e63dbf Guido Trotter
      self._LockInstancesNodes()
7495 8729e0d7 Iustin Pop
7496 8729e0d7 Iustin Pop
  def BuildHooksEnv(self):
7497 8729e0d7 Iustin Pop
    """Build hooks env.
7498 8729e0d7 Iustin Pop

7499 8729e0d7 Iustin Pop
    This runs on the master, the primary and all the secondaries.
7500 8729e0d7 Iustin Pop

7501 8729e0d7 Iustin Pop
    """
7502 8729e0d7 Iustin Pop
    env = {
7503 8729e0d7 Iustin Pop
      "DISK": self.op.disk,
7504 8729e0d7 Iustin Pop
      "AMOUNT": self.op.amount,
7505 8729e0d7 Iustin Pop
      }
7506 338e51e8 Iustin Pop
    env.update(_BuildInstanceHookEnvByObject(self, self.instance))
7507 abd8e836 Iustin Pop
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
7508 8729e0d7 Iustin Pop
    return env, nl, nl
7509 8729e0d7 Iustin Pop
7510 8729e0d7 Iustin Pop
  def CheckPrereq(self):
7511 8729e0d7 Iustin Pop
    """Check prerequisites.
7512 8729e0d7 Iustin Pop

7513 8729e0d7 Iustin Pop
    This checks that the instance is in the cluster.
7514 8729e0d7 Iustin Pop

7515 8729e0d7 Iustin Pop
    """
7516 31e63dbf Guido Trotter
    instance = self.cfg.GetInstanceInfo(self.op.instance_name)
7517 31e63dbf Guido Trotter
    assert instance is not None, \
7518 31e63dbf Guido Trotter
      "Cannot retrieve locked instance %s" % self.op.instance_name
7519 6b12959c Iustin Pop
    nodenames = list(instance.all_nodes)
7520 6b12959c Iustin Pop
    for node in nodenames:
7521 7527a8a4 Iustin Pop
      _CheckNodeOnline(self, node)
7522 7527a8a4 Iustin Pop
7523 31e63dbf Guido Trotter
7524 8729e0d7 Iustin Pop
    self.instance = instance
7525 8729e0d7 Iustin Pop
7526 8729e0d7 Iustin Pop
    if instance.disk_template not in (constants.DT_PLAIN, constants.DT_DRBD8):
7527 8729e0d7 Iustin Pop
      raise errors.OpPrereqError("Instance's disk layout does not support"
7528 5c983ee5 Iustin Pop
                                 " growing.", errors.ECODE_INVAL)
7529 8729e0d7 Iustin Pop
7530 ad24e046 Iustin Pop
    self.disk = instance.FindDisk(self.op.disk)
7531 8729e0d7 Iustin Pop
7532 701384a9 Iustin Pop
    _CheckNodesFreeDisk(self, nodenames, self.op.amount)
7533 8729e0d7 Iustin Pop
7534 8729e0d7 Iustin Pop
  def Exec(self, feedback_fn):
7535 8729e0d7 Iustin Pop
    """Execute disk grow.
7536 8729e0d7 Iustin Pop

7537 8729e0d7 Iustin Pop
    """
7538 8729e0d7 Iustin Pop
    instance = self.instance
7539 ad24e046 Iustin Pop
    disk = self.disk
7540 6b12959c Iustin Pop
    for node in instance.all_nodes:
7541 8729e0d7 Iustin Pop
      self.cfg.SetDiskID(disk, node)
7542 72737a7f Iustin Pop
      result = self.rpc.call_blockdev_grow(node, disk, self.op.amount)
7543 4c4e4e1e Iustin Pop
      result.Raise("Grow request failed to node %s" % node)
7544 5bc556dd Michael Hanselmann
7545 5bc556dd Michael Hanselmann
      # TODO: Rewrite code to work properly
7546 5bc556dd Michael Hanselmann
      # DRBD goes into sync mode for a short amount of time after executing the
7547 5bc556dd Michael Hanselmann
      # "resize" command. DRBD 8.x below version 8.0.13 contains a bug whereby
7548 5bc556dd Michael Hanselmann
      # calling "resize" in sync mode fails. Sleeping for a short amount of
7549 5bc556dd Michael Hanselmann
      # time is a work-around.
7550 5bc556dd Michael Hanselmann
      time.sleep(5)
7551 5bc556dd Michael Hanselmann
7552 8729e0d7 Iustin Pop
    disk.RecordGrow(self.op.amount)
7553 a4eae71f Michael Hanselmann
    self.cfg.Update(instance, feedback_fn)
7554 6605411d Iustin Pop
    if self.op.wait_for_sync:
7555 cd4d138f Guido Trotter
      disk_abort = not _WaitForSync(self, instance)
7556 6605411d Iustin Pop
      if disk_abort:
7557 86d9d3bb Iustin Pop
        self.proc.LogWarning("Warning: disk sync-ing has not returned a good"
7558 86d9d3bb Iustin Pop
                             " status.\nPlease check the instance.")
7559 8729e0d7 Iustin Pop
7560 8729e0d7 Iustin Pop
7561 a8083063 Iustin Pop
class LUQueryInstanceData(NoHooksLU):
7562 a8083063 Iustin Pop
  """Query runtime instance data.
7563 a8083063 Iustin Pop

7564 a8083063 Iustin Pop
  """
7565 57821cac Iustin Pop
  _OP_REQP = ["instances", "static"]
7566 a987fa48 Guido Trotter
  REQ_BGL = False
7567 ae5849b5 Michael Hanselmann
7568 a987fa48 Guido Trotter
  def ExpandNames(self):
7569 a987fa48 Guido Trotter
    self.needed_locks = {}
7570 c772d142 Michael Hanselmann
    self.share_locks = dict.fromkeys(locking.LEVELS, 1)
7571 a987fa48 Guido Trotter
7572 a987fa48 Guido Trotter
    if not isinstance(self.op.instances, list):
7573 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Invalid argument type 'instances'",
7574 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
7575 a987fa48 Guido Trotter
7576 a987fa48 Guido Trotter
    if self.op.instances:
7577 a987fa48 Guido Trotter
      self.wanted_names = []
7578 a987fa48 Guido Trotter
      for name in self.op.instances:
7579 cf26a87a Iustin Pop
        full_name = _ExpandInstanceName(self.cfg, name)
7580 a987fa48 Guido Trotter
        self.wanted_names.append(full_name)
7581 a987fa48 Guido Trotter
      self.needed_locks[locking.LEVEL_INSTANCE] = self.wanted_names
7582 a987fa48 Guido Trotter
    else:
7583 a987fa48 Guido Trotter
      self.wanted_names = None
7584 a987fa48 Guido Trotter
      self.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET
7585 a987fa48 Guido Trotter
7586 a987fa48 Guido Trotter
    self.needed_locks[locking.LEVEL_NODE] = []
7587 a987fa48 Guido Trotter
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
7588 a987fa48 Guido Trotter
7589 a987fa48 Guido Trotter
  def DeclareLocks(self, level):
7590 a987fa48 Guido Trotter
    if level == locking.LEVEL_NODE:
7591 a987fa48 Guido Trotter
      self._LockInstancesNodes()
7592 a8083063 Iustin Pop
7593 a8083063 Iustin Pop
  def CheckPrereq(self):
7594 a8083063 Iustin Pop
    """Check prerequisites.
7595 a8083063 Iustin Pop

7596 a8083063 Iustin Pop
    This only checks the optional instance list against the existing names.
7597 a8083063 Iustin Pop

7598 a8083063 Iustin Pop
    """
7599 a987fa48 Guido Trotter
    if self.wanted_names is None:
7600 a987fa48 Guido Trotter
      self.wanted_names = self.acquired_locks[locking.LEVEL_INSTANCE]
7601 a8083063 Iustin Pop
7602 a987fa48 Guido Trotter
    self.wanted_instances = [self.cfg.GetInstanceInfo(name) for name
7603 a987fa48 Guido Trotter
                             in self.wanted_names]
7604 a987fa48 Guido Trotter
    return
7605 a8083063 Iustin Pop
7606 98825740 Michael Hanselmann
  def _ComputeBlockdevStatus(self, node, instance_name, dev):
7607 98825740 Michael Hanselmann
    """Returns the status of a block device
7608 98825740 Michael Hanselmann

7609 98825740 Michael Hanselmann
    """
7610 4dce1a83 Michael Hanselmann
    if self.op.static or not node:
7611 98825740 Michael Hanselmann
      return None
7612 98825740 Michael Hanselmann
7613 98825740 Michael Hanselmann
    self.cfg.SetDiskID(dev, node)
7614 98825740 Michael Hanselmann
7615 98825740 Michael Hanselmann
    result = self.rpc.call_blockdev_find(node, dev)
7616 98825740 Michael Hanselmann
    if result.offline:
7617 98825740 Michael Hanselmann
      return None
7618 98825740 Michael Hanselmann
7619 98825740 Michael Hanselmann
    result.Raise("Can't compute disk status for %s" % instance_name)
7620 98825740 Michael Hanselmann
7621 98825740 Michael Hanselmann
    status = result.payload
7622 ddfe2228 Michael Hanselmann
    if status is None:
7623 ddfe2228 Michael Hanselmann
      return None
7624 98825740 Michael Hanselmann
7625 98825740 Michael Hanselmann
    return (status.dev_path, status.major, status.minor,
7626 98825740 Michael Hanselmann
            status.sync_percent, status.estimated_time,
7627 f208978a Michael Hanselmann
            status.is_degraded, status.ldisk_status)
7628 98825740 Michael Hanselmann
7629 a8083063 Iustin Pop
  def _ComputeDiskStatus(self, instance, snode, dev):
7630 a8083063 Iustin Pop
    """Compute block device status.
7631 a8083063 Iustin Pop

7632 a8083063 Iustin Pop
    """
7633 a1f445d3 Iustin Pop
    if dev.dev_type in constants.LDS_DRBD:
7634 a8083063 Iustin Pop
      # we change the snode then (otherwise we use the one passed in)
7635 a8083063 Iustin Pop
      if dev.logical_id[0] == instance.primary_node:
7636 a8083063 Iustin Pop
        snode = dev.logical_id[1]
7637 a8083063 Iustin Pop
      else:
7638 a8083063 Iustin Pop
        snode = dev.logical_id[0]
7639 a8083063 Iustin Pop
7640 98825740 Michael Hanselmann
    dev_pstatus = self._ComputeBlockdevStatus(instance.primary_node,
7641 98825740 Michael Hanselmann
                                              instance.name, dev)
7642 98825740 Michael Hanselmann
    dev_sstatus = self._ComputeBlockdevStatus(snode, instance.name, dev)
7643 a8083063 Iustin Pop
7644 a8083063 Iustin Pop
    if dev.children:
7645 a8083063 Iustin Pop
      dev_children = [self._ComputeDiskStatus(instance, snode, child)
7646 a8083063 Iustin Pop
                      for child in dev.children]
7647 a8083063 Iustin Pop
    else:
7648 a8083063 Iustin Pop
      dev_children = []
7649 a8083063 Iustin Pop
7650 a8083063 Iustin Pop
    data = {
7651 a8083063 Iustin Pop
      "iv_name": dev.iv_name,
7652 a8083063 Iustin Pop
      "dev_type": dev.dev_type,
7653 a8083063 Iustin Pop
      "logical_id": dev.logical_id,
7654 a8083063 Iustin Pop
      "physical_id": dev.physical_id,
7655 a8083063 Iustin Pop
      "pstatus": dev_pstatus,
7656 a8083063 Iustin Pop
      "sstatus": dev_sstatus,
7657 a8083063 Iustin Pop
      "children": dev_children,
7658 b6fdf8b8 Iustin Pop
      "mode": dev.mode,
7659 c98162a7 Iustin Pop
      "size": dev.size,
7660 a8083063 Iustin Pop
      }
7661 a8083063 Iustin Pop
7662 a8083063 Iustin Pop
    return data
7663 a8083063 Iustin Pop
7664 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
7665 a8083063 Iustin Pop
    """Gather and return data"""
7666 a8083063 Iustin Pop
    result = {}
7667 338e51e8 Iustin Pop
7668 338e51e8 Iustin Pop
    cluster = self.cfg.GetClusterInfo()
7669 338e51e8 Iustin Pop
7670 a8083063 Iustin Pop
    for instance in self.wanted_instances:
7671 57821cac Iustin Pop
      if not self.op.static:
7672 57821cac Iustin Pop
        remote_info = self.rpc.call_instance_info(instance.primary_node,
7673 57821cac Iustin Pop
                                                  instance.name,
7674 57821cac Iustin Pop
                                                  instance.hypervisor)
7675 4c4e4e1e Iustin Pop
        remote_info.Raise("Error checking node %s" % instance.primary_node)
7676 7ad1af4a Iustin Pop
        remote_info = remote_info.payload
7677 57821cac Iustin Pop
        if remote_info and "state" in remote_info:
7678 57821cac Iustin Pop
          remote_state = "up"
7679 57821cac Iustin Pop
        else:
7680 57821cac Iustin Pop
          remote_state = "down"
7681 a8083063 Iustin Pop
      else:
7682 57821cac Iustin Pop
        remote_state = None
7683 0d68c45d Iustin Pop
      if instance.admin_up:
7684 a8083063 Iustin Pop
        config_state = "up"
7685 0d68c45d Iustin Pop
      else:
7686 0d68c45d Iustin Pop
        config_state = "down"
7687 a8083063 Iustin Pop
7688 a8083063 Iustin Pop
      disks = [self._ComputeDiskStatus(instance, None, device)
7689 a8083063 Iustin Pop
               for device in instance.disks]
7690 a8083063 Iustin Pop
7691 a8083063 Iustin Pop
      idict = {
7692 a8083063 Iustin Pop
        "name": instance.name,
7693 a8083063 Iustin Pop
        "config_state": config_state,
7694 a8083063 Iustin Pop
        "run_state": remote_state,
7695 a8083063 Iustin Pop
        "pnode": instance.primary_node,
7696 a8083063 Iustin Pop
        "snodes": instance.secondary_nodes,
7697 a8083063 Iustin Pop
        "os": instance.os,
7698 0b13832c Guido Trotter
        # this happens to be the same format used for hooks
7699 0b13832c Guido Trotter
        "nics": _NICListToTuple(self, instance.nics),
7700 a8083063 Iustin Pop
        "disks": disks,
7701 e69d05fd Iustin Pop
        "hypervisor": instance.hypervisor,
7702 24838135 Iustin Pop
        "network_port": instance.network_port,
7703 24838135 Iustin Pop
        "hv_instance": instance.hvparams,
7704 7736a5f2 Iustin Pop
        "hv_actual": cluster.FillHV(instance, skip_globals=True),
7705 338e51e8 Iustin Pop
        "be_instance": instance.beparams,
7706 338e51e8 Iustin Pop
        "be_actual": cluster.FillBE(instance),
7707 90f72445 Iustin Pop
        "serial_no": instance.serial_no,
7708 90f72445 Iustin Pop
        "mtime": instance.mtime,
7709 90f72445 Iustin Pop
        "ctime": instance.ctime,
7710 033d58b0 Iustin Pop
        "uuid": instance.uuid,
7711 a8083063 Iustin Pop
        }
7712 a8083063 Iustin Pop
7713 a8083063 Iustin Pop
      result[instance.name] = idict
7714 a8083063 Iustin Pop
7715 a8083063 Iustin Pop
    return result
7716 a8083063 Iustin Pop
7717 a8083063 Iustin Pop
7718 7767bbf5 Manuel Franceschini
class LUSetInstanceParams(LogicalUnit):
7719 a8083063 Iustin Pop
  """Modifies an instances's parameters.
7720 a8083063 Iustin Pop

7721 a8083063 Iustin Pop
  """
7722 a8083063 Iustin Pop
  HPATH = "instance-modify"
7723 a8083063 Iustin Pop
  HTYPE = constants.HTYPE_INSTANCE
7724 24991749 Iustin Pop
  _OP_REQP = ["instance_name"]
7725 1a5c7281 Guido Trotter
  REQ_BGL = False
7726 1a5c7281 Guido Trotter
7727 24991749 Iustin Pop
  def CheckArguments(self):
7728 24991749 Iustin Pop
    if not hasattr(self.op, 'nics'):
7729 24991749 Iustin Pop
      self.op.nics = []
7730 24991749 Iustin Pop
    if not hasattr(self.op, 'disks'):
7731 24991749 Iustin Pop
      self.op.disks = []
7732 24991749 Iustin Pop
    if not hasattr(self.op, 'beparams'):
7733 24991749 Iustin Pop
      self.op.beparams = {}
7734 24991749 Iustin Pop
    if not hasattr(self.op, 'hvparams'):
7735 24991749 Iustin Pop
      self.op.hvparams = {}
7736 e29e9550 Iustin Pop
    if not hasattr(self.op, "disk_template"):
7737 e29e9550 Iustin Pop
      self.op.disk_template = None
7738 e29e9550 Iustin Pop
    if not hasattr(self.op, "remote_node"):
7739 e29e9550 Iustin Pop
      self.op.remote_node = None
7740 24991749 Iustin Pop
    self.op.force = getattr(self.op, "force", False)
7741 e29e9550 Iustin Pop
    if not (self.op.nics or self.op.disks or self.op.disk_template or
7742 24991749 Iustin Pop
            self.op.hvparams or self.op.beparams):
7743 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("No changes submitted", errors.ECODE_INVAL)
7744 24991749 Iustin Pop
7745 7736a5f2 Iustin Pop
    if self.op.hvparams:
7746 7736a5f2 Iustin Pop
      _CheckGlobalHvParams(self.op.hvparams)
7747 7736a5f2 Iustin Pop
7748 24991749 Iustin Pop
    # Disk validation
7749 24991749 Iustin Pop
    disk_addremove = 0
7750 24991749 Iustin Pop
    for disk_op, disk_dict in self.op.disks:
7751 24991749 Iustin Pop
      if disk_op == constants.DDM_REMOVE:
7752 24991749 Iustin Pop
        disk_addremove += 1
7753 24991749 Iustin Pop
        continue
7754 24991749 Iustin Pop
      elif disk_op == constants.DDM_ADD:
7755 24991749 Iustin Pop
        disk_addremove += 1
7756 24991749 Iustin Pop
      else:
7757 24991749 Iustin Pop
        if not isinstance(disk_op, int):
7758 5c983ee5 Iustin Pop
          raise errors.OpPrereqError("Invalid disk index", errors.ECODE_INVAL)
7759 8b46606c Guido Trotter
        if not isinstance(disk_dict, dict):
7760 8b46606c Guido Trotter
          msg = "Invalid disk value: expected dict, got '%s'" % disk_dict
7761 5c983ee5 Iustin Pop
          raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
7762 8b46606c Guido Trotter
7763 24991749 Iustin Pop
      if disk_op == constants.DDM_ADD:
7764 24991749 Iustin Pop
        mode = disk_dict.setdefault('mode', constants.DISK_RDWR)
7765 6ec66eae Iustin Pop
        if mode not in constants.DISK_ACCESS_SET:
7766 5c983ee5 Iustin Pop
          raise errors.OpPrereqError("Invalid disk access mode '%s'" % mode,
7767 5c983ee5 Iustin Pop
                                     errors.ECODE_INVAL)
7768 24991749 Iustin Pop
        size = disk_dict.get('size', None)
7769 24991749 Iustin Pop
        if size is None:
7770 5c983ee5 Iustin Pop
          raise errors.OpPrereqError("Required disk parameter size missing",
7771 5c983ee5 Iustin Pop
                                     errors.ECODE_INVAL)
7772 24991749 Iustin Pop
        try:
7773 24991749 Iustin Pop
          size = int(size)
7774 691744c4 Iustin Pop
        except (TypeError, ValueError), err:
7775 24991749 Iustin Pop
          raise errors.OpPrereqError("Invalid disk size parameter: %s" %
7776 5c983ee5 Iustin Pop
                                     str(err), errors.ECODE_INVAL)
7777 24991749 Iustin Pop
        disk_dict['size'] = size
7778 24991749 Iustin Pop
      else:
7779 24991749 Iustin Pop
        # modification of disk
7780 24991749 Iustin Pop
        if 'size' in disk_dict:
7781 24991749 Iustin Pop
          raise errors.OpPrereqError("Disk size change not possible, use"
7782 5c983ee5 Iustin Pop
                                     " grow-disk", errors.ECODE_INVAL)
7783 24991749 Iustin Pop
7784 24991749 Iustin Pop
    if disk_addremove > 1:
7785 24991749 Iustin Pop
      raise errors.OpPrereqError("Only one disk add or remove operation"
7786 5c983ee5 Iustin Pop
                                 " supported at a time", errors.ECODE_INVAL)
7787 24991749 Iustin Pop
7788 e29e9550 Iustin Pop
    if self.op.disks and self.op.disk_template is not None:
7789 e29e9550 Iustin Pop
      raise errors.OpPrereqError("Disk template conversion and other disk"
7790 e29e9550 Iustin Pop
                                 " changes not supported at the same time",
7791 e29e9550 Iustin Pop
                                 errors.ECODE_INVAL)
7792 e29e9550 Iustin Pop
7793 e29e9550 Iustin Pop
    if self.op.disk_template:
7794 e29e9550 Iustin Pop
      _CheckDiskTemplate(self.op.disk_template)
7795 e29e9550 Iustin Pop
      if (self.op.disk_template in constants.DTS_NET_MIRROR and
7796 e29e9550 Iustin Pop
          self.op.remote_node is None):
7797 e29e9550 Iustin Pop
        raise errors.OpPrereqError("Changing the disk template to a mirrored"
7798 e29e9550 Iustin Pop
                                   " one requires specifying a secondary node",
7799 e29e9550 Iustin Pop
                                   errors.ECODE_INVAL)
7800 e29e9550 Iustin Pop
7801 24991749 Iustin Pop
    # NIC validation
7802 24991749 Iustin Pop
    nic_addremove = 0
7803 24991749 Iustin Pop
    for nic_op, nic_dict in self.op.nics:
7804 24991749 Iustin Pop
      if nic_op == constants.DDM_REMOVE:
7805 24991749 Iustin Pop
        nic_addremove += 1
7806 24991749 Iustin Pop
        continue
7807 24991749 Iustin Pop
      elif nic_op == constants.DDM_ADD:
7808 24991749 Iustin Pop
        nic_addremove += 1
7809 24991749 Iustin Pop
      else:
7810 24991749 Iustin Pop
        if not isinstance(nic_op, int):
7811 5c983ee5 Iustin Pop
          raise errors.OpPrereqError("Invalid nic index", errors.ECODE_INVAL)
7812 8b46606c Guido Trotter
        if not isinstance(nic_dict, dict):
7813 8b46606c Guido Trotter
          msg = "Invalid nic value: expected dict, got '%s'" % nic_dict
7814 5c983ee5 Iustin Pop
          raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
7815 24991749 Iustin Pop
7816 24991749 Iustin Pop
      # nic_dict should be a dict
7817 24991749 Iustin Pop
      nic_ip = nic_dict.get('ip', None)
7818 24991749 Iustin Pop
      if nic_ip is not None:
7819 5c44da6a Guido Trotter
        if nic_ip.lower() == constants.VALUE_NONE:
7820 24991749 Iustin Pop
          nic_dict['ip'] = None
7821 24991749 Iustin Pop
        else:
7822 24991749 Iustin Pop
          if not utils.IsValidIP(nic_ip):
7823 5c983ee5 Iustin Pop
            raise errors.OpPrereqError("Invalid IP address '%s'" % nic_ip,
7824 5c983ee5 Iustin Pop
                                       errors.ECODE_INVAL)
7825 5c44da6a Guido Trotter
7826 cd098c41 Guido Trotter
      nic_bridge = nic_dict.get('bridge', None)
7827 cd098c41 Guido Trotter
      nic_link = nic_dict.get('link', None)
7828 cd098c41 Guido Trotter
      if nic_bridge and nic_link:
7829 29921401 Iustin Pop
        raise errors.OpPrereqError("Cannot pass 'bridge' and 'link'"
7830 5c983ee5 Iustin Pop
                                   " at the same time", errors.ECODE_INVAL)
7831 cd098c41 Guido Trotter
      elif nic_bridge and nic_bridge.lower() == constants.VALUE_NONE:
7832 cd098c41 Guido Trotter
        nic_dict['bridge'] = None
7833 cd098c41 Guido Trotter
      elif nic_link and nic_link.lower() == constants.VALUE_NONE:
7834 cd098c41 Guido Trotter
        nic_dict['link'] = None
7835 cd098c41 Guido Trotter
7836 5c44da6a Guido Trotter
      if nic_op == constants.DDM_ADD:
7837 5c44da6a Guido Trotter
        nic_mac = nic_dict.get('mac', None)
7838 5c44da6a Guido Trotter
        if nic_mac is None:
7839 5c44da6a Guido Trotter
          nic_dict['mac'] = constants.VALUE_AUTO
7840 5c44da6a Guido Trotter
7841 5c44da6a Guido Trotter
      if 'mac' in nic_dict:
7842 5c44da6a Guido Trotter
        nic_mac = nic_dict['mac']
7843 24991749 Iustin Pop
        if nic_mac not in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
7844 82187135 René Nussbaumer
          nic_mac = utils.NormalizeAndValidateMac(nic_mac)
7845 82187135 René Nussbaumer
7846 5c44da6a Guido Trotter
        if nic_op != constants.DDM_ADD and nic_mac == constants.VALUE_AUTO:
7847 5c44da6a Guido Trotter
          raise errors.OpPrereqError("'auto' is not a valid MAC address when"
7848 5c983ee5 Iustin Pop
                                     " modifying an existing nic",
7849 5c983ee5 Iustin Pop
                                     errors.ECODE_INVAL)
7850 5c44da6a Guido Trotter
7851 24991749 Iustin Pop
    if nic_addremove > 1:
7852 24991749 Iustin Pop
      raise errors.OpPrereqError("Only one NIC add or remove operation"
7853 5c983ee5 Iustin Pop
                                 " supported at a time", errors.ECODE_INVAL)
7854 24991749 Iustin Pop
7855 1a5c7281 Guido Trotter
  def ExpandNames(self):
7856 1a5c7281 Guido Trotter
    self._ExpandAndLockInstance()
7857 74409b12 Iustin Pop
    self.needed_locks[locking.LEVEL_NODE] = []
7858 74409b12 Iustin Pop
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
7859 74409b12 Iustin Pop
7860 74409b12 Iustin Pop
  def DeclareLocks(self, level):
7861 74409b12 Iustin Pop
    if level == locking.LEVEL_NODE:
7862 74409b12 Iustin Pop
      self._LockInstancesNodes()
7863 e29e9550 Iustin Pop
      if self.op.disk_template and self.op.remote_node:
7864 e29e9550 Iustin Pop
        self.op.remote_node = _ExpandNodeName(self.cfg, self.op.remote_node)
7865 e29e9550 Iustin Pop
        self.needed_locks[locking.LEVEL_NODE].append(self.op.remote_node)
7866 a8083063 Iustin Pop
7867 a8083063 Iustin Pop
  def BuildHooksEnv(self):
7868 a8083063 Iustin Pop
    """Build hooks env.
7869 a8083063 Iustin Pop

7870 a8083063 Iustin Pop
    This runs on the master, primary and secondaries.
7871 a8083063 Iustin Pop

7872 a8083063 Iustin Pop
    """
7873 396e1b78 Michael Hanselmann
    args = dict()
7874 338e51e8 Iustin Pop
    if constants.BE_MEMORY in self.be_new:
7875 338e51e8 Iustin Pop
      args['memory'] = self.be_new[constants.BE_MEMORY]
7876 338e51e8 Iustin Pop
    if constants.BE_VCPUS in self.be_new:
7877 61be6ba4 Iustin Pop
      args['vcpus'] = self.be_new[constants.BE_VCPUS]
7878 d8dcf3c9 Guido Trotter
    # TODO: export disk changes. Note: _BuildInstanceHookEnv* don't export disk
7879 d8dcf3c9 Guido Trotter
    # information at all.
7880 d8dcf3c9 Guido Trotter
    if self.op.nics:
7881 d8dcf3c9 Guido Trotter
      args['nics'] = []
7882 d8dcf3c9 Guido Trotter
      nic_override = dict(self.op.nics)
7883 62f0dd02 Guido Trotter
      c_nicparams = self.cluster.nicparams[constants.PP_DEFAULT]
7884 d8dcf3c9 Guido Trotter
      for idx, nic in enumerate(self.instance.nics):
7885 d8dcf3c9 Guido Trotter
        if idx in nic_override:
7886 d8dcf3c9 Guido Trotter
          this_nic_override = nic_override[idx]
7887 d8dcf3c9 Guido Trotter
        else:
7888 d8dcf3c9 Guido Trotter
          this_nic_override = {}
7889 d8dcf3c9 Guido Trotter
        if 'ip' in this_nic_override:
7890 d8dcf3c9 Guido Trotter
          ip = this_nic_override['ip']
7891 d8dcf3c9 Guido Trotter
        else:
7892 d8dcf3c9 Guido Trotter
          ip = nic.ip
7893 d8dcf3c9 Guido Trotter
        if 'mac' in this_nic_override:
7894 d8dcf3c9 Guido Trotter
          mac = this_nic_override['mac']
7895 d8dcf3c9 Guido Trotter
        else:
7896 d8dcf3c9 Guido Trotter
          mac = nic.mac
7897 62f0dd02 Guido Trotter
        if idx in self.nic_pnew:
7898 62f0dd02 Guido Trotter
          nicparams = self.nic_pnew[idx]
7899 62f0dd02 Guido Trotter
        else:
7900 62f0dd02 Guido Trotter
          nicparams = objects.FillDict(c_nicparams, nic.nicparams)
7901 62f0dd02 Guido Trotter
        mode = nicparams[constants.NIC_MODE]
7902 62f0dd02 Guido Trotter
        link = nicparams[constants.NIC_LINK]
7903 62f0dd02 Guido Trotter
        args['nics'].append((ip, mac, mode, link))
7904 d8dcf3c9 Guido Trotter
      if constants.DDM_ADD in nic_override:
7905 d8dcf3c9 Guido Trotter
        ip = nic_override[constants.DDM_ADD].get('ip', None)
7906 d8dcf3c9 Guido Trotter
        mac = nic_override[constants.DDM_ADD]['mac']
7907 62f0dd02 Guido Trotter
        nicparams = self.nic_pnew[constants.DDM_ADD]
7908 62f0dd02 Guido Trotter
        mode = nicparams[constants.NIC_MODE]
7909 62f0dd02 Guido Trotter
        link = nicparams[constants.NIC_LINK]
7910 62f0dd02 Guido Trotter
        args['nics'].append((ip, mac, mode, link))
7911 d8dcf3c9 Guido Trotter
      elif constants.DDM_REMOVE in nic_override:
7912 d8dcf3c9 Guido Trotter
        del args['nics'][-1]
7913 d8dcf3c9 Guido Trotter
7914 338e51e8 Iustin Pop
    env = _BuildInstanceHookEnvByObject(self, self.instance, override=args)
7915 e29e9550 Iustin Pop
    if self.op.disk_template:
7916 e29e9550 Iustin Pop
      env["NEW_DISK_TEMPLATE"] = self.op.disk_template
7917 6b12959c Iustin Pop
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
7918 a8083063 Iustin Pop
    return env, nl, nl
7919 a8083063 Iustin Pop
7920 7e950d31 Iustin Pop
  @staticmethod
7921 7e950d31 Iustin Pop
  def _GetUpdatedParams(old_params, update_dict,
7922 0329617a Guido Trotter
                        default_values, parameter_types):
7923 0329617a Guido Trotter
    """Return the new params dict for the given params.
7924 0329617a Guido Trotter

7925 0329617a Guido Trotter
    @type old_params: dict
7926 f2fd87d7 Iustin Pop
    @param old_params: old parameters
7927 0329617a Guido Trotter
    @type update_dict: dict
7928 f2fd87d7 Iustin Pop
    @param update_dict: dict containing new parameter values,
7929 f2fd87d7 Iustin Pop
                        or constants.VALUE_DEFAULT to reset the
7930 f2fd87d7 Iustin Pop
                        parameter to its default value
7931 0329617a Guido Trotter
    @type default_values: dict
7932 0329617a Guido Trotter
    @param default_values: default values for the filled parameters
7933 0329617a Guido Trotter
    @type parameter_types: dict
7934 0329617a Guido Trotter
    @param parameter_types: dict mapping target dict keys to types
7935 0329617a Guido Trotter
                            in constants.ENFORCEABLE_TYPES
7936 0329617a Guido Trotter
    @rtype: (dict, dict)
7937 0329617a Guido Trotter
    @return: (new_parameters, filled_parameters)
7938 0329617a Guido Trotter

7939 0329617a Guido Trotter
    """
7940 0329617a Guido Trotter
    params_copy = copy.deepcopy(old_params)
7941 0329617a Guido Trotter
    for key, val in update_dict.iteritems():
7942 0329617a Guido Trotter
      if val == constants.VALUE_DEFAULT:
7943 0329617a Guido Trotter
        try:
7944 0329617a Guido Trotter
          del params_copy[key]
7945 0329617a Guido Trotter
        except KeyError:
7946 0329617a Guido Trotter
          pass
7947 0329617a Guido Trotter
      else:
7948 0329617a Guido Trotter
        params_copy[key] = val
7949 0329617a Guido Trotter
    utils.ForceDictType(params_copy, parameter_types)
7950 0329617a Guido Trotter
    params_filled = objects.FillDict(default_values, params_copy)
7951 0329617a Guido Trotter
    return (params_copy, params_filled)
7952 0329617a Guido Trotter
7953 a8083063 Iustin Pop
  def CheckPrereq(self):
7954 a8083063 Iustin Pop
    """Check prerequisites.
7955 a8083063 Iustin Pop

7956 a8083063 Iustin Pop
    This only checks the instance list against the existing names.
7957 a8083063 Iustin Pop

7958 a8083063 Iustin Pop
    """
7959 7c4d6c7b Michael Hanselmann
    self.force = self.op.force
7960 a8083063 Iustin Pop
7961 74409b12 Iustin Pop
    # checking the new params on the primary/secondary nodes
7962 31a853d2 Iustin Pop
7963 cfefe007 Guido Trotter
    instance = self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
7964 2ee88aeb Guido Trotter
    cluster = self.cluster = self.cfg.GetClusterInfo()
7965 1a5c7281 Guido Trotter
    assert self.instance is not None, \
7966 1a5c7281 Guido Trotter
      "Cannot retrieve locked instance %s" % self.op.instance_name
7967 6b12959c Iustin Pop
    pnode = instance.primary_node
7968 6b12959c Iustin Pop
    nodelist = list(instance.all_nodes)
7969 74409b12 Iustin Pop
7970 e29e9550 Iustin Pop
    if self.op.disk_template:
7971 e29e9550 Iustin Pop
      if instance.disk_template == self.op.disk_template:
7972 e29e9550 Iustin Pop
        raise errors.OpPrereqError("Instance already has disk template %s" %
7973 e29e9550 Iustin Pop
                                   instance.disk_template, errors.ECODE_INVAL)
7974 e29e9550 Iustin Pop
7975 e29e9550 Iustin Pop
      if (instance.disk_template,
7976 e29e9550 Iustin Pop
          self.op.disk_template) not in self._DISK_CONVERSIONS:
7977 e29e9550 Iustin Pop
        raise errors.OpPrereqError("Unsupported disk template conversion from"
7978 e29e9550 Iustin Pop
                                   " %s to %s" % (instance.disk_template,
7979 e29e9550 Iustin Pop
                                                  self.op.disk_template),
7980 e29e9550 Iustin Pop
                                   errors.ECODE_INVAL)
7981 e29e9550 Iustin Pop
      if self.op.disk_template in constants.DTS_NET_MIRROR:
7982 e29e9550 Iustin Pop
        _CheckNodeOnline(self, self.op.remote_node)
7983 e29e9550 Iustin Pop
        _CheckNodeNotDrained(self, self.op.remote_node)
7984 e29e9550 Iustin Pop
        disks = [{"size": d.size} for d in instance.disks]
7985 e29e9550 Iustin Pop
        required = _ComputeDiskSize(self.op.disk_template, disks)
7986 e29e9550 Iustin Pop
        _CheckNodesFreeDisk(self, [self.op.remote_node], required)
7987 e29e9550 Iustin Pop
        _CheckInstanceDown(self, instance, "cannot change disk template")
7988 e29e9550 Iustin Pop
7989 338e51e8 Iustin Pop
    # hvparams processing
7990 74409b12 Iustin Pop
    if self.op.hvparams:
7991 0329617a Guido Trotter
      i_hvdict, hv_new = self._GetUpdatedParams(
7992 0329617a Guido Trotter
                             instance.hvparams, self.op.hvparams,
7993 0329617a Guido Trotter
                             cluster.hvparams[instance.hypervisor],
7994 0329617a Guido Trotter
                             constants.HVS_PARAMETER_TYPES)
7995 74409b12 Iustin Pop
      # local check
7996 74409b12 Iustin Pop
      hypervisor.GetHypervisor(
7997 74409b12 Iustin Pop
        instance.hypervisor).CheckParameterSyntax(hv_new)
7998 74409b12 Iustin Pop
      _CheckHVParams(self, nodelist, instance.hypervisor, hv_new)
7999 338e51e8 Iustin Pop
      self.hv_new = hv_new # the new actual values
8000 338e51e8 Iustin Pop
      self.hv_inst = i_hvdict # the new dict (without defaults)
8001 338e51e8 Iustin Pop
    else:
8002 338e51e8 Iustin Pop
      self.hv_new = self.hv_inst = {}
8003 338e51e8 Iustin Pop
8004 338e51e8 Iustin Pop
    # beparams processing
8005 338e51e8 Iustin Pop
    if self.op.beparams:
8006 0329617a Guido Trotter
      i_bedict, be_new = self._GetUpdatedParams(
8007 0329617a Guido Trotter
                             instance.beparams, self.op.beparams,
8008 0329617a Guido Trotter
                             cluster.beparams[constants.PP_DEFAULT],
8009 0329617a Guido Trotter
                             constants.BES_PARAMETER_TYPES)
8010 338e51e8 Iustin Pop
      self.be_new = be_new # the new actual values
8011 338e51e8 Iustin Pop
      self.be_inst = i_bedict # the new dict (without defaults)
8012 338e51e8 Iustin Pop
    else:
8013 b637ae4d Iustin Pop
      self.be_new = self.be_inst = {}
8014 74409b12 Iustin Pop
8015 cfefe007 Guido Trotter
    self.warn = []
8016 647a5d80 Iustin Pop
8017 338e51e8 Iustin Pop
    if constants.BE_MEMORY in self.op.beparams and not self.force:
8018 647a5d80 Iustin Pop
      mem_check_list = [pnode]
8019 c0f2b229 Iustin Pop
      if be_new[constants.BE_AUTO_BALANCE]:
8020 c0f2b229 Iustin Pop
        # either we changed auto_balance to yes or it was from before
8021 647a5d80 Iustin Pop
        mem_check_list.extend(instance.secondary_nodes)
8022 72737a7f Iustin Pop
      instance_info = self.rpc.call_instance_info(pnode, instance.name,
8023 72737a7f Iustin Pop
                                                  instance.hypervisor)
8024 647a5d80 Iustin Pop
      nodeinfo = self.rpc.call_node_info(mem_check_list, self.cfg.GetVGName(),
8025 72737a7f Iustin Pop
                                         instance.hypervisor)
8026 070e998b Iustin Pop
      pninfo = nodeinfo[pnode]
8027 4c4e4e1e Iustin Pop
      msg = pninfo.fail_msg
8028 070e998b Iustin Pop
      if msg:
8029 cfefe007 Guido Trotter
        # Assume the primary node is unreachable and go ahead
8030 070e998b Iustin Pop
        self.warn.append("Can't get info from primary node %s: %s" %
8031 070e998b Iustin Pop
                         (pnode,  msg))
8032 070e998b Iustin Pop
      elif not isinstance(pninfo.payload.get('memory_free', None), int):
8033 070e998b Iustin Pop
        self.warn.append("Node data from primary node %s doesn't contain"
8034 070e998b Iustin Pop
                         " free memory information" % pnode)
8035 4c4e4e1e Iustin Pop
      elif instance_info.fail_msg:
8036 7ad1af4a Iustin Pop
        self.warn.append("Can't get instance runtime information: %s" %
8037 4c4e4e1e Iustin Pop
                        instance_info.fail_msg)
8038 cfefe007 Guido Trotter
      else:
8039 7ad1af4a Iustin Pop
        if instance_info.payload:
8040 7ad1af4a Iustin Pop
          current_mem = int(instance_info.payload['memory'])
8041 cfefe007 Guido Trotter
        else:
8042 cfefe007 Guido Trotter
          # Assume instance not running
8043 cfefe007 Guido Trotter
          # (there is a slight race condition here, but it's not very probable,
8044 cfefe007 Guido Trotter
          # and we have no other way to check)
8045 cfefe007 Guido Trotter
          current_mem = 0
8046 338e51e8 Iustin Pop
        miss_mem = (be_new[constants.BE_MEMORY] - current_mem -
8047 070e998b Iustin Pop
                    pninfo.payload['memory_free'])
8048 cfefe007 Guido Trotter
        if miss_mem > 0:
8049 cfefe007 Guido Trotter
          raise errors.OpPrereqError("This change will prevent the instance"
8050 cfefe007 Guido Trotter
                                     " from starting, due to %d MB of memory"
8051 5c983ee5 Iustin Pop
                                     " missing on its primary node" % miss_mem,
8052 5c983ee5 Iustin Pop
                                     errors.ECODE_NORES)
8053 cfefe007 Guido Trotter
8054 c0f2b229 Iustin Pop
      if be_new[constants.BE_AUTO_BALANCE]:
8055 070e998b Iustin Pop
        for node, nres in nodeinfo.items():
8056 ea33068f Iustin Pop
          if node not in instance.secondary_nodes:
8057 ea33068f Iustin Pop
            continue
8058 4c4e4e1e Iustin Pop
          msg = nres.fail_msg
8059 070e998b Iustin Pop
          if msg:
8060 070e998b Iustin Pop
            self.warn.append("Can't get info from secondary node %s: %s" %
8061 070e998b Iustin Pop
                             (node, msg))
8062 070e998b Iustin Pop
          elif not isinstance(nres.payload.get('memory_free', None), int):
8063 070e998b Iustin Pop
            self.warn.append("Secondary node %s didn't return free"
8064 070e998b Iustin Pop
                             " memory information" % node)
8065 070e998b Iustin Pop
          elif be_new[constants.BE_MEMORY] > nres.payload['memory_free']:
8066 647a5d80 Iustin Pop
            self.warn.append("Not enough memory to failover instance to"
8067 647a5d80 Iustin Pop
                             " secondary node %s" % node)
8068 5bc84f33 Alexander Schreiber
8069 24991749 Iustin Pop
    # NIC processing
8070 cd098c41 Guido Trotter
    self.nic_pnew = {}
8071 cd098c41 Guido Trotter
    self.nic_pinst = {}
8072 24991749 Iustin Pop
    for nic_op, nic_dict in self.op.nics:
8073 24991749 Iustin Pop
      if nic_op == constants.DDM_REMOVE:
8074 24991749 Iustin Pop
        if not instance.nics:
8075 5c983ee5 Iustin Pop
          raise errors.OpPrereqError("Instance has no NICs, cannot remove",
8076 5c983ee5 Iustin Pop
                                     errors.ECODE_INVAL)
8077 24991749 Iustin Pop
        continue
8078 24991749 Iustin Pop
      if nic_op != constants.DDM_ADD:
8079 24991749 Iustin Pop
        # an existing nic
8080 21bcb9aa Michael Hanselmann
        if not instance.nics:
8081 21bcb9aa Michael Hanselmann
          raise errors.OpPrereqError("Invalid NIC index %s, instance has"
8082 21bcb9aa Michael Hanselmann
                                     " no NICs" % nic_op,
8083 21bcb9aa Michael Hanselmann
                                     errors.ECODE_INVAL)
8084 24991749 Iustin Pop
        if nic_op < 0 or nic_op >= len(instance.nics):
8085 24991749 Iustin Pop
          raise errors.OpPrereqError("Invalid NIC index %s, valid values"
8086 24991749 Iustin Pop
                                     " are 0 to %d" %
8087 21bcb9aa Michael Hanselmann
                                     (nic_op, len(instance.nics) - 1),
8088 5c983ee5 Iustin Pop
                                     errors.ECODE_INVAL)
8089 cd098c41 Guido Trotter
        old_nic_params = instance.nics[nic_op].nicparams
8090 cd098c41 Guido Trotter
        old_nic_ip = instance.nics[nic_op].ip
8091 cd098c41 Guido Trotter
      else:
8092 cd098c41 Guido Trotter
        old_nic_params = {}
8093 cd098c41 Guido Trotter
        old_nic_ip = None
8094 cd098c41 Guido Trotter
8095 cd098c41 Guido Trotter
      update_params_dict = dict([(key, nic_dict[key])
8096 cd098c41 Guido Trotter
                                 for key in constants.NICS_PARAMETERS
8097 cd098c41 Guido Trotter
                                 if key in nic_dict])
8098 cd098c41 Guido Trotter
8099 5c44da6a Guido Trotter
      if 'bridge' in nic_dict:
8100 cd098c41 Guido Trotter
        update_params_dict[constants.NIC_LINK] = nic_dict['bridge']
8101 cd098c41 Guido Trotter
8102 cd098c41 Guido Trotter
      new_nic_params, new_filled_nic_params = \
8103 cd098c41 Guido Trotter
          self._GetUpdatedParams(old_nic_params, update_params_dict,
8104 cd098c41 Guido Trotter
                                 cluster.nicparams[constants.PP_DEFAULT],
8105 cd098c41 Guido Trotter
                                 constants.NICS_PARAMETER_TYPES)
8106 cd098c41 Guido Trotter
      objects.NIC.CheckParameterSyntax(new_filled_nic_params)
8107 cd098c41 Guido Trotter
      self.nic_pinst[nic_op] = new_nic_params
8108 cd098c41 Guido Trotter
      self.nic_pnew[nic_op] = new_filled_nic_params
8109 cd098c41 Guido Trotter
      new_nic_mode = new_filled_nic_params[constants.NIC_MODE]
8110 cd098c41 Guido Trotter
8111 cd098c41 Guido Trotter
      if new_nic_mode == constants.NIC_MODE_BRIDGED:
8112 cd098c41 Guido Trotter
        nic_bridge = new_filled_nic_params[constants.NIC_LINK]
8113 4c4e4e1e Iustin Pop
        msg = self.rpc.call_bridges_exist(pnode, [nic_bridge]).fail_msg
8114 35c0c8da Iustin Pop
        if msg:
8115 35c0c8da Iustin Pop
          msg = "Error checking bridges on node %s: %s" % (pnode, msg)
8116 24991749 Iustin Pop
          if self.force:
8117 24991749 Iustin Pop
            self.warn.append(msg)
8118 24991749 Iustin Pop
          else:
8119 5c983ee5 Iustin Pop
            raise errors.OpPrereqError(msg, errors.ECODE_ENVIRON)
8120 cd098c41 Guido Trotter
      if new_nic_mode == constants.NIC_MODE_ROUTED:
8121 cd098c41 Guido Trotter
        if 'ip' in nic_dict:
8122 cd098c41 Guido Trotter
          nic_ip = nic_dict['ip']
8123 cd098c41 Guido Trotter
        else:
8124 cd098c41 Guido Trotter
          nic_ip = old_nic_ip
8125 cd098c41 Guido Trotter
        if nic_ip is None:
8126 cd098c41 Guido Trotter
          raise errors.OpPrereqError('Cannot set the nic ip to None'
8127 5c983ee5 Iustin Pop
                                     ' on a routed nic', errors.ECODE_INVAL)
8128 5c44da6a Guido Trotter
      if 'mac' in nic_dict:
8129 5c44da6a Guido Trotter
        nic_mac = nic_dict['mac']
8130 5c44da6a Guido Trotter
        if nic_mac is None:
8131 5c983ee5 Iustin Pop
          raise errors.OpPrereqError('Cannot set the nic mac to None',
8132 5c983ee5 Iustin Pop
                                     errors.ECODE_INVAL)
8133 5c44da6a Guido Trotter
        elif nic_mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
8134 5c44da6a Guido Trotter
          # otherwise generate the mac
8135 36b66e6e Guido Trotter
          nic_dict['mac'] = self.cfg.GenerateMAC(self.proc.GetECId())
8136 5c44da6a Guido Trotter
        else:
8137 5c44da6a Guido Trotter
          # or validate/reserve the current one
8138 36b66e6e Guido Trotter
          try:
8139 36b66e6e Guido Trotter
            self.cfg.ReserveMAC(nic_mac, self.proc.GetECId())
8140 36b66e6e Guido Trotter
          except errors.ReservationError:
8141 5c44da6a Guido Trotter
            raise errors.OpPrereqError("MAC address %s already in use"
8142 5c983ee5 Iustin Pop
                                       " in cluster" % nic_mac,
8143 5c983ee5 Iustin Pop
                                       errors.ECODE_NOTUNIQUE)
8144 24991749 Iustin Pop
8145 24991749 Iustin Pop
    # DISK processing
8146 24991749 Iustin Pop
    if self.op.disks and instance.disk_template == constants.DT_DISKLESS:
8147 24991749 Iustin Pop
      raise errors.OpPrereqError("Disk operations not supported for"
8148 5c983ee5 Iustin Pop
                                 " diskless instances",
8149 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
8150 1122eb25 Iustin Pop
    for disk_op, _ in self.op.disks:
8151 24991749 Iustin Pop
      if disk_op == constants.DDM_REMOVE:
8152 24991749 Iustin Pop
        if len(instance.disks) == 1:
8153 24991749 Iustin Pop
          raise errors.OpPrereqError("Cannot remove the last disk of"
8154 31624382 Iustin Pop
                                     " an instance", errors.ECODE_INVAL)
8155 31624382 Iustin Pop
        _CheckInstanceDown(self, instance, "cannot remove disks")
8156 24991749 Iustin Pop
8157 24991749 Iustin Pop
      if (disk_op == constants.DDM_ADD and
8158 24991749 Iustin Pop
          len(instance.nics) >= constants.MAX_DISKS):
8159 24991749 Iustin Pop
        raise errors.OpPrereqError("Instance has too many disks (%d), cannot"
8160 5c983ee5 Iustin Pop
                                   " add more" % constants.MAX_DISKS,
8161 5c983ee5 Iustin Pop
                                   errors.ECODE_STATE)
8162 24991749 Iustin Pop
      if disk_op not in (constants.DDM_ADD, constants.DDM_REMOVE):
8163 24991749 Iustin Pop
        # an existing disk
8164 24991749 Iustin Pop
        if disk_op < 0 or disk_op >= len(instance.disks):
8165 24991749 Iustin Pop
          raise errors.OpPrereqError("Invalid disk index %s, valid values"
8166 24991749 Iustin Pop
                                     " are 0 to %d" %
8167 5c983ee5 Iustin Pop
                                     (disk_op, len(instance.disks)),
8168 5c983ee5 Iustin Pop
                                     errors.ECODE_INVAL)
8169 24991749 Iustin Pop
8170 a8083063 Iustin Pop
    return
8171 a8083063 Iustin Pop
8172 e29e9550 Iustin Pop
  def _ConvertPlainToDrbd(self, feedback_fn):
8173 e29e9550 Iustin Pop
    """Converts an instance from plain to drbd.
8174 e29e9550 Iustin Pop

8175 e29e9550 Iustin Pop
    """
8176 e29e9550 Iustin Pop
    feedback_fn("Converting template to drbd")
8177 e29e9550 Iustin Pop
    instance = self.instance
8178 e29e9550 Iustin Pop
    pnode = instance.primary_node
8179 e29e9550 Iustin Pop
    snode = self.op.remote_node
8180 e29e9550 Iustin Pop
8181 e29e9550 Iustin Pop
    # create a fake disk info for _GenerateDiskTemplate
8182 e29e9550 Iustin Pop
    disk_info = [{"size": d.size, "mode": d.mode} for d in instance.disks]
8183 e29e9550 Iustin Pop
    new_disks = _GenerateDiskTemplate(self, self.op.disk_template,
8184 e29e9550 Iustin Pop
                                      instance.name, pnode, [snode],
8185 e29e9550 Iustin Pop
                                      disk_info, None, None, 0)
8186 e29e9550 Iustin Pop
    info = _GetInstanceInfoText(instance)
8187 e29e9550 Iustin Pop
    feedback_fn("Creating aditional volumes...")
8188 e29e9550 Iustin Pop
    # first, create the missing data and meta devices
8189 e29e9550 Iustin Pop
    for disk in new_disks:
8190 e29e9550 Iustin Pop
      # unfortunately this is... not too nice
8191 e29e9550 Iustin Pop
      _CreateSingleBlockDev(self, pnode, instance, disk.children[1],
8192 e29e9550 Iustin Pop
                            info, True)
8193 e29e9550 Iustin Pop
      for child in disk.children:
8194 e29e9550 Iustin Pop
        _CreateSingleBlockDev(self, snode, instance, child, info, True)
8195 e29e9550 Iustin Pop
    # at this stage, all new LVs have been created, we can rename the
8196 e29e9550 Iustin Pop
    # old ones
8197 e29e9550 Iustin Pop
    feedback_fn("Renaming original volumes...")
8198 e29e9550 Iustin Pop
    rename_list = [(o, n.children[0].logical_id)
8199 e29e9550 Iustin Pop
                   for (o, n) in zip(instance.disks, new_disks)]
8200 e29e9550 Iustin Pop
    result = self.rpc.call_blockdev_rename(pnode, rename_list)
8201 e29e9550 Iustin Pop
    result.Raise("Failed to rename original LVs")
8202 e29e9550 Iustin Pop
8203 e29e9550 Iustin Pop
    feedback_fn("Initializing DRBD devices...")
8204 e29e9550 Iustin Pop
    # all child devices are in place, we can now create the DRBD devices
8205 e29e9550 Iustin Pop
    for disk in new_disks:
8206 e29e9550 Iustin Pop
      for node in [pnode, snode]:
8207 e29e9550 Iustin Pop
        f_create = node == pnode
8208 e29e9550 Iustin Pop
        _CreateSingleBlockDev(self, node, instance, disk, info, f_create)
8209 e29e9550 Iustin Pop
8210 e29e9550 Iustin Pop
    # at this point, the instance has been modified
8211 e29e9550 Iustin Pop
    instance.disk_template = constants.DT_DRBD8
8212 e29e9550 Iustin Pop
    instance.disks = new_disks
8213 e29e9550 Iustin Pop
    self.cfg.Update(instance, feedback_fn)
8214 e29e9550 Iustin Pop
8215 e29e9550 Iustin Pop
    # disks are created, waiting for sync
8216 e29e9550 Iustin Pop
    disk_abort = not _WaitForSync(self, instance)
8217 e29e9550 Iustin Pop
    if disk_abort:
8218 e29e9550 Iustin Pop
      raise errors.OpExecError("There are some degraded disks for"
8219 e29e9550 Iustin Pop
                               " this instance, please cleanup manually")
8220 e29e9550 Iustin Pop
8221 2f414c48 Iustin Pop
  def _ConvertDrbdToPlain(self, feedback_fn):
8222 2f414c48 Iustin Pop
    """Converts an instance from drbd to plain.
8223 2f414c48 Iustin Pop

8224 2f414c48 Iustin Pop
    """
8225 2f414c48 Iustin Pop
    instance = self.instance
8226 2f414c48 Iustin Pop
    assert len(instance.secondary_nodes) == 1
8227 2f414c48 Iustin Pop
    pnode = instance.primary_node
8228 2f414c48 Iustin Pop
    snode = instance.secondary_nodes[0]
8229 2f414c48 Iustin Pop
    feedback_fn("Converting template to plain")
8230 2f414c48 Iustin Pop
8231 2f414c48 Iustin Pop
    old_disks = instance.disks
8232 2f414c48 Iustin Pop
    new_disks = [d.children[0] for d in old_disks]
8233 2f414c48 Iustin Pop
8234 2f414c48 Iustin Pop
    # copy over size and mode
8235 2f414c48 Iustin Pop
    for parent, child in zip(old_disks, new_disks):
8236 2f414c48 Iustin Pop
      child.size = parent.size
8237 2f414c48 Iustin Pop
      child.mode = parent.mode
8238 2f414c48 Iustin Pop
8239 2f414c48 Iustin Pop
    # update instance structure
8240 2f414c48 Iustin Pop
    instance.disks = new_disks
8241 2f414c48 Iustin Pop
    instance.disk_template = constants.DT_PLAIN
8242 2f414c48 Iustin Pop
    self.cfg.Update(instance, feedback_fn)
8243 2f414c48 Iustin Pop
8244 2f414c48 Iustin Pop
    feedback_fn("Removing volumes on the secondary node...")
8245 2f414c48 Iustin Pop
    for disk in old_disks:
8246 2f414c48 Iustin Pop
      self.cfg.SetDiskID(disk, snode)
8247 2f414c48 Iustin Pop
      msg = self.rpc.call_blockdev_remove(snode, disk).fail_msg
8248 2f414c48 Iustin Pop
      if msg:
8249 2f414c48 Iustin Pop
        self.LogWarning("Could not remove block device %s on node %s,"
8250 2f414c48 Iustin Pop
                        " continuing anyway: %s", disk.iv_name, snode, msg)
8251 2f414c48 Iustin Pop
8252 2f414c48 Iustin Pop
    feedback_fn("Removing unneeded volumes on the primary node...")
8253 2f414c48 Iustin Pop
    for idx, disk in enumerate(old_disks):
8254 2f414c48 Iustin Pop
      meta = disk.children[1]
8255 2f414c48 Iustin Pop
      self.cfg.SetDiskID(meta, pnode)
8256 2f414c48 Iustin Pop
      msg = self.rpc.call_blockdev_remove(pnode, meta).fail_msg
8257 2f414c48 Iustin Pop
      if msg:
8258 2f414c48 Iustin Pop
        self.LogWarning("Could not remove metadata for disk %d on node %s,"
8259 2f414c48 Iustin Pop
                        " continuing anyway: %s", idx, pnode, msg)
8260 2f414c48 Iustin Pop
8261 2f414c48 Iustin Pop
8262 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
8263 a8083063 Iustin Pop
    """Modifies an instance.
8264 a8083063 Iustin Pop

8265 a8083063 Iustin Pop
    All parameters take effect only at the next restart of the instance.
8266 24991749 Iustin Pop

8267 a8083063 Iustin Pop
    """
8268 cfefe007 Guido Trotter
    # Process here the warnings from CheckPrereq, as we don't have a
8269 cfefe007 Guido Trotter
    # feedback_fn there.
8270 cfefe007 Guido Trotter
    for warn in self.warn:
8271 cfefe007 Guido Trotter
      feedback_fn("WARNING: %s" % warn)
8272 cfefe007 Guido Trotter
8273 a8083063 Iustin Pop
    result = []
8274 a8083063 Iustin Pop
    instance = self.instance
8275 24991749 Iustin Pop
    # disk changes
8276 24991749 Iustin Pop
    for disk_op, disk_dict in self.op.disks:
8277 24991749 Iustin Pop
      if disk_op == constants.DDM_REMOVE:
8278 24991749 Iustin Pop
        # remove the last disk
8279 24991749 Iustin Pop
        device = instance.disks.pop()
8280 24991749 Iustin Pop
        device_idx = len(instance.disks)
8281 24991749 Iustin Pop
        for node, disk in device.ComputeNodeTree(instance.primary_node):
8282 24991749 Iustin Pop
          self.cfg.SetDiskID(disk, node)
8283 4c4e4e1e Iustin Pop
          msg = self.rpc.call_blockdev_remove(node, disk).fail_msg
8284 e1bc0878 Iustin Pop
          if msg:
8285 e1bc0878 Iustin Pop
            self.LogWarning("Could not remove disk/%d on node %s: %s,"
8286 e1bc0878 Iustin Pop
                            " continuing anyway", device_idx, node, msg)
8287 24991749 Iustin Pop
        result.append(("disk/%d" % device_idx, "remove"))
8288 24991749 Iustin Pop
      elif disk_op == constants.DDM_ADD:
8289 24991749 Iustin Pop
        # add a new disk
8290 24991749 Iustin Pop
        if instance.disk_template == constants.DT_FILE:
8291 24991749 Iustin Pop
          file_driver, file_path = instance.disks[0].logical_id
8292 24991749 Iustin Pop
          file_path = os.path.dirname(file_path)
8293 24991749 Iustin Pop
        else:
8294 24991749 Iustin Pop
          file_driver = file_path = None
8295 24991749 Iustin Pop
        disk_idx_base = len(instance.disks)
8296 24991749 Iustin Pop
        new_disk = _GenerateDiskTemplate(self,
8297 24991749 Iustin Pop
                                         instance.disk_template,
8298 32388e6d Iustin Pop
                                         instance.name, instance.primary_node,
8299 24991749 Iustin Pop
                                         instance.secondary_nodes,
8300 24991749 Iustin Pop
                                         [disk_dict],
8301 24991749 Iustin Pop
                                         file_path,
8302 24991749 Iustin Pop
                                         file_driver,
8303 24991749 Iustin Pop
                                         disk_idx_base)[0]
8304 24991749 Iustin Pop
        instance.disks.append(new_disk)
8305 24991749 Iustin Pop
        info = _GetInstanceInfoText(instance)
8306 24991749 Iustin Pop
8307 24991749 Iustin Pop
        logging.info("Creating volume %s for instance %s",
8308 24991749 Iustin Pop
                     new_disk.iv_name, instance.name)
8309 24991749 Iustin Pop
        # Note: this needs to be kept in sync with _CreateDisks
8310 24991749 Iustin Pop
        #HARDCODE
8311 428958aa Iustin Pop
        for node in instance.all_nodes:
8312 428958aa Iustin Pop
          f_create = node == instance.primary_node
8313 796cab27 Iustin Pop
          try:
8314 428958aa Iustin Pop
            _CreateBlockDev(self, node, instance, new_disk,
8315 428958aa Iustin Pop
                            f_create, info, f_create)
8316 1492cca7 Iustin Pop
          except errors.OpExecError, err:
8317 24991749 Iustin Pop
            self.LogWarning("Failed to create volume %s (%s) on"
8318 428958aa Iustin Pop
                            " node %s: %s",
8319 428958aa Iustin Pop
                            new_disk.iv_name, new_disk, node, err)
8320 24991749 Iustin Pop
        result.append(("disk/%d" % disk_idx_base, "add:size=%s,mode=%s" %
8321 24991749 Iustin Pop
                       (new_disk.size, new_disk.mode)))
8322 24991749 Iustin Pop
      else:
8323 24991749 Iustin Pop
        # change a given disk
8324 24991749 Iustin Pop
        instance.disks[disk_op].mode = disk_dict['mode']
8325 24991749 Iustin Pop
        result.append(("disk.mode/%d" % disk_op, disk_dict['mode']))
8326 e29e9550 Iustin Pop
8327 e29e9550 Iustin Pop
    if self.op.disk_template:
8328 e29e9550 Iustin Pop
      r_shut = _ShutdownInstanceDisks(self, instance)
8329 e29e9550 Iustin Pop
      if not r_shut:
8330 e29e9550 Iustin Pop
        raise errors.OpExecError("Cannot shutdow instance disks, unable to"
8331 e29e9550 Iustin Pop
                                 " proceed with disk template conversion")
8332 e29e9550 Iustin Pop
      mode = (instance.disk_template, self.op.disk_template)
8333 e29e9550 Iustin Pop
      try:
8334 e29e9550 Iustin Pop
        self._DISK_CONVERSIONS[mode](self, feedback_fn)
8335 e29e9550 Iustin Pop
      except:
8336 e29e9550 Iustin Pop
        self.cfg.ReleaseDRBDMinors(instance.name)
8337 e29e9550 Iustin Pop
        raise
8338 e29e9550 Iustin Pop
      result.append(("disk_template", self.op.disk_template))
8339 e29e9550 Iustin Pop
8340 24991749 Iustin Pop
    # NIC changes
8341 24991749 Iustin Pop
    for nic_op, nic_dict in self.op.nics:
8342 24991749 Iustin Pop
      if nic_op == constants.DDM_REMOVE:
8343 24991749 Iustin Pop
        # remove the last nic
8344 24991749 Iustin Pop
        del instance.nics[-1]
8345 24991749 Iustin Pop
        result.append(("nic.%d" % len(instance.nics), "remove"))
8346 24991749 Iustin Pop
      elif nic_op == constants.DDM_ADD:
8347 5c44da6a Guido Trotter
        # mac and bridge should be set, by now
8348 5c44da6a Guido Trotter
        mac = nic_dict['mac']
8349 cd098c41 Guido Trotter
        ip = nic_dict.get('ip', None)
8350 cd098c41 Guido Trotter
        nicparams = self.nic_pinst[constants.DDM_ADD]
8351 cd098c41 Guido Trotter
        new_nic = objects.NIC(mac=mac, ip=ip, nicparams=nicparams)
8352 24991749 Iustin Pop
        instance.nics.append(new_nic)
8353 24991749 Iustin Pop
        result.append(("nic.%d" % (len(instance.nics) - 1),
8354 cd098c41 Guido Trotter
                       "add:mac=%s,ip=%s,mode=%s,link=%s" %
8355 cd098c41 Guido Trotter
                       (new_nic.mac, new_nic.ip,
8356 cd098c41 Guido Trotter
                        self.nic_pnew[constants.DDM_ADD][constants.NIC_MODE],
8357 cd098c41 Guido Trotter
                        self.nic_pnew[constants.DDM_ADD][constants.NIC_LINK]
8358 cd098c41 Guido Trotter
                       )))
8359 24991749 Iustin Pop
      else:
8360 cd098c41 Guido Trotter
        for key in 'mac', 'ip':
8361 24991749 Iustin Pop
          if key in nic_dict:
8362 24991749 Iustin Pop
            setattr(instance.nics[nic_op], key, nic_dict[key])
8363 beabf067 Guido Trotter
        if nic_op in self.nic_pinst:
8364 beabf067 Guido Trotter
          instance.nics[nic_op].nicparams = self.nic_pinst[nic_op]
8365 cd098c41 Guido Trotter
        for key, val in nic_dict.iteritems():
8366 cd098c41 Guido Trotter
          result.append(("nic.%s/%d" % (key, nic_op), val))
8367 24991749 Iustin Pop
8368 24991749 Iustin Pop
    # hvparams changes
8369 74409b12 Iustin Pop
    if self.op.hvparams:
8370 12649e35 Guido Trotter
      instance.hvparams = self.hv_inst
8371 74409b12 Iustin Pop
      for key, val in self.op.hvparams.iteritems():
8372 74409b12 Iustin Pop
        result.append(("hv/%s" % key, val))
8373 24991749 Iustin Pop
8374 24991749 Iustin Pop
    # beparams changes
8375 338e51e8 Iustin Pop
    if self.op.beparams:
8376 338e51e8 Iustin Pop
      instance.beparams = self.be_inst
8377 338e51e8 Iustin Pop
      for key, val in self.op.beparams.iteritems():
8378 338e51e8 Iustin Pop
        result.append(("be/%s" % key, val))
8379 a8083063 Iustin Pop
8380 a4eae71f Michael Hanselmann
    self.cfg.Update(instance, feedback_fn)
8381 a8083063 Iustin Pop
8382 a8083063 Iustin Pop
    return result
8383 a8083063 Iustin Pop
8384 e29e9550 Iustin Pop
  _DISK_CONVERSIONS = {
8385 e29e9550 Iustin Pop
    (constants.DT_PLAIN, constants.DT_DRBD8): _ConvertPlainToDrbd,
8386 2f414c48 Iustin Pop
    (constants.DT_DRBD8, constants.DT_PLAIN): _ConvertDrbdToPlain,
8387 e29e9550 Iustin Pop
    }
8388 a8083063 Iustin Pop
8389 a8083063 Iustin Pop
class LUQueryExports(NoHooksLU):
8390 a8083063 Iustin Pop
  """Query the exports list
8391 a8083063 Iustin Pop

8392 a8083063 Iustin Pop
  """
8393 895ecd9c Guido Trotter
  _OP_REQP = ['nodes']
8394 21a15682 Guido Trotter
  REQ_BGL = False
8395 21a15682 Guido Trotter
8396 21a15682 Guido Trotter
  def ExpandNames(self):
8397 21a15682 Guido Trotter
    self.needed_locks = {}
8398 21a15682 Guido Trotter
    self.share_locks[locking.LEVEL_NODE] = 1
8399 21a15682 Guido Trotter
    if not self.op.nodes:
8400 e310b019 Guido Trotter
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
8401 21a15682 Guido Trotter
    else:
8402 21a15682 Guido Trotter
      self.needed_locks[locking.LEVEL_NODE] = \
8403 21a15682 Guido Trotter
        _GetWantedNodes(self, self.op.nodes)
8404 a8083063 Iustin Pop
8405 a8083063 Iustin Pop
  def CheckPrereq(self):
8406 21a15682 Guido Trotter
    """Check prerequisites.
8407 a8083063 Iustin Pop

8408 a8083063 Iustin Pop
    """
8409 21a15682 Guido Trotter
    self.nodes = self.acquired_locks[locking.LEVEL_NODE]
8410 a8083063 Iustin Pop
8411 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
8412 a8083063 Iustin Pop
    """Compute the list of all the exported system images.
8413 a8083063 Iustin Pop

8414 e4376078 Iustin Pop
    @rtype: dict
8415 e4376078 Iustin Pop
    @return: a dictionary with the structure node->(export-list)
8416 e4376078 Iustin Pop
        where export-list is a list of the instances exported on
8417 e4376078 Iustin Pop
        that node.
8418 a8083063 Iustin Pop

8419 a8083063 Iustin Pop
    """
8420 b04285f2 Guido Trotter
    rpcresult = self.rpc.call_export_list(self.nodes)
8421 b04285f2 Guido Trotter
    result = {}
8422 b04285f2 Guido Trotter
    for node in rpcresult:
8423 4c4e4e1e Iustin Pop
      if rpcresult[node].fail_msg:
8424 b04285f2 Guido Trotter
        result[node] = False
8425 b04285f2 Guido Trotter
      else:
8426 1b7bfbb7 Iustin Pop
        result[node] = rpcresult[node].payload
8427 b04285f2 Guido Trotter
8428 b04285f2 Guido Trotter
    return result
8429 a8083063 Iustin Pop
8430 a8083063 Iustin Pop
8431 a8083063 Iustin Pop
class LUExportInstance(LogicalUnit):
8432 a8083063 Iustin Pop
  """Export an instance to an image in the cluster.
8433 a8083063 Iustin Pop

8434 a8083063 Iustin Pop
  """
8435 a8083063 Iustin Pop
  HPATH = "instance-export"
8436 a8083063 Iustin Pop
  HTYPE = constants.HTYPE_INSTANCE
8437 a8083063 Iustin Pop
  _OP_REQP = ["instance_name", "target_node", "shutdown"]
8438 6657590e Guido Trotter
  REQ_BGL = False
8439 6657590e Guido Trotter
8440 17c3f802 Guido Trotter
  def CheckArguments(self):
8441 17c3f802 Guido Trotter
    """Check the arguments.
8442 17c3f802 Guido Trotter

8443 17c3f802 Guido Trotter
    """
8444 17c3f802 Guido Trotter
    self.shutdown_timeout = getattr(self.op, "shutdown_timeout",
8445 17c3f802 Guido Trotter
                                    constants.DEFAULT_SHUTDOWN_TIMEOUT)
8446 17c3f802 Guido Trotter
8447 6657590e Guido Trotter
  def ExpandNames(self):
8448 6657590e Guido Trotter
    self._ExpandAndLockInstance()
8449 6657590e Guido Trotter
    # FIXME: lock only instance primary and destination node
8450 6657590e Guido Trotter
    #
8451 6657590e Guido Trotter
    # Sad but true, for now we have do lock all nodes, as we don't know where
8452 6657590e Guido Trotter
    # the previous export might be, and and in this LU we search for it and
8453 6657590e Guido Trotter
    # remove it from its current node. In the future we could fix this by:
8454 6657590e Guido Trotter
    #  - making a tasklet to search (share-lock all), then create the new one,
8455 6657590e Guido Trotter
    #    then one to remove, after
8456 5bbd3f7f Michael Hanselmann
    #  - removing the removal operation altogether
8457 6657590e Guido Trotter
    self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
8458 6657590e Guido Trotter
8459 6657590e Guido Trotter
  def DeclareLocks(self, level):
8460 6657590e Guido Trotter
    """Last minute lock declaration."""
8461 6657590e Guido Trotter
    # All nodes are locked anyway, so nothing to do here.
8462 a8083063 Iustin Pop
8463 a8083063 Iustin Pop
  def BuildHooksEnv(self):
8464 a8083063 Iustin Pop
    """Build hooks env.
8465 a8083063 Iustin Pop

8466 a8083063 Iustin Pop
    This will run on the master, primary node and target node.
8467 a8083063 Iustin Pop

8468 a8083063 Iustin Pop
    """
8469 a8083063 Iustin Pop
    env = {
8470 a8083063 Iustin Pop
      "EXPORT_NODE": self.op.target_node,
8471 a8083063 Iustin Pop
      "EXPORT_DO_SHUTDOWN": self.op.shutdown,
8472 17c3f802 Guido Trotter
      "SHUTDOWN_TIMEOUT": self.shutdown_timeout,
8473 a8083063 Iustin Pop
      }
8474 338e51e8 Iustin Pop
    env.update(_BuildInstanceHookEnvByObject(self, self.instance))
8475 d6a02168 Michael Hanselmann
    nl = [self.cfg.GetMasterNode(), self.instance.primary_node,
8476 a8083063 Iustin Pop
          self.op.target_node]
8477 a8083063 Iustin Pop
    return env, nl, nl
8478 a8083063 Iustin Pop
8479 a8083063 Iustin Pop
  def CheckPrereq(self):
8480 a8083063 Iustin Pop
    """Check prerequisites.
8481 a8083063 Iustin Pop

8482 9ac99fda Guido Trotter
    This checks that the instance and node names are valid.
8483 a8083063 Iustin Pop

8484 a8083063 Iustin Pop
    """
8485 6657590e Guido Trotter
    instance_name = self.op.instance_name
8486 a8083063 Iustin Pop
    self.instance = self.cfg.GetInstanceInfo(instance_name)
8487 6657590e Guido Trotter
    assert self.instance is not None, \
8488 6657590e Guido Trotter
          "Cannot retrieve locked instance %s" % self.op.instance_name
8489 43017d26 Iustin Pop
    _CheckNodeOnline(self, self.instance.primary_node)
8490 a8083063 Iustin Pop
8491 cf26a87a Iustin Pop
    self.op.target_node = _ExpandNodeName(self.cfg, self.op.target_node)
8492 cf26a87a Iustin Pop
    self.dst_node = self.cfg.GetNodeInfo(self.op.target_node)
8493 cf26a87a Iustin Pop
    assert self.dst_node is not None
8494 a8083063 Iustin Pop
8495 aeb83a2b Iustin Pop
    _CheckNodeOnline(self, self.dst_node.name)
8496 733a2b6a Iustin Pop
    _CheckNodeNotDrained(self, self.dst_node.name)
8497 a8083063 Iustin Pop
8498 b6023d6c Manuel Franceschini
    # instance disk type verification
8499 b6023d6c Manuel Franceschini
    for disk in self.instance.disks:
8500 b6023d6c Manuel Franceschini
      if disk.dev_type == constants.LD_FILE:
8501 b6023d6c Manuel Franceschini
        raise errors.OpPrereqError("Export not supported for instances with"
8502 5c983ee5 Iustin Pop
                                   " file-based disks", errors.ECODE_INVAL)
8503 b6023d6c Manuel Franceschini
8504 a8083063 Iustin Pop
  def Exec(self, feedback_fn):
8505 a8083063 Iustin Pop
    """Export an instance to an image in the cluster.
8506 a8083063 Iustin Pop

8507 a8083063 Iustin Pop
    """
8508 a8083063 Iustin Pop
    instance = self.instance
8509 a8083063 Iustin Pop
    dst_node = self.dst_node
8510 a8083063 Iustin Pop
    src_node = instance.primary_node
8511 37972df0 Michael Hanselmann
8512 a8083063 Iustin Pop
    if self.op.shutdown:
8513 fb300fb7 Guido Trotter
      # shutdown the instance, but not the disks
8514 37972df0 Michael Hanselmann
      feedback_fn("Shutting down instance %s" % instance.name)
8515 17c3f802 Guido Trotter
      result = self.rpc.call_instance_shutdown(src_node, instance,
8516 17c3f802 Guido Trotter
                                               self.shutdown_timeout)
8517 4c4e4e1e Iustin Pop
      result.Raise("Could not shutdown instance %s on"
8518 4c4e4e1e Iustin Pop
                   " node %s" % (instance.name, src_node))
8519 a8083063 Iustin Pop
8520 a8083063 Iustin Pop
    vgname = self.cfg.GetVGName()
8521 a8083063 Iustin Pop
8522 a8083063 Iustin Pop
    snap_disks = []
8523 a8083063 Iustin Pop
8524 998c712c Iustin Pop
    # set the disks ID correctly since call_instance_start needs the
8525 998c712c Iustin Pop
    # correct drbd minor to create the symlinks
8526 998c712c Iustin Pop
    for disk in instance.disks:
8527 998c712c Iustin Pop
      self.cfg.SetDiskID(disk, src_node)
8528 998c712c Iustin Pop
8529 3e53a60b Michael Hanselmann
    activate_disks = (not instance.admin_up)
8530 3e53a60b Michael Hanselmann
8531 3e53a60b Michael Hanselmann
    if activate_disks:
8532 3e53a60b Michael Hanselmann
      # Activate the instance disks if we'exporting a stopped instance
8533 3e53a60b Michael Hanselmann
      feedback_fn("Activating disks for %s" % instance.name)
8534 3e53a60b Michael Hanselmann
      _StartInstanceDisks(self, instance, None)
8535 3e53a60b Michael Hanselmann
8536 a8083063 Iustin Pop
    try:
8537 3e53a60b Michael Hanselmann
      # per-disk results
8538 3e53a60b Michael Hanselmann
      dresults = []
8539 3e53a60b Michael Hanselmann
      try:
8540 3e53a60b Michael Hanselmann
        for idx, disk in enumerate(instance.disks):
8541 3e53a60b Michael Hanselmann
          feedback_fn("Creating a snapshot of disk/%s on node %s" %
8542 3e53a60b Michael Hanselmann
                      (idx, src_node))
8543 3e53a60b Michael Hanselmann
8544 3e53a60b Michael Hanselmann
          # result.payload will be a snapshot of an lvm leaf of the one we
8545 3e53a60b Michael Hanselmann
          # passed
8546 3e53a60b Michael Hanselmann
          result = self.rpc.call_blockdev_snapshot(src_node, disk)
8547 3e53a60b Michael Hanselmann
          msg = result.fail_msg
8548 3e53a60b Michael Hanselmann
          if msg:
8549 3e53a60b Michael Hanselmann
            self.LogWarning("Could not snapshot disk/%s on node %s: %s",
8550 3e53a60b Michael Hanselmann
                            idx, src_node, msg)
8551 3e53a60b Michael Hanselmann
            snap_disks.append(False)
8552 3e53a60b Michael Hanselmann
          else:
8553 3e53a60b Michael Hanselmann
            disk_id = (vgname, result.payload)
8554 3e53a60b Michael Hanselmann
            new_dev = objects.Disk(dev_type=constants.LD_LV, size=disk.size,
8555 3e53a60b Michael Hanselmann
                                   logical_id=disk_id, physical_id=disk_id,
8556 3e53a60b Michael Hanselmann
                                   iv_name=disk.iv_name)
8557 3e53a60b Michael Hanselmann
            snap_disks.append(new_dev)
8558 37972df0 Michael Hanselmann
8559 3e53a60b Michael Hanselmann
      finally:
8560 3e53a60b Michael Hanselmann
        if self.op.shutdown and instance.admin_up:
8561 3e53a60b Michael Hanselmann
          feedback_fn("Starting instance %s" % instance.name)
8562 3e53a60b Michael Hanselmann
          result = self.rpc.call_instance_start(src_node, instance, None, None)
8563 3e53a60b Michael Hanselmann
          msg = result.fail_msg
8564 3e53a60b Michael Hanselmann
          if msg:
8565 3e53a60b Michael Hanselmann
            _ShutdownInstanceDisks(self, instance)
8566 3e53a60b Michael Hanselmann
            raise errors.OpExecError("Could not start instance: %s" % msg)
8567 3e53a60b Michael Hanselmann
8568 3e53a60b Michael Hanselmann
      # TODO: check for size
8569 3e53a60b Michael Hanselmann
8570 3e53a60b Michael Hanselmann
      cluster_name = self.cfg.GetClusterName()
8571 3e53a60b Michael Hanselmann
      for idx, dev in enumerate(snap_disks):
8572 3e53a60b Michael Hanselmann
        feedback_fn("Exporting snapshot %s from %s to %s" %
8573 3e53a60b Michael Hanselmann
                    (idx, src_node, dst_node.name))
8574 3e53a60b Michael Hanselmann
        if dev:
8575 4a0e011f Iustin Pop
          # FIXME: pass debug from opcode to backend
8576 3e53a60b Michael Hanselmann
          result = self.rpc.call_snapshot_export(src_node, dev, dst_node.name,
8577 4a0e011f Iustin Pop
                                                 instance, cluster_name,
8578 dd713605 Iustin Pop
                                                 idx, self.op.debug_level)
8579 3e53a60b Michael Hanselmann
          msg = result.fail_msg
8580 3e53a60b Michael Hanselmann
          if msg:
8581 3e53a60b Michael Hanselmann
            self.LogWarning("Could not export disk/%s from node %s to"
8582 3e53a60b Michael Hanselmann
                            " node %s: %s", idx, src_node, dst_node.name, msg)
8583 3e53a60b Michael Hanselmann
            dresults.append(False)
8584 3e53a60b Michael Hanselmann
          else:
8585 3e53a60b Michael Hanselmann
            dresults.append(True)
8586 3e53a60b Michael Hanselmann
          msg = self.rpc.call_blockdev_remove(src_node, dev).fail_msg
8587 3e53a60b Michael Hanselmann
          if msg:
8588 3e53a60b Michael Hanselmann
            self.LogWarning("Could not remove snapshot for disk/%d from node"
8589 3e53a60b Michael Hanselmann
                            " %s: %s", idx, src_node, msg)
8590 19d7f90a Guido Trotter
        else:
8591 084f05a5 Iustin Pop
          dresults.append(False)
8592 a8083063 Iustin Pop
8593 3e53a60b Michael Hanselmann
      feedback_fn("Finalizing export on %s" % dst_node.name)
8594 3e53a60b Michael Hanselmann
      result = self.rpc.call_finalize_export(dst_node.name, instance,
8595 3e53a60b Michael Hanselmann
                                             snap_disks)
8596 3e53a60b Michael Hanselmann
      fin_resu = True
8597 3e53a60b Michael Hanselmann
      msg = result.fail_msg
8598 3e53a60b Michael Hanselmann
      if msg:
8599 3e53a60b Michael Hanselmann
        self.LogWarning("Could not finalize export for instance %s"
8600 3e53a60b Michael Hanselmann
                        " on node %s: %s", instance.name, dst_node.name, msg)
8601 3e53a60b Michael Hanselmann
        fin_resu = False
8602 3e53a60b Michael Hanselmann
8603 3e53a60b Michael Hanselmann
    finally:
8604 3e53a60b Michael Hanselmann
      if activate_disks:
8605 3e53a60b Michael Hanselmann
        feedback_fn("Deactivating disks for %s" % instance.name)
8606 3e53a60b Michael Hanselmann
        _ShutdownInstanceDisks(self, instance)
8607 a8083063 Iustin Pop
8608 a8083063 Iustin Pop
    nodelist = self.cfg.GetNodeList()
8609 a8083063 Iustin Pop
    nodelist.remove(dst_node.name)
8610 a8083063 Iustin Pop
8611 a8083063 Iustin Pop
    # on one-node clusters nodelist will be empty after the removal
8612 a8083063 Iustin Pop
    # if we proceed the backup would be removed because OpQueryExports
8613 a8083063 Iustin Pop
    # substitutes an empty list with the full cluster node list.
8614 35fbcd11 Iustin Pop
    iname = instance.name
8615 a8083063 Iustin Pop
    if nodelist:
8616 37972df0 Michael Hanselmann
      feedback_fn("Removing old exports for instance %s" % iname)
8617 72737a7f Iustin Pop
      exportlist = self.rpc.call_export_list(nodelist)
8618 a8083063 Iustin Pop
      for node in exportlist:
8619 4c4e4e1e Iustin Pop
        if exportlist[node].fail_msg:
8620 781de953 Iustin Pop
          continue
8621 35fbcd11 Iustin Pop
        if iname in exportlist[node].payload:
8622 4c4e4e1e Iustin Pop
          msg = self.rpc.call_export_remove(node, iname).fail_msg
8623 35fbcd11 Iustin Pop
          if msg:
8624 19d7f90a Guido Trotter
            self.LogWarning("Could not remove older export for instance %s"
8625 35fbcd11 Iustin Pop
                            " on node %s: %s", iname, node, msg)
8626 084f05a5 Iustin Pop
    return fin_resu, dresults
8627 5c947f38 Iustin Pop
8628 5c947f38 Iustin Pop
8629 9ac99fda Guido Trotter
class LURemoveExport(NoHooksLU):
8630 9ac99fda Guido Trotter
  """Remove exports related to the named instance.
8631 9ac99fda Guido Trotter

8632 9ac99fda Guido Trotter
  """
8633 9ac99fda Guido Trotter
  _OP_REQP = ["instance_name"]
8634 3656b3af Guido Trotter
  REQ_BGL = False
8635 3656b3af Guido Trotter
8636 3656b3af Guido Trotter
  def ExpandNames(self):
8637 3656b3af Guido Trotter
    self.needed_locks = {}
8638 3656b3af Guido Trotter
    # We need all nodes to be locked in order for RemoveExport to work, but we
8639 3656b3af Guido Trotter
    # don't need to lock the instance itself, as nothing will happen to it (and
8640 3656b3af Guido Trotter
    # we can remove exports also for a removed instance)
8641 3656b3af Guido Trotter
    self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
8642 9ac99fda Guido Trotter
8643 9ac99fda Guido Trotter
  def CheckPrereq(self):
8644 9ac99fda Guido Trotter
    """Check prerequisites.
8645 9ac99fda Guido Trotter
    """
8646 9ac99fda Guido Trotter
    pass
8647 9ac99fda Guido Trotter
8648 9ac99fda Guido Trotter
  def Exec(self, feedback_fn):
8649 9ac99fda Guido Trotter
    """Remove any export.
8650 9ac99fda Guido Trotter

8651 9ac99fda Guido Trotter
    """
8652 9ac99fda Guido Trotter
    instance_name = self.cfg.ExpandInstanceName(self.op.instance_name)
8653 9ac99fda Guido Trotter
    # If the instance was not found we'll try with the name that was passed in.
8654 9ac99fda Guido Trotter
    # This will only work if it was an FQDN, though.
8655 9ac99fda Guido Trotter
    fqdn_warn = False
8656 9ac99fda Guido Trotter
    if not instance_name:
8657 9ac99fda Guido Trotter
      fqdn_warn = True
8658 9ac99fda Guido Trotter
      instance_name = self.op.instance_name
8659 9ac99fda Guido Trotter
8660 1b7bfbb7 Iustin Pop
    locked_nodes = self.acquired_locks[locking.LEVEL_NODE]
8661 1b7bfbb7 Iustin Pop
    exportlist = self.rpc.call_export_list(locked_nodes)
8662 9ac99fda Guido Trotter
    found = False
8663 9ac99fda Guido Trotter
    for node in exportlist:
8664 4c4e4e1e Iustin Pop
      msg = exportlist[node].fail_msg
8665 1b7bfbb7 Iustin Pop
      if msg:
8666 1b7bfbb7 Iustin Pop
        self.LogWarning("Failed to query node %s (continuing): %s", node, msg)
8667 781de953 Iustin Pop
        continue
8668 1b7bfbb7 Iustin Pop
      if instance_name in exportlist[node].payload:
8669 9ac99fda Guido Trotter
        found = True
8670 781de953 Iustin Pop
        result = self.rpc.call_export_remove(node, instance_name)
8671 4c4e4e1e Iustin Pop
        msg = result.fail_msg
8672 35fbcd11 Iustin Pop
        if msg:
8673 9a4f63d1 Iustin Pop
          logging.error("Could not remove export for instance %s"
8674 35fbcd11 Iustin Pop
                        " on node %s: %s", instance_name, node, msg)
8675 9ac99fda Guido Trotter
8676 9ac99fda Guido Trotter
    if fqdn_warn and not found:
8677 9ac99fda Guido Trotter
      feedback_fn("Export not found. If trying to remove an export belonging"
8678 9ac99fda Guido Trotter
                  " to a deleted instance please use its Fully Qualified"
8679 9ac99fda Guido Trotter
                  " Domain Name.")
8680 9ac99fda Guido Trotter
8681 9ac99fda Guido Trotter
8682 fe267188 Iustin Pop
class TagsLU(NoHooksLU): # pylint: disable-msg=W0223
8683 5c947f38 Iustin Pop
  """Generic tags LU.
8684 5c947f38 Iustin Pop

8685 5c947f38 Iustin Pop
  This is an abstract class which is the parent of all the other tags LUs.
8686 5c947f38 Iustin Pop

8687 5c947f38 Iustin Pop
  """
8688 5c947f38 Iustin Pop
8689 8646adce Guido Trotter
  def ExpandNames(self):
8690 8646adce Guido Trotter
    self.needed_locks = {}
8691 8646adce Guido Trotter
    if self.op.kind == constants.TAG_NODE:
8692 cf26a87a Iustin Pop
      self.op.name = _ExpandNodeName(self.cfg, self.op.name)
8693 cf26a87a Iustin Pop
      self.needed_locks[locking.LEVEL_NODE] = self.op.name
8694 5c947f38 Iustin Pop
    elif self.op.kind == constants.TAG_INSTANCE:
8695 cf26a87a Iustin Pop
      self.op.name = _ExpandInstanceName(self.cfg, self.op.name)
8696 cf26a87a Iustin Pop
      self.needed_locks[locking.LEVEL_INSTANCE] = self.op.name
8697 8646adce Guido Trotter
8698 8646adce Guido Trotter
  def CheckPrereq(self):
8699 8646adce Guido Trotter
    """Check prerequisites.
8700 8646adce Guido Trotter

8701 8646adce Guido Trotter
    """
8702 8646adce Guido Trotter
    if self.op.kind == constants.TAG_CLUSTER:
8703 8646adce Guido Trotter
      self.target = self.cfg.GetClusterInfo()
8704 8646adce Guido Trotter
    elif self.op.kind == constants.TAG_NODE:
8705 8646adce Guido Trotter
      self.target = self.cfg.GetNodeInfo(self.op.name)
8706 8646adce Guido Trotter
    elif self.op.kind == constants.TAG_INSTANCE:
8707 8646adce Guido Trotter
      self.target = self.cfg.GetInstanceInfo(self.op.name)
8708 5c947f38 Iustin Pop
    else:
8709 3ecf6786 Iustin Pop
      raise errors.OpPrereqError("Wrong tag type requested (%s)" %
8710 5c983ee5 Iustin Pop
                                 str(self.op.kind), errors.ECODE_INVAL)
8711 5c947f38 Iustin Pop
8712 5c947f38 Iustin Pop
8713 5c947f38 Iustin Pop
class LUGetTags(TagsLU):
8714 5c947f38 Iustin Pop
  """Returns the tags of a given object.
8715 5c947f38 Iustin Pop

8716 5c947f38 Iustin Pop
  """
8717 5c947f38 Iustin Pop
  _OP_REQP = ["kind", "name"]
8718 8646adce Guido Trotter
  REQ_BGL = False
8719 5c947f38 Iustin Pop
8720 5c947f38 Iustin Pop
  def Exec(self, feedback_fn):
8721 5c947f38 Iustin Pop
    """Returns the tag list.
8722 5c947f38 Iustin Pop

8723 5c947f38 Iustin Pop
    """
8724 5d414478 Oleksiy Mishchenko
    return list(self.target.GetTags())
8725 5c947f38 Iustin Pop
8726 5c947f38 Iustin Pop
8727 73415719 Iustin Pop
class LUSearchTags(NoHooksLU):
8728 73415719 Iustin Pop
  """Searches the tags for a given pattern.
8729 73415719 Iustin Pop

8730 73415719 Iustin Pop
  """
8731 73415719 Iustin Pop
  _OP_REQP = ["pattern"]
8732 8646adce Guido Trotter
  REQ_BGL = False
8733 8646adce Guido Trotter
8734 8646adce Guido Trotter
  def ExpandNames(self):
8735 8646adce Guido Trotter
    self.needed_locks = {}
8736 73415719 Iustin Pop
8737 73415719 Iustin Pop
  def CheckPrereq(self):
8738 73415719 Iustin Pop
    """Check prerequisites.
8739 73415719 Iustin Pop

8740 73415719 Iustin Pop
    This checks the pattern passed for validity by compiling it.
8741 73415719 Iustin Pop

8742 73415719 Iustin Pop
    """
8743 73415719 Iustin Pop
    try:
8744 73415719 Iustin Pop
      self.re = re.compile(self.op.pattern)
8745 73415719 Iustin Pop
    except re.error, err:
8746 73415719 Iustin Pop
      raise errors.OpPrereqError("Invalid search pattern '%s': %s" %
8747 5c983ee5 Iustin Pop
                                 (self.op.pattern, err), errors.ECODE_INVAL)
8748 73415719 Iustin Pop
8749 73415719 Iustin Pop
  def Exec(self, feedback_fn):
8750 73415719 Iustin Pop
    """Returns the tag list.
8751 73415719 Iustin Pop

8752 73415719 Iustin Pop
    """
8753 73415719 Iustin Pop
    cfg = self.cfg
8754 73415719 Iustin Pop
    tgts = [("/cluster", cfg.GetClusterInfo())]
8755 8646adce Guido Trotter
    ilist = cfg.GetAllInstancesInfo().values()
8756 73415719 Iustin Pop
    tgts.extend([("/instances/%s" % i.name, i) for i in ilist])
8757 8646adce Guido Trotter
    nlist = cfg.GetAllNodesInfo().values()
8758 73415719 Iustin Pop
    tgts.extend([("/nodes/%s" % n.name, n) for n in nlist])
8759 73415719 Iustin Pop
    results = []
8760 73415719 Iustin Pop
    for path, target in tgts:
8761 73415719 Iustin Pop
      for tag in target.GetTags():
8762 73415719 Iustin Pop
        if self.re.search(tag):
8763 73415719 Iustin Pop
          results.append((path, tag))
8764 73415719 Iustin Pop
    return results
8765 73415719 Iustin Pop
8766 73415719 Iustin Pop
8767 f27302fa Iustin Pop
class LUAddTags(TagsLU):
8768 5c947f38 Iustin Pop
  """Sets a tag on a given object.
8769 5c947f38 Iustin Pop

8770 5c947f38 Iustin Pop
  """
8771 f27302fa Iustin Pop
  _OP_REQP = ["kind", "name", "tags"]
8772 8646adce Guido Trotter
  REQ_BGL = False
8773 5c947f38 Iustin Pop
8774 5c947f38 Iustin Pop
  def CheckPrereq(self):
8775 5c947f38 Iustin Pop
    """Check prerequisites.
8776 5c947f38 Iustin Pop

8777 5c947f38 Iustin Pop
    This checks the type and length of the tag name and value.
8778 5c947f38 Iustin Pop

8779 5c947f38 Iustin Pop
    """
8780 5c947f38 Iustin Pop
    TagsLU.CheckPrereq(self)
8781 f27302fa Iustin Pop
    for tag in self.op.tags:
8782 f27302fa Iustin Pop
      objects.TaggableObject.ValidateTag(tag)
8783 5c947f38 Iustin Pop
8784 5c947f38 Iustin Pop
  def Exec(self, feedback_fn):
8785 5c947f38 Iustin Pop
    """Sets the tag.
8786 5c947f38 Iustin Pop

8787 5c947f38 Iustin Pop
    """
8788 5c947f38 Iustin Pop
    try:
8789 f27302fa Iustin Pop
      for tag in self.op.tags:
8790 f27302fa Iustin Pop
        self.target.AddTag(tag)
8791 5c947f38 Iustin Pop
    except errors.TagError, err:
8792 3ecf6786 Iustin Pop
      raise errors.OpExecError("Error while setting tag: %s" % str(err))
8793 159d4ec6 Iustin Pop
    self.cfg.Update(self.target, feedback_fn)
8794 5c947f38 Iustin Pop
8795 5c947f38 Iustin Pop
8796 f27302fa Iustin Pop
class LUDelTags(TagsLU):
8797 f27302fa Iustin Pop
  """Delete a list of tags from a given object.
8798 5c947f38 Iustin Pop

8799 5c947f38 Iustin Pop
  """
8800 f27302fa Iustin Pop
  _OP_REQP = ["kind", "name", "tags"]
8801 8646adce Guido Trotter
  REQ_BGL = False
8802 5c947f38 Iustin Pop
8803 5c947f38 Iustin Pop
  def CheckPrereq(self):
8804 5c947f38 Iustin Pop
    """Check prerequisites.
8805 5c947f38 Iustin Pop

8806 5c947f38 Iustin Pop
    This checks that we have the given tag.
8807 5c947f38 Iustin Pop

8808 5c947f38 Iustin Pop
    """
8809 5c947f38 Iustin Pop
    TagsLU.CheckPrereq(self)
8810 f27302fa Iustin Pop
    for tag in self.op.tags:
8811 f27302fa Iustin Pop
      objects.TaggableObject.ValidateTag(tag)
8812 f27302fa Iustin Pop
    del_tags = frozenset(self.op.tags)
8813 f27302fa Iustin Pop
    cur_tags = self.target.GetTags()
8814 f27302fa Iustin Pop
    if not del_tags <= cur_tags:
8815 f27302fa Iustin Pop
      diff_tags = del_tags - cur_tags
8816 f27302fa Iustin Pop
      diff_names = ["'%s'" % tag for tag in diff_tags]
8817 f27302fa Iustin Pop
      diff_names.sort()
8818 f27302fa Iustin Pop
      raise errors.OpPrereqError("Tag(s) %s not found" %
8819 5c983ee5 Iustin Pop
                                 (",".join(diff_names)), errors.ECODE_NOENT)
8820 5c947f38 Iustin Pop
8821 5c947f38 Iustin Pop
  def Exec(self, feedback_fn):
8822 5c947f38 Iustin Pop
    """Remove the tag from the object.
8823 5c947f38 Iustin Pop

8824 5c947f38 Iustin Pop
    """
8825 f27302fa Iustin Pop
    for tag in self.op.tags:
8826 f27302fa Iustin Pop
      self.target.RemoveTag(tag)
8827 159d4ec6 Iustin Pop
    self.cfg.Update(self.target, feedback_fn)
8828 06009e27 Iustin Pop
8829 0eed6e61 Guido Trotter
8830 06009e27 Iustin Pop
class LUTestDelay(NoHooksLU):
8831 06009e27 Iustin Pop
  """Sleep for a specified amount of time.
8832 06009e27 Iustin Pop

8833 0b097284 Guido Trotter
  This LU sleeps on the master and/or nodes for a specified amount of
8834 06009e27 Iustin Pop
  time.
8835 06009e27 Iustin Pop

8836 06009e27 Iustin Pop
  """
8837 06009e27 Iustin Pop
  _OP_REQP = ["duration", "on_master", "on_nodes"]
8838 fbe9022f Guido Trotter
  REQ_BGL = False
8839 06009e27 Iustin Pop
8840 fbe9022f Guido Trotter
  def ExpandNames(self):
8841 fbe9022f Guido Trotter
    """Expand names and set required locks.
8842 06009e27 Iustin Pop

8843 fbe9022f Guido Trotter
    This expands the node list, if any.
8844 06009e27 Iustin Pop

8845 06009e27 Iustin Pop
    """
8846 fbe9022f Guido Trotter
    self.needed_locks = {}
8847 06009e27 Iustin Pop
    if self.op.on_nodes:
8848 fbe9022f Guido Trotter
      # _GetWantedNodes can be used here, but is not always appropriate to use
8849 fbe9022f Guido Trotter
      # this way in ExpandNames. Check LogicalUnit.ExpandNames docstring for
8850 fbe9022f Guido Trotter
      # more information.
8851 06009e27 Iustin Pop
      self.op.on_nodes = _GetWantedNodes(self, self.op.on_nodes)
8852 fbe9022f Guido Trotter
      self.needed_locks[locking.LEVEL_NODE] = self.op.on_nodes
8853 fbe9022f Guido Trotter
8854 fbe9022f Guido Trotter
  def CheckPrereq(self):
8855 fbe9022f Guido Trotter
    """Check prerequisites.
8856 fbe9022f Guido Trotter

8857 fbe9022f Guido Trotter
    """
8858 06009e27 Iustin Pop
8859 06009e27 Iustin Pop
  def Exec(self, feedback_fn):
8860 06009e27 Iustin Pop
    """Do the actual sleep.
8861 06009e27 Iustin Pop

8862 06009e27 Iustin Pop
    """
8863 06009e27 Iustin Pop
    if self.op.on_master:
8864 06009e27 Iustin Pop
      if not utils.TestDelay(self.op.duration):
8865 06009e27 Iustin Pop
        raise errors.OpExecError("Error during master delay test")
8866 06009e27 Iustin Pop
    if self.op.on_nodes:
8867 72737a7f Iustin Pop
      result = self.rpc.call_test_delay(self.op.on_nodes, self.op.duration)
8868 06009e27 Iustin Pop
      for node, node_result in result.items():
8869 4c4e4e1e Iustin Pop
        node_result.Raise("Failure during rpc call to node %s" % node)
8870 d61df03e Iustin Pop
8871 d61df03e Iustin Pop
8872 d1c2dd75 Iustin Pop
class IAllocator(object):
8873 d1c2dd75 Iustin Pop
  """IAllocator framework.
8874 d61df03e Iustin Pop

8875 d1c2dd75 Iustin Pop
  An IAllocator instance has three sets of attributes:
8876 d6a02168 Michael Hanselmann
    - cfg that is needed to query the cluster
8877 d1c2dd75 Iustin Pop
    - input data (all members of the _KEYS class attribute are required)
8878 d1c2dd75 Iustin Pop
    - four buffer attributes (in|out_data|text), that represent the
8879 d1c2dd75 Iustin Pop
      input (to the external script) in text and data structure format,
8880 d1c2dd75 Iustin Pop
      and the output from it, again in two formats
8881 d1c2dd75 Iustin Pop
    - the result variables from the script (success, info, nodes) for
8882 d1c2dd75 Iustin Pop
      easy usage
8883 d61df03e Iustin Pop

8884 d61df03e Iustin Pop
  """
8885 7260cfbe Iustin Pop
  # pylint: disable-msg=R0902
8886 7260cfbe Iustin Pop
  # lots of instance attributes
8887 29859cb7 Iustin Pop
  _ALLO_KEYS = [
8888 8d3f86a0 Iustin Pop
    "name", "mem_size", "disks", "disk_template",
8889 8cc7e742 Guido Trotter
    "os", "tags", "nics", "vcpus", "hypervisor",
8890 d1c2dd75 Iustin Pop
    ]
8891 29859cb7 Iustin Pop
  _RELO_KEYS = [
8892 8d3f86a0 Iustin Pop
    "name", "relocate_from",
8893 29859cb7 Iustin Pop
    ]
8894 7f60a422 Iustin Pop
  _EVAC_KEYS = [
8895 7f60a422 Iustin Pop
    "evac_nodes",
8896 7f60a422 Iustin Pop
    ]
8897 d1c2dd75 Iustin Pop
8898 8d3f86a0 Iustin Pop
  def __init__(self, cfg, rpc, mode, **kwargs):
8899 923ddac0 Michael Hanselmann
    self.cfg = cfg
8900 923ddac0 Michael Hanselmann
    self.rpc = rpc
8901 d1c2dd75 Iustin Pop
    # init buffer variables
8902 d1c2dd75 Iustin Pop
    self.in_text = self.out_text = self.in_data = self.out_data = None
8903 d1c2dd75 Iustin Pop
    # init all input fields so that pylint is happy
8904 29859cb7 Iustin Pop
    self.mode = mode
8905 d1c2dd75 Iustin Pop
    self.mem_size = self.disks = self.disk_template = None
8906 d1c2dd75 Iustin Pop
    self.os = self.tags = self.nics = self.vcpus = None
8907 a0add446 Iustin Pop
    self.hypervisor = None
8908 29859cb7 Iustin Pop
    self.relocate_from = None
8909 8d3f86a0 Iustin Pop
    self.name = None
8910 7f60a422 Iustin Pop
    self.evac_nodes = None
8911 27579978 Iustin Pop
    # computed fields
8912 27579978 Iustin Pop
    self.required_nodes = None
8913 d1c2dd75 Iustin Pop
    # init result fields
8914 680f0a89 Iustin Pop
    self.success = self.info = self.result = None
8915 29859cb7 Iustin Pop
    if self.mode == constants.IALLOCATOR_MODE_ALLOC:
8916 29859cb7 Iustin Pop
      keyset = self._ALLO_KEYS
8917 9757cc90 Iustin Pop
      fn = self._AddNewInstance
8918 29859cb7 Iustin Pop
    elif self.mode == constants.IALLOCATOR_MODE_RELOC:
8919 29859cb7 Iustin Pop
      keyset = self._RELO_KEYS
8920 9757cc90 Iustin Pop
      fn = self._AddRelocateInstance
8921 7f60a422 Iustin Pop
    elif self.mode == constants.IALLOCATOR_MODE_MEVAC:
8922 7f60a422 Iustin Pop
      keyset = self._EVAC_KEYS
8923 7f60a422 Iustin Pop
      fn = self._AddEvacuateNodes
8924 29859cb7 Iustin Pop
    else:
8925 29859cb7 Iustin Pop
      raise errors.ProgrammerError("Unknown mode '%s' passed to the"
8926 29859cb7 Iustin Pop
                                   " IAllocator" % self.mode)
8927 d1c2dd75 Iustin Pop
    for key in kwargs:
8928 29859cb7 Iustin Pop
      if key not in keyset:
8929 d1c2dd75 Iustin Pop
        raise errors.ProgrammerError("Invalid input parameter '%s' to"
8930 d1c2dd75 Iustin Pop
                                     " IAllocator" % key)
8931 d1c2dd75 Iustin Pop
      setattr(self, key, kwargs[key])
8932 7f60a422 Iustin Pop
8933 29859cb7 Iustin Pop
    for key in keyset:
8934 d1c2dd75 Iustin Pop
      if key not in kwargs:
8935 d1c2dd75 Iustin Pop
        raise errors.ProgrammerError("Missing input parameter '%s' to"
8936 d1c2dd75 Iustin Pop
                                     " IAllocator" % key)
8937 9757cc90 Iustin Pop
    self._BuildInputData(fn)
8938 d1c2dd75 Iustin Pop
8939 d1c2dd75 Iustin Pop
  def _ComputeClusterData(self):
8940 d1c2dd75 Iustin Pop
    """Compute the generic allocator input data.
8941 d1c2dd75 Iustin Pop

8942 d1c2dd75 Iustin Pop
    This is the data that is independent of the actual operation.
8943 d1c2dd75 Iustin Pop

8944 d1c2dd75 Iustin Pop
    """
8945 923ddac0 Michael Hanselmann
    cfg = self.cfg
8946 e69d05fd Iustin Pop
    cluster_info = cfg.GetClusterInfo()
8947 d1c2dd75 Iustin Pop
    # cluster data
8948 d1c2dd75 Iustin Pop
    data = {
8949 77031881 Iustin Pop
      "version": constants.IALLOCATOR_VERSION,
8950 72737a7f Iustin Pop
      "cluster_name": cfg.GetClusterName(),
8951 e69d05fd Iustin Pop
      "cluster_tags": list(cluster_info.GetTags()),
8952 1325da74 Iustin Pop
      "enabled_hypervisors": list(cluster_info.enabled_hypervisors),
8953 d1c2dd75 Iustin Pop
      # we don't have job IDs
8954 d61df03e Iustin Pop
      }
8955 b57e9819 Guido Trotter
    iinfo = cfg.GetAllInstancesInfo().values()
8956 b57e9819 Guido Trotter
    i_list = [(inst, cluster_info.FillBE(inst)) for inst in iinfo]
8957 6286519f Iustin Pop
8958 d1c2dd75 Iustin Pop
    # node data
8959 d1c2dd75 Iustin Pop
    node_results = {}
8960 d1c2dd75 Iustin Pop
    node_list = cfg.GetNodeList()
8961 8cc7e742 Guido Trotter
8962 8cc7e742 Guido Trotter
    if self.mode == constants.IALLOCATOR_MODE_ALLOC:
8963 a0add446 Iustin Pop
      hypervisor_name = self.hypervisor
8964 8cc7e742 Guido Trotter
    elif self.mode == constants.IALLOCATOR_MODE_RELOC:
8965 a0add446 Iustin Pop
      hypervisor_name = cfg.GetInstanceInfo(self.name).hypervisor
8966 7f60a422 Iustin Pop
    elif self.mode == constants.IALLOCATOR_MODE_MEVAC:
8967 7f60a422 Iustin Pop
      hypervisor_name = cluster_info.enabled_hypervisors[0]
8968 8cc7e742 Guido Trotter
8969 923ddac0 Michael Hanselmann
    node_data = self.rpc.call_node_info(node_list, cfg.GetVGName(),
8970 923ddac0 Michael Hanselmann
                                        hypervisor_name)
8971 923ddac0 Michael Hanselmann
    node_iinfo = \
8972 923ddac0 Michael Hanselmann
      self.rpc.call_all_instances_info(node_list,
8973 923ddac0 Michael Hanselmann
                                       cluster_info.enabled_hypervisors)
8974 1325da74 Iustin Pop
    for nname, nresult in node_data.items():
8975 1325da74 Iustin Pop
      # first fill in static (config-based) values
8976 d1c2dd75 Iustin Pop
      ninfo = cfg.GetNodeInfo(nname)
8977 d1c2dd75 Iustin Pop
      pnr = {
8978 d1c2dd75 Iustin Pop
        "tags": list(ninfo.GetTags()),
8979 d1c2dd75 Iustin Pop
        "primary_ip": ninfo.primary_ip,
8980 d1c2dd75 Iustin Pop
        "secondary_ip": ninfo.secondary_ip,
8981 fc0fe88c Iustin Pop
        "offline": ninfo.offline,
8982 0b2454b9 Iustin Pop
        "drained": ninfo.drained,
8983 1325da74 Iustin Pop
        "master_candidate": ninfo.master_candidate,
8984 d1c2dd75 Iustin Pop
        }
8985 1325da74 Iustin Pop
8986 0d853843 Iustin Pop
      if not (ninfo.offline or ninfo.drained):
8987 4c4e4e1e Iustin Pop
        nresult.Raise("Can't get data for node %s" % nname)
8988 4c4e4e1e Iustin Pop
        node_iinfo[nname].Raise("Can't get node instance info from node %s" %
8989 4c4e4e1e Iustin Pop
                                nname)
8990 070e998b Iustin Pop
        remote_info = nresult.payload
8991 b142ef15 Iustin Pop
8992 1325da74 Iustin Pop
        for attr in ['memory_total', 'memory_free', 'memory_dom0',
8993 1325da74 Iustin Pop
                     'vg_size', 'vg_free', 'cpu_total']:
8994 1325da74 Iustin Pop
          if attr not in remote_info:
8995 1325da74 Iustin Pop
            raise errors.OpExecError("Node '%s' didn't return attribute"
8996 1325da74 Iustin Pop
                                     " '%s'" % (nname, attr))
8997 070e998b Iustin Pop
          if not isinstance(remote_info[attr], int):
8998 1325da74 Iustin Pop
            raise errors.OpExecError("Node '%s' returned invalid value"
8999 070e998b Iustin Pop
                                     " for '%s': %s" %
9000 070e998b Iustin Pop
                                     (nname, attr, remote_info[attr]))
9001 1325da74 Iustin Pop
        # compute memory used by primary instances
9002 1325da74 Iustin Pop
        i_p_mem = i_p_up_mem = 0
9003 1325da74 Iustin Pop
        for iinfo, beinfo in i_list:
9004 1325da74 Iustin Pop
          if iinfo.primary_node == nname:
9005 1325da74 Iustin Pop
            i_p_mem += beinfo[constants.BE_MEMORY]
9006 2fa74ef4 Iustin Pop
            if iinfo.name not in node_iinfo[nname].payload:
9007 1325da74 Iustin Pop
              i_used_mem = 0
9008 1325da74 Iustin Pop
            else:
9009 2fa74ef4 Iustin Pop
              i_used_mem = int(node_iinfo[nname].payload[iinfo.name]['memory'])
9010 1325da74 Iustin Pop
            i_mem_diff = beinfo[constants.BE_MEMORY] - i_used_mem
9011 1325da74 Iustin Pop
            remote_info['memory_free'] -= max(0, i_mem_diff)
9012 1325da74 Iustin Pop
9013 1325da74 Iustin Pop
            if iinfo.admin_up:
9014 1325da74 Iustin Pop
              i_p_up_mem += beinfo[constants.BE_MEMORY]
9015 1325da74 Iustin Pop
9016 1325da74 Iustin Pop
        # compute memory used by instances
9017 1325da74 Iustin Pop
        pnr_dyn = {
9018 1325da74 Iustin Pop
          "total_memory": remote_info['memory_total'],
9019 1325da74 Iustin Pop
          "reserved_memory": remote_info['memory_dom0'],
9020 1325da74 Iustin Pop
          "free_memory": remote_info['memory_free'],
9021 1325da74 Iustin Pop
          "total_disk": remote_info['vg_size'],
9022 1325da74 Iustin Pop
          "free_disk": remote_info['vg_free'],
9023 1325da74 Iustin Pop
          "total_cpus": remote_info['cpu_total'],
9024 1325da74 Iustin Pop
          "i_pri_memory": i_p_mem,
9025 1325da74 Iustin Pop
          "i_pri_up_memory": i_p_up_mem,
9026 1325da74 Iustin Pop
          }
9027 1325da74 Iustin Pop
        pnr.update(pnr_dyn)
9028 1325da74 Iustin Pop
9029 d1c2dd75 Iustin Pop
      node_results[nname] = pnr
9030 d1c2dd75 Iustin Pop
    data["nodes"] = node_results
9031 d1c2dd75 Iustin Pop
9032 d1c2dd75 Iustin Pop
    # instance data
9033 d1c2dd75 Iustin Pop
    instance_data = {}
9034 338e51e8 Iustin Pop
    for iinfo, beinfo in i_list:
9035 a9fe7e8f Guido Trotter
      nic_data = []
9036 a9fe7e8f Guido Trotter
      for nic in iinfo.nics:
9037 a9fe7e8f Guido Trotter
        filled_params = objects.FillDict(
9038 a9fe7e8f Guido Trotter
            cluster_info.nicparams[constants.PP_DEFAULT],
9039 a9fe7e8f Guido Trotter
            nic.nicparams)
9040 a9fe7e8f Guido Trotter
        nic_dict = {"mac": nic.mac,
9041 a9fe7e8f Guido Trotter
                    "ip": nic.ip,
9042 a9fe7e8f Guido Trotter
                    "mode": filled_params[constants.NIC_MODE],
9043 a9fe7e8f Guido Trotter
                    "link": filled_params[constants.NIC_LINK],
9044 a9fe7e8f Guido Trotter
                   }
9045 a9fe7e8f Guido Trotter
        if filled_params[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
9046 a9fe7e8f Guido Trotter
          nic_dict["bridge"] = filled_params[constants.NIC_LINK]
9047 a9fe7e8f Guido Trotter
        nic_data.append(nic_dict)
9048 d1c2dd75 Iustin Pop
      pir = {
9049 d1c2dd75 Iustin Pop
        "tags": list(iinfo.GetTags()),
9050 1325da74 Iustin Pop
        "admin_up": iinfo.admin_up,
9051 338e51e8 Iustin Pop
        "vcpus": beinfo[constants.BE_VCPUS],
9052 338e51e8 Iustin Pop
        "memory": beinfo[constants.BE_MEMORY],
9053 d1c2dd75 Iustin Pop
        "os": iinfo.os,
9054 1325da74 Iustin Pop
        "nodes": [iinfo.primary_node] + list(iinfo.secondary_nodes),
9055 d1c2dd75 Iustin Pop
        "nics": nic_data,
9056 1325da74 Iustin Pop
        "disks": [{"size": dsk.size, "mode": dsk.mode} for dsk in iinfo.disks],
9057 d1c2dd75 Iustin Pop
        "disk_template": iinfo.disk_template,
9058 e69d05fd Iustin Pop
        "hypervisor": iinfo.hypervisor,
9059 d1c2dd75 Iustin Pop
        }
9060 88ae4f85 Iustin Pop
      pir["disk_space_total"] = _ComputeDiskSize(iinfo.disk_template,
9061 88ae4f85 Iustin Pop
                                                 pir["disks"])
9062 768f0a80 Iustin Pop
      instance_data[iinfo.name] = pir
9063 d61df03e Iustin Pop
9064 d1c2dd75 Iustin Pop
    data["instances"] = instance_data
9065 d61df03e Iustin Pop
9066 d1c2dd75 Iustin Pop
    self.in_data = data
9067 d61df03e Iustin Pop
9068 d1c2dd75 Iustin Pop
  def _AddNewInstance(self):
9069 d1c2dd75 Iustin Pop
    """Add new instance data to allocator structure.
9070 d61df03e Iustin Pop

9071 d1c2dd75 Iustin Pop
    This in combination with _AllocatorGetClusterData will create the
9072 d1c2dd75 Iustin Pop
    correct structure needed as input for the allocator.
9073 d61df03e Iustin Pop

9074 d1c2dd75 Iustin Pop
    The checks for the completeness of the opcode must have already been
9075 d1c2dd75 Iustin Pop
    done.
9076 d61df03e Iustin Pop

9077 d1c2dd75 Iustin Pop
    """
9078 dafc7302 Guido Trotter
    disk_space = _ComputeDiskSize(self.disk_template, self.disks)
9079 d1c2dd75 Iustin Pop
9080 27579978 Iustin Pop
    if self.disk_template in constants.DTS_NET_MIRROR:
9081 27579978 Iustin Pop
      self.required_nodes = 2
9082 27579978 Iustin Pop
    else:
9083 27579978 Iustin Pop
      self.required_nodes = 1
9084 d1c2dd75 Iustin Pop
    request = {
9085 d1c2dd75 Iustin Pop
      "name": self.name,
9086 d1c2dd75 Iustin Pop
      "disk_template": self.disk_template,
9087 d1c2dd75 Iustin Pop
      "tags": self.tags,
9088 d1c2dd75 Iustin Pop
      "os": self.os,
9089 d1c2dd75 Iustin Pop
      "vcpus": self.vcpus,
9090 d1c2dd75 Iustin Pop
      "memory": self.mem_size,
9091 d1c2dd75 Iustin Pop
      "disks": self.disks,
9092 d1c2dd75 Iustin Pop
      "disk_space_total": disk_space,
9093 d1c2dd75 Iustin Pop
      "nics": self.nics,
9094 27579978 Iustin Pop
      "required_nodes": self.required_nodes,
9095 d1c2dd75 Iustin Pop
      }
9096 9757cc90 Iustin Pop
    return request
9097 298fe380 Iustin Pop
9098 d1c2dd75 Iustin Pop
  def _AddRelocateInstance(self):
9099 d1c2dd75 Iustin Pop
    """Add relocate instance data to allocator structure.
9100 298fe380 Iustin Pop

9101 d1c2dd75 Iustin Pop
    This in combination with _IAllocatorGetClusterData will create the
9102 d1c2dd75 Iustin Pop
    correct structure needed as input for the allocator.
9103 d61df03e Iustin Pop

9104 d1c2dd75 Iustin Pop
    The checks for the completeness of the opcode must have already been
9105 d1c2dd75 Iustin Pop
    done.
9106 d61df03e Iustin Pop

9107 d1c2dd75 Iustin Pop
    """
9108 923ddac0 Michael Hanselmann
    instance = self.cfg.GetInstanceInfo(self.name)
9109 27579978 Iustin Pop
    if instance is None:
9110 27579978 Iustin Pop
      raise errors.ProgrammerError("Unknown instance '%s' passed to"
9111 27579978 Iustin Pop
                                   " IAllocator" % self.name)
9112 27579978 Iustin Pop
9113 27579978 Iustin Pop
    if instance.disk_template not in constants.DTS_NET_MIRROR:
9114 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Can't relocate non-mirrored instances",
9115 5c983ee5 Iustin Pop
                                 errors.ECODE_INVAL)
9116 27579978 Iustin Pop
9117 2a139bb0 Iustin Pop
    if len(instance.secondary_nodes) != 1:
9118 5c983ee5 Iustin Pop
      raise errors.OpPrereqError("Instance has not exactly one secondary node",
9119 5c983ee5 Iustin Pop
                                 errors.ECODE_STATE)
9120 2a139bb0 Iustin Pop
9121 27579978 Iustin Pop
    self.required_nodes = 1
9122 dafc7302 Guido Trotter
    disk_sizes = [{'size': disk.size} for disk in instance.disks]
9123 dafc7302 Guido Trotter
    disk_space = _ComputeDiskSize(instance.disk_template, disk_sizes)
9124 27579978 Iustin Pop
9125 d1c2dd75 Iustin Pop
    request = {
9126 d1c2dd75 Iustin Pop
      "name": self.name,
9127 27579978 Iustin Pop
      "disk_space_total": disk_space,
9128 27579978 Iustin Pop
      "required_nodes": self.required_nodes,
9129 29859cb7 Iustin Pop
      "relocate_from": self.relocate_from,
9130 d1c2dd75 Iustin Pop
      }
9131 9757cc90 Iustin Pop
    return request
9132 d61df03e Iustin Pop
9133 7f60a422 Iustin Pop
  def _AddEvacuateNodes(self):
9134 7f60a422 Iustin Pop
    """Add evacuate nodes data to allocator structure.
9135 7f60a422 Iustin Pop

9136 7f60a422 Iustin Pop
    """
9137 7f60a422 Iustin Pop
    request = {
9138 7f60a422 Iustin Pop
      "evac_nodes": self.evac_nodes
9139 7f60a422 Iustin Pop
      }
9140 7f60a422 Iustin Pop
    return request
9141 7f60a422 Iustin Pop
9142 9757cc90 Iustin Pop
  def _BuildInputData(self, fn):
9143 d1c2dd75 Iustin Pop
    """Build input data structures.
9144 d61df03e Iustin Pop

9145 d1c2dd75 Iustin Pop
    """
9146 d1c2dd75 Iustin Pop
    self._ComputeClusterData()
9147 d61df03e Iustin Pop
9148 9757cc90 Iustin Pop
    request = fn()
9149 9757cc90 Iustin Pop
    request["type"] = self.mode
9150 9757cc90 Iustin Pop
    self.in_data["request"] = request
9151 d61df03e Iustin Pop
9152 d1c2dd75 Iustin Pop
    self.in_text = serializer.Dump(self.in_data)
9153 d61df03e Iustin Pop
9154 72737a7f Iustin Pop
  def Run(self, name, validate=True, call_fn=None):
9155 d1c2dd75 Iustin Pop
    """Run an instance allocator and return the results.
9156 298fe380 Iustin Pop

9157 d1c2dd75 Iustin Pop
    """
9158 72737a7f Iustin Pop
    if call_fn is None:
9159 923ddac0 Michael Hanselmann
      call_fn = self.rpc.call_iallocator_runner
9160 298fe380 Iustin Pop
9161 923ddac0 Michael Hanselmann
    result = call_fn(self.cfg.GetMasterNode(), name, self.in_text)
9162 4c4e4e1e Iustin Pop
    result.Raise("Failure while running the iallocator script")
9163 8d528b7c Iustin Pop
9164 87f5c298 Iustin Pop
    self.out_text = result.payload
9165 d1c2dd75 Iustin Pop
    if validate:
9166 d1c2dd75 Iustin Pop
      self._ValidateResult()
9167 298fe380 Iustin Pop
9168 d1c2dd75 Iustin Pop
  def _ValidateResult(self):
9169 d1c2dd75 Iustin Pop
    """Process the allocator results.
9170 538475ca Iustin Pop

9171 d1c2dd75 Iustin Pop
    This will process and if successful save the result in
9172 d1c2dd75 Iustin Pop
    self.out_data and the other parameters.
9173 538475ca Iustin Pop

9174 d1c2dd75 Iustin Pop
    """
9175 d1c2dd75 Iustin Pop
    try:
9176 d1c2dd75 Iustin Pop
      rdict = serializer.Load(self.out_text)
9177 d1c2dd75 Iustin Pop
    except Exception, err:
9178 d1c2dd75 Iustin Pop
      raise errors.OpExecError("Can't parse iallocator results: %s" % str(err))
9179 d1c2dd75 Iustin Pop
9180 d1c2dd75 Iustin Pop
    if not isinstance(rdict, dict):
9181 d1c2dd75 Iustin Pop
      raise errors.OpExecError("Can't parse iallocator results: not a dict")
9182 538475ca Iustin Pop
9183 680f0a89 Iustin Pop
    # TODO: remove backwards compatiblity in later versions
9184 680f0a89 Iustin Pop
    if "nodes" in rdict and "result" not in rdict:
9185 680f0a89 Iustin Pop
      rdict["result"] = rdict["nodes"]
9186 680f0a89 Iustin Pop
      del rdict["nodes"]
9187 680f0a89 Iustin Pop
9188 680f0a89 Iustin Pop
    for key in "success", "info", "result":
9189 d1c2dd75 Iustin Pop
      if key not in rdict:
9190 d1c2dd75 Iustin Pop
        raise errors.OpExecError("Can't parse iallocator results:"
9191 d1c2dd75 Iustin Pop
                                 " missing key '%s'" % key)
9192 d1c2dd75 Iustin Pop
      setattr(self, key, rdict[key])
9193 538475ca Iustin Pop
9194 680f0a89 Iustin Pop
    if not isinstance(rdict["result"], list):
9195 680f0a89 Iustin Pop
      raise errors.OpExecError("Can't parse iallocator results: 'result' key"
9196 d1c2dd75 Iustin Pop
                               " is not a list")
9197 d1c2dd75 Iustin Pop
    self.out_data = rdict
9198 538475ca Iustin Pop
9199 538475ca Iustin Pop
9200 d61df03e Iustin Pop
class LUTestAllocator(NoHooksLU):
9201 d61df03e Iustin Pop
  """Run allocator tests.
9202 d61df03e Iustin Pop

9203 d61df03e Iustin Pop
  This LU runs the allocator tests
9204 d61df03e Iustin Pop

9205 d61df03e Iustin Pop
  """
9206 d61df03e Iustin Pop
  _OP_REQP = ["direction", "mode", "name"]
9207 d61df03e Iustin Pop
9208 d61df03e Iustin Pop
  def CheckPrereq(self):
9209 d61df03e Iustin Pop
    """Check prerequisites.
9210 d61df03e Iustin Pop

9211 d61df03e Iustin Pop
    This checks the opcode parameters depending on the director and mode test.
9212 d61df03e Iustin Pop

9213 d61df03e Iustin Pop
    """
9214 298fe380 Iustin Pop
    if self.op.mode == constants.IALLOCATOR_MODE_ALLOC:
9215 d61df03e Iustin Pop
      for attr in ["name", "mem_size", "disks", "disk_template",
9216 d61df03e Iustin Pop
                   "os", "tags", "nics", "vcpus"]:
9217 d61df03e Iustin Pop
        if not hasattr(self.op, attr):
9218 d61df03e Iustin Pop
          raise errors.OpPrereqError("Missing attribute '%s' on opcode input" %
9219 5c983ee5 Iustin Pop
                                     attr, errors.ECODE_INVAL)
9220 d61df03e Iustin Pop
      iname = self.cfg.ExpandInstanceName(self.op.name)
9221 d61df03e Iustin Pop
      if iname is not None:
9222 d61df03e Iustin Pop
        raise errors.OpPrereqError("Instance '%s' already in the cluster" %
9223 5c983ee5 Iustin Pop
                                   iname, errors.ECODE_EXISTS)
9224 d61df03e Iustin Pop
      if not isinstance(self.op.nics, list):
9225 5c983ee5 Iustin Pop
        raise errors.OpPrereqError("Invalid parameter 'nics'",
9226 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
9227 d61df03e Iustin Pop
      for row in self.op.nics:
9228 d61df03e Iustin Pop
        if (not isinstance(row, dict) or
9229 d61df03e Iustin Pop
            "mac" not in row or
9230 d61df03e Iustin Pop
            "ip" not in row or
9231 d61df03e Iustin Pop
            "bridge" not in row):
9232 5c983ee5 Iustin Pop
          raise errors.OpPrereqError("Invalid contents of the 'nics'"
9233 5c983ee5 Iustin Pop
                                     " parameter", errors.ECODE_INVAL)
9234 d61df03e Iustin Pop
      if not isinstance(self.op.disks, list):
9235 5c983ee5 Iustin Pop
        raise errors.OpPrereqError("Invalid parameter 'disks'",
9236 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
9237 d61df03e Iustin Pop
      for row in self.op.disks:
9238 d61df03e Iustin Pop
        if (not isinstance(row, dict) or
9239 d61df03e Iustin Pop
            "size" not in row or
9240 d61df03e Iustin Pop
            not isinstance(row["size"], int) or
9241 d61df03e Iustin Pop
            "mode" not in row or
9242 d61df03e Iustin Pop
            row["mode"] not in ['r', 'w']):
9243 5c983ee5 Iustin Pop
          raise errors.OpPrereqError("Invalid contents of the 'disks'"
9244 5c983ee5 Iustin Pop
                                     " parameter", errors.ECODE_INVAL)
9245 8901997e Iustin Pop
      if not hasattr(self.op, "hypervisor") or self.op.hypervisor is None:
9246 8cc7e742 Guido Trotter
        self.op.hypervisor = self.cfg.GetHypervisorType()
9247 298fe380 Iustin Pop
    elif self.op.mode == constants.IALLOCATOR_MODE_RELOC:
9248 d61df03e Iustin Pop
      if not hasattr(self.op, "name"):
9249 5c983ee5 Iustin Pop
        raise errors.OpPrereqError("Missing attribute 'name' on opcode input",
9250 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
9251 cf26a87a Iustin Pop
      fname = _ExpandInstanceName(self.cfg, self.op.name)
9252 d61df03e Iustin Pop
      self.op.name = fname
9253 29859cb7 Iustin Pop
      self.relocate_from = self.cfg.GetInstanceInfo(fname).secondary_nodes
9254 823a72bc Iustin Pop
    elif self.op.mode == constants.IALLOCATOR_MODE_MEVAC:
9255 823a72bc Iustin Pop
      if not hasattr(self.op, "evac_nodes"):
9256 823a72bc Iustin Pop
        raise errors.OpPrereqError("Missing attribute 'evac_nodes' on"
9257 823a72bc Iustin Pop
                                   " opcode input", errors.ECODE_INVAL)
9258 d61df03e Iustin Pop
    else:
9259 d61df03e Iustin Pop
      raise errors.OpPrereqError("Invalid test allocator mode '%s'" %
9260 5c983ee5 Iustin Pop
                                 self.op.mode, errors.ECODE_INVAL)
9261 d61df03e Iustin Pop
9262 298fe380 Iustin Pop
    if self.op.direction == constants.IALLOCATOR_DIR_OUT:
9263 298fe380 Iustin Pop
      if not hasattr(self.op, "allocator") or self.op.allocator is None:
9264 5c983ee5 Iustin Pop
        raise errors.OpPrereqError("Missing allocator name",
9265 5c983ee5 Iustin Pop
                                   errors.ECODE_INVAL)
9266 298fe380 Iustin Pop
    elif self.op.direction != constants.IALLOCATOR_DIR_IN:
9267 d61df03e Iustin Pop
      raise errors.OpPrereqError("Wrong allocator test '%s'" %
9268 5c983ee5 Iustin Pop
                                 self.op.direction, errors.ECODE_INVAL)
9269 d61df03e Iustin Pop
9270 d61df03e Iustin Pop
  def Exec(self, feedback_fn):
9271 d61df03e Iustin Pop
    """Run the allocator test.
9272 d61df03e Iustin Pop

9273 d61df03e Iustin Pop
    """
9274 29859cb7 Iustin Pop
    if self.op.mode == constants.IALLOCATOR_MODE_ALLOC:
9275 923ddac0 Michael Hanselmann
      ial = IAllocator(self.cfg, self.rpc,
9276 29859cb7 Iustin Pop
                       mode=self.op.mode,
9277 29859cb7 Iustin Pop
                       name=self.op.name,
9278 29859cb7 Iustin Pop
                       mem_size=self.op.mem_size,
9279 29859cb7 Iustin Pop
                       disks=self.op.disks,
9280 29859cb7 Iustin Pop
                       disk_template=self.op.disk_template,
9281 29859cb7 Iustin Pop
                       os=self.op.os,
9282 29859cb7 Iustin Pop
                       tags=self.op.tags,
9283 29859cb7 Iustin Pop
                       nics=self.op.nics,
9284 29859cb7 Iustin Pop
                       vcpus=self.op.vcpus,
9285 8cc7e742 Guido Trotter
                       hypervisor=self.op.hypervisor,
9286 29859cb7 Iustin Pop
                       )
9287 823a72bc Iustin Pop
    elif self.op.mode == constants.IALLOCATOR_MODE_RELOC:
9288 923ddac0 Michael Hanselmann
      ial = IAllocator(self.cfg, self.rpc,
9289 29859cb7 Iustin Pop
                       mode=self.op.mode,
9290 29859cb7 Iustin Pop
                       name=self.op.name,
9291 29859cb7 Iustin Pop
                       relocate_from=list(self.relocate_from),
9292 29859cb7 Iustin Pop
                       )
9293 823a72bc Iustin Pop
    elif self.op.mode == constants.IALLOCATOR_MODE_MEVAC:
9294 823a72bc Iustin Pop
      ial = IAllocator(self.cfg, self.rpc,
9295 823a72bc Iustin Pop
                       mode=self.op.mode,
9296 823a72bc Iustin Pop
                       evac_nodes=self.op.evac_nodes)
9297 823a72bc Iustin Pop
    else:
9298 823a72bc Iustin Pop
      raise errors.ProgrammerError("Uncatched mode %s in"
9299 823a72bc Iustin Pop
                                   " LUTestAllocator.Exec", self.op.mode)
9300 d61df03e Iustin Pop
9301 298fe380 Iustin Pop
    if self.op.direction == constants.IALLOCATOR_DIR_IN:
9302 d1c2dd75 Iustin Pop
      result = ial.in_text
9303 298fe380 Iustin Pop
    else:
9304 d1c2dd75 Iustin Pop
      ial.Run(self.op.allocator, validate=False)
9305 d1c2dd75 Iustin Pop
      result = ial.out_text
9306 298fe380 Iustin Pop
    return result