Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance.py @ ec3a7362

History | View | Annotate | Download (154.9 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 e25625ab Jose A. Lopes
def _ComputeInstanceCommunicationNIC(instance_name):
298 e25625ab Jose A. Lopes
  """Compute the name of the instance NIC used by instance
299 e25625ab Jose A. Lopes
  communication.
300 e25625ab Jose A. Lopes

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

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

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

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

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

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

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

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

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

554 22b7f6f8 Thomas Thrainer
    Figure out the right locks for instance creation.
555 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

730 22b7f6f8 Thomas Thrainer
    @return: the export information
731 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

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

1572 22b7f6f8 Thomas Thrainer
  """
1573 22b7f6f8 Thomas Thrainer
  HPATH = "instance-rename"
1574 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1575 22b7f6f8 Thomas Thrainer
1576 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1577 22b7f6f8 Thomas Thrainer
    """Check arguments.
1578 22b7f6f8 Thomas Thrainer

1579 22b7f6f8 Thomas Thrainer
    """
1580 22b7f6f8 Thomas Thrainer
    if self.op.ip_check and not self.op.name_check:
1581 22b7f6f8 Thomas Thrainer
      # TODO: make the ip check more flexible and not depend on the name check
1582 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("IP address check requires a name check",
1583 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1584 22b7f6f8 Thomas Thrainer
1585 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1586 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1587 22b7f6f8 Thomas Thrainer

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

1590 22b7f6f8 Thomas Thrainer
    """
1591 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1592 22b7f6f8 Thomas Thrainer
    env["INSTANCE_NEW_NAME"] = self.op.new_name
1593 22b7f6f8 Thomas Thrainer
    return env
1594 22b7f6f8 Thomas Thrainer
1595 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1596 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1597 22b7f6f8 Thomas Thrainer

1598 22b7f6f8 Thomas Thrainer
    """
1599 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
1600 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1601 22b7f6f8 Thomas Thrainer
1602 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1603 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1604 22b7f6f8 Thomas Thrainer

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

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

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

1706 22b7f6f8 Thomas Thrainer
  """
1707 22b7f6f8 Thomas Thrainer
  HPATH = "instance-remove"
1708 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1709 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1710 22b7f6f8 Thomas Thrainer
1711 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1712 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1713 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
1714 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1715 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
1716 22b7f6f8 Thomas Thrainer
1717 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1718 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1719 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
1720 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1721 22b7f6f8 Thomas Thrainer
      # Copy node locks
1722 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1723 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1724 22b7f6f8 Thomas Thrainer
1725 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1726 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1727 22b7f6f8 Thomas Thrainer

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

1730 22b7f6f8 Thomas Thrainer
    """
1731 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1732 22b7f6f8 Thomas Thrainer
    env["SHUTDOWN_TIMEOUT"] = self.op.shutdown_timeout
1733 22b7f6f8 Thomas Thrainer
    return env
1734 22b7f6f8 Thomas Thrainer
1735 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1736 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1737 22b7f6f8 Thomas Thrainer

1738 22b7f6f8 Thomas Thrainer
    """
1739 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()]
1740 22b7f6f8 Thomas Thrainer
    nl_post = list(self.instance.all_nodes) + nl
1741 22b7f6f8 Thomas Thrainer
    return (nl, nl_post)
1742 22b7f6f8 Thomas Thrainer
1743 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1744 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1745 22b7f6f8 Thomas Thrainer

1746 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1747 22b7f6f8 Thomas Thrainer

1748 22b7f6f8 Thomas Thrainer
    """
1749 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1750 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1751 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1752 22b7f6f8 Thomas Thrainer
1753 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1754 22b7f6f8 Thomas Thrainer
    """Remove the instance.
1755 22b7f6f8 Thomas Thrainer

1756 22b7f6f8 Thomas Thrainer
    """
1757 d0d7d7cf Thomas Thrainer
    logging.info("Shutting down instance %s on node %s", self.instance.name,
1758 d0d7d7cf Thomas Thrainer
                 self.cfg.GetNodeName(self.instance.primary_node))
1759 22b7f6f8 Thomas Thrainer
1760 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_instance_shutdown(self.instance.primary_node,
1761 d0d7d7cf Thomas Thrainer
                                             self.instance,
1762 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1763 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1764 c7dd65be Klaus Aehlig
    if self.op.ignore_failures:
1765 c7dd65be Klaus Aehlig
      result.Warn("Warning: can't shutdown instance", feedback_fn)
1766 c7dd65be Klaus Aehlig
    else:
1767 c7dd65be Klaus Aehlig
      result.Raise("Could not shutdown instance %s on node %s" %
1768 d0d7d7cf Thomas Thrainer
                   (self.instance.name,
1769 d0d7d7cf Thomas Thrainer
                    self.cfg.GetNodeName(self.instance.primary_node)))
1770 22b7f6f8 Thomas Thrainer
1771 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1772 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1773 d0d7d7cf Thomas Thrainer
    assert not (set(self.instance.all_nodes) -
1774 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
1775 22b7f6f8 Thomas Thrainer
      "Not owning correct locks"
1776 22b7f6f8 Thomas Thrainer
1777 d0d7d7cf Thomas Thrainer
    RemoveInstance(self, feedback_fn, self.instance, self.op.ignore_failures)
1778 22b7f6f8 Thomas Thrainer
1779 22b7f6f8 Thomas Thrainer
1780 22b7f6f8 Thomas Thrainer
class LUInstanceMove(LogicalUnit):
1781 22b7f6f8 Thomas Thrainer
  """Move an instance by data-copying.
1782 22b7f6f8 Thomas Thrainer

1783 22b7f6f8 Thomas Thrainer
  """
1784 22b7f6f8 Thomas Thrainer
  HPATH = "instance-move"
1785 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1786 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1787 22b7f6f8 Thomas Thrainer
1788 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1789 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1790 1c3231aa Thomas Thrainer
    (self.op.target_node_uuid, self.op.target_node) = \
1791 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.target_node_uuid,
1792 1c3231aa Thomas Thrainer
                            self.op.target_node)
1793 b9aae98b Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = [self.op.target_node_uuid]
1794 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1795 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
1796 22b7f6f8 Thomas Thrainer
1797 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1798 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1799 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes(primary_only=True)
1800 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1801 22b7f6f8 Thomas Thrainer
      # Copy node locks
1802 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1803 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1804 22b7f6f8 Thomas Thrainer
1805 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1806 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1807 22b7f6f8 Thomas Thrainer

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

1810 22b7f6f8 Thomas Thrainer
    """
1811 22b7f6f8 Thomas Thrainer
    env = {
1812 22b7f6f8 Thomas Thrainer
      "TARGET_NODE": self.op.target_node,
1813 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
1814 22b7f6f8 Thomas Thrainer
      }
1815 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
1816 22b7f6f8 Thomas Thrainer
    return env
1817 22b7f6f8 Thomas Thrainer
1818 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1819 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1820 22b7f6f8 Thomas Thrainer

1821 22b7f6f8 Thomas Thrainer
    """
1822 22b7f6f8 Thomas Thrainer
    nl = [
1823 22b7f6f8 Thomas Thrainer
      self.cfg.GetMasterNode(),
1824 22b7f6f8 Thomas Thrainer
      self.instance.primary_node,
1825 1c3231aa Thomas Thrainer
      self.op.target_node_uuid,
1826 22b7f6f8 Thomas Thrainer
      ]
1827 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1828 22b7f6f8 Thomas Thrainer
1829 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1830 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1831 22b7f6f8 Thomas Thrainer

1832 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1833 22b7f6f8 Thomas Thrainer

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

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

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

1983 22b7f6f8 Thomas Thrainer
  """
1984 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1985 22b7f6f8 Thomas Thrainer
1986 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1987 22b7f6f8 Thomas Thrainer
    """Check arguments.
1988 22b7f6f8 Thomas Thrainer

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

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

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

2109 22b7f6f8 Thomas Thrainer
    """
2110 0c072225 Thomas Thrainer
    if self.op.iallocator:
2111 0c072225 Thomas Thrainer
      (allocatable, failed_insts) = self.ia_result
2112 0c072225 Thomas Thrainer
      allocatable_insts = map(compat.fst, allocatable)
2113 0c072225 Thomas Thrainer
    else:
2114 0c072225 Thomas Thrainer
      allocatable_insts = [op.instance_name for op in self.op.instances]
2115 0c072225 Thomas Thrainer
      failed_insts = []
2116 0c072225 Thomas Thrainer
2117 22b7f6f8 Thomas Thrainer
    return {
2118 1ca326c8 Thomas Thrainer
      constants.ALLOCATABLE_KEY: allocatable_insts,
2119 1ca326c8 Thomas Thrainer
      constants.FAILED_KEY: failed_insts,
2120 22b7f6f8 Thomas Thrainer
      }
2121 22b7f6f8 Thomas Thrainer
2122 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2123 22b7f6f8 Thomas Thrainer
    """Executes the opcode.
2124 22b7f6f8 Thomas Thrainer

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

2155 22b7f6f8 Thomas Thrainer
  Used by L{LUInstanceSetParams}.
2156 22b7f6f8 Thomas Thrainer

2157 22b7f6f8 Thomas Thrainer
  """
2158 22b7f6f8 Thomas Thrainer
  def __init__(self):
2159 22b7f6f8 Thomas Thrainer
    self.params = None
2160 22b7f6f8 Thomas Thrainer
    self.filled = None
2161 22b7f6f8 Thomas Thrainer
2162 22b7f6f8 Thomas Thrainer
2163 5eacbcae Thomas Thrainer
def _PrepareContainerMods(mods, private_fn):
2164 22b7f6f8 Thomas Thrainer
  """Prepares a list of container modifications by adding a private data field.
2165 22b7f6f8 Thomas Thrainer

2166 22b7f6f8 Thomas Thrainer
  @type mods: list of tuples; (operation, index, parameters)
2167 22b7f6f8 Thomas Thrainer
  @param mods: List of modifications
2168 22b7f6f8 Thomas Thrainer
  @type private_fn: callable or None
2169 22b7f6f8 Thomas Thrainer
  @param private_fn: Callable for constructing a private data field for a
2170 22b7f6f8 Thomas Thrainer
    modification
2171 22b7f6f8 Thomas Thrainer
  @rtype: list
2172 22b7f6f8 Thomas Thrainer

2173 22b7f6f8 Thomas Thrainer
  """
2174 22b7f6f8 Thomas Thrainer
  if private_fn is None:
2175 22b7f6f8 Thomas Thrainer
    fn = lambda: None
2176 22b7f6f8 Thomas Thrainer
  else:
2177 22b7f6f8 Thomas Thrainer
    fn = private_fn
2178 22b7f6f8 Thomas Thrainer
2179 22b7f6f8 Thomas Thrainer
  return [(op, idx, params, fn()) for (op, idx, params) in mods]
2180 22b7f6f8 Thomas Thrainer
2181 22b7f6f8 Thomas Thrainer
2182 1c3231aa Thomas Thrainer
def _CheckNodesPhysicalCPUs(lu, node_uuids, requested, hypervisor_specs):
2183 22b7f6f8 Thomas Thrainer
  """Checks if nodes have enough physical CPUs
2184 22b7f6f8 Thomas Thrainer

2185 22b7f6f8 Thomas Thrainer
  This function checks if all given nodes have the needed number of
2186 22b7f6f8 Thomas Thrainer
  physical CPUs. In case any node has less CPUs or we cannot get the
2187 22b7f6f8 Thomas Thrainer
  information from the node, this function raises an OpPrereqError
2188 22b7f6f8 Thomas Thrainer
  exception.
2189 22b7f6f8 Thomas Thrainer

2190 22b7f6f8 Thomas Thrainer
  @type lu: C{LogicalUnit}
2191 22b7f6f8 Thomas Thrainer
  @param lu: a logical unit from which we get configuration data
2192 1c3231aa Thomas Thrainer
  @type node_uuids: C{list}
2193 1c3231aa Thomas Thrainer
  @param node_uuids: the list of node UUIDs to check
2194 22b7f6f8 Thomas Thrainer
  @type requested: C{int}
2195 22b7f6f8 Thomas Thrainer
  @param requested: the minimum acceptable number of physical CPUs
2196 a295eb80 Helga Velroyen
  @type hypervisor_specs: list of pairs (string, dict of strings)
2197 a295eb80 Helga Velroyen
  @param hypervisor_specs: list of hypervisor specifications in
2198 a295eb80 Helga Velroyen
      pairs (hypervisor_name, hvparams)
2199 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node doesn't have enough CPUs,
2200 22b7f6f8 Thomas Thrainer
      or we cannot check the node
2201 22b7f6f8 Thomas Thrainer

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

2224 22b7f6f8 Thomas Thrainer
  @type identifier: string
2225 22b7f6f8 Thomas Thrainer
  @param identifier: Item index or name or UUID
2226 22b7f6f8 Thomas Thrainer
  @type kind: string
2227 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2228 22b7f6f8 Thomas Thrainer
  @type container: list
2229 22b7f6f8 Thomas Thrainer
  @param container: Container to get the item from
2230 22b7f6f8 Thomas Thrainer

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

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

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

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

2360 22b7f6f8 Thomas Thrainer
  """
2361 22b7f6f8 Thomas Thrainer
  for (idx, disk) in enumerate(disks):
2362 22b7f6f8 Thomas Thrainer
    disk.iv_name = "disk/%s" % (base_index + idx, )
2363 22b7f6f8 Thomas Thrainer
2364 22b7f6f8 Thomas Thrainer
2365 22b7f6f8 Thomas Thrainer
class LUInstanceSetParams(LogicalUnit):
2366 22b7f6f8 Thomas Thrainer
  """Modifies an instances's parameters.
2367 22b7f6f8 Thomas Thrainer

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

2404 22b7f6f8 Thomas Thrainer
    """
2405 22b7f6f8 Thomas Thrainer
    for (op, _, params) in mods:
2406 22b7f6f8 Thomas Thrainer
      assert ht.TDict(params)
2407 22b7f6f8 Thomas Thrainer
2408 22b7f6f8 Thomas Thrainer
      # If 'key_types' is an empty dict, we assume we have an
2409 22b7f6f8 Thomas Thrainer
      # 'ext' template and thus do not ForceDictType
2410 22b7f6f8 Thomas Thrainer
      if key_types:
2411 22b7f6f8 Thomas Thrainer
        utils.ForceDictType(params, key_types)
2412 22b7f6f8 Thomas Thrainer
2413 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2414 22b7f6f8 Thomas Thrainer
        if params:
2415 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("No settings should be passed when"
2416 22b7f6f8 Thomas Thrainer
                                     " removing a %s" % kind,
2417 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2418 22b7f6f8 Thomas Thrainer
      elif op in (constants.DDM_ADD, constants.DDM_MODIFY):
2419 22b7f6f8 Thomas Thrainer
        item_fn(op, params)
2420 22b7f6f8 Thomas Thrainer
      else:
2421 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
2422 22b7f6f8 Thomas Thrainer
2423 affe1792 Klaus Aehlig
  def _VerifyDiskModification(self, op, params, excl_stor):
2424 22b7f6f8 Thomas Thrainer
    """Verifies a disk modification.
2425 22b7f6f8 Thomas Thrainer

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

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

2581 22b7f6f8 Thomas Thrainer
    This runs on the master, primary and secondaries.
2582 22b7f6f8 Thomas Thrainer

2583 22b7f6f8 Thomas Thrainer
    """
2584 22b7f6f8 Thomas Thrainer
    args = {}
2585 22b7f6f8 Thomas Thrainer
    if constants.BE_MINMEM in self.be_new:
2586 22b7f6f8 Thomas Thrainer
      args["minmem"] = self.be_new[constants.BE_MINMEM]
2587 22b7f6f8 Thomas Thrainer
    if constants.BE_MAXMEM in self.be_new:
2588 22b7f6f8 Thomas Thrainer
      args["maxmem"] = self.be_new[constants.BE_MAXMEM]
2589 22b7f6f8 Thomas Thrainer
    if constants.BE_VCPUS in self.be_new:
2590 22b7f6f8 Thomas Thrainer
      args["vcpus"] = self.be_new[constants.BE_VCPUS]
2591 22b7f6f8 Thomas Thrainer
    # TODO: export disk changes. Note: _BuildInstanceHookEnv* don't export disk
2592 22b7f6f8 Thomas Thrainer
    # information at all.
2593 22b7f6f8 Thomas Thrainer
2594 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
2595 22b7f6f8 Thomas Thrainer
      nics = []
2596 22b7f6f8 Thomas Thrainer
2597 22b7f6f8 Thomas Thrainer
      for nic in self._new_nics:
2598 22b7f6f8 Thomas Thrainer
        n = copy.deepcopy(nic)
2599 22b7f6f8 Thomas Thrainer
        nicparams = self.cluster.SimpleFillNIC(n.nicparams)
2600 22b7f6f8 Thomas Thrainer
        n.nicparams = nicparams
2601 5eacbcae Thomas Thrainer
        nics.append(NICToTuple(self, n))
2602 22b7f6f8 Thomas Thrainer
2603 22b7f6f8 Thomas Thrainer
      args["nics"] = nics
2604 22b7f6f8 Thomas Thrainer
2605 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance, override=args)
2606 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
2607 22b7f6f8 Thomas Thrainer
      env["NEW_DISK_TEMPLATE"] = self.op.disk_template
2608 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
2609 22b7f6f8 Thomas Thrainer
      env["RUNTIME_MEMORY"] = self.op.runtime_mem
2610 22b7f6f8 Thomas Thrainer
2611 22b7f6f8 Thomas Thrainer
    return env
2612 22b7f6f8 Thomas Thrainer
2613 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2614 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2615 22b7f6f8 Thomas Thrainer

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

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

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

2914 9808764a Jose A. Lopes
    The NICS are dynamically created using the Dynamic Device
2915 9808764a Jose A. Lopes
    Modification (DDM).  This function produces a NIC modification
2916 9808764a Jose A. Lopes
    (mod) that inserts an additional NIC meant for instance
2917 9808764a Jose A. Lopes
    communication in or removes an existing instance communication NIC
2918 9808764a Jose A. Lopes
    from a running instance, using DDM.
2919 9808764a Jose A. Lopes

2920 9808764a Jose A. Lopes
    @type cfg: L{config.ConfigWriter}
2921 9808764a Jose A. Lopes
    @param cfg: cluster configuration
2922 9808764a Jose A. Lopes

2923 9808764a Jose A. Lopes
    @type instance_communication: boolean
2924 9808764a Jose A. Lopes
    @param instance_communication: whether instance communication is
2925 9808764a Jose A. Lopes
                                   enabled or disabled
2926 9808764a Jose A. Lopes

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

2930 9808764a Jose A. Lopes
    @rtype: (L{constants.DDM_ADD}, -1, parameters) or
2931 9808764a Jose A. Lopes
            (L{constants.DDM_REMOVE}, -1, parameters) or
2932 9808764a Jose A. Lopes
            L{None}
2933 9808764a Jose A. Lopes
    @return: DDM mod containing an action to add or remove the NIC, or
2934 9808764a Jose A. Lopes
             None if nothing needs to be done
2935 9808764a Jose A. Lopes

2936 9808764a Jose A. Lopes
    """
2937 e25625ab Jose A. Lopes
    nic_name = _ComputeInstanceCommunicationNIC(instance.name)
2938 9808764a Jose A. Lopes
2939 9808764a Jose A. Lopes
    instance_communication_nic = None
2940 9808764a Jose A. Lopes
2941 9808764a Jose A. Lopes
    for nic in instance.nics:
2942 9808764a Jose A. Lopes
      if nic.name == nic_name:
2943 9808764a Jose A. Lopes
        instance_communication_nic = nic
2944 9808764a Jose A. Lopes
        break
2945 9808764a Jose A. Lopes
2946 9808764a Jose A. Lopes
    if instance_communication and not instance_communication_nic:
2947 9808764a Jose A. Lopes
      action = constants.DDM_ADD
2948 9808764a Jose A. Lopes
      params = {constants.INIC_NAME: nic_name,
2949 9808764a Jose A. Lopes
                constants.INIC_MAC: constants.VALUE_GENERATE,
2950 9808764a Jose A. Lopes
                constants.INIC_IP: constants.NIC_IP_POOL,
2951 9808764a Jose A. Lopes
                constants.INIC_NETWORK:
2952 9808764a Jose A. Lopes
                  cfg.GetInstanceCommunicationNetwork()}
2953 9808764a Jose A. Lopes
    elif not instance_communication and instance_communication_nic:
2954 9808764a Jose A. Lopes
      action = constants.DDM_REMOVE
2955 9808764a Jose A. Lopes
      params = None
2956 9808764a Jose A. Lopes
    else:
2957 9808764a Jose A. Lopes
      action = None
2958 9808764a Jose A. Lopes
      params = None
2959 9808764a Jose A. Lopes
2960 9808764a Jose A. Lopes
    if action is not None:
2961 9808764a Jose A. Lopes
      return (action, -1, params)
2962 9808764a Jose A. Lopes
    else:
2963 9808764a Jose A. Lopes
      return None
2964 9808764a Jose A. Lopes
2965 1bb99a33 Bernardo Dal Seno
  def CheckPrereq(self):
2966 1bb99a33 Bernardo Dal Seno
    """Check prerequisites.
2967 1bb99a33 Bernardo Dal Seno

2968 1bb99a33 Bernardo Dal Seno
    This only checks the instance list against the existing names.
2969 1bb99a33 Bernardo Dal Seno

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

3315 22b7f6f8 Thomas Thrainer
    """
3316 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to drbd")
3317 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3318 1c3231aa Thomas Thrainer
    snode_uuid = self.op.remote_node_uuid
3319 22b7f6f8 Thomas Thrainer
3320 d0d7d7cf Thomas Thrainer
    assert self.instance.disk_template == constants.DT_PLAIN
3321 22b7f6f8 Thomas Thrainer
3322 22b7f6f8 Thomas Thrainer
    # create a fake disk info for _GenerateDiskTemplate
3323 22b7f6f8 Thomas Thrainer
    disk_info = [{constants.IDISK_SIZE: d.size, constants.IDISK_MODE: d.mode,
3324 22b7f6f8 Thomas Thrainer
                  constants.IDISK_VG: d.logical_id[0],
3325 22b7f6f8 Thomas Thrainer
                  constants.IDISK_NAME: d.name}
3326 d0d7d7cf Thomas Thrainer
                 for d in self.instance.disks]
3327 5eacbcae Thomas Thrainer
    new_disks = GenerateDiskTemplate(self, self.op.disk_template,
3328 da4a52a3 Thomas Thrainer
                                     self.instance.uuid, pnode_uuid,
3329 d0d7d7cf Thomas Thrainer
                                     [snode_uuid], disk_info, None, None, 0,
3330 d0d7d7cf Thomas Thrainer
                                     feedback_fn, self.diskparams)
3331 0c3d9c7c Thomas Thrainer
    anno_disks = rpc.AnnotateDiskParams(new_disks, self.diskparams)
3332 1c3231aa Thomas Thrainer
    p_excl_stor = IsExclusiveStorageEnabledNodeUuid(self.cfg, pnode_uuid)
3333 1c3231aa Thomas Thrainer
    s_excl_stor = IsExclusiveStorageEnabledNodeUuid(self.cfg, snode_uuid)
3334 d0d7d7cf Thomas Thrainer
    info = GetInstanceInfoText(self.instance)
3335 22b7f6f8 Thomas Thrainer
    feedback_fn("Creating additional volumes...")
3336 22b7f6f8 Thomas Thrainer
    # first, create the missing data and meta devices
3337 22b7f6f8 Thomas Thrainer
    for disk in anno_disks:
3338 22b7f6f8 Thomas Thrainer
      # unfortunately this is... not too nice
3339 d0d7d7cf Thomas Thrainer
      CreateSingleBlockDev(self, pnode_uuid, self.instance, disk.children[1],
3340 5eacbcae Thomas Thrainer
                           info, True, p_excl_stor)
3341 22b7f6f8 Thomas Thrainer
      for child in disk.children:
3342 d0d7d7cf Thomas Thrainer
        CreateSingleBlockDev(self, snode_uuid, self.instance, child, info, True,
3343 5eacbcae Thomas Thrainer
                             s_excl_stor)
3344 22b7f6f8 Thomas Thrainer
    # at this stage, all new LVs have been created, we can rename the
3345 22b7f6f8 Thomas Thrainer
    # old ones
3346 22b7f6f8 Thomas Thrainer
    feedback_fn("Renaming original volumes...")
3347 22b7f6f8 Thomas Thrainer
    rename_list = [(o, n.children[0].logical_id)
3348 d0d7d7cf Thomas Thrainer
                   for (o, n) in zip(self.instance.disks, new_disks)]
3349 1c3231aa Thomas Thrainer
    result = self.rpc.call_blockdev_rename(pnode_uuid, rename_list)
3350 22b7f6f8 Thomas Thrainer
    result.Raise("Failed to rename original LVs")
3351 22b7f6f8 Thomas Thrainer
3352 22b7f6f8 Thomas Thrainer
    feedback_fn("Initializing DRBD devices...")
3353 22b7f6f8 Thomas Thrainer
    # all child devices are in place, we can now create the DRBD devices
3354 22b7f6f8 Thomas Thrainer
    try:
3355 22b7f6f8 Thomas Thrainer
      for disk in anno_disks:
3356 1c3231aa Thomas Thrainer
        for (node_uuid, excl_stor) in [(pnode_uuid, p_excl_stor),
3357 1c3231aa Thomas Thrainer
                                       (snode_uuid, s_excl_stor)]:
3358 1c3231aa Thomas Thrainer
          f_create = node_uuid == pnode_uuid
3359 d0d7d7cf Thomas Thrainer
          CreateSingleBlockDev(self, node_uuid, self.instance, disk, info,
3360 d0d7d7cf Thomas Thrainer
                               f_create, excl_stor)
3361 22b7f6f8 Thomas Thrainer
    except errors.GenericError, e:
3362 22b7f6f8 Thomas Thrainer
      feedback_fn("Initializing of DRBD devices failed;"
3363 22b7f6f8 Thomas Thrainer
                  " renaming back original volumes...")
3364 22b7f6f8 Thomas Thrainer
      rename_back_list = [(n.children[0], o.logical_id)
3365 d0d7d7cf Thomas Thrainer
                          for (n, o) in zip(new_disks, self.instance.disks)]
3366 1c3231aa Thomas Thrainer
      result = self.rpc.call_blockdev_rename(pnode_uuid, rename_back_list)
3367 22b7f6f8 Thomas Thrainer
      result.Raise("Failed to rename LVs back after error %s" % str(e))
3368 22b7f6f8 Thomas Thrainer
      raise
3369 22b7f6f8 Thomas Thrainer
3370 22b7f6f8 Thomas Thrainer
    # at this point, the instance has been modified
3371 d0d7d7cf Thomas Thrainer
    self.instance.disk_template = constants.DT_DRBD8
3372 d0d7d7cf Thomas Thrainer
    self.instance.disks = new_disks
3373 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
3374 22b7f6f8 Thomas Thrainer
3375 22b7f6f8 Thomas Thrainer
    # Release node locks while waiting for sync
3376 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3377 22b7f6f8 Thomas Thrainer
3378 22b7f6f8 Thomas Thrainer
    # disks are created, waiting for sync
3379 d0d7d7cf Thomas Thrainer
    disk_abort = not WaitForSync(self, self.instance,
3380 5eacbcae Thomas Thrainer
                                 oneshot=not self.op.wait_for_sync)
3381 22b7f6f8 Thomas Thrainer
    if disk_abort:
3382 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("There are some degraded disks for"
3383 22b7f6f8 Thomas Thrainer
                               " this instance, please cleanup manually")
3384 22b7f6f8 Thomas Thrainer
3385 22b7f6f8 Thomas Thrainer
    # Node resource locks will be released by caller
3386 22b7f6f8 Thomas Thrainer
3387 22b7f6f8 Thomas Thrainer
  def _ConvertDrbdToPlain(self, feedback_fn):
3388 22b7f6f8 Thomas Thrainer
    """Converts an instance from drbd to plain.
3389 22b7f6f8 Thomas Thrainer

3390 22b7f6f8 Thomas Thrainer
    """
3391 d0d7d7cf Thomas Thrainer
    assert len(self.instance.secondary_nodes) == 1
3392 d0d7d7cf Thomas Thrainer
    assert self.instance.disk_template == constants.DT_DRBD8
3393 22b7f6f8 Thomas Thrainer
3394 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3395 d0d7d7cf Thomas Thrainer
    snode_uuid = self.instance.secondary_nodes[0]
3396 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to plain")
3397 22b7f6f8 Thomas Thrainer
3398 d0d7d7cf Thomas Thrainer
    old_disks = AnnotateDiskParams(self.instance, self.instance.disks, self.cfg)
3399 d0d7d7cf Thomas Thrainer
    new_disks = [d.children[0] for d in self.instance.disks]
3400 22b7f6f8 Thomas Thrainer
3401 22b7f6f8 Thomas Thrainer
    # copy over size, mode and name
3402 22b7f6f8 Thomas Thrainer
    for parent, child in zip(old_disks, new_disks):
3403 22b7f6f8 Thomas Thrainer
      child.size = parent.size
3404 22b7f6f8 Thomas Thrainer
      child.mode = parent.mode
3405 22b7f6f8 Thomas Thrainer
      child.name = parent.name
3406 22b7f6f8 Thomas Thrainer
3407 22b7f6f8 Thomas Thrainer
    # this is a DRBD disk, return its port to the pool
3408 22b7f6f8 Thomas Thrainer
    # NOTE: this must be done right before the call to cfg.Update!
3409 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3410 22b7f6f8 Thomas Thrainer
      tcp_port = disk.logical_id[2]
3411 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(tcp_port)
3412 22b7f6f8 Thomas Thrainer
3413 22b7f6f8 Thomas Thrainer
    # update instance structure
3414 d0d7d7cf Thomas Thrainer
    self.instance.disks = new_disks
3415 d0d7d7cf Thomas Thrainer
    self.instance.disk_template = constants.DT_PLAIN
3416 d0d7d7cf Thomas Thrainer
    _UpdateIvNames(0, self.instance.disks)
3417 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
3418 22b7f6f8 Thomas Thrainer
3419 22b7f6f8 Thomas Thrainer
    # Release locks in case removing disks takes a while
3420 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3421 22b7f6f8 Thomas Thrainer
3422 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing volumes on the secondary node...")
3423 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3424 0c3d9c7c Thomas Thrainer
      result = self.rpc.call_blockdev_remove(snode_uuid, (disk, self.instance))
3425 aefc2f89 Thomas Thrainer
      result.Warn("Could not remove block device %s on node %s,"
3426 aefc2f89 Thomas Thrainer
                  " continuing anyway" %
3427 aefc2f89 Thomas Thrainer
                  (disk.iv_name, self.cfg.GetNodeName(snode_uuid)),
3428 aefc2f89 Thomas Thrainer
                  self.LogWarning)
3429 22b7f6f8 Thomas Thrainer
3430 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing unneeded volumes on the primary node...")
3431 22b7f6f8 Thomas Thrainer
    for idx, disk in enumerate(old_disks):
3432 22b7f6f8 Thomas Thrainer
      meta = disk.children[1]
3433 0c3d9c7c Thomas Thrainer
      result = self.rpc.call_blockdev_remove(pnode_uuid, (meta, self.instance))
3434 aefc2f89 Thomas Thrainer
      result.Warn("Could not remove metadata for disk %d on node %s,"
3435 aefc2f89 Thomas Thrainer
                  " continuing anyway" %
3436 aefc2f89 Thomas Thrainer
                  (idx, self.cfg.GetNodeName(pnode_uuid)),
3437 aefc2f89 Thomas Thrainer
                  self.LogWarning)
3438 22b7f6f8 Thomas Thrainer
3439 ba924970 Dimitris Aragiorgis
  def _HotplugDevice(self, action, dev_type, device, extra, seq):
3440 ba924970 Dimitris Aragiorgis
    self.LogInfo("Trying to hotplug device...")
3441 e15a00dc Dimitris Aragiorgis
    msg = "hotplug:"
3442 ba924970 Dimitris Aragiorgis
    result = self.rpc.call_hotplug_device(self.instance.primary_node,
3443 ba924970 Dimitris Aragiorgis
                                          self.instance, action, dev_type,
3444 51951d38 Dimitris Aragiorgis
                                          (device, self.instance),
3445 51951d38 Dimitris Aragiorgis
                                          extra, seq)
3446 ba924970 Dimitris Aragiorgis
    if result.fail_msg:
3447 ba924970 Dimitris Aragiorgis
      self.LogWarning("Could not hotplug device: %s" % result.fail_msg)
3448 ba924970 Dimitris Aragiorgis
      self.LogInfo("Continuing execution..")
3449 e15a00dc Dimitris Aragiorgis
      msg += "failed"
3450 ba924970 Dimitris Aragiorgis
    else:
3451 ba924970 Dimitris Aragiorgis
      self.LogInfo("Hotplug done.")
3452 e15a00dc Dimitris Aragiorgis
      msg += "done"
3453 e15a00dc Dimitris Aragiorgis
    return msg
3454 ba924970 Dimitris Aragiorgis
3455 22b7f6f8 Thomas Thrainer
  def _CreateNewDisk(self, idx, params, _):
3456 22b7f6f8 Thomas Thrainer
    """Creates a new disk.
3457 22b7f6f8 Thomas Thrainer

3458 22b7f6f8 Thomas Thrainer
    """
3459 22b7f6f8 Thomas Thrainer
    # add a new disk
3460 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DTS_FILEBASED:
3461 d0d7d7cf Thomas Thrainer
      (file_driver, file_path) = self.instance.disks[0].logical_id
3462 22b7f6f8 Thomas Thrainer
      file_path = os.path.dirname(file_path)
3463 22b7f6f8 Thomas Thrainer
    else:
3464 22b7f6f8 Thomas Thrainer
      file_driver = file_path = None
3465 22b7f6f8 Thomas Thrainer
3466 22b7f6f8 Thomas Thrainer
    disk = \
3467 d0d7d7cf Thomas Thrainer
      GenerateDiskTemplate(self, self.instance.disk_template,
3468 da4a52a3 Thomas Thrainer
                           self.instance.uuid, self.instance.primary_node,
3469 d0d7d7cf Thomas Thrainer
                           self.instance.secondary_nodes, [params], file_path,
3470 d0d7d7cf Thomas Thrainer
                           file_driver, idx, self.Log, self.diskparams)[0]
3471 22b7f6f8 Thomas Thrainer
3472 d0d7d7cf Thomas Thrainer
    new_disks = CreateDisks(self, self.instance, disks=[disk])
3473 22b7f6f8 Thomas Thrainer
3474 22b7f6f8 Thomas Thrainer
    if self.cluster.prealloc_wipe_disks:
3475 22b7f6f8 Thomas Thrainer
      # Wipe new disk
3476 d0d7d7cf Thomas Thrainer
      WipeOrCleanupDisks(self, self.instance,
3477 a365b47f Bernardo Dal Seno
                         disks=[(idx, disk, 0)],
3478 a365b47f Bernardo Dal Seno
                         cleanup=new_disks)
3479 22b7f6f8 Thomas Thrainer
3480 e15a00dc Dimitris Aragiorgis
    changes = [
3481 e15a00dc Dimitris Aragiorgis
      ("disk/%d" % idx,
3482 b15d5bd3 Petr Pudlak
       "add:size=%s,mode=%s" % (disk.size, disk.mode)),
3483 e15a00dc Dimitris Aragiorgis
      ]
3484 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3485 ba924970 Dimitris Aragiorgis
      result = self.rpc.call_blockdev_assemble(self.instance.primary_node,
3486 ba924970 Dimitris Aragiorgis
                                               (disk, self.instance),
3487 ba924970 Dimitris Aragiorgis
                                               self.instance.name, True, idx)
3488 ba924970 Dimitris Aragiorgis
      if result.fail_msg:
3489 e15a00dc Dimitris Aragiorgis
        changes.append(("disk/%d" % idx, "assemble:failed"))
3490 ba924970 Dimitris Aragiorgis
        self.LogWarning("Can't assemble newly created disk %d: %s",
3491 ba924970 Dimitris Aragiorgis
                        idx, result.fail_msg)
3492 ba924970 Dimitris Aragiorgis
      else:
3493 ba924970 Dimitris Aragiorgis
        _, link_name = result.payload
3494 e15a00dc Dimitris Aragiorgis
        msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
3495 e15a00dc Dimitris Aragiorgis
                                  constants.HOTPLUG_TARGET_DISK,
3496 e15a00dc Dimitris Aragiorgis
                                  disk, link_name, idx)
3497 e15a00dc Dimitris Aragiorgis
        changes.append(("disk/%d" % idx, msg))
3498 ba924970 Dimitris Aragiorgis
3499 e15a00dc Dimitris Aragiorgis
    return (disk, changes)
3500 22b7f6f8 Thomas Thrainer
3501 922a9e65 Thomas Thrainer
  def _PostAddDisk(self, _, disk):
3502 922a9e65 Thomas Thrainer
    if not WaitForSync(self, self.instance, disks=[disk],
3503 922a9e65 Thomas Thrainer
                       oneshot=not self.op.wait_for_sync):
3504 922a9e65 Thomas Thrainer
      raise errors.OpExecError("Failed to sync disks of %s" %
3505 922a9e65 Thomas Thrainer
                               self.instance.name)
3506 922a9e65 Thomas Thrainer
3507 3c260845 Thomas Thrainer
    # the disk is active at this point, so deactivate it if the instance disks
3508 3c260845 Thomas Thrainer
    # are supposed to be inactive
3509 3c260845 Thomas Thrainer
    if not self.instance.disks_active:
3510 3c260845 Thomas Thrainer
      ShutdownInstanceDisks(self, self.instance, disks=[disk])
3511 3c260845 Thomas Thrainer
3512 c5c72215 Dimitris Aragiorgis
  def _ModifyDisk(self, idx, disk, params, _):
3513 22b7f6f8 Thomas Thrainer
    """Modifies a disk.
3514 22b7f6f8 Thomas Thrainer

3515 22b7f6f8 Thomas Thrainer
    """
3516 22b7f6f8 Thomas Thrainer
    changes = []
3517 4eef428e Dimitris Aragiorgis
    if constants.IDISK_MODE in params:
3518 4eef428e Dimitris Aragiorgis
      disk.mode = params.get(constants.IDISK_MODE)
3519 22b7f6f8 Thomas Thrainer
      changes.append(("disk.mode/%d" % idx, disk.mode))
3520 22b7f6f8 Thomas Thrainer
3521 4eef428e Dimitris Aragiorgis
    if constants.IDISK_NAME in params:
3522 4eef428e Dimitris Aragiorgis
      disk.name = params.get(constants.IDISK_NAME)
3523 4eef428e Dimitris Aragiorgis
      changes.append(("disk.name/%d" % idx, disk.name))
3524 22b7f6f8 Thomas Thrainer
3525 c5c72215 Dimitris Aragiorgis
    # Modify arbitrary params in case instance template is ext
3526 c5c72215 Dimitris Aragiorgis
    for key, value in params.iteritems():
3527 c5c72215 Dimitris Aragiorgis
      if (key not in constants.MODIFIABLE_IDISK_PARAMS and
3528 c5c72215 Dimitris Aragiorgis
          self.instance.disk_template == constants.DT_EXT):
3529 e228ab9c Dimitris Aragiorgis
        # stolen from GetUpdatedParams: default means reset/delete
3530 e228ab9c Dimitris Aragiorgis
        if value.lower() == constants.VALUE_DEFAULT:
3531 e228ab9c Dimitris Aragiorgis
          try:
3532 e228ab9c Dimitris Aragiorgis
            del disk.params[key]
3533 e228ab9c Dimitris Aragiorgis
          except KeyError:
3534 e228ab9c Dimitris Aragiorgis
            pass
3535 e228ab9c Dimitris Aragiorgis
        else:
3536 e228ab9c Dimitris Aragiorgis
          disk.params[key] = value
3537 c5c72215 Dimitris Aragiorgis
        changes.append(("disk.params:%s/%d" % (key, idx), value))
3538 22b7f6f8 Thomas Thrainer
3539 22b7f6f8 Thomas Thrainer
    return changes
3540 22b7f6f8 Thomas Thrainer
3541 22b7f6f8 Thomas Thrainer
  def _RemoveDisk(self, idx, root, _):
3542 22b7f6f8 Thomas Thrainer
    """Removes a disk.
3543 22b7f6f8 Thomas Thrainer

3544 22b7f6f8 Thomas Thrainer
    """
3545 e15a00dc Dimitris Aragiorgis
    hotmsg = ""
3546 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3547 e15a00dc Dimitris Aragiorgis
      hotmsg = self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
3548 e15a00dc Dimitris Aragiorgis
                                   constants.HOTPLUG_TARGET_DISK,
3549 e15a00dc Dimitris Aragiorgis
                                   root, None, idx)
3550 ba924970 Dimitris Aragiorgis
      ShutdownInstanceDisks(self, self.instance, [root])
3551 ba924970 Dimitris Aragiorgis
3552 5eacbcae Thomas Thrainer
    (anno_disk,) = AnnotateDiskParams(self.instance, [root], self.cfg)
3553 1c3231aa Thomas Thrainer
    for node_uuid, disk in anno_disk.ComputeNodeTree(
3554 1c3231aa Thomas Thrainer
                             self.instance.primary_node):
3555 0c3d9c7c Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(node_uuid, (disk, self.instance)) \
3556 0c3d9c7c Thomas Thrainer
              .fail_msg
3557 22b7f6f8 Thomas Thrainer
      if msg:
3558 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove disk/%d on node '%s': %s,"
3559 1c3231aa Thomas Thrainer
                        " continuing anyway", idx,
3560 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
3561 22b7f6f8 Thomas Thrainer
3562 22b7f6f8 Thomas Thrainer
    # if this is a DRBD disk, return its port to the pool
3563 66a37e7a Helga Velroyen
    if root.dev_type in constants.DTS_DRBD:
3564 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(root.logical_id[2])
3565 22b7f6f8 Thomas Thrainer
3566 e15a00dc Dimitris Aragiorgis
    return hotmsg
3567 e15a00dc Dimitris Aragiorgis
3568 22b7f6f8 Thomas Thrainer
  def _CreateNewNic(self, idx, params, private):
3569 22b7f6f8 Thomas Thrainer
    """Creates data structure for a new network interface.
3570 22b7f6f8 Thomas Thrainer

3571 22b7f6f8 Thomas Thrainer
    """
3572 22b7f6f8 Thomas Thrainer
    mac = params[constants.INIC_MAC]
3573 22b7f6f8 Thomas Thrainer
    ip = params.get(constants.INIC_IP, None)
3574 22b7f6f8 Thomas Thrainer
    net = params.get(constants.INIC_NETWORK, None)
3575 22b7f6f8 Thomas Thrainer
    name = params.get(constants.INIC_NAME, None)
3576 22b7f6f8 Thomas Thrainer
    net_uuid = self.cfg.LookupNetwork(net)
3577 22b7f6f8 Thomas Thrainer
    #TODO: not private.filled?? can a nic have no nicparams??
3578 22b7f6f8 Thomas Thrainer
    nicparams = private.filled
3579 22b7f6f8 Thomas Thrainer
    nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, name=name,
3580 22b7f6f8 Thomas Thrainer
                       nicparams=nicparams)
3581 22b7f6f8 Thomas Thrainer
    nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
3582 22b7f6f8 Thomas Thrainer
3583 e15a00dc Dimitris Aragiorgis
    changes = [
3584 22b7f6f8 Thomas Thrainer
      ("nic.%d" % idx,
3585 22b7f6f8 Thomas Thrainer
       "add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" %
3586 22b7f6f8 Thomas Thrainer
       (mac, ip, private.filled[constants.NIC_MODE],
3587 ba924970 Dimitris Aragiorgis
       private.filled[constants.NIC_LINK], net)),
3588 ba924970 Dimitris Aragiorgis
      ]
3589 ba924970 Dimitris Aragiorgis
3590 e15a00dc Dimitris Aragiorgis
    if self.op.hotplug:
3591 e15a00dc Dimitris Aragiorgis
      msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
3592 e15a00dc Dimitris Aragiorgis
                                constants.HOTPLUG_TARGET_NIC,
3593 e15a00dc Dimitris Aragiorgis
                                nobj, None, idx)
3594 e15a00dc Dimitris Aragiorgis
      changes.append(("nic.%d" % idx, msg))
3595 e15a00dc Dimitris Aragiorgis
3596 e15a00dc Dimitris Aragiorgis
    return (nobj, changes)
3597 22b7f6f8 Thomas Thrainer
3598 22b7f6f8 Thomas Thrainer
  def _ApplyNicMods(self, idx, nic, params, private):
3599 22b7f6f8 Thomas Thrainer
    """Modifies a network interface.
3600 22b7f6f8 Thomas Thrainer

3601 22b7f6f8 Thomas Thrainer
    """
3602 22b7f6f8 Thomas Thrainer
    changes = []
3603 22b7f6f8 Thomas Thrainer
3604 22b7f6f8 Thomas Thrainer
    for key in [constants.INIC_MAC, constants.INIC_IP, constants.INIC_NAME]:
3605 22b7f6f8 Thomas Thrainer
      if key in params:
3606 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), params[key]))
3607 22b7f6f8 Thomas Thrainer
        setattr(nic, key, params[key])
3608 22b7f6f8 Thomas Thrainer
3609 22b7f6f8 Thomas Thrainer
    new_net = params.get(constants.INIC_NETWORK, nic.network)
3610 22b7f6f8 Thomas Thrainer
    new_net_uuid = self.cfg.LookupNetwork(new_net)
3611 22b7f6f8 Thomas Thrainer
    if new_net_uuid != nic.network:
3612 22b7f6f8 Thomas Thrainer
      changes.append(("nic.network/%d" % idx, new_net))
3613 22b7f6f8 Thomas Thrainer
      nic.network = new_net_uuid
3614 22b7f6f8 Thomas Thrainer
3615 22b7f6f8 Thomas Thrainer
    if private.filled:
3616 22b7f6f8 Thomas Thrainer
      nic.nicparams = private.filled
3617 22b7f6f8 Thomas Thrainer
3618 22b7f6f8 Thomas Thrainer
      for (key, val) in nic.nicparams.items():
3619 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), val))
3620 22b7f6f8 Thomas Thrainer
3621 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3622 e15a00dc Dimitris Aragiorgis
      msg = self._HotplugDevice(constants.HOTPLUG_ACTION_MODIFY,
3623 e15a00dc Dimitris Aragiorgis
                                constants.HOTPLUG_TARGET_NIC,
3624 e15a00dc Dimitris Aragiorgis
                                nic, None, idx)
3625 e15a00dc Dimitris Aragiorgis
      changes.append(("nic/%d" % idx, msg))
3626 ba924970 Dimitris Aragiorgis
3627 22b7f6f8 Thomas Thrainer
    return changes
3628 22b7f6f8 Thomas Thrainer
3629 ba924970 Dimitris Aragiorgis
  def _RemoveNic(self, idx, nic, _):
3630 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3631 e15a00dc Dimitris Aragiorgis
      return self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
3632 e15a00dc Dimitris Aragiorgis
                                 constants.HOTPLUG_TARGET_NIC,
3633 e15a00dc Dimitris Aragiorgis
                                 nic, None, idx)
3634 ba924970 Dimitris Aragiorgis
3635 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3636 22b7f6f8 Thomas Thrainer
    """Modifies an instance.
3637 22b7f6f8 Thomas Thrainer

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

3640 22b7f6f8 Thomas Thrainer
    """
3641 22b7f6f8 Thomas Thrainer
    # Process here the warnings from CheckPrereq, as we don't have a
3642 22b7f6f8 Thomas Thrainer
    # feedback_fn there.
3643 22b7f6f8 Thomas Thrainer
    # TODO: Replace with self.LogWarning
3644 22b7f6f8 Thomas Thrainer
    for warn in self.warn:
3645 22b7f6f8 Thomas Thrainer
      feedback_fn("WARNING: %s" % warn)
3646 22b7f6f8 Thomas Thrainer
3647 22b7f6f8 Thomas Thrainer
    assert ((self.op.disk_template is None) ^
3648 22b7f6f8 Thomas Thrainer
            bool(self.owned_locks(locking.LEVEL_NODE_RES))), \
3649 22b7f6f8 Thomas Thrainer
      "Not owning any node resource locks"
3650 22b7f6f8 Thomas Thrainer
3651 22b7f6f8 Thomas Thrainer
    result = []
3652 22b7f6f8 Thomas Thrainer
3653 22b7f6f8 Thomas Thrainer
    # New primary node
3654 1c3231aa Thomas Thrainer
    if self.op.pnode_uuid:
3655 d0d7d7cf Thomas Thrainer
      self.instance.primary_node = self.op.pnode_uuid
3656 22b7f6f8 Thomas Thrainer
3657 22b7f6f8 Thomas Thrainer
    # runtime memory
3658 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
3659 d0d7d7cf Thomas Thrainer
      rpcres = self.rpc.call_instance_balloon_memory(self.instance.primary_node,
3660 d0d7d7cf Thomas Thrainer
                                                     self.instance,
3661 22b7f6f8 Thomas Thrainer
                                                     self.op.runtime_mem)
3662 22b7f6f8 Thomas Thrainer
      rpcres.Raise("Cannot modify instance runtime memory")
3663 22b7f6f8 Thomas Thrainer
      result.append(("runtime_memory", self.op.runtime_mem))
3664 22b7f6f8 Thomas Thrainer
3665 22b7f6f8 Thomas Thrainer
    # Apply disk changes
3666 d0d7d7cf Thomas Thrainer
    _ApplyContainerMods("disk", self.instance.disks, result, self.diskmod,
3667 5eacbcae Thomas Thrainer
                        self._CreateNewDisk, self._ModifyDisk,
3668 922a9e65 Thomas Thrainer
                        self._RemoveDisk, post_add_fn=self._PostAddDisk)
3669 d0d7d7cf Thomas Thrainer
    _UpdateIvNames(0, self.instance.disks)
3670 22b7f6f8 Thomas Thrainer
3671 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
3672 22b7f6f8 Thomas Thrainer
      if __debug__:
3673 d0d7d7cf Thomas Thrainer
        check_nodes = set(self.instance.all_nodes)
3674 1c3231aa Thomas Thrainer
        if self.op.remote_node_uuid:
3675 1c3231aa Thomas Thrainer
          check_nodes.add(self.op.remote_node_uuid)
3676 22b7f6f8 Thomas Thrainer
        for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
3677 22b7f6f8 Thomas Thrainer
          owned = self.owned_locks(level)
3678 22b7f6f8 Thomas Thrainer
          assert not (check_nodes - owned), \
3679 22b7f6f8 Thomas Thrainer
            ("Not owning the correct locks, owning %r, expected at least %r" %
3680 22b7f6f8 Thomas Thrainer
             (owned, check_nodes))
3681 22b7f6f8 Thomas Thrainer
3682 d0d7d7cf Thomas Thrainer
      r_shut = ShutdownInstanceDisks(self, self.instance)
3683 22b7f6f8 Thomas Thrainer
      if not r_shut:
3684 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Cannot shutdown instance disks, unable to"
3685 22b7f6f8 Thomas Thrainer
                                 " proceed with disk template conversion")
3686 d0d7d7cf Thomas Thrainer
      mode = (self.instance.disk_template, self.op.disk_template)
3687 22b7f6f8 Thomas Thrainer
      try:
3688 22b7f6f8 Thomas Thrainer
        self._DISK_CONVERSIONS[mode](self, feedback_fn)
3689 22b7f6f8 Thomas Thrainer
      except:
3690 da4a52a3 Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(self.instance.uuid)
3691 22b7f6f8 Thomas Thrainer
        raise
3692 22b7f6f8 Thomas Thrainer
      result.append(("disk_template", self.op.disk_template))
3693 22b7f6f8 Thomas Thrainer
3694 d0d7d7cf Thomas Thrainer
      assert self.instance.disk_template == self.op.disk_template, \
3695 22b7f6f8 Thomas Thrainer
        ("Expected disk template '%s', found '%s'" %
3696 d0d7d7cf Thomas Thrainer
         (self.op.disk_template, self.instance.disk_template))
3697 22b7f6f8 Thomas Thrainer
3698 22b7f6f8 Thomas Thrainer
    # Release node and resource locks if there are any (they might already have
3699 22b7f6f8 Thomas Thrainer
    # been released during disk conversion)
3700 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3701 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE_RES)
3702 22b7f6f8 Thomas Thrainer
3703 22b7f6f8 Thomas Thrainer
    # Apply NIC changes
3704 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
3705 d0d7d7cf Thomas Thrainer
      self.instance.nics = self._new_nics
3706 22b7f6f8 Thomas Thrainer
      result.extend(self._nic_chgdesc)
3707 22b7f6f8 Thomas Thrainer
3708 22b7f6f8 Thomas Thrainer
    # hvparams changes
3709 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
3710 d0d7d7cf Thomas Thrainer
      self.instance.hvparams = self.hv_inst
3711 22b7f6f8 Thomas Thrainer
      for key, val in self.op.hvparams.iteritems():
3712 22b7f6f8 Thomas Thrainer
        result.append(("hv/%s" % key, val))
3713 22b7f6f8 Thomas Thrainer
3714 22b7f6f8 Thomas Thrainer
    # beparams changes
3715 22b7f6f8 Thomas Thrainer
    if self.op.beparams:
3716 d0d7d7cf Thomas Thrainer
      self.instance.beparams = self.be_inst
3717 22b7f6f8 Thomas Thrainer
      for key, val in self.op.beparams.iteritems():
3718 22b7f6f8 Thomas Thrainer
        result.append(("be/%s" % key, val))
3719 22b7f6f8 Thomas Thrainer
3720 22b7f6f8 Thomas Thrainer
    # OS change
3721 22b7f6f8 Thomas Thrainer
    if self.op.os_name:
3722 d0d7d7cf Thomas Thrainer
      self.instance.os = self.op.os_name
3723 22b7f6f8 Thomas Thrainer
3724 22b7f6f8 Thomas Thrainer
    # osparams changes
3725 22b7f6f8 Thomas Thrainer
    if self.op.osparams:
3726 d0d7d7cf Thomas Thrainer
      self.instance.osparams = self.os_inst
3727 22b7f6f8 Thomas Thrainer
      for key, val in self.op.osparams.iteritems():
3728 22b7f6f8 Thomas Thrainer
        result.append(("os/%s" % key, val))
3729 22b7f6f8 Thomas Thrainer
3730 1a182390 Santi Raffa
    if self.op.osparams_private:
3731 1a182390 Santi Raffa
      self.instance.osparams_private = self.os_inst_private
3732 1a182390 Santi Raffa
      for key, val in self.op.osparams_private.iteritems():
3733 1a182390 Santi Raffa
        # Show the Private(...) blurb.
3734 1a182390 Santi Raffa
        result.append(("os_private/%s" % key, repr(val)))
3735 1a182390 Santi Raffa
3736 22b7f6f8 Thomas Thrainer
    if self.op.offline is None:
3737 22b7f6f8 Thomas Thrainer
      # Ignore
3738 22b7f6f8 Thomas Thrainer
      pass
3739 22b7f6f8 Thomas Thrainer
    elif self.op.offline:
3740 22b7f6f8 Thomas Thrainer
      # Mark instance as offline
3741 da4a52a3 Thomas Thrainer
      self.cfg.MarkInstanceOffline(self.instance.uuid)
3742 22b7f6f8 Thomas Thrainer
      result.append(("admin_state", constants.ADMINST_OFFLINE))
3743 22b7f6f8 Thomas Thrainer
    else:
3744 22b7f6f8 Thomas Thrainer
      # Mark instance as online, but stopped
3745 da4a52a3 Thomas Thrainer
      self.cfg.MarkInstanceDown(self.instance.uuid)
3746 22b7f6f8 Thomas Thrainer
      result.append(("admin_state", constants.ADMINST_DOWN))
3747 22b7f6f8 Thomas Thrainer
3748 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn, self.proc.GetECId())
3749 22b7f6f8 Thomas Thrainer
3750 22b7f6f8 Thomas Thrainer
    assert not (self.owned_locks(locking.LEVEL_NODE_RES) or
3751 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
3752 22b7f6f8 Thomas Thrainer
      "All node locks should have been released by now"
3753 22b7f6f8 Thomas Thrainer
3754 22b7f6f8 Thomas Thrainer
    return result
3755 22b7f6f8 Thomas Thrainer
3756 22b7f6f8 Thomas Thrainer
  _DISK_CONVERSIONS = {
3757 22b7f6f8 Thomas Thrainer
    (constants.DT_PLAIN, constants.DT_DRBD8): _ConvertPlainToDrbd,
3758 22b7f6f8 Thomas Thrainer
    (constants.DT_DRBD8, constants.DT_PLAIN): _ConvertDrbdToPlain,
3759 22b7f6f8 Thomas Thrainer
    }
3760 22b7f6f8 Thomas Thrainer
3761 22b7f6f8 Thomas Thrainer
3762 22b7f6f8 Thomas Thrainer
class LUInstanceChangeGroup(LogicalUnit):
3763 22b7f6f8 Thomas Thrainer
  HPATH = "instance-change-group"
3764 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
3765 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
3766 22b7f6f8 Thomas Thrainer
3767 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
3768 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
3769 22b7f6f8 Thomas Thrainer
3770 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
3771 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODEGROUP: [],
3772 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE: [],
3773 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
3774 22b7f6f8 Thomas Thrainer
      }
3775 22b7f6f8 Thomas Thrainer
3776 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
3777 22b7f6f8 Thomas Thrainer
3778 22b7f6f8 Thomas Thrainer
    if self.op.target_groups:
3779 22b7f6f8 Thomas Thrainer
      self.req_target_uuids = map(self.cfg.LookupNodeGroup,
3780 22b7f6f8 Thomas Thrainer
                                  self.op.target_groups)
3781 22b7f6f8 Thomas Thrainer
    else:
3782 22b7f6f8 Thomas Thrainer
      self.req_target_uuids = None
3783 22b7f6f8 Thomas Thrainer
3784 5eacbcae Thomas Thrainer
    self.op.iallocator = GetDefaultIAllocator(self.cfg, self.op.iallocator)
3785 22b7f6f8 Thomas Thrainer
3786 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
3787 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
3788 22b7f6f8 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
3789 22b7f6f8 Thomas Thrainer
3790 22b7f6f8 Thomas Thrainer
      if self.req_target_uuids:
3791 22b7f6f8 Thomas Thrainer
        lock_groups = set(self.req_target_uuids)
3792 22b7f6f8 Thomas Thrainer
3793 22b7f6f8 Thomas Thrainer
        # Lock all groups used by instance optimistically; this requires going
3794 22b7f6f8 Thomas Thrainer
        # via the node before it's locked, requiring verification later on
3795 da4a52a3 Thomas Thrainer
        instance_groups = self.cfg.GetInstanceNodeGroups(self.op.instance_uuid)
3796 22b7f6f8 Thomas Thrainer
        lock_groups.update(instance_groups)
3797 22b7f6f8 Thomas Thrainer
      else:
3798 22b7f6f8 Thomas Thrainer
        # No target groups, need to lock all of them
3799 22b7f6f8 Thomas Thrainer
        lock_groups = locking.ALL_SET
3800 22b7f6f8 Thomas Thrainer
3801 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = lock_groups
3802 22b7f6f8 Thomas Thrainer
3803 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE:
3804 22b7f6f8 Thomas Thrainer
      if self.req_target_uuids:
3805 22b7f6f8 Thomas Thrainer
        # Lock all nodes used by instances
3806 22b7f6f8 Thomas Thrainer
        self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
3807 22b7f6f8 Thomas Thrainer
        self._LockInstancesNodes()
3808 22b7f6f8 Thomas Thrainer
3809 22b7f6f8 Thomas Thrainer
        # Lock all nodes in all potential target groups
3810 22b7f6f8 Thomas Thrainer
        lock_groups = (frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) -
3811 da4a52a3 Thomas Thrainer
                       self.cfg.GetInstanceNodeGroups(self.op.instance_uuid))
3812 1c3231aa Thomas Thrainer
        member_nodes = [node_uuid
3813 22b7f6f8 Thomas Thrainer
                        for group in lock_groups
3814 1c3231aa Thomas Thrainer
                        for node_uuid in self.cfg.GetNodeGroup(group).members]
3815 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE].extend(member_nodes)
3816 22b7f6f8 Thomas Thrainer
      else:
3817 22b7f6f8 Thomas Thrainer
        # Lock all nodes as all groups are potential targets
3818 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
3819 22b7f6f8 Thomas Thrainer
3820 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
3821 da4a52a3 Thomas Thrainer
    owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
3822 22b7f6f8 Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
3823 22b7f6f8 Thomas Thrainer
    owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE))
3824 22b7f6f8 Thomas Thrainer
3825 22b7f6f8 Thomas Thrainer
    assert (self.req_target_uuids is None or
3826 22b7f6f8 Thomas Thrainer
            owned_groups.issuperset(self.req_target_uuids))
3827 da4a52a3 Thomas Thrainer
    assert owned_instance_names == set([self.op.instance_name])
3828 22b7f6f8 Thomas Thrainer
3829 22b7f6f8 Thomas Thrainer
    # Get instance information
3830 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
3831 22b7f6f8 Thomas Thrainer
3832 22b7f6f8 Thomas Thrainer
    # Check if node groups for locked instance are still correct
3833 22b7f6f8 Thomas Thrainer
    assert owned_nodes.issuperset(self.instance.all_nodes), \
3834 22b7f6f8 Thomas Thrainer
      ("Instance %s's nodes changed while we kept the lock" %
3835 22b7f6f8 Thomas Thrainer
       self.op.instance_name)
3836 22b7f6f8 Thomas Thrainer
3837 da4a52a3 Thomas Thrainer
    inst_groups = CheckInstanceNodeGroups(self.cfg, self.op.instance_uuid,
3838 5eacbcae Thomas Thrainer
                                          owned_groups)
3839 22b7f6f8 Thomas Thrainer
3840 22b7f6f8 Thomas Thrainer
    if self.req_target_uuids:
3841 22b7f6f8 Thomas Thrainer
      # User requested specific target groups
3842 22b7f6f8 Thomas Thrainer
      self.target_uuids = frozenset(self.req_target_uuids)
3843 22b7f6f8 Thomas Thrainer
    else:
3844 22b7f6f8 Thomas Thrainer
      # All groups except those used by the instance are potential targets
3845 22b7f6f8 Thomas Thrainer
      self.target_uuids = owned_groups - inst_groups
3846 22b7f6f8 Thomas Thrainer
3847 22b7f6f8 Thomas Thrainer
    conflicting_groups = self.target_uuids & inst_groups
3848 22b7f6f8 Thomas Thrainer
    if conflicting_groups:
3849 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't use group(s) '%s' as targets, they are"
3850 22b7f6f8 Thomas Thrainer
                                 " used by the instance '%s'" %
3851 22b7f6f8 Thomas Thrainer
                                 (utils.CommaJoin(conflicting_groups),
3852 22b7f6f8 Thomas Thrainer
                                  self.op.instance_name),
3853 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3854 22b7f6f8 Thomas Thrainer
3855 22b7f6f8 Thomas Thrainer
    if not self.target_uuids:
3856 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are no possible target groups",
3857 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3858 22b7f6f8 Thomas Thrainer
3859 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
3860 22b7f6f8 Thomas Thrainer
    """Build hooks env.
3861 22b7f6f8 Thomas Thrainer

3862 22b7f6f8 Thomas Thrainer
    """
3863 22b7f6f8 Thomas Thrainer
    assert self.target_uuids
3864 22b7f6f8 Thomas Thrainer
3865 22b7f6f8 Thomas Thrainer
    env = {
3866 22b7f6f8 Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
3867 22b7f6f8 Thomas Thrainer
      }
3868 22b7f6f8 Thomas Thrainer
3869 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
3870 22b7f6f8 Thomas Thrainer
3871 22b7f6f8 Thomas Thrainer
    return env
3872 22b7f6f8 Thomas Thrainer
3873 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
3874 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
3875 22b7f6f8 Thomas Thrainer

3876 22b7f6f8 Thomas Thrainer
    """
3877 22b7f6f8 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
3878 22b7f6f8 Thomas Thrainer
    return ([mn], [mn])
3879 22b7f6f8 Thomas Thrainer
3880 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3881 22b7f6f8 Thomas Thrainer
    instances = list(self.owned_locks(locking.LEVEL_INSTANCE))
3882 22b7f6f8 Thomas Thrainer
3883 22b7f6f8 Thomas Thrainer
    assert instances == [self.op.instance_name], "Instance not locked"
3884 22b7f6f8 Thomas Thrainer
3885 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=instances,
3886 22b7f6f8 Thomas Thrainer
                                      target_groups=list(self.target_uuids))
3887 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
3888 22b7f6f8 Thomas Thrainer
3889 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
3890 22b7f6f8 Thomas Thrainer
3891 22b7f6f8 Thomas Thrainer
    if not ial.success:
3892 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute solution for changing group of"
3893 22b7f6f8 Thomas Thrainer
                                 " instance '%s' using iallocator '%s': %s" %
3894 22b7f6f8 Thomas Thrainer
                                 (self.op.instance_name, self.op.iallocator,
3895 22b7f6f8 Thomas Thrainer
                                  ial.info), errors.ECODE_NORES)
3896 22b7f6f8 Thomas Thrainer
3897 5eacbcae Thomas Thrainer
    jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
3898 22b7f6f8 Thomas Thrainer
3899 22b7f6f8 Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for changing group of"
3900 22b7f6f8 Thomas Thrainer
                 " instance '%s'", len(jobs), self.op.instance_name)
3901 22b7f6f8 Thomas Thrainer
3902 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs)