Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance_migration.py @ 74676af4

History | View | Annotate | Download (37.7 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 da4a52a3 Thomas Thrainer
                        False, True, False, 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 da4a52a3 Thomas Thrainer
      TLMigrateInstance(self, self.op.instance_uuid, self.op.instance_name,
181 da4a52a3 Thomas Thrainer
                        self.op.cleanup, 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 da4a52a3 Thomas Thrainer
  def __init__(self, lu, instance_uuid, instance_name, cleanup, failover,
259 da4a52a3 Thomas Thrainer
               fallback, ignore_consistency, allow_runtime_changes,
260 da4a52a3 Thomas Thrainer
               shutdown_timeout, 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 da4a52a3 Thomas Thrainer
    self.instance_uuid = instance_uuid
268 87e25be1 Thomas Thrainer
    self.instance_name = instance_name
269 87e25be1 Thomas Thrainer
    self.cleanup = cleanup
270 87e25be1 Thomas Thrainer
    self.live = False # will be overridden later
271 87e25be1 Thomas Thrainer
    self.failover = failover
272 87e25be1 Thomas Thrainer
    self.fallback = fallback
273 87e25be1 Thomas Thrainer
    self.ignore_consistency = ignore_consistency
274 87e25be1 Thomas Thrainer
    self.shutdown_timeout = shutdown_timeout
275 87e25be1 Thomas Thrainer
    self.ignore_ipolicy = ignore_ipolicy
276 87e25be1 Thomas Thrainer
    self.allow_runtime_changes = allow_runtime_changes
277 87e25be1 Thomas Thrainer
278 87e25be1 Thomas Thrainer
  def CheckPrereq(self):
279 87e25be1 Thomas Thrainer
    """Check prerequisites.
280 87e25be1 Thomas Thrainer

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

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

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

473 87e25be1 Thomas Thrainer
    This uses our own step-based rpc call.
474 87e25be1 Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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