Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (37.8 kB)

1 87e25be1 Thomas Thrainer
#
2 87e25be1 Thomas Thrainer
#
3 87e25be1 Thomas Thrainer
4 87e25be1 Thomas Thrainer
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5 87e25be1 Thomas Thrainer
#
6 87e25be1 Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 87e25be1 Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 87e25be1 Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 87e25be1 Thomas Thrainer
# (at your option) any later version.
10 87e25be1 Thomas Thrainer
#
11 87e25be1 Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 87e25be1 Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 87e25be1 Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 87e25be1 Thomas Thrainer
# General Public License for more details.
15 87e25be1 Thomas Thrainer
#
16 87e25be1 Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 87e25be1 Thomas Thrainer
# along with this program; if not, write to the Free Software
18 87e25be1 Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 87e25be1 Thomas Thrainer
# 02110-1301, USA.
20 87e25be1 Thomas Thrainer
21 87e25be1 Thomas Thrainer
22 87e25be1 Thomas Thrainer
"""Logical units dealing with instance migration an failover."""
23 87e25be1 Thomas Thrainer
24 87e25be1 Thomas Thrainer
import logging
25 87e25be1 Thomas Thrainer
import time
26 87e25be1 Thomas Thrainer
27 87e25be1 Thomas Thrainer
from ganeti import constants
28 87e25be1 Thomas Thrainer
from ganeti import errors
29 87e25be1 Thomas Thrainer
from ganeti import locking
30 87e25be1 Thomas Thrainer
from ganeti.masterd import iallocator
31 87e25be1 Thomas Thrainer
from ganeti import utils
32 87e25be1 Thomas Thrainer
from ganeti.cmdlib.base import LogicalUnit, Tasklet
33 da4a52a3 Thomas Thrainer
from ganeti.cmdlib.common import ExpandInstanceUuidAndName, \
34 1c3231aa Thomas Thrainer
  CheckIAllocatorOrNode, ExpandNodeUuidAndName
35 5eacbcae Thomas Thrainer
from ganeti.cmdlib.instance_storage import CheckDiskConsistency, \
36 5eacbcae Thomas Thrainer
  ExpandCheckDisks, ShutdownInstanceDisks, AssembleInstanceDisks
37 5eacbcae Thomas Thrainer
from ganeti.cmdlib.instance_utils import BuildInstanceHookEnvByObject, \
38 5eacbcae Thomas Thrainer
  CheckTargetNodeIPolicy, ReleaseLocks, CheckNodeNotDrained, \
39 5eacbcae Thomas Thrainer
  CopyLockList, CheckNodeFreeMemory, CheckInstanceBridgesExist
40 87e25be1 Thomas Thrainer
41 87e25be1 Thomas Thrainer
import ganeti.masterd.instance
42 87e25be1 Thomas Thrainer
43 87e25be1 Thomas Thrainer
44 87e25be1 Thomas Thrainer
def _ExpandNamesForMigration(lu):
45 87e25be1 Thomas Thrainer
  """Expands names for use with L{TLMigrateInstance}.
46 87e25be1 Thomas Thrainer

47 87e25be1 Thomas Thrainer
  @type lu: L{LogicalUnit}
48 87e25be1 Thomas Thrainer

49 87e25be1 Thomas Thrainer
  """
50 87e25be1 Thomas Thrainer
  if lu.op.target_node is not None:
51 1c3231aa Thomas Thrainer
    (lu.op.target_node_uuid, lu.op.target_node) = \
52 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(lu.cfg, lu.op.target_node_uuid, lu.op.target_node)
53 87e25be1 Thomas Thrainer
54 87e25be1 Thomas Thrainer
  lu.needed_locks[locking.LEVEL_NODE] = []
55 87e25be1 Thomas Thrainer
  lu.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
56 87e25be1 Thomas Thrainer
57 87e25be1 Thomas Thrainer
  lu.needed_locks[locking.LEVEL_NODE_RES] = []
58 87e25be1 Thomas Thrainer
  lu.recalculate_locks[locking.LEVEL_NODE_RES] = constants.LOCKS_REPLACE
59 87e25be1 Thomas Thrainer
60 87e25be1 Thomas Thrainer
  # The node allocation lock is actually only needed for externally replicated
61 87e25be1 Thomas Thrainer
  # instances (e.g. sharedfile or RBD) and if an iallocator is used.
62 87e25be1 Thomas Thrainer
  lu.needed_locks[locking.LEVEL_NODE_ALLOC] = []
63 87e25be1 Thomas Thrainer
64 87e25be1 Thomas Thrainer
65 87e25be1 Thomas Thrainer
def _DeclareLocksForMigration(lu, level):
66 87e25be1 Thomas Thrainer
  """Declares locks for L{TLMigrateInstance}.
67 87e25be1 Thomas Thrainer

68 87e25be1 Thomas Thrainer
  @type lu: L{LogicalUnit}
69 87e25be1 Thomas Thrainer
  @param level: Lock level
70 87e25be1 Thomas Thrainer

71 87e25be1 Thomas Thrainer
  """
72 87e25be1 Thomas Thrainer
  if level == locking.LEVEL_NODE_ALLOC:
73 87e25be1 Thomas Thrainer
    assert lu.op.instance_name in lu.owned_locks(locking.LEVEL_INSTANCE)
74 87e25be1 Thomas Thrainer
75 da4a52a3 Thomas Thrainer
    instance = lu.cfg.GetInstanceInfo(lu.op.instance_uuid)
76 87e25be1 Thomas Thrainer
77 87e25be1 Thomas Thrainer
    # Node locks are already declared here rather than at LEVEL_NODE as we need
78 87e25be1 Thomas Thrainer
    # the instance object anyway to declare the node allocation lock.
79 87e25be1 Thomas Thrainer
    if instance.disk_template in constants.DTS_EXT_MIRROR:
80 87e25be1 Thomas Thrainer
      if lu.op.target_node is None:
81 87e25be1 Thomas Thrainer
        lu.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
82 87e25be1 Thomas Thrainer
        lu.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
83 87e25be1 Thomas Thrainer
      else:
84 87e25be1 Thomas Thrainer
        lu.needed_locks[locking.LEVEL_NODE] = [instance.primary_node,
85 1c3231aa Thomas Thrainer
                                               lu.op.target_node_uuid]
86 87e25be1 Thomas Thrainer
      del lu.recalculate_locks[locking.LEVEL_NODE]
87 87e25be1 Thomas Thrainer
    else:
88 87e25be1 Thomas Thrainer
      lu._LockInstancesNodes() # pylint: disable=W0212
89 87e25be1 Thomas Thrainer
90 87e25be1 Thomas Thrainer
  elif level == locking.LEVEL_NODE:
91 87e25be1 Thomas Thrainer
    # Node locks are declared together with the node allocation lock
92 87e25be1 Thomas Thrainer
    assert (lu.needed_locks[locking.LEVEL_NODE] or
93 87e25be1 Thomas Thrainer
            lu.needed_locks[locking.LEVEL_NODE] is locking.ALL_SET)
94 87e25be1 Thomas Thrainer
95 87e25be1 Thomas Thrainer
  elif level == locking.LEVEL_NODE_RES:
96 87e25be1 Thomas Thrainer
    # Copy node locks
97 87e25be1 Thomas Thrainer
    lu.needed_locks[locking.LEVEL_NODE_RES] = \
98 5eacbcae Thomas Thrainer
      CopyLockList(lu.needed_locks[locking.LEVEL_NODE])
99 87e25be1 Thomas Thrainer
100 87e25be1 Thomas Thrainer
101 87e25be1 Thomas Thrainer
class LUInstanceFailover(LogicalUnit):
102 87e25be1 Thomas Thrainer
  """Failover an instance.
103 87e25be1 Thomas Thrainer

104 87e25be1 Thomas Thrainer
  """
105 87e25be1 Thomas Thrainer
  HPATH = "instance-failover"
106 87e25be1 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
107 87e25be1 Thomas Thrainer
  REQ_BGL = False
108 87e25be1 Thomas Thrainer
109 87e25be1 Thomas Thrainer
  def CheckArguments(self):
110 87e25be1 Thomas Thrainer
    """Check the arguments.
111 87e25be1 Thomas Thrainer

112 87e25be1 Thomas Thrainer
    """
113 87e25be1 Thomas Thrainer
    self.iallocator = getattr(self.op, "iallocator", None)
114 87e25be1 Thomas Thrainer
    self.target_node = getattr(self.op, "target_node", None)
115 87e25be1 Thomas Thrainer
116 87e25be1 Thomas Thrainer
  def ExpandNames(self):
117 87e25be1 Thomas Thrainer
    self._ExpandAndLockInstance()
118 87e25be1 Thomas Thrainer
    _ExpandNamesForMigration(self)
119 87e25be1 Thomas Thrainer
120 87e25be1 Thomas Thrainer
    self._migrater = \
121 da4a52a3 Thomas Thrainer
      TLMigrateInstance(self, self.op.instance_uuid, self.op.instance_name,
122 804d72eb Thomas Thrainer
                        self.op.cleanup, True, False,
123 804d72eb Thomas Thrainer
                        self.op.ignore_consistency, True,
124 87e25be1 Thomas Thrainer
                        self.op.shutdown_timeout, self.op.ignore_ipolicy)
125 87e25be1 Thomas Thrainer
126 87e25be1 Thomas Thrainer
    self.tasklets = [self._migrater]
127 87e25be1 Thomas Thrainer
128 87e25be1 Thomas Thrainer
  def DeclareLocks(self, level):
129 87e25be1 Thomas Thrainer
    _DeclareLocksForMigration(self, level)
130 87e25be1 Thomas Thrainer
131 87e25be1 Thomas Thrainer
  def BuildHooksEnv(self):
132 87e25be1 Thomas Thrainer
    """Build hooks env.
133 87e25be1 Thomas Thrainer

134 87e25be1 Thomas Thrainer
    This runs on master, primary and secondary nodes of the instance.
135 87e25be1 Thomas Thrainer

136 87e25be1 Thomas Thrainer
    """
137 87e25be1 Thomas Thrainer
    instance = self._migrater.instance
138 1c3231aa Thomas Thrainer
    source_node_uuid = instance.primary_node
139 87e25be1 Thomas Thrainer
    env = {
140 87e25be1 Thomas Thrainer
      "IGNORE_CONSISTENCY": self.op.ignore_consistency,
141 87e25be1 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
142 1c3231aa Thomas Thrainer
      "OLD_PRIMARY": self.cfg.GetNodeName(source_node_uuid),
143 1c3231aa Thomas Thrainer
      "NEW_PRIMARY": self.op.target_node,
144 aa7a5c90 Michele Tartara
      "FAILOVER_CLEANUP": self.op.cleanup,
145 87e25be1 Thomas Thrainer
      }
146 87e25be1 Thomas Thrainer
147 87e25be1 Thomas Thrainer
    if instance.disk_template in constants.DTS_INT_MIRROR:
148 1c3231aa Thomas Thrainer
      env["OLD_SECONDARY"] = self.cfg.GetNodeName(instance.secondary_nodes[0])
149 1c3231aa Thomas Thrainer
      env["NEW_SECONDARY"] = self.cfg.GetNodeName(source_node_uuid)
150 87e25be1 Thomas Thrainer
    else:
151 87e25be1 Thomas Thrainer
      env["OLD_SECONDARY"] = env["NEW_SECONDARY"] = ""
152 87e25be1 Thomas Thrainer
153 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, instance))
154 87e25be1 Thomas Thrainer
155 87e25be1 Thomas Thrainer
    return env
156 87e25be1 Thomas Thrainer
157 87e25be1 Thomas Thrainer
  def BuildHooksNodes(self):
158 87e25be1 Thomas Thrainer
    """Build hooks nodes.
159 87e25be1 Thomas Thrainer

160 87e25be1 Thomas Thrainer
    """
161 87e25be1 Thomas Thrainer
    instance = self._migrater.instance
162 87e25be1 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(instance.secondary_nodes)
163 87e25be1 Thomas Thrainer
    return (nl, nl + [instance.primary_node])
164 87e25be1 Thomas Thrainer
165 87e25be1 Thomas Thrainer
166 87e25be1 Thomas Thrainer
class LUInstanceMigrate(LogicalUnit):
167 87e25be1 Thomas Thrainer
  """Migrate an instance.
168 87e25be1 Thomas Thrainer

169 87e25be1 Thomas Thrainer
  This is migration without shutting down, compared to the failover,
170 87e25be1 Thomas Thrainer
  which is done with shutdown.
171 87e25be1 Thomas Thrainer

172 87e25be1 Thomas Thrainer
  """
173 87e25be1 Thomas Thrainer
  HPATH = "instance-migrate"
174 87e25be1 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
175 87e25be1 Thomas Thrainer
  REQ_BGL = False
176 87e25be1 Thomas Thrainer
177 87e25be1 Thomas Thrainer
  def ExpandNames(self):
178 87e25be1 Thomas Thrainer
    self._ExpandAndLockInstance()
179 87e25be1 Thomas Thrainer
    _ExpandNamesForMigration(self)
180 87e25be1 Thomas Thrainer
181 87e25be1 Thomas Thrainer
    self._migrater = \
182 da4a52a3 Thomas Thrainer
      TLMigrateInstance(self, self.op.instance_uuid, self.op.instance_name,
183 da4a52a3 Thomas Thrainer
                        self.op.cleanup, False, self.op.allow_failover, False,
184 87e25be1 Thomas Thrainer
                        self.op.allow_runtime_changes,
185 87e25be1 Thomas Thrainer
                        constants.DEFAULT_SHUTDOWN_TIMEOUT,
186 87e25be1 Thomas Thrainer
                        self.op.ignore_ipolicy)
187 87e25be1 Thomas Thrainer
188 87e25be1 Thomas Thrainer
    self.tasklets = [self._migrater]
189 87e25be1 Thomas Thrainer
190 87e25be1 Thomas Thrainer
  def DeclareLocks(self, level):
191 87e25be1 Thomas Thrainer
    _DeclareLocksForMigration(self, level)
192 87e25be1 Thomas Thrainer
193 87e25be1 Thomas Thrainer
  def BuildHooksEnv(self):
194 87e25be1 Thomas Thrainer
    """Build hooks env.
195 87e25be1 Thomas Thrainer

196 87e25be1 Thomas Thrainer
    This runs on master, primary and secondary nodes of the instance.
197 87e25be1 Thomas Thrainer

198 87e25be1 Thomas Thrainer
    """
199 87e25be1 Thomas Thrainer
    instance = self._migrater.instance
200 1c3231aa Thomas Thrainer
    source_node_uuid = instance.primary_node
201 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, instance)
202 87e25be1 Thomas Thrainer
    env.update({
203 87e25be1 Thomas Thrainer
      "MIGRATE_LIVE": self._migrater.live,
204 87e25be1 Thomas Thrainer
      "MIGRATE_CLEANUP": self.op.cleanup,
205 1c3231aa Thomas Thrainer
      "OLD_PRIMARY": self.cfg.GetNodeName(source_node_uuid),
206 1c3231aa Thomas Thrainer
      "NEW_PRIMARY": self.op.target_node,
207 87e25be1 Thomas Thrainer
      "ALLOW_RUNTIME_CHANGES": self.op.allow_runtime_changes,
208 87e25be1 Thomas Thrainer
      })
209 87e25be1 Thomas Thrainer
210 87e25be1 Thomas Thrainer
    if instance.disk_template in constants.DTS_INT_MIRROR:
211 1c3231aa Thomas Thrainer
      env["OLD_SECONDARY"] = self.cfg.GetNodeName(instance.secondary_nodes[0])
212 1c3231aa Thomas Thrainer
      env["NEW_SECONDARY"] = self.cfg.GetNodeName(source_node_uuid)
213 87e25be1 Thomas Thrainer
    else:
214 87e25be1 Thomas Thrainer
      env["OLD_SECONDARY"] = env["NEW_SECONDARY"] = None
215 87e25be1 Thomas Thrainer
216 87e25be1 Thomas Thrainer
    return env
217 87e25be1 Thomas Thrainer
218 87e25be1 Thomas Thrainer
  def BuildHooksNodes(self):
219 87e25be1 Thomas Thrainer
    """Build hooks nodes.
220 87e25be1 Thomas Thrainer

221 87e25be1 Thomas Thrainer
    """
222 87e25be1 Thomas Thrainer
    instance = self._migrater.instance
223 1c3231aa Thomas Thrainer
    snode_uuids = list(instance.secondary_nodes)
224 1c3231aa Thomas Thrainer
    nl = [self.cfg.GetMasterNode(), instance.primary_node] + snode_uuids
225 87e25be1 Thomas Thrainer
    return (nl, nl)
226 87e25be1 Thomas Thrainer
227 87e25be1 Thomas Thrainer
228 87e25be1 Thomas Thrainer
class TLMigrateInstance(Tasklet):
229 87e25be1 Thomas Thrainer
  """Tasklet class for instance migration.
230 87e25be1 Thomas Thrainer

231 87e25be1 Thomas Thrainer
  @type live: boolean
232 87e25be1 Thomas Thrainer
  @ivar live: whether the migration will be done live or non-live;
233 87e25be1 Thomas Thrainer
      this variable is initalized only after CheckPrereq has run
234 87e25be1 Thomas Thrainer
  @type cleanup: boolean
235 87e25be1 Thomas Thrainer
  @ivar cleanup: Wheater we cleanup from a failed migration
236 87e25be1 Thomas Thrainer
  @type iallocator: string
237 87e25be1 Thomas Thrainer
  @ivar iallocator: The iallocator used to determine target_node
238 1c3231aa Thomas Thrainer
  @type target_node_uuid: string
239 1c3231aa Thomas Thrainer
  @ivar target_node_uuid: If given, the target node UUID to reallocate the
240 1c3231aa Thomas Thrainer
      instance to
241 87e25be1 Thomas Thrainer
  @type failover: boolean
242 87e25be1 Thomas Thrainer
  @ivar failover: Whether operation results in failover or migration
243 87e25be1 Thomas Thrainer
  @type fallback: boolean
244 87e25be1 Thomas Thrainer
  @ivar fallback: Whether fallback to failover is allowed if migration not
245 87e25be1 Thomas Thrainer
                  possible
246 87e25be1 Thomas Thrainer
  @type ignore_consistency: boolean
247 87e25be1 Thomas Thrainer
  @ivar ignore_consistency: Wheter we should ignore consistency between source
248 87e25be1 Thomas Thrainer
                            and target node
249 87e25be1 Thomas Thrainer
  @type shutdown_timeout: int
250 87e25be1 Thomas Thrainer
  @ivar shutdown_timeout: In case of failover timeout of the shutdown
251 87e25be1 Thomas Thrainer
  @type ignore_ipolicy: bool
252 87e25be1 Thomas Thrainer
  @ivar ignore_ipolicy: If true, we can ignore instance policy when migrating
253 87e25be1 Thomas Thrainer

254 87e25be1 Thomas Thrainer
  """
255 87e25be1 Thomas Thrainer
256 87e25be1 Thomas Thrainer
  # Constants
257 87e25be1 Thomas Thrainer
  _MIGRATION_POLL_INTERVAL = 1      # seconds
258 87e25be1 Thomas Thrainer
  _MIGRATION_FEEDBACK_INTERVAL = 10 # seconds
259 87e25be1 Thomas Thrainer
260 da4a52a3 Thomas Thrainer
  def __init__(self, lu, instance_uuid, instance_name, cleanup, failover,
261 da4a52a3 Thomas Thrainer
               fallback, ignore_consistency, allow_runtime_changes,
262 da4a52a3 Thomas Thrainer
               shutdown_timeout, ignore_ipolicy):
263 87e25be1 Thomas Thrainer
    """Initializes this class.
264 87e25be1 Thomas Thrainer

265 87e25be1 Thomas Thrainer
    """
266 87e25be1 Thomas Thrainer
    Tasklet.__init__(self, lu)
267 87e25be1 Thomas Thrainer
268 87e25be1 Thomas Thrainer
    # Parameters
269 da4a52a3 Thomas Thrainer
    self.instance_uuid = instance_uuid
270 87e25be1 Thomas Thrainer
    self.instance_name = instance_name
271 87e25be1 Thomas Thrainer
    self.cleanup = cleanup
272 87e25be1 Thomas Thrainer
    self.live = False # will be overridden later
273 87e25be1 Thomas Thrainer
    self.failover = failover
274 87e25be1 Thomas Thrainer
    self.fallback = fallback
275 87e25be1 Thomas Thrainer
    self.ignore_consistency = ignore_consistency
276 87e25be1 Thomas Thrainer
    self.shutdown_timeout = shutdown_timeout
277 87e25be1 Thomas Thrainer
    self.ignore_ipolicy = ignore_ipolicy
278 87e25be1 Thomas Thrainer
    self.allow_runtime_changes = allow_runtime_changes
279 87e25be1 Thomas Thrainer
280 87e25be1 Thomas Thrainer
  def CheckPrereq(self):
281 87e25be1 Thomas Thrainer
    """Check prerequisites.
282 87e25be1 Thomas Thrainer

283 87e25be1 Thomas Thrainer
    This checks that the instance is in the cluster.
284 87e25be1 Thomas Thrainer

285 87e25be1 Thomas Thrainer
    """
286 da4a52a3 Thomas Thrainer
    (self.instance_uuid, self.instance_name) = \
287 da4a52a3 Thomas Thrainer
      ExpandInstanceUuidAndName(self.lu.cfg, self.instance_uuid,
288 da4a52a3 Thomas Thrainer
                                self.instance_name)
289 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.instance_uuid)
290 d0d7d7cf Thomas Thrainer
    assert self.instance is not None
291 87e25be1 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
292 87e25be1 Thomas Thrainer
293 87e25be1 Thomas Thrainer
    if (not self.cleanup and
294 d0d7d7cf Thomas Thrainer
        not self.instance.admin_state == constants.ADMINST_UP and
295 87e25be1 Thomas Thrainer
        not self.failover and self.fallback):
296 87e25be1 Thomas Thrainer
      self.lu.LogInfo("Instance is marked down or offline, fallback allowed,"
297 87e25be1 Thomas Thrainer
                      " switching to failover")
298 87e25be1 Thomas Thrainer
      self.failover = True
299 87e25be1 Thomas Thrainer
300 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template not in constants.DTS_MIRRORED:
301 87e25be1 Thomas Thrainer
      if self.failover:
302 87e25be1 Thomas Thrainer
        text = "failovers"
303 87e25be1 Thomas Thrainer
      else:
304 87e25be1 Thomas Thrainer
        text = "migrations"
305 87e25be1 Thomas Thrainer
      raise errors.OpPrereqError("Instance's disk layout '%s' does not allow"
306 d0d7d7cf Thomas Thrainer
                                 " %s" % (self.instance.disk_template, text),
307 87e25be1 Thomas Thrainer
                                 errors.ECODE_STATE)
308 87e25be1 Thomas Thrainer
309 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DTS_EXT_MIRROR:
310 5eacbcae Thomas Thrainer
      CheckIAllocatorOrNode(self.lu, "iallocator", "target_node")
311 87e25be1 Thomas Thrainer
312 87e25be1 Thomas Thrainer
      if self.lu.op.iallocator:
313 87e25be1 Thomas Thrainer
        assert locking.NAL in self.lu.owned_locks(locking.LEVEL_NODE_ALLOC)
314 87e25be1 Thomas Thrainer
        self._RunAllocator()
315 87e25be1 Thomas Thrainer
      else:
316 1c3231aa Thomas Thrainer
        # We set set self.target_node_uuid as it is required by
317 87e25be1 Thomas Thrainer
        # BuildHooksEnv
318 1c3231aa Thomas Thrainer
        self.target_node_uuid = self.lu.op.target_node_uuid
319 87e25be1 Thomas Thrainer
320 87e25be1 Thomas Thrainer
      # Check that the target node is correct in terms of instance policy
321 1c3231aa Thomas Thrainer
      nodeinfo = self.cfg.GetNodeInfo(self.target_node_uuid)
322 87e25be1 Thomas Thrainer
      group_info = self.cfg.GetNodeGroup(nodeinfo.group)
323 87e25be1 Thomas Thrainer
      ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
324 87e25be1 Thomas Thrainer
                                                              group_info)
325 d0d7d7cf Thomas Thrainer
      CheckTargetNodeIPolicy(self.lu, ipolicy, self.instance, nodeinfo,
326 d0d7d7cf Thomas Thrainer
                             self.cfg, ignore=self.ignore_ipolicy)
327 87e25be1 Thomas Thrainer
328 87e25be1 Thomas Thrainer
      # self.target_node is already populated, either directly or by the
329 87e25be1 Thomas Thrainer
      # iallocator run
330 1c3231aa Thomas Thrainer
      target_node_uuid = self.target_node_uuid
331 d0d7d7cf Thomas Thrainer
      if self.target_node_uuid == self.instance.primary_node:
332 1c3231aa Thomas Thrainer
        raise errors.OpPrereqError(
333 1c3231aa Thomas Thrainer
          "Cannot migrate instance %s to its primary (%s)" %
334 d0d7d7cf Thomas Thrainer
          (self.instance.name,
335 d0d7d7cf Thomas Thrainer
           self.cfg.GetNodeName(self.instance.primary_node)),
336 1c3231aa Thomas Thrainer
          errors.ECODE_STATE)
337 87e25be1 Thomas Thrainer
338 87e25be1 Thomas Thrainer
      if len(self.lu.tasklets) == 1:
339 87e25be1 Thomas Thrainer
        # It is safe to release locks only when we're the only tasklet
340 87e25be1 Thomas Thrainer
        # in the LU
341 5eacbcae Thomas Thrainer
        ReleaseLocks(self.lu, locking.LEVEL_NODE,
342 d0d7d7cf Thomas Thrainer
                     keep=[self.instance.primary_node, self.target_node_uuid])
343 5eacbcae Thomas Thrainer
        ReleaseLocks(self.lu, locking.LEVEL_NODE_ALLOC)
344 87e25be1 Thomas Thrainer
345 87e25be1 Thomas Thrainer
    else:
346 87e25be1 Thomas Thrainer
      assert not self.lu.glm.is_owned(locking.LEVEL_NODE_ALLOC)
347 87e25be1 Thomas Thrainer
348 d0d7d7cf Thomas Thrainer
      secondary_node_uuids = self.instance.secondary_nodes
349 1c3231aa Thomas Thrainer
      if not secondary_node_uuids:
350 87e25be1 Thomas Thrainer
        raise errors.ConfigurationError("No secondary node but using"
351 87e25be1 Thomas Thrainer
                                        " %s disk template" %
352 d0d7d7cf Thomas Thrainer
                                        self.instance.disk_template)
353 1c3231aa Thomas Thrainer
      target_node_uuid = secondary_node_uuids[0]
354 1c3231aa Thomas Thrainer
      if self.lu.op.iallocator or \
355 1c3231aa Thomas Thrainer
        (self.lu.op.target_node_uuid and
356 1c3231aa Thomas Thrainer
         self.lu.op.target_node_uuid != target_node_uuid):
357 87e25be1 Thomas Thrainer
        if self.failover:
358 87e25be1 Thomas Thrainer
          text = "failed over"
359 87e25be1 Thomas Thrainer
        else:
360 87e25be1 Thomas Thrainer
          text = "migrated"
361 87e25be1 Thomas Thrainer
        raise errors.OpPrereqError("Instances with disk template %s cannot"
362 87e25be1 Thomas Thrainer
                                   " be %s to arbitrary nodes"
363 87e25be1 Thomas Thrainer
                                   " (neither an iallocator nor a target"
364 87e25be1 Thomas Thrainer
                                   " node can be passed)" %
365 d0d7d7cf Thomas Thrainer
                                   (self.instance.disk_template, text),
366 87e25be1 Thomas Thrainer
                                   errors.ECODE_INVAL)
367 1c3231aa Thomas Thrainer
      nodeinfo = self.cfg.GetNodeInfo(target_node_uuid)
368 87e25be1 Thomas Thrainer
      group_info = self.cfg.GetNodeGroup(nodeinfo.group)
369 87e25be1 Thomas Thrainer
      ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
370 87e25be1 Thomas Thrainer
                                                              group_info)
371 d0d7d7cf Thomas Thrainer
      CheckTargetNodeIPolicy(self.lu, ipolicy, self.instance, nodeinfo,
372 d0d7d7cf Thomas Thrainer
                             self.cfg, ignore=self.ignore_ipolicy)
373 87e25be1 Thomas Thrainer
374 d0d7d7cf Thomas Thrainer
    i_be = cluster.FillBE(self.instance)
375 87e25be1 Thomas Thrainer
376 87e25be1 Thomas Thrainer
    # check memory requirements on the secondary node
377 87e25be1 Thomas Thrainer
    if (not self.cleanup and
378 d0d7d7cf Thomas Thrainer
         (not self.failover or
379 d0d7d7cf Thomas Thrainer
           self.instance.admin_state == constants.ADMINST_UP)):
380 a295eb80 Helga Velroyen
      self.tgt_free_mem = CheckNodeFreeMemory(
381 d0d7d7cf Thomas Thrainer
          self.lu, target_node_uuid,
382 d0d7d7cf Thomas Thrainer
          "migrating instance %s" % self.instance.name,
383 d0d7d7cf Thomas Thrainer
          i_be[constants.BE_MINMEM], self.instance.hypervisor,
384 d0d7d7cf Thomas Thrainer
          self.cfg.GetClusterInfo().hvparams[self.instance.hypervisor])
385 87e25be1 Thomas Thrainer
    else:
386 87e25be1 Thomas Thrainer
      self.lu.LogInfo("Not checking memory on the secondary node as"
387 87e25be1 Thomas Thrainer
                      " instance will not be started")
388 87e25be1 Thomas Thrainer
389 87e25be1 Thomas Thrainer
    # check if failover must be forced instead of migration
390 87e25be1 Thomas Thrainer
    if (not self.cleanup and not self.failover and
391 87e25be1 Thomas Thrainer
        i_be[constants.BE_ALWAYS_FAILOVER]):
392 87e25be1 Thomas Thrainer
      self.lu.LogInfo("Instance configured to always failover; fallback"
393 87e25be1 Thomas Thrainer
                      " to failover")
394 87e25be1 Thomas Thrainer
      self.failover = True
395 87e25be1 Thomas Thrainer
396 87e25be1 Thomas Thrainer
    # check bridge existance
397 d0d7d7cf Thomas Thrainer
    CheckInstanceBridgesExist(self.lu, self.instance,
398 d0d7d7cf Thomas Thrainer
                              node_uuid=target_node_uuid)
399 87e25be1 Thomas Thrainer
400 87e25be1 Thomas Thrainer
    if not self.cleanup:
401 1c3231aa Thomas Thrainer
      CheckNodeNotDrained(self.lu, target_node_uuid)
402 87e25be1 Thomas Thrainer
      if not self.failover:
403 d0d7d7cf Thomas Thrainer
        result = self.rpc.call_instance_migratable(self.instance.primary_node,
404 d0d7d7cf Thomas Thrainer
                                                   self.instance)
405 87e25be1 Thomas Thrainer
        if result.fail_msg and self.fallback:
406 87e25be1 Thomas Thrainer
          self.lu.LogInfo("Can't migrate, instance offline, fallback to"
407 87e25be1 Thomas Thrainer
                          " failover")
408 87e25be1 Thomas Thrainer
          self.failover = True
409 87e25be1 Thomas Thrainer
        else:
410 87e25be1 Thomas Thrainer
          result.Raise("Can't migrate, please use failover",
411 87e25be1 Thomas Thrainer
                       prereq=True, ecode=errors.ECODE_STATE)
412 87e25be1 Thomas Thrainer
413 87e25be1 Thomas Thrainer
    assert not (self.failover and self.cleanup)
414 87e25be1 Thomas Thrainer
415 87e25be1 Thomas Thrainer
    if not self.failover:
416 87e25be1 Thomas Thrainer
      if self.lu.op.live is not None and self.lu.op.mode is not None:
417 87e25be1 Thomas Thrainer
        raise errors.OpPrereqError("Only one of the 'live' and 'mode'"
418 87e25be1 Thomas Thrainer
                                   " parameters are accepted",
419 87e25be1 Thomas Thrainer
                                   errors.ECODE_INVAL)
420 87e25be1 Thomas Thrainer
      if self.lu.op.live is not None:
421 87e25be1 Thomas Thrainer
        if self.lu.op.live:
422 87e25be1 Thomas Thrainer
          self.lu.op.mode = constants.HT_MIGRATION_LIVE
423 87e25be1 Thomas Thrainer
        else:
424 87e25be1 Thomas Thrainer
          self.lu.op.mode = constants.HT_MIGRATION_NONLIVE
425 87e25be1 Thomas Thrainer
        # reset the 'live' parameter to None so that repeated
426 87e25be1 Thomas Thrainer
        # invocations of CheckPrereq do not raise an exception
427 87e25be1 Thomas Thrainer
        self.lu.op.live = None
428 87e25be1 Thomas Thrainer
      elif self.lu.op.mode is None:
429 87e25be1 Thomas Thrainer
        # read the default value from the hypervisor
430 87e25be1 Thomas Thrainer
        i_hv = cluster.FillHV(self.instance, skip_globals=False)
431 87e25be1 Thomas Thrainer
        self.lu.op.mode = i_hv[constants.HV_MIGRATION_MODE]
432 87e25be1 Thomas Thrainer
433 87e25be1 Thomas Thrainer
      self.live = self.lu.op.mode == constants.HT_MIGRATION_LIVE
434 87e25be1 Thomas Thrainer
    else:
435 87e25be1 Thomas Thrainer
      # Failover is never live
436 87e25be1 Thomas Thrainer
      self.live = False
437 87e25be1 Thomas Thrainer
438 87e25be1 Thomas Thrainer
    if not (self.failover or self.cleanup):
439 0bbec3af Helga Velroyen
      remote_info = self.rpc.call_instance_info(
440 d0d7d7cf Thomas Thrainer
          self.instance.primary_node, self.instance.name,
441 d0d7d7cf Thomas Thrainer
          self.instance.hypervisor, cluster.hvparams[self.instance.hypervisor])
442 87e25be1 Thomas Thrainer
      remote_info.Raise("Error checking instance on node %s" %
443 d0d7d7cf Thomas Thrainer
                        self.cfg.GetNodeName(self.instance.primary_node))
444 87e25be1 Thomas Thrainer
      instance_running = bool(remote_info.payload)
445 87e25be1 Thomas Thrainer
      if instance_running:
446 87e25be1 Thomas Thrainer
        self.current_mem = int(remote_info.payload["memory"])
447 87e25be1 Thomas Thrainer
448 87e25be1 Thomas Thrainer
  def _RunAllocator(self):
449 87e25be1 Thomas Thrainer
    """Run the allocator based on input opcode.
450 87e25be1 Thomas Thrainer

451 87e25be1 Thomas Thrainer
    """
452 87e25be1 Thomas Thrainer
    assert locking.NAL in self.lu.owned_locks(locking.LEVEL_NODE_ALLOC)
453 87e25be1 Thomas Thrainer
454 87e25be1 Thomas Thrainer
    # FIXME: add a self.ignore_ipolicy option
455 1c3231aa Thomas Thrainer
    req = iallocator.IAReqRelocate(
456 da4a52a3 Thomas Thrainer
          inst_uuid=self.instance_uuid,
457 1c3231aa Thomas Thrainer
          relocate_from_node_uuids=[self.instance.primary_node])
458 87e25be1 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
459 87e25be1 Thomas Thrainer
460 87e25be1 Thomas Thrainer
    ial.Run(self.lu.op.iallocator)
461 87e25be1 Thomas Thrainer
462 87e25be1 Thomas Thrainer
    if not ial.success:
463 87e25be1 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute nodes using"
464 87e25be1 Thomas Thrainer
                                 " iallocator '%s': %s" %
465 87e25be1 Thomas Thrainer
                                 (self.lu.op.iallocator, ial.info),
466 87e25be1 Thomas Thrainer
                                 errors.ECODE_NORES)
467 1c3231aa Thomas Thrainer
    self.target_node_uuid = self.cfg.GetNodeInfoByName(ial.result[0]).uuid
468 87e25be1 Thomas Thrainer
    self.lu.LogInfo("Selected nodes for instance %s via iallocator %s: %s",
469 87e25be1 Thomas Thrainer
                    self.instance_name, self.lu.op.iallocator,
470 87e25be1 Thomas Thrainer
                    utils.CommaJoin(ial.result))
471 87e25be1 Thomas Thrainer
472 87e25be1 Thomas Thrainer
  def _WaitUntilSync(self):
473 87e25be1 Thomas Thrainer
    """Poll with custom rpc for disk sync.
474 87e25be1 Thomas Thrainer

475 87e25be1 Thomas Thrainer
    This uses our own step-based rpc call.
476 87e25be1 Thomas Thrainer

477 87e25be1 Thomas Thrainer
    """
478 87e25be1 Thomas Thrainer
    self.feedback_fn("* wait until resync is done")
479 87e25be1 Thomas Thrainer
    all_done = False
480 87e25be1 Thomas Thrainer
    while not all_done:
481 87e25be1 Thomas Thrainer
      all_done = True
482 1c3231aa Thomas Thrainer
      result = self.rpc.call_drbd_wait_sync(self.all_node_uuids,
483 87e25be1 Thomas Thrainer
                                            self.nodes_ip,
484 87e25be1 Thomas Thrainer
                                            (self.instance.disks,
485 87e25be1 Thomas Thrainer
                                             self.instance))
486 87e25be1 Thomas Thrainer
      min_percent = 100
487 1c3231aa Thomas Thrainer
      for node_uuid, nres in result.items():
488 1c3231aa Thomas Thrainer
        nres.Raise("Cannot resync disks on node %s" %
489 1c3231aa Thomas Thrainer
                   self.cfg.GetNodeName(node_uuid))
490 87e25be1 Thomas Thrainer
        node_done, node_percent = nres.payload
491 87e25be1 Thomas Thrainer
        all_done = all_done and node_done
492 87e25be1 Thomas Thrainer
        if node_percent is not None:
493 87e25be1 Thomas Thrainer
          min_percent = min(min_percent, node_percent)
494 87e25be1 Thomas Thrainer
      if not all_done:
495 87e25be1 Thomas Thrainer
        if min_percent < 100:
496 87e25be1 Thomas Thrainer
          self.feedback_fn("   - progress: %.1f%%" % min_percent)
497 87e25be1 Thomas Thrainer
        time.sleep(2)
498 87e25be1 Thomas Thrainer
499 1c3231aa Thomas Thrainer
  def _EnsureSecondary(self, node_uuid):
500 87e25be1 Thomas Thrainer
    """Demote a node to secondary.
501 87e25be1 Thomas Thrainer

502 87e25be1 Thomas Thrainer
    """
503 1c3231aa Thomas Thrainer
    self.feedback_fn("* switching node %s to secondary mode" %
504 1c3231aa Thomas Thrainer
                     self.cfg.GetNodeName(node_uuid))
505 87e25be1 Thomas Thrainer
506 87e25be1 Thomas Thrainer
    for dev in self.instance.disks:
507 1c3231aa Thomas Thrainer
      self.cfg.SetDiskID(dev, node_uuid)
508 87e25be1 Thomas Thrainer
509 1c3231aa Thomas Thrainer
    result = self.rpc.call_blockdev_close(node_uuid, self.instance.name,
510 87e25be1 Thomas Thrainer
                                          self.instance.disks)
511 1c3231aa Thomas Thrainer
    result.Raise("Cannot change disk to secondary on node %s" %
512 1c3231aa Thomas Thrainer
                 self.cfg.GetNodeName(node_uuid))
513 87e25be1 Thomas Thrainer
514 87e25be1 Thomas Thrainer
  def _GoStandalone(self):
515 87e25be1 Thomas Thrainer
    """Disconnect from the network.
516 87e25be1 Thomas Thrainer

517 87e25be1 Thomas Thrainer
    """
518 87e25be1 Thomas Thrainer
    self.feedback_fn("* changing into standalone mode")
519 1c3231aa Thomas Thrainer
    result = self.rpc.call_drbd_disconnect_net(self.all_node_uuids,
520 1c3231aa Thomas Thrainer
                                               self.nodes_ip,
521 87e25be1 Thomas Thrainer
                                               self.instance.disks)
522 1c3231aa Thomas Thrainer
    for node_uuid, nres in result.items():
523 1c3231aa Thomas Thrainer
      nres.Raise("Cannot disconnect disks node %s" %
524 1c3231aa Thomas Thrainer
                 self.cfg.GetNodeName(node_uuid))
525 87e25be1 Thomas Thrainer
526 87e25be1 Thomas Thrainer
  def _GoReconnect(self, multimaster):
527 87e25be1 Thomas Thrainer
    """Reconnect to the network.
528 87e25be1 Thomas Thrainer

529 87e25be1 Thomas Thrainer
    """
530 87e25be1 Thomas Thrainer
    if multimaster:
531 87e25be1 Thomas Thrainer
      msg = "dual-master"
532 87e25be1 Thomas Thrainer
    else:
533 87e25be1 Thomas Thrainer
      msg = "single-master"
534 87e25be1 Thomas Thrainer
    self.feedback_fn("* changing disks into %s mode" % msg)
535 1c3231aa Thomas Thrainer
    result = self.rpc.call_drbd_attach_net(self.all_node_uuids, self.nodes_ip,
536 87e25be1 Thomas Thrainer
                                           (self.instance.disks, self.instance),
537 87e25be1 Thomas Thrainer
                                           self.instance.name, multimaster)
538 1c3231aa Thomas Thrainer
    for node_uuid, nres in result.items():
539 1c3231aa Thomas Thrainer
      nres.Raise("Cannot change disks config on node %s" %
540 1c3231aa Thomas Thrainer
                 self.cfg.GetNodeName(node_uuid))
541 87e25be1 Thomas Thrainer
542 87e25be1 Thomas Thrainer
  def _ExecCleanup(self):
543 87e25be1 Thomas Thrainer
    """Try to cleanup after a failed migration.
544 87e25be1 Thomas Thrainer

545 87e25be1 Thomas Thrainer
    The cleanup is done by:
546 87e25be1 Thomas Thrainer
      - check that the instance is running only on one node
547 87e25be1 Thomas Thrainer
        (and update the config if needed)
548 87e25be1 Thomas Thrainer
      - change disks on its secondary node to secondary
549 87e25be1 Thomas Thrainer
      - wait until disks are fully synchronized
550 87e25be1 Thomas Thrainer
      - disconnect from the network
551 87e25be1 Thomas Thrainer
      - change disks into single-master mode
552 87e25be1 Thomas Thrainer
      - wait again until disks are fully synchronized
553 87e25be1 Thomas Thrainer

554 87e25be1 Thomas Thrainer
    """
555 87e25be1 Thomas Thrainer
    # check running on only one node
556 87e25be1 Thomas Thrainer
    self.feedback_fn("* checking where the instance actually runs"
557 87e25be1 Thomas Thrainer
                     " (if this hangs, the hypervisor might be in"
558 87e25be1 Thomas Thrainer
                     " a bad state)")
559 8ac806e6 Helga Velroyen
    cluster_hvparams = self.cfg.GetClusterInfo().hvparams
560 1c3231aa Thomas Thrainer
    ins_l = self.rpc.call_instance_list(self.all_node_uuids,
561 d0d7d7cf Thomas Thrainer
                                        [self.instance.hypervisor],
562 8ac806e6 Helga Velroyen
                                        cluster_hvparams)
563 1c3231aa Thomas Thrainer
    for node_uuid, result in ins_l.items():
564 1c3231aa Thomas Thrainer
      result.Raise("Can't contact node %s" % node_uuid)
565 87e25be1 Thomas Thrainer
566 d0d7d7cf Thomas Thrainer
    runningon_source = self.instance.name in \
567 d0d7d7cf Thomas Thrainer
                         ins_l[self.source_node_uuid].payload
568 d0d7d7cf Thomas Thrainer
    runningon_target = self.instance.name in \
569 d0d7d7cf Thomas Thrainer
                         ins_l[self.target_node_uuid].payload
570 87e25be1 Thomas Thrainer
571 87e25be1 Thomas Thrainer
    if runningon_source and runningon_target:
572 87e25be1 Thomas Thrainer
      raise errors.OpExecError("Instance seems to be running on two nodes,"
573 87e25be1 Thomas Thrainer
                               " or the hypervisor is confused; you will have"
574 87e25be1 Thomas Thrainer
                               " to ensure manually that it runs only on one"
575 87e25be1 Thomas Thrainer
                               " and restart this operation")
576 87e25be1 Thomas Thrainer
577 87e25be1 Thomas Thrainer
    if not (runningon_source or runningon_target):
578 87e25be1 Thomas Thrainer
      raise errors.OpExecError("Instance does not seem to be running at all;"
579 87e25be1 Thomas Thrainer
                               " in this case it's safer to repair by"
580 87e25be1 Thomas Thrainer
                               " running 'gnt-instance stop' to ensure disk"
581 87e25be1 Thomas Thrainer
                               " shutdown, and then restarting it")
582 87e25be1 Thomas Thrainer
583 87e25be1 Thomas Thrainer
    if runningon_target:
584 87e25be1 Thomas Thrainer
      # the migration has actually succeeded, we need to update the config
585 87e25be1 Thomas Thrainer
      self.feedback_fn("* instance running on secondary node (%s),"
586 1c3231aa Thomas Thrainer
                       " updating config" %
587 d0d7d7cf Thomas Thrainer
                       self.cfg.GetNodeName(self.target_node_uuid))
588 d0d7d7cf Thomas Thrainer
      self.instance.primary_node = self.target_node_uuid
589 d0d7d7cf Thomas Thrainer
      self.cfg.Update(self.instance, self.feedback_fn)
590 d0d7d7cf Thomas Thrainer
      demoted_node_uuid = self.source_node_uuid
591 87e25be1 Thomas Thrainer
    else:
592 87e25be1 Thomas Thrainer
      self.feedback_fn("* instance confirmed to be running on its"
593 1c3231aa Thomas Thrainer
                       " primary node (%s)" %
594 d0d7d7cf Thomas Thrainer
                       self.cfg.GetNodeName(self.source_node_uuid))
595 d0d7d7cf Thomas Thrainer
      demoted_node_uuid = self.target_node_uuid
596 87e25be1 Thomas Thrainer
597 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DTS_INT_MIRROR:
598 1c3231aa Thomas Thrainer
      self._EnsureSecondary(demoted_node_uuid)
599 87e25be1 Thomas Thrainer
      try:
600 87e25be1 Thomas Thrainer
        self._WaitUntilSync()
601 87e25be1 Thomas Thrainer
      except errors.OpExecError:
602 87e25be1 Thomas Thrainer
        # we ignore here errors, since if the device is standalone, it
603 87e25be1 Thomas Thrainer
        # won't be able to sync
604 87e25be1 Thomas Thrainer
        pass
605 87e25be1 Thomas Thrainer
      self._GoStandalone()
606 87e25be1 Thomas Thrainer
      self._GoReconnect(False)
607 87e25be1 Thomas Thrainer
      self._WaitUntilSync()
608 87e25be1 Thomas Thrainer
609 87e25be1 Thomas Thrainer
    self.feedback_fn("* done")
610 87e25be1 Thomas Thrainer
611 87e25be1 Thomas Thrainer
  def _RevertDiskStatus(self):
612 87e25be1 Thomas Thrainer
    """Try to revert the disk status after a failed migration.
613 87e25be1 Thomas Thrainer

614 87e25be1 Thomas Thrainer
    """
615 87e25be1 Thomas Thrainer
    if self.instance.disk_template in constants.DTS_EXT_MIRROR:
616 87e25be1 Thomas Thrainer
      return
617 87e25be1 Thomas Thrainer
618 87e25be1 Thomas Thrainer
    try:
619 1c3231aa Thomas Thrainer
      self._EnsureSecondary(self.target_node_uuid)
620 87e25be1 Thomas Thrainer
      self._GoStandalone()
621 87e25be1 Thomas Thrainer
      self._GoReconnect(False)
622 87e25be1 Thomas Thrainer
      self._WaitUntilSync()
623 87e25be1 Thomas Thrainer
    except errors.OpExecError, err:
624 87e25be1 Thomas Thrainer
      self.lu.LogWarning("Migration failed and I can't reconnect the drives,"
625 87e25be1 Thomas Thrainer
                         " please try to recover the instance manually;"
626 87e25be1 Thomas Thrainer
                         " error '%s'" % str(err))
627 87e25be1 Thomas Thrainer
628 87e25be1 Thomas Thrainer
  def _AbortMigration(self):
629 87e25be1 Thomas Thrainer
    """Call the hypervisor code to abort a started migration.
630 87e25be1 Thomas Thrainer

631 87e25be1 Thomas Thrainer
    """
632 1c3231aa Thomas Thrainer
    abort_result = self.rpc.call_instance_finalize_migration_dst(
633 d0d7d7cf Thomas Thrainer
                     self.target_node_uuid, self.instance, self.migration_info,
634 d0d7d7cf Thomas Thrainer
                     False)
635 87e25be1 Thomas Thrainer
    abort_msg = abort_result.fail_msg
636 87e25be1 Thomas Thrainer
    if abort_msg:
637 87e25be1 Thomas Thrainer
      logging.error("Aborting migration failed on target node %s: %s",
638 1c3231aa Thomas Thrainer
                    self.cfg.GetNodeName(self.target_node_uuid), abort_msg)
639 87e25be1 Thomas Thrainer
      # Don't raise an exception here, as we stil have to try to revert the
640 87e25be1 Thomas Thrainer
      # disk status, even if this step failed.
641 87e25be1 Thomas Thrainer
642 87e25be1 Thomas Thrainer
    abort_result = self.rpc.call_instance_finalize_migration_src(
643 d0d7d7cf Thomas Thrainer
      self.source_node_uuid, self.instance, False, self.live)
644 87e25be1 Thomas Thrainer
    abort_msg = abort_result.fail_msg
645 87e25be1 Thomas Thrainer
    if abort_msg:
646 87e25be1 Thomas Thrainer
      logging.error("Aborting migration failed on source node %s: %s",
647 1c3231aa Thomas Thrainer
                    self.cfg.GetNodeName(self.source_node_uuid), abort_msg)
648 87e25be1 Thomas Thrainer
649 87e25be1 Thomas Thrainer
  def _ExecMigration(self):
650 87e25be1 Thomas Thrainer
    """Migrate an instance.
651 87e25be1 Thomas Thrainer

652 87e25be1 Thomas Thrainer
    The migrate is done by:
653 87e25be1 Thomas Thrainer
      - change the disks into dual-master mode
654 87e25be1 Thomas Thrainer
      - wait until disks are fully synchronized again
655 87e25be1 Thomas Thrainer
      - migrate the instance
656 87e25be1 Thomas Thrainer
      - change disks on the new secondary node (the old primary) to secondary
657 87e25be1 Thomas Thrainer
      - wait until disks are fully synchronized
658 87e25be1 Thomas Thrainer
      - change disks into single-master mode
659 87e25be1 Thomas Thrainer

660 87e25be1 Thomas Thrainer
    """
661 87e25be1 Thomas Thrainer
    # Check for hypervisor version mismatch and warn the user.
662 d0d7d7cf Thomas Thrainer
    hvspecs = [(self.instance.hypervisor,
663 d0d7d7cf Thomas Thrainer
                self.cfg.GetClusterInfo().hvparams[self.instance.hypervisor])]
664 d0d7d7cf Thomas Thrainer
    nodeinfo = self.rpc.call_node_info(
665 da803ff1 Helga Velroyen
                 [self.source_node_uuid, self.target_node_uuid], None, hvspecs)
666 87e25be1 Thomas Thrainer
    for ninfo in nodeinfo.values():
667 87e25be1 Thomas Thrainer
      ninfo.Raise("Unable to retrieve node information from node '%s'" %
668 87e25be1 Thomas Thrainer
                  ninfo.node)
669 d0d7d7cf Thomas Thrainer
    (_, _, (src_info, )) = nodeinfo[self.source_node_uuid].payload
670 d0d7d7cf Thomas Thrainer
    (_, _, (dst_info, )) = nodeinfo[self.target_node_uuid].payload
671 87e25be1 Thomas Thrainer
672 87e25be1 Thomas Thrainer
    if ((constants.HV_NODEINFO_KEY_VERSION in src_info) and
673 87e25be1 Thomas Thrainer
        (constants.HV_NODEINFO_KEY_VERSION in dst_info)):
674 87e25be1 Thomas Thrainer
      src_version = src_info[constants.HV_NODEINFO_KEY_VERSION]
675 87e25be1 Thomas Thrainer
      dst_version = dst_info[constants.HV_NODEINFO_KEY_VERSION]
676 87e25be1 Thomas Thrainer
      if src_version != dst_version:
677 87e25be1 Thomas Thrainer
        self.feedback_fn("* warning: hypervisor version mismatch between"
678 87e25be1 Thomas Thrainer
                         " source (%s) and target (%s) node" %
679 87e25be1 Thomas Thrainer
                         (src_version, dst_version))
680 87e25be1 Thomas Thrainer
681 87e25be1 Thomas Thrainer
    self.feedback_fn("* checking disk consistency between source and target")
682 d0d7d7cf Thomas Thrainer
    for (idx, dev) in enumerate(self.instance.disks):
683 d0d7d7cf Thomas Thrainer
      if not CheckDiskConsistency(self.lu, self.instance, dev,
684 d0d7d7cf Thomas Thrainer
                                  self.target_node_uuid,
685 1c3231aa Thomas Thrainer
                                  False):
686 87e25be1 Thomas Thrainer
        raise errors.OpExecError("Disk %s is degraded or not fully"
687 87e25be1 Thomas Thrainer
                                 " synchronized on target node,"
688 87e25be1 Thomas Thrainer
                                 " aborting migration" % idx)
689 87e25be1 Thomas Thrainer
690 87e25be1 Thomas Thrainer
    if self.current_mem > self.tgt_free_mem:
691 87e25be1 Thomas Thrainer
      if not self.allow_runtime_changes:
692 87e25be1 Thomas Thrainer
        raise errors.OpExecError("Memory ballooning not allowed and not enough"
693 87e25be1 Thomas Thrainer
                                 " free memory to fit instance %s on target"
694 87e25be1 Thomas Thrainer
                                 " node %s (have %dMB, need %dMB)" %
695 d0d7d7cf Thomas Thrainer
                                 (self.instance.name,
696 d0d7d7cf Thomas Thrainer
                                  self.cfg.GetNodeName(self.target_node_uuid),
697 87e25be1 Thomas Thrainer
                                  self.tgt_free_mem, self.current_mem))
698 87e25be1 Thomas Thrainer
      self.feedback_fn("* setting instance memory to %s" % self.tgt_free_mem)
699 d0d7d7cf Thomas Thrainer
      rpcres = self.rpc.call_instance_balloon_memory(self.instance.primary_node,
700 d0d7d7cf Thomas Thrainer
                                                     self.instance,
701 87e25be1 Thomas Thrainer
                                                     self.tgt_free_mem)
702 87e25be1 Thomas Thrainer
      rpcres.Raise("Cannot modify instance runtime memory")
703 87e25be1 Thomas Thrainer
704 87e25be1 Thomas Thrainer
    # First get the migration information from the remote node
705 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_migration_info(self.source_node_uuid, self.instance)
706 87e25be1 Thomas Thrainer
    msg = result.fail_msg
707 87e25be1 Thomas Thrainer
    if msg:
708 87e25be1 Thomas Thrainer
      log_err = ("Failed fetching source migration information from %s: %s" %
709 d0d7d7cf Thomas Thrainer
                 (self.cfg.GetNodeName(self.source_node_uuid), msg))
710 87e25be1 Thomas Thrainer
      logging.error(log_err)
711 87e25be1 Thomas Thrainer
      raise errors.OpExecError(log_err)
712 87e25be1 Thomas Thrainer
713 87e25be1 Thomas Thrainer
    self.migration_info = migration_info = result.payload
714 87e25be1 Thomas Thrainer
715 87e25be1 Thomas Thrainer
    if self.instance.disk_template not in constants.DTS_EXT_MIRROR:
716 87e25be1 Thomas Thrainer
      # Then switch the disks to master/master mode
717 d0d7d7cf Thomas Thrainer
      self._EnsureSecondary(self.target_node_uuid)
718 87e25be1 Thomas Thrainer
      self._GoStandalone()
719 87e25be1 Thomas Thrainer
      self._GoReconnect(True)
720 87e25be1 Thomas Thrainer
      self._WaitUntilSync()
721 87e25be1 Thomas Thrainer
722 1c3231aa Thomas Thrainer
    self.feedback_fn("* preparing %s to accept the instance" %
723 d0d7d7cf Thomas Thrainer
                     self.cfg.GetNodeName(self.target_node_uuid))
724 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_accept_instance(self.target_node_uuid,
725 d0d7d7cf Thomas Thrainer
                                           self.instance,
726 87e25be1 Thomas Thrainer
                                           migration_info,
727 d0d7d7cf Thomas Thrainer
                                           self.nodes_ip[self.target_node_uuid])
728 87e25be1 Thomas Thrainer
729 87e25be1 Thomas Thrainer
    msg = result.fail_msg
730 87e25be1 Thomas Thrainer
    if msg:
731 87e25be1 Thomas Thrainer
      logging.error("Instance pre-migration failed, trying to revert"
732 87e25be1 Thomas Thrainer
                    " disk status: %s", msg)
733 87e25be1 Thomas Thrainer
      self.feedback_fn("Pre-migration failed, aborting")
734 87e25be1 Thomas Thrainer
      self._AbortMigration()
735 87e25be1 Thomas Thrainer
      self._RevertDiskStatus()
736 87e25be1 Thomas Thrainer
      raise errors.OpExecError("Could not pre-migrate instance %s: %s" %
737 d0d7d7cf Thomas Thrainer
                               (self.instance.name, msg))
738 87e25be1 Thomas Thrainer
739 1c3231aa Thomas Thrainer
    self.feedback_fn("* migrating instance to %s" %
740 d0d7d7cf Thomas Thrainer
                     self.cfg.GetNodeName(self.target_node_uuid))
741 bc0a2284 Helga Velroyen
    cluster = self.cfg.GetClusterInfo()
742 bc0a2284 Helga Velroyen
    result = self.rpc.call_instance_migrate(
743 d0d7d7cf Thomas Thrainer
        self.source_node_uuid, cluster.cluster_name, self.instance,
744 d0d7d7cf Thomas Thrainer
        self.nodes_ip[self.target_node_uuid], self.live)
745 87e25be1 Thomas Thrainer
    msg = result.fail_msg
746 87e25be1 Thomas Thrainer
    if msg:
747 87e25be1 Thomas Thrainer
      logging.error("Instance migration failed, trying to revert"
748 87e25be1 Thomas Thrainer
                    " disk status: %s", msg)
749 87e25be1 Thomas Thrainer
      self.feedback_fn("Migration failed, aborting")
750 87e25be1 Thomas Thrainer
      self._AbortMigration()
751 87e25be1 Thomas Thrainer
      self._RevertDiskStatus()
752 87e25be1 Thomas Thrainer
      raise errors.OpExecError("Could not migrate instance %s: %s" %
753 d0d7d7cf Thomas Thrainer
                               (self.instance.name, msg))
754 87e25be1 Thomas Thrainer
755 87e25be1 Thomas Thrainer
    self.feedback_fn("* starting memory transfer")
756 87e25be1 Thomas Thrainer
    last_feedback = time.time()
757 87e25be1 Thomas Thrainer
    while True:
758 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_instance_get_migration_status(
759 d0d7d7cf Thomas Thrainer
                 self.source_node_uuid, self.instance)
760 87e25be1 Thomas Thrainer
      msg = result.fail_msg
761 87e25be1 Thomas Thrainer
      ms = result.payload   # MigrationStatus instance
762 87e25be1 Thomas Thrainer
      if msg or (ms.status in constants.HV_MIGRATION_FAILED_STATUSES):
763 87e25be1 Thomas Thrainer
        logging.error("Instance migration failed, trying to revert"
764 87e25be1 Thomas Thrainer
                      " disk status: %s", msg)
765 87e25be1 Thomas Thrainer
        self.feedback_fn("Migration failed, aborting")
766 87e25be1 Thomas Thrainer
        self._AbortMigration()
767 87e25be1 Thomas Thrainer
        self._RevertDiskStatus()
768 87e25be1 Thomas Thrainer
        if not msg:
769 87e25be1 Thomas Thrainer
          msg = "hypervisor returned failure"
770 87e25be1 Thomas Thrainer
        raise errors.OpExecError("Could not migrate instance %s: %s" %
771 d0d7d7cf Thomas Thrainer
                                 (self.instance.name, msg))
772 87e25be1 Thomas Thrainer
773 87e25be1 Thomas Thrainer
      if result.payload.status != constants.HV_MIGRATION_ACTIVE:
774 87e25be1 Thomas Thrainer
        self.feedback_fn("* memory transfer complete")
775 87e25be1 Thomas Thrainer
        break
776 87e25be1 Thomas Thrainer
777 87e25be1 Thomas Thrainer
      if (utils.TimeoutExpired(last_feedback,
778 87e25be1 Thomas Thrainer
                               self._MIGRATION_FEEDBACK_INTERVAL) and
779 87e25be1 Thomas Thrainer
          ms.transferred_ram is not None):
780 87e25be1 Thomas Thrainer
        mem_progress = 100 * float(ms.transferred_ram) / float(ms.total_ram)
781 87e25be1 Thomas Thrainer
        self.feedback_fn("* memory transfer progress: %.2f %%" % mem_progress)
782 87e25be1 Thomas Thrainer
        last_feedback = time.time()
783 87e25be1 Thomas Thrainer
784 87e25be1 Thomas Thrainer
      time.sleep(self._MIGRATION_POLL_INTERVAL)
785 87e25be1 Thomas Thrainer
786 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_instance_finalize_migration_src(
787 d0d7d7cf Thomas Thrainer
               self.source_node_uuid, self.instance, True, self.live)
788 87e25be1 Thomas Thrainer
    msg = result.fail_msg
789 87e25be1 Thomas Thrainer
    if msg:
790 87e25be1 Thomas Thrainer
      logging.error("Instance migration succeeded, but finalization failed"
791 87e25be1 Thomas Thrainer
                    " on the source node: %s", msg)
792 87e25be1 Thomas Thrainer
      raise errors.OpExecError("Could not finalize instance migration: %s" %
793 87e25be1 Thomas Thrainer
                               msg)
794 87e25be1 Thomas Thrainer
795 d0d7d7cf Thomas Thrainer
    self.instance.primary_node = self.target_node_uuid
796 87e25be1 Thomas Thrainer
797 87e25be1 Thomas Thrainer
    # distribute new instance config to the other nodes
798 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, self.feedback_fn)
799 87e25be1 Thomas Thrainer
800 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_instance_finalize_migration_dst(
801 d0d7d7cf Thomas Thrainer
               self.target_node_uuid, self.instance, migration_info, True)
802 87e25be1 Thomas Thrainer
    msg = result.fail_msg
803 87e25be1 Thomas Thrainer
    if msg:
804 87e25be1 Thomas Thrainer
      logging.error("Instance migration succeeded, but finalization failed"
805 87e25be1 Thomas Thrainer
                    " on the target node: %s", msg)
806 87e25be1 Thomas Thrainer
      raise errors.OpExecError("Could not finalize instance migration: %s" %
807 87e25be1 Thomas Thrainer
                               msg)
808 87e25be1 Thomas Thrainer
809 87e25be1 Thomas Thrainer
    if self.instance.disk_template not in constants.DTS_EXT_MIRROR:
810 d0d7d7cf Thomas Thrainer
      self._EnsureSecondary(self.source_node_uuid)
811 87e25be1 Thomas Thrainer
      self._WaitUntilSync()
812 87e25be1 Thomas Thrainer
      self._GoStandalone()
813 87e25be1 Thomas Thrainer
      self._GoReconnect(False)
814 87e25be1 Thomas Thrainer
      self._WaitUntilSync()
815 87e25be1 Thomas Thrainer
816 87e25be1 Thomas Thrainer
    # If the instance's disk template is `rbd' or `ext' and there was a
817 87e25be1 Thomas Thrainer
    # successful migration, unmap the device from the source node.
818 87e25be1 Thomas Thrainer
    if self.instance.disk_template in (constants.DT_RBD, constants.DT_EXT):
819 d0d7d7cf Thomas Thrainer
      disks = ExpandCheckDisks(self.instance, self.instance.disks)
820 1c3231aa Thomas Thrainer
      self.feedback_fn("* unmapping instance's disks from %s" %
821 d0d7d7cf Thomas Thrainer
                       self.cfg.GetNodeName(self.source_node_uuid))
822 87e25be1 Thomas Thrainer
      for disk in disks:
823 d0d7d7cf Thomas Thrainer
        result = self.rpc.call_blockdev_shutdown(self.source_node_uuid,
824 d0d7d7cf Thomas Thrainer
                                                 (disk, self.instance))
825 87e25be1 Thomas Thrainer
        msg = result.fail_msg
826 87e25be1 Thomas Thrainer
        if msg:
827 87e25be1 Thomas Thrainer
          logging.error("Migration was successful, but couldn't unmap the"
828 87e25be1 Thomas Thrainer
                        " block device %s on source node %s: %s",
829 d0d7d7cf Thomas Thrainer
                        disk.iv_name,
830 d0d7d7cf Thomas Thrainer
                        self.cfg.GetNodeName(self.source_node_uuid), msg)
831 87e25be1 Thomas Thrainer
          logging.error("You need to unmap the device %s manually on %s",
832 d0d7d7cf Thomas Thrainer
                        disk.iv_name,
833 d0d7d7cf Thomas Thrainer
                        self.cfg.GetNodeName(self.source_node_uuid))
834 87e25be1 Thomas Thrainer
835 87e25be1 Thomas Thrainer
    self.feedback_fn("* done")
836 87e25be1 Thomas Thrainer
837 87e25be1 Thomas Thrainer
  def _ExecFailover(self):
838 87e25be1 Thomas Thrainer
    """Failover an instance.
839 87e25be1 Thomas Thrainer

840 87e25be1 Thomas Thrainer
    The failover is done by shutting it down on its present node and
841 87e25be1 Thomas Thrainer
    starting it on the secondary.
842 87e25be1 Thomas Thrainer

843 87e25be1 Thomas Thrainer
    """
844 d0d7d7cf Thomas Thrainer
    primary_node = self.cfg.GetNodeInfo(self.instance.primary_node)
845 87e25be1 Thomas Thrainer
846 d0d7d7cf Thomas Thrainer
    source_node_uuid = self.instance.primary_node
847 87e25be1 Thomas Thrainer
848 d0d7d7cf Thomas Thrainer
    if self.instance.disks_active:
849 87e25be1 Thomas Thrainer
      self.feedback_fn("* checking disk consistency between source and target")
850 d0d7d7cf Thomas Thrainer
      for (idx, dev) in enumerate(self.instance.disks):
851 87e25be1 Thomas Thrainer
        # for drbd, these are drbd over lvm
852 d0d7d7cf Thomas Thrainer
        if not CheckDiskConsistency(self.lu, self.instance, dev,
853 d0d7d7cf Thomas Thrainer
                                    self.target_node_uuid, False):
854 87e25be1 Thomas Thrainer
          if primary_node.offline:
855 87e25be1 Thomas Thrainer
            self.feedback_fn("Node %s is offline, ignoring degraded disk %s on"
856 87e25be1 Thomas Thrainer
                             " target node %s" %
857 1c3231aa Thomas Thrainer
                             (primary_node.name, idx,
858 d0d7d7cf Thomas Thrainer
                              self.cfg.GetNodeName(self.target_node_uuid)))
859 87e25be1 Thomas Thrainer
          elif not self.ignore_consistency:
860 87e25be1 Thomas Thrainer
            raise errors.OpExecError("Disk %s is degraded on target node,"
861 87e25be1 Thomas Thrainer
                                     " aborting failover" % idx)
862 87e25be1 Thomas Thrainer
    else:
863 87e25be1 Thomas Thrainer
      self.feedback_fn("* not checking disk consistency as instance is not"
864 87e25be1 Thomas Thrainer
                       " running")
865 87e25be1 Thomas Thrainer
866 87e25be1 Thomas Thrainer
    self.feedback_fn("* shutting down instance on source node")
867 87e25be1 Thomas Thrainer
    logging.info("Shutting down instance %s on node %s",
868 d0d7d7cf Thomas Thrainer
                 self.instance.name, self.cfg.GetNodeName(source_node_uuid))
869 87e25be1 Thomas Thrainer
870 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_instance_shutdown(source_node_uuid, self.instance,
871 87e25be1 Thomas Thrainer
                                             self.shutdown_timeout,
872 87e25be1 Thomas Thrainer
                                             self.lu.op.reason)
873 87e25be1 Thomas Thrainer
    msg = result.fail_msg
874 87e25be1 Thomas Thrainer
    if msg:
875 87e25be1 Thomas Thrainer
      if self.ignore_consistency or primary_node.offline:
876 87e25be1 Thomas Thrainer
        self.lu.LogWarning("Could not shutdown instance %s on node %s,"
877 87e25be1 Thomas Thrainer
                           " proceeding anyway; please make sure node"
878 87e25be1 Thomas Thrainer
                           " %s is down; error details: %s",
879 d0d7d7cf Thomas Thrainer
                           self.instance.name,
880 1c3231aa Thomas Thrainer
                           self.cfg.GetNodeName(source_node_uuid),
881 1c3231aa Thomas Thrainer
                           self.cfg.GetNodeName(source_node_uuid), msg)
882 87e25be1 Thomas Thrainer
      else:
883 87e25be1 Thomas Thrainer
        raise errors.OpExecError("Could not shutdown instance %s on"
884 87e25be1 Thomas Thrainer
                                 " node %s: %s" %
885 d0d7d7cf Thomas Thrainer
                                 (self.instance.name,
886 1c3231aa Thomas Thrainer
                                  self.cfg.GetNodeName(source_node_uuid), msg))
887 87e25be1 Thomas Thrainer
888 87e25be1 Thomas Thrainer
    self.feedback_fn("* deactivating the instance's disks on source node")
889 d0d7d7cf Thomas Thrainer
    if not ShutdownInstanceDisks(self.lu, self.instance, ignore_primary=True):
890 87e25be1 Thomas Thrainer
      raise errors.OpExecError("Can't shut down the instance's disks")
891 87e25be1 Thomas Thrainer
892 d0d7d7cf Thomas Thrainer
    self.instance.primary_node = self.target_node_uuid
893 87e25be1 Thomas Thrainer
    # distribute new instance config to the other nodes
894 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, self.feedback_fn)
895 87e25be1 Thomas Thrainer
896 87e25be1 Thomas Thrainer
    # Only start the instance if it's marked as up
897 d0d7d7cf Thomas Thrainer
    if self.instance.admin_state == constants.ADMINST_UP:
898 87e25be1 Thomas Thrainer
      self.feedback_fn("* activating the instance's disks on target node %s" %
899 d0d7d7cf Thomas Thrainer
                       self.cfg.GetNodeName(self.target_node_uuid))
900 d0d7d7cf Thomas Thrainer
      logging.info("Starting instance %s on node %s", self.instance.name,
901 d0d7d7cf Thomas Thrainer
                   self.cfg.GetNodeName(self.target_node_uuid))
902 87e25be1 Thomas Thrainer
903 d0d7d7cf Thomas Thrainer
      disks_ok, _ = AssembleInstanceDisks(self.lu, self.instance,
904 5eacbcae Thomas Thrainer
                                          ignore_secondaries=True)
905 87e25be1 Thomas Thrainer
      if not disks_ok:
906 d0d7d7cf Thomas Thrainer
        ShutdownInstanceDisks(self.lu, self.instance)
907 87e25be1 Thomas Thrainer
        raise errors.OpExecError("Can't activate the instance's disks")
908 87e25be1 Thomas Thrainer
909 87e25be1 Thomas Thrainer
      self.feedback_fn("* starting the instance on the target node %s" %
910 d0d7d7cf Thomas Thrainer
                       self.cfg.GetNodeName(self.target_node_uuid))
911 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_instance_start(self.target_node_uuid,
912 d0d7d7cf Thomas Thrainer
                                            (self.instance, None, None), False,
913 1c3231aa Thomas Thrainer
                                            self.lu.op.reason)
914 87e25be1 Thomas Thrainer
      msg = result.fail_msg
915 87e25be1 Thomas Thrainer
      if msg:
916 d0d7d7cf Thomas Thrainer
        ShutdownInstanceDisks(self.lu, self.instance)
917 87e25be1 Thomas Thrainer
        raise errors.OpExecError("Could not start instance %s on node %s: %s" %
918 d0d7d7cf Thomas Thrainer
                                 (self.instance.name,
919 d0d7d7cf Thomas Thrainer
                                  self.cfg.GetNodeName(self.target_node_uuid),
920 d0d7d7cf Thomas Thrainer
                                  msg))
921 87e25be1 Thomas Thrainer
922 87e25be1 Thomas Thrainer
  def Exec(self, feedback_fn):
923 87e25be1 Thomas Thrainer
    """Perform the migration.
924 87e25be1 Thomas Thrainer

925 87e25be1 Thomas Thrainer
    """
926 87e25be1 Thomas Thrainer
    self.feedback_fn = feedback_fn
927 1c3231aa Thomas Thrainer
    self.source_node_uuid = self.instance.primary_node
928 87e25be1 Thomas Thrainer
929 87e25be1 Thomas Thrainer
    # FIXME: if we implement migrate-to-any in DRBD, this needs fixing
930 87e25be1 Thomas Thrainer
    if self.instance.disk_template in constants.DTS_INT_MIRROR:
931 1c3231aa Thomas Thrainer
      self.target_node_uuid = self.instance.secondary_nodes[0]
932 87e25be1 Thomas Thrainer
      # Otherwise self.target_node has been populated either
933 87e25be1 Thomas Thrainer
      # directly, or through an iallocator.
934 87e25be1 Thomas Thrainer
935 1c3231aa Thomas Thrainer
    self.all_node_uuids = [self.source_node_uuid, self.target_node_uuid]
936 1c3231aa Thomas Thrainer
    self.nodes_ip = dict((uuid, node.secondary_ip) for (uuid, node)
937 1c3231aa Thomas Thrainer
                         in self.cfg.GetMultiNodeInfo(self.all_node_uuids))
938 87e25be1 Thomas Thrainer
939 87e25be1 Thomas Thrainer
    if self.failover:
940 87e25be1 Thomas Thrainer
      feedback_fn("Failover instance %s" % self.instance.name)
941 87e25be1 Thomas Thrainer
      self._ExecFailover()
942 87e25be1 Thomas Thrainer
    else:
943 87e25be1 Thomas Thrainer
      feedback_fn("Migrating instance %s" % self.instance.name)
944 87e25be1 Thomas Thrainer
945 87e25be1 Thomas Thrainer
      if self.cleanup:
946 87e25be1 Thomas Thrainer
        return self._ExecCleanup()
947 87e25be1 Thomas Thrainer
      else:
948 87e25be1 Thomas Thrainer
        return self._ExecMigration()