Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance_migration.py @ 896cc964

History | View | Annotate | Download (37.5 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.instance.disks,
484 87e25be1 Thomas Thrainer
                                             self.instance))
485 87e25be1 Thomas Thrainer
      min_percent = 100
486 1c3231aa Thomas Thrainer
      for node_uuid, nres in result.items():
487 1c3231aa Thomas Thrainer
        nres.Raise("Cannot resync disks on node %s" %
488 1c3231aa Thomas Thrainer
                   self.cfg.GetNodeName(node_uuid))
489 87e25be1 Thomas Thrainer
        node_done, node_percent = nres.payload
490 87e25be1 Thomas Thrainer
        all_done = all_done and node_done
491 87e25be1 Thomas Thrainer
        if node_percent is not None:
492 87e25be1 Thomas Thrainer
          min_percent = min(min_percent, node_percent)
493 87e25be1 Thomas Thrainer
      if not all_done:
494 87e25be1 Thomas Thrainer
        if min_percent < 100:
495 87e25be1 Thomas Thrainer
          self.feedback_fn("   - progress: %.1f%%" % min_percent)
496 87e25be1 Thomas Thrainer
        time.sleep(2)
497 87e25be1 Thomas Thrainer
498 1c3231aa Thomas Thrainer
  def _EnsureSecondary(self, node_uuid):
499 87e25be1 Thomas Thrainer
    """Demote a node to secondary.
500 87e25be1 Thomas Thrainer

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

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

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

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

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

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

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

647 87e25be1 Thomas Thrainer
    The migrate is done by:
648 87e25be1 Thomas Thrainer
      - change the disks into dual-master mode
649 87e25be1 Thomas Thrainer
      - wait until disks are fully synchronized again
650 87e25be1 Thomas Thrainer
      - migrate the instance
651 87e25be1 Thomas Thrainer
      - change disks on the new secondary node (the old primary) to secondary
652 87e25be1 Thomas Thrainer
      - wait until disks are fully synchronized
653 87e25be1 Thomas Thrainer
      - change disks into single-master mode
654 87e25be1 Thomas Thrainer

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

835 87e25be1 Thomas Thrainer
    The failover is done by shutting it down on its present node and
836 87e25be1 Thomas Thrainer
    starting it on the secondary.
837 87e25be1 Thomas Thrainer

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

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