Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance_migration.py @ da27bc7d

History | View | Annotate | Download (37.8 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

224 87e25be1 Thomas Thrainer
    """
225 87e25be1 Thomas Thrainer
    instance = self._migrater.instance
226 1c3231aa Thomas Thrainer
    snode_uuids = list(instance.secondary_nodes)
227 1c3231aa Thomas Thrainer
    nl = [self.cfg.GetMasterNode(), instance.primary_node] + snode_uuids
228 7b5f0674 Dimitris Aragiorgis
    nl.append(self._migrater.target_node_uuid)
229 87e25be1 Thomas Thrainer
    return (nl, nl)
230 87e25be1 Thomas Thrainer
231 87e25be1 Thomas Thrainer
232 87e25be1 Thomas Thrainer
class TLMigrateInstance(Tasklet):
233 87e25be1 Thomas Thrainer
  """Tasklet class for instance migration.
234 87e25be1 Thomas Thrainer

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

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

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

287 87e25be1 Thomas Thrainer
    This checks that the instance is in the cluster.
288 87e25be1 Thomas Thrainer

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

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

479 87e25be1 Thomas Thrainer
    This uses our own step-based rpc call.
480 87e25be1 Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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