Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance.py @ e8dd6643

History | View | Annotate | Download (156.8 kB)

1 22b7f6f8 Thomas Thrainer
#
2 22b7f6f8 Thomas Thrainer
#
3 22b7f6f8 Thomas Thrainer
4 81c222af Jose A. Lopes
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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 logging
27 22b7f6f8 Thomas Thrainer
import os
28 22b7f6f8 Thomas Thrainer
29 22b7f6f8 Thomas Thrainer
from ganeti import compat
30 22b7f6f8 Thomas Thrainer
from ganeti import constants
31 22b7f6f8 Thomas Thrainer
from ganeti import errors
32 22b7f6f8 Thomas Thrainer
from ganeti import ht
33 22b7f6f8 Thomas Thrainer
from ganeti import hypervisor
34 22b7f6f8 Thomas Thrainer
from ganeti import locking
35 22b7f6f8 Thomas Thrainer
from ganeti.masterd import iallocator
36 22b7f6f8 Thomas Thrainer
from ganeti import masterd
37 22b7f6f8 Thomas Thrainer
from ganeti import netutils
38 22b7f6f8 Thomas Thrainer
from ganeti import objects
39 22b7f6f8 Thomas Thrainer
from ganeti import pathutils
40 6bce7ba2 Santi Raffa
from ganeti import serializer
41 4869595d Petr Pudlak
import ganeti.rpc.node as rpc
42 22b7f6f8 Thomas Thrainer
from ganeti import utils
43 22b7f6f8 Thomas Thrainer
44 8aa8f6b1 Thomas Thrainer
from ganeti.cmdlib.base import NoHooksLU, LogicalUnit, ResultWithJobs
45 22b7f6f8 Thomas Thrainer
46 13f6af81 Thomas Thrainer
from ganeti.cmdlib.common import INSTANCE_DOWN, \
47 5eacbcae Thomas Thrainer
  INSTANCE_NOT_RUNNING, CAN_CHANGE_INSTANCE_OFFLINE, CheckNodeOnline, \
48 5eacbcae Thomas Thrainer
  ShareAll, GetDefaultIAllocator, CheckInstanceNodeGroups, \
49 5eacbcae Thomas Thrainer
  LoadNodeEvacResult, CheckIAllocatorOrNode, CheckParamsNotGlobal, \
50 1c4910f7 Jose A. Lopes
  IsExclusiveStorageEnabledNode, CheckHVParams, CheckOSParams, CheckOSImage, \
51 da4a52a3 Thomas Thrainer
  AnnotateDiskParams, GetUpdatedParams, ExpandInstanceUuidAndName, \
52 1f7c8208 Helga Velroyen
  ComputeIPolicySpecViolation, CheckInstanceState, ExpandNodeUuidAndName, \
53 294254b1 Raffa Santi
  CheckDiskTemplateEnabled, IsValidDiskAccessModeCombination
54 5eacbcae Thomas Thrainer
from ganeti.cmdlib.instance_storage import CreateDisks, \
55 2329ffdd Jose A. Lopes
  CheckNodesFreeDiskPerVG, WipeDisks, WipeOrCleanupDisks, ImageDisks, \
56 2329ffdd Jose A. Lopes
  WaitForSync, IsExclusiveStorageEnabledNodeUuid, CreateSingleBlockDev, \
57 2329ffdd Jose A. Lopes
  ComputeDisks, CheckRADOSFreeSpace, ComputeDiskSizePerVG, \
58 2329ffdd Jose A. Lopes
  GenerateDiskTemplate, StartInstanceDisks, ShutdownInstanceDisks, \
59 2329ffdd Jose A. Lopes
  AssembleInstanceDisks, CheckSpindlesExclusiveStorage
60 5eacbcae Thomas Thrainer
from ganeti.cmdlib.instance_utils import BuildInstanceHookEnvByObject, \
61 5eacbcae Thomas Thrainer
  GetClusterDomainSecret, BuildInstanceHookEnv, NICListToTuple, \
62 5eacbcae Thomas Thrainer
  NICToTuple, CheckNodeNotDrained, RemoveInstance, CopyLockList, \
63 5eacbcae Thomas Thrainer
  ReleaseLocks, CheckNodeVmCapable, CheckTargetNodeIPolicy, \
64 5eacbcae Thomas Thrainer
  GetInstanceInfoText, RemoveDisks, CheckNodeFreeMemory, \
65 8c58dc45 Jose A. Lopes
  CheckInstanceBridgesExist, CheckNicsBridgesExist, UpdateMetadata
66 22b7f6f8 Thomas Thrainer
import ganeti.masterd.instance
67 22b7f6f8 Thomas Thrainer
68 22b7f6f8 Thomas Thrainer
69 5eacbcae Thomas Thrainer
#: Type description for changes as returned by L{_ApplyContainerMods}'s
70 22b7f6f8 Thomas Thrainer
#: callbacks
71 22b7f6f8 Thomas Thrainer
_TApplyContModsCbChanges = \
72 22b7f6f8 Thomas Thrainer
  ht.TMaybeListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([
73 22b7f6f8 Thomas Thrainer
    ht.TNonEmptyString,
74 22b7f6f8 Thomas Thrainer
    ht.TAny,
75 22b7f6f8 Thomas Thrainer
    ])))
76 22b7f6f8 Thomas Thrainer
77 22b7f6f8 Thomas Thrainer
78 22b7f6f8 Thomas Thrainer
def _CheckHostnameSane(lu, name):
79 22b7f6f8 Thomas Thrainer
  """Ensures that a given hostname resolves to a 'sane' name.
80 22b7f6f8 Thomas Thrainer

81 22b7f6f8 Thomas Thrainer
  The given name is required to be a prefix of the resolved hostname,
82 22b7f6f8 Thomas Thrainer
  to prevent accidental mismatches.
83 22b7f6f8 Thomas Thrainer

84 22b7f6f8 Thomas Thrainer
  @param lu: the logical unit on behalf of which we're checking
85 22b7f6f8 Thomas Thrainer
  @param name: the name we should resolve and check
86 22b7f6f8 Thomas Thrainer
  @return: the resolved hostname object
87 22b7f6f8 Thomas Thrainer

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

102 22b7f6f8 Thomas Thrainer
  """
103 22b7f6f8 Thomas Thrainer
  if op.opportunistic_locking and not op.iallocator:
104 22b7f6f8 Thomas Thrainer
    raise errors.OpPrereqError("Opportunistic locking is only available in"
105 22b7f6f8 Thomas Thrainer
                               " combination with an instance allocator",
106 22b7f6f8 Thomas Thrainer
                               errors.ECODE_INVAL)
107 22b7f6f8 Thomas Thrainer
108 22b7f6f8 Thomas Thrainer
109 1c3231aa Thomas Thrainer
def _CreateInstanceAllocRequest(op, disks, nics, beparams, node_name_whitelist):
110 22b7f6f8 Thomas Thrainer
  """Wrapper around IAReqInstanceAlloc.
111 22b7f6f8 Thomas Thrainer

112 22b7f6f8 Thomas Thrainer
  @param op: The instance opcode
113 22b7f6f8 Thomas Thrainer
  @param disks: The computed disks
114 22b7f6f8 Thomas Thrainer
  @param nics: The computed nics
115 22b7f6f8 Thomas Thrainer
  @param beparams: The full filled beparams
116 1c3231aa Thomas Thrainer
  @param node_name_whitelist: List of nodes which should appear as online to the
117 22b7f6f8 Thomas Thrainer
    allocator (unless the node is already marked offline)
118 22b7f6f8 Thomas Thrainer

119 22b7f6f8 Thomas Thrainer
  @returns: A filled L{iallocator.IAReqInstanceAlloc}
120 22b7f6f8 Thomas Thrainer

121 22b7f6f8 Thomas Thrainer
  """
122 22b7f6f8 Thomas Thrainer
  spindle_use = beparams[constants.BE_SPINDLE_USE]
123 22b7f6f8 Thomas Thrainer
  return iallocator.IAReqInstanceAlloc(name=op.instance_name,
124 22b7f6f8 Thomas Thrainer
                                       disk_template=op.disk_template,
125 22b7f6f8 Thomas Thrainer
                                       tags=op.tags,
126 22b7f6f8 Thomas Thrainer
                                       os=op.os_type,
127 22b7f6f8 Thomas Thrainer
                                       vcpus=beparams[constants.BE_VCPUS],
128 22b7f6f8 Thomas Thrainer
                                       memory=beparams[constants.BE_MAXMEM],
129 22b7f6f8 Thomas Thrainer
                                       spindle_use=spindle_use,
130 22b7f6f8 Thomas Thrainer
                                       disks=disks,
131 22b7f6f8 Thomas Thrainer
                                       nics=[n.ToDict() for n in nics],
132 22b7f6f8 Thomas Thrainer
                                       hypervisor=op.hypervisor,
133 1c3231aa Thomas Thrainer
                                       node_whitelist=node_name_whitelist)
134 22b7f6f8 Thomas Thrainer
135 22b7f6f8 Thomas Thrainer
136 22b7f6f8 Thomas Thrainer
def _ComputeFullBeParams(op, cluster):
137 22b7f6f8 Thomas Thrainer
  """Computes the full beparams.
138 22b7f6f8 Thomas Thrainer

139 22b7f6f8 Thomas Thrainer
  @param op: The instance opcode
140 22b7f6f8 Thomas Thrainer
  @param cluster: The cluster config object
141 22b7f6f8 Thomas Thrainer

142 22b7f6f8 Thomas Thrainer
  @return: The fully filled beparams
143 22b7f6f8 Thomas Thrainer

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

157 22b7f6f8 Thomas Thrainer
  @param op: The instance opcode
158 22b7f6f8 Thomas Thrainer
  @param cluster: Cluster configuration object
159 22b7f6f8 Thomas Thrainer
  @param default_ip: The default ip to assign
160 22b7f6f8 Thomas Thrainer
  @param cfg: An instance of the configuration object
161 22b7f6f8 Thomas Thrainer
  @param ec_id: Execution context ID
162 22b7f6f8 Thomas Thrainer

163 22b7f6f8 Thomas Thrainer
  @returns: The build up nics
164 22b7f6f8 Thomas Thrainer

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

254 22b7f6f8 Thomas Thrainer
  @type ip: string
255 22b7f6f8 Thomas Thrainer
  @param ip: IP address
256 1c3231aa Thomas Thrainer
  @type node_uuid: string
257 1c3231aa Thomas Thrainer
  @param node_uuid: node UUID
258 22b7f6f8 Thomas Thrainer

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

275 22b7f6f8 Thomas Thrainer
  @type ipolicy: dict
276 22b7f6f8 Thomas Thrainer
  @param ipolicy: The ipolicy to verify against
277 22b7f6f8 Thomas Thrainer
  @param instance_spec: dict
278 22b7f6f8 Thomas Thrainer
  @param instance_spec: The instance spec to verify
279 22b7f6f8 Thomas Thrainer
  @type disk_template: string
280 22b7f6f8 Thomas Thrainer
  @param disk_template: the disk template of the instance
281 22b7f6f8 Thomas Thrainer
  @param _compute_fn: The function to verify ipolicy (unittest only)
282 5eacbcae Thomas Thrainer
  @see: L{ComputeIPolicySpecViolation}
283 22b7f6f8 Thomas Thrainer

284 22b7f6f8 Thomas Thrainer
  """
285 22b7f6f8 Thomas Thrainer
  mem_size = instance_spec.get(constants.ISPEC_MEM_SIZE, None)
286 22b7f6f8 Thomas Thrainer
  cpu_count = instance_spec.get(constants.ISPEC_CPU_COUNT, None)
287 22b7f6f8 Thomas Thrainer
  disk_count = instance_spec.get(constants.ISPEC_DISK_COUNT, 0)
288 22b7f6f8 Thomas Thrainer
  disk_sizes = instance_spec.get(constants.ISPEC_DISK_SIZE, [])
289 22b7f6f8 Thomas Thrainer
  nic_count = instance_spec.get(constants.ISPEC_NIC_COUNT, 0)
290 22b7f6f8 Thomas Thrainer
  spindle_use = instance_spec.get(constants.ISPEC_SPINDLE_USE, None)
291 22b7f6f8 Thomas Thrainer
292 22b7f6f8 Thomas Thrainer
  return _compute_fn(ipolicy, mem_size, cpu_count, disk_count, nic_count,
293 22b7f6f8 Thomas Thrainer
                     disk_sizes, spindle_use, disk_template)
294 22b7f6f8 Thomas Thrainer
295 22b7f6f8 Thomas Thrainer
296 e25625ab Jose A. Lopes
def _ComputeInstanceCommunicationNIC(instance_name):
297 e25625ab Jose A. Lopes
  """Compute the name of the instance NIC used by instance
298 e25625ab Jose A. Lopes
  communication.
299 e25625ab Jose A. Lopes

300 e25625ab Jose A. Lopes
  With instance communication, a new NIC is added to the instance.
301 e25625ab Jose A. Lopes
  This NIC has a special name that identities it as being part of
302 e25625ab Jose A. Lopes
  instance communication, and not just a normal NIC.  This function
303 e25625ab Jose A. Lopes
  generates the name of the NIC based on a prefix and the instance
304 e25625ab Jose A. Lopes
  name
305 e25625ab Jose A. Lopes

306 e25625ab Jose A. Lopes
  @type instance_name: string
307 e25625ab Jose A. Lopes
  @param instance_name: name of the instance the NIC belongs to
308 e25625ab Jose A. Lopes

309 e25625ab Jose A. Lopes
  @rtype: string
310 e25625ab Jose A. Lopes
  @return: name of the NIC
311 e25625ab Jose A. Lopes

312 e25625ab Jose A. Lopes
  """
313 e25625ab Jose A. Lopes
  return constants.INSTANCE_COMMUNICATION_NIC_PREFIX + instance_name
314 e25625ab Jose A. Lopes
315 e25625ab Jose A. Lopes
316 22b7f6f8 Thomas Thrainer
class LUInstanceCreate(LogicalUnit):
317 22b7f6f8 Thomas Thrainer
  """Create an instance.
318 22b7f6f8 Thomas Thrainer

319 22b7f6f8 Thomas Thrainer
  """
320 22b7f6f8 Thomas Thrainer
  HPATH = "instance-add"
321 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
322 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
323 22b7f6f8 Thomas Thrainer
324 dab6ea3d Helga Velroyen
  def _CheckDiskTemplateValid(self):
325 dab6ea3d Helga Velroyen
    """Checks validity of disk template.
326 22b7f6f8 Thomas Thrainer

327 22b7f6f8 Thomas Thrainer
    """
328 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
329 72080db1 Helga Velroyen
    if self.op.disk_template is None:
330 72080db1 Helga Velroyen
      # FIXME: It would be better to take the default disk template from the
331 72080db1 Helga Velroyen
      # ipolicy, but for the ipolicy we need the primary node, which we get from
332 72080db1 Helga Velroyen
      # the iallocator, which wants the disk template as input. To solve this
333 72080db1 Helga Velroyen
      # chicken-and-egg problem, it should be possible to specify just a node
334 72080db1 Helga Velroyen
      # group from the iallocator and take the ipolicy from that.
335 72080db1 Helga Velroyen
      self.op.disk_template = cluster.enabled_disk_templates[0]
336 1f7c8208 Helga Velroyen
    CheckDiskTemplateEnabled(cluster, self.op.disk_template)
337 22b7f6f8 Thomas Thrainer
338 dab6ea3d Helga Velroyen
  def _CheckDiskArguments(self):
339 dab6ea3d Helga Velroyen
    """Checks validity of disk-related arguments.
340 dab6ea3d Helga Velroyen

341 dab6ea3d Helga Velroyen
    """
342 dab6ea3d Helga Velroyen
    # check that disk's names are unique and valid
343 dab6ea3d Helga Velroyen
    utils.ValidateDeviceNames("disk", self.op.disks)
344 dab6ea3d Helga Velroyen
345 dab6ea3d Helga Velroyen
    self._CheckDiskTemplateValid()
346 dab6ea3d Helga Velroyen
347 22b7f6f8 Thomas Thrainer
    # check disks. parameter names and consistent adopt/no-adopt strategy
348 22b7f6f8 Thomas Thrainer
    has_adopt = has_no_adopt = False
349 22b7f6f8 Thomas Thrainer
    for disk in self.op.disks:
350 22b7f6f8 Thomas Thrainer
      if self.op.disk_template != constants.DT_EXT:
351 22b7f6f8 Thomas Thrainer
        utils.ForceDictType(disk, constants.IDISK_PARAMS_TYPES)
352 22b7f6f8 Thomas Thrainer
      if constants.IDISK_ADOPT in disk:
353 22b7f6f8 Thomas Thrainer
        has_adopt = True
354 22b7f6f8 Thomas Thrainer
      else:
355 22b7f6f8 Thomas Thrainer
        has_no_adopt = True
356 22b7f6f8 Thomas Thrainer
    if has_adopt and has_no_adopt:
357 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Either all disks are adopted or none is",
358 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
359 22b7f6f8 Thomas Thrainer
    if has_adopt:
360 22b7f6f8 Thomas Thrainer
      if self.op.disk_template not in constants.DTS_MAY_ADOPT:
361 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk adoption is not supported for the"
362 22b7f6f8 Thomas Thrainer
                                   " '%s' disk template" %
363 22b7f6f8 Thomas Thrainer
                                   self.op.disk_template,
364 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
365 22b7f6f8 Thomas Thrainer
      if self.op.iallocator is not None:
366 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk adoption not allowed with an"
367 22b7f6f8 Thomas Thrainer
                                   " iallocator script", errors.ECODE_INVAL)
368 22b7f6f8 Thomas Thrainer
      if self.op.mode == constants.INSTANCE_IMPORT:
369 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk adoption not allowed for"
370 22b7f6f8 Thomas Thrainer
                                   " instance import", errors.ECODE_INVAL)
371 22b7f6f8 Thomas Thrainer
    else:
372 22b7f6f8 Thomas Thrainer
      if self.op.disk_template in constants.DTS_MUST_ADOPT:
373 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk template %s requires disk adoption,"
374 22b7f6f8 Thomas Thrainer
                                   " but no 'adopt' parameter given" %
375 22b7f6f8 Thomas Thrainer
                                   self.op.disk_template,
376 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
377 22b7f6f8 Thomas Thrainer
378 22b7f6f8 Thomas Thrainer
    self.adopt_disks = has_adopt
379 22b7f6f8 Thomas Thrainer
380 9f7d5fe4 Sebastian Gebhard
  def _CheckVLANArguments(self):
381 9f7d5fe4 Sebastian Gebhard
    """ Check validity of VLANs if given
382 9f7d5fe4 Sebastian Gebhard

383 9f7d5fe4 Sebastian Gebhard
    """
384 9f7d5fe4 Sebastian Gebhard
    for nic in self.op.nics:
385 5ce6fa9a Sebastian Gebhard
      vlan = nic.get(constants.INIC_VLAN, None)
386 5ce6fa9a Sebastian Gebhard
      if vlan:
387 9f7d5fe4 Sebastian Gebhard
        if vlan[0] == ".":
388 9f7d5fe4 Sebastian Gebhard
          # vlan starting with dot means single untagged vlan,
389 9f7d5fe4 Sebastian Gebhard
          # might be followed by trunk (:)
390 9f7d5fe4 Sebastian Gebhard
          if not vlan[1:].isdigit():
391 9f7d5fe4 Sebastian Gebhard
            vlanlist = vlan[1:].split(':')
392 9f7d5fe4 Sebastian Gebhard
            for vl in vlanlist:
393 9f7d5fe4 Sebastian Gebhard
              if not vl.isdigit():
394 9f7d5fe4 Sebastian Gebhard
                raise errors.OpPrereqError("Specified VLAN parameter is "
395 9f7d5fe4 Sebastian Gebhard
                                           "invalid : %s" % vlan,
396 9f7d5fe4 Sebastian Gebhard
                                             errors.ECODE_INVAL)
397 9f7d5fe4 Sebastian Gebhard
        elif vlan[0] == ":":
398 9f7d5fe4 Sebastian Gebhard
          # Trunk - tagged only
399 9f7d5fe4 Sebastian Gebhard
          vlanlist = vlan[1:].split(':')
400 9f7d5fe4 Sebastian Gebhard
          for vl in vlanlist:
401 9f7d5fe4 Sebastian Gebhard
            if not vl.isdigit():
402 9f7d5fe4 Sebastian Gebhard
              raise errors.OpPrereqError("Specified VLAN parameter is invalid"
403 9f7d5fe4 Sebastian Gebhard
                                           " : %s" % vlan, errors.ECODE_INVAL)
404 9f7d5fe4 Sebastian Gebhard
        elif vlan.isdigit():
405 9f7d5fe4 Sebastian Gebhard
          # This is the simplest case. No dots, only single digit
406 9f7d5fe4 Sebastian Gebhard
          # -> Create untagged access port, dot needs to be added
407 9f7d5fe4 Sebastian Gebhard
          nic[constants.INIC_VLAN] = "." + vlan
408 9f7d5fe4 Sebastian Gebhard
        else:
409 9f7d5fe4 Sebastian Gebhard
          raise errors.OpPrereqError("Specified VLAN parameter is invalid"
410 9f7d5fe4 Sebastian Gebhard
                                       " : %s" % vlan, errors.ECODE_INVAL)
411 9f7d5fe4 Sebastian Gebhard
412 dab6ea3d Helga Velroyen
  def CheckArguments(self):
413 dab6ea3d Helga Velroyen
    """Check arguments.
414 dab6ea3d Helga Velroyen

415 dab6ea3d Helga Velroyen
    """
416 dab6ea3d Helga Velroyen
    # do not require name_check to ease forward/backward compatibility
417 dab6ea3d Helga Velroyen
    # for tools
418 dab6ea3d Helga Velroyen
    if self.op.no_install and self.op.start:
419 dab6ea3d Helga Velroyen
      self.LogInfo("No-installation mode selected, disabling startup")
420 dab6ea3d Helga Velroyen
      self.op.start = False
421 dab6ea3d Helga Velroyen
    # validate/normalize the instance name
422 dab6ea3d Helga Velroyen
    self.op.instance_name = \
423 dab6ea3d Helga Velroyen
      netutils.Hostname.GetNormalizedName(self.op.instance_name)
424 dab6ea3d Helga Velroyen
425 dab6ea3d Helga Velroyen
    if self.op.ip_check and not self.op.name_check:
426 dab6ea3d Helga Velroyen
      # TODO: make the ip check more flexible and not depend on the name check
427 dab6ea3d Helga Velroyen
      raise errors.OpPrereqError("Cannot do IP address check without a name"
428 dab6ea3d Helga Velroyen
                                 " check", errors.ECODE_INVAL)
429 dab6ea3d Helga Velroyen
430 7a51281a Jose A. Lopes
    # add NIC for instance communication
431 81c222af Jose A. Lopes
    if self.op.instance_communication:
432 e25625ab Jose A. Lopes
      nic_name = _ComputeInstanceCommunicationNIC(self.op.instance_name)
433 81c222af Jose A. Lopes
434 81c222af Jose A. Lopes
      self.op.nics.append({constants.INIC_NAME: nic_name,
435 81c222af Jose A. Lopes
                           constants.INIC_MAC: constants.VALUE_GENERATE,
436 81c222af Jose A. Lopes
                           constants.INIC_IP: constants.NIC_IP_POOL,
437 9a94cee3 Jose A. Lopes
                           constants.INIC_NETWORK:
438 9a94cee3 Jose A. Lopes
                             self.cfg.GetInstanceCommunicationNetwork()})
439 81c222af Jose A. Lopes
440 dab6ea3d Helga Velroyen
    # check nics' parameter names
441 dab6ea3d Helga Velroyen
    for nic in self.op.nics:
442 dab6ea3d Helga Velroyen
      utils.ForceDictType(nic, constants.INIC_PARAMS_TYPES)
443 dab6ea3d Helga Velroyen
    # check that NIC's parameters names are unique and valid
444 dab6ea3d Helga Velroyen
    utils.ValidateDeviceNames("NIC", self.op.nics)
445 dab6ea3d Helga Velroyen
446 9f7d5fe4 Sebastian Gebhard
    self._CheckVLANArguments()
447 9f7d5fe4 Sebastian Gebhard
448 dab6ea3d Helga Velroyen
    self._CheckDiskArguments()
449 66222813 Thomas Thrainer
    assert self.op.disk_template is not None
450 dab6ea3d Helga Velroyen
451 22b7f6f8 Thomas Thrainer
    # instance name verification
452 22b7f6f8 Thomas Thrainer
    if self.op.name_check:
453 da4a52a3 Thomas Thrainer
      self.hostname = _CheckHostnameSane(self, self.op.instance_name)
454 da4a52a3 Thomas Thrainer
      self.op.instance_name = self.hostname.name
455 22b7f6f8 Thomas Thrainer
      # used in CheckPrereq for ip ping check
456 da4a52a3 Thomas Thrainer
      self.check_ip = self.hostname.ip
457 22b7f6f8 Thomas Thrainer
    else:
458 22b7f6f8 Thomas Thrainer
      self.check_ip = None
459 22b7f6f8 Thomas Thrainer
460 22b7f6f8 Thomas Thrainer
    # file storage checks
461 22b7f6f8 Thomas Thrainer
    if (self.op.file_driver and
462 22b7f6f8 Thomas Thrainer
        not self.op.file_driver in constants.FILE_DRIVER):
463 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Invalid file driver name '%s'" %
464 22b7f6f8 Thomas Thrainer
                                 self.op.file_driver, errors.ECODE_INVAL)
465 22b7f6f8 Thomas Thrainer
466 ff34fb97 Michele Tartara
    # set default file_driver if unset and required
467 ff34fb97 Michele Tartara
    if (not self.op.file_driver and
468 a09639d1 Santi Raffa
        self.op.disk_template in constants.DTS_FILEBASED):
469 ff34fb97 Michele Tartara
      self.op.file_driver = constants.FD_LOOP
470 ff34fb97 Michele Tartara
471 22b7f6f8 Thomas Thrainer
    ### Node/iallocator related checks
472 5eacbcae Thomas Thrainer
    CheckIAllocatorOrNode(self, "iallocator", "pnode")
473 22b7f6f8 Thomas Thrainer
474 22b7f6f8 Thomas Thrainer
    if self.op.pnode is not None:
475 22b7f6f8 Thomas Thrainer
      if self.op.disk_template in constants.DTS_INT_MIRROR:
476 22b7f6f8 Thomas Thrainer
        if self.op.snode is None:
477 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("The networked disk templates need"
478 22b7f6f8 Thomas Thrainer
                                     " a mirror node", errors.ECODE_INVAL)
479 22b7f6f8 Thomas Thrainer
      elif self.op.snode:
480 22b7f6f8 Thomas Thrainer
        self.LogWarning("Secondary node will be ignored on non-mirrored disk"
481 22b7f6f8 Thomas Thrainer
                        " template")
482 22b7f6f8 Thomas Thrainer
        self.op.snode = None
483 22b7f6f8 Thomas Thrainer
484 22b7f6f8 Thomas Thrainer
    _CheckOpportunisticLocking(self.op)
485 22b7f6f8 Thomas Thrainer
486 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
487 22b7f6f8 Thomas Thrainer
      # On import force_variant must be True, because if we forced it at
488 22b7f6f8 Thomas Thrainer
      # initial install, our only chance when importing it back is that it
489 22b7f6f8 Thomas Thrainer
      # works again!
490 22b7f6f8 Thomas Thrainer
      self.op.force_variant = True
491 22b7f6f8 Thomas Thrainer
492 22b7f6f8 Thomas Thrainer
      if self.op.no_install:
493 22b7f6f8 Thomas Thrainer
        self.LogInfo("No-installation mode has no effect during import")
494 22b7f6f8 Thomas Thrainer
495 1c4910f7 Jose A. Lopes
      if objects.GetOSImage(self.op.osparams):
496 1c4910f7 Jose A. Lopes
        self.LogInfo("OS image has no effect during import")
497 22b7f6f8 Thomas Thrainer
    elif self.op.mode == constants.INSTANCE_CREATE:
498 1c4910f7 Jose A. Lopes
      os_image = CheckOSImage(self.op)
499 1c4910f7 Jose A. Lopes
500 1c4910f7 Jose A. Lopes
      if self.op.os_type is None and os_image is None:
501 1c4910f7 Jose A. Lopes
        raise errors.OpPrereqError("No guest OS or OS image specified",
502 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
503 1c4910f7 Jose A. Lopes
504 1c4910f7 Jose A. Lopes
      if self.op.os_type is not None \
505 1c4910f7 Jose A. Lopes
            and self.op.os_type in self.cfg.GetClusterInfo().blacklisted_os:
506 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Guest OS '%s' is not allowed for"
507 22b7f6f8 Thomas Thrainer
                                   " installation" % self.op.os_type,
508 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
509 22b7f6f8 Thomas Thrainer
    elif self.op.mode == constants.INSTANCE_REMOTE_IMPORT:
510 1c4910f7 Jose A. Lopes
      if objects.GetOSImage(self.op.osparams):
511 1c4910f7 Jose A. Lopes
        self.LogInfo("OS image has no effect during import")
512 1c4910f7 Jose A. Lopes
513 66222813 Thomas Thrainer
      self._cds = GetClusterDomainSecret()
514 66222813 Thomas Thrainer
515 22b7f6f8 Thomas Thrainer
      # Check handshake to ensure both clusters have the same domain secret
516 22b7f6f8 Thomas Thrainer
      src_handshake = self.op.source_handshake
517 22b7f6f8 Thomas Thrainer
      if not src_handshake:
518 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing source handshake",
519 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
520 22b7f6f8 Thomas Thrainer
521 22b7f6f8 Thomas Thrainer
      errmsg = masterd.instance.CheckRemoteExportHandshake(self._cds,
522 22b7f6f8 Thomas Thrainer
                                                           src_handshake)
523 22b7f6f8 Thomas Thrainer
      if errmsg:
524 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid handshake: %s" % errmsg,
525 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
526 22b7f6f8 Thomas Thrainer
527 22b7f6f8 Thomas Thrainer
      # Load and check source CA
528 22b7f6f8 Thomas Thrainer
      self.source_x509_ca_pem = self.op.source_x509_ca
529 22b7f6f8 Thomas Thrainer
      if not self.source_x509_ca_pem:
530 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing source X509 CA",
531 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
532 22b7f6f8 Thomas Thrainer
533 22b7f6f8 Thomas Thrainer
      try:
534 22b7f6f8 Thomas Thrainer
        (cert, _) = utils.LoadSignedX509Certificate(self.source_x509_ca_pem,
535 22b7f6f8 Thomas Thrainer
                                                    self._cds)
536 22b7f6f8 Thomas Thrainer
      except OpenSSL.crypto.Error, err:
537 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Unable to load source X509 CA (%s)" %
538 22b7f6f8 Thomas Thrainer
                                   (err, ), errors.ECODE_INVAL)
539 22b7f6f8 Thomas Thrainer
540 22b7f6f8 Thomas Thrainer
      (errcode, msg) = utils.VerifyX509Certificate(cert, None, None)
541 22b7f6f8 Thomas Thrainer
      if errcode is not None:
542 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid source X509 CA (%s)" % (msg, ),
543 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
544 22b7f6f8 Thomas Thrainer
545 22b7f6f8 Thomas Thrainer
      self.source_x509_ca = cert
546 22b7f6f8 Thomas Thrainer
547 22b7f6f8 Thomas Thrainer
      src_instance_name = self.op.source_instance_name
548 22b7f6f8 Thomas Thrainer
      if not src_instance_name:
549 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing source instance name",
550 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
551 22b7f6f8 Thomas Thrainer
552 22b7f6f8 Thomas Thrainer
      self.source_instance_name = \
553 22b7f6f8 Thomas Thrainer
        netutils.GetHostname(name=src_instance_name).name
554 22b7f6f8 Thomas Thrainer
555 22b7f6f8 Thomas Thrainer
    else:
556 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Invalid instance creation mode %r" %
557 22b7f6f8 Thomas Thrainer
                                 self.op.mode, errors.ECODE_INVAL)
558 22b7f6f8 Thomas Thrainer
559 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
560 22b7f6f8 Thomas Thrainer
    """ExpandNames for CreateInstance.
561 22b7f6f8 Thomas Thrainer

562 22b7f6f8 Thomas Thrainer
    Figure out the right locks for instance creation.
563 22b7f6f8 Thomas Thrainer

564 22b7f6f8 Thomas Thrainer
    """
565 22b7f6f8 Thomas Thrainer
    self.needed_locks = {}
566 22b7f6f8 Thomas Thrainer
567 22b7f6f8 Thomas Thrainer
    # this is just a preventive check, but someone might still add this
568 22b7f6f8 Thomas Thrainer
    # instance in the meantime, and creation will fail at lock-add time
569 da4a52a3 Thomas Thrainer
    if self.op.instance_name in\
570 da4a52a3 Thomas Thrainer
      [inst.name for inst in self.cfg.GetAllInstancesInfo().values()]:
571 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
572 d0d7d7cf Thomas Thrainer
                                 self.op.instance_name, errors.ECODE_EXISTS)
573 22b7f6f8 Thomas Thrainer
574 d0d7d7cf Thomas Thrainer
    self.add_locks[locking.LEVEL_INSTANCE] = self.op.instance_name
575 22b7f6f8 Thomas Thrainer
576 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
577 22b7f6f8 Thomas Thrainer
      # TODO: Find a solution to not lock all nodes in the cluster, e.g. by
578 22b7f6f8 Thomas Thrainer
      # specifying a group on instance creation and then selecting nodes from
579 22b7f6f8 Thomas Thrainer
      # that group
580 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
581 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
582 22b7f6f8 Thomas Thrainer
583 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
584 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
585 a278ef7f Hrvoje Ribicic
        self.opportunistic_locks[locking.LEVEL_NODE_RES] = True
586 22b7f6f8 Thomas Thrainer
    else:
587 1c3231aa Thomas Thrainer
      (self.op.pnode_uuid, self.op.pnode) = \
588 1c3231aa Thomas Thrainer
        ExpandNodeUuidAndName(self.cfg, self.op.pnode_uuid, self.op.pnode)
589 1c3231aa Thomas Thrainer
      nodelist = [self.op.pnode_uuid]
590 22b7f6f8 Thomas Thrainer
      if self.op.snode is not None:
591 1c3231aa Thomas Thrainer
        (self.op.snode_uuid, self.op.snode) = \
592 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, self.op.snode_uuid, self.op.snode)
593 1c3231aa Thomas Thrainer
        nodelist.append(self.op.snode_uuid)
594 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodelist
595 22b7f6f8 Thomas Thrainer
596 22b7f6f8 Thomas Thrainer
    # in case of import lock the source node too
597 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
598 22b7f6f8 Thomas Thrainer
      src_node = self.op.src_node
599 22b7f6f8 Thomas Thrainer
      src_path = self.op.src_path
600 22b7f6f8 Thomas Thrainer
601 22b7f6f8 Thomas Thrainer
      if src_path is None:
602 22b7f6f8 Thomas Thrainer
        self.op.src_path = src_path = self.op.instance_name
603 22b7f6f8 Thomas Thrainer
604 22b7f6f8 Thomas Thrainer
      if src_node is None:
605 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
606 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
607 22b7f6f8 Thomas Thrainer
        self.op.src_node = None
608 22b7f6f8 Thomas Thrainer
        if os.path.isabs(src_path):
609 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("Importing an instance from a path"
610 22b7f6f8 Thomas Thrainer
                                     " requires a source node option",
611 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
612 22b7f6f8 Thomas Thrainer
      else:
613 1c3231aa Thomas Thrainer
        (self.op.src_node_uuid, self.op.src_node) = (_, src_node) = \
614 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, self.op.src_node_uuid, src_node)
615 22b7f6f8 Thomas Thrainer
        if self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET:
616 1c3231aa Thomas Thrainer
          self.needed_locks[locking.LEVEL_NODE].append(self.op.src_node_uuid)
617 22b7f6f8 Thomas Thrainer
        if not os.path.isabs(src_path):
618 a61a0813 Thomas Thrainer
          self.op.src_path = \
619 22b7f6f8 Thomas Thrainer
            utils.PathJoin(pathutils.EXPORT_DIR, src_path)
620 22b7f6f8 Thomas Thrainer
621 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = \
622 5eacbcae Thomas Thrainer
      CopyLockList(self.needed_locks[locking.LEVEL_NODE])
623 22b7f6f8 Thomas Thrainer
624 8b9887c5 Petr Pudlak
    # Optimistically acquire shared group locks (we're reading the
625 8b9887c5 Petr Pudlak
    # configuration).  We can't just call GetInstanceNodeGroups, because the
626 8b9887c5 Petr Pudlak
    # instance doesn't exist yet. Therefore we lock all node groups of all
627 8b9887c5 Petr Pudlak
    # nodes we have.
628 8b9887c5 Petr Pudlak
    if self.needed_locks[locking.LEVEL_NODE] == locking.ALL_SET:
629 8b9887c5 Petr Pudlak
      # In the case we lock all nodes for opportunistic allocation, we have no
630 8b9887c5 Petr Pudlak
      # choice than to lock all groups, because they're allocated before nodes.
631 8b9887c5 Petr Pudlak
      # This is sad, but true. At least we release all those we don't need in
632 8b9887c5 Petr Pudlak
      # CheckPrereq later.
633 8b9887c5 Petr Pudlak
      self.needed_locks[locking.LEVEL_NODEGROUP] = locking.ALL_SET
634 8b9887c5 Petr Pudlak
    else:
635 8b9887c5 Petr Pudlak
      self.needed_locks[locking.LEVEL_NODEGROUP] = \
636 8b9887c5 Petr Pudlak
        list(self.cfg.GetNodeGroupsFromNodes(
637 8b9887c5 Petr Pudlak
          self.needed_locks[locking.LEVEL_NODE]))
638 8b9887c5 Petr Pudlak
    self.share_locks[locking.LEVEL_NODEGROUP] = 1
639 8b9887c5 Petr Pudlak
640 22b7f6f8 Thomas Thrainer
  def _RunAllocator(self):
641 22b7f6f8 Thomas Thrainer
    """Run the allocator based on input opcode.
642 22b7f6f8 Thomas Thrainer

643 22b7f6f8 Thomas Thrainer
    """
644 22b7f6f8 Thomas Thrainer
    if self.op.opportunistic_locking:
645 22b7f6f8 Thomas Thrainer
      # Only consider nodes for which a lock is held
646 4665b94f Thomas Thrainer
      node_name_whitelist = self.cfg.GetNodeNames(
647 a278ef7f Hrvoje Ribicic
        set(self.owned_locks(locking.LEVEL_NODE)) &
648 a278ef7f Hrvoje Ribicic
        set(self.owned_locks(locking.LEVEL_NODE_RES)))
649 22b7f6f8 Thomas Thrainer
    else:
650 4665b94f Thomas Thrainer
      node_name_whitelist = None
651 22b7f6f8 Thomas Thrainer
652 22b7f6f8 Thomas Thrainer
    req = _CreateInstanceAllocRequest(self.op, self.disks,
653 22b7f6f8 Thomas Thrainer
                                      self.nics, self.be_full,
654 4665b94f Thomas Thrainer
                                      node_name_whitelist)
655 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
656 22b7f6f8 Thomas Thrainer
657 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
658 22b7f6f8 Thomas Thrainer
659 22b7f6f8 Thomas Thrainer
    if not ial.success:
660 22b7f6f8 Thomas Thrainer
      # When opportunistic locks are used only a temporary failure is generated
661 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
662 22b7f6f8 Thomas Thrainer
        ecode = errors.ECODE_TEMP_NORES
663 22b7f6f8 Thomas Thrainer
      else:
664 22b7f6f8 Thomas Thrainer
        ecode = errors.ECODE_NORES
665 22b7f6f8 Thomas Thrainer
666 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute nodes using"
667 22b7f6f8 Thomas Thrainer
                                 " iallocator '%s': %s" %
668 22b7f6f8 Thomas Thrainer
                                 (self.op.iallocator, ial.info),
669 22b7f6f8 Thomas Thrainer
                                 ecode)
670 22b7f6f8 Thomas Thrainer
671 1c3231aa Thomas Thrainer
    (self.op.pnode_uuid, self.op.pnode) = \
672 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, None, ial.result[0])
673 22b7f6f8 Thomas Thrainer
    self.LogInfo("Selected nodes for instance %s via iallocator %s: %s",
674 22b7f6f8 Thomas Thrainer
                 self.op.instance_name, self.op.iallocator,
675 22b7f6f8 Thomas Thrainer
                 utils.CommaJoin(ial.result))
676 22b7f6f8 Thomas Thrainer
677 22b7f6f8 Thomas Thrainer
    assert req.RequiredNodes() in (1, 2), "Wrong node count from iallocator"
678 22b7f6f8 Thomas Thrainer
679 22b7f6f8 Thomas Thrainer
    if req.RequiredNodes() == 2:
680 1c3231aa Thomas Thrainer
      (self.op.snode_uuid, self.op.snode) = \
681 1c3231aa Thomas Thrainer
        ExpandNodeUuidAndName(self.cfg, None, ial.result[1])
682 22b7f6f8 Thomas Thrainer
683 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
684 22b7f6f8 Thomas Thrainer
    """Build hooks env.
685 22b7f6f8 Thomas Thrainer

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

688 22b7f6f8 Thomas Thrainer
    """
689 22b7f6f8 Thomas Thrainer
    env = {
690 22b7f6f8 Thomas Thrainer
      "ADD_MODE": self.op.mode,
691 22b7f6f8 Thomas Thrainer
      }
692 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
693 22b7f6f8 Thomas Thrainer
      env["SRC_NODE"] = self.op.src_node
694 22b7f6f8 Thomas Thrainer
      env["SRC_PATH"] = self.op.src_path
695 22b7f6f8 Thomas Thrainer
      env["SRC_IMAGES"] = self.src_images
696 22b7f6f8 Thomas Thrainer
697 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnv(
698 22b7f6f8 Thomas Thrainer
      name=self.op.instance_name,
699 1c3231aa Thomas Thrainer
      primary_node_name=self.op.pnode,
700 1c3231aa Thomas Thrainer
      secondary_node_names=self.cfg.GetNodeNames(self.secondaries),
701 22b7f6f8 Thomas Thrainer
      status=self.op.start,
702 22b7f6f8 Thomas Thrainer
      os_type=self.op.os_type,
703 22b7f6f8 Thomas Thrainer
      minmem=self.be_full[constants.BE_MINMEM],
704 22b7f6f8 Thomas Thrainer
      maxmem=self.be_full[constants.BE_MAXMEM],
705 22b7f6f8 Thomas Thrainer
      vcpus=self.be_full[constants.BE_VCPUS],
706 5eacbcae Thomas Thrainer
      nics=NICListToTuple(self, self.nics),
707 22b7f6f8 Thomas Thrainer
      disk_template=self.op.disk_template,
708 8a348b15 Christos Stavrakakis
      disks=[(d[constants.IDISK_NAME], d.get("uuid", ""),
709 8a348b15 Christos Stavrakakis
              d[constants.IDISK_SIZE], d[constants.IDISK_MODE])
710 8a348b15 Christos Stavrakakis
             for d in self.disks],
711 22b7f6f8 Thomas Thrainer
      bep=self.be_full,
712 22b7f6f8 Thomas Thrainer
      hvp=self.hv_full,
713 22b7f6f8 Thomas Thrainer
      hypervisor_name=self.op.hypervisor,
714 22b7f6f8 Thomas Thrainer
      tags=self.op.tags,
715 22b7f6f8 Thomas Thrainer
      ))
716 22b7f6f8 Thomas Thrainer
717 22b7f6f8 Thomas Thrainer
    return env
718 22b7f6f8 Thomas Thrainer
719 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
720 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
721 22b7f6f8 Thomas Thrainer

722 22b7f6f8 Thomas Thrainer
    """
723 1c3231aa Thomas Thrainer
    nl = [self.cfg.GetMasterNode(), self.op.pnode_uuid] + self.secondaries
724 22b7f6f8 Thomas Thrainer
    return nl, nl
725 22b7f6f8 Thomas Thrainer
726 22b7f6f8 Thomas Thrainer
  def _ReadExportInfo(self):
727 22b7f6f8 Thomas Thrainer
    """Reads the export information from disk.
728 22b7f6f8 Thomas Thrainer

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

732 22b7f6f8 Thomas Thrainer
    @return: the export information
733 22b7f6f8 Thomas Thrainer

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

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

779 22b7f6f8 Thomas Thrainer
    """
780 22b7f6f8 Thomas Thrainer
    self.op.os_type = einfo.get(constants.INISECT_EXP, "os")
781 22b7f6f8 Thomas Thrainer
782 22b7f6f8 Thomas Thrainer
    if not self.op.disks:
783 22b7f6f8 Thomas Thrainer
      disks = []
784 22b7f6f8 Thomas Thrainer
      # TODO: import the disk iv_name too
785 22b7f6f8 Thomas Thrainer
      for idx in range(constants.MAX_DISKS):
786 22b7f6f8 Thomas Thrainer
        if einfo.has_option(constants.INISECT_INS, "disk%d_size" % idx):
787 22b7f6f8 Thomas Thrainer
          disk_sz = einfo.getint(constants.INISECT_INS, "disk%d_size" % idx)
788 0f68f7fa Dimitris Aragiorgis
          disk_name = einfo.get(constants.INISECT_INS, "disk%d_name" % idx)
789 0f68f7fa Dimitris Aragiorgis
          disk = {
790 0f68f7fa Dimitris Aragiorgis
            constants.IDISK_SIZE: disk_sz,
791 0f68f7fa Dimitris Aragiorgis
            constants.IDISK_NAME: disk_name
792 0f68f7fa Dimitris Aragiorgis
            }
793 0f68f7fa Dimitris Aragiorgis
          disks.append(disk)
794 22b7f6f8 Thomas Thrainer
      self.op.disks = disks
795 22b7f6f8 Thomas Thrainer
      if not disks and self.op.disk_template != constants.DT_DISKLESS:
796 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No disk info specified and the export"
797 22b7f6f8 Thomas Thrainer
                                   " is missing the disk information",
798 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
799 22b7f6f8 Thomas Thrainer
800 22b7f6f8 Thomas Thrainer
    if not self.op.nics:
801 22b7f6f8 Thomas Thrainer
      nics = []
802 22b7f6f8 Thomas Thrainer
      for idx in range(constants.MAX_NICS):
803 22b7f6f8 Thomas Thrainer
        if einfo.has_option(constants.INISECT_INS, "nic%d_mac" % idx):
804 22b7f6f8 Thomas Thrainer
          ndict = {}
805 0f68f7fa Dimitris Aragiorgis
          for name in [constants.INIC_IP,
806 0f68f7fa Dimitris Aragiorgis
                       constants.INIC_MAC, constants.INIC_NAME]:
807 a61a0813 Thomas Thrainer
            nic_param_name = "nic%d_%s" % (idx, name)
808 a61a0813 Thomas Thrainer
            if einfo.has_option(constants.INISECT_INS, nic_param_name):
809 fd5578ee Klaus Aehlig
              v = einfo.get(constants.INISECT_INS, "nic%d_%s" % (idx, name))
810 fd5578ee Klaus Aehlig
              ndict[name] = v
811 0973f9ed Dimitris Aragiorgis
          network = einfo.get(constants.INISECT_INS,
812 0973f9ed Dimitris Aragiorgis
                              "nic%d_%s" % (idx, constants.INIC_NETWORK))
813 0973f9ed Dimitris Aragiorgis
          # in case network is given link and mode are inherited
814 0973f9ed Dimitris Aragiorgis
          # from nodegroup's netparams and thus should not be passed here
815 0973f9ed Dimitris Aragiorgis
          if network:
816 0973f9ed Dimitris Aragiorgis
            ndict[constants.INIC_NETWORK] = network
817 0973f9ed Dimitris Aragiorgis
          else:
818 0973f9ed Dimitris Aragiorgis
            for name in list(constants.NICS_PARAMETERS):
819 0973f9ed Dimitris Aragiorgis
              v = einfo.get(constants.INISECT_INS, "nic%d_%s" % (idx, name))
820 a61a0813 Thomas Thrainer
              ndict[name] = v
821 22b7f6f8 Thomas Thrainer
          nics.append(ndict)
822 22b7f6f8 Thomas Thrainer
        else:
823 22b7f6f8 Thomas Thrainer
          break
824 22b7f6f8 Thomas Thrainer
      self.op.nics = nics
825 22b7f6f8 Thomas Thrainer
826 22b7f6f8 Thomas Thrainer
    if not self.op.tags and einfo.has_option(constants.INISECT_INS, "tags"):
827 22b7f6f8 Thomas Thrainer
      self.op.tags = einfo.get(constants.INISECT_INS, "tags").split()
828 22b7f6f8 Thomas Thrainer
829 22b7f6f8 Thomas Thrainer
    if (self.op.hypervisor is None and
830 22b7f6f8 Thomas Thrainer
        einfo.has_option(constants.INISECT_INS, "hypervisor")):
831 22b7f6f8 Thomas Thrainer
      self.op.hypervisor = einfo.get(constants.INISECT_INS, "hypervisor")
832 22b7f6f8 Thomas Thrainer
833 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_HYP):
834 22b7f6f8 Thomas Thrainer
      # use the export parameters but do not override the ones
835 22b7f6f8 Thomas Thrainer
      # specified by the user
836 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_HYP):
837 22b7f6f8 Thomas Thrainer
        if name not in self.op.hvparams:
838 22b7f6f8 Thomas Thrainer
          self.op.hvparams[name] = value
839 22b7f6f8 Thomas Thrainer
840 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_BEP):
841 22b7f6f8 Thomas Thrainer
      # use the parameters, without overriding
842 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_BEP):
843 22b7f6f8 Thomas Thrainer
        if name not in self.op.beparams:
844 22b7f6f8 Thomas Thrainer
          self.op.beparams[name] = value
845 22b7f6f8 Thomas Thrainer
        # Compatibility for the old "memory" be param
846 22b7f6f8 Thomas Thrainer
        if name == constants.BE_MEMORY:
847 22b7f6f8 Thomas Thrainer
          if constants.BE_MAXMEM not in self.op.beparams:
848 22b7f6f8 Thomas Thrainer
            self.op.beparams[constants.BE_MAXMEM] = value
849 22b7f6f8 Thomas Thrainer
          if constants.BE_MINMEM not in self.op.beparams:
850 22b7f6f8 Thomas Thrainer
            self.op.beparams[constants.BE_MINMEM] = value
851 22b7f6f8 Thomas Thrainer
    else:
852 22b7f6f8 Thomas Thrainer
      # try to read the parameters old style, from the main section
853 22b7f6f8 Thomas Thrainer
      for name in constants.BES_PARAMETERS:
854 22b7f6f8 Thomas Thrainer
        if (name not in self.op.beparams and
855 22b7f6f8 Thomas Thrainer
            einfo.has_option(constants.INISECT_INS, name)):
856 22b7f6f8 Thomas Thrainer
          self.op.beparams[name] = einfo.get(constants.INISECT_INS, name)
857 22b7f6f8 Thomas Thrainer
858 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_OSP):
859 22b7f6f8 Thomas Thrainer
      # use the parameters, without overriding
860 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_OSP):
861 22b7f6f8 Thomas Thrainer
        if name not in self.op.osparams:
862 22b7f6f8 Thomas Thrainer
          self.op.osparams[name] = value
863 22b7f6f8 Thomas Thrainer
864 6bce7ba2 Santi Raffa
    if einfo.has_section(constants.INISECT_OSP_PRIVATE):
865 6bce7ba2 Santi Raffa
      # use the parameters, without overriding
866 6bce7ba2 Santi Raffa
      for name, value in einfo.items(constants.INISECT_OSP_PRIVATE):
867 6bce7ba2 Santi Raffa
        if name not in self.op.osparams_private:
868 6bce7ba2 Santi Raffa
          self.op.osparams_private[name] = serializer.Private(value, descr=name)
869 6bce7ba2 Santi Raffa
870 22b7f6f8 Thomas Thrainer
  def _RevertToDefaults(self, cluster):
871 22b7f6f8 Thomas Thrainer
    """Revert the instance parameters to the default values.
872 22b7f6f8 Thomas Thrainer

873 22b7f6f8 Thomas Thrainer
    """
874 22b7f6f8 Thomas Thrainer
    # hvparams
875 22b7f6f8 Thomas Thrainer
    hv_defs = cluster.SimpleFillHV(self.op.hypervisor, self.op.os_type, {})
876 22b7f6f8 Thomas Thrainer
    for name in self.op.hvparams.keys():
877 22b7f6f8 Thomas Thrainer
      if name in hv_defs and hv_defs[name] == self.op.hvparams[name]:
878 22b7f6f8 Thomas Thrainer
        del self.op.hvparams[name]
879 22b7f6f8 Thomas Thrainer
    # beparams
880 22b7f6f8 Thomas Thrainer
    be_defs = cluster.SimpleFillBE({})
881 22b7f6f8 Thomas Thrainer
    for name in self.op.beparams.keys():
882 22b7f6f8 Thomas Thrainer
      if name in be_defs and be_defs[name] == self.op.beparams[name]:
883 22b7f6f8 Thomas Thrainer
        del self.op.beparams[name]
884 22b7f6f8 Thomas Thrainer
    # nic params
885 22b7f6f8 Thomas Thrainer
    nic_defs = cluster.SimpleFillNIC({})
886 22b7f6f8 Thomas Thrainer
    for nic in self.op.nics:
887 22b7f6f8 Thomas Thrainer
      for name in constants.NICS_PARAMETERS:
888 22b7f6f8 Thomas Thrainer
        if name in nic and name in nic_defs and nic[name] == nic_defs[name]:
889 22b7f6f8 Thomas Thrainer
          del nic[name]
890 22b7f6f8 Thomas Thrainer
    # osparams
891 22b7f6f8 Thomas Thrainer
    os_defs = cluster.SimpleFillOS(self.op.os_type, {})
892 22b7f6f8 Thomas Thrainer
    for name in self.op.osparams.keys():
893 22b7f6f8 Thomas Thrainer
      if name in os_defs and os_defs[name] == self.op.osparams[name]:
894 22b7f6f8 Thomas Thrainer
        del self.op.osparams[name]
895 22b7f6f8 Thomas Thrainer
896 6bce7ba2 Santi Raffa
    os_defs_ = cluster.SimpleFillOS(self.op.os_type, {},
897 6bce7ba2 Santi Raffa
                                    os_params_private={})
898 6bce7ba2 Santi Raffa
    for name in self.op.osparams_private.keys():
899 6bce7ba2 Santi Raffa
      if name in os_defs_ and os_defs_[name] == self.op.osparams_private[name]:
900 6bce7ba2 Santi Raffa
        del self.op.osparams_private[name]
901 6bce7ba2 Santi Raffa
902 22b7f6f8 Thomas Thrainer
  def _CalculateFileStorageDir(self):
903 22b7f6f8 Thomas Thrainer
    """Calculate final instance file storage dir.
904 22b7f6f8 Thomas Thrainer

905 22b7f6f8 Thomas Thrainer
    """
906 22b7f6f8 Thomas Thrainer
    # file storage dir calculation/check
907 22b7f6f8 Thomas Thrainer
    self.instance_file_storage_dir = None
908 22b7f6f8 Thomas Thrainer
    if self.op.disk_template in constants.DTS_FILEBASED:
909 22b7f6f8 Thomas Thrainer
      # build the full file storage dir path
910 22b7f6f8 Thomas Thrainer
      joinargs = []
911 22b7f6f8 Thomas Thrainer
912 ac156ecd Santi Raffa
      cfg_storage = None
913 ac156ecd Santi Raffa
      if self.op.disk_template == constants.DT_FILE:
914 ac156ecd Santi Raffa
        cfg_storage = self.cfg.GetFileStorageDir()
915 ac156ecd Santi Raffa
      elif self.op.disk_template == constants.DT_SHARED_FILE:
916 ac156ecd Santi Raffa
        cfg_storage = self.cfg.GetSharedFileStorageDir()
917 ac156ecd Santi Raffa
      elif self.op.disk_template == constants.DT_GLUSTER:
918 ac156ecd Santi Raffa
        cfg_storage = self.cfg.GetGlusterStorageDir()
919 ac156ecd Santi Raffa
920 ac156ecd Santi Raffa
      if not cfg_storage:
921 ac156ecd Santi Raffa
        raise errors.OpPrereqError(
922 ac156ecd Santi Raffa
          "Cluster file storage dir for {tpl} storage type not defined".format(
923 ac156ecd Santi Raffa
            tpl=repr(self.op.disk_template)
924 ac156ecd Santi Raffa
          ),
925 ac156ecd Santi Raffa
          errors.ECODE_STATE
926 ac156ecd Santi Raffa
      )
927 ac156ecd Santi Raffa
928 ac156ecd Santi Raffa
      joinargs.append(cfg_storage)
929 22b7f6f8 Thomas Thrainer
930 22b7f6f8 Thomas Thrainer
      if self.op.file_storage_dir is not None:
931 22b7f6f8 Thomas Thrainer
        joinargs.append(self.op.file_storage_dir)
932 22b7f6f8 Thomas Thrainer
933 ac156ecd Santi Raffa
      if self.op.disk_template != constants.DT_GLUSTER:
934 ac156ecd Santi Raffa
        joinargs.append(self.op.instance_name)
935 22b7f6f8 Thomas Thrainer
936 ac156ecd Santi Raffa
      if len(joinargs) > 1:
937 ac156ecd Santi Raffa
        # pylint: disable=W0142
938 ac156ecd Santi Raffa
        self.instance_file_storage_dir = utils.PathJoin(*joinargs)
939 ac156ecd Santi Raffa
      else:
940 ac156ecd Santi Raffa
        self.instance_file_storage_dir = joinargs[0]
941 22b7f6f8 Thomas Thrainer
942 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self): # pylint: disable=R0914
943 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
944 22b7f6f8 Thomas Thrainer

945 22b7f6f8 Thomas Thrainer
    """
946 8b9887c5 Petr Pudlak
    # Check that the optimistically acquired groups are correct wrt the
947 8b9887c5 Petr Pudlak
    # acquired nodes
948 8b9887c5 Petr Pudlak
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
949 8b9887c5 Petr Pudlak
    owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE))
950 8b9887c5 Petr Pudlak
    cur_groups = list(self.cfg.GetNodeGroupsFromNodes(owned_nodes))
951 8b9887c5 Petr Pudlak
    if not owned_groups.issuperset(cur_groups):
952 8b9887c5 Petr Pudlak
      raise errors.OpPrereqError("New instance %s's node groups changed since"
953 8b9887c5 Petr Pudlak
                                 " locks were acquired, current groups are"
954 8b9887c5 Petr Pudlak
                                 " are '%s', owning groups '%s'; retry the"
955 8b9887c5 Petr Pudlak
                                 " operation" %
956 8b9887c5 Petr Pudlak
                                 (self.op.instance_name,
957 8b9887c5 Petr Pudlak
                                  utils.CommaJoin(cur_groups),
958 8b9887c5 Petr Pudlak
                                  utils.CommaJoin(owned_groups)),
959 8b9887c5 Petr Pudlak
                                 errors.ECODE_STATE)
960 8b9887c5 Petr Pudlak
961 22b7f6f8 Thomas Thrainer
    self._CalculateFileStorageDir()
962 22b7f6f8 Thomas Thrainer
963 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
964 22b7f6f8 Thomas Thrainer
      export_info = self._ReadExportInfo()
965 22b7f6f8 Thomas Thrainer
      self._ReadExportParams(export_info)
966 22b7f6f8 Thomas Thrainer
      self._old_instance_name = export_info.get(constants.INISECT_INS, "name")
967 22b7f6f8 Thomas Thrainer
    else:
968 22b7f6f8 Thomas Thrainer
      self._old_instance_name = None
969 22b7f6f8 Thomas Thrainer
970 22b7f6f8 Thomas Thrainer
    if (not self.cfg.GetVGName() and
971 22b7f6f8 Thomas Thrainer
        self.op.disk_template not in constants.DTS_NOT_LVM):
972 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cluster does not support lvm-based"
973 22b7f6f8 Thomas Thrainer
                                 " instances", errors.ECODE_STATE)
974 22b7f6f8 Thomas Thrainer
975 22b7f6f8 Thomas Thrainer
    if (self.op.hypervisor is None or
976 22b7f6f8 Thomas Thrainer
        self.op.hypervisor == constants.VALUE_AUTO):
977 22b7f6f8 Thomas Thrainer
      self.op.hypervisor = self.cfg.GetHypervisorType()
978 22b7f6f8 Thomas Thrainer
979 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
980 22b7f6f8 Thomas Thrainer
    enabled_hvs = cluster.enabled_hypervisors
981 22b7f6f8 Thomas Thrainer
    if self.op.hypervisor not in enabled_hvs:
982 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Selected hypervisor (%s) not enabled in the"
983 22b7f6f8 Thomas Thrainer
                                 " cluster (%s)" %
984 22b7f6f8 Thomas Thrainer
                                 (self.op.hypervisor, ",".join(enabled_hvs)),
985 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
986 22b7f6f8 Thomas Thrainer
987 22b7f6f8 Thomas Thrainer
    # Check tag validity
988 22b7f6f8 Thomas Thrainer
    for tag in self.op.tags:
989 22b7f6f8 Thomas Thrainer
      objects.TaggableObject.ValidateTag(tag)
990 22b7f6f8 Thomas Thrainer
991 22b7f6f8 Thomas Thrainer
    # check hypervisor parameter syntax (locally)
992 22b7f6f8 Thomas Thrainer
    utils.ForceDictType(self.op.hvparams, constants.HVS_PARAMETER_TYPES)
993 22b7f6f8 Thomas Thrainer
    filled_hvp = cluster.SimpleFillHV(self.op.hypervisor, self.op.os_type,
994 22b7f6f8 Thomas Thrainer
                                      self.op.hvparams)
995 22b7f6f8 Thomas Thrainer
    hv_type = hypervisor.GetHypervisorClass(self.op.hypervisor)
996 22b7f6f8 Thomas Thrainer
    hv_type.CheckParameterSyntax(filled_hvp)
997 22b7f6f8 Thomas Thrainer
    self.hv_full = filled_hvp
998 22b7f6f8 Thomas Thrainer
    # check that we don't specify global parameters on an instance
999 5eacbcae Thomas Thrainer
    CheckParamsNotGlobal(self.op.hvparams, constants.HVC_GLOBALS, "hypervisor",
1000 5eacbcae Thomas Thrainer
                         "instance", "cluster")
1001 22b7f6f8 Thomas Thrainer
1002 22b7f6f8 Thomas Thrainer
    # fill and remember the beparams dict
1003 22b7f6f8 Thomas Thrainer
    self.be_full = _ComputeFullBeParams(self.op, cluster)
1004 22b7f6f8 Thomas Thrainer
1005 22b7f6f8 Thomas Thrainer
    # build os parameters
1006 6bce7ba2 Santi Raffa
    if self.op.osparams_private is None:
1007 6bce7ba2 Santi Raffa
      self.op.osparams_private = serializer.PrivateDict()
1008 6bce7ba2 Santi Raffa
    if self.op.osparams_secret is None:
1009 6bce7ba2 Santi Raffa
      self.op.osparams_secret = serializer.PrivateDict()
1010 6bce7ba2 Santi Raffa
1011 6bce7ba2 Santi Raffa
    self.os_full = cluster.SimpleFillOS(
1012 6bce7ba2 Santi Raffa
      self.op.os_type,
1013 6bce7ba2 Santi Raffa
      self.op.osparams,
1014 6bce7ba2 Santi Raffa
      os_params_private=self.op.osparams_private,
1015 6bce7ba2 Santi Raffa
      os_params_secret=self.op.osparams_secret
1016 6bce7ba2 Santi Raffa
    )
1017 22b7f6f8 Thomas Thrainer
1018 22b7f6f8 Thomas Thrainer
    # now that hvp/bep are in final format, let's reset to defaults,
1019 22b7f6f8 Thomas Thrainer
    # if told to do so
1020 22b7f6f8 Thomas Thrainer
    if self.op.identify_defaults:
1021 22b7f6f8 Thomas Thrainer
      self._RevertToDefaults(cluster)
1022 22b7f6f8 Thomas Thrainer
1023 22b7f6f8 Thomas Thrainer
    # NIC buildup
1024 22b7f6f8 Thomas Thrainer
    self.nics = _ComputeNics(self.op, cluster, self.check_ip, self.cfg,
1025 22b7f6f8 Thomas Thrainer
                             self.proc.GetECId())
1026 22b7f6f8 Thomas Thrainer
1027 22b7f6f8 Thomas Thrainer
    # disk checks/pre-build
1028 22b7f6f8 Thomas Thrainer
    default_vg = self.cfg.GetVGName()
1029 5eacbcae Thomas Thrainer
    self.disks = ComputeDisks(self.op, default_vg)
1030 22b7f6f8 Thomas Thrainer
1031 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
1032 22b7f6f8 Thomas Thrainer
      disk_images = []
1033 22b7f6f8 Thomas Thrainer
      for idx in range(len(self.disks)):
1034 22b7f6f8 Thomas Thrainer
        option = "disk%d_dump" % idx
1035 22b7f6f8 Thomas Thrainer
        if export_info.has_option(constants.INISECT_INS, option):
1036 22b7f6f8 Thomas Thrainer
          # FIXME: are the old os-es, disk sizes, etc. useful?
1037 22b7f6f8 Thomas Thrainer
          export_name = export_info.get(constants.INISECT_INS, option)
1038 22b7f6f8 Thomas Thrainer
          image = utils.PathJoin(self.op.src_path, export_name)
1039 22b7f6f8 Thomas Thrainer
          disk_images.append(image)
1040 22b7f6f8 Thomas Thrainer
        else:
1041 22b7f6f8 Thomas Thrainer
          disk_images.append(False)
1042 22b7f6f8 Thomas Thrainer
1043 22b7f6f8 Thomas Thrainer
      self.src_images = disk_images
1044 22b7f6f8 Thomas Thrainer
1045 22b7f6f8 Thomas Thrainer
      if self.op.instance_name == self._old_instance_name:
1046 22b7f6f8 Thomas Thrainer
        for idx, nic in enumerate(self.nics):
1047 22b7f6f8 Thomas Thrainer
          if nic.mac == constants.VALUE_AUTO:
1048 22b7f6f8 Thomas Thrainer
            nic_mac_ini = "nic%d_mac" % idx
1049 22b7f6f8 Thomas Thrainer
            nic.mac = export_info.get(constants.INISECT_INS, nic_mac_ini)
1050 22b7f6f8 Thomas Thrainer
1051 22b7f6f8 Thomas Thrainer
    # ENDIF: self.op.mode == constants.INSTANCE_IMPORT
1052 22b7f6f8 Thomas Thrainer
1053 22b7f6f8 Thomas Thrainer
    # ip ping checks (we use the same ip that was resolved in ExpandNames)
1054 22b7f6f8 Thomas Thrainer
    if self.op.ip_check:
1055 22b7f6f8 Thomas Thrainer
      if netutils.TcpPing(self.check_ip, constants.DEFAULT_NODED_PORT):
1056 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("IP %s of instance %s already in use" %
1057 22b7f6f8 Thomas Thrainer
                                   (self.check_ip, self.op.instance_name),
1058 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
1059 22b7f6f8 Thomas Thrainer
1060 22b7f6f8 Thomas Thrainer
    #### mac address generation
1061 22b7f6f8 Thomas Thrainer
    # By generating here the mac address both the allocator and the hooks get
1062 22b7f6f8 Thomas Thrainer
    # the real final mac address rather than the 'auto' or 'generate' value.
1063 22b7f6f8 Thomas Thrainer
    # There is a race condition between the generation and the instance object
1064 22b7f6f8 Thomas Thrainer
    # creation, which means that we know the mac is valid now, but we're not
1065 22b7f6f8 Thomas Thrainer
    # sure it will be when we actually add the instance. If things go bad
1066 22b7f6f8 Thomas Thrainer
    # adding the instance will abort because of a duplicate mac, and the
1067 22b7f6f8 Thomas Thrainer
    # creation job will fail.
1068 22b7f6f8 Thomas Thrainer
    for nic in self.nics:
1069 22b7f6f8 Thomas Thrainer
      if nic.mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
1070 22b7f6f8 Thomas Thrainer
        nic.mac = self.cfg.GenerateMAC(nic.network, self.proc.GetECId())
1071 22b7f6f8 Thomas Thrainer
1072 22b7f6f8 Thomas Thrainer
    #### allocator run
1073 22b7f6f8 Thomas Thrainer
1074 22b7f6f8 Thomas Thrainer
    if self.op.iallocator is not None:
1075 22b7f6f8 Thomas Thrainer
      self._RunAllocator()
1076 22b7f6f8 Thomas Thrainer
1077 22b7f6f8 Thomas Thrainer
    # Release all unneeded node locks
1078 1c3231aa Thomas Thrainer
    keep_locks = filter(None, [self.op.pnode_uuid, self.op.snode_uuid,
1079 1c3231aa Thomas Thrainer
                               self.op.src_node_uuid])
1080 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE, keep=keep_locks)
1081 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE_RES, keep=keep_locks)
1082 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE_ALLOC)
1083 8b9887c5 Petr Pudlak
    # Release all unneeded group locks
1084 8b9887c5 Petr Pudlak
    ReleaseLocks(self, locking.LEVEL_NODEGROUP,
1085 8b9887c5 Petr Pudlak
                 keep=self.cfg.GetNodeGroupsFromNodes(keep_locks))
1086 22b7f6f8 Thomas Thrainer
1087 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1088 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES)), \
1089 22b7f6f8 Thomas Thrainer
      "Node locks differ from node resource locks"
1090 22b7f6f8 Thomas Thrainer
1091 22b7f6f8 Thomas Thrainer
    #### node related checks
1092 22b7f6f8 Thomas Thrainer
1093 22b7f6f8 Thomas Thrainer
    # check primary node
1094 1c3231aa Thomas Thrainer
    self.pnode = pnode = self.cfg.GetNodeInfo(self.op.pnode_uuid)
1095 22b7f6f8 Thomas Thrainer
    assert self.pnode is not None, \
1096 1c3231aa Thomas Thrainer
      "Cannot retrieve locked node %s" % self.op.pnode_uuid
1097 22b7f6f8 Thomas Thrainer
    if pnode.offline:
1098 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot use offline primary node '%s'" %
1099 22b7f6f8 Thomas Thrainer
                                 pnode.name, errors.ECODE_STATE)
1100 22b7f6f8 Thomas Thrainer
    if pnode.drained:
1101 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot use drained primary node '%s'" %
1102 22b7f6f8 Thomas Thrainer
                                 pnode.name, errors.ECODE_STATE)
1103 22b7f6f8 Thomas Thrainer
    if not pnode.vm_capable:
1104 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot use non-vm_capable primary node"
1105 22b7f6f8 Thomas Thrainer
                                 " '%s'" % pnode.name, errors.ECODE_STATE)
1106 22b7f6f8 Thomas Thrainer
1107 22b7f6f8 Thomas Thrainer
    self.secondaries = []
1108 22b7f6f8 Thomas Thrainer
1109 22b7f6f8 Thomas Thrainer
    # Fill in any IPs from IP pools. This must happen here, because we need to
1110 22b7f6f8 Thomas Thrainer
    # know the nic's primary node, as specified by the iallocator
1111 22b7f6f8 Thomas Thrainer
    for idx, nic in enumerate(self.nics):
1112 22b7f6f8 Thomas Thrainer
      net_uuid = nic.network
1113 22b7f6f8 Thomas Thrainer
      if net_uuid is not None:
1114 22b7f6f8 Thomas Thrainer
        nobj = self.cfg.GetNetwork(net_uuid)
1115 1c3231aa Thomas Thrainer
        netparams = self.cfg.GetGroupNetParams(net_uuid, self.pnode.uuid)
1116 22b7f6f8 Thomas Thrainer
        if netparams is None:
1117 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("No netparams found for network"
1118 66222813 Thomas Thrainer
                                     " %s. Probably not connected to"
1119 22b7f6f8 Thomas Thrainer
                                     " node's %s nodegroup" %
1120 22b7f6f8 Thomas Thrainer
                                     (nobj.name, self.pnode.name),
1121 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
1122 22b7f6f8 Thomas Thrainer
        self.LogInfo("NIC/%d inherits netparams %s" %
1123 22b7f6f8 Thomas Thrainer
                     (idx, netparams.values()))
1124 22b7f6f8 Thomas Thrainer
        nic.nicparams = dict(netparams)
1125 22b7f6f8 Thomas Thrainer
        if nic.ip is not None:
1126 22b7f6f8 Thomas Thrainer
          if nic.ip.lower() == constants.NIC_IP_POOL:
1127 22b7f6f8 Thomas Thrainer
            try:
1128 22b7f6f8 Thomas Thrainer
              nic.ip = self.cfg.GenerateIp(net_uuid, self.proc.GetECId())
1129 22b7f6f8 Thomas Thrainer
            except errors.ReservationError:
1130 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("Unable to get a free IP for NIC %d"
1131 22b7f6f8 Thomas Thrainer
                                         " from the address pool" % idx,
1132 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_STATE)
1133 22b7f6f8 Thomas Thrainer
            self.LogInfo("Chose IP %s from network %s", nic.ip, nobj.name)
1134 22b7f6f8 Thomas Thrainer
          else:
1135 22b7f6f8 Thomas Thrainer
            try:
1136 031d2db1 Dimitris Aragiorgis
              self.cfg.ReserveIp(net_uuid, nic.ip, self.proc.GetECId(),
1137 031d2db1 Dimitris Aragiorgis
                                 check=self.op.conflicts_check)
1138 22b7f6f8 Thomas Thrainer
            except errors.ReservationError:
1139 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("IP address %s already in use"
1140 22b7f6f8 Thomas Thrainer
                                         " or does not belong to network %s" %
1141 22b7f6f8 Thomas Thrainer
                                         (nic.ip, nobj.name),
1142 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_NOTUNIQUE)
1143 22b7f6f8 Thomas Thrainer
1144 22b7f6f8 Thomas Thrainer
      # net is None, ip None or given
1145 22b7f6f8 Thomas Thrainer
      elif self.op.conflicts_check:
1146 1c3231aa Thomas Thrainer
        _CheckForConflictingIp(self, nic.ip, self.pnode.uuid)
1147 22b7f6f8 Thomas Thrainer
1148 22b7f6f8 Thomas Thrainer
    # mirror node verification
1149 22b7f6f8 Thomas Thrainer
    if self.op.disk_template in constants.DTS_INT_MIRROR:
1150 1c3231aa Thomas Thrainer
      if self.op.snode_uuid == pnode.uuid:
1151 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("The secondary node cannot be the"
1152 22b7f6f8 Thomas Thrainer
                                   " primary node", errors.ECODE_INVAL)
1153 1c3231aa Thomas Thrainer
      CheckNodeOnline(self, self.op.snode_uuid)
1154 1c3231aa Thomas Thrainer
      CheckNodeNotDrained(self, self.op.snode_uuid)
1155 1c3231aa Thomas Thrainer
      CheckNodeVmCapable(self, self.op.snode_uuid)
1156 1c3231aa Thomas Thrainer
      self.secondaries.append(self.op.snode_uuid)
1157 22b7f6f8 Thomas Thrainer
1158 1c3231aa Thomas Thrainer
      snode = self.cfg.GetNodeInfo(self.op.snode_uuid)
1159 22b7f6f8 Thomas Thrainer
      if pnode.group != snode.group:
1160 22b7f6f8 Thomas Thrainer
        self.LogWarning("The primary and secondary nodes are in two"
1161 22b7f6f8 Thomas Thrainer
                        " different node groups; the disk parameters"
1162 22b7f6f8 Thomas Thrainer
                        " from the first disk's node group will be"
1163 22b7f6f8 Thomas Thrainer
                        " used")
1164 22b7f6f8 Thomas Thrainer
1165 1bb99a33 Bernardo Dal Seno
    nodes = [pnode]
1166 1bb99a33 Bernardo Dal Seno
    if self.op.disk_template in constants.DTS_INT_MIRROR:
1167 1bb99a33 Bernardo Dal Seno
      nodes.append(snode)
1168 1bb99a33 Bernardo Dal Seno
    has_es = lambda n: IsExclusiveStorageEnabledNode(self.cfg, n)
1169 3f3ea14c Bernardo Dal Seno
    excl_stor = compat.any(map(has_es, nodes))
1170 3f3ea14c Bernardo Dal Seno
    if excl_stor and not self.op.disk_template in constants.DTS_EXCL_STORAGE:
1171 3f3ea14c Bernardo Dal Seno
      raise errors.OpPrereqError("Disk template %s not supported with"
1172 3f3ea14c Bernardo Dal Seno
                                 " exclusive storage" % self.op.disk_template,
1173 3f3ea14c Bernardo Dal Seno
                                 errors.ECODE_STATE)
1174 3f3ea14c Bernardo Dal Seno
    for disk in self.disks:
1175 7c848a6a Bernardo Dal Seno
      CheckSpindlesExclusiveStorage(disk, excl_stor, True)
1176 22b7f6f8 Thomas Thrainer
1177 1c3231aa Thomas Thrainer
    node_uuids = [pnode.uuid] + self.secondaries
1178 22b7f6f8 Thomas Thrainer
1179 22b7f6f8 Thomas Thrainer
    if not self.adopt_disks:
1180 22b7f6f8 Thomas Thrainer
      if self.op.disk_template == constants.DT_RBD:
1181 22b7f6f8 Thomas Thrainer
        # _CheckRADOSFreeSpace() is just a placeholder.
1182 22b7f6f8 Thomas Thrainer
        # Any function that checks prerequisites can be placed here.
1183 22b7f6f8 Thomas Thrainer
        # Check if there is enough space on the RADOS cluster.
1184 5eacbcae Thomas Thrainer
        CheckRADOSFreeSpace()
1185 22b7f6f8 Thomas Thrainer
      elif self.op.disk_template == constants.DT_EXT:
1186 22b7f6f8 Thomas Thrainer
        # FIXME: Function that checks prereqs if needed
1187 22b7f6f8 Thomas Thrainer
        pass
1188 d48c944b Helga Velroyen
      elif self.op.disk_template in constants.DTS_LVM:
1189 22b7f6f8 Thomas Thrainer
        # Check lv size requirements, if not adopting
1190 5eacbcae Thomas Thrainer
        req_sizes = ComputeDiskSizePerVG(self.op.disk_template, self.disks)
1191 1c3231aa Thomas Thrainer
        CheckNodesFreeDiskPerVG(self, node_uuids, req_sizes)
1192 4b92e992 Helga Velroyen
      else:
1193 4b92e992 Helga Velroyen
        # FIXME: add checks for other, non-adopting, non-lvm disk templates
1194 4b92e992 Helga Velroyen
        pass
1195 22b7f6f8 Thomas Thrainer
1196 22b7f6f8 Thomas Thrainer
    elif self.op.disk_template == constants.DT_PLAIN: # Check the adoption data
1197 22b7f6f8 Thomas Thrainer
      all_lvs = set(["%s/%s" % (disk[constants.IDISK_VG],
1198 22b7f6f8 Thomas Thrainer
                                disk[constants.IDISK_ADOPT])
1199 22b7f6f8 Thomas Thrainer
                     for disk in self.disks])
1200 22b7f6f8 Thomas Thrainer
      if len(all_lvs) != len(self.disks):
1201 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Duplicate volume names given for adoption",
1202 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1203 22b7f6f8 Thomas Thrainer
      for lv_name in all_lvs:
1204 22b7f6f8 Thomas Thrainer
        try:
1205 22b7f6f8 Thomas Thrainer
          # FIXME: lv_name here is "vg/lv" need to ensure that other calls
1206 22b7f6f8 Thomas Thrainer
          # to ReserveLV uses the same syntax
1207 22b7f6f8 Thomas Thrainer
          self.cfg.ReserveLV(lv_name, self.proc.GetECId())
1208 22b7f6f8 Thomas Thrainer
        except errors.ReservationError:
1209 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("LV named %s used by another instance" %
1210 22b7f6f8 Thomas Thrainer
                                     lv_name, errors.ECODE_NOTUNIQUE)
1211 22b7f6f8 Thomas Thrainer
1212 1c3231aa Thomas Thrainer
      vg_names = self.rpc.call_vg_list([pnode.uuid])[pnode.uuid]
1213 22b7f6f8 Thomas Thrainer
      vg_names.Raise("Cannot get VG information from node %s" % pnode.name)
1214 22b7f6f8 Thomas Thrainer
1215 1c3231aa Thomas Thrainer
      node_lvs = self.rpc.call_lv_list([pnode.uuid],
1216 1c3231aa Thomas Thrainer
                                       vg_names.payload.keys())[pnode.uuid]
1217 22b7f6f8 Thomas Thrainer
      node_lvs.Raise("Cannot get LV information from node %s" % pnode.name)
1218 22b7f6f8 Thomas Thrainer
      node_lvs = node_lvs.payload
1219 22b7f6f8 Thomas Thrainer
1220 22b7f6f8 Thomas Thrainer
      delta = all_lvs.difference(node_lvs.keys())
1221 22b7f6f8 Thomas Thrainer
      if delta:
1222 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing logical volume(s): %s" %
1223 22b7f6f8 Thomas Thrainer
                                   utils.CommaJoin(delta),
1224 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1225 22b7f6f8 Thomas Thrainer
      online_lvs = [lv for lv in all_lvs if node_lvs[lv][2]]
1226 22b7f6f8 Thomas Thrainer
      if online_lvs:
1227 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Online logical volumes found, cannot"
1228 22b7f6f8 Thomas Thrainer
                                   " adopt: %s" % utils.CommaJoin(online_lvs),
1229 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
1230 22b7f6f8 Thomas Thrainer
      # update the size of disk based on what is found
1231 22b7f6f8 Thomas Thrainer
      for dsk in self.disks:
1232 22b7f6f8 Thomas Thrainer
        dsk[constants.IDISK_SIZE] = \
1233 22b7f6f8 Thomas Thrainer
          int(float(node_lvs["%s/%s" % (dsk[constants.IDISK_VG],
1234 22b7f6f8 Thomas Thrainer
                                        dsk[constants.IDISK_ADOPT])][0]))
1235 22b7f6f8 Thomas Thrainer
1236 22b7f6f8 Thomas Thrainer
    elif self.op.disk_template == constants.DT_BLOCK:
1237 22b7f6f8 Thomas Thrainer
      # Normalize and de-duplicate device paths
1238 22b7f6f8 Thomas Thrainer
      all_disks = set([os.path.abspath(disk[constants.IDISK_ADOPT])
1239 22b7f6f8 Thomas Thrainer
                       for disk in self.disks])
1240 22b7f6f8 Thomas Thrainer
      if len(all_disks) != len(self.disks):
1241 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Duplicate disk names given for adoption",
1242 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1243 22b7f6f8 Thomas Thrainer
      baddisks = [d for d in all_disks
1244 22b7f6f8 Thomas Thrainer
                  if not d.startswith(constants.ADOPTABLE_BLOCKDEV_ROOT)]
1245 22b7f6f8 Thomas Thrainer
      if baddisks:
1246 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Device node(s) %s lie outside %s and"
1247 22b7f6f8 Thomas Thrainer
                                   " cannot be adopted" %
1248 22b7f6f8 Thomas Thrainer
                                   (utils.CommaJoin(baddisks),
1249 22b7f6f8 Thomas Thrainer
                                    constants.ADOPTABLE_BLOCKDEV_ROOT),
1250 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1251 22b7f6f8 Thomas Thrainer
1252 1c3231aa Thomas Thrainer
      node_disks = self.rpc.call_bdev_sizes([pnode.uuid],
1253 1c3231aa Thomas Thrainer
                                            list(all_disks))[pnode.uuid]
1254 22b7f6f8 Thomas Thrainer
      node_disks.Raise("Cannot get block device information from node %s" %
1255 22b7f6f8 Thomas Thrainer
                       pnode.name)
1256 22b7f6f8 Thomas Thrainer
      node_disks = node_disks.payload
1257 22b7f6f8 Thomas Thrainer
      delta = all_disks.difference(node_disks.keys())
1258 22b7f6f8 Thomas Thrainer
      if delta:
1259 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing block device(s): %s" %
1260 22b7f6f8 Thomas Thrainer
                                   utils.CommaJoin(delta),
1261 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1262 22b7f6f8 Thomas Thrainer
      for dsk in self.disks:
1263 22b7f6f8 Thomas Thrainer
        dsk[constants.IDISK_SIZE] = \
1264 22b7f6f8 Thomas Thrainer
          int(float(node_disks[dsk[constants.IDISK_ADOPT]]))
1265 22b7f6f8 Thomas Thrainer
1266 294254b1 Raffa Santi
    # Check disk access param to be compatible with specified hypervisor
1267 294254b1 Raffa Santi
    node_info = self.cfg.GetNodeInfo(self.op.pnode_uuid)
1268 294254b1 Raffa Santi
    node_group = self.cfg.GetNodeGroup(node_info.group)
1269 294254b1 Raffa Santi
    disk_params = self.cfg.GetGroupDiskParams(node_group)
1270 294254b1 Raffa Santi
    access_type = disk_params[self.op.disk_template].get(
1271 294254b1 Raffa Santi
      constants.RBD_ACCESS, constants.DISK_KERNELSPACE
1272 294254b1 Raffa Santi
    )
1273 294254b1 Raffa Santi
1274 294254b1 Raffa Santi
    if not IsValidDiskAccessModeCombination(self.op.hypervisor,
1275 294254b1 Raffa Santi
                                            self.op.disk_template,
1276 294254b1 Raffa Santi
                                            access_type):
1277 294254b1 Raffa Santi
      raise errors.OpPrereqError("Selected hypervisor (%s) cannot be"
1278 294254b1 Raffa Santi
                                 " used with %s disk access param" %
1279 294254b1 Raffa Santi
                                 (self.op.hypervisor, access_type),
1280 294254b1 Raffa Santi
                                  errors.ECODE_STATE)
1281 294254b1 Raffa Santi
1282 22b7f6f8 Thomas Thrainer
    # Verify instance specs
1283 22b7f6f8 Thomas Thrainer
    spindle_use = self.be_full.get(constants.BE_SPINDLE_USE, None)
1284 22b7f6f8 Thomas Thrainer
    ispec = {
1285 22b7f6f8 Thomas Thrainer
      constants.ISPEC_MEM_SIZE: self.be_full.get(constants.BE_MAXMEM, None),
1286 22b7f6f8 Thomas Thrainer
      constants.ISPEC_CPU_COUNT: self.be_full.get(constants.BE_VCPUS, None),
1287 22b7f6f8 Thomas Thrainer
      constants.ISPEC_DISK_COUNT: len(self.disks),
1288 22b7f6f8 Thomas Thrainer
      constants.ISPEC_DISK_SIZE: [disk[constants.IDISK_SIZE]
1289 22b7f6f8 Thomas Thrainer
                                  for disk in self.disks],
1290 22b7f6f8 Thomas Thrainer
      constants.ISPEC_NIC_COUNT: len(self.nics),
1291 22b7f6f8 Thomas Thrainer
      constants.ISPEC_SPINDLE_USE: spindle_use,
1292 22b7f6f8 Thomas Thrainer
      }
1293 22b7f6f8 Thomas Thrainer
1294 22b7f6f8 Thomas Thrainer
    group_info = self.cfg.GetNodeGroup(pnode.group)
1295 22b7f6f8 Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster, group_info)
1296 22b7f6f8 Thomas Thrainer
    res = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec,
1297 22b7f6f8 Thomas Thrainer
                                               self.op.disk_template)
1298 22b7f6f8 Thomas Thrainer
    if not self.op.ignore_ipolicy and res:
1299 22b7f6f8 Thomas Thrainer
      msg = ("Instance allocation to group %s (%s) violates policy: %s" %
1300 22b7f6f8 Thomas Thrainer
             (pnode.group, group_info.name, utils.CommaJoin(res)))
1301 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
1302 22b7f6f8 Thomas Thrainer
1303 1c3231aa Thomas Thrainer
    CheckHVParams(self, node_uuids, self.op.hypervisor, self.op.hvparams)
1304 22b7f6f8 Thomas Thrainer
1305 ff030c75 Jose A. Lopes
    CheckOSParams(self, True, node_uuids, self.op.os_type, self.os_full,
1306 ff030c75 Jose A. Lopes
                  self.op.force_variant)
1307 22b7f6f8 Thomas Thrainer
1308 1c3231aa Thomas Thrainer
    CheckNicsBridgesExist(self, self.nics, self.pnode.uuid)
1309 22b7f6f8 Thomas Thrainer
1310 22b7f6f8 Thomas Thrainer
    #TODO: _CheckExtParams (remotely)
1311 22b7f6f8 Thomas Thrainer
    # Check parameters for extstorage
1312 22b7f6f8 Thomas Thrainer
1313 22b7f6f8 Thomas Thrainer
    # memory check on primary node
1314 22b7f6f8 Thomas Thrainer
    #TODO(dynmem): use MINMEM for checking
1315 22b7f6f8 Thomas Thrainer
    if self.op.start:
1316 a295eb80 Helga Velroyen
      hvfull = objects.FillDict(cluster.hvparams.get(self.op.hypervisor, {}),
1317 a295eb80 Helga Velroyen
                                self.op.hvparams)
1318 1c3231aa Thomas Thrainer
      CheckNodeFreeMemory(self, self.pnode.uuid,
1319 5eacbcae Thomas Thrainer
                          "creating instance %s" % self.op.instance_name,
1320 5eacbcae Thomas Thrainer
                          self.be_full[constants.BE_MAXMEM],
1321 a295eb80 Helga Velroyen
                          self.op.hypervisor, hvfull)
1322 22b7f6f8 Thomas Thrainer
1323 1c3231aa Thomas Thrainer
    self.dry_run_result = list(node_uuids)
1324 22b7f6f8 Thomas Thrainer
1325 987ec378 Jose A. Lopes
  def _RemoveDegradedDisks(self, feedback_fn, disk_abort, instance):
1326 987ec378 Jose A. Lopes
    """Removes degraded disks and instance.
1327 987ec378 Jose A. Lopes

1328 987ec378 Jose A. Lopes
    It optionally checks whether disks are degraded.  If the disks are
1329 987ec378 Jose A. Lopes
    degraded, they are removed and the instance is also removed from
1330 987ec378 Jose A. Lopes
    the configuration.
1331 987ec378 Jose A. Lopes

1332 987ec378 Jose A. Lopes
    If L{disk_abort} is True, then the disks are considered degraded
1333 987ec378 Jose A. Lopes
    and removed, and the instance is removed from the configuration.
1334 987ec378 Jose A. Lopes

1335 987ec378 Jose A. Lopes
    If L{disk_abort} is False, then it first checks whether disks are
1336 987ec378 Jose A. Lopes
    degraded and, if so, it removes the disks and the instance is
1337 987ec378 Jose A. Lopes
    removed from the configuration.
1338 987ec378 Jose A. Lopes

1339 987ec378 Jose A. Lopes
    @type feedback_fn: callable
1340 987ec378 Jose A. Lopes
    @param feedback_fn: function used send feedback back to the caller
1341 987ec378 Jose A. Lopes

1342 987ec378 Jose A. Lopes
    @type disk_abort: boolean
1343 987ec378 Jose A. Lopes
    @param disk_abort:
1344 987ec378 Jose A. Lopes
      True if disks are degraded, False to first check if disks are
1345 987ec378 Jose A. Lopes
      degraded
1346 987ec378 Jose A. Lopes

1347 987ec378 Jose A. Lopes
    @type instance: L{objects.Instance}
1348 987ec378 Jose A. Lopes
    @param instance: instance containing the disks to check
1349 987ec378 Jose A. Lopes

1350 987ec378 Jose A. Lopes
    @rtype: NoneType
1351 987ec378 Jose A. Lopes
    @return: None
1352 987ec378 Jose A. Lopes
    @raise errors.OpPrereqError: if disks are degraded
1353 987ec378 Jose A. Lopes

1354 987ec378 Jose A. Lopes
    """
1355 987ec378 Jose A. Lopes
    if disk_abort:
1356 987ec378 Jose A. Lopes
      pass
1357 987ec378 Jose A. Lopes
    elif self.op.wait_for_sync:
1358 987ec378 Jose A. Lopes
      disk_abort = not WaitForSync(self, instance)
1359 987ec378 Jose A. Lopes
    elif instance.disk_template in constants.DTS_INT_MIRROR:
1360 987ec378 Jose A. Lopes
      # make sure the disks are not degraded (still sync-ing is ok)
1361 987ec378 Jose A. Lopes
      feedback_fn("* checking mirrors status")
1362 987ec378 Jose A. Lopes
      disk_abort = not WaitForSync(self, instance, oneshot=True)
1363 987ec378 Jose A. Lopes
    else:
1364 987ec378 Jose A. Lopes
      disk_abort = False
1365 987ec378 Jose A. Lopes
1366 987ec378 Jose A. Lopes
    if disk_abort:
1367 987ec378 Jose A. Lopes
      RemoveDisks(self, instance)
1368 987ec378 Jose A. Lopes
      self.cfg.RemoveInstance(instance.uuid)
1369 987ec378 Jose A. Lopes
      # Make sure the instance lock gets removed
1370 987ec378 Jose A. Lopes
      self.remove_locks[locking.LEVEL_INSTANCE] = instance.name
1371 987ec378 Jose A. Lopes
      raise errors.OpExecError("There are some degraded disks for"
1372 987ec378 Jose A. Lopes
                               " this instance")
1373 987ec378 Jose A. Lopes
1374 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1375 22b7f6f8 Thomas Thrainer
    """Create and add the instance to the cluster.
1376 22b7f6f8 Thomas Thrainer

1377 22b7f6f8 Thomas Thrainer
    """
1378 22b7f6f8 Thomas Thrainer
    assert not (self.owned_locks(locking.LEVEL_NODE_RES) -
1379 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
1380 22b7f6f8 Thomas Thrainer
      "Node locks differ from node resource locks"
1381 22b7f6f8 Thomas Thrainer
1382 22b7f6f8 Thomas Thrainer
    ht_kind = self.op.hypervisor
1383 22b7f6f8 Thomas Thrainer
    if ht_kind in constants.HTS_REQ_PORT:
1384 22b7f6f8 Thomas Thrainer
      network_port = self.cfg.AllocatePort()
1385 22b7f6f8 Thomas Thrainer
    else:
1386 22b7f6f8 Thomas Thrainer
      network_port = None
1387 22b7f6f8 Thomas Thrainer
1388 da4a52a3 Thomas Thrainer
    instance_uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
1389 da4a52a3 Thomas Thrainer
1390 22b7f6f8 Thomas Thrainer
    # This is ugly but we got a chicken-egg problem here
1391 22b7f6f8 Thomas Thrainer
    # We can only take the group disk parameters, as the instance
1392 22b7f6f8 Thomas Thrainer
    # has no disks yet (we are generating them right here).
1393 1c3231aa Thomas Thrainer
    nodegroup = self.cfg.GetNodeGroup(self.pnode.group)
1394 5eacbcae Thomas Thrainer
    disks = GenerateDiskTemplate(self,
1395 5eacbcae Thomas Thrainer
                                 self.op.disk_template,
1396 da4a52a3 Thomas Thrainer
                                 instance_uuid, self.pnode.uuid,
1397 5eacbcae Thomas Thrainer
                                 self.secondaries,
1398 5eacbcae Thomas Thrainer
                                 self.disks,
1399 5eacbcae Thomas Thrainer
                                 self.instance_file_storage_dir,
1400 5eacbcae Thomas Thrainer
                                 self.op.file_driver,
1401 5eacbcae Thomas Thrainer
                                 0,
1402 5eacbcae Thomas Thrainer
                                 feedback_fn,
1403 5eacbcae Thomas Thrainer
                                 self.cfg.GetGroupDiskParams(nodegroup))
1404 22b7f6f8 Thomas Thrainer
1405 1c4910f7 Jose A. Lopes
    if self.op.os_type is None:
1406 1c4910f7 Jose A. Lopes
      os_type = ""
1407 1c4910f7 Jose A. Lopes
    else:
1408 1c4910f7 Jose A. Lopes
      os_type = self.op.os_type
1409 1c4910f7 Jose A. Lopes
1410 da4a52a3 Thomas Thrainer
    iobj = objects.Instance(name=self.op.instance_name,
1411 da4a52a3 Thomas Thrainer
                            uuid=instance_uuid,
1412 1c4910f7 Jose A. Lopes
                            os=os_type,
1413 1c3231aa Thomas Thrainer
                            primary_node=self.pnode.uuid,
1414 22b7f6f8 Thomas Thrainer
                            nics=self.nics, disks=disks,
1415 22b7f6f8 Thomas Thrainer
                            disk_template=self.op.disk_template,
1416 1d4a4b26 Thomas Thrainer
                            disks_active=False,
1417 22b7f6f8 Thomas Thrainer
                            admin_state=constants.ADMINST_DOWN,
1418 22b7f6f8 Thomas Thrainer
                            network_port=network_port,
1419 22b7f6f8 Thomas Thrainer
                            beparams=self.op.beparams,
1420 22b7f6f8 Thomas Thrainer
                            hvparams=self.op.hvparams,
1421 22b7f6f8 Thomas Thrainer
                            hypervisor=self.op.hypervisor,
1422 22b7f6f8 Thomas Thrainer
                            osparams=self.op.osparams,
1423 6bce7ba2 Santi Raffa
                            osparams_private=self.op.osparams_private,
1424 22b7f6f8 Thomas Thrainer
                            )
1425 22b7f6f8 Thomas Thrainer
1426 22b7f6f8 Thomas Thrainer
    if self.op.tags:
1427 22b7f6f8 Thomas Thrainer
      for tag in self.op.tags:
1428 22b7f6f8 Thomas Thrainer
        iobj.AddTag(tag)
1429 22b7f6f8 Thomas Thrainer
1430 22b7f6f8 Thomas Thrainer
    if self.adopt_disks:
1431 22b7f6f8 Thomas Thrainer
      if self.op.disk_template == constants.DT_PLAIN:
1432 22b7f6f8 Thomas Thrainer
        # rename LVs to the newly-generated names; we need to construct
1433 22b7f6f8 Thomas Thrainer
        # 'fake' LV disks with the old data, plus the new unique_id
1434 22b7f6f8 Thomas Thrainer
        tmp_disks = [objects.Disk.FromDict(v.ToDict()) for v in disks]
1435 22b7f6f8 Thomas Thrainer
        rename_to = []
1436 22b7f6f8 Thomas Thrainer
        for t_dsk, a_dsk in zip(tmp_disks, self.disks):
1437 22b7f6f8 Thomas Thrainer
          rename_to.append(t_dsk.logical_id)
1438 22b7f6f8 Thomas Thrainer
          t_dsk.logical_id = (t_dsk.logical_id[0], a_dsk[constants.IDISK_ADOPT])
1439 1c3231aa Thomas Thrainer
        result = self.rpc.call_blockdev_rename(self.pnode.uuid,
1440 22b7f6f8 Thomas Thrainer
                                               zip(tmp_disks, rename_to))
1441 22b7f6f8 Thomas Thrainer
        result.Raise("Failed to rename adoped LVs")
1442 22b7f6f8 Thomas Thrainer
    else:
1443 22b7f6f8 Thomas Thrainer
      feedback_fn("* creating instance disks...")
1444 22b7f6f8 Thomas Thrainer
      try:
1445 5eacbcae Thomas Thrainer
        CreateDisks(self, iobj)
1446 22b7f6f8 Thomas Thrainer
      except errors.OpExecError:
1447 22b7f6f8 Thomas Thrainer
        self.LogWarning("Device creation failed")
1448 462d6658 Hrvoje Ribicic
        self.cfg.ReleaseDRBDMinors(instance_uuid)
1449 22b7f6f8 Thomas Thrainer
        raise
1450 22b7f6f8 Thomas Thrainer
1451 d0d7d7cf Thomas Thrainer
    feedback_fn("adding instance %s to cluster config" % self.op.instance_name)
1452 22b7f6f8 Thomas Thrainer
1453 22b7f6f8 Thomas Thrainer
    self.cfg.AddInstance(iobj, self.proc.GetECId())
1454 22b7f6f8 Thomas Thrainer
1455 22b7f6f8 Thomas Thrainer
    # Declare that we don't want to remove the instance lock anymore, as we've
1456 22b7f6f8 Thomas Thrainer
    # added the instance to the config
1457 22b7f6f8 Thomas Thrainer
    del self.remove_locks[locking.LEVEL_INSTANCE]
1458 22b7f6f8 Thomas Thrainer
1459 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
1460 22b7f6f8 Thomas Thrainer
      # Release unused nodes
1461 1c3231aa Thomas Thrainer
      ReleaseLocks(self, locking.LEVEL_NODE, keep=[self.op.src_node_uuid])
1462 22b7f6f8 Thomas Thrainer
    else:
1463 22b7f6f8 Thomas Thrainer
      # Release all nodes
1464 5eacbcae Thomas Thrainer
      ReleaseLocks(self, locking.LEVEL_NODE)
1465 22b7f6f8 Thomas Thrainer
1466 987ec378 Jose A. Lopes
    # Wipe disks
1467 22b7f6f8 Thomas Thrainer
    disk_abort = False
1468 22b7f6f8 Thomas Thrainer
    if not self.adopt_disks and self.cfg.GetClusterInfo().prealloc_wipe_disks:
1469 22b7f6f8 Thomas Thrainer
      feedback_fn("* wiping instance disks...")
1470 22b7f6f8 Thomas Thrainer
      try:
1471 5eacbcae Thomas Thrainer
        WipeDisks(self, iobj)
1472 22b7f6f8 Thomas Thrainer
      except errors.OpExecError, err:
1473 22b7f6f8 Thomas Thrainer
        logging.exception("Wiping disks failed")
1474 22b7f6f8 Thomas Thrainer
        self.LogWarning("Wiping instance disks failed (%s)", err)
1475 22b7f6f8 Thomas Thrainer
        disk_abort = True
1476 22b7f6f8 Thomas Thrainer
1477 987ec378 Jose A. Lopes
    self._RemoveDegradedDisks(feedback_fn, disk_abort, iobj)
1478 22b7f6f8 Thomas Thrainer
1479 2329ffdd Jose A. Lopes
    # Image disks
1480 2329ffdd Jose A. Lopes
    os_image = objects.GetOSImage(iobj.osparams)
1481 2329ffdd Jose A. Lopes
    disk_abort = False
1482 2329ffdd Jose A. Lopes
1483 2329ffdd Jose A. Lopes
    if not self.adopt_disks and os_image is not None:
1484 2329ffdd Jose A. Lopes
      feedback_fn("* imaging instance disks...")
1485 2329ffdd Jose A. Lopes
      try:
1486 2329ffdd Jose A. Lopes
        ImageDisks(self, iobj, os_image)
1487 2329ffdd Jose A. Lopes
      except errors.OpExecError, err:
1488 2329ffdd Jose A. Lopes
        logging.exception("Imaging disks failed")
1489 2329ffdd Jose A. Lopes
        self.LogWarning("Imaging instance disks failed (%s)", err)
1490 2329ffdd Jose A. Lopes
        disk_abort = True
1491 2329ffdd Jose A. Lopes
1492 2329ffdd Jose A. Lopes
    self._RemoveDegradedDisks(feedback_fn, disk_abort, iobj)
1493 22b7f6f8 Thomas Thrainer
1494 1d4a4b26 Thomas Thrainer
    # instance disks are now active
1495 1d4a4b26 Thomas Thrainer
    iobj.disks_active = True
1496 1d4a4b26 Thomas Thrainer
1497 22b7f6f8 Thomas Thrainer
    # Release all node resource locks
1498 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE_RES)
1499 22b7f6f8 Thomas Thrainer
1500 22b7f6f8 Thomas Thrainer
    if iobj.disk_template != constants.DT_DISKLESS and not self.adopt_disks:
1501 22b7f6f8 Thomas Thrainer
      if self.op.mode == constants.INSTANCE_CREATE:
1502 1c4910f7 Jose A. Lopes
        os_image = objects.GetOSImage(self.op.osparams)
1503 1c4910f7 Jose A. Lopes
1504 1c4910f7 Jose A. Lopes
        if os_image is None and not self.op.no_install:
1505 22b7f6f8 Thomas Thrainer
          pause_sync = (iobj.disk_template in constants.DTS_INT_MIRROR and
1506 22b7f6f8 Thomas Thrainer
                        not self.op.wait_for_sync)
1507 22b7f6f8 Thomas Thrainer
          if pause_sync:
1508 22b7f6f8 Thomas Thrainer
            feedback_fn("* pausing disk sync to install instance OS")
1509 1c3231aa Thomas Thrainer
            result = self.rpc.call_blockdev_pause_resume_sync(self.pnode.uuid,
1510 22b7f6f8 Thomas Thrainer
                                                              (iobj.disks,
1511 22b7f6f8 Thomas Thrainer
                                                               iobj), True)
1512 22b7f6f8 Thomas Thrainer
            for idx, success in enumerate(result.payload):
1513 22b7f6f8 Thomas Thrainer
              if not success:
1514 22b7f6f8 Thomas Thrainer
                logging.warn("pause-sync of instance %s for disk %d failed",
1515 d0d7d7cf Thomas Thrainer
                             self.op.instance_name, idx)
1516 22b7f6f8 Thomas Thrainer
1517 22b7f6f8 Thomas Thrainer
          feedback_fn("* running the instance OS create scripts...")
1518 22b7f6f8 Thomas Thrainer
          # FIXME: pass debug option from opcode to backend
1519 22b7f6f8 Thomas Thrainer
          os_add_result = \
1520 6bce7ba2 Santi Raffa
            self.rpc.call_instance_os_add(self.pnode.uuid,
1521 6bce7ba2 Santi Raffa
                                          (iobj, self.op.osparams_secret),
1522 6bce7ba2 Santi Raffa
                                          False,
1523 22b7f6f8 Thomas Thrainer
                                          self.op.debug_level)
1524 22b7f6f8 Thomas Thrainer
          if pause_sync:
1525 22b7f6f8 Thomas Thrainer
            feedback_fn("* resuming disk sync")
1526 1c3231aa Thomas Thrainer
            result = self.rpc.call_blockdev_pause_resume_sync(self.pnode.uuid,
1527 22b7f6f8 Thomas Thrainer
                                                              (iobj.disks,
1528 22b7f6f8 Thomas Thrainer
                                                               iobj), False)
1529 22b7f6f8 Thomas Thrainer
            for idx, success in enumerate(result.payload):
1530 22b7f6f8 Thomas Thrainer
              if not success:
1531 22b7f6f8 Thomas Thrainer
                logging.warn("resume-sync of instance %s for disk %d failed",
1532 d0d7d7cf Thomas Thrainer
                             self.op.instance_name, idx)
1533 22b7f6f8 Thomas Thrainer
1534 22b7f6f8 Thomas Thrainer
          os_add_result.Raise("Could not add os for instance %s"
1535 d0d7d7cf Thomas Thrainer
                              " on node %s" % (self.op.instance_name,
1536 d0d7d7cf Thomas Thrainer
                                               self.pnode.name))
1537 22b7f6f8 Thomas Thrainer
1538 22b7f6f8 Thomas Thrainer
      else:
1539 22b7f6f8 Thomas Thrainer
        if self.op.mode == constants.INSTANCE_IMPORT:
1540 22b7f6f8 Thomas Thrainer
          feedback_fn("* running the instance OS import scripts...")
1541 22b7f6f8 Thomas Thrainer
1542 22b7f6f8 Thomas Thrainer
          transfers = []
1543 22b7f6f8 Thomas Thrainer
1544 22b7f6f8 Thomas Thrainer
          for idx, image in enumerate(self.src_images):
1545 22b7f6f8 Thomas Thrainer
            if not image:
1546 22b7f6f8 Thomas Thrainer
              continue
1547 22b7f6f8 Thomas Thrainer
1548 3473fb81 Jose A. Lopes
            if iobj.os:
1549 3473fb81 Jose A. Lopes
              dst_io = constants.IEIO_SCRIPT
1550 3473fb81 Jose A. Lopes
              dst_ioargs = ((iobj.disks[idx], iobj), idx)
1551 3473fb81 Jose A. Lopes
            else:
1552 3473fb81 Jose A. Lopes
              dst_io = constants.IEIO_RAW_DISK
1553 3473fb81 Jose A. Lopes
              dst_ioargs = (iobj.disks[idx], iobj)
1554 3473fb81 Jose A. Lopes
1555 22b7f6f8 Thomas Thrainer
            # FIXME: pass debug option from opcode to backend
1556 22b7f6f8 Thomas Thrainer
            dt = masterd.instance.DiskTransfer("disk/%s" % idx,
1557 22b7f6f8 Thomas Thrainer
                                               constants.IEIO_FILE, (image, ),
1558 3473fb81 Jose A. Lopes
                                               dst_io, dst_ioargs,
1559 22b7f6f8 Thomas Thrainer
                                               None)
1560 22b7f6f8 Thomas Thrainer
            transfers.append(dt)
1561 22b7f6f8 Thomas Thrainer
1562 22b7f6f8 Thomas Thrainer
          import_result = \
1563 22b7f6f8 Thomas Thrainer
            masterd.instance.TransferInstanceData(self, feedback_fn,
1564 1c3231aa Thomas Thrainer
                                                  self.op.src_node_uuid,
1565 1c3231aa Thomas Thrainer
                                                  self.pnode.uuid,
1566 22b7f6f8 Thomas Thrainer
                                                  self.pnode.secondary_ip,
1567 51d7ac96 Thomas Thrainer
                                                  self.op.compress,
1568 22b7f6f8 Thomas Thrainer
                                                  iobj, transfers)
1569 22b7f6f8 Thomas Thrainer
          if not compat.all(import_result):
1570 22b7f6f8 Thomas Thrainer
            self.LogWarning("Some disks for instance %s on node %s were not"
1571 d0d7d7cf Thomas Thrainer
                            " imported successfully" % (self.op.instance_name,
1572 d0d7d7cf Thomas Thrainer
                                                        self.pnode.name))
1573 22b7f6f8 Thomas Thrainer
1574 22b7f6f8 Thomas Thrainer
          rename_from = self._old_instance_name
1575 22b7f6f8 Thomas Thrainer
1576 22b7f6f8 Thomas Thrainer
        elif self.op.mode == constants.INSTANCE_REMOTE_IMPORT:
1577 22b7f6f8 Thomas Thrainer
          feedback_fn("* preparing remote import...")
1578 22b7f6f8 Thomas Thrainer
          # The source cluster will stop the instance before attempting to make
1579 22b7f6f8 Thomas Thrainer
          # a connection. In some cases stopping an instance can take a long
1580 22b7f6f8 Thomas Thrainer
          # time, hence the shutdown timeout is added to the connection
1581 22b7f6f8 Thomas Thrainer
          # timeout.
1582 22b7f6f8 Thomas Thrainer
          connect_timeout = (constants.RIE_CONNECT_TIMEOUT +
1583 22b7f6f8 Thomas Thrainer
                             self.op.source_shutdown_timeout)
1584 22b7f6f8 Thomas Thrainer
          timeouts = masterd.instance.ImportExportTimeouts(connect_timeout)
1585 22b7f6f8 Thomas Thrainer
1586 1c3231aa Thomas Thrainer
          assert iobj.primary_node == self.pnode.uuid
1587 22b7f6f8 Thomas Thrainer
          disk_results = \
1588 22b7f6f8 Thomas Thrainer
            masterd.instance.RemoteImport(self, feedback_fn, iobj, self.pnode,
1589 22b7f6f8 Thomas Thrainer
                                          self.source_x509_ca,
1590 88acff3f Thomas Thrainer
                                          self._cds, self.op.compress, timeouts)
1591 22b7f6f8 Thomas Thrainer
          if not compat.all(disk_results):
1592 22b7f6f8 Thomas Thrainer
            # TODO: Should the instance still be started, even if some disks
1593 22b7f6f8 Thomas Thrainer
            # failed to import (valid for local imports, too)?
1594 22b7f6f8 Thomas Thrainer
            self.LogWarning("Some disks for instance %s on node %s were not"
1595 d0d7d7cf Thomas Thrainer
                            " imported successfully" % (self.op.instance_name,
1596 d0d7d7cf Thomas Thrainer
                                                        self.pnode.name))
1597 22b7f6f8 Thomas Thrainer
1598 22b7f6f8 Thomas Thrainer
          rename_from = self.source_instance_name
1599 22b7f6f8 Thomas Thrainer
1600 22b7f6f8 Thomas Thrainer
        else:
1601 22b7f6f8 Thomas Thrainer
          # also checked in the prereq part
1602 22b7f6f8 Thomas Thrainer
          raise errors.ProgrammerError("Unknown OS initialization mode '%s'"
1603 22b7f6f8 Thomas Thrainer
                                       % self.op.mode)
1604 22b7f6f8 Thomas Thrainer
1605 d0d7d7cf Thomas Thrainer
        assert iobj.name == self.op.instance_name
1606 83fc0ab6 Jose A. Lopes
1607 83fc0ab6 Jose A. Lopes
        # Run rename script on newly imported instance
1608 83fc0ab6 Jose A. Lopes
        if iobj.os:
1609 83fc0ab6 Jose A. Lopes
          feedback_fn("Running rename script for %s" % self.op.instance_name)
1610 83fc0ab6 Jose A. Lopes
          result = self.rpc.call_instance_run_rename(self.pnode.uuid, iobj,
1611 83fc0ab6 Jose A. Lopes
                                                     rename_from,
1612 83fc0ab6 Jose A. Lopes
                                                     self.op.debug_level)
1613 83fc0ab6 Jose A. Lopes
          result.Warn("Failed to run rename script for %s on node %s" %
1614 83fc0ab6 Jose A. Lopes
                      (self.op.instance_name, self.pnode.name), self.LogWarning)
1615 22b7f6f8 Thomas Thrainer
1616 22b7f6f8 Thomas Thrainer
    assert not self.owned_locks(locking.LEVEL_NODE_RES)
1617 22b7f6f8 Thomas Thrainer
1618 22b7f6f8 Thomas Thrainer
    if self.op.start:
1619 22b7f6f8 Thomas Thrainer
      iobj.admin_state = constants.ADMINST_UP
1620 22b7f6f8 Thomas Thrainer
      self.cfg.Update(iobj, feedback_fn)
1621 d0d7d7cf Thomas Thrainer
      logging.info("Starting instance %s on node %s", self.op.instance_name,
1622 d0d7d7cf Thomas Thrainer
                   self.pnode.name)
1623 22b7f6f8 Thomas Thrainer
      feedback_fn("* starting instance...")
1624 1c3231aa Thomas Thrainer
      result = self.rpc.call_instance_start(self.pnode.uuid, (iobj, None, None),
1625 22b7f6f8 Thomas Thrainer
                                            False, self.op.reason)
1626 22b7f6f8 Thomas Thrainer
      result.Raise("Could not start instance")
1627 22b7f6f8 Thomas Thrainer
1628 8c58dc45 Jose A. Lopes
    UpdateMetadata(feedback_fn, self.rpc, iobj,
1629 8c58dc45 Jose A. Lopes
                   osparams_private=self.op.osparams_private,
1630 8c58dc45 Jose A. Lopes
                   osparams_secret=self.op.osparams_secret)
1631 8c58dc45 Jose A. Lopes
1632 b7a990e3 Hrvoje Ribicic
    return self.cfg.GetNodeNames(list(iobj.all_nodes))
1633 22b7f6f8 Thomas Thrainer
1634 22b7f6f8 Thomas Thrainer
1635 22b7f6f8 Thomas Thrainer
class LUInstanceRename(LogicalUnit):
1636 22b7f6f8 Thomas Thrainer
  """Rename an instance.
1637 22b7f6f8 Thomas Thrainer

1638 22b7f6f8 Thomas Thrainer
  """
1639 22b7f6f8 Thomas Thrainer
  HPATH = "instance-rename"
1640 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1641 22b7f6f8 Thomas Thrainer
1642 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1643 22b7f6f8 Thomas Thrainer
    """Check arguments.
1644 22b7f6f8 Thomas Thrainer

1645 22b7f6f8 Thomas Thrainer
    """
1646 22b7f6f8 Thomas Thrainer
    if self.op.ip_check and not self.op.name_check:
1647 22b7f6f8 Thomas Thrainer
      # TODO: make the ip check more flexible and not depend on the name check
1648 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("IP address check requires a name check",
1649 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1650 22b7f6f8 Thomas Thrainer
1651 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1652 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1653 22b7f6f8 Thomas Thrainer

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

1656 22b7f6f8 Thomas Thrainer
    """
1657 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1658 22b7f6f8 Thomas Thrainer
    env["INSTANCE_NEW_NAME"] = self.op.new_name
1659 22b7f6f8 Thomas Thrainer
    return env
1660 22b7f6f8 Thomas Thrainer
1661 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1662 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1663 22b7f6f8 Thomas Thrainer

1664 22b7f6f8 Thomas Thrainer
    """
1665 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
1666 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1667 22b7f6f8 Thomas Thrainer
1668 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1669 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1670 22b7f6f8 Thomas Thrainer

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

1673 22b7f6f8 Thomas Thrainer
    """
1674 da4a52a3 Thomas Thrainer
    (self.op.instance_uuid, self.op.instance_name) = \
1675 da4a52a3 Thomas Thrainer
      ExpandInstanceUuidAndName(self.cfg, self.op.instance_uuid,
1676 da4a52a3 Thomas Thrainer
                                self.op.instance_name)
1677 da4a52a3 Thomas Thrainer
    instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1678 22b7f6f8 Thomas Thrainer
    assert instance is not None
1679 1f7c8208 Helga Velroyen
1680 1f7c8208 Helga Velroyen
    # It should actually not happen that an instance is running with a disabled
1681 1f7c8208 Helga Velroyen
    # disk template, but in case it does, the renaming of file-based instances
1682 1f7c8208 Helga Velroyen
    # will fail horribly. Thus, we test it before.
1683 1f7c8208 Helga Velroyen
    if (instance.disk_template in constants.DTS_FILEBASED and
1684 1f7c8208 Helga Velroyen
        self.op.new_name != instance.name):
1685 1f7c8208 Helga Velroyen
      CheckDiskTemplateEnabled(self.cfg.GetClusterInfo(),
1686 1f7c8208 Helga Velroyen
                               instance.disk_template)
1687 1f7c8208 Helga Velroyen
1688 5eacbcae Thomas Thrainer
    CheckNodeOnline(self, instance.primary_node)
1689 5eacbcae Thomas Thrainer
    CheckInstanceState(self, instance, INSTANCE_NOT_RUNNING,
1690 5eacbcae Thomas Thrainer
                       msg="cannot rename")
1691 22b7f6f8 Thomas Thrainer
    self.instance = instance
1692 22b7f6f8 Thomas Thrainer
1693 22b7f6f8 Thomas Thrainer
    new_name = self.op.new_name
1694 22b7f6f8 Thomas Thrainer
    if self.op.name_check:
1695 22b7f6f8 Thomas Thrainer
      hostname = _CheckHostnameSane(self, new_name)
1696 22b7f6f8 Thomas Thrainer
      new_name = self.op.new_name = hostname.name
1697 22b7f6f8 Thomas Thrainer
      if (self.op.ip_check and
1698 22b7f6f8 Thomas Thrainer
          netutils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT)):
1699 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("IP %s of instance %s already in use" %
1700 22b7f6f8 Thomas Thrainer
                                   (hostname.ip, new_name),
1701 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
1702 22b7f6f8 Thomas Thrainer
1703 da4a52a3 Thomas Thrainer
    instance_names = [inst.name for
1704 da4a52a3 Thomas Thrainer
                      inst in self.cfg.GetAllInstancesInfo().values()]
1705 da4a52a3 Thomas Thrainer
    if new_name in instance_names and new_name != instance.name:
1706 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
1707 22b7f6f8 Thomas Thrainer
                                 new_name, errors.ECODE_EXISTS)
1708 22b7f6f8 Thomas Thrainer
1709 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1710 22b7f6f8 Thomas Thrainer
    """Rename the instance.
1711 22b7f6f8 Thomas Thrainer

1712 22b7f6f8 Thomas Thrainer
    """
1713 d0d7d7cf Thomas Thrainer
    old_name = self.instance.name
1714 22b7f6f8 Thomas Thrainer
1715 22b7f6f8 Thomas Thrainer
    rename_file_storage = False
1716 845b7ed1 Santi Raffa
    if (self.instance.disk_template in (constants.DT_FILE,
1717 845b7ed1 Santi Raffa
                                        constants.DT_SHARED_FILE) and
1718 d0d7d7cf Thomas Thrainer
        self.op.new_name != self.instance.name):
1719 d0d7d7cf Thomas Thrainer
      old_file_storage_dir = os.path.dirname(
1720 d0d7d7cf Thomas Thrainer
                               self.instance.disks[0].logical_id[1])
1721 22b7f6f8 Thomas Thrainer
      rename_file_storage = True
1722 22b7f6f8 Thomas Thrainer
1723 da4a52a3 Thomas Thrainer
    self.cfg.RenameInstance(self.instance.uuid, self.op.new_name)
1724 22b7f6f8 Thomas Thrainer
    # Change the instance lock. This is definitely safe while we hold the BGL.
1725 22b7f6f8 Thomas Thrainer
    # Otherwise the new lock would have to be added in acquired mode.
1726 22b7f6f8 Thomas Thrainer
    assert self.REQ_BGL
1727 22b7f6f8 Thomas Thrainer
    assert locking.BGL in self.owned_locks(locking.LEVEL_CLUSTER)
1728 22b7f6f8 Thomas Thrainer
1729 22b7f6f8 Thomas Thrainer
    # re-read the instance from the configuration after rename
1730 da4a52a3 Thomas Thrainer
    renamed_inst = self.cfg.GetInstanceInfo(self.instance.uuid)
1731 22b7f6f8 Thomas Thrainer
1732 22b7f6f8 Thomas Thrainer
    if rename_file_storage:
1733 d0d7d7cf Thomas Thrainer
      new_file_storage_dir = os.path.dirname(
1734 d0d7d7cf Thomas Thrainer
                               renamed_inst.disks[0].logical_id[1])
1735 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_file_storage_dir_rename(renamed_inst.primary_node,
1736 22b7f6f8 Thomas Thrainer
                                                     old_file_storage_dir,
1737 22b7f6f8 Thomas Thrainer
                                                     new_file_storage_dir)
1738 22b7f6f8 Thomas Thrainer
      result.Raise("Could not rename on node %s directory '%s' to '%s'"
1739 22b7f6f8 Thomas Thrainer
                   " (but the instance has been renamed in Ganeti)" %
1740 d0d7d7cf Thomas Thrainer
                   (self.cfg.GetNodeName(renamed_inst.primary_node),
1741 1c3231aa Thomas Thrainer
                    old_file_storage_dir, new_file_storage_dir))
1742 22b7f6f8 Thomas Thrainer
1743 d0d7d7cf Thomas Thrainer
    StartInstanceDisks(self, renamed_inst, None)
1744 5a20e569 Petr Pudlak
    renamed_inst = self.cfg.GetInstanceInfo(renamed_inst.uuid)
1745 5a20e569 Petr Pudlak
1746 22b7f6f8 Thomas Thrainer
    # update info on disks
1747 d0d7d7cf Thomas Thrainer
    info = GetInstanceInfoText(renamed_inst)
1748 d0d7d7cf Thomas Thrainer
    for (idx, disk) in enumerate(renamed_inst.disks):
1749 d0d7d7cf Thomas Thrainer
      for node_uuid in renamed_inst.all_nodes:
1750 0c3d9c7c Thomas Thrainer
        result = self.rpc.call_blockdev_setinfo(node_uuid,
1751 0c3d9c7c Thomas Thrainer
                                                (disk, renamed_inst), info)
1752 1c3231aa Thomas Thrainer
        result.Warn("Error setting info on node %s for disk %s" %
1753 1c3231aa Thomas Thrainer
                    (self.cfg.GetNodeName(node_uuid), idx), self.LogWarning)
1754 22b7f6f8 Thomas Thrainer
    try:
1755 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_instance_run_rename(renamed_inst.primary_node,
1756 d0d7d7cf Thomas Thrainer
                                                 renamed_inst, old_name,
1757 d0d7d7cf Thomas Thrainer
                                                 self.op.debug_level)
1758 c7dd65be Klaus Aehlig
      result.Warn("Could not run OS rename script for instance %s on node %s"
1759 c7dd65be Klaus Aehlig
                  " (but the instance has been renamed in Ganeti)" %
1760 d0d7d7cf Thomas Thrainer
                  (renamed_inst.name,
1761 d0d7d7cf Thomas Thrainer
                   self.cfg.GetNodeName(renamed_inst.primary_node)),
1762 1c3231aa Thomas Thrainer
                  self.LogWarning)
1763 22b7f6f8 Thomas Thrainer
    finally:
1764 d0d7d7cf Thomas Thrainer
      ShutdownInstanceDisks(self, renamed_inst)
1765 22b7f6f8 Thomas Thrainer
1766 d0d7d7cf Thomas Thrainer
    return renamed_inst.name
1767 22b7f6f8 Thomas Thrainer
1768 22b7f6f8 Thomas Thrainer
1769 22b7f6f8 Thomas Thrainer
class LUInstanceRemove(LogicalUnit):
1770 22b7f6f8 Thomas Thrainer
  """Remove an instance.
1771 22b7f6f8 Thomas Thrainer

1772 22b7f6f8 Thomas Thrainer
  """
1773 22b7f6f8 Thomas Thrainer
  HPATH = "instance-remove"
1774 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1775 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1776 22b7f6f8 Thomas Thrainer
1777 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1778 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1779 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
1780 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1781 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
1782 22b7f6f8 Thomas Thrainer
1783 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1784 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1785 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
1786 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1787 22b7f6f8 Thomas Thrainer
      # Copy node locks
1788 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1789 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1790 22b7f6f8 Thomas Thrainer
1791 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1792 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1793 22b7f6f8 Thomas Thrainer

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

1796 22b7f6f8 Thomas Thrainer
    """
1797 e8dd6643 Ilias Tsitsimpis
    env = BuildInstanceHookEnvByObject(self, self.instance,
1798 e8dd6643 Ilias Tsitsimpis
                                       secondary_nodes=self.secondary_nodes,
1799 e8dd6643 Ilias Tsitsimpis
                                       disks=self.inst_disks)
1800 22b7f6f8 Thomas Thrainer
    env["SHUTDOWN_TIMEOUT"] = self.op.shutdown_timeout
1801 22b7f6f8 Thomas Thrainer
    return env
1802 22b7f6f8 Thomas Thrainer
1803 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1804 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1805 22b7f6f8 Thomas Thrainer

1806 22b7f6f8 Thomas Thrainer
    """
1807 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()]
1808 22b7f6f8 Thomas Thrainer
    nl_post = list(self.instance.all_nodes) + nl
1809 22b7f6f8 Thomas Thrainer
    return (nl, nl_post)
1810 22b7f6f8 Thomas Thrainer
1811 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1812 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1813 22b7f6f8 Thomas Thrainer

1814 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1815 22b7f6f8 Thomas Thrainer

1816 22b7f6f8 Thomas Thrainer
    """
1817 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1818 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1819 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1820 e8dd6643 Ilias Tsitsimpis
    self.secondary_nodes = self.instance.secondary_nodes
1821 e8dd6643 Ilias Tsitsimpis
    self.inst_disks = self.instance.disks
1822 22b7f6f8 Thomas Thrainer
1823 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1824 22b7f6f8 Thomas Thrainer
    """Remove the instance.
1825 22b7f6f8 Thomas Thrainer

1826 22b7f6f8 Thomas Thrainer
    """
1827 d0d7d7cf Thomas Thrainer
    logging.info("Shutting down instance %s on node %s", self.instance.name,
1828 d0d7d7cf Thomas Thrainer
                 self.cfg.GetNodeName(self.instance.primary_node))
1829 22b7f6f8 Thomas Thrainer
1830 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_instance_shutdown(self.instance.primary_node,
1831 d0d7d7cf Thomas Thrainer
                                             self.instance,
1832 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1833 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1834 c7dd65be Klaus Aehlig
    if self.op.ignore_failures:
1835 c7dd65be Klaus Aehlig
      result.Warn("Warning: can't shutdown instance", feedback_fn)
1836 c7dd65be Klaus Aehlig
    else:
1837 c7dd65be Klaus Aehlig
      result.Raise("Could not shutdown instance %s on node %s" %
1838 d0d7d7cf Thomas Thrainer
                   (self.instance.name,
1839 d0d7d7cf Thomas Thrainer
                    self.cfg.GetNodeName(self.instance.primary_node)))
1840 22b7f6f8 Thomas Thrainer
1841 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1842 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1843 d0d7d7cf Thomas Thrainer
    assert not (set(self.instance.all_nodes) -
1844 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
1845 22b7f6f8 Thomas Thrainer
      "Not owning correct locks"
1846 22b7f6f8 Thomas Thrainer
1847 d0d7d7cf Thomas Thrainer
    RemoveInstance(self, feedback_fn, self.instance, self.op.ignore_failures)
1848 22b7f6f8 Thomas Thrainer
1849 22b7f6f8 Thomas Thrainer
1850 22b7f6f8 Thomas Thrainer
class LUInstanceMove(LogicalUnit):
1851 22b7f6f8 Thomas Thrainer
  """Move an instance by data-copying.
1852 22b7f6f8 Thomas Thrainer

1853 22b7f6f8 Thomas Thrainer
  """
1854 22b7f6f8 Thomas Thrainer
  HPATH = "instance-move"
1855 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1856 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1857 22b7f6f8 Thomas Thrainer
1858 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1859 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1860 1c3231aa Thomas Thrainer
    (self.op.target_node_uuid, self.op.target_node) = \
1861 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.target_node_uuid,
1862 1c3231aa Thomas Thrainer
                            self.op.target_node)
1863 b9aae98b Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = [self.op.target_node_uuid]
1864 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1865 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
1866 22b7f6f8 Thomas Thrainer
1867 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1868 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1869 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes(primary_only=True)
1870 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1871 22b7f6f8 Thomas Thrainer
      # Copy node locks
1872 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1873 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1874 22b7f6f8 Thomas Thrainer
1875 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1876 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1877 22b7f6f8 Thomas Thrainer

1878 83266db6 Thomas Thrainer
    This runs on master, primary and target nodes of the instance.
1879 22b7f6f8 Thomas Thrainer

1880 22b7f6f8 Thomas Thrainer
    """
1881 22b7f6f8 Thomas Thrainer
    env = {
1882 22b7f6f8 Thomas Thrainer
      "TARGET_NODE": self.op.target_node,
1883 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
1884 22b7f6f8 Thomas Thrainer
      }
1885 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
1886 22b7f6f8 Thomas Thrainer
    return env
1887 22b7f6f8 Thomas Thrainer
1888 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1889 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1890 22b7f6f8 Thomas Thrainer

1891 22b7f6f8 Thomas Thrainer
    """
1892 22b7f6f8 Thomas Thrainer
    nl = [
1893 22b7f6f8 Thomas Thrainer
      self.cfg.GetMasterNode(),
1894 22b7f6f8 Thomas Thrainer
      self.instance.primary_node,
1895 1c3231aa Thomas Thrainer
      self.op.target_node_uuid,
1896 22b7f6f8 Thomas Thrainer
      ]
1897 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1898 22b7f6f8 Thomas Thrainer
1899 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1900 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1901 22b7f6f8 Thomas Thrainer

1902 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1903 22b7f6f8 Thomas Thrainer

1904 22b7f6f8 Thomas Thrainer
    """
1905 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1906 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1907 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1908 22b7f6f8 Thomas Thrainer
1909 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template not in constants.DTS_COPYABLE:
1910 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk template %s not suitable for copying" %
1911 d0d7d7cf Thomas Thrainer
                                 self.instance.disk_template,
1912 d0d7d7cf Thomas Thrainer
                                 errors.ECODE_STATE)
1913 22b7f6f8 Thomas Thrainer
1914 1c3231aa Thomas Thrainer
    target_node = self.cfg.GetNodeInfo(self.op.target_node_uuid)
1915 1c3231aa Thomas Thrainer
    assert target_node is not None, \
1916 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked node %s" % self.op.target_node
1917 22b7f6f8 Thomas Thrainer
1918 1c3231aa Thomas Thrainer
    self.target_node_uuid = target_node.uuid
1919 d0d7d7cf Thomas Thrainer
    if target_node.uuid == self.instance.primary_node:
1920 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance %s is already on the node %s" %
1921 d0d7d7cf Thomas Thrainer
                                 (self.instance.name, target_node.name),
1922 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
1923 22b7f6f8 Thomas Thrainer
1924 d29f52a6 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
1925 d29f52a6 Thomas Thrainer
    bep = cluster.FillBE(self.instance)
1926 22b7f6f8 Thomas Thrainer
1927 d0d7d7cf Thomas Thrainer
    for idx, dsk in enumerate(self.instance.disks):
1928 cd3b4ff4 Helga Velroyen
      if dsk.dev_type not in (constants.DT_PLAIN, constants.DT_FILE,
1929 8106dd64 Santi Raffa
                              constants.DT_SHARED_FILE, constants.DT_GLUSTER):
1930 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance disk %d has a complex layout,"
1931 22b7f6f8 Thomas Thrainer
                                   " cannot copy" % idx, errors.ECODE_STATE)
1932 22b7f6f8 Thomas Thrainer
1933 1c3231aa Thomas Thrainer
    CheckNodeOnline(self, target_node.uuid)
1934 1c3231aa Thomas Thrainer
    CheckNodeNotDrained(self, target_node.uuid)
1935 1c3231aa Thomas Thrainer
    CheckNodeVmCapable(self, target_node.uuid)
1936 1c3231aa Thomas Thrainer
    group_info = self.cfg.GetNodeGroup(target_node.group)
1937 22b7f6f8 Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster, group_info)
1938 d0d7d7cf Thomas Thrainer
    CheckTargetNodeIPolicy(self, ipolicy, self.instance, target_node, self.cfg,
1939 5eacbcae Thomas Thrainer
                           ignore=self.op.ignore_ipolicy)
1940 22b7f6f8 Thomas Thrainer
1941 d0d7d7cf Thomas Thrainer
    if self.instance.admin_state == constants.ADMINST_UP:
1942 83266db6 Thomas Thrainer
      # check memory requirements on the target node
1943 a295eb80 Helga Velroyen
      CheckNodeFreeMemory(
1944 1c3231aa Thomas Thrainer
          self, target_node.uuid, "failing over instance %s" %
1945 d0d7d7cf Thomas Thrainer
          self.instance.name, bep[constants.BE_MAXMEM],
1946 d0d7d7cf Thomas Thrainer
          self.instance.hypervisor,
1947 d29f52a6 Thomas Thrainer
          cluster.hvparams[self.instance.hypervisor])
1948 22b7f6f8 Thomas Thrainer
    else:
1949 22b7f6f8 Thomas Thrainer
      self.LogInfo("Not checking memory on the secondary node as"
1950 22b7f6f8 Thomas Thrainer
                   " instance will not be started")
1951 22b7f6f8 Thomas Thrainer
1952 22b7f6f8 Thomas Thrainer
    # check bridge existance
1953 d0d7d7cf Thomas Thrainer
    CheckInstanceBridgesExist(self, self.instance, node_uuid=target_node.uuid)
1954 22b7f6f8 Thomas Thrainer
1955 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1956 22b7f6f8 Thomas Thrainer
    """Move an instance.
1957 22b7f6f8 Thomas Thrainer

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

1961 22b7f6f8 Thomas Thrainer
    """
1962 d0d7d7cf Thomas Thrainer
    source_node = self.cfg.GetNodeInfo(self.instance.primary_node)
1963 1c3231aa Thomas Thrainer
    target_node = self.cfg.GetNodeInfo(self.target_node_uuid)
1964 22b7f6f8 Thomas Thrainer
1965 22b7f6f8 Thomas Thrainer
    self.LogInfo("Shutting down instance %s on source node %s",
1966 d0d7d7cf Thomas Thrainer
                 self.instance.name, source_node.name)
1967 22b7f6f8 Thomas Thrainer
1968 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1969 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1970 22b7f6f8 Thomas Thrainer
1971 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_instance_shutdown(source_node.uuid, self.instance,
1972 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1973 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1974 c7dd65be Klaus Aehlig
    if self.op.ignore_consistency:
1975 c7dd65be Klaus Aehlig
      result.Warn("Could not shutdown instance %s on node %s. Proceeding"
1976 c7dd65be Klaus Aehlig
                  " anyway. Please make sure node %s is down. Error details" %
1977 d0d7d7cf Thomas Thrainer
                  (self.instance.name, source_node.name, source_node.name),
1978 1c3231aa Thomas Thrainer
                  self.LogWarning)
1979 c7dd65be Klaus Aehlig
    else:
1980 c7dd65be Klaus Aehlig
      result.Raise("Could not shutdown instance %s on node %s" %
1981 d0d7d7cf Thomas Thrainer
                   (self.instance.name, source_node.name))
1982 22b7f6f8 Thomas Thrainer
1983 22b7f6f8 Thomas Thrainer
    # create the target disks
1984 22b7f6f8 Thomas Thrainer
    try:
1985 d0d7d7cf Thomas Thrainer
      CreateDisks(self, self.instance, target_node_uuid=target_node.uuid)
1986 22b7f6f8 Thomas Thrainer
    except errors.OpExecError:
1987 22b7f6f8 Thomas Thrainer
      self.LogWarning("Device creation failed")
1988 da4a52a3 Thomas Thrainer
      self.cfg.ReleaseDRBDMinors(self.instance.uuid)
1989 22b7f6f8 Thomas Thrainer
      raise
1990 22b7f6f8 Thomas Thrainer
1991 22b7f6f8 Thomas Thrainer
    errs = []
1992 063613aa Thomas Thrainer
    transfers = []
1993 063613aa Thomas Thrainer
    # activate, get path, create transfer jobs
1994 d0d7d7cf Thomas Thrainer
    for idx, disk in enumerate(self.instance.disks):
1995 063613aa Thomas Thrainer
      # FIXME: pass debug option from opcode to backend
1996 063613aa Thomas Thrainer
      dt = masterd.instance.DiskTransfer("disk/%s" % idx,
1997 063613aa Thomas Thrainer
                                         constants.IEIO_RAW_DISK,
1998 063613aa Thomas Thrainer
                                         (disk, self.instance),
1999 063613aa Thomas Thrainer
                                         constants.IEIO_RAW_DISK,
2000 063613aa Thomas Thrainer
                                         (disk, self.instance),
2001 063613aa Thomas Thrainer
                                         None)
2002 063613aa Thomas Thrainer
      transfers.append(dt)
2003 063613aa Thomas Thrainer
2004 063613aa Thomas Thrainer
    import_result = \
2005 063613aa Thomas Thrainer
      masterd.instance.TransferInstanceData(self, feedback_fn,
2006 063613aa Thomas Thrainer
                                            source_node.uuid,
2007 063613aa Thomas Thrainer
                                            target_node.uuid,
2008 063613aa Thomas Thrainer
                                            target_node.secondary_ip,
2009 f198cf91 Thomas Thrainer
                                            self.op.compress,
2010 063613aa Thomas Thrainer
                                            self.instance, transfers)
2011 063613aa Thomas Thrainer
    if not compat.all(import_result):
2012 063613aa Thomas Thrainer
      errs.append("Failed to transfer instance data")
2013 22b7f6f8 Thomas Thrainer
2014 22b7f6f8 Thomas Thrainer
    if errs:
2015 22b7f6f8 Thomas Thrainer
      self.LogWarning("Some disks failed to copy, aborting")
2016 22b7f6f8 Thomas Thrainer
      try:
2017 d0d7d7cf Thomas Thrainer
        RemoveDisks(self, self.instance, target_node_uuid=target_node.uuid)
2018 22b7f6f8 Thomas Thrainer
      finally:
2019 da4a52a3 Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(self.instance.uuid)
2020 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Errors during disk copy: %s" %
2021 22b7f6f8 Thomas Thrainer
                                 (",".join(errs),))
2022 22b7f6f8 Thomas Thrainer
2023 d0d7d7cf Thomas Thrainer
    self.instance.primary_node = target_node.uuid
2024 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
2025 22b7f6f8 Thomas Thrainer
2026 22b7f6f8 Thomas Thrainer
    self.LogInfo("Removing the disks on the original node")
2027 d0d7d7cf Thomas Thrainer
    RemoveDisks(self, self.instance, target_node_uuid=source_node.uuid)
2028 22b7f6f8 Thomas Thrainer
2029 22b7f6f8 Thomas Thrainer
    # Only start the instance if it's marked as up
2030 d0d7d7cf Thomas Thrainer
    if self.instance.admin_state == constants.ADMINST_UP:
2031 22b7f6f8 Thomas Thrainer
      self.LogInfo("Starting instance %s on node %s",
2032 d0d7d7cf Thomas Thrainer
                   self.instance.name, target_node.name)
2033 22b7f6f8 Thomas Thrainer
2034 d0d7d7cf Thomas Thrainer
      disks_ok, _ = AssembleInstanceDisks(self, self.instance,
2035 5eacbcae Thomas Thrainer
                                          ignore_secondaries=True)
2036 22b7f6f8 Thomas Thrainer
      if not disks_ok:
2037 d0d7d7cf Thomas Thrainer
        ShutdownInstanceDisks(self, self.instance)
2038 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Can't activate the instance's disks")
2039 22b7f6f8 Thomas Thrainer
2040 1c3231aa Thomas Thrainer
      result = self.rpc.call_instance_start(target_node.uuid,
2041 d0d7d7cf Thomas Thrainer
                                            (self.instance, None, None), False,
2042 22b7f6f8 Thomas Thrainer
                                            self.op.reason)
2043 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
2044 22b7f6f8 Thomas Thrainer
      if msg:
2045 d0d7d7cf Thomas Thrainer
        ShutdownInstanceDisks(self, self.instance)
2046 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not start instance %s on node %s: %s" %
2047 d0d7d7cf Thomas Thrainer
                                 (self.instance.name, target_node.name, msg))
2048 22b7f6f8 Thomas Thrainer
2049 22b7f6f8 Thomas Thrainer
2050 22b7f6f8 Thomas Thrainer
class LUInstanceMultiAlloc(NoHooksLU):
2051 22b7f6f8 Thomas Thrainer
  """Allocates multiple instances at the same time.
2052 22b7f6f8 Thomas Thrainer

2053 22b7f6f8 Thomas Thrainer
  """
2054 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2055 22b7f6f8 Thomas Thrainer
2056 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
2057 22b7f6f8 Thomas Thrainer
    """Check arguments.
2058 22b7f6f8 Thomas Thrainer

2059 22b7f6f8 Thomas Thrainer
    """
2060 22b7f6f8 Thomas Thrainer
    nodes = []
2061 22b7f6f8 Thomas Thrainer
    for inst in self.op.instances:
2062 22b7f6f8 Thomas Thrainer
      if inst.iallocator is not None:
2063 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("iallocator are not allowed to be set on"
2064 22b7f6f8 Thomas Thrainer
                                   " instance objects", errors.ECODE_INVAL)
2065 22b7f6f8 Thomas Thrainer
      nodes.append(bool(inst.pnode))
2066 22b7f6f8 Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
2067 22b7f6f8 Thomas Thrainer
        nodes.append(bool(inst.snode))
2068 22b7f6f8 Thomas Thrainer
2069 22b7f6f8 Thomas Thrainer
    has_nodes = compat.any(nodes)
2070 22b7f6f8 Thomas Thrainer
    if compat.all(nodes) ^ has_nodes:
2071 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are instance objects providing"
2072 22b7f6f8 Thomas Thrainer
                                 " pnode/snode while others do not",
2073 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2074 22b7f6f8 Thomas Thrainer
2075 0c072225 Thomas Thrainer
    if not has_nodes and self.op.iallocator is None:
2076 22b7f6f8 Thomas Thrainer
      default_iallocator = self.cfg.GetDefaultIAllocator()
2077 0c072225 Thomas Thrainer
      if default_iallocator:
2078 22b7f6f8 Thomas Thrainer
        self.op.iallocator = default_iallocator
2079 22b7f6f8 Thomas Thrainer
      else:
2080 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No iallocator or nodes on the instances"
2081 22b7f6f8 Thomas Thrainer
                                   " given and no cluster-wide default"
2082 22b7f6f8 Thomas Thrainer
                                   " iallocator found; please specify either"
2083 22b7f6f8 Thomas Thrainer
                                   " an iallocator or nodes on the instances"
2084 22b7f6f8 Thomas Thrainer
                                   " or set a cluster-wide default iallocator",
2085 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2086 22b7f6f8 Thomas Thrainer
2087 22b7f6f8 Thomas Thrainer
    _CheckOpportunisticLocking(self.op)
2088 22b7f6f8 Thomas Thrainer
2089 22b7f6f8 Thomas Thrainer
    dups = utils.FindDuplicates([op.instance_name for op in self.op.instances])
2090 22b7f6f8 Thomas Thrainer
    if dups:
2091 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are duplicate instance names: %s" %
2092 22b7f6f8 Thomas Thrainer
                                 utils.CommaJoin(dups), errors.ECODE_INVAL)
2093 22b7f6f8 Thomas Thrainer
2094 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2095 22b7f6f8 Thomas Thrainer
    """Calculate the locks.
2096 22b7f6f8 Thomas Thrainer

2097 22b7f6f8 Thomas Thrainer
    """
2098 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
2099 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
2100 22b7f6f8 Thomas Thrainer
      # iallocator will select nodes and even if no iallocator is used,
2101 22b7f6f8 Thomas Thrainer
      # collisions with LUInstanceCreate should be avoided
2102 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
2103 22b7f6f8 Thomas Thrainer
      }
2104 22b7f6f8 Thomas Thrainer
2105 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
2106 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
2107 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = locking.ALL_SET
2108 22b7f6f8 Thomas Thrainer
2109 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
2110 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
2111 a278ef7f Hrvoje Ribicic
        self.opportunistic_locks[locking.LEVEL_NODE_RES] = True
2112 22b7f6f8 Thomas Thrainer
    else:
2113 22b7f6f8 Thomas Thrainer
      nodeslist = []
2114 22b7f6f8 Thomas Thrainer
      for inst in self.op.instances:
2115 1c3231aa Thomas Thrainer
        (inst.pnode_uuid, inst.pnode) = \
2116 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, inst.pnode_uuid, inst.pnode)
2117 6869f673 Thomas Thrainer
        nodeslist.append(inst.pnode_uuid)
2118 22b7f6f8 Thomas Thrainer
        if inst.snode is not None:
2119 1c3231aa Thomas Thrainer
          (inst.snode_uuid, inst.snode) = \
2120 1c3231aa Thomas Thrainer
            ExpandNodeUuidAndName(self.cfg, inst.snode_uuid, inst.snode)
2121 6869f673 Thomas Thrainer
          nodeslist.append(inst.snode_uuid)
2122 22b7f6f8 Thomas Thrainer
2123 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodeslist
2124 22b7f6f8 Thomas Thrainer
      # Lock resources of instance's primary and secondary nodes (copy to
2125 22b7f6f8 Thomas Thrainer
      # prevent accidential modification)
2126 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = list(nodeslist)
2127 22b7f6f8 Thomas Thrainer
2128 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2129 22b7f6f8 Thomas Thrainer
    """Check prerequisite.
2130 22b7f6f8 Thomas Thrainer

2131 22b7f6f8 Thomas Thrainer
    """
2132 0c072225 Thomas Thrainer
    if self.op.iallocator:
2133 0c072225 Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
2134 0c072225 Thomas Thrainer
      default_vg = self.cfg.GetVGName()
2135 0c072225 Thomas Thrainer
      ec_id = self.proc.GetECId()
2136 22b7f6f8 Thomas Thrainer
2137 0c072225 Thomas Thrainer
      if self.op.opportunistic_locking:
2138 0c072225 Thomas Thrainer
        # Only consider nodes for which a lock is held
2139 804d72eb Thomas Thrainer
        node_whitelist = self.cfg.GetNodeNames(
2140 a278ef7f Hrvoje Ribicic
          set(self.owned_locks(locking.LEVEL_NODE)) &
2141 a278ef7f Hrvoje Ribicic
          set(self.owned_locks(locking.LEVEL_NODE_RES)))
2142 0c072225 Thomas Thrainer
      else:
2143 0c072225 Thomas Thrainer
        node_whitelist = None
2144 22b7f6f8 Thomas Thrainer
2145 0c072225 Thomas Thrainer
      insts = [_CreateInstanceAllocRequest(op, ComputeDisks(op, default_vg),
2146 0c072225 Thomas Thrainer
                                           _ComputeNics(op, cluster, None,
2147 0c072225 Thomas Thrainer
                                                        self.cfg, ec_id),
2148 0c072225 Thomas Thrainer
                                           _ComputeFullBeParams(op, cluster),
2149 0c072225 Thomas Thrainer
                                           node_whitelist)
2150 0c072225 Thomas Thrainer
               for op in self.op.instances]
2151 22b7f6f8 Thomas Thrainer
2152 0c072225 Thomas Thrainer
      req = iallocator.IAReqMultiInstanceAlloc(instances=insts)
2153 0c072225 Thomas Thrainer
      ial = iallocator.IAllocator(self.cfg, self.rpc, req)
2154 22b7f6f8 Thomas Thrainer
2155 0c072225 Thomas Thrainer
      ial.Run(self.op.iallocator)
2156 22b7f6f8 Thomas Thrainer
2157 0c072225 Thomas Thrainer
      if not ial.success:
2158 0c072225 Thomas Thrainer
        raise errors.OpPrereqError("Can't compute nodes using"
2159 0c072225 Thomas Thrainer
                                   " iallocator '%s': %s" %
2160 0c072225 Thomas Thrainer
                                   (self.op.iallocator, ial.info),
2161 0c072225 Thomas Thrainer
                                   errors.ECODE_NORES)
2162 22b7f6f8 Thomas Thrainer
2163 0c072225 Thomas Thrainer
      self.ia_result = ial.result
2164 22b7f6f8 Thomas Thrainer
2165 22b7f6f8 Thomas Thrainer
    if self.op.dry_run:
2166 22b7f6f8 Thomas Thrainer
      self.dry_run_result = objects.FillDict(self._ConstructPartialResult(), {
2167 22b7f6f8 Thomas Thrainer
        constants.JOB_IDS_KEY: [],
2168 22b7f6f8 Thomas Thrainer
        })
2169 22b7f6f8 Thomas Thrainer
2170 22b7f6f8 Thomas Thrainer
  def _ConstructPartialResult(self):
2171 22b7f6f8 Thomas Thrainer
    """Contructs the partial result.
2172 22b7f6f8 Thomas Thrainer

2173 22b7f6f8 Thomas Thrainer
    """
2174 0c072225 Thomas Thrainer
    if self.op.iallocator:
2175 0c072225 Thomas Thrainer
      (allocatable, failed_insts) = self.ia_result
2176 0c072225 Thomas Thrainer
      allocatable_insts = map(compat.fst, allocatable)
2177 0c072225 Thomas Thrainer
    else:
2178 0c072225 Thomas Thrainer
      allocatable_insts = [op.instance_name for op in self.op.instances]
2179 0c072225 Thomas Thrainer
      failed_insts = []
2180 0c072225 Thomas Thrainer
2181 22b7f6f8 Thomas Thrainer
    return {
2182 1ca326c8 Thomas Thrainer
      constants.ALLOCATABLE_KEY: allocatable_insts,
2183 1ca326c8 Thomas Thrainer
      constants.FAILED_KEY: failed_insts,
2184 22b7f6f8 Thomas Thrainer
      }
2185 22b7f6f8 Thomas Thrainer
2186 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2187 22b7f6f8 Thomas Thrainer
    """Executes the opcode.
2188 22b7f6f8 Thomas Thrainer

2189 22b7f6f8 Thomas Thrainer
    """
2190 22b7f6f8 Thomas Thrainer
    jobs = []
2191 0c072225 Thomas Thrainer
    if self.op.iallocator:
2192 0c072225 Thomas Thrainer
      op2inst = dict((op.instance_name, op) for op in self.op.instances)
2193 0c072225 Thomas Thrainer
      (allocatable, failed) = self.ia_result
2194 22b7f6f8 Thomas Thrainer
2195 804d72eb Thomas Thrainer
      for (name, node_names) in allocatable:
2196 0c072225 Thomas Thrainer
        op = op2inst.pop(name)
2197 22b7f6f8 Thomas Thrainer
2198 804d72eb Thomas Thrainer
        (op.pnode_uuid, op.pnode) = \
2199 804d72eb Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, None, node_names[0])
2200 804d72eb Thomas Thrainer
        if len(node_names) > 1:
2201 804d72eb Thomas Thrainer
          (op.snode_uuid, op.snode) = \
2202 804d72eb Thomas Thrainer
            ExpandNodeUuidAndName(self.cfg, None, node_names[1])
2203 22b7f6f8 Thomas Thrainer
2204 804d72eb Thomas Thrainer
          jobs.append([op])
2205 22b7f6f8 Thomas Thrainer
2206 804d72eb Thomas Thrainer
        missing = set(op2inst.keys()) - set(failed)
2207 804d72eb Thomas Thrainer
        assert not missing, \
2208 804d72eb Thomas Thrainer
          "Iallocator did return incomplete result: %s" % \
2209 804d72eb Thomas Thrainer
          utils.CommaJoin(missing)
2210 0c072225 Thomas Thrainer
    else:
2211 0c072225 Thomas Thrainer
      jobs.extend([op] for op in self.op.instances)
2212 22b7f6f8 Thomas Thrainer
2213 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs, **self._ConstructPartialResult())
2214 22b7f6f8 Thomas Thrainer
2215 22b7f6f8 Thomas Thrainer
2216 22b7f6f8 Thomas Thrainer
class _InstNicModPrivate:
2217 22b7f6f8 Thomas Thrainer
  """Data structure for network interface modifications.
2218 22b7f6f8 Thomas Thrainer

2219 22b7f6f8 Thomas Thrainer
  Used by L{LUInstanceSetParams}.
2220 22b7f6f8 Thomas Thrainer

2221 22b7f6f8 Thomas Thrainer
  """
2222 22b7f6f8 Thomas Thrainer
  def __init__(self):
2223 22b7f6f8 Thomas Thrainer
    self.params = None
2224 22b7f6f8 Thomas Thrainer
    self.filled = None
2225 22b7f6f8 Thomas Thrainer
2226 22b7f6f8 Thomas Thrainer
2227 5eacbcae Thomas Thrainer
def _PrepareContainerMods(mods, private_fn):
2228 22b7f6f8 Thomas Thrainer
  """Prepares a list of container modifications by adding a private data field.
2229 22b7f6f8 Thomas Thrainer

2230 22b7f6f8 Thomas Thrainer
  @type mods: list of tuples; (operation, index, parameters)
2231 22b7f6f8 Thomas Thrainer
  @param mods: List of modifications
2232 22b7f6f8 Thomas Thrainer
  @type private_fn: callable or None
2233 22b7f6f8 Thomas Thrainer
  @param private_fn: Callable for constructing a private data field for a
2234 22b7f6f8 Thomas Thrainer
    modification
2235 22b7f6f8 Thomas Thrainer
  @rtype: list
2236 22b7f6f8 Thomas Thrainer

2237 22b7f6f8 Thomas Thrainer
  """
2238 22b7f6f8 Thomas Thrainer
  if private_fn is None:
2239 22b7f6f8 Thomas Thrainer
    fn = lambda: None
2240 22b7f6f8 Thomas Thrainer
  else:
2241 22b7f6f8 Thomas Thrainer
    fn = private_fn
2242 22b7f6f8 Thomas Thrainer
2243 22b7f6f8 Thomas Thrainer
  return [(op, idx, params, fn()) for (op, idx, params) in mods]
2244 22b7f6f8 Thomas Thrainer
2245 22b7f6f8 Thomas Thrainer
2246 1c3231aa Thomas Thrainer
def _CheckNodesPhysicalCPUs(lu, node_uuids, requested, hypervisor_specs):
2247 22b7f6f8 Thomas Thrainer
  """Checks if nodes have enough physical CPUs
2248 22b7f6f8 Thomas Thrainer

2249 22b7f6f8 Thomas Thrainer
  This function checks if all given nodes have the needed number of
2250 22b7f6f8 Thomas Thrainer
  physical CPUs. In case any node has less CPUs or we cannot get the
2251 22b7f6f8 Thomas Thrainer
  information from the node, this function raises an OpPrereqError
2252 22b7f6f8 Thomas Thrainer
  exception.
2253 22b7f6f8 Thomas Thrainer

2254 22b7f6f8 Thomas Thrainer
  @type lu: C{LogicalUnit}
2255 22b7f6f8 Thomas Thrainer
  @param lu: a logical unit from which we get configuration data
2256 1c3231aa Thomas Thrainer
  @type node_uuids: C{list}
2257 1c3231aa Thomas Thrainer
  @param node_uuids: the list of node UUIDs to check
2258 22b7f6f8 Thomas Thrainer
  @type requested: C{int}
2259 22b7f6f8 Thomas Thrainer
  @param requested: the minimum acceptable number of physical CPUs
2260 a295eb80 Helga Velroyen
  @type hypervisor_specs: list of pairs (string, dict of strings)
2261 a295eb80 Helga Velroyen
  @param hypervisor_specs: list of hypervisor specifications in
2262 a295eb80 Helga Velroyen
      pairs (hypervisor_name, hvparams)
2263 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node doesn't have enough CPUs,
2264 22b7f6f8 Thomas Thrainer
      or we cannot check the node
2265 22b7f6f8 Thomas Thrainer

2266 22b7f6f8 Thomas Thrainer
  """
2267 da803ff1 Helga Velroyen
  nodeinfo = lu.rpc.call_node_info(node_uuids, None, hypervisor_specs)
2268 1c3231aa Thomas Thrainer
  for node_uuid in node_uuids:
2269 1c3231aa Thomas Thrainer
    info = nodeinfo[node_uuid]
2270 1c3231aa Thomas Thrainer
    node_name = lu.cfg.GetNodeName(node_uuid)
2271 1c3231aa Thomas Thrainer
    info.Raise("Cannot get current information from node %s" % node_name,
2272 22b7f6f8 Thomas Thrainer
               prereq=True, ecode=errors.ECODE_ENVIRON)
2273 22b7f6f8 Thomas Thrainer
    (_, _, (hv_info, )) = info.payload
2274 22b7f6f8 Thomas Thrainer
    num_cpus = hv_info.get("cpu_total", None)
2275 22b7f6f8 Thomas Thrainer
    if not isinstance(num_cpus, int):
2276 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute the number of physical CPUs"
2277 22b7f6f8 Thomas Thrainer
                                 " on node %s, result was '%s'" %
2278 1c3231aa Thomas Thrainer
                                 (node_name, num_cpus), errors.ECODE_ENVIRON)
2279 22b7f6f8 Thomas Thrainer
    if requested > num_cpus:
2280 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Node %s has %s physical CPUs, but %s are "
2281 1c3231aa Thomas Thrainer
                                 "required" % (node_name, num_cpus, requested),
2282 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_NORES)
2283 22b7f6f8 Thomas Thrainer
2284 22b7f6f8 Thomas Thrainer
2285 22b7f6f8 Thomas Thrainer
def GetItemFromContainer(identifier, kind, container):
2286 22b7f6f8 Thomas Thrainer
  """Return the item refered by the identifier.
2287 22b7f6f8 Thomas Thrainer

2288 22b7f6f8 Thomas Thrainer
  @type identifier: string
2289 22b7f6f8 Thomas Thrainer
  @param identifier: Item index or name or UUID
2290 22b7f6f8 Thomas Thrainer
  @type kind: string
2291 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2292 22b7f6f8 Thomas Thrainer
  @type container: list
2293 22b7f6f8 Thomas Thrainer
  @param container: Container to get the item from
2294 22b7f6f8 Thomas Thrainer

2295 22b7f6f8 Thomas Thrainer
  """
2296 22b7f6f8 Thomas Thrainer
  # Index
2297 22b7f6f8 Thomas Thrainer
  try:
2298 22b7f6f8 Thomas Thrainer
    idx = int(identifier)
2299 22b7f6f8 Thomas Thrainer
    if idx == -1:
2300 22b7f6f8 Thomas Thrainer
      # Append
2301 22b7f6f8 Thomas Thrainer
      absidx = len(container) - 1
2302 22b7f6f8 Thomas Thrainer
    elif idx < 0:
2303 22b7f6f8 Thomas Thrainer
      raise IndexError("Not accepting negative indices other than -1")
2304 22b7f6f8 Thomas Thrainer
    elif idx > len(container):
2305 22b7f6f8 Thomas Thrainer
      raise IndexError("Got %s index %s, but there are only %s" %
2306 22b7f6f8 Thomas Thrainer
                       (kind, idx, len(container)))
2307 22b7f6f8 Thomas Thrainer
    else:
2308 22b7f6f8 Thomas Thrainer
      absidx = idx
2309 22b7f6f8 Thomas Thrainer
    return (absidx, container[idx])
2310 22b7f6f8 Thomas Thrainer
  except ValueError:
2311 22b7f6f8 Thomas Thrainer
    pass
2312 22b7f6f8 Thomas Thrainer
2313 22b7f6f8 Thomas Thrainer
  for idx, item in enumerate(container):
2314 22b7f6f8 Thomas Thrainer
    if item.uuid == identifier or item.name == identifier:
2315 22b7f6f8 Thomas Thrainer
      return (idx, item)
2316 22b7f6f8 Thomas Thrainer
2317 22b7f6f8 Thomas Thrainer
  raise errors.OpPrereqError("Cannot find %s with identifier %s" %
2318 22b7f6f8 Thomas Thrainer
                             (kind, identifier), errors.ECODE_NOENT)
2319 22b7f6f8 Thomas Thrainer
2320 22b7f6f8 Thomas Thrainer
2321 5eacbcae Thomas Thrainer
def _ApplyContainerMods(kind, container, chgdesc, mods,
2322 922a9e65 Thomas Thrainer
                        create_fn, modify_fn, remove_fn,
2323 922a9e65 Thomas Thrainer
                        post_add_fn=None):
2324 22b7f6f8 Thomas Thrainer
  """Applies descriptions in C{mods} to C{container}.
2325 22b7f6f8 Thomas Thrainer

2326 22b7f6f8 Thomas Thrainer
  @type kind: string
2327 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2328 22b7f6f8 Thomas Thrainer
  @type container: list
2329 22b7f6f8 Thomas Thrainer
  @param container: Container to modify
2330 22b7f6f8 Thomas Thrainer
  @type chgdesc: None or list
2331 22b7f6f8 Thomas Thrainer
  @param chgdesc: List of applied changes
2332 22b7f6f8 Thomas Thrainer
  @type mods: list
2333 5eacbcae Thomas Thrainer
  @param mods: Modifications as returned by L{_PrepareContainerMods}
2334 22b7f6f8 Thomas Thrainer
  @type create_fn: callable
2335 22b7f6f8 Thomas Thrainer
  @param create_fn: Callback for creating a new item (L{constants.DDM_ADD});
2336 22b7f6f8 Thomas Thrainer
    receives absolute item index, parameters and private data object as added
2337 5eacbcae Thomas Thrainer
    by L{_PrepareContainerMods}, returns tuple containing new item and changes
2338 22b7f6f8 Thomas Thrainer
    as list
2339 22b7f6f8 Thomas Thrainer
  @type modify_fn: callable
2340 22b7f6f8 Thomas Thrainer
  @param modify_fn: Callback for modifying an existing item
2341 22b7f6f8 Thomas Thrainer
    (L{constants.DDM_MODIFY}); receives absolute item index, item, parameters
2342 5eacbcae Thomas Thrainer
    and private data object as added by L{_PrepareContainerMods}, returns
2343 22b7f6f8 Thomas Thrainer
    changes as list
2344 22b7f6f8 Thomas Thrainer
  @type remove_fn: callable
2345 22b7f6f8 Thomas Thrainer
  @param remove_fn: Callback on removing item; receives absolute item index,
2346 5eacbcae Thomas Thrainer
    item and private data object as added by L{_PrepareContainerMods}
2347 922a9e65 Thomas Thrainer
  @type post_add_fn: callable
2348 922a9e65 Thomas Thrainer
  @param post_add_fn: Callable for post-processing a newly created item after
2349 922a9e65 Thomas Thrainer
    it has been put into the container. It receives the index of the new item
2350 922a9e65 Thomas Thrainer
    and the new item as parameters.
2351 22b7f6f8 Thomas Thrainer

2352 22b7f6f8 Thomas Thrainer
  """
2353 22b7f6f8 Thomas Thrainer
  for (op, identifier, params, private) in mods:
2354 22b7f6f8 Thomas Thrainer
    changes = None
2355 22b7f6f8 Thomas Thrainer
2356 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2357 22b7f6f8 Thomas Thrainer
      # Calculate where item will be added
2358 22b7f6f8 Thomas Thrainer
      # When adding an item, identifier can only be an index
2359 22b7f6f8 Thomas Thrainer
      try:
2360 22b7f6f8 Thomas Thrainer
        idx = int(identifier)
2361 22b7f6f8 Thomas Thrainer
      except ValueError:
2362 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Only possitive integer or -1 is accepted as"
2363 22b7f6f8 Thomas Thrainer
                                   " identifier for %s" % constants.DDM_ADD,
2364 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2365 22b7f6f8 Thomas Thrainer
      if idx == -1:
2366 22b7f6f8 Thomas Thrainer
        addidx = len(container)
2367 22b7f6f8 Thomas Thrainer
      else:
2368 22b7f6f8 Thomas Thrainer
        if idx < 0:
2369 22b7f6f8 Thomas Thrainer
          raise IndexError("Not accepting negative indices other than -1")
2370 22b7f6f8 Thomas Thrainer
        elif idx > len(container):
2371 22b7f6f8 Thomas Thrainer
          raise IndexError("Got %s index %s, but there are only %s" %
2372 22b7f6f8 Thomas Thrainer
                           (kind, idx, len(container)))
2373 22b7f6f8 Thomas Thrainer
        addidx = idx
2374 22b7f6f8 Thomas Thrainer
2375 22b7f6f8 Thomas Thrainer
      if create_fn is None:
2376 22b7f6f8 Thomas Thrainer
        item = params
2377 22b7f6f8 Thomas Thrainer
      else:
2378 22b7f6f8 Thomas Thrainer
        (item, changes) = create_fn(addidx, params, private)
2379 22b7f6f8 Thomas Thrainer
2380 22b7f6f8 Thomas Thrainer
      if idx == -1:
2381 22b7f6f8 Thomas Thrainer
        container.append(item)
2382 22b7f6f8 Thomas Thrainer
      else:
2383 22b7f6f8 Thomas Thrainer
        assert idx >= 0
2384 22b7f6f8 Thomas Thrainer
        assert idx <= len(container)
2385 22b7f6f8 Thomas Thrainer
        # list.insert does so before the specified index
2386 22b7f6f8 Thomas Thrainer
        container.insert(idx, item)
2387 922a9e65 Thomas Thrainer
2388 922a9e65 Thomas Thrainer
      if post_add_fn is not None:
2389 922a9e65 Thomas Thrainer
        post_add_fn(addidx, item)
2390 922a9e65 Thomas Thrainer
2391 22b7f6f8 Thomas Thrainer
    else:
2392 22b7f6f8 Thomas Thrainer
      # Retrieve existing item
2393 22b7f6f8 Thomas Thrainer
      (absidx, item) = GetItemFromContainer(identifier, kind, container)
2394 22b7f6f8 Thomas Thrainer
2395 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2396 22b7f6f8 Thomas Thrainer
        assert not params
2397 22b7f6f8 Thomas Thrainer
2398 22b7f6f8 Thomas Thrainer
        changes = [("%s/%s" % (kind, absidx), "remove")]
2399 22b7f6f8 Thomas Thrainer
2400 e15a00dc Dimitris Aragiorgis
        if remove_fn is not None:
2401 e15a00dc Dimitris Aragiorgis
          msg = remove_fn(absidx, item, private)
2402 e15a00dc Dimitris Aragiorgis
          if msg:
2403 e15a00dc Dimitris Aragiorgis
            changes.append(("%s/%s" % (kind, absidx), msg))
2404 e15a00dc Dimitris Aragiorgis
2405 22b7f6f8 Thomas Thrainer
        assert container[absidx] == item
2406 22b7f6f8 Thomas Thrainer
        del container[absidx]
2407 22b7f6f8 Thomas Thrainer
      elif op == constants.DDM_MODIFY:
2408 22b7f6f8 Thomas Thrainer
        if modify_fn is not None:
2409 22b7f6f8 Thomas Thrainer
          changes = modify_fn(absidx, item, params, private)
2410 22b7f6f8 Thomas Thrainer
      else:
2411 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
2412 22b7f6f8 Thomas Thrainer
2413 22b7f6f8 Thomas Thrainer
    assert _TApplyContModsCbChanges(changes)
2414 22b7f6f8 Thomas Thrainer
2415 22b7f6f8 Thomas Thrainer
    if not (chgdesc is None or changes is None):
2416 22b7f6f8 Thomas Thrainer
      chgdesc.extend(changes)
2417 22b7f6f8 Thomas Thrainer
2418 22b7f6f8 Thomas Thrainer
2419 22b7f6f8 Thomas Thrainer
def _UpdateIvNames(base_index, disks):
2420 22b7f6f8 Thomas Thrainer
  """Updates the C{iv_name} attribute of disks.
2421 22b7f6f8 Thomas Thrainer

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

2424 22b7f6f8 Thomas Thrainer
  """
2425 22b7f6f8 Thomas Thrainer
  for (idx, disk) in enumerate(disks):
2426 22b7f6f8 Thomas Thrainer
    disk.iv_name = "disk/%s" % (base_index + idx, )
2427 22b7f6f8 Thomas Thrainer
2428 22b7f6f8 Thomas Thrainer
2429 22b7f6f8 Thomas Thrainer
class LUInstanceSetParams(LogicalUnit):
2430 22b7f6f8 Thomas Thrainer
  """Modifies an instances's parameters.
2431 22b7f6f8 Thomas Thrainer

2432 22b7f6f8 Thomas Thrainer
  """
2433 22b7f6f8 Thomas Thrainer
  HPATH = "instance-modify"
2434 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2435 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2436 22b7f6f8 Thomas Thrainer
2437 22b7f6f8 Thomas Thrainer
  @staticmethod
2438 22b7f6f8 Thomas Thrainer
  def _UpgradeDiskNicMods(kind, mods, verify_fn):
2439 22b7f6f8 Thomas Thrainer
    assert ht.TList(mods)
2440 22b7f6f8 Thomas Thrainer
    assert not mods or len(mods[0]) in (2, 3)
2441 22b7f6f8 Thomas Thrainer
2442 22b7f6f8 Thomas Thrainer
    if mods and len(mods[0]) == 2:
2443 22b7f6f8 Thomas Thrainer
      result = []
2444 22b7f6f8 Thomas Thrainer
2445 22b7f6f8 Thomas Thrainer
      addremove = 0
2446 22b7f6f8 Thomas Thrainer
      for op, params in mods:
2447 22b7f6f8 Thomas Thrainer
        if op in (constants.DDM_ADD, constants.DDM_REMOVE):
2448 22b7f6f8 Thomas Thrainer
          result.append((op, -1, params))
2449 22b7f6f8 Thomas Thrainer
          addremove += 1
2450 22b7f6f8 Thomas Thrainer
2451 22b7f6f8 Thomas Thrainer
          if addremove > 1:
2452 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Only one %s add or remove operation is"
2453 22b7f6f8 Thomas Thrainer
                                       " supported at a time" % kind,
2454 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
2455 22b7f6f8 Thomas Thrainer
        else:
2456 22b7f6f8 Thomas Thrainer
          result.append((constants.DDM_MODIFY, op, params))
2457 22b7f6f8 Thomas Thrainer
2458 22b7f6f8 Thomas Thrainer
      assert verify_fn(result)
2459 22b7f6f8 Thomas Thrainer
    else:
2460 22b7f6f8 Thomas Thrainer
      result = mods
2461 22b7f6f8 Thomas Thrainer
2462 22b7f6f8 Thomas Thrainer
    return result
2463 22b7f6f8 Thomas Thrainer
2464 22b7f6f8 Thomas Thrainer
  @staticmethod
2465 22b7f6f8 Thomas Thrainer
  def _CheckMods(kind, mods, key_types, item_fn):
2466 22b7f6f8 Thomas Thrainer
    """Ensures requested disk/NIC modifications are valid.
2467 22b7f6f8 Thomas Thrainer

2468 22b7f6f8 Thomas Thrainer
    """
2469 22b7f6f8 Thomas Thrainer
    for (op, _, params) in mods:
2470 22b7f6f8 Thomas Thrainer
      assert ht.TDict(params)
2471 22b7f6f8 Thomas Thrainer
2472 22b7f6f8 Thomas Thrainer
      # If 'key_types' is an empty dict, we assume we have an
2473 22b7f6f8 Thomas Thrainer
      # 'ext' template and thus do not ForceDictType
2474 22b7f6f8 Thomas Thrainer
      if key_types:
2475 22b7f6f8 Thomas Thrainer
        utils.ForceDictType(params, key_types)
2476 22b7f6f8 Thomas Thrainer
2477 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2478 22b7f6f8 Thomas Thrainer
        if params:
2479 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("No settings should be passed when"
2480 22b7f6f8 Thomas Thrainer
                                     " removing a %s" % kind,
2481 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2482 22b7f6f8 Thomas Thrainer
      elif op in (constants.DDM_ADD, constants.DDM_MODIFY):
2483 22b7f6f8 Thomas Thrainer
        item_fn(op, params)
2484 22b7f6f8 Thomas Thrainer
      else:
2485 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
2486 22b7f6f8 Thomas Thrainer
2487 affe1792 Klaus Aehlig
  def _VerifyDiskModification(self, op, params, excl_stor):
2488 22b7f6f8 Thomas Thrainer
    """Verifies a disk modification.
2489 22b7f6f8 Thomas Thrainer

2490 22b7f6f8 Thomas Thrainer
    """
2491 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2492 22b7f6f8 Thomas Thrainer
      mode = params.setdefault(constants.IDISK_MODE, constants.DISK_RDWR)
2493 22b7f6f8 Thomas Thrainer
      if mode not in constants.DISK_ACCESS_SET:
2494 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk access mode '%s'" % mode,
2495 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2496 22b7f6f8 Thomas Thrainer
2497 22b7f6f8 Thomas Thrainer
      size = params.get(constants.IDISK_SIZE, None)
2498 22b7f6f8 Thomas Thrainer
      if size is None:
2499 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Required disk parameter '%s' missing" %
2500 22b7f6f8 Thomas Thrainer
                                   constants.IDISK_SIZE, errors.ECODE_INVAL)
2501 34956ece Thomas Thrainer
      size = int(size)
2502 22b7f6f8 Thomas Thrainer
2503 22b7f6f8 Thomas Thrainer
      params[constants.IDISK_SIZE] = size
2504 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2505 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2506 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2507 22b7f6f8 Thomas Thrainer
2508 7c848a6a Bernardo Dal Seno
      CheckSpindlesExclusiveStorage(params, excl_stor, True)
2509 3f3ea14c Bernardo Dal Seno
2510 22b7f6f8 Thomas Thrainer
    elif op == constants.DDM_MODIFY:
2511 22b7f6f8 Thomas Thrainer
      if constants.IDISK_SIZE in params:
2512 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk size change not possible, use"
2513 22b7f6f8 Thomas Thrainer
                                   " grow-disk", errors.ECODE_INVAL)
2514 c5c72215 Dimitris Aragiorgis
2515 c5c72215 Dimitris Aragiorgis
      # Disk modification supports changing only the disk name and mode.
2516 c5c72215 Dimitris Aragiorgis
      # Changing arbitrary parameters is allowed only for ext disk template",
2517 c5c72215 Dimitris Aragiorgis
      if self.instance.disk_template != constants.DT_EXT:
2518 c5c72215 Dimitris Aragiorgis
        utils.ForceDictType(params, constants.MODIFIABLE_IDISK_PARAMS_TYPES)
2519 c5c72215 Dimitris Aragiorgis
2520 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2521 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2522 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2523 22b7f6f8 Thomas Thrainer
2524 22b7f6f8 Thomas Thrainer
  @staticmethod
2525 22b7f6f8 Thomas Thrainer
  def _VerifyNicModification(op, params):
2526 22b7f6f8 Thomas Thrainer
    """Verifies a network interface modification.
2527 22b7f6f8 Thomas Thrainer

2528 22b7f6f8 Thomas Thrainer
    """
2529 22b7f6f8 Thomas Thrainer
    if op in (constants.DDM_ADD, constants.DDM_MODIFY):
2530 22b7f6f8 Thomas Thrainer
      ip = params.get(constants.INIC_IP, None)
2531 22b7f6f8 Thomas Thrainer
      name = params.get(constants.INIC_NAME, None)
2532 22b7f6f8 Thomas Thrainer
      req_net = params.get(constants.INIC_NETWORK, None)
2533 22b7f6f8 Thomas Thrainer
      link = params.get(constants.NIC_LINK, None)
2534 22b7f6f8 Thomas Thrainer
      mode = params.get(constants.NIC_MODE, None)
2535 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2536 22b7f6f8 Thomas Thrainer
        params[constants.INIC_NAME] = None
2537 22b7f6f8 Thomas Thrainer
      if req_net is not None:
2538 22b7f6f8 Thomas Thrainer
        if req_net.lower() == constants.VALUE_NONE:
2539 22b7f6f8 Thomas Thrainer
          params[constants.INIC_NETWORK] = None
2540 22b7f6f8 Thomas Thrainer
          req_net = None
2541 22b7f6f8 Thomas Thrainer
        elif link is not None or mode is not None:
2542 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("If network is given"
2543 22b7f6f8 Thomas Thrainer
                                     " mode or link should not",
2544 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2545 22b7f6f8 Thomas Thrainer
2546 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_ADD:
2547 22b7f6f8 Thomas Thrainer
        macaddr = params.get(constants.INIC_MAC, None)
2548 22b7f6f8 Thomas Thrainer
        if macaddr is None:
2549 22b7f6f8 Thomas Thrainer
          params[constants.INIC_MAC] = constants.VALUE_AUTO
2550 22b7f6f8 Thomas Thrainer
2551 22b7f6f8 Thomas Thrainer
      if ip is not None:
2552 22b7f6f8 Thomas Thrainer
        if ip.lower() == constants.VALUE_NONE:
2553 22b7f6f8 Thomas Thrainer
          params[constants.INIC_IP] = None
2554 22b7f6f8 Thomas Thrainer
        else:
2555 22b7f6f8 Thomas Thrainer
          if ip.lower() == constants.NIC_IP_POOL:
2556 22b7f6f8 Thomas Thrainer
            if op == constants.DDM_ADD and req_net is None:
2557 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("If ip=pool, parameter network"
2558 22b7f6f8 Thomas Thrainer
                                         " cannot be none",
2559 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_INVAL)
2560 22b7f6f8 Thomas Thrainer
          else:
2561 22b7f6f8 Thomas Thrainer
            if not netutils.IPAddress.IsValid(ip):
2562 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("Invalid IP address '%s'" % ip,
2563 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_INVAL)
2564 22b7f6f8 Thomas Thrainer
2565 22b7f6f8 Thomas Thrainer
      if constants.INIC_MAC in params:
2566 22b7f6f8 Thomas Thrainer
        macaddr = params[constants.INIC_MAC]
2567 22b7f6f8 Thomas Thrainer
        if macaddr not in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
2568 22b7f6f8 Thomas Thrainer
          macaddr = utils.NormalizeAndValidateMac(macaddr)
2569 22b7f6f8 Thomas Thrainer
2570 22b7f6f8 Thomas Thrainer
        if op == constants.DDM_MODIFY and macaddr == constants.VALUE_AUTO:
2571 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("'auto' is not a valid MAC address when"
2572 22b7f6f8 Thomas Thrainer
                                     " modifying an existing NIC",
2573 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2574 22b7f6f8 Thomas Thrainer
2575 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
2576 22b7f6f8 Thomas Thrainer
    if not (self.op.nics or self.op.disks or self.op.disk_template or
2577 22b7f6f8 Thomas Thrainer
            self.op.hvparams or self.op.beparams or self.op.os_name or
2578 5eae613c Thomas Thrainer
            self.op.osparams or self.op.offline is not None or
2579 93f1e606 Jose A. Lopes
            self.op.runtime_mem or self.op.pnode or self.op.osparams_private or
2580 93f1e606 Jose A. Lopes
            self.op.instance_communication is not None):
2581 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("No changes submitted", errors.ECODE_INVAL)
2582 22b7f6f8 Thomas Thrainer
2583 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
2584 5eacbcae Thomas Thrainer
      CheckParamsNotGlobal(self.op.hvparams, constants.HVC_GLOBALS,
2585 5eacbcae Thomas Thrainer
                           "hypervisor", "instance", "cluster")
2586 22b7f6f8 Thomas Thrainer
2587 22b7f6f8 Thomas Thrainer
    self.op.disks = self._UpgradeDiskNicMods(
2588 72cd5493 Jose A. Lopes
      "disk", self.op.disks, ht.TSetParamsMods(ht.TIDiskParams))
2589 22b7f6f8 Thomas Thrainer
    self.op.nics = self._UpgradeDiskNicMods(
2590 72cd5493 Jose A. Lopes
      "NIC", self.op.nics, ht.TSetParamsMods(ht.TINicParams))
2591 22b7f6f8 Thomas Thrainer
2592 22b7f6f8 Thomas Thrainer
    if self.op.disks and self.op.disk_template is not None:
2593 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk template conversion and other disk"
2594 22b7f6f8 Thomas Thrainer
                                 " changes not supported at the same time",
2595 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2596 22b7f6f8 Thomas Thrainer
2597 22b7f6f8 Thomas Thrainer
    if (self.op.disk_template and
2598 22b7f6f8 Thomas Thrainer
        self.op.disk_template in constants.DTS_INT_MIRROR and
2599 22b7f6f8 Thomas Thrainer
        self.op.remote_node is None):
2600 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Changing the disk template to a mirrored"
2601 22b7f6f8 Thomas Thrainer
                                 " one requires specifying a secondary node",
2602 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2603 22b7f6f8 Thomas Thrainer
2604 22b7f6f8 Thomas Thrainer
    # Check NIC modifications
2605 22b7f6f8 Thomas Thrainer
    self._CheckMods("NIC", self.op.nics, constants.INIC_PARAMS_TYPES,
2606 22b7f6f8 Thomas Thrainer
                    self._VerifyNicModification)
2607 22b7f6f8 Thomas Thrainer
2608 22b7f6f8 Thomas Thrainer
    if self.op.pnode:
2609 1c3231aa Thomas Thrainer
      (self.op.pnode_uuid, self.op.pnode) = \
2610 1c3231aa Thomas Thrainer
        ExpandNodeUuidAndName(self.cfg, self.op.pnode_uuid, self.op.pnode)
2611 22b7f6f8 Thomas Thrainer
2612 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2613 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2614 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODEGROUP] = []
2615 22b7f6f8 Thomas Thrainer
    # Can't even acquire node locks in shared mode as upcoming changes in
2616 22b7f6f8 Thomas Thrainer
    # Ganeti 2.6 will start to modify the node object on disk conversion
2617 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
2618 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
2619 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
2620 22b7f6f8 Thomas Thrainer
    # Look node group to look up the ipolicy
2621 22b7f6f8 Thomas Thrainer
    self.share_locks[locking.LEVEL_NODEGROUP] = 1
2622 22b7f6f8 Thomas Thrainer
2623 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
2624 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
2625 22b7f6f8 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
2626 22b7f6f8 Thomas Thrainer
      # Acquire locks for the instance's nodegroups optimistically. Needs
2627 22b7f6f8 Thomas Thrainer
      # to be verified in CheckPrereq
2628 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = \
2629 da4a52a3 Thomas Thrainer
        self.cfg.GetInstanceNodeGroups(self.op.instance_uuid)
2630 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE:
2631 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
2632 22b7f6f8 Thomas Thrainer
      if self.op.disk_template and self.op.remote_node:
2633 1c3231aa Thomas Thrainer
        (self.op.remote_node_uuid, self.op.remote_node) = \
2634 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, self.op.remote_node_uuid,
2635 1c3231aa Thomas Thrainer
                                self.op.remote_node)
2636 1c3231aa Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE].append(self.op.remote_node_uuid)
2637 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES and self.op.disk_template:
2638 22b7f6f8 Thomas Thrainer
      # Copy node locks
2639 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
2640 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
2641 22b7f6f8 Thomas Thrainer
2642 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
2643 22b7f6f8 Thomas Thrainer
    """Build hooks env.
2644 22b7f6f8 Thomas Thrainer

2645 22b7f6f8 Thomas Thrainer
    This runs on the master, primary and secondaries.
2646 22b7f6f8 Thomas Thrainer

2647 22b7f6f8 Thomas Thrainer
    """
2648 22b7f6f8 Thomas Thrainer
    args = {}
2649 22b7f6f8 Thomas Thrainer
    if constants.BE_MINMEM in self.be_new:
2650 22b7f6f8 Thomas Thrainer
      args["minmem"] = self.be_new[constants.BE_MINMEM]
2651 22b7f6f8 Thomas Thrainer
    if constants.BE_MAXMEM in self.be_new:
2652 22b7f6f8 Thomas Thrainer
      args["maxmem"] = self.be_new[constants.BE_MAXMEM]
2653 22b7f6f8 Thomas Thrainer
    if constants.BE_VCPUS in self.be_new:
2654 22b7f6f8 Thomas Thrainer
      args["vcpus"] = self.be_new[constants.BE_VCPUS]
2655 22b7f6f8 Thomas Thrainer
    # TODO: export disk changes. Note: _BuildInstanceHookEnv* don't export disk
2656 22b7f6f8 Thomas Thrainer
    # information at all.
2657 22b7f6f8 Thomas Thrainer
2658 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
2659 22b7f6f8 Thomas Thrainer
      nics = []
2660 22b7f6f8 Thomas Thrainer
2661 22b7f6f8 Thomas Thrainer
      for nic in self._new_nics:
2662 22b7f6f8 Thomas Thrainer
        n = copy.deepcopy(nic)
2663 22b7f6f8 Thomas Thrainer
        nicparams = self.cluster.SimpleFillNIC(n.nicparams)
2664 22b7f6f8 Thomas Thrainer
        n.nicparams = nicparams
2665 5eacbcae Thomas Thrainer
        nics.append(NICToTuple(self, n))
2666 22b7f6f8 Thomas Thrainer
2667 22b7f6f8 Thomas Thrainer
      args["nics"] = nics
2668 22b7f6f8 Thomas Thrainer
2669 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance, override=args)
2670 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
2671 22b7f6f8 Thomas Thrainer
      env["NEW_DISK_TEMPLATE"] = self.op.disk_template
2672 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
2673 22b7f6f8 Thomas Thrainer
      env["RUNTIME_MEMORY"] = self.op.runtime_mem
2674 22b7f6f8 Thomas Thrainer
2675 22b7f6f8 Thomas Thrainer
    return env
2676 22b7f6f8 Thomas Thrainer
2677 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2678 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2679 22b7f6f8 Thomas Thrainer

2680 22b7f6f8 Thomas Thrainer
    """
2681 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
2682 22b7f6f8 Thomas Thrainer
    return (nl, nl)
2683 22b7f6f8 Thomas Thrainer
2684 22b7f6f8 Thomas Thrainer
  def _PrepareNicModification(self, params, private, old_ip, old_net_uuid,
2685 1c3231aa Thomas Thrainer
                              old_params, cluster, pnode_uuid):
2686 22b7f6f8 Thomas Thrainer
2687 22b7f6f8 Thomas Thrainer
    update_params_dict = dict([(key, params[key])
2688 22b7f6f8 Thomas Thrainer
                               for key in constants.NICS_PARAMETERS
2689 22b7f6f8 Thomas Thrainer
                               if key in params])
2690 22b7f6f8 Thomas Thrainer
2691 22b7f6f8 Thomas Thrainer
    req_link = update_params_dict.get(constants.NIC_LINK, None)
2692 22b7f6f8 Thomas Thrainer
    req_mode = update_params_dict.get(constants.NIC_MODE, None)
2693 22b7f6f8 Thomas Thrainer
2694 22b7f6f8 Thomas Thrainer
    new_net_uuid = None
2695 22b7f6f8 Thomas Thrainer
    new_net_uuid_or_name = params.get(constants.INIC_NETWORK, old_net_uuid)
2696 22b7f6f8 Thomas Thrainer
    if new_net_uuid_or_name:
2697 22b7f6f8 Thomas Thrainer
      new_net_uuid = self.cfg.LookupNetwork(new_net_uuid_or_name)
2698 22b7f6f8 Thomas Thrainer
      new_net_obj = self.cfg.GetNetwork(new_net_uuid)
2699 22b7f6f8 Thomas Thrainer
2700 22b7f6f8 Thomas Thrainer
    if old_net_uuid:
2701 22b7f6f8 Thomas Thrainer
      old_net_obj = self.cfg.GetNetwork(old_net_uuid)
2702 22b7f6f8 Thomas Thrainer
2703 22b7f6f8 Thomas Thrainer
    if new_net_uuid:
2704 1c3231aa Thomas Thrainer
      netparams = self.cfg.GetGroupNetParams(new_net_uuid, pnode_uuid)
2705 22b7f6f8 Thomas Thrainer
      if not netparams:
2706 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No netparams found for the network"
2707 22b7f6f8 Thomas Thrainer
                                   " %s, probably not connected" %
2708 22b7f6f8 Thomas Thrainer
                                   new_net_obj.name, errors.ECODE_INVAL)
2709 22b7f6f8 Thomas Thrainer
      new_params = dict(netparams)
2710 22b7f6f8 Thomas Thrainer
    else:
2711 5eacbcae Thomas Thrainer
      new_params = GetUpdatedParams(old_params, update_params_dict)
2712 22b7f6f8 Thomas Thrainer
2713 22b7f6f8 Thomas Thrainer
    utils.ForceDictType(new_params, constants.NICS_PARAMETER_TYPES)
2714 22b7f6f8 Thomas Thrainer
2715 22b7f6f8 Thomas Thrainer
    new_filled_params = cluster.SimpleFillNIC(new_params)
2716 22b7f6f8 Thomas Thrainer
    objects.NIC.CheckParameterSyntax(new_filled_params)
2717 22b7f6f8 Thomas Thrainer
2718 22b7f6f8 Thomas Thrainer
    new_mode = new_filled_params[constants.NIC_MODE]
2719 22b7f6f8 Thomas Thrainer
    if new_mode == constants.NIC_MODE_BRIDGED:
2720 22b7f6f8 Thomas Thrainer
      bridge = new_filled_params[constants.NIC_LINK]
2721 1c3231aa Thomas Thrainer
      msg = self.rpc.call_bridges_exist(pnode_uuid, [bridge]).fail_msg
2722 22b7f6f8 Thomas Thrainer
      if msg:
2723 1c3231aa Thomas Thrainer
        msg = "Error checking bridges on node '%s': %s" % \
2724 1c3231aa Thomas Thrainer
                (self.cfg.GetNodeName(pnode_uuid), msg)
2725 22b7f6f8 Thomas Thrainer
        if self.op.force:
2726 22b7f6f8 Thomas Thrainer
          self.warn.append(msg)
2727 22b7f6f8 Thomas Thrainer
        else:
2728 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError(msg, errors.ECODE_ENVIRON)
2729 22b7f6f8 Thomas Thrainer
2730 22b7f6f8 Thomas Thrainer
    elif new_mode == constants.NIC_MODE_ROUTED:
2731 22b7f6f8 Thomas Thrainer
      ip = params.get(constants.INIC_IP, old_ip)
2732 22b7f6f8 Thomas Thrainer
      if ip is None:
2733 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cannot set the NIC IP address to None"
2734 22b7f6f8 Thomas Thrainer
                                   " on a routed NIC", errors.ECODE_INVAL)
2735 22b7f6f8 Thomas Thrainer
2736 22b7f6f8 Thomas Thrainer
    elif new_mode == constants.NIC_MODE_OVS:
2737 22b7f6f8 Thomas Thrainer
      # TODO: check OVS link
2738 22b7f6f8 Thomas Thrainer
      self.LogInfo("OVS links are currently not checked for correctness")
2739 22b7f6f8 Thomas Thrainer
2740 22b7f6f8 Thomas Thrainer
    if constants.INIC_MAC in params:
2741 22b7f6f8 Thomas Thrainer
      mac = params[constants.INIC_MAC]
2742 22b7f6f8 Thomas Thrainer
      if mac is None:
2743 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cannot unset the NIC MAC address",
2744 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2745 22b7f6f8 Thomas Thrainer
      elif mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
2746 22b7f6f8 Thomas Thrainer
        # otherwise generate the MAC address
2747 22b7f6f8 Thomas Thrainer
        params[constants.INIC_MAC] = \
2748 22b7f6f8 Thomas Thrainer
          self.cfg.GenerateMAC(new_net_uuid, self.proc.GetECId())
2749 22b7f6f8 Thomas Thrainer
      else:
2750 22b7f6f8 Thomas Thrainer
        # or validate/reserve the current one
2751 22b7f6f8 Thomas Thrainer
        try:
2752 22b7f6f8 Thomas Thrainer
          self.cfg.ReserveMAC(mac, self.proc.GetECId())
2753 22b7f6f8 Thomas Thrainer
        except errors.ReservationError:
2754 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("MAC address '%s' already in use"
2755 22b7f6f8 Thomas Thrainer
                                     " in cluster" % mac,
2756 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_NOTUNIQUE)
2757 22b7f6f8 Thomas Thrainer
    elif new_net_uuid != old_net_uuid:
2758 22b7f6f8 Thomas Thrainer
2759 22b7f6f8 Thomas Thrainer
      def get_net_prefix(net_uuid):
2760 22b7f6f8 Thomas Thrainer
        mac_prefix = None
2761 22b7f6f8 Thomas Thrainer
        if net_uuid:
2762 22b7f6f8 Thomas Thrainer
          nobj = self.cfg.GetNetwork(net_uuid)
2763 22b7f6f8 Thomas Thrainer
          mac_prefix = nobj.mac_prefix
2764 22b7f6f8 Thomas Thrainer
2765 22b7f6f8 Thomas Thrainer
        return mac_prefix
2766 22b7f6f8 Thomas Thrainer
2767 22b7f6f8 Thomas Thrainer
      new_prefix = get_net_prefix(new_net_uuid)
2768 22b7f6f8 Thomas Thrainer
      old_prefix = get_net_prefix(old_net_uuid)
2769 22b7f6f8 Thomas Thrainer
      if old_prefix != new_prefix:
2770 22b7f6f8 Thomas Thrainer
        params[constants.INIC_MAC] = \
2771 22b7f6f8 Thomas Thrainer
          self.cfg.GenerateMAC(new_net_uuid, self.proc.GetECId())
2772 22b7f6f8 Thomas Thrainer
2773 22b7f6f8 Thomas Thrainer
    # if there is a change in (ip, network) tuple
2774 22b7f6f8 Thomas Thrainer
    new_ip = params.get(constants.INIC_IP, old_ip)
2775 22b7f6f8 Thomas Thrainer
    if (new_ip, new_net_uuid) != (old_ip, old_net_uuid):
2776 22b7f6f8 Thomas Thrainer
      if new_ip:
2777 22b7f6f8 Thomas Thrainer
        # if IP is pool then require a network and generate one IP
2778 22b7f6f8 Thomas Thrainer
        if new_ip.lower() == constants.NIC_IP_POOL:
2779 22b7f6f8 Thomas Thrainer
          if new_net_uuid:
2780 22b7f6f8 Thomas Thrainer
            try:
2781 22b7f6f8 Thomas Thrainer
              new_ip = self.cfg.GenerateIp(new_net_uuid, self.proc.GetECId())
2782 22b7f6f8 Thomas Thrainer
            except errors.ReservationError:
2783 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("Unable to get a free IP"
2784 22b7f6f8 Thomas Thrainer
                                         " from the address pool",
2785 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_STATE)
2786 22b7f6f8 Thomas Thrainer
            self.LogInfo("Chose IP %s from network %s",
2787 22b7f6f8 Thomas Thrainer
                         new_ip,
2788 22b7f6f8 Thomas Thrainer
                         new_net_obj.name)
2789 22b7f6f8 Thomas Thrainer
            params[constants.INIC_IP] = new_ip
2790 22b7f6f8 Thomas Thrainer
          else:
2791 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("ip=pool, but no network found",
2792 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
2793 22b7f6f8 Thomas Thrainer
        # Reserve new IP if in the new network if any
2794 22b7f6f8 Thomas Thrainer
        elif new_net_uuid:
2795 22b7f6f8 Thomas Thrainer
          try:
2796 031d2db1 Dimitris Aragiorgis
            self.cfg.ReserveIp(new_net_uuid, new_ip, self.proc.GetECId(),
2797 031d2db1 Dimitris Aragiorgis
                               check=self.op.conflicts_check)
2798 22b7f6f8 Thomas Thrainer
            self.LogInfo("Reserving IP %s in network %s",
2799 22b7f6f8 Thomas Thrainer
                         new_ip, new_net_obj.name)
2800 22b7f6f8 Thomas Thrainer
          except errors.ReservationError:
2801 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("IP %s not available in network %s" %
2802 22b7f6f8 Thomas Thrainer
                                       (new_ip, new_net_obj.name),
2803 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_NOTUNIQUE)
2804 22b7f6f8 Thomas Thrainer
        # new network is None so check if new IP is a conflicting IP
2805 22b7f6f8 Thomas Thrainer
        elif self.op.conflicts_check:
2806 1c3231aa Thomas Thrainer
          _CheckForConflictingIp(self, new_ip, pnode_uuid)
2807 22b7f6f8 Thomas Thrainer
2808 22b7f6f8 Thomas Thrainer
      # release old IP if old network is not None
2809 22b7f6f8 Thomas Thrainer
      if old_ip and old_net_uuid:
2810 22b7f6f8 Thomas Thrainer
        try:
2811 22b7f6f8 Thomas Thrainer
          self.cfg.ReleaseIp(old_net_uuid, old_ip, self.proc.GetECId())
2812 22b7f6f8 Thomas Thrainer
        except errors.AddressPoolError:
2813 22b7f6f8 Thomas Thrainer
          logging.warning("Release IP %s not contained in network %s",
2814 22b7f6f8 Thomas Thrainer
                          old_ip, old_net_obj.name)
2815 22b7f6f8 Thomas Thrainer
2816 22b7f6f8 Thomas Thrainer
    # there are no changes in (ip, network) tuple and old network is not None
2817 22b7f6f8 Thomas Thrainer
    elif (old_net_uuid is not None and
2818 22b7f6f8 Thomas Thrainer
          (req_link is not None or req_mode is not None)):
2819 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Not allowed to change link or mode of"
2820 22b7f6f8 Thomas Thrainer
                                 " a NIC that is connected to a network",
2821 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2822 22b7f6f8 Thomas Thrainer
2823 22b7f6f8 Thomas Thrainer
    private.params = new_params
2824 22b7f6f8 Thomas Thrainer
    private.filled = new_filled_params
2825 22b7f6f8 Thomas Thrainer
2826 22b7f6f8 Thomas Thrainer
  def _PreCheckDiskTemplate(self, pnode_info):
2827 22b7f6f8 Thomas Thrainer
    """CheckPrereq checks related to a new disk template."""
2828 22b7f6f8 Thomas Thrainer
    # Arguments are passed to avoid configuration lookups
2829 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
2830 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template == self.op.disk_template:
2831 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance already has disk template %s" %
2832 d0d7d7cf Thomas Thrainer
                                 self.instance.disk_template,
2833 d0d7d7cf Thomas Thrainer
                                 errors.ECODE_INVAL)
2834 22b7f6f8 Thomas Thrainer
2835 7bb0c47f Thomas Thrainer
    if not self.cluster.IsDiskTemplateEnabled(self.op.disk_template):
2836 9d276e93 Helga Velroyen
      raise errors.OpPrereqError("Disk template '%s' is not enabled for this"
2837 7bb0c47f Thomas Thrainer
                                 " cluster." % self.op.disk_template)
2838 9d276e93 Helga Velroyen
2839 d0d7d7cf Thomas Thrainer
    if (self.instance.disk_template,
2840 22b7f6f8 Thomas Thrainer
        self.op.disk_template) not in self._DISK_CONVERSIONS:
2841 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Unsupported disk template conversion from"
2842 d0d7d7cf Thomas Thrainer
                                 " %s to %s" % (self.instance.disk_template,
2843 22b7f6f8 Thomas Thrainer
                                                self.op.disk_template),
2844 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2845 d0d7d7cf Thomas Thrainer
    CheckInstanceState(self, self.instance, INSTANCE_DOWN,
2846 5eacbcae Thomas Thrainer
                       msg="cannot change disk template")
2847 22b7f6f8 Thomas Thrainer
    if self.op.disk_template in constants.DTS_INT_MIRROR:
2848 1c3231aa Thomas Thrainer
      if self.op.remote_node_uuid == pnode_uuid:
2849 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Given new secondary node %s is the same"
2850 22b7f6f8 Thomas Thrainer
                                   " as the primary node of the instance" %
2851 22b7f6f8 Thomas Thrainer
                                   self.op.remote_node, errors.ECODE_STATE)
2852 1c3231aa Thomas Thrainer
      CheckNodeOnline(self, self.op.remote_node_uuid)
2853 1c3231aa Thomas Thrainer
      CheckNodeNotDrained(self, self.op.remote_node_uuid)
2854 22b7f6f8 Thomas Thrainer
      # FIXME: here we assume that the old instance type is DT_PLAIN
2855 d0d7d7cf Thomas Thrainer
      assert self.instance.disk_template == constants.DT_PLAIN
2856 22b7f6f8 Thomas Thrainer
      disks = [{constants.IDISK_SIZE: d.size,
2857 22b7f6f8 Thomas Thrainer
                constants.IDISK_VG: d.logical_id[0]}
2858 d0d7d7cf Thomas Thrainer
               for d in self.instance.disks]
2859 5eacbcae Thomas Thrainer
      required = ComputeDiskSizePerVG(self.op.disk_template, disks)
2860 1c3231aa Thomas Thrainer
      CheckNodesFreeDiskPerVG(self, [self.op.remote_node_uuid], required)
2861 22b7f6f8 Thomas Thrainer
2862 1c3231aa Thomas Thrainer
      snode_info = self.cfg.GetNodeInfo(self.op.remote_node_uuid)
2863 22b7f6f8 Thomas Thrainer
      snode_group = self.cfg.GetNodeGroup(snode_info.group)
2864 d0d7d7cf Thomas Thrainer
      ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(self.cluster,
2865 22b7f6f8 Thomas Thrainer
                                                              snode_group)
2866 d0d7d7cf Thomas Thrainer
      CheckTargetNodeIPolicy(self, ipolicy, self.instance, snode_info, self.cfg,
2867 5eacbcae Thomas Thrainer
                             ignore=self.op.ignore_ipolicy)
2868 22b7f6f8 Thomas Thrainer
      if pnode_info.group != snode_info.group:
2869 22b7f6f8 Thomas Thrainer
        self.LogWarning("The primary and secondary nodes are in two"
2870 22b7f6f8 Thomas Thrainer
                        " different node groups; the disk parameters"
2871 22b7f6f8 Thomas Thrainer
                        " from the first disk's node group will be"
2872 22b7f6f8 Thomas Thrainer
                        " used")
2873 22b7f6f8 Thomas Thrainer
2874 22b7f6f8 Thomas Thrainer
    if not self.op.disk_template in constants.DTS_EXCL_STORAGE:
2875 22b7f6f8 Thomas Thrainer
      # Make sure none of the nodes require exclusive storage
2876 22b7f6f8 Thomas Thrainer
      nodes = [pnode_info]
2877 22b7f6f8 Thomas Thrainer
      if self.op.disk_template in constants.DTS_INT_MIRROR:
2878 22b7f6f8 Thomas Thrainer
        assert snode_info
2879 22b7f6f8 Thomas Thrainer
        nodes.append(snode_info)
2880 5eacbcae Thomas Thrainer
      has_es = lambda n: IsExclusiveStorageEnabledNode(self.cfg, n)
2881 22b7f6f8 Thomas Thrainer
      if compat.any(map(has_es, nodes)):
2882 22b7f6f8 Thomas Thrainer
        errmsg = ("Cannot convert disk template from %s to %s when exclusive"
2883 d0d7d7cf Thomas Thrainer
                  " storage is enabled" % (self.instance.disk_template,
2884 22b7f6f8 Thomas Thrainer
                                           self.op.disk_template))
2885 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError(errmsg, errors.ECODE_STATE)
2886 22b7f6f8 Thomas Thrainer
2887 1bb99a33 Bernardo Dal Seno
  def _PreCheckDisks(self, ispec):
2888 1bb99a33 Bernardo Dal Seno
    """CheckPrereq checks related to disk changes.
2889 22b7f6f8 Thomas Thrainer

2890 1bb99a33 Bernardo Dal Seno
    @type ispec: dict
2891 1bb99a33 Bernardo Dal Seno
    @param ispec: instance specs to be updated with the new disks
2892 22b7f6f8 Thomas Thrainer

2893 22b7f6f8 Thomas Thrainer
    """
2894 d0d7d7cf Thomas Thrainer
    self.diskparams = self.cfg.GetInstanceDiskParams(self.instance)
2895 22b7f6f8 Thomas Thrainer
2896 3f3ea14c Bernardo Dal Seno
    excl_stor = compat.any(
2897 d0d7d7cf Thomas Thrainer
      rpc.GetExclusiveStorageForNodes(self.cfg,
2898 d0d7d7cf Thomas Thrainer
                                      self.instance.all_nodes).values()
2899 3f3ea14c Bernardo Dal Seno
      )
2900 3f3ea14c Bernardo Dal Seno
2901 22b7f6f8 Thomas Thrainer
    # Check disk modifications. This is done here and not in CheckArguments
2902 22b7f6f8 Thomas Thrainer
    # (as with NICs), because we need to know the instance's disk template
2903 3f3ea14c Bernardo Dal Seno
    ver_fn = lambda op, par: self._VerifyDiskModification(op, par, excl_stor)
2904 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template == constants.DT_EXT:
2905 3f3ea14c Bernardo Dal Seno
      self._CheckMods("disk", self.op.disks, {}, ver_fn)
2906 22b7f6f8 Thomas Thrainer
    else:
2907 22b7f6f8 Thomas Thrainer
      self._CheckMods("disk", self.op.disks, constants.IDISK_PARAMS_TYPES,
2908 3f3ea14c Bernardo Dal Seno
                      ver_fn)
2909 22b7f6f8 Thomas Thrainer
2910 5eacbcae Thomas Thrainer
    self.diskmod = _PrepareContainerMods(self.op.disks, None)
2911 22b7f6f8 Thomas Thrainer
2912 22b7f6f8 Thomas Thrainer
    # Check the validity of the `provider' parameter
2913 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DT_EXT:
2914 22b7f6f8 Thomas Thrainer
      for mod in self.diskmod:
2915 22b7f6f8 Thomas Thrainer
        ext_provider = mod[2].get(constants.IDISK_PROVIDER, None)
2916 22b7f6f8 Thomas Thrainer
        if mod[0] == constants.DDM_ADD:
2917 22b7f6f8 Thomas Thrainer
          if ext_provider is None:
2918 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Instance template is '%s' and parameter"
2919 22b7f6f8 Thomas Thrainer
                                       " '%s' missing, during disk add" %
2920 22b7f6f8 Thomas Thrainer
                                       (constants.DT_EXT,
2921 22b7f6f8 Thomas Thrainer
                                        constants.IDISK_PROVIDER),
2922 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_NOENT)
2923 22b7f6f8 Thomas Thrainer
        elif mod[0] == constants.DDM_MODIFY:
2924 22b7f6f8 Thomas Thrainer
          if ext_provider:
2925 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Parameter '%s' is invalid during disk"
2926 22b7f6f8 Thomas Thrainer
                                       " modification" %
2927 22b7f6f8 Thomas Thrainer
                                       constants.IDISK_PROVIDER,
2928 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
2929 22b7f6f8 Thomas Thrainer
    else:
2930 22b7f6f8 Thomas Thrainer
      for mod in self.diskmod:
2931 22b7f6f8 Thomas Thrainer
        ext_provider = mod[2].get(constants.IDISK_PROVIDER, None)
2932 22b7f6f8 Thomas Thrainer
        if ext_provider is not None:
2933 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("Parameter '%s' is only valid for"
2934 22b7f6f8 Thomas Thrainer
                                     " instances of type '%s'" %
2935 22b7f6f8 Thomas Thrainer
                                     (constants.IDISK_PROVIDER,
2936 22b7f6f8 Thomas Thrainer
                                      constants.DT_EXT),
2937 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2938 22b7f6f8 Thomas Thrainer
2939 3c260845 Thomas Thrainer
    if not self.op.wait_for_sync and self.instance.disks_active:
2940 3c260845 Thomas Thrainer
      for mod in self.diskmod:
2941 3c260845 Thomas Thrainer
        if mod[0] == constants.DDM_ADD:
2942 3c260845 Thomas Thrainer
          raise errors.OpPrereqError("Can't add a disk to an instance with"
2943 3c260845 Thomas Thrainer
                                     " activated disks and"
2944 3c260845 Thomas Thrainer
                                     " --no-wait-for-sync given.",
2945 3c260845 Thomas Thrainer
                                     errors.ECODE_INVAL)
2946 3c260845 Thomas Thrainer
2947 d0d7d7cf Thomas Thrainer
    if self.op.disks and self.instance.disk_template == constants.DT_DISKLESS:
2948 1bb99a33 Bernardo Dal Seno
      raise errors.OpPrereqError("Disk operations not supported for"
2949 1bb99a33 Bernardo Dal Seno
                                 " diskless instances", errors.ECODE_INVAL)
2950 1bb99a33 Bernardo Dal Seno
2951 1bb99a33 Bernardo Dal Seno
    def _PrepareDiskMod(_, disk, params, __):
2952 1bb99a33 Bernardo Dal Seno
      disk.name = params.get(constants.IDISK_NAME, None)
2953 1bb99a33 Bernardo Dal Seno
2954 1bb99a33 Bernardo Dal Seno
    # Verify disk changes (operating on a copy)
2955 d0d7d7cf Thomas Thrainer
    disks = copy.deepcopy(self.instance.disks)
2956 1bb99a33 Bernardo Dal Seno
    _ApplyContainerMods("disk", disks, None, self.diskmod, None,
2957 1bb99a33 Bernardo Dal Seno
                        _PrepareDiskMod, None)
2958 1bb99a33 Bernardo Dal Seno
    utils.ValidateDeviceNames("disk", disks)
2959 1bb99a33 Bernardo Dal Seno
    if len(disks) > constants.MAX_DISKS:
2960 1bb99a33 Bernardo Dal Seno
      raise errors.OpPrereqError("Instance has too many disks (%d), cannot add"
2961 1bb99a33 Bernardo Dal Seno
                                 " more" % constants.MAX_DISKS,
2962 1bb99a33 Bernardo Dal Seno
                                 errors.ECODE_STATE)
2963 d0d7d7cf Thomas Thrainer
    disk_sizes = [disk.size for disk in self.instance.disks]
2964 1bb99a33 Bernardo Dal Seno
    disk_sizes.extend(params["size"] for (op, idx, params, private) in
2965 1bb99a33 Bernardo Dal Seno
                      self.diskmod if op == constants.DDM_ADD)
2966 1bb99a33 Bernardo Dal Seno
    ispec[constants.ISPEC_DISK_COUNT] = len(disk_sizes)
2967 1bb99a33 Bernardo Dal Seno
    ispec[constants.ISPEC_DISK_SIZE] = disk_sizes
2968 1bb99a33 Bernardo Dal Seno
2969 1bb99a33 Bernardo Dal Seno
    if self.op.offline is not None and self.op.offline:
2970 d0d7d7cf Thomas Thrainer
      CheckInstanceState(self, self.instance, CAN_CHANGE_INSTANCE_OFFLINE,
2971 1bb99a33 Bernardo Dal Seno
                         msg="can't change to offline")
2972 1bb99a33 Bernardo Dal Seno
2973 9808764a Jose A. Lopes
  @staticmethod
2974 9808764a Jose A. Lopes
  def _InstanceCommunicationDDM(cfg, instance_communication, instance):
2975 9808764a Jose A. Lopes
    """Create a NIC mod that adds or removes the instance
2976 9808764a Jose A. Lopes
    communication NIC to a running instance.
2977 9808764a Jose A. Lopes

2978 9808764a Jose A. Lopes
    The NICS are dynamically created using the Dynamic Device
2979 9808764a Jose A. Lopes
    Modification (DDM).  This function produces a NIC modification
2980 9808764a Jose A. Lopes
    (mod) that inserts an additional NIC meant for instance
2981 9808764a Jose A. Lopes
    communication in or removes an existing instance communication NIC
2982 9808764a Jose A. Lopes
    from a running instance, using DDM.
2983 9808764a Jose A. Lopes

2984 9808764a Jose A. Lopes
    @type cfg: L{config.ConfigWriter}
2985 9808764a Jose A. Lopes
    @param cfg: cluster configuration
2986 9808764a Jose A. Lopes

2987 9808764a Jose A. Lopes
    @type instance_communication: boolean
2988 9808764a Jose A. Lopes
    @param instance_communication: whether instance communication is
2989 9808764a Jose A. Lopes
                                   enabled or disabled
2990 9808764a Jose A. Lopes

2991 9808764a Jose A. Lopes
    @type instance: L{objects.Instance}
2992 9808764a Jose A. Lopes
    @param instance: instance to which the NIC mod will be applied to
2993 9808764a Jose A. Lopes

2994 9808764a Jose A. Lopes
    @rtype: (L{constants.DDM_ADD}, -1, parameters) or
2995 9808764a Jose A. Lopes
            (L{constants.DDM_REMOVE}, -1, parameters) or
2996 9808764a Jose A. Lopes
            L{None}
2997 9808764a Jose A. Lopes
    @return: DDM mod containing an action to add or remove the NIC, or
2998 9808764a Jose A. Lopes
             None if nothing needs to be done
2999 9808764a Jose A. Lopes

3000 9808764a Jose A. Lopes
    """
3001 e25625ab Jose A. Lopes
    nic_name = _ComputeInstanceCommunicationNIC(instance.name)
3002 9808764a Jose A. Lopes
3003 9808764a Jose A. Lopes
    instance_communication_nic = None
3004 9808764a Jose A. Lopes
3005 9808764a Jose A. Lopes
    for nic in instance.nics:
3006 9808764a Jose A. Lopes
      if nic.name == nic_name:
3007 9808764a Jose A. Lopes
        instance_communication_nic = nic
3008 9808764a Jose A. Lopes
        break
3009 9808764a Jose A. Lopes
3010 9808764a Jose A. Lopes
    if instance_communication and not instance_communication_nic:
3011 9808764a Jose A. Lopes
      action = constants.DDM_ADD
3012 9808764a Jose A. Lopes
      params = {constants.INIC_NAME: nic_name,
3013 9808764a Jose A. Lopes
                constants.INIC_MAC: constants.VALUE_GENERATE,
3014 9808764a Jose A. Lopes
                constants.INIC_IP: constants.NIC_IP_POOL,
3015 9808764a Jose A. Lopes
                constants.INIC_NETWORK:
3016 9808764a Jose A. Lopes
                  cfg.GetInstanceCommunicationNetwork()}
3017 9808764a Jose A. Lopes
    elif not instance_communication and instance_communication_nic:
3018 9808764a Jose A. Lopes
      action = constants.DDM_REMOVE
3019 9808764a Jose A. Lopes
      params = None
3020 9808764a Jose A. Lopes
    else:
3021 9808764a Jose A. Lopes
      action = None
3022 9808764a Jose A. Lopes
      params = None
3023 9808764a Jose A. Lopes
3024 9808764a Jose A. Lopes
    if action is not None:
3025 9808764a Jose A. Lopes
      return (action, -1, params)
3026 9808764a Jose A. Lopes
    else:
3027 9808764a Jose A. Lopes
      return None
3028 9808764a Jose A. Lopes
3029 1bb99a33 Bernardo Dal Seno
  def CheckPrereq(self):
3030 1bb99a33 Bernardo Dal Seno
    """Check prerequisites.
3031 1bb99a33 Bernardo Dal Seno

3032 1bb99a33 Bernardo Dal Seno
    This only checks the instance list against the existing names.
3033 1bb99a33 Bernardo Dal Seno

3034 1bb99a33 Bernardo Dal Seno
    """
3035 1bb99a33 Bernardo Dal Seno
    assert self.op.instance_name in self.owned_locks(locking.LEVEL_INSTANCE)
3036 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
3037 d0d7d7cf Thomas Thrainer
    self.cluster = self.cfg.GetClusterInfo()
3038 9c8f7bf4 Helga Velroyen
    cluster_hvparams = self.cluster.hvparams[self.instance.hypervisor]
3039 1bb99a33 Bernardo Dal Seno
3040 1bb99a33 Bernardo Dal Seno
    assert self.instance is not None, \
3041 1bb99a33 Bernardo Dal Seno
      "Cannot retrieve locked instance %s" % self.op.instance_name
3042 1bb99a33 Bernardo Dal Seno
3043 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3044 1bb99a33 Bernardo Dal Seno
3045 1bb99a33 Bernardo Dal Seno
    self.warn = []
3046 1bb99a33 Bernardo Dal Seno
3047 1c3231aa Thomas Thrainer
    if (self.op.pnode_uuid is not None and self.op.pnode_uuid != pnode_uuid and
3048 1bb99a33 Bernardo Dal Seno
        not self.op.force):
3049 1bb99a33 Bernardo Dal Seno
      # verify that the instance is not up
3050 0bbec3af Helga Velroyen
      instance_info = self.rpc.call_instance_info(
3051 d0d7d7cf Thomas Thrainer
          pnode_uuid, self.instance.name, self.instance.hypervisor,
3052 9c8f7bf4 Helga Velroyen
          cluster_hvparams)
3053 1bb99a33 Bernardo Dal Seno
      if instance_info.fail_msg:
3054 1bb99a33 Bernardo Dal Seno
        self.warn.append("Can't get instance runtime information: %s" %
3055 1bb99a33 Bernardo Dal Seno
                         instance_info.fail_msg)
3056 1bb99a33 Bernardo Dal Seno
      elif instance_info.payload:
3057 1c3231aa Thomas Thrainer
        raise errors.OpPrereqError("Instance is still running on %s" %
3058 1c3231aa Thomas Thrainer
                                   self.cfg.GetNodeName(pnode_uuid),
3059 1bb99a33 Bernardo Dal Seno
                                   errors.ECODE_STATE)
3060 1bb99a33 Bernardo Dal Seno
3061 1c3231aa Thomas Thrainer
    assert pnode_uuid in self.owned_locks(locking.LEVEL_NODE)
3062 d0d7d7cf Thomas Thrainer
    node_uuids = list(self.instance.all_nodes)
3063 1c3231aa Thomas Thrainer
    pnode_info = self.cfg.GetNodeInfo(pnode_uuid)
3064 1bb99a33 Bernardo Dal Seno
3065 1bb99a33 Bernardo Dal Seno
    #_CheckInstanceNodeGroups(self.cfg, self.op.instance_name, owned_groups)
3066 1bb99a33 Bernardo Dal Seno
    assert pnode_info.group in self.owned_locks(locking.LEVEL_NODEGROUP)
3067 1bb99a33 Bernardo Dal Seno
    group_info = self.cfg.GetNodeGroup(pnode_info.group)
3068 1bb99a33 Bernardo Dal Seno
3069 1bb99a33 Bernardo Dal Seno
    # dictionary with instance information after the modification
3070 1bb99a33 Bernardo Dal Seno
    ispec = {}
3071 1bb99a33 Bernardo Dal Seno
3072 96ed3a3e Dimitris Aragiorgis
    if self.op.hotplug or self.op.hotplug_if_possible:
3073 24711492 Dimitris Aragiorgis
      result = self.rpc.call_hotplug_supported(self.instance.primary_node,
3074 24711492 Dimitris Aragiorgis
                                               self.instance)
3075 96ed3a3e Dimitris Aragiorgis
      if result.fail_msg:
3076 96ed3a3e Dimitris Aragiorgis
        if self.op.hotplug:
3077 96ed3a3e Dimitris Aragiorgis
          result.Raise("Hotplug is not possible: %s" % result.fail_msg,
3078 96ed3a3e Dimitris Aragiorgis
                       prereq=True)
3079 96ed3a3e Dimitris Aragiorgis
        else:
3080 96ed3a3e Dimitris Aragiorgis
          self.LogWarning(result.fail_msg)
3081 96ed3a3e Dimitris Aragiorgis
          self.op.hotplug = False
3082 96ed3a3e Dimitris Aragiorgis
          self.LogInfo("Modification will take place without hotplugging.")
3083 96ed3a3e Dimitris Aragiorgis
      else:
3084 96ed3a3e Dimitris Aragiorgis
        self.op.hotplug = True
3085 24711492 Dimitris Aragiorgis
3086 1bb99a33 Bernardo Dal Seno
    # Prepare NIC modifications
3087 9808764a Jose A. Lopes
    # add or remove NIC for instance communication
3088 9808764a Jose A. Lopes
    if self.op.instance_communication is not None:
3089 9808764a Jose A. Lopes
      mod = self._InstanceCommunicationDDM(self.cfg,
3090 9808764a Jose A. Lopes
                                           self.op.instance_communication,
3091 9808764a Jose A. Lopes
                                           self.instance)
3092 9808764a Jose A. Lopes
      if mod is not None:
3093 9808764a Jose A. Lopes
        self.op.nics.append(mod)
3094 9808764a Jose A. Lopes
3095 1bb99a33 Bernardo Dal Seno
    self.nicmod = _PrepareContainerMods(self.op.nics, _InstNicModPrivate)
3096 1bb99a33 Bernardo Dal Seno
3097 703f9a66 Jose A. Lopes
    # disks processing
3098 22b7f6f8 Thomas Thrainer
    assert not (self.op.disk_template and self.op.disks), \
3099 22b7f6f8 Thomas Thrainer
      "Can't modify disk template and apply disk changes at the same time"
3100 22b7f6f8 Thomas Thrainer
3101 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
3102 22b7f6f8 Thomas Thrainer
      self._PreCheckDiskTemplate(pnode_info)
3103 22b7f6f8 Thomas Thrainer
3104 1bb99a33 Bernardo Dal Seno
    self._PreCheckDisks(ispec)
3105 1bb99a33 Bernardo Dal Seno
3106 22b7f6f8 Thomas Thrainer
    # hvparams processing
3107 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
3108 d0d7d7cf Thomas Thrainer
      hv_type = self.instance.hypervisor
3109 d0d7d7cf Thomas Thrainer
      i_hvdict = GetUpdatedParams(self.instance.hvparams, self.op.hvparams)
3110 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(i_hvdict, constants.HVS_PARAMETER_TYPES)
3111 d0d7d7cf Thomas Thrainer
      hv_new = self.cluster.SimpleFillHV(hv_type, self.instance.os, i_hvdict)
3112 22b7f6f8 Thomas Thrainer
3113 22b7f6f8 Thomas Thrainer
      # local check
3114 22b7f6f8 Thomas Thrainer
      hypervisor.GetHypervisorClass(hv_type).CheckParameterSyntax(hv_new)
3115 d0d7d7cf Thomas Thrainer
      CheckHVParams(self, node_uuids, self.instance.hypervisor, hv_new)
3116 22b7f6f8 Thomas Thrainer
      self.hv_proposed = self.hv_new = hv_new # the new actual values
3117 22b7f6f8 Thomas Thrainer
      self.hv_inst = i_hvdict # the new dict (without defaults)
3118 22b7f6f8 Thomas Thrainer
    else:
3119 d0d7d7cf Thomas Thrainer
      self.hv_proposed = self.cluster.SimpleFillHV(self.instance.hypervisor,
3120 d0d7d7cf Thomas Thrainer
                                                   self.instance.os,
3121 d0d7d7cf Thomas Thrainer
                                                   self.instance.hvparams)
3122 22b7f6f8 Thomas Thrainer
      self.hv_new = self.hv_inst = {}
3123 22b7f6f8 Thomas Thrainer
3124 22b7f6f8 Thomas Thrainer
    # beparams processing
3125 22b7f6f8 Thomas Thrainer
    if self.op.beparams:
3126 d0d7d7cf Thomas Thrainer
      i_bedict = GetUpdatedParams(self.instance.beparams, self.op.beparams,
3127 5eacbcae Thomas Thrainer
                                  use_none=True)
3128 22b7f6f8 Thomas Thrainer
      objects.UpgradeBeParams(i_bedict)
3129 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(i_bedict, constants.BES_PARAMETER_TYPES)
3130 d0d7d7cf Thomas Thrainer
      be_new = self.cluster.SimpleFillBE(i_bedict)
3131 22b7f6f8 Thomas Thrainer
      self.be_proposed = self.be_new = be_new # the new actual values
3132 22b7f6f8 Thomas Thrainer
      self.be_inst = i_bedict # the new dict (without defaults)
3133 22b7f6f8 Thomas Thrainer
    else:
3134 22b7f6f8 Thomas Thrainer
      self.be_new = self.be_inst = {}
3135 d0d7d7cf Thomas Thrainer
      self.be_proposed = self.cluster.SimpleFillBE(self.instance.beparams)
3136 d0d7d7cf Thomas Thrainer
    be_old = self.cluster.FillBE(self.instance)
3137 22b7f6f8 Thomas Thrainer
3138 22b7f6f8 Thomas Thrainer
    # CPU param validation -- checking every time a parameter is
3139 22b7f6f8 Thomas Thrainer
    # changed to cover all cases where either CPU mask or vcpus have
3140 22b7f6f8 Thomas Thrainer
    # changed
3141 22b7f6f8 Thomas Thrainer
    if (constants.BE_VCPUS in self.be_proposed and
3142 22b7f6f8 Thomas Thrainer
        constants.HV_CPU_MASK in self.hv_proposed):
3143 22b7f6f8 Thomas Thrainer
      cpu_list = \
3144 22b7f6f8 Thomas Thrainer
        utils.ParseMultiCpuMask(self.hv_proposed[constants.HV_CPU_MASK])
3145 22b7f6f8 Thomas Thrainer
      # Verify mask is consistent with number of vCPUs. Can skip this
3146 22b7f6f8 Thomas Thrainer
      # test if only 1 entry in the CPU mask, which means same mask
3147 22b7f6f8 Thomas Thrainer
      # is applied to all vCPUs.
3148 22b7f6f8 Thomas Thrainer
      if (len(cpu_list) > 1 and
3149 22b7f6f8 Thomas Thrainer
          len(cpu_list) != self.be_proposed[constants.BE_VCPUS]):
3150 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Number of vCPUs [%d] does not match the"
3151 22b7f6f8 Thomas Thrainer
                                   " CPU mask [%s]" %
3152 22b7f6f8 Thomas Thrainer
                                   (self.be_proposed[constants.BE_VCPUS],
3153 22b7f6f8 Thomas Thrainer
                                    self.hv_proposed[constants.HV_CPU_MASK]),
3154 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3155 22b7f6f8 Thomas Thrainer
3156 22b7f6f8 Thomas Thrainer
      # Only perform this test if a new CPU mask is given
3157 22b7f6f8 Thomas Thrainer
      if constants.HV_CPU_MASK in self.hv_new:
3158 22b7f6f8 Thomas Thrainer
        # Calculate the largest CPU number requested
3159 22b7f6f8 Thomas Thrainer
        max_requested_cpu = max(map(max, cpu_list))
3160 22b7f6f8 Thomas Thrainer
        # Check that all of the instance's nodes have enough physical CPUs to
3161 22b7f6f8 Thomas Thrainer
        # satisfy the requested CPU mask
3162 d0d7d7cf Thomas Thrainer
        hvspecs = [(self.instance.hypervisor,
3163 d0d7d7cf Thomas Thrainer
                    self.cfg.GetClusterInfo()
3164 d0d7d7cf Thomas Thrainer
                      .hvparams[self.instance.hypervisor])]
3165 d0d7d7cf Thomas Thrainer
        _CheckNodesPhysicalCPUs(self, self.instance.all_nodes,
3166 a295eb80 Helga Velroyen
                                max_requested_cpu + 1,
3167 a295eb80 Helga Velroyen
                                hvspecs)
3168 22b7f6f8 Thomas Thrainer
3169 22b7f6f8 Thomas Thrainer
    # osparams processing
3170 703f9a66 Jose A. Lopes
    if self.op.os_name and not self.op.force:
3171 703f9a66 Jose A. Lopes
      instance_os = self.op.os_name
3172 703f9a66 Jose A. Lopes
    else:
3173 703f9a66 Jose A. Lopes
      instance_os = self.instance.os
3174 703f9a66 Jose A. Lopes
3175 6bce7ba2 Santi Raffa
    if self.op.osparams or self.op.osparams_private:
3176 1a182390 Santi Raffa
      public_parms = self.op.osparams or {}
3177 6bce7ba2 Santi Raffa
      private_parms = self.op.osparams_private or {}
3178 1a182390 Santi Raffa
      dupe_keys = utils.GetRepeatedKeys(public_parms, private_parms)
3179 1a182390 Santi Raffa
3180 1a182390 Santi Raffa
      if dupe_keys:
3181 1a182390 Santi Raffa
        raise errors.OpPrereqError("OS parameters repeated multiple times: %s" %
3182 1a182390 Santi Raffa
                                   utils.CommaJoin(dupe_keys))
3183 1a182390 Santi Raffa
3184 1a182390 Santi Raffa
      self.os_inst = GetUpdatedParams(self.instance.osparams,
3185 1a182390 Santi Raffa
                                      public_parms)
3186 1a182390 Santi Raffa
      self.os_inst_private = GetUpdatedParams(self.instance.osparams_private,
3187 1a182390 Santi Raffa
                                              private_parms)
3188 1a182390 Santi Raffa
3189 1a182390 Santi Raffa
      CheckOSParams(self, True, node_uuids, instance_os,
3190 1a182390 Santi Raffa
                    objects.FillDict(self.os_inst,
3191 ff030c75 Jose A. Lopes
                                     self.os_inst_private),
3192 ff030c75 Jose A. Lopes
                    self.op.force_variant)
3193 1a182390 Santi Raffa
3194 22b7f6f8 Thomas Thrainer
    else:
3195 22b7f6f8 Thomas Thrainer
      self.os_inst = {}
3196 1a182390 Santi Raffa
      self.os_inst_private = {}
3197 22b7f6f8 Thomas Thrainer
3198 22b7f6f8 Thomas Thrainer
    #TODO(dynmem): do the appropriate check involving MINMEM
3199 22b7f6f8 Thomas Thrainer
    if (constants.BE_MAXMEM in self.op.beparams and not self.op.force and
3200 22b7f6f8 Thomas Thrainer
        be_new[constants.BE_MAXMEM] > be_old[constants.BE_MAXMEM]):
3201 1c3231aa Thomas Thrainer
      mem_check_list = [pnode_uuid]
3202 22b7f6f8 Thomas Thrainer
      if be_new[constants.BE_AUTO_BALANCE]:
3203 22b7f6f8 Thomas Thrainer
        # either we changed auto_balance to yes or it was from before
3204 d0d7d7cf Thomas Thrainer
        mem_check_list.extend(self.instance.secondary_nodes)
3205 a295eb80 Helga Velroyen
      instance_info = self.rpc.call_instance_info(
3206 d0d7d7cf Thomas Thrainer
          pnode_uuid, self.instance.name, self.instance.hypervisor,
3207 9c8f7bf4 Helga Velroyen
          cluster_hvparams)
3208 d0d7d7cf Thomas Thrainer
      hvspecs = [(self.instance.hypervisor,
3209 9c8f7bf4 Helga Velroyen
                  cluster_hvparams)]
3210 22b7f6f8 Thomas Thrainer
      nodeinfo = self.rpc.call_node_info(mem_check_list, None,
3211 da803ff1 Helga Velroyen
                                         hvspecs)
3212 1c3231aa Thomas Thrainer
      pninfo = nodeinfo[pnode_uuid]
3213 22b7f6f8 Thomas Thrainer
      msg = pninfo.fail_msg
3214 22b7f6f8 Thomas Thrainer
      if msg:
3215 22b7f6f8 Thomas Thrainer
        # Assume the primary node is unreachable and go ahead
3216 22b7f6f8 Thomas Thrainer
        self.warn.append("Can't get info from primary node %s: %s" %
3217 1c3231aa Thomas Thrainer
                         (self.cfg.GetNodeName(pnode_uuid), msg))
3218 22b7f6f8 Thomas Thrainer
      else:
3219 22b7f6f8 Thomas Thrainer
        (_, _, (pnhvinfo, )) = pninfo.payload
3220 22b7f6f8 Thomas Thrainer
        if not isinstance(pnhvinfo.get("memory_free", None), int):
3221 22b7f6f8 Thomas Thrainer
          self.warn.append("Node data from primary node %s doesn't contain"
3222 1c3231aa Thomas Thrainer
                           " free memory information" %
3223 1c3231aa Thomas Thrainer
                           self.cfg.GetNodeName(pnode_uuid))
3224 22b7f6f8 Thomas Thrainer
        elif instance_info.fail_msg:
3225 22b7f6f8 Thomas Thrainer
          self.warn.append("Can't get instance runtime information: %s" %
3226 22b7f6f8 Thomas Thrainer
                           instance_info.fail_msg)
3227 22b7f6f8 Thomas Thrainer
        else:
3228 22b7f6f8 Thomas Thrainer
          if instance_info.payload:
3229 22b7f6f8 Thomas Thrainer
            current_mem = int(instance_info.payload["memory"])
3230 22b7f6f8 Thomas Thrainer
          else:
3231 22b7f6f8 Thomas Thrainer
            # Assume instance not running
3232 22b7f6f8 Thomas Thrainer
            # (there is a slight race condition here, but it's not very
3233 22b7f6f8 Thomas Thrainer
            # probable, and we have no other way to check)
3234 22b7f6f8 Thomas Thrainer
            # TODO: Describe race condition
3235 22b7f6f8 Thomas Thrainer
            current_mem = 0
3236 22b7f6f8 Thomas Thrainer
          #TODO(dynmem): do the appropriate check involving MINMEM
3237 22b7f6f8 Thomas Thrainer
          miss_mem = (be_new[constants.BE_MAXMEM] - current_mem -
3238 22b7f6f8 Thomas Thrainer
                      pnhvinfo["memory_free"])
3239 22b7f6f8 Thomas Thrainer
          if miss_mem > 0:
3240 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("This change will prevent the instance"
3241 22b7f6f8 Thomas Thrainer
                                       " from starting, due to %d MB of memory"
3242 22b7f6f8 Thomas Thrainer
                                       " missing on its primary node" %
3243 22b7f6f8 Thomas Thrainer
                                       miss_mem, errors.ECODE_NORES)
3244 22b7f6f8 Thomas Thrainer
3245 22b7f6f8 Thomas Thrainer
      if be_new[constants.BE_AUTO_BALANCE]:
3246 1c3231aa Thomas Thrainer
        for node_uuid, nres in nodeinfo.items():
3247 d0d7d7cf Thomas Thrainer
          if node_uuid not in self.instance.secondary_nodes:
3248 22b7f6f8 Thomas Thrainer
            continue
3249 1c3231aa Thomas Thrainer
          nres.Raise("Can't get info from secondary node %s" %
3250 1c3231aa Thomas Thrainer
                     self.cfg.GetNodeName(node_uuid), prereq=True,
3251 1c3231aa Thomas Thrainer
                     ecode=errors.ECODE_STATE)
3252 22b7f6f8 Thomas Thrainer
          (_, _, (nhvinfo, )) = nres.payload
3253 22b7f6f8 Thomas Thrainer
          if not isinstance(nhvinfo.get("memory_free", None), int):
3254 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Secondary node %s didn't return free"
3255 1c3231aa Thomas Thrainer
                                       " memory information" %
3256 1c3231aa Thomas Thrainer
                                       self.cfg.GetNodeName(node_uuid),
3257 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_STATE)
3258 22b7f6f8 Thomas Thrainer
          #TODO(dynmem): do the appropriate check involving MINMEM
3259 22b7f6f8 Thomas Thrainer
          elif be_new[constants.BE_MAXMEM] > nhvinfo["memory_free"]:
3260 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("This change will prevent the instance"
3261 22b7f6f8 Thomas Thrainer
                                       " from failover to its secondary node"
3262 1c3231aa Thomas Thrainer
                                       " %s, due to not enough memory" %
3263 1c3231aa Thomas Thrainer
                                       self.cfg.GetNodeName(node_uuid),
3264 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_STATE)
3265 22b7f6f8 Thomas Thrainer
3266 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
3267 0bbec3af Helga Velroyen
      remote_info = self.rpc.call_instance_info(
3268 d0d7d7cf Thomas Thrainer
         self.instance.primary_node, self.instance.name,
3269 b666a94c Helga Velroyen
         self.instance.hypervisor,
3270 9c8f7bf4 Helga Velroyen
         cluster_hvparams)
3271 1c3231aa Thomas Thrainer
      remote_info.Raise("Error checking node %s" %
3272 d0d7d7cf Thomas Thrainer
                        self.cfg.GetNodeName(self.instance.primary_node))
3273 22b7f6f8 Thomas Thrainer
      if not remote_info.payload: # not running already
3274 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s is not running" %
3275 d0d7d7cf Thomas Thrainer
                                   self.instance.name, errors.ECODE_STATE)
3276 22b7f6f8 Thomas Thrainer
3277 22b7f6f8 Thomas Thrainer
      current_memory = remote_info.payload["memory"]
3278 22b7f6f8 Thomas Thrainer
      if (not self.op.force and
3279 22b7f6f8 Thomas Thrainer
           (self.op.runtime_mem > self.be_proposed[constants.BE_MAXMEM] or
3280 22b7f6f8 Thomas Thrainer
            self.op.runtime_mem < self.be_proposed[constants.BE_MINMEM])):
3281 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s must have memory between %d"
3282 22b7f6f8 Thomas Thrainer
                                   " and %d MB of memory unless --force is"
3283 22b7f6f8 Thomas Thrainer
                                   " given" %
3284 d0d7d7cf Thomas Thrainer
                                   (self.instance.name,
3285 22b7f6f8 Thomas Thrainer
                                    self.be_proposed[constants.BE_MINMEM],
3286 22b7f6f8 Thomas Thrainer
                                    self.be_proposed[constants.BE_MAXMEM]),
3287 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3288 22b7f6f8 Thomas Thrainer
3289 22b7f6f8 Thomas Thrainer
      delta = self.op.runtime_mem - current_memory
3290 22b7f6f8 Thomas Thrainer
      if delta > 0:
3291 a295eb80 Helga Velroyen
        CheckNodeFreeMemory(
3292 d0d7d7cf Thomas Thrainer
            self, self.instance.primary_node,
3293 d0d7d7cf Thomas Thrainer
            "ballooning memory for instance %s" % self.instance.name, delta,
3294 d0d7d7cf Thomas Thrainer
            self.instance.hypervisor,
3295 d0d7d7cf Thomas Thrainer
            self.cfg.GetClusterInfo().hvparams[self.instance.hypervisor])
3296 d0d7d7cf Thomas Thrainer
3297 d0d7d7cf Thomas Thrainer
    # make self.cluster visible in the functions below
3298 d0d7d7cf Thomas Thrainer
    cluster = self.cluster
3299 22b7f6f8 Thomas Thrainer
3300 22b7f6f8 Thomas Thrainer
    def _PrepareNicCreate(_, params, private):
3301 22b7f6f8 Thomas Thrainer
      self._PrepareNicModification(params, private, None, None,
3302 1c3231aa Thomas Thrainer
                                   {}, cluster, pnode_uuid)
3303 22b7f6f8 Thomas Thrainer
      return (None, None)
3304 22b7f6f8 Thomas Thrainer
3305 22b7f6f8 Thomas Thrainer
    def _PrepareNicMod(_, nic, params, private):
3306 22b7f6f8 Thomas Thrainer
      self._PrepareNicModification(params, private, nic.ip, nic.network,
3307 1c3231aa Thomas Thrainer
                                   nic.nicparams, cluster, pnode_uuid)
3308 22b7f6f8 Thomas Thrainer
      return None
3309 22b7f6f8 Thomas Thrainer
3310 22b7f6f8 Thomas Thrainer
    def _PrepareNicRemove(_, params, __):
3311 22b7f6f8 Thomas Thrainer
      ip = params.ip
3312 22b7f6f8 Thomas Thrainer
      net = params.network
3313 22b7f6f8 Thomas Thrainer
      if net is not None and ip is not None:
3314 22b7f6f8 Thomas Thrainer
        self.cfg.ReleaseIp(net, ip, self.proc.GetECId())
3315 22b7f6f8 Thomas Thrainer
3316 22b7f6f8 Thomas Thrainer
    # Verify NIC changes (operating on copy)
3317 ebe70850 Jose A. Lopes
    nics = [nic.Copy() for nic in self.instance.nics]
3318 5eacbcae Thomas Thrainer
    _ApplyContainerMods("NIC", nics, None, self.nicmod,
3319 5eacbcae Thomas Thrainer
                        _PrepareNicCreate, _PrepareNicMod, _PrepareNicRemove)
3320 22b7f6f8 Thomas Thrainer
    if len(nics) > constants.MAX_NICS:
3321 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance has too many network interfaces"
3322 22b7f6f8 Thomas Thrainer
                                 " (%d), cannot add more" % constants.MAX_NICS,
3323 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
3324 22b7f6f8 Thomas Thrainer
3325 22b7f6f8 Thomas Thrainer
    # Pre-compute NIC changes (necessary to use result in hooks)
3326 22b7f6f8 Thomas Thrainer
    self._nic_chgdesc = []
3327 22b7f6f8 Thomas Thrainer
    if self.nicmod:
3328 22b7f6f8 Thomas Thrainer
      # Operate on copies as this is still in prereq
3329 d0d7d7cf Thomas Thrainer
      nics = [nic.Copy() for nic in self.instance.nics]
3330 5eacbcae Thomas Thrainer
      _ApplyContainerMods("NIC", nics, self._nic_chgdesc, self.nicmod,
3331 ba924970 Dimitris Aragiorgis
                          self._CreateNewNic, self._ApplyNicMods,
3332 ba924970 Dimitris Aragiorgis
                          self._RemoveNic)
3333 22b7f6f8 Thomas Thrainer
      # Verify that NIC names are unique and valid
3334 22b7f6f8 Thomas Thrainer
      utils.ValidateDeviceNames("NIC", nics)
3335 22b7f6f8 Thomas Thrainer
      self._new_nics = nics
3336 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_NIC_COUNT] = len(self._new_nics)
3337 22b7f6f8 Thomas Thrainer
    else:
3338 22b7f6f8 Thomas Thrainer
      self._new_nics = None
3339 d0d7d7cf Thomas Thrainer
      ispec[constants.ISPEC_NIC_COUNT] = len(self.instance.nics)
3340 22b7f6f8 Thomas Thrainer
3341 22b7f6f8 Thomas Thrainer
    if not self.op.ignore_ipolicy:
3342 d0d7d7cf Thomas Thrainer
      ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(self.cluster,
3343 22b7f6f8 Thomas Thrainer
                                                              group_info)
3344 22b7f6f8 Thomas Thrainer
3345 22b7f6f8 Thomas Thrainer
      # Fill ispec with backend parameters
3346 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_SPINDLE_USE] = \
3347 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_SPINDLE_USE, None)
3348 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_CPU_COUNT] = self.be_new.get(constants.BE_VCPUS,
3349 22b7f6f8 Thomas Thrainer
                                                         None)
3350 22b7f6f8 Thomas Thrainer
3351 22b7f6f8 Thomas Thrainer
      # Copy ispec to verify parameters with min/max values separately
3352 22b7f6f8 Thomas Thrainer
      if self.op.disk_template:
3353 22b7f6f8 Thomas Thrainer
        new_disk_template = self.op.disk_template
3354 22b7f6f8 Thomas Thrainer
      else:
3355 d0d7d7cf Thomas Thrainer
        new_disk_template = self.instance.disk_template
3356 22b7f6f8 Thomas Thrainer
      ispec_max = ispec.copy()
3357 22b7f6f8 Thomas Thrainer
      ispec_max[constants.ISPEC_MEM_SIZE] = \
3358 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_MAXMEM, None)
3359 22b7f6f8 Thomas Thrainer
      res_max = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec_max,
3360 22b7f6f8 Thomas Thrainer
                                                     new_disk_template)
3361 22b7f6f8 Thomas Thrainer
      ispec_min = ispec.copy()
3362 22b7f6f8 Thomas Thrainer
      ispec_min[constants.ISPEC_MEM_SIZE] = \
3363 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_MINMEM, None)
3364 22b7f6f8 Thomas Thrainer
      res_min = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec_min,
3365 22b7f6f8 Thomas Thrainer
                                                     new_disk_template)
3366 22b7f6f8 Thomas Thrainer
3367 22b7f6f8 Thomas Thrainer
      if (res_max or res_min):
3368 22b7f6f8 Thomas Thrainer
        # FIXME: Improve error message by including information about whether
3369 22b7f6f8 Thomas Thrainer
        # the upper or lower limit of the parameter fails the ipolicy.
3370 22b7f6f8 Thomas Thrainer
        msg = ("Instance allocation to group %s (%s) violates policy: %s" %
3371 22b7f6f8 Thomas Thrainer
               (group_info, group_info.name,
3372 22b7f6f8 Thomas Thrainer
                utils.CommaJoin(set(res_max + res_min))))
3373 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
3374 22b7f6f8 Thomas Thrainer
3375 22b7f6f8 Thomas Thrainer
  def _ConvertPlainToDrbd(self, feedback_fn):
3376 22b7f6f8 Thomas Thrainer
    """Converts an instance from plain to drbd.
3377 22b7f6f8 Thomas Thrainer

3378 22b7f6f8 Thomas Thrainer
    """
3379 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to drbd")
3380 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3381 1c3231aa Thomas Thrainer
    snode_uuid = self.op.remote_node_uuid
3382 22b7f6f8 Thomas Thrainer
3383 d0d7d7cf Thomas Thrainer
    assert self.instance.disk_template == constants.DT_PLAIN
3384 22b7f6f8 Thomas Thrainer
3385 22b7f6f8 Thomas Thrainer
    # create a fake disk info for _GenerateDiskTemplate
3386 22b7f6f8 Thomas Thrainer
    disk_info = [{constants.IDISK_SIZE: d.size, constants.IDISK_MODE: d.mode,
3387 22b7f6f8 Thomas Thrainer
                  constants.IDISK_VG: d.logical_id[0],
3388 22b7f6f8 Thomas Thrainer
                  constants.IDISK_NAME: d.name}
3389 d0d7d7cf Thomas Thrainer
                 for d in self.instance.disks]
3390 5eacbcae Thomas Thrainer
    new_disks = GenerateDiskTemplate(self, self.op.disk_template,
3391 da4a52a3 Thomas Thrainer
                                     self.instance.uuid, pnode_uuid,
3392 d0d7d7cf Thomas Thrainer
                                     [snode_uuid], disk_info, None, None, 0,
3393 d0d7d7cf Thomas Thrainer
                                     feedback_fn, self.diskparams)
3394 0c3d9c7c Thomas Thrainer
    anno_disks = rpc.AnnotateDiskParams(new_disks, self.diskparams)
3395 1c3231aa Thomas Thrainer
    p_excl_stor = IsExclusiveStorageEnabledNodeUuid(self.cfg, pnode_uuid)
3396 1c3231aa Thomas Thrainer
    s_excl_stor = IsExclusiveStorageEnabledNodeUuid(self.cfg, snode_uuid)
3397 d0d7d7cf Thomas Thrainer
    info = GetInstanceInfoText(self.instance)
3398 22b7f6f8 Thomas Thrainer
    feedback_fn("Creating additional volumes...")
3399 22b7f6f8 Thomas Thrainer
    # first, create the missing data and meta devices
3400 22b7f6f8 Thomas Thrainer
    for disk in anno_disks:
3401 22b7f6f8 Thomas Thrainer
      # unfortunately this is... not too nice
3402 d0d7d7cf Thomas Thrainer
      CreateSingleBlockDev(self, pnode_uuid, self.instance, disk.children[1],
3403 5eacbcae Thomas Thrainer
                           info, True, p_excl_stor)
3404 22b7f6f8 Thomas Thrainer
      for child in disk.children:
3405 d0d7d7cf Thomas Thrainer
        CreateSingleBlockDev(self, snode_uuid, self.instance, child, info, True,
3406 5eacbcae Thomas Thrainer
                             s_excl_stor)
3407 22b7f6f8 Thomas Thrainer
    # at this stage, all new LVs have been created, we can rename the
3408 22b7f6f8 Thomas Thrainer
    # old ones
3409 22b7f6f8 Thomas Thrainer
    feedback_fn("Renaming original volumes...")
3410 22b7f6f8 Thomas Thrainer
    rename_list = [(o, n.children[0].logical_id)
3411 d0d7d7cf Thomas Thrainer
                   for (o, n) in zip(self.instance.disks, new_disks)]
3412 1c3231aa Thomas Thrainer
    result = self.rpc.call_blockdev_rename(pnode_uuid, rename_list)
3413 22b7f6f8 Thomas Thrainer
    result.Raise("Failed to rename original LVs")
3414 22b7f6f8 Thomas Thrainer
3415 22b7f6f8 Thomas Thrainer
    feedback_fn("Initializing DRBD devices...")
3416 22b7f6f8 Thomas Thrainer
    # all child devices are in place, we can now create the DRBD devices
3417 22b7f6f8 Thomas Thrainer
    try:
3418 22b7f6f8 Thomas Thrainer
      for disk in anno_disks:
3419 1c3231aa Thomas Thrainer
        for (node_uuid, excl_stor) in [(pnode_uuid, p_excl_stor),
3420 1c3231aa Thomas Thrainer
                                       (snode_uuid, s_excl_stor)]:
3421 1c3231aa Thomas Thrainer
          f_create = node_uuid == pnode_uuid
3422 d0d7d7cf Thomas Thrainer
          CreateSingleBlockDev(self, node_uuid, self.instance, disk, info,
3423 d0d7d7cf Thomas Thrainer
                               f_create, excl_stor)
3424 22b7f6f8 Thomas Thrainer
    except errors.GenericError, e:
3425 22b7f6f8 Thomas Thrainer
      feedback_fn("Initializing of DRBD devices failed;"
3426 22b7f6f8 Thomas Thrainer
                  " renaming back original volumes...")
3427 22b7f6f8 Thomas Thrainer
      rename_back_list = [(n.children[0], o.logical_id)
3428 d0d7d7cf Thomas Thrainer
                          for (n, o) in zip(new_disks, self.instance.disks)]
3429 1c3231aa Thomas Thrainer
      result = self.rpc.call_blockdev_rename(pnode_uuid, rename_back_list)
3430 22b7f6f8 Thomas Thrainer
      result.Raise("Failed to rename LVs back after error %s" % str(e))
3431 22b7f6f8 Thomas Thrainer
      raise
3432 22b7f6f8 Thomas Thrainer
3433 22b7f6f8 Thomas Thrainer
    # at this point, the instance has been modified
3434 d0d7d7cf Thomas Thrainer
    self.instance.disk_template = constants.DT_DRBD8
3435 d0d7d7cf Thomas Thrainer
    self.instance.disks = new_disks
3436 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
3437 22b7f6f8 Thomas Thrainer
3438 22b7f6f8 Thomas Thrainer
    # Release node locks while waiting for sync
3439 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3440 22b7f6f8 Thomas Thrainer
3441 22b7f6f8 Thomas Thrainer
    # disks are created, waiting for sync
3442 d0d7d7cf Thomas Thrainer
    disk_abort = not WaitForSync(self, self.instance,
3443 5eacbcae Thomas Thrainer
                                 oneshot=not self.op.wait_for_sync)
3444 22b7f6f8 Thomas Thrainer
    if disk_abort:
3445 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("There are some degraded disks for"
3446 22b7f6f8 Thomas Thrainer
                               " this instance, please cleanup manually")
3447 22b7f6f8 Thomas Thrainer
3448 22b7f6f8 Thomas Thrainer
    # Node resource locks will be released by caller
3449 22b7f6f8 Thomas Thrainer
3450 22b7f6f8 Thomas Thrainer
  def _ConvertDrbdToPlain(self, feedback_fn):
3451 22b7f6f8 Thomas Thrainer
    """Converts an instance from drbd to plain.
3452 22b7f6f8 Thomas Thrainer

3453 22b7f6f8 Thomas Thrainer
    """
3454 d0d7d7cf Thomas Thrainer
    assert len(self.instance.secondary_nodes) == 1
3455 d0d7d7cf Thomas Thrainer
    assert self.instance.disk_template == constants.DT_DRBD8
3456 22b7f6f8 Thomas Thrainer
3457 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3458 d0d7d7cf Thomas Thrainer
    snode_uuid = self.instance.secondary_nodes[0]
3459 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to plain")
3460 22b7f6f8 Thomas Thrainer
3461 d0d7d7cf Thomas Thrainer
    old_disks = AnnotateDiskParams(self.instance, self.instance.disks, self.cfg)
3462 d0d7d7cf Thomas Thrainer
    new_disks = [d.children[0] for d in self.instance.disks]
3463 22b7f6f8 Thomas Thrainer
3464 22b7f6f8 Thomas Thrainer
    # copy over size, mode and name
3465 22b7f6f8 Thomas Thrainer
    for parent, child in zip(old_disks, new_disks):
3466 22b7f6f8 Thomas Thrainer
      child.size = parent.size
3467 22b7f6f8 Thomas Thrainer
      child.mode = parent.mode
3468 22b7f6f8 Thomas Thrainer
      child.name = parent.name
3469 22b7f6f8 Thomas Thrainer
3470 22b7f6f8 Thomas Thrainer
    # this is a DRBD disk, return its port to the pool
3471 22b7f6f8 Thomas Thrainer
    # NOTE: this must be done right before the call to cfg.Update!
3472 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3473 22b7f6f8 Thomas Thrainer
      tcp_port = disk.logical_id[2]
3474 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(tcp_port)
3475 22b7f6f8 Thomas Thrainer
3476 22b7f6f8 Thomas Thrainer
    # update instance structure
3477 d0d7d7cf Thomas Thrainer
    self.instance.disks = new_disks
3478 d0d7d7cf Thomas Thrainer
    self.instance.disk_template = constants.DT_PLAIN
3479 d0d7d7cf Thomas Thrainer
    _UpdateIvNames(0, self.instance.disks)
3480 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
3481 22b7f6f8 Thomas Thrainer
3482 22b7f6f8 Thomas Thrainer
    # Release locks in case removing disks takes a while
3483 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3484 22b7f6f8 Thomas Thrainer
3485 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing volumes on the secondary node...")
3486 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3487 0c3d9c7c Thomas Thrainer
      result = self.rpc.call_blockdev_remove(snode_uuid, (disk, self.instance))
3488 aefc2f89 Thomas Thrainer
      result.Warn("Could not remove block device %s on node %s,"
3489 aefc2f89 Thomas Thrainer
                  " continuing anyway" %
3490 aefc2f89 Thomas Thrainer
                  (disk.iv_name, self.cfg.GetNodeName(snode_uuid)),
3491 aefc2f89 Thomas Thrainer
                  self.LogWarning)
3492 22b7f6f8 Thomas Thrainer
3493 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing unneeded volumes on the primary node...")
3494 22b7f6f8 Thomas Thrainer
    for idx, disk in enumerate(old_disks):
3495 22b7f6f8 Thomas Thrainer
      meta = disk.children[1]
3496 0c3d9c7c Thomas Thrainer
      result = self.rpc.call_blockdev_remove(pnode_uuid, (meta, self.instance))
3497 aefc2f89 Thomas Thrainer
      result.Warn("Could not remove metadata for disk %d on node %s,"
3498 aefc2f89 Thomas Thrainer
                  " continuing anyway" %
3499 aefc2f89 Thomas Thrainer
                  (idx, self.cfg.GetNodeName(pnode_uuid)),
3500 aefc2f89 Thomas Thrainer
                  self.LogWarning)
3501 22b7f6f8 Thomas Thrainer
3502 ba924970 Dimitris Aragiorgis
  def _HotplugDevice(self, action, dev_type, device, extra, seq):
3503 ba924970 Dimitris Aragiorgis
    self.LogInfo("Trying to hotplug device...")
3504 e15a00dc Dimitris Aragiorgis
    msg = "hotplug:"
3505 ba924970 Dimitris Aragiorgis
    result = self.rpc.call_hotplug_device(self.instance.primary_node,
3506 ba924970 Dimitris Aragiorgis
                                          self.instance, action, dev_type,
3507 51951d38 Dimitris Aragiorgis
                                          (device, self.instance),
3508 51951d38 Dimitris Aragiorgis
                                          extra, seq)
3509 ba924970 Dimitris Aragiorgis
    if result.fail_msg:
3510 ba924970 Dimitris Aragiorgis
      self.LogWarning("Could not hotplug device: %s" % result.fail_msg)
3511 ba924970 Dimitris Aragiorgis
      self.LogInfo("Continuing execution..")
3512 e15a00dc Dimitris Aragiorgis
      msg += "failed"
3513 ba924970 Dimitris Aragiorgis
    else:
3514 ba924970 Dimitris Aragiorgis
      self.LogInfo("Hotplug done.")
3515 e15a00dc Dimitris Aragiorgis
      msg += "done"
3516 e15a00dc Dimitris Aragiorgis
    return msg
3517 ba924970 Dimitris Aragiorgis
3518 22b7f6f8 Thomas Thrainer
  def _CreateNewDisk(self, idx, params, _):
3519 22b7f6f8 Thomas Thrainer
    """Creates a new disk.
3520 22b7f6f8 Thomas Thrainer

3521 22b7f6f8 Thomas Thrainer
    """
3522 22b7f6f8 Thomas Thrainer
    # add a new disk
3523 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DTS_FILEBASED:
3524 d0d7d7cf Thomas Thrainer
      (file_driver, file_path) = self.instance.disks[0].logical_id
3525 22b7f6f8 Thomas Thrainer
      file_path = os.path.dirname(file_path)
3526 22b7f6f8 Thomas Thrainer
    else:
3527 22b7f6f8 Thomas Thrainer
      file_driver = file_path = None
3528 22b7f6f8 Thomas Thrainer
3529 22b7f6f8 Thomas Thrainer
    disk = \
3530 d0d7d7cf Thomas Thrainer
      GenerateDiskTemplate(self, self.instance.disk_template,
3531 da4a52a3 Thomas Thrainer
                           self.instance.uuid, self.instance.primary_node,
3532 d0d7d7cf Thomas Thrainer
                           self.instance.secondary_nodes, [params], file_path,
3533 d0d7d7cf Thomas Thrainer
                           file_driver, idx, self.Log, self.diskparams)[0]
3534 22b7f6f8 Thomas Thrainer
3535 d0d7d7cf Thomas Thrainer
    new_disks = CreateDisks(self, self.instance, disks=[disk])
3536 22b7f6f8 Thomas Thrainer
3537 22b7f6f8 Thomas Thrainer
    if self.cluster.prealloc_wipe_disks:
3538 22b7f6f8 Thomas Thrainer
      # Wipe new disk
3539 d0d7d7cf Thomas Thrainer
      WipeOrCleanupDisks(self, self.instance,
3540 a365b47f Bernardo Dal Seno
                         disks=[(idx, disk, 0)],
3541 a365b47f Bernardo Dal Seno
                         cleanup=new_disks)
3542 22b7f6f8 Thomas Thrainer
3543 e15a00dc Dimitris Aragiorgis
    changes = [
3544 e15a00dc Dimitris Aragiorgis
      ("disk/%d" % idx,
3545 b15d5bd3 Petr Pudlak
       "add:size=%s,mode=%s" % (disk.size, disk.mode)),
3546 e15a00dc Dimitris Aragiorgis
      ]
3547 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3548 ba924970 Dimitris Aragiorgis
      result = self.rpc.call_blockdev_assemble(self.instance.primary_node,
3549 ba924970 Dimitris Aragiorgis
                                               (disk, self.instance),
3550 ba924970 Dimitris Aragiorgis
                                               self.instance.name, True, idx)
3551 ba924970 Dimitris Aragiorgis
      if result.fail_msg:
3552 e15a00dc Dimitris Aragiorgis
        changes.append(("disk/%d" % idx, "assemble:failed"))
3553 ba924970 Dimitris Aragiorgis
        self.LogWarning("Can't assemble newly created disk %d: %s",
3554 ba924970 Dimitris Aragiorgis
                        idx, result.fail_msg)
3555 ba924970 Dimitris Aragiorgis
      else:
3556 ba924970 Dimitris Aragiorgis
        _, link_name = result.payload
3557 e15a00dc Dimitris Aragiorgis
        msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
3558 e15a00dc Dimitris Aragiorgis
                                  constants.HOTPLUG_TARGET_DISK,
3559 e15a00dc Dimitris Aragiorgis
                                  disk, link_name, idx)
3560 e15a00dc Dimitris Aragiorgis
        changes.append(("disk/%d" % idx, msg))
3561 ba924970 Dimitris Aragiorgis
3562 e15a00dc Dimitris Aragiorgis
    return (disk, changes)
3563 22b7f6f8 Thomas Thrainer
3564 922a9e65 Thomas Thrainer
  def _PostAddDisk(self, _, disk):
3565 922a9e65 Thomas Thrainer
    if not WaitForSync(self, self.instance, disks=[disk],
3566 922a9e65 Thomas Thrainer
                       oneshot=not self.op.wait_for_sync):
3567 922a9e65 Thomas Thrainer
      raise errors.OpExecError("Failed to sync disks of %s" %
3568 922a9e65 Thomas Thrainer
                               self.instance.name)
3569 922a9e65 Thomas Thrainer
3570 3c260845 Thomas Thrainer
    # the disk is active at this point, so deactivate it if the instance disks
3571 3c260845 Thomas Thrainer
    # are supposed to be inactive
3572 3c260845 Thomas Thrainer
    if not self.instance.disks_active:
3573 3c260845 Thomas Thrainer
      ShutdownInstanceDisks(self, self.instance, disks=[disk])
3574 3c260845 Thomas Thrainer
3575 c5c72215 Dimitris Aragiorgis
  def _ModifyDisk(self, idx, disk, params, _):
3576 22b7f6f8 Thomas Thrainer
    """Modifies a disk.
3577 22b7f6f8 Thomas Thrainer

3578 22b7f6f8 Thomas Thrainer
    """
3579 22b7f6f8 Thomas Thrainer
    changes = []
3580 4eef428e Dimitris Aragiorgis
    if constants.IDISK_MODE in params:
3581 4eef428e Dimitris Aragiorgis
      disk.mode = params.get(constants.IDISK_MODE)
3582 22b7f6f8 Thomas Thrainer
      changes.append(("disk.mode/%d" % idx, disk.mode))
3583 22b7f6f8 Thomas Thrainer
3584 4eef428e Dimitris Aragiorgis
    if constants.IDISK_NAME in params:
3585 4eef428e Dimitris Aragiorgis
      disk.name = params.get(constants.IDISK_NAME)
3586 4eef428e Dimitris Aragiorgis
      changes.append(("disk.name/%d" % idx, disk.name))
3587 22b7f6f8 Thomas Thrainer
3588 c5c72215 Dimitris Aragiorgis
    # Modify arbitrary params in case instance template is ext
3589 c5c72215 Dimitris Aragiorgis
    for key, value in params.iteritems():
3590 c5c72215 Dimitris Aragiorgis
      if (key not in constants.MODIFIABLE_IDISK_PARAMS and
3591 c5c72215 Dimitris Aragiorgis
          self.instance.disk_template == constants.DT_EXT):
3592 e228ab9c Dimitris Aragiorgis
        # stolen from GetUpdatedParams: default means reset/delete
3593 e228ab9c Dimitris Aragiorgis
        if value.lower() == constants.VALUE_DEFAULT:
3594 e228ab9c Dimitris Aragiorgis
          try:
3595 e228ab9c Dimitris Aragiorgis
            del disk.params[key]
3596 e228ab9c Dimitris Aragiorgis
          except KeyError:
3597 e228ab9c Dimitris Aragiorgis
            pass
3598 e228ab9c Dimitris Aragiorgis
        else:
3599 e228ab9c Dimitris Aragiorgis
          disk.params[key] = value
3600 c5c72215 Dimitris Aragiorgis
        changes.append(("disk.params:%s/%d" % (key, idx), value))
3601 22b7f6f8 Thomas Thrainer
3602 22b7f6f8 Thomas Thrainer
    return changes
3603 22b7f6f8 Thomas Thrainer
3604 22b7f6f8 Thomas Thrainer
  def _RemoveDisk(self, idx, root, _):
3605 22b7f6f8 Thomas Thrainer
    """Removes a disk.
3606 22b7f6f8 Thomas Thrainer

3607 22b7f6f8 Thomas Thrainer
    """
3608 e15a00dc Dimitris Aragiorgis
    hotmsg = ""
3609 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3610 e15a00dc Dimitris Aragiorgis
      hotmsg = self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
3611 e15a00dc Dimitris Aragiorgis
                                   constants.HOTPLUG_TARGET_DISK,
3612 e15a00dc Dimitris Aragiorgis
                                   root, None, idx)
3613 ba924970 Dimitris Aragiorgis
      ShutdownInstanceDisks(self, self.instance, [root])
3614 ba924970 Dimitris Aragiorgis
3615 5eacbcae Thomas Thrainer
    (anno_disk,) = AnnotateDiskParams(self.instance, [root], self.cfg)
3616 1c3231aa Thomas Thrainer
    for node_uuid, disk in anno_disk.ComputeNodeTree(
3617 1c3231aa Thomas Thrainer
                             self.instance.primary_node):
3618 0c3d9c7c Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(node_uuid, (disk, self.instance)) \
3619 0c3d9c7c Thomas Thrainer
              .fail_msg
3620 22b7f6f8 Thomas Thrainer
      if msg:
3621 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove disk/%d on node '%s': %s,"
3622 1c3231aa Thomas Thrainer
                        " continuing anyway", idx,
3623 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
3624 22b7f6f8 Thomas Thrainer
3625 22b7f6f8 Thomas Thrainer
    # if this is a DRBD disk, return its port to the pool
3626 66a37e7a Helga Velroyen
    if root.dev_type in constants.DTS_DRBD:
3627 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(root.logical_id[2])
3628 22b7f6f8 Thomas Thrainer
3629 e15a00dc Dimitris Aragiorgis
    return hotmsg
3630 e15a00dc Dimitris Aragiorgis
3631 22b7f6f8 Thomas Thrainer
  def _CreateNewNic(self, idx, params, private):
3632 22b7f6f8 Thomas Thrainer
    """Creates data structure for a new network interface.
3633 22b7f6f8 Thomas Thrainer

3634 22b7f6f8 Thomas Thrainer
    """
3635 22b7f6f8 Thomas Thrainer
    mac = params[constants.INIC_MAC]
3636 22b7f6f8 Thomas Thrainer
    ip = params.get(constants.INIC_IP, None)
3637 22b7f6f8 Thomas Thrainer
    net = params.get(constants.INIC_NETWORK, None)
3638 22b7f6f8 Thomas Thrainer
    name = params.get(constants.INIC_NAME, None)
3639 22b7f6f8 Thomas Thrainer
    net_uuid = self.cfg.LookupNetwork(net)
3640 22b7f6f8 Thomas Thrainer
    #TODO: not private.filled?? can a nic have no nicparams??
3641 22b7f6f8 Thomas Thrainer
    nicparams = private.filled
3642 22b7f6f8 Thomas Thrainer
    nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, name=name,
3643 22b7f6f8 Thomas Thrainer
                       nicparams=nicparams)
3644 22b7f6f8 Thomas Thrainer
    nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
3645 22b7f6f8 Thomas Thrainer
3646 e15a00dc Dimitris Aragiorgis
    changes = [
3647 22b7f6f8 Thomas Thrainer
      ("nic.%d" % idx,
3648 22b7f6f8 Thomas Thrainer
       "add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" %
3649 22b7f6f8 Thomas Thrainer
       (mac, ip, private.filled[constants.NIC_MODE],
3650 ba924970 Dimitris Aragiorgis
       private.filled[constants.NIC_LINK], net)),
3651 ba924970 Dimitris Aragiorgis
      ]
3652 ba924970 Dimitris Aragiorgis
3653 e15a00dc Dimitris Aragiorgis
    if self.op.hotplug:
3654 e15a00dc Dimitris Aragiorgis
      msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
3655 e15a00dc Dimitris Aragiorgis
                                constants.HOTPLUG_TARGET_NIC,
3656 e15a00dc Dimitris Aragiorgis
                                nobj, None, idx)
3657 e15a00dc Dimitris Aragiorgis
      changes.append(("nic.%d" % idx, msg))
3658 e15a00dc Dimitris Aragiorgis
3659 e15a00dc Dimitris Aragiorgis
    return (nobj, changes)
3660 22b7f6f8 Thomas Thrainer
3661 22b7f6f8 Thomas Thrainer
  def _ApplyNicMods(self, idx, nic, params, private):
3662 22b7f6f8 Thomas Thrainer
    """Modifies a network interface.
3663 22b7f6f8 Thomas Thrainer

3664 22b7f6f8 Thomas Thrainer
    """
3665 22b7f6f8 Thomas Thrainer
    changes = []
3666 22b7f6f8 Thomas Thrainer
3667 22b7f6f8 Thomas Thrainer
    for key in [constants.INIC_MAC, constants.INIC_IP, constants.INIC_NAME]:
3668 22b7f6f8 Thomas Thrainer
      if key in params:
3669 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), params[key]))
3670 22b7f6f8 Thomas Thrainer
        setattr(nic, key, params[key])
3671 22b7f6f8 Thomas Thrainer
3672 22b7f6f8 Thomas Thrainer
    new_net = params.get(constants.INIC_NETWORK, nic.network)
3673 22b7f6f8 Thomas Thrainer
    new_net_uuid = self.cfg.LookupNetwork(new_net)
3674 22b7f6f8 Thomas Thrainer
    if new_net_uuid != nic.network:
3675 22b7f6f8 Thomas Thrainer
      changes.append(("nic.network/%d" % idx, new_net))
3676 22b7f6f8 Thomas Thrainer
      nic.network = new_net_uuid
3677 22b7f6f8 Thomas Thrainer
3678 22b7f6f8 Thomas Thrainer
    if private.filled:
3679 22b7f6f8 Thomas Thrainer
      nic.nicparams = private.filled
3680 22b7f6f8 Thomas Thrainer
3681 22b7f6f8 Thomas Thrainer
      for (key, val) in nic.nicparams.items():
3682 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), val))
3683 22b7f6f8 Thomas Thrainer
3684 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3685 e15a00dc Dimitris Aragiorgis
      msg = self._HotplugDevice(constants.HOTPLUG_ACTION_MODIFY,
3686 e15a00dc Dimitris Aragiorgis
                                constants.HOTPLUG_TARGET_NIC,
3687 e15a00dc Dimitris Aragiorgis
                                nic, None, idx)
3688 e15a00dc Dimitris Aragiorgis
      changes.append(("nic/%d" % idx, msg))
3689 ba924970 Dimitris Aragiorgis
3690 22b7f6f8 Thomas Thrainer
    return changes
3691 22b7f6f8 Thomas Thrainer
3692 ba924970 Dimitris Aragiorgis
  def _RemoveNic(self, idx, nic, _):
3693 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3694 e15a00dc Dimitris Aragiorgis
      return self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
3695 e15a00dc Dimitris Aragiorgis
                                 constants.HOTPLUG_TARGET_NIC,
3696 e15a00dc Dimitris Aragiorgis
                                 nic, None, idx)
3697 ba924970 Dimitris Aragiorgis
3698 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3699 22b7f6f8 Thomas Thrainer
    """Modifies an instance.
3700 22b7f6f8 Thomas Thrainer

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

3703 22b7f6f8 Thomas Thrainer
    """
3704 22b7f6f8 Thomas Thrainer
    # Process here the warnings from CheckPrereq, as we don't have a
3705 22b7f6f8 Thomas Thrainer
    # feedback_fn there.
3706 22b7f6f8 Thomas Thrainer
    # TODO: Replace with self.LogWarning
3707 22b7f6f8 Thomas Thrainer
    for warn in self.warn:
3708 22b7f6f8 Thomas Thrainer
      feedback_fn("WARNING: %s" % warn)
3709 22b7f6f8 Thomas Thrainer
3710 22b7f6f8 Thomas Thrainer
    assert ((self.op.disk_template is None) ^
3711 22b7f6f8 Thomas Thrainer
            bool(self.owned_locks(locking.LEVEL_NODE_RES))), \
3712 22b7f6f8 Thomas Thrainer
      "Not owning any node resource locks"
3713 22b7f6f8 Thomas Thrainer
3714 22b7f6f8 Thomas Thrainer
    result = []
3715 22b7f6f8 Thomas Thrainer
3716 22b7f6f8 Thomas Thrainer
    # New primary node
3717 1c3231aa Thomas Thrainer
    if self.op.pnode_uuid:
3718 d0d7d7cf Thomas Thrainer
      self.instance.primary_node = self.op.pnode_uuid
3719 22b7f6f8 Thomas Thrainer
3720 22b7f6f8 Thomas Thrainer
    # runtime memory
3721 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
3722 d0d7d7cf Thomas Thrainer
      rpcres = self.rpc.call_instance_balloon_memory(self.instance.primary_node,
3723 d0d7d7cf Thomas Thrainer
                                                     self.instance,
3724 22b7f6f8 Thomas Thrainer
                                                     self.op.runtime_mem)
3725 22b7f6f8 Thomas Thrainer
      rpcres.Raise("Cannot modify instance runtime memory")
3726 22b7f6f8 Thomas Thrainer
      result.append(("runtime_memory", self.op.runtime_mem))
3727 22b7f6f8 Thomas Thrainer
3728 22b7f6f8 Thomas Thrainer
    # Apply disk changes
3729 d0d7d7cf Thomas Thrainer
    _ApplyContainerMods("disk", self.instance.disks, result, self.diskmod,
3730 5eacbcae Thomas Thrainer
                        self._CreateNewDisk, self._ModifyDisk,
3731 922a9e65 Thomas Thrainer
                        self._RemoveDisk, post_add_fn=self._PostAddDisk)
3732 d0d7d7cf Thomas Thrainer
    _UpdateIvNames(0, self.instance.disks)
3733 22b7f6f8 Thomas Thrainer
3734 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
3735 22b7f6f8 Thomas Thrainer
      if __debug__:
3736 d0d7d7cf Thomas Thrainer
        check_nodes = set(self.instance.all_nodes)
3737 1c3231aa Thomas Thrainer
        if self.op.remote_node_uuid:
3738 1c3231aa Thomas Thrainer
          check_nodes.add(self.op.remote_node_uuid)
3739 22b7f6f8 Thomas Thrainer
        for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
3740 22b7f6f8 Thomas Thrainer
          owned = self.owned_locks(level)
3741 22b7f6f8 Thomas Thrainer
          assert not (check_nodes - owned), \
3742 22b7f6f8 Thomas Thrainer
            ("Not owning the correct locks, owning %r, expected at least %r" %
3743 22b7f6f8 Thomas Thrainer
             (owned, check_nodes))
3744 22b7f6f8 Thomas Thrainer
3745 d0d7d7cf Thomas Thrainer
      r_shut = ShutdownInstanceDisks(self, self.instance)
3746 22b7f6f8 Thomas Thrainer
      if not r_shut:
3747 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Cannot shutdown instance disks, unable to"
3748 22b7f6f8 Thomas Thrainer
                                 " proceed with disk template conversion")
3749 d0d7d7cf Thomas Thrainer
      mode = (self.instance.disk_template, self.op.disk_template)
3750 22b7f6f8 Thomas Thrainer
      try:
3751 22b7f6f8 Thomas Thrainer
        self._DISK_CONVERSIONS[mode](self, feedback_fn)
3752 22b7f6f8 Thomas Thrainer
      except:
3753 da4a52a3 Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(self.instance.uuid)
3754 22b7f6f8 Thomas Thrainer
        raise
3755 22b7f6f8 Thomas Thrainer
      result.append(("disk_template", self.op.disk_template))
3756 22b7f6f8 Thomas Thrainer
3757 d0d7d7cf Thomas Thrainer
      assert self.instance.disk_template == self.op.disk_template, \
3758 22b7f6f8 Thomas Thrainer
        ("Expected disk template '%s', found '%s'" %
3759 d0d7d7cf Thomas Thrainer
         (self.op.disk_template, self.instance.disk_template))
3760 22b7f6f8 Thomas Thrainer
3761 22b7f6f8 Thomas Thrainer
    # Release node and resource locks if there are any (they might already have
3762 22b7f6f8 Thomas Thrainer
    # been released during disk conversion)
3763 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3764 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE_RES)
3765 22b7f6f8 Thomas Thrainer
3766 22b7f6f8 Thomas Thrainer
    # Apply NIC changes
3767 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
3768 d0d7d7cf Thomas Thrainer
      self.instance.nics = self._new_nics
3769 22b7f6f8 Thomas Thrainer
      result.extend(self._nic_chgdesc)
3770 22b7f6f8 Thomas Thrainer
3771 22b7f6f8 Thomas Thrainer
    # hvparams changes
3772 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
3773 d0d7d7cf Thomas Thrainer
      self.instance.hvparams = self.hv_inst
3774 22b7f6f8 Thomas Thrainer
      for key, val in self.op.hvparams.iteritems():
3775 22b7f6f8 Thomas Thrainer
        result.append(("hv/%s" % key, val))
3776 22b7f6f8 Thomas Thrainer
3777 22b7f6f8 Thomas Thrainer
    # beparams changes
3778 22b7f6f8 Thomas Thrainer
    if self.op.beparams:
3779 d0d7d7cf Thomas Thrainer
      self.instance.beparams = self.be_inst
3780 22b7f6f8 Thomas Thrainer
      for key, val in self.op.beparams.iteritems():
3781 22b7f6f8 Thomas Thrainer
        result.append(("be/%s" % key, val))
3782 22b7f6f8 Thomas Thrainer
3783 22b7f6f8 Thomas Thrainer
    # OS change
3784 22b7f6f8 Thomas Thrainer
    if self.op.os_name:
3785 d0d7d7cf Thomas Thrainer
      self.instance.os = self.op.os_name
3786 22b7f6f8 Thomas Thrainer
3787 22b7f6f8 Thomas Thrainer
    # osparams changes
3788 22b7f6f8 Thomas Thrainer
    if self.op.osparams:
3789 d0d7d7cf Thomas Thrainer
      self.instance.osparams = self.os_inst
3790 22b7f6f8 Thomas Thrainer
      for key, val in self.op.osparams.iteritems():
3791 22b7f6f8 Thomas Thrainer
        result.append(("os/%s" % key, val))
3792 22b7f6f8 Thomas Thrainer
3793 1a182390 Santi Raffa
    if self.op.osparams_private:
3794 1a182390 Santi Raffa
      self.instance.osparams_private = self.os_inst_private
3795 1a182390 Santi Raffa
      for key, val in self.op.osparams_private.iteritems():
3796 1a182390 Santi Raffa
        # Show the Private(...) blurb.
3797 1a182390 Santi Raffa
        result.append(("os_private/%s" % key, repr(val)))
3798 1a182390 Santi Raffa
3799 66223061 Petr Pudlak
    self.cfg.Update(self.instance, feedback_fn, self.proc.GetECId())
3800 66223061 Petr Pudlak
3801 22b7f6f8 Thomas Thrainer
    if self.op.offline is None:
3802 22b7f6f8 Thomas Thrainer
      # Ignore
3803 22b7f6f8 Thomas Thrainer
      pass
3804 22b7f6f8 Thomas Thrainer
    elif self.op.offline:
3805 22b7f6f8 Thomas Thrainer
      # Mark instance as offline
3806 da4a52a3 Thomas Thrainer
      self.cfg.MarkInstanceOffline(self.instance.uuid)
3807 22b7f6f8 Thomas Thrainer
      result.append(("admin_state", constants.ADMINST_OFFLINE))
3808 22b7f6f8 Thomas Thrainer
    else:
3809 22b7f6f8 Thomas Thrainer
      # Mark instance as online, but stopped
3810 da4a52a3 Thomas Thrainer
      self.cfg.MarkInstanceDown(self.instance.uuid)
3811 22b7f6f8 Thomas Thrainer
      result.append(("admin_state", constants.ADMINST_DOWN))
3812 22b7f6f8 Thomas Thrainer
3813 8c58dc45 Jose A. Lopes
    UpdateMetadata(feedback_fn, self.rpc, self.instance)
3814 8c58dc45 Jose A. Lopes
3815 22b7f6f8 Thomas Thrainer
    assert not (self.owned_locks(locking.LEVEL_NODE_RES) or
3816 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
3817 22b7f6f8 Thomas Thrainer
      "All node locks should have been released by now"
3818 22b7f6f8 Thomas Thrainer
3819 22b7f6f8 Thomas Thrainer
    return result
3820 22b7f6f8 Thomas Thrainer
3821 22b7f6f8 Thomas Thrainer
  _DISK_CONVERSIONS = {
3822 22b7f6f8 Thomas Thrainer
    (constants.DT_PLAIN, constants.DT_DRBD8): _ConvertPlainToDrbd,
3823 22b7f6f8 Thomas Thrainer
    (constants.DT_DRBD8, constants.DT_PLAIN): _ConvertDrbdToPlain,
3824 22b7f6f8 Thomas Thrainer
    }
3825 22b7f6f8 Thomas Thrainer
3826 22b7f6f8 Thomas Thrainer
3827 22b7f6f8 Thomas Thrainer
class LUInstanceChangeGroup(LogicalUnit):
3828 22b7f6f8 Thomas Thrainer
  HPATH = "instance-change-group"
3829 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
3830 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
3831 22b7f6f8 Thomas Thrainer
3832 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
3833 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
3834 22b7f6f8 Thomas Thrainer
3835 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
3836 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODEGROUP: [],
3837 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE: [],
3838 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
3839 22b7f6f8 Thomas Thrainer
      }
3840 22b7f6f8 Thomas Thrainer
3841 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
3842 22b7f6f8 Thomas Thrainer
3843 22b7f6f8 Thomas Thrainer
    if self.op.target_groups:
3844 22b7f6f8 Thomas Thrainer
      self.req_target_uuids = map(self.cfg.LookupNodeGroup,
3845 22b7f6f8 Thomas Thrainer
                                  self.op.target_groups)
3846 22b7f6f8 Thomas Thrainer
    else:
3847 22b7f6f8 Thomas Thrainer
      self.req_target_uuids = None
3848 22b7f6f8 Thomas Thrainer
3849 5eacbcae Thomas Thrainer
    self.op.iallocator = GetDefaultIAllocator(self.cfg, self.op.iallocator)
3850 22b7f6f8 Thomas Thrainer
3851 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
3852 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
3853 22b7f6f8 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
3854 22b7f6f8 Thomas Thrainer
3855 22b7f6f8 Thomas Thrainer
      if self.req_target_uuids:
3856 22b7f6f8 Thomas Thrainer
        lock_groups = set(self.req_target_uuids)
3857 22b7f6f8 Thomas Thrainer
3858 22b7f6f8 Thomas Thrainer
        # Lock all groups used by instance optimistically; this requires going
3859 22b7f6f8 Thomas Thrainer
        # via the node before it's locked, requiring verification later on
3860 da4a52a3 Thomas Thrainer
        instance_groups = self.cfg.GetInstanceNodeGroups(self.op.instance_uuid)
3861 22b7f6f8 Thomas Thrainer
        lock_groups.update(instance_groups)
3862 22b7f6f8 Thomas Thrainer
      else:
3863 22b7f6f8 Thomas Thrainer
        # No target groups, need to lock all of them
3864 22b7f6f8 Thomas Thrainer
        lock_groups = locking.ALL_SET
3865 22b7f6f8 Thomas Thrainer
3866 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = lock_groups
3867 22b7f6f8 Thomas Thrainer
3868 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE:
3869 22b7f6f8 Thomas Thrainer
      if self.req_target_uuids:
3870 22b7f6f8 Thomas Thrainer
        # Lock all nodes used by instances
3871 22b7f6f8 Thomas Thrainer
        self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
3872 22b7f6f8 Thomas Thrainer
        self._LockInstancesNodes()
3873 22b7f6f8 Thomas Thrainer
3874 22b7f6f8 Thomas Thrainer
        # Lock all nodes in all potential target groups
3875 22b7f6f8 Thomas Thrainer
        lock_groups = (frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) -
3876 da4a52a3 Thomas Thrainer
                       self.cfg.GetInstanceNodeGroups(self.op.instance_uuid))
3877 1c3231aa Thomas Thrainer
        member_nodes = [node_uuid
3878 22b7f6f8 Thomas Thrainer
                        for group in lock_groups
3879 1c3231aa Thomas Thrainer
                        for node_uuid in self.cfg.GetNodeGroup(group).members]
3880 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE].extend(member_nodes)
3881 22b7f6f8 Thomas Thrainer
      else:
3882 22b7f6f8 Thomas Thrainer
        # Lock all nodes as all groups are potential targets
3883 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
3884 22b7f6f8 Thomas Thrainer
3885 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
3886 da4a52a3 Thomas Thrainer
    owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
3887 22b7f6f8 Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
3888 22b7f6f8 Thomas Thrainer
    owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE))
3889 22b7f6f8 Thomas Thrainer
3890 22b7f6f8 Thomas Thrainer
    assert (self.req_target_uuids is None or
3891 22b7f6f8 Thomas Thrainer
            owned_groups.issuperset(self.req_target_uuids))
3892 da4a52a3 Thomas Thrainer
    assert owned_instance_names == set([self.op.instance_name])
3893 22b7f6f8 Thomas Thrainer
3894 22b7f6f8 Thomas Thrainer
    # Get instance information
3895 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
3896 22b7f6f8 Thomas Thrainer
3897 22b7f6f8 Thomas Thrainer
    # Check if node groups for locked instance are still correct
3898 22b7f6f8 Thomas Thrainer
    assert owned_nodes.issuperset(self.instance.all_nodes), \
3899 22b7f6f8 Thomas Thrainer
      ("Instance %s's nodes changed while we kept the lock" %
3900 22b7f6f8 Thomas Thrainer
       self.op.instance_name)
3901 22b7f6f8 Thomas Thrainer
3902 da4a52a3 Thomas Thrainer
    inst_groups = CheckInstanceNodeGroups(self.cfg, self.op.instance_uuid,
3903 5eacbcae Thomas Thrainer
                                          owned_groups)
3904 22b7f6f8 Thomas Thrainer
3905 22b7f6f8 Thomas Thrainer
    if self.req_target_uuids:
3906 22b7f6f8 Thomas Thrainer
      # User requested specific target groups
3907 22b7f6f8 Thomas Thrainer
      self.target_uuids = frozenset(self.req_target_uuids)
3908 22b7f6f8 Thomas Thrainer
    else:
3909 22b7f6f8 Thomas Thrainer
      # All groups except those used by the instance are potential targets
3910 22b7f6f8 Thomas Thrainer
      self.target_uuids = owned_groups - inst_groups
3911 22b7f6f8 Thomas Thrainer
3912 22b7f6f8 Thomas Thrainer
    conflicting_groups = self.target_uuids & inst_groups
3913 22b7f6f8 Thomas Thrainer
    if conflicting_groups:
3914 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't use group(s) '%s' as targets, they are"
3915 22b7f6f8 Thomas Thrainer
                                 " used by the instance '%s'" %
3916 22b7f6f8 Thomas Thrainer
                                 (utils.CommaJoin(conflicting_groups),
3917 22b7f6f8 Thomas Thrainer
                                  self.op.instance_name),
3918 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3919 22b7f6f8 Thomas Thrainer
3920 22b7f6f8 Thomas Thrainer
    if not self.target_uuids:
3921 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are no possible target groups",
3922 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3923 22b7f6f8 Thomas Thrainer
3924 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
3925 22b7f6f8 Thomas Thrainer
    """Build hooks env.
3926 22b7f6f8 Thomas Thrainer

3927 22b7f6f8 Thomas Thrainer
    """
3928 22b7f6f8 Thomas Thrainer
    assert self.target_uuids
3929 22b7f6f8 Thomas Thrainer
3930 22b7f6f8 Thomas Thrainer
    env = {
3931 22b7f6f8 Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
3932 22b7f6f8 Thomas Thrainer
      }
3933 22b7f6f8 Thomas Thrainer
3934 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
3935 22b7f6f8 Thomas Thrainer
3936 22b7f6f8 Thomas Thrainer
    return env
3937 22b7f6f8 Thomas Thrainer
3938 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
3939 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
3940 22b7f6f8 Thomas Thrainer

3941 22b7f6f8 Thomas Thrainer
    """
3942 22b7f6f8 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
3943 22b7f6f8 Thomas Thrainer
    return ([mn], [mn])
3944 22b7f6f8 Thomas Thrainer
3945 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3946 22b7f6f8 Thomas Thrainer
    instances = list(self.owned_locks(locking.LEVEL_INSTANCE))
3947 22b7f6f8 Thomas Thrainer
3948 22b7f6f8 Thomas Thrainer
    assert instances == [self.op.instance_name], "Instance not locked"
3949 22b7f6f8 Thomas Thrainer
3950 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=instances,
3951 22b7f6f8 Thomas Thrainer
                                      target_groups=list(self.target_uuids))
3952 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
3953 22b7f6f8 Thomas Thrainer
3954 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
3955 22b7f6f8 Thomas Thrainer
3956 22b7f6f8 Thomas Thrainer
    if not ial.success:
3957 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute solution for changing group of"
3958 22b7f6f8 Thomas Thrainer
                                 " instance '%s' using iallocator '%s': %s" %
3959 22b7f6f8 Thomas Thrainer
                                 (self.op.instance_name, self.op.iallocator,
3960 22b7f6f8 Thomas Thrainer
                                  ial.info), errors.ECODE_NORES)
3961 22b7f6f8 Thomas Thrainer
3962 5eacbcae Thomas Thrainer
    jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
3963 22b7f6f8 Thomas Thrainer
3964 22b7f6f8 Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for changing group of"
3965 22b7f6f8 Thomas Thrainer
                 " instance '%s'", len(jobs), self.op.instance_name)
3966 22b7f6f8 Thomas Thrainer
3967 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs)