Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance.py @ 763ad5be

History | View | Annotate | Download (200.4 kB)

1 22b7f6f8 Thomas Thrainer
#
2 22b7f6f8 Thomas Thrainer
#
3 22b7f6f8 Thomas Thrainer
4 22b7f6f8 Thomas Thrainer
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5 22b7f6f8 Thomas Thrainer
#
6 22b7f6f8 Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 22b7f6f8 Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 22b7f6f8 Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 22b7f6f8 Thomas Thrainer
# (at your option) any later version.
10 22b7f6f8 Thomas Thrainer
#
11 22b7f6f8 Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 22b7f6f8 Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 22b7f6f8 Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 22b7f6f8 Thomas Thrainer
# General Public License for more details.
15 22b7f6f8 Thomas Thrainer
#
16 22b7f6f8 Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 22b7f6f8 Thomas Thrainer
# along with this program; if not, write to the Free Software
18 22b7f6f8 Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 22b7f6f8 Thomas Thrainer
# 02110-1301, USA.
20 22b7f6f8 Thomas Thrainer
21 22b7f6f8 Thomas Thrainer
22 22b7f6f8 Thomas Thrainer
"""Logical units dealing with instances."""
23 22b7f6f8 Thomas Thrainer
24 22b7f6f8 Thomas Thrainer
import OpenSSL
25 22b7f6f8 Thomas Thrainer
import copy
26 22b7f6f8 Thomas Thrainer
import itertools
27 22b7f6f8 Thomas Thrainer
import logging
28 22b7f6f8 Thomas Thrainer
import operator
29 22b7f6f8 Thomas Thrainer
import os
30 22b7f6f8 Thomas Thrainer
import time
31 22b7f6f8 Thomas Thrainer
32 22b7f6f8 Thomas Thrainer
from ganeti import compat
33 22b7f6f8 Thomas Thrainer
from ganeti import constants
34 22b7f6f8 Thomas Thrainer
from ganeti import errors
35 22b7f6f8 Thomas Thrainer
from ganeti import ht
36 22b7f6f8 Thomas Thrainer
from ganeti import hypervisor
37 22b7f6f8 Thomas Thrainer
from ganeti import locking
38 22b7f6f8 Thomas Thrainer
from ganeti.masterd import iallocator
39 22b7f6f8 Thomas Thrainer
from ganeti import masterd
40 22b7f6f8 Thomas Thrainer
from ganeti import netutils
41 22b7f6f8 Thomas Thrainer
from ganeti import objects
42 22b7f6f8 Thomas Thrainer
from ganeti import opcodes
43 22b7f6f8 Thomas Thrainer
from ganeti import pathutils
44 22b7f6f8 Thomas Thrainer
from ganeti import qlang
45 22b7f6f8 Thomas Thrainer
from ganeti import rpc
46 22b7f6f8 Thomas Thrainer
from ganeti import utils
47 22b7f6f8 Thomas Thrainer
from ganeti import query
48 22b7f6f8 Thomas Thrainer
49 22b7f6f8 Thomas Thrainer
from ganeti.cmdlib.base import NoHooksLU, LogicalUnit, _QueryBase, \
50 22b7f6f8 Thomas Thrainer
  ResultWithJobs, Tasklet
51 22b7f6f8 Thomas Thrainer
52 22b7f6f8 Thomas Thrainer
from ganeti.cmdlib.common import INSTANCE_ONLINE, INSTANCE_DOWN, \
53 22b7f6f8 Thomas Thrainer
  INSTANCE_NOT_RUNNING, CAN_CHANGE_INSTANCE_OFFLINE, _CheckNodeOnline, \
54 22b7f6f8 Thomas Thrainer
  _ShareAll, _GetDefaultIAllocator, _CheckInstanceNodeGroups, \
55 22b7f6f8 Thomas Thrainer
  _LoadNodeEvacResult, _CheckIAllocatorOrNode, _CheckParamsNotGlobal, \
56 22b7f6f8 Thomas Thrainer
  _IsExclusiveStorageEnabledNode, _CheckHVParams, _CheckOSParams, \
57 22b7f6f8 Thomas Thrainer
  _GetWantedInstances, _CheckInstancesNodeGroups, _AnnotateDiskParams, \
58 763ad5be Thomas Thrainer
  _GetUpdatedParams, _ExpandInstanceName, _ComputeIPolicySpecViolation, \
59 22b7f6f8 Thomas Thrainer
  _CheckInstanceState, _ExpandNodeName
60 763ad5be Thomas Thrainer
from ganeti.cmdlib.instance_storage import _CreateDisks, \
61 763ad5be Thomas Thrainer
  _CheckNodesFreeDiskPerVG, _WipeDisks, _WaitForSync, _CheckDiskConsistency, \
62 763ad5be Thomas Thrainer
  _IsExclusiveStorageEnabledNodeName, _CreateSingleBlockDev, _ComputeDisks, \
63 763ad5be Thomas Thrainer
  _CheckRADOSFreeSpace, _ComputeDiskSizePerVG, _GenerateDiskTemplate, \
64 763ad5be Thomas Thrainer
  _CreateBlockDev, _StartInstanceDisks, _ShutdownInstanceDisks, \
65 763ad5be Thomas Thrainer
  _AssembleInstanceDisks, _ExpandCheckDisks
66 763ad5be Thomas Thrainer
from ganeti.cmdlib.instance_utils import _BuildInstanceHookEnvByObject, \
67 763ad5be Thomas Thrainer
  _GetClusterDomainSecret, _BuildInstanceHookEnv, _NICListToTuple, \
68 763ad5be Thomas Thrainer
  _NICToTuple, _CheckNodeNotDrained, _RemoveInstance, _CopyLockList, \
69 763ad5be Thomas Thrainer
  _ReleaseLocks, _CheckNodeVmCapable, _CheckTargetNodeIPolicy, \
70 763ad5be Thomas Thrainer
  _GetInstanceInfoText, _RemoveDisks
71 22b7f6f8 Thomas Thrainer
72 22b7f6f8 Thomas Thrainer
import ganeti.masterd.instance
73 22b7f6f8 Thomas Thrainer
74 22b7f6f8 Thomas Thrainer
75 22b7f6f8 Thomas Thrainer
#: Type description for changes as returned by L{ApplyContainerMods}'s
76 22b7f6f8 Thomas Thrainer
#: callbacks
77 22b7f6f8 Thomas Thrainer
_TApplyContModsCbChanges = \
78 22b7f6f8 Thomas Thrainer
  ht.TMaybeListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([
79 22b7f6f8 Thomas Thrainer
    ht.TNonEmptyString,
80 22b7f6f8 Thomas Thrainer
    ht.TAny,
81 22b7f6f8 Thomas Thrainer
    ])))
82 22b7f6f8 Thomas Thrainer
83 22b7f6f8 Thomas Thrainer
84 22b7f6f8 Thomas Thrainer
def _CheckHostnameSane(lu, name):
85 22b7f6f8 Thomas Thrainer
  """Ensures that a given hostname resolves to a 'sane' name.
86 22b7f6f8 Thomas Thrainer

87 22b7f6f8 Thomas Thrainer
  The given name is required to be a prefix of the resolved hostname,
88 22b7f6f8 Thomas Thrainer
  to prevent accidental mismatches.
89 22b7f6f8 Thomas Thrainer

90 22b7f6f8 Thomas Thrainer
  @param lu: the logical unit on behalf of which we're checking
91 22b7f6f8 Thomas Thrainer
  @param name: the name we should resolve and check
92 22b7f6f8 Thomas Thrainer
  @return: the resolved hostname object
93 22b7f6f8 Thomas Thrainer

94 22b7f6f8 Thomas Thrainer
  """
95 22b7f6f8 Thomas Thrainer
  hostname = netutils.GetHostname(name=name)
96 22b7f6f8 Thomas Thrainer
  if hostname.name != name:
97 22b7f6f8 Thomas Thrainer
    lu.LogInfo("Resolved given name '%s' to '%s'", name, hostname.name)
98 22b7f6f8 Thomas Thrainer
  if not utils.MatchNameComponent(name, [hostname.name]):
99 22b7f6f8 Thomas Thrainer
    raise errors.OpPrereqError(("Resolved hostname '%s' does not look the"
100 22b7f6f8 Thomas Thrainer
                                " same as given hostname '%s'") %
101 22b7f6f8 Thomas Thrainer
                               (hostname.name, name), errors.ECODE_INVAL)
102 22b7f6f8 Thomas Thrainer
  return hostname
103 22b7f6f8 Thomas Thrainer
104 22b7f6f8 Thomas Thrainer
105 22b7f6f8 Thomas Thrainer
def _CheckOpportunisticLocking(op):
106 22b7f6f8 Thomas Thrainer
  """Generate error if opportunistic locking is not possible.
107 22b7f6f8 Thomas Thrainer

108 22b7f6f8 Thomas Thrainer
  """
109 22b7f6f8 Thomas Thrainer
  if op.opportunistic_locking and not op.iallocator:
110 22b7f6f8 Thomas Thrainer
    raise errors.OpPrereqError("Opportunistic locking is only available in"
111 22b7f6f8 Thomas Thrainer
                               " combination with an instance allocator",
112 22b7f6f8 Thomas Thrainer
                               errors.ECODE_INVAL)
113 22b7f6f8 Thomas Thrainer
114 22b7f6f8 Thomas Thrainer
115 22b7f6f8 Thomas Thrainer
def _CreateInstanceAllocRequest(op, disks, nics, beparams, node_whitelist):
116 22b7f6f8 Thomas Thrainer
  """Wrapper around IAReqInstanceAlloc.
117 22b7f6f8 Thomas Thrainer

118 22b7f6f8 Thomas Thrainer
  @param op: The instance opcode
119 22b7f6f8 Thomas Thrainer
  @param disks: The computed disks
120 22b7f6f8 Thomas Thrainer
  @param nics: The computed nics
121 22b7f6f8 Thomas Thrainer
  @param beparams: The full filled beparams
122 22b7f6f8 Thomas Thrainer
  @param node_whitelist: List of nodes which should appear as online to the
123 22b7f6f8 Thomas Thrainer
    allocator (unless the node is already marked offline)
124 22b7f6f8 Thomas Thrainer

125 22b7f6f8 Thomas Thrainer
  @returns: A filled L{iallocator.IAReqInstanceAlloc}
126 22b7f6f8 Thomas Thrainer

127 22b7f6f8 Thomas Thrainer
  """
128 22b7f6f8 Thomas Thrainer
  spindle_use = beparams[constants.BE_SPINDLE_USE]
129 22b7f6f8 Thomas Thrainer
  return iallocator.IAReqInstanceAlloc(name=op.instance_name,
130 22b7f6f8 Thomas Thrainer
                                       disk_template=op.disk_template,
131 22b7f6f8 Thomas Thrainer
                                       tags=op.tags,
132 22b7f6f8 Thomas Thrainer
                                       os=op.os_type,
133 22b7f6f8 Thomas Thrainer
                                       vcpus=beparams[constants.BE_VCPUS],
134 22b7f6f8 Thomas Thrainer
                                       memory=beparams[constants.BE_MAXMEM],
135 22b7f6f8 Thomas Thrainer
                                       spindle_use=spindle_use,
136 22b7f6f8 Thomas Thrainer
                                       disks=disks,
137 22b7f6f8 Thomas Thrainer
                                       nics=[n.ToDict() for n in nics],
138 22b7f6f8 Thomas Thrainer
                                       hypervisor=op.hypervisor,
139 22b7f6f8 Thomas Thrainer
                                       node_whitelist=node_whitelist)
140 22b7f6f8 Thomas Thrainer
141 22b7f6f8 Thomas Thrainer
142 22b7f6f8 Thomas Thrainer
def _ComputeFullBeParams(op, cluster):
143 22b7f6f8 Thomas Thrainer
  """Computes the full beparams.
144 22b7f6f8 Thomas Thrainer

145 22b7f6f8 Thomas Thrainer
  @param op: The instance opcode
146 22b7f6f8 Thomas Thrainer
  @param cluster: The cluster config object
147 22b7f6f8 Thomas Thrainer

148 22b7f6f8 Thomas Thrainer
  @return: The fully filled beparams
149 22b7f6f8 Thomas Thrainer

150 22b7f6f8 Thomas Thrainer
  """
151 22b7f6f8 Thomas Thrainer
  default_beparams = cluster.beparams[constants.PP_DEFAULT]
152 22b7f6f8 Thomas Thrainer
  for param, value in op.beparams.iteritems():
153 22b7f6f8 Thomas Thrainer
    if value == constants.VALUE_AUTO:
154 22b7f6f8 Thomas Thrainer
      op.beparams[param] = default_beparams[param]
155 22b7f6f8 Thomas Thrainer
  objects.UpgradeBeParams(op.beparams)
156 22b7f6f8 Thomas Thrainer
  utils.ForceDictType(op.beparams, constants.BES_PARAMETER_TYPES)
157 22b7f6f8 Thomas Thrainer
  return cluster.SimpleFillBE(op.beparams)
158 22b7f6f8 Thomas Thrainer
159 22b7f6f8 Thomas Thrainer
160 22b7f6f8 Thomas Thrainer
def _ComputeNics(op, cluster, default_ip, cfg, ec_id):
161 22b7f6f8 Thomas Thrainer
  """Computes the nics.
162 22b7f6f8 Thomas Thrainer

163 22b7f6f8 Thomas Thrainer
  @param op: The instance opcode
164 22b7f6f8 Thomas Thrainer
  @param cluster: Cluster configuration object
165 22b7f6f8 Thomas Thrainer
  @param default_ip: The default ip to assign
166 22b7f6f8 Thomas Thrainer
  @param cfg: An instance of the configuration object
167 22b7f6f8 Thomas Thrainer
  @param ec_id: Execution context ID
168 22b7f6f8 Thomas Thrainer

169 22b7f6f8 Thomas Thrainer
  @returns: The build up nics
170 22b7f6f8 Thomas Thrainer

171 22b7f6f8 Thomas Thrainer
  """
172 22b7f6f8 Thomas Thrainer
  nics = []
173 22b7f6f8 Thomas Thrainer
  for nic in op.nics:
174 22b7f6f8 Thomas Thrainer
    nic_mode_req = nic.get(constants.INIC_MODE, None)
175 22b7f6f8 Thomas Thrainer
    nic_mode = nic_mode_req
176 22b7f6f8 Thomas Thrainer
    if nic_mode is None or nic_mode == constants.VALUE_AUTO:
177 22b7f6f8 Thomas Thrainer
      nic_mode = cluster.nicparams[constants.PP_DEFAULT][constants.NIC_MODE]
178 22b7f6f8 Thomas Thrainer
179 22b7f6f8 Thomas Thrainer
    net = nic.get(constants.INIC_NETWORK, None)
180 22b7f6f8 Thomas Thrainer
    link = nic.get(constants.NIC_LINK, None)
181 22b7f6f8 Thomas Thrainer
    ip = nic.get(constants.INIC_IP, None)
182 22b7f6f8 Thomas Thrainer
183 22b7f6f8 Thomas Thrainer
    if net is None or net.lower() == constants.VALUE_NONE:
184 22b7f6f8 Thomas Thrainer
      net = None
185 22b7f6f8 Thomas Thrainer
    else:
186 22b7f6f8 Thomas Thrainer
      if nic_mode_req is not None or link is not None:
187 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("If network is given, no mode or link"
188 22b7f6f8 Thomas Thrainer
                                   " is allowed to be passed",
189 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
190 22b7f6f8 Thomas Thrainer
191 22b7f6f8 Thomas Thrainer
    # ip validity checks
192 22b7f6f8 Thomas Thrainer
    if ip is None or ip.lower() == constants.VALUE_NONE:
193 22b7f6f8 Thomas Thrainer
      nic_ip = None
194 22b7f6f8 Thomas Thrainer
    elif ip.lower() == constants.VALUE_AUTO:
195 22b7f6f8 Thomas Thrainer
      if not op.name_check:
196 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("IP address set to auto but name checks"
197 22b7f6f8 Thomas Thrainer
                                   " have been skipped",
198 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
199 22b7f6f8 Thomas Thrainer
      nic_ip = default_ip
200 22b7f6f8 Thomas Thrainer
    else:
201 22b7f6f8 Thomas Thrainer
      # We defer pool operations until later, so that the iallocator has
202 22b7f6f8 Thomas Thrainer
      # filled in the instance's node(s) dimara
203 22b7f6f8 Thomas Thrainer
      if ip.lower() == constants.NIC_IP_POOL:
204 22b7f6f8 Thomas Thrainer
        if net is None:
205 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("if ip=pool, parameter network"
206 22b7f6f8 Thomas Thrainer
                                     " must be passed too",
207 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
208 22b7f6f8 Thomas Thrainer
209 22b7f6f8 Thomas Thrainer
      elif not netutils.IPAddress.IsValid(ip):
210 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid IP address '%s'" % ip,
211 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
212 22b7f6f8 Thomas Thrainer
213 22b7f6f8 Thomas Thrainer
      nic_ip = ip
214 22b7f6f8 Thomas Thrainer
215 22b7f6f8 Thomas Thrainer
    # TODO: check the ip address for uniqueness
216 22b7f6f8 Thomas Thrainer
    if nic_mode == constants.NIC_MODE_ROUTED and not nic_ip:
217 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Routed nic mode requires an ip address",
218 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
219 22b7f6f8 Thomas Thrainer
220 22b7f6f8 Thomas Thrainer
    # MAC address verification
221 22b7f6f8 Thomas Thrainer
    mac = nic.get(constants.INIC_MAC, constants.VALUE_AUTO)
222 22b7f6f8 Thomas Thrainer
    if mac not in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
223 22b7f6f8 Thomas Thrainer
      mac = utils.NormalizeAndValidateMac(mac)
224 22b7f6f8 Thomas Thrainer
225 22b7f6f8 Thomas Thrainer
      try:
226 22b7f6f8 Thomas Thrainer
        # TODO: We need to factor this out
227 22b7f6f8 Thomas Thrainer
        cfg.ReserveMAC(mac, ec_id)
228 22b7f6f8 Thomas Thrainer
      except errors.ReservationError:
229 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("MAC address %s already in use"
230 22b7f6f8 Thomas Thrainer
                                   " in cluster" % mac,
231 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
232 22b7f6f8 Thomas Thrainer
233 22b7f6f8 Thomas Thrainer
    #  Build nic parameters
234 22b7f6f8 Thomas Thrainer
    nicparams = {}
235 22b7f6f8 Thomas Thrainer
    if nic_mode_req:
236 22b7f6f8 Thomas Thrainer
      nicparams[constants.NIC_MODE] = nic_mode
237 22b7f6f8 Thomas Thrainer
    if link:
238 22b7f6f8 Thomas Thrainer
      nicparams[constants.NIC_LINK] = link
239 22b7f6f8 Thomas Thrainer
240 22b7f6f8 Thomas Thrainer
    check_params = cluster.SimpleFillNIC(nicparams)
241 22b7f6f8 Thomas Thrainer
    objects.NIC.CheckParameterSyntax(check_params)
242 22b7f6f8 Thomas Thrainer
    net_uuid = cfg.LookupNetwork(net)
243 22b7f6f8 Thomas Thrainer
    name = nic.get(constants.INIC_NAME, None)
244 22b7f6f8 Thomas Thrainer
    if name is not None and name.lower() == constants.VALUE_NONE:
245 22b7f6f8 Thomas Thrainer
      name = None
246 22b7f6f8 Thomas Thrainer
    nic_obj = objects.NIC(mac=mac, ip=nic_ip, name=name,
247 22b7f6f8 Thomas Thrainer
                          network=net_uuid, nicparams=nicparams)
248 22b7f6f8 Thomas Thrainer
    nic_obj.uuid = cfg.GenerateUniqueID(ec_id)
249 22b7f6f8 Thomas Thrainer
    nics.append(nic_obj)
250 22b7f6f8 Thomas Thrainer
251 22b7f6f8 Thomas Thrainer
  return nics
252 22b7f6f8 Thomas Thrainer
253 22b7f6f8 Thomas Thrainer
254 22b7f6f8 Thomas Thrainer
def _CheckForConflictingIp(lu, ip, node):
255 22b7f6f8 Thomas Thrainer
  """In case of conflicting IP address raise error.
256 22b7f6f8 Thomas Thrainer

257 22b7f6f8 Thomas Thrainer
  @type ip: string
258 22b7f6f8 Thomas Thrainer
  @param ip: IP address
259 22b7f6f8 Thomas Thrainer
  @type node: string
260 22b7f6f8 Thomas Thrainer
  @param node: node name
261 22b7f6f8 Thomas Thrainer

262 22b7f6f8 Thomas Thrainer
  """
263 22b7f6f8 Thomas Thrainer
  (conf_net, _) = lu.cfg.CheckIPInNodeGroup(ip, node)
264 22b7f6f8 Thomas Thrainer
  if conf_net is not None:
265 22b7f6f8 Thomas Thrainer
    raise errors.OpPrereqError(("The requested IP address (%s) belongs to"
266 22b7f6f8 Thomas Thrainer
                                " network %s, but the target NIC does not." %
267 22b7f6f8 Thomas Thrainer
                                (ip, conf_net)),
268 22b7f6f8 Thomas Thrainer
                               errors.ECODE_STATE)
269 22b7f6f8 Thomas Thrainer
270 22b7f6f8 Thomas Thrainer
  return (None, None)
271 22b7f6f8 Thomas Thrainer
272 22b7f6f8 Thomas Thrainer
273 22b7f6f8 Thomas Thrainer
def _ComputeIPolicyInstanceSpecViolation(
274 22b7f6f8 Thomas Thrainer
  ipolicy, instance_spec, disk_template,
275 22b7f6f8 Thomas Thrainer
  _compute_fn=_ComputeIPolicySpecViolation):
276 22b7f6f8 Thomas Thrainer
  """Compute if instance specs meets the specs of ipolicy.
277 22b7f6f8 Thomas Thrainer

278 22b7f6f8 Thomas Thrainer
  @type ipolicy: dict
279 22b7f6f8 Thomas Thrainer
  @param ipolicy: The ipolicy to verify against
280 22b7f6f8 Thomas Thrainer
  @param instance_spec: dict
281 22b7f6f8 Thomas Thrainer
  @param instance_spec: The instance spec to verify
282 22b7f6f8 Thomas Thrainer
  @type disk_template: string
283 22b7f6f8 Thomas Thrainer
  @param disk_template: the disk template of the instance
284 22b7f6f8 Thomas Thrainer
  @param _compute_fn: The function to verify ipolicy (unittest only)
285 22b7f6f8 Thomas Thrainer
  @see: L{_ComputeIPolicySpecViolation}
286 22b7f6f8 Thomas Thrainer

287 22b7f6f8 Thomas Thrainer
  """
288 22b7f6f8 Thomas Thrainer
  mem_size = instance_spec.get(constants.ISPEC_MEM_SIZE, None)
289 22b7f6f8 Thomas Thrainer
  cpu_count = instance_spec.get(constants.ISPEC_CPU_COUNT, None)
290 22b7f6f8 Thomas Thrainer
  disk_count = instance_spec.get(constants.ISPEC_DISK_COUNT, 0)
291 22b7f6f8 Thomas Thrainer
  disk_sizes = instance_spec.get(constants.ISPEC_DISK_SIZE, [])
292 22b7f6f8 Thomas Thrainer
  nic_count = instance_spec.get(constants.ISPEC_NIC_COUNT, 0)
293 22b7f6f8 Thomas Thrainer
  spindle_use = instance_spec.get(constants.ISPEC_SPINDLE_USE, None)
294 22b7f6f8 Thomas Thrainer
295 22b7f6f8 Thomas Thrainer
  return _compute_fn(ipolicy, mem_size, cpu_count, disk_count, nic_count,
296 22b7f6f8 Thomas Thrainer
                     disk_sizes, spindle_use, disk_template)
297 22b7f6f8 Thomas Thrainer
298 22b7f6f8 Thomas Thrainer
299 22b7f6f8 Thomas Thrainer
def _CheckOSVariant(os_obj, name):
300 22b7f6f8 Thomas Thrainer
  """Check whether an OS name conforms to the os variants specification.
301 22b7f6f8 Thomas Thrainer

302 22b7f6f8 Thomas Thrainer
  @type os_obj: L{objects.OS}
303 22b7f6f8 Thomas Thrainer
  @param os_obj: OS object to check
304 22b7f6f8 Thomas Thrainer
  @type name: string
305 22b7f6f8 Thomas Thrainer
  @param name: OS name passed by the user, to check for validity
306 22b7f6f8 Thomas Thrainer

307 22b7f6f8 Thomas Thrainer
  """
308 22b7f6f8 Thomas Thrainer
  variant = objects.OS.GetVariant(name)
309 22b7f6f8 Thomas Thrainer
  if not os_obj.supported_variants:
310 22b7f6f8 Thomas Thrainer
    if variant:
311 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("OS '%s' doesn't support variants ('%s'"
312 22b7f6f8 Thomas Thrainer
                                 " passed)" % (os_obj.name, variant),
313 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
314 22b7f6f8 Thomas Thrainer
    return
315 22b7f6f8 Thomas Thrainer
  if not variant:
316 22b7f6f8 Thomas Thrainer
    raise errors.OpPrereqError("OS name must include a variant",
317 22b7f6f8 Thomas Thrainer
                               errors.ECODE_INVAL)
318 22b7f6f8 Thomas Thrainer
319 22b7f6f8 Thomas Thrainer
  if variant not in os_obj.supported_variants:
320 22b7f6f8 Thomas Thrainer
    raise errors.OpPrereqError("Unsupported OS variant", errors.ECODE_INVAL)
321 22b7f6f8 Thomas Thrainer
322 22b7f6f8 Thomas Thrainer
323 22b7f6f8 Thomas Thrainer
def _CheckNodeHasOS(lu, node, os_name, force_variant):
324 22b7f6f8 Thomas Thrainer
  """Ensure that a node supports a given OS.
325 22b7f6f8 Thomas Thrainer

326 22b7f6f8 Thomas Thrainer
  @param lu: the LU on behalf of which we make the check
327 22b7f6f8 Thomas Thrainer
  @param node: the node to check
328 22b7f6f8 Thomas Thrainer
  @param os_name: the OS to query about
329 22b7f6f8 Thomas Thrainer
  @param force_variant: whether to ignore variant errors
330 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node is not supporting the OS
331 22b7f6f8 Thomas Thrainer

332 22b7f6f8 Thomas Thrainer
  """
333 22b7f6f8 Thomas Thrainer
  result = lu.rpc.call_os_get(node, os_name)
334 22b7f6f8 Thomas Thrainer
  result.Raise("OS '%s' not in supported OS list for node %s" %
335 22b7f6f8 Thomas Thrainer
               (os_name, node),
336 22b7f6f8 Thomas Thrainer
               prereq=True, ecode=errors.ECODE_INVAL)
337 22b7f6f8 Thomas Thrainer
  if not force_variant:
338 22b7f6f8 Thomas Thrainer
    _CheckOSVariant(result.payload, os_name)
339 22b7f6f8 Thomas Thrainer
340 22b7f6f8 Thomas Thrainer
341 22b7f6f8 Thomas Thrainer
def _CheckNicsBridgesExist(lu, target_nics, target_node):
342 22b7f6f8 Thomas Thrainer
  """Check that the brigdes needed by a list of nics exist.
343 22b7f6f8 Thomas Thrainer

344 22b7f6f8 Thomas Thrainer
  """
345 22b7f6f8 Thomas Thrainer
  cluster = lu.cfg.GetClusterInfo()
346 22b7f6f8 Thomas Thrainer
  paramslist = [cluster.SimpleFillNIC(nic.nicparams) for nic in target_nics]
347 22b7f6f8 Thomas Thrainer
  brlist = [params[constants.NIC_LINK] for params in paramslist
348 22b7f6f8 Thomas Thrainer
            if params[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED]
349 22b7f6f8 Thomas Thrainer
  if brlist:
350 22b7f6f8 Thomas Thrainer
    result = lu.rpc.call_bridges_exist(target_node, brlist)
351 22b7f6f8 Thomas Thrainer
    result.Raise("Error checking bridges on destination node '%s'" %
352 22b7f6f8 Thomas Thrainer
                 target_node, prereq=True, ecode=errors.ECODE_ENVIRON)
353 22b7f6f8 Thomas Thrainer
354 22b7f6f8 Thomas Thrainer
355 22b7f6f8 Thomas Thrainer
def _CheckNodeFreeMemory(lu, node, reason, requested, hypervisor_name):
356 22b7f6f8 Thomas Thrainer
  """Checks if a node has enough free memory.
357 22b7f6f8 Thomas Thrainer

358 22b7f6f8 Thomas Thrainer
  This function checks if a given node has the needed amount of free
359 22b7f6f8 Thomas Thrainer
  memory. In case the node has less memory or we cannot get the
360 22b7f6f8 Thomas Thrainer
  information from the node, this function raises an OpPrereqError
361 22b7f6f8 Thomas Thrainer
  exception.
362 22b7f6f8 Thomas Thrainer

363 22b7f6f8 Thomas Thrainer
  @type lu: C{LogicalUnit}
364 22b7f6f8 Thomas Thrainer
  @param lu: a logical unit from which we get configuration data
365 22b7f6f8 Thomas Thrainer
  @type node: C{str}
366 22b7f6f8 Thomas Thrainer
  @param node: the node to check
367 22b7f6f8 Thomas Thrainer
  @type reason: C{str}
368 22b7f6f8 Thomas Thrainer
  @param reason: string to use in the error message
369 22b7f6f8 Thomas Thrainer
  @type requested: C{int}
370 22b7f6f8 Thomas Thrainer
  @param requested: the amount of memory in MiB to check for
371 22b7f6f8 Thomas Thrainer
  @type hypervisor_name: C{str}
372 22b7f6f8 Thomas Thrainer
  @param hypervisor_name: the hypervisor to ask for memory stats
373 22b7f6f8 Thomas Thrainer
  @rtype: integer
374 22b7f6f8 Thomas Thrainer
  @return: node current free memory
375 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node doesn't have enough memory, or
376 22b7f6f8 Thomas Thrainer
      we cannot check the node
377 22b7f6f8 Thomas Thrainer

378 22b7f6f8 Thomas Thrainer
  """
379 22b7f6f8 Thomas Thrainer
  nodeinfo = lu.rpc.call_node_info([node], None, [hypervisor_name], False)
380 22b7f6f8 Thomas Thrainer
  nodeinfo[node].Raise("Can't get data from node %s" % node,
381 22b7f6f8 Thomas Thrainer
                       prereq=True, ecode=errors.ECODE_ENVIRON)
382 22b7f6f8 Thomas Thrainer
  (_, _, (hv_info, )) = nodeinfo[node].payload
383 22b7f6f8 Thomas Thrainer
384 22b7f6f8 Thomas Thrainer
  free_mem = hv_info.get("memory_free", None)
385 22b7f6f8 Thomas Thrainer
  if not isinstance(free_mem, int):
386 22b7f6f8 Thomas Thrainer
    raise errors.OpPrereqError("Can't compute free memory on node %s, result"
387 22b7f6f8 Thomas Thrainer
                               " was '%s'" % (node, free_mem),
388 22b7f6f8 Thomas Thrainer
                               errors.ECODE_ENVIRON)
389 22b7f6f8 Thomas Thrainer
  if requested > free_mem:
390 22b7f6f8 Thomas Thrainer
    raise errors.OpPrereqError("Not enough memory on node %s for %s:"
391 22b7f6f8 Thomas Thrainer
                               " needed %s MiB, available %s MiB" %
392 22b7f6f8 Thomas Thrainer
                               (node, reason, requested, free_mem),
393 22b7f6f8 Thomas Thrainer
                               errors.ECODE_NORES)
394 22b7f6f8 Thomas Thrainer
  return free_mem
395 22b7f6f8 Thomas Thrainer
396 22b7f6f8 Thomas Thrainer
397 22b7f6f8 Thomas Thrainer
class LUInstanceCreate(LogicalUnit):
398 22b7f6f8 Thomas Thrainer
  """Create an instance.
399 22b7f6f8 Thomas Thrainer

400 22b7f6f8 Thomas Thrainer
  """
401 22b7f6f8 Thomas Thrainer
  HPATH = "instance-add"
402 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
403 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
404 22b7f6f8 Thomas Thrainer
405 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
406 22b7f6f8 Thomas Thrainer
    """Check arguments.
407 22b7f6f8 Thomas Thrainer

408 22b7f6f8 Thomas Thrainer
    """
409 22b7f6f8 Thomas Thrainer
    # do not require name_check to ease forward/backward compatibility
410 22b7f6f8 Thomas Thrainer
    # for tools
411 22b7f6f8 Thomas Thrainer
    if self.op.no_install and self.op.start:
412 22b7f6f8 Thomas Thrainer
      self.LogInfo("No-installation mode selected, disabling startup")
413 22b7f6f8 Thomas Thrainer
      self.op.start = False
414 22b7f6f8 Thomas Thrainer
    # validate/normalize the instance name
415 22b7f6f8 Thomas Thrainer
    self.op.instance_name = \
416 22b7f6f8 Thomas Thrainer
      netutils.Hostname.GetNormalizedName(self.op.instance_name)
417 22b7f6f8 Thomas Thrainer
418 22b7f6f8 Thomas Thrainer
    if self.op.ip_check and not self.op.name_check:
419 22b7f6f8 Thomas Thrainer
      # TODO: make the ip check more flexible and not depend on the name check
420 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot do IP address check without a name"
421 22b7f6f8 Thomas Thrainer
                                 " check", errors.ECODE_INVAL)
422 22b7f6f8 Thomas Thrainer
423 22b7f6f8 Thomas Thrainer
    # check nics' parameter names
424 22b7f6f8 Thomas Thrainer
    for nic in self.op.nics:
425 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(nic, constants.INIC_PARAMS_TYPES)
426 22b7f6f8 Thomas Thrainer
    # check that NIC's parameters names are unique and valid
427 22b7f6f8 Thomas Thrainer
    utils.ValidateDeviceNames("NIC", self.op.nics)
428 22b7f6f8 Thomas Thrainer
429 22b7f6f8 Thomas Thrainer
    # check that disk's names are unique and valid
430 22b7f6f8 Thomas Thrainer
    utils.ValidateDeviceNames("disk", self.op.disks)
431 22b7f6f8 Thomas Thrainer
432 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
433 22b7f6f8 Thomas Thrainer
    if not self.op.disk_template in cluster.enabled_disk_templates:
434 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot create an instance with disk template"
435 22b7f6f8 Thomas Thrainer
                                 " '%s', because it is not enabled in the"
436 22b7f6f8 Thomas Thrainer
                                 " cluster. Enabled disk templates are: %s." %
437 22b7f6f8 Thomas Thrainer
                                 (self.op.disk_template,
438 22b7f6f8 Thomas Thrainer
                                  ",".join(cluster.enabled_disk_templates)))
439 22b7f6f8 Thomas Thrainer
440 22b7f6f8 Thomas Thrainer
    # check disks. parameter names and consistent adopt/no-adopt strategy
441 22b7f6f8 Thomas Thrainer
    has_adopt = has_no_adopt = False
442 22b7f6f8 Thomas Thrainer
    for disk in self.op.disks:
443 22b7f6f8 Thomas Thrainer
      if self.op.disk_template != constants.DT_EXT:
444 22b7f6f8 Thomas Thrainer
        utils.ForceDictType(disk, constants.IDISK_PARAMS_TYPES)
445 22b7f6f8 Thomas Thrainer
      if constants.IDISK_ADOPT in disk:
446 22b7f6f8 Thomas Thrainer
        has_adopt = True
447 22b7f6f8 Thomas Thrainer
      else:
448 22b7f6f8 Thomas Thrainer
        has_no_adopt = True
449 22b7f6f8 Thomas Thrainer
    if has_adopt and has_no_adopt:
450 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Either all disks are adopted or none is",
451 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
452 22b7f6f8 Thomas Thrainer
    if has_adopt:
453 22b7f6f8 Thomas Thrainer
      if self.op.disk_template not in constants.DTS_MAY_ADOPT:
454 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk adoption is not supported for the"
455 22b7f6f8 Thomas Thrainer
                                   " '%s' disk template" %
456 22b7f6f8 Thomas Thrainer
                                   self.op.disk_template,
457 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
458 22b7f6f8 Thomas Thrainer
      if self.op.iallocator is not None:
459 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk adoption not allowed with an"
460 22b7f6f8 Thomas Thrainer
                                   " iallocator script", errors.ECODE_INVAL)
461 22b7f6f8 Thomas Thrainer
      if self.op.mode == constants.INSTANCE_IMPORT:
462 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk adoption not allowed for"
463 22b7f6f8 Thomas Thrainer
                                   " instance import", errors.ECODE_INVAL)
464 22b7f6f8 Thomas Thrainer
    else:
465 22b7f6f8 Thomas Thrainer
      if self.op.disk_template in constants.DTS_MUST_ADOPT:
466 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk template %s requires disk adoption,"
467 22b7f6f8 Thomas Thrainer
                                   " but no 'adopt' parameter given" %
468 22b7f6f8 Thomas Thrainer
                                   self.op.disk_template,
469 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
470 22b7f6f8 Thomas Thrainer
471 22b7f6f8 Thomas Thrainer
    self.adopt_disks = has_adopt
472 22b7f6f8 Thomas Thrainer
473 22b7f6f8 Thomas Thrainer
    # instance name verification
474 22b7f6f8 Thomas Thrainer
    if self.op.name_check:
475 22b7f6f8 Thomas Thrainer
      self.hostname1 = _CheckHostnameSane(self, self.op.instance_name)
476 22b7f6f8 Thomas Thrainer
      self.op.instance_name = self.hostname1.name
477 22b7f6f8 Thomas Thrainer
      # used in CheckPrereq for ip ping check
478 22b7f6f8 Thomas Thrainer
      self.check_ip = self.hostname1.ip
479 22b7f6f8 Thomas Thrainer
    else:
480 22b7f6f8 Thomas Thrainer
      self.check_ip = None
481 22b7f6f8 Thomas Thrainer
482 22b7f6f8 Thomas Thrainer
    # file storage checks
483 22b7f6f8 Thomas Thrainer
    if (self.op.file_driver and
484 22b7f6f8 Thomas Thrainer
        not self.op.file_driver in constants.FILE_DRIVER):
485 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Invalid file driver name '%s'" %
486 22b7f6f8 Thomas Thrainer
                                 self.op.file_driver, errors.ECODE_INVAL)
487 22b7f6f8 Thomas Thrainer
488 22b7f6f8 Thomas Thrainer
    if self.op.disk_template == constants.DT_FILE:
489 22b7f6f8 Thomas Thrainer
      opcodes.RequireFileStorage()
490 22b7f6f8 Thomas Thrainer
    elif self.op.disk_template == constants.DT_SHARED_FILE:
491 22b7f6f8 Thomas Thrainer
      opcodes.RequireSharedFileStorage()
492 22b7f6f8 Thomas Thrainer
493 22b7f6f8 Thomas Thrainer
    ### Node/iallocator related checks
494 22b7f6f8 Thomas Thrainer
    _CheckIAllocatorOrNode(self, "iallocator", "pnode")
495 22b7f6f8 Thomas Thrainer
496 22b7f6f8 Thomas Thrainer
    if self.op.pnode is not None:
497 22b7f6f8 Thomas Thrainer
      if self.op.disk_template in constants.DTS_INT_MIRROR:
498 22b7f6f8 Thomas Thrainer
        if self.op.snode is None:
499 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("The networked disk templates need"
500 22b7f6f8 Thomas Thrainer
                                     " a mirror node", errors.ECODE_INVAL)
501 22b7f6f8 Thomas Thrainer
      elif self.op.snode:
502 22b7f6f8 Thomas Thrainer
        self.LogWarning("Secondary node will be ignored on non-mirrored disk"
503 22b7f6f8 Thomas Thrainer
                        " template")
504 22b7f6f8 Thomas Thrainer
        self.op.snode = None
505 22b7f6f8 Thomas Thrainer
506 22b7f6f8 Thomas Thrainer
    _CheckOpportunisticLocking(self.op)
507 22b7f6f8 Thomas Thrainer
508 22b7f6f8 Thomas Thrainer
    self._cds = _GetClusterDomainSecret()
509 22b7f6f8 Thomas Thrainer
510 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
511 22b7f6f8 Thomas Thrainer
      # On import force_variant must be True, because if we forced it at
512 22b7f6f8 Thomas Thrainer
      # initial install, our only chance when importing it back is that it
513 22b7f6f8 Thomas Thrainer
      # works again!
514 22b7f6f8 Thomas Thrainer
      self.op.force_variant = True
515 22b7f6f8 Thomas Thrainer
516 22b7f6f8 Thomas Thrainer
      if self.op.no_install:
517 22b7f6f8 Thomas Thrainer
        self.LogInfo("No-installation mode has no effect during import")
518 22b7f6f8 Thomas Thrainer
519 22b7f6f8 Thomas Thrainer
    elif self.op.mode == constants.INSTANCE_CREATE:
520 22b7f6f8 Thomas Thrainer
      if self.op.os_type is None:
521 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No guest OS specified",
522 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
523 22b7f6f8 Thomas Thrainer
      if self.op.os_type in self.cfg.GetClusterInfo().blacklisted_os:
524 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Guest OS '%s' is not allowed for"
525 22b7f6f8 Thomas Thrainer
                                   " installation" % self.op.os_type,
526 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
527 22b7f6f8 Thomas Thrainer
      if self.op.disk_template is None:
528 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No disk template specified",
529 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
530 22b7f6f8 Thomas Thrainer
531 22b7f6f8 Thomas Thrainer
    elif self.op.mode == constants.INSTANCE_REMOTE_IMPORT:
532 22b7f6f8 Thomas Thrainer
      # Check handshake to ensure both clusters have the same domain secret
533 22b7f6f8 Thomas Thrainer
      src_handshake = self.op.source_handshake
534 22b7f6f8 Thomas Thrainer
      if not src_handshake:
535 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing source handshake",
536 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
537 22b7f6f8 Thomas Thrainer
538 22b7f6f8 Thomas Thrainer
      errmsg = masterd.instance.CheckRemoteExportHandshake(self._cds,
539 22b7f6f8 Thomas Thrainer
                                                           src_handshake)
540 22b7f6f8 Thomas Thrainer
      if errmsg:
541 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid handshake: %s" % errmsg,
542 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
543 22b7f6f8 Thomas Thrainer
544 22b7f6f8 Thomas Thrainer
      # Load and check source CA
545 22b7f6f8 Thomas Thrainer
      self.source_x509_ca_pem = self.op.source_x509_ca
546 22b7f6f8 Thomas Thrainer
      if not self.source_x509_ca_pem:
547 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing source X509 CA",
548 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
549 22b7f6f8 Thomas Thrainer
550 22b7f6f8 Thomas Thrainer
      try:
551 22b7f6f8 Thomas Thrainer
        (cert, _) = utils.LoadSignedX509Certificate(self.source_x509_ca_pem,
552 22b7f6f8 Thomas Thrainer
                                                    self._cds)
553 22b7f6f8 Thomas Thrainer
      except OpenSSL.crypto.Error, err:
554 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Unable to load source X509 CA (%s)" %
555 22b7f6f8 Thomas Thrainer
                                   (err, ), errors.ECODE_INVAL)
556 22b7f6f8 Thomas Thrainer
557 22b7f6f8 Thomas Thrainer
      (errcode, msg) = utils.VerifyX509Certificate(cert, None, None)
558 22b7f6f8 Thomas Thrainer
      if errcode is not None:
559 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid source X509 CA (%s)" % (msg, ),
560 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
561 22b7f6f8 Thomas Thrainer
562 22b7f6f8 Thomas Thrainer
      self.source_x509_ca = cert
563 22b7f6f8 Thomas Thrainer
564 22b7f6f8 Thomas Thrainer
      src_instance_name = self.op.source_instance_name
565 22b7f6f8 Thomas Thrainer
      if not src_instance_name:
566 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing source instance name",
567 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
568 22b7f6f8 Thomas Thrainer
569 22b7f6f8 Thomas Thrainer
      self.source_instance_name = \
570 22b7f6f8 Thomas Thrainer
        netutils.GetHostname(name=src_instance_name).name
571 22b7f6f8 Thomas Thrainer
572 22b7f6f8 Thomas Thrainer
    else:
573 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Invalid instance creation mode %r" %
574 22b7f6f8 Thomas Thrainer
                                 self.op.mode, errors.ECODE_INVAL)
575 22b7f6f8 Thomas Thrainer
576 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
577 22b7f6f8 Thomas Thrainer
    """ExpandNames for CreateInstance.
578 22b7f6f8 Thomas Thrainer

579 22b7f6f8 Thomas Thrainer
    Figure out the right locks for instance creation.
580 22b7f6f8 Thomas Thrainer

581 22b7f6f8 Thomas Thrainer
    """
582 22b7f6f8 Thomas Thrainer
    self.needed_locks = {}
583 22b7f6f8 Thomas Thrainer
584 22b7f6f8 Thomas Thrainer
    instance_name = self.op.instance_name
585 22b7f6f8 Thomas Thrainer
    # this is just a preventive check, but someone might still add this
586 22b7f6f8 Thomas Thrainer
    # instance in the meantime, and creation will fail at lock-add time
587 22b7f6f8 Thomas Thrainer
    if instance_name in self.cfg.GetInstanceList():
588 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
589 22b7f6f8 Thomas Thrainer
                                 instance_name, errors.ECODE_EXISTS)
590 22b7f6f8 Thomas Thrainer
591 22b7f6f8 Thomas Thrainer
    self.add_locks[locking.LEVEL_INSTANCE] = instance_name
592 22b7f6f8 Thomas Thrainer
593 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
594 22b7f6f8 Thomas Thrainer
      # TODO: Find a solution to not lock all nodes in the cluster, e.g. by
595 22b7f6f8 Thomas Thrainer
      # specifying a group on instance creation and then selecting nodes from
596 22b7f6f8 Thomas Thrainer
      # that group
597 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
598 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
599 22b7f6f8 Thomas Thrainer
600 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
601 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
602 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE_RES] = True
603 22b7f6f8 Thomas Thrainer
    else:
604 22b7f6f8 Thomas Thrainer
      self.op.pnode = _ExpandNodeName(self.cfg, self.op.pnode)
605 22b7f6f8 Thomas Thrainer
      nodelist = [self.op.pnode]
606 22b7f6f8 Thomas Thrainer
      if self.op.snode is not None:
607 22b7f6f8 Thomas Thrainer
        self.op.snode = _ExpandNodeName(self.cfg, self.op.snode)
608 22b7f6f8 Thomas Thrainer
        nodelist.append(self.op.snode)
609 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodelist
610 22b7f6f8 Thomas Thrainer
611 22b7f6f8 Thomas Thrainer
    # in case of import lock the source node too
612 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
613 22b7f6f8 Thomas Thrainer
      src_node = self.op.src_node
614 22b7f6f8 Thomas Thrainer
      src_path = self.op.src_path
615 22b7f6f8 Thomas Thrainer
616 22b7f6f8 Thomas Thrainer
      if src_path is None:
617 22b7f6f8 Thomas Thrainer
        self.op.src_path = src_path = self.op.instance_name
618 22b7f6f8 Thomas Thrainer
619 22b7f6f8 Thomas Thrainer
      if src_node is None:
620 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
621 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
622 22b7f6f8 Thomas Thrainer
        self.op.src_node = None
623 22b7f6f8 Thomas Thrainer
        if os.path.isabs(src_path):
624 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("Importing an instance from a path"
625 22b7f6f8 Thomas Thrainer
                                     " requires a source node option",
626 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
627 22b7f6f8 Thomas Thrainer
      else:
628 22b7f6f8 Thomas Thrainer
        self.op.src_node = src_node = _ExpandNodeName(self.cfg, src_node)
629 22b7f6f8 Thomas Thrainer
        if self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET:
630 22b7f6f8 Thomas Thrainer
          self.needed_locks[locking.LEVEL_NODE].append(src_node)
631 22b7f6f8 Thomas Thrainer
        if not os.path.isabs(src_path):
632 22b7f6f8 Thomas Thrainer
          self.op.src_path = src_path = \
633 22b7f6f8 Thomas Thrainer
            utils.PathJoin(pathutils.EXPORT_DIR, src_path)
634 22b7f6f8 Thomas Thrainer
635 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = \
636 22b7f6f8 Thomas Thrainer
      _CopyLockList(self.needed_locks[locking.LEVEL_NODE])
637 22b7f6f8 Thomas Thrainer
638 22b7f6f8 Thomas Thrainer
  def _RunAllocator(self):
639 22b7f6f8 Thomas Thrainer
    """Run the allocator based on input opcode.
640 22b7f6f8 Thomas Thrainer

641 22b7f6f8 Thomas Thrainer
    """
642 22b7f6f8 Thomas Thrainer
    if self.op.opportunistic_locking:
643 22b7f6f8 Thomas Thrainer
      # Only consider nodes for which a lock is held
644 22b7f6f8 Thomas Thrainer
      node_whitelist = list(self.owned_locks(locking.LEVEL_NODE))
645 22b7f6f8 Thomas Thrainer
    else:
646 22b7f6f8 Thomas Thrainer
      node_whitelist = None
647 22b7f6f8 Thomas Thrainer
648 22b7f6f8 Thomas Thrainer
    #TODO Export network to iallocator so that it chooses a pnode
649 22b7f6f8 Thomas Thrainer
    #     in a nodegroup that has the desired network connected to
650 22b7f6f8 Thomas Thrainer
    req = _CreateInstanceAllocRequest(self.op, self.disks,
651 22b7f6f8 Thomas Thrainer
                                      self.nics, self.be_full,
652 22b7f6f8 Thomas Thrainer
                                      node_whitelist)
653 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
654 22b7f6f8 Thomas Thrainer
655 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
656 22b7f6f8 Thomas Thrainer
657 22b7f6f8 Thomas Thrainer
    if not ial.success:
658 22b7f6f8 Thomas Thrainer
      # When opportunistic locks are used only a temporary failure is generated
659 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
660 22b7f6f8 Thomas Thrainer
        ecode = errors.ECODE_TEMP_NORES
661 22b7f6f8 Thomas Thrainer
      else:
662 22b7f6f8 Thomas Thrainer
        ecode = errors.ECODE_NORES
663 22b7f6f8 Thomas Thrainer
664 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute nodes using"
665 22b7f6f8 Thomas Thrainer
                                 " iallocator '%s': %s" %
666 22b7f6f8 Thomas Thrainer
                                 (self.op.iallocator, ial.info),
667 22b7f6f8 Thomas Thrainer
                                 ecode)
668 22b7f6f8 Thomas Thrainer
669 22b7f6f8 Thomas Thrainer
    self.op.pnode = ial.result[0]
670 22b7f6f8 Thomas Thrainer
    self.LogInfo("Selected nodes for instance %s via iallocator %s: %s",
671 22b7f6f8 Thomas Thrainer
                 self.op.instance_name, self.op.iallocator,
672 22b7f6f8 Thomas Thrainer
                 utils.CommaJoin(ial.result))
673 22b7f6f8 Thomas Thrainer
674 22b7f6f8 Thomas Thrainer
    assert req.RequiredNodes() in (1, 2), "Wrong node count from iallocator"
675 22b7f6f8 Thomas Thrainer
676 22b7f6f8 Thomas Thrainer
    if req.RequiredNodes() == 2:
677 22b7f6f8 Thomas Thrainer
      self.op.snode = ial.result[1]
678 22b7f6f8 Thomas Thrainer
679 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
680 22b7f6f8 Thomas Thrainer
    """Build hooks env.
681 22b7f6f8 Thomas Thrainer

682 22b7f6f8 Thomas Thrainer
    This runs on master, primary and secondary nodes of the instance.
683 22b7f6f8 Thomas Thrainer

684 22b7f6f8 Thomas Thrainer
    """
685 22b7f6f8 Thomas Thrainer
    env = {
686 22b7f6f8 Thomas Thrainer
      "ADD_MODE": self.op.mode,
687 22b7f6f8 Thomas Thrainer
      }
688 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
689 22b7f6f8 Thomas Thrainer
      env["SRC_NODE"] = self.op.src_node
690 22b7f6f8 Thomas Thrainer
      env["SRC_PATH"] = self.op.src_path
691 22b7f6f8 Thomas Thrainer
      env["SRC_IMAGES"] = self.src_images
692 22b7f6f8 Thomas Thrainer
693 22b7f6f8 Thomas Thrainer
    env.update(_BuildInstanceHookEnv(
694 22b7f6f8 Thomas Thrainer
      name=self.op.instance_name,
695 22b7f6f8 Thomas Thrainer
      primary_node=self.op.pnode,
696 22b7f6f8 Thomas Thrainer
      secondary_nodes=self.secondaries,
697 22b7f6f8 Thomas Thrainer
      status=self.op.start,
698 22b7f6f8 Thomas Thrainer
      os_type=self.op.os_type,
699 22b7f6f8 Thomas Thrainer
      minmem=self.be_full[constants.BE_MINMEM],
700 22b7f6f8 Thomas Thrainer
      maxmem=self.be_full[constants.BE_MAXMEM],
701 22b7f6f8 Thomas Thrainer
      vcpus=self.be_full[constants.BE_VCPUS],
702 22b7f6f8 Thomas Thrainer
      nics=_NICListToTuple(self, self.nics),
703 22b7f6f8 Thomas Thrainer
      disk_template=self.op.disk_template,
704 22b7f6f8 Thomas Thrainer
      disks=[(d[constants.IDISK_NAME], d[constants.IDISK_SIZE],
705 22b7f6f8 Thomas Thrainer
              d[constants.IDISK_MODE]) for d in self.disks],
706 22b7f6f8 Thomas Thrainer
      bep=self.be_full,
707 22b7f6f8 Thomas Thrainer
      hvp=self.hv_full,
708 22b7f6f8 Thomas Thrainer
      hypervisor_name=self.op.hypervisor,
709 22b7f6f8 Thomas Thrainer
      tags=self.op.tags,
710 22b7f6f8 Thomas Thrainer
      ))
711 22b7f6f8 Thomas Thrainer
712 22b7f6f8 Thomas Thrainer
    return env
713 22b7f6f8 Thomas Thrainer
714 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
715 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
716 22b7f6f8 Thomas Thrainer

717 22b7f6f8 Thomas Thrainer
    """
718 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode(), self.op.pnode] + self.secondaries
719 22b7f6f8 Thomas Thrainer
    return nl, nl
720 22b7f6f8 Thomas Thrainer
721 22b7f6f8 Thomas Thrainer
  def _ReadExportInfo(self):
722 22b7f6f8 Thomas Thrainer
    """Reads the export information from disk.
723 22b7f6f8 Thomas Thrainer

724 22b7f6f8 Thomas Thrainer
    It will override the opcode source node and path with the actual
725 22b7f6f8 Thomas Thrainer
    information, if these two were not specified before.
726 22b7f6f8 Thomas Thrainer

727 22b7f6f8 Thomas Thrainer
    @return: the export information
728 22b7f6f8 Thomas Thrainer

729 22b7f6f8 Thomas Thrainer
    """
730 22b7f6f8 Thomas Thrainer
    assert self.op.mode == constants.INSTANCE_IMPORT
731 22b7f6f8 Thomas Thrainer
732 22b7f6f8 Thomas Thrainer
    src_node = self.op.src_node
733 22b7f6f8 Thomas Thrainer
    src_path = self.op.src_path
734 22b7f6f8 Thomas Thrainer
735 22b7f6f8 Thomas Thrainer
    if src_node is None:
736 22b7f6f8 Thomas Thrainer
      locked_nodes = self.owned_locks(locking.LEVEL_NODE)
737 22b7f6f8 Thomas Thrainer
      exp_list = self.rpc.call_export_list(locked_nodes)
738 22b7f6f8 Thomas Thrainer
      found = False
739 22b7f6f8 Thomas Thrainer
      for node in exp_list:
740 22b7f6f8 Thomas Thrainer
        if exp_list[node].fail_msg:
741 22b7f6f8 Thomas Thrainer
          continue
742 22b7f6f8 Thomas Thrainer
        if src_path in exp_list[node].payload:
743 22b7f6f8 Thomas Thrainer
          found = True
744 22b7f6f8 Thomas Thrainer
          self.op.src_node = src_node = node
745 22b7f6f8 Thomas Thrainer
          self.op.src_path = src_path = utils.PathJoin(pathutils.EXPORT_DIR,
746 22b7f6f8 Thomas Thrainer
                                                       src_path)
747 22b7f6f8 Thomas Thrainer
          break
748 22b7f6f8 Thomas Thrainer
      if not found:
749 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No export found for relative path %s" %
750 22b7f6f8 Thomas Thrainer
                                   src_path, errors.ECODE_INVAL)
751 22b7f6f8 Thomas Thrainer
752 22b7f6f8 Thomas Thrainer
    _CheckNodeOnline(self, src_node)
753 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_export_info(src_node, src_path)
754 22b7f6f8 Thomas Thrainer
    result.Raise("No export or invalid export found in dir %s" % src_path)
755 22b7f6f8 Thomas Thrainer
756 22b7f6f8 Thomas Thrainer
    export_info = objects.SerializableConfigParser.Loads(str(result.payload))
757 22b7f6f8 Thomas Thrainer
    if not export_info.has_section(constants.INISECT_EXP):
758 22b7f6f8 Thomas Thrainer
      raise errors.ProgrammerError("Corrupted export config",
759 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_ENVIRON)
760 22b7f6f8 Thomas Thrainer
761 22b7f6f8 Thomas Thrainer
    ei_version = export_info.get(constants.INISECT_EXP, "version")
762 22b7f6f8 Thomas Thrainer
    if (int(ei_version) != constants.EXPORT_VERSION):
763 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Wrong export version %s (wanted %d)" %
764 22b7f6f8 Thomas Thrainer
                                 (ei_version, constants.EXPORT_VERSION),
765 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_ENVIRON)
766 22b7f6f8 Thomas Thrainer
    return export_info
767 22b7f6f8 Thomas Thrainer
768 22b7f6f8 Thomas Thrainer
  def _ReadExportParams(self, einfo):
769 22b7f6f8 Thomas Thrainer
    """Use export parameters as defaults.
770 22b7f6f8 Thomas Thrainer

771 22b7f6f8 Thomas Thrainer
    In case the opcode doesn't specify (as in override) some instance
772 22b7f6f8 Thomas Thrainer
    parameters, then try to use them from the export information, if
773 22b7f6f8 Thomas Thrainer
    that declares them.
774 22b7f6f8 Thomas Thrainer

775 22b7f6f8 Thomas Thrainer
    """
776 22b7f6f8 Thomas Thrainer
    self.op.os_type = einfo.get(constants.INISECT_EXP, "os")
777 22b7f6f8 Thomas Thrainer
778 22b7f6f8 Thomas Thrainer
    if self.op.disk_template is None:
779 22b7f6f8 Thomas Thrainer
      if einfo.has_option(constants.INISECT_INS, "disk_template"):
780 22b7f6f8 Thomas Thrainer
        self.op.disk_template = einfo.get(constants.INISECT_INS,
781 22b7f6f8 Thomas Thrainer
                                          "disk_template")
782 22b7f6f8 Thomas Thrainer
        if self.op.disk_template not in constants.DISK_TEMPLATES:
783 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("Disk template specified in configuration"
784 22b7f6f8 Thomas Thrainer
                                     " file is not one of the allowed values:"
785 22b7f6f8 Thomas Thrainer
                                     " %s" %
786 22b7f6f8 Thomas Thrainer
                                     " ".join(constants.DISK_TEMPLATES),
787 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
788 22b7f6f8 Thomas Thrainer
      else:
789 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No disk template specified and the export"
790 22b7f6f8 Thomas Thrainer
                                   " is missing the disk_template information",
791 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
792 22b7f6f8 Thomas Thrainer
793 22b7f6f8 Thomas Thrainer
    if not self.op.disks:
794 22b7f6f8 Thomas Thrainer
      disks = []
795 22b7f6f8 Thomas Thrainer
      # TODO: import the disk iv_name too
796 22b7f6f8 Thomas Thrainer
      for idx in range(constants.MAX_DISKS):
797 22b7f6f8 Thomas Thrainer
        if einfo.has_option(constants.INISECT_INS, "disk%d_size" % idx):
798 22b7f6f8 Thomas Thrainer
          disk_sz = einfo.getint(constants.INISECT_INS, "disk%d_size" % idx)
799 22b7f6f8 Thomas Thrainer
          disks.append({constants.IDISK_SIZE: disk_sz})
800 22b7f6f8 Thomas Thrainer
      self.op.disks = disks
801 22b7f6f8 Thomas Thrainer
      if not disks and self.op.disk_template != constants.DT_DISKLESS:
802 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No disk info specified and the export"
803 22b7f6f8 Thomas Thrainer
                                   " is missing the disk information",
804 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
805 22b7f6f8 Thomas Thrainer
806 22b7f6f8 Thomas Thrainer
    if not self.op.nics:
807 22b7f6f8 Thomas Thrainer
      nics = []
808 22b7f6f8 Thomas Thrainer
      for idx in range(constants.MAX_NICS):
809 22b7f6f8 Thomas Thrainer
        if einfo.has_option(constants.INISECT_INS, "nic%d_mac" % idx):
810 22b7f6f8 Thomas Thrainer
          ndict = {}
811 22b7f6f8 Thomas Thrainer
          for name in list(constants.NICS_PARAMETERS) + ["ip", "mac"]:
812 22b7f6f8 Thomas Thrainer
            v = einfo.get(constants.INISECT_INS, "nic%d_%s" % (idx, name))
813 22b7f6f8 Thomas Thrainer
            ndict[name] = v
814 22b7f6f8 Thomas Thrainer
          nics.append(ndict)
815 22b7f6f8 Thomas Thrainer
        else:
816 22b7f6f8 Thomas Thrainer
          break
817 22b7f6f8 Thomas Thrainer
      self.op.nics = nics
818 22b7f6f8 Thomas Thrainer
819 22b7f6f8 Thomas Thrainer
    if not self.op.tags and einfo.has_option(constants.INISECT_INS, "tags"):
820 22b7f6f8 Thomas Thrainer
      self.op.tags = einfo.get(constants.INISECT_INS, "tags").split()
821 22b7f6f8 Thomas Thrainer
822 22b7f6f8 Thomas Thrainer
    if (self.op.hypervisor is None and
823 22b7f6f8 Thomas Thrainer
        einfo.has_option(constants.INISECT_INS, "hypervisor")):
824 22b7f6f8 Thomas Thrainer
      self.op.hypervisor = einfo.get(constants.INISECT_INS, "hypervisor")
825 22b7f6f8 Thomas Thrainer
826 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_HYP):
827 22b7f6f8 Thomas Thrainer
      # use the export parameters but do not override the ones
828 22b7f6f8 Thomas Thrainer
      # specified by the user
829 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_HYP):
830 22b7f6f8 Thomas Thrainer
        if name not in self.op.hvparams:
831 22b7f6f8 Thomas Thrainer
          self.op.hvparams[name] = value
832 22b7f6f8 Thomas Thrainer
833 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_BEP):
834 22b7f6f8 Thomas Thrainer
      # use the parameters, without overriding
835 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_BEP):
836 22b7f6f8 Thomas Thrainer
        if name not in self.op.beparams:
837 22b7f6f8 Thomas Thrainer
          self.op.beparams[name] = value
838 22b7f6f8 Thomas Thrainer
        # Compatibility for the old "memory" be param
839 22b7f6f8 Thomas Thrainer
        if name == constants.BE_MEMORY:
840 22b7f6f8 Thomas Thrainer
          if constants.BE_MAXMEM not in self.op.beparams:
841 22b7f6f8 Thomas Thrainer
            self.op.beparams[constants.BE_MAXMEM] = value
842 22b7f6f8 Thomas Thrainer
          if constants.BE_MINMEM not in self.op.beparams:
843 22b7f6f8 Thomas Thrainer
            self.op.beparams[constants.BE_MINMEM] = value
844 22b7f6f8 Thomas Thrainer
    else:
845 22b7f6f8 Thomas Thrainer
      # try to read the parameters old style, from the main section
846 22b7f6f8 Thomas Thrainer
      for name in constants.BES_PARAMETERS:
847 22b7f6f8 Thomas Thrainer
        if (name not in self.op.beparams and
848 22b7f6f8 Thomas Thrainer
            einfo.has_option(constants.INISECT_INS, name)):
849 22b7f6f8 Thomas Thrainer
          self.op.beparams[name] = einfo.get(constants.INISECT_INS, name)
850 22b7f6f8 Thomas Thrainer
851 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_OSP):
852 22b7f6f8 Thomas Thrainer
      # use the parameters, without overriding
853 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_OSP):
854 22b7f6f8 Thomas Thrainer
        if name not in self.op.osparams:
855 22b7f6f8 Thomas Thrainer
          self.op.osparams[name] = value
856 22b7f6f8 Thomas Thrainer
857 22b7f6f8 Thomas Thrainer
  def _RevertToDefaults(self, cluster):
858 22b7f6f8 Thomas Thrainer
    """Revert the instance parameters to the default values.
859 22b7f6f8 Thomas Thrainer

860 22b7f6f8 Thomas Thrainer
    """
861 22b7f6f8 Thomas Thrainer
    # hvparams
862 22b7f6f8 Thomas Thrainer
    hv_defs = cluster.SimpleFillHV(self.op.hypervisor, self.op.os_type, {})
863 22b7f6f8 Thomas Thrainer
    for name in self.op.hvparams.keys():
864 22b7f6f8 Thomas Thrainer
      if name in hv_defs and hv_defs[name] == self.op.hvparams[name]:
865 22b7f6f8 Thomas Thrainer
        del self.op.hvparams[name]
866 22b7f6f8 Thomas Thrainer
    # beparams
867 22b7f6f8 Thomas Thrainer
    be_defs = cluster.SimpleFillBE({})
868 22b7f6f8 Thomas Thrainer
    for name in self.op.beparams.keys():
869 22b7f6f8 Thomas Thrainer
      if name in be_defs and be_defs[name] == self.op.beparams[name]:
870 22b7f6f8 Thomas Thrainer
        del self.op.beparams[name]
871 22b7f6f8 Thomas Thrainer
    # nic params
872 22b7f6f8 Thomas Thrainer
    nic_defs = cluster.SimpleFillNIC({})
873 22b7f6f8 Thomas Thrainer
    for nic in self.op.nics:
874 22b7f6f8 Thomas Thrainer
      for name in constants.NICS_PARAMETERS:
875 22b7f6f8 Thomas Thrainer
        if name in nic and name in nic_defs and nic[name] == nic_defs[name]:
876 22b7f6f8 Thomas Thrainer
          del nic[name]
877 22b7f6f8 Thomas Thrainer
    # osparams
878 22b7f6f8 Thomas Thrainer
    os_defs = cluster.SimpleFillOS(self.op.os_type, {})
879 22b7f6f8 Thomas Thrainer
    for name in self.op.osparams.keys():
880 22b7f6f8 Thomas Thrainer
      if name in os_defs and os_defs[name] == self.op.osparams[name]:
881 22b7f6f8 Thomas Thrainer
        del self.op.osparams[name]
882 22b7f6f8 Thomas Thrainer
883 22b7f6f8 Thomas Thrainer
  def _CalculateFileStorageDir(self):
884 22b7f6f8 Thomas Thrainer
    """Calculate final instance file storage dir.
885 22b7f6f8 Thomas Thrainer

886 22b7f6f8 Thomas Thrainer
    """
887 22b7f6f8 Thomas Thrainer
    # file storage dir calculation/check
888 22b7f6f8 Thomas Thrainer
    self.instance_file_storage_dir = None
889 22b7f6f8 Thomas Thrainer
    if self.op.disk_template in constants.DTS_FILEBASED:
890 22b7f6f8 Thomas Thrainer
      # build the full file storage dir path
891 22b7f6f8 Thomas Thrainer
      joinargs = []
892 22b7f6f8 Thomas Thrainer
893 22b7f6f8 Thomas Thrainer
      if self.op.disk_template == constants.DT_SHARED_FILE:
894 22b7f6f8 Thomas Thrainer
        get_fsd_fn = self.cfg.GetSharedFileStorageDir
895 22b7f6f8 Thomas Thrainer
      else:
896 22b7f6f8 Thomas Thrainer
        get_fsd_fn = self.cfg.GetFileStorageDir
897 22b7f6f8 Thomas Thrainer
898 22b7f6f8 Thomas Thrainer
      cfg_storagedir = get_fsd_fn()
899 22b7f6f8 Thomas Thrainer
      if not cfg_storagedir:
900 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cluster file storage dir not defined",
901 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
902 22b7f6f8 Thomas Thrainer
      joinargs.append(cfg_storagedir)
903 22b7f6f8 Thomas Thrainer
904 22b7f6f8 Thomas Thrainer
      if self.op.file_storage_dir is not None:
905 22b7f6f8 Thomas Thrainer
        joinargs.append(self.op.file_storage_dir)
906 22b7f6f8 Thomas Thrainer
907 22b7f6f8 Thomas Thrainer
      joinargs.append(self.op.instance_name)
908 22b7f6f8 Thomas Thrainer
909 22b7f6f8 Thomas Thrainer
      # pylint: disable=W0142
910 22b7f6f8 Thomas Thrainer
      self.instance_file_storage_dir = utils.PathJoin(*joinargs)
911 22b7f6f8 Thomas Thrainer
912 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self): # pylint: disable=R0914
913 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
914 22b7f6f8 Thomas Thrainer

915 22b7f6f8 Thomas Thrainer
    """
916 22b7f6f8 Thomas Thrainer
    self._CalculateFileStorageDir()
917 22b7f6f8 Thomas Thrainer
918 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
919 22b7f6f8 Thomas Thrainer
      export_info = self._ReadExportInfo()
920 22b7f6f8 Thomas Thrainer
      self._ReadExportParams(export_info)
921 22b7f6f8 Thomas Thrainer
      self._old_instance_name = export_info.get(constants.INISECT_INS, "name")
922 22b7f6f8 Thomas Thrainer
    else:
923 22b7f6f8 Thomas Thrainer
      self._old_instance_name = None
924 22b7f6f8 Thomas Thrainer
925 22b7f6f8 Thomas Thrainer
    if (not self.cfg.GetVGName() and
926 22b7f6f8 Thomas Thrainer
        self.op.disk_template not in constants.DTS_NOT_LVM):
927 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cluster does not support lvm-based"
928 22b7f6f8 Thomas Thrainer
                                 " instances", errors.ECODE_STATE)
929 22b7f6f8 Thomas Thrainer
930 22b7f6f8 Thomas Thrainer
    if (self.op.hypervisor is None or
931 22b7f6f8 Thomas Thrainer
        self.op.hypervisor == constants.VALUE_AUTO):
932 22b7f6f8 Thomas Thrainer
      self.op.hypervisor = self.cfg.GetHypervisorType()
933 22b7f6f8 Thomas Thrainer
934 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
935 22b7f6f8 Thomas Thrainer
    enabled_hvs = cluster.enabled_hypervisors
936 22b7f6f8 Thomas Thrainer
    if self.op.hypervisor not in enabled_hvs:
937 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Selected hypervisor (%s) not enabled in the"
938 22b7f6f8 Thomas Thrainer
                                 " cluster (%s)" %
939 22b7f6f8 Thomas Thrainer
                                 (self.op.hypervisor, ",".join(enabled_hvs)),
940 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
941 22b7f6f8 Thomas Thrainer
942 22b7f6f8 Thomas Thrainer
    # Check tag validity
943 22b7f6f8 Thomas Thrainer
    for tag in self.op.tags:
944 22b7f6f8 Thomas Thrainer
      objects.TaggableObject.ValidateTag(tag)
945 22b7f6f8 Thomas Thrainer
946 22b7f6f8 Thomas Thrainer
    # check hypervisor parameter syntax (locally)
947 22b7f6f8 Thomas Thrainer
    utils.ForceDictType(self.op.hvparams, constants.HVS_PARAMETER_TYPES)
948 22b7f6f8 Thomas Thrainer
    filled_hvp = cluster.SimpleFillHV(self.op.hypervisor, self.op.os_type,
949 22b7f6f8 Thomas Thrainer
                                      self.op.hvparams)
950 22b7f6f8 Thomas Thrainer
    hv_type = hypervisor.GetHypervisorClass(self.op.hypervisor)
951 22b7f6f8 Thomas Thrainer
    hv_type.CheckParameterSyntax(filled_hvp)
952 22b7f6f8 Thomas Thrainer
    self.hv_full = filled_hvp
953 22b7f6f8 Thomas Thrainer
    # check that we don't specify global parameters on an instance
954 22b7f6f8 Thomas Thrainer
    _CheckParamsNotGlobal(self.op.hvparams, constants.HVC_GLOBALS, "hypervisor",
955 22b7f6f8 Thomas Thrainer
                          "instance", "cluster")
956 22b7f6f8 Thomas Thrainer
957 22b7f6f8 Thomas Thrainer
    # fill and remember the beparams dict
958 22b7f6f8 Thomas Thrainer
    self.be_full = _ComputeFullBeParams(self.op, cluster)
959 22b7f6f8 Thomas Thrainer
960 22b7f6f8 Thomas Thrainer
    # build os parameters
961 22b7f6f8 Thomas Thrainer
    self.os_full = cluster.SimpleFillOS(self.op.os_type, self.op.osparams)
962 22b7f6f8 Thomas Thrainer
963 22b7f6f8 Thomas Thrainer
    # now that hvp/bep are in final format, let's reset to defaults,
964 22b7f6f8 Thomas Thrainer
    # if told to do so
965 22b7f6f8 Thomas Thrainer
    if self.op.identify_defaults:
966 22b7f6f8 Thomas Thrainer
      self._RevertToDefaults(cluster)
967 22b7f6f8 Thomas Thrainer
968 22b7f6f8 Thomas Thrainer
    # NIC buildup
969 22b7f6f8 Thomas Thrainer
    self.nics = _ComputeNics(self.op, cluster, self.check_ip, self.cfg,
970 22b7f6f8 Thomas Thrainer
                             self.proc.GetECId())
971 22b7f6f8 Thomas Thrainer
972 22b7f6f8 Thomas Thrainer
    # disk checks/pre-build
973 22b7f6f8 Thomas Thrainer
    default_vg = self.cfg.GetVGName()
974 22b7f6f8 Thomas Thrainer
    self.disks = _ComputeDisks(self.op, default_vg)
975 22b7f6f8 Thomas Thrainer
976 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
977 22b7f6f8 Thomas Thrainer
      disk_images = []
978 22b7f6f8 Thomas Thrainer
      for idx in range(len(self.disks)):
979 22b7f6f8 Thomas Thrainer
        option = "disk%d_dump" % idx
980 22b7f6f8 Thomas Thrainer
        if export_info.has_option(constants.INISECT_INS, option):
981 22b7f6f8 Thomas Thrainer
          # FIXME: are the old os-es, disk sizes, etc. useful?
982 22b7f6f8 Thomas Thrainer
          export_name = export_info.get(constants.INISECT_INS, option)
983 22b7f6f8 Thomas Thrainer
          image = utils.PathJoin(self.op.src_path, export_name)
984 22b7f6f8 Thomas Thrainer
          disk_images.append(image)
985 22b7f6f8 Thomas Thrainer
        else:
986 22b7f6f8 Thomas Thrainer
          disk_images.append(False)
987 22b7f6f8 Thomas Thrainer
988 22b7f6f8 Thomas Thrainer
      self.src_images = disk_images
989 22b7f6f8 Thomas Thrainer
990 22b7f6f8 Thomas Thrainer
      if self.op.instance_name == self._old_instance_name:
991 22b7f6f8 Thomas Thrainer
        for idx, nic in enumerate(self.nics):
992 22b7f6f8 Thomas Thrainer
          if nic.mac == constants.VALUE_AUTO:
993 22b7f6f8 Thomas Thrainer
            nic_mac_ini = "nic%d_mac" % idx
994 22b7f6f8 Thomas Thrainer
            nic.mac = export_info.get(constants.INISECT_INS, nic_mac_ini)
995 22b7f6f8 Thomas Thrainer
996 22b7f6f8 Thomas Thrainer
    # ENDIF: self.op.mode == constants.INSTANCE_IMPORT
997 22b7f6f8 Thomas Thrainer
998 22b7f6f8 Thomas Thrainer
    # ip ping checks (we use the same ip that was resolved in ExpandNames)
999 22b7f6f8 Thomas Thrainer
    if self.op.ip_check:
1000 22b7f6f8 Thomas Thrainer
      if netutils.TcpPing(self.check_ip, constants.DEFAULT_NODED_PORT):
1001 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("IP %s of instance %s already in use" %
1002 22b7f6f8 Thomas Thrainer
                                   (self.check_ip, self.op.instance_name),
1003 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
1004 22b7f6f8 Thomas Thrainer
1005 22b7f6f8 Thomas Thrainer
    #### mac address generation
1006 22b7f6f8 Thomas Thrainer
    # By generating here the mac address both the allocator and the hooks get
1007 22b7f6f8 Thomas Thrainer
    # the real final mac address rather than the 'auto' or 'generate' value.
1008 22b7f6f8 Thomas Thrainer
    # There is a race condition between the generation and the instance object
1009 22b7f6f8 Thomas Thrainer
    # creation, which means that we know the mac is valid now, but we're not
1010 22b7f6f8 Thomas Thrainer
    # sure it will be when we actually add the instance. If things go bad
1011 22b7f6f8 Thomas Thrainer
    # adding the instance will abort because of a duplicate mac, and the
1012 22b7f6f8 Thomas Thrainer
    # creation job will fail.
1013 22b7f6f8 Thomas Thrainer
    for nic in self.nics:
1014 22b7f6f8 Thomas Thrainer
      if nic.mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
1015 22b7f6f8 Thomas Thrainer
        nic.mac = self.cfg.GenerateMAC(nic.network, self.proc.GetECId())
1016 22b7f6f8 Thomas Thrainer
1017 22b7f6f8 Thomas Thrainer
    #### allocator run
1018 22b7f6f8 Thomas Thrainer
1019 22b7f6f8 Thomas Thrainer
    if self.op.iallocator is not None:
1020 22b7f6f8 Thomas Thrainer
      self._RunAllocator()
1021 22b7f6f8 Thomas Thrainer
1022 22b7f6f8 Thomas Thrainer
    # Release all unneeded node locks
1023 22b7f6f8 Thomas Thrainer
    keep_locks = filter(None, [self.op.pnode, self.op.snode, self.op.src_node])
1024 22b7f6f8 Thomas Thrainer
    _ReleaseLocks(self, locking.LEVEL_NODE, keep=keep_locks)
1025 22b7f6f8 Thomas Thrainer
    _ReleaseLocks(self, locking.LEVEL_NODE_RES, keep=keep_locks)
1026 22b7f6f8 Thomas Thrainer
    _ReleaseLocks(self, locking.LEVEL_NODE_ALLOC)
1027 22b7f6f8 Thomas Thrainer
1028 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1029 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES)), \
1030 22b7f6f8 Thomas Thrainer
      "Node locks differ from node resource locks"
1031 22b7f6f8 Thomas Thrainer
1032 22b7f6f8 Thomas Thrainer
    #### node related checks
1033 22b7f6f8 Thomas Thrainer
1034 22b7f6f8 Thomas Thrainer
    # check primary node
1035 22b7f6f8 Thomas Thrainer
    self.pnode = pnode = self.cfg.GetNodeInfo(self.op.pnode)
1036 22b7f6f8 Thomas Thrainer
    assert self.pnode is not None, \
1037 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked node %s" % self.op.pnode
1038 22b7f6f8 Thomas Thrainer
    if pnode.offline:
1039 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot use offline primary node '%s'" %
1040 22b7f6f8 Thomas Thrainer
                                 pnode.name, errors.ECODE_STATE)
1041 22b7f6f8 Thomas Thrainer
    if pnode.drained:
1042 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot use drained primary node '%s'" %
1043 22b7f6f8 Thomas Thrainer
                                 pnode.name, errors.ECODE_STATE)
1044 22b7f6f8 Thomas Thrainer
    if not pnode.vm_capable:
1045 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot use non-vm_capable primary node"
1046 22b7f6f8 Thomas Thrainer
                                 " '%s'" % pnode.name, errors.ECODE_STATE)
1047 22b7f6f8 Thomas Thrainer
1048 22b7f6f8 Thomas Thrainer
    self.secondaries = []
1049 22b7f6f8 Thomas Thrainer
1050 22b7f6f8 Thomas Thrainer
    # Fill in any IPs from IP pools. This must happen here, because we need to
1051 22b7f6f8 Thomas Thrainer
    # know the nic's primary node, as specified by the iallocator
1052 22b7f6f8 Thomas Thrainer
    for idx, nic in enumerate(self.nics):
1053 22b7f6f8 Thomas Thrainer
      net_uuid = nic.network
1054 22b7f6f8 Thomas Thrainer
      if net_uuid is not None:
1055 22b7f6f8 Thomas Thrainer
        nobj = self.cfg.GetNetwork(net_uuid)
1056 22b7f6f8 Thomas Thrainer
        netparams = self.cfg.GetGroupNetParams(net_uuid, self.pnode.name)
1057 22b7f6f8 Thomas Thrainer
        if netparams is None:
1058 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("No netparams found for network"
1059 22b7f6f8 Thomas Thrainer
                                     " %s. Propably not connected to"
1060 22b7f6f8 Thomas Thrainer
                                     " node's %s nodegroup" %
1061 22b7f6f8 Thomas Thrainer
                                     (nobj.name, self.pnode.name),
1062 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
1063 22b7f6f8 Thomas Thrainer
        self.LogInfo("NIC/%d inherits netparams %s" %
1064 22b7f6f8 Thomas Thrainer
                     (idx, netparams.values()))
1065 22b7f6f8 Thomas Thrainer
        nic.nicparams = dict(netparams)
1066 22b7f6f8 Thomas Thrainer
        if nic.ip is not None:
1067 22b7f6f8 Thomas Thrainer
          if nic.ip.lower() == constants.NIC_IP_POOL:
1068 22b7f6f8 Thomas Thrainer
            try:
1069 22b7f6f8 Thomas Thrainer
              nic.ip = self.cfg.GenerateIp(net_uuid, self.proc.GetECId())
1070 22b7f6f8 Thomas Thrainer
            except errors.ReservationError:
1071 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("Unable to get a free IP for NIC %d"
1072 22b7f6f8 Thomas Thrainer
                                         " from the address pool" % idx,
1073 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_STATE)
1074 22b7f6f8 Thomas Thrainer
            self.LogInfo("Chose IP %s from network %s", nic.ip, nobj.name)
1075 22b7f6f8 Thomas Thrainer
          else:
1076 22b7f6f8 Thomas Thrainer
            try:
1077 22b7f6f8 Thomas Thrainer
              self.cfg.ReserveIp(net_uuid, nic.ip, self.proc.GetECId())
1078 22b7f6f8 Thomas Thrainer
            except errors.ReservationError:
1079 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("IP address %s already in use"
1080 22b7f6f8 Thomas Thrainer
                                         " or does not belong to network %s" %
1081 22b7f6f8 Thomas Thrainer
                                         (nic.ip, nobj.name),
1082 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_NOTUNIQUE)
1083 22b7f6f8 Thomas Thrainer
1084 22b7f6f8 Thomas Thrainer
      # net is None, ip None or given
1085 22b7f6f8 Thomas Thrainer
      elif self.op.conflicts_check:
1086 22b7f6f8 Thomas Thrainer
        _CheckForConflictingIp(self, nic.ip, self.pnode.name)
1087 22b7f6f8 Thomas Thrainer
1088 22b7f6f8 Thomas Thrainer
    # mirror node verification
1089 22b7f6f8 Thomas Thrainer
    if self.op.disk_template in constants.DTS_INT_MIRROR:
1090 22b7f6f8 Thomas Thrainer
      if self.op.snode == pnode.name:
1091 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("The secondary node cannot be the"
1092 22b7f6f8 Thomas Thrainer
                                   " primary node", errors.ECODE_INVAL)
1093 22b7f6f8 Thomas Thrainer
      _CheckNodeOnline(self, self.op.snode)
1094 22b7f6f8 Thomas Thrainer
      _CheckNodeNotDrained(self, self.op.snode)
1095 22b7f6f8 Thomas Thrainer
      _CheckNodeVmCapable(self, self.op.snode)
1096 22b7f6f8 Thomas Thrainer
      self.secondaries.append(self.op.snode)
1097 22b7f6f8 Thomas Thrainer
1098 22b7f6f8 Thomas Thrainer
      snode = self.cfg.GetNodeInfo(self.op.snode)
1099 22b7f6f8 Thomas Thrainer
      if pnode.group != snode.group:
1100 22b7f6f8 Thomas Thrainer
        self.LogWarning("The primary and secondary nodes are in two"
1101 22b7f6f8 Thomas Thrainer
                        " different node groups; the disk parameters"
1102 22b7f6f8 Thomas Thrainer
                        " from the first disk's node group will be"
1103 22b7f6f8 Thomas Thrainer
                        " used")
1104 22b7f6f8 Thomas Thrainer
1105 22b7f6f8 Thomas Thrainer
    if not self.op.disk_template in constants.DTS_EXCL_STORAGE:
1106 22b7f6f8 Thomas Thrainer
      nodes = [pnode]
1107 22b7f6f8 Thomas Thrainer
      if self.op.disk_template in constants.DTS_INT_MIRROR:
1108 22b7f6f8 Thomas Thrainer
        nodes.append(snode)
1109 22b7f6f8 Thomas Thrainer
      has_es = lambda n: _IsExclusiveStorageEnabledNode(self.cfg, n)
1110 22b7f6f8 Thomas Thrainer
      if compat.any(map(has_es, nodes)):
1111 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk template %s not supported with"
1112 22b7f6f8 Thomas Thrainer
                                   " exclusive storage" % self.op.disk_template,
1113 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
1114 22b7f6f8 Thomas Thrainer
1115 22b7f6f8 Thomas Thrainer
    nodenames = [pnode.name] + self.secondaries
1116 22b7f6f8 Thomas Thrainer
1117 22b7f6f8 Thomas Thrainer
    if not self.adopt_disks:
1118 22b7f6f8 Thomas Thrainer
      if self.op.disk_template == constants.DT_RBD:
1119 22b7f6f8 Thomas Thrainer
        # _CheckRADOSFreeSpace() is just a placeholder.
1120 22b7f6f8 Thomas Thrainer
        # Any function that checks prerequisites can be placed here.
1121 22b7f6f8 Thomas Thrainer
        # Check if there is enough space on the RADOS cluster.
1122 22b7f6f8 Thomas Thrainer
        _CheckRADOSFreeSpace()
1123 22b7f6f8 Thomas Thrainer
      elif self.op.disk_template == constants.DT_EXT:
1124 22b7f6f8 Thomas Thrainer
        # FIXME: Function that checks prereqs if needed
1125 22b7f6f8 Thomas Thrainer
        pass
1126 22b7f6f8 Thomas Thrainer
      else:
1127 22b7f6f8 Thomas Thrainer
        # Check lv size requirements, if not adopting
1128 22b7f6f8 Thomas Thrainer
        req_sizes = _ComputeDiskSizePerVG(self.op.disk_template, self.disks)
1129 22b7f6f8 Thomas Thrainer
        _CheckNodesFreeDiskPerVG(self, nodenames, req_sizes)
1130 22b7f6f8 Thomas Thrainer
1131 22b7f6f8 Thomas Thrainer
    elif self.op.disk_template == constants.DT_PLAIN: # Check the adoption data
1132 22b7f6f8 Thomas Thrainer
      all_lvs = set(["%s/%s" % (disk[constants.IDISK_VG],
1133 22b7f6f8 Thomas Thrainer
                                disk[constants.IDISK_ADOPT])
1134 22b7f6f8 Thomas Thrainer
                     for disk in self.disks])
1135 22b7f6f8 Thomas Thrainer
      if len(all_lvs) != len(self.disks):
1136 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Duplicate volume names given for adoption",
1137 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1138 22b7f6f8 Thomas Thrainer
      for lv_name in all_lvs:
1139 22b7f6f8 Thomas Thrainer
        try:
1140 22b7f6f8 Thomas Thrainer
          # FIXME: lv_name here is "vg/lv" need to ensure that other calls
1141 22b7f6f8 Thomas Thrainer
          # to ReserveLV uses the same syntax
1142 22b7f6f8 Thomas Thrainer
          self.cfg.ReserveLV(lv_name, self.proc.GetECId())
1143 22b7f6f8 Thomas Thrainer
        except errors.ReservationError:
1144 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("LV named %s used by another instance" %
1145 22b7f6f8 Thomas Thrainer
                                     lv_name, errors.ECODE_NOTUNIQUE)
1146 22b7f6f8 Thomas Thrainer
1147 22b7f6f8 Thomas Thrainer
      vg_names = self.rpc.call_vg_list([pnode.name])[pnode.name]
1148 22b7f6f8 Thomas Thrainer
      vg_names.Raise("Cannot get VG information from node %s" % pnode.name)
1149 22b7f6f8 Thomas Thrainer
1150 22b7f6f8 Thomas Thrainer
      node_lvs = self.rpc.call_lv_list([pnode.name],
1151 22b7f6f8 Thomas Thrainer
                                       vg_names.payload.keys())[pnode.name]
1152 22b7f6f8 Thomas Thrainer
      node_lvs.Raise("Cannot get LV information from node %s" % pnode.name)
1153 22b7f6f8 Thomas Thrainer
      node_lvs = node_lvs.payload
1154 22b7f6f8 Thomas Thrainer
1155 22b7f6f8 Thomas Thrainer
      delta = all_lvs.difference(node_lvs.keys())
1156 22b7f6f8 Thomas Thrainer
      if delta:
1157 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing logical volume(s): %s" %
1158 22b7f6f8 Thomas Thrainer
                                   utils.CommaJoin(delta),
1159 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1160 22b7f6f8 Thomas Thrainer
      online_lvs = [lv for lv in all_lvs if node_lvs[lv][2]]
1161 22b7f6f8 Thomas Thrainer
      if online_lvs:
1162 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Online logical volumes found, cannot"
1163 22b7f6f8 Thomas Thrainer
                                   " adopt: %s" % utils.CommaJoin(online_lvs),
1164 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
1165 22b7f6f8 Thomas Thrainer
      # update the size of disk based on what is found
1166 22b7f6f8 Thomas Thrainer
      for dsk in self.disks:
1167 22b7f6f8 Thomas Thrainer
        dsk[constants.IDISK_SIZE] = \
1168 22b7f6f8 Thomas Thrainer
          int(float(node_lvs["%s/%s" % (dsk[constants.IDISK_VG],
1169 22b7f6f8 Thomas Thrainer
                                        dsk[constants.IDISK_ADOPT])][0]))
1170 22b7f6f8 Thomas Thrainer
1171 22b7f6f8 Thomas Thrainer
    elif self.op.disk_template == constants.DT_BLOCK:
1172 22b7f6f8 Thomas Thrainer
      # Normalize and de-duplicate device paths
1173 22b7f6f8 Thomas Thrainer
      all_disks = set([os.path.abspath(disk[constants.IDISK_ADOPT])
1174 22b7f6f8 Thomas Thrainer
                       for disk in self.disks])
1175 22b7f6f8 Thomas Thrainer
      if len(all_disks) != len(self.disks):
1176 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Duplicate disk names given for adoption",
1177 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1178 22b7f6f8 Thomas Thrainer
      baddisks = [d for d in all_disks
1179 22b7f6f8 Thomas Thrainer
                  if not d.startswith(constants.ADOPTABLE_BLOCKDEV_ROOT)]
1180 22b7f6f8 Thomas Thrainer
      if baddisks:
1181 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Device node(s) %s lie outside %s and"
1182 22b7f6f8 Thomas Thrainer
                                   " cannot be adopted" %
1183 22b7f6f8 Thomas Thrainer
                                   (utils.CommaJoin(baddisks),
1184 22b7f6f8 Thomas Thrainer
                                    constants.ADOPTABLE_BLOCKDEV_ROOT),
1185 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1186 22b7f6f8 Thomas Thrainer
1187 22b7f6f8 Thomas Thrainer
      node_disks = self.rpc.call_bdev_sizes([pnode.name],
1188 22b7f6f8 Thomas Thrainer
                                            list(all_disks))[pnode.name]
1189 22b7f6f8 Thomas Thrainer
      node_disks.Raise("Cannot get block device information from node %s" %
1190 22b7f6f8 Thomas Thrainer
                       pnode.name)
1191 22b7f6f8 Thomas Thrainer
      node_disks = node_disks.payload
1192 22b7f6f8 Thomas Thrainer
      delta = all_disks.difference(node_disks.keys())
1193 22b7f6f8 Thomas Thrainer
      if delta:
1194 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing block device(s): %s" %
1195 22b7f6f8 Thomas Thrainer
                                   utils.CommaJoin(delta),
1196 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1197 22b7f6f8 Thomas Thrainer
      for dsk in self.disks:
1198 22b7f6f8 Thomas Thrainer
        dsk[constants.IDISK_SIZE] = \
1199 22b7f6f8 Thomas Thrainer
          int(float(node_disks[dsk[constants.IDISK_ADOPT]]))
1200 22b7f6f8 Thomas Thrainer
1201 22b7f6f8 Thomas Thrainer
    # Verify instance specs
1202 22b7f6f8 Thomas Thrainer
    spindle_use = self.be_full.get(constants.BE_SPINDLE_USE, None)
1203 22b7f6f8 Thomas Thrainer
    ispec = {
1204 22b7f6f8 Thomas Thrainer
      constants.ISPEC_MEM_SIZE: self.be_full.get(constants.BE_MAXMEM, None),
1205 22b7f6f8 Thomas Thrainer
      constants.ISPEC_CPU_COUNT: self.be_full.get(constants.BE_VCPUS, None),
1206 22b7f6f8 Thomas Thrainer
      constants.ISPEC_DISK_COUNT: len(self.disks),
1207 22b7f6f8 Thomas Thrainer
      constants.ISPEC_DISK_SIZE: [disk[constants.IDISK_SIZE]
1208 22b7f6f8 Thomas Thrainer
                                  for disk in self.disks],
1209 22b7f6f8 Thomas Thrainer
      constants.ISPEC_NIC_COUNT: len(self.nics),
1210 22b7f6f8 Thomas Thrainer
      constants.ISPEC_SPINDLE_USE: spindle_use,
1211 22b7f6f8 Thomas Thrainer
      }
1212 22b7f6f8 Thomas Thrainer
1213 22b7f6f8 Thomas Thrainer
    group_info = self.cfg.GetNodeGroup(pnode.group)
1214 22b7f6f8 Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster, group_info)
1215 22b7f6f8 Thomas Thrainer
    res = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec,
1216 22b7f6f8 Thomas Thrainer
                                               self.op.disk_template)
1217 22b7f6f8 Thomas Thrainer
    if not self.op.ignore_ipolicy and res:
1218 22b7f6f8 Thomas Thrainer
      msg = ("Instance allocation to group %s (%s) violates policy: %s" %
1219 22b7f6f8 Thomas Thrainer
             (pnode.group, group_info.name, utils.CommaJoin(res)))
1220 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
1221 22b7f6f8 Thomas Thrainer
1222 22b7f6f8 Thomas Thrainer
    _CheckHVParams(self, nodenames, self.op.hypervisor, self.op.hvparams)
1223 22b7f6f8 Thomas Thrainer
1224 22b7f6f8 Thomas Thrainer
    _CheckNodeHasOS(self, pnode.name, self.op.os_type, self.op.force_variant)
1225 22b7f6f8 Thomas Thrainer
    # check OS parameters (remotely)
1226 22b7f6f8 Thomas Thrainer
    _CheckOSParams(self, True, nodenames, self.op.os_type, self.os_full)
1227 22b7f6f8 Thomas Thrainer
1228 22b7f6f8 Thomas Thrainer
    _CheckNicsBridgesExist(self, self.nics, self.pnode.name)
1229 22b7f6f8 Thomas Thrainer
1230 22b7f6f8 Thomas Thrainer
    #TODO: _CheckExtParams (remotely)
1231 22b7f6f8 Thomas Thrainer
    # Check parameters for extstorage
1232 22b7f6f8 Thomas Thrainer
1233 22b7f6f8 Thomas Thrainer
    # memory check on primary node
1234 22b7f6f8 Thomas Thrainer
    #TODO(dynmem): use MINMEM for checking
1235 22b7f6f8 Thomas Thrainer
    if self.op.start:
1236 22b7f6f8 Thomas Thrainer
      _CheckNodeFreeMemory(self, self.pnode.name,
1237 22b7f6f8 Thomas Thrainer
                           "creating instance %s" % self.op.instance_name,
1238 22b7f6f8 Thomas Thrainer
                           self.be_full[constants.BE_MAXMEM],
1239 22b7f6f8 Thomas Thrainer
                           self.op.hypervisor)
1240 22b7f6f8 Thomas Thrainer
1241 22b7f6f8 Thomas Thrainer
    self.dry_run_result = list(nodenames)
1242 22b7f6f8 Thomas Thrainer
1243 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1244 22b7f6f8 Thomas Thrainer
    """Create and add the instance to the cluster.
1245 22b7f6f8 Thomas Thrainer

1246 22b7f6f8 Thomas Thrainer
    """
1247 22b7f6f8 Thomas Thrainer
    instance = self.op.instance_name
1248 22b7f6f8 Thomas Thrainer
    pnode_name = self.pnode.name
1249 22b7f6f8 Thomas Thrainer
1250 22b7f6f8 Thomas Thrainer
    assert not (self.owned_locks(locking.LEVEL_NODE_RES) -
1251 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
1252 22b7f6f8 Thomas Thrainer
      "Node locks differ from node resource locks"
1253 22b7f6f8 Thomas Thrainer
    assert not self.glm.is_owned(locking.LEVEL_NODE_ALLOC)
1254 22b7f6f8 Thomas Thrainer
1255 22b7f6f8 Thomas Thrainer
    ht_kind = self.op.hypervisor
1256 22b7f6f8 Thomas Thrainer
    if ht_kind in constants.HTS_REQ_PORT:
1257 22b7f6f8 Thomas Thrainer
      network_port = self.cfg.AllocatePort()
1258 22b7f6f8 Thomas Thrainer
    else:
1259 22b7f6f8 Thomas Thrainer
      network_port = None
1260 22b7f6f8 Thomas Thrainer
1261 22b7f6f8 Thomas Thrainer
    # This is ugly but we got a chicken-egg problem here
1262 22b7f6f8 Thomas Thrainer
    # We can only take the group disk parameters, as the instance
1263 22b7f6f8 Thomas Thrainer
    # has no disks yet (we are generating them right here).
1264 22b7f6f8 Thomas Thrainer
    node = self.cfg.GetNodeInfo(pnode_name)
1265 22b7f6f8 Thomas Thrainer
    nodegroup = self.cfg.GetNodeGroup(node.group)
1266 22b7f6f8 Thomas Thrainer
    disks = _GenerateDiskTemplate(self,
1267 22b7f6f8 Thomas Thrainer
                                  self.op.disk_template,
1268 22b7f6f8 Thomas Thrainer
                                  instance, pnode_name,
1269 22b7f6f8 Thomas Thrainer
                                  self.secondaries,
1270 22b7f6f8 Thomas Thrainer
                                  self.disks,
1271 22b7f6f8 Thomas Thrainer
                                  self.instance_file_storage_dir,
1272 22b7f6f8 Thomas Thrainer
                                  self.op.file_driver,
1273 22b7f6f8 Thomas Thrainer
                                  0,
1274 22b7f6f8 Thomas Thrainer
                                  feedback_fn,
1275 22b7f6f8 Thomas Thrainer
                                  self.cfg.GetGroupDiskParams(nodegroup))
1276 22b7f6f8 Thomas Thrainer
1277 22b7f6f8 Thomas Thrainer
    iobj = objects.Instance(name=instance, os=self.op.os_type,
1278 22b7f6f8 Thomas Thrainer
                            primary_node=pnode_name,
1279 22b7f6f8 Thomas Thrainer
                            nics=self.nics, disks=disks,
1280 22b7f6f8 Thomas Thrainer
                            disk_template=self.op.disk_template,
1281 22b7f6f8 Thomas Thrainer
                            admin_state=constants.ADMINST_DOWN,
1282 22b7f6f8 Thomas Thrainer
                            network_port=network_port,
1283 22b7f6f8 Thomas Thrainer
                            beparams=self.op.beparams,
1284 22b7f6f8 Thomas Thrainer
                            hvparams=self.op.hvparams,
1285 22b7f6f8 Thomas Thrainer
                            hypervisor=self.op.hypervisor,
1286 22b7f6f8 Thomas Thrainer
                            osparams=self.op.osparams,
1287 22b7f6f8 Thomas Thrainer
                            )
1288 22b7f6f8 Thomas Thrainer
1289 22b7f6f8 Thomas Thrainer
    if self.op.tags:
1290 22b7f6f8 Thomas Thrainer
      for tag in self.op.tags:
1291 22b7f6f8 Thomas Thrainer
        iobj.AddTag(tag)
1292 22b7f6f8 Thomas Thrainer
1293 22b7f6f8 Thomas Thrainer
    if self.adopt_disks:
1294 22b7f6f8 Thomas Thrainer
      if self.op.disk_template == constants.DT_PLAIN:
1295 22b7f6f8 Thomas Thrainer
        # rename LVs to the newly-generated names; we need to construct
1296 22b7f6f8 Thomas Thrainer
        # 'fake' LV disks with the old data, plus the new unique_id
1297 22b7f6f8 Thomas Thrainer
        tmp_disks = [objects.Disk.FromDict(v.ToDict()) for v in disks]
1298 22b7f6f8 Thomas Thrainer
        rename_to = []
1299 22b7f6f8 Thomas Thrainer
        for t_dsk, a_dsk in zip(tmp_disks, self.disks):
1300 22b7f6f8 Thomas Thrainer
          rename_to.append(t_dsk.logical_id)
1301 22b7f6f8 Thomas Thrainer
          t_dsk.logical_id = (t_dsk.logical_id[0], a_dsk[constants.IDISK_ADOPT])
1302 22b7f6f8 Thomas Thrainer
          self.cfg.SetDiskID(t_dsk, pnode_name)
1303 22b7f6f8 Thomas Thrainer
        result = self.rpc.call_blockdev_rename(pnode_name,
1304 22b7f6f8 Thomas Thrainer
                                               zip(tmp_disks, rename_to))
1305 22b7f6f8 Thomas Thrainer
        result.Raise("Failed to rename adoped LVs")
1306 22b7f6f8 Thomas Thrainer
    else:
1307 22b7f6f8 Thomas Thrainer
      feedback_fn("* creating instance disks...")
1308 22b7f6f8 Thomas Thrainer
      try:
1309 22b7f6f8 Thomas Thrainer
        _CreateDisks(self, iobj)
1310 22b7f6f8 Thomas Thrainer
      except errors.OpExecError:
1311 22b7f6f8 Thomas Thrainer
        self.LogWarning("Device creation failed")
1312 22b7f6f8 Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(instance)
1313 22b7f6f8 Thomas Thrainer
        raise
1314 22b7f6f8 Thomas Thrainer
1315 22b7f6f8 Thomas Thrainer
    feedback_fn("adding instance %s to cluster config" % instance)
1316 22b7f6f8 Thomas Thrainer
1317 22b7f6f8 Thomas Thrainer
    self.cfg.AddInstance(iobj, self.proc.GetECId())
1318 22b7f6f8 Thomas Thrainer
1319 22b7f6f8 Thomas Thrainer
    # Declare that we don't want to remove the instance lock anymore, as we've
1320 22b7f6f8 Thomas Thrainer
    # added the instance to the config
1321 22b7f6f8 Thomas Thrainer
    del self.remove_locks[locking.LEVEL_INSTANCE]
1322 22b7f6f8 Thomas Thrainer
1323 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
1324 22b7f6f8 Thomas Thrainer
      # Release unused nodes
1325 22b7f6f8 Thomas Thrainer
      _ReleaseLocks(self, locking.LEVEL_NODE, keep=[self.op.src_node])
1326 22b7f6f8 Thomas Thrainer
    else:
1327 22b7f6f8 Thomas Thrainer
      # Release all nodes
1328 22b7f6f8 Thomas Thrainer
      _ReleaseLocks(self, locking.LEVEL_NODE)
1329 22b7f6f8 Thomas Thrainer
1330 22b7f6f8 Thomas Thrainer
    disk_abort = False
1331 22b7f6f8 Thomas Thrainer
    if not self.adopt_disks and self.cfg.GetClusterInfo().prealloc_wipe_disks:
1332 22b7f6f8 Thomas Thrainer
      feedback_fn("* wiping instance disks...")
1333 22b7f6f8 Thomas Thrainer
      try:
1334 22b7f6f8 Thomas Thrainer
        _WipeDisks(self, iobj)
1335 22b7f6f8 Thomas Thrainer
      except errors.OpExecError, err:
1336 22b7f6f8 Thomas Thrainer
        logging.exception("Wiping disks failed")
1337 22b7f6f8 Thomas Thrainer
        self.LogWarning("Wiping instance disks failed (%s)", err)
1338 22b7f6f8 Thomas Thrainer
        disk_abort = True
1339 22b7f6f8 Thomas Thrainer
1340 22b7f6f8 Thomas Thrainer
    if disk_abort:
1341 22b7f6f8 Thomas Thrainer
      # Something is already wrong with the disks, don't do anything else
1342 22b7f6f8 Thomas Thrainer
      pass
1343 22b7f6f8 Thomas Thrainer
    elif self.op.wait_for_sync:
1344 22b7f6f8 Thomas Thrainer
      disk_abort = not _WaitForSync(self, iobj)
1345 22b7f6f8 Thomas Thrainer
    elif iobj.disk_template in constants.DTS_INT_MIRROR:
1346 22b7f6f8 Thomas Thrainer
      # make sure the disks are not degraded (still sync-ing is ok)
1347 22b7f6f8 Thomas Thrainer
      feedback_fn("* checking mirrors status")
1348 22b7f6f8 Thomas Thrainer
      disk_abort = not _WaitForSync(self, iobj, oneshot=True)
1349 22b7f6f8 Thomas Thrainer
    else:
1350 22b7f6f8 Thomas Thrainer
      disk_abort = False
1351 22b7f6f8 Thomas Thrainer
1352 22b7f6f8 Thomas Thrainer
    if disk_abort:
1353 22b7f6f8 Thomas Thrainer
      _RemoveDisks(self, iobj)
1354 22b7f6f8 Thomas Thrainer
      self.cfg.RemoveInstance(iobj.name)
1355 22b7f6f8 Thomas Thrainer
      # Make sure the instance lock gets removed
1356 22b7f6f8 Thomas Thrainer
      self.remove_locks[locking.LEVEL_INSTANCE] = iobj.name
1357 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("There are some degraded disks for"
1358 22b7f6f8 Thomas Thrainer
                               " this instance")
1359 22b7f6f8 Thomas Thrainer
1360 22b7f6f8 Thomas Thrainer
    # Release all node resource locks
1361 22b7f6f8 Thomas Thrainer
    _ReleaseLocks(self, locking.LEVEL_NODE_RES)
1362 22b7f6f8 Thomas Thrainer
1363 22b7f6f8 Thomas Thrainer
    if iobj.disk_template != constants.DT_DISKLESS and not self.adopt_disks:
1364 22b7f6f8 Thomas Thrainer
      # we need to set the disks ID to the primary node, since the
1365 22b7f6f8 Thomas Thrainer
      # preceding code might or might have not done it, depending on
1366 22b7f6f8 Thomas Thrainer
      # disk template and other options
1367 22b7f6f8 Thomas Thrainer
      for disk in iobj.disks:
1368 22b7f6f8 Thomas Thrainer
        self.cfg.SetDiskID(disk, pnode_name)
1369 22b7f6f8 Thomas Thrainer
      if self.op.mode == constants.INSTANCE_CREATE:
1370 22b7f6f8 Thomas Thrainer
        if not self.op.no_install:
1371 22b7f6f8 Thomas Thrainer
          pause_sync = (iobj.disk_template in constants.DTS_INT_MIRROR and
1372 22b7f6f8 Thomas Thrainer
                        not self.op.wait_for_sync)
1373 22b7f6f8 Thomas Thrainer
          if pause_sync:
1374 22b7f6f8 Thomas Thrainer
            feedback_fn("* pausing disk sync to install instance OS")
1375 22b7f6f8 Thomas Thrainer
            result = self.rpc.call_blockdev_pause_resume_sync(pnode_name,
1376 22b7f6f8 Thomas Thrainer
                                                              (iobj.disks,
1377 22b7f6f8 Thomas Thrainer
                                                               iobj), True)
1378 22b7f6f8 Thomas Thrainer
            for idx, success in enumerate(result.payload):
1379 22b7f6f8 Thomas Thrainer
              if not success:
1380 22b7f6f8 Thomas Thrainer
                logging.warn("pause-sync of instance %s for disk %d failed",
1381 22b7f6f8 Thomas Thrainer
                             instance, idx)
1382 22b7f6f8 Thomas Thrainer
1383 22b7f6f8 Thomas Thrainer
          feedback_fn("* running the instance OS create scripts...")
1384 22b7f6f8 Thomas Thrainer
          # FIXME: pass debug option from opcode to backend
1385 22b7f6f8 Thomas Thrainer
          os_add_result = \
1386 22b7f6f8 Thomas Thrainer
            self.rpc.call_instance_os_add(pnode_name, (iobj, None), False,
1387 22b7f6f8 Thomas Thrainer
                                          self.op.debug_level)
1388 22b7f6f8 Thomas Thrainer
          if pause_sync:
1389 22b7f6f8 Thomas Thrainer
            feedback_fn("* resuming disk sync")
1390 22b7f6f8 Thomas Thrainer
            result = self.rpc.call_blockdev_pause_resume_sync(pnode_name,
1391 22b7f6f8 Thomas Thrainer
                                                              (iobj.disks,
1392 22b7f6f8 Thomas Thrainer
                                                               iobj), False)
1393 22b7f6f8 Thomas Thrainer
            for idx, success in enumerate(result.payload):
1394 22b7f6f8 Thomas Thrainer
              if not success:
1395 22b7f6f8 Thomas Thrainer
                logging.warn("resume-sync of instance %s for disk %d failed",
1396 22b7f6f8 Thomas Thrainer
                             instance, idx)
1397 22b7f6f8 Thomas Thrainer
1398 22b7f6f8 Thomas Thrainer
          os_add_result.Raise("Could not add os for instance %s"
1399 22b7f6f8 Thomas Thrainer
                              " on node %s" % (instance, pnode_name))
1400 22b7f6f8 Thomas Thrainer
1401 22b7f6f8 Thomas Thrainer
      else:
1402 22b7f6f8 Thomas Thrainer
        if self.op.mode == constants.INSTANCE_IMPORT:
1403 22b7f6f8 Thomas Thrainer
          feedback_fn("* running the instance OS import scripts...")
1404 22b7f6f8 Thomas Thrainer
1405 22b7f6f8 Thomas Thrainer
          transfers = []
1406 22b7f6f8 Thomas Thrainer
1407 22b7f6f8 Thomas Thrainer
          for idx, image in enumerate(self.src_images):
1408 22b7f6f8 Thomas Thrainer
            if not image:
1409 22b7f6f8 Thomas Thrainer
              continue
1410 22b7f6f8 Thomas Thrainer
1411 22b7f6f8 Thomas Thrainer
            # FIXME: pass debug option from opcode to backend
1412 22b7f6f8 Thomas Thrainer
            dt = masterd.instance.DiskTransfer("disk/%s" % idx,
1413 22b7f6f8 Thomas Thrainer
                                               constants.IEIO_FILE, (image, ),
1414 22b7f6f8 Thomas Thrainer
                                               constants.IEIO_SCRIPT,
1415 22b7f6f8 Thomas Thrainer
                                               (iobj.disks[idx], idx),
1416 22b7f6f8 Thomas Thrainer
                                               None)
1417 22b7f6f8 Thomas Thrainer
            transfers.append(dt)
1418 22b7f6f8 Thomas Thrainer
1419 22b7f6f8 Thomas Thrainer
          import_result = \
1420 22b7f6f8 Thomas Thrainer
            masterd.instance.TransferInstanceData(self, feedback_fn,
1421 22b7f6f8 Thomas Thrainer
                                                  self.op.src_node, pnode_name,
1422 22b7f6f8 Thomas Thrainer
                                                  self.pnode.secondary_ip,
1423 22b7f6f8 Thomas Thrainer
                                                  iobj, transfers)
1424 22b7f6f8 Thomas Thrainer
          if not compat.all(import_result):
1425 22b7f6f8 Thomas Thrainer
            self.LogWarning("Some disks for instance %s on node %s were not"
1426 22b7f6f8 Thomas Thrainer
                            " imported successfully" % (instance, pnode_name))
1427 22b7f6f8 Thomas Thrainer
1428 22b7f6f8 Thomas Thrainer
          rename_from = self._old_instance_name
1429 22b7f6f8 Thomas Thrainer
1430 22b7f6f8 Thomas Thrainer
        elif self.op.mode == constants.INSTANCE_REMOTE_IMPORT:
1431 22b7f6f8 Thomas Thrainer
          feedback_fn("* preparing remote import...")
1432 22b7f6f8 Thomas Thrainer
          # The source cluster will stop the instance before attempting to make
1433 22b7f6f8 Thomas Thrainer
          # a connection. In some cases stopping an instance can take a long
1434 22b7f6f8 Thomas Thrainer
          # time, hence the shutdown timeout is added to the connection
1435 22b7f6f8 Thomas Thrainer
          # timeout.
1436 22b7f6f8 Thomas Thrainer
          connect_timeout = (constants.RIE_CONNECT_TIMEOUT +
1437 22b7f6f8 Thomas Thrainer
                             self.op.source_shutdown_timeout)
1438 22b7f6f8 Thomas Thrainer
          timeouts = masterd.instance.ImportExportTimeouts(connect_timeout)
1439 22b7f6f8 Thomas Thrainer
1440 22b7f6f8 Thomas Thrainer
          assert iobj.primary_node == self.pnode.name
1441 22b7f6f8 Thomas Thrainer
          disk_results = \
1442 22b7f6f8 Thomas Thrainer
            masterd.instance.RemoteImport(self, feedback_fn, iobj, self.pnode,
1443 22b7f6f8 Thomas Thrainer
                                          self.source_x509_ca,
1444 22b7f6f8 Thomas Thrainer
                                          self._cds, timeouts)
1445 22b7f6f8 Thomas Thrainer
          if not compat.all(disk_results):
1446 22b7f6f8 Thomas Thrainer
            # TODO: Should the instance still be started, even if some disks
1447 22b7f6f8 Thomas Thrainer
            # failed to import (valid for local imports, too)?
1448 22b7f6f8 Thomas Thrainer
            self.LogWarning("Some disks for instance %s on node %s were not"
1449 22b7f6f8 Thomas Thrainer
                            " imported successfully" % (instance, pnode_name))
1450 22b7f6f8 Thomas Thrainer
1451 22b7f6f8 Thomas Thrainer
          rename_from = self.source_instance_name
1452 22b7f6f8 Thomas Thrainer
1453 22b7f6f8 Thomas Thrainer
        else:
1454 22b7f6f8 Thomas Thrainer
          # also checked in the prereq part
1455 22b7f6f8 Thomas Thrainer
          raise errors.ProgrammerError("Unknown OS initialization mode '%s'"
1456 22b7f6f8 Thomas Thrainer
                                       % self.op.mode)
1457 22b7f6f8 Thomas Thrainer
1458 22b7f6f8 Thomas Thrainer
        # Run rename script on newly imported instance
1459 22b7f6f8 Thomas Thrainer
        assert iobj.name == instance
1460 22b7f6f8 Thomas Thrainer
        feedback_fn("Running rename script for %s" % instance)
1461 22b7f6f8 Thomas Thrainer
        result = self.rpc.call_instance_run_rename(pnode_name, iobj,
1462 22b7f6f8 Thomas Thrainer
                                                   rename_from,
1463 22b7f6f8 Thomas Thrainer
                                                   self.op.debug_level)
1464 22b7f6f8 Thomas Thrainer
        if result.fail_msg:
1465 22b7f6f8 Thomas Thrainer
          self.LogWarning("Failed to run rename script for %s on node"
1466 22b7f6f8 Thomas Thrainer
                          " %s: %s" % (instance, pnode_name, result.fail_msg))
1467 22b7f6f8 Thomas Thrainer
1468 22b7f6f8 Thomas Thrainer
    assert not self.owned_locks(locking.LEVEL_NODE_RES)
1469 22b7f6f8 Thomas Thrainer
1470 22b7f6f8 Thomas Thrainer
    if self.op.start:
1471 22b7f6f8 Thomas Thrainer
      iobj.admin_state = constants.ADMINST_UP
1472 22b7f6f8 Thomas Thrainer
      self.cfg.Update(iobj, feedback_fn)
1473 22b7f6f8 Thomas Thrainer
      logging.info("Starting instance %s on node %s", instance, pnode_name)
1474 22b7f6f8 Thomas Thrainer
      feedback_fn("* starting instance...")
1475 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_start(pnode_name, (iobj, None, None),
1476 22b7f6f8 Thomas Thrainer
                                            False, self.op.reason)
1477 22b7f6f8 Thomas Thrainer
      result.Raise("Could not start instance")
1478 22b7f6f8 Thomas Thrainer
1479 22b7f6f8 Thomas Thrainer
    return list(iobj.all_nodes)
1480 22b7f6f8 Thomas Thrainer
1481 22b7f6f8 Thomas Thrainer
1482 22b7f6f8 Thomas Thrainer
class LUInstanceRename(LogicalUnit):
1483 22b7f6f8 Thomas Thrainer
  """Rename an instance.
1484 22b7f6f8 Thomas Thrainer

1485 22b7f6f8 Thomas Thrainer
  """
1486 22b7f6f8 Thomas Thrainer
  HPATH = "instance-rename"
1487 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1488 22b7f6f8 Thomas Thrainer
1489 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1490 22b7f6f8 Thomas Thrainer
    """Check arguments.
1491 22b7f6f8 Thomas Thrainer

1492 22b7f6f8 Thomas Thrainer
    """
1493 22b7f6f8 Thomas Thrainer
    if self.op.ip_check and not self.op.name_check:
1494 22b7f6f8 Thomas Thrainer
      # TODO: make the ip check more flexible and not depend on the name check
1495 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("IP address check requires a name check",
1496 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1497 22b7f6f8 Thomas Thrainer
1498 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1499 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1500 22b7f6f8 Thomas Thrainer

1501 22b7f6f8 Thomas Thrainer
    This runs on master, primary and secondary nodes of the instance.
1502 22b7f6f8 Thomas Thrainer

1503 22b7f6f8 Thomas Thrainer
    """
1504 22b7f6f8 Thomas Thrainer
    env = _BuildInstanceHookEnvByObject(self, self.instance)
1505 22b7f6f8 Thomas Thrainer
    env["INSTANCE_NEW_NAME"] = self.op.new_name
1506 22b7f6f8 Thomas Thrainer
    return env
1507 22b7f6f8 Thomas Thrainer
1508 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1509 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1510 22b7f6f8 Thomas Thrainer

1511 22b7f6f8 Thomas Thrainer
    """
1512 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
1513 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1514 22b7f6f8 Thomas Thrainer
1515 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1516 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1517 22b7f6f8 Thomas Thrainer

1518 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster and is not running.
1519 22b7f6f8 Thomas Thrainer

1520 22b7f6f8 Thomas Thrainer
    """
1521 22b7f6f8 Thomas Thrainer
    self.op.instance_name = _ExpandInstanceName(self.cfg,
1522 22b7f6f8 Thomas Thrainer
                                                self.op.instance_name)
1523 22b7f6f8 Thomas Thrainer
    instance = self.cfg.GetInstanceInfo(self.op.instance_name)
1524 22b7f6f8 Thomas Thrainer
    assert instance is not None
1525 22b7f6f8 Thomas Thrainer
    _CheckNodeOnline(self, instance.primary_node)
1526 22b7f6f8 Thomas Thrainer
    _CheckInstanceState(self, instance, INSTANCE_NOT_RUNNING,
1527 22b7f6f8 Thomas Thrainer
                        msg="cannot rename")
1528 22b7f6f8 Thomas Thrainer
    self.instance = instance
1529 22b7f6f8 Thomas Thrainer
1530 22b7f6f8 Thomas Thrainer
    new_name = self.op.new_name
1531 22b7f6f8 Thomas Thrainer
    if self.op.name_check:
1532 22b7f6f8 Thomas Thrainer
      hostname = _CheckHostnameSane(self, new_name)
1533 22b7f6f8 Thomas Thrainer
      new_name = self.op.new_name = hostname.name
1534 22b7f6f8 Thomas Thrainer
      if (self.op.ip_check and
1535 22b7f6f8 Thomas Thrainer
          netutils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT)):
1536 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("IP %s of instance %s already in use" %
1537 22b7f6f8 Thomas Thrainer
                                   (hostname.ip, new_name),
1538 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
1539 22b7f6f8 Thomas Thrainer
1540 22b7f6f8 Thomas Thrainer
    instance_list = self.cfg.GetInstanceList()
1541 22b7f6f8 Thomas Thrainer
    if new_name in instance_list and new_name != instance.name:
1542 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
1543 22b7f6f8 Thomas Thrainer
                                 new_name, errors.ECODE_EXISTS)
1544 22b7f6f8 Thomas Thrainer
1545 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1546 22b7f6f8 Thomas Thrainer
    """Rename the instance.
1547 22b7f6f8 Thomas Thrainer

1548 22b7f6f8 Thomas Thrainer
    """
1549 22b7f6f8 Thomas Thrainer
    inst = self.instance
1550 22b7f6f8 Thomas Thrainer
    old_name = inst.name
1551 22b7f6f8 Thomas Thrainer
1552 22b7f6f8 Thomas Thrainer
    rename_file_storage = False
1553 22b7f6f8 Thomas Thrainer
    if (inst.disk_template in constants.DTS_FILEBASED and
1554 22b7f6f8 Thomas Thrainer
        self.op.new_name != inst.name):
1555 22b7f6f8 Thomas Thrainer
      old_file_storage_dir = os.path.dirname(inst.disks[0].logical_id[1])
1556 22b7f6f8 Thomas Thrainer
      rename_file_storage = True
1557 22b7f6f8 Thomas Thrainer
1558 22b7f6f8 Thomas Thrainer
    self.cfg.RenameInstance(inst.name, self.op.new_name)
1559 22b7f6f8 Thomas Thrainer
    # Change the instance lock. This is definitely safe while we hold the BGL.
1560 22b7f6f8 Thomas Thrainer
    # Otherwise the new lock would have to be added in acquired mode.
1561 22b7f6f8 Thomas Thrainer
    assert self.REQ_BGL
1562 22b7f6f8 Thomas Thrainer
    assert locking.BGL in self.owned_locks(locking.LEVEL_CLUSTER)
1563 22b7f6f8 Thomas Thrainer
    self.glm.remove(locking.LEVEL_INSTANCE, old_name)
1564 22b7f6f8 Thomas Thrainer
    self.glm.add(locking.LEVEL_INSTANCE, self.op.new_name)
1565 22b7f6f8 Thomas Thrainer
1566 22b7f6f8 Thomas Thrainer
    # re-read the instance from the configuration after rename
1567 22b7f6f8 Thomas Thrainer
    inst = self.cfg.GetInstanceInfo(self.op.new_name)
1568 22b7f6f8 Thomas Thrainer
1569 22b7f6f8 Thomas Thrainer
    if rename_file_storage:
1570 22b7f6f8 Thomas Thrainer
      new_file_storage_dir = os.path.dirname(inst.disks[0].logical_id[1])
1571 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_file_storage_dir_rename(inst.primary_node,
1572 22b7f6f8 Thomas Thrainer
                                                     old_file_storage_dir,
1573 22b7f6f8 Thomas Thrainer
                                                     new_file_storage_dir)
1574 22b7f6f8 Thomas Thrainer
      result.Raise("Could not rename on node %s directory '%s' to '%s'"
1575 22b7f6f8 Thomas Thrainer
                   " (but the instance has been renamed in Ganeti)" %
1576 22b7f6f8 Thomas Thrainer
                   (inst.primary_node, old_file_storage_dir,
1577 22b7f6f8 Thomas Thrainer
                    new_file_storage_dir))
1578 22b7f6f8 Thomas Thrainer
1579 22b7f6f8 Thomas Thrainer
    _StartInstanceDisks(self, inst, None)
1580 22b7f6f8 Thomas Thrainer
    # update info on disks
1581 22b7f6f8 Thomas Thrainer
    info = _GetInstanceInfoText(inst)
1582 22b7f6f8 Thomas Thrainer
    for (idx, disk) in enumerate(inst.disks):
1583 22b7f6f8 Thomas Thrainer
      for node in inst.all_nodes:
1584 22b7f6f8 Thomas Thrainer
        self.cfg.SetDiskID(disk, node)
1585 22b7f6f8 Thomas Thrainer
        result = self.rpc.call_blockdev_setinfo(node, disk, info)
1586 22b7f6f8 Thomas Thrainer
        if result.fail_msg:
1587 22b7f6f8 Thomas Thrainer
          self.LogWarning("Error setting info on node %s for disk %s: %s",
1588 22b7f6f8 Thomas Thrainer
                          node, idx, result.fail_msg)
1589 22b7f6f8 Thomas Thrainer
    try:
1590 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_run_rename(inst.primary_node, inst,
1591 22b7f6f8 Thomas Thrainer
                                                 old_name, self.op.debug_level)
1592 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
1593 22b7f6f8 Thomas Thrainer
      if msg:
1594 22b7f6f8 Thomas Thrainer
        msg = ("Could not run OS rename script for instance %s on node %s"
1595 22b7f6f8 Thomas Thrainer
               " (but the instance has been renamed in Ganeti): %s" %
1596 22b7f6f8 Thomas Thrainer
               (inst.name, inst.primary_node, msg))
1597 22b7f6f8 Thomas Thrainer
        self.LogWarning(msg)
1598 22b7f6f8 Thomas Thrainer
    finally:
1599 22b7f6f8 Thomas Thrainer
      _ShutdownInstanceDisks(self, inst)
1600 22b7f6f8 Thomas Thrainer
1601 22b7f6f8 Thomas Thrainer
    return inst.name
1602 22b7f6f8 Thomas Thrainer
1603 22b7f6f8 Thomas Thrainer
1604 22b7f6f8 Thomas Thrainer
class LUInstanceRemove(LogicalUnit):
1605 22b7f6f8 Thomas Thrainer
  """Remove an instance.
1606 22b7f6f8 Thomas Thrainer

1607 22b7f6f8 Thomas Thrainer
  """
1608 22b7f6f8 Thomas Thrainer
  HPATH = "instance-remove"
1609 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1610 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1611 22b7f6f8 Thomas Thrainer
1612 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1613 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1614 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
1615 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1616 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
1617 22b7f6f8 Thomas Thrainer
1618 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1619 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1620 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
1621 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1622 22b7f6f8 Thomas Thrainer
      # Copy node locks
1623 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1624 22b7f6f8 Thomas Thrainer
        _CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1625 22b7f6f8 Thomas Thrainer
1626 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1627 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1628 22b7f6f8 Thomas Thrainer

1629 22b7f6f8 Thomas Thrainer
    This runs on master, primary and secondary nodes of the instance.
1630 22b7f6f8 Thomas Thrainer

1631 22b7f6f8 Thomas Thrainer
    """
1632 22b7f6f8 Thomas Thrainer
    env = _BuildInstanceHookEnvByObject(self, self.instance)
1633 22b7f6f8 Thomas Thrainer
    env["SHUTDOWN_TIMEOUT"] = self.op.shutdown_timeout
1634 22b7f6f8 Thomas Thrainer
    return env
1635 22b7f6f8 Thomas Thrainer
1636 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1637 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1638 22b7f6f8 Thomas Thrainer

1639 22b7f6f8 Thomas Thrainer
    """
1640 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()]
1641 22b7f6f8 Thomas Thrainer
    nl_post = list(self.instance.all_nodes) + nl
1642 22b7f6f8 Thomas Thrainer
    return (nl, nl_post)
1643 22b7f6f8 Thomas Thrainer
1644 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1645 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1646 22b7f6f8 Thomas Thrainer

1647 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1648 22b7f6f8 Thomas Thrainer

1649 22b7f6f8 Thomas Thrainer
    """
1650 22b7f6f8 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
1651 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1652 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1653 22b7f6f8 Thomas Thrainer
1654 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1655 22b7f6f8 Thomas Thrainer
    """Remove the instance.
1656 22b7f6f8 Thomas Thrainer

1657 22b7f6f8 Thomas Thrainer
    """
1658 22b7f6f8 Thomas Thrainer
    instance = self.instance
1659 22b7f6f8 Thomas Thrainer
    logging.info("Shutting down instance %s on node %s",
1660 22b7f6f8 Thomas Thrainer
                 instance.name, instance.primary_node)
1661 22b7f6f8 Thomas Thrainer
1662 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_instance_shutdown(instance.primary_node, instance,
1663 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1664 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1665 22b7f6f8 Thomas Thrainer
    msg = result.fail_msg
1666 22b7f6f8 Thomas Thrainer
    if msg:
1667 22b7f6f8 Thomas Thrainer
      if self.op.ignore_failures:
1668 22b7f6f8 Thomas Thrainer
        feedback_fn("Warning: can't shutdown instance: %s" % msg)
1669 22b7f6f8 Thomas Thrainer
      else:
1670 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not shutdown instance %s on"
1671 22b7f6f8 Thomas Thrainer
                                 " node %s: %s" %
1672 22b7f6f8 Thomas Thrainer
                                 (instance.name, instance.primary_node, msg))
1673 22b7f6f8 Thomas Thrainer
1674 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1675 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1676 22b7f6f8 Thomas Thrainer
    assert not (set(instance.all_nodes) -
1677 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
1678 22b7f6f8 Thomas Thrainer
      "Not owning correct locks"
1679 22b7f6f8 Thomas Thrainer
1680 22b7f6f8 Thomas Thrainer
    _RemoveInstance(self, feedback_fn, instance, self.op.ignore_failures)
1681 22b7f6f8 Thomas Thrainer
1682 22b7f6f8 Thomas Thrainer
1683 22b7f6f8 Thomas Thrainer
def _CheckInstanceBridgesExist(lu, instance, node=None):
1684 22b7f6f8 Thomas Thrainer
  """Check that the brigdes needed by an instance exist.
1685 22b7f6f8 Thomas Thrainer

1686 22b7f6f8 Thomas Thrainer
  """
1687 22b7f6f8 Thomas Thrainer
  if node is None:
1688 22b7f6f8 Thomas Thrainer
    node = instance.primary_node
1689 22b7f6f8 Thomas Thrainer
  _CheckNicsBridgesExist(lu, instance.nics, node)
1690 22b7f6f8 Thomas Thrainer
1691 22b7f6f8 Thomas Thrainer
1692 22b7f6f8 Thomas Thrainer
class LUInstanceMove(LogicalUnit):
1693 22b7f6f8 Thomas Thrainer
  """Move an instance by data-copying.
1694 22b7f6f8 Thomas Thrainer

1695 22b7f6f8 Thomas Thrainer
  """
1696 22b7f6f8 Thomas Thrainer
  HPATH = "instance-move"
1697 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1698 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1699 22b7f6f8 Thomas Thrainer
1700 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1701 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1702 22b7f6f8 Thomas Thrainer
    target_node = _ExpandNodeName(self.cfg, self.op.target_node)
1703 22b7f6f8 Thomas Thrainer
    self.op.target_node = target_node
1704 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = [target_node]
1705 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1706 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
1707 22b7f6f8 Thomas Thrainer
1708 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1709 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1710 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes(primary_only=True)
1711 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1712 22b7f6f8 Thomas Thrainer
      # Copy node locks
1713 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1714 22b7f6f8 Thomas Thrainer
        _CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1715 22b7f6f8 Thomas Thrainer
1716 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1717 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1718 22b7f6f8 Thomas Thrainer

1719 22b7f6f8 Thomas Thrainer
    This runs on master, primary and secondary nodes of the instance.
1720 22b7f6f8 Thomas Thrainer

1721 22b7f6f8 Thomas Thrainer
    """
1722 22b7f6f8 Thomas Thrainer
    env = {
1723 22b7f6f8 Thomas Thrainer
      "TARGET_NODE": self.op.target_node,
1724 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
1725 22b7f6f8 Thomas Thrainer
      }
1726 22b7f6f8 Thomas Thrainer
    env.update(_BuildInstanceHookEnvByObject(self, self.instance))
1727 22b7f6f8 Thomas Thrainer
    return env
1728 22b7f6f8 Thomas Thrainer
1729 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1730 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1731 22b7f6f8 Thomas Thrainer

1732 22b7f6f8 Thomas Thrainer
    """
1733 22b7f6f8 Thomas Thrainer
    nl = [
1734 22b7f6f8 Thomas Thrainer
      self.cfg.GetMasterNode(),
1735 22b7f6f8 Thomas Thrainer
      self.instance.primary_node,
1736 22b7f6f8 Thomas Thrainer
      self.op.target_node,
1737 22b7f6f8 Thomas Thrainer
      ]
1738 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1739 22b7f6f8 Thomas Thrainer
1740 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1741 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1742 22b7f6f8 Thomas Thrainer

1743 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1744 22b7f6f8 Thomas Thrainer

1745 22b7f6f8 Thomas Thrainer
    """
1746 22b7f6f8 Thomas Thrainer
    self.instance = instance = self.cfg.GetInstanceInfo(self.op.instance_name)
1747 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1748 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1749 22b7f6f8 Thomas Thrainer
1750 22b7f6f8 Thomas Thrainer
    if instance.disk_template not in constants.DTS_COPYABLE:
1751 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk template %s not suitable for copying" %
1752 22b7f6f8 Thomas Thrainer
                                 instance.disk_template, errors.ECODE_STATE)
1753 22b7f6f8 Thomas Thrainer
1754 22b7f6f8 Thomas Thrainer
    node = self.cfg.GetNodeInfo(self.op.target_node)
1755 22b7f6f8 Thomas Thrainer
    assert node is not None, \
1756 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked node %s" % self.op.target_node
1757 22b7f6f8 Thomas Thrainer
1758 22b7f6f8 Thomas Thrainer
    self.target_node = target_node = node.name
1759 22b7f6f8 Thomas Thrainer
1760 22b7f6f8 Thomas Thrainer
    if target_node == instance.primary_node:
1761 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance %s is already on the node %s" %
1762 22b7f6f8 Thomas Thrainer
                                 (instance.name, target_node),
1763 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
1764 22b7f6f8 Thomas Thrainer
1765 22b7f6f8 Thomas Thrainer
    bep = self.cfg.GetClusterInfo().FillBE(instance)
1766 22b7f6f8 Thomas Thrainer
1767 22b7f6f8 Thomas Thrainer
    for idx, dsk in enumerate(instance.disks):
1768 22b7f6f8 Thomas Thrainer
      if dsk.dev_type not in (constants.LD_LV, constants.LD_FILE):
1769 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance disk %d has a complex layout,"
1770 22b7f6f8 Thomas Thrainer
                                   " cannot copy" % idx, errors.ECODE_STATE)
1771 22b7f6f8 Thomas Thrainer
1772 22b7f6f8 Thomas Thrainer
    _CheckNodeOnline(self, target_node)
1773 22b7f6f8 Thomas Thrainer
    _CheckNodeNotDrained(self, target_node)
1774 22b7f6f8 Thomas Thrainer
    _CheckNodeVmCapable(self, target_node)
1775 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
1776 22b7f6f8 Thomas Thrainer
    group_info = self.cfg.GetNodeGroup(node.group)
1777 22b7f6f8 Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster, group_info)
1778 22b7f6f8 Thomas Thrainer
    _CheckTargetNodeIPolicy(self, ipolicy, instance, node, self.cfg,
1779 22b7f6f8 Thomas Thrainer
                            ignore=self.op.ignore_ipolicy)
1780 22b7f6f8 Thomas Thrainer
1781 22b7f6f8 Thomas Thrainer
    if instance.admin_state == constants.ADMINST_UP:
1782 22b7f6f8 Thomas Thrainer
      # check memory requirements on the secondary node
1783 22b7f6f8 Thomas Thrainer
      _CheckNodeFreeMemory(self, target_node,
1784 22b7f6f8 Thomas Thrainer
                           "failing over instance %s" %
1785 22b7f6f8 Thomas Thrainer
                           instance.name, bep[constants.BE_MAXMEM],
1786 22b7f6f8 Thomas Thrainer
                           instance.hypervisor)
1787 22b7f6f8 Thomas Thrainer
    else:
1788 22b7f6f8 Thomas Thrainer
      self.LogInfo("Not checking memory on the secondary node as"
1789 22b7f6f8 Thomas Thrainer
                   " instance will not be started")
1790 22b7f6f8 Thomas Thrainer
1791 22b7f6f8 Thomas Thrainer
    # check bridge existance
1792 22b7f6f8 Thomas Thrainer
    _CheckInstanceBridgesExist(self, instance, node=target_node)
1793 22b7f6f8 Thomas Thrainer
1794 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1795 22b7f6f8 Thomas Thrainer
    """Move an instance.
1796 22b7f6f8 Thomas Thrainer

1797 22b7f6f8 Thomas Thrainer
    The move is done by shutting it down on its present node, copying
1798 22b7f6f8 Thomas Thrainer
    the data over (slow) and starting it on the new node.
1799 22b7f6f8 Thomas Thrainer

1800 22b7f6f8 Thomas Thrainer
    """
1801 22b7f6f8 Thomas Thrainer
    instance = self.instance
1802 22b7f6f8 Thomas Thrainer
1803 22b7f6f8 Thomas Thrainer
    source_node = instance.primary_node
1804 22b7f6f8 Thomas Thrainer
    target_node = self.target_node
1805 22b7f6f8 Thomas Thrainer
1806 22b7f6f8 Thomas Thrainer
    self.LogInfo("Shutting down instance %s on source node %s",
1807 22b7f6f8 Thomas Thrainer
                 instance.name, source_node)
1808 22b7f6f8 Thomas Thrainer
1809 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1810 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1811 22b7f6f8 Thomas Thrainer
1812 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_instance_shutdown(source_node, instance,
1813 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1814 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1815 22b7f6f8 Thomas Thrainer
    msg = result.fail_msg
1816 22b7f6f8 Thomas Thrainer
    if msg:
1817 22b7f6f8 Thomas Thrainer
      if self.op.ignore_consistency:
1818 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not shutdown instance %s on node %s."
1819 22b7f6f8 Thomas Thrainer
                        " Proceeding anyway. Please make sure node"
1820 22b7f6f8 Thomas Thrainer
                        " %s is down. Error details: %s",
1821 22b7f6f8 Thomas Thrainer
                        instance.name, source_node, source_node, msg)
1822 22b7f6f8 Thomas Thrainer
      else:
1823 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not shutdown instance %s on"
1824 22b7f6f8 Thomas Thrainer
                                 " node %s: %s" %
1825 22b7f6f8 Thomas Thrainer
                                 (instance.name, source_node, msg))
1826 22b7f6f8 Thomas Thrainer
1827 22b7f6f8 Thomas Thrainer
    # create the target disks
1828 22b7f6f8 Thomas Thrainer
    try:
1829 22b7f6f8 Thomas Thrainer
      _CreateDisks(self, instance, target_node=target_node)
1830 22b7f6f8 Thomas Thrainer
    except errors.OpExecError:
1831 22b7f6f8 Thomas Thrainer
      self.LogWarning("Device creation failed")
1832 22b7f6f8 Thomas Thrainer
      self.cfg.ReleaseDRBDMinors(instance.name)
1833 22b7f6f8 Thomas Thrainer
      raise
1834 22b7f6f8 Thomas Thrainer
1835 22b7f6f8 Thomas Thrainer
    cluster_name = self.cfg.GetClusterInfo().cluster_name
1836 22b7f6f8 Thomas Thrainer
1837 22b7f6f8 Thomas Thrainer
    errs = []
1838 22b7f6f8 Thomas Thrainer
    # activate, get path, copy the data over
1839 22b7f6f8 Thomas Thrainer
    for idx, disk in enumerate(instance.disks):
1840 22b7f6f8 Thomas Thrainer
      self.LogInfo("Copying data for disk %d", idx)
1841 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_blockdev_assemble(target_node, (disk, instance),
1842 22b7f6f8 Thomas Thrainer
                                               instance.name, True, idx)
1843 22b7f6f8 Thomas Thrainer
      if result.fail_msg:
1844 22b7f6f8 Thomas Thrainer
        self.LogWarning("Can't assemble newly created disk %d: %s",
1845 22b7f6f8 Thomas Thrainer
                        idx, result.fail_msg)
1846 22b7f6f8 Thomas Thrainer
        errs.append(result.fail_msg)
1847 22b7f6f8 Thomas Thrainer
        break
1848 22b7f6f8 Thomas Thrainer
      dev_path = result.payload
1849 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_blockdev_export(source_node, (disk, instance),
1850 22b7f6f8 Thomas Thrainer
                                             target_node, dev_path,
1851 22b7f6f8 Thomas Thrainer
                                             cluster_name)
1852 22b7f6f8 Thomas Thrainer
      if result.fail_msg:
1853 22b7f6f8 Thomas Thrainer
        self.LogWarning("Can't copy data over for disk %d: %s",
1854 22b7f6f8 Thomas Thrainer
                        idx, result.fail_msg)
1855 22b7f6f8 Thomas Thrainer
        errs.append(result.fail_msg)
1856 22b7f6f8 Thomas Thrainer
        break
1857 22b7f6f8 Thomas Thrainer
1858 22b7f6f8 Thomas Thrainer
    if errs:
1859 22b7f6f8 Thomas Thrainer
      self.LogWarning("Some disks failed to copy, aborting")
1860 22b7f6f8 Thomas Thrainer
      try:
1861 22b7f6f8 Thomas Thrainer
        _RemoveDisks(self, instance, target_node=target_node)
1862 22b7f6f8 Thomas Thrainer
      finally:
1863 22b7f6f8 Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(instance.name)
1864 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Errors during disk copy: %s" %
1865 22b7f6f8 Thomas Thrainer
                                 (",".join(errs),))
1866 22b7f6f8 Thomas Thrainer
1867 22b7f6f8 Thomas Thrainer
    instance.primary_node = target_node
1868 22b7f6f8 Thomas Thrainer
    self.cfg.Update(instance, feedback_fn)
1869 22b7f6f8 Thomas Thrainer
1870 22b7f6f8 Thomas Thrainer
    self.LogInfo("Removing the disks on the original node")
1871 22b7f6f8 Thomas Thrainer
    _RemoveDisks(self, instance, target_node=source_node)
1872 22b7f6f8 Thomas Thrainer
1873 22b7f6f8 Thomas Thrainer
    # Only start the instance if it's marked as up
1874 22b7f6f8 Thomas Thrainer
    if instance.admin_state == constants.ADMINST_UP:
1875 22b7f6f8 Thomas Thrainer
      self.LogInfo("Starting instance %s on node %s",
1876 22b7f6f8 Thomas Thrainer
                   instance.name, target_node)
1877 22b7f6f8 Thomas Thrainer
1878 22b7f6f8 Thomas Thrainer
      disks_ok, _ = _AssembleInstanceDisks(self, instance,
1879 22b7f6f8 Thomas Thrainer
                                           ignore_secondaries=True)
1880 22b7f6f8 Thomas Thrainer
      if not disks_ok:
1881 22b7f6f8 Thomas Thrainer
        _ShutdownInstanceDisks(self, instance)
1882 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Can't activate the instance's disks")
1883 22b7f6f8 Thomas Thrainer
1884 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_start(target_node,
1885 22b7f6f8 Thomas Thrainer
                                            (instance, None, None), False,
1886 22b7f6f8 Thomas Thrainer
                                            self.op.reason)
1887 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
1888 22b7f6f8 Thomas Thrainer
      if msg:
1889 22b7f6f8 Thomas Thrainer
        _ShutdownInstanceDisks(self, instance)
1890 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not start instance %s on node %s: %s" %
1891 22b7f6f8 Thomas Thrainer
                                 (instance.name, target_node, msg))
1892 22b7f6f8 Thomas Thrainer
1893 22b7f6f8 Thomas Thrainer
1894 22b7f6f8 Thomas Thrainer
def _GetInstanceConsole(cluster, instance):
1895 22b7f6f8 Thomas Thrainer
  """Returns console information for an instance.
1896 22b7f6f8 Thomas Thrainer

1897 22b7f6f8 Thomas Thrainer
  @type cluster: L{objects.Cluster}
1898 22b7f6f8 Thomas Thrainer
  @type instance: L{objects.Instance}
1899 22b7f6f8 Thomas Thrainer
  @rtype: dict
1900 22b7f6f8 Thomas Thrainer

1901 22b7f6f8 Thomas Thrainer
  """
1902 22b7f6f8 Thomas Thrainer
  hyper = hypervisor.GetHypervisorClass(instance.hypervisor)
1903 22b7f6f8 Thomas Thrainer
  # beparams and hvparams are passed separately, to avoid editing the
1904 22b7f6f8 Thomas Thrainer
  # instance and then saving the defaults in the instance itself.
1905 22b7f6f8 Thomas Thrainer
  hvparams = cluster.FillHV(instance)
1906 22b7f6f8 Thomas Thrainer
  beparams = cluster.FillBE(instance)
1907 22b7f6f8 Thomas Thrainer
  console = hyper.GetInstanceConsole(instance, hvparams, beparams)
1908 22b7f6f8 Thomas Thrainer
1909 22b7f6f8 Thomas Thrainer
  assert console.instance == instance.name
1910 22b7f6f8 Thomas Thrainer
  assert console.Validate()
1911 22b7f6f8 Thomas Thrainer
1912 22b7f6f8 Thomas Thrainer
  return console.ToDict()
1913 22b7f6f8 Thomas Thrainer
1914 22b7f6f8 Thomas Thrainer
1915 22b7f6f8 Thomas Thrainer
class _InstanceQuery(_QueryBase):
1916 22b7f6f8 Thomas Thrainer
  FIELDS = query.INSTANCE_FIELDS
1917 22b7f6f8 Thomas Thrainer
1918 22b7f6f8 Thomas Thrainer
  def ExpandNames(self, lu):
1919 22b7f6f8 Thomas Thrainer
    lu.needed_locks = {}
1920 22b7f6f8 Thomas Thrainer
    lu.share_locks = _ShareAll()
1921 22b7f6f8 Thomas Thrainer
1922 22b7f6f8 Thomas Thrainer
    if self.names:
1923 22b7f6f8 Thomas Thrainer
      self.wanted = _GetWantedInstances(lu, self.names)
1924 22b7f6f8 Thomas Thrainer
    else:
1925 22b7f6f8 Thomas Thrainer
      self.wanted = locking.ALL_SET
1926 22b7f6f8 Thomas Thrainer
1927 22b7f6f8 Thomas Thrainer
    self.do_locking = (self.use_locking and
1928 22b7f6f8 Thomas Thrainer
                       query.IQ_LIVE in self.requested_data)
1929 22b7f6f8 Thomas Thrainer
    if self.do_locking:
1930 22b7f6f8 Thomas Thrainer
      lu.needed_locks[locking.LEVEL_INSTANCE] = self.wanted
1931 22b7f6f8 Thomas Thrainer
      lu.needed_locks[locking.LEVEL_NODEGROUP] = []
1932 22b7f6f8 Thomas Thrainer
      lu.needed_locks[locking.LEVEL_NODE] = []
1933 22b7f6f8 Thomas Thrainer
      lu.needed_locks[locking.LEVEL_NETWORK] = []
1934 22b7f6f8 Thomas Thrainer
      lu.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
1935 22b7f6f8 Thomas Thrainer
1936 22b7f6f8 Thomas Thrainer
    self.do_grouplocks = (self.do_locking and
1937 22b7f6f8 Thomas Thrainer
                          query.IQ_NODES in self.requested_data)
1938 22b7f6f8 Thomas Thrainer
1939 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, lu, level):
1940 22b7f6f8 Thomas Thrainer
    if self.do_locking:
1941 22b7f6f8 Thomas Thrainer
      if level == locking.LEVEL_NODEGROUP and self.do_grouplocks:
1942 22b7f6f8 Thomas Thrainer
        assert not lu.needed_locks[locking.LEVEL_NODEGROUP]
1943 22b7f6f8 Thomas Thrainer
1944 22b7f6f8 Thomas Thrainer
        # Lock all groups used by instances optimistically; this requires going
1945 22b7f6f8 Thomas Thrainer
        # via the node before it's locked, requiring verification later on
1946 22b7f6f8 Thomas Thrainer
        lu.needed_locks[locking.LEVEL_NODEGROUP] = \
1947 22b7f6f8 Thomas Thrainer
          set(group_uuid
1948 22b7f6f8 Thomas Thrainer
              for instance_name in lu.owned_locks(locking.LEVEL_INSTANCE)
1949 22b7f6f8 Thomas Thrainer
              for group_uuid in lu.cfg.GetInstanceNodeGroups(instance_name))
1950 22b7f6f8 Thomas Thrainer
      elif level == locking.LEVEL_NODE:
1951 22b7f6f8 Thomas Thrainer
        lu._LockInstancesNodes() # pylint: disable=W0212
1952 22b7f6f8 Thomas Thrainer
1953 22b7f6f8 Thomas Thrainer
      elif level == locking.LEVEL_NETWORK:
1954 22b7f6f8 Thomas Thrainer
        lu.needed_locks[locking.LEVEL_NETWORK] = \
1955 22b7f6f8 Thomas Thrainer
          frozenset(net_uuid
1956 22b7f6f8 Thomas Thrainer
                    for instance_name in lu.owned_locks(locking.LEVEL_INSTANCE)
1957 22b7f6f8 Thomas Thrainer
                    for net_uuid in lu.cfg.GetInstanceNetworks(instance_name))
1958 22b7f6f8 Thomas Thrainer
1959 22b7f6f8 Thomas Thrainer
  @staticmethod
1960 22b7f6f8 Thomas Thrainer
  def _CheckGroupLocks(lu):
1961 22b7f6f8 Thomas Thrainer
    owned_instances = frozenset(lu.owned_locks(locking.LEVEL_INSTANCE))
1962 22b7f6f8 Thomas Thrainer
    owned_groups = frozenset(lu.owned_locks(locking.LEVEL_NODEGROUP))
1963 22b7f6f8 Thomas Thrainer
1964 22b7f6f8 Thomas Thrainer
    # Check if node groups for locked instances are still correct
1965 22b7f6f8 Thomas Thrainer
    for instance_name in owned_instances:
1966 22b7f6f8 Thomas Thrainer
      _CheckInstanceNodeGroups(lu.cfg, instance_name, owned_groups)
1967 22b7f6f8 Thomas Thrainer
1968 22b7f6f8 Thomas Thrainer
  def _GetQueryData(self, lu):
1969 22b7f6f8 Thomas Thrainer
    """Computes the list of instances and their attributes.
1970 22b7f6f8 Thomas Thrainer

1971 22b7f6f8 Thomas Thrainer
    """
1972 22b7f6f8 Thomas Thrainer
    if self.do_grouplocks:
1973 22b7f6f8 Thomas Thrainer
      self._CheckGroupLocks(lu)
1974 22b7f6f8 Thomas Thrainer
1975 22b7f6f8 Thomas Thrainer
    cluster = lu.cfg.GetClusterInfo()
1976 22b7f6f8 Thomas Thrainer
    all_info = lu.cfg.GetAllInstancesInfo()
1977 22b7f6f8 Thomas Thrainer
1978 22b7f6f8 Thomas Thrainer
    instance_names = self._GetNames(lu, all_info.keys(), locking.LEVEL_INSTANCE)
1979 22b7f6f8 Thomas Thrainer
1980 22b7f6f8 Thomas Thrainer
    instance_list = [all_info[name] for name in instance_names]
1981 22b7f6f8 Thomas Thrainer
    nodes = frozenset(itertools.chain(*(inst.all_nodes
1982 22b7f6f8 Thomas Thrainer
                                        for inst in instance_list)))
1983 22b7f6f8 Thomas Thrainer
    hv_list = list(set([inst.hypervisor for inst in instance_list]))
1984 22b7f6f8 Thomas Thrainer
    bad_nodes = []
1985 22b7f6f8 Thomas Thrainer
    offline_nodes = []
1986 22b7f6f8 Thomas Thrainer
    wrongnode_inst = set()
1987 22b7f6f8 Thomas Thrainer
1988 22b7f6f8 Thomas Thrainer
    # Gather data as requested
1989 22b7f6f8 Thomas Thrainer
    if self.requested_data & set([query.IQ_LIVE, query.IQ_CONSOLE]):
1990 22b7f6f8 Thomas Thrainer
      live_data = {}
1991 22b7f6f8 Thomas Thrainer
      node_data = lu.rpc.call_all_instances_info(nodes, hv_list)
1992 22b7f6f8 Thomas Thrainer
      for name in nodes:
1993 22b7f6f8 Thomas Thrainer
        result = node_data[name]
1994 22b7f6f8 Thomas Thrainer
        if result.offline:
1995 22b7f6f8 Thomas Thrainer
          # offline nodes will be in both lists
1996 22b7f6f8 Thomas Thrainer
          assert result.fail_msg
1997 22b7f6f8 Thomas Thrainer
          offline_nodes.append(name)
1998 22b7f6f8 Thomas Thrainer
        if result.fail_msg:
1999 22b7f6f8 Thomas Thrainer
          bad_nodes.append(name)
2000 22b7f6f8 Thomas Thrainer
        elif result.payload:
2001 22b7f6f8 Thomas Thrainer
          for inst in result.payload:
2002 22b7f6f8 Thomas Thrainer
            if inst in all_info:
2003 22b7f6f8 Thomas Thrainer
              if all_info[inst].primary_node == name:
2004 22b7f6f8 Thomas Thrainer
                live_data.update(result.payload)
2005 22b7f6f8 Thomas Thrainer
              else:
2006 22b7f6f8 Thomas Thrainer
                wrongnode_inst.add(inst)
2007 22b7f6f8 Thomas Thrainer
            else:
2008 22b7f6f8 Thomas Thrainer
              # orphan instance; we don't list it here as we don't
2009 22b7f6f8 Thomas Thrainer
              # handle this case yet in the output of instance listing
2010 22b7f6f8 Thomas Thrainer
              logging.warning("Orphan instance '%s' found on node %s",
2011 22b7f6f8 Thomas Thrainer
                              inst, name)
2012 22b7f6f8 Thomas Thrainer
              # else no instance is alive
2013 22b7f6f8 Thomas Thrainer
    else:
2014 22b7f6f8 Thomas Thrainer
      live_data = {}
2015 22b7f6f8 Thomas Thrainer
2016 22b7f6f8 Thomas Thrainer
    if query.IQ_DISKUSAGE in self.requested_data:
2017 22b7f6f8 Thomas Thrainer
      gmi = ganeti.masterd.instance
2018 22b7f6f8 Thomas Thrainer
      disk_usage = dict((inst.name,
2019 22b7f6f8 Thomas Thrainer
                         gmi.ComputeDiskSize(inst.disk_template,
2020 22b7f6f8 Thomas Thrainer
                                             [{constants.IDISK_SIZE: disk.size}
2021 22b7f6f8 Thomas Thrainer
                                              for disk in inst.disks]))
2022 22b7f6f8 Thomas Thrainer
                        for inst in instance_list)
2023 22b7f6f8 Thomas Thrainer
    else:
2024 22b7f6f8 Thomas Thrainer
      disk_usage = None
2025 22b7f6f8 Thomas Thrainer
2026 22b7f6f8 Thomas Thrainer
    if query.IQ_CONSOLE in self.requested_data:
2027 22b7f6f8 Thomas Thrainer
      consinfo = {}
2028 22b7f6f8 Thomas Thrainer
      for inst in instance_list:
2029 22b7f6f8 Thomas Thrainer
        if inst.name in live_data:
2030 22b7f6f8 Thomas Thrainer
          # Instance is running
2031 22b7f6f8 Thomas Thrainer
          consinfo[inst.name] = _GetInstanceConsole(cluster, inst)
2032 22b7f6f8 Thomas Thrainer
        else:
2033 22b7f6f8 Thomas Thrainer
          consinfo[inst.name] = None
2034 22b7f6f8 Thomas Thrainer
      assert set(consinfo.keys()) == set(instance_names)
2035 22b7f6f8 Thomas Thrainer
    else:
2036 22b7f6f8 Thomas Thrainer
      consinfo = None
2037 22b7f6f8 Thomas Thrainer
2038 22b7f6f8 Thomas Thrainer
    if query.IQ_NODES in self.requested_data:
2039 22b7f6f8 Thomas Thrainer
      node_names = set(itertools.chain(*map(operator.attrgetter("all_nodes"),
2040 22b7f6f8 Thomas Thrainer
                                            instance_list)))
2041 22b7f6f8 Thomas Thrainer
      nodes = dict(lu.cfg.GetMultiNodeInfo(node_names))
2042 22b7f6f8 Thomas Thrainer
      groups = dict((uuid, lu.cfg.GetNodeGroup(uuid))
2043 22b7f6f8 Thomas Thrainer
                    for uuid in set(map(operator.attrgetter("group"),
2044 22b7f6f8 Thomas Thrainer
                                        nodes.values())))
2045 22b7f6f8 Thomas Thrainer
    else:
2046 22b7f6f8 Thomas Thrainer
      nodes = None
2047 22b7f6f8 Thomas Thrainer
      groups = None
2048 22b7f6f8 Thomas Thrainer
2049 22b7f6f8 Thomas Thrainer
    if query.IQ_NETWORKS in self.requested_data:
2050 22b7f6f8 Thomas Thrainer
      net_uuids = itertools.chain(*(lu.cfg.GetInstanceNetworks(i.name)
2051 22b7f6f8 Thomas Thrainer
                                    for i in instance_list))
2052 22b7f6f8 Thomas Thrainer
      networks = dict((uuid, lu.cfg.GetNetwork(uuid)) for uuid in net_uuids)
2053 22b7f6f8 Thomas Thrainer
    else:
2054 22b7f6f8 Thomas Thrainer
      networks = None
2055 22b7f6f8 Thomas Thrainer
2056 22b7f6f8 Thomas Thrainer
    return query.InstanceQueryData(instance_list, lu.cfg.GetClusterInfo(),
2057 22b7f6f8 Thomas Thrainer
                                   disk_usage, offline_nodes, bad_nodes,
2058 22b7f6f8 Thomas Thrainer
                                   live_data, wrongnode_inst, consinfo,
2059 22b7f6f8 Thomas Thrainer
                                   nodes, groups, networks)
2060 22b7f6f8 Thomas Thrainer
2061 22b7f6f8 Thomas Thrainer
2062 22b7f6f8 Thomas Thrainer
class LUInstanceQuery(NoHooksLU):
2063 22b7f6f8 Thomas Thrainer
  """Logical unit for querying instances.
2064 22b7f6f8 Thomas Thrainer

2065 22b7f6f8 Thomas Thrainer
  """
2066 22b7f6f8 Thomas Thrainer
  # pylint: disable=W0142
2067 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2068 22b7f6f8 Thomas Thrainer
2069 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
2070 22b7f6f8 Thomas Thrainer
    self.iq = _InstanceQuery(qlang.MakeSimpleFilter("name", self.op.names),
2071 22b7f6f8 Thomas Thrainer
                             self.op.output_fields, self.op.use_locking)
2072 22b7f6f8 Thomas Thrainer
2073 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2074 22b7f6f8 Thomas Thrainer
    self.iq.ExpandNames(self)
2075 22b7f6f8 Thomas Thrainer
2076 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
2077 22b7f6f8 Thomas Thrainer
    self.iq.DeclareLocks(self, level)
2078 22b7f6f8 Thomas Thrainer
2079 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2080 22b7f6f8 Thomas Thrainer
    return self.iq.OldStyleQuery(self)
2081 22b7f6f8 Thomas Thrainer
2082 22b7f6f8 Thomas Thrainer
2083 22b7f6f8 Thomas Thrainer
class LUInstanceQueryData(NoHooksLU):
2084 22b7f6f8 Thomas Thrainer
  """Query runtime instance data.
2085 22b7f6f8 Thomas Thrainer

2086 22b7f6f8 Thomas Thrainer
  """
2087 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2088 22b7f6f8 Thomas Thrainer
2089 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2090 22b7f6f8 Thomas Thrainer
    self.needed_locks = {}
2091 22b7f6f8 Thomas Thrainer
2092 22b7f6f8 Thomas Thrainer
    # Use locking if requested or when non-static information is wanted
2093 22b7f6f8 Thomas Thrainer
    if not (self.op.static or self.op.use_locking):
2094 22b7f6f8 Thomas Thrainer
      self.LogWarning("Non-static data requested, locks need to be acquired")
2095 22b7f6f8 Thomas Thrainer
      self.op.use_locking = True
2096 22b7f6f8 Thomas Thrainer
2097 22b7f6f8 Thomas Thrainer
    if self.op.instances or not self.op.use_locking:
2098 22b7f6f8 Thomas Thrainer
      # Expand instance names right here
2099 22b7f6f8 Thomas Thrainer
      self.wanted_names = _GetWantedInstances(self, self.op.instances)
2100 22b7f6f8 Thomas Thrainer
    else:
2101 22b7f6f8 Thomas Thrainer
      # Will use acquired locks
2102 22b7f6f8 Thomas Thrainer
      self.wanted_names = None
2103 22b7f6f8 Thomas Thrainer
2104 22b7f6f8 Thomas Thrainer
    if self.op.use_locking:
2105 22b7f6f8 Thomas Thrainer
      self.share_locks = _ShareAll()
2106 22b7f6f8 Thomas Thrainer
2107 22b7f6f8 Thomas Thrainer
      if self.wanted_names is None:
2108 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET
2109 22b7f6f8 Thomas Thrainer
      else:
2110 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_INSTANCE] = self.wanted_names
2111 22b7f6f8 Thomas Thrainer
2112 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = []
2113 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = []
2114 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NETWORK] = []
2115 22b7f6f8 Thomas Thrainer
      self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
2116 22b7f6f8 Thomas Thrainer
2117 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
2118 22b7f6f8 Thomas Thrainer
    if self.op.use_locking:
2119 22b7f6f8 Thomas Thrainer
      owned_instances = self.owned_locks(locking.LEVEL_INSTANCE)
2120 22b7f6f8 Thomas Thrainer
      if level == locking.LEVEL_NODEGROUP:
2121 22b7f6f8 Thomas Thrainer
2122 22b7f6f8 Thomas Thrainer
        # Lock all groups used by instances optimistically; this requires going
2123 22b7f6f8 Thomas Thrainer
        # via the node before it's locked, requiring verification later on
2124 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODEGROUP] = \
2125 22b7f6f8 Thomas Thrainer
          frozenset(group_uuid
2126 22b7f6f8 Thomas Thrainer
                    for instance_name in owned_instances
2127 22b7f6f8 Thomas Thrainer
                    for group_uuid in
2128 22b7f6f8 Thomas Thrainer
                    self.cfg.GetInstanceNodeGroups(instance_name))
2129 22b7f6f8 Thomas Thrainer
2130 22b7f6f8 Thomas Thrainer
      elif level == locking.LEVEL_NODE:
2131 22b7f6f8 Thomas Thrainer
        self._LockInstancesNodes()
2132 22b7f6f8 Thomas Thrainer
2133 22b7f6f8 Thomas Thrainer
      elif level == locking.LEVEL_NETWORK:
2134 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NETWORK] = \
2135 22b7f6f8 Thomas Thrainer
          frozenset(net_uuid
2136 22b7f6f8 Thomas Thrainer
                    for instance_name in owned_instances
2137 22b7f6f8 Thomas Thrainer
                    for net_uuid in
2138 22b7f6f8 Thomas Thrainer
                    self.cfg.GetInstanceNetworks(instance_name))
2139 22b7f6f8 Thomas Thrainer
2140 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2141 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
2142 22b7f6f8 Thomas Thrainer

2143 22b7f6f8 Thomas Thrainer
    This only checks the optional instance list against the existing names.
2144 22b7f6f8 Thomas Thrainer

2145 22b7f6f8 Thomas Thrainer
    """
2146 22b7f6f8 Thomas Thrainer
    owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
2147 22b7f6f8 Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
2148 22b7f6f8 Thomas Thrainer
    owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE))
2149 22b7f6f8 Thomas Thrainer
    owned_networks = frozenset(self.owned_locks(locking.LEVEL_NETWORK))
2150 22b7f6f8 Thomas Thrainer
2151 22b7f6f8 Thomas Thrainer
    if self.wanted_names is None:
2152 22b7f6f8 Thomas Thrainer
      assert self.op.use_locking, "Locking was not used"
2153 22b7f6f8 Thomas Thrainer
      self.wanted_names = owned_instances
2154 22b7f6f8 Thomas Thrainer
2155 22b7f6f8 Thomas Thrainer
    instances = dict(self.cfg.GetMultiInstanceInfo(self.wanted_names))
2156 22b7f6f8 Thomas Thrainer
2157 22b7f6f8 Thomas Thrainer
    if self.op.use_locking:
2158 22b7f6f8 Thomas Thrainer
      _CheckInstancesNodeGroups(self.cfg, instances, owned_groups, owned_nodes,
2159 22b7f6f8 Thomas Thrainer
                                None)
2160 22b7f6f8 Thomas Thrainer
    else:
2161 22b7f6f8 Thomas Thrainer
      assert not (owned_instances or owned_groups or
2162 22b7f6f8 Thomas Thrainer
                  owned_nodes or owned_networks)
2163 22b7f6f8 Thomas Thrainer
2164 22b7f6f8 Thomas Thrainer
    self.wanted_instances = instances.values()
2165 22b7f6f8 Thomas Thrainer
2166 22b7f6f8 Thomas Thrainer
  def _ComputeBlockdevStatus(self, node, instance, dev):
2167 22b7f6f8 Thomas Thrainer
    """Returns the status of a block device
2168 22b7f6f8 Thomas Thrainer

2169 22b7f6f8 Thomas Thrainer
    """
2170 22b7f6f8 Thomas Thrainer
    if self.op.static or not node:
2171 22b7f6f8 Thomas Thrainer
      return None
2172 22b7f6f8 Thomas Thrainer
2173 22b7f6f8 Thomas Thrainer
    self.cfg.SetDiskID(dev, node)
2174 22b7f6f8 Thomas Thrainer
2175 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_blockdev_find(node, dev)
2176 22b7f6f8 Thomas Thrainer
    if result.offline:
2177 22b7f6f8 Thomas Thrainer
      return None
2178 22b7f6f8 Thomas Thrainer
2179 22b7f6f8 Thomas Thrainer
    result.Raise("Can't compute disk status for %s" % instance.name)
2180 22b7f6f8 Thomas Thrainer
2181 22b7f6f8 Thomas Thrainer
    status = result.payload
2182 22b7f6f8 Thomas Thrainer
    if status is None:
2183 22b7f6f8 Thomas Thrainer
      return None
2184 22b7f6f8 Thomas Thrainer
2185 22b7f6f8 Thomas Thrainer
    return (status.dev_path, status.major, status.minor,
2186 22b7f6f8 Thomas Thrainer
            status.sync_percent, status.estimated_time,
2187 22b7f6f8 Thomas Thrainer
            status.is_degraded, status.ldisk_status)
2188 22b7f6f8 Thomas Thrainer
2189 22b7f6f8 Thomas Thrainer
  def _ComputeDiskStatus(self, instance, snode, dev):
2190 22b7f6f8 Thomas Thrainer
    """Compute block device status.
2191 22b7f6f8 Thomas Thrainer

2192 22b7f6f8 Thomas Thrainer
    """
2193 22b7f6f8 Thomas Thrainer
    (anno_dev,) = _AnnotateDiskParams(instance, [dev], self.cfg)
2194 22b7f6f8 Thomas Thrainer
2195 22b7f6f8 Thomas Thrainer
    return self._ComputeDiskStatusInner(instance, snode, anno_dev)
2196 22b7f6f8 Thomas Thrainer
2197 22b7f6f8 Thomas Thrainer
  def _ComputeDiskStatusInner(self, instance, snode, dev):
2198 22b7f6f8 Thomas Thrainer
    """Compute block device status.
2199 22b7f6f8 Thomas Thrainer

2200 22b7f6f8 Thomas Thrainer
    @attention: The device has to be annotated already.
2201 22b7f6f8 Thomas Thrainer

2202 22b7f6f8 Thomas Thrainer
    """
2203 22b7f6f8 Thomas Thrainer
    if dev.dev_type in constants.LDS_DRBD:
2204 22b7f6f8 Thomas Thrainer
      # we change the snode then (otherwise we use the one passed in)
2205 22b7f6f8 Thomas Thrainer
      if dev.logical_id[0] == instance.primary_node:
2206 22b7f6f8 Thomas Thrainer
        snode = dev.logical_id[1]
2207 22b7f6f8 Thomas Thrainer
      else:
2208 22b7f6f8 Thomas Thrainer
        snode = dev.logical_id[0]
2209 22b7f6f8 Thomas Thrainer
2210 22b7f6f8 Thomas Thrainer
    dev_pstatus = self._ComputeBlockdevStatus(instance.primary_node,
2211 22b7f6f8 Thomas Thrainer
                                              instance, dev)
2212 22b7f6f8 Thomas Thrainer
    dev_sstatus = self._ComputeBlockdevStatus(snode, instance, dev)
2213 22b7f6f8 Thomas Thrainer
2214 22b7f6f8 Thomas Thrainer
    if dev.children:
2215 22b7f6f8 Thomas Thrainer
      dev_children = map(compat.partial(self._ComputeDiskStatusInner,
2216 22b7f6f8 Thomas Thrainer
                                        instance, snode),
2217 22b7f6f8 Thomas Thrainer
                         dev.children)
2218 22b7f6f8 Thomas Thrainer
    else:
2219 22b7f6f8 Thomas Thrainer
      dev_children = []
2220 22b7f6f8 Thomas Thrainer
2221 22b7f6f8 Thomas Thrainer
    return {
2222 22b7f6f8 Thomas Thrainer
      "iv_name": dev.iv_name,
2223 22b7f6f8 Thomas Thrainer
      "dev_type": dev.dev_type,
2224 22b7f6f8 Thomas Thrainer
      "logical_id": dev.logical_id,
2225 22b7f6f8 Thomas Thrainer
      "physical_id": dev.physical_id,
2226 22b7f6f8 Thomas Thrainer
      "pstatus": dev_pstatus,
2227 22b7f6f8 Thomas Thrainer
      "sstatus": dev_sstatus,
2228 22b7f6f8 Thomas Thrainer
      "children": dev_children,
2229 22b7f6f8 Thomas Thrainer
      "mode": dev.mode,
2230 22b7f6f8 Thomas Thrainer
      "size": dev.size,
2231 22b7f6f8 Thomas Thrainer
      "name": dev.name,
2232 22b7f6f8 Thomas Thrainer
      "uuid": dev.uuid,
2233 22b7f6f8 Thomas Thrainer
      }
2234 22b7f6f8 Thomas Thrainer
2235 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2236 22b7f6f8 Thomas Thrainer
    """Gather and return data"""
2237 22b7f6f8 Thomas Thrainer
    result = {}
2238 22b7f6f8 Thomas Thrainer
2239 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
2240 22b7f6f8 Thomas Thrainer
2241 763ad5be Thomas Thrainer
    node_names = itertools.chain(*(i.all_nodes for i in self.wanted_instances))
2242 763ad5be Thomas Thrainer
    nodes = dict(self.cfg.GetMultiNodeInfo(node_names))
2243 22b7f6f8 Thomas Thrainer
2244 763ad5be Thomas Thrainer
    groups = dict(self.cfg.GetMultiNodeGroupInfo(node.group
2245 763ad5be Thomas Thrainer
                                                 for node in nodes.values()))
2246 22b7f6f8 Thomas Thrainer
2247 763ad5be Thomas Thrainer
    group2name_fn = lambda uuid: groups[uuid].name
2248 763ad5be Thomas Thrainer
    for instance in self.wanted_instances:
2249 763ad5be Thomas Thrainer
      pnode = nodes[instance.primary_node]
2250 22b7f6f8 Thomas Thrainer
2251 763ad5be Thomas Thrainer
      if self.op.static or pnode.offline:
2252 763ad5be Thomas Thrainer
        remote_state = None
2253 763ad5be Thomas Thrainer
        if pnode.offline:
2254 763ad5be Thomas Thrainer
          self.LogWarning("Primary node %s is marked offline, returning static"
2255 763ad5be Thomas Thrainer
                          " information only for instance %s" %
2256 763ad5be Thomas Thrainer
                          (pnode.name, instance.name))
2257 763ad5be Thomas Thrainer
      else:
2258 763ad5be Thomas Thrainer
        remote_info = self.rpc.call_instance_info(instance.primary_node,
2259 763ad5be Thomas Thrainer
                                                  instance.name,
2260 763ad5be Thomas Thrainer
                                                  instance.hypervisor)
2261 763ad5be Thomas Thrainer
        remote_info.Raise("Error checking node %s" % instance.primary_node)
2262 763ad5be Thomas Thrainer
        remote_info = remote_info.payload
2263 763ad5be Thomas Thrainer
        if remote_info and "state" in remote_info:
2264 763ad5be Thomas Thrainer
          remote_state = "up"
2265 763ad5be Thomas Thrainer
        else:
2266 763ad5be Thomas Thrainer
          if instance.admin_state == constants.ADMINST_UP:
2267 763ad5be Thomas Thrainer
            remote_state = "down"
2268 763ad5be Thomas Thrainer
          else:
2269 763ad5be Thomas Thrainer
            remote_state = instance.admin_state
2270 22b7f6f8 Thomas Thrainer
2271 763ad5be Thomas Thrainer
      disks = map(compat.partial(self._ComputeDiskStatus, instance, None),
2272 763ad5be Thomas Thrainer
                  instance.disks)
2273 22b7f6f8 Thomas Thrainer
2274 763ad5be Thomas Thrainer
      snodes_group_uuids = [nodes[snode_name].group
2275 763ad5be Thomas Thrainer
                            for snode_name in instance.secondary_nodes]
2276 22b7f6f8 Thomas Thrainer
2277 763ad5be Thomas Thrainer
      result[instance.name] = {
2278 763ad5be Thomas Thrainer
        "name": instance.name,
2279 763ad5be Thomas Thrainer
        "config_state": instance.admin_state,
2280 763ad5be Thomas Thrainer
        "run_state": remote_state,
2281 763ad5be Thomas Thrainer
        "pnode": instance.primary_node,
2282 763ad5be Thomas Thrainer
        "pnode_group_uuid": pnode.group,
2283 763ad5be Thomas Thrainer
        "pnode_group_name": group2name_fn(pnode.group),
2284 763ad5be Thomas Thrainer
        "snodes": instance.secondary_nodes,
2285 763ad5be Thomas Thrainer
        "snodes_group_uuids": snodes_group_uuids,
2286 763ad5be Thomas Thrainer
        "snodes_group_names": map(group2name_fn, snodes_group_uuids),
2287 763ad5be Thomas Thrainer
        "os": instance.os,
2288 763ad5be Thomas Thrainer
        # this happens to be the same format used for hooks
2289 763ad5be Thomas Thrainer
        "nics": _NICListToTuple(self, instance.nics),
2290 763ad5be Thomas Thrainer
        "disk_template": instance.disk_template,
2291 763ad5be Thomas Thrainer
        "disks": disks,
2292 763ad5be Thomas Thrainer
        "hypervisor": instance.hypervisor,
2293 763ad5be Thomas Thrainer
        "network_port": instance.network_port,
2294 763ad5be Thomas Thrainer
        "hv_instance": instance.hvparams,
2295 763ad5be Thomas Thrainer
        "hv_actual": cluster.FillHV(instance, skip_globals=True),
2296 763ad5be Thomas Thrainer
        "be_instance": instance.beparams,
2297 763ad5be Thomas Thrainer
        "be_actual": cluster.FillBE(instance),
2298 763ad5be Thomas Thrainer
        "os_instance": instance.osparams,
2299 763ad5be Thomas Thrainer
        "os_actual": cluster.SimpleFillOS(instance.os, instance.osparams),
2300 763ad5be Thomas Thrainer
        "serial_no": instance.serial_no,
2301 763ad5be Thomas Thrainer
        "mtime": instance.mtime,
2302 763ad5be Thomas Thrainer
        "ctime": instance.ctime,
2303 763ad5be Thomas Thrainer
        "uuid": instance.uuid,
2304 763ad5be Thomas Thrainer
        }
2305 763ad5be Thomas Thrainer
2306 763ad5be Thomas Thrainer
    return result
2307 22b7f6f8 Thomas Thrainer
2308 22b7f6f8 Thomas Thrainer
2309 22b7f6f8 Thomas Thrainer
class LUInstanceStartup(LogicalUnit):
2310 22b7f6f8 Thomas Thrainer
  """Starts an instance.
2311 22b7f6f8 Thomas Thrainer

2312 22b7f6f8 Thomas Thrainer
  """
2313 22b7f6f8 Thomas Thrainer
  HPATH = "instance-start"
2314 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2315 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2316 22b7f6f8 Thomas Thrainer
2317 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
2318 22b7f6f8 Thomas Thrainer
    # extra beparams
2319 22b7f6f8 Thomas Thrainer
    if self.op.beparams:
2320 22b7f6f8 Thomas Thrainer
      # fill the beparams dict
2321 22b7f6f8 Thomas Thrainer
      objects.UpgradeBeParams(self.op.beparams)
2322 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
2323 22b7f6f8 Thomas Thrainer
2324 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2325 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2326 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE_RES] = constants.LOCKS_REPLACE
2327 22b7f6f8 Thomas Thrainer
2328 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
2329 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE_RES:
2330 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes(primary_only=True, level=locking.LEVEL_NODE_RES)
2331 22b7f6f8 Thomas Thrainer
2332 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
2333 22b7f6f8 Thomas Thrainer
    """Build hooks env.
2334 22b7f6f8 Thomas Thrainer

2335 22b7f6f8 Thomas Thrainer
    This runs on master, primary and secondary nodes of the instance.
2336 22b7f6f8 Thomas Thrainer

2337 22b7f6f8 Thomas Thrainer
    """
2338 22b7f6f8 Thomas Thrainer
    env = {
2339 22b7f6f8 Thomas Thrainer
      "FORCE": self.op.force,
2340 22b7f6f8 Thomas Thrainer
      }
2341 22b7f6f8 Thomas Thrainer
2342 22b7f6f8 Thomas Thrainer
    env.update(_BuildInstanceHookEnvByObject(self, self.instance))
2343 22b7f6f8 Thomas Thrainer
2344 22b7f6f8 Thomas Thrainer
    return env
2345 22b7f6f8 Thomas Thrainer
2346 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2347 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2348 22b7f6f8 Thomas Thrainer

2349 22b7f6f8 Thomas Thrainer
    """
2350 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
2351 22b7f6f8 Thomas Thrainer
    return (nl, nl)
2352 22b7f6f8 Thomas Thrainer
2353 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2354 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
2355 22b7f6f8 Thomas Thrainer

2356 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
2357 22b7f6f8 Thomas Thrainer

2358 22b7f6f8 Thomas Thrainer
    """
2359 22b7f6f8 Thomas Thrainer
    self.instance = instance = self.cfg.GetInstanceInfo(self.op.instance_name)
2360 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
2361 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
2362 22b7f6f8 Thomas Thrainer
2363 22b7f6f8 Thomas Thrainer
    # extra hvparams
2364 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
2365 22b7f6f8 Thomas Thrainer
      # check hypervisor parameter syntax (locally)
2366 22b7f6f8 Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
2367 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(self.op.hvparams, constants.HVS_PARAMETER_TYPES)
2368 22b7f6f8 Thomas Thrainer
      filled_hvp = cluster.FillHV(instance)
2369 22b7f6f8 Thomas Thrainer
      filled_hvp.update(self.op.hvparams)
2370 22b7f6f8 Thomas Thrainer
      hv_type = hypervisor.GetHypervisorClass(instance.hypervisor)
2371 22b7f6f8 Thomas Thrainer
      hv_type.CheckParameterSyntax(filled_hvp)
2372 22b7f6f8 Thomas Thrainer
      _CheckHVParams(self, instance.all_nodes, instance.hypervisor, filled_hvp)
2373 22b7f6f8 Thomas Thrainer
2374 22b7f6f8 Thomas Thrainer
    _CheckInstanceState(self, instance, INSTANCE_ONLINE)
2375 22b7f6f8 Thomas Thrainer
2376 22b7f6f8 Thomas Thrainer
    self.primary_offline = self.cfg.GetNodeInfo(instance.primary_node).offline
2377 22b7f6f8 Thomas Thrainer
2378 22b7f6f8 Thomas Thrainer
    if self.primary_offline and self.op.ignore_offline_nodes:
2379 22b7f6f8 Thomas Thrainer
      self.LogWarning("Ignoring offline primary node")
2380 22b7f6f8 Thomas Thrainer
2381 22b7f6f8 Thomas Thrainer
      if self.op.hvparams or self.op.beparams:
2382 22b7f6f8 Thomas Thrainer
        self.LogWarning("Overridden parameters are ignored")
2383 22b7f6f8 Thomas Thrainer
    else:
2384 22b7f6f8 Thomas Thrainer
      _CheckNodeOnline(self, instance.primary_node)
2385 22b7f6f8 Thomas Thrainer
2386 22b7f6f8 Thomas Thrainer
      bep = self.cfg.GetClusterInfo().FillBE(instance)
2387 22b7f6f8 Thomas Thrainer
      bep.update(self.op.beparams)
2388 22b7f6f8 Thomas Thrainer
2389 22b7f6f8 Thomas Thrainer
      # check bridges existence
2390 22b7f6f8 Thomas Thrainer
      _CheckInstanceBridgesExist(self, instance)
2391 22b7f6f8 Thomas Thrainer
2392 22b7f6f8 Thomas Thrainer
      remote_info = self.rpc.call_instance_info(instance.primary_node,
2393 22b7f6f8 Thomas Thrainer
                                                instance.name,
2394 22b7f6f8 Thomas Thrainer
                                                instance.hypervisor)
2395 22b7f6f8 Thomas Thrainer
      remote_info.Raise("Error checking node %s" % instance.primary_node,
2396 22b7f6f8 Thomas Thrainer
                        prereq=True, ecode=errors.ECODE_ENVIRON)
2397 22b7f6f8 Thomas Thrainer
      if not remote_info.payload: # not running already
2398 22b7f6f8 Thomas Thrainer
        _CheckNodeFreeMemory(self, instance.primary_node,
2399 22b7f6f8 Thomas Thrainer
                             "starting instance %s" % instance.name,
2400 22b7f6f8 Thomas Thrainer
                             bep[constants.BE_MINMEM], instance.hypervisor)
2401 22b7f6f8 Thomas Thrainer
2402 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2403 22b7f6f8 Thomas Thrainer
    """Start the instance.
2404 22b7f6f8 Thomas Thrainer

2405 22b7f6f8 Thomas Thrainer
    """
2406 22b7f6f8 Thomas Thrainer
    instance = self.instance
2407 22b7f6f8 Thomas Thrainer
    force = self.op.force
2408 22b7f6f8 Thomas Thrainer
    reason = self.op.reason
2409 22b7f6f8 Thomas Thrainer
2410 22b7f6f8 Thomas Thrainer
    if not self.op.no_remember:
2411 22b7f6f8 Thomas Thrainer
      self.cfg.MarkInstanceUp(instance.name)
2412 22b7f6f8 Thomas Thrainer
2413 22b7f6f8 Thomas Thrainer
    if self.primary_offline:
2414 22b7f6f8 Thomas Thrainer
      assert self.op.ignore_offline_nodes
2415 22b7f6f8 Thomas Thrainer
      self.LogInfo("Primary node offline, marked instance as started")
2416 22b7f6f8 Thomas Thrainer
    else:
2417 22b7f6f8 Thomas Thrainer
      node_current = instance.primary_node
2418 22b7f6f8 Thomas Thrainer
2419 22b7f6f8 Thomas Thrainer
      _StartInstanceDisks(self, instance, force)
2420 22b7f6f8 Thomas Thrainer
2421 22b7f6f8 Thomas Thrainer
      result = \
2422 22b7f6f8 Thomas Thrainer
        self.rpc.call_instance_start(node_current,
2423 22b7f6f8 Thomas Thrainer
                                     (instance, self.op.hvparams,
2424 22b7f6f8 Thomas Thrainer
                                      self.op.beparams),
2425 22b7f6f8 Thomas Thrainer
                                     self.op.startup_paused, reason)
2426 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
2427 22b7f6f8 Thomas Thrainer
      if msg:
2428 22b7f6f8 Thomas Thrainer
        _ShutdownInstanceDisks(self, instance)
2429 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not start instance: %s" % msg)
2430 22b7f6f8 Thomas Thrainer
2431 22b7f6f8 Thomas Thrainer
2432 22b7f6f8 Thomas Thrainer
class LUInstanceShutdown(LogicalUnit):
2433 22b7f6f8 Thomas Thrainer
  """Shutdown an instance.
2434 22b7f6f8 Thomas Thrainer

2435 22b7f6f8 Thomas Thrainer
  """
2436 22b7f6f8 Thomas Thrainer
  HPATH = "instance-stop"
2437 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2438 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2439 22b7f6f8 Thomas Thrainer
2440 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2441 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2442 22b7f6f8 Thomas Thrainer
2443 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
2444 22b7f6f8 Thomas Thrainer
    """Build hooks env.
2445 22b7f6f8 Thomas Thrainer

2446 22b7f6f8 Thomas Thrainer
    This runs on master, primary and secondary nodes of the instance.
2447 22b7f6f8 Thomas Thrainer

2448 22b7f6f8 Thomas Thrainer
    """
2449 22b7f6f8 Thomas Thrainer
    env = _BuildInstanceHookEnvByObject(self, self.instance)
2450 22b7f6f8 Thomas Thrainer
    env["TIMEOUT"] = self.op.timeout
2451 22b7f6f8 Thomas Thrainer
    return env
2452 22b7f6f8 Thomas Thrainer
2453 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2454 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2455 22b7f6f8 Thomas Thrainer

2456 22b7f6f8 Thomas Thrainer
    """
2457 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
2458 22b7f6f8 Thomas Thrainer
    return (nl, nl)
2459 22b7f6f8 Thomas Thrainer
2460 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2461 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
2462 22b7f6f8 Thomas Thrainer

2463 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
2464 22b7f6f8 Thomas Thrainer

2465 22b7f6f8 Thomas Thrainer
    """
2466 22b7f6f8 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
2467 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
2468 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
2469 22b7f6f8 Thomas Thrainer
2470 22b7f6f8 Thomas Thrainer
    if not self.op.force:
2471 22b7f6f8 Thomas Thrainer
      _CheckInstanceState(self, self.instance, INSTANCE_ONLINE)
2472 22b7f6f8 Thomas Thrainer
    else:
2473 22b7f6f8 Thomas Thrainer
      self.LogWarning("Ignoring offline instance check")
2474 22b7f6f8 Thomas Thrainer
2475 22b7f6f8 Thomas Thrainer
    self.primary_offline = \
2476 22b7f6f8 Thomas Thrainer
      self.cfg.GetNodeInfo(self.instance.primary_node).offline
2477 22b7f6f8 Thomas Thrainer
2478 22b7f6f8 Thomas Thrainer
    if self.primary_offline and self.op.ignore_offline_nodes:
2479 22b7f6f8 Thomas Thrainer
      self.LogWarning("Ignoring offline primary node")
2480 22b7f6f8 Thomas Thrainer
    else:
2481 22b7f6f8 Thomas Thrainer
      _CheckNodeOnline(self, self.instance.primary_node)
2482 22b7f6f8 Thomas Thrainer
2483 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2484 22b7f6f8 Thomas Thrainer
    """Shutdown the instance.
2485 22b7f6f8 Thomas Thrainer

2486 22b7f6f8 Thomas Thrainer
    """
2487 22b7f6f8 Thomas Thrainer
    instance = self.instance
2488 22b7f6f8 Thomas Thrainer
    node_current = instance.primary_node
2489 22b7f6f8 Thomas Thrainer
    timeout = self.op.timeout
2490 22b7f6f8 Thomas Thrainer
    reason = self.op.reason
2491 22b7f6f8 Thomas Thrainer
2492 22b7f6f8 Thomas Thrainer
    # If the instance is offline we shouldn't mark it as down, as that
2493 22b7f6f8 Thomas Thrainer
    # resets the offline flag.
2494 22b7f6f8 Thomas Thrainer
    if not self.op.no_remember and instance.admin_state in INSTANCE_ONLINE:
2495 22b7f6f8 Thomas Thrainer
      self.cfg.MarkInstanceDown(instance.name)
2496 22b7f6f8 Thomas Thrainer
2497 22b7f6f8 Thomas Thrainer
    if self.primary_offline:
2498 22b7f6f8 Thomas Thrainer
      assert self.op.ignore_offline_nodes
2499 22b7f6f8 Thomas Thrainer
      self.LogInfo("Primary node offline, marked instance as stopped")
2500 22b7f6f8 Thomas Thrainer
    else:
2501 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_shutdown(node_current, instance, timeout,
2502 22b7f6f8 Thomas Thrainer
                                               reason)
2503 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
2504 22b7f6f8 Thomas Thrainer
      if msg:
2505 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not shutdown instance: %s", msg)
2506 22b7f6f8 Thomas Thrainer
2507 22b7f6f8 Thomas Thrainer
      _ShutdownInstanceDisks(self, instance)
2508 22b7f6f8 Thomas Thrainer
2509 22b7f6f8 Thomas Thrainer
2510 22b7f6f8 Thomas Thrainer
class LUInstanceReinstall(LogicalUnit):
2511 22b7f6f8 Thomas Thrainer
  """Reinstall an instance.
2512 22b7f6f8 Thomas Thrainer

2513 22b7f6f8 Thomas Thrainer
  """
2514 22b7f6f8 Thomas Thrainer
  HPATH = "instance-reinstall"
2515 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2516 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2517 22b7f6f8 Thomas Thrainer
2518 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2519 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2520 22b7f6f8 Thomas Thrainer
2521 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
2522 22b7f6f8 Thomas Thrainer
    """Build hooks env.
2523 22b7f6f8 Thomas Thrainer

2524 22b7f6f8 Thomas Thrainer
    This runs on master, primary and secondary nodes of the instance.
2525 22b7f6f8 Thomas Thrainer

2526 22b7f6f8 Thomas Thrainer
    """
2527 22b7f6f8 Thomas Thrainer
    return _BuildInstanceHookEnvByObject(self, self.instance)
2528 22b7f6f8 Thomas Thrainer
2529 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2530 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2531 22b7f6f8 Thomas Thrainer

2532 22b7f6f8 Thomas Thrainer
    """
2533 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
2534 22b7f6f8 Thomas Thrainer
    return (nl, nl)
2535 22b7f6f8 Thomas Thrainer
2536 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2537 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
2538 22b7f6f8 Thomas Thrainer

2539 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster and is not running.
2540 22b7f6f8 Thomas Thrainer

2541 22b7f6f8 Thomas Thrainer
    """
2542 22b7f6f8 Thomas Thrainer
    instance = self.cfg.GetInstanceInfo(self.op.instance_name)
2543 22b7f6f8 Thomas Thrainer
    assert instance is not None, \
2544 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
2545 22b7f6f8 Thomas Thrainer
    _CheckNodeOnline(self, instance.primary_node, "Instance primary node"
2546 22b7f6f8 Thomas Thrainer
                     " offline, cannot reinstall")
2547 22b7f6f8 Thomas Thrainer
2548 22b7f6f8 Thomas Thrainer
    if instance.disk_template == constants.DT_DISKLESS:
2549 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' has no disks" %
2550 22b7f6f8 Thomas Thrainer
                                 self.op.instance_name,
2551 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2552 22b7f6f8 Thomas Thrainer
    _CheckInstanceState(self, instance, INSTANCE_DOWN, msg="cannot reinstall")
2553 22b7f6f8 Thomas Thrainer
2554 22b7f6f8 Thomas Thrainer
    if self.op.os_type is not None:
2555 22b7f6f8 Thomas Thrainer
      # OS verification
2556 22b7f6f8 Thomas Thrainer
      pnode = _ExpandNodeName(self.cfg, instance.primary_node)
2557 22b7f6f8 Thomas Thrainer
      _CheckNodeHasOS(self, pnode, self.op.os_type, self.op.force_variant)
2558 22b7f6f8 Thomas Thrainer
      instance_os = self.op.os_type
2559 22b7f6f8 Thomas Thrainer
    else:
2560 22b7f6f8 Thomas Thrainer
      instance_os = instance.os
2561 22b7f6f8 Thomas Thrainer
2562 22b7f6f8 Thomas Thrainer
    nodelist = list(instance.all_nodes)
2563 22b7f6f8 Thomas Thrainer
2564 22b7f6f8 Thomas Thrainer
    if self.op.osparams:
2565 22b7f6f8 Thomas Thrainer
      i_osdict = _GetUpdatedParams(instance.osparams, self.op.osparams)
2566 22b7f6f8 Thomas Thrainer
      _CheckOSParams(self, True, nodelist, instance_os, i_osdict)
2567 22b7f6f8 Thomas Thrainer
      self.os_inst = i_osdict # the new dict (without defaults)
2568 22b7f6f8 Thomas Thrainer
    else:
2569 22b7f6f8 Thomas Thrainer
      self.os_inst = None
2570 22b7f6f8 Thomas Thrainer
2571 22b7f6f8 Thomas Thrainer
    self.instance = instance
2572 22b7f6f8 Thomas Thrainer
2573 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2574 22b7f6f8 Thomas Thrainer
    """Reinstall the instance.
2575 22b7f6f8 Thomas Thrainer

2576 22b7f6f8 Thomas Thrainer
    """
2577 22b7f6f8 Thomas Thrainer
    inst = self.instance
2578 22b7f6f8 Thomas Thrainer
2579 22b7f6f8 Thomas Thrainer
    if self.op.os_type is not None:
2580 22b7f6f8 Thomas Thrainer
      feedback_fn("Changing OS to '%s'..." % self.op.os_type)
2581 22b7f6f8 Thomas Thrainer
      inst.os = self.op.os_type
2582 22b7f6f8 Thomas Thrainer
      # Write to configuration
2583 22b7f6f8 Thomas Thrainer
      self.cfg.Update(inst, feedback_fn)
2584 22b7f6f8 Thomas Thrainer
2585 22b7f6f8 Thomas Thrainer
    _StartInstanceDisks(self, inst, None)
2586 22b7f6f8 Thomas Thrainer
    try:
2587 22b7f6f8 Thomas Thrainer
      feedback_fn("Running the instance OS create scripts...")
2588 22b7f6f8 Thomas Thrainer
      # FIXME: pass debug option from opcode to backend
2589 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_os_add(inst.primary_node,
2590 22b7f6f8 Thomas Thrainer
                                             (inst, self.os_inst), True,
2591 22b7f6f8 Thomas Thrainer
                                             self.op.debug_level)
2592 22b7f6f8 Thomas Thrainer
      result.Raise("Could not install OS for instance %s on node %s" %
2593 22b7f6f8 Thomas Thrainer
                   (inst.name, inst.primary_node))
2594 22b7f6f8 Thomas Thrainer
    finally:
2595 22b7f6f8 Thomas Thrainer
      _ShutdownInstanceDisks(self, inst)
2596 22b7f6f8 Thomas Thrainer
2597 22b7f6f8 Thomas Thrainer
2598 22b7f6f8 Thomas Thrainer
class LUInstanceReboot(LogicalUnit):
2599 22b7f6f8 Thomas Thrainer
  """Reboot an instance.
2600 22b7f6f8 Thomas Thrainer

2601 22b7f6f8 Thomas Thrainer
  """
2602 22b7f6f8 Thomas Thrainer
  HPATH = "instance-reboot"
2603 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2604 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2605 22b7f6f8 Thomas Thrainer
2606 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2607 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2608 22b7f6f8 Thomas Thrainer
2609 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
2610 22b7f6f8 Thomas Thrainer
    """Build hooks env.
2611 22b7f6f8 Thomas Thrainer

2612 22b7f6f8 Thomas Thrainer
    This runs on master, primary and secondary nodes of the instance.
2613 22b7f6f8 Thomas Thrainer

2614 22b7f6f8 Thomas Thrainer
    """
2615 22b7f6f8 Thomas Thrainer
    env = {
2616 22b7f6f8 Thomas Thrainer
      "IGNORE_SECONDARIES": self.op.ignore_secondaries,
2617 22b7f6f8 Thomas Thrainer
      "REBOOT_TYPE": self.op.reboot_type,
2618 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
2619 22b7f6f8 Thomas Thrainer
      }
2620 22b7f6f8 Thomas Thrainer
2621 22b7f6f8 Thomas Thrainer
    env.update(_BuildInstanceHookEnvByObject(self, self.instance))
2622 22b7f6f8 Thomas Thrainer
2623 22b7f6f8 Thomas Thrainer
    return env
2624 22b7f6f8 Thomas Thrainer
2625 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2626 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2627 22b7f6f8 Thomas Thrainer

2628 22b7f6f8 Thomas Thrainer
    """
2629 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
2630 22b7f6f8 Thomas Thrainer
    return (nl, nl)
2631 22b7f6f8 Thomas Thrainer
2632 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2633 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
2634 22b7f6f8 Thomas Thrainer

2635 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
2636 22b7f6f8 Thomas Thrainer

2637 22b7f6f8 Thomas Thrainer
    """
2638 22b7f6f8 Thomas Thrainer
    self.instance = instance = self.cfg.GetInstanceInfo(self.op.instance_name)
2639 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
2640 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
2641 22b7f6f8 Thomas Thrainer
    _CheckInstanceState(self, instance, INSTANCE_ONLINE)
2642 22b7f6f8 Thomas Thrainer
    _CheckNodeOnline(self, instance.primary_node)
2643 22b7f6f8 Thomas Thrainer
2644 22b7f6f8 Thomas Thrainer
    # check bridges existence
2645 22b7f6f8 Thomas Thrainer
    _CheckInstanceBridgesExist(self, instance)
2646 22b7f6f8 Thomas Thrainer
2647 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2648 22b7f6f8 Thomas Thrainer
    """Reboot the instance.
2649 22b7f6f8 Thomas Thrainer

2650 22b7f6f8 Thomas Thrainer
    """
2651 22b7f6f8 Thomas Thrainer
    instance = self.instance
2652 22b7f6f8 Thomas Thrainer
    ignore_secondaries = self.op.ignore_secondaries
2653 22b7f6f8 Thomas Thrainer
    reboot_type = self.op.reboot_type
2654 22b7f6f8 Thomas Thrainer
    reason = self.op.reason
2655 22b7f6f8 Thomas Thrainer
2656 22b7f6f8 Thomas Thrainer
    remote_info = self.rpc.call_instance_info(instance.primary_node,
2657 22b7f6f8 Thomas Thrainer
                                              instance.name,
2658 22b7f6f8 Thomas Thrainer
                                              instance.hypervisor)
2659 22b7f6f8 Thomas Thrainer
    remote_info.Raise("Error checking node %s" % instance.primary_node)
2660 22b7f6f8 Thomas Thrainer
    instance_running = bool(remote_info.payload)
2661 22b7f6f8 Thomas Thrainer
2662 22b7f6f8 Thomas Thrainer
    node_current = instance.primary_node
2663 22b7f6f8 Thomas Thrainer
2664 22b7f6f8 Thomas Thrainer
    if instance_running and reboot_type in [constants.INSTANCE_REBOOT_SOFT,
2665 22b7f6f8 Thomas Thrainer
                                            constants.INSTANCE_REBOOT_HARD]:
2666 22b7f6f8 Thomas Thrainer
      for disk in instance.disks:
2667 22b7f6f8 Thomas Thrainer
        self.cfg.SetDiskID(disk, node_current)
2668 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_reboot(node_current, instance,
2669 22b7f6f8 Thomas Thrainer
                                             reboot_type,
2670 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout, reason)
2671 22b7f6f8 Thomas Thrainer
      result.Raise("Could not reboot instance")
2672 22b7f6f8 Thomas Thrainer
    else:
2673 22b7f6f8 Thomas Thrainer
      if instance_running:
2674 22b7f6f8 Thomas Thrainer
        result = self.rpc.call_instance_shutdown(node_current, instance,
2675 22b7f6f8 Thomas Thrainer
                                                 self.op.shutdown_timeout,
2676 22b7f6f8 Thomas Thrainer
                                                 reason)
2677 22b7f6f8 Thomas Thrainer
        result.Raise("Could not shutdown instance for full reboot")
2678 22b7f6f8 Thomas Thrainer
        _ShutdownInstanceDisks(self, instance)
2679 22b7f6f8 Thomas Thrainer
      else:
2680 22b7f6f8 Thomas Thrainer
        self.LogInfo("Instance %s was already stopped, starting now",
2681 22b7f6f8 Thomas Thrainer
                     instance.name)
2682 22b7f6f8 Thomas Thrainer
      _StartInstanceDisks(self, instance, ignore_secondaries)
2683 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_start(node_current,
2684 22b7f6f8 Thomas Thrainer
                                            (instance, None, None), False,
2685 22b7f6f8 Thomas Thrainer
                                            reason)
2686 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
2687 22b7f6f8 Thomas Thrainer
      if msg:
2688 22b7f6f8 Thomas Thrainer
        _ShutdownInstanceDisks(self, instance)
2689 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not start instance for"
2690 22b7f6f8 Thomas Thrainer
                                 " full reboot: %s" % msg)
2691 22b7f6f8 Thomas Thrainer
2692 22b7f6f8 Thomas Thrainer
    self.cfg.MarkInstanceUp(instance.name)
2693 22b7f6f8 Thomas Thrainer
2694 22b7f6f8 Thomas Thrainer
2695 22b7f6f8 Thomas Thrainer
class LUInstanceConsole(NoHooksLU):
2696 22b7f6f8 Thomas Thrainer
  """Connect to an instance's console.
2697 22b7f6f8 Thomas Thrainer

2698 22b7f6f8 Thomas Thrainer
  This is somewhat special in that it returns the command line that
2699 22b7f6f8 Thomas Thrainer
  you need to run on the master node in order to connect to the
2700 22b7f6f8 Thomas Thrainer
  console.
2701 22b7f6f8 Thomas Thrainer

2702 22b7f6f8 Thomas Thrainer
  """
2703 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2704 22b7f6f8 Thomas Thrainer
2705 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2706 22b7f6f8 Thomas Thrainer
    self.share_locks = _ShareAll()
2707 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2708 22b7f6f8 Thomas Thrainer
2709 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2710 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
2711 22b7f6f8 Thomas Thrainer

2712 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
2713 22b7f6f8 Thomas Thrainer

2714 22b7f6f8 Thomas Thrainer
    """
2715 22b7f6f8 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
2716 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
2717 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
2718 22b7f6f8 Thomas Thrainer
    _CheckNodeOnline(self, self.instance.primary_node)
2719 22b7f6f8 Thomas Thrainer
2720 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2721 22b7f6f8 Thomas Thrainer
    """Connect to the console of an instance
2722 22b7f6f8 Thomas Thrainer

2723 22b7f6f8 Thomas Thrainer
    """
2724 22b7f6f8 Thomas Thrainer
    instance = self.instance
2725 22b7f6f8 Thomas Thrainer
    node = instance.primary_node
2726 22b7f6f8 Thomas Thrainer
2727 22b7f6f8 Thomas Thrainer
    node_insts = self.rpc.call_instance_list([node],
2728 22b7f6f8 Thomas Thrainer
                                             [instance.hypervisor])[node]
2729 22b7f6f8 Thomas Thrainer
    node_insts.Raise("Can't get node information from %s" % node)
2730 22b7f6f8 Thomas Thrainer
2731 22b7f6f8 Thomas Thrainer
    if instance.name not in node_insts.payload:
2732 22b7f6f8 Thomas Thrainer
      if instance.admin_state == constants.ADMINST_UP:
2733 22b7f6f8 Thomas Thrainer
        state = constants.INSTST_ERRORDOWN
2734 22b7f6f8 Thomas Thrainer
      elif instance.admin_state == constants.ADMINST_DOWN:
2735 22b7f6f8 Thomas Thrainer
        state = constants.INSTST_ADMINDOWN
2736 22b7f6f8 Thomas Thrainer
      else:
2737 22b7f6f8 Thomas Thrainer
        state = constants.INSTST_ADMINOFFLINE
2738 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("Instance %s is not running (state %s)" %
2739 22b7f6f8 Thomas Thrainer
                               (instance.name, state))
2740 22b7f6f8 Thomas Thrainer
2741 22b7f6f8 Thomas Thrainer
    logging.debug("Connecting to console of %s on %s", instance.name, node)
2742 22b7f6f8 Thomas Thrainer
2743 22b7f6f8 Thomas Thrainer
    return _GetInstanceConsole(self.cfg.GetClusterInfo(), instance)
2744 22b7f6f8 Thomas Thrainer
2745 22b7f6f8 Thomas Thrainer
2746 22b7f6f8 Thomas Thrainer
def _DeclareLocksForMigration(lu, level):
2747 22b7f6f8 Thomas Thrainer
  """Declares locks for L{TLMigrateInstance}.
2748 22b7f6f8 Thomas Thrainer

2749 22b7f6f8 Thomas Thrainer
  @type lu: L{LogicalUnit}
2750 22b7f6f8 Thomas Thrainer
  @param level: Lock level
2751 22b7f6f8 Thomas Thrainer

2752 22b7f6f8 Thomas Thrainer
  """
2753 22b7f6f8 Thomas Thrainer
  if level == locking.LEVEL_NODE_ALLOC:
2754 22b7f6f8 Thomas Thrainer
    assert lu.op.instance_name in lu.owned_locks(locking.LEVEL_INSTANCE)
2755 22b7f6f8 Thomas Thrainer
2756 22b7f6f8 Thomas Thrainer
    instance = lu.cfg.GetInstanceInfo(lu.op.instance_name)
2757 22b7f6f8 Thomas Thrainer
2758 22b7f6f8 Thomas Thrainer
    # Node locks are already declared here rather than at LEVEL_NODE as we need
2759 22b7f6f8 Thomas Thrainer
    # the instance object anyway to declare the node allocation lock.
2760 22b7f6f8 Thomas Thrainer
    if instance.disk_template in constants.DTS_EXT_MIRROR:
2761 22b7f6f8 Thomas Thrainer
      if lu.op.target_node is None:
2762 22b7f6f8 Thomas Thrainer
        lu.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
2763 22b7f6f8 Thomas Thrainer
        lu.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
2764 22b7f6f8 Thomas Thrainer
      else:
2765 22b7f6f8 Thomas Thrainer
        lu.needed_locks[locking.LEVEL_NODE] = [instance.primary_node,
2766 22b7f6f8 Thomas Thrainer
                                               lu.op.target_node]
2767 22b7f6f8 Thomas Thrainer
      del lu.recalculate_locks[locking.LEVEL_NODE]
2768 22b7f6f8 Thomas Thrainer
    else:
2769 22b7f6f8 Thomas Thrainer
      lu._LockInstancesNodes() # pylint: disable=W0212
2770 22b7f6f8 Thomas Thrainer
2771 22b7f6f8 Thomas Thrainer
  elif level == locking.LEVEL_NODE:
2772 22b7f6f8 Thomas Thrainer
    # Node locks are declared together with the node allocation lock
2773 22b7f6f8 Thomas Thrainer
    assert (lu.needed_locks[locking.LEVEL_NODE] or
2774 22b7f6f8 Thomas Thrainer
            lu.needed_locks[locking.LEVEL_NODE] is locking.ALL_SET)
2775 22b7f6f8 Thomas Thrainer
2776 22b7f6f8 Thomas Thrainer
  elif level == locking.LEVEL_NODE_RES:
2777 22b7f6f8 Thomas Thrainer
    # Copy node locks
2778 22b7f6f8 Thomas Thrainer
    lu.needed_locks[locking.LEVEL_NODE_RES] = \
2779 22b7f6f8 Thomas Thrainer
      _CopyLockList(lu.needed_locks[locking.LEVEL_NODE])
2780 22b7f6f8 Thomas Thrainer
2781 22b7f6f8 Thomas Thrainer
2782 22b7f6f8 Thomas Thrainer
def _ExpandNamesForMigration(lu):
2783 22b7f6f8 Thomas Thrainer
  """Expands names for use with L{TLMigrateInstance}.
2784 22b7f6f8 Thomas Thrainer

2785 22b7f6f8 Thomas Thrainer
  @type lu: L{LogicalUnit}
2786 22b7f6f8 Thomas Thrainer

2787 22b7f6f8 Thomas Thrainer
  """
2788 22b7f6f8 Thomas Thrainer
  if lu.op.target_node is not None:
2789 22b7f6f8 Thomas Thrainer
    lu.op.target_node = _ExpandNodeName(lu.cfg, lu.op.target_node)
2790 22b7f6f8 Thomas Thrainer
2791 22b7f6f8 Thomas Thrainer
  lu.needed_locks[locking.LEVEL_NODE] = []
2792 22b7f6f8 Thomas Thrainer
  lu.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
2793 22b7f6f8 Thomas Thrainer
2794 22b7f6f8 Thomas Thrainer
  lu.needed_locks[locking.LEVEL_NODE_RES] = []
2795 22b7f6f8 Thomas Thrainer
  lu.recalculate_locks[locking.LEVEL_NODE_RES] = constants.LOCKS_REPLACE
2796 22b7f6f8 Thomas Thrainer
2797 22b7f6f8 Thomas Thrainer
  # The node allocation lock is actually only needed for externally replicated
2798 22b7f6f8 Thomas Thrainer
  # instances (e.g. sharedfile or RBD) and if an iallocator is used.
2799 22b7f6f8 Thomas Thrainer
  lu.needed_locks[locking.LEVEL_NODE_ALLOC] = []
2800 22b7f6f8 Thomas Thrainer
2801 22b7f6f8 Thomas Thrainer
2802 22b7f6f8 Thomas Thrainer
class LUInstanceFailover(LogicalUnit):
2803 22b7f6f8 Thomas Thrainer
  """Failover an instance.
2804 22b7f6f8 Thomas Thrainer

2805 22b7f6f8 Thomas Thrainer
  """
2806 22b7f6f8 Thomas Thrainer
  HPATH = "instance-failover"
2807 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2808 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2809 22b7f6f8 Thomas Thrainer
2810 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
2811 22b7f6f8 Thomas Thrainer
    """Check the arguments.
2812 22b7f6f8 Thomas Thrainer

2813 22b7f6f8 Thomas Thrainer
    """
2814 22b7f6f8 Thomas Thrainer
    self.iallocator = getattr(self.op, "iallocator", None)
2815 22b7f6f8 Thomas Thrainer
    self.target_node = getattr(self.op, "target_node", None)
2816 22b7f6f8 Thomas Thrainer
2817 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2818 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2819 22b7f6f8 Thomas Thrainer
    _ExpandNamesForMigration(self)
2820 22b7f6f8 Thomas Thrainer
2821 22b7f6f8 Thomas Thrainer
    self._migrater = \
2822 22b7f6f8 Thomas Thrainer
      TLMigrateInstance(self, self.op.instance_name, False, True, False,
2823 22b7f6f8 Thomas Thrainer
                        self.op.ignore_consistency, True,
2824 22b7f6f8 Thomas Thrainer
                        self.op.shutdown_timeout, self.op.ignore_ipolicy)
2825 22b7f6f8 Thomas Thrainer
2826 22b7f6f8 Thomas Thrainer
    self.tasklets = [self._migrater]
2827 22b7f6f8 Thomas Thrainer
2828 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
2829 22b7f6f8 Thomas Thrainer
    _DeclareLocksForMigration(self, level)
2830 22b7f6f8 Thomas Thrainer
2831 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
2832 22b7f6f8 Thomas Thrainer
    """Build hooks env.
2833 22b7f6f8 Thomas Thrainer

2834 22b7f6f8 Thomas Thrainer
    This runs on master, primary and secondary nodes of the instance.
2835 22b7f6f8 Thomas Thrainer

2836 22b7f6f8 Thomas Thrainer
    """
2837 22b7f6f8 Thomas Thrainer
    instance = self._migrater.instance
2838 22b7f6f8 Thomas Thrainer
    source_node = instance.primary_node
2839 22b7f6f8 Thomas Thrainer
    target_node = self.op.target_node
2840 22b7f6f8 Thomas Thrainer
    env = {
2841 22b7f6f8 Thomas Thrainer
      "IGNORE_CONSISTENCY": self.op.ignore_consistency,
2842 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
2843 22b7f6f8 Thomas Thrainer
      "OLD_PRIMARY": source_node,
2844 22b7f6f8 Thomas Thrainer
      "NEW_PRIMARY": target_node,
2845 22b7f6f8 Thomas Thrainer
      }
2846 22b7f6f8 Thomas Thrainer
2847 22b7f6f8 Thomas Thrainer
    if instance.disk_template in constants.DTS_INT_MIRROR:
2848 22b7f6f8 Thomas Thrainer
      env["OLD_SECONDARY"] = instance.secondary_nodes[0]
2849 22b7f6f8 Thomas Thrainer
      env["NEW_SECONDARY"] = source_node
2850 22b7f6f8 Thomas Thrainer
    else:
2851 22b7f6f8 Thomas Thrainer
      env["OLD_SECONDARY"] = env["NEW_SECONDARY"] = ""
2852 22b7f6f8 Thomas Thrainer
2853 22b7f6f8 Thomas Thrainer
    env.update(_BuildInstanceHookEnvByObject(self, instance))
2854 22b7f6f8 Thomas Thrainer
2855 22b7f6f8 Thomas Thrainer
    return env
2856 22b7f6f8 Thomas Thrainer
2857 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2858 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2859 22b7f6f8 Thomas Thrainer

2860 22b7f6f8 Thomas Thrainer
    """
2861 22b7f6f8 Thomas Thrainer
    instance = self._migrater.instance
2862 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(instance.secondary_nodes)
2863 22b7f6f8 Thomas Thrainer
    return (nl, nl + [instance.primary_node])
2864 22b7f6f8 Thomas Thrainer
2865 22b7f6f8 Thomas Thrainer
2866 22b7f6f8 Thomas Thrainer
class LUInstanceMigrate(LogicalUnit):
2867 22b7f6f8 Thomas Thrainer
  """Migrate an instance.
2868 22b7f6f8 Thomas Thrainer

2869 22b7f6f8 Thomas Thrainer
  This is migration without shutting down, compared to the failover,
2870 22b7f6f8 Thomas Thrainer
  which is done with shutdown.
2871 22b7f6f8 Thomas Thrainer

2872 22b7f6f8 Thomas Thrainer
  """
2873 22b7f6f8 Thomas Thrainer
  HPATH = "instance-migrate"
2874 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2875 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2876 22b7f6f8 Thomas Thrainer
2877 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2878 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2879 22b7f6f8 Thomas Thrainer
    _ExpandNamesForMigration(self)
2880 22b7f6f8 Thomas Thrainer
2881 22b7f6f8 Thomas Thrainer
    self._migrater = \
2882 22b7f6f8 Thomas Thrainer
      TLMigrateInstance(self, self.op.instance_name, self.op.cleanup,
2883 22b7f6f8 Thomas Thrainer
                        False, self.op.allow_failover, False,
2884 22b7f6f8 Thomas Thrainer
                        self.op.allow_runtime_changes,
2885 22b7f6f8 Thomas Thrainer
                        constants.DEFAULT_SHUTDOWN_TIMEOUT,
2886 22b7f6f8 Thomas Thrainer
                        self.op.ignore_ipolicy)
2887 22b7f6f8 Thomas Thrainer
2888 22b7f6f8 Thomas Thrainer
    self.tasklets = [self._migrater]
2889 22b7f6f8 Thomas Thrainer
2890 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
2891 22b7f6f8 Thomas Thrainer
    _DeclareLocksForMigration(self, level)
2892 22b7f6f8 Thomas Thrainer
2893 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
2894 22b7f6f8 Thomas Thrainer
    """Build hooks env.
2895 22b7f6f8 Thomas Thrainer

2896 22b7f6f8 Thomas Thrainer
    This runs on master, primary and secondary nodes of the instance.
2897 22b7f6f8 Thomas Thrainer

2898 22b7f6f8 Thomas Thrainer
    """
2899 22b7f6f8 Thomas Thrainer
    instance = self._migrater.instance
2900 22b7f6f8 Thomas Thrainer
    source_node = instance.primary_node
2901 22b7f6f8 Thomas Thrainer
    target_node = self.op.target_node
2902 22b7f6f8 Thomas Thrainer
    env = _BuildInstanceHookEnvByObject(self, instance)
2903 22b7f6f8 Thomas Thrainer
    env.update({
2904 22b7f6f8 Thomas Thrainer
      "MIGRATE_LIVE": self._migrater.live,
2905 22b7f6f8 Thomas Thrainer
      "MIGRATE_CLEANUP": self.op.cleanup,
2906 22b7f6f8 Thomas Thrainer
      "OLD_PRIMARY": source_node,
2907 22b7f6f8 Thomas Thrainer
      "NEW_PRIMARY": target_node,
2908 22b7f6f8 Thomas Thrainer
      "ALLOW_RUNTIME_CHANGES": self.op.allow_runtime_changes,
2909 22b7f6f8 Thomas Thrainer
      })
2910 22b7f6f8 Thomas Thrainer
2911 22b7f6f8 Thomas Thrainer
    if instance.disk_template in constants.DTS_INT_MIRROR:
2912 22b7f6f8 Thomas Thrainer
      env["OLD_SECONDARY"] = target_node
2913 22b7f6f8 Thomas Thrainer
      env["NEW_SECONDARY"] = source_node
2914 22b7f6f8 Thomas Thrainer
    else:
2915 22b7f6f8 Thomas Thrainer
      env["OLD_SECONDARY"] = env["NEW_SECONDARY"] = None
2916 22b7f6f8 Thomas Thrainer
2917 22b7f6f8 Thomas Thrainer
    return env
2918 22b7f6f8 Thomas Thrainer
2919 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2920 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2921 22b7f6f8 Thomas Thrainer

2922 22b7f6f8 Thomas Thrainer
    """
2923 22b7f6f8 Thomas Thrainer
    instance = self._migrater.instance
2924 22b7f6f8 Thomas Thrainer
    snodes = list(instance.secondary_nodes)
2925 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode(), instance.primary_node] + snodes
2926 22b7f6f8 Thomas Thrainer
    return (nl, nl)
2927 22b7f6f8 Thomas Thrainer
2928 22b7f6f8 Thomas Thrainer
2929 22b7f6f8 Thomas Thrainer
class LUInstanceMultiAlloc(NoHooksLU):
2930 22b7f6f8 Thomas Thrainer
  """Allocates multiple instances at the same time.
2931 22b7f6f8 Thomas Thrainer

2932 22b7f6f8 Thomas Thrainer
  """
2933 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2934 22b7f6f8 Thomas Thrainer
2935 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
2936 22b7f6f8 Thomas Thrainer
    """Check arguments.
2937 22b7f6f8 Thomas Thrainer

2938 22b7f6f8 Thomas Thrainer
    """
2939 22b7f6f8 Thomas Thrainer
    nodes = []
2940 22b7f6f8 Thomas Thrainer
    for inst in self.op.instances:
2941 22b7f6f8 Thomas Thrainer
      if inst.iallocator is not None:
2942 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("iallocator are not allowed to be set on"
2943 22b7f6f8 Thomas Thrainer
                                   " instance objects", errors.ECODE_INVAL)
2944 22b7f6f8 Thomas Thrainer
      nodes.append(bool(inst.pnode))
2945 22b7f6f8 Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
2946 22b7f6f8 Thomas Thrainer
        nodes.append(bool(inst.snode))
2947 22b7f6f8 Thomas Thrainer
2948 22b7f6f8 Thomas Thrainer
    has_nodes = compat.any(nodes)
2949 22b7f6f8 Thomas Thrainer
    if compat.all(nodes) ^ has_nodes:
2950 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are instance objects providing"
2951 22b7f6f8 Thomas Thrainer
                                 " pnode/snode while others do not",
2952 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2953 22b7f6f8 Thomas Thrainer
2954 22b7f6f8 Thomas Thrainer
    if self.op.iallocator is None:
2955 22b7f6f8 Thomas Thrainer
      default_iallocator = self.cfg.GetDefaultIAllocator()
2956 22b7f6f8 Thomas Thrainer
      if default_iallocator and has_nodes:
2957 22b7f6f8 Thomas Thrainer
        self.op.iallocator = default_iallocator
2958 22b7f6f8 Thomas Thrainer
      else:
2959 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No iallocator or nodes on the instances"
2960 22b7f6f8 Thomas Thrainer
                                   " given and no cluster-wide default"
2961 22b7f6f8 Thomas Thrainer
                                   " iallocator found; please specify either"
2962 22b7f6f8 Thomas Thrainer
                                   " an iallocator or nodes on the instances"
2963 22b7f6f8 Thomas Thrainer
                                   " or set a cluster-wide default iallocator",
2964 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2965 22b7f6f8 Thomas Thrainer
2966 22b7f6f8 Thomas Thrainer
    _CheckOpportunisticLocking(self.op)
2967 22b7f6f8 Thomas Thrainer
2968 22b7f6f8 Thomas Thrainer
    dups = utils.FindDuplicates([op.instance_name for op in self.op.instances])
2969 22b7f6f8 Thomas Thrainer
    if dups:
2970 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are duplicate instance names: %s" %
2971 22b7f6f8 Thomas Thrainer
                                 utils.CommaJoin(dups), errors.ECODE_INVAL)
2972 22b7f6f8 Thomas Thrainer
2973 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2974 22b7f6f8 Thomas Thrainer
    """Calculate the locks.
2975 22b7f6f8 Thomas Thrainer

2976 22b7f6f8 Thomas Thrainer
    """
2977 22b7f6f8 Thomas Thrainer
    self.share_locks = _ShareAll()
2978 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
2979 22b7f6f8 Thomas Thrainer
      # iallocator will select nodes and even if no iallocator is used,
2980 22b7f6f8 Thomas Thrainer
      # collisions with LUInstanceCreate should be avoided
2981 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
2982 22b7f6f8 Thomas Thrainer
      }
2983 22b7f6f8 Thomas Thrainer
2984 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
2985 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
2986 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = locking.ALL_SET
2987 22b7f6f8 Thomas Thrainer
2988 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
2989 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
2990 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE_RES] = True
2991 22b7f6f8 Thomas Thrainer
    else:
2992 22b7f6f8 Thomas Thrainer
      nodeslist = []
2993 22b7f6f8 Thomas Thrainer
      for inst in self.op.instances:
2994 22b7f6f8 Thomas Thrainer
        inst.pnode = _ExpandNodeName(self.cfg, inst.pnode)
2995 22b7f6f8 Thomas Thrainer
        nodeslist.append(inst.pnode)
2996 22b7f6f8 Thomas Thrainer
        if inst.snode is not None:
2997 22b7f6f8 Thomas Thrainer
          inst.snode = _ExpandNodeName(self.cfg, inst.snode)
2998 22b7f6f8 Thomas Thrainer
          nodeslist.append(inst.snode)
2999 22b7f6f8 Thomas Thrainer
3000 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodeslist
3001 22b7f6f8 Thomas Thrainer
      # Lock resources of instance's primary and secondary nodes (copy to
3002 22b7f6f8 Thomas Thrainer
      # prevent accidential modification)
3003 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = list(nodeslist)
3004 22b7f6f8 Thomas Thrainer
3005 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
3006 22b7f6f8 Thomas Thrainer
    """Check prerequisite.
3007 22b7f6f8 Thomas Thrainer

3008 22b7f6f8 Thomas Thrainer
    """
3009 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
3010 22b7f6f8 Thomas Thrainer
    default_vg = self.cfg.GetVGName()
3011 22b7f6f8 Thomas Thrainer
    ec_id = self.proc.GetECId()
3012 22b7f6f8 Thomas Thrainer
3013 22b7f6f8 Thomas Thrainer
    if self.op.opportunistic_locking:
3014 22b7f6f8 Thomas Thrainer
      # Only consider nodes for which a lock is held
3015 22b7f6f8 Thomas Thrainer
      node_whitelist = list(self.owned_locks(locking.LEVEL_NODE))
3016 22b7f6f8 Thomas Thrainer
    else:
3017 22b7f6f8 Thomas Thrainer
      node_whitelist = None
3018 22b7f6f8 Thomas Thrainer
3019 22b7f6f8 Thomas Thrainer
    insts = [_CreateInstanceAllocRequest(op, _ComputeDisks(op, default_vg),
3020 22b7f6f8 Thomas Thrainer
                                         _ComputeNics(op, cluster, None,
3021 22b7f6f8 Thomas Thrainer
                                                      self.cfg, ec_id),
3022 22b7f6f8 Thomas Thrainer
                                         _ComputeFullBeParams(op, cluster),
3023 22b7f6f8 Thomas Thrainer
                                         node_whitelist)
3024 22b7f6f8 Thomas Thrainer
             for op in self.op.instances]
3025 22b7f6f8 Thomas Thrainer
3026 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqMultiInstanceAlloc(instances=insts)
3027 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
3028 22b7f6f8 Thomas Thrainer
3029 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
3030 22b7f6f8 Thomas Thrainer
3031 22b7f6f8 Thomas Thrainer
    if not ial.success:
3032 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute nodes using"
3033 22b7f6f8 Thomas Thrainer
                                 " iallocator '%s': %s" %
3034 22b7f6f8 Thomas Thrainer
                                 (self.op.iallocator, ial.info),
3035 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_NORES)
3036 22b7f6f8 Thomas Thrainer
3037 22b7f6f8 Thomas Thrainer
    self.ia_result = ial.result
3038 22b7f6f8 Thomas Thrainer
3039 22b7f6f8 Thomas Thrainer
    if self.op.dry_run:
3040 22b7f6f8 Thomas Thrainer
      self.dry_run_result = objects.FillDict(self._ConstructPartialResult(), {
3041 22b7f6f8 Thomas Thrainer
        constants.JOB_IDS_KEY: [],
3042 22b7f6f8 Thomas Thrainer
        })
3043 22b7f6f8 Thomas Thrainer
3044 22b7f6f8 Thomas Thrainer
  def _ConstructPartialResult(self):
3045 22b7f6f8 Thomas Thrainer
    """Contructs the partial result.
3046 22b7f6f8 Thomas Thrainer

3047 22b7f6f8 Thomas Thrainer
    """
3048 22b7f6f8 Thomas Thrainer
    (allocatable, failed) = self.ia_result
3049 22b7f6f8 Thomas Thrainer
    return {
3050 22b7f6f8 Thomas Thrainer
      opcodes.OpInstanceMultiAlloc.ALLOCATABLE_KEY:
3051 22b7f6f8 Thomas Thrainer
        map(compat.fst, allocatable),
3052 22b7f6f8 Thomas Thrainer
      opcodes.OpInstanceMultiAlloc.FAILED_KEY: failed,
3053 22b7f6f8 Thomas Thrainer
      }
3054 22b7f6f8 Thomas Thrainer
3055 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3056 22b7f6f8 Thomas Thrainer
    """Executes the opcode.
3057 22b7f6f8 Thomas Thrainer

3058 22b7f6f8 Thomas Thrainer
    """
3059 22b7f6f8 Thomas Thrainer
    op2inst = dict((op.instance_name, op) for op in self.op.instances)
3060 22b7f6f8 Thomas Thrainer
    (allocatable, failed) = self.ia_result
3061 22b7f6f8 Thomas Thrainer
3062 22b7f6f8 Thomas Thrainer
    jobs = []
3063 22b7f6f8 Thomas Thrainer
    for (name, nodes) in allocatable:
3064 22b7f6f8 Thomas Thrainer
      op = op2inst.pop(name)
3065 22b7f6f8 Thomas Thrainer
3066 22b7f6f8 Thomas Thrainer
      if len(nodes) > 1:
3067 22b7f6f8 Thomas Thrainer
        (op.pnode, op.snode) = nodes
3068 22b7f6f8 Thomas Thrainer
      else:
3069 22b7f6f8 Thomas Thrainer
        (op.pnode,) = nodes
3070 22b7f6f8 Thomas Thrainer
3071 22b7f6f8 Thomas Thrainer
      jobs.append([op])
3072 22b7f6f8 Thomas Thrainer
3073 22b7f6f8 Thomas Thrainer
    missing = set(op2inst.keys()) - set(failed)
3074 22b7f6f8 Thomas Thrainer
    assert not missing, \
3075 22b7f6f8 Thomas Thrainer
      "Iallocator did return incomplete result: %s" % utils.CommaJoin(missing)
3076 22b7f6f8 Thomas Thrainer
3077 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs, **self._ConstructPartialResult())
3078 22b7f6f8 Thomas Thrainer
3079 22b7f6f8 Thomas Thrainer
3080 22b7f6f8 Thomas Thrainer
class _InstNicModPrivate:
3081 22b7f6f8 Thomas Thrainer
  """Data structure for network interface modifications.
3082 22b7f6f8 Thomas Thrainer

3083 22b7f6f8 Thomas Thrainer
  Used by L{LUInstanceSetParams}.
3084 22b7f6f8 Thomas Thrainer

3085 22b7f6f8 Thomas Thrainer
  """
3086 22b7f6f8 Thomas Thrainer
  def __init__(self):
3087 22b7f6f8 Thomas Thrainer
    self.params = None
3088 22b7f6f8 Thomas Thrainer
    self.filled = None
3089 22b7f6f8 Thomas Thrainer
3090 22b7f6f8 Thomas Thrainer
3091 22b7f6f8 Thomas Thrainer
def PrepareContainerMods(mods, private_fn):
3092 22b7f6f8 Thomas Thrainer
  """Prepares a list of container modifications by adding a private data field.
3093 22b7f6f8 Thomas Thrainer

3094 22b7f6f8 Thomas Thrainer
  @type mods: list of tuples; (operation, index, parameters)
3095 22b7f6f8 Thomas Thrainer
  @param mods: List of modifications
3096 22b7f6f8 Thomas Thrainer
  @type private_fn: callable or None
3097 22b7f6f8 Thomas Thrainer
  @param private_fn: Callable for constructing a private data field for a
3098 22b7f6f8 Thomas Thrainer
    modification
3099 22b7f6f8 Thomas Thrainer
  @rtype: list
3100 22b7f6f8 Thomas Thrainer

3101 22b7f6f8 Thomas Thrainer
  """
3102 22b7f6f8 Thomas Thrainer
  if private_fn is None:
3103 22b7f6f8 Thomas Thrainer
    fn = lambda: None
3104 22b7f6f8 Thomas Thrainer
  else:
3105 22b7f6f8 Thomas Thrainer
    fn = private_fn
3106 22b7f6f8 Thomas Thrainer
3107 22b7f6f8 Thomas Thrainer
  return [(op, idx, params, fn()) for (op, idx, params) in mods]
3108 22b7f6f8 Thomas Thrainer
3109 22b7f6f8 Thomas Thrainer
3110 22b7f6f8 Thomas Thrainer
def _CheckNodesPhysicalCPUs(lu, nodenames, requested, hypervisor_name):
3111 22b7f6f8 Thomas Thrainer
  """Checks if nodes have enough physical CPUs
3112 22b7f6f8 Thomas Thrainer

3113 22b7f6f8 Thomas Thrainer
  This function checks if all given nodes have the needed number of
3114 22b7f6f8 Thomas Thrainer
  physical CPUs. In case any node has less CPUs or we cannot get the
3115 22b7f6f8 Thomas Thrainer
  information from the node, this function raises an OpPrereqError
3116 22b7f6f8 Thomas Thrainer
  exception.
3117 22b7f6f8 Thomas Thrainer

3118 22b7f6f8 Thomas Thrainer
  @type lu: C{LogicalUnit}
3119 22b7f6f8 Thomas Thrainer
  @param lu: a logical unit from which we get configuration data
3120 22b7f6f8 Thomas Thrainer
  @type nodenames: C{list}
3121 22b7f6f8 Thomas Thrainer
  @param nodenames: the list of node names to check
3122 22b7f6f8 Thomas Thrainer
  @type requested: C{int}
3123 22b7f6f8 Thomas Thrainer
  @param requested: the minimum acceptable number of physical CPUs
3124 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node doesn't have enough CPUs,
3125 22b7f6f8 Thomas Thrainer
      or we cannot check the node
3126 22b7f6f8 Thomas Thrainer

3127 22b7f6f8 Thomas Thrainer
  """
3128 22b7f6f8 Thomas Thrainer
  nodeinfo = lu.rpc.call_node_info(nodenames, None, [hypervisor_name], None)
3129 22b7f6f8 Thomas Thrainer
  for node in nodenames:
3130 22b7f6f8 Thomas Thrainer
    info = nodeinfo[node]
3131 22b7f6f8 Thomas Thrainer
    info.Raise("Cannot get current information from node %s" % node,
3132 22b7f6f8 Thomas Thrainer
               prereq=True, ecode=errors.ECODE_ENVIRON)
3133 22b7f6f8 Thomas Thrainer
    (_, _, (hv_info, )) = info.payload
3134 22b7f6f8 Thomas Thrainer
    num_cpus = hv_info.get("cpu_total", None)
3135 22b7f6f8 Thomas Thrainer
    if not isinstance(num_cpus, int):
3136 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute the number of physical CPUs"
3137 22b7f6f8 Thomas Thrainer
                                 " on node %s, result was '%s'" %
3138 22b7f6f8 Thomas Thrainer
                                 (node, num_cpus), errors.ECODE_ENVIRON)
3139 22b7f6f8 Thomas Thrainer
    if requested > num_cpus:
3140 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Node %s has %s physical CPUs, but %s are "
3141 22b7f6f8 Thomas Thrainer
                                 "required" % (node, num_cpus, requested),
3142 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_NORES)
3143 22b7f6f8 Thomas Thrainer
3144 22b7f6f8 Thomas Thrainer
3145 22b7f6f8 Thomas Thrainer
def GetItemFromContainer(identifier, kind, container):
3146 22b7f6f8 Thomas Thrainer
  """Return the item refered by the identifier.
3147 22b7f6f8 Thomas Thrainer

3148 22b7f6f8 Thomas Thrainer
  @type identifier: string
3149 22b7f6f8 Thomas Thrainer
  @param identifier: Item index or name or UUID
3150 22b7f6f8 Thomas Thrainer
  @type kind: string
3151 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
3152 22b7f6f8 Thomas Thrainer
  @type container: list
3153 22b7f6f8 Thomas Thrainer
  @param container: Container to get the item from
3154 22b7f6f8 Thomas Thrainer

3155 22b7f6f8 Thomas Thrainer
  """
3156 22b7f6f8 Thomas Thrainer
  # Index
3157 22b7f6f8 Thomas Thrainer
  try:
3158 22b7f6f8 Thomas Thrainer
    idx = int(identifier)
3159 22b7f6f8 Thomas Thrainer
    if idx == -1:
3160 22b7f6f8 Thomas Thrainer
      # Append
3161 22b7f6f8 Thomas Thrainer
      absidx = len(container) - 1
3162 22b7f6f8 Thomas Thrainer
    elif idx < 0:
3163 22b7f6f8 Thomas Thrainer
      raise IndexError("Not accepting negative indices other than -1")
3164 22b7f6f8 Thomas Thrainer
    elif idx > len(container):
3165 22b7f6f8 Thomas Thrainer
      raise IndexError("Got %s index %s, but there are only %s" %
3166 22b7f6f8 Thomas Thrainer
                       (kind, idx, len(container)))
3167 22b7f6f8 Thomas Thrainer
    else:
3168 22b7f6f8 Thomas Thrainer
      absidx = idx
3169 22b7f6f8 Thomas Thrainer
    return (absidx, container[idx])
3170 22b7f6f8 Thomas Thrainer
  except ValueError:
3171 22b7f6f8 Thomas Thrainer
    pass
3172 22b7f6f8 Thomas Thrainer
3173 22b7f6f8 Thomas Thrainer
  for idx, item in enumerate(container):
3174 22b7f6f8 Thomas Thrainer
    if item.uuid == identifier or item.name == identifier:
3175 22b7f6f8 Thomas Thrainer
      return (idx, item)
3176 22b7f6f8 Thomas Thrainer
3177 22b7f6f8 Thomas Thrainer
  raise errors.OpPrereqError("Cannot find %s with identifier %s" %
3178 22b7f6f8 Thomas Thrainer
                             (kind, identifier), errors.ECODE_NOENT)
3179 22b7f6f8 Thomas Thrainer
3180 22b7f6f8 Thomas Thrainer
3181 22b7f6f8 Thomas Thrainer
def ApplyContainerMods(kind, container, chgdesc, mods,
3182 22b7f6f8 Thomas Thrainer
                       create_fn, modify_fn, remove_fn):
3183 22b7f6f8 Thomas Thrainer
  """Applies descriptions in C{mods} to C{container}.
3184 22b7f6f8 Thomas Thrainer

3185 22b7f6f8 Thomas Thrainer
  @type kind: string
3186 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
3187 22b7f6f8 Thomas Thrainer
  @type container: list
3188 22b7f6f8 Thomas Thrainer
  @param container: Container to modify
3189 22b7f6f8 Thomas Thrainer
  @type chgdesc: None or list
3190 22b7f6f8 Thomas Thrainer
  @param chgdesc: List of applied changes
3191 22b7f6f8 Thomas Thrainer
  @type mods: list
3192 22b7f6f8 Thomas Thrainer
  @param mods: Modifications as returned by L{PrepareContainerMods}
3193 22b7f6f8 Thomas Thrainer
  @type create_fn: callable
3194 22b7f6f8 Thomas Thrainer
  @param create_fn: Callback for creating a new item (L{constants.DDM_ADD});
3195 22b7f6f8 Thomas Thrainer
    receives absolute item index, parameters and private data object as added
3196 22b7f6f8 Thomas Thrainer
    by L{PrepareContainerMods}, returns tuple containing new item and changes
3197 22b7f6f8 Thomas Thrainer
    as list
3198 22b7f6f8 Thomas Thrainer
  @type modify_fn: callable
3199 22b7f6f8 Thomas Thrainer
  @param modify_fn: Callback for modifying an existing item
3200 22b7f6f8 Thomas Thrainer
    (L{constants.DDM_MODIFY}); receives absolute item index, item, parameters
3201 22b7f6f8 Thomas Thrainer
    and private data object as added by L{PrepareContainerMods}, returns
3202 22b7f6f8 Thomas Thrainer
    changes as list
3203 22b7f6f8 Thomas Thrainer
  @type remove_fn: callable
3204 22b7f6f8 Thomas Thrainer
  @param remove_fn: Callback on removing item; receives absolute item index,
3205 22b7f6f8 Thomas Thrainer
    item and private data object as added by L{PrepareContainerMods}
3206 22b7f6f8 Thomas Thrainer

3207 22b7f6f8 Thomas Thrainer
  """
3208 22b7f6f8 Thomas Thrainer
  for (op, identifier, params, private) in mods:
3209 22b7f6f8 Thomas Thrainer
    changes = None
3210 22b7f6f8 Thomas Thrainer
3211 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
3212 22b7f6f8 Thomas Thrainer
      # Calculate where item will be added
3213 22b7f6f8 Thomas Thrainer
      # When adding an item, identifier can only be an index
3214 22b7f6f8 Thomas Thrainer
      try:
3215 22b7f6f8 Thomas Thrainer
        idx = int(identifier)
3216 22b7f6f8 Thomas Thrainer
      except ValueError:
3217 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Only possitive integer or -1 is accepted as"
3218 22b7f6f8 Thomas Thrainer
                                   " identifier for %s" % constants.DDM_ADD,
3219 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3220 22b7f6f8 Thomas Thrainer
      if idx == -1:
3221 22b7f6f8 Thomas Thrainer
        addidx = len(container)
3222 22b7f6f8 Thomas Thrainer
      else:
3223 22b7f6f8 Thomas Thrainer
        if idx < 0:
3224 22b7f6f8 Thomas Thrainer
          raise IndexError("Not accepting negative indices other than -1")
3225 22b7f6f8 Thomas Thrainer
        elif idx > len(container):
3226 22b7f6f8 Thomas Thrainer
          raise IndexError("Got %s index %s, but there are only %s" %
3227 22b7f6f8 Thomas Thrainer
                           (kind, idx, len(container)))
3228 22b7f6f8 Thomas Thrainer
        addidx = idx
3229 22b7f6f8 Thomas Thrainer
3230 22b7f6f8 Thomas Thrainer
      if create_fn is None:
3231 22b7f6f8 Thomas Thrainer
        item = params
3232 22b7f6f8 Thomas Thrainer
      else:
3233 22b7f6f8 Thomas Thrainer
        (item, changes) = create_fn(addidx, params, private)
3234 22b7f6f8 Thomas Thrainer
3235 22b7f6f8 Thomas Thrainer
      if idx == -1:
3236 22b7f6f8 Thomas Thrainer
        container.append(item)
3237 22b7f6f8 Thomas Thrainer
      else:
3238 22b7f6f8 Thomas Thrainer
        assert idx >= 0
3239 22b7f6f8 Thomas Thrainer
        assert idx <= len(container)
3240 22b7f6f8 Thomas Thrainer
        # list.insert does so before the specified index
3241 22b7f6f8 Thomas Thrainer
        container.insert(idx, item)
3242 22b7f6f8 Thomas Thrainer
    else:
3243 22b7f6f8 Thomas Thrainer
      # Retrieve existing item
3244 22b7f6f8 Thomas Thrainer
      (absidx, item) = GetItemFromContainer(identifier, kind, container)
3245 22b7f6f8 Thomas Thrainer
3246 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
3247 22b7f6f8 Thomas Thrainer
        assert not params
3248 22b7f6f8 Thomas Thrainer
3249 22b7f6f8 Thomas Thrainer
        if remove_fn is not None:
3250 22b7f6f8 Thomas Thrainer
          remove_fn(absidx, item, private)
3251 22b7f6f8 Thomas Thrainer
3252 22b7f6f8 Thomas Thrainer
        changes = [("%s/%s" % (kind, absidx), "remove")]
3253 22b7f6f8 Thomas Thrainer
3254 22b7f6f8 Thomas Thrainer
        assert container[absidx] == item
3255 22b7f6f8 Thomas Thrainer
        del container[absidx]
3256 22b7f6f8 Thomas Thrainer
      elif op == constants.DDM_MODIFY:
3257 22b7f6f8 Thomas Thrainer
        if modify_fn is not None:
3258 22b7f6f8 Thomas Thrainer
          changes = modify_fn(absidx, item, params, private)
3259 22b7f6f8 Thomas Thrainer
      else:
3260 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
3261 22b7f6f8 Thomas Thrainer
3262 22b7f6f8 Thomas Thrainer
    assert _TApplyContModsCbChanges(changes)
3263 22b7f6f8 Thomas Thrainer
3264 22b7f6f8 Thomas Thrainer
    if not (chgdesc is None or changes is None):
3265 22b7f6f8 Thomas Thrainer
      chgdesc.extend(changes)
3266 22b7f6f8 Thomas Thrainer
3267 22b7f6f8 Thomas Thrainer
3268 22b7f6f8 Thomas Thrainer
def _UpdateIvNames(base_index, disks):
3269 22b7f6f8 Thomas Thrainer
  """Updates the C{iv_name} attribute of disks.
3270 22b7f6f8 Thomas Thrainer

3271 22b7f6f8 Thomas Thrainer
  @type disks: list of L{objects.Disk}
3272 22b7f6f8 Thomas Thrainer

3273 22b7f6f8 Thomas Thrainer
  """
3274 22b7f6f8 Thomas Thrainer
  for (idx, disk) in enumerate(disks):
3275 22b7f6f8 Thomas Thrainer
    disk.iv_name = "disk/%s" % (base_index + idx, )
3276 22b7f6f8 Thomas Thrainer
3277 22b7f6f8 Thomas Thrainer
3278 22b7f6f8 Thomas Thrainer
class LUInstanceSetParams(LogicalUnit):
3279 22b7f6f8 Thomas Thrainer
  """Modifies an instances's parameters.
3280 22b7f6f8 Thomas Thrainer

3281 22b7f6f8 Thomas Thrainer
  """
3282 22b7f6f8 Thomas Thrainer
  HPATH = "instance-modify"
3283 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
3284 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
3285 22b7f6f8 Thomas Thrainer
3286 22b7f6f8 Thomas Thrainer
  @staticmethod
3287 22b7f6f8 Thomas Thrainer
  def _UpgradeDiskNicMods(kind, mods, verify_fn):
3288 22b7f6f8 Thomas Thrainer
    assert ht.TList(mods)
3289 22b7f6f8 Thomas Thrainer
    assert not mods or len(mods[0]) in (2, 3)
3290 22b7f6f8 Thomas Thrainer
3291 22b7f6f8 Thomas Thrainer
    if mods and len(mods[0]) == 2:
3292 22b7f6f8 Thomas Thrainer
      result = []
3293 22b7f6f8 Thomas Thrainer
3294 22b7f6f8 Thomas Thrainer
      addremove = 0
3295 22b7f6f8 Thomas Thrainer
      for op, params in mods:
3296 22b7f6f8 Thomas Thrainer
        if op in (constants.DDM_ADD, constants.DDM_REMOVE):
3297 22b7f6f8 Thomas Thrainer
          result.append((op, -1, params))
3298 22b7f6f8 Thomas Thrainer
          addremove += 1
3299 22b7f6f8 Thomas Thrainer
3300 22b7f6f8 Thomas Thrainer
          if addremove > 1:
3301 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Only one %s add or remove operation is"
3302 22b7f6f8 Thomas Thrainer
                                       " supported at a time" % kind,
3303 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
3304 22b7f6f8 Thomas Thrainer
        else:
3305 22b7f6f8 Thomas Thrainer
          result.append((constants.DDM_MODIFY, op, params))
3306 22b7f6f8 Thomas Thrainer
3307 22b7f6f8 Thomas Thrainer
      assert verify_fn(result)
3308 22b7f6f8 Thomas Thrainer
    else:
3309 22b7f6f8 Thomas Thrainer
      result = mods
3310 22b7f6f8 Thomas Thrainer
3311 22b7f6f8 Thomas Thrainer
    return result
3312 22b7f6f8 Thomas Thrainer
3313 22b7f6f8 Thomas Thrainer
  @staticmethod
3314 22b7f6f8 Thomas Thrainer
  def _CheckMods(kind, mods, key_types, item_fn):
3315 22b7f6f8 Thomas Thrainer
    """Ensures requested disk/NIC modifications are valid.
3316 22b7f6f8 Thomas Thrainer

3317 22b7f6f8 Thomas Thrainer
    """
3318 22b7f6f8 Thomas Thrainer
    for (op, _, params) in mods:
3319 22b7f6f8 Thomas Thrainer
      assert ht.TDict(params)
3320 22b7f6f8 Thomas Thrainer
3321 22b7f6f8 Thomas Thrainer
      # If 'key_types' is an empty dict, we assume we have an
3322 22b7f6f8 Thomas Thrainer
      # 'ext' template and thus do not ForceDictType
3323 22b7f6f8 Thomas Thrainer
      if key_types:
3324 22b7f6f8 Thomas Thrainer
        utils.ForceDictType(params, key_types)
3325 22b7f6f8 Thomas Thrainer
3326 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
3327 22b7f6f8 Thomas Thrainer
        if params:
3328 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("No settings should be passed when"
3329 22b7f6f8 Thomas Thrainer
                                     " removing a %s" % kind,
3330 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
3331 22b7f6f8 Thomas Thrainer
      elif op in (constants.DDM_ADD, constants.DDM_MODIFY):
3332 22b7f6f8 Thomas Thrainer
        item_fn(op, params)
3333 22b7f6f8 Thomas Thrainer
      else:
3334 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
3335 22b7f6f8 Thomas Thrainer
3336 22b7f6f8 Thomas Thrainer
  @staticmethod
3337 22b7f6f8 Thomas Thrainer
  def _VerifyDiskModification(op, params):
3338 22b7f6f8 Thomas Thrainer
    """Verifies a disk modification.
3339 22b7f6f8 Thomas Thrainer

3340 22b7f6f8 Thomas Thrainer
    """
3341 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
3342 22b7f6f8 Thomas Thrainer
      mode = params.setdefault(constants.IDISK_MODE, constants.DISK_RDWR)
3343 22b7f6f8 Thomas Thrainer
      if mode not in constants.DISK_ACCESS_SET:
3344 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk access mode '%s'" % mode,
3345 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3346 22b7f6f8 Thomas Thrainer
3347 22b7f6f8 Thomas Thrainer
      size = params.get(constants.IDISK_SIZE, None)
3348 22b7f6f8 Thomas Thrainer
      if size is None:
3349 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Required disk parameter '%s' missing" %
3350 22b7f6f8 Thomas Thrainer
                                   constants.IDISK_SIZE, errors.ECODE_INVAL)
3351 22b7f6f8 Thomas Thrainer
3352 22b7f6f8 Thomas Thrainer
      try:
3353 22b7f6f8 Thomas Thrainer
        size = int(size)
3354 22b7f6f8 Thomas Thrainer
      except (TypeError, ValueError), err:
3355 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk size parameter: %s" % err,
3356 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3357 22b7f6f8 Thomas Thrainer
3358 22b7f6f8 Thomas Thrainer
      params[constants.IDISK_SIZE] = size
3359 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
3360 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
3361 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
3362 22b7f6f8 Thomas Thrainer
3363 22b7f6f8 Thomas Thrainer
    elif op == constants.DDM_MODIFY:
3364 22b7f6f8 Thomas Thrainer
      if constants.IDISK_SIZE in params:
3365 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk size change not possible, use"
3366 22b7f6f8 Thomas Thrainer
                                   " grow-disk", errors.ECODE_INVAL)
3367 22b7f6f8 Thomas Thrainer
      if len(params) > 2:
3368 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk modification doesn't support"
3369 22b7f6f8 Thomas Thrainer
                                   " additional arbitrary parameters",
3370 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3371 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
3372 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
3373 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
3374 22b7f6f8 Thomas Thrainer
3375 22b7f6f8 Thomas Thrainer
  @staticmethod
3376 22b7f6f8 Thomas Thrainer
  def _VerifyNicModification(op, params):
3377 22b7f6f8 Thomas Thrainer
    """Verifies a network interface modification.
3378 22b7f6f8 Thomas Thrainer

3379 22b7f6f8 Thomas Thrainer
    """
3380 22b7f6f8 Thomas Thrainer
    if op in (constants.DDM_ADD, constants.DDM_MODIFY):
3381 22b7f6f8 Thomas Thrainer
      ip = params.get(constants.INIC_IP, None)
3382 22b7f6f8 Thomas Thrainer
      name = params.get(constants.INIC_NAME, None)
3383 22b7f6f8 Thomas Thrainer
      req_net = params.get(constants.INIC_NETWORK, None)
3384 22b7f6f8 Thomas Thrainer
      link = params.get(constants.NIC_LINK, None)
3385 22b7f6f8 Thomas Thrainer
      mode = params.get(constants.NIC_MODE, None)
3386 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
3387 22b7f6f8 Thomas Thrainer
        params[constants.INIC_NAME] = None
3388 22b7f6f8 Thomas Thrainer
      if req_net is not None:
3389 22b7f6f8 Thomas Thrainer
        if req_net.lower() == constants.VALUE_NONE:
3390 22b7f6f8 Thomas Thrainer
          params[constants.INIC_NETWORK] = None
3391 22b7f6f8 Thomas Thrainer
          req_net = None
3392 22b7f6f8 Thomas Thrainer
        elif link is not None or mode is not None:
3393 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("If network is given"
3394 22b7f6f8 Thomas Thrainer
                                     " mode or link should not",
3395 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
3396 22b7f6f8 Thomas Thrainer
3397 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_ADD:
3398 22b7f6f8 Thomas Thrainer
        macaddr = params.get(constants.INIC_MAC, None)
3399 22b7f6f8 Thomas Thrainer
        if macaddr is None:
3400 22b7f6f8 Thomas Thrainer
          params[constants.INIC_MAC] = constants.VALUE_AUTO
3401 22b7f6f8 Thomas Thrainer
3402 22b7f6f8 Thomas Thrainer
      if ip is not None:
3403 22b7f6f8 Thomas Thrainer
        if ip.lower() == constants.VALUE_NONE:
3404 22b7f6f8 Thomas Thrainer
          params[constants.INIC_IP] = None
3405 22b7f6f8 Thomas Thrainer
        else:
3406 22b7f6f8 Thomas Thrainer
          if ip.lower() == constants.NIC_IP_POOL:
3407 22b7f6f8 Thomas Thrainer
            if op == constants.DDM_ADD and req_net is None:
3408 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("If ip=pool, parameter network"
3409 22b7f6f8 Thomas Thrainer
                                         " cannot be none",
3410 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_INVAL)
3411 22b7f6f8 Thomas Thrainer
          else:
3412 22b7f6f8 Thomas Thrainer
            if not netutils.IPAddress.IsValid(ip):
3413 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("Invalid IP address '%s'" % ip,
3414 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_INVAL)
3415 22b7f6f8 Thomas Thrainer
3416 22b7f6f8 Thomas Thrainer
      if constants.INIC_MAC in params:
3417 22b7f6f8 Thomas Thrainer
        macaddr = params[constants.INIC_MAC]
3418 22b7f6f8 Thomas Thrainer
        if macaddr not in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
3419 22b7f6f8 Thomas Thrainer
          macaddr = utils.NormalizeAndValidateMac(macaddr)
3420 22b7f6f8 Thomas Thrainer
3421 22b7f6f8 Thomas Thrainer
        if op == constants.DDM_MODIFY and macaddr == constants.VALUE_AUTO:
3422 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("'auto' is not a valid MAC address when"
3423 22b7f6f8 Thomas Thrainer
                                     " modifying an existing NIC",
3424 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
3425 22b7f6f8 Thomas Thrainer
3426 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
3427 22b7f6f8 Thomas Thrainer
    if not (self.op.nics or self.op.disks or self.op.disk_template or
3428 22b7f6f8 Thomas Thrainer
            self.op.hvparams or self.op.beparams or self.op.os_name or
3429 22b7f6f8 Thomas Thrainer
            self.op.offline is not None or self.op.runtime_mem or
3430 22b7f6f8 Thomas Thrainer
            self.op.pnode):
3431 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("No changes submitted", errors.ECODE_INVAL)
3432 22b7f6f8 Thomas Thrainer
3433 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
3434 22b7f6f8 Thomas Thrainer
      _CheckParamsNotGlobal(self.op.hvparams, constants.HVC_GLOBALS,
3435 22b7f6f8 Thomas Thrainer
                            "hypervisor", "instance", "cluster")
3436 22b7f6f8 Thomas Thrainer
3437 22b7f6f8 Thomas Thrainer
    self.op.disks = self._UpgradeDiskNicMods(
3438 22b7f6f8 Thomas Thrainer
      "disk", self.op.disks, opcodes.OpInstanceSetParams.TestDiskModifications)
3439 22b7f6f8 Thomas Thrainer
    self.op.nics = self._UpgradeDiskNicMods(
3440 22b7f6f8 Thomas Thrainer
      "NIC", self.op.nics, opcodes.OpInstanceSetParams.TestNicModifications)
3441 22b7f6f8 Thomas Thrainer
3442 22b7f6f8 Thomas Thrainer
    if self.op.disks and self.op.disk_template is not None:
3443 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk template conversion and other disk"
3444 22b7f6f8 Thomas Thrainer
                                 " changes not supported at the same time",
3445 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3446 22b7f6f8 Thomas Thrainer
3447 22b7f6f8 Thomas Thrainer
    if (self.op.disk_template and
3448 22b7f6f8 Thomas Thrainer
        self.op.disk_template in constants.DTS_INT_MIRROR and
3449 22b7f6f8 Thomas Thrainer
        self.op.remote_node is None):
3450 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Changing the disk template to a mirrored"
3451 22b7f6f8 Thomas Thrainer
                                 " one requires specifying a secondary node",
3452 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3453 22b7f6f8 Thomas Thrainer
3454 22b7f6f8 Thomas Thrainer
    # Check NIC modifications
3455 22b7f6f8 Thomas Thrainer
    self._CheckMods("NIC", self.op.nics, constants.INIC_PARAMS_TYPES,
3456 22b7f6f8 Thomas Thrainer
                    self._VerifyNicModification)
3457 22b7f6f8 Thomas Thrainer
3458 22b7f6f8 Thomas Thrainer
    if self.op.pnode:
3459 22b7f6f8 Thomas Thrainer
      self.op.pnode = _ExpandNodeName(self.cfg, self.op.pnode)
3460 22b7f6f8 Thomas Thrainer
3461 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
3462 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
3463 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODEGROUP] = []
3464 22b7f6f8 Thomas Thrainer
    # Can't even acquire node locks in shared mode as upcoming changes in
3465 22b7f6f8 Thomas Thrainer
    # Ganeti 2.6 will start to modify the node object on disk conversion
3466 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
3467 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
3468 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
3469 22b7f6f8 Thomas Thrainer
    # Look node group to look up the ipolicy
3470 22b7f6f8 Thomas Thrainer
    self.share_locks[locking.LEVEL_NODEGROUP] = 1
3471 22b7f6f8 Thomas Thrainer
3472 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
3473 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
3474 22b7f6f8 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
3475 22b7f6f8 Thomas Thrainer
      # Acquire locks for the instance's nodegroups optimistically. Needs
3476 22b7f6f8 Thomas Thrainer
      # to be verified in CheckPrereq
3477 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = \
3478 22b7f6f8 Thomas Thrainer
        self.cfg.GetInstanceNodeGroups(self.op.instance_name)
3479 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE:
3480 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
3481 22b7f6f8 Thomas Thrainer
      if self.op.disk_template and self.op.remote_node:
3482 22b7f6f8 Thomas Thrainer
        self.op.remote_node = _ExpandNodeName(self.cfg, self.op.remote_node)
3483 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE].append(self.op.remote_node)
3484 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES and self.op.disk_template:
3485 22b7f6f8 Thomas Thrainer
      # Copy node locks
3486 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
3487 22b7f6f8 Thomas Thrainer
        _CopyLockList(self.needed_locks[locking.LEVEL_NODE])
3488 22b7f6f8 Thomas Thrainer
3489 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
3490 22b7f6f8 Thomas Thrainer
    """Build hooks env.
3491 22b7f6f8 Thomas Thrainer

3492 22b7f6f8 Thomas Thrainer
    This runs on the master, primary and secondaries.
3493 22b7f6f8 Thomas Thrainer

3494 22b7f6f8 Thomas Thrainer
    """
3495 22b7f6f8 Thomas Thrainer
    args = {}
3496 22b7f6f8 Thomas Thrainer
    if constants.BE_MINMEM in self.be_new:
3497 22b7f6f8 Thomas Thrainer
      args["minmem"] = self.be_new[constants.BE_MINMEM]
3498 22b7f6f8 Thomas Thrainer
    if constants.BE_MAXMEM in self.be_new:
3499 22b7f6f8 Thomas Thrainer
      args["maxmem"] = self.be_new[constants.BE_MAXMEM]
3500 22b7f6f8 Thomas Thrainer
    if constants.BE_VCPUS in self.be_new:
3501 22b7f6f8 Thomas Thrainer
      args["vcpus"] = self.be_new[constants.BE_VCPUS]
3502 22b7f6f8 Thomas Thrainer
    # TODO: export disk changes. Note: _BuildInstanceHookEnv* don't export disk
3503 22b7f6f8 Thomas Thrainer
    # information at all.
3504 22b7f6f8 Thomas Thrainer
3505 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
3506 22b7f6f8 Thomas Thrainer
      nics = []
3507 22b7f6f8 Thomas Thrainer
3508 22b7f6f8 Thomas Thrainer
      for nic in self._new_nics:
3509 22b7f6f8 Thomas Thrainer
        n = copy.deepcopy(nic)
3510 22b7f6f8 Thomas Thrainer
        nicparams = self.cluster.SimpleFillNIC(n.nicparams)
3511 22b7f6f8 Thomas Thrainer
        n.nicparams = nicparams
3512 22b7f6f8 Thomas Thrainer
        nics.append(_NICToTuple(self, n))
3513 22b7f6f8 Thomas Thrainer
3514 22b7f6f8 Thomas Thrainer
      args["nics"] = nics
3515 22b7f6f8 Thomas Thrainer
3516 22b7f6f8 Thomas Thrainer
    env = _BuildInstanceHookEnvByObject(self, self.instance, override=args)
3517 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
3518 22b7f6f8 Thomas Thrainer
      env["NEW_DISK_TEMPLATE"] = self.op.disk_template
3519 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
3520 22b7f6f8 Thomas Thrainer
      env["RUNTIME_MEMORY"] = self.op.runtime_mem
3521 22b7f6f8 Thomas Thrainer
3522 22b7f6f8 Thomas Thrainer
    return env
3523 22b7f6f8 Thomas Thrainer
3524 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
3525 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
3526 22b7f6f8 Thomas Thrainer

3527 22b7f6f8 Thomas Thrainer
    """
3528 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
3529 22b7f6f8 Thomas Thrainer
    return (nl, nl)
3530 22b7f6f8 Thomas Thrainer
3531 22b7f6f8 Thomas Thrainer
  def _PrepareNicModification(self, params, private, old_ip, old_net_uuid,
3532 22b7f6f8 Thomas Thrainer
                              old_params, cluster, pnode):
3533 22b7f6f8 Thomas Thrainer
3534 22b7f6f8 Thomas Thrainer
    update_params_dict = dict([(key, params[key])
3535 22b7f6f8 Thomas Thrainer
                               for key in constants.NICS_PARAMETERS
3536 22b7f6f8 Thomas Thrainer
                               if key in params])
3537 22b7f6f8 Thomas Thrainer
3538 22b7f6f8 Thomas Thrainer
    req_link = update_params_dict.get(constants.NIC_LINK, None)
3539 22b7f6f8 Thomas Thrainer
    req_mode = update_params_dict.get(constants.NIC_MODE, None)
3540 22b7f6f8 Thomas Thrainer
3541 22b7f6f8 Thomas Thrainer
    new_net_uuid = None
3542 22b7f6f8 Thomas Thrainer
    new_net_uuid_or_name = params.get(constants.INIC_NETWORK, old_net_uuid)
3543 22b7f6f8 Thomas Thrainer
    if new_net_uuid_or_name:
3544 22b7f6f8 Thomas Thrainer
      new_net_uuid = self.cfg.LookupNetwork(new_net_uuid_or_name)
3545 22b7f6f8 Thomas Thrainer
      new_net_obj = self.cfg.GetNetwork(new_net_uuid)
3546 22b7f6f8 Thomas Thrainer
3547 22b7f6f8 Thomas Thrainer
    if old_net_uuid:
3548 22b7f6f8 Thomas Thrainer
      old_net_obj = self.cfg.GetNetwork(old_net_uuid)
3549 22b7f6f8 Thomas Thrainer
3550 22b7f6f8 Thomas Thrainer
    if new_net_uuid:
3551 22b7f6f8 Thomas Thrainer
      netparams = self.cfg.GetGroupNetParams(new_net_uuid, pnode)
3552 22b7f6f8 Thomas Thrainer
      if not netparams:
3553 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No netparams found for the network"
3554 22b7f6f8 Thomas Thrainer
                                   " %s, probably not connected" %
3555 22b7f6f8 Thomas Thrainer
                                   new_net_obj.name, errors.ECODE_INVAL)
3556 22b7f6f8 Thomas Thrainer
      new_params = dict(netparams)
3557 22b7f6f8 Thomas Thrainer
    else:
3558 22b7f6f8 Thomas Thrainer
      new_params = _GetUpdatedParams(old_params, update_params_dict)
3559 22b7f6f8 Thomas Thrainer
3560 22b7f6f8 Thomas Thrainer
    utils.ForceDictType(new_params, constants.NICS_PARAMETER_TYPES)
3561 22b7f6f8 Thomas Thrainer
3562 22b7f6f8 Thomas Thrainer
    new_filled_params = cluster.SimpleFillNIC(new_params)
3563 22b7f6f8 Thomas Thrainer
    objects.NIC.CheckParameterSyntax(new_filled_params)
3564 22b7f6f8 Thomas Thrainer
3565 22b7f6f8 Thomas Thrainer
    new_mode = new_filled_params[constants.NIC_MODE]
3566 22b7f6f8 Thomas Thrainer
    if new_mode == constants.NIC_MODE_BRIDGED:
3567 22b7f6f8 Thomas Thrainer
      bridge = new_filled_params[constants.NIC_LINK]
3568 22b7f6f8 Thomas Thrainer
      msg = self.rpc.call_bridges_exist(pnode, [bridge]).fail_msg
3569 22b7f6f8 Thomas Thrainer
      if msg:
3570 22b7f6f8 Thomas Thrainer
        msg = "Error checking bridges on node '%s': %s" % (pnode, msg)
3571 22b7f6f8 Thomas Thrainer
        if self.op.force:
3572 22b7f6f8 Thomas Thrainer
          self.warn.append(msg)
3573 22b7f6f8 Thomas Thrainer
        else:
3574 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError(msg, errors.ECODE_ENVIRON)
3575 22b7f6f8 Thomas Thrainer
3576 22b7f6f8 Thomas Thrainer
    elif new_mode == constants.NIC_MODE_ROUTED:
3577 22b7f6f8 Thomas Thrainer
      ip = params.get(constants.INIC_IP, old_ip)
3578 22b7f6f8 Thomas Thrainer
      if ip is None:
3579 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cannot set the NIC IP address to None"
3580 22b7f6f8 Thomas Thrainer
                                   " on a routed NIC", errors.ECODE_INVAL)
3581 22b7f6f8 Thomas Thrainer
3582 22b7f6f8 Thomas Thrainer
    elif new_mode == constants.NIC_MODE_OVS:
3583 22b7f6f8 Thomas Thrainer
      # TODO: check OVS link
3584 22b7f6f8 Thomas Thrainer
      self.LogInfo("OVS links are currently not checked for correctness")
3585 22b7f6f8 Thomas Thrainer
3586 22b7f6f8 Thomas Thrainer
    if constants.INIC_MAC in params:
3587 22b7f6f8 Thomas Thrainer
      mac = params[constants.INIC_MAC]
3588 22b7f6f8 Thomas Thrainer
      if mac is None:
3589 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cannot unset the NIC MAC address",
3590 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3591 22b7f6f8 Thomas Thrainer
      elif mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
3592 22b7f6f8 Thomas Thrainer
        # otherwise generate the MAC address
3593 22b7f6f8 Thomas Thrainer
        params[constants.INIC_MAC] = \
3594 22b7f6f8 Thomas Thrainer
          self.cfg.GenerateMAC(new_net_uuid, self.proc.GetECId())
3595 22b7f6f8 Thomas Thrainer
      else:
3596 22b7f6f8 Thomas Thrainer
        # or validate/reserve the current one
3597 22b7f6f8 Thomas Thrainer
        try:
3598 22b7f6f8 Thomas Thrainer
          self.cfg.ReserveMAC(mac, self.proc.GetECId())
3599 22b7f6f8 Thomas Thrainer
        except errors.ReservationError:
3600 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("MAC address '%s' already in use"
3601 22b7f6f8 Thomas Thrainer
                                     " in cluster" % mac,
3602 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_NOTUNIQUE)
3603 22b7f6f8 Thomas Thrainer
    elif new_net_uuid != old_net_uuid:
3604 22b7f6f8 Thomas Thrainer
3605 22b7f6f8 Thomas Thrainer
      def get_net_prefix(net_uuid):
3606 22b7f6f8 Thomas Thrainer
        mac_prefix = None
3607 22b7f6f8 Thomas Thrainer
        if net_uuid:
3608 22b7f6f8 Thomas Thrainer
          nobj = self.cfg.GetNetwork(net_uuid)
3609 22b7f6f8 Thomas Thrainer
          mac_prefix = nobj.mac_prefix
3610 22b7f6f8 Thomas Thrainer
3611 22b7f6f8 Thomas Thrainer
        return mac_prefix
3612 22b7f6f8 Thomas Thrainer
3613 22b7f6f8 Thomas Thrainer
      new_prefix = get_net_prefix(new_net_uuid)
3614 22b7f6f8 Thomas Thrainer
      old_prefix = get_net_prefix(old_net_uuid)
3615 22b7f6f8 Thomas Thrainer
      if old_prefix != new_prefix:
3616 22b7f6f8 Thomas Thrainer
        params[constants.INIC_MAC] = \
3617 22b7f6f8 Thomas Thrainer
          self.cfg.GenerateMAC(new_net_uuid, self.proc.GetECId())
3618 22b7f6f8 Thomas Thrainer
3619 22b7f6f8 Thomas Thrainer
    # if there is a change in (ip, network) tuple
3620 22b7f6f8 Thomas Thrainer
    new_ip = params.get(constants.INIC_IP, old_ip)
3621 22b7f6f8 Thomas Thrainer
    if (new_ip, new_net_uuid) != (old_ip, old_net_uuid):
3622 22b7f6f8 Thomas Thrainer
      if new_ip:
3623 22b7f6f8 Thomas Thrainer
        # if IP is pool then require a network and generate one IP
3624 22b7f6f8 Thomas Thrainer
        if new_ip.lower() == constants.NIC_IP_POOL:
3625 22b7f6f8 Thomas Thrainer
          if new_net_uuid:
3626 22b7f6f8 Thomas Thrainer
            try:
3627 22b7f6f8 Thomas Thrainer
              new_ip = self.cfg.GenerateIp(new_net_uuid, self.proc.GetECId())
3628 22b7f6f8 Thomas Thrainer
            except errors.ReservationError:
3629 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("Unable to get a free IP"
3630 22b7f6f8 Thomas Thrainer
                                         " from the address pool",
3631 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_STATE)
3632 22b7f6f8 Thomas Thrainer
            self.LogInfo("Chose IP %s from network %s",
3633 22b7f6f8 Thomas Thrainer
                         new_ip,
3634 22b7f6f8 Thomas Thrainer
                         new_net_obj.name)
3635 22b7f6f8 Thomas Thrainer
            params[constants.INIC_IP] = new_ip
3636 22b7f6f8 Thomas Thrainer
          else:
3637 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("ip=pool, but no network found",
3638 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
3639 22b7f6f8 Thomas Thrainer
        # Reserve new IP if in the new network if any
3640 22b7f6f8 Thomas Thrainer
        elif new_net_uuid:
3641 22b7f6f8 Thomas Thrainer
          try:
3642 22b7f6f8 Thomas Thrainer
            self.cfg.ReserveIp(new_net_uuid, new_ip, self.proc.GetECId())
3643 22b7f6f8 Thomas Thrainer
            self.LogInfo("Reserving IP %s in network %s",
3644 22b7f6f8 Thomas Thrainer
                         new_ip, new_net_obj.name)
3645 22b7f6f8 Thomas Thrainer
          except errors.ReservationError:
3646 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("IP %s not available in network %s" %
3647 22b7f6f8 Thomas Thrainer
                                       (new_ip, new_net_obj.name),
3648 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_NOTUNIQUE)
3649 22b7f6f8 Thomas Thrainer
        # new network is None so check if new IP is a conflicting IP
3650 22b7f6f8 Thomas Thrainer
        elif self.op.conflicts_check:
3651 22b7f6f8 Thomas Thrainer
          _CheckForConflictingIp(self, new_ip, pnode)
3652 22b7f6f8 Thomas Thrainer
3653 22b7f6f8 Thomas Thrainer
      # release old IP if old network is not None
3654 22b7f6f8 Thomas Thrainer
      if old_ip and old_net_uuid:
3655 22b7f6f8 Thomas Thrainer
        try:
3656 22b7f6f8 Thomas Thrainer
          self.cfg.ReleaseIp(old_net_uuid, old_ip, self.proc.GetECId())
3657 22b7f6f8 Thomas Thrainer
        except errors.AddressPoolError:
3658 22b7f6f8 Thomas Thrainer
          logging.warning("Release IP %s not contained in network %s",
3659 22b7f6f8 Thomas Thrainer
                          old_ip, old_net_obj.name)
3660 22b7f6f8 Thomas Thrainer
3661 22b7f6f8 Thomas Thrainer
    # there are no changes in (ip, network) tuple and old network is not None
3662 22b7f6f8 Thomas Thrainer
    elif (old_net_uuid is not None and
3663 22b7f6f8 Thomas Thrainer
          (req_link is not None or req_mode is not None)):
3664 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Not allowed to change link or mode of"
3665 22b7f6f8 Thomas Thrainer
                                 " a NIC that is connected to a network",
3666 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3667 22b7f6f8 Thomas Thrainer
3668 22b7f6f8 Thomas Thrainer
    private.params = new_params
3669 22b7f6f8 Thomas Thrainer
    private.filled = new_filled_params
3670 22b7f6f8 Thomas Thrainer
3671 22b7f6f8 Thomas Thrainer
  def _PreCheckDiskTemplate(self, pnode_info):
3672 22b7f6f8 Thomas Thrainer
    """CheckPrereq checks related to a new disk template."""
3673 22b7f6f8 Thomas Thrainer
    # Arguments are passed to avoid configuration lookups
3674 22b7f6f8 Thomas Thrainer
    instance = self.instance
3675 22b7f6f8 Thomas Thrainer
    pnode = instance.primary_node
3676 22b7f6f8 Thomas Thrainer
    cluster = self.cluster
3677 22b7f6f8 Thomas Thrainer
    if instance.disk_template == self.op.disk_template:
3678 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance already has disk template %s" %
3679 22b7f6f8 Thomas Thrainer
                                 instance.disk_template, errors.ECODE_INVAL)
3680 22b7f6f8 Thomas Thrainer
3681 22b7f6f8 Thomas Thrainer
    if (instance.disk_template,
3682 22b7f6f8 Thomas Thrainer
        self.op.disk_template) not in self._DISK_CONVERSIONS:
3683 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Unsupported disk template conversion from"
3684 22b7f6f8 Thomas Thrainer
                                 " %s to %s" % (instance.disk_template,
3685 22b7f6f8 Thomas Thrainer
                                                self.op.disk_template),
3686 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3687 22b7f6f8 Thomas Thrainer
    _CheckInstanceState(self, instance, INSTANCE_DOWN,
3688 22b7f6f8 Thomas Thrainer
                        msg="cannot change disk template")
3689 22b7f6f8 Thomas Thrainer
    if self.op.disk_template in constants.DTS_INT_MIRROR:
3690 22b7f6f8 Thomas Thrainer
      if self.op.remote_node == pnode:
3691 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Given new secondary node %s is the same"
3692 22b7f6f8 Thomas Thrainer
                                   " as the primary node of the instance" %
3693 22b7f6f8 Thomas Thrainer
                                   self.op.remote_node, errors.ECODE_STATE)
3694 22b7f6f8 Thomas Thrainer
      _CheckNodeOnline(self, self.op.remote_node)
3695 22b7f6f8 Thomas Thrainer
      _CheckNodeNotDrained(self, self.op.remote_node)
3696 22b7f6f8 Thomas Thrainer
      # FIXME: here we assume that the old instance type is DT_PLAIN
3697 22b7f6f8 Thomas Thrainer
      assert instance.disk_template == constants.DT_PLAIN
3698 22b7f6f8 Thomas Thrainer
      disks = [{constants.IDISK_SIZE: d.size,
3699 22b7f6f8 Thomas Thrainer
                constants.IDISK_VG: d.logical_id[0]}
3700 22b7f6f8 Thomas Thrainer
               for d in instance.disks]
3701 22b7f6f8 Thomas Thrainer
      required = _ComputeDiskSizePerVG(self.op.disk_template, disks)
3702 22b7f6f8 Thomas Thrainer
      _CheckNodesFreeDiskPerVG(self, [self.op.remote_node], required)
3703 22b7f6f8 Thomas Thrainer
3704 22b7f6f8 Thomas Thrainer
      snode_info = self.cfg.GetNodeInfo(self.op.remote_node)
3705 22b7f6f8 Thomas Thrainer
      snode_group = self.cfg.GetNodeGroup(snode_info.group)
3706 22b7f6f8 Thomas Thrainer
      ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
3707 22b7f6f8 Thomas Thrainer
                                                              snode_group)
3708 22b7f6f8 Thomas Thrainer
      _CheckTargetNodeIPolicy(self, ipolicy, instance, snode_info, self.cfg,
3709 22b7f6f8 Thomas Thrainer
                              ignore=self.op.ignore_ipolicy)
3710 22b7f6f8 Thomas Thrainer
      if pnode_info.group != snode_info.group:
3711 22b7f6f8 Thomas Thrainer
        self.LogWarning("The primary and secondary nodes are in two"
3712 22b7f6f8 Thomas Thrainer
                        " different node groups; the disk parameters"
3713 22b7f6f8 Thomas Thrainer
                        " from the first disk's node group will be"
3714 22b7f6f8 Thomas Thrainer
                        " used")
3715 22b7f6f8 Thomas Thrainer
3716 22b7f6f8 Thomas Thrainer
    if not self.op.disk_template in constants.DTS_EXCL_STORAGE:
3717 22b7f6f8 Thomas Thrainer
      # Make sure none of the nodes require exclusive storage
3718 22b7f6f8 Thomas Thrainer
      nodes = [pnode_info]
3719 22b7f6f8 Thomas Thrainer
      if self.op.disk_template in constants.DTS_INT_MIRROR:
3720 22b7f6f8 Thomas Thrainer
        assert snode_info
3721 22b7f6f8 Thomas Thrainer
        nodes.append(snode_info)
3722 22b7f6f8 Thomas Thrainer
      has_es = lambda n: _IsExclusiveStorageEnabledNode(self.cfg, n)
3723 22b7f6f8 Thomas Thrainer
      if compat.any(map(has_es, nodes)):
3724 22b7f6f8 Thomas Thrainer
        errmsg = ("Cannot convert disk template from %s to %s when exclusive"
3725 22b7f6f8 Thomas Thrainer
                  " storage is enabled" % (instance.disk_template,
3726 22b7f6f8 Thomas Thrainer
                                           self.op.disk_template))
3727 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError(errmsg, errors.ECODE_STATE)
3728 22b7f6f8 Thomas Thrainer
3729 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
3730 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
3731 22b7f6f8 Thomas Thrainer

3732 22b7f6f8 Thomas Thrainer
    This only checks the instance list against the existing names.
3733 22b7f6f8 Thomas Thrainer

3734 22b7f6f8 Thomas Thrainer
    """
3735 22b7f6f8 Thomas Thrainer
    assert self.op.instance_name in self.owned_locks(locking.LEVEL_INSTANCE)
3736 22b7f6f8 Thomas Thrainer
    instance = self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
3737 22b7f6f8 Thomas Thrainer
3738 22b7f6f8 Thomas Thrainer
    cluster = self.cluster = self.cfg.GetClusterInfo()
3739 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
3740 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
3741 22b7f6f8 Thomas Thrainer
3742 22b7f6f8 Thomas Thrainer
    pnode = instance.primary_node
3743 22b7f6f8 Thomas Thrainer
3744 22b7f6f8 Thomas Thrainer
    self.warn = []
3745 22b7f6f8 Thomas Thrainer
3746 22b7f6f8 Thomas Thrainer
    if (self.op.pnode is not None and self.op.pnode != pnode and
3747 22b7f6f8 Thomas Thrainer
        not self.op.force):
3748 22b7f6f8 Thomas Thrainer
      # verify that the instance is not up
3749 22b7f6f8 Thomas Thrainer
      instance_info = self.rpc.call_instance_info(pnode, instance.name,
3750 22b7f6f8 Thomas Thrainer
                                                  instance.hypervisor)
3751 22b7f6f8 Thomas Thrainer
      if instance_info.fail_msg:
3752 22b7f6f8 Thomas Thrainer
        self.warn.append("Can't get instance runtime information: %s" %
3753 22b7f6f8 Thomas Thrainer
                         instance_info.fail_msg)
3754 22b7f6f8 Thomas Thrainer
      elif instance_info.payload:
3755 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance is still running on %s" % pnode,
3756 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
3757 22b7f6f8 Thomas Thrainer
3758 22b7f6f8 Thomas Thrainer
    assert pnode in self.owned_locks(locking.LEVEL_NODE)
3759 22b7f6f8 Thomas Thrainer
    nodelist = list(instance.all_nodes)
3760 22b7f6f8 Thomas Thrainer
    pnode_info = self.cfg.GetNodeInfo(pnode)
3761 22b7f6f8 Thomas Thrainer
    self.diskparams = self.cfg.GetInstanceDiskParams(instance)
3762 22b7f6f8 Thomas Thrainer
3763 22b7f6f8 Thomas Thrainer
    #_CheckInstanceNodeGroups(self.cfg, self.op.instance_name, owned_groups)
3764 22b7f6f8 Thomas Thrainer
    assert pnode_info.group in self.owned_locks(locking.LEVEL_NODEGROUP)
3765 22b7f6f8 Thomas Thrainer
    group_info = self.cfg.GetNodeGroup(pnode_info.group)
3766 22b7f6f8 Thomas Thrainer
3767 22b7f6f8 Thomas Thrainer
    # dictionary with instance information after the modification
3768 22b7f6f8 Thomas Thrainer
    ispec = {}
3769 22b7f6f8 Thomas Thrainer
3770 22b7f6f8 Thomas Thrainer
    # Check disk modifications. This is done here and not in CheckArguments
3771 22b7f6f8 Thomas Thrainer
    # (as with NICs), because we need to know the instance's disk template
3772 22b7f6f8 Thomas Thrainer
    if instance.disk_template == constants.DT_EXT:
3773 22b7f6f8 Thomas Thrainer
      self._CheckMods("disk", self.op.disks, {},
3774 22b7f6f8 Thomas Thrainer
                      self._VerifyDiskModification)
3775 22b7f6f8 Thomas Thrainer
    else:
3776 22b7f6f8 Thomas Thrainer
      self._CheckMods("disk", self.op.disks, constants.IDISK_PARAMS_TYPES,
3777 22b7f6f8 Thomas Thrainer
                      self._VerifyDiskModification)
3778 22b7f6f8 Thomas Thrainer
3779 22b7f6f8 Thomas Thrainer
    # Prepare disk/NIC modifications
3780 22b7f6f8 Thomas Thrainer
    self.diskmod = PrepareContainerMods(self.op.disks, None)
3781 22b7f6f8 Thomas Thrainer
    self.nicmod = PrepareContainerMods(self.op.nics, _InstNicModPrivate)
3782 22b7f6f8 Thomas Thrainer
3783 22b7f6f8 Thomas Thrainer
    # Check the validity of the `provider' parameter
3784 22b7f6f8 Thomas Thrainer
    if instance.disk_template in constants.DT_EXT:
3785 22b7f6f8 Thomas Thrainer
      for mod in self.diskmod:
3786 22b7f6f8 Thomas Thrainer
        ext_provider = mod[2].get(constants.IDISK_PROVIDER, None)
3787 22b7f6f8 Thomas Thrainer
        if mod[0] == constants.DDM_ADD:
3788 22b7f6f8 Thomas Thrainer
          if ext_provider is None:
3789 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Instance template is '%s' and parameter"
3790 22b7f6f8 Thomas Thrainer
                                       " '%s' missing, during disk add" %
3791 22b7f6f8 Thomas Thrainer
                                       (constants.DT_EXT,
3792 22b7f6f8 Thomas Thrainer
                                        constants.IDISK_PROVIDER),
3793 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_NOENT)
3794 22b7f6f8 Thomas Thrainer
        elif mod[0] == constants.DDM_MODIFY:
3795 22b7f6f8 Thomas Thrainer
          if ext_provider:
3796 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Parameter '%s' is invalid during disk"
3797 22b7f6f8 Thomas Thrainer
                                       " modification" %
3798 22b7f6f8 Thomas Thrainer
                                       constants.IDISK_PROVIDER,
3799 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
3800 22b7f6f8 Thomas Thrainer
    else:
3801 22b7f6f8 Thomas Thrainer
      for mod in self.diskmod:
3802 22b7f6f8 Thomas Thrainer
        ext_provider = mod[2].get(constants.IDISK_PROVIDER, None)
3803 22b7f6f8 Thomas Thrainer
        if ext_provider is not None:
3804 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("Parameter '%s' is only valid for"
3805 22b7f6f8 Thomas Thrainer
                                     " instances of type '%s'" %
3806 22b7f6f8 Thomas Thrainer
                                     (constants.IDISK_PROVIDER,
3807 22b7f6f8 Thomas Thrainer
                                      constants.DT_EXT),
3808 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
3809 22b7f6f8 Thomas Thrainer
3810 22b7f6f8 Thomas Thrainer
    # OS change
3811 22b7f6f8 Thomas Thrainer
    if self.op.os_name and not self.op.force:
3812 22b7f6f8 Thomas Thrainer
      _CheckNodeHasOS(self, instance.primary_node, self.op.os_name,
3813 22b7f6f8 Thomas Thrainer
                      self.op.force_variant)
3814 22b7f6f8 Thomas Thrainer
      instance_os = self.op.os_name
3815 22b7f6f8 Thomas Thrainer
    else:
3816 22b7f6f8 Thomas Thrainer
      instance_os = instance.os
3817 22b7f6f8 Thomas Thrainer
3818 22b7f6f8 Thomas Thrainer
    assert not (self.op.disk_template and self.op.disks), \
3819 22b7f6f8 Thomas Thrainer
      "Can't modify disk template and apply disk changes at the same time"
3820 22b7f6f8 Thomas Thrainer
3821 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
3822 22b7f6f8 Thomas Thrainer
      self._PreCheckDiskTemplate(pnode_info)
3823 22b7f6f8 Thomas Thrainer
3824 22b7f6f8 Thomas Thrainer
    # hvparams processing
3825 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
3826 22b7f6f8 Thomas Thrainer
      hv_type = instance.hypervisor
3827 22b7f6f8 Thomas Thrainer
      i_hvdict = _GetUpdatedParams(instance.hvparams, self.op.hvparams)
3828 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(i_hvdict, constants.HVS_PARAMETER_TYPES)
3829 22b7f6f8 Thomas Thrainer
      hv_new = cluster.SimpleFillHV(hv_type, instance.os, i_hvdict)
3830 22b7f6f8 Thomas Thrainer
3831 22b7f6f8 Thomas Thrainer
      # local check
3832 22b7f6f8 Thomas Thrainer
      hypervisor.GetHypervisorClass(hv_type).CheckParameterSyntax(hv_new)
3833 22b7f6f8 Thomas Thrainer
      _CheckHVParams(self, nodelist, instance.hypervisor, hv_new)
3834 22b7f6f8 Thomas Thrainer
      self.hv_proposed = self.hv_new = hv_new # the new actual values
3835 22b7f6f8 Thomas Thrainer
      self.hv_inst = i_hvdict # the new dict (without defaults)
3836 22b7f6f8 Thomas Thrainer
    else:
3837 22b7f6f8 Thomas Thrainer
      self.hv_proposed = cluster.SimpleFillHV(instance.hypervisor, instance.os,
3838 22b7f6f8 Thomas Thrainer
                                              instance.hvparams)
3839 22b7f6f8 Thomas Thrainer
      self.hv_new = self.hv_inst = {}
3840 22b7f6f8 Thomas Thrainer
3841 22b7f6f8 Thomas Thrainer
    # beparams processing
3842 22b7f6f8 Thomas Thrainer
    if self.op.beparams:
3843 22b7f6f8 Thomas Thrainer
      i_bedict = _GetUpdatedParams(instance.beparams, self.op.beparams,
3844 22b7f6f8 Thomas Thrainer
                                   use_none=True)
3845 22b7f6f8 Thomas Thrainer
      objects.UpgradeBeParams(i_bedict)
3846 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(i_bedict, constants.BES_PARAMETER_TYPES)
3847 22b7f6f8 Thomas Thrainer
      be_new = cluster.SimpleFillBE(i_bedict)
3848 22b7f6f8 Thomas Thrainer
      self.be_proposed = self.be_new = be_new # the new actual values
3849 22b7f6f8 Thomas Thrainer
      self.be_inst = i_bedict # the new dict (without defaults)
3850 22b7f6f8 Thomas Thrainer
    else:
3851 22b7f6f8 Thomas Thrainer
      self.be_new = self.be_inst = {}
3852 22b7f6f8 Thomas Thrainer
      self.be_proposed = cluster.SimpleFillBE(instance.beparams)
3853 22b7f6f8 Thomas Thrainer
    be_old = cluster.FillBE(instance)
3854 22b7f6f8 Thomas Thrainer
3855 22b7f6f8 Thomas Thrainer
    # CPU param validation -- checking every time a parameter is
3856 22b7f6f8 Thomas Thrainer
    # changed to cover all cases where either CPU mask or vcpus have
3857 22b7f6f8 Thomas Thrainer
    # changed
3858 22b7f6f8 Thomas Thrainer
    if (constants.BE_VCPUS in self.be_proposed and
3859 22b7f6f8 Thomas Thrainer
        constants.HV_CPU_MASK in self.hv_proposed):
3860 22b7f6f8 Thomas Thrainer
      cpu_list = \
3861 22b7f6f8 Thomas Thrainer
        utils.ParseMultiCpuMask(self.hv_proposed[constants.HV_CPU_MASK])
3862 22b7f6f8 Thomas Thrainer
      # Verify mask is consistent with number of vCPUs. Can skip this
3863 22b7f6f8 Thomas Thrainer
      # test if only 1 entry in the CPU mask, which means same mask
3864 22b7f6f8 Thomas Thrainer
      # is applied to all vCPUs.
3865 22b7f6f8 Thomas Thrainer
      if (len(cpu_list) > 1 and
3866 22b7f6f8 Thomas Thrainer
          len(cpu_list) != self.be_proposed[constants.BE_VCPUS]):
3867 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Number of vCPUs [%d] does not match the"
3868 22b7f6f8 Thomas Thrainer
                                   " CPU mask [%s]" %
3869 22b7f6f8 Thomas Thrainer
                                   (self.be_proposed[constants.BE_VCPUS],
3870 22b7f6f8 Thomas Thrainer
                                    self.hv_proposed[constants.HV_CPU_MASK]),
3871 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3872 22b7f6f8 Thomas Thrainer
3873 22b7f6f8 Thomas Thrainer
      # Only perform this test if a new CPU mask is given
3874 22b7f6f8 Thomas Thrainer
      if constants.HV_CPU_MASK in self.hv_new:
3875 22b7f6f8 Thomas Thrainer
        # Calculate the largest CPU number requested
3876 22b7f6f8 Thomas Thrainer
        max_requested_cpu = max(map(max, cpu_list))
3877 22b7f6f8 Thomas Thrainer
        # Check that all of the instance's nodes have enough physical CPUs to
3878 22b7f6f8 Thomas Thrainer
        # satisfy the requested CPU mask
3879 22b7f6f8 Thomas Thrainer
        _CheckNodesPhysicalCPUs(self, instance.all_nodes,
3880 22b7f6f8 Thomas Thrainer
                                max_requested_cpu + 1, instance.hypervisor)
3881 22b7f6f8 Thomas Thrainer
3882 22b7f6f8 Thomas Thrainer
    # osparams processing
3883 22b7f6f8 Thomas Thrainer
    if self.op.osparams:
3884 22b7f6f8 Thomas Thrainer
      i_osdict = _GetUpdatedParams(instance.osparams, self.op.osparams)
3885 22b7f6f8 Thomas Thrainer
      _CheckOSParams(self, True, nodelist, instance_os, i_osdict)
3886 22b7f6f8 Thomas Thrainer
      self.os_inst = i_osdict # the new dict (without defaults)
3887 22b7f6f8 Thomas Thrainer
    else:
3888 22b7f6f8 Thomas Thrainer
      self.os_inst = {}
3889 22b7f6f8 Thomas Thrainer
3890 22b7f6f8 Thomas Thrainer
    #TODO(dynmem): do the appropriate check involving MINMEM
3891 22b7f6f8 Thomas Thrainer
    if (constants.BE_MAXMEM in self.op.beparams and not self.op.force and
3892 22b7f6f8 Thomas Thrainer
        be_new[constants.BE_MAXMEM] > be_old[constants.BE_MAXMEM]):
3893 22b7f6f8 Thomas Thrainer
      mem_check_list = [pnode]
3894 22b7f6f8 Thomas Thrainer
      if be_new[constants.BE_AUTO_BALANCE]:
3895 22b7f6f8 Thomas Thrainer
        # either we changed auto_balance to yes or it was from before
3896 22b7f6f8 Thomas Thrainer
        mem_check_list.extend(instance.secondary_nodes)
3897 22b7f6f8 Thomas Thrainer
      instance_info = self.rpc.call_instance_info(pnode, instance.name,
3898 22b7f6f8 Thomas Thrainer
                                                  instance.hypervisor)
3899 22b7f6f8 Thomas Thrainer
      nodeinfo = self.rpc.call_node_info(mem_check_list, None,
3900 22b7f6f8 Thomas Thrainer
                                         [instance.hypervisor], False)
3901 22b7f6f8 Thomas Thrainer
      pninfo = nodeinfo[pnode]
3902 22b7f6f8 Thomas Thrainer
      msg = pninfo.fail_msg
3903 22b7f6f8 Thomas Thrainer
      if msg:
3904 22b7f6f8 Thomas Thrainer
        # Assume the primary node is unreachable and go ahead
3905 22b7f6f8 Thomas Thrainer
        self.warn.append("Can't get info from primary node %s: %s" %
3906 22b7f6f8 Thomas Thrainer
                         (pnode, msg))
3907 22b7f6f8 Thomas Thrainer
      else:
3908 22b7f6f8 Thomas Thrainer
        (_, _, (pnhvinfo, )) = pninfo.payload
3909 22b7f6f8 Thomas Thrainer
        if not isinstance(pnhvinfo.get("memory_free", None), int):
3910 22b7f6f8 Thomas Thrainer
          self.warn.append("Node data from primary node %s doesn't contain"
3911 22b7f6f8 Thomas Thrainer
                           " free memory information" % pnode)
3912 22b7f6f8 Thomas Thrainer
        elif instance_info.fail_msg:
3913 22b7f6f8 Thomas Thrainer
          self.warn.append("Can't get instance runtime information: %s" %
3914 22b7f6f8 Thomas Thrainer
                           instance_info.fail_msg)
3915 22b7f6f8 Thomas Thrainer
        else:
3916 22b7f6f8 Thomas Thrainer
          if instance_info.payload:
3917 22b7f6f8 Thomas Thrainer
            current_mem = int(instance_info.payload["memory"])
3918 22b7f6f8 Thomas Thrainer
          else:
3919 22b7f6f8 Thomas Thrainer
            # Assume instance not running
3920 22b7f6f8 Thomas Thrainer
            # (there is a slight race condition here, but it's not very
3921 22b7f6f8 Thomas Thrainer
            # probable, and we have no other way to check)
3922 22b7f6f8 Thomas Thrainer
            # TODO: Describe race condition
3923 22b7f6f8 Thomas Thrainer
            current_mem = 0
3924 22b7f6f8 Thomas Thrainer
          #TODO(dynmem): do the appropriate check involving MINMEM
3925 22b7f6f8 Thomas Thrainer
          miss_mem = (be_new[constants.BE_MAXMEM] - current_mem -
3926 22b7f6f8 Thomas Thrainer
                      pnhvinfo["memory_free"])
3927 22b7f6f8 Thomas Thrainer
          if miss_mem > 0:
3928 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("This change will prevent the instance"
3929 22b7f6f8 Thomas Thrainer
                                       " from starting, due to %d MB of memory"
3930 22b7f6f8 Thomas Thrainer
                                       " missing on its primary node" %
3931 22b7f6f8 Thomas Thrainer
                                       miss_mem, errors.ECODE_NORES)
3932 22b7f6f8 Thomas Thrainer
3933 22b7f6f8 Thomas Thrainer
      if be_new[constants.BE_AUTO_BALANCE]:
3934 22b7f6f8 Thomas Thrainer
        for node, nres in nodeinfo.items():
3935 22b7f6f8 Thomas Thrainer
          if node not in instance.secondary_nodes:
3936 22b7f6f8 Thomas Thrainer
            continue
3937 22b7f6f8 Thomas Thrainer
          nres.Raise("Can't get info from secondary node %s" % node,
3938 22b7f6f8 Thomas Thrainer
                     prereq=True, ecode=errors.ECODE_STATE)
3939 22b7f6f8 Thomas Thrainer
          (_, _, (nhvinfo, )) = nres.payload
3940 22b7f6f8 Thomas Thrainer
          if not isinstance(nhvinfo.get("memory_free", None), int):
3941 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Secondary node %s didn't return free"
3942 22b7f6f8 Thomas Thrainer
                                       " memory information" % node,
3943 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_STATE)
3944 22b7f6f8 Thomas Thrainer
          #TODO(dynmem): do the appropriate check involving MINMEM
3945 22b7f6f8 Thomas Thrainer
          elif be_new[constants.BE_MAXMEM] > nhvinfo["memory_free"]:
3946 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("This change will prevent the instance"
3947 22b7f6f8 Thomas Thrainer
                                       " from failover to its secondary node"
3948 22b7f6f8 Thomas Thrainer
                                       " %s, due to not enough memory" % node,
3949 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_STATE)
3950 22b7f6f8 Thomas Thrainer
3951 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
3952 22b7f6f8 Thomas Thrainer
      remote_info = self.rpc.call_instance_info(instance.primary_node,
3953 22b7f6f8 Thomas Thrainer
                                                instance.name,
3954 22b7f6f8 Thomas Thrainer
                                                instance.hypervisor)
3955 22b7f6f8 Thomas Thrainer
      remote_info.Raise("Error checking node %s" % instance.primary_node)
3956 22b7f6f8 Thomas Thrainer
      if not remote_info.payload: # not running already
3957 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s is not running" %
3958 22b7f6f8 Thomas Thrainer
                                   instance.name, errors.ECODE_STATE)
3959 22b7f6f8 Thomas Thrainer
3960 22b7f6f8 Thomas Thrainer
      current_memory = remote_info.payload["memory"]
3961 22b7f6f8 Thomas Thrainer
      if (not self.op.force and
3962 22b7f6f8 Thomas Thrainer
           (self.op.runtime_mem > self.be_proposed[constants.BE_MAXMEM] or
3963 22b7f6f8 Thomas Thrainer
            self.op.runtime_mem < self.be_proposed[constants.BE_MINMEM])):
3964 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s must have memory between %d"
3965 22b7f6f8 Thomas Thrainer
                                   " and %d MB of memory unless --force is"
3966 22b7f6f8 Thomas Thrainer
                                   " given" %
3967 22b7f6f8 Thomas Thrainer
                                   (instance.name,
3968 22b7f6f8 Thomas Thrainer
                                    self.be_proposed[constants.BE_MINMEM],
3969 22b7f6f8 Thomas Thrainer
                                    self.be_proposed[constants.BE_MAXMEM]),
3970 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3971 22b7f6f8 Thomas Thrainer
3972 22b7f6f8 Thomas Thrainer
      delta = self.op.runtime_mem - current_memory
3973 22b7f6f8 Thomas Thrainer
      if delta > 0:
3974 22b7f6f8 Thomas Thrainer
        _CheckNodeFreeMemory(self, instance.primary_node,
3975 22b7f6f8 Thomas Thrainer
                             "ballooning memory for instance %s" %
3976 22b7f6f8 Thomas Thrainer
                             instance.name, delta, instance.hypervisor)
3977 22b7f6f8 Thomas Thrainer
3978 22b7f6f8 Thomas Thrainer
    if self.op.disks and instance.disk_template == constants.DT_DISKLESS:
3979 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk operations not supported for"
3980 22b7f6f8 Thomas Thrainer
                                 " diskless instances", errors.ECODE_INVAL)
3981 22b7f6f8 Thomas Thrainer
3982 22b7f6f8 Thomas Thrainer
    def _PrepareNicCreate(_, params, private):
3983 22b7f6f8 Thomas Thrainer
      self._PrepareNicModification(params, private, None, None,
3984 22b7f6f8 Thomas Thrainer
                                   {}, cluster, pnode)
3985 22b7f6f8 Thomas Thrainer
      return (None, None)
3986 22b7f6f8 Thomas Thrainer
3987 22b7f6f8 Thomas Thrainer
    def _PrepareNicMod(_, nic, params, private):
3988 22b7f6f8 Thomas Thrainer
      self._PrepareNicModification(params, private, nic.ip, nic.network,
3989 22b7f6f8 Thomas Thrainer
                                   nic.nicparams, cluster, pnode)
3990 22b7f6f8 Thomas Thrainer
      return None
3991 22b7f6f8 Thomas Thrainer
3992 22b7f6f8 Thomas Thrainer
    def _PrepareNicRemove(_, params, __):
3993 22b7f6f8 Thomas Thrainer
      ip = params.ip
3994 22b7f6f8 Thomas Thrainer
      net = params.network
3995 22b7f6f8 Thomas Thrainer
      if net is not None and ip is not None:
3996 22b7f6f8 Thomas Thrainer
        self.cfg.ReleaseIp(net, ip, self.proc.GetECId())
3997 22b7f6f8 Thomas Thrainer
3998 22b7f6f8 Thomas Thrainer
    # Verify NIC changes (operating on copy)
3999 22b7f6f8 Thomas Thrainer
    nics = instance.nics[:]
4000 22b7f6f8 Thomas Thrainer
    ApplyContainerMods("NIC", nics, None, self.nicmod,
4001 22b7f6f8 Thomas Thrainer
                       _PrepareNicCreate, _PrepareNicMod, _PrepareNicRemove)
4002 22b7f6f8 Thomas Thrainer
    if len(nics) > constants.MAX_NICS:
4003 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance has too many network interfaces"
4004 22b7f6f8 Thomas Thrainer
                                 " (%d), cannot add more" % constants.MAX_NICS,
4005 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
4006 22b7f6f8 Thomas Thrainer
4007 22b7f6f8 Thomas Thrainer
    def _PrepareDiskMod(_, disk, params, __):
4008 22b7f6f8 Thomas Thrainer
      disk.name = params.get(constants.IDISK_NAME, None)
4009 22b7f6f8 Thomas Thrainer
4010 22b7f6f8 Thomas Thrainer
    # Verify disk changes (operating on a copy)
4011 22b7f6f8 Thomas Thrainer
    disks = copy.deepcopy(instance.disks)
4012 22b7f6f8 Thomas Thrainer
    ApplyContainerMods("disk", disks, None, self.diskmod, None, _PrepareDiskMod,
4013 22b7f6f8 Thomas Thrainer
                       None)
4014 22b7f6f8 Thomas Thrainer
    utils.ValidateDeviceNames("disk", disks)
4015 22b7f6f8 Thomas Thrainer
    if len(disks) > constants.MAX_DISKS:
4016 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance has too many disks (%d), cannot add"
4017 22b7f6f8 Thomas Thrainer
                                 " more" % constants.MAX_DISKS,
4018 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
4019 22b7f6f8 Thomas Thrainer
    disk_sizes = [disk.size for disk in instance.disks]
4020 22b7f6f8 Thomas Thrainer
    disk_sizes.extend(params["size"] for (op, idx, params, private) in
4021 22b7f6f8 Thomas Thrainer
                      self.diskmod if op == constants.DDM_ADD)
4022 22b7f6f8 Thomas Thrainer
    ispec[constants.ISPEC_DISK_COUNT] = len(disk_sizes)
4023 22b7f6f8 Thomas Thrainer
    ispec[constants.ISPEC_DISK_SIZE] = disk_sizes
4024 22b7f6f8 Thomas Thrainer
4025 22b7f6f8 Thomas Thrainer
    if self.op.offline is not None and self.op.offline:
4026 22b7f6f8 Thomas Thrainer
      _CheckInstanceState(self, instance, CAN_CHANGE_INSTANCE_OFFLINE,
4027 22b7f6f8 Thomas Thrainer
                          msg="can't change to offline")
4028 22b7f6f8 Thomas Thrainer
4029 22b7f6f8 Thomas Thrainer
    # Pre-compute NIC changes (necessary to use result in hooks)
4030 22b7f6f8 Thomas Thrainer
    self._nic_chgdesc = []
4031 22b7f6f8 Thomas Thrainer
    if self.nicmod:
4032 22b7f6f8 Thomas Thrainer
      # Operate on copies as this is still in prereq
4033 22b7f6f8 Thomas Thrainer
      nics = [nic.Copy() for nic in instance.nics]
4034 22b7f6f8 Thomas Thrainer
      ApplyContainerMods("NIC", nics, self._nic_chgdesc, self.nicmod,
4035 22b7f6f8 Thomas Thrainer
                         self._CreateNewNic, self._ApplyNicMods, None)
4036 22b7f6f8 Thomas Thrainer
      # Verify that NIC names are unique and valid
4037 22b7f6f8 Thomas Thrainer
      utils.ValidateDeviceNames("NIC", nics)
4038 22b7f6f8 Thomas Thrainer
      self._new_nics = nics
4039 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_NIC_COUNT] = len(self._new_nics)
4040 22b7f6f8 Thomas Thrainer
    else:
4041 22b7f6f8 Thomas Thrainer
      self._new_nics = None
4042 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_NIC_COUNT] = len(instance.nics)
4043 22b7f6f8 Thomas Thrainer
4044 22b7f6f8 Thomas Thrainer
    if not self.op.ignore_ipolicy:
4045 22b7f6f8 Thomas Thrainer
      ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
4046 22b7f6f8 Thomas Thrainer
                                                              group_info)
4047 22b7f6f8 Thomas Thrainer
4048 22b7f6f8 Thomas Thrainer
      # Fill ispec with backend parameters
4049 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_SPINDLE_USE] = \
4050 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_SPINDLE_USE, None)
4051 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_CPU_COUNT] = self.be_new.get(constants.BE_VCPUS,
4052 22b7f6f8 Thomas Thrainer
                                                         None)
4053 22b7f6f8 Thomas Thrainer
4054 22b7f6f8 Thomas Thrainer
      # Copy ispec to verify parameters with min/max values separately
4055 22b7f6f8 Thomas Thrainer
      if self.op.disk_template:
4056 22b7f6f8 Thomas Thrainer
        new_disk_template = self.op.disk_template
4057 22b7f6f8 Thomas Thrainer
      else:
4058 22b7f6f8 Thomas Thrainer
        new_disk_template = instance.disk_template
4059 22b7f6f8 Thomas Thrainer
      ispec_max = ispec.copy()
4060 22b7f6f8 Thomas Thrainer
      ispec_max[constants.ISPEC_MEM_SIZE] = \
4061 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_MAXMEM, None)
4062 22b7f6f8 Thomas Thrainer
      res_max = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec_max,
4063 22b7f6f8 Thomas Thrainer
                                                     new_disk_template)
4064 22b7f6f8 Thomas Thrainer
      ispec_min = ispec.copy()
4065 22b7f6f8 Thomas Thrainer
      ispec_min[constants.ISPEC_MEM_SIZE] = \
4066 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_MINMEM, None)
4067 22b7f6f8 Thomas Thrainer
      res_min = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec_min,
4068 22b7f6f8 Thomas Thrainer
                                                     new_disk_template)
4069 22b7f6f8 Thomas Thrainer
4070 22b7f6f8 Thomas Thrainer
      if (res_max or res_min):
4071 22b7f6f8 Thomas Thrainer
        # FIXME: Improve error message by including information about whether
4072 22b7f6f8 Thomas Thrainer
        # the upper or lower limit of the parameter fails the ipolicy.
4073 22b7f6f8 Thomas Thrainer
        msg = ("Instance allocation to group %s (%s) violates policy: %s" %
4074 22b7f6f8 Thomas Thrainer
               (group_info, group_info.name,
4075 22b7f6f8 Thomas Thrainer
                utils.CommaJoin(set(res_max + res_min))))
4076 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
4077 22b7f6f8 Thomas Thrainer
4078 22b7f6f8 Thomas Thrainer
  def _ConvertPlainToDrbd(self, feedback_fn):
4079 22b7f6f8 Thomas Thrainer
    """Converts an instance from plain to drbd.
4080 22b7f6f8 Thomas Thrainer

4081 22b7f6f8 Thomas Thrainer
    """
4082 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to drbd")
4083 22b7f6f8 Thomas Thrainer
    instance = self.instance
4084 22b7f6f8 Thomas Thrainer
    pnode = instance.primary_node
4085 22b7f6f8 Thomas Thrainer
    snode = self.op.remote_node
4086 22b7f6f8 Thomas Thrainer
4087 22b7f6f8 Thomas Thrainer
    assert instance.disk_template == constants.DT_PLAIN
4088 22b7f6f8 Thomas Thrainer
4089 22b7f6f8 Thomas Thrainer
    # create a fake disk info for _GenerateDiskTemplate
4090 22b7f6f8 Thomas Thrainer
    disk_info = [{constants.IDISK_SIZE: d.size, constants.IDISK_MODE: d.mode,
4091 22b7f6f8 Thomas Thrainer
                  constants.IDISK_VG: d.logical_id[0],
4092 22b7f6f8 Thomas Thrainer
                  constants.IDISK_NAME: d.name}
4093 22b7f6f8 Thomas Thrainer
                 for d in instance.disks]
4094 22b7f6f8 Thomas Thrainer
    new_disks = _GenerateDiskTemplate(self, self.op.disk_template,
4095 22b7f6f8 Thomas Thrainer
                                      instance.name, pnode, [snode],
4096 22b7f6f8 Thomas Thrainer
                                      disk_info, None, None, 0, feedback_fn,
4097 22b7f6f8 Thomas Thrainer
                                      self.diskparams)
4098 22b7f6f8 Thomas Thrainer
    anno_disks = rpc.AnnotateDiskParams(constants.DT_DRBD8, new_disks,
4099 22b7f6f8 Thomas Thrainer
                                        self.diskparams)
4100 22b7f6f8 Thomas Thrainer
    p_excl_stor = _IsExclusiveStorageEnabledNodeName(self.cfg, pnode)
4101 22b7f6f8 Thomas Thrainer
    s_excl_stor = _IsExclusiveStorageEnabledNodeName(self.cfg, snode)
4102 22b7f6f8 Thomas Thrainer
    info = _GetInstanceInfoText(instance)
4103 22b7f6f8 Thomas Thrainer
    feedback_fn("Creating additional volumes...")
4104 22b7f6f8 Thomas Thrainer
    # first, create the missing data and meta devices
4105 22b7f6f8 Thomas Thrainer
    for disk in anno_disks:
4106 22b7f6f8 Thomas Thrainer
      # unfortunately this is... not too nice
4107 22b7f6f8 Thomas Thrainer
      _CreateSingleBlockDev(self, pnode, instance, disk.children[1],
4108 22b7f6f8 Thomas Thrainer
                            info, True, p_excl_stor)
4109 22b7f6f8 Thomas Thrainer
      for child in disk.children:
4110 22b7f6f8 Thomas Thrainer
        _CreateSingleBlockDev(self, snode, instance, child, info, True,
4111 22b7f6f8 Thomas Thrainer
                              s_excl_stor)
4112 22b7f6f8 Thomas Thrainer
    # at this stage, all new LVs have been created, we can rename the
4113 22b7f6f8 Thomas Thrainer
    # old ones
4114 22b7f6f8 Thomas Thrainer
    feedback_fn("Renaming original volumes...")
4115 22b7f6f8 Thomas Thrainer
    rename_list = [(o, n.children[0].logical_id)
4116 22b7f6f8 Thomas Thrainer
                   for (o, n) in zip(instance.disks, new_disks)]
4117 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_blockdev_rename(pnode, rename_list)
4118 22b7f6f8 Thomas Thrainer
    result.Raise("Failed to rename original LVs")
4119 22b7f6f8 Thomas Thrainer
4120 22b7f6f8 Thomas Thrainer
    feedback_fn("Initializing DRBD devices...")
4121 22b7f6f8 Thomas Thrainer
    # all child devices are in place, we can now create the DRBD devices
4122 22b7f6f8 Thomas Thrainer
    try:
4123 22b7f6f8 Thomas Thrainer
      for disk in anno_disks:
4124 22b7f6f8 Thomas Thrainer
        for (node, excl_stor) in [(pnode, p_excl_stor), (snode, s_excl_stor)]:
4125 22b7f6f8 Thomas Thrainer
          f_create = node == pnode
4126 22b7f6f8 Thomas Thrainer
          _CreateSingleBlockDev(self, node, instance, disk, info, f_create,
4127 22b7f6f8 Thomas Thrainer
                                excl_stor)
4128 22b7f6f8 Thomas Thrainer
    except errors.GenericError, e:
4129 22b7f6f8 Thomas Thrainer
      feedback_fn("Initializing of DRBD devices failed;"
4130 22b7f6f8 Thomas Thrainer
                  " renaming back original volumes...")
4131 22b7f6f8 Thomas Thrainer
      for disk in new_disks:
4132 22b7f6f8 Thomas Thrainer
        self.cfg.SetDiskID(disk, pnode)
4133 22b7f6f8 Thomas Thrainer
      rename_back_list = [(n.children[0], o.logical_id)
4134 22b7f6f8 Thomas Thrainer
                          for (n, o) in zip(new_disks, instance.disks)]
4135 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_blockdev_rename(pnode, rename_back_list)
4136 22b7f6f8 Thomas Thrainer
      result.Raise("Failed to rename LVs back after error %s" % str(e))
4137 22b7f6f8 Thomas Thrainer
      raise
4138 22b7f6f8 Thomas Thrainer
4139 22b7f6f8 Thomas Thrainer
    # at this point, the instance has been modified
4140 22b7f6f8 Thomas Thrainer
    instance.disk_template = constants.DT_DRBD8
4141 22b7f6f8 Thomas Thrainer
    instance.disks = new_disks
4142 22b7f6f8 Thomas Thrainer
    self.cfg.Update(instance, feedback_fn)
4143 22b7f6f8 Thomas Thrainer
4144 22b7f6f8 Thomas Thrainer
    # Release node locks while waiting for sync
4145 22b7f6f8 Thomas Thrainer
    _ReleaseLocks(self, locking.LEVEL_NODE)
4146 22b7f6f8 Thomas Thrainer
4147 22b7f6f8 Thomas Thrainer
    # disks are created, waiting for sync
4148 22b7f6f8 Thomas Thrainer
    disk_abort = not _WaitForSync(self, instance,
4149 22b7f6f8 Thomas Thrainer
                                  oneshot=not self.op.wait_for_sync)
4150 22b7f6f8 Thomas Thrainer
    if disk_abort:
4151 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("There are some degraded disks for"
4152 22b7f6f8 Thomas Thrainer
                               " this instance, please cleanup manually")
4153 22b7f6f8 Thomas Thrainer
4154 22b7f6f8 Thomas Thrainer
    # Node resource locks will be released by caller
4155 22b7f6f8 Thomas Thrainer
4156 22b7f6f8 Thomas Thrainer
  def _ConvertDrbdToPlain(self, feedback_fn):
4157 22b7f6f8 Thomas Thrainer
    """Converts an instance from drbd to plain.
4158 22b7f6f8 Thomas Thrainer

4159 22b7f6f8 Thomas Thrainer
    """
4160 22b7f6f8 Thomas Thrainer
    instance = self.instance
4161 22b7f6f8 Thomas Thrainer
4162 22b7f6f8 Thomas Thrainer
    assert len(instance.secondary_nodes) == 1
4163 22b7f6f8 Thomas Thrainer
    assert instance.disk_template == constants.DT_DRBD8
4164 22b7f6f8 Thomas Thrainer
4165 22b7f6f8 Thomas Thrainer
    pnode = instance.primary_node
4166 22b7f6f8 Thomas Thrainer
    snode = instance.secondary_nodes[0]
4167 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to plain")
4168 22b7f6f8 Thomas Thrainer
4169 22b7f6f8 Thomas Thrainer
    old_disks = _AnnotateDiskParams(instance, instance.disks, self.cfg)
4170 22b7f6f8 Thomas Thrainer
    new_disks = [d.children[0] for d in instance.disks]
4171 22b7f6f8 Thomas Thrainer
4172 22b7f6f8 Thomas Thrainer
    # copy over size, mode and name
4173 22b7f6f8 Thomas Thrainer
    for parent, child in zip(old_disks, new_disks):
4174 22b7f6f8 Thomas Thrainer
      child.size = parent.size
4175 22b7f6f8 Thomas Thrainer
      child.mode = parent.mode
4176 22b7f6f8 Thomas Thrainer
      child.name = parent.name
4177 22b7f6f8 Thomas Thrainer
4178 22b7f6f8 Thomas Thrainer
    # this is a DRBD disk, return its port to the pool
4179 22b7f6f8 Thomas Thrainer
    # NOTE: this must be done right before the call to cfg.Update!
4180 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
4181 22b7f6f8 Thomas Thrainer
      tcp_port = disk.logical_id[2]
4182 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(tcp_port)
4183 22b7f6f8 Thomas Thrainer
4184 22b7f6f8 Thomas Thrainer
    # update instance structure
4185 22b7f6f8 Thomas Thrainer
    instance.disks = new_disks
4186 22b7f6f8 Thomas Thrainer
    instance.disk_template = constants.DT_PLAIN
4187 22b7f6f8 Thomas Thrainer
    _UpdateIvNames(0, instance.disks)
4188 22b7f6f8 Thomas Thrainer
    self.cfg.Update(instance, feedback_fn)
4189 22b7f6f8 Thomas Thrainer
4190 22b7f6f8 Thomas Thrainer
    # Release locks in case removing disks takes a while
4191 22b7f6f8 Thomas Thrainer
    _ReleaseLocks(self, locking.LEVEL_NODE)
4192 22b7f6f8 Thomas Thrainer
4193 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing volumes on the secondary node...")
4194 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
4195 22b7f6f8 Thomas Thrainer
      self.cfg.SetDiskID(disk, snode)
4196 22b7f6f8 Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(snode, disk).fail_msg
4197 22b7f6f8 Thomas Thrainer
      if msg:
4198 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove block device %s on node %s,"
4199 22b7f6f8 Thomas Thrainer
                        " continuing anyway: %s", disk.iv_name, snode, msg)
4200 22b7f6f8 Thomas Thrainer
4201 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing unneeded volumes on the primary node...")
4202 22b7f6f8 Thomas Thrainer
    for idx, disk in enumerate(old_disks):
4203 22b7f6f8 Thomas Thrainer
      meta = disk.children[1]
4204 22b7f6f8 Thomas Thrainer
      self.cfg.SetDiskID(meta, pnode)
4205 22b7f6f8 Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(pnode, meta).fail_msg
4206 22b7f6f8 Thomas Thrainer
      if msg:
4207 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove metadata for disk %d on node %s,"
4208 22b7f6f8 Thomas Thrainer
                        " continuing anyway: %s", idx, pnode, msg)
4209 22b7f6f8 Thomas Thrainer
4210 22b7f6f8 Thomas Thrainer
  def _CreateNewDisk(self, idx, params, _):
4211 22b7f6f8 Thomas Thrainer
    """Creates a new disk.
4212 22b7f6f8 Thomas Thrainer

4213 22b7f6f8 Thomas Thrainer
    """
4214 22b7f6f8 Thomas Thrainer
    instance = self.instance
4215 22b7f6f8 Thomas Thrainer
4216 22b7f6f8 Thomas Thrainer
    # add a new disk
4217 22b7f6f8 Thomas Thrainer
    if instance.disk_template in constants.DTS_FILEBASED:
4218 22b7f6f8 Thomas Thrainer
      (file_driver, file_path) = instance.disks[0].logical_id
4219 22b7f6f8 Thomas Thrainer
      file_path = os.path.dirname(file_path)
4220 22b7f6f8 Thomas Thrainer
    else:
4221 22b7f6f8 Thomas Thrainer
      file_driver = file_path = None
4222 22b7f6f8 Thomas Thrainer
4223 22b7f6f8 Thomas Thrainer
    disk = \
4224 22b7f6f8 Thomas Thrainer
      _GenerateDiskTemplate(self, instance.disk_template, instance.name,
4225 22b7f6f8 Thomas Thrainer
                            instance.primary_node, instance.secondary_nodes,
4226 22b7f6f8 Thomas Thrainer
                            [params], file_path, file_driver, idx,
4227 22b7f6f8 Thomas Thrainer
                            self.Log, self.diskparams)[0]
4228 22b7f6f8 Thomas Thrainer
4229 22b7f6f8 Thomas Thrainer
    info = _GetInstanceInfoText(instance)
4230 22b7f6f8 Thomas Thrainer
4231 22b7f6f8 Thomas Thrainer
    logging.info("Creating volume %s for instance %s",
4232 22b7f6f8 Thomas Thrainer
                 disk.iv_name, instance.name)
4233 22b7f6f8 Thomas Thrainer
    # Note: this needs to be kept in sync with _CreateDisks
4234 22b7f6f8 Thomas Thrainer
    #HARDCODE
4235 22b7f6f8 Thomas Thrainer
    for node in instance.all_nodes:
4236 22b7f6f8 Thomas Thrainer
      f_create = (node == instance.primary_node)
4237 22b7f6f8 Thomas Thrainer
      try:
4238 22b7f6f8 Thomas Thrainer
        _CreateBlockDev(self, node, instance, disk, f_create, info, f_create)
4239 22b7f6f8 Thomas Thrainer
      except errors.OpExecError, err:
4240 22b7f6f8 Thomas Thrainer
        self.LogWarning("Failed to create volume %s (%s) on node '%s': %s",
4241 22b7f6f8 Thomas Thrainer
                        disk.iv_name, disk, node, err)
4242 22b7f6f8 Thomas Thrainer
4243 22b7f6f8 Thomas Thrainer
    if self.cluster.prealloc_wipe_disks:
4244 22b7f6f8 Thomas Thrainer
      # Wipe new disk
4245 22b7f6f8 Thomas Thrainer
      _WipeDisks(self, instance,
4246 22b7f6f8 Thomas Thrainer
                 disks=[(idx, disk, 0)])
4247 22b7f6f8 Thomas Thrainer
4248 22b7f6f8 Thomas Thrainer
    return (disk, [
4249 22b7f6f8 Thomas Thrainer
      ("disk/%d" % idx, "add:size=%s,mode=%s" % (disk.size, disk.mode)),
4250 22b7f6f8 Thomas Thrainer
      ])
4251 22b7f6f8 Thomas Thrainer
4252 22b7f6f8 Thomas Thrainer
  @staticmethod
4253 22b7f6f8 Thomas Thrainer
  def _ModifyDisk(idx, disk, params, _):
4254 22b7f6f8 Thomas Thrainer
    """Modifies a disk.
4255 22b7f6f8 Thomas Thrainer

4256 22b7f6f8 Thomas Thrainer
    """
4257 22b7f6f8 Thomas Thrainer
    changes = []
4258 22b7f6f8 Thomas Thrainer
    mode = params.get(constants.IDISK_MODE, None)
4259 22b7f6f8 Thomas Thrainer
    if mode:
4260 22b7f6f8 Thomas Thrainer
      disk.mode = mode
4261 22b7f6f8 Thomas Thrainer
      changes.append(("disk.mode/%d" % idx, disk.mode))
4262 22b7f6f8 Thomas Thrainer
4263 22b7f6f8 Thomas Thrainer
    name = params.get(constants.IDISK_NAME, None)
4264 22b7f6f8 Thomas Thrainer
    disk.name = name
4265 22b7f6f8 Thomas Thrainer
    changes.append(("disk.name/%d" % idx, disk.name))
4266 22b7f6f8 Thomas Thrainer
4267 22b7f6f8 Thomas Thrainer
    return changes
4268 22b7f6f8 Thomas Thrainer
4269 22b7f6f8 Thomas Thrainer
  def _RemoveDisk(self, idx, root, _):
4270 22b7f6f8 Thomas Thrainer
    """Removes a disk.
4271 22b7f6f8 Thomas Thrainer

4272 22b7f6f8 Thomas Thrainer
    """
4273 22b7f6f8 Thomas Thrainer
    (anno_disk,) = _AnnotateDiskParams(self.instance, [root], self.cfg)
4274 22b7f6f8 Thomas Thrainer
    for node, disk in anno_disk.ComputeNodeTree(self.instance.primary_node):
4275 22b7f6f8 Thomas Thrainer
      self.cfg.SetDiskID(disk, node)
4276 22b7f6f8 Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(node, disk).fail_msg
4277 22b7f6f8 Thomas Thrainer
      if msg:
4278 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove disk/%d on node '%s': %s,"
4279 22b7f6f8 Thomas Thrainer
                        " continuing anyway", idx, node, msg)
4280 22b7f6f8 Thomas Thrainer
4281 22b7f6f8 Thomas Thrainer
    # if this is a DRBD disk, return its port to the pool
4282 22b7f6f8 Thomas Thrainer
    if root.dev_type in constants.LDS_DRBD:
4283 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(root.logical_id[2])
4284 22b7f6f8 Thomas Thrainer
4285 22b7f6f8 Thomas Thrainer
  def _CreateNewNic(self, idx, params, private):
4286 22b7f6f8 Thomas Thrainer
    """Creates data structure for a new network interface.
4287 22b7f6f8 Thomas Thrainer

4288 22b7f6f8 Thomas Thrainer
    """
4289 22b7f6f8 Thomas Thrainer
    mac = params[constants.INIC_MAC]
4290 22b7f6f8 Thomas Thrainer
    ip = params.get(constants.INIC_IP, None)
4291 22b7f6f8 Thomas Thrainer
    net = params.get(constants.INIC_NETWORK, None)
4292 22b7f6f8 Thomas Thrainer
    name = params.get(constants.INIC_NAME, None)
4293 22b7f6f8 Thomas Thrainer
    net_uuid = self.cfg.LookupNetwork(net)
4294 22b7f6f8 Thomas Thrainer
    #TODO: not private.filled?? can a nic have no nicparams??
4295 22b7f6f8 Thomas Thrainer
    nicparams = private.filled
4296 22b7f6f8 Thomas Thrainer
    nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, name=name,
4297 22b7f6f8 Thomas Thrainer
                       nicparams=nicparams)
4298 22b7f6f8 Thomas Thrainer
    nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
4299 22b7f6f8 Thomas Thrainer
4300 22b7f6f8 Thomas Thrainer
    return (nobj, [
4301 22b7f6f8 Thomas Thrainer
      ("nic.%d" % idx,
4302 22b7f6f8 Thomas Thrainer
       "add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" %
4303 22b7f6f8 Thomas Thrainer
       (mac, ip, private.filled[constants.NIC_MODE],
4304 22b7f6f8 Thomas Thrainer
       private.filled[constants.NIC_LINK],
4305 22b7f6f8 Thomas Thrainer
       net)),
4306 22b7f6f8 Thomas Thrainer
      ])
4307 22b7f6f8 Thomas Thrainer
4308 22b7f6f8 Thomas Thrainer
  def _ApplyNicMods(self, idx, nic, params, private):
4309 22b7f6f8 Thomas Thrainer
    """Modifies a network interface.
4310 22b7f6f8 Thomas Thrainer

4311 22b7f6f8 Thomas Thrainer
    """
4312 22b7f6f8 Thomas Thrainer
    changes = []
4313 22b7f6f8 Thomas Thrainer
4314 22b7f6f8 Thomas Thrainer
    for key in [constants.INIC_MAC, constants.INIC_IP, constants.INIC_NAME]:
4315 22b7f6f8 Thomas Thrainer
      if key in params:
4316 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), params[key]))
4317 22b7f6f8 Thomas Thrainer
        setattr(nic, key, params[key])
4318 22b7f6f8 Thomas Thrainer
4319 22b7f6f8 Thomas Thrainer
    new_net = params.get(constants.INIC_NETWORK, nic.network)
4320 22b7f6f8 Thomas Thrainer
    new_net_uuid = self.cfg.LookupNetwork(new_net)
4321 22b7f6f8 Thomas Thrainer
    if new_net_uuid != nic.network:
4322 22b7f6f8 Thomas Thrainer
      changes.append(("nic.network/%d" % idx, new_net))
4323 22b7f6f8 Thomas Thrainer
      nic.network = new_net_uuid
4324 22b7f6f8 Thomas Thrainer
4325 22b7f6f8 Thomas Thrainer
    if private.filled:
4326 22b7f6f8 Thomas Thrainer
      nic.nicparams = private.filled
4327 22b7f6f8 Thomas Thrainer
4328 22b7f6f8 Thomas Thrainer
      for (key, val) in nic.nicparams.items():
4329 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), val))
4330 22b7f6f8 Thomas Thrainer
4331 22b7f6f8 Thomas Thrainer
    return changes
4332 22b7f6f8 Thomas Thrainer
4333 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
4334 22b7f6f8 Thomas Thrainer
    """Modifies an instance.
4335 22b7f6f8 Thomas Thrainer

4336 22b7f6f8 Thomas Thrainer
    All parameters take effect only at the next restart of the instance.
4337 22b7f6f8 Thomas Thrainer

4338 22b7f6f8 Thomas Thrainer
    """
4339 22b7f6f8 Thomas Thrainer
    # Process here the warnings from CheckPrereq, as we don't have a
4340 22b7f6f8 Thomas Thrainer
    # feedback_fn there.
4341 22b7f6f8 Thomas Thrainer
    # TODO: Replace with self.LogWarning
4342 22b7f6f8 Thomas Thrainer
    for warn in self.warn:
4343 22b7f6f8 Thomas Thrainer
      feedback_fn("WARNING: %s" % warn)
4344 22b7f6f8 Thomas Thrainer
4345 22b7f6f8 Thomas Thrainer
    assert ((self.op.disk_template is None) ^
4346 22b7f6f8 Thomas Thrainer
            bool(self.owned_locks(locking.LEVEL_NODE_RES))), \
4347 22b7f6f8 Thomas Thrainer
      "Not owning any node resource locks"
4348 22b7f6f8 Thomas Thrainer
4349 22b7f6f8 Thomas Thrainer
    result = []
4350 22b7f6f8 Thomas Thrainer
    instance = self.instance
4351 22b7f6f8 Thomas Thrainer
4352 22b7f6f8 Thomas Thrainer
    # New primary node
4353 22b7f6f8 Thomas Thrainer
    if self.op.pnode:
4354 22b7f6f8 Thomas Thrainer
      instance.primary_node = self.op.pnode
4355 22b7f6f8 Thomas Thrainer
4356 22b7f6f8 Thomas Thrainer
    # runtime memory
4357 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
4358 22b7f6f8 Thomas Thrainer
      rpcres = self.rpc.call_instance_balloon_memory(instance.primary_node,
4359 22b7f6f8 Thomas Thrainer
                                                     instance,
4360 22b7f6f8 Thomas Thrainer
                                                     self.op.runtime_mem)
4361 22b7f6f8 Thomas Thrainer
      rpcres.Raise("Cannot modify instance runtime memory")
4362 22b7f6f8 Thomas Thrainer
      result.append(("runtime_memory", self.op.runtime_mem))
4363 22b7f6f8 Thomas Thrainer
4364 22b7f6f8 Thomas Thrainer
    # Apply disk changes
4365 22b7f6f8 Thomas Thrainer
    ApplyContainerMods("disk", instance.disks, result, self.diskmod,
4366 22b7f6f8 Thomas Thrainer
                       self._CreateNewDisk, self._ModifyDisk, self._RemoveDisk)
4367 22b7f6f8 Thomas Thrainer
    _UpdateIvNames(0, instance.disks)
4368 22b7f6f8 Thomas Thrainer
4369 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
4370 22b7f6f8 Thomas Thrainer
      if __debug__:
4371 22b7f6f8 Thomas Thrainer
        check_nodes = set(instance.all_nodes)
4372 22b7f6f8 Thomas Thrainer
        if self.op.remote_node:
4373 22b7f6f8 Thomas Thrainer
          check_nodes.add(self.op.remote_node)
4374 22b7f6f8 Thomas Thrainer
        for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
4375 22b7f6f8 Thomas Thrainer
          owned = self.owned_locks(level)
4376 22b7f6f8 Thomas Thrainer
          assert not (check_nodes - owned), \
4377 22b7f6f8 Thomas Thrainer
            ("Not owning the correct locks, owning %r, expected at least %r" %
4378 22b7f6f8 Thomas Thrainer
             (owned, check_nodes))
4379 22b7f6f8 Thomas Thrainer
4380 22b7f6f8 Thomas Thrainer
      r_shut = _ShutdownInstanceDisks(self, instance)
4381 22b7f6f8 Thomas Thrainer
      if not r_shut:
4382 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Cannot shutdown instance disks, unable to"
4383 22b7f6f8 Thomas Thrainer
                                 " proceed with disk template conversion")
4384 22b7f6f8 Thomas Thrainer
      mode = (instance.disk_template, self.op.disk_template)
4385 22b7f6f8 Thomas Thrainer
      try:
4386 22b7f6f8 Thomas Thrainer
        self._DISK_CONVERSIONS[mode](self, feedback_fn)
4387 22b7f6f8 Thomas Thrainer
      except:
4388 22b7f6f8 Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(instance.name)
4389 22b7f6f8 Thomas Thrainer
        raise
4390 22b7f6f8 Thomas Thrainer
      result.append(("disk_template", self.op.disk_template))
4391 22b7f6f8 Thomas Thrainer
4392 22b7f6f8 Thomas Thrainer
      assert instance.disk_template == self.op.disk_template, \
4393 22b7f6f8 Thomas Thrainer
        ("Expected disk template '%s', found '%s'" %
4394 22b7f6f8 Thomas Thrainer
         (self.op.disk_template, instance.disk_template))
4395 22b7f6f8 Thomas Thrainer
4396 22b7f6f8 Thomas Thrainer
    # Release node and resource locks if there are any (they might already have
4397 22b7f6f8 Thomas Thrainer
    # been released during disk conversion)
4398 22b7f6f8 Thomas Thrainer
    _ReleaseLocks(self, locking.LEVEL_NODE)
4399 22b7f6f8 Thomas Thrainer
    _ReleaseLocks(self, locking.LEVEL_NODE_RES)
4400 22b7f6f8 Thomas Thrainer
4401 22b7f6f8 Thomas Thrainer
    # Apply NIC changes
4402 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
4403 22b7f6f8 Thomas Thrainer
      instance.nics = self._new_nics
4404 22b7f6f8 Thomas Thrainer
      result.extend(self._nic_chgdesc)
4405 22b7f6f8 Thomas Thrainer
4406 22b7f6f8 Thomas Thrainer
    # hvparams changes
4407 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
4408 22b7f6f8 Thomas Thrainer
      instance.hvparams = self.hv_inst
4409 22b7f6f8 Thomas Thrainer
      for key, val in self.op.hvparams.iteritems():
4410 22b7f6f8 Thomas Thrainer
        result.append(("hv/%s" % key, val))
4411 22b7f6f8 Thomas Thrainer
4412 22b7f6f8 Thomas Thrainer
    # beparams changes
4413 22b7f6f8 Thomas Thrainer
    if self.op.beparams:
4414 22b7f6f8 Thomas Thrainer
      instance.beparams = self.be_inst
4415 22b7f6f8 Thomas Thrainer
      for key, val in self.op.beparams.iteritems():
4416 22b7f6f8 Thomas Thrainer
        result.append(("be/%s" % key, val))
4417 22b7f6f8 Thomas Thrainer
4418 22b7f6f8 Thomas Thrainer
    # OS change
4419 22b7f6f8 Thomas Thrainer
    if self.op.os_name:
4420 22b7f6f8 Thomas Thrainer
      instance.os = self.op.os_name
4421 22b7f6f8 Thomas Thrainer
4422 22b7f6f8 Thomas Thrainer
    # osparams changes
4423 22b7f6f8 Thomas Thrainer
    if self.op.osparams:
4424 22b7f6f8 Thomas Thrainer
      instance.osparams = self.os_inst
4425 22b7f6f8 Thomas Thrainer
      for key, val in self.op.osparams.iteritems():
4426 22b7f6f8 Thomas Thrainer
        result.append(("os/%s" % key, val))
4427 22b7f6f8 Thomas Thrainer
4428 22b7f6f8 Thomas Thrainer
    if self.op.offline is None:
4429 22b7f6f8 Thomas Thrainer
      # Ignore
4430 22b7f6f8 Thomas Thrainer
      pass
4431 22b7f6f8 Thomas Thrainer
    elif self.op.offline:
4432 22b7f6f8 Thomas Thrainer
      # Mark instance as offline
4433 22b7f6f8 Thomas Thrainer
      self.cfg.MarkInstanceOffline(instance.name)
4434 22b7f6f8 Thomas Thrainer
      result.append(("admin_state", constants.ADMINST_OFFLINE))
4435 22b7f6f8 Thomas Thrainer
    else:
4436 22b7f6f8 Thomas Thrainer
      # Mark instance as online, but stopped
4437 22b7f6f8 Thomas Thrainer
      self.cfg.MarkInstanceDown(instance.name)
4438 22b7f6f8 Thomas Thrainer
      result.append(("admin_state", constants.ADMINST_DOWN))
4439 22b7f6f8 Thomas Thrainer
4440 22b7f6f8 Thomas Thrainer
    self.cfg.Update(instance, feedback_fn, self.proc.GetECId())
4441 22b7f6f8 Thomas Thrainer
4442 22b7f6f8 Thomas Thrainer
    assert not (self.owned_locks(locking.LEVEL_NODE_RES) or
4443 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
4444 22b7f6f8 Thomas Thrainer
      "All node locks should have been released by now"
4445 22b7f6f8 Thomas Thrainer
4446 22b7f6f8 Thomas Thrainer
    return result
4447 22b7f6f8 Thomas Thrainer
4448 22b7f6f8 Thomas Thrainer
  _DISK_CONVERSIONS = {
4449 22b7f6f8 Thomas Thrainer
    (constants.DT_PLAIN, constants.DT_DRBD8): _ConvertPlainToDrbd,
4450 22b7f6f8 Thomas Thrainer
    (constants.DT_DRBD8, constants.DT_PLAIN): _ConvertDrbdToPlain,
4451 22b7f6f8 Thomas Thrainer
    }
4452 22b7f6f8 Thomas Thrainer
4453 22b7f6f8 Thomas Thrainer
4454 22b7f6f8 Thomas Thrainer
class LUInstanceChangeGroup(LogicalUnit):
4455 22b7f6f8 Thomas Thrainer
  HPATH = "instance-change-group"
4456 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
4457 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
4458 22b7f6f8 Thomas Thrainer
4459 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
4460 22b7f6f8 Thomas Thrainer
    self.share_locks = _ShareAll()
4461 22b7f6f8 Thomas Thrainer
4462 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
4463 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODEGROUP: [],
4464 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE: [],
4465 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
4466 22b7f6f8 Thomas Thrainer
      }
4467 22b7f6f8 Thomas Thrainer
4468 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
4469 22b7f6f8 Thomas Thrainer
4470 22b7f6f8 Thomas Thrainer
    if self.op.target_groups:
4471 22b7f6f8 Thomas Thrainer
      self.req_target_uuids = map(self.cfg.LookupNodeGroup,
4472 22b7f6f8 Thomas Thrainer
                                  self.op.target_groups)
4473 22b7f6f8 Thomas Thrainer
    else:
4474 22b7f6f8 Thomas Thrainer
      self.req_target_uuids = None
4475 22b7f6f8 Thomas Thrainer
4476 22b7f6f8 Thomas Thrainer
    self.op.iallocator = _GetDefaultIAllocator(self.cfg, self.op.iallocator)
4477 22b7f6f8 Thomas Thrainer
4478 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
4479 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
4480 22b7f6f8 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
4481 22b7f6f8 Thomas Thrainer
4482 22b7f6f8 Thomas Thrainer
      if self.req_target_uuids:
4483 22b7f6f8 Thomas Thrainer
        lock_groups = set(self.req_target_uuids)
4484 22b7f6f8 Thomas Thrainer
4485 22b7f6f8 Thomas Thrainer
        # Lock all groups used by instance optimistically; this requires going
4486 22b7f6f8 Thomas Thrainer
        # via the node before it's locked, requiring verification later on
4487 22b7f6f8 Thomas Thrainer
        instance_groups = self.cfg.GetInstanceNodeGroups(self.op.instance_name)
4488 22b7f6f8 Thomas Thrainer
        lock_groups.update(instance_groups)
4489 22b7f6f8 Thomas Thrainer
      else:
4490 22b7f6f8 Thomas Thrainer
        # No target groups, need to lock all of them
4491 22b7f6f8 Thomas Thrainer
        lock_groups = locking.ALL_SET
4492 22b7f6f8 Thomas Thrainer
4493 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = lock_groups
4494 22b7f6f8 Thomas Thrainer
4495 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE:
4496 22b7f6f8 Thomas Thrainer
      if self.req_target_uuids:
4497 22b7f6f8 Thomas Thrainer
        # Lock all nodes used by instances
4498 22b7f6f8 Thomas Thrainer
        self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
4499 22b7f6f8 Thomas Thrainer
        self._LockInstancesNodes()
4500 22b7f6f8 Thomas Thrainer
4501 22b7f6f8 Thomas Thrainer
        # Lock all nodes in all potential target groups
4502 22b7f6f8 Thomas Thrainer
        lock_groups = (frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) -
4503 22b7f6f8 Thomas Thrainer
                       self.cfg.GetInstanceNodeGroups(self.op.instance_name))
4504 22b7f6f8 Thomas Thrainer
        member_nodes = [node_name
4505 22b7f6f8 Thomas Thrainer
                        for group in lock_groups
4506 22b7f6f8 Thomas Thrainer
                        for node_name in self.cfg.GetNodeGroup(group).members]
4507 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE].extend(member_nodes)
4508 22b7f6f8 Thomas Thrainer
      else:
4509 22b7f6f8 Thomas Thrainer
        # Lock all nodes as all groups are potential targets
4510 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
4511 22b7f6f8 Thomas Thrainer
4512 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
4513 22b7f6f8 Thomas Thrainer
    owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
4514 22b7f6f8 Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
4515 22b7f6f8 Thomas Thrainer
    owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE))
4516 22b7f6f8 Thomas Thrainer
4517 22b7f6f8 Thomas Thrainer
    assert (self.req_target_uuids is None or
4518 22b7f6f8 Thomas Thrainer
            owned_groups.issuperset(self.req_target_uuids))
4519 22b7f6f8 Thomas Thrainer
    assert owned_instances == set([self.op.instance_name])
4520 22b7f6f8 Thomas Thrainer
4521 22b7f6f8 Thomas Thrainer
    # Get instance information
4522 22b7f6f8 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
4523 22b7f6f8 Thomas Thrainer
4524 22b7f6f8 Thomas Thrainer
    # Check if node groups for locked instance are still correct
4525 22b7f6f8 Thomas Thrainer
    assert owned_nodes.issuperset(self.instance.all_nodes), \
4526 22b7f6f8 Thomas Thrainer
      ("Instance %s's nodes changed while we kept the lock" %
4527 22b7f6f8 Thomas Thrainer
       self.op.instance_name)
4528 22b7f6f8 Thomas Thrainer
4529 22b7f6f8 Thomas Thrainer
    inst_groups = _CheckInstanceNodeGroups(self.cfg, self.op.instance_name,
4530 22b7f6f8 Thomas Thrainer
                                           owned_groups)
4531 22b7f6f8 Thomas Thrainer
4532 22b7f6f8 Thomas Thrainer
    if self.req_target_uuids:
4533 22b7f6f8 Thomas Thrainer
      # User requested specific target groups
4534 22b7f6f8 Thomas Thrainer
      self.target_uuids = frozenset(self.req_target_uuids)
4535 22b7f6f8 Thomas Thrainer
    else:
4536 22b7f6f8 Thomas Thrainer
      # All groups except those used by the instance are potential targets
4537 22b7f6f8 Thomas Thrainer
      self.target_uuids = owned_groups - inst_groups
4538 22b7f6f8 Thomas Thrainer
4539 22b7f6f8 Thomas Thrainer
    conflicting_groups = self.target_uuids & inst_groups
4540 22b7f6f8 Thomas Thrainer
    if conflicting_groups:
4541 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't use group(s) '%s' as targets, they are"
4542 22b7f6f8 Thomas Thrainer
                                 " used by the instance '%s'" %
4543 22b7f6f8 Thomas Thrainer
                                 (utils.CommaJoin(conflicting_groups),
4544 22b7f6f8 Thomas Thrainer
                                  self.op.instance_name),
4545 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
4546 22b7f6f8 Thomas Thrainer
4547 22b7f6f8 Thomas Thrainer
    if not self.target_uuids:
4548 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are no possible target groups",
4549 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
4550 22b7f6f8 Thomas Thrainer
4551 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
4552 22b7f6f8 Thomas Thrainer
    """Build hooks env.
4553 22b7f6f8 Thomas Thrainer

4554 22b7f6f8 Thomas Thrainer
    """
4555 22b7f6f8 Thomas Thrainer
    assert self.target_uuids
4556 22b7f6f8 Thomas Thrainer
4557 22b7f6f8 Thomas Thrainer
    env = {
4558 22b7f6f8 Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
4559 22b7f6f8 Thomas Thrainer
      }
4560 22b7f6f8 Thomas Thrainer
4561 22b7f6f8 Thomas Thrainer
    env.update(_BuildInstanceHookEnvByObject(self, self.instance))
4562 22b7f6f8 Thomas Thrainer
4563 22b7f6f8 Thomas Thrainer
    return env
4564 22b7f6f8 Thomas Thrainer
4565 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
4566 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
4567 22b7f6f8 Thomas Thrainer

4568 22b7f6f8 Thomas Thrainer
    """
4569 22b7f6f8 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
4570 22b7f6f8 Thomas Thrainer
    return ([mn], [mn])
4571 22b7f6f8 Thomas Thrainer
4572 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
4573 22b7f6f8 Thomas Thrainer
    instances = list(self.owned_locks(locking.LEVEL_INSTANCE))
4574 22b7f6f8 Thomas Thrainer
4575 22b7f6f8 Thomas Thrainer
    assert instances == [self.op.instance_name], "Instance not locked"
4576 22b7f6f8 Thomas Thrainer
4577 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=instances,
4578 22b7f6f8 Thomas Thrainer
                                      target_groups=list(self.target_uuids))
4579 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
4580 22b7f6f8 Thomas Thrainer
4581 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
4582 22b7f6f8 Thomas Thrainer
4583 22b7f6f8 Thomas Thrainer
    if not ial.success:
4584 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute solution for changing group of"
4585 22b7f6f8 Thomas Thrainer
                                 " instance '%s' using iallocator '%s': %s" %
4586 22b7f6f8 Thomas Thrainer
                                 (self.op.instance_name, self.op.iallocator,
4587 22b7f6f8 Thomas Thrainer
                                  ial.info), errors.ECODE_NORES)
4588 22b7f6f8 Thomas Thrainer
4589 22b7f6f8 Thomas Thrainer
    jobs = _LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
4590 22b7f6f8 Thomas Thrainer
4591 22b7f6f8 Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for changing group of"
4592 22b7f6f8 Thomas Thrainer
                 " instance '%s'", len(jobs), self.op.instance_name)
4593 22b7f6f8 Thomas Thrainer
4594 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs)
4595 22b7f6f8 Thomas Thrainer
4596 22b7f6f8 Thomas Thrainer
4597 22b7f6f8 Thomas Thrainer
class TLMigrateInstance(Tasklet):
4598 22b7f6f8 Thomas Thrainer
  """Tasklet class for instance migration.
4599 22b7f6f8 Thomas Thrainer

4600 22b7f6f8 Thomas Thrainer
  @type live: boolean
4601 22b7f6f8 Thomas Thrainer
  @ivar live: whether the migration will be done live or non-live;
4602 22b7f6f8 Thomas Thrainer
      this variable is initalized only after CheckPrereq has run
4603 22b7f6f8 Thomas Thrainer
  @type cleanup: boolean
4604 22b7f6f8 Thomas Thrainer
  @ivar cleanup: Wheater we cleanup from a failed migration
4605 22b7f6f8 Thomas Thrainer
  @type iallocator: string
4606 22b7f6f8 Thomas Thrainer
  @ivar iallocator: The iallocator used to determine target_node
4607 22b7f6f8 Thomas Thrainer
  @type target_node: string
4608 22b7f6f8 Thomas Thrainer
  @ivar target_node: If given, the target_node to reallocate the instance to
4609 22b7f6f8 Thomas Thrainer
  @type failover: boolean
4610 22b7f6f8 Thomas Thrainer
  @ivar failover: Whether operation results in failover or migration
4611 22b7f6f8 Thomas Thrainer
  @type fallback: boolean
4612 22b7f6f8 Thomas Thrainer
  @ivar fallback: Whether fallback to failover is allowed if migration not
4613 22b7f6f8 Thomas Thrainer
                  possible
4614 22b7f6f8 Thomas Thrainer
  @type ignore_consistency: boolean
4615 22b7f6f8 Thomas Thrainer
  @ivar ignore_consistency: Wheter we should ignore consistency between source
4616 22b7f6f8 Thomas Thrainer
                            and target node
4617 22b7f6f8 Thomas Thrainer
  @type shutdown_timeout: int
4618 22b7f6f8 Thomas Thrainer
  @ivar shutdown_timeout: In case of failover timeout of the shutdown
4619 22b7f6f8 Thomas Thrainer
  @type ignore_ipolicy: bool
4620 22b7f6f8 Thomas Thrainer
  @ivar ignore_ipolicy: If true, we can ignore instance policy when migrating
4621 22b7f6f8 Thomas Thrainer

4622 22b7f6f8 Thomas Thrainer
  """
4623 22b7f6f8 Thomas Thrainer
4624 22b7f6f8 Thomas Thrainer
  # Constants
4625 22b7f6f8 Thomas Thrainer
  _MIGRATION_POLL_INTERVAL = 1      # seconds
4626 22b7f6f8 Thomas Thrainer
  _MIGRATION_FEEDBACK_INTERVAL = 10 # seconds
4627 22b7f6f8 Thomas Thrainer
4628 22b7f6f8 Thomas Thrainer
  def __init__(self, lu, instance_name, cleanup, failover, fallback,
4629 22b7f6f8 Thomas Thrainer
               ignore_consistency, allow_runtime_changes, shutdown_timeout,
4630 22b7f6f8 Thomas Thrainer
               ignore_ipolicy):
4631 22b7f6f8 Thomas Thrainer
    """Initializes this class.
4632 22b7f6f8 Thomas Thrainer

4633 22b7f6f8 Thomas Thrainer
    """
4634 22b7f6f8 Thomas Thrainer
    Tasklet.__init__(self, lu)
4635 22b7f6f8 Thomas Thrainer
4636 22b7f6f8 Thomas Thrainer
    # Parameters
4637 22b7f6f8 Thomas Thrainer
    self.instance_name = instance_name
4638 22b7f6f8 Thomas Thrainer
    self.cleanup = cleanup
4639 22b7f6f8 Thomas Thrainer
    self.live = False # will be overridden later
4640 22b7f6f8 Thomas Thrainer
    self.failover = failover
4641 22b7f6f8 Thomas Thrainer
    self.fallback = fallback
4642 22b7f6f8 Thomas Thrainer
    self.ignore_consistency = ignore_consistency
4643 22b7f6f8 Thomas Thrainer
    self.shutdown_timeout = shutdown_timeout
4644 22b7f6f8 Thomas Thrainer
    self.ignore_ipolicy = ignore_ipolicy
4645 22b7f6f8 Thomas Thrainer
    self.allow_runtime_changes = allow_runtime_changes
4646 22b7f6f8 Thomas Thrainer
4647 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
4648 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
4649 22b7f6f8 Thomas Thrainer

4650 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
4651 22b7f6f8 Thomas Thrainer

4652 22b7f6f8 Thomas Thrainer
    """
4653 22b7f6f8 Thomas Thrainer
    instance_name = _ExpandInstanceName(self.lu.cfg, self.instance_name)
4654 22b7f6f8 Thomas Thrainer
    instance = self.cfg.GetInstanceInfo(instance_name)
4655 22b7f6f8 Thomas Thrainer
    assert instance is not None
4656 22b7f6f8 Thomas Thrainer
    self.instance = instance
4657 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
4658 22b7f6f8 Thomas Thrainer
4659 22b7f6f8 Thomas Thrainer
    if (not self.cleanup and
4660 22b7f6f8 Thomas Thrainer
        not instance.admin_state == constants.ADMINST_UP and
4661 22b7f6f8 Thomas Thrainer
        not self.failover and self.fallback):
4662 22b7f6f8 Thomas Thrainer
      self.lu.LogInfo("Instance is marked down or offline, fallback allowed,"
4663 22b7f6f8 Thomas Thrainer
                      " switching to failover")
4664 22b7f6f8 Thomas Thrainer
      self.failover = True
4665 22b7f6f8 Thomas Thrainer
4666 22b7f6f8 Thomas Thrainer
    if instance.disk_template not in constants.DTS_MIRRORED:
4667 22b7f6f8 Thomas Thrainer
      if self.failover:
4668 22b7f6f8 Thomas Thrainer
        text = "failovers"
4669 22b7f6f8 Thomas Thrainer
      else:
4670 22b7f6f8 Thomas Thrainer
        text = "migrations"
4671 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance's disk layout '%s' does not allow"
4672 22b7f6f8 Thomas Thrainer
                                 " %s" % (instance.disk_template, text),
4673 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
4674 22b7f6f8 Thomas Thrainer
4675 22b7f6f8 Thomas Thrainer
    if instance.disk_template in constants.DTS_EXT_MIRROR:
4676 22b7f6f8 Thomas Thrainer
      _CheckIAllocatorOrNode(self.lu, "iallocator", "target_node")
4677 22b7f6f8 Thomas Thrainer
4678 22b7f6f8 Thomas Thrainer
      if self.lu.op.iallocator:
4679 22b7f6f8 Thomas Thrainer
        assert locking.NAL in self.lu.owned_locks(locking.LEVEL_NODE_ALLOC)
4680 22b7f6f8 Thomas Thrainer
        self._RunAllocator()
4681 22b7f6f8 Thomas Thrainer
      else:
4682 22b7f6f8 Thomas Thrainer
        # We set set self.target_node as it is required by
4683 22b7f6f8 Thomas Thrainer
        # BuildHooksEnv
4684 22b7f6f8 Thomas Thrainer
        self.target_node = self.lu.op.target_node
4685 22b7f6f8 Thomas Thrainer
4686 22b7f6f8 Thomas Thrainer
      # Check that the target node is correct in terms of instance policy
4687 22b7f6f8 Thomas Thrainer
      nodeinfo = self.cfg.GetNodeInfo(self.target_node)
4688 22b7f6f8 Thomas Thrainer
      group_info = self.cfg.GetNodeGroup(nodeinfo.group)
4689 22b7f6f8 Thomas Thrainer
      ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
4690 22b7f6f8 Thomas Thrainer
                                                              group_info)
4691 22b7f6f8 Thomas Thrainer
      _CheckTargetNodeIPolicy(self.lu, ipolicy, instance, nodeinfo, self.cfg,
4692 22b7f6f8 Thomas Thrainer
                              ignore=self.ignore_ipolicy)
4693 22b7f6f8 Thomas Thrainer
4694 22b7f6f8 Thomas Thrainer
      # self.target_node is already populated, either directly or by the
4695 22b7f6f8 Thomas Thrainer
      # iallocator run
4696 22b7f6f8 Thomas Thrainer
      target_node = self.target_node
4697 22b7f6f8 Thomas Thrainer
      if self.target_node == instance.primary_node:
4698 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cannot migrate instance %s"
4699 22b7f6f8 Thomas Thrainer
                                   " to its primary (%s)" %
4700 22b7f6f8 Thomas Thrainer
                                   (instance.name, instance.primary_node),
4701 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
4702 22b7f6f8 Thomas Thrainer
4703 22b7f6f8 Thomas Thrainer
      if len(self.lu.tasklets) == 1:
4704 22b7f6f8 Thomas Thrainer
        # It is safe to release locks only when we're the only tasklet
4705 22b7f6f8 Thomas Thrainer
        # in the LU
4706 22b7f6f8 Thomas Thrainer
        _ReleaseLocks(self.lu, locking.LEVEL_NODE,
4707 22b7f6f8 Thomas Thrainer
                      keep=[instance.primary_node, self.target_node])
4708 22b7f6f8 Thomas Thrainer
        _ReleaseLocks(self.lu, locking.LEVEL_NODE_ALLOC)
4709 22b7f6f8 Thomas Thrainer
4710 22b7f6f8 Thomas Thrainer
    else:
4711 22b7f6f8 Thomas Thrainer
      assert not self.lu.glm.is_owned(locking.LEVEL_NODE_ALLOC)
4712 22b7f6f8 Thomas Thrainer
4713 22b7f6f8 Thomas Thrainer
      secondary_nodes = instance.secondary_nodes
4714 22b7f6f8 Thomas Thrainer
      if not secondary_nodes:
4715 22b7f6f8 Thomas Thrainer
        raise errors.ConfigurationError("No secondary node but using"
4716 22b7f6f8 Thomas Thrainer
                                        " %s disk template" %
4717 22b7f6f8 Thomas Thrainer
                                        instance.disk_template)
4718 22b7f6f8 Thomas Thrainer
      target_node = secondary_nodes[0]
4719 22b7f6f8 Thomas Thrainer
      if self.lu.op.iallocator or (self.lu.op.target_node and
4720 22b7f6f8 Thomas Thrainer
                                   self.lu.op.target_node != target_node):
4721 22b7f6f8 Thomas Thrainer
        if self.failover:
4722 22b7f6f8 Thomas Thrainer
          text = "failed over"
4723 22b7f6f8 Thomas Thrainer
        else:
4724 22b7f6f8 Thomas Thrainer
          text = "migrated"
4725 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instances with disk template %s cannot"
4726 22b7f6f8 Thomas Thrainer
                                   " be %s to arbitrary nodes"
4727 22b7f6f8 Thomas Thrainer
                                   " (neither an iallocator nor a target"
4728 22b7f6f8 Thomas Thrainer
                                   " node can be passed)" %
4729 22b7f6f8 Thomas Thrainer
                                   (instance.disk_template, text),
4730 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
4731 22b7f6f8 Thomas Thrainer
      nodeinfo = self.cfg.GetNodeInfo(target_node)
4732 22b7f6f8 Thomas Thrainer
      group_info = self.cfg.GetNodeGroup(nodeinfo.group)
4733 22b7f6f8 Thomas Thrainer
      ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
4734 22b7f6f8 Thomas Thrainer
                                                              group_info)
4735 22b7f6f8 Thomas Thrainer
      _CheckTargetNodeIPolicy(self.lu, ipolicy, instance, nodeinfo, self.cfg,
4736 22b7f6f8 Thomas Thrainer
                              ignore=self.ignore_ipolicy)
4737 22b7f6f8 Thomas Thrainer
4738 22b7f6f8 Thomas Thrainer
    i_be = cluster.FillBE(instance)
4739 22b7f6f8 Thomas Thrainer
4740 22b7f6f8 Thomas Thrainer
    # check memory requirements on the secondary node
4741 22b7f6f8 Thomas Thrainer
    if (not self.cleanup and
4742 22b7f6f8 Thomas Thrainer
         (not self.failover or instance.admin_state == constants.ADMINST_UP)):
4743 22b7f6f8 Thomas Thrainer
      self.tgt_free_mem = _CheckNodeFreeMemory(self.lu, target_node,
4744 22b7f6f8 Thomas Thrainer
                                               "migrating instance %s" %
4745 22b7f6f8 Thomas Thrainer
                                               instance.name,
4746 22b7f6f8 Thomas Thrainer
                                               i_be[constants.BE_MINMEM],
4747 22b7f6f8 Thomas Thrainer
                                               instance.hypervisor)
4748 22b7f6f8 Thomas Thrainer
    else:
4749 22b7f6f8 Thomas Thrainer
      self.lu.LogInfo("Not checking memory on the secondary node as"
4750 22b7f6f8 Thomas Thrainer
                      " instance will not be started")
4751 22b7f6f8 Thomas Thrainer
4752 22b7f6f8 Thomas Thrainer
    # check if failover must be forced instead of migration
4753 22b7f6f8 Thomas Thrainer
    if (not self.cleanup and not self.failover and
4754 22b7f6f8 Thomas Thrainer
        i_be[constants.BE_ALWAYS_FAILOVER]):
4755 22b7f6f8 Thomas Thrainer
      self.lu.LogInfo("Instance configured to always failover; fallback"
4756 22b7f6f8 Thomas Thrainer
                      " to failover")
4757 22b7f6f8 Thomas Thrainer
      self.failover = True
4758 22b7f6f8 Thomas Thrainer
4759 22b7f6f8 Thomas Thrainer
    # check bridge existance
4760 22b7f6f8 Thomas Thrainer
    _CheckInstanceBridgesExist(self.lu, instance, node=target_node)
4761 22b7f6f8 Thomas Thrainer
4762 22b7f6f8 Thomas Thrainer
    if not self.cleanup:
4763 22b7f6f8 Thomas Thrainer
      _CheckNodeNotDrained(self.lu, target_node)
4764 22b7f6f8 Thomas Thrainer
      if not self.failover:
4765 22b7f6f8 Thomas Thrainer
        result = self.rpc.call_instance_migratable(instance.primary_node,
4766 22b7f6f8 Thomas Thrainer
                                                   instance)
4767 22b7f6f8 Thomas Thrainer
        if result.fail_msg and self.fallback:
4768 22b7f6f8 Thomas Thrainer
          self.lu.LogInfo("Can't migrate, instance offline, fallback to"
4769 22b7f6f8 Thomas Thrainer
                          " failover")
4770 22b7f6f8 Thomas Thrainer
          self.failover = True
4771 22b7f6f8 Thomas Thrainer
        else:
4772 22b7f6f8 Thomas Thrainer
          result.Raise("Can't migrate, please use failover",
4773 22b7f6f8 Thomas Thrainer
                       prereq=True, ecode=errors.ECODE_STATE)
4774 22b7f6f8 Thomas Thrainer
4775 22b7f6f8 Thomas Thrainer
    assert not (self.failover and self.cleanup)
4776 22b7f6f8 Thomas Thrainer
4777 22b7f6f8 Thomas Thrainer
    if not self.failover:
4778 22b7f6f8 Thomas Thrainer
      if self.lu.op.live is not None and self.lu.op.mode is not None:
4779 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Only one of the 'live' and 'mode'"
4780 22b7f6f8 Thomas Thrainer
                                   " parameters are accepted",
4781 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
4782 22b7f6f8 Thomas Thrainer
      if self.lu.op.live is not None:
4783 22b7f6f8 Thomas Thrainer
        if self.lu.op.live:
4784 22b7f6f8 Thomas Thrainer
          self.lu.op.mode = constants.HT_MIGRATION_LIVE
4785 22b7f6f8 Thomas Thrainer
        else:
4786 22b7f6f8 Thomas Thrainer
          self.lu.op.mode = constants.HT_MIGRATION_NONLIVE
4787 22b7f6f8 Thomas Thrainer
        # reset the 'live' parameter to None so that repeated
4788 22b7f6f8 Thomas Thrainer
        # invocations of CheckPrereq do not raise an exception
4789 22b7f6f8 Thomas Thrainer
        self.lu.op.live = None
4790 22b7f6f8 Thomas Thrainer
      elif self.lu.op.mode is None:
4791 22b7f6f8 Thomas Thrainer
        # read the default value from the hypervisor
4792 22b7f6f8 Thomas Thrainer
        i_hv = cluster.FillHV(self.instance, skip_globals=False)
4793 22b7f6f8 Thomas Thrainer
        self.lu.op.mode = i_hv[constants.HV_MIGRATION_MODE]
4794 22b7f6f8 Thomas Thrainer
4795 22b7f6f8 Thomas Thrainer
      self.live = self.lu.op.mode == constants.HT_MIGRATION_LIVE
4796 22b7f6f8 Thomas Thrainer
    else:
4797 22b7f6f8 Thomas Thrainer
      # Failover is never live
4798 22b7f6f8 Thomas Thrainer
      self.live = False
4799 22b7f6f8 Thomas Thrainer
4800 22b7f6f8 Thomas Thrainer
    if not (self.failover or self.cleanup):
4801 22b7f6f8 Thomas Thrainer
      remote_info = self.rpc.call_instance_info(instance.primary_node,
4802 22b7f6f8 Thomas Thrainer
                                                instance.name,
4803 22b7f6f8 Thomas Thrainer
                                                instance.hypervisor)
4804 22b7f6f8 Thomas Thrainer
      remote_info.Raise("Error checking instance on node %s" %
4805 22b7f6f8 Thomas Thrainer
                        instance.primary_node)
4806 22b7f6f8 Thomas Thrainer
      instance_running = bool(remote_info.payload)
4807 22b7f6f8 Thomas Thrainer
      if instance_running:
4808 22b7f6f8 Thomas Thrainer
        self.current_mem = int(remote_info.payload["memory"])
4809 22b7f6f8 Thomas Thrainer
4810 22b7f6f8 Thomas Thrainer
  def _RunAllocator(self):
4811 22b7f6f8 Thomas Thrainer
    """Run the allocator based on input opcode.
4812 22b7f6f8 Thomas Thrainer

4813 22b7f6f8 Thomas Thrainer
    """
4814 22b7f6f8 Thomas Thrainer
    assert locking.NAL in self.lu.owned_locks(locking.LEVEL_NODE_ALLOC)
4815 22b7f6f8 Thomas Thrainer
4816 22b7f6f8 Thomas Thrainer
    # FIXME: add a self.ignore_ipolicy option
4817 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqRelocate(name=self.instance_name,
4818 22b7f6f8 Thomas Thrainer
                                   relocate_from=[self.instance.primary_node])
4819 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
4820 22b7f6f8 Thomas Thrainer
4821 22b7f6f8 Thomas Thrainer
    ial.Run(self.lu.op.iallocator)
4822 22b7f6f8 Thomas Thrainer
4823 22b7f6f8 Thomas Thrainer
    if not ial.success:
4824 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute nodes using"
4825 22b7f6f8 Thomas Thrainer
                                 " iallocator '%s': %s" %
4826 22b7f6f8 Thomas Thrainer
                                 (self.lu.op.iallocator, ial.info),
4827 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_NORES)
4828 22b7f6f8 Thomas Thrainer
    self.target_node = ial.result[0]
4829 22b7f6f8 Thomas Thrainer
    self.lu.LogInfo("Selected nodes for instance %s via iallocator %s: %s",
4830 22b7f6f8 Thomas Thrainer
                    self.instance_name, self.lu.op.iallocator,
4831 22b7f6f8 Thomas Thrainer
                    utils.CommaJoin(ial.result))
4832 22b7f6f8 Thomas Thrainer
4833 22b7f6f8 Thomas Thrainer
  def _WaitUntilSync(self):
4834 22b7f6f8 Thomas Thrainer
    """Poll with custom rpc for disk sync.
4835 22b7f6f8 Thomas Thrainer

4836 22b7f6f8 Thomas Thrainer
    This uses our own step-based rpc call.
4837 22b7f6f8 Thomas Thrainer

4838 22b7f6f8 Thomas Thrainer
    """
4839 22b7f6f8 Thomas Thrainer
    self.feedback_fn("* wait until resync is done")
4840 22b7f6f8 Thomas Thrainer
    all_done = False
4841 22b7f6f8 Thomas Thrainer
    while not all_done:
4842 22b7f6f8 Thomas Thrainer
      all_done = True
4843 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_drbd_wait_sync(self.all_nodes,
4844 22b7f6f8 Thomas Thrainer
                                            self.nodes_ip,
4845 22b7f6f8 Thomas Thrainer
                                            (self.instance.disks,
4846 22b7f6f8 Thomas Thrainer
                                             self.instance))
4847 22b7f6f8 Thomas Thrainer
      min_percent = 100
4848 22b7f6f8 Thomas Thrainer
      for node, nres in result.items():
4849 22b7f6f8 Thomas Thrainer
        nres.Raise("Cannot resync disks on node %s" % node)
4850 22b7f6f8 Thomas Thrainer
        node_done, node_percent = nres.payload
4851 22b7f6f8 Thomas Thrainer
        all_done = all_done and node_done
4852 22b7f6f8 Thomas Thrainer
        if node_percent is not None:
4853 22b7f6f8 Thomas Thrainer
          min_percent = min(min_percent, node_percent)
4854 22b7f6f8 Thomas Thrainer
      if not all_done:
4855 22b7f6f8 Thomas Thrainer
        if min_percent < 100:
4856 22b7f6f8 Thomas Thrainer
          self.feedback_fn("   - progress: %.1f%%" % min_percent)
4857 22b7f6f8 Thomas Thrainer
        time.sleep(2)
4858 22b7f6f8 Thomas Thrainer
4859 22b7f6f8 Thomas Thrainer
  def _EnsureSecondary(self, node):
4860 22b7f6f8 Thomas Thrainer
    """Demote a node to secondary.
4861 22b7f6f8 Thomas Thrainer

4862 22b7f6f8 Thomas Thrainer
    """
4863 22b7f6f8 Thomas Thrainer
    self.feedback_fn("* switching node %s to secondary mode" % node)
4864 22b7f6f8 Thomas Thrainer
4865 22b7f6f8 Thomas Thrainer
    for dev in self.instance.disks:
4866 22b7f6f8 Thomas Thrainer
      self.cfg.SetDiskID(dev, node)
4867 22b7f6f8 Thomas Thrainer
4868 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_blockdev_close(node, self.instance.name,
4869 22b7f6f8 Thomas Thrainer
                                          self.instance.disks)
4870 22b7f6f8 Thomas Thrainer
    result.Raise("Cannot change disk to secondary on node %s" % node)
4871 22b7f6f8 Thomas Thrainer
4872 22b7f6f8 Thomas Thrainer
  def _GoStandalone(self):
4873 22b7f6f8 Thomas Thrainer
    """Disconnect from the network.
4874 22b7f6f8 Thomas Thrainer

4875 22b7f6f8 Thomas Thrainer
    """
4876 22b7f6f8 Thomas Thrainer
    self.feedback_fn("* changing into standalone mode")
4877 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_drbd_disconnect_net(self.all_nodes, self.nodes_ip,
4878 22b7f6f8 Thomas Thrainer
                                               self.instance.disks)
4879 22b7f6f8 Thomas Thrainer
    for node, nres in result.items():
4880 22b7f6f8 Thomas Thrainer
      nres.Raise("Cannot disconnect disks node %s" % node)
4881 22b7f6f8 Thomas Thrainer
4882 22b7f6f8 Thomas Thrainer
  def _GoReconnect(self, multimaster):
4883 22b7f6f8 Thomas Thrainer
    """Reconnect to the network.
4884 22b7f6f8 Thomas Thrainer

4885 22b7f6f8 Thomas Thrainer
    """
4886 22b7f6f8 Thomas Thrainer
    if multimaster:
4887 22b7f6f8 Thomas Thrainer
      msg = "dual-master"
4888 22b7f6f8 Thomas Thrainer
    else:
4889 22b7f6f8 Thomas Thrainer
      msg = "single-master"
4890 22b7f6f8 Thomas Thrainer
    self.feedback_fn("* changing disks into %s mode" % msg)
4891 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_drbd_attach_net(self.all_nodes, self.nodes_ip,
4892 22b7f6f8 Thomas Thrainer
                                           (self.instance.disks, self.instance),
4893 22b7f6f8 Thomas Thrainer
                                           self.instance.name, multimaster)
4894 22b7f6f8 Thomas Thrainer
    for node, nres in result.items():
4895 22b7f6f8 Thomas Thrainer
      nres.Raise("Cannot change disks config on node %s" % node)
4896 22b7f6f8 Thomas Thrainer
4897 22b7f6f8 Thomas Thrainer
  def _ExecCleanup(self):
4898 22b7f6f8 Thomas Thrainer
    """Try to cleanup after a failed migration.
4899 22b7f6f8 Thomas Thrainer

4900 22b7f6f8 Thomas Thrainer
    The cleanup is done by:
4901 22b7f6f8 Thomas Thrainer
      - check that the instance is running only on one node
4902 22b7f6f8 Thomas Thrainer
        (and update the config if needed)
4903 22b7f6f8 Thomas Thrainer
      - change disks on its secondary node to secondary
4904 22b7f6f8 Thomas Thrainer
      - wait until disks are fully synchronized
4905 22b7f6f8 Thomas Thrainer
      - disconnect from the network
4906 22b7f6f8 Thomas Thrainer
      - change disks into single-master mode
4907 22b7f6f8 Thomas Thrainer
      - wait again until disks are fully synchronized
4908 22b7f6f8 Thomas Thrainer

4909 22b7f6f8 Thomas Thrainer
    """
4910 22b7f6f8 Thomas Thrainer
    instance = self.instance
4911 22b7f6f8 Thomas Thrainer
    target_node = self.target_node
4912 22b7f6f8 Thomas Thrainer
    source_node = self.source_node
4913 22b7f6f8 Thomas Thrainer
4914 22b7f6f8 Thomas Thrainer
    # check running on only one node
4915 22b7f6f8 Thomas Thrainer
    self.feedback_fn("* checking where the instance actually runs"
4916 22b7f6f8 Thomas Thrainer
                     " (if this hangs, the hypervisor might be in"
4917 22b7f6f8 Thomas Thrainer
                     " a bad state)")
4918 22b7f6f8 Thomas Thrainer
    ins_l = self.rpc.call_instance_list(self.all_nodes, [instance.hypervisor])
4919 22b7f6f8 Thomas Thrainer
    for node, result in ins_l.items():
4920 22b7f6f8 Thomas Thrainer
      result.Raise("Can't contact node %s" % node)
4921 22b7f6f8 Thomas Thrainer
4922 22b7f6f8 Thomas Thrainer
    runningon_source = instance.name in ins_l[source_node].payload
4923 22b7f6f8 Thomas Thrainer
    runningon_target = instance.name in ins_l[target_node].payload
4924 22b7f6f8 Thomas Thrainer
4925 22b7f6f8 Thomas Thrainer
    if runningon_source and runningon_target:
4926 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("Instance seems to be running on two nodes,"
4927 22b7f6f8 Thomas Thrainer
                               " or the hypervisor is confused; you will have"
4928 22b7f6f8 Thomas Thrainer
                               " to ensure manually that it runs only on one"
4929 22b7f6f8 Thomas Thrainer
                               " and restart this operation")
4930 22b7f6f8 Thomas Thrainer
4931 22b7f6f8 Thomas Thrainer
    if not (runningon_source or runningon_target):
4932 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("Instance does not seem to be running at all;"
4933 22b7f6f8 Thomas Thrainer
                               " in this case it's safer to repair by"
4934 22b7f6f8 Thomas Thrainer
                               " running 'gnt-instance stop' to ensure disk"
4935 22b7f6f8 Thomas Thrainer
                               " shutdown, and then restarting it")
4936 22b7f6f8 Thomas Thrainer
4937 22b7f6f8 Thomas Thrainer
    if runningon_target:
4938 22b7f6f8 Thomas Thrainer
      # the migration has actually succeeded, we need to update the config
4939 22b7f6f8 Thomas Thrainer
      self.feedback_fn("* instance running on secondary node (%s),"
4940 22b7f6f8 Thomas Thrainer
                       " updating config" % target_node)
4941 22b7f6f8 Thomas Thrainer
      instance.primary_node = target_node
4942 22b7f6f8 Thomas Thrainer
      self.cfg.Update(instance, self.feedback_fn)
4943 22b7f6f8 Thomas Thrainer
      demoted_node = source_node
4944 22b7f6f8 Thomas Thrainer
    else:
4945 22b7f6f8 Thomas Thrainer
      self.feedback_fn("* instance confirmed to be running on its"
4946 22b7f6f8 Thomas Thrainer
                       " primary node (%s)" % source_node)
4947 22b7f6f8 Thomas Thrainer
      demoted_node = target_node
4948 22b7f6f8 Thomas Thrainer
4949 22b7f6f8 Thomas Thrainer
    if instance.disk_template in constants.DTS_INT_MIRROR:
4950 22b7f6f8 Thomas Thrainer
      self._EnsureSecondary(demoted_node)
4951 22b7f6f8 Thomas Thrainer
      try:
4952 22b7f6f8 Thomas Thrainer
        self._WaitUntilSync()
4953 22b7f6f8 Thomas Thrainer
      except errors.OpExecError:
4954 22b7f6f8 Thomas Thrainer
        # we ignore here errors, since if the device is standalone, it
4955 22b7f6f8 Thomas Thrainer
        # won't be able to sync
4956 22b7f6f8 Thomas Thrainer
        pass
4957 22b7f6f8 Thomas Thrainer
      self._GoStandalone()
4958 22b7f6f8 Thomas Thrainer
      self._GoReconnect(False)
4959 22b7f6f8 Thomas Thrainer
      self._WaitUntilSync()
4960 22b7f6f8 Thomas Thrainer
4961 22b7f6f8 Thomas Thrainer
    self.feedback_fn("* done")
4962 22b7f6f8 Thomas Thrainer
4963 22b7f6f8 Thomas Thrainer
  def _RevertDiskStatus(self):
4964 22b7f6f8 Thomas Thrainer
    """Try to revert the disk status after a failed migration.
4965 22b7f6f8 Thomas Thrainer

4966 22b7f6f8 Thomas Thrainer
    """
4967 22b7f6f8 Thomas Thrainer
    target_node = self.target_node
4968 22b7f6f8 Thomas Thrainer
    if self.instance.disk_template in constants.DTS_EXT_MIRROR:
4969 22b7f6f8 Thomas Thrainer
      return
4970 22b7f6f8 Thomas Thrainer
4971 22b7f6f8 Thomas Thrainer
    try:
4972 22b7f6f8 Thomas Thrainer
      self._EnsureSecondary(target_node)
4973 22b7f6f8 Thomas Thrainer
      self._GoStandalone()
4974 22b7f6f8 Thomas Thrainer
      self._GoReconnect(False)
4975 22b7f6f8 Thomas Thrainer
      self._WaitUntilSync()
4976 22b7f6f8 Thomas Thrainer
    except errors.OpExecError, err:
4977 22b7f6f8 Thomas Thrainer
      self.lu.LogWarning("Migration failed and I can't reconnect the drives,"
4978 22b7f6f8 Thomas Thrainer
                         " please try to recover the instance manually;"
4979 22b7f6f8 Thomas Thrainer
                         " error '%s'" % str(err))
4980 22b7f6f8 Thomas Thrainer
4981 22b7f6f8 Thomas Thrainer
  def _AbortMigration(self):
4982 22b7f6f8 Thomas Thrainer
    """Call the hypervisor code to abort a started migration.
4983 22b7f6f8 Thomas Thrainer

4984 22b7f6f8 Thomas Thrainer
    """
4985 22b7f6f8 Thomas Thrainer
    instance = self.instance
4986 22b7f6f8 Thomas Thrainer
    target_node = self.target_node
4987 22b7f6f8 Thomas Thrainer
    source_node = self.source_node
4988 22b7f6f8 Thomas Thrainer
    migration_info = self.migration_info
4989 22b7f6f8 Thomas Thrainer
4990 22b7f6f8 Thomas Thrainer
    abort_result = self.rpc.call_instance_finalize_migration_dst(target_node,
4991 22b7f6f8 Thomas Thrainer
                                                                 instance,
4992 22b7f6f8 Thomas Thrainer
                                                                 migration_info,
4993 22b7f6f8 Thomas Thrainer
                                                                 False)
4994 22b7f6f8 Thomas Thrainer
    abort_msg = abort_result.fail_msg
4995 22b7f6f8 Thomas Thrainer
    if abort_msg:
4996 22b7f6f8 Thomas Thrainer
      logging.error("Aborting migration failed on target node %s: %s",
4997 22b7f6f8 Thomas Thrainer
                    target_node, abort_msg)
4998 22b7f6f8 Thomas Thrainer
      # Don't raise an exception here, as we stil have to try to revert the
4999 22b7f6f8 Thomas Thrainer
      # disk status, even if this step failed.
5000 22b7f6f8 Thomas Thrainer
5001 22b7f6f8 Thomas Thrainer
    abort_result = self.rpc.call_instance_finalize_migration_src(
5002 22b7f6f8 Thomas Thrainer
      source_node, instance, False, self.live)
5003 22b7f6f8 Thomas Thrainer
    abort_msg = abort_result.fail_msg
5004 22b7f6f8 Thomas Thrainer
    if abort_msg:
5005 22b7f6f8 Thomas Thrainer
      logging.error("Aborting migration failed on source node %s: %s",
5006 22b7f6f8 Thomas Thrainer
                    source_node, abort_msg)
5007 22b7f6f8 Thomas Thrainer
5008 22b7f6f8 Thomas Thrainer
  def _ExecMigration(self):
5009 22b7f6f8 Thomas Thrainer
    """Migrate an instance.
5010 22b7f6f8 Thomas Thrainer

5011 22b7f6f8 Thomas Thrainer
    The migrate is done by:
5012 22b7f6f8 Thomas Thrainer
      - change the disks into dual-master mode
5013 22b7f6f8 Thomas Thrainer
      - wait until disks are fully synchronized again
5014 22b7f6f8 Thomas Thrainer
      - migrate the instance
5015 22b7f6f8 Thomas Thrainer
      - change disks on the new secondary node (the old primary) to secondary
5016 22b7f6f8 Thomas Thrainer
      - wait until disks are fully synchronized
5017 22b7f6f8 Thomas Thrainer
      - change disks into single-master mode
5018 22b7f6f8 Thomas Thrainer

5019 22b7f6f8 Thomas Thrainer
    """
5020 22b7f6f8 Thomas Thrainer
    instance = self.instance
5021 22b7f6f8 Thomas Thrainer
    target_node = self.target_node
5022 22b7f6f8 Thomas Thrainer
    source_node = self.source_node
5023 22b7f6f8 Thomas Thrainer
5024 22b7f6f8 Thomas Thrainer
    # Check for hypervisor version mismatch and warn the user.
5025 22b7f6f8 Thomas Thrainer
    nodeinfo = self.rpc.call_node_info([source_node, target_node],
5026 22b7f6f8 Thomas Thrainer
                                       None, [self.instance.hypervisor], False)
5027 22b7f6f8 Thomas Thrainer
    for ninfo in nodeinfo.values():
5028 22b7f6f8 Thomas Thrainer
      ninfo.Raise("Unable to retrieve node information from node '%s'" %
5029 22b7f6f8 Thomas Thrainer
                  ninfo.node)
5030 22b7f6f8 Thomas Thrainer
    (_, _, (src_info, )) = nodeinfo[source_node].payload
5031 22b7f6f8 Thomas Thrainer
    (_, _, (dst_info, )) = nodeinfo[target_node].payload
5032 22b7f6f8 Thomas Thrainer
5033 22b7f6f8 Thomas Thrainer
    if ((constants.HV_NODEINFO_KEY_VERSION in src_info) and
5034 22b7f6f8 Thomas Thrainer
        (constants.HV_NODEINFO_KEY_VERSION in dst_info)):
5035 22b7f6f8 Thomas Thrainer
      src_version = src_info[constants.HV_NODEINFO_KEY_VERSION]
5036 22b7f6f8 Thomas Thrainer
      dst_version = dst_info[constants.HV_NODEINFO_KEY_VERSION]
5037 22b7f6f8 Thomas Thrainer
      if src_version != dst_version:
5038 22b7f6f8 Thomas Thrainer
        self.feedback_fn("* warning: hypervisor version mismatch between"
5039 22b7f6f8 Thomas Thrainer
                         " source (%s) and target (%s) node" %
5040 22b7f6f8 Thomas Thrainer
                         (src_version, dst_version))
5041 22b7f6f8 Thomas Thrainer
5042 22b7f6f8 Thomas Thrainer
    self.feedback_fn("* checking disk consistency between source and target")
5043 22b7f6f8 Thomas Thrainer
    for (idx, dev) in enumerate(instance.disks):
5044 22b7f6f8 Thomas Thrainer
      if not _CheckDiskConsistency(self.lu, instance, dev, target_node, False):
5045 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Disk %s is degraded or not fully"
5046 22b7f6f8 Thomas Thrainer
                                 " synchronized on target node,"
5047 22b7f6f8 Thomas Thrainer
                                 " aborting migration" % idx)
5048 22b7f6f8 Thomas Thrainer
5049 22b7f6f8 Thomas Thrainer
    if self.current_mem > self.tgt_free_mem:
5050 22b7f6f8 Thomas Thrainer
      if not self.allow_runtime_changes:
5051 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Memory ballooning not allowed and not enough"
5052 22b7f6f8 Thomas Thrainer
                                 " free memory to fit instance %s on target"
5053 22b7f6f8 Thomas Thrainer
                                 " node %s (have %dMB, need %dMB)" %
5054 22b7f6f8 Thomas Thrainer
                                 (instance.name, target_node,
5055 22b7f6f8 Thomas Thrainer
                                  self.tgt_free_mem, self.current_mem))
5056 22b7f6f8 Thomas Thrainer
      self.feedback_fn("* setting instance memory to %s" % self.tgt_free_mem)
5057 22b7f6f8 Thomas Thrainer
      rpcres = self.rpc.call_instance_balloon_memory(instance.primary_node,
5058 22b7f6f8 Thomas Thrainer
                                                     instance,
5059 22b7f6f8 Thomas Thrainer
                                                     self.tgt_free_mem)
5060 22b7f6f8 Thomas Thrainer
      rpcres.Raise("Cannot modify instance runtime memory")
5061 22b7f6f8 Thomas Thrainer
5062 22b7f6f8 Thomas Thrainer
    # First get the migration information from the remote node
5063 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_migration_info(source_node, instance)
5064 22b7f6f8 Thomas Thrainer
    msg = result.fail_msg
5065 22b7f6f8 Thomas Thrainer
    if msg:
5066 22b7f6f8 Thomas Thrainer
      log_err = ("Failed fetching source migration information from %s: %s" %
5067 22b7f6f8 Thomas Thrainer
                 (source_node, msg))
5068 22b7f6f8 Thomas Thrainer
      logging.error(log_err)
5069 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError(log_err)
5070 22b7f6f8 Thomas Thrainer
5071 22b7f6f8 Thomas Thrainer
    self.migration_info = migration_info = result.payload
5072 22b7f6f8 Thomas Thrainer
5073 22b7f6f8 Thomas Thrainer
    if self.instance.disk_template not in constants.DTS_EXT_MIRROR:
5074 22b7f6f8 Thomas Thrainer
      # Then switch the disks to master/master mode
5075 22b7f6f8 Thomas Thrainer
      self._EnsureSecondary(target_node)
5076 22b7f6f8 Thomas Thrainer
      self._GoStandalone()
5077 22b7f6f8 Thomas Thrainer
      self._GoReconnect(True)
5078 22b7f6f8 Thomas Thrainer
      self._WaitUntilSync()
5079 22b7f6f8 Thomas Thrainer
5080 22b7f6f8 Thomas Thrainer
    self.feedback_fn("* preparing %s to accept the instance" % target_node)
5081 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_accept_instance(target_node,
5082 22b7f6f8 Thomas Thrainer
                                           instance,
5083 22b7f6f8 Thomas Thrainer
                                           migration_info,
5084 22b7f6f8 Thomas Thrainer
                                           self.nodes_ip[target_node])
5085 22b7f6f8 Thomas Thrainer
5086 22b7f6f8 Thomas Thrainer
    msg = result.fail_msg
5087 22b7f6f8 Thomas Thrainer
    if msg:
5088 22b7f6f8 Thomas Thrainer
      logging.error("Instance pre-migration failed, trying to revert"
5089 22b7f6f8 Thomas Thrainer
                    " disk status: %s", msg)
5090 22b7f6f8 Thomas Thrainer
      self.feedback_fn("Pre-migration failed, aborting")
5091 22b7f6f8 Thomas Thrainer
      self._AbortMigration()
5092 22b7f6f8 Thomas Thrainer
      self._RevertDiskStatus()
5093 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("Could not pre-migrate instance %s: %s" %
5094 22b7f6f8 Thomas Thrainer
                               (instance.name, msg))
5095 22b7f6f8 Thomas Thrainer
5096 22b7f6f8 Thomas Thrainer
    self.feedback_fn("* migrating instance to %s" % target_node)
5097 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_instance_migrate(source_node, instance,
5098 22b7f6f8 Thomas Thrainer
                                            self.nodes_ip[target_node],
5099 22b7f6f8 Thomas Thrainer
                                            self.live)
5100 22b7f6f8 Thomas Thrainer
    msg = result.fail_msg
5101 22b7f6f8 Thomas Thrainer
    if msg:
5102 22b7f6f8 Thomas Thrainer
      logging.error("Instance migration failed, trying to revert"
5103 22b7f6f8 Thomas Thrainer
                    " disk status: %s", msg)
5104 22b7f6f8 Thomas Thrainer
      self.feedback_fn("Migration failed, aborting")
5105 22b7f6f8 Thomas Thrainer
      self._AbortMigration()
5106 22b7f6f8 Thomas Thrainer
      self._RevertDiskStatus()
5107 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("Could not migrate instance %s: %s" %
5108 22b7f6f8 Thomas Thrainer
                               (instance.name, msg))
5109 22b7f6f8 Thomas Thrainer
5110 22b7f6f8 Thomas Thrainer
    self.feedback_fn("* starting memory transfer")
5111 22b7f6f8 Thomas Thrainer
    last_feedback = time.time()
5112 22b7f6f8 Thomas Thrainer
    while True:
5113 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_get_migration_status(source_node,
5114 22b7f6f8 Thomas Thrainer
                                                           instance)
5115 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
5116 22b7f6f8 Thomas Thrainer
      ms = result.payload   # MigrationStatus instance
5117 22b7f6f8 Thomas Thrainer
      if msg or (ms.status in constants.HV_MIGRATION_FAILED_STATUSES):
5118 22b7f6f8 Thomas Thrainer
        logging.error("Instance migration failed, trying to revert"
5119 22b7f6f8 Thomas Thrainer
                      " disk status: %s", msg)
5120 22b7f6f8 Thomas Thrainer
        self.feedback_fn("Migration failed, aborting")
5121 22b7f6f8 Thomas Thrainer
        self._AbortMigration()
5122 22b7f6f8 Thomas Thrainer
        self._RevertDiskStatus()
5123 22b7f6f8 Thomas Thrainer
        if not msg:
5124 22b7f6f8 Thomas Thrainer
          msg = "hypervisor returned failure"
5125 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not migrate instance %s: %s" %
5126 22b7f6f8 Thomas Thrainer
                                 (instance.name, msg))
5127 22b7f6f8 Thomas Thrainer
5128 22b7f6f8 Thomas Thrainer
      if result.payload.status != constants.HV_MIGRATION_ACTIVE:
5129 22b7f6f8 Thomas Thrainer
        self.feedback_fn("* memory transfer complete")
5130 22b7f6f8 Thomas Thrainer
        break
5131 22b7f6f8 Thomas Thrainer
5132 22b7f6f8 Thomas Thrainer
      if (utils.TimeoutExpired(last_feedback,
5133 22b7f6f8 Thomas Thrainer
                               self._MIGRATION_FEEDBACK_INTERVAL) and
5134 22b7f6f8 Thomas Thrainer
          ms.transferred_ram is not None):
5135 22b7f6f8 Thomas Thrainer
        mem_progress = 100 * float(ms.transferred_ram) / float(ms.total_ram)
5136 22b7f6f8 Thomas Thrainer
        self.feedback_fn("* memory transfer progress: %.2f %%" % mem_progress)
5137 22b7f6f8 Thomas Thrainer
        last_feedback = time.time()
5138 22b7f6f8 Thomas Thrainer
5139 22b7f6f8 Thomas Thrainer
      time.sleep(self._MIGRATION_POLL_INTERVAL)
5140 22b7f6f8 Thomas Thrainer
5141 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_instance_finalize_migration_src(source_node,
5142 22b7f6f8 Thomas Thrainer
                                                           instance,
5143 22b7f6f8 Thomas Thrainer
                                                           True,
5144 22b7f6f8 Thomas Thrainer
                                                           self.live)
5145 22b7f6f8 Thomas Thrainer
    msg = result.fail_msg
5146 22b7f6f8 Thomas Thrainer
    if msg:
5147 22b7f6f8 Thomas Thrainer
      logging.error("Instance migration succeeded, but finalization failed"
5148 22b7f6f8 Thomas Thrainer
                    " on the source node: %s", msg)
5149 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("Could not finalize instance migration: %s" %
5150 22b7f6f8 Thomas Thrainer
                               msg)
5151 22b7f6f8 Thomas Thrainer
5152 22b7f6f8 Thomas Thrainer
    instance.primary_node = target_node
5153 22b7f6f8 Thomas Thrainer
5154 22b7f6f8 Thomas Thrainer
    # distribute new instance config to the other nodes
5155 22b7f6f8 Thomas Thrainer
    self.cfg.Update(instance, self.feedback_fn)
5156 22b7f6f8 Thomas Thrainer
5157 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_instance_finalize_migration_dst(target_node,
5158 22b7f6f8 Thomas Thrainer
                                                           instance,
5159 22b7f6f8 Thomas Thrainer
                                                           migration_info,
5160 22b7f6f8 Thomas Thrainer
                                                           True)
5161 22b7f6f8 Thomas Thrainer
    msg = result.fail_msg
5162 22b7f6f8 Thomas Thrainer
    if msg:
5163 22b7f6f8 Thomas Thrainer
      logging.error("Instance migration succeeded, but finalization failed"
5164 22b7f6f8 Thomas Thrainer
                    " on the target node: %s", msg)
5165 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("Could not finalize instance migration: %s" %
5166 22b7f6f8 Thomas Thrainer
                               msg)
5167 22b7f6f8 Thomas Thrainer
5168 22b7f6f8 Thomas Thrainer
    if self.instance.disk_template not in constants.DTS_EXT_MIRROR:
5169 22b7f6f8 Thomas Thrainer
      self._EnsureSecondary(source_node)
5170 22b7f6f8 Thomas Thrainer
      self._WaitUntilSync()
5171 22b7f6f8 Thomas Thrainer
      self._GoStandalone()
5172 22b7f6f8 Thomas Thrainer
      self._GoReconnect(False)
5173 22b7f6f8 Thomas Thrainer
      self._WaitUntilSync()
5174 22b7f6f8 Thomas Thrainer
5175 22b7f6f8 Thomas Thrainer
    # If the instance's disk template is `rbd' or `ext' and there was a
5176 22b7f6f8 Thomas Thrainer
    # successful migration, unmap the device from the source node.
5177 22b7f6f8 Thomas Thrainer
    if self.instance.disk_template in (constants.DT_RBD, constants.DT_EXT):
5178 22b7f6f8 Thomas Thrainer
      disks = _ExpandCheckDisks(instance, instance.disks)
5179 22b7f6f8 Thomas Thrainer
      self.feedback_fn("* unmapping instance's disks from %s" % source_node)
5180 22b7f6f8 Thomas Thrainer
      for disk in disks:
5181 22b7f6f8 Thomas Thrainer
        result = self.rpc.call_blockdev_shutdown(source_node, (disk, instance))
5182 22b7f6f8 Thomas Thrainer
        msg = result.fail_msg
5183 22b7f6f8 Thomas Thrainer
        if msg:
5184 22b7f6f8 Thomas Thrainer
          logging.error("Migration was successful, but couldn't unmap the"
5185 22b7f6f8 Thomas Thrainer
                        " block device %s on source node %s: %s",
5186 22b7f6f8 Thomas Thrainer
                        disk.iv_name, source_node, msg)
5187 22b7f6f8 Thomas Thrainer
          logging.error("You need to unmap the device %s manually on %s",
5188 22b7f6f8 Thomas Thrainer
                        disk.iv_name, source_node)
5189 22b7f6f8 Thomas Thrainer
5190 22b7f6f8 Thomas Thrainer
    self.feedback_fn("* done")
5191 22b7f6f8 Thomas Thrainer
5192 22b7f6f8 Thomas Thrainer
  def _ExecFailover(self):
5193 22b7f6f8 Thomas Thrainer
    """Failover an instance.
5194 22b7f6f8 Thomas Thrainer

5195 22b7f6f8 Thomas Thrainer
    The failover is done by shutting it down on its present node and
5196 22b7f6f8 Thomas Thrainer
    starting it on the secondary.
5197 22b7f6f8 Thomas Thrainer

5198 22b7f6f8 Thomas Thrainer
    """
5199 22b7f6f8 Thomas Thrainer
    instance = self.instance
5200 22b7f6f8 Thomas Thrainer
    primary_node = self.cfg.GetNodeInfo(instance.primary_node)
5201 22b7f6f8 Thomas Thrainer
5202 22b7f6f8 Thomas Thrainer
    source_node = instance.primary_node
5203 22b7f6f8 Thomas Thrainer
    target_node = self.target_node
5204 22b7f6f8 Thomas Thrainer
5205 22b7f6f8 Thomas Thrainer
    if instance.admin_state == constants.ADMINST_UP:
5206 22b7f6f8 Thomas Thrainer
      self.feedback_fn("* checking disk consistency between source and target")
5207 22b7f6f8 Thomas Thrainer
      for (idx, dev) in enumerate(instance.disks):
5208 22b7f6f8 Thomas Thrainer
        # for drbd, these are drbd over lvm
5209 22b7f6f8 Thomas Thrainer
        if not _CheckDiskConsistency(self.lu, instance, dev, target_node,
5210 22b7f6f8 Thomas Thrainer
                                     False):
5211 22b7f6f8 Thomas Thrainer
          if primary_node.offline:
5212 22b7f6f8 Thomas Thrainer
            self.feedback_fn("Node %s is offline, ignoring degraded disk %s on"
5213 22b7f6f8 Thomas Thrainer
                             " target node %s" %
5214 22b7f6f8 Thomas Thrainer
                             (primary_node.name, idx, target_node))
5215 22b7f6f8 Thomas Thrainer
          elif not self.ignore_consistency:
5216 22b7f6f8 Thomas Thrainer
            raise errors.OpExecError("Disk %s is degraded on target node,"
5217 22b7f6f8 Thomas Thrainer
                                     " aborting failover" % idx)
5218 22b7f6f8 Thomas Thrainer
    else:
5219 22b7f6f8 Thomas Thrainer
      self.feedback_fn("* not checking disk consistency as instance is not"
5220 22b7f6f8 Thomas Thrainer
                       " running")
5221 22b7f6f8 Thomas Thrainer
5222 22b7f6f8 Thomas Thrainer
    self.feedback_fn("* shutting down instance on source node")
5223 22b7f6f8 Thomas Thrainer
    logging.info("Shutting down instance %s on node %s",
5224 22b7f6f8 Thomas Thrainer
                 instance.name, source_node)
5225 22b7f6f8 Thomas Thrainer
5226 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_instance_shutdown(source_node, instance,
5227 22b7f6f8 Thomas Thrainer
                                             self.shutdown_timeout,
5228 22b7f6f8 Thomas Thrainer
                                             self.lu.op.reason)
5229 22b7f6f8 Thomas Thrainer
    msg = result.fail_msg
5230 22b7f6f8 Thomas Thrainer
    if msg:
5231 22b7f6f8 Thomas Thrainer
      if self.ignore_consistency or primary_node.offline:
5232 22b7f6f8 Thomas Thrainer
        self.lu.LogWarning("Could not shutdown instance %s on node %s,"
5233 22b7f6f8 Thomas Thrainer
                           " proceeding anyway; please make sure node"
5234 22b7f6f8 Thomas Thrainer
                           " %s is down; error details: %s",
5235 22b7f6f8 Thomas Thrainer
                           instance.name, source_node, source_node, msg)
5236 22b7f6f8 Thomas Thrainer
      else:
5237 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not shutdown instance %s on"
5238 22b7f6f8 Thomas Thrainer
                                 " node %s: %s" %
5239 22b7f6f8 Thomas Thrainer
                                 (instance.name, source_node, msg))
5240 22b7f6f8 Thomas Thrainer
5241 22b7f6f8 Thomas Thrainer
    self.feedback_fn("* deactivating the instance's disks on source node")
5242 22b7f6f8 Thomas Thrainer
    if not _ShutdownInstanceDisks(self.lu, instance, ignore_primary=True):
5243 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("Can't shut down the instance's disks")
5244 22b7f6f8 Thomas Thrainer
5245 22b7f6f8 Thomas Thrainer
    instance.primary_node = target_node
5246 22b7f6f8 Thomas Thrainer
    # distribute new instance config to the other nodes
5247 22b7f6f8 Thomas Thrainer
    self.cfg.Update(instance, self.feedback_fn)
5248 22b7f6f8 Thomas Thrainer
5249 22b7f6f8 Thomas Thrainer
    # Only start the instance if it's marked as up
5250 22b7f6f8 Thomas Thrainer
    if instance.admin_state == constants.ADMINST_UP:
5251 22b7f6f8 Thomas Thrainer
      self.feedback_fn("* activating the instance's disks on target node %s" %
5252 22b7f6f8 Thomas Thrainer
                       target_node)
5253 22b7f6f8 Thomas Thrainer
      logging.info("Starting instance %s on node %s",
5254 22b7f6f8 Thomas Thrainer
                   instance.name, target_node)
5255 22b7f6f8 Thomas Thrainer
5256 22b7f6f8 Thomas Thrainer
      disks_ok, _ = _AssembleInstanceDisks(self.lu, instance,
5257 22b7f6f8 Thomas Thrainer
                                           ignore_secondaries=True)
5258 22b7f6f8 Thomas Thrainer
      if not disks_ok:
5259 22b7f6f8 Thomas Thrainer
        _ShutdownInstanceDisks(self.lu, instance)
5260 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Can't activate the instance's disks")
5261 22b7f6f8 Thomas Thrainer
5262 22b7f6f8 Thomas Thrainer
      self.feedback_fn("* starting the instance on the target node %s" %
5263 22b7f6f8 Thomas Thrainer
                       target_node)
5264 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_start(target_node, (instance, None, None),
5265 22b7f6f8 Thomas Thrainer
                                            False, self.lu.op.reason)
5266 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
5267 22b7f6f8 Thomas Thrainer
      if msg:
5268 22b7f6f8 Thomas Thrainer
        _ShutdownInstanceDisks(self.lu, instance)
5269 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not start instance %s on node %s: %s" %
5270 22b7f6f8 Thomas Thrainer
                                 (instance.name, target_node, msg))
5271 22b7f6f8 Thomas Thrainer
5272 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
5273 22b7f6f8 Thomas Thrainer
    """Perform the migration.
5274 22b7f6f8 Thomas Thrainer

5275 22b7f6f8 Thomas Thrainer
    """
5276 22b7f6f8 Thomas Thrainer
    self.feedback_fn = feedback_fn
5277 22b7f6f8 Thomas Thrainer
    self.source_node = self.instance.primary_node
5278 22b7f6f8 Thomas Thrainer
5279 22b7f6f8 Thomas Thrainer
    # FIXME: if we implement migrate-to-any in DRBD, this needs fixing
5280 22b7f6f8 Thomas Thrainer
    if self.instance.disk_template in constants.DTS_INT_MIRROR:
5281 22b7f6f8 Thomas Thrainer
      self.target_node = self.instance.secondary_nodes[0]
5282 22b7f6f8 Thomas Thrainer
      # Otherwise self.target_node has been populated either
5283 22b7f6f8 Thomas Thrainer
      # directly, or through an iallocator.
5284 22b7f6f8 Thomas Thrainer
5285 22b7f6f8 Thomas Thrainer
    self.all_nodes = [self.source_node, self.target_node]
5286 22b7f6f8 Thomas Thrainer
    self.nodes_ip = dict((name, node.secondary_ip) for (name, node)
5287 22b7f6f8 Thomas Thrainer
                         in self.cfg.GetMultiNodeInfo(self.all_nodes))
5288 22b7f6f8 Thomas Thrainer
5289 22b7f6f8 Thomas Thrainer
    if self.failover:
5290 22b7f6f8 Thomas Thrainer
      feedback_fn("Failover instance %s" % self.instance.name)
5291 22b7f6f8 Thomas Thrainer
      self._ExecFailover()
5292 22b7f6f8 Thomas Thrainer
    else:
5293 22b7f6f8 Thomas Thrainer
      feedback_fn("Migrating instance %s" % self.instance.name)
5294 22b7f6f8 Thomas Thrainer
5295 22b7f6f8 Thomas Thrainer
      if self.cleanup:
5296 22b7f6f8 Thomas Thrainer
        return self._ExecCleanup()
5297 22b7f6f8 Thomas Thrainer
      else:
5298 22b7f6f8 Thomas Thrainer
        return self._ExecMigration()