Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance.py @ 31d3b918

History | View | Annotate | Download (152.1 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 5eacbcae Thomas Thrainer
  IsExclusiveStorageEnabledNode, CheckHVParams, CheckOSParams, \
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 a365b47f Bernardo Dal Seno
  CheckNodesFreeDiskPerVG, WipeDisks, WipeOrCleanupDisks, WaitForSync, \
56 1c3231aa Thomas Thrainer
  IsExclusiveStorageEnabledNodeUuid, CreateSingleBlockDev, ComputeDisks, \
57 5eacbcae Thomas Thrainer
  CheckRADOSFreeSpace, ComputeDiskSizePerVG, GenerateDiskTemplate, \
58 3f3ea14c Bernardo Dal Seno
  StartInstanceDisks, ShutdownInstanceDisks, AssembleInstanceDisks, \
59 3f3ea14c Bernardo Dal Seno
  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 5eacbcae Thomas Thrainer
  CheckInstanceBridgesExist, CheckNicsBridgesExist, CheckNodeHasOS
66 22b7f6f8 Thomas Thrainer
67 22b7f6f8 Thomas Thrainer
import ganeti.masterd.instance
68 22b7f6f8 Thomas Thrainer
69 22b7f6f8 Thomas Thrainer
70 5eacbcae Thomas Thrainer
#: Type description for changes as returned by L{_ApplyContainerMods}'s
71 22b7f6f8 Thomas Thrainer
#: callbacks
72 22b7f6f8 Thomas Thrainer
_TApplyContModsCbChanges = \
73 22b7f6f8 Thomas Thrainer
  ht.TMaybeListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([
74 22b7f6f8 Thomas Thrainer
    ht.TNonEmptyString,
75 22b7f6f8 Thomas Thrainer
    ht.TAny,
76 22b7f6f8 Thomas Thrainer
    ])))
77 22b7f6f8 Thomas Thrainer
78 22b7f6f8 Thomas Thrainer
79 22b7f6f8 Thomas Thrainer
def _CheckHostnameSane(lu, name):
80 22b7f6f8 Thomas Thrainer
  """Ensures that a given hostname resolves to a 'sane' name.
81 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

305 22b7f6f8 Thomas Thrainer
  """
306 22b7f6f8 Thomas Thrainer
  variant = objects.OS.GetVariant(name)
307 22b7f6f8 Thomas Thrainer
  if not os_obj.supported_variants:
308 22b7f6f8 Thomas Thrainer
    if variant:
309 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("OS '%s' doesn't support variants ('%s'"
310 22b7f6f8 Thomas Thrainer
                                 " passed)" % (os_obj.name, variant),
311 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
312 22b7f6f8 Thomas Thrainer
    return
313 22b7f6f8 Thomas Thrainer
  if not variant:
314 22b7f6f8 Thomas Thrainer
    raise errors.OpPrereqError("OS name must include a variant",
315 22b7f6f8 Thomas Thrainer
                               errors.ECODE_INVAL)
316 22b7f6f8 Thomas Thrainer
317 22b7f6f8 Thomas Thrainer
  if variant not in os_obj.supported_variants:
318 22b7f6f8 Thomas Thrainer
    raise errors.OpPrereqError("Unsupported OS variant", errors.ECODE_INVAL)
319 22b7f6f8 Thomas Thrainer
320 22b7f6f8 Thomas Thrainer
321 22b7f6f8 Thomas Thrainer
class LUInstanceCreate(LogicalUnit):
322 22b7f6f8 Thomas Thrainer
  """Create an instance.
323 22b7f6f8 Thomas Thrainer

324 22b7f6f8 Thomas Thrainer
  """
325 22b7f6f8 Thomas Thrainer
  HPATH = "instance-add"
326 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
327 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
328 22b7f6f8 Thomas Thrainer
329 dab6ea3d Helga Velroyen
  def _CheckDiskTemplateValid(self):
330 dab6ea3d Helga Velroyen
    """Checks validity of disk template.
331 22b7f6f8 Thomas Thrainer

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

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

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

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

559 22b7f6f8 Thomas Thrainer
    Figure out the right locks for instance creation.
560 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

735 22b7f6f8 Thomas Thrainer
    @return: the export information
736 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

1316 22b7f6f8 Thomas Thrainer
    """
1317 22b7f6f8 Thomas Thrainer
    assert not (self.owned_locks(locking.LEVEL_NODE_RES) -
1318 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
1319 22b7f6f8 Thomas Thrainer
      "Node locks differ from node resource locks"
1320 22b7f6f8 Thomas Thrainer
    assert not self.glm.is_owned(locking.LEVEL_NODE_ALLOC)
1321 22b7f6f8 Thomas Thrainer
1322 22b7f6f8 Thomas Thrainer
    ht_kind = self.op.hypervisor
1323 22b7f6f8 Thomas Thrainer
    if ht_kind in constants.HTS_REQ_PORT:
1324 22b7f6f8 Thomas Thrainer
      network_port = self.cfg.AllocatePort()
1325 22b7f6f8 Thomas Thrainer
    else:
1326 22b7f6f8 Thomas Thrainer
      network_port = None
1327 22b7f6f8 Thomas Thrainer
1328 da4a52a3 Thomas Thrainer
    instance_uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
1329 da4a52a3 Thomas Thrainer
1330 22b7f6f8 Thomas Thrainer
    # This is ugly but we got a chicken-egg problem here
1331 22b7f6f8 Thomas Thrainer
    # We can only take the group disk parameters, as the instance
1332 22b7f6f8 Thomas Thrainer
    # has no disks yet (we are generating them right here).
1333 1c3231aa Thomas Thrainer
    nodegroup = self.cfg.GetNodeGroup(self.pnode.group)
1334 5eacbcae Thomas Thrainer
    disks = GenerateDiskTemplate(self,
1335 5eacbcae Thomas Thrainer
                                 self.op.disk_template,
1336 da4a52a3 Thomas Thrainer
                                 instance_uuid, self.pnode.uuid,
1337 5eacbcae Thomas Thrainer
                                 self.secondaries,
1338 5eacbcae Thomas Thrainer
                                 self.disks,
1339 5eacbcae Thomas Thrainer
                                 self.instance_file_storage_dir,
1340 5eacbcae Thomas Thrainer
                                 self.op.file_driver,
1341 5eacbcae Thomas Thrainer
                                 0,
1342 5eacbcae Thomas Thrainer
                                 feedback_fn,
1343 5eacbcae Thomas Thrainer
                                 self.cfg.GetGroupDiskParams(nodegroup))
1344 22b7f6f8 Thomas Thrainer
1345 da4a52a3 Thomas Thrainer
    iobj = objects.Instance(name=self.op.instance_name,
1346 da4a52a3 Thomas Thrainer
                            uuid=instance_uuid,
1347 da4a52a3 Thomas Thrainer
                            os=self.op.os_type,
1348 1c3231aa Thomas Thrainer
                            primary_node=self.pnode.uuid,
1349 22b7f6f8 Thomas Thrainer
                            nics=self.nics, disks=disks,
1350 22b7f6f8 Thomas Thrainer
                            disk_template=self.op.disk_template,
1351 1d4a4b26 Thomas Thrainer
                            disks_active=False,
1352 22b7f6f8 Thomas Thrainer
                            admin_state=constants.ADMINST_DOWN,
1353 22b7f6f8 Thomas Thrainer
                            network_port=network_port,
1354 22b7f6f8 Thomas Thrainer
                            beparams=self.op.beparams,
1355 22b7f6f8 Thomas Thrainer
                            hvparams=self.op.hvparams,
1356 22b7f6f8 Thomas Thrainer
                            hypervisor=self.op.hypervisor,
1357 22b7f6f8 Thomas Thrainer
                            osparams=self.op.osparams,
1358 6bce7ba2 Santi Raffa
                            osparams_private=self.op.osparams_private,
1359 22b7f6f8 Thomas Thrainer
                            )
1360 22b7f6f8 Thomas Thrainer
1361 22b7f6f8 Thomas Thrainer
    if self.op.tags:
1362 22b7f6f8 Thomas Thrainer
      for tag in self.op.tags:
1363 22b7f6f8 Thomas Thrainer
        iobj.AddTag(tag)
1364 22b7f6f8 Thomas Thrainer
1365 22b7f6f8 Thomas Thrainer
    if self.adopt_disks:
1366 22b7f6f8 Thomas Thrainer
      if self.op.disk_template == constants.DT_PLAIN:
1367 22b7f6f8 Thomas Thrainer
        # rename LVs to the newly-generated names; we need to construct
1368 22b7f6f8 Thomas Thrainer
        # 'fake' LV disks with the old data, plus the new unique_id
1369 22b7f6f8 Thomas Thrainer
        tmp_disks = [objects.Disk.FromDict(v.ToDict()) for v in disks]
1370 22b7f6f8 Thomas Thrainer
        rename_to = []
1371 22b7f6f8 Thomas Thrainer
        for t_dsk, a_dsk in zip(tmp_disks, self.disks):
1372 22b7f6f8 Thomas Thrainer
          rename_to.append(t_dsk.logical_id)
1373 22b7f6f8 Thomas Thrainer
          t_dsk.logical_id = (t_dsk.logical_id[0], a_dsk[constants.IDISK_ADOPT])
1374 1c3231aa Thomas Thrainer
        result = self.rpc.call_blockdev_rename(self.pnode.uuid,
1375 22b7f6f8 Thomas Thrainer
                                               zip(tmp_disks, rename_to))
1376 22b7f6f8 Thomas Thrainer
        result.Raise("Failed to rename adoped LVs")
1377 22b7f6f8 Thomas Thrainer
    else:
1378 22b7f6f8 Thomas Thrainer
      feedback_fn("* creating instance disks...")
1379 22b7f6f8 Thomas Thrainer
      try:
1380 5eacbcae Thomas Thrainer
        CreateDisks(self, iobj)
1381 22b7f6f8 Thomas Thrainer
      except errors.OpExecError:
1382 22b7f6f8 Thomas Thrainer
        self.LogWarning("Device creation failed")
1383 d0d7d7cf Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(self.op.instance_name)
1384 22b7f6f8 Thomas Thrainer
        raise
1385 22b7f6f8 Thomas Thrainer
1386 d0d7d7cf Thomas Thrainer
    feedback_fn("adding instance %s to cluster config" % self.op.instance_name)
1387 22b7f6f8 Thomas Thrainer
1388 22b7f6f8 Thomas Thrainer
    self.cfg.AddInstance(iobj, self.proc.GetECId())
1389 22b7f6f8 Thomas Thrainer
1390 22b7f6f8 Thomas Thrainer
    # Declare that we don't want to remove the instance lock anymore, as we've
1391 22b7f6f8 Thomas Thrainer
    # added the instance to the config
1392 22b7f6f8 Thomas Thrainer
    del self.remove_locks[locking.LEVEL_INSTANCE]
1393 22b7f6f8 Thomas Thrainer
1394 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
1395 22b7f6f8 Thomas Thrainer
      # Release unused nodes
1396 1c3231aa Thomas Thrainer
      ReleaseLocks(self, locking.LEVEL_NODE, keep=[self.op.src_node_uuid])
1397 22b7f6f8 Thomas Thrainer
    else:
1398 22b7f6f8 Thomas Thrainer
      # Release all nodes
1399 5eacbcae Thomas Thrainer
      ReleaseLocks(self, locking.LEVEL_NODE)
1400 22b7f6f8 Thomas Thrainer
1401 22b7f6f8 Thomas Thrainer
    disk_abort = False
1402 22b7f6f8 Thomas Thrainer
    if not self.adopt_disks and self.cfg.GetClusterInfo().prealloc_wipe_disks:
1403 22b7f6f8 Thomas Thrainer
      feedback_fn("* wiping instance disks...")
1404 22b7f6f8 Thomas Thrainer
      try:
1405 5eacbcae Thomas Thrainer
        WipeDisks(self, iobj)
1406 22b7f6f8 Thomas Thrainer
      except errors.OpExecError, err:
1407 22b7f6f8 Thomas Thrainer
        logging.exception("Wiping disks failed")
1408 22b7f6f8 Thomas Thrainer
        self.LogWarning("Wiping instance disks failed (%s)", err)
1409 22b7f6f8 Thomas Thrainer
        disk_abort = True
1410 22b7f6f8 Thomas Thrainer
1411 22b7f6f8 Thomas Thrainer
    if disk_abort:
1412 22b7f6f8 Thomas Thrainer
      # Something is already wrong with the disks, don't do anything else
1413 22b7f6f8 Thomas Thrainer
      pass
1414 22b7f6f8 Thomas Thrainer
    elif self.op.wait_for_sync:
1415 5eacbcae Thomas Thrainer
      disk_abort = not WaitForSync(self, iobj)
1416 22b7f6f8 Thomas Thrainer
    elif iobj.disk_template in constants.DTS_INT_MIRROR:
1417 22b7f6f8 Thomas Thrainer
      # make sure the disks are not degraded (still sync-ing is ok)
1418 22b7f6f8 Thomas Thrainer
      feedback_fn("* checking mirrors status")
1419 5eacbcae Thomas Thrainer
      disk_abort = not WaitForSync(self, iobj, oneshot=True)
1420 22b7f6f8 Thomas Thrainer
    else:
1421 22b7f6f8 Thomas Thrainer
      disk_abort = False
1422 22b7f6f8 Thomas Thrainer
1423 22b7f6f8 Thomas Thrainer
    if disk_abort:
1424 5eacbcae Thomas Thrainer
      RemoveDisks(self, iobj)
1425 da4a52a3 Thomas Thrainer
      self.cfg.RemoveInstance(iobj.uuid)
1426 22b7f6f8 Thomas Thrainer
      # Make sure the instance lock gets removed
1427 22b7f6f8 Thomas Thrainer
      self.remove_locks[locking.LEVEL_INSTANCE] = iobj.name
1428 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("There are some degraded disks for"
1429 22b7f6f8 Thomas Thrainer
                               " this instance")
1430 22b7f6f8 Thomas Thrainer
1431 1d4a4b26 Thomas Thrainer
    # instance disks are now active
1432 1d4a4b26 Thomas Thrainer
    iobj.disks_active = True
1433 1d4a4b26 Thomas Thrainer
1434 22b7f6f8 Thomas Thrainer
    # Release all node resource locks
1435 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE_RES)
1436 22b7f6f8 Thomas Thrainer
1437 22b7f6f8 Thomas Thrainer
    if iobj.disk_template != constants.DT_DISKLESS and not self.adopt_disks:
1438 22b7f6f8 Thomas Thrainer
      if self.op.mode == constants.INSTANCE_CREATE:
1439 22b7f6f8 Thomas Thrainer
        if not self.op.no_install:
1440 22b7f6f8 Thomas Thrainer
          pause_sync = (iobj.disk_template in constants.DTS_INT_MIRROR and
1441 22b7f6f8 Thomas Thrainer
                        not self.op.wait_for_sync)
1442 22b7f6f8 Thomas Thrainer
          if pause_sync:
1443 22b7f6f8 Thomas Thrainer
            feedback_fn("* pausing disk sync to install instance OS")
1444 1c3231aa Thomas Thrainer
            result = self.rpc.call_blockdev_pause_resume_sync(self.pnode.uuid,
1445 22b7f6f8 Thomas Thrainer
                                                              (iobj.disks,
1446 22b7f6f8 Thomas Thrainer
                                                               iobj), True)
1447 22b7f6f8 Thomas Thrainer
            for idx, success in enumerate(result.payload):
1448 22b7f6f8 Thomas Thrainer
              if not success:
1449 22b7f6f8 Thomas Thrainer
                logging.warn("pause-sync of instance %s for disk %d failed",
1450 d0d7d7cf Thomas Thrainer
                             self.op.instance_name, idx)
1451 22b7f6f8 Thomas Thrainer
1452 22b7f6f8 Thomas Thrainer
          feedback_fn("* running the instance OS create scripts...")
1453 22b7f6f8 Thomas Thrainer
          # FIXME: pass debug option from opcode to backend
1454 22b7f6f8 Thomas Thrainer
          os_add_result = \
1455 6bce7ba2 Santi Raffa
            self.rpc.call_instance_os_add(self.pnode.uuid,
1456 6bce7ba2 Santi Raffa
                                          (iobj, self.op.osparams_secret),
1457 6bce7ba2 Santi Raffa
                                          False,
1458 22b7f6f8 Thomas Thrainer
                                          self.op.debug_level)
1459 22b7f6f8 Thomas Thrainer
          if pause_sync:
1460 22b7f6f8 Thomas Thrainer
            feedback_fn("* resuming disk sync")
1461 1c3231aa Thomas Thrainer
            result = self.rpc.call_blockdev_pause_resume_sync(self.pnode.uuid,
1462 22b7f6f8 Thomas Thrainer
                                                              (iobj.disks,
1463 22b7f6f8 Thomas Thrainer
                                                               iobj), False)
1464 22b7f6f8 Thomas Thrainer
            for idx, success in enumerate(result.payload):
1465 22b7f6f8 Thomas Thrainer
              if not success:
1466 22b7f6f8 Thomas Thrainer
                logging.warn("resume-sync of instance %s for disk %d failed",
1467 d0d7d7cf Thomas Thrainer
                             self.op.instance_name, idx)
1468 22b7f6f8 Thomas Thrainer
1469 22b7f6f8 Thomas Thrainer
          os_add_result.Raise("Could not add os for instance %s"
1470 d0d7d7cf Thomas Thrainer
                              " on node %s" % (self.op.instance_name,
1471 d0d7d7cf Thomas Thrainer
                                               self.pnode.name))
1472 22b7f6f8 Thomas Thrainer
1473 22b7f6f8 Thomas Thrainer
      else:
1474 22b7f6f8 Thomas Thrainer
        if self.op.mode == constants.INSTANCE_IMPORT:
1475 22b7f6f8 Thomas Thrainer
          feedback_fn("* running the instance OS import scripts...")
1476 22b7f6f8 Thomas Thrainer
1477 22b7f6f8 Thomas Thrainer
          transfers = []
1478 22b7f6f8 Thomas Thrainer
1479 22b7f6f8 Thomas Thrainer
          for idx, image in enumerate(self.src_images):
1480 22b7f6f8 Thomas Thrainer
            if not image:
1481 22b7f6f8 Thomas Thrainer
              continue
1482 22b7f6f8 Thomas Thrainer
1483 22b7f6f8 Thomas Thrainer
            # FIXME: pass debug option from opcode to backend
1484 22b7f6f8 Thomas Thrainer
            dt = masterd.instance.DiskTransfer("disk/%s" % idx,
1485 22b7f6f8 Thomas Thrainer
                                               constants.IEIO_FILE, (image, ),
1486 22b7f6f8 Thomas Thrainer
                                               constants.IEIO_SCRIPT,
1487 0c3d9c7c Thomas Thrainer
                                               ((iobj.disks[idx], iobj), idx),
1488 22b7f6f8 Thomas Thrainer
                                               None)
1489 22b7f6f8 Thomas Thrainer
            transfers.append(dt)
1490 22b7f6f8 Thomas Thrainer
1491 22b7f6f8 Thomas Thrainer
          import_result = \
1492 22b7f6f8 Thomas Thrainer
            masterd.instance.TransferInstanceData(self, feedback_fn,
1493 1c3231aa Thomas Thrainer
                                                  self.op.src_node_uuid,
1494 1c3231aa Thomas Thrainer
                                                  self.pnode.uuid,
1495 22b7f6f8 Thomas Thrainer
                                                  self.pnode.secondary_ip,
1496 51d7ac96 Thomas Thrainer
                                                  self.op.compress,
1497 22b7f6f8 Thomas Thrainer
                                                  iobj, transfers)
1498 22b7f6f8 Thomas Thrainer
          if not compat.all(import_result):
1499 22b7f6f8 Thomas Thrainer
            self.LogWarning("Some disks for instance %s on node %s were not"
1500 d0d7d7cf Thomas Thrainer
                            " imported successfully" % (self.op.instance_name,
1501 d0d7d7cf Thomas Thrainer
                                                        self.pnode.name))
1502 22b7f6f8 Thomas Thrainer
1503 22b7f6f8 Thomas Thrainer
          rename_from = self._old_instance_name
1504 22b7f6f8 Thomas Thrainer
1505 22b7f6f8 Thomas Thrainer
        elif self.op.mode == constants.INSTANCE_REMOTE_IMPORT:
1506 22b7f6f8 Thomas Thrainer
          feedback_fn("* preparing remote import...")
1507 22b7f6f8 Thomas Thrainer
          # The source cluster will stop the instance before attempting to make
1508 22b7f6f8 Thomas Thrainer
          # a connection. In some cases stopping an instance can take a long
1509 22b7f6f8 Thomas Thrainer
          # time, hence the shutdown timeout is added to the connection
1510 22b7f6f8 Thomas Thrainer
          # timeout.
1511 22b7f6f8 Thomas Thrainer
          connect_timeout = (constants.RIE_CONNECT_TIMEOUT +
1512 22b7f6f8 Thomas Thrainer
                             self.op.source_shutdown_timeout)
1513 22b7f6f8 Thomas Thrainer
          timeouts = masterd.instance.ImportExportTimeouts(connect_timeout)
1514 22b7f6f8 Thomas Thrainer
1515 1c3231aa Thomas Thrainer
          assert iobj.primary_node == self.pnode.uuid
1516 22b7f6f8 Thomas Thrainer
          disk_results = \
1517 22b7f6f8 Thomas Thrainer
            masterd.instance.RemoteImport(self, feedback_fn, iobj, self.pnode,
1518 22b7f6f8 Thomas Thrainer
                                          self.source_x509_ca,
1519 88acff3f Thomas Thrainer
                                          self._cds, self.op.compress, timeouts)
1520 22b7f6f8 Thomas Thrainer
          if not compat.all(disk_results):
1521 22b7f6f8 Thomas Thrainer
            # TODO: Should the instance still be started, even if some disks
1522 22b7f6f8 Thomas Thrainer
            # failed to import (valid for local imports, too)?
1523 22b7f6f8 Thomas Thrainer
            self.LogWarning("Some disks for instance %s on node %s were not"
1524 d0d7d7cf Thomas Thrainer
                            " imported successfully" % (self.op.instance_name,
1525 d0d7d7cf Thomas Thrainer
                                                        self.pnode.name))
1526 22b7f6f8 Thomas Thrainer
1527 22b7f6f8 Thomas Thrainer
          rename_from = self.source_instance_name
1528 22b7f6f8 Thomas Thrainer
1529 22b7f6f8 Thomas Thrainer
        else:
1530 22b7f6f8 Thomas Thrainer
          # also checked in the prereq part
1531 22b7f6f8 Thomas Thrainer
          raise errors.ProgrammerError("Unknown OS initialization mode '%s'"
1532 22b7f6f8 Thomas Thrainer
                                       % self.op.mode)
1533 22b7f6f8 Thomas Thrainer
1534 22b7f6f8 Thomas Thrainer
        # Run rename script on newly imported instance
1535 d0d7d7cf Thomas Thrainer
        assert iobj.name == self.op.instance_name
1536 d0d7d7cf Thomas Thrainer
        feedback_fn("Running rename script for %s" % self.op.instance_name)
1537 1c3231aa Thomas Thrainer
        result = self.rpc.call_instance_run_rename(self.pnode.uuid, iobj,
1538 22b7f6f8 Thomas Thrainer
                                                   rename_from,
1539 22b7f6f8 Thomas Thrainer
                                                   self.op.debug_level)
1540 c7dd65be Klaus Aehlig
        result.Warn("Failed to run rename script for %s on node %s" %
1541 d0d7d7cf Thomas Thrainer
                    (self.op.instance_name, self.pnode.name), self.LogWarning)
1542 22b7f6f8 Thomas Thrainer
1543 22b7f6f8 Thomas Thrainer
    assert not self.owned_locks(locking.LEVEL_NODE_RES)
1544 22b7f6f8 Thomas Thrainer
1545 22b7f6f8 Thomas Thrainer
    if self.op.start:
1546 22b7f6f8 Thomas Thrainer
      iobj.admin_state = constants.ADMINST_UP
1547 22b7f6f8 Thomas Thrainer
      self.cfg.Update(iobj, feedback_fn)
1548 d0d7d7cf Thomas Thrainer
      logging.info("Starting instance %s on node %s", self.op.instance_name,
1549 d0d7d7cf Thomas Thrainer
                   self.pnode.name)
1550 22b7f6f8 Thomas Thrainer
      feedback_fn("* starting instance...")
1551 1c3231aa Thomas Thrainer
      result = self.rpc.call_instance_start(self.pnode.uuid, (iobj, None, None),
1552 22b7f6f8 Thomas Thrainer
                                            False, self.op.reason)
1553 22b7f6f8 Thomas Thrainer
      result.Raise("Could not start instance")
1554 22b7f6f8 Thomas Thrainer
1555 22b7f6f8 Thomas Thrainer
    return list(iobj.all_nodes)
1556 22b7f6f8 Thomas Thrainer
1557 22b7f6f8 Thomas Thrainer
1558 22b7f6f8 Thomas Thrainer
class LUInstanceRename(LogicalUnit):
1559 22b7f6f8 Thomas Thrainer
  """Rename an instance.
1560 22b7f6f8 Thomas Thrainer

1561 22b7f6f8 Thomas Thrainer
  """
1562 22b7f6f8 Thomas Thrainer
  HPATH = "instance-rename"
1563 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1564 22b7f6f8 Thomas Thrainer
1565 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1566 22b7f6f8 Thomas Thrainer
    """Check arguments.
1567 22b7f6f8 Thomas Thrainer

1568 22b7f6f8 Thomas Thrainer
    """
1569 22b7f6f8 Thomas Thrainer
    if self.op.ip_check and not self.op.name_check:
1570 22b7f6f8 Thomas Thrainer
      # TODO: make the ip check more flexible and not depend on the name check
1571 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("IP address check requires a name check",
1572 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1573 22b7f6f8 Thomas Thrainer
1574 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1575 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1576 22b7f6f8 Thomas Thrainer

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

1579 22b7f6f8 Thomas Thrainer
    """
1580 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1581 22b7f6f8 Thomas Thrainer
    env["INSTANCE_NEW_NAME"] = self.op.new_name
1582 22b7f6f8 Thomas Thrainer
    return env
1583 22b7f6f8 Thomas Thrainer
1584 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1585 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1586 22b7f6f8 Thomas Thrainer

1587 22b7f6f8 Thomas Thrainer
    """
1588 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
1589 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1590 22b7f6f8 Thomas Thrainer
1591 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1592 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1593 22b7f6f8 Thomas Thrainer

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

1596 22b7f6f8 Thomas Thrainer
    """
1597 da4a52a3 Thomas Thrainer
    (self.op.instance_uuid, self.op.instance_name) = \
1598 da4a52a3 Thomas Thrainer
      ExpandInstanceUuidAndName(self.cfg, self.op.instance_uuid,
1599 da4a52a3 Thomas Thrainer
                                self.op.instance_name)
1600 da4a52a3 Thomas Thrainer
    instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1601 22b7f6f8 Thomas Thrainer
    assert instance is not None
1602 1f7c8208 Helga Velroyen
1603 1f7c8208 Helga Velroyen
    # It should actually not happen that an instance is running with a disabled
1604 1f7c8208 Helga Velroyen
    # disk template, but in case it does, the renaming of file-based instances
1605 1f7c8208 Helga Velroyen
    # will fail horribly. Thus, we test it before.
1606 1f7c8208 Helga Velroyen
    if (instance.disk_template in constants.DTS_FILEBASED and
1607 1f7c8208 Helga Velroyen
        self.op.new_name != instance.name):
1608 1f7c8208 Helga Velroyen
      CheckDiskTemplateEnabled(self.cfg.GetClusterInfo(),
1609 1f7c8208 Helga Velroyen
                               instance.disk_template)
1610 1f7c8208 Helga Velroyen
1611 5eacbcae Thomas Thrainer
    CheckNodeOnline(self, instance.primary_node)
1612 5eacbcae Thomas Thrainer
    CheckInstanceState(self, instance, INSTANCE_NOT_RUNNING,
1613 5eacbcae Thomas Thrainer
                       msg="cannot rename")
1614 22b7f6f8 Thomas Thrainer
    self.instance = instance
1615 22b7f6f8 Thomas Thrainer
1616 22b7f6f8 Thomas Thrainer
    new_name = self.op.new_name
1617 22b7f6f8 Thomas Thrainer
    if self.op.name_check:
1618 22b7f6f8 Thomas Thrainer
      hostname = _CheckHostnameSane(self, new_name)
1619 22b7f6f8 Thomas Thrainer
      new_name = self.op.new_name = hostname.name
1620 22b7f6f8 Thomas Thrainer
      if (self.op.ip_check and
1621 22b7f6f8 Thomas Thrainer
          netutils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT)):
1622 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("IP %s of instance %s already in use" %
1623 22b7f6f8 Thomas Thrainer
                                   (hostname.ip, new_name),
1624 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
1625 22b7f6f8 Thomas Thrainer
1626 da4a52a3 Thomas Thrainer
    instance_names = [inst.name for
1627 da4a52a3 Thomas Thrainer
                      inst in self.cfg.GetAllInstancesInfo().values()]
1628 da4a52a3 Thomas Thrainer
    if new_name in instance_names and new_name != instance.name:
1629 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
1630 22b7f6f8 Thomas Thrainer
                                 new_name, errors.ECODE_EXISTS)
1631 22b7f6f8 Thomas Thrainer
1632 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1633 22b7f6f8 Thomas Thrainer
    """Rename the instance.
1634 22b7f6f8 Thomas Thrainer

1635 22b7f6f8 Thomas Thrainer
    """
1636 d0d7d7cf Thomas Thrainer
    old_name = self.instance.name
1637 22b7f6f8 Thomas Thrainer
1638 22b7f6f8 Thomas Thrainer
    rename_file_storage = False
1639 845b7ed1 Santi Raffa
    if (self.instance.disk_template in (constants.DT_FILE,
1640 845b7ed1 Santi Raffa
                                        constants.DT_SHARED_FILE) and
1641 d0d7d7cf Thomas Thrainer
        self.op.new_name != self.instance.name):
1642 d0d7d7cf Thomas Thrainer
      old_file_storage_dir = os.path.dirname(
1643 d0d7d7cf Thomas Thrainer
                               self.instance.disks[0].logical_id[1])
1644 22b7f6f8 Thomas Thrainer
      rename_file_storage = True
1645 22b7f6f8 Thomas Thrainer
1646 da4a52a3 Thomas Thrainer
    self.cfg.RenameInstance(self.instance.uuid, self.op.new_name)
1647 22b7f6f8 Thomas Thrainer
    # Change the instance lock. This is definitely safe while we hold the BGL.
1648 22b7f6f8 Thomas Thrainer
    # Otherwise the new lock would have to be added in acquired mode.
1649 22b7f6f8 Thomas Thrainer
    assert self.REQ_BGL
1650 22b7f6f8 Thomas Thrainer
    assert locking.BGL in self.owned_locks(locking.LEVEL_CLUSTER)
1651 22b7f6f8 Thomas Thrainer
    self.glm.remove(locking.LEVEL_INSTANCE, old_name)
1652 22b7f6f8 Thomas Thrainer
    self.glm.add(locking.LEVEL_INSTANCE, self.op.new_name)
1653 22b7f6f8 Thomas Thrainer
1654 22b7f6f8 Thomas Thrainer
    # re-read the instance from the configuration after rename
1655 da4a52a3 Thomas Thrainer
    renamed_inst = self.cfg.GetInstanceInfo(self.instance.uuid)
1656 22b7f6f8 Thomas Thrainer
1657 22b7f6f8 Thomas Thrainer
    if rename_file_storage:
1658 d0d7d7cf Thomas Thrainer
      new_file_storage_dir = os.path.dirname(
1659 d0d7d7cf Thomas Thrainer
                               renamed_inst.disks[0].logical_id[1])
1660 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_file_storage_dir_rename(renamed_inst.primary_node,
1661 22b7f6f8 Thomas Thrainer
                                                     old_file_storage_dir,
1662 22b7f6f8 Thomas Thrainer
                                                     new_file_storage_dir)
1663 22b7f6f8 Thomas Thrainer
      result.Raise("Could not rename on node %s directory '%s' to '%s'"
1664 22b7f6f8 Thomas Thrainer
                   " (but the instance has been renamed in Ganeti)" %
1665 d0d7d7cf Thomas Thrainer
                   (self.cfg.GetNodeName(renamed_inst.primary_node),
1666 1c3231aa Thomas Thrainer
                    old_file_storage_dir, new_file_storage_dir))
1667 22b7f6f8 Thomas Thrainer
1668 d0d7d7cf Thomas Thrainer
    StartInstanceDisks(self, renamed_inst, None)
1669 22b7f6f8 Thomas Thrainer
    # update info on disks
1670 d0d7d7cf Thomas Thrainer
    info = GetInstanceInfoText(renamed_inst)
1671 d0d7d7cf Thomas Thrainer
    for (idx, disk) in enumerate(renamed_inst.disks):
1672 d0d7d7cf Thomas Thrainer
      for node_uuid in renamed_inst.all_nodes:
1673 0c3d9c7c Thomas Thrainer
        result = self.rpc.call_blockdev_setinfo(node_uuid,
1674 0c3d9c7c Thomas Thrainer
                                                (disk, renamed_inst), info)
1675 1c3231aa Thomas Thrainer
        result.Warn("Error setting info on node %s for disk %s" %
1676 1c3231aa Thomas Thrainer
                    (self.cfg.GetNodeName(node_uuid), idx), self.LogWarning)
1677 22b7f6f8 Thomas Thrainer
    try:
1678 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_instance_run_rename(renamed_inst.primary_node,
1679 d0d7d7cf Thomas Thrainer
                                                 renamed_inst, old_name,
1680 d0d7d7cf Thomas Thrainer
                                                 self.op.debug_level)
1681 c7dd65be Klaus Aehlig
      result.Warn("Could not run OS rename script for instance %s on node %s"
1682 c7dd65be Klaus Aehlig
                  " (but the instance has been renamed in Ganeti)" %
1683 d0d7d7cf Thomas Thrainer
                  (renamed_inst.name,
1684 d0d7d7cf Thomas Thrainer
                   self.cfg.GetNodeName(renamed_inst.primary_node)),
1685 1c3231aa Thomas Thrainer
                  self.LogWarning)
1686 22b7f6f8 Thomas Thrainer
    finally:
1687 d0d7d7cf Thomas Thrainer
      ShutdownInstanceDisks(self, renamed_inst)
1688 22b7f6f8 Thomas Thrainer
1689 d0d7d7cf Thomas Thrainer
    return renamed_inst.name
1690 22b7f6f8 Thomas Thrainer
1691 22b7f6f8 Thomas Thrainer
1692 22b7f6f8 Thomas Thrainer
class LUInstanceRemove(LogicalUnit):
1693 22b7f6f8 Thomas Thrainer
  """Remove an instance.
1694 22b7f6f8 Thomas Thrainer

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

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

1719 22b7f6f8 Thomas Thrainer
    """
1720 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1721 22b7f6f8 Thomas Thrainer
    env["SHUTDOWN_TIMEOUT"] = self.op.shutdown_timeout
1722 22b7f6f8 Thomas Thrainer
    return env
1723 22b7f6f8 Thomas Thrainer
1724 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1725 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1726 22b7f6f8 Thomas Thrainer

1727 22b7f6f8 Thomas Thrainer
    """
1728 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()]
1729 22b7f6f8 Thomas Thrainer
    nl_post = list(self.instance.all_nodes) + nl
1730 22b7f6f8 Thomas Thrainer
    return (nl, nl_post)
1731 22b7f6f8 Thomas Thrainer
1732 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1733 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1734 22b7f6f8 Thomas Thrainer

1735 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1736 22b7f6f8 Thomas Thrainer

1737 22b7f6f8 Thomas Thrainer
    """
1738 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1739 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1740 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1741 22b7f6f8 Thomas Thrainer
1742 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1743 22b7f6f8 Thomas Thrainer
    """Remove the instance.
1744 22b7f6f8 Thomas Thrainer

1745 22b7f6f8 Thomas Thrainer
    """
1746 d0d7d7cf Thomas Thrainer
    logging.info("Shutting down instance %s on node %s", self.instance.name,
1747 d0d7d7cf Thomas Thrainer
                 self.cfg.GetNodeName(self.instance.primary_node))
1748 22b7f6f8 Thomas Thrainer
1749 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_instance_shutdown(self.instance.primary_node,
1750 d0d7d7cf Thomas Thrainer
                                             self.instance,
1751 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1752 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1753 c7dd65be Klaus Aehlig
    if self.op.ignore_failures:
1754 c7dd65be Klaus Aehlig
      result.Warn("Warning: can't shutdown instance", feedback_fn)
1755 c7dd65be Klaus Aehlig
    else:
1756 c7dd65be Klaus Aehlig
      result.Raise("Could not shutdown instance %s on node %s" %
1757 d0d7d7cf Thomas Thrainer
                   (self.instance.name,
1758 d0d7d7cf Thomas Thrainer
                    self.cfg.GetNodeName(self.instance.primary_node)))
1759 22b7f6f8 Thomas Thrainer
1760 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1761 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1762 d0d7d7cf Thomas Thrainer
    assert not (set(self.instance.all_nodes) -
1763 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
1764 22b7f6f8 Thomas Thrainer
      "Not owning correct locks"
1765 22b7f6f8 Thomas Thrainer
1766 d0d7d7cf Thomas Thrainer
    RemoveInstance(self, feedback_fn, self.instance, self.op.ignore_failures)
1767 22b7f6f8 Thomas Thrainer
1768 22b7f6f8 Thomas Thrainer
1769 22b7f6f8 Thomas Thrainer
class LUInstanceMove(LogicalUnit):
1770 22b7f6f8 Thomas Thrainer
  """Move an instance by data-copying.
1771 22b7f6f8 Thomas Thrainer

1772 22b7f6f8 Thomas Thrainer
  """
1773 22b7f6f8 Thomas Thrainer
  HPATH = "instance-move"
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 1c3231aa Thomas Thrainer
    (self.op.target_node_uuid, self.op.target_node) = \
1780 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.target_node_uuid,
1781 1c3231aa Thomas Thrainer
                            self.op.target_node)
1782 b9aae98b Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = [self.op.target_node_uuid]
1783 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1784 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
1785 22b7f6f8 Thomas Thrainer
1786 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1787 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1788 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes(primary_only=True)
1789 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1790 22b7f6f8 Thomas Thrainer
      # Copy node locks
1791 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1792 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1793 22b7f6f8 Thomas Thrainer
1794 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1795 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1796 22b7f6f8 Thomas Thrainer

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

1799 22b7f6f8 Thomas Thrainer
    """
1800 22b7f6f8 Thomas Thrainer
    env = {
1801 22b7f6f8 Thomas Thrainer
      "TARGET_NODE": self.op.target_node,
1802 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
1803 22b7f6f8 Thomas Thrainer
      }
1804 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
1805 22b7f6f8 Thomas Thrainer
    return env
1806 22b7f6f8 Thomas Thrainer
1807 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1808 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1809 22b7f6f8 Thomas Thrainer

1810 22b7f6f8 Thomas Thrainer
    """
1811 22b7f6f8 Thomas Thrainer
    nl = [
1812 22b7f6f8 Thomas Thrainer
      self.cfg.GetMasterNode(),
1813 22b7f6f8 Thomas Thrainer
      self.instance.primary_node,
1814 1c3231aa Thomas Thrainer
      self.op.target_node_uuid,
1815 22b7f6f8 Thomas Thrainer
      ]
1816 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1817 22b7f6f8 Thomas Thrainer
1818 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1819 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1820 22b7f6f8 Thomas Thrainer

1821 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1822 22b7f6f8 Thomas Thrainer

1823 22b7f6f8 Thomas Thrainer
    """
1824 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1825 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1826 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1827 22b7f6f8 Thomas Thrainer
1828 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template not in constants.DTS_COPYABLE:
1829 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk template %s not suitable for copying" %
1830 d0d7d7cf Thomas Thrainer
                                 self.instance.disk_template,
1831 d0d7d7cf Thomas Thrainer
                                 errors.ECODE_STATE)
1832 22b7f6f8 Thomas Thrainer
1833 1c3231aa Thomas Thrainer
    target_node = self.cfg.GetNodeInfo(self.op.target_node_uuid)
1834 1c3231aa Thomas Thrainer
    assert target_node is not None, \
1835 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked node %s" % self.op.target_node
1836 22b7f6f8 Thomas Thrainer
1837 1c3231aa Thomas Thrainer
    self.target_node_uuid = target_node.uuid
1838 d0d7d7cf Thomas Thrainer
    if target_node.uuid == self.instance.primary_node:
1839 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance %s is already on the node %s" %
1840 d0d7d7cf Thomas Thrainer
                                 (self.instance.name, target_node.name),
1841 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
1842 22b7f6f8 Thomas Thrainer
1843 d29f52a6 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
1844 d29f52a6 Thomas Thrainer
    bep = cluster.FillBE(self.instance)
1845 22b7f6f8 Thomas Thrainer
1846 d0d7d7cf Thomas Thrainer
    for idx, dsk in enumerate(self.instance.disks):
1847 cd3b4ff4 Helga Velroyen
      if dsk.dev_type not in (constants.DT_PLAIN, constants.DT_FILE,
1848 8106dd64 Santi Raffa
                              constants.DT_SHARED_FILE, constants.DT_GLUSTER):
1849 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance disk %d has a complex layout,"
1850 22b7f6f8 Thomas Thrainer
                                   " cannot copy" % idx, errors.ECODE_STATE)
1851 22b7f6f8 Thomas Thrainer
1852 1c3231aa Thomas Thrainer
    CheckNodeOnline(self, target_node.uuid)
1853 1c3231aa Thomas Thrainer
    CheckNodeNotDrained(self, target_node.uuid)
1854 1c3231aa Thomas Thrainer
    CheckNodeVmCapable(self, target_node.uuid)
1855 1c3231aa Thomas Thrainer
    group_info = self.cfg.GetNodeGroup(target_node.group)
1856 22b7f6f8 Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster, group_info)
1857 d0d7d7cf Thomas Thrainer
    CheckTargetNodeIPolicy(self, ipolicy, self.instance, target_node, self.cfg,
1858 5eacbcae Thomas Thrainer
                           ignore=self.op.ignore_ipolicy)
1859 22b7f6f8 Thomas Thrainer
1860 d0d7d7cf Thomas Thrainer
    if self.instance.admin_state == constants.ADMINST_UP:
1861 83266db6 Thomas Thrainer
      # check memory requirements on the target node
1862 a295eb80 Helga Velroyen
      CheckNodeFreeMemory(
1863 1c3231aa Thomas Thrainer
          self, target_node.uuid, "failing over instance %s" %
1864 d0d7d7cf Thomas Thrainer
          self.instance.name, bep[constants.BE_MAXMEM],
1865 d0d7d7cf Thomas Thrainer
          self.instance.hypervisor,
1866 d29f52a6 Thomas Thrainer
          cluster.hvparams[self.instance.hypervisor])
1867 22b7f6f8 Thomas Thrainer
    else:
1868 22b7f6f8 Thomas Thrainer
      self.LogInfo("Not checking memory on the secondary node as"
1869 22b7f6f8 Thomas Thrainer
                   " instance will not be started")
1870 22b7f6f8 Thomas Thrainer
1871 22b7f6f8 Thomas Thrainer
    # check bridge existance
1872 d0d7d7cf Thomas Thrainer
    CheckInstanceBridgesExist(self, self.instance, node_uuid=target_node.uuid)
1873 22b7f6f8 Thomas Thrainer
1874 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1875 22b7f6f8 Thomas Thrainer
    """Move an instance.
1876 22b7f6f8 Thomas Thrainer

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

1880 22b7f6f8 Thomas Thrainer
    """
1881 d0d7d7cf Thomas Thrainer
    source_node = self.cfg.GetNodeInfo(self.instance.primary_node)
1882 1c3231aa Thomas Thrainer
    target_node = self.cfg.GetNodeInfo(self.target_node_uuid)
1883 22b7f6f8 Thomas Thrainer
1884 22b7f6f8 Thomas Thrainer
    self.LogInfo("Shutting down instance %s on source node %s",
1885 d0d7d7cf Thomas Thrainer
                 self.instance.name, source_node.name)
1886 22b7f6f8 Thomas Thrainer
1887 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1888 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1889 22b7f6f8 Thomas Thrainer
1890 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_instance_shutdown(source_node.uuid, self.instance,
1891 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1892 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1893 c7dd65be Klaus Aehlig
    if self.op.ignore_consistency:
1894 c7dd65be Klaus Aehlig
      result.Warn("Could not shutdown instance %s on node %s. Proceeding"
1895 c7dd65be Klaus Aehlig
                  " anyway. Please make sure node %s is down. Error details" %
1896 d0d7d7cf Thomas Thrainer
                  (self.instance.name, source_node.name, source_node.name),
1897 1c3231aa Thomas Thrainer
                  self.LogWarning)
1898 c7dd65be Klaus Aehlig
    else:
1899 c7dd65be Klaus Aehlig
      result.Raise("Could not shutdown instance %s on node %s" %
1900 d0d7d7cf Thomas Thrainer
                   (self.instance.name, source_node.name))
1901 22b7f6f8 Thomas Thrainer
1902 22b7f6f8 Thomas Thrainer
    # create the target disks
1903 22b7f6f8 Thomas Thrainer
    try:
1904 d0d7d7cf Thomas Thrainer
      CreateDisks(self, self.instance, target_node_uuid=target_node.uuid)
1905 22b7f6f8 Thomas Thrainer
    except errors.OpExecError:
1906 22b7f6f8 Thomas Thrainer
      self.LogWarning("Device creation failed")
1907 da4a52a3 Thomas Thrainer
      self.cfg.ReleaseDRBDMinors(self.instance.uuid)
1908 22b7f6f8 Thomas Thrainer
      raise
1909 22b7f6f8 Thomas Thrainer
1910 22b7f6f8 Thomas Thrainer
    errs = []
1911 063613aa Thomas Thrainer
    transfers = []
1912 063613aa Thomas Thrainer
    # activate, get path, create transfer jobs
1913 d0d7d7cf Thomas Thrainer
    for idx, disk in enumerate(self.instance.disks):
1914 063613aa Thomas Thrainer
      # FIXME: pass debug option from opcode to backend
1915 063613aa Thomas Thrainer
      dt = masterd.instance.DiskTransfer("disk/%s" % idx,
1916 063613aa Thomas Thrainer
                                         constants.IEIO_RAW_DISK,
1917 063613aa Thomas Thrainer
                                         (disk, self.instance),
1918 063613aa Thomas Thrainer
                                         constants.IEIO_RAW_DISK,
1919 063613aa Thomas Thrainer
                                         (disk, self.instance),
1920 063613aa Thomas Thrainer
                                         None)
1921 063613aa Thomas Thrainer
      transfers.append(dt)
1922 063613aa Thomas Thrainer
1923 063613aa Thomas Thrainer
    import_result = \
1924 063613aa Thomas Thrainer
      masterd.instance.TransferInstanceData(self, feedback_fn,
1925 063613aa Thomas Thrainer
                                            source_node.uuid,
1926 063613aa Thomas Thrainer
                                            target_node.uuid,
1927 063613aa Thomas Thrainer
                                            target_node.secondary_ip,
1928 f198cf91 Thomas Thrainer
                                            self.op.compress,
1929 063613aa Thomas Thrainer
                                            self.instance, transfers)
1930 063613aa Thomas Thrainer
    if not compat.all(import_result):
1931 063613aa Thomas Thrainer
      errs.append("Failed to transfer instance data")
1932 22b7f6f8 Thomas Thrainer
1933 22b7f6f8 Thomas Thrainer
    if errs:
1934 22b7f6f8 Thomas Thrainer
      self.LogWarning("Some disks failed to copy, aborting")
1935 22b7f6f8 Thomas Thrainer
      try:
1936 d0d7d7cf Thomas Thrainer
        RemoveDisks(self, self.instance, target_node_uuid=target_node.uuid)
1937 22b7f6f8 Thomas Thrainer
      finally:
1938 da4a52a3 Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(self.instance.uuid)
1939 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Errors during disk copy: %s" %
1940 22b7f6f8 Thomas Thrainer
                                 (",".join(errs),))
1941 22b7f6f8 Thomas Thrainer
1942 d0d7d7cf Thomas Thrainer
    self.instance.primary_node = target_node.uuid
1943 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
1944 22b7f6f8 Thomas Thrainer
1945 22b7f6f8 Thomas Thrainer
    self.LogInfo("Removing the disks on the original node")
1946 d0d7d7cf Thomas Thrainer
    RemoveDisks(self, self.instance, target_node_uuid=source_node.uuid)
1947 22b7f6f8 Thomas Thrainer
1948 22b7f6f8 Thomas Thrainer
    # Only start the instance if it's marked as up
1949 d0d7d7cf Thomas Thrainer
    if self.instance.admin_state == constants.ADMINST_UP:
1950 22b7f6f8 Thomas Thrainer
      self.LogInfo("Starting instance %s on node %s",
1951 d0d7d7cf Thomas Thrainer
                   self.instance.name, target_node.name)
1952 22b7f6f8 Thomas Thrainer
1953 d0d7d7cf Thomas Thrainer
      disks_ok, _ = AssembleInstanceDisks(self, self.instance,
1954 5eacbcae Thomas Thrainer
                                          ignore_secondaries=True)
1955 22b7f6f8 Thomas Thrainer
      if not disks_ok:
1956 d0d7d7cf Thomas Thrainer
        ShutdownInstanceDisks(self, self.instance)
1957 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Can't activate the instance's disks")
1958 22b7f6f8 Thomas Thrainer
1959 1c3231aa Thomas Thrainer
      result = self.rpc.call_instance_start(target_node.uuid,
1960 d0d7d7cf Thomas Thrainer
                                            (self.instance, None, None), False,
1961 22b7f6f8 Thomas Thrainer
                                            self.op.reason)
1962 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
1963 22b7f6f8 Thomas Thrainer
      if msg:
1964 d0d7d7cf Thomas Thrainer
        ShutdownInstanceDisks(self, self.instance)
1965 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not start instance %s on node %s: %s" %
1966 d0d7d7cf Thomas Thrainer
                                 (self.instance.name, target_node.name, msg))
1967 22b7f6f8 Thomas Thrainer
1968 22b7f6f8 Thomas Thrainer
1969 22b7f6f8 Thomas Thrainer
class LUInstanceMultiAlloc(NoHooksLU):
1970 22b7f6f8 Thomas Thrainer
  """Allocates multiple instances at the same time.
1971 22b7f6f8 Thomas Thrainer

1972 22b7f6f8 Thomas Thrainer
  """
1973 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1974 22b7f6f8 Thomas Thrainer
1975 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1976 22b7f6f8 Thomas Thrainer
    """Check arguments.
1977 22b7f6f8 Thomas Thrainer

1978 22b7f6f8 Thomas Thrainer
    """
1979 22b7f6f8 Thomas Thrainer
    nodes = []
1980 22b7f6f8 Thomas Thrainer
    for inst in self.op.instances:
1981 22b7f6f8 Thomas Thrainer
      if inst.iallocator is not None:
1982 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("iallocator are not allowed to be set on"
1983 22b7f6f8 Thomas Thrainer
                                   " instance objects", errors.ECODE_INVAL)
1984 22b7f6f8 Thomas Thrainer
      nodes.append(bool(inst.pnode))
1985 22b7f6f8 Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
1986 22b7f6f8 Thomas Thrainer
        nodes.append(bool(inst.snode))
1987 22b7f6f8 Thomas Thrainer
1988 22b7f6f8 Thomas Thrainer
    has_nodes = compat.any(nodes)
1989 22b7f6f8 Thomas Thrainer
    if compat.all(nodes) ^ has_nodes:
1990 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are instance objects providing"
1991 22b7f6f8 Thomas Thrainer
                                 " pnode/snode while others do not",
1992 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1993 22b7f6f8 Thomas Thrainer
1994 0c072225 Thomas Thrainer
    if not has_nodes and self.op.iallocator is None:
1995 22b7f6f8 Thomas Thrainer
      default_iallocator = self.cfg.GetDefaultIAllocator()
1996 0c072225 Thomas Thrainer
      if default_iallocator:
1997 22b7f6f8 Thomas Thrainer
        self.op.iallocator = default_iallocator
1998 22b7f6f8 Thomas Thrainer
      else:
1999 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No iallocator or nodes on the instances"
2000 22b7f6f8 Thomas Thrainer
                                   " given and no cluster-wide default"
2001 22b7f6f8 Thomas Thrainer
                                   " iallocator found; please specify either"
2002 22b7f6f8 Thomas Thrainer
                                   " an iallocator or nodes on the instances"
2003 22b7f6f8 Thomas Thrainer
                                   " or set a cluster-wide default iallocator",
2004 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2005 22b7f6f8 Thomas Thrainer
2006 22b7f6f8 Thomas Thrainer
    _CheckOpportunisticLocking(self.op)
2007 22b7f6f8 Thomas Thrainer
2008 22b7f6f8 Thomas Thrainer
    dups = utils.FindDuplicates([op.instance_name for op in self.op.instances])
2009 22b7f6f8 Thomas Thrainer
    if dups:
2010 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are duplicate instance names: %s" %
2011 22b7f6f8 Thomas Thrainer
                                 utils.CommaJoin(dups), errors.ECODE_INVAL)
2012 22b7f6f8 Thomas Thrainer
2013 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2014 22b7f6f8 Thomas Thrainer
    """Calculate the locks.
2015 22b7f6f8 Thomas Thrainer

2016 22b7f6f8 Thomas Thrainer
    """
2017 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
2018 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
2019 22b7f6f8 Thomas Thrainer
      # iallocator will select nodes and even if no iallocator is used,
2020 22b7f6f8 Thomas Thrainer
      # collisions with LUInstanceCreate should be avoided
2021 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
2022 22b7f6f8 Thomas Thrainer
      }
2023 22b7f6f8 Thomas Thrainer
2024 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
2025 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
2026 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = locking.ALL_SET
2027 22b7f6f8 Thomas Thrainer
2028 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
2029 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
2030 22b7f6f8 Thomas Thrainer
    else:
2031 22b7f6f8 Thomas Thrainer
      nodeslist = []
2032 22b7f6f8 Thomas Thrainer
      for inst in self.op.instances:
2033 1c3231aa Thomas Thrainer
        (inst.pnode_uuid, inst.pnode) = \
2034 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, inst.pnode_uuid, inst.pnode)
2035 6869f673 Thomas Thrainer
        nodeslist.append(inst.pnode_uuid)
2036 22b7f6f8 Thomas Thrainer
        if inst.snode is not None:
2037 1c3231aa Thomas Thrainer
          (inst.snode_uuid, inst.snode) = \
2038 1c3231aa Thomas Thrainer
            ExpandNodeUuidAndName(self.cfg, inst.snode_uuid, inst.snode)
2039 6869f673 Thomas Thrainer
          nodeslist.append(inst.snode_uuid)
2040 22b7f6f8 Thomas Thrainer
2041 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodeslist
2042 22b7f6f8 Thomas Thrainer
      # Lock resources of instance's primary and secondary nodes (copy to
2043 22b7f6f8 Thomas Thrainer
      # prevent accidential modification)
2044 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = list(nodeslist)
2045 22b7f6f8 Thomas Thrainer
2046 4289f617 Thomas Thrainer
  def DeclareLocks(self, level):
2047 4289f617 Thomas Thrainer
    if level == locking.LEVEL_NODE_RES and \
2048 4289f617 Thomas Thrainer
      self.opportunistic_locks[locking.LEVEL_NODE]:
2049 4289f617 Thomas Thrainer
      # Even when using opportunistic locking, we require the same set of
2050 4289f617 Thomas Thrainer
      # NODE_RES locks as we got NODE locks
2051 4289f617 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
2052 4289f617 Thomas Thrainer
        self.owned_locks(locking.LEVEL_NODE)
2053 4289f617 Thomas Thrainer
2054 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2055 22b7f6f8 Thomas Thrainer
    """Check prerequisite.
2056 22b7f6f8 Thomas Thrainer

2057 22b7f6f8 Thomas Thrainer
    """
2058 0c072225 Thomas Thrainer
    if self.op.iallocator:
2059 0c072225 Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
2060 0c072225 Thomas Thrainer
      default_vg = self.cfg.GetVGName()
2061 0c072225 Thomas Thrainer
      ec_id = self.proc.GetECId()
2062 22b7f6f8 Thomas Thrainer
2063 0c072225 Thomas Thrainer
      if self.op.opportunistic_locking:
2064 0c072225 Thomas Thrainer
        # Only consider nodes for which a lock is held
2065 804d72eb Thomas Thrainer
        node_whitelist = self.cfg.GetNodeNames(
2066 804d72eb Thomas Thrainer
                           list(self.owned_locks(locking.LEVEL_NODE)))
2067 0c072225 Thomas Thrainer
      else:
2068 0c072225 Thomas Thrainer
        node_whitelist = None
2069 22b7f6f8 Thomas Thrainer
2070 0c072225 Thomas Thrainer
      insts = [_CreateInstanceAllocRequest(op, ComputeDisks(op, default_vg),
2071 0c072225 Thomas Thrainer
                                           _ComputeNics(op, cluster, None,
2072 0c072225 Thomas Thrainer
                                                        self.cfg, ec_id),
2073 0c072225 Thomas Thrainer
                                           _ComputeFullBeParams(op, cluster),
2074 0c072225 Thomas Thrainer
                                           node_whitelist)
2075 0c072225 Thomas Thrainer
               for op in self.op.instances]
2076 22b7f6f8 Thomas Thrainer
2077 0c072225 Thomas Thrainer
      req = iallocator.IAReqMultiInstanceAlloc(instances=insts)
2078 0c072225 Thomas Thrainer
      ial = iallocator.IAllocator(self.cfg, self.rpc, req)
2079 22b7f6f8 Thomas Thrainer
2080 0c072225 Thomas Thrainer
      ial.Run(self.op.iallocator)
2081 22b7f6f8 Thomas Thrainer
2082 0c072225 Thomas Thrainer
      if not ial.success:
2083 0c072225 Thomas Thrainer
        raise errors.OpPrereqError("Can't compute nodes using"
2084 0c072225 Thomas Thrainer
                                   " iallocator '%s': %s" %
2085 0c072225 Thomas Thrainer
                                   (self.op.iallocator, ial.info),
2086 0c072225 Thomas Thrainer
                                   errors.ECODE_NORES)
2087 22b7f6f8 Thomas Thrainer
2088 0c072225 Thomas Thrainer
      self.ia_result = ial.result
2089 22b7f6f8 Thomas Thrainer
2090 22b7f6f8 Thomas Thrainer
    if self.op.dry_run:
2091 22b7f6f8 Thomas Thrainer
      self.dry_run_result = objects.FillDict(self._ConstructPartialResult(), {
2092 22b7f6f8 Thomas Thrainer
        constants.JOB_IDS_KEY: [],
2093 22b7f6f8 Thomas Thrainer
        })
2094 22b7f6f8 Thomas Thrainer
2095 22b7f6f8 Thomas Thrainer
  def _ConstructPartialResult(self):
2096 22b7f6f8 Thomas Thrainer
    """Contructs the partial result.
2097 22b7f6f8 Thomas Thrainer

2098 22b7f6f8 Thomas Thrainer
    """
2099 0c072225 Thomas Thrainer
    if self.op.iallocator:
2100 0c072225 Thomas Thrainer
      (allocatable, failed_insts) = self.ia_result
2101 0c072225 Thomas Thrainer
      allocatable_insts = map(compat.fst, allocatable)
2102 0c072225 Thomas Thrainer
    else:
2103 0c072225 Thomas Thrainer
      allocatable_insts = [op.instance_name for op in self.op.instances]
2104 0c072225 Thomas Thrainer
      failed_insts = []
2105 0c072225 Thomas Thrainer
2106 22b7f6f8 Thomas Thrainer
    return {
2107 1ca326c8 Thomas Thrainer
      constants.ALLOCATABLE_KEY: allocatable_insts,
2108 1ca326c8 Thomas Thrainer
      constants.FAILED_KEY: failed_insts,
2109 22b7f6f8 Thomas Thrainer
      }
2110 22b7f6f8 Thomas Thrainer
2111 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2112 22b7f6f8 Thomas Thrainer
    """Executes the opcode.
2113 22b7f6f8 Thomas Thrainer

2114 22b7f6f8 Thomas Thrainer
    """
2115 22b7f6f8 Thomas Thrainer
    jobs = []
2116 0c072225 Thomas Thrainer
    if self.op.iallocator:
2117 0c072225 Thomas Thrainer
      op2inst = dict((op.instance_name, op) for op in self.op.instances)
2118 0c072225 Thomas Thrainer
      (allocatable, failed) = self.ia_result
2119 22b7f6f8 Thomas Thrainer
2120 804d72eb Thomas Thrainer
      for (name, node_names) in allocatable:
2121 0c072225 Thomas Thrainer
        op = op2inst.pop(name)
2122 22b7f6f8 Thomas Thrainer
2123 804d72eb Thomas Thrainer
        (op.pnode_uuid, op.pnode) = \
2124 804d72eb Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, None, node_names[0])
2125 804d72eb Thomas Thrainer
        if len(node_names) > 1:
2126 804d72eb Thomas Thrainer
          (op.snode_uuid, op.snode) = \
2127 804d72eb Thomas Thrainer
            ExpandNodeUuidAndName(self.cfg, None, node_names[1])
2128 22b7f6f8 Thomas Thrainer
2129 804d72eb Thomas Thrainer
          jobs.append([op])
2130 22b7f6f8 Thomas Thrainer
2131 804d72eb Thomas Thrainer
        missing = set(op2inst.keys()) - set(failed)
2132 804d72eb Thomas Thrainer
        assert not missing, \
2133 804d72eb Thomas Thrainer
          "Iallocator did return incomplete result: %s" % \
2134 804d72eb Thomas Thrainer
          utils.CommaJoin(missing)
2135 0c072225 Thomas Thrainer
    else:
2136 0c072225 Thomas Thrainer
      jobs.extend([op] for op in self.op.instances)
2137 22b7f6f8 Thomas Thrainer
2138 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs, **self._ConstructPartialResult())
2139 22b7f6f8 Thomas Thrainer
2140 22b7f6f8 Thomas Thrainer
2141 22b7f6f8 Thomas Thrainer
class _InstNicModPrivate:
2142 22b7f6f8 Thomas Thrainer
  """Data structure for network interface modifications.
2143 22b7f6f8 Thomas Thrainer

2144 22b7f6f8 Thomas Thrainer
  Used by L{LUInstanceSetParams}.
2145 22b7f6f8 Thomas Thrainer

2146 22b7f6f8 Thomas Thrainer
  """
2147 22b7f6f8 Thomas Thrainer
  def __init__(self):
2148 22b7f6f8 Thomas Thrainer
    self.params = None
2149 22b7f6f8 Thomas Thrainer
    self.filled = None
2150 22b7f6f8 Thomas Thrainer
2151 22b7f6f8 Thomas Thrainer
2152 5eacbcae Thomas Thrainer
def _PrepareContainerMods(mods, private_fn):
2153 22b7f6f8 Thomas Thrainer
  """Prepares a list of container modifications by adding a private data field.
2154 22b7f6f8 Thomas Thrainer

2155 22b7f6f8 Thomas Thrainer
  @type mods: list of tuples; (operation, index, parameters)
2156 22b7f6f8 Thomas Thrainer
  @param mods: List of modifications
2157 22b7f6f8 Thomas Thrainer
  @type private_fn: callable or None
2158 22b7f6f8 Thomas Thrainer
  @param private_fn: Callable for constructing a private data field for a
2159 22b7f6f8 Thomas Thrainer
    modification
2160 22b7f6f8 Thomas Thrainer
  @rtype: list
2161 22b7f6f8 Thomas Thrainer

2162 22b7f6f8 Thomas Thrainer
  """
2163 22b7f6f8 Thomas Thrainer
  if private_fn is None:
2164 22b7f6f8 Thomas Thrainer
    fn = lambda: None
2165 22b7f6f8 Thomas Thrainer
  else:
2166 22b7f6f8 Thomas Thrainer
    fn = private_fn
2167 22b7f6f8 Thomas Thrainer
2168 22b7f6f8 Thomas Thrainer
  return [(op, idx, params, fn()) for (op, idx, params) in mods]
2169 22b7f6f8 Thomas Thrainer
2170 22b7f6f8 Thomas Thrainer
2171 1c3231aa Thomas Thrainer
def _CheckNodesPhysicalCPUs(lu, node_uuids, requested, hypervisor_specs):
2172 22b7f6f8 Thomas Thrainer
  """Checks if nodes have enough physical CPUs
2173 22b7f6f8 Thomas Thrainer

2174 22b7f6f8 Thomas Thrainer
  This function checks if all given nodes have the needed number of
2175 22b7f6f8 Thomas Thrainer
  physical CPUs. In case any node has less CPUs or we cannot get the
2176 22b7f6f8 Thomas Thrainer
  information from the node, this function raises an OpPrereqError
2177 22b7f6f8 Thomas Thrainer
  exception.
2178 22b7f6f8 Thomas Thrainer

2179 22b7f6f8 Thomas Thrainer
  @type lu: C{LogicalUnit}
2180 22b7f6f8 Thomas Thrainer
  @param lu: a logical unit from which we get configuration data
2181 1c3231aa Thomas Thrainer
  @type node_uuids: C{list}
2182 1c3231aa Thomas Thrainer
  @param node_uuids: the list of node UUIDs to check
2183 22b7f6f8 Thomas Thrainer
  @type requested: C{int}
2184 22b7f6f8 Thomas Thrainer
  @param requested: the minimum acceptable number of physical CPUs
2185 a295eb80 Helga Velroyen
  @type hypervisor_specs: list of pairs (string, dict of strings)
2186 a295eb80 Helga Velroyen
  @param hypervisor_specs: list of hypervisor specifications in
2187 a295eb80 Helga Velroyen
      pairs (hypervisor_name, hvparams)
2188 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node doesn't have enough CPUs,
2189 22b7f6f8 Thomas Thrainer
      or we cannot check the node
2190 22b7f6f8 Thomas Thrainer

2191 22b7f6f8 Thomas Thrainer
  """
2192 da803ff1 Helga Velroyen
  nodeinfo = lu.rpc.call_node_info(node_uuids, None, hypervisor_specs)
2193 1c3231aa Thomas Thrainer
  for node_uuid in node_uuids:
2194 1c3231aa Thomas Thrainer
    info = nodeinfo[node_uuid]
2195 1c3231aa Thomas Thrainer
    node_name = lu.cfg.GetNodeName(node_uuid)
2196 1c3231aa Thomas Thrainer
    info.Raise("Cannot get current information from node %s" % node_name,
2197 22b7f6f8 Thomas Thrainer
               prereq=True, ecode=errors.ECODE_ENVIRON)
2198 22b7f6f8 Thomas Thrainer
    (_, _, (hv_info, )) = info.payload
2199 22b7f6f8 Thomas Thrainer
    num_cpus = hv_info.get("cpu_total", None)
2200 22b7f6f8 Thomas Thrainer
    if not isinstance(num_cpus, int):
2201 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute the number of physical CPUs"
2202 22b7f6f8 Thomas Thrainer
                                 " on node %s, result was '%s'" %
2203 1c3231aa Thomas Thrainer
                                 (node_name, num_cpus), errors.ECODE_ENVIRON)
2204 22b7f6f8 Thomas Thrainer
    if requested > num_cpus:
2205 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Node %s has %s physical CPUs, but %s are "
2206 1c3231aa Thomas Thrainer
                                 "required" % (node_name, num_cpus, requested),
2207 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_NORES)
2208 22b7f6f8 Thomas Thrainer
2209 22b7f6f8 Thomas Thrainer
2210 22b7f6f8 Thomas Thrainer
def GetItemFromContainer(identifier, kind, container):
2211 22b7f6f8 Thomas Thrainer
  """Return the item refered by the identifier.
2212 22b7f6f8 Thomas Thrainer

2213 22b7f6f8 Thomas Thrainer
  @type identifier: string
2214 22b7f6f8 Thomas Thrainer
  @param identifier: Item index or name or UUID
2215 22b7f6f8 Thomas Thrainer
  @type kind: string
2216 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2217 22b7f6f8 Thomas Thrainer
  @type container: list
2218 22b7f6f8 Thomas Thrainer
  @param container: Container to get the item from
2219 22b7f6f8 Thomas Thrainer

2220 22b7f6f8 Thomas Thrainer
  """
2221 22b7f6f8 Thomas Thrainer
  # Index
2222 22b7f6f8 Thomas Thrainer
  try:
2223 22b7f6f8 Thomas Thrainer
    idx = int(identifier)
2224 22b7f6f8 Thomas Thrainer
    if idx == -1:
2225 22b7f6f8 Thomas Thrainer
      # Append
2226 22b7f6f8 Thomas Thrainer
      absidx = len(container) - 1
2227 22b7f6f8 Thomas Thrainer
    elif idx < 0:
2228 22b7f6f8 Thomas Thrainer
      raise IndexError("Not accepting negative indices other than -1")
2229 22b7f6f8 Thomas Thrainer
    elif idx > len(container):
2230 22b7f6f8 Thomas Thrainer
      raise IndexError("Got %s index %s, but there are only %s" %
2231 22b7f6f8 Thomas Thrainer
                       (kind, idx, len(container)))
2232 22b7f6f8 Thomas Thrainer
    else:
2233 22b7f6f8 Thomas Thrainer
      absidx = idx
2234 22b7f6f8 Thomas Thrainer
    return (absidx, container[idx])
2235 22b7f6f8 Thomas Thrainer
  except ValueError:
2236 22b7f6f8 Thomas Thrainer
    pass
2237 22b7f6f8 Thomas Thrainer
2238 22b7f6f8 Thomas Thrainer
  for idx, item in enumerate(container):
2239 22b7f6f8 Thomas Thrainer
    if item.uuid == identifier or item.name == identifier:
2240 22b7f6f8 Thomas Thrainer
      return (idx, item)
2241 22b7f6f8 Thomas Thrainer
2242 22b7f6f8 Thomas Thrainer
  raise errors.OpPrereqError("Cannot find %s with identifier %s" %
2243 22b7f6f8 Thomas Thrainer
                             (kind, identifier), errors.ECODE_NOENT)
2244 22b7f6f8 Thomas Thrainer
2245 22b7f6f8 Thomas Thrainer
2246 5eacbcae Thomas Thrainer
def _ApplyContainerMods(kind, container, chgdesc, mods,
2247 922a9e65 Thomas Thrainer
                        create_fn, modify_fn, remove_fn,
2248 922a9e65 Thomas Thrainer
                        post_add_fn=None):
2249 22b7f6f8 Thomas Thrainer
  """Applies descriptions in C{mods} to C{container}.
2250 22b7f6f8 Thomas Thrainer

2251 22b7f6f8 Thomas Thrainer
  @type kind: string
2252 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2253 22b7f6f8 Thomas Thrainer
  @type container: list
2254 22b7f6f8 Thomas Thrainer
  @param container: Container to modify
2255 22b7f6f8 Thomas Thrainer
  @type chgdesc: None or list
2256 22b7f6f8 Thomas Thrainer
  @param chgdesc: List of applied changes
2257 22b7f6f8 Thomas Thrainer
  @type mods: list
2258 5eacbcae Thomas Thrainer
  @param mods: Modifications as returned by L{_PrepareContainerMods}
2259 22b7f6f8 Thomas Thrainer
  @type create_fn: callable
2260 22b7f6f8 Thomas Thrainer
  @param create_fn: Callback for creating a new item (L{constants.DDM_ADD});
2261 22b7f6f8 Thomas Thrainer
    receives absolute item index, parameters and private data object as added
2262 5eacbcae Thomas Thrainer
    by L{_PrepareContainerMods}, returns tuple containing new item and changes
2263 22b7f6f8 Thomas Thrainer
    as list
2264 22b7f6f8 Thomas Thrainer
  @type modify_fn: callable
2265 22b7f6f8 Thomas Thrainer
  @param modify_fn: Callback for modifying an existing item
2266 22b7f6f8 Thomas Thrainer
    (L{constants.DDM_MODIFY}); receives absolute item index, item, parameters
2267 5eacbcae Thomas Thrainer
    and private data object as added by L{_PrepareContainerMods}, returns
2268 22b7f6f8 Thomas Thrainer
    changes as list
2269 22b7f6f8 Thomas Thrainer
  @type remove_fn: callable
2270 22b7f6f8 Thomas Thrainer
  @param remove_fn: Callback on removing item; receives absolute item index,
2271 5eacbcae Thomas Thrainer
    item and private data object as added by L{_PrepareContainerMods}
2272 922a9e65 Thomas Thrainer
  @type post_add_fn: callable
2273 922a9e65 Thomas Thrainer
  @param post_add_fn: Callable for post-processing a newly created item after
2274 922a9e65 Thomas Thrainer
    it has been put into the container. It receives the index of the new item
2275 922a9e65 Thomas Thrainer
    and the new item as parameters.
2276 22b7f6f8 Thomas Thrainer

2277 22b7f6f8 Thomas Thrainer
  """
2278 22b7f6f8 Thomas Thrainer
  for (op, identifier, params, private) in mods:
2279 22b7f6f8 Thomas Thrainer
    changes = None
2280 22b7f6f8 Thomas Thrainer
2281 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2282 22b7f6f8 Thomas Thrainer
      # Calculate where item will be added
2283 22b7f6f8 Thomas Thrainer
      # When adding an item, identifier can only be an index
2284 22b7f6f8 Thomas Thrainer
      try:
2285 22b7f6f8 Thomas Thrainer
        idx = int(identifier)
2286 22b7f6f8 Thomas Thrainer
      except ValueError:
2287 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Only possitive integer or -1 is accepted as"
2288 22b7f6f8 Thomas Thrainer
                                   " identifier for %s" % constants.DDM_ADD,
2289 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2290 22b7f6f8 Thomas Thrainer
      if idx == -1:
2291 22b7f6f8 Thomas Thrainer
        addidx = len(container)
2292 22b7f6f8 Thomas Thrainer
      else:
2293 22b7f6f8 Thomas Thrainer
        if idx < 0:
2294 22b7f6f8 Thomas Thrainer
          raise IndexError("Not accepting negative indices other than -1")
2295 22b7f6f8 Thomas Thrainer
        elif idx > len(container):
2296 22b7f6f8 Thomas Thrainer
          raise IndexError("Got %s index %s, but there are only %s" %
2297 22b7f6f8 Thomas Thrainer
                           (kind, idx, len(container)))
2298 22b7f6f8 Thomas Thrainer
        addidx = idx
2299 22b7f6f8 Thomas Thrainer
2300 22b7f6f8 Thomas Thrainer
      if create_fn is None:
2301 22b7f6f8 Thomas Thrainer
        item = params
2302 22b7f6f8 Thomas Thrainer
      else:
2303 22b7f6f8 Thomas Thrainer
        (item, changes) = create_fn(addidx, params, private)
2304 22b7f6f8 Thomas Thrainer
2305 22b7f6f8 Thomas Thrainer
      if idx == -1:
2306 22b7f6f8 Thomas Thrainer
        container.append(item)
2307 22b7f6f8 Thomas Thrainer
      else:
2308 22b7f6f8 Thomas Thrainer
        assert idx >= 0
2309 22b7f6f8 Thomas Thrainer
        assert idx <= len(container)
2310 22b7f6f8 Thomas Thrainer
        # list.insert does so before the specified index
2311 22b7f6f8 Thomas Thrainer
        container.insert(idx, item)
2312 922a9e65 Thomas Thrainer
2313 922a9e65 Thomas Thrainer
      if post_add_fn is not None:
2314 922a9e65 Thomas Thrainer
        post_add_fn(addidx, item)
2315 922a9e65 Thomas Thrainer
2316 22b7f6f8 Thomas Thrainer
    else:
2317 22b7f6f8 Thomas Thrainer
      # Retrieve existing item
2318 22b7f6f8 Thomas Thrainer
      (absidx, item) = GetItemFromContainer(identifier, kind, container)
2319 22b7f6f8 Thomas Thrainer
2320 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2321 22b7f6f8 Thomas Thrainer
        assert not params
2322 22b7f6f8 Thomas Thrainer
2323 22b7f6f8 Thomas Thrainer
        changes = [("%s/%s" % (kind, absidx), "remove")]
2324 22b7f6f8 Thomas Thrainer
2325 e15a00dc Dimitris Aragiorgis
        if remove_fn is not None:
2326 e15a00dc Dimitris Aragiorgis
          msg = remove_fn(absidx, item, private)
2327 e15a00dc Dimitris Aragiorgis
          if msg:
2328 e15a00dc Dimitris Aragiorgis
            changes.append(("%s/%s" % (kind, absidx), msg))
2329 e15a00dc Dimitris Aragiorgis
2330 22b7f6f8 Thomas Thrainer
        assert container[absidx] == item
2331 22b7f6f8 Thomas Thrainer
        del container[absidx]
2332 22b7f6f8 Thomas Thrainer
      elif op == constants.DDM_MODIFY:
2333 22b7f6f8 Thomas Thrainer
        if modify_fn is not None:
2334 22b7f6f8 Thomas Thrainer
          changes = modify_fn(absidx, item, params, private)
2335 22b7f6f8 Thomas Thrainer
      else:
2336 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
2337 22b7f6f8 Thomas Thrainer
2338 22b7f6f8 Thomas Thrainer
    assert _TApplyContModsCbChanges(changes)
2339 22b7f6f8 Thomas Thrainer
2340 22b7f6f8 Thomas Thrainer
    if not (chgdesc is None or changes is None):
2341 22b7f6f8 Thomas Thrainer
      chgdesc.extend(changes)
2342 22b7f6f8 Thomas Thrainer
2343 22b7f6f8 Thomas Thrainer
2344 22b7f6f8 Thomas Thrainer
def _UpdateIvNames(base_index, disks):
2345 22b7f6f8 Thomas Thrainer
  """Updates the C{iv_name} attribute of disks.
2346 22b7f6f8 Thomas Thrainer

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

2349 22b7f6f8 Thomas Thrainer
  """
2350 22b7f6f8 Thomas Thrainer
  for (idx, disk) in enumerate(disks):
2351 22b7f6f8 Thomas Thrainer
    disk.iv_name = "disk/%s" % (base_index + idx, )
2352 22b7f6f8 Thomas Thrainer
2353 22b7f6f8 Thomas Thrainer
2354 22b7f6f8 Thomas Thrainer
class LUInstanceSetParams(LogicalUnit):
2355 22b7f6f8 Thomas Thrainer
  """Modifies an instances's parameters.
2356 22b7f6f8 Thomas Thrainer

2357 22b7f6f8 Thomas Thrainer
  """
2358 22b7f6f8 Thomas Thrainer
  HPATH = "instance-modify"
2359 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2360 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2361 22b7f6f8 Thomas Thrainer
2362 22b7f6f8 Thomas Thrainer
  @staticmethod
2363 22b7f6f8 Thomas Thrainer
  def _UpgradeDiskNicMods(kind, mods, verify_fn):
2364 22b7f6f8 Thomas Thrainer
    assert ht.TList(mods)
2365 22b7f6f8 Thomas Thrainer
    assert not mods or len(mods[0]) in (2, 3)
2366 22b7f6f8 Thomas Thrainer
2367 22b7f6f8 Thomas Thrainer
    if mods and len(mods[0]) == 2:
2368 22b7f6f8 Thomas Thrainer
      result = []
2369 22b7f6f8 Thomas Thrainer
2370 22b7f6f8 Thomas Thrainer
      addremove = 0
2371 22b7f6f8 Thomas Thrainer
      for op, params in mods:
2372 22b7f6f8 Thomas Thrainer
        if op in (constants.DDM_ADD, constants.DDM_REMOVE):
2373 22b7f6f8 Thomas Thrainer
          result.append((op, -1, params))
2374 22b7f6f8 Thomas Thrainer
          addremove += 1
2375 22b7f6f8 Thomas Thrainer
2376 22b7f6f8 Thomas Thrainer
          if addremove > 1:
2377 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Only one %s add or remove operation is"
2378 22b7f6f8 Thomas Thrainer
                                       " supported at a time" % kind,
2379 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
2380 22b7f6f8 Thomas Thrainer
        else:
2381 22b7f6f8 Thomas Thrainer
          result.append((constants.DDM_MODIFY, op, params))
2382 22b7f6f8 Thomas Thrainer
2383 22b7f6f8 Thomas Thrainer
      assert verify_fn(result)
2384 22b7f6f8 Thomas Thrainer
    else:
2385 22b7f6f8 Thomas Thrainer
      result = mods
2386 22b7f6f8 Thomas Thrainer
2387 22b7f6f8 Thomas Thrainer
    return result
2388 22b7f6f8 Thomas Thrainer
2389 22b7f6f8 Thomas Thrainer
  @staticmethod
2390 22b7f6f8 Thomas Thrainer
  def _CheckMods(kind, mods, key_types, item_fn):
2391 22b7f6f8 Thomas Thrainer
    """Ensures requested disk/NIC modifications are valid.
2392 22b7f6f8 Thomas Thrainer

2393 22b7f6f8 Thomas Thrainer
    """
2394 22b7f6f8 Thomas Thrainer
    for (op, _, params) in mods:
2395 22b7f6f8 Thomas Thrainer
      assert ht.TDict(params)
2396 22b7f6f8 Thomas Thrainer
2397 22b7f6f8 Thomas Thrainer
      # If 'key_types' is an empty dict, we assume we have an
2398 22b7f6f8 Thomas Thrainer
      # 'ext' template and thus do not ForceDictType
2399 22b7f6f8 Thomas Thrainer
      if key_types:
2400 22b7f6f8 Thomas Thrainer
        utils.ForceDictType(params, key_types)
2401 22b7f6f8 Thomas Thrainer
2402 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2403 22b7f6f8 Thomas Thrainer
        if params:
2404 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("No settings should be passed when"
2405 22b7f6f8 Thomas Thrainer
                                     " removing a %s" % kind,
2406 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2407 22b7f6f8 Thomas Thrainer
      elif op in (constants.DDM_ADD, constants.DDM_MODIFY):
2408 22b7f6f8 Thomas Thrainer
        item_fn(op, params)
2409 22b7f6f8 Thomas Thrainer
      else:
2410 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
2411 22b7f6f8 Thomas Thrainer
2412 affe1792 Klaus Aehlig
  def _VerifyDiskModification(self, op, params, excl_stor):
2413 22b7f6f8 Thomas Thrainer
    """Verifies a disk modification.
2414 22b7f6f8 Thomas Thrainer

2415 22b7f6f8 Thomas Thrainer
    """
2416 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2417 22b7f6f8 Thomas Thrainer
      mode = params.setdefault(constants.IDISK_MODE, constants.DISK_RDWR)
2418 22b7f6f8 Thomas Thrainer
      if mode not in constants.DISK_ACCESS_SET:
2419 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk access mode '%s'" % mode,
2420 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2421 22b7f6f8 Thomas Thrainer
2422 22b7f6f8 Thomas Thrainer
      size = params.get(constants.IDISK_SIZE, None)
2423 22b7f6f8 Thomas Thrainer
      if size is None:
2424 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Required disk parameter '%s' missing" %
2425 22b7f6f8 Thomas Thrainer
                                   constants.IDISK_SIZE, errors.ECODE_INVAL)
2426 34956ece Thomas Thrainer
      size = int(size)
2427 22b7f6f8 Thomas Thrainer
2428 22b7f6f8 Thomas Thrainer
      params[constants.IDISK_SIZE] = size
2429 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2430 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2431 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2432 22b7f6f8 Thomas Thrainer
2433 7c848a6a Bernardo Dal Seno
      CheckSpindlesExclusiveStorage(params, excl_stor, True)
2434 3f3ea14c Bernardo Dal Seno
2435 22b7f6f8 Thomas Thrainer
    elif op == constants.DDM_MODIFY:
2436 22b7f6f8 Thomas Thrainer
      if constants.IDISK_SIZE in params:
2437 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk size change not possible, use"
2438 22b7f6f8 Thomas Thrainer
                                   " grow-disk", errors.ECODE_INVAL)
2439 c5c72215 Dimitris Aragiorgis
2440 c5c72215 Dimitris Aragiorgis
      # Disk modification supports changing only the disk name and mode.
2441 c5c72215 Dimitris Aragiorgis
      # Changing arbitrary parameters is allowed only for ext disk template",
2442 c5c72215 Dimitris Aragiorgis
      if self.instance.disk_template != constants.DT_EXT:
2443 c5c72215 Dimitris Aragiorgis
        utils.ForceDictType(params, constants.MODIFIABLE_IDISK_PARAMS_TYPES)
2444 c5c72215 Dimitris Aragiorgis
2445 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2446 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2447 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2448 22b7f6f8 Thomas Thrainer
2449 22b7f6f8 Thomas Thrainer
  @staticmethod
2450 22b7f6f8 Thomas Thrainer
  def _VerifyNicModification(op, params):
2451 22b7f6f8 Thomas Thrainer
    """Verifies a network interface modification.
2452 22b7f6f8 Thomas Thrainer

2453 22b7f6f8 Thomas Thrainer
    """
2454 22b7f6f8 Thomas Thrainer
    if op in (constants.DDM_ADD, constants.DDM_MODIFY):
2455 22b7f6f8 Thomas Thrainer
      ip = params.get(constants.INIC_IP, None)
2456 22b7f6f8 Thomas Thrainer
      name = params.get(constants.INIC_NAME, None)
2457 22b7f6f8 Thomas Thrainer
      req_net = params.get(constants.INIC_NETWORK, None)
2458 22b7f6f8 Thomas Thrainer
      link = params.get(constants.NIC_LINK, None)
2459 22b7f6f8 Thomas Thrainer
      mode = params.get(constants.NIC_MODE, None)
2460 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2461 22b7f6f8 Thomas Thrainer
        params[constants.INIC_NAME] = None
2462 22b7f6f8 Thomas Thrainer
      if req_net is not None:
2463 22b7f6f8 Thomas Thrainer
        if req_net.lower() == constants.VALUE_NONE:
2464 22b7f6f8 Thomas Thrainer
          params[constants.INIC_NETWORK] = None
2465 22b7f6f8 Thomas Thrainer
          req_net = None
2466 22b7f6f8 Thomas Thrainer
        elif link is not None or mode is not None:
2467 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("If network is given"
2468 22b7f6f8 Thomas Thrainer
                                     " mode or link should not",
2469 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2470 22b7f6f8 Thomas Thrainer
2471 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_ADD:
2472 22b7f6f8 Thomas Thrainer
        macaddr = params.get(constants.INIC_MAC, None)
2473 22b7f6f8 Thomas Thrainer
        if macaddr is None:
2474 22b7f6f8 Thomas Thrainer
          params[constants.INIC_MAC] = constants.VALUE_AUTO
2475 22b7f6f8 Thomas Thrainer
2476 22b7f6f8 Thomas Thrainer
      if ip is not None:
2477 22b7f6f8 Thomas Thrainer
        if ip.lower() == constants.VALUE_NONE:
2478 22b7f6f8 Thomas Thrainer
          params[constants.INIC_IP] = None
2479 22b7f6f8 Thomas Thrainer
        else:
2480 22b7f6f8 Thomas Thrainer
          if ip.lower() == constants.NIC_IP_POOL:
2481 22b7f6f8 Thomas Thrainer
            if op == constants.DDM_ADD and req_net is None:
2482 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("If ip=pool, parameter network"
2483 22b7f6f8 Thomas Thrainer
                                         " cannot be none",
2484 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_INVAL)
2485 22b7f6f8 Thomas Thrainer
          else:
2486 22b7f6f8 Thomas Thrainer
            if not netutils.IPAddress.IsValid(ip):
2487 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("Invalid IP address '%s'" % ip,
2488 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_INVAL)
2489 22b7f6f8 Thomas Thrainer
2490 22b7f6f8 Thomas Thrainer
      if constants.INIC_MAC in params:
2491 22b7f6f8 Thomas Thrainer
        macaddr = params[constants.INIC_MAC]
2492 22b7f6f8 Thomas Thrainer
        if macaddr not in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
2493 22b7f6f8 Thomas Thrainer
          macaddr = utils.NormalizeAndValidateMac(macaddr)
2494 22b7f6f8 Thomas Thrainer
2495 22b7f6f8 Thomas Thrainer
        if op == constants.DDM_MODIFY and macaddr == constants.VALUE_AUTO:
2496 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("'auto' is not a valid MAC address when"
2497 22b7f6f8 Thomas Thrainer
                                     " modifying an existing NIC",
2498 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2499 22b7f6f8 Thomas Thrainer
2500 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
2501 22b7f6f8 Thomas Thrainer
    if not (self.op.nics or self.op.disks or self.op.disk_template or
2502 22b7f6f8 Thomas Thrainer
            self.op.hvparams or self.op.beparams or self.op.os_name or
2503 5eae613c Thomas Thrainer
            self.op.osparams or self.op.offline is not None or
2504 1a182390 Santi Raffa
            self.op.runtime_mem or self.op.pnode or self.op.osparams_private):
2505 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("No changes submitted", errors.ECODE_INVAL)
2506 22b7f6f8 Thomas Thrainer
2507 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
2508 5eacbcae Thomas Thrainer
      CheckParamsNotGlobal(self.op.hvparams, constants.HVC_GLOBALS,
2509 5eacbcae Thomas Thrainer
                           "hypervisor", "instance", "cluster")
2510 22b7f6f8 Thomas Thrainer
2511 22b7f6f8 Thomas Thrainer
    self.op.disks = self._UpgradeDiskNicMods(
2512 72cd5493 Jose A. Lopes
      "disk", self.op.disks, ht.TSetParamsMods(ht.TIDiskParams))
2513 22b7f6f8 Thomas Thrainer
    self.op.nics = self._UpgradeDiskNicMods(
2514 72cd5493 Jose A. Lopes
      "NIC", self.op.nics, ht.TSetParamsMods(ht.TINicParams))
2515 22b7f6f8 Thomas Thrainer
2516 22b7f6f8 Thomas Thrainer
    if self.op.disks and self.op.disk_template is not None:
2517 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk template conversion and other disk"
2518 22b7f6f8 Thomas Thrainer
                                 " changes not supported at the same time",
2519 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2520 22b7f6f8 Thomas Thrainer
2521 22b7f6f8 Thomas Thrainer
    if (self.op.disk_template and
2522 22b7f6f8 Thomas Thrainer
        self.op.disk_template in constants.DTS_INT_MIRROR and
2523 22b7f6f8 Thomas Thrainer
        self.op.remote_node is None):
2524 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Changing the disk template to a mirrored"
2525 22b7f6f8 Thomas Thrainer
                                 " one requires specifying a secondary node",
2526 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2527 22b7f6f8 Thomas Thrainer
2528 22b7f6f8 Thomas Thrainer
    # Check NIC modifications
2529 22b7f6f8 Thomas Thrainer
    self._CheckMods("NIC", self.op.nics, constants.INIC_PARAMS_TYPES,
2530 22b7f6f8 Thomas Thrainer
                    self._VerifyNicModification)
2531 22b7f6f8 Thomas Thrainer
2532 22b7f6f8 Thomas Thrainer
    if self.op.pnode:
2533 1c3231aa Thomas Thrainer
      (self.op.pnode_uuid, self.op.pnode) = \
2534 1c3231aa Thomas Thrainer
        ExpandNodeUuidAndName(self.cfg, self.op.pnode_uuid, self.op.pnode)
2535 22b7f6f8 Thomas Thrainer
2536 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2537 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2538 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODEGROUP] = []
2539 22b7f6f8 Thomas Thrainer
    # Can't even acquire node locks in shared mode as upcoming changes in
2540 22b7f6f8 Thomas Thrainer
    # Ganeti 2.6 will start to modify the node object on disk conversion
2541 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
2542 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
2543 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
2544 22b7f6f8 Thomas Thrainer
    # Look node group to look up the ipolicy
2545 22b7f6f8 Thomas Thrainer
    self.share_locks[locking.LEVEL_NODEGROUP] = 1
2546 22b7f6f8 Thomas Thrainer
2547 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
2548 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
2549 22b7f6f8 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
2550 22b7f6f8 Thomas Thrainer
      # Acquire locks for the instance's nodegroups optimistically. Needs
2551 22b7f6f8 Thomas Thrainer
      # to be verified in CheckPrereq
2552 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = \
2553 da4a52a3 Thomas Thrainer
        self.cfg.GetInstanceNodeGroups(self.op.instance_uuid)
2554 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE:
2555 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
2556 22b7f6f8 Thomas Thrainer
      if self.op.disk_template and self.op.remote_node:
2557 1c3231aa Thomas Thrainer
        (self.op.remote_node_uuid, self.op.remote_node) = \
2558 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, self.op.remote_node_uuid,
2559 1c3231aa Thomas Thrainer
                                self.op.remote_node)
2560 1c3231aa Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE].append(self.op.remote_node_uuid)
2561 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES and self.op.disk_template:
2562 22b7f6f8 Thomas Thrainer
      # Copy node locks
2563 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
2564 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
2565 22b7f6f8 Thomas Thrainer
2566 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
2567 22b7f6f8 Thomas Thrainer
    """Build hooks env.
2568 22b7f6f8 Thomas Thrainer

2569 22b7f6f8 Thomas Thrainer
    This runs on the master, primary and secondaries.
2570 22b7f6f8 Thomas Thrainer

2571 22b7f6f8 Thomas Thrainer
    """
2572 22b7f6f8 Thomas Thrainer
    args = {}
2573 22b7f6f8 Thomas Thrainer
    if constants.BE_MINMEM in self.be_new:
2574 22b7f6f8 Thomas Thrainer
      args["minmem"] = self.be_new[constants.BE_MINMEM]
2575 22b7f6f8 Thomas Thrainer
    if constants.BE_MAXMEM in self.be_new:
2576 22b7f6f8 Thomas Thrainer
      args["maxmem"] = self.be_new[constants.BE_MAXMEM]
2577 22b7f6f8 Thomas Thrainer
    if constants.BE_VCPUS in self.be_new:
2578 22b7f6f8 Thomas Thrainer
      args["vcpus"] = self.be_new[constants.BE_VCPUS]
2579 22b7f6f8 Thomas Thrainer
    # TODO: export disk changes. Note: _BuildInstanceHookEnv* don't export disk
2580 22b7f6f8 Thomas Thrainer
    # information at all.
2581 22b7f6f8 Thomas Thrainer
2582 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
2583 22b7f6f8 Thomas Thrainer
      nics = []
2584 22b7f6f8 Thomas Thrainer
2585 22b7f6f8 Thomas Thrainer
      for nic in self._new_nics:
2586 22b7f6f8 Thomas Thrainer
        n = copy.deepcopy(nic)
2587 22b7f6f8 Thomas Thrainer
        nicparams = self.cluster.SimpleFillNIC(n.nicparams)
2588 22b7f6f8 Thomas Thrainer
        n.nicparams = nicparams
2589 5eacbcae Thomas Thrainer
        nics.append(NICToTuple(self, n))
2590 22b7f6f8 Thomas Thrainer
2591 22b7f6f8 Thomas Thrainer
      args["nics"] = nics
2592 22b7f6f8 Thomas Thrainer
2593 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance, override=args)
2594 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
2595 22b7f6f8 Thomas Thrainer
      env["NEW_DISK_TEMPLATE"] = self.op.disk_template
2596 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
2597 22b7f6f8 Thomas Thrainer
      env["RUNTIME_MEMORY"] = self.op.runtime_mem
2598 22b7f6f8 Thomas Thrainer
2599 22b7f6f8 Thomas Thrainer
    return env
2600 22b7f6f8 Thomas Thrainer
2601 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2602 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2603 22b7f6f8 Thomas Thrainer

2604 22b7f6f8 Thomas Thrainer
    """
2605 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
2606 22b7f6f8 Thomas Thrainer
    return (nl, nl)
2607 22b7f6f8 Thomas Thrainer
2608 22b7f6f8 Thomas Thrainer
  def _PrepareNicModification(self, params, private, old_ip, old_net_uuid,
2609 1c3231aa Thomas Thrainer
                              old_params, cluster, pnode_uuid):
2610 22b7f6f8 Thomas Thrainer
2611 22b7f6f8 Thomas Thrainer
    update_params_dict = dict([(key, params[key])
2612 22b7f6f8 Thomas Thrainer
                               for key in constants.NICS_PARAMETERS
2613 22b7f6f8 Thomas Thrainer
                               if key in params])
2614 22b7f6f8 Thomas Thrainer
2615 22b7f6f8 Thomas Thrainer
    req_link = update_params_dict.get(constants.NIC_LINK, None)
2616 22b7f6f8 Thomas Thrainer
    req_mode = update_params_dict.get(constants.NIC_MODE, None)
2617 22b7f6f8 Thomas Thrainer
2618 22b7f6f8 Thomas Thrainer
    new_net_uuid = None
2619 22b7f6f8 Thomas Thrainer
    new_net_uuid_or_name = params.get(constants.INIC_NETWORK, old_net_uuid)
2620 22b7f6f8 Thomas Thrainer
    if new_net_uuid_or_name:
2621 22b7f6f8 Thomas Thrainer
      new_net_uuid = self.cfg.LookupNetwork(new_net_uuid_or_name)
2622 22b7f6f8 Thomas Thrainer
      new_net_obj = self.cfg.GetNetwork(new_net_uuid)
2623 22b7f6f8 Thomas Thrainer
2624 22b7f6f8 Thomas Thrainer
    if old_net_uuid:
2625 22b7f6f8 Thomas Thrainer
      old_net_obj = self.cfg.GetNetwork(old_net_uuid)
2626 22b7f6f8 Thomas Thrainer
2627 22b7f6f8 Thomas Thrainer
    if new_net_uuid:
2628 1c3231aa Thomas Thrainer
      netparams = self.cfg.GetGroupNetParams(new_net_uuid, pnode_uuid)
2629 22b7f6f8 Thomas Thrainer
      if not netparams:
2630 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No netparams found for the network"
2631 22b7f6f8 Thomas Thrainer
                                   " %s, probably not connected" %
2632 22b7f6f8 Thomas Thrainer
                                   new_net_obj.name, errors.ECODE_INVAL)
2633 22b7f6f8 Thomas Thrainer
      new_params = dict(netparams)
2634 22b7f6f8 Thomas Thrainer
    else:
2635 5eacbcae Thomas Thrainer
      new_params = GetUpdatedParams(old_params, update_params_dict)
2636 22b7f6f8 Thomas Thrainer
2637 22b7f6f8 Thomas Thrainer
    utils.ForceDictType(new_params, constants.NICS_PARAMETER_TYPES)
2638 22b7f6f8 Thomas Thrainer
2639 22b7f6f8 Thomas Thrainer
    new_filled_params = cluster.SimpleFillNIC(new_params)
2640 22b7f6f8 Thomas Thrainer
    objects.NIC.CheckParameterSyntax(new_filled_params)
2641 22b7f6f8 Thomas Thrainer
2642 22b7f6f8 Thomas Thrainer
    new_mode = new_filled_params[constants.NIC_MODE]
2643 22b7f6f8 Thomas Thrainer
    if new_mode == constants.NIC_MODE_BRIDGED:
2644 22b7f6f8 Thomas Thrainer
      bridge = new_filled_params[constants.NIC_LINK]
2645 1c3231aa Thomas Thrainer
      msg = self.rpc.call_bridges_exist(pnode_uuid, [bridge]).fail_msg
2646 22b7f6f8 Thomas Thrainer
      if msg:
2647 1c3231aa Thomas Thrainer
        msg = "Error checking bridges on node '%s': %s" % \
2648 1c3231aa Thomas Thrainer
                (self.cfg.GetNodeName(pnode_uuid), msg)
2649 22b7f6f8 Thomas Thrainer
        if self.op.force:
2650 22b7f6f8 Thomas Thrainer
          self.warn.append(msg)
2651 22b7f6f8 Thomas Thrainer
        else:
2652 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError(msg, errors.ECODE_ENVIRON)
2653 22b7f6f8 Thomas Thrainer
2654 22b7f6f8 Thomas Thrainer
    elif new_mode == constants.NIC_MODE_ROUTED:
2655 22b7f6f8 Thomas Thrainer
      ip = params.get(constants.INIC_IP, old_ip)
2656 22b7f6f8 Thomas Thrainer
      if ip is None:
2657 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cannot set the NIC IP address to None"
2658 22b7f6f8 Thomas Thrainer
                                   " on a routed NIC", errors.ECODE_INVAL)
2659 22b7f6f8 Thomas Thrainer
2660 22b7f6f8 Thomas Thrainer
    elif new_mode == constants.NIC_MODE_OVS:
2661 22b7f6f8 Thomas Thrainer
      # TODO: check OVS link
2662 22b7f6f8 Thomas Thrainer
      self.LogInfo("OVS links are currently not checked for correctness")
2663 22b7f6f8 Thomas Thrainer
2664 22b7f6f8 Thomas Thrainer
    if constants.INIC_MAC in params:
2665 22b7f6f8 Thomas Thrainer
      mac = params[constants.INIC_MAC]
2666 22b7f6f8 Thomas Thrainer
      if mac is None:
2667 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cannot unset the NIC MAC address",
2668 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2669 22b7f6f8 Thomas Thrainer
      elif mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
2670 22b7f6f8 Thomas Thrainer
        # otherwise generate the MAC address
2671 22b7f6f8 Thomas Thrainer
        params[constants.INIC_MAC] = \
2672 22b7f6f8 Thomas Thrainer
          self.cfg.GenerateMAC(new_net_uuid, self.proc.GetECId())
2673 22b7f6f8 Thomas Thrainer
      else:
2674 22b7f6f8 Thomas Thrainer
        # or validate/reserve the current one
2675 22b7f6f8 Thomas Thrainer
        try:
2676 22b7f6f8 Thomas Thrainer
          self.cfg.ReserveMAC(mac, self.proc.GetECId())
2677 22b7f6f8 Thomas Thrainer
        except errors.ReservationError:
2678 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("MAC address '%s' already in use"
2679 22b7f6f8 Thomas Thrainer
                                     " in cluster" % mac,
2680 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_NOTUNIQUE)
2681 22b7f6f8 Thomas Thrainer
    elif new_net_uuid != old_net_uuid:
2682 22b7f6f8 Thomas Thrainer
2683 22b7f6f8 Thomas Thrainer
      def get_net_prefix(net_uuid):
2684 22b7f6f8 Thomas Thrainer
        mac_prefix = None
2685 22b7f6f8 Thomas Thrainer
        if net_uuid:
2686 22b7f6f8 Thomas Thrainer
          nobj = self.cfg.GetNetwork(net_uuid)
2687 22b7f6f8 Thomas Thrainer
          mac_prefix = nobj.mac_prefix
2688 22b7f6f8 Thomas Thrainer
2689 22b7f6f8 Thomas Thrainer
        return mac_prefix
2690 22b7f6f8 Thomas Thrainer
2691 22b7f6f8 Thomas Thrainer
      new_prefix = get_net_prefix(new_net_uuid)
2692 22b7f6f8 Thomas Thrainer
      old_prefix = get_net_prefix(old_net_uuid)
2693 22b7f6f8 Thomas Thrainer
      if old_prefix != new_prefix:
2694 22b7f6f8 Thomas Thrainer
        params[constants.INIC_MAC] = \
2695 22b7f6f8 Thomas Thrainer
          self.cfg.GenerateMAC(new_net_uuid, self.proc.GetECId())
2696 22b7f6f8 Thomas Thrainer
2697 22b7f6f8 Thomas Thrainer
    # if there is a change in (ip, network) tuple
2698 22b7f6f8 Thomas Thrainer
    new_ip = params.get(constants.INIC_IP, old_ip)
2699 22b7f6f8 Thomas Thrainer
    if (new_ip, new_net_uuid) != (old_ip, old_net_uuid):
2700 22b7f6f8 Thomas Thrainer
      if new_ip:
2701 22b7f6f8 Thomas Thrainer
        # if IP is pool then require a network and generate one IP
2702 22b7f6f8 Thomas Thrainer
        if new_ip.lower() == constants.NIC_IP_POOL:
2703 22b7f6f8 Thomas Thrainer
          if new_net_uuid:
2704 22b7f6f8 Thomas Thrainer
            try:
2705 22b7f6f8 Thomas Thrainer
              new_ip = self.cfg.GenerateIp(new_net_uuid, self.proc.GetECId())
2706 22b7f6f8 Thomas Thrainer
            except errors.ReservationError:
2707 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("Unable to get a free IP"
2708 22b7f6f8 Thomas Thrainer
                                         " from the address pool",
2709 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_STATE)
2710 22b7f6f8 Thomas Thrainer
            self.LogInfo("Chose IP %s from network %s",
2711 22b7f6f8 Thomas Thrainer
                         new_ip,
2712 22b7f6f8 Thomas Thrainer
                         new_net_obj.name)
2713 22b7f6f8 Thomas Thrainer
            params[constants.INIC_IP] = new_ip
2714 22b7f6f8 Thomas Thrainer
          else:
2715 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("ip=pool, but no network found",
2716 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
2717 22b7f6f8 Thomas Thrainer
        # Reserve new IP if in the new network if any
2718 22b7f6f8 Thomas Thrainer
        elif new_net_uuid:
2719 22b7f6f8 Thomas Thrainer
          try:
2720 031d2db1 Dimitris Aragiorgis
            self.cfg.ReserveIp(new_net_uuid, new_ip, self.proc.GetECId(),
2721 031d2db1 Dimitris Aragiorgis
                               check=self.op.conflicts_check)
2722 22b7f6f8 Thomas Thrainer
            self.LogInfo("Reserving IP %s in network %s",
2723 22b7f6f8 Thomas Thrainer
                         new_ip, new_net_obj.name)
2724 22b7f6f8 Thomas Thrainer
          except errors.ReservationError:
2725 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("IP %s not available in network %s" %
2726 22b7f6f8 Thomas Thrainer
                                       (new_ip, new_net_obj.name),
2727 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_NOTUNIQUE)
2728 22b7f6f8 Thomas Thrainer
        # new network is None so check if new IP is a conflicting IP
2729 22b7f6f8 Thomas Thrainer
        elif self.op.conflicts_check:
2730 1c3231aa Thomas Thrainer
          _CheckForConflictingIp(self, new_ip, pnode_uuid)
2731 22b7f6f8 Thomas Thrainer
2732 22b7f6f8 Thomas Thrainer
      # release old IP if old network is not None
2733 22b7f6f8 Thomas Thrainer
      if old_ip and old_net_uuid:
2734 22b7f6f8 Thomas Thrainer
        try:
2735 22b7f6f8 Thomas Thrainer
          self.cfg.ReleaseIp(old_net_uuid, old_ip, self.proc.GetECId())
2736 22b7f6f8 Thomas Thrainer
        except errors.AddressPoolError:
2737 22b7f6f8 Thomas Thrainer
          logging.warning("Release IP %s not contained in network %s",
2738 22b7f6f8 Thomas Thrainer
                          old_ip, old_net_obj.name)
2739 22b7f6f8 Thomas Thrainer
2740 22b7f6f8 Thomas Thrainer
    # there are no changes in (ip, network) tuple and old network is not None
2741 22b7f6f8 Thomas Thrainer
    elif (old_net_uuid is not None and
2742 22b7f6f8 Thomas Thrainer
          (req_link is not None or req_mode is not None)):
2743 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Not allowed to change link or mode of"
2744 22b7f6f8 Thomas Thrainer
                                 " a NIC that is connected to a network",
2745 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2746 22b7f6f8 Thomas Thrainer
2747 22b7f6f8 Thomas Thrainer
    private.params = new_params
2748 22b7f6f8 Thomas Thrainer
    private.filled = new_filled_params
2749 22b7f6f8 Thomas Thrainer
2750 22b7f6f8 Thomas Thrainer
  def _PreCheckDiskTemplate(self, pnode_info):
2751 22b7f6f8 Thomas Thrainer
    """CheckPrereq checks related to a new disk template."""
2752 22b7f6f8 Thomas Thrainer
    # Arguments are passed to avoid configuration lookups
2753 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
2754 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template == self.op.disk_template:
2755 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance already has disk template %s" %
2756 d0d7d7cf Thomas Thrainer
                                 self.instance.disk_template,
2757 d0d7d7cf Thomas Thrainer
                                 errors.ECODE_INVAL)
2758 22b7f6f8 Thomas Thrainer
2759 7bb0c47f Thomas Thrainer
    if not self.cluster.IsDiskTemplateEnabled(self.op.disk_template):
2760 9d276e93 Helga Velroyen
      raise errors.OpPrereqError("Disk template '%s' is not enabled for this"
2761 7bb0c47f Thomas Thrainer
                                 " cluster." % self.op.disk_template)
2762 9d276e93 Helga Velroyen
2763 d0d7d7cf Thomas Thrainer
    if (self.instance.disk_template,
2764 22b7f6f8 Thomas Thrainer
        self.op.disk_template) not in self._DISK_CONVERSIONS:
2765 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Unsupported disk template conversion from"
2766 d0d7d7cf Thomas Thrainer
                                 " %s to %s" % (self.instance.disk_template,
2767 22b7f6f8 Thomas Thrainer
                                                self.op.disk_template),
2768 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2769 d0d7d7cf Thomas Thrainer
    CheckInstanceState(self, self.instance, INSTANCE_DOWN,
2770 5eacbcae Thomas Thrainer
                       msg="cannot change disk template")
2771 22b7f6f8 Thomas Thrainer
    if self.op.disk_template in constants.DTS_INT_MIRROR:
2772 1c3231aa Thomas Thrainer
      if self.op.remote_node_uuid == pnode_uuid:
2773 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Given new secondary node %s is the same"
2774 22b7f6f8 Thomas Thrainer
                                   " as the primary node of the instance" %
2775 22b7f6f8 Thomas Thrainer
                                   self.op.remote_node, errors.ECODE_STATE)
2776 1c3231aa Thomas Thrainer
      CheckNodeOnline(self, self.op.remote_node_uuid)
2777 1c3231aa Thomas Thrainer
      CheckNodeNotDrained(self, self.op.remote_node_uuid)
2778 22b7f6f8 Thomas Thrainer
      # FIXME: here we assume that the old instance type is DT_PLAIN
2779 d0d7d7cf Thomas Thrainer
      assert self.instance.disk_template == constants.DT_PLAIN
2780 22b7f6f8 Thomas Thrainer
      disks = [{constants.IDISK_SIZE: d.size,
2781 22b7f6f8 Thomas Thrainer
                constants.IDISK_VG: d.logical_id[0]}
2782 d0d7d7cf Thomas Thrainer
               for d in self.instance.disks]
2783 5eacbcae Thomas Thrainer
      required = ComputeDiskSizePerVG(self.op.disk_template, disks)
2784 1c3231aa Thomas Thrainer
      CheckNodesFreeDiskPerVG(self, [self.op.remote_node_uuid], required)
2785 22b7f6f8 Thomas Thrainer
2786 1c3231aa Thomas Thrainer
      snode_info = self.cfg.GetNodeInfo(self.op.remote_node_uuid)
2787 22b7f6f8 Thomas Thrainer
      snode_group = self.cfg.GetNodeGroup(snode_info.group)
2788 d0d7d7cf Thomas Thrainer
      ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(self.cluster,
2789 22b7f6f8 Thomas Thrainer
                                                              snode_group)
2790 d0d7d7cf Thomas Thrainer
      CheckTargetNodeIPolicy(self, ipolicy, self.instance, snode_info, self.cfg,
2791 5eacbcae Thomas Thrainer
                             ignore=self.op.ignore_ipolicy)
2792 22b7f6f8 Thomas Thrainer
      if pnode_info.group != snode_info.group:
2793 22b7f6f8 Thomas Thrainer
        self.LogWarning("The primary and secondary nodes are in two"
2794 22b7f6f8 Thomas Thrainer
                        " different node groups; the disk parameters"
2795 22b7f6f8 Thomas Thrainer
                        " from the first disk's node group will be"
2796 22b7f6f8 Thomas Thrainer
                        " used")
2797 22b7f6f8 Thomas Thrainer
2798 22b7f6f8 Thomas Thrainer
    if not self.op.disk_template in constants.DTS_EXCL_STORAGE:
2799 22b7f6f8 Thomas Thrainer
      # Make sure none of the nodes require exclusive storage
2800 22b7f6f8 Thomas Thrainer
      nodes = [pnode_info]
2801 22b7f6f8 Thomas Thrainer
      if self.op.disk_template in constants.DTS_INT_MIRROR:
2802 22b7f6f8 Thomas Thrainer
        assert snode_info
2803 22b7f6f8 Thomas Thrainer
        nodes.append(snode_info)
2804 5eacbcae Thomas Thrainer
      has_es = lambda n: IsExclusiveStorageEnabledNode(self.cfg, n)
2805 22b7f6f8 Thomas Thrainer
      if compat.any(map(has_es, nodes)):
2806 22b7f6f8 Thomas Thrainer
        errmsg = ("Cannot convert disk template from %s to %s when exclusive"
2807 d0d7d7cf Thomas Thrainer
                  " storage is enabled" % (self.instance.disk_template,
2808 22b7f6f8 Thomas Thrainer
                                           self.op.disk_template))
2809 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError(errmsg, errors.ECODE_STATE)
2810 22b7f6f8 Thomas Thrainer
2811 1bb99a33 Bernardo Dal Seno
  def _PreCheckDisks(self, ispec):
2812 1bb99a33 Bernardo Dal Seno
    """CheckPrereq checks related to disk changes.
2813 22b7f6f8 Thomas Thrainer

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

2817 22b7f6f8 Thomas Thrainer
    """
2818 d0d7d7cf Thomas Thrainer
    self.diskparams = self.cfg.GetInstanceDiskParams(self.instance)
2819 22b7f6f8 Thomas Thrainer
2820 3f3ea14c Bernardo Dal Seno
    excl_stor = compat.any(
2821 d0d7d7cf Thomas Thrainer
      rpc.GetExclusiveStorageForNodes(self.cfg,
2822 d0d7d7cf Thomas Thrainer
                                      self.instance.all_nodes).values()
2823 3f3ea14c Bernardo Dal Seno
      )
2824 3f3ea14c Bernardo Dal Seno
2825 22b7f6f8 Thomas Thrainer
    # Check disk modifications. This is done here and not in CheckArguments
2826 22b7f6f8 Thomas Thrainer
    # (as with NICs), because we need to know the instance's disk template
2827 3f3ea14c Bernardo Dal Seno
    ver_fn = lambda op, par: self._VerifyDiskModification(op, par, excl_stor)
2828 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template == constants.DT_EXT:
2829 3f3ea14c Bernardo Dal Seno
      self._CheckMods("disk", self.op.disks, {}, ver_fn)
2830 22b7f6f8 Thomas Thrainer
    else:
2831 22b7f6f8 Thomas Thrainer
      self._CheckMods("disk", self.op.disks, constants.IDISK_PARAMS_TYPES,
2832 3f3ea14c Bernardo Dal Seno
                      ver_fn)
2833 22b7f6f8 Thomas Thrainer
2834 5eacbcae Thomas Thrainer
    self.diskmod = _PrepareContainerMods(self.op.disks, None)
2835 22b7f6f8 Thomas Thrainer
2836 22b7f6f8 Thomas Thrainer
    # Check the validity of the `provider' parameter
2837 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DT_EXT:
2838 22b7f6f8 Thomas Thrainer
      for mod in self.diskmod:
2839 22b7f6f8 Thomas Thrainer
        ext_provider = mod[2].get(constants.IDISK_PROVIDER, None)
2840 22b7f6f8 Thomas Thrainer
        if mod[0] == constants.DDM_ADD:
2841 22b7f6f8 Thomas Thrainer
          if ext_provider is None:
2842 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Instance template is '%s' and parameter"
2843 22b7f6f8 Thomas Thrainer
                                       " '%s' missing, during disk add" %
2844 22b7f6f8 Thomas Thrainer
                                       (constants.DT_EXT,
2845 22b7f6f8 Thomas Thrainer
                                        constants.IDISK_PROVIDER),
2846 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_NOENT)
2847 22b7f6f8 Thomas Thrainer
        elif mod[0] == constants.DDM_MODIFY:
2848 22b7f6f8 Thomas Thrainer
          if ext_provider:
2849 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Parameter '%s' is invalid during disk"
2850 22b7f6f8 Thomas Thrainer
                                       " modification" %
2851 22b7f6f8 Thomas Thrainer
                                       constants.IDISK_PROVIDER,
2852 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
2853 22b7f6f8 Thomas Thrainer
    else:
2854 22b7f6f8 Thomas Thrainer
      for mod in self.diskmod:
2855 22b7f6f8 Thomas Thrainer
        ext_provider = mod[2].get(constants.IDISK_PROVIDER, None)
2856 22b7f6f8 Thomas Thrainer
        if ext_provider is not None:
2857 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("Parameter '%s' is only valid for"
2858 22b7f6f8 Thomas Thrainer
                                     " instances of type '%s'" %
2859 22b7f6f8 Thomas Thrainer
                                     (constants.IDISK_PROVIDER,
2860 22b7f6f8 Thomas Thrainer
                                      constants.DT_EXT),
2861 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2862 22b7f6f8 Thomas Thrainer
2863 3c260845 Thomas Thrainer
    if not self.op.wait_for_sync and self.instance.disks_active:
2864 3c260845 Thomas Thrainer
      for mod in self.diskmod:
2865 3c260845 Thomas Thrainer
        if mod[0] == constants.DDM_ADD:
2866 3c260845 Thomas Thrainer
          raise errors.OpPrereqError("Can't add a disk to an instance with"
2867 3c260845 Thomas Thrainer
                                     " activated disks and"
2868 3c260845 Thomas Thrainer
                                     " --no-wait-for-sync given.",
2869 3c260845 Thomas Thrainer
                                     errors.ECODE_INVAL)
2870 3c260845 Thomas Thrainer
2871 d0d7d7cf Thomas Thrainer
    if self.op.disks and self.instance.disk_template == constants.DT_DISKLESS:
2872 1bb99a33 Bernardo Dal Seno
      raise errors.OpPrereqError("Disk operations not supported for"
2873 1bb99a33 Bernardo Dal Seno
                                 " diskless instances", errors.ECODE_INVAL)
2874 1bb99a33 Bernardo Dal Seno
2875 1bb99a33 Bernardo Dal Seno
    def _PrepareDiskMod(_, disk, params, __):
2876 1bb99a33 Bernardo Dal Seno
      disk.name = params.get(constants.IDISK_NAME, None)
2877 1bb99a33 Bernardo Dal Seno
2878 1bb99a33 Bernardo Dal Seno
    # Verify disk changes (operating on a copy)
2879 d0d7d7cf Thomas Thrainer
    disks = copy.deepcopy(self.instance.disks)
2880 1bb99a33 Bernardo Dal Seno
    _ApplyContainerMods("disk", disks, None, self.diskmod, None,
2881 1bb99a33 Bernardo Dal Seno
                        _PrepareDiskMod, None)
2882 1bb99a33 Bernardo Dal Seno
    utils.ValidateDeviceNames("disk", disks)
2883 1bb99a33 Bernardo Dal Seno
    if len(disks) > constants.MAX_DISKS:
2884 1bb99a33 Bernardo Dal Seno
      raise errors.OpPrereqError("Instance has too many disks (%d), cannot add"
2885 1bb99a33 Bernardo Dal Seno
                                 " more" % constants.MAX_DISKS,
2886 1bb99a33 Bernardo Dal Seno
                                 errors.ECODE_STATE)
2887 d0d7d7cf Thomas Thrainer
    disk_sizes = [disk.size for disk in self.instance.disks]
2888 1bb99a33 Bernardo Dal Seno
    disk_sizes.extend(params["size"] for (op, idx, params, private) in
2889 1bb99a33 Bernardo Dal Seno
                      self.diskmod if op == constants.DDM_ADD)
2890 1bb99a33 Bernardo Dal Seno
    ispec[constants.ISPEC_DISK_COUNT] = len(disk_sizes)
2891 1bb99a33 Bernardo Dal Seno
    ispec[constants.ISPEC_DISK_SIZE] = disk_sizes
2892 1bb99a33 Bernardo Dal Seno
2893 1bb99a33 Bernardo Dal Seno
    if self.op.offline is not None and self.op.offline:
2894 d0d7d7cf Thomas Thrainer
      CheckInstanceState(self, self.instance, CAN_CHANGE_INSTANCE_OFFLINE,
2895 1bb99a33 Bernardo Dal Seno
                         msg="can't change to offline")
2896 1bb99a33 Bernardo Dal Seno
2897 1bb99a33 Bernardo Dal Seno
  def CheckPrereq(self):
2898 1bb99a33 Bernardo Dal Seno
    """Check prerequisites.
2899 1bb99a33 Bernardo Dal Seno

2900 1bb99a33 Bernardo Dal Seno
    This only checks the instance list against the existing names.
2901 1bb99a33 Bernardo Dal Seno

2902 1bb99a33 Bernardo Dal Seno
    """
2903 1bb99a33 Bernardo Dal Seno
    assert self.op.instance_name in self.owned_locks(locking.LEVEL_INSTANCE)
2904 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
2905 d0d7d7cf Thomas Thrainer
    self.cluster = self.cfg.GetClusterInfo()
2906 9c8f7bf4 Helga Velroyen
    cluster_hvparams = self.cluster.hvparams[self.instance.hypervisor]
2907 1bb99a33 Bernardo Dal Seno
2908 1bb99a33 Bernardo Dal Seno
    assert self.instance is not None, \
2909 1bb99a33 Bernardo Dal Seno
      "Cannot retrieve locked instance %s" % self.op.instance_name
2910 1bb99a33 Bernardo Dal Seno
2911 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
2912 1bb99a33 Bernardo Dal Seno
2913 1bb99a33 Bernardo Dal Seno
    self.warn = []
2914 1bb99a33 Bernardo Dal Seno
2915 1c3231aa Thomas Thrainer
    if (self.op.pnode_uuid is not None and self.op.pnode_uuid != pnode_uuid and
2916 1bb99a33 Bernardo Dal Seno
        not self.op.force):
2917 1bb99a33 Bernardo Dal Seno
      # verify that the instance is not up
2918 0bbec3af Helga Velroyen
      instance_info = self.rpc.call_instance_info(
2919 d0d7d7cf Thomas Thrainer
          pnode_uuid, self.instance.name, self.instance.hypervisor,
2920 9c8f7bf4 Helga Velroyen
          cluster_hvparams)
2921 1bb99a33 Bernardo Dal Seno
      if instance_info.fail_msg:
2922 1bb99a33 Bernardo Dal Seno
        self.warn.append("Can't get instance runtime information: %s" %
2923 1bb99a33 Bernardo Dal Seno
                         instance_info.fail_msg)
2924 1bb99a33 Bernardo Dal Seno
      elif instance_info.payload:
2925 1c3231aa Thomas Thrainer
        raise errors.OpPrereqError("Instance is still running on %s" %
2926 1c3231aa Thomas Thrainer
                                   self.cfg.GetNodeName(pnode_uuid),
2927 1bb99a33 Bernardo Dal Seno
                                   errors.ECODE_STATE)
2928 1bb99a33 Bernardo Dal Seno
2929 1c3231aa Thomas Thrainer
    assert pnode_uuid in self.owned_locks(locking.LEVEL_NODE)
2930 d0d7d7cf Thomas Thrainer
    node_uuids = list(self.instance.all_nodes)
2931 1c3231aa Thomas Thrainer
    pnode_info = self.cfg.GetNodeInfo(pnode_uuid)
2932 1bb99a33 Bernardo Dal Seno
2933 1bb99a33 Bernardo Dal Seno
    #_CheckInstanceNodeGroups(self.cfg, self.op.instance_name, owned_groups)
2934 1bb99a33 Bernardo Dal Seno
    assert pnode_info.group in self.owned_locks(locking.LEVEL_NODEGROUP)
2935 1bb99a33 Bernardo Dal Seno
    group_info = self.cfg.GetNodeGroup(pnode_info.group)
2936 1bb99a33 Bernardo Dal Seno
2937 1bb99a33 Bernardo Dal Seno
    # dictionary with instance information after the modification
2938 1bb99a33 Bernardo Dal Seno
    ispec = {}
2939 1bb99a33 Bernardo Dal Seno
2940 96ed3a3e Dimitris Aragiorgis
    if self.op.hotplug or self.op.hotplug_if_possible:
2941 24711492 Dimitris Aragiorgis
      result = self.rpc.call_hotplug_supported(self.instance.primary_node,
2942 24711492 Dimitris Aragiorgis
                                               self.instance)
2943 96ed3a3e Dimitris Aragiorgis
      if result.fail_msg:
2944 96ed3a3e Dimitris Aragiorgis
        if self.op.hotplug:
2945 96ed3a3e Dimitris Aragiorgis
          result.Raise("Hotplug is not possible: %s" % result.fail_msg,
2946 96ed3a3e Dimitris Aragiorgis
                       prereq=True)
2947 96ed3a3e Dimitris Aragiorgis
        else:
2948 96ed3a3e Dimitris Aragiorgis
          self.LogWarning(result.fail_msg)
2949 96ed3a3e Dimitris Aragiorgis
          self.op.hotplug = False
2950 96ed3a3e Dimitris Aragiorgis
          self.LogInfo("Modification will take place without hotplugging.")
2951 96ed3a3e Dimitris Aragiorgis
      else:
2952 96ed3a3e Dimitris Aragiorgis
        self.op.hotplug = True
2953 24711492 Dimitris Aragiorgis
2954 1bb99a33 Bernardo Dal Seno
    # Prepare NIC modifications
2955 1bb99a33 Bernardo Dal Seno
    self.nicmod = _PrepareContainerMods(self.op.nics, _InstNicModPrivate)
2956 1bb99a33 Bernardo Dal Seno
2957 22b7f6f8 Thomas Thrainer
    # OS change
2958 22b7f6f8 Thomas Thrainer
    if self.op.os_name and not self.op.force:
2959 d0d7d7cf Thomas Thrainer
      CheckNodeHasOS(self, self.instance.primary_node, self.op.os_name,
2960 5eacbcae Thomas Thrainer
                     self.op.force_variant)
2961 22b7f6f8 Thomas Thrainer
      instance_os = self.op.os_name
2962 22b7f6f8 Thomas Thrainer
    else:
2963 d0d7d7cf Thomas Thrainer
      instance_os = self.instance.os
2964 22b7f6f8 Thomas Thrainer
2965 22b7f6f8 Thomas Thrainer
    assert not (self.op.disk_template and self.op.disks), \
2966 22b7f6f8 Thomas Thrainer
      "Can't modify disk template and apply disk changes at the same time"
2967 22b7f6f8 Thomas Thrainer
2968 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
2969 22b7f6f8 Thomas Thrainer
      self._PreCheckDiskTemplate(pnode_info)
2970 22b7f6f8 Thomas Thrainer
2971 1bb99a33 Bernardo Dal Seno
    self._PreCheckDisks(ispec)
2972 1bb99a33 Bernardo Dal Seno
2973 22b7f6f8 Thomas Thrainer
    # hvparams processing
2974 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
2975 d0d7d7cf Thomas Thrainer
      hv_type = self.instance.hypervisor
2976 d0d7d7cf Thomas Thrainer
      i_hvdict = GetUpdatedParams(self.instance.hvparams, self.op.hvparams)
2977 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(i_hvdict, constants.HVS_PARAMETER_TYPES)
2978 d0d7d7cf Thomas Thrainer
      hv_new = self.cluster.SimpleFillHV(hv_type, self.instance.os, i_hvdict)
2979 22b7f6f8 Thomas Thrainer
2980 22b7f6f8 Thomas Thrainer
      # local check
2981 22b7f6f8 Thomas Thrainer
      hypervisor.GetHypervisorClass(hv_type).CheckParameterSyntax(hv_new)
2982 d0d7d7cf Thomas Thrainer
      CheckHVParams(self, node_uuids, self.instance.hypervisor, hv_new)
2983 22b7f6f8 Thomas Thrainer
      self.hv_proposed = self.hv_new = hv_new # the new actual values
2984 22b7f6f8 Thomas Thrainer
      self.hv_inst = i_hvdict # the new dict (without defaults)
2985 22b7f6f8 Thomas Thrainer
    else:
2986 d0d7d7cf Thomas Thrainer
      self.hv_proposed = self.cluster.SimpleFillHV(self.instance.hypervisor,
2987 d0d7d7cf Thomas Thrainer
                                                   self.instance.os,
2988 d0d7d7cf Thomas Thrainer
                                                   self.instance.hvparams)
2989 22b7f6f8 Thomas Thrainer
      self.hv_new = self.hv_inst = {}
2990 22b7f6f8 Thomas Thrainer
2991 22b7f6f8 Thomas Thrainer
    # beparams processing
2992 22b7f6f8 Thomas Thrainer
    if self.op.beparams:
2993 d0d7d7cf Thomas Thrainer
      i_bedict = GetUpdatedParams(self.instance.beparams, self.op.beparams,
2994 5eacbcae Thomas Thrainer
                                  use_none=True)
2995 22b7f6f8 Thomas Thrainer
      objects.UpgradeBeParams(i_bedict)
2996 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(i_bedict, constants.BES_PARAMETER_TYPES)
2997 d0d7d7cf Thomas Thrainer
      be_new = self.cluster.SimpleFillBE(i_bedict)
2998 22b7f6f8 Thomas Thrainer
      self.be_proposed = self.be_new = be_new # the new actual values
2999 22b7f6f8 Thomas Thrainer
      self.be_inst = i_bedict # the new dict (without defaults)
3000 22b7f6f8 Thomas Thrainer
    else:
3001 22b7f6f8 Thomas Thrainer
      self.be_new = self.be_inst = {}
3002 d0d7d7cf Thomas Thrainer
      self.be_proposed = self.cluster.SimpleFillBE(self.instance.beparams)
3003 d0d7d7cf Thomas Thrainer
    be_old = self.cluster.FillBE(self.instance)
3004 22b7f6f8 Thomas Thrainer
3005 22b7f6f8 Thomas Thrainer
    # CPU param validation -- checking every time a parameter is
3006 22b7f6f8 Thomas Thrainer
    # changed to cover all cases where either CPU mask or vcpus have
3007 22b7f6f8 Thomas Thrainer
    # changed
3008 22b7f6f8 Thomas Thrainer
    if (constants.BE_VCPUS in self.be_proposed and
3009 22b7f6f8 Thomas Thrainer
        constants.HV_CPU_MASK in self.hv_proposed):
3010 22b7f6f8 Thomas Thrainer
      cpu_list = \
3011 22b7f6f8 Thomas Thrainer
        utils.ParseMultiCpuMask(self.hv_proposed[constants.HV_CPU_MASK])
3012 22b7f6f8 Thomas Thrainer
      # Verify mask is consistent with number of vCPUs. Can skip this
3013 22b7f6f8 Thomas Thrainer
      # test if only 1 entry in the CPU mask, which means same mask
3014 22b7f6f8 Thomas Thrainer
      # is applied to all vCPUs.
3015 22b7f6f8 Thomas Thrainer
      if (len(cpu_list) > 1 and
3016 22b7f6f8 Thomas Thrainer
          len(cpu_list) != self.be_proposed[constants.BE_VCPUS]):
3017 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Number of vCPUs [%d] does not match the"
3018 22b7f6f8 Thomas Thrainer
                                   " CPU mask [%s]" %
3019 22b7f6f8 Thomas Thrainer
                                   (self.be_proposed[constants.BE_VCPUS],
3020 22b7f6f8 Thomas Thrainer
                                    self.hv_proposed[constants.HV_CPU_MASK]),
3021 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3022 22b7f6f8 Thomas Thrainer
3023 22b7f6f8 Thomas Thrainer
      # Only perform this test if a new CPU mask is given
3024 22b7f6f8 Thomas Thrainer
      if constants.HV_CPU_MASK in self.hv_new:
3025 22b7f6f8 Thomas Thrainer
        # Calculate the largest CPU number requested
3026 22b7f6f8 Thomas Thrainer
        max_requested_cpu = max(map(max, cpu_list))
3027 22b7f6f8 Thomas Thrainer
        # Check that all of the instance's nodes have enough physical CPUs to
3028 22b7f6f8 Thomas Thrainer
        # satisfy the requested CPU mask
3029 d0d7d7cf Thomas Thrainer
        hvspecs = [(self.instance.hypervisor,
3030 d0d7d7cf Thomas Thrainer
                    self.cfg.GetClusterInfo()
3031 d0d7d7cf Thomas Thrainer
                      .hvparams[self.instance.hypervisor])]
3032 d0d7d7cf Thomas Thrainer
        _CheckNodesPhysicalCPUs(self, self.instance.all_nodes,
3033 a295eb80 Helga Velroyen
                                max_requested_cpu + 1,
3034 a295eb80 Helga Velroyen
                                hvspecs)
3035 22b7f6f8 Thomas Thrainer
3036 22b7f6f8 Thomas Thrainer
    # osparams processing
3037 6bce7ba2 Santi Raffa
    if self.op.osparams or self.op.osparams_private:
3038 1a182390 Santi Raffa
      public_parms = self.op.osparams or {}
3039 6bce7ba2 Santi Raffa
      private_parms = self.op.osparams_private or {}
3040 1a182390 Santi Raffa
      dupe_keys = utils.GetRepeatedKeys(public_parms, private_parms)
3041 1a182390 Santi Raffa
3042 1a182390 Santi Raffa
      if dupe_keys:
3043 1a182390 Santi Raffa
        raise errors.OpPrereqError("OS parameters repeated multiple times: %s" %
3044 1a182390 Santi Raffa
                                   utils.CommaJoin(dupe_keys))
3045 1a182390 Santi Raffa
3046 1a182390 Santi Raffa
      self.os_inst = GetUpdatedParams(self.instance.osparams,
3047 1a182390 Santi Raffa
                                      public_parms)
3048 1a182390 Santi Raffa
      self.os_inst_private = GetUpdatedParams(self.instance.osparams_private,
3049 1a182390 Santi Raffa
                                              private_parms)
3050 1a182390 Santi Raffa
3051 1a182390 Santi Raffa
      CheckOSParams(self, True, node_uuids, instance_os,
3052 1a182390 Santi Raffa
                    objects.FillDict(self.os_inst,
3053 1a182390 Santi Raffa
                                     self.os_inst_private))
3054 1a182390 Santi Raffa
3055 22b7f6f8 Thomas Thrainer
    else:
3056 22b7f6f8 Thomas Thrainer
      self.os_inst = {}
3057 1a182390 Santi Raffa
      self.os_inst_private = {}
3058 22b7f6f8 Thomas Thrainer
3059 22b7f6f8 Thomas Thrainer
    #TODO(dynmem): do the appropriate check involving MINMEM
3060 22b7f6f8 Thomas Thrainer
    if (constants.BE_MAXMEM in self.op.beparams and not self.op.force and
3061 22b7f6f8 Thomas Thrainer
        be_new[constants.BE_MAXMEM] > be_old[constants.BE_MAXMEM]):
3062 1c3231aa Thomas Thrainer
      mem_check_list = [pnode_uuid]
3063 22b7f6f8 Thomas Thrainer
      if be_new[constants.BE_AUTO_BALANCE]:
3064 22b7f6f8 Thomas Thrainer
        # either we changed auto_balance to yes or it was from before
3065 d0d7d7cf Thomas Thrainer
        mem_check_list.extend(self.instance.secondary_nodes)
3066 a295eb80 Helga Velroyen
      instance_info = self.rpc.call_instance_info(
3067 d0d7d7cf Thomas Thrainer
          pnode_uuid, self.instance.name, self.instance.hypervisor,
3068 9c8f7bf4 Helga Velroyen
          cluster_hvparams)
3069 d0d7d7cf Thomas Thrainer
      hvspecs = [(self.instance.hypervisor,
3070 9c8f7bf4 Helga Velroyen
                  cluster_hvparams)]
3071 22b7f6f8 Thomas Thrainer
      nodeinfo = self.rpc.call_node_info(mem_check_list, None,
3072 da803ff1 Helga Velroyen
                                         hvspecs)
3073 1c3231aa Thomas Thrainer
      pninfo = nodeinfo[pnode_uuid]
3074 22b7f6f8 Thomas Thrainer
      msg = pninfo.fail_msg
3075 22b7f6f8 Thomas Thrainer
      if msg:
3076 22b7f6f8 Thomas Thrainer
        # Assume the primary node is unreachable and go ahead
3077 22b7f6f8 Thomas Thrainer
        self.warn.append("Can't get info from primary node %s: %s" %
3078 1c3231aa Thomas Thrainer
                         (self.cfg.GetNodeName(pnode_uuid), msg))
3079 22b7f6f8 Thomas Thrainer
      else:
3080 22b7f6f8 Thomas Thrainer
        (_, _, (pnhvinfo, )) = pninfo.payload
3081 22b7f6f8 Thomas Thrainer
        if not isinstance(pnhvinfo.get("memory_free", None), int):
3082 22b7f6f8 Thomas Thrainer
          self.warn.append("Node data from primary node %s doesn't contain"
3083 1c3231aa Thomas Thrainer
                           " free memory information" %
3084 1c3231aa Thomas Thrainer
                           self.cfg.GetNodeName(pnode_uuid))
3085 22b7f6f8 Thomas Thrainer
        elif instance_info.fail_msg:
3086 22b7f6f8 Thomas Thrainer
          self.warn.append("Can't get instance runtime information: %s" %
3087 22b7f6f8 Thomas Thrainer
                           instance_info.fail_msg)
3088 22b7f6f8 Thomas Thrainer
        else:
3089 22b7f6f8 Thomas Thrainer
          if instance_info.payload:
3090 22b7f6f8 Thomas Thrainer
            current_mem = int(instance_info.payload["memory"])
3091 22b7f6f8 Thomas Thrainer
          else:
3092 22b7f6f8 Thomas Thrainer
            # Assume instance not running
3093 22b7f6f8 Thomas Thrainer
            # (there is a slight race condition here, but it's not very
3094 22b7f6f8 Thomas Thrainer
            # probable, and we have no other way to check)
3095 22b7f6f8 Thomas Thrainer
            # TODO: Describe race condition
3096 22b7f6f8 Thomas Thrainer
            current_mem = 0
3097 22b7f6f8 Thomas Thrainer
          #TODO(dynmem): do the appropriate check involving MINMEM
3098 22b7f6f8 Thomas Thrainer
          miss_mem = (be_new[constants.BE_MAXMEM] - current_mem -
3099 22b7f6f8 Thomas Thrainer
                      pnhvinfo["memory_free"])
3100 22b7f6f8 Thomas Thrainer
          if miss_mem > 0:
3101 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("This change will prevent the instance"
3102 22b7f6f8 Thomas Thrainer
                                       " from starting, due to %d MB of memory"
3103 22b7f6f8 Thomas Thrainer
                                       " missing on its primary node" %
3104 22b7f6f8 Thomas Thrainer
                                       miss_mem, errors.ECODE_NORES)
3105 22b7f6f8 Thomas Thrainer
3106 22b7f6f8 Thomas Thrainer
      if be_new[constants.BE_AUTO_BALANCE]:
3107 1c3231aa Thomas Thrainer
        for node_uuid, nres in nodeinfo.items():
3108 d0d7d7cf Thomas Thrainer
          if node_uuid not in self.instance.secondary_nodes:
3109 22b7f6f8 Thomas Thrainer
            continue
3110 1c3231aa Thomas Thrainer
          nres.Raise("Can't get info from secondary node %s" %
3111 1c3231aa Thomas Thrainer
                     self.cfg.GetNodeName(node_uuid), prereq=True,
3112 1c3231aa Thomas Thrainer
                     ecode=errors.ECODE_STATE)
3113 22b7f6f8 Thomas Thrainer
          (_, _, (nhvinfo, )) = nres.payload
3114 22b7f6f8 Thomas Thrainer
          if not isinstance(nhvinfo.get("memory_free", None), int):
3115 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Secondary node %s didn't return free"
3116 1c3231aa Thomas Thrainer
                                       " memory information" %
3117 1c3231aa Thomas Thrainer
                                       self.cfg.GetNodeName(node_uuid),
3118 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_STATE)
3119 22b7f6f8 Thomas Thrainer
          #TODO(dynmem): do the appropriate check involving MINMEM
3120 22b7f6f8 Thomas Thrainer
          elif be_new[constants.BE_MAXMEM] > nhvinfo["memory_free"]:
3121 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("This change will prevent the instance"
3122 22b7f6f8 Thomas Thrainer
                                       " from failover to its secondary node"
3123 1c3231aa Thomas Thrainer
                                       " %s, due to not enough memory" %
3124 1c3231aa Thomas Thrainer
                                       self.cfg.GetNodeName(node_uuid),
3125 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_STATE)
3126 22b7f6f8 Thomas Thrainer
3127 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
3128 0bbec3af Helga Velroyen
      remote_info = self.rpc.call_instance_info(
3129 d0d7d7cf Thomas Thrainer
         self.instance.primary_node, self.instance.name,
3130 b666a94c Helga Velroyen
         self.instance.hypervisor,
3131 9c8f7bf4 Helga Velroyen
         cluster_hvparams)
3132 1c3231aa Thomas Thrainer
      remote_info.Raise("Error checking node %s" %
3133 d0d7d7cf Thomas Thrainer
                        self.cfg.GetNodeName(self.instance.primary_node))
3134 22b7f6f8 Thomas Thrainer
      if not remote_info.payload: # not running already
3135 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s is not running" %
3136 d0d7d7cf Thomas Thrainer
                                   self.instance.name, errors.ECODE_STATE)
3137 22b7f6f8 Thomas Thrainer
3138 22b7f6f8 Thomas Thrainer
      current_memory = remote_info.payload["memory"]
3139 22b7f6f8 Thomas Thrainer
      if (not self.op.force and
3140 22b7f6f8 Thomas Thrainer
           (self.op.runtime_mem > self.be_proposed[constants.BE_MAXMEM] or
3141 22b7f6f8 Thomas Thrainer
            self.op.runtime_mem < self.be_proposed[constants.BE_MINMEM])):
3142 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s must have memory between %d"
3143 22b7f6f8 Thomas Thrainer
                                   " and %d MB of memory unless --force is"
3144 22b7f6f8 Thomas Thrainer
                                   " given" %
3145 d0d7d7cf Thomas Thrainer
                                   (self.instance.name,
3146 22b7f6f8 Thomas Thrainer
                                    self.be_proposed[constants.BE_MINMEM],
3147 22b7f6f8 Thomas Thrainer
                                    self.be_proposed[constants.BE_MAXMEM]),
3148 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3149 22b7f6f8 Thomas Thrainer
3150 22b7f6f8 Thomas Thrainer
      delta = self.op.runtime_mem - current_memory
3151 22b7f6f8 Thomas Thrainer
      if delta > 0:
3152 a295eb80 Helga Velroyen
        CheckNodeFreeMemory(
3153 d0d7d7cf Thomas Thrainer
            self, self.instance.primary_node,
3154 d0d7d7cf Thomas Thrainer
            "ballooning memory for instance %s" % self.instance.name, delta,
3155 d0d7d7cf Thomas Thrainer
            self.instance.hypervisor,
3156 d0d7d7cf Thomas Thrainer
            self.cfg.GetClusterInfo().hvparams[self.instance.hypervisor])
3157 d0d7d7cf Thomas Thrainer
3158 d0d7d7cf Thomas Thrainer
    # make self.cluster visible in the functions below
3159 d0d7d7cf Thomas Thrainer
    cluster = self.cluster
3160 22b7f6f8 Thomas Thrainer
3161 22b7f6f8 Thomas Thrainer
    def _PrepareNicCreate(_, params, private):
3162 22b7f6f8 Thomas Thrainer
      self._PrepareNicModification(params, private, None, None,
3163 1c3231aa Thomas Thrainer
                                   {}, cluster, pnode_uuid)
3164 22b7f6f8 Thomas Thrainer
      return (None, None)
3165 22b7f6f8 Thomas Thrainer
3166 22b7f6f8 Thomas Thrainer
    def _PrepareNicMod(_, nic, params, private):
3167 22b7f6f8 Thomas Thrainer
      self._PrepareNicModification(params, private, nic.ip, nic.network,
3168 1c3231aa Thomas Thrainer
                                   nic.nicparams, cluster, pnode_uuid)
3169 22b7f6f8 Thomas Thrainer
      return None
3170 22b7f6f8 Thomas Thrainer
3171 22b7f6f8 Thomas Thrainer
    def _PrepareNicRemove(_, params, __):
3172 22b7f6f8 Thomas Thrainer
      ip = params.ip
3173 22b7f6f8 Thomas Thrainer
      net = params.network
3174 22b7f6f8 Thomas Thrainer
      if net is not None and ip is not None:
3175 22b7f6f8 Thomas Thrainer
        self.cfg.ReleaseIp(net, ip, self.proc.GetECId())
3176 22b7f6f8 Thomas Thrainer
3177 22b7f6f8 Thomas Thrainer
    # Verify NIC changes (operating on copy)
3178 d0d7d7cf Thomas Thrainer
    nics = self.instance.nics[:]
3179 5eacbcae Thomas Thrainer
    _ApplyContainerMods("NIC", nics, None, self.nicmod,
3180 5eacbcae Thomas Thrainer
                        _PrepareNicCreate, _PrepareNicMod, _PrepareNicRemove)
3181 22b7f6f8 Thomas Thrainer
    if len(nics) > constants.MAX_NICS:
3182 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance has too many network interfaces"
3183 22b7f6f8 Thomas Thrainer
                                 " (%d), cannot add more" % constants.MAX_NICS,
3184 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
3185 22b7f6f8 Thomas Thrainer
3186 22b7f6f8 Thomas Thrainer
    # Pre-compute NIC changes (necessary to use result in hooks)
3187 22b7f6f8 Thomas Thrainer
    self._nic_chgdesc = []
3188 22b7f6f8 Thomas Thrainer
    if self.nicmod:
3189 22b7f6f8 Thomas Thrainer
      # Operate on copies as this is still in prereq
3190 d0d7d7cf Thomas Thrainer
      nics = [nic.Copy() for nic in self.instance.nics]
3191 5eacbcae Thomas Thrainer
      _ApplyContainerMods("NIC", nics, self._nic_chgdesc, self.nicmod,
3192 ba924970 Dimitris Aragiorgis
                          self._CreateNewNic, self._ApplyNicMods,
3193 ba924970 Dimitris Aragiorgis
                          self._RemoveNic)
3194 22b7f6f8 Thomas Thrainer
      # Verify that NIC names are unique and valid
3195 22b7f6f8 Thomas Thrainer
      utils.ValidateDeviceNames("NIC", nics)
3196 22b7f6f8 Thomas Thrainer
      self._new_nics = nics
3197 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_NIC_COUNT] = len(self._new_nics)
3198 22b7f6f8 Thomas Thrainer
    else:
3199 22b7f6f8 Thomas Thrainer
      self._new_nics = None
3200 d0d7d7cf Thomas Thrainer
      ispec[constants.ISPEC_NIC_COUNT] = len(self.instance.nics)
3201 22b7f6f8 Thomas Thrainer
3202 22b7f6f8 Thomas Thrainer
    if not self.op.ignore_ipolicy:
3203 d0d7d7cf Thomas Thrainer
      ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(self.cluster,
3204 22b7f6f8 Thomas Thrainer
                                                              group_info)
3205 22b7f6f8 Thomas Thrainer
3206 22b7f6f8 Thomas Thrainer
      # Fill ispec with backend parameters
3207 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_SPINDLE_USE] = \
3208 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_SPINDLE_USE, None)
3209 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_CPU_COUNT] = self.be_new.get(constants.BE_VCPUS,
3210 22b7f6f8 Thomas Thrainer
                                                         None)
3211 22b7f6f8 Thomas Thrainer
3212 22b7f6f8 Thomas Thrainer
      # Copy ispec to verify parameters with min/max values separately
3213 22b7f6f8 Thomas Thrainer
      if self.op.disk_template:
3214 22b7f6f8 Thomas Thrainer
        new_disk_template = self.op.disk_template
3215 22b7f6f8 Thomas Thrainer
      else:
3216 d0d7d7cf Thomas Thrainer
        new_disk_template = self.instance.disk_template
3217 22b7f6f8 Thomas Thrainer
      ispec_max = ispec.copy()
3218 22b7f6f8 Thomas Thrainer
      ispec_max[constants.ISPEC_MEM_SIZE] = \
3219 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_MAXMEM, None)
3220 22b7f6f8 Thomas Thrainer
      res_max = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec_max,
3221 22b7f6f8 Thomas Thrainer
                                                     new_disk_template)
3222 22b7f6f8 Thomas Thrainer
      ispec_min = ispec.copy()
3223 22b7f6f8 Thomas Thrainer
      ispec_min[constants.ISPEC_MEM_SIZE] = \
3224 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_MINMEM, None)
3225 22b7f6f8 Thomas Thrainer
      res_min = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec_min,
3226 22b7f6f8 Thomas Thrainer
                                                     new_disk_template)
3227 22b7f6f8 Thomas Thrainer
3228 22b7f6f8 Thomas Thrainer
      if (res_max or res_min):
3229 22b7f6f8 Thomas Thrainer
        # FIXME: Improve error message by including information about whether
3230 22b7f6f8 Thomas Thrainer
        # the upper or lower limit of the parameter fails the ipolicy.
3231 22b7f6f8 Thomas Thrainer
        msg = ("Instance allocation to group %s (%s) violates policy: %s" %
3232 22b7f6f8 Thomas Thrainer
               (group_info, group_info.name,
3233 22b7f6f8 Thomas Thrainer
                utils.CommaJoin(set(res_max + res_min))))
3234 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
3235 22b7f6f8 Thomas Thrainer
3236 22b7f6f8 Thomas Thrainer
  def _ConvertPlainToDrbd(self, feedback_fn):
3237 22b7f6f8 Thomas Thrainer
    """Converts an instance from plain to drbd.
3238 22b7f6f8 Thomas Thrainer

3239 22b7f6f8 Thomas Thrainer
    """
3240 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to drbd")
3241 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3242 1c3231aa Thomas Thrainer
    snode_uuid = self.op.remote_node_uuid
3243 22b7f6f8 Thomas Thrainer
3244 d0d7d7cf Thomas Thrainer
    assert self.instance.disk_template == constants.DT_PLAIN
3245 22b7f6f8 Thomas Thrainer
3246 22b7f6f8 Thomas Thrainer
    # create a fake disk info for _GenerateDiskTemplate
3247 22b7f6f8 Thomas Thrainer
    disk_info = [{constants.IDISK_SIZE: d.size, constants.IDISK_MODE: d.mode,
3248 22b7f6f8 Thomas Thrainer
                  constants.IDISK_VG: d.logical_id[0],
3249 22b7f6f8 Thomas Thrainer
                  constants.IDISK_NAME: d.name}
3250 d0d7d7cf Thomas Thrainer
                 for d in self.instance.disks]
3251 5eacbcae Thomas Thrainer
    new_disks = GenerateDiskTemplate(self, self.op.disk_template,
3252 da4a52a3 Thomas Thrainer
                                     self.instance.uuid, pnode_uuid,
3253 d0d7d7cf Thomas Thrainer
                                     [snode_uuid], disk_info, None, None, 0,
3254 d0d7d7cf Thomas Thrainer
                                     feedback_fn, self.diskparams)
3255 0c3d9c7c Thomas Thrainer
    anno_disks = rpc.AnnotateDiskParams(new_disks, self.diskparams)
3256 1c3231aa Thomas Thrainer
    p_excl_stor = IsExclusiveStorageEnabledNodeUuid(self.cfg, pnode_uuid)
3257 1c3231aa Thomas Thrainer
    s_excl_stor = IsExclusiveStorageEnabledNodeUuid(self.cfg, snode_uuid)
3258 d0d7d7cf Thomas Thrainer
    info = GetInstanceInfoText(self.instance)
3259 22b7f6f8 Thomas Thrainer
    feedback_fn("Creating additional volumes...")
3260 22b7f6f8 Thomas Thrainer
    # first, create the missing data and meta devices
3261 22b7f6f8 Thomas Thrainer
    for disk in anno_disks:
3262 22b7f6f8 Thomas Thrainer
      # unfortunately this is... not too nice
3263 d0d7d7cf Thomas Thrainer
      CreateSingleBlockDev(self, pnode_uuid, self.instance, disk.children[1],
3264 5eacbcae Thomas Thrainer
                           info, True, p_excl_stor)
3265 22b7f6f8 Thomas Thrainer
      for child in disk.children:
3266 d0d7d7cf Thomas Thrainer
        CreateSingleBlockDev(self, snode_uuid, self.instance, child, info, True,
3267 5eacbcae Thomas Thrainer
                             s_excl_stor)
3268 22b7f6f8 Thomas Thrainer
    # at this stage, all new LVs have been created, we can rename the
3269 22b7f6f8 Thomas Thrainer
    # old ones
3270 22b7f6f8 Thomas Thrainer
    feedback_fn("Renaming original volumes...")
3271 22b7f6f8 Thomas Thrainer
    rename_list = [(o, n.children[0].logical_id)
3272 d0d7d7cf Thomas Thrainer
                   for (o, n) in zip(self.instance.disks, new_disks)]
3273 1c3231aa Thomas Thrainer
    result = self.rpc.call_blockdev_rename(pnode_uuid, rename_list)
3274 22b7f6f8 Thomas Thrainer
    result.Raise("Failed to rename original LVs")
3275 22b7f6f8 Thomas Thrainer
3276 22b7f6f8 Thomas Thrainer
    feedback_fn("Initializing DRBD devices...")
3277 22b7f6f8 Thomas Thrainer
    # all child devices are in place, we can now create the DRBD devices
3278 22b7f6f8 Thomas Thrainer
    try:
3279 22b7f6f8 Thomas Thrainer
      for disk in anno_disks:
3280 1c3231aa Thomas Thrainer
        for (node_uuid, excl_stor) in [(pnode_uuid, p_excl_stor),
3281 1c3231aa Thomas Thrainer
                                       (snode_uuid, s_excl_stor)]:
3282 1c3231aa Thomas Thrainer
          f_create = node_uuid == pnode_uuid
3283 d0d7d7cf Thomas Thrainer
          CreateSingleBlockDev(self, node_uuid, self.instance, disk, info,
3284 d0d7d7cf Thomas Thrainer
                               f_create, excl_stor)
3285 22b7f6f8 Thomas Thrainer
    except errors.GenericError, e:
3286 22b7f6f8 Thomas Thrainer
      feedback_fn("Initializing of DRBD devices failed;"
3287 22b7f6f8 Thomas Thrainer
                  " renaming back original volumes...")
3288 22b7f6f8 Thomas Thrainer
      rename_back_list = [(n.children[0], o.logical_id)
3289 d0d7d7cf Thomas Thrainer
                          for (n, o) in zip(new_disks, self.instance.disks)]
3290 1c3231aa Thomas Thrainer
      result = self.rpc.call_blockdev_rename(pnode_uuid, rename_back_list)
3291 22b7f6f8 Thomas Thrainer
      result.Raise("Failed to rename LVs back after error %s" % str(e))
3292 22b7f6f8 Thomas Thrainer
      raise
3293 22b7f6f8 Thomas Thrainer
3294 22b7f6f8 Thomas Thrainer
    # at this point, the instance has been modified
3295 d0d7d7cf Thomas Thrainer
    self.instance.disk_template = constants.DT_DRBD8
3296 d0d7d7cf Thomas Thrainer
    self.instance.disks = new_disks
3297 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
3298 22b7f6f8 Thomas Thrainer
3299 22b7f6f8 Thomas Thrainer
    # Release node locks while waiting for sync
3300 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3301 22b7f6f8 Thomas Thrainer
3302 22b7f6f8 Thomas Thrainer
    # disks are created, waiting for sync
3303 d0d7d7cf Thomas Thrainer
    disk_abort = not WaitForSync(self, self.instance,
3304 5eacbcae Thomas Thrainer
                                 oneshot=not self.op.wait_for_sync)
3305 22b7f6f8 Thomas Thrainer
    if disk_abort:
3306 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("There are some degraded disks for"
3307 22b7f6f8 Thomas Thrainer
                               " this instance, please cleanup manually")
3308 22b7f6f8 Thomas Thrainer
3309 22b7f6f8 Thomas Thrainer
    # Node resource locks will be released by caller
3310 22b7f6f8 Thomas Thrainer
3311 22b7f6f8 Thomas Thrainer
  def _ConvertDrbdToPlain(self, feedback_fn):
3312 22b7f6f8 Thomas Thrainer
    """Converts an instance from drbd to plain.
3313 22b7f6f8 Thomas Thrainer

3314 22b7f6f8 Thomas Thrainer
    """
3315 d0d7d7cf Thomas Thrainer
    assert len(self.instance.secondary_nodes) == 1
3316 d0d7d7cf Thomas Thrainer
    assert self.instance.disk_template == constants.DT_DRBD8
3317 22b7f6f8 Thomas Thrainer
3318 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3319 d0d7d7cf Thomas Thrainer
    snode_uuid = self.instance.secondary_nodes[0]
3320 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to plain")
3321 22b7f6f8 Thomas Thrainer
3322 d0d7d7cf Thomas Thrainer
    old_disks = AnnotateDiskParams(self.instance, self.instance.disks, self.cfg)
3323 d0d7d7cf Thomas Thrainer
    new_disks = [d.children[0] for d in self.instance.disks]
3324 22b7f6f8 Thomas Thrainer
3325 22b7f6f8 Thomas Thrainer
    # copy over size, mode and name
3326 22b7f6f8 Thomas Thrainer
    for parent, child in zip(old_disks, new_disks):
3327 22b7f6f8 Thomas Thrainer
      child.size = parent.size
3328 22b7f6f8 Thomas Thrainer
      child.mode = parent.mode
3329 22b7f6f8 Thomas Thrainer
      child.name = parent.name
3330 22b7f6f8 Thomas Thrainer
3331 22b7f6f8 Thomas Thrainer
    # this is a DRBD disk, return its port to the pool
3332 22b7f6f8 Thomas Thrainer
    # NOTE: this must be done right before the call to cfg.Update!
3333 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3334 22b7f6f8 Thomas Thrainer
      tcp_port = disk.logical_id[2]
3335 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(tcp_port)
3336 22b7f6f8 Thomas Thrainer
3337 22b7f6f8 Thomas Thrainer
    # update instance structure
3338 d0d7d7cf Thomas Thrainer
    self.instance.disks = new_disks
3339 d0d7d7cf Thomas Thrainer
    self.instance.disk_template = constants.DT_PLAIN
3340 d0d7d7cf Thomas Thrainer
    _UpdateIvNames(0, self.instance.disks)
3341 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
3342 22b7f6f8 Thomas Thrainer
3343 22b7f6f8 Thomas Thrainer
    # Release locks in case removing disks takes a while
3344 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3345 22b7f6f8 Thomas Thrainer
3346 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing volumes on the secondary node...")
3347 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3348 0c3d9c7c Thomas Thrainer
      result = self.rpc.call_blockdev_remove(snode_uuid, (disk, self.instance))
3349 aefc2f89 Thomas Thrainer
      result.Warn("Could not remove block device %s on node %s,"
3350 aefc2f89 Thomas Thrainer
                  " continuing anyway" %
3351 aefc2f89 Thomas Thrainer
                  (disk.iv_name, self.cfg.GetNodeName(snode_uuid)),
3352 aefc2f89 Thomas Thrainer
                  self.LogWarning)
3353 22b7f6f8 Thomas Thrainer
3354 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing unneeded volumes on the primary node...")
3355 22b7f6f8 Thomas Thrainer
    for idx, disk in enumerate(old_disks):
3356 22b7f6f8 Thomas Thrainer
      meta = disk.children[1]
3357 0c3d9c7c Thomas Thrainer
      result = self.rpc.call_blockdev_remove(pnode_uuid, (meta, self.instance))
3358 aefc2f89 Thomas Thrainer
      result.Warn("Could not remove metadata for disk %d on node %s,"
3359 aefc2f89 Thomas Thrainer
                  " continuing anyway" %
3360 aefc2f89 Thomas Thrainer
                  (idx, self.cfg.GetNodeName(pnode_uuid)),
3361 aefc2f89 Thomas Thrainer
                  self.LogWarning)
3362 22b7f6f8 Thomas Thrainer
3363 ba924970 Dimitris Aragiorgis
  def _HotplugDevice(self, action, dev_type, device, extra, seq):
3364 ba924970 Dimitris Aragiorgis
    self.LogInfo("Trying to hotplug device...")
3365 e15a00dc Dimitris Aragiorgis
    msg = "hotplug:"
3366 ba924970 Dimitris Aragiorgis
    result = self.rpc.call_hotplug_device(self.instance.primary_node,
3367 ba924970 Dimitris Aragiorgis
                                          self.instance, action, dev_type,
3368 51951d38 Dimitris Aragiorgis
                                          (device, self.instance),
3369 51951d38 Dimitris Aragiorgis
                                          extra, seq)
3370 ba924970 Dimitris Aragiorgis
    if result.fail_msg:
3371 ba924970 Dimitris Aragiorgis
      self.LogWarning("Could not hotplug device: %s" % result.fail_msg)
3372 ba924970 Dimitris Aragiorgis
      self.LogInfo("Continuing execution..")
3373 e15a00dc Dimitris Aragiorgis
      msg += "failed"
3374 ba924970 Dimitris Aragiorgis
    else:
3375 ba924970 Dimitris Aragiorgis
      self.LogInfo("Hotplug done.")
3376 e15a00dc Dimitris Aragiorgis
      msg += "done"
3377 e15a00dc Dimitris Aragiorgis
    return msg
3378 ba924970 Dimitris Aragiorgis
3379 22b7f6f8 Thomas Thrainer
  def _CreateNewDisk(self, idx, params, _):
3380 22b7f6f8 Thomas Thrainer
    """Creates a new disk.
3381 22b7f6f8 Thomas Thrainer

3382 22b7f6f8 Thomas Thrainer
    """
3383 22b7f6f8 Thomas Thrainer
    # add a new disk
3384 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DTS_FILEBASED:
3385 d0d7d7cf Thomas Thrainer
      (file_driver, file_path) = self.instance.disks[0].logical_id
3386 22b7f6f8 Thomas Thrainer
      file_path = os.path.dirname(file_path)
3387 22b7f6f8 Thomas Thrainer
    else:
3388 22b7f6f8 Thomas Thrainer
      file_driver = file_path = None
3389 22b7f6f8 Thomas Thrainer
3390 22b7f6f8 Thomas Thrainer
    disk = \
3391 d0d7d7cf Thomas Thrainer
      GenerateDiskTemplate(self, self.instance.disk_template,
3392 da4a52a3 Thomas Thrainer
                           self.instance.uuid, self.instance.primary_node,
3393 d0d7d7cf Thomas Thrainer
                           self.instance.secondary_nodes, [params], file_path,
3394 d0d7d7cf Thomas Thrainer
                           file_driver, idx, self.Log, self.diskparams)[0]
3395 22b7f6f8 Thomas Thrainer
3396 d0d7d7cf Thomas Thrainer
    new_disks = CreateDisks(self, self.instance, disks=[disk])
3397 22b7f6f8 Thomas Thrainer
3398 22b7f6f8 Thomas Thrainer
    if self.cluster.prealloc_wipe_disks:
3399 22b7f6f8 Thomas Thrainer
      # Wipe new disk
3400 d0d7d7cf Thomas Thrainer
      WipeOrCleanupDisks(self, self.instance,
3401 a365b47f Bernardo Dal Seno
                         disks=[(idx, disk, 0)],
3402 a365b47f Bernardo Dal Seno
                         cleanup=new_disks)
3403 22b7f6f8 Thomas Thrainer
3404 e15a00dc Dimitris Aragiorgis
    changes = [
3405 e15a00dc Dimitris Aragiorgis
      ("disk/%d" % idx,
3406 b15d5bd3 Petr Pudlak
       "add:size=%s,mode=%s" % (disk.size, disk.mode)),
3407 e15a00dc Dimitris Aragiorgis
      ]
3408 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3409 ba924970 Dimitris Aragiorgis
      result = self.rpc.call_blockdev_assemble(self.instance.primary_node,
3410 ba924970 Dimitris Aragiorgis
                                               (disk, self.instance),
3411 ba924970 Dimitris Aragiorgis
                                               self.instance.name, True, idx)
3412 ba924970 Dimitris Aragiorgis
      if result.fail_msg:
3413 e15a00dc Dimitris Aragiorgis
        changes.append(("disk/%d" % idx, "assemble:failed"))
3414 ba924970 Dimitris Aragiorgis
        self.LogWarning("Can't assemble newly created disk %d: %s",
3415 ba924970 Dimitris Aragiorgis
                        idx, result.fail_msg)
3416 ba924970 Dimitris Aragiorgis
      else:
3417 ba924970 Dimitris Aragiorgis
        _, link_name = result.payload
3418 e15a00dc Dimitris Aragiorgis
        msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
3419 e15a00dc Dimitris Aragiorgis
                                  constants.HOTPLUG_TARGET_DISK,
3420 e15a00dc Dimitris Aragiorgis
                                  disk, link_name, idx)
3421 e15a00dc Dimitris Aragiorgis
        changes.append(("disk/%d" % idx, msg))
3422 ba924970 Dimitris Aragiorgis
3423 e15a00dc Dimitris Aragiorgis
    return (disk, changes)
3424 22b7f6f8 Thomas Thrainer
3425 922a9e65 Thomas Thrainer
  def _PostAddDisk(self, _, disk):
3426 922a9e65 Thomas Thrainer
    if not WaitForSync(self, self.instance, disks=[disk],
3427 922a9e65 Thomas Thrainer
                       oneshot=not self.op.wait_for_sync):
3428 922a9e65 Thomas Thrainer
      raise errors.OpExecError("Failed to sync disks of %s" %
3429 922a9e65 Thomas Thrainer
                               self.instance.name)
3430 922a9e65 Thomas Thrainer
3431 3c260845 Thomas Thrainer
    # the disk is active at this point, so deactivate it if the instance disks
3432 3c260845 Thomas Thrainer
    # are supposed to be inactive
3433 3c260845 Thomas Thrainer
    if not self.instance.disks_active:
3434 3c260845 Thomas Thrainer
      ShutdownInstanceDisks(self, self.instance, disks=[disk])
3435 3c260845 Thomas Thrainer
3436 c5c72215 Dimitris Aragiorgis
  def _ModifyDisk(self, idx, disk, params, _):
3437 22b7f6f8 Thomas Thrainer
    """Modifies a disk.
3438 22b7f6f8 Thomas Thrainer

3439 22b7f6f8 Thomas Thrainer
    """
3440 22b7f6f8 Thomas Thrainer
    changes = []
3441 4eef428e Dimitris Aragiorgis
    if constants.IDISK_MODE in params:
3442 4eef428e Dimitris Aragiorgis
      disk.mode = params.get(constants.IDISK_MODE)
3443 22b7f6f8 Thomas Thrainer
      changes.append(("disk.mode/%d" % idx, disk.mode))
3444 22b7f6f8 Thomas Thrainer
3445 4eef428e Dimitris Aragiorgis
    if constants.IDISK_NAME in params:
3446 4eef428e Dimitris Aragiorgis
      disk.name = params.get(constants.IDISK_NAME)
3447 4eef428e Dimitris Aragiorgis
      changes.append(("disk.name/%d" % idx, disk.name))
3448 22b7f6f8 Thomas Thrainer
3449 c5c72215 Dimitris Aragiorgis
    # Modify arbitrary params in case instance template is ext
3450 c5c72215 Dimitris Aragiorgis
    for key, value in params.iteritems():
3451 c5c72215 Dimitris Aragiorgis
      if (key not in constants.MODIFIABLE_IDISK_PARAMS and
3452 c5c72215 Dimitris Aragiorgis
          self.instance.disk_template == constants.DT_EXT):
3453 e228ab9c Dimitris Aragiorgis
        # stolen from GetUpdatedParams: default means reset/delete
3454 e228ab9c Dimitris Aragiorgis
        if value.lower() == constants.VALUE_DEFAULT:
3455 e228ab9c Dimitris Aragiorgis
          try:
3456 e228ab9c Dimitris Aragiorgis
            del disk.params[key]
3457 e228ab9c Dimitris Aragiorgis
          except KeyError:
3458 e228ab9c Dimitris Aragiorgis
            pass
3459 e228ab9c Dimitris Aragiorgis
        else:
3460 e228ab9c Dimitris Aragiorgis
          disk.params[key] = value
3461 c5c72215 Dimitris Aragiorgis
        changes.append(("disk.params:%s/%d" % (key, idx), value))
3462 22b7f6f8 Thomas Thrainer
3463 22b7f6f8 Thomas Thrainer
    return changes
3464 22b7f6f8 Thomas Thrainer
3465 22b7f6f8 Thomas Thrainer
  def _RemoveDisk(self, idx, root, _):
3466 22b7f6f8 Thomas Thrainer
    """Removes a disk.
3467 22b7f6f8 Thomas Thrainer

3468 22b7f6f8 Thomas Thrainer
    """
3469 e15a00dc Dimitris Aragiorgis
    hotmsg = ""
3470 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3471 e15a00dc Dimitris Aragiorgis
      hotmsg = self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
3472 e15a00dc Dimitris Aragiorgis
                                   constants.HOTPLUG_TARGET_DISK,
3473 e15a00dc Dimitris Aragiorgis
                                   root, None, idx)
3474 ba924970 Dimitris Aragiorgis
      ShutdownInstanceDisks(self, self.instance, [root])
3475 ba924970 Dimitris Aragiorgis
3476 5eacbcae Thomas Thrainer
    (anno_disk,) = AnnotateDiskParams(self.instance, [root], self.cfg)
3477 1c3231aa Thomas Thrainer
    for node_uuid, disk in anno_disk.ComputeNodeTree(
3478 1c3231aa Thomas Thrainer
                             self.instance.primary_node):
3479 0c3d9c7c Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(node_uuid, (disk, self.instance)) \
3480 0c3d9c7c Thomas Thrainer
              .fail_msg
3481 22b7f6f8 Thomas Thrainer
      if msg:
3482 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove disk/%d on node '%s': %s,"
3483 1c3231aa Thomas Thrainer
                        " continuing anyway", idx,
3484 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
3485 22b7f6f8 Thomas Thrainer
3486 22b7f6f8 Thomas Thrainer
    # if this is a DRBD disk, return its port to the pool
3487 66a37e7a Helga Velroyen
    if root.dev_type in constants.DTS_DRBD:
3488 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(root.logical_id[2])
3489 22b7f6f8 Thomas Thrainer
3490 e15a00dc Dimitris Aragiorgis
    return hotmsg
3491 e15a00dc Dimitris Aragiorgis
3492 22b7f6f8 Thomas Thrainer
  def _CreateNewNic(self, idx, params, private):
3493 22b7f6f8 Thomas Thrainer
    """Creates data structure for a new network interface.
3494 22b7f6f8 Thomas Thrainer

3495 22b7f6f8 Thomas Thrainer
    """
3496 22b7f6f8 Thomas Thrainer
    mac = params[constants.INIC_MAC]
3497 22b7f6f8 Thomas Thrainer
    ip = params.get(constants.INIC_IP, None)
3498 22b7f6f8 Thomas Thrainer
    net = params.get(constants.INIC_NETWORK, None)
3499 22b7f6f8 Thomas Thrainer
    name = params.get(constants.INIC_NAME, None)
3500 22b7f6f8 Thomas Thrainer
    net_uuid = self.cfg.LookupNetwork(net)
3501 22b7f6f8 Thomas Thrainer
    #TODO: not private.filled?? can a nic have no nicparams??
3502 22b7f6f8 Thomas Thrainer
    nicparams = private.filled
3503 22b7f6f8 Thomas Thrainer
    nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, name=name,
3504 22b7f6f8 Thomas Thrainer
                       nicparams=nicparams)
3505 22b7f6f8 Thomas Thrainer
    nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
3506 22b7f6f8 Thomas Thrainer
3507 e15a00dc Dimitris Aragiorgis
    changes = [
3508 22b7f6f8 Thomas Thrainer
      ("nic.%d" % idx,
3509 22b7f6f8 Thomas Thrainer
       "add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" %
3510 22b7f6f8 Thomas Thrainer
       (mac, ip, private.filled[constants.NIC_MODE],
3511 ba924970 Dimitris Aragiorgis
       private.filled[constants.NIC_LINK], net)),
3512 ba924970 Dimitris Aragiorgis
      ]
3513 ba924970 Dimitris Aragiorgis
3514 e15a00dc Dimitris Aragiorgis
    if self.op.hotplug:
3515 e15a00dc Dimitris Aragiorgis
      msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
3516 e15a00dc Dimitris Aragiorgis
                                constants.HOTPLUG_TARGET_NIC,
3517 e15a00dc Dimitris Aragiorgis
                                nobj, None, idx)
3518 e15a00dc Dimitris Aragiorgis
      changes.append(("nic.%d" % idx, msg))
3519 e15a00dc Dimitris Aragiorgis
3520 e15a00dc Dimitris Aragiorgis
    return (nobj, changes)
3521 22b7f6f8 Thomas Thrainer
3522 22b7f6f8 Thomas Thrainer
  def _ApplyNicMods(self, idx, nic, params, private):
3523 22b7f6f8 Thomas Thrainer
    """Modifies a network interface.
3524 22b7f6f8 Thomas Thrainer

3525 22b7f6f8 Thomas Thrainer
    """
3526 22b7f6f8 Thomas Thrainer
    changes = []
3527 22b7f6f8 Thomas Thrainer
3528 22b7f6f8 Thomas Thrainer
    for key in [constants.INIC_MAC, constants.INIC_IP, constants.INIC_NAME]:
3529 22b7f6f8 Thomas Thrainer
      if key in params:
3530 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), params[key]))
3531 22b7f6f8 Thomas Thrainer
        setattr(nic, key, params[key])
3532 22b7f6f8 Thomas Thrainer
3533 22b7f6f8 Thomas Thrainer
    new_net = params.get(constants.INIC_NETWORK, nic.network)
3534 22b7f6f8 Thomas Thrainer
    new_net_uuid = self.cfg.LookupNetwork(new_net)
3535 22b7f6f8 Thomas Thrainer
    if new_net_uuid != nic.network:
3536 22b7f6f8 Thomas Thrainer
      changes.append(("nic.network/%d" % idx, new_net))
3537 22b7f6f8 Thomas Thrainer
      nic.network = new_net_uuid
3538 22b7f6f8 Thomas Thrainer
3539 22b7f6f8 Thomas Thrainer
    if private.filled:
3540 22b7f6f8 Thomas Thrainer
      nic.nicparams = private.filled
3541 22b7f6f8 Thomas Thrainer
3542 22b7f6f8 Thomas Thrainer
      for (key, val) in nic.nicparams.items():
3543 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), val))
3544 22b7f6f8 Thomas Thrainer
3545 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3546 e15a00dc Dimitris Aragiorgis
      msg = self._HotplugDevice(constants.HOTPLUG_ACTION_MODIFY,
3547 e15a00dc Dimitris Aragiorgis
                                constants.HOTPLUG_TARGET_NIC,
3548 e15a00dc Dimitris Aragiorgis
                                nic, None, idx)
3549 e15a00dc Dimitris Aragiorgis
      changes.append(("nic/%d" % idx, msg))
3550 ba924970 Dimitris Aragiorgis
3551 22b7f6f8 Thomas Thrainer
    return changes
3552 22b7f6f8 Thomas Thrainer
3553 ba924970 Dimitris Aragiorgis
  def _RemoveNic(self, idx, nic, _):
3554 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3555 e15a00dc Dimitris Aragiorgis
      return self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
3556 e15a00dc Dimitris Aragiorgis
                                 constants.HOTPLUG_TARGET_NIC,
3557 e15a00dc Dimitris Aragiorgis
                                 nic, None, idx)
3558 ba924970 Dimitris Aragiorgis
3559 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3560 22b7f6f8 Thomas Thrainer
    """Modifies an instance.
3561 22b7f6f8 Thomas Thrainer

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

3564 22b7f6f8 Thomas Thrainer
    """
3565 22b7f6f8 Thomas Thrainer
    # Process here the warnings from CheckPrereq, as we don't have a
3566 22b7f6f8 Thomas Thrainer
    # feedback_fn there.
3567 22b7f6f8 Thomas Thrainer
    # TODO: Replace with self.LogWarning
3568 22b7f6f8 Thomas Thrainer
    for warn in self.warn:
3569 22b7f6f8 Thomas Thrainer
      feedback_fn("WARNING: %s" % warn)
3570 22b7f6f8 Thomas Thrainer
3571 22b7f6f8 Thomas Thrainer
    assert ((self.op.disk_template is None) ^
3572 22b7f6f8 Thomas Thrainer
            bool(self.owned_locks(locking.LEVEL_NODE_RES))), \
3573 22b7f6f8 Thomas Thrainer
      "Not owning any node resource locks"
3574 22b7f6f8 Thomas Thrainer
3575 22b7f6f8 Thomas Thrainer
    result = []
3576 22b7f6f8 Thomas Thrainer
3577 22b7f6f8 Thomas Thrainer
    # New primary node
3578 1c3231aa Thomas Thrainer
    if self.op.pnode_uuid:
3579 d0d7d7cf Thomas Thrainer
      self.instance.primary_node = self.op.pnode_uuid
3580 22b7f6f8 Thomas Thrainer
3581 22b7f6f8 Thomas Thrainer
    # runtime memory
3582 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
3583 d0d7d7cf Thomas Thrainer
      rpcres = self.rpc.call_instance_balloon_memory(self.instance.primary_node,
3584 d0d7d7cf Thomas Thrainer
                                                     self.instance,
3585 22b7f6f8 Thomas Thrainer
                                                     self.op.runtime_mem)
3586 22b7f6f8 Thomas Thrainer
      rpcres.Raise("Cannot modify instance runtime memory")
3587 22b7f6f8 Thomas Thrainer
      result.append(("runtime_memory", self.op.runtime_mem))
3588 22b7f6f8 Thomas Thrainer
3589 22b7f6f8 Thomas Thrainer
    # Apply disk changes
3590 d0d7d7cf Thomas Thrainer
    _ApplyContainerMods("disk", self.instance.disks, result, self.diskmod,
3591 5eacbcae Thomas Thrainer
                        self._CreateNewDisk, self._ModifyDisk,
3592 922a9e65 Thomas Thrainer
                        self._RemoveDisk, post_add_fn=self._PostAddDisk)
3593 d0d7d7cf Thomas Thrainer
    _UpdateIvNames(0, self.instance.disks)
3594 22b7f6f8 Thomas Thrainer
3595 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
3596 22b7f6f8 Thomas Thrainer
      if __debug__:
3597 d0d7d7cf Thomas Thrainer
        check_nodes = set(self.instance.all_nodes)
3598 1c3231aa Thomas Thrainer
        if self.op.remote_node_uuid:
3599 1c3231aa Thomas Thrainer
          check_nodes.add(self.op.remote_node_uuid)
3600 22b7f6f8 Thomas Thrainer
        for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
3601 22b7f6f8 Thomas Thrainer
          owned = self.owned_locks(level)
3602 22b7f6f8 Thomas Thrainer
          assert not (check_nodes - owned), \
3603 22b7f6f8 Thomas Thrainer
            ("Not owning the correct locks, owning %r, expected at least %r" %
3604 22b7f6f8 Thomas Thrainer
             (owned, check_nodes))
3605 22b7f6f8 Thomas Thrainer
3606 d0d7d7cf Thomas Thrainer
      r_shut = ShutdownInstanceDisks(self, self.instance)
3607 22b7f6f8 Thomas Thrainer
      if not r_shut:
3608 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Cannot shutdown instance disks, unable to"
3609 22b7f6f8 Thomas Thrainer
                                 " proceed with disk template conversion")
3610 d0d7d7cf Thomas Thrainer
      mode = (self.instance.disk_template, self.op.disk_template)
3611 22b7f6f8 Thomas Thrainer
      try:
3612 22b7f6f8 Thomas Thrainer
        self._DISK_CONVERSIONS[mode](self, feedback_fn)
3613 22b7f6f8 Thomas Thrainer
      except:
3614 da4a52a3 Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(self.instance.uuid)
3615 22b7f6f8 Thomas Thrainer
        raise
3616 22b7f6f8 Thomas Thrainer
      result.append(("disk_template", self.op.disk_template))
3617 22b7f6f8 Thomas Thrainer
3618 d0d7d7cf Thomas Thrainer
      assert self.instance.disk_template == self.op.disk_template, \
3619 22b7f6f8 Thomas Thrainer
        ("Expected disk template '%s', found '%s'" %
3620 d0d7d7cf Thomas Thrainer
         (self.op.disk_template, self.instance.disk_template))
3621 22b7f6f8 Thomas Thrainer
3622 22b7f6f8 Thomas Thrainer
    # Release node and resource locks if there are any (they might already have
3623 22b7f6f8 Thomas Thrainer
    # been released during disk conversion)
3624 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3625 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE_RES)
3626 22b7f6f8 Thomas Thrainer
3627 22b7f6f8 Thomas Thrainer
    # Apply NIC changes
3628 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
3629 d0d7d7cf Thomas Thrainer
      self.instance.nics = self._new_nics
3630 22b7f6f8 Thomas Thrainer
      result.extend(self._nic_chgdesc)
3631 22b7f6f8 Thomas Thrainer
3632 22b7f6f8 Thomas Thrainer
    # hvparams changes
3633 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
3634 d0d7d7cf Thomas Thrainer
      self.instance.hvparams = self.hv_inst
3635 22b7f6f8 Thomas Thrainer
      for key, val in self.op.hvparams.iteritems():
3636 22b7f6f8 Thomas Thrainer
        result.append(("hv/%s" % key, val))
3637 22b7f6f8 Thomas Thrainer
3638 22b7f6f8 Thomas Thrainer
    # beparams changes
3639 22b7f6f8 Thomas Thrainer
    if self.op.beparams:
3640 d0d7d7cf Thomas Thrainer
      self.instance.beparams = self.be_inst
3641 22b7f6f8 Thomas Thrainer
      for key, val in self.op.beparams.iteritems():
3642 22b7f6f8 Thomas Thrainer
        result.append(("be/%s" % key, val))
3643 22b7f6f8 Thomas Thrainer
3644 22b7f6f8 Thomas Thrainer
    # OS change
3645 22b7f6f8 Thomas Thrainer
    if self.op.os_name:
3646 d0d7d7cf Thomas Thrainer
      self.instance.os = self.op.os_name
3647 22b7f6f8 Thomas Thrainer
3648 22b7f6f8 Thomas Thrainer
    # osparams changes
3649 22b7f6f8 Thomas Thrainer
    if self.op.osparams:
3650 d0d7d7cf Thomas Thrainer
      self.instance.osparams = self.os_inst
3651 22b7f6f8 Thomas Thrainer
      for key, val in self.op.osparams.iteritems():
3652 22b7f6f8 Thomas Thrainer
        result.append(("os/%s" % key, val))
3653 22b7f6f8 Thomas Thrainer
3654 1a182390 Santi Raffa
    if self.op.osparams_private:
3655 1a182390 Santi Raffa
      self.instance.osparams_private = self.os_inst_private
3656 1a182390 Santi Raffa
      for key, val in self.op.osparams_private.iteritems():
3657 1a182390 Santi Raffa
        # Show the Private(...) blurb.
3658 1a182390 Santi Raffa
        result.append(("os_private/%s" % key, repr(val)))
3659 1a182390 Santi Raffa
3660 22b7f6f8 Thomas Thrainer
    if self.op.offline is None:
3661 22b7f6f8 Thomas Thrainer
      # Ignore
3662 22b7f6f8 Thomas Thrainer
      pass
3663 22b7f6f8 Thomas Thrainer
    elif self.op.offline:
3664 22b7f6f8 Thomas Thrainer
      # Mark instance as offline
3665 da4a52a3 Thomas Thrainer
      self.cfg.MarkInstanceOffline(self.instance.uuid)
3666 22b7f6f8 Thomas Thrainer
      result.append(("admin_state", constants.ADMINST_OFFLINE))
3667 22b7f6f8 Thomas Thrainer
    else:
3668 22b7f6f8 Thomas Thrainer
      # Mark instance as online, but stopped
3669 da4a52a3 Thomas Thrainer
      self.cfg.MarkInstanceDown(self.instance.uuid)
3670 22b7f6f8 Thomas Thrainer
      result.append(("admin_state", constants.ADMINST_DOWN))
3671 22b7f6f8 Thomas Thrainer
3672 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn, self.proc.GetECId())
3673 22b7f6f8 Thomas Thrainer
3674 22b7f6f8 Thomas Thrainer
    assert not (self.owned_locks(locking.LEVEL_NODE_RES) or
3675 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
3676 22b7f6f8 Thomas Thrainer
      "All node locks should have been released by now"
3677 22b7f6f8 Thomas Thrainer
3678 22b7f6f8 Thomas Thrainer
    return result
3679 22b7f6f8 Thomas Thrainer
3680 22b7f6f8 Thomas Thrainer
  _DISK_CONVERSIONS = {
3681 22b7f6f8 Thomas Thrainer
    (constants.DT_PLAIN, constants.DT_DRBD8): _ConvertPlainToDrbd,
3682 22b7f6f8 Thomas Thrainer
    (constants.DT_DRBD8, constants.DT_PLAIN): _ConvertDrbdToPlain,
3683 22b7f6f8 Thomas Thrainer
    }
3684 22b7f6f8 Thomas Thrainer
3685 22b7f6f8 Thomas Thrainer
3686 22b7f6f8 Thomas Thrainer
class LUInstanceChangeGroup(LogicalUnit):
3687 22b7f6f8 Thomas Thrainer
  HPATH = "instance-change-group"
3688 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
3689 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
3690 22b7f6f8 Thomas Thrainer
3691 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
3692 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
3693 22b7f6f8 Thomas Thrainer
3694 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
3695 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODEGROUP: [],
3696 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE: [],
3697 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
3698 22b7f6f8 Thomas Thrainer
      }
3699 22b7f6f8 Thomas Thrainer
3700 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
3701 22b7f6f8 Thomas Thrainer
3702 22b7f6f8 Thomas Thrainer
    if self.op.target_groups:
3703 22b7f6f8 Thomas Thrainer
      self.req_target_uuids = map(self.cfg.LookupNodeGroup,
3704 22b7f6f8 Thomas Thrainer
                                  self.op.target_groups)
3705 22b7f6f8 Thomas Thrainer
    else:
3706 22b7f6f8 Thomas Thrainer
      self.req_target_uuids = None
3707 22b7f6f8 Thomas Thrainer
3708 5eacbcae Thomas Thrainer
    self.op.iallocator = GetDefaultIAllocator(self.cfg, self.op.iallocator)
3709 22b7f6f8 Thomas Thrainer
3710 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
3711 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
3712 22b7f6f8 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
3713 22b7f6f8 Thomas Thrainer
3714 22b7f6f8 Thomas Thrainer
      if self.req_target_uuids:
3715 22b7f6f8 Thomas Thrainer
        lock_groups = set(self.req_target_uuids)
3716 22b7f6f8 Thomas Thrainer
3717 22b7f6f8 Thomas Thrainer
        # Lock all groups used by instance optimistically; this requires going
3718 22b7f6f8 Thomas Thrainer
        # via the node before it's locked, requiring verification later on
3719 da4a52a3 Thomas Thrainer
        instance_groups = self.cfg.GetInstanceNodeGroups(self.op.instance_uuid)
3720 22b7f6f8 Thomas Thrainer
        lock_groups.update(instance_groups)
3721 22b7f6f8 Thomas Thrainer
      else:
3722 22b7f6f8 Thomas Thrainer
        # No target groups, need to lock all of them
3723 22b7f6f8 Thomas Thrainer
        lock_groups = locking.ALL_SET
3724 22b7f6f8 Thomas Thrainer
3725 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = lock_groups
3726 22b7f6f8 Thomas Thrainer
3727 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE:
3728 22b7f6f8 Thomas Thrainer
      if self.req_target_uuids:
3729 22b7f6f8 Thomas Thrainer
        # Lock all nodes used by instances
3730 22b7f6f8 Thomas Thrainer
        self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
3731 22b7f6f8 Thomas Thrainer
        self._LockInstancesNodes()
3732 22b7f6f8 Thomas Thrainer
3733 22b7f6f8 Thomas Thrainer
        # Lock all nodes in all potential target groups
3734 22b7f6f8 Thomas Thrainer
        lock_groups = (frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) -
3735 da4a52a3 Thomas Thrainer
                       self.cfg.GetInstanceNodeGroups(self.op.instance_uuid))
3736 1c3231aa Thomas Thrainer
        member_nodes = [node_uuid
3737 22b7f6f8 Thomas Thrainer
                        for group in lock_groups
3738 1c3231aa Thomas Thrainer
                        for node_uuid in self.cfg.GetNodeGroup(group).members]
3739 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE].extend(member_nodes)
3740 22b7f6f8 Thomas Thrainer
      else:
3741 22b7f6f8 Thomas Thrainer
        # Lock all nodes as all groups are potential targets
3742 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
3743 22b7f6f8 Thomas Thrainer
3744 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
3745 da4a52a3 Thomas Thrainer
    owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
3746 22b7f6f8 Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
3747 22b7f6f8 Thomas Thrainer
    owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE))
3748 22b7f6f8 Thomas Thrainer
3749 22b7f6f8 Thomas Thrainer
    assert (self.req_target_uuids is None or
3750 22b7f6f8 Thomas Thrainer
            owned_groups.issuperset(self.req_target_uuids))
3751 da4a52a3 Thomas Thrainer
    assert owned_instance_names == set([self.op.instance_name])
3752 22b7f6f8 Thomas Thrainer
3753 22b7f6f8 Thomas Thrainer
    # Get instance information
3754 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
3755 22b7f6f8 Thomas Thrainer
3756 22b7f6f8 Thomas Thrainer
    # Check if node groups for locked instance are still correct
3757 22b7f6f8 Thomas Thrainer
    assert owned_nodes.issuperset(self.instance.all_nodes), \
3758 22b7f6f8 Thomas Thrainer
      ("Instance %s's nodes changed while we kept the lock" %
3759 22b7f6f8 Thomas Thrainer
       self.op.instance_name)
3760 22b7f6f8 Thomas Thrainer
3761 da4a52a3 Thomas Thrainer
    inst_groups = CheckInstanceNodeGroups(self.cfg, self.op.instance_uuid,
3762 5eacbcae Thomas Thrainer
                                          owned_groups)
3763 22b7f6f8 Thomas Thrainer
3764 22b7f6f8 Thomas Thrainer
    if self.req_target_uuids:
3765 22b7f6f8 Thomas Thrainer
      # User requested specific target groups
3766 22b7f6f8 Thomas Thrainer
      self.target_uuids = frozenset(self.req_target_uuids)
3767 22b7f6f8 Thomas Thrainer
    else:
3768 22b7f6f8 Thomas Thrainer
      # All groups except those used by the instance are potential targets
3769 22b7f6f8 Thomas Thrainer
      self.target_uuids = owned_groups - inst_groups
3770 22b7f6f8 Thomas Thrainer
3771 22b7f6f8 Thomas Thrainer
    conflicting_groups = self.target_uuids & inst_groups
3772 22b7f6f8 Thomas Thrainer
    if conflicting_groups:
3773 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't use group(s) '%s' as targets, they are"
3774 22b7f6f8 Thomas Thrainer
                                 " used by the instance '%s'" %
3775 22b7f6f8 Thomas Thrainer
                                 (utils.CommaJoin(conflicting_groups),
3776 22b7f6f8 Thomas Thrainer
                                  self.op.instance_name),
3777 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3778 22b7f6f8 Thomas Thrainer
3779 22b7f6f8 Thomas Thrainer
    if not self.target_uuids:
3780 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are no possible target groups",
3781 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3782 22b7f6f8 Thomas Thrainer
3783 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
3784 22b7f6f8 Thomas Thrainer
    """Build hooks env.
3785 22b7f6f8 Thomas Thrainer

3786 22b7f6f8 Thomas Thrainer
    """
3787 22b7f6f8 Thomas Thrainer
    assert self.target_uuids
3788 22b7f6f8 Thomas Thrainer
3789 22b7f6f8 Thomas Thrainer
    env = {
3790 22b7f6f8 Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
3791 22b7f6f8 Thomas Thrainer
      }
3792 22b7f6f8 Thomas Thrainer
3793 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
3794 22b7f6f8 Thomas Thrainer
3795 22b7f6f8 Thomas Thrainer
    return env
3796 22b7f6f8 Thomas Thrainer
3797 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
3798 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
3799 22b7f6f8 Thomas Thrainer

3800 22b7f6f8 Thomas Thrainer
    """
3801 22b7f6f8 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
3802 22b7f6f8 Thomas Thrainer
    return ([mn], [mn])
3803 22b7f6f8 Thomas Thrainer
3804 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3805 22b7f6f8 Thomas Thrainer
    instances = list(self.owned_locks(locking.LEVEL_INSTANCE))
3806 22b7f6f8 Thomas Thrainer
3807 22b7f6f8 Thomas Thrainer
    assert instances == [self.op.instance_name], "Instance not locked"
3808 22b7f6f8 Thomas Thrainer
3809 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=instances,
3810 22b7f6f8 Thomas Thrainer
                                      target_groups=list(self.target_uuids))
3811 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
3812 22b7f6f8 Thomas Thrainer
3813 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
3814 22b7f6f8 Thomas Thrainer
3815 22b7f6f8 Thomas Thrainer
    if not ial.success:
3816 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute solution for changing group of"
3817 22b7f6f8 Thomas Thrainer
                                 " instance '%s' using iallocator '%s': %s" %
3818 22b7f6f8 Thomas Thrainer
                                 (self.op.instance_name, self.op.iallocator,
3819 22b7f6f8 Thomas Thrainer
                                  ial.info), errors.ECODE_NORES)
3820 22b7f6f8 Thomas Thrainer
3821 5eacbcae Thomas Thrainer
    jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
3822 22b7f6f8 Thomas Thrainer
3823 22b7f6f8 Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for changing group of"
3824 22b7f6f8 Thomas Thrainer
                 " instance '%s'", len(jobs), self.op.instance_name)
3825 22b7f6f8 Thomas Thrainer
3826 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs)