Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance_migration.py @ 32389d91

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 5eacbcae Thomas Thrainer
from ganeti.cmdlib.common import ExpandInstanceName, \
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 87e25be1 Thomas Thrainer
    instance = lu.cfg.GetInstanceInfo(lu.op.instance_name)
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 87e25be1 Thomas Thrainer
      TLMigrateInstance(self, self.op.instance_name, False, True, False,
122 87e25be1 Thomas Thrainer
                        self.op.ignore_consistency, True,
123 87e25be1 Thomas Thrainer
                        self.op.shutdown_timeout, self.op.ignore_ipolicy)
124 87e25be1 Thomas Thrainer
125 87e25be1 Thomas Thrainer
    self.tasklets = [self._migrater]
126 87e25be1 Thomas Thrainer
127 87e25be1 Thomas Thrainer
  def DeclareLocks(self, level):
128 87e25be1 Thomas Thrainer
    _DeclareLocksForMigration(self, level)
129 87e25be1 Thomas Thrainer
130 87e25be1 Thomas Thrainer
  def BuildHooksEnv(self):
131 87e25be1 Thomas Thrainer
    """Build hooks env.
132 87e25be1 Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

280 87e25be1 Thomas Thrainer
    This checks that the instance is in the cluster.
281 87e25be1 Thomas Thrainer

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

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

470 87e25be1 Thomas Thrainer
    This uses our own step-based rpc call.
471 87e25be1 Thomas Thrainer

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

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

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

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

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

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