Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance.py @ edada04b

History | View | Annotate | Download (140.5 kB)

1 22b7f6f8 Thomas Thrainer
#
2 22b7f6f8 Thomas Thrainer
#
3 22b7f6f8 Thomas Thrainer
4 22b7f6f8 Thomas Thrainer
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5 22b7f6f8 Thomas Thrainer
#
6 22b7f6f8 Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 22b7f6f8 Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 22b7f6f8 Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 22b7f6f8 Thomas Thrainer
# (at your option) any later version.
10 22b7f6f8 Thomas Thrainer
#
11 22b7f6f8 Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 22b7f6f8 Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 22b7f6f8 Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 22b7f6f8 Thomas Thrainer
# General Public License for more details.
15 22b7f6f8 Thomas Thrainer
#
16 22b7f6f8 Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 22b7f6f8 Thomas Thrainer
# along with this program; if not, write to the Free Software
18 22b7f6f8 Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 22b7f6f8 Thomas Thrainer
# 02110-1301, USA.
20 22b7f6f8 Thomas Thrainer
21 22b7f6f8 Thomas Thrainer
22 22b7f6f8 Thomas Thrainer
"""Logical units dealing with instances."""
23 22b7f6f8 Thomas Thrainer
24 22b7f6f8 Thomas Thrainer
import OpenSSL
25 22b7f6f8 Thomas Thrainer
import copy
26 22b7f6f8 Thomas Thrainer
import 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 opcodes
40 22b7f6f8 Thomas Thrainer
from ganeti import pathutils
41 22b7f6f8 Thomas Thrainer
from ganeti import 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 1f7c8208 Helga Velroyen
  CheckDiskTemplateEnabled
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 22b7f6f8 Thomas Thrainer
178 22b7f6f8 Thomas Thrainer
    if net is None or net.lower() == constants.VALUE_NONE:
179 22b7f6f8 Thomas Thrainer
      net = None
180 22b7f6f8 Thomas Thrainer
    else:
181 22b7f6f8 Thomas Thrainer
      if nic_mode_req is not None or link is not None:
182 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("If network is given, no mode or link"
183 22b7f6f8 Thomas Thrainer
                                   " is allowed to be passed",
184 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
185 22b7f6f8 Thomas Thrainer
186 22b7f6f8 Thomas Thrainer
    # ip validity checks
187 22b7f6f8 Thomas Thrainer
    if ip is None or ip.lower() == constants.VALUE_NONE:
188 22b7f6f8 Thomas Thrainer
      nic_ip = None
189 22b7f6f8 Thomas Thrainer
    elif ip.lower() == constants.VALUE_AUTO:
190 22b7f6f8 Thomas Thrainer
      if not op.name_check:
191 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("IP address set to auto but name checks"
192 22b7f6f8 Thomas Thrainer
                                   " have been skipped",
193 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
194 22b7f6f8 Thomas Thrainer
      nic_ip = default_ip
195 22b7f6f8 Thomas Thrainer
    else:
196 22b7f6f8 Thomas Thrainer
      # We defer pool operations until later, so that the iallocator has
197 22b7f6f8 Thomas Thrainer
      # filled in the instance's node(s) dimara
198 22b7f6f8 Thomas Thrainer
      if ip.lower() == constants.NIC_IP_POOL:
199 22b7f6f8 Thomas Thrainer
        if net is None:
200 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("if ip=pool, parameter network"
201 22b7f6f8 Thomas Thrainer
                                     " must be passed too",
202 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
203 22b7f6f8 Thomas Thrainer
204 22b7f6f8 Thomas Thrainer
      elif not netutils.IPAddress.IsValid(ip):
205 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid IP address '%s'" % ip,
206 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
207 22b7f6f8 Thomas Thrainer
208 22b7f6f8 Thomas Thrainer
      nic_ip = ip
209 22b7f6f8 Thomas Thrainer
210 22b7f6f8 Thomas Thrainer
    # TODO: check the ip address for uniqueness
211 22b7f6f8 Thomas Thrainer
    if nic_mode == constants.NIC_MODE_ROUTED and not nic_ip:
212 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Routed nic mode requires an ip address",
213 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
214 22b7f6f8 Thomas Thrainer
215 22b7f6f8 Thomas Thrainer
    # MAC address verification
216 22b7f6f8 Thomas Thrainer
    mac = nic.get(constants.INIC_MAC, constants.VALUE_AUTO)
217 22b7f6f8 Thomas Thrainer
    if mac not in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
218 22b7f6f8 Thomas Thrainer
      mac = utils.NormalizeAndValidateMac(mac)
219 22b7f6f8 Thomas Thrainer
220 22b7f6f8 Thomas Thrainer
      try:
221 22b7f6f8 Thomas Thrainer
        # TODO: We need to factor this out
222 22b7f6f8 Thomas Thrainer
        cfg.ReserveMAC(mac, ec_id)
223 22b7f6f8 Thomas Thrainer
      except errors.ReservationError:
224 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("MAC address %s already in use"
225 22b7f6f8 Thomas Thrainer
                                   " in cluster" % mac,
226 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
227 22b7f6f8 Thomas Thrainer
228 22b7f6f8 Thomas Thrainer
    #  Build nic parameters
229 22b7f6f8 Thomas Thrainer
    nicparams = {}
230 22b7f6f8 Thomas Thrainer
    if nic_mode_req:
231 22b7f6f8 Thomas Thrainer
      nicparams[constants.NIC_MODE] = nic_mode
232 22b7f6f8 Thomas Thrainer
    if link:
233 22b7f6f8 Thomas Thrainer
      nicparams[constants.NIC_LINK] = link
234 22b7f6f8 Thomas Thrainer
235 22b7f6f8 Thomas Thrainer
    check_params = cluster.SimpleFillNIC(nicparams)
236 22b7f6f8 Thomas Thrainer
    objects.NIC.CheckParameterSyntax(check_params)
237 22b7f6f8 Thomas Thrainer
    net_uuid = cfg.LookupNetwork(net)
238 22b7f6f8 Thomas Thrainer
    name = nic.get(constants.INIC_NAME, None)
239 22b7f6f8 Thomas Thrainer
    if name is not None and name.lower() == constants.VALUE_NONE:
240 22b7f6f8 Thomas Thrainer
      name = None
241 22b7f6f8 Thomas Thrainer
    nic_obj = objects.NIC(mac=mac, ip=nic_ip, name=name,
242 22b7f6f8 Thomas Thrainer
                          network=net_uuid, nicparams=nicparams)
243 22b7f6f8 Thomas Thrainer
    nic_obj.uuid = cfg.GenerateUniqueID(ec_id)
244 22b7f6f8 Thomas Thrainer
    nics.append(nic_obj)
245 22b7f6f8 Thomas Thrainer
246 22b7f6f8 Thomas Thrainer
  return nics
247 22b7f6f8 Thomas Thrainer
248 22b7f6f8 Thomas Thrainer
249 1c3231aa Thomas Thrainer
def _CheckForConflictingIp(lu, ip, node_uuid):
250 22b7f6f8 Thomas Thrainer
  """In case of conflicting IP address raise error.
251 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

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

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

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

385 dab6ea3d Helga Velroyen
    """
386 dab6ea3d Helga Velroyen
    # do not require name_check to ease forward/backward compatibility
387 dab6ea3d Helga Velroyen
    # for tools
388 dab6ea3d Helga Velroyen
    if self.op.no_install and self.op.start:
389 dab6ea3d Helga Velroyen
      self.LogInfo("No-installation mode selected, disabling startup")
390 dab6ea3d Helga Velroyen
      self.op.start = False
391 dab6ea3d Helga Velroyen
    # validate/normalize the instance name
392 dab6ea3d Helga Velroyen
    self.op.instance_name = \
393 dab6ea3d Helga Velroyen
      netutils.Hostname.GetNormalizedName(self.op.instance_name)
394 dab6ea3d Helga Velroyen
395 dab6ea3d Helga Velroyen
    if self.op.ip_check and not self.op.name_check:
396 dab6ea3d Helga Velroyen
      # TODO: make the ip check more flexible and not depend on the name check
397 dab6ea3d Helga Velroyen
      raise errors.OpPrereqError("Cannot do IP address check without a name"
398 dab6ea3d Helga Velroyen
                                 " check", errors.ECODE_INVAL)
399 dab6ea3d Helga Velroyen
400 dab6ea3d Helga Velroyen
    # check nics' parameter names
401 dab6ea3d Helga Velroyen
    for nic in self.op.nics:
402 dab6ea3d Helga Velroyen
      utils.ForceDictType(nic, constants.INIC_PARAMS_TYPES)
403 dab6ea3d Helga Velroyen
    # check that NIC's parameters names are unique and valid
404 dab6ea3d Helga Velroyen
    utils.ValidateDeviceNames("NIC", self.op.nics)
405 dab6ea3d Helga Velroyen
406 dab6ea3d Helga Velroyen
    self._CheckDiskArguments()
407 dab6ea3d Helga Velroyen
408 22b7f6f8 Thomas Thrainer
    # instance name verification
409 22b7f6f8 Thomas Thrainer
    if self.op.name_check:
410 da4a52a3 Thomas Thrainer
      self.hostname = _CheckHostnameSane(self, self.op.instance_name)
411 da4a52a3 Thomas Thrainer
      self.op.instance_name = self.hostname.name
412 22b7f6f8 Thomas Thrainer
      # used in CheckPrereq for ip ping check
413 da4a52a3 Thomas Thrainer
      self.check_ip = self.hostname.ip
414 22b7f6f8 Thomas Thrainer
    else:
415 22b7f6f8 Thomas Thrainer
      self.check_ip = None
416 22b7f6f8 Thomas Thrainer
417 22b7f6f8 Thomas Thrainer
    # file storage checks
418 22b7f6f8 Thomas Thrainer
    if (self.op.file_driver and
419 22b7f6f8 Thomas Thrainer
        not self.op.file_driver in constants.FILE_DRIVER):
420 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Invalid file driver name '%s'" %
421 22b7f6f8 Thomas Thrainer
                                 self.op.file_driver, errors.ECODE_INVAL)
422 22b7f6f8 Thomas Thrainer
423 ff34fb97 Michele Tartara
    # set default file_driver if unset and required
424 ff34fb97 Michele Tartara
    if (not self.op.file_driver and
425 ff34fb97 Michele Tartara
        self.op.disk_template in [constants.DT_FILE,
426 ff34fb97 Michele Tartara
                                  constants.DT_SHARED_FILE]):
427 ff34fb97 Michele Tartara
      self.op.file_driver = constants.FD_LOOP
428 ff34fb97 Michele Tartara
429 22b7f6f8 Thomas Thrainer
    ### Node/iallocator related checks
430 5eacbcae Thomas Thrainer
    CheckIAllocatorOrNode(self, "iallocator", "pnode")
431 22b7f6f8 Thomas Thrainer
432 22b7f6f8 Thomas Thrainer
    if self.op.pnode is not None:
433 22b7f6f8 Thomas Thrainer
      if self.op.disk_template in constants.DTS_INT_MIRROR:
434 22b7f6f8 Thomas Thrainer
        if self.op.snode is None:
435 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("The networked disk templates need"
436 22b7f6f8 Thomas Thrainer
                                     " a mirror node", errors.ECODE_INVAL)
437 22b7f6f8 Thomas Thrainer
      elif self.op.snode:
438 22b7f6f8 Thomas Thrainer
        self.LogWarning("Secondary node will be ignored on non-mirrored disk"
439 22b7f6f8 Thomas Thrainer
                        " template")
440 22b7f6f8 Thomas Thrainer
        self.op.snode = None
441 22b7f6f8 Thomas Thrainer
442 22b7f6f8 Thomas Thrainer
    _CheckOpportunisticLocking(self.op)
443 22b7f6f8 Thomas Thrainer
444 5eacbcae Thomas Thrainer
    self._cds = GetClusterDomainSecret()
445 22b7f6f8 Thomas Thrainer
446 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
447 22b7f6f8 Thomas Thrainer
      # On import force_variant must be True, because if we forced it at
448 22b7f6f8 Thomas Thrainer
      # initial install, our only chance when importing it back is that it
449 22b7f6f8 Thomas Thrainer
      # works again!
450 22b7f6f8 Thomas Thrainer
      self.op.force_variant = True
451 22b7f6f8 Thomas Thrainer
452 22b7f6f8 Thomas Thrainer
      if self.op.no_install:
453 22b7f6f8 Thomas Thrainer
        self.LogInfo("No-installation mode has no effect during import")
454 22b7f6f8 Thomas Thrainer
455 22b7f6f8 Thomas Thrainer
    elif self.op.mode == constants.INSTANCE_CREATE:
456 22b7f6f8 Thomas Thrainer
      if self.op.os_type is None:
457 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No guest OS specified",
458 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
459 22b7f6f8 Thomas Thrainer
      if self.op.os_type in self.cfg.GetClusterInfo().blacklisted_os:
460 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Guest OS '%s' is not allowed for"
461 22b7f6f8 Thomas Thrainer
                                   " installation" % self.op.os_type,
462 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
463 22b7f6f8 Thomas Thrainer
      if self.op.disk_template is None:
464 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No disk template specified",
465 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
466 22b7f6f8 Thomas Thrainer
467 22b7f6f8 Thomas Thrainer
    elif self.op.mode == constants.INSTANCE_REMOTE_IMPORT:
468 22b7f6f8 Thomas Thrainer
      # Check handshake to ensure both clusters have the same domain secret
469 22b7f6f8 Thomas Thrainer
      src_handshake = self.op.source_handshake
470 22b7f6f8 Thomas Thrainer
      if not src_handshake:
471 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing source handshake",
472 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
473 22b7f6f8 Thomas Thrainer
474 22b7f6f8 Thomas Thrainer
      errmsg = masterd.instance.CheckRemoteExportHandshake(self._cds,
475 22b7f6f8 Thomas Thrainer
                                                           src_handshake)
476 22b7f6f8 Thomas Thrainer
      if errmsg:
477 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid handshake: %s" % errmsg,
478 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
479 22b7f6f8 Thomas Thrainer
480 22b7f6f8 Thomas Thrainer
      # Load and check source CA
481 22b7f6f8 Thomas Thrainer
      self.source_x509_ca_pem = self.op.source_x509_ca
482 22b7f6f8 Thomas Thrainer
      if not self.source_x509_ca_pem:
483 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing source X509 CA",
484 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
485 22b7f6f8 Thomas Thrainer
486 22b7f6f8 Thomas Thrainer
      try:
487 22b7f6f8 Thomas Thrainer
        (cert, _) = utils.LoadSignedX509Certificate(self.source_x509_ca_pem,
488 22b7f6f8 Thomas Thrainer
                                                    self._cds)
489 22b7f6f8 Thomas Thrainer
      except OpenSSL.crypto.Error, err:
490 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Unable to load source X509 CA (%s)" %
491 22b7f6f8 Thomas Thrainer
                                   (err, ), errors.ECODE_INVAL)
492 22b7f6f8 Thomas Thrainer
493 22b7f6f8 Thomas Thrainer
      (errcode, msg) = utils.VerifyX509Certificate(cert, None, None)
494 22b7f6f8 Thomas Thrainer
      if errcode is not None:
495 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid source X509 CA (%s)" % (msg, ),
496 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
497 22b7f6f8 Thomas Thrainer
498 22b7f6f8 Thomas Thrainer
      self.source_x509_ca = cert
499 22b7f6f8 Thomas Thrainer
500 22b7f6f8 Thomas Thrainer
      src_instance_name = self.op.source_instance_name
501 22b7f6f8 Thomas Thrainer
      if not src_instance_name:
502 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing source instance name",
503 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
504 22b7f6f8 Thomas Thrainer
505 22b7f6f8 Thomas Thrainer
      self.source_instance_name = \
506 22b7f6f8 Thomas Thrainer
        netutils.GetHostname(name=src_instance_name).name
507 22b7f6f8 Thomas Thrainer
508 22b7f6f8 Thomas Thrainer
    else:
509 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Invalid instance creation mode %r" %
510 22b7f6f8 Thomas Thrainer
                                 self.op.mode, errors.ECODE_INVAL)
511 22b7f6f8 Thomas Thrainer
512 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
513 22b7f6f8 Thomas Thrainer
    """ExpandNames for CreateInstance.
514 22b7f6f8 Thomas Thrainer

515 22b7f6f8 Thomas Thrainer
    Figure out the right locks for instance creation.
516 22b7f6f8 Thomas Thrainer

517 22b7f6f8 Thomas Thrainer
    """
518 22b7f6f8 Thomas Thrainer
    self.needed_locks = {}
519 22b7f6f8 Thomas Thrainer
520 22b7f6f8 Thomas Thrainer
    # this is just a preventive check, but someone might still add this
521 22b7f6f8 Thomas Thrainer
    # instance in the meantime, and creation will fail at lock-add time
522 da4a52a3 Thomas Thrainer
    if self.op.instance_name in\
523 da4a52a3 Thomas Thrainer
      [inst.name for inst in self.cfg.GetAllInstancesInfo().values()]:
524 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
525 d0d7d7cf Thomas Thrainer
                                 self.op.instance_name, errors.ECODE_EXISTS)
526 22b7f6f8 Thomas Thrainer
527 d0d7d7cf Thomas Thrainer
    self.add_locks[locking.LEVEL_INSTANCE] = self.op.instance_name
528 22b7f6f8 Thomas Thrainer
529 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
530 22b7f6f8 Thomas Thrainer
      # TODO: Find a solution to not lock all nodes in the cluster, e.g. by
531 22b7f6f8 Thomas Thrainer
      # specifying a group on instance creation and then selecting nodes from
532 22b7f6f8 Thomas Thrainer
      # that group
533 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
534 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
535 22b7f6f8 Thomas Thrainer
536 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
537 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
538 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE_RES] = True
539 22b7f6f8 Thomas Thrainer
    else:
540 1c3231aa Thomas Thrainer
      (self.op.pnode_uuid, self.op.pnode) = \
541 1c3231aa Thomas Thrainer
        ExpandNodeUuidAndName(self.cfg, self.op.pnode_uuid, self.op.pnode)
542 1c3231aa Thomas Thrainer
      nodelist = [self.op.pnode_uuid]
543 22b7f6f8 Thomas Thrainer
      if self.op.snode is not None:
544 1c3231aa Thomas Thrainer
        (self.op.snode_uuid, self.op.snode) = \
545 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, self.op.snode_uuid, self.op.snode)
546 1c3231aa Thomas Thrainer
        nodelist.append(self.op.snode_uuid)
547 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodelist
548 22b7f6f8 Thomas Thrainer
549 22b7f6f8 Thomas Thrainer
    # in case of import lock the source node too
550 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
551 22b7f6f8 Thomas Thrainer
      src_node = self.op.src_node
552 22b7f6f8 Thomas Thrainer
      src_path = self.op.src_path
553 22b7f6f8 Thomas Thrainer
554 22b7f6f8 Thomas Thrainer
      if src_path is None:
555 22b7f6f8 Thomas Thrainer
        self.op.src_path = src_path = self.op.instance_name
556 22b7f6f8 Thomas Thrainer
557 22b7f6f8 Thomas Thrainer
      if src_node is None:
558 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
559 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
560 22b7f6f8 Thomas Thrainer
        self.op.src_node = None
561 22b7f6f8 Thomas Thrainer
        if os.path.isabs(src_path):
562 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("Importing an instance from a path"
563 22b7f6f8 Thomas Thrainer
                                     " requires a source node option",
564 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
565 22b7f6f8 Thomas Thrainer
      else:
566 1c3231aa Thomas Thrainer
        (self.op.src_node_uuid, self.op.src_node) = (_, src_node) = \
567 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, self.op.src_node_uuid, src_node)
568 22b7f6f8 Thomas Thrainer
        if self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET:
569 1c3231aa Thomas Thrainer
          self.needed_locks[locking.LEVEL_NODE].append(self.op.src_node_uuid)
570 22b7f6f8 Thomas Thrainer
        if not os.path.isabs(src_path):
571 a61a0813 Thomas Thrainer
          self.op.src_path = \
572 22b7f6f8 Thomas Thrainer
            utils.PathJoin(pathutils.EXPORT_DIR, src_path)
573 22b7f6f8 Thomas Thrainer
574 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = \
575 5eacbcae Thomas Thrainer
      CopyLockList(self.needed_locks[locking.LEVEL_NODE])
576 22b7f6f8 Thomas Thrainer
577 22b7f6f8 Thomas Thrainer
  def _RunAllocator(self):
578 22b7f6f8 Thomas Thrainer
    """Run the allocator based on input opcode.
579 22b7f6f8 Thomas Thrainer

580 22b7f6f8 Thomas Thrainer
    """
581 22b7f6f8 Thomas Thrainer
    if self.op.opportunistic_locking:
582 22b7f6f8 Thomas Thrainer
      # Only consider nodes for which a lock is held
583 4665b94f Thomas Thrainer
      node_name_whitelist = self.cfg.GetNodeNames(
584 4665b94f Thomas Thrainer
        self.owned_locks(locking.LEVEL_NODE))
585 22b7f6f8 Thomas Thrainer
    else:
586 4665b94f Thomas Thrainer
      node_name_whitelist = None
587 22b7f6f8 Thomas Thrainer
588 22b7f6f8 Thomas Thrainer
    #TODO Export network to iallocator so that it chooses a pnode
589 22b7f6f8 Thomas Thrainer
    #     in a nodegroup that has the desired network connected to
590 22b7f6f8 Thomas Thrainer
    req = _CreateInstanceAllocRequest(self.op, self.disks,
591 22b7f6f8 Thomas Thrainer
                                      self.nics, self.be_full,
592 4665b94f Thomas Thrainer
                                      node_name_whitelist)
593 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
594 22b7f6f8 Thomas Thrainer
595 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
596 22b7f6f8 Thomas Thrainer
597 22b7f6f8 Thomas Thrainer
    if not ial.success:
598 22b7f6f8 Thomas Thrainer
      # When opportunistic locks are used only a temporary failure is generated
599 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
600 22b7f6f8 Thomas Thrainer
        ecode = errors.ECODE_TEMP_NORES
601 22b7f6f8 Thomas Thrainer
      else:
602 22b7f6f8 Thomas Thrainer
        ecode = errors.ECODE_NORES
603 22b7f6f8 Thomas Thrainer
604 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute nodes using"
605 22b7f6f8 Thomas Thrainer
                                 " iallocator '%s': %s" %
606 22b7f6f8 Thomas Thrainer
                                 (self.op.iallocator, ial.info),
607 22b7f6f8 Thomas Thrainer
                                 ecode)
608 22b7f6f8 Thomas Thrainer
609 1c3231aa Thomas Thrainer
    (self.op.pnode_uuid, self.op.pnode) = \
610 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, None, ial.result[0])
611 22b7f6f8 Thomas Thrainer
    self.LogInfo("Selected nodes for instance %s via iallocator %s: %s",
612 22b7f6f8 Thomas Thrainer
                 self.op.instance_name, self.op.iallocator,
613 22b7f6f8 Thomas Thrainer
                 utils.CommaJoin(ial.result))
614 22b7f6f8 Thomas Thrainer
615 22b7f6f8 Thomas Thrainer
    assert req.RequiredNodes() in (1, 2), "Wrong node count from iallocator"
616 22b7f6f8 Thomas Thrainer
617 22b7f6f8 Thomas Thrainer
    if req.RequiredNodes() == 2:
618 1c3231aa Thomas Thrainer
      (self.op.snode_uuid, self.op.snode) = \
619 1c3231aa Thomas Thrainer
        ExpandNodeUuidAndName(self.cfg, None, ial.result[1])
620 22b7f6f8 Thomas Thrainer
621 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
622 22b7f6f8 Thomas Thrainer
    """Build hooks env.
623 22b7f6f8 Thomas Thrainer

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

626 22b7f6f8 Thomas Thrainer
    """
627 22b7f6f8 Thomas Thrainer
    env = {
628 22b7f6f8 Thomas Thrainer
      "ADD_MODE": self.op.mode,
629 22b7f6f8 Thomas Thrainer
      }
630 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
631 22b7f6f8 Thomas Thrainer
      env["SRC_NODE"] = self.op.src_node
632 22b7f6f8 Thomas Thrainer
      env["SRC_PATH"] = self.op.src_path
633 22b7f6f8 Thomas Thrainer
      env["SRC_IMAGES"] = self.src_images
634 22b7f6f8 Thomas Thrainer
635 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnv(
636 22b7f6f8 Thomas Thrainer
      name=self.op.instance_name,
637 1c3231aa Thomas Thrainer
      primary_node_name=self.op.pnode,
638 1c3231aa Thomas Thrainer
      secondary_node_names=self.cfg.GetNodeNames(self.secondaries),
639 22b7f6f8 Thomas Thrainer
      status=self.op.start,
640 22b7f6f8 Thomas Thrainer
      os_type=self.op.os_type,
641 22b7f6f8 Thomas Thrainer
      minmem=self.be_full[constants.BE_MINMEM],
642 22b7f6f8 Thomas Thrainer
      maxmem=self.be_full[constants.BE_MAXMEM],
643 22b7f6f8 Thomas Thrainer
      vcpus=self.be_full[constants.BE_VCPUS],
644 5eacbcae Thomas Thrainer
      nics=NICListToTuple(self, self.nics),
645 22b7f6f8 Thomas Thrainer
      disk_template=self.op.disk_template,
646 8a348b15 Christos Stavrakakis
      disks=[(d[constants.IDISK_NAME], d.get("uuid", ""),
647 8a348b15 Christos Stavrakakis
              d[constants.IDISK_SIZE], d[constants.IDISK_MODE])
648 8a348b15 Christos Stavrakakis
             for d in self.disks],
649 22b7f6f8 Thomas Thrainer
      bep=self.be_full,
650 22b7f6f8 Thomas Thrainer
      hvp=self.hv_full,
651 22b7f6f8 Thomas Thrainer
      hypervisor_name=self.op.hypervisor,
652 22b7f6f8 Thomas Thrainer
      tags=self.op.tags,
653 22b7f6f8 Thomas Thrainer
      ))
654 22b7f6f8 Thomas Thrainer
655 22b7f6f8 Thomas Thrainer
    return env
656 22b7f6f8 Thomas Thrainer
657 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
658 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
659 22b7f6f8 Thomas Thrainer

660 22b7f6f8 Thomas Thrainer
    """
661 1c3231aa Thomas Thrainer
    nl = [self.cfg.GetMasterNode(), self.op.pnode_uuid] + self.secondaries
662 22b7f6f8 Thomas Thrainer
    return nl, nl
663 22b7f6f8 Thomas Thrainer
664 22b7f6f8 Thomas Thrainer
  def _ReadExportInfo(self):
665 22b7f6f8 Thomas Thrainer
    """Reads the export information from disk.
666 22b7f6f8 Thomas Thrainer

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

670 22b7f6f8 Thomas Thrainer
    @return: the export information
671 22b7f6f8 Thomas Thrainer

672 22b7f6f8 Thomas Thrainer
    """
673 22b7f6f8 Thomas Thrainer
    assert self.op.mode == constants.INSTANCE_IMPORT
674 22b7f6f8 Thomas Thrainer
675 d0d7d7cf Thomas Thrainer
    if self.op.src_node_uuid is None:
676 22b7f6f8 Thomas Thrainer
      locked_nodes = self.owned_locks(locking.LEVEL_NODE)
677 22b7f6f8 Thomas Thrainer
      exp_list = self.rpc.call_export_list(locked_nodes)
678 22b7f6f8 Thomas Thrainer
      found = False
679 a61a0813 Thomas Thrainer
      for node_uuid in exp_list:
680 a61a0813 Thomas Thrainer
        if exp_list[node_uuid].fail_msg:
681 22b7f6f8 Thomas Thrainer
          continue
682 a61a0813 Thomas Thrainer
        if self.op.src_path in exp_list[node_uuid].payload:
683 22b7f6f8 Thomas Thrainer
          found = True
684 a61a0813 Thomas Thrainer
          self.op.src_node = self.cfg.GetNodeInfo(node_uuid).name
685 a61a0813 Thomas Thrainer
          self.op.src_node_uuid = node_uuid
686 d0d7d7cf Thomas Thrainer
          self.op.src_path = utils.PathJoin(pathutils.EXPORT_DIR,
687 d0d7d7cf Thomas Thrainer
                                            self.op.src_path)
688 22b7f6f8 Thomas Thrainer
          break
689 22b7f6f8 Thomas Thrainer
      if not found:
690 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No export found for relative path %s" %
691 d0d7d7cf Thomas Thrainer
                                   self.op.src_path, errors.ECODE_INVAL)
692 22b7f6f8 Thomas Thrainer
693 d0d7d7cf Thomas Thrainer
    CheckNodeOnline(self, self.op.src_node_uuid)
694 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_export_info(self.op.src_node_uuid, self.op.src_path)
695 d0d7d7cf Thomas Thrainer
    result.Raise("No export or invalid export found in dir %s" %
696 d0d7d7cf Thomas Thrainer
                 self.op.src_path)
697 22b7f6f8 Thomas Thrainer
698 22b7f6f8 Thomas Thrainer
    export_info = objects.SerializableConfigParser.Loads(str(result.payload))
699 22b7f6f8 Thomas Thrainer
    if not export_info.has_section(constants.INISECT_EXP):
700 22b7f6f8 Thomas Thrainer
      raise errors.ProgrammerError("Corrupted export config",
701 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_ENVIRON)
702 22b7f6f8 Thomas Thrainer
703 22b7f6f8 Thomas Thrainer
    ei_version = export_info.get(constants.INISECT_EXP, "version")
704 1c3231aa Thomas Thrainer
    if int(ei_version) != constants.EXPORT_VERSION:
705 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Wrong export version %s (wanted %d)" %
706 22b7f6f8 Thomas Thrainer
                                 (ei_version, constants.EXPORT_VERSION),
707 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_ENVIRON)
708 22b7f6f8 Thomas Thrainer
    return export_info
709 22b7f6f8 Thomas Thrainer
710 22b7f6f8 Thomas Thrainer
  def _ReadExportParams(self, einfo):
711 22b7f6f8 Thomas Thrainer
    """Use export parameters as defaults.
712 22b7f6f8 Thomas Thrainer

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

717 22b7f6f8 Thomas Thrainer
    """
718 22b7f6f8 Thomas Thrainer
    self.op.os_type = einfo.get(constants.INISECT_EXP, "os")
719 22b7f6f8 Thomas Thrainer
720 22b7f6f8 Thomas Thrainer
    if not self.op.disks:
721 22b7f6f8 Thomas Thrainer
      disks = []
722 22b7f6f8 Thomas Thrainer
      # TODO: import the disk iv_name too
723 22b7f6f8 Thomas Thrainer
      for idx in range(constants.MAX_DISKS):
724 22b7f6f8 Thomas Thrainer
        if einfo.has_option(constants.INISECT_INS, "disk%d_size" % idx):
725 22b7f6f8 Thomas Thrainer
          disk_sz = einfo.getint(constants.INISECT_INS, "disk%d_size" % idx)
726 22b7f6f8 Thomas Thrainer
          disks.append({constants.IDISK_SIZE: disk_sz})
727 22b7f6f8 Thomas Thrainer
      self.op.disks = disks
728 22b7f6f8 Thomas Thrainer
      if not disks and self.op.disk_template != constants.DT_DISKLESS:
729 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No disk info specified and the export"
730 22b7f6f8 Thomas Thrainer
                                   " is missing the disk information",
731 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
732 22b7f6f8 Thomas Thrainer
733 22b7f6f8 Thomas Thrainer
    if not self.op.nics:
734 22b7f6f8 Thomas Thrainer
      nics = []
735 22b7f6f8 Thomas Thrainer
      for idx in range(constants.MAX_NICS):
736 22b7f6f8 Thomas Thrainer
        if einfo.has_option(constants.INISECT_INS, "nic%d_mac" % idx):
737 22b7f6f8 Thomas Thrainer
          ndict = {}
738 22b7f6f8 Thomas Thrainer
          for name in list(constants.NICS_PARAMETERS) + ["ip", "mac"]:
739 a61a0813 Thomas Thrainer
            nic_param_name = "nic%d_%s" % (idx, name)
740 a61a0813 Thomas Thrainer
            if einfo.has_option(constants.INISECT_INS, nic_param_name):
741 a61a0813 Thomas Thrainer
              v = einfo.get(constants.INISECT_INS, nic_param_name)
742 a61a0813 Thomas Thrainer
              ndict[name] = v
743 22b7f6f8 Thomas Thrainer
          nics.append(ndict)
744 22b7f6f8 Thomas Thrainer
        else:
745 22b7f6f8 Thomas Thrainer
          break
746 22b7f6f8 Thomas Thrainer
      self.op.nics = nics
747 22b7f6f8 Thomas Thrainer
748 22b7f6f8 Thomas Thrainer
    if not self.op.tags and einfo.has_option(constants.INISECT_INS, "tags"):
749 22b7f6f8 Thomas Thrainer
      self.op.tags = einfo.get(constants.INISECT_INS, "tags").split()
750 22b7f6f8 Thomas Thrainer
751 22b7f6f8 Thomas Thrainer
    if (self.op.hypervisor is None and
752 22b7f6f8 Thomas Thrainer
        einfo.has_option(constants.INISECT_INS, "hypervisor")):
753 22b7f6f8 Thomas Thrainer
      self.op.hypervisor = einfo.get(constants.INISECT_INS, "hypervisor")
754 22b7f6f8 Thomas Thrainer
755 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_HYP):
756 22b7f6f8 Thomas Thrainer
      # use the export parameters but do not override the ones
757 22b7f6f8 Thomas Thrainer
      # specified by the user
758 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_HYP):
759 22b7f6f8 Thomas Thrainer
        if name not in self.op.hvparams:
760 22b7f6f8 Thomas Thrainer
          self.op.hvparams[name] = value
761 22b7f6f8 Thomas Thrainer
762 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_BEP):
763 22b7f6f8 Thomas Thrainer
      # use the parameters, without overriding
764 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_BEP):
765 22b7f6f8 Thomas Thrainer
        if name not in self.op.beparams:
766 22b7f6f8 Thomas Thrainer
          self.op.beparams[name] = value
767 22b7f6f8 Thomas Thrainer
        # Compatibility for the old "memory" be param
768 22b7f6f8 Thomas Thrainer
        if name == constants.BE_MEMORY:
769 22b7f6f8 Thomas Thrainer
          if constants.BE_MAXMEM not in self.op.beparams:
770 22b7f6f8 Thomas Thrainer
            self.op.beparams[constants.BE_MAXMEM] = value
771 22b7f6f8 Thomas Thrainer
          if constants.BE_MINMEM not in self.op.beparams:
772 22b7f6f8 Thomas Thrainer
            self.op.beparams[constants.BE_MINMEM] = value
773 22b7f6f8 Thomas Thrainer
    else:
774 22b7f6f8 Thomas Thrainer
      # try to read the parameters old style, from the main section
775 22b7f6f8 Thomas Thrainer
      for name in constants.BES_PARAMETERS:
776 22b7f6f8 Thomas Thrainer
        if (name not in self.op.beparams and
777 22b7f6f8 Thomas Thrainer
            einfo.has_option(constants.INISECT_INS, name)):
778 22b7f6f8 Thomas Thrainer
          self.op.beparams[name] = einfo.get(constants.INISECT_INS, name)
779 22b7f6f8 Thomas Thrainer
780 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_OSP):
781 22b7f6f8 Thomas Thrainer
      # use the parameters, without overriding
782 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_OSP):
783 22b7f6f8 Thomas Thrainer
        if name not in self.op.osparams:
784 22b7f6f8 Thomas Thrainer
          self.op.osparams[name] = value
785 22b7f6f8 Thomas Thrainer
786 22b7f6f8 Thomas Thrainer
  def _RevertToDefaults(self, cluster):
787 22b7f6f8 Thomas Thrainer
    """Revert the instance parameters to the default values.
788 22b7f6f8 Thomas Thrainer

789 22b7f6f8 Thomas Thrainer
    """
790 22b7f6f8 Thomas Thrainer
    # hvparams
791 22b7f6f8 Thomas Thrainer
    hv_defs = cluster.SimpleFillHV(self.op.hypervisor, self.op.os_type, {})
792 22b7f6f8 Thomas Thrainer
    for name in self.op.hvparams.keys():
793 22b7f6f8 Thomas Thrainer
      if name in hv_defs and hv_defs[name] == self.op.hvparams[name]:
794 22b7f6f8 Thomas Thrainer
        del self.op.hvparams[name]
795 22b7f6f8 Thomas Thrainer
    # beparams
796 22b7f6f8 Thomas Thrainer
    be_defs = cluster.SimpleFillBE({})
797 22b7f6f8 Thomas Thrainer
    for name in self.op.beparams.keys():
798 22b7f6f8 Thomas Thrainer
      if name in be_defs and be_defs[name] == self.op.beparams[name]:
799 22b7f6f8 Thomas Thrainer
        del self.op.beparams[name]
800 22b7f6f8 Thomas Thrainer
    # nic params
801 22b7f6f8 Thomas Thrainer
    nic_defs = cluster.SimpleFillNIC({})
802 22b7f6f8 Thomas Thrainer
    for nic in self.op.nics:
803 22b7f6f8 Thomas Thrainer
      for name in constants.NICS_PARAMETERS:
804 22b7f6f8 Thomas Thrainer
        if name in nic and name in nic_defs and nic[name] == nic_defs[name]:
805 22b7f6f8 Thomas Thrainer
          del nic[name]
806 22b7f6f8 Thomas Thrainer
    # osparams
807 22b7f6f8 Thomas Thrainer
    os_defs = cluster.SimpleFillOS(self.op.os_type, {})
808 22b7f6f8 Thomas Thrainer
    for name in self.op.osparams.keys():
809 22b7f6f8 Thomas Thrainer
      if name in os_defs and os_defs[name] == self.op.osparams[name]:
810 22b7f6f8 Thomas Thrainer
        del self.op.osparams[name]
811 22b7f6f8 Thomas Thrainer
812 22b7f6f8 Thomas Thrainer
  def _CalculateFileStorageDir(self):
813 22b7f6f8 Thomas Thrainer
    """Calculate final instance file storage dir.
814 22b7f6f8 Thomas Thrainer

815 22b7f6f8 Thomas Thrainer
    """
816 22b7f6f8 Thomas Thrainer
    # file storage dir calculation/check
817 22b7f6f8 Thomas Thrainer
    self.instance_file_storage_dir = None
818 22b7f6f8 Thomas Thrainer
    if self.op.disk_template in constants.DTS_FILEBASED:
819 22b7f6f8 Thomas Thrainer
      # build the full file storage dir path
820 22b7f6f8 Thomas Thrainer
      joinargs = []
821 22b7f6f8 Thomas Thrainer
822 22b7f6f8 Thomas Thrainer
      if self.op.disk_template == constants.DT_SHARED_FILE:
823 22b7f6f8 Thomas Thrainer
        get_fsd_fn = self.cfg.GetSharedFileStorageDir
824 22b7f6f8 Thomas Thrainer
      else:
825 22b7f6f8 Thomas Thrainer
        get_fsd_fn = self.cfg.GetFileStorageDir
826 22b7f6f8 Thomas Thrainer
827 22b7f6f8 Thomas Thrainer
      cfg_storagedir = get_fsd_fn()
828 22b7f6f8 Thomas Thrainer
      if not cfg_storagedir:
829 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cluster file storage dir not defined",
830 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
831 22b7f6f8 Thomas Thrainer
      joinargs.append(cfg_storagedir)
832 22b7f6f8 Thomas Thrainer
833 22b7f6f8 Thomas Thrainer
      if self.op.file_storage_dir is not None:
834 22b7f6f8 Thomas Thrainer
        joinargs.append(self.op.file_storage_dir)
835 22b7f6f8 Thomas Thrainer
836 22b7f6f8 Thomas Thrainer
      joinargs.append(self.op.instance_name)
837 22b7f6f8 Thomas Thrainer
838 22b7f6f8 Thomas Thrainer
      # pylint: disable=W0142
839 22b7f6f8 Thomas Thrainer
      self.instance_file_storage_dir = utils.PathJoin(*joinargs)
840 22b7f6f8 Thomas Thrainer
841 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self): # pylint: disable=R0914
842 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
843 22b7f6f8 Thomas Thrainer

844 22b7f6f8 Thomas Thrainer
    """
845 22b7f6f8 Thomas Thrainer
    self._CalculateFileStorageDir()
846 22b7f6f8 Thomas Thrainer
847 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
848 22b7f6f8 Thomas Thrainer
      export_info = self._ReadExportInfo()
849 22b7f6f8 Thomas Thrainer
      self._ReadExportParams(export_info)
850 22b7f6f8 Thomas Thrainer
      self._old_instance_name = export_info.get(constants.INISECT_INS, "name")
851 22b7f6f8 Thomas Thrainer
    else:
852 22b7f6f8 Thomas Thrainer
      self._old_instance_name = None
853 22b7f6f8 Thomas Thrainer
854 22b7f6f8 Thomas Thrainer
    if (not self.cfg.GetVGName() and
855 22b7f6f8 Thomas Thrainer
        self.op.disk_template not in constants.DTS_NOT_LVM):
856 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cluster does not support lvm-based"
857 22b7f6f8 Thomas Thrainer
                                 " instances", errors.ECODE_STATE)
858 22b7f6f8 Thomas Thrainer
859 22b7f6f8 Thomas Thrainer
    if (self.op.hypervisor is None or
860 22b7f6f8 Thomas Thrainer
        self.op.hypervisor == constants.VALUE_AUTO):
861 22b7f6f8 Thomas Thrainer
      self.op.hypervisor = self.cfg.GetHypervisorType()
862 22b7f6f8 Thomas Thrainer
863 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
864 22b7f6f8 Thomas Thrainer
    enabled_hvs = cluster.enabled_hypervisors
865 22b7f6f8 Thomas Thrainer
    if self.op.hypervisor not in enabled_hvs:
866 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Selected hypervisor (%s) not enabled in the"
867 22b7f6f8 Thomas Thrainer
                                 " cluster (%s)" %
868 22b7f6f8 Thomas Thrainer
                                 (self.op.hypervisor, ",".join(enabled_hvs)),
869 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
870 22b7f6f8 Thomas Thrainer
871 22b7f6f8 Thomas Thrainer
    # Check tag validity
872 22b7f6f8 Thomas Thrainer
    for tag in self.op.tags:
873 22b7f6f8 Thomas Thrainer
      objects.TaggableObject.ValidateTag(tag)
874 22b7f6f8 Thomas Thrainer
875 22b7f6f8 Thomas Thrainer
    # check hypervisor parameter syntax (locally)
876 22b7f6f8 Thomas Thrainer
    utils.ForceDictType(self.op.hvparams, constants.HVS_PARAMETER_TYPES)
877 22b7f6f8 Thomas Thrainer
    filled_hvp = cluster.SimpleFillHV(self.op.hypervisor, self.op.os_type,
878 22b7f6f8 Thomas Thrainer
                                      self.op.hvparams)
879 22b7f6f8 Thomas Thrainer
    hv_type = hypervisor.GetHypervisorClass(self.op.hypervisor)
880 22b7f6f8 Thomas Thrainer
    hv_type.CheckParameterSyntax(filled_hvp)
881 22b7f6f8 Thomas Thrainer
    self.hv_full = filled_hvp
882 22b7f6f8 Thomas Thrainer
    # check that we don't specify global parameters on an instance
883 5eacbcae Thomas Thrainer
    CheckParamsNotGlobal(self.op.hvparams, constants.HVC_GLOBALS, "hypervisor",
884 5eacbcae Thomas Thrainer
                         "instance", "cluster")
885 22b7f6f8 Thomas Thrainer
886 22b7f6f8 Thomas Thrainer
    # fill and remember the beparams dict
887 22b7f6f8 Thomas Thrainer
    self.be_full = _ComputeFullBeParams(self.op, cluster)
888 22b7f6f8 Thomas Thrainer
889 22b7f6f8 Thomas Thrainer
    # build os parameters
890 22b7f6f8 Thomas Thrainer
    self.os_full = cluster.SimpleFillOS(self.op.os_type, self.op.osparams)
891 22b7f6f8 Thomas Thrainer
892 22b7f6f8 Thomas Thrainer
    # now that hvp/bep are in final format, let's reset to defaults,
893 22b7f6f8 Thomas Thrainer
    # if told to do so
894 22b7f6f8 Thomas Thrainer
    if self.op.identify_defaults:
895 22b7f6f8 Thomas Thrainer
      self._RevertToDefaults(cluster)
896 22b7f6f8 Thomas Thrainer
897 22b7f6f8 Thomas Thrainer
    # NIC buildup
898 22b7f6f8 Thomas Thrainer
    self.nics = _ComputeNics(self.op, cluster, self.check_ip, self.cfg,
899 22b7f6f8 Thomas Thrainer
                             self.proc.GetECId())
900 22b7f6f8 Thomas Thrainer
901 22b7f6f8 Thomas Thrainer
    # disk checks/pre-build
902 22b7f6f8 Thomas Thrainer
    default_vg = self.cfg.GetVGName()
903 5eacbcae Thomas Thrainer
    self.disks = ComputeDisks(self.op, default_vg)
904 22b7f6f8 Thomas Thrainer
905 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
906 22b7f6f8 Thomas Thrainer
      disk_images = []
907 22b7f6f8 Thomas Thrainer
      for idx in range(len(self.disks)):
908 22b7f6f8 Thomas Thrainer
        option = "disk%d_dump" % idx
909 22b7f6f8 Thomas Thrainer
        if export_info.has_option(constants.INISECT_INS, option):
910 22b7f6f8 Thomas Thrainer
          # FIXME: are the old os-es, disk sizes, etc. useful?
911 22b7f6f8 Thomas Thrainer
          export_name = export_info.get(constants.INISECT_INS, option)
912 22b7f6f8 Thomas Thrainer
          image = utils.PathJoin(self.op.src_path, export_name)
913 22b7f6f8 Thomas Thrainer
          disk_images.append(image)
914 22b7f6f8 Thomas Thrainer
        else:
915 22b7f6f8 Thomas Thrainer
          disk_images.append(False)
916 22b7f6f8 Thomas Thrainer
917 22b7f6f8 Thomas Thrainer
      self.src_images = disk_images
918 22b7f6f8 Thomas Thrainer
919 22b7f6f8 Thomas Thrainer
      if self.op.instance_name == self._old_instance_name:
920 22b7f6f8 Thomas Thrainer
        for idx, nic in enumerate(self.nics):
921 22b7f6f8 Thomas Thrainer
          if nic.mac == constants.VALUE_AUTO:
922 22b7f6f8 Thomas Thrainer
            nic_mac_ini = "nic%d_mac" % idx
923 22b7f6f8 Thomas Thrainer
            nic.mac = export_info.get(constants.INISECT_INS, nic_mac_ini)
924 22b7f6f8 Thomas Thrainer
925 22b7f6f8 Thomas Thrainer
    # ENDIF: self.op.mode == constants.INSTANCE_IMPORT
926 22b7f6f8 Thomas Thrainer
927 22b7f6f8 Thomas Thrainer
    # ip ping checks (we use the same ip that was resolved in ExpandNames)
928 22b7f6f8 Thomas Thrainer
    if self.op.ip_check:
929 22b7f6f8 Thomas Thrainer
      if netutils.TcpPing(self.check_ip, constants.DEFAULT_NODED_PORT):
930 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("IP %s of instance %s already in use" %
931 22b7f6f8 Thomas Thrainer
                                   (self.check_ip, self.op.instance_name),
932 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
933 22b7f6f8 Thomas Thrainer
934 22b7f6f8 Thomas Thrainer
    #### mac address generation
935 22b7f6f8 Thomas Thrainer
    # By generating here the mac address both the allocator and the hooks get
936 22b7f6f8 Thomas Thrainer
    # the real final mac address rather than the 'auto' or 'generate' value.
937 22b7f6f8 Thomas Thrainer
    # There is a race condition between the generation and the instance object
938 22b7f6f8 Thomas Thrainer
    # creation, which means that we know the mac is valid now, but we're not
939 22b7f6f8 Thomas Thrainer
    # sure it will be when we actually add the instance. If things go bad
940 22b7f6f8 Thomas Thrainer
    # adding the instance will abort because of a duplicate mac, and the
941 22b7f6f8 Thomas Thrainer
    # creation job will fail.
942 22b7f6f8 Thomas Thrainer
    for nic in self.nics:
943 22b7f6f8 Thomas Thrainer
      if nic.mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
944 22b7f6f8 Thomas Thrainer
        nic.mac = self.cfg.GenerateMAC(nic.network, self.proc.GetECId())
945 22b7f6f8 Thomas Thrainer
946 22b7f6f8 Thomas Thrainer
    #### allocator run
947 22b7f6f8 Thomas Thrainer
948 22b7f6f8 Thomas Thrainer
    if self.op.iallocator is not None:
949 22b7f6f8 Thomas Thrainer
      self._RunAllocator()
950 22b7f6f8 Thomas Thrainer
951 22b7f6f8 Thomas Thrainer
    # Release all unneeded node locks
952 1c3231aa Thomas Thrainer
    keep_locks = filter(None, [self.op.pnode_uuid, self.op.snode_uuid,
953 1c3231aa Thomas Thrainer
                               self.op.src_node_uuid])
954 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE, keep=keep_locks)
955 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE_RES, keep=keep_locks)
956 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE_ALLOC)
957 22b7f6f8 Thomas Thrainer
958 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
959 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES)), \
960 22b7f6f8 Thomas Thrainer
      "Node locks differ from node resource locks"
961 22b7f6f8 Thomas Thrainer
962 22b7f6f8 Thomas Thrainer
    #### node related checks
963 22b7f6f8 Thomas Thrainer
964 22b7f6f8 Thomas Thrainer
    # check primary node
965 1c3231aa Thomas Thrainer
    self.pnode = pnode = self.cfg.GetNodeInfo(self.op.pnode_uuid)
966 22b7f6f8 Thomas Thrainer
    assert self.pnode is not None, \
967 1c3231aa Thomas Thrainer
      "Cannot retrieve locked node %s" % self.op.pnode_uuid
968 22b7f6f8 Thomas Thrainer
    if pnode.offline:
969 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot use offline primary node '%s'" %
970 22b7f6f8 Thomas Thrainer
                                 pnode.name, errors.ECODE_STATE)
971 22b7f6f8 Thomas Thrainer
    if pnode.drained:
972 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot use drained primary node '%s'" %
973 22b7f6f8 Thomas Thrainer
                                 pnode.name, errors.ECODE_STATE)
974 22b7f6f8 Thomas Thrainer
    if not pnode.vm_capable:
975 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot use non-vm_capable primary node"
976 22b7f6f8 Thomas Thrainer
                                 " '%s'" % pnode.name, errors.ECODE_STATE)
977 22b7f6f8 Thomas Thrainer
978 22b7f6f8 Thomas Thrainer
    self.secondaries = []
979 22b7f6f8 Thomas Thrainer
980 22b7f6f8 Thomas Thrainer
    # Fill in any IPs from IP pools. This must happen here, because we need to
981 22b7f6f8 Thomas Thrainer
    # know the nic's primary node, as specified by the iallocator
982 22b7f6f8 Thomas Thrainer
    for idx, nic in enumerate(self.nics):
983 22b7f6f8 Thomas Thrainer
      net_uuid = nic.network
984 22b7f6f8 Thomas Thrainer
      if net_uuid is not None:
985 22b7f6f8 Thomas Thrainer
        nobj = self.cfg.GetNetwork(net_uuid)
986 1c3231aa Thomas Thrainer
        netparams = self.cfg.GetGroupNetParams(net_uuid, self.pnode.uuid)
987 22b7f6f8 Thomas Thrainer
        if netparams is None:
988 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("No netparams found for network"
989 22b7f6f8 Thomas Thrainer
                                     " %s. Propably not connected to"
990 22b7f6f8 Thomas Thrainer
                                     " node's %s nodegroup" %
991 22b7f6f8 Thomas Thrainer
                                     (nobj.name, self.pnode.name),
992 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
993 22b7f6f8 Thomas Thrainer
        self.LogInfo("NIC/%d inherits netparams %s" %
994 22b7f6f8 Thomas Thrainer
                     (idx, netparams.values()))
995 22b7f6f8 Thomas Thrainer
        nic.nicparams = dict(netparams)
996 22b7f6f8 Thomas Thrainer
        if nic.ip is not None:
997 22b7f6f8 Thomas Thrainer
          if nic.ip.lower() == constants.NIC_IP_POOL:
998 22b7f6f8 Thomas Thrainer
            try:
999 22b7f6f8 Thomas Thrainer
              nic.ip = self.cfg.GenerateIp(net_uuid, self.proc.GetECId())
1000 22b7f6f8 Thomas Thrainer
            except errors.ReservationError:
1001 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("Unable to get a free IP for NIC %d"
1002 22b7f6f8 Thomas Thrainer
                                         " from the address pool" % idx,
1003 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_STATE)
1004 22b7f6f8 Thomas Thrainer
            self.LogInfo("Chose IP %s from network %s", nic.ip, nobj.name)
1005 22b7f6f8 Thomas Thrainer
          else:
1006 22b7f6f8 Thomas Thrainer
            try:
1007 22b7f6f8 Thomas Thrainer
              self.cfg.ReserveIp(net_uuid, nic.ip, self.proc.GetECId())
1008 22b7f6f8 Thomas Thrainer
            except errors.ReservationError:
1009 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("IP address %s already in use"
1010 22b7f6f8 Thomas Thrainer
                                         " or does not belong to network %s" %
1011 22b7f6f8 Thomas Thrainer
                                         (nic.ip, nobj.name),
1012 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_NOTUNIQUE)
1013 22b7f6f8 Thomas Thrainer
1014 22b7f6f8 Thomas Thrainer
      # net is None, ip None or given
1015 22b7f6f8 Thomas Thrainer
      elif self.op.conflicts_check:
1016 1c3231aa Thomas Thrainer
        _CheckForConflictingIp(self, nic.ip, self.pnode.uuid)
1017 22b7f6f8 Thomas Thrainer
1018 22b7f6f8 Thomas Thrainer
    # mirror node verification
1019 22b7f6f8 Thomas Thrainer
    if self.op.disk_template in constants.DTS_INT_MIRROR:
1020 1c3231aa Thomas Thrainer
      if self.op.snode_uuid == pnode.uuid:
1021 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("The secondary node cannot be the"
1022 22b7f6f8 Thomas Thrainer
                                   " primary node", errors.ECODE_INVAL)
1023 1c3231aa Thomas Thrainer
      CheckNodeOnline(self, self.op.snode_uuid)
1024 1c3231aa Thomas Thrainer
      CheckNodeNotDrained(self, self.op.snode_uuid)
1025 1c3231aa Thomas Thrainer
      CheckNodeVmCapable(self, self.op.snode_uuid)
1026 1c3231aa Thomas Thrainer
      self.secondaries.append(self.op.snode_uuid)
1027 22b7f6f8 Thomas Thrainer
1028 1c3231aa Thomas Thrainer
      snode = self.cfg.GetNodeInfo(self.op.snode_uuid)
1029 22b7f6f8 Thomas Thrainer
      if pnode.group != snode.group:
1030 22b7f6f8 Thomas Thrainer
        self.LogWarning("The primary and secondary nodes are in two"
1031 22b7f6f8 Thomas Thrainer
                        " different node groups; the disk parameters"
1032 22b7f6f8 Thomas Thrainer
                        " from the first disk's node group will be"
1033 22b7f6f8 Thomas Thrainer
                        " used")
1034 22b7f6f8 Thomas Thrainer
1035 1bb99a33 Bernardo Dal Seno
    nodes = [pnode]
1036 1bb99a33 Bernardo Dal Seno
    if self.op.disk_template in constants.DTS_INT_MIRROR:
1037 1bb99a33 Bernardo Dal Seno
      nodes.append(snode)
1038 1bb99a33 Bernardo Dal Seno
    has_es = lambda n: IsExclusiveStorageEnabledNode(self.cfg, n)
1039 3f3ea14c Bernardo Dal Seno
    excl_stor = compat.any(map(has_es, nodes))
1040 3f3ea14c Bernardo Dal Seno
    if excl_stor and not self.op.disk_template in constants.DTS_EXCL_STORAGE:
1041 3f3ea14c Bernardo Dal Seno
      raise errors.OpPrereqError("Disk template %s not supported with"
1042 3f3ea14c Bernardo Dal Seno
                                 " exclusive storage" % self.op.disk_template,
1043 3f3ea14c Bernardo Dal Seno
                                 errors.ECODE_STATE)
1044 3f3ea14c Bernardo Dal Seno
    for disk in self.disks:
1045 7c848a6a Bernardo Dal Seno
      CheckSpindlesExclusiveStorage(disk, excl_stor, True)
1046 22b7f6f8 Thomas Thrainer
1047 1c3231aa Thomas Thrainer
    node_uuids = [pnode.uuid] + self.secondaries
1048 22b7f6f8 Thomas Thrainer
1049 22b7f6f8 Thomas Thrainer
    if not self.adopt_disks:
1050 22b7f6f8 Thomas Thrainer
      if self.op.disk_template == constants.DT_RBD:
1051 22b7f6f8 Thomas Thrainer
        # _CheckRADOSFreeSpace() is just a placeholder.
1052 22b7f6f8 Thomas Thrainer
        # Any function that checks prerequisites can be placed here.
1053 22b7f6f8 Thomas Thrainer
        # Check if there is enough space on the RADOS cluster.
1054 5eacbcae Thomas Thrainer
        CheckRADOSFreeSpace()
1055 22b7f6f8 Thomas Thrainer
      elif self.op.disk_template == constants.DT_EXT:
1056 22b7f6f8 Thomas Thrainer
        # FIXME: Function that checks prereqs if needed
1057 22b7f6f8 Thomas Thrainer
        pass
1058 4b92e992 Helga Velroyen
      elif self.op.disk_template in utils.GetLvmDiskTemplates():
1059 22b7f6f8 Thomas Thrainer
        # Check lv size requirements, if not adopting
1060 5eacbcae Thomas Thrainer
        req_sizes = ComputeDiskSizePerVG(self.op.disk_template, self.disks)
1061 1c3231aa Thomas Thrainer
        CheckNodesFreeDiskPerVG(self, node_uuids, req_sizes)
1062 4b92e992 Helga Velroyen
      else:
1063 4b92e992 Helga Velroyen
        # FIXME: add checks for other, non-adopting, non-lvm disk templates
1064 4b92e992 Helga Velroyen
        pass
1065 22b7f6f8 Thomas Thrainer
1066 22b7f6f8 Thomas Thrainer
    elif self.op.disk_template == constants.DT_PLAIN: # Check the adoption data
1067 22b7f6f8 Thomas Thrainer
      all_lvs = set(["%s/%s" % (disk[constants.IDISK_VG],
1068 22b7f6f8 Thomas Thrainer
                                disk[constants.IDISK_ADOPT])
1069 22b7f6f8 Thomas Thrainer
                     for disk in self.disks])
1070 22b7f6f8 Thomas Thrainer
      if len(all_lvs) != len(self.disks):
1071 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Duplicate volume names given for adoption",
1072 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1073 22b7f6f8 Thomas Thrainer
      for lv_name in all_lvs:
1074 22b7f6f8 Thomas Thrainer
        try:
1075 22b7f6f8 Thomas Thrainer
          # FIXME: lv_name here is "vg/lv" need to ensure that other calls
1076 22b7f6f8 Thomas Thrainer
          # to ReserveLV uses the same syntax
1077 22b7f6f8 Thomas Thrainer
          self.cfg.ReserveLV(lv_name, self.proc.GetECId())
1078 22b7f6f8 Thomas Thrainer
        except errors.ReservationError:
1079 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("LV named %s used by another instance" %
1080 22b7f6f8 Thomas Thrainer
                                     lv_name, errors.ECODE_NOTUNIQUE)
1081 22b7f6f8 Thomas Thrainer
1082 1c3231aa Thomas Thrainer
      vg_names = self.rpc.call_vg_list([pnode.uuid])[pnode.uuid]
1083 22b7f6f8 Thomas Thrainer
      vg_names.Raise("Cannot get VG information from node %s" % pnode.name)
1084 22b7f6f8 Thomas Thrainer
1085 1c3231aa Thomas Thrainer
      node_lvs = self.rpc.call_lv_list([pnode.uuid],
1086 1c3231aa Thomas Thrainer
                                       vg_names.payload.keys())[pnode.uuid]
1087 22b7f6f8 Thomas Thrainer
      node_lvs.Raise("Cannot get LV information from node %s" % pnode.name)
1088 22b7f6f8 Thomas Thrainer
      node_lvs = node_lvs.payload
1089 22b7f6f8 Thomas Thrainer
1090 22b7f6f8 Thomas Thrainer
      delta = all_lvs.difference(node_lvs.keys())
1091 22b7f6f8 Thomas Thrainer
      if delta:
1092 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing logical volume(s): %s" %
1093 22b7f6f8 Thomas Thrainer
                                   utils.CommaJoin(delta),
1094 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1095 22b7f6f8 Thomas Thrainer
      online_lvs = [lv for lv in all_lvs if node_lvs[lv][2]]
1096 22b7f6f8 Thomas Thrainer
      if online_lvs:
1097 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Online logical volumes found, cannot"
1098 22b7f6f8 Thomas Thrainer
                                   " adopt: %s" % utils.CommaJoin(online_lvs),
1099 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
1100 22b7f6f8 Thomas Thrainer
      # update the size of disk based on what is found
1101 22b7f6f8 Thomas Thrainer
      for dsk in self.disks:
1102 22b7f6f8 Thomas Thrainer
        dsk[constants.IDISK_SIZE] = \
1103 22b7f6f8 Thomas Thrainer
          int(float(node_lvs["%s/%s" % (dsk[constants.IDISK_VG],
1104 22b7f6f8 Thomas Thrainer
                                        dsk[constants.IDISK_ADOPT])][0]))
1105 22b7f6f8 Thomas Thrainer
1106 22b7f6f8 Thomas Thrainer
    elif self.op.disk_template == constants.DT_BLOCK:
1107 22b7f6f8 Thomas Thrainer
      # Normalize and de-duplicate device paths
1108 22b7f6f8 Thomas Thrainer
      all_disks = set([os.path.abspath(disk[constants.IDISK_ADOPT])
1109 22b7f6f8 Thomas Thrainer
                       for disk in self.disks])
1110 22b7f6f8 Thomas Thrainer
      if len(all_disks) != len(self.disks):
1111 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Duplicate disk names given for adoption",
1112 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1113 22b7f6f8 Thomas Thrainer
      baddisks = [d for d in all_disks
1114 22b7f6f8 Thomas Thrainer
                  if not d.startswith(constants.ADOPTABLE_BLOCKDEV_ROOT)]
1115 22b7f6f8 Thomas Thrainer
      if baddisks:
1116 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Device node(s) %s lie outside %s and"
1117 22b7f6f8 Thomas Thrainer
                                   " cannot be adopted" %
1118 22b7f6f8 Thomas Thrainer
                                   (utils.CommaJoin(baddisks),
1119 22b7f6f8 Thomas Thrainer
                                    constants.ADOPTABLE_BLOCKDEV_ROOT),
1120 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1121 22b7f6f8 Thomas Thrainer
1122 1c3231aa Thomas Thrainer
      node_disks = self.rpc.call_bdev_sizes([pnode.uuid],
1123 1c3231aa Thomas Thrainer
                                            list(all_disks))[pnode.uuid]
1124 22b7f6f8 Thomas Thrainer
      node_disks.Raise("Cannot get block device information from node %s" %
1125 22b7f6f8 Thomas Thrainer
                       pnode.name)
1126 22b7f6f8 Thomas Thrainer
      node_disks = node_disks.payload
1127 22b7f6f8 Thomas Thrainer
      delta = all_disks.difference(node_disks.keys())
1128 22b7f6f8 Thomas Thrainer
      if delta:
1129 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing block device(s): %s" %
1130 22b7f6f8 Thomas Thrainer
                                   utils.CommaJoin(delta),
1131 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1132 22b7f6f8 Thomas Thrainer
      for dsk in self.disks:
1133 22b7f6f8 Thomas Thrainer
        dsk[constants.IDISK_SIZE] = \
1134 22b7f6f8 Thomas Thrainer
          int(float(node_disks[dsk[constants.IDISK_ADOPT]]))
1135 22b7f6f8 Thomas Thrainer
1136 22b7f6f8 Thomas Thrainer
    # Verify instance specs
1137 22b7f6f8 Thomas Thrainer
    spindle_use = self.be_full.get(constants.BE_SPINDLE_USE, None)
1138 22b7f6f8 Thomas Thrainer
    ispec = {
1139 22b7f6f8 Thomas Thrainer
      constants.ISPEC_MEM_SIZE: self.be_full.get(constants.BE_MAXMEM, None),
1140 22b7f6f8 Thomas Thrainer
      constants.ISPEC_CPU_COUNT: self.be_full.get(constants.BE_VCPUS, None),
1141 22b7f6f8 Thomas Thrainer
      constants.ISPEC_DISK_COUNT: len(self.disks),
1142 22b7f6f8 Thomas Thrainer
      constants.ISPEC_DISK_SIZE: [disk[constants.IDISK_SIZE]
1143 22b7f6f8 Thomas Thrainer
                                  for disk in self.disks],
1144 22b7f6f8 Thomas Thrainer
      constants.ISPEC_NIC_COUNT: len(self.nics),
1145 22b7f6f8 Thomas Thrainer
      constants.ISPEC_SPINDLE_USE: spindle_use,
1146 22b7f6f8 Thomas Thrainer
      }
1147 22b7f6f8 Thomas Thrainer
1148 22b7f6f8 Thomas Thrainer
    group_info = self.cfg.GetNodeGroup(pnode.group)
1149 22b7f6f8 Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster, group_info)
1150 22b7f6f8 Thomas Thrainer
    res = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec,
1151 22b7f6f8 Thomas Thrainer
                                               self.op.disk_template)
1152 22b7f6f8 Thomas Thrainer
    if not self.op.ignore_ipolicy and res:
1153 22b7f6f8 Thomas Thrainer
      msg = ("Instance allocation to group %s (%s) violates policy: %s" %
1154 22b7f6f8 Thomas Thrainer
             (pnode.group, group_info.name, utils.CommaJoin(res)))
1155 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
1156 22b7f6f8 Thomas Thrainer
1157 1c3231aa Thomas Thrainer
    CheckHVParams(self, node_uuids, self.op.hypervisor, self.op.hvparams)
1158 22b7f6f8 Thomas Thrainer
1159 1c3231aa Thomas Thrainer
    CheckNodeHasOS(self, pnode.uuid, self.op.os_type, self.op.force_variant)
1160 22b7f6f8 Thomas Thrainer
    # check OS parameters (remotely)
1161 1c3231aa Thomas Thrainer
    CheckOSParams(self, True, node_uuids, self.op.os_type, self.os_full)
1162 22b7f6f8 Thomas Thrainer
1163 1c3231aa Thomas Thrainer
    CheckNicsBridgesExist(self, self.nics, self.pnode.uuid)
1164 22b7f6f8 Thomas Thrainer
1165 22b7f6f8 Thomas Thrainer
    #TODO: _CheckExtParams (remotely)
1166 22b7f6f8 Thomas Thrainer
    # Check parameters for extstorage
1167 22b7f6f8 Thomas Thrainer
1168 22b7f6f8 Thomas Thrainer
    # memory check on primary node
1169 22b7f6f8 Thomas Thrainer
    #TODO(dynmem): use MINMEM for checking
1170 22b7f6f8 Thomas Thrainer
    if self.op.start:
1171 a295eb80 Helga Velroyen
      hvfull = objects.FillDict(cluster.hvparams.get(self.op.hypervisor, {}),
1172 a295eb80 Helga Velroyen
                                self.op.hvparams)
1173 1c3231aa Thomas Thrainer
      CheckNodeFreeMemory(self, self.pnode.uuid,
1174 5eacbcae Thomas Thrainer
                          "creating instance %s" % self.op.instance_name,
1175 5eacbcae Thomas Thrainer
                          self.be_full[constants.BE_MAXMEM],
1176 a295eb80 Helga Velroyen
                          self.op.hypervisor, hvfull)
1177 22b7f6f8 Thomas Thrainer
1178 1c3231aa Thomas Thrainer
    self.dry_run_result = list(node_uuids)
1179 22b7f6f8 Thomas Thrainer
1180 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1181 22b7f6f8 Thomas Thrainer
    """Create and add the instance to the cluster.
1182 22b7f6f8 Thomas Thrainer

1183 22b7f6f8 Thomas Thrainer
    """
1184 22b7f6f8 Thomas Thrainer
    assert not (self.owned_locks(locking.LEVEL_NODE_RES) -
1185 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
1186 22b7f6f8 Thomas Thrainer
      "Node locks differ from node resource locks"
1187 22b7f6f8 Thomas Thrainer
    assert not self.glm.is_owned(locking.LEVEL_NODE_ALLOC)
1188 22b7f6f8 Thomas Thrainer
1189 22b7f6f8 Thomas Thrainer
    ht_kind = self.op.hypervisor
1190 22b7f6f8 Thomas Thrainer
    if ht_kind in constants.HTS_REQ_PORT:
1191 22b7f6f8 Thomas Thrainer
      network_port = self.cfg.AllocatePort()
1192 22b7f6f8 Thomas Thrainer
    else:
1193 22b7f6f8 Thomas Thrainer
      network_port = None
1194 22b7f6f8 Thomas Thrainer
1195 da4a52a3 Thomas Thrainer
    instance_uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
1196 da4a52a3 Thomas Thrainer
1197 22b7f6f8 Thomas Thrainer
    # This is ugly but we got a chicken-egg problem here
1198 22b7f6f8 Thomas Thrainer
    # We can only take the group disk parameters, as the instance
1199 22b7f6f8 Thomas Thrainer
    # has no disks yet (we are generating them right here).
1200 1c3231aa Thomas Thrainer
    nodegroup = self.cfg.GetNodeGroup(self.pnode.group)
1201 5eacbcae Thomas Thrainer
    disks = GenerateDiskTemplate(self,
1202 5eacbcae Thomas Thrainer
                                 self.op.disk_template,
1203 da4a52a3 Thomas Thrainer
                                 instance_uuid, self.pnode.uuid,
1204 5eacbcae Thomas Thrainer
                                 self.secondaries,
1205 5eacbcae Thomas Thrainer
                                 self.disks,
1206 5eacbcae Thomas Thrainer
                                 self.instance_file_storage_dir,
1207 5eacbcae Thomas Thrainer
                                 self.op.file_driver,
1208 5eacbcae Thomas Thrainer
                                 0,
1209 5eacbcae Thomas Thrainer
                                 feedback_fn,
1210 5eacbcae Thomas Thrainer
                                 self.cfg.GetGroupDiskParams(nodegroup))
1211 22b7f6f8 Thomas Thrainer
1212 da4a52a3 Thomas Thrainer
    iobj = objects.Instance(name=self.op.instance_name,
1213 da4a52a3 Thomas Thrainer
                            uuid=instance_uuid,
1214 da4a52a3 Thomas Thrainer
                            os=self.op.os_type,
1215 1c3231aa Thomas Thrainer
                            primary_node=self.pnode.uuid,
1216 22b7f6f8 Thomas Thrainer
                            nics=self.nics, disks=disks,
1217 22b7f6f8 Thomas Thrainer
                            disk_template=self.op.disk_template,
1218 1d4a4b26 Thomas Thrainer
                            disks_active=False,
1219 22b7f6f8 Thomas Thrainer
                            admin_state=constants.ADMINST_DOWN,
1220 22b7f6f8 Thomas Thrainer
                            network_port=network_port,
1221 22b7f6f8 Thomas Thrainer
                            beparams=self.op.beparams,
1222 22b7f6f8 Thomas Thrainer
                            hvparams=self.op.hvparams,
1223 22b7f6f8 Thomas Thrainer
                            hypervisor=self.op.hypervisor,
1224 22b7f6f8 Thomas Thrainer
                            osparams=self.op.osparams,
1225 22b7f6f8 Thomas Thrainer
                            )
1226 22b7f6f8 Thomas Thrainer
1227 22b7f6f8 Thomas Thrainer
    if self.op.tags:
1228 22b7f6f8 Thomas Thrainer
      for tag in self.op.tags:
1229 22b7f6f8 Thomas Thrainer
        iobj.AddTag(tag)
1230 22b7f6f8 Thomas Thrainer
1231 22b7f6f8 Thomas Thrainer
    if self.adopt_disks:
1232 22b7f6f8 Thomas Thrainer
      if self.op.disk_template == constants.DT_PLAIN:
1233 22b7f6f8 Thomas Thrainer
        # rename LVs to the newly-generated names; we need to construct
1234 22b7f6f8 Thomas Thrainer
        # 'fake' LV disks with the old data, plus the new unique_id
1235 22b7f6f8 Thomas Thrainer
        tmp_disks = [objects.Disk.FromDict(v.ToDict()) for v in disks]
1236 22b7f6f8 Thomas Thrainer
        rename_to = []
1237 22b7f6f8 Thomas Thrainer
        for t_dsk, a_dsk in zip(tmp_disks, self.disks):
1238 22b7f6f8 Thomas Thrainer
          rename_to.append(t_dsk.logical_id)
1239 22b7f6f8 Thomas Thrainer
          t_dsk.logical_id = (t_dsk.logical_id[0], a_dsk[constants.IDISK_ADOPT])
1240 1c3231aa Thomas Thrainer
          self.cfg.SetDiskID(t_dsk, self.pnode.uuid)
1241 1c3231aa Thomas Thrainer
        result = self.rpc.call_blockdev_rename(self.pnode.uuid,
1242 22b7f6f8 Thomas Thrainer
                                               zip(tmp_disks, rename_to))
1243 22b7f6f8 Thomas Thrainer
        result.Raise("Failed to rename adoped LVs")
1244 22b7f6f8 Thomas Thrainer
    else:
1245 22b7f6f8 Thomas Thrainer
      feedback_fn("* creating instance disks...")
1246 22b7f6f8 Thomas Thrainer
      try:
1247 5eacbcae Thomas Thrainer
        CreateDisks(self, iobj)
1248 22b7f6f8 Thomas Thrainer
      except errors.OpExecError:
1249 22b7f6f8 Thomas Thrainer
        self.LogWarning("Device creation failed")
1250 d0d7d7cf Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(self.op.instance_name)
1251 22b7f6f8 Thomas Thrainer
        raise
1252 22b7f6f8 Thomas Thrainer
1253 d0d7d7cf Thomas Thrainer
    feedback_fn("adding instance %s to cluster config" % self.op.instance_name)
1254 22b7f6f8 Thomas Thrainer
1255 22b7f6f8 Thomas Thrainer
    self.cfg.AddInstance(iobj, self.proc.GetECId())
1256 22b7f6f8 Thomas Thrainer
1257 22b7f6f8 Thomas Thrainer
    # Declare that we don't want to remove the instance lock anymore, as we've
1258 22b7f6f8 Thomas Thrainer
    # added the instance to the config
1259 22b7f6f8 Thomas Thrainer
    del self.remove_locks[locking.LEVEL_INSTANCE]
1260 22b7f6f8 Thomas Thrainer
1261 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
1262 22b7f6f8 Thomas Thrainer
      # Release unused nodes
1263 1c3231aa Thomas Thrainer
      ReleaseLocks(self, locking.LEVEL_NODE, keep=[self.op.src_node_uuid])
1264 22b7f6f8 Thomas Thrainer
    else:
1265 22b7f6f8 Thomas Thrainer
      # Release all nodes
1266 5eacbcae Thomas Thrainer
      ReleaseLocks(self, locking.LEVEL_NODE)
1267 22b7f6f8 Thomas Thrainer
1268 22b7f6f8 Thomas Thrainer
    disk_abort = False
1269 22b7f6f8 Thomas Thrainer
    if not self.adopt_disks and self.cfg.GetClusterInfo().prealloc_wipe_disks:
1270 22b7f6f8 Thomas Thrainer
      feedback_fn("* wiping instance disks...")
1271 22b7f6f8 Thomas Thrainer
      try:
1272 5eacbcae Thomas Thrainer
        WipeDisks(self, iobj)
1273 22b7f6f8 Thomas Thrainer
      except errors.OpExecError, err:
1274 22b7f6f8 Thomas Thrainer
        logging.exception("Wiping disks failed")
1275 22b7f6f8 Thomas Thrainer
        self.LogWarning("Wiping instance disks failed (%s)", err)
1276 22b7f6f8 Thomas Thrainer
        disk_abort = True
1277 22b7f6f8 Thomas Thrainer
1278 22b7f6f8 Thomas Thrainer
    if disk_abort:
1279 22b7f6f8 Thomas Thrainer
      # Something is already wrong with the disks, don't do anything else
1280 22b7f6f8 Thomas Thrainer
      pass
1281 22b7f6f8 Thomas Thrainer
    elif self.op.wait_for_sync:
1282 5eacbcae Thomas Thrainer
      disk_abort = not WaitForSync(self, iobj)
1283 22b7f6f8 Thomas Thrainer
    elif iobj.disk_template in constants.DTS_INT_MIRROR:
1284 22b7f6f8 Thomas Thrainer
      # make sure the disks are not degraded (still sync-ing is ok)
1285 22b7f6f8 Thomas Thrainer
      feedback_fn("* checking mirrors status")
1286 5eacbcae Thomas Thrainer
      disk_abort = not WaitForSync(self, iobj, oneshot=True)
1287 22b7f6f8 Thomas Thrainer
    else:
1288 22b7f6f8 Thomas Thrainer
      disk_abort = False
1289 22b7f6f8 Thomas Thrainer
1290 22b7f6f8 Thomas Thrainer
    if disk_abort:
1291 5eacbcae Thomas Thrainer
      RemoveDisks(self, iobj)
1292 da4a52a3 Thomas Thrainer
      self.cfg.RemoveInstance(iobj.uuid)
1293 22b7f6f8 Thomas Thrainer
      # Make sure the instance lock gets removed
1294 22b7f6f8 Thomas Thrainer
      self.remove_locks[locking.LEVEL_INSTANCE] = iobj.name
1295 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("There are some degraded disks for"
1296 22b7f6f8 Thomas Thrainer
                               " this instance")
1297 22b7f6f8 Thomas Thrainer
1298 1d4a4b26 Thomas Thrainer
    # instance disks are now active
1299 1d4a4b26 Thomas Thrainer
    iobj.disks_active = True
1300 1d4a4b26 Thomas Thrainer
1301 22b7f6f8 Thomas Thrainer
    # Release all node resource locks
1302 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE_RES)
1303 22b7f6f8 Thomas Thrainer
1304 22b7f6f8 Thomas Thrainer
    if iobj.disk_template != constants.DT_DISKLESS and not self.adopt_disks:
1305 22b7f6f8 Thomas Thrainer
      # we need to set the disks ID to the primary node, since the
1306 22b7f6f8 Thomas Thrainer
      # preceding code might or might have not done it, depending on
1307 22b7f6f8 Thomas Thrainer
      # disk template and other options
1308 22b7f6f8 Thomas Thrainer
      for disk in iobj.disks:
1309 1c3231aa Thomas Thrainer
        self.cfg.SetDiskID(disk, self.pnode.uuid)
1310 22b7f6f8 Thomas Thrainer
      if self.op.mode == constants.INSTANCE_CREATE:
1311 22b7f6f8 Thomas Thrainer
        if not self.op.no_install:
1312 22b7f6f8 Thomas Thrainer
          pause_sync = (iobj.disk_template in constants.DTS_INT_MIRROR and
1313 22b7f6f8 Thomas Thrainer
                        not self.op.wait_for_sync)
1314 22b7f6f8 Thomas Thrainer
          if pause_sync:
1315 22b7f6f8 Thomas Thrainer
            feedback_fn("* pausing disk sync to install instance OS")
1316 1c3231aa Thomas Thrainer
            result = self.rpc.call_blockdev_pause_resume_sync(self.pnode.uuid,
1317 22b7f6f8 Thomas Thrainer
                                                              (iobj.disks,
1318 22b7f6f8 Thomas Thrainer
                                                               iobj), True)
1319 22b7f6f8 Thomas Thrainer
            for idx, success in enumerate(result.payload):
1320 22b7f6f8 Thomas Thrainer
              if not success:
1321 22b7f6f8 Thomas Thrainer
                logging.warn("pause-sync of instance %s for disk %d failed",
1322 d0d7d7cf Thomas Thrainer
                             self.op.instance_name, idx)
1323 22b7f6f8 Thomas Thrainer
1324 22b7f6f8 Thomas Thrainer
          feedback_fn("* running the instance OS create scripts...")
1325 22b7f6f8 Thomas Thrainer
          # FIXME: pass debug option from opcode to backend
1326 22b7f6f8 Thomas Thrainer
          os_add_result = \
1327 1c3231aa Thomas Thrainer
            self.rpc.call_instance_os_add(self.pnode.uuid, (iobj, None), False,
1328 22b7f6f8 Thomas Thrainer
                                          self.op.debug_level)
1329 22b7f6f8 Thomas Thrainer
          if pause_sync:
1330 22b7f6f8 Thomas Thrainer
            feedback_fn("* resuming disk sync")
1331 1c3231aa Thomas Thrainer
            result = self.rpc.call_blockdev_pause_resume_sync(self.pnode.uuid,
1332 22b7f6f8 Thomas Thrainer
                                                              (iobj.disks,
1333 22b7f6f8 Thomas Thrainer
                                                               iobj), False)
1334 22b7f6f8 Thomas Thrainer
            for idx, success in enumerate(result.payload):
1335 22b7f6f8 Thomas Thrainer
              if not success:
1336 22b7f6f8 Thomas Thrainer
                logging.warn("resume-sync of instance %s for disk %d failed",
1337 d0d7d7cf Thomas Thrainer
                             self.op.instance_name, idx)
1338 22b7f6f8 Thomas Thrainer
1339 22b7f6f8 Thomas Thrainer
          os_add_result.Raise("Could not add os for instance %s"
1340 d0d7d7cf Thomas Thrainer
                              " on node %s" % (self.op.instance_name,
1341 d0d7d7cf Thomas Thrainer
                                               self.pnode.name))
1342 22b7f6f8 Thomas Thrainer
1343 22b7f6f8 Thomas Thrainer
      else:
1344 22b7f6f8 Thomas Thrainer
        if self.op.mode == constants.INSTANCE_IMPORT:
1345 22b7f6f8 Thomas Thrainer
          feedback_fn("* running the instance OS import scripts...")
1346 22b7f6f8 Thomas Thrainer
1347 22b7f6f8 Thomas Thrainer
          transfers = []
1348 22b7f6f8 Thomas Thrainer
1349 22b7f6f8 Thomas Thrainer
          for idx, image in enumerate(self.src_images):
1350 22b7f6f8 Thomas Thrainer
            if not image:
1351 22b7f6f8 Thomas Thrainer
              continue
1352 22b7f6f8 Thomas Thrainer
1353 22b7f6f8 Thomas Thrainer
            # FIXME: pass debug option from opcode to backend
1354 22b7f6f8 Thomas Thrainer
            dt = masterd.instance.DiskTransfer("disk/%s" % idx,
1355 22b7f6f8 Thomas Thrainer
                                               constants.IEIO_FILE, (image, ),
1356 22b7f6f8 Thomas Thrainer
                                               constants.IEIO_SCRIPT,
1357 22b7f6f8 Thomas Thrainer
                                               (iobj.disks[idx], idx),
1358 22b7f6f8 Thomas Thrainer
                                               None)
1359 22b7f6f8 Thomas Thrainer
            transfers.append(dt)
1360 22b7f6f8 Thomas Thrainer
1361 22b7f6f8 Thomas Thrainer
          import_result = \
1362 22b7f6f8 Thomas Thrainer
            masterd.instance.TransferInstanceData(self, feedback_fn,
1363 1c3231aa Thomas Thrainer
                                                  self.op.src_node_uuid,
1364 1c3231aa Thomas Thrainer
                                                  self.pnode.uuid,
1365 22b7f6f8 Thomas Thrainer
                                                  self.pnode.secondary_ip,
1366 22b7f6f8 Thomas Thrainer
                                                  iobj, transfers)
1367 22b7f6f8 Thomas Thrainer
          if not compat.all(import_result):
1368 22b7f6f8 Thomas Thrainer
            self.LogWarning("Some disks for instance %s on node %s were not"
1369 d0d7d7cf Thomas Thrainer
                            " imported successfully" % (self.op.instance_name,
1370 d0d7d7cf Thomas Thrainer
                                                        self.pnode.name))
1371 22b7f6f8 Thomas Thrainer
1372 22b7f6f8 Thomas Thrainer
          rename_from = self._old_instance_name
1373 22b7f6f8 Thomas Thrainer
1374 22b7f6f8 Thomas Thrainer
        elif self.op.mode == constants.INSTANCE_REMOTE_IMPORT:
1375 22b7f6f8 Thomas Thrainer
          feedback_fn("* preparing remote import...")
1376 22b7f6f8 Thomas Thrainer
          # The source cluster will stop the instance before attempting to make
1377 22b7f6f8 Thomas Thrainer
          # a connection. In some cases stopping an instance can take a long
1378 22b7f6f8 Thomas Thrainer
          # time, hence the shutdown timeout is added to the connection
1379 22b7f6f8 Thomas Thrainer
          # timeout.
1380 22b7f6f8 Thomas Thrainer
          connect_timeout = (constants.RIE_CONNECT_TIMEOUT +
1381 22b7f6f8 Thomas Thrainer
                             self.op.source_shutdown_timeout)
1382 22b7f6f8 Thomas Thrainer
          timeouts = masterd.instance.ImportExportTimeouts(connect_timeout)
1383 22b7f6f8 Thomas Thrainer
1384 1c3231aa Thomas Thrainer
          assert iobj.primary_node == self.pnode.uuid
1385 22b7f6f8 Thomas Thrainer
          disk_results = \
1386 22b7f6f8 Thomas Thrainer
            masterd.instance.RemoteImport(self, feedback_fn, iobj, self.pnode,
1387 22b7f6f8 Thomas Thrainer
                                          self.source_x509_ca,
1388 22b7f6f8 Thomas Thrainer
                                          self._cds, timeouts)
1389 22b7f6f8 Thomas Thrainer
          if not compat.all(disk_results):
1390 22b7f6f8 Thomas Thrainer
            # TODO: Should the instance still be started, even if some disks
1391 22b7f6f8 Thomas Thrainer
            # failed to import (valid for local imports, too)?
1392 22b7f6f8 Thomas Thrainer
            self.LogWarning("Some disks for instance %s on node %s were not"
1393 d0d7d7cf Thomas Thrainer
                            " imported successfully" % (self.op.instance_name,
1394 d0d7d7cf Thomas Thrainer
                                                        self.pnode.name))
1395 22b7f6f8 Thomas Thrainer
1396 22b7f6f8 Thomas Thrainer
          rename_from = self.source_instance_name
1397 22b7f6f8 Thomas Thrainer
1398 22b7f6f8 Thomas Thrainer
        else:
1399 22b7f6f8 Thomas Thrainer
          # also checked in the prereq part
1400 22b7f6f8 Thomas Thrainer
          raise errors.ProgrammerError("Unknown OS initialization mode '%s'"
1401 22b7f6f8 Thomas Thrainer
                                       % self.op.mode)
1402 22b7f6f8 Thomas Thrainer
1403 22b7f6f8 Thomas Thrainer
        # Run rename script on newly imported instance
1404 d0d7d7cf Thomas Thrainer
        assert iobj.name == self.op.instance_name
1405 d0d7d7cf Thomas Thrainer
        feedback_fn("Running rename script for %s" % self.op.instance_name)
1406 1c3231aa Thomas Thrainer
        result = self.rpc.call_instance_run_rename(self.pnode.uuid, iobj,
1407 22b7f6f8 Thomas Thrainer
                                                   rename_from,
1408 22b7f6f8 Thomas Thrainer
                                                   self.op.debug_level)
1409 c7dd65be Klaus Aehlig
        result.Warn("Failed to run rename script for %s on node %s" %
1410 d0d7d7cf Thomas Thrainer
                    (self.op.instance_name, self.pnode.name), self.LogWarning)
1411 22b7f6f8 Thomas Thrainer
1412 22b7f6f8 Thomas Thrainer
    assert not self.owned_locks(locking.LEVEL_NODE_RES)
1413 22b7f6f8 Thomas Thrainer
1414 22b7f6f8 Thomas Thrainer
    if self.op.start:
1415 22b7f6f8 Thomas Thrainer
      iobj.admin_state = constants.ADMINST_UP
1416 22b7f6f8 Thomas Thrainer
      self.cfg.Update(iobj, feedback_fn)
1417 d0d7d7cf Thomas Thrainer
      logging.info("Starting instance %s on node %s", self.op.instance_name,
1418 d0d7d7cf Thomas Thrainer
                   self.pnode.name)
1419 22b7f6f8 Thomas Thrainer
      feedback_fn("* starting instance...")
1420 1c3231aa Thomas Thrainer
      result = self.rpc.call_instance_start(self.pnode.uuid, (iobj, None, None),
1421 22b7f6f8 Thomas Thrainer
                                            False, self.op.reason)
1422 22b7f6f8 Thomas Thrainer
      result.Raise("Could not start instance")
1423 22b7f6f8 Thomas Thrainer
1424 22b7f6f8 Thomas Thrainer
    return list(iobj.all_nodes)
1425 22b7f6f8 Thomas Thrainer
1426 22b7f6f8 Thomas Thrainer
1427 22b7f6f8 Thomas Thrainer
class LUInstanceRename(LogicalUnit):
1428 22b7f6f8 Thomas Thrainer
  """Rename an instance.
1429 22b7f6f8 Thomas Thrainer

1430 22b7f6f8 Thomas Thrainer
  """
1431 22b7f6f8 Thomas Thrainer
  HPATH = "instance-rename"
1432 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1433 22b7f6f8 Thomas Thrainer
1434 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1435 22b7f6f8 Thomas Thrainer
    """Check arguments.
1436 22b7f6f8 Thomas Thrainer

1437 22b7f6f8 Thomas Thrainer
    """
1438 22b7f6f8 Thomas Thrainer
    if self.op.ip_check and not self.op.name_check:
1439 22b7f6f8 Thomas Thrainer
      # TODO: make the ip check more flexible and not depend on the name check
1440 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("IP address check requires a name check",
1441 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1442 22b7f6f8 Thomas Thrainer
1443 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1444 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1445 22b7f6f8 Thomas Thrainer

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

1448 22b7f6f8 Thomas Thrainer
    """
1449 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1450 22b7f6f8 Thomas Thrainer
    env["INSTANCE_NEW_NAME"] = self.op.new_name
1451 22b7f6f8 Thomas Thrainer
    return env
1452 22b7f6f8 Thomas Thrainer
1453 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1454 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1455 22b7f6f8 Thomas Thrainer

1456 22b7f6f8 Thomas Thrainer
    """
1457 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
1458 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1459 22b7f6f8 Thomas Thrainer
1460 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1461 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1462 22b7f6f8 Thomas Thrainer

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

1465 22b7f6f8 Thomas Thrainer
    """
1466 da4a52a3 Thomas Thrainer
    (self.op.instance_uuid, self.op.instance_name) = \
1467 da4a52a3 Thomas Thrainer
      ExpandInstanceUuidAndName(self.cfg, self.op.instance_uuid,
1468 da4a52a3 Thomas Thrainer
                                self.op.instance_name)
1469 da4a52a3 Thomas Thrainer
    instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1470 22b7f6f8 Thomas Thrainer
    assert instance is not None
1471 1f7c8208 Helga Velroyen
1472 1f7c8208 Helga Velroyen
    # It should actually not happen that an instance is running with a disabled
1473 1f7c8208 Helga Velroyen
    # disk template, but in case it does, the renaming of file-based instances
1474 1f7c8208 Helga Velroyen
    # will fail horribly. Thus, we test it before.
1475 1f7c8208 Helga Velroyen
    if (instance.disk_template in constants.DTS_FILEBASED and
1476 1f7c8208 Helga Velroyen
        self.op.new_name != instance.name):
1477 1f7c8208 Helga Velroyen
      CheckDiskTemplateEnabled(self.cfg.GetClusterInfo(),
1478 1f7c8208 Helga Velroyen
                               instance.disk_template)
1479 1f7c8208 Helga Velroyen
1480 5eacbcae Thomas Thrainer
    CheckNodeOnline(self, instance.primary_node)
1481 5eacbcae Thomas Thrainer
    CheckInstanceState(self, instance, INSTANCE_NOT_RUNNING,
1482 5eacbcae Thomas Thrainer
                       msg="cannot rename")
1483 22b7f6f8 Thomas Thrainer
    self.instance = instance
1484 22b7f6f8 Thomas Thrainer
1485 22b7f6f8 Thomas Thrainer
    new_name = self.op.new_name
1486 22b7f6f8 Thomas Thrainer
    if self.op.name_check:
1487 22b7f6f8 Thomas Thrainer
      hostname = _CheckHostnameSane(self, new_name)
1488 22b7f6f8 Thomas Thrainer
      new_name = self.op.new_name = hostname.name
1489 22b7f6f8 Thomas Thrainer
      if (self.op.ip_check and
1490 22b7f6f8 Thomas Thrainer
          netutils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT)):
1491 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("IP %s of instance %s already in use" %
1492 22b7f6f8 Thomas Thrainer
                                   (hostname.ip, new_name),
1493 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
1494 22b7f6f8 Thomas Thrainer
1495 da4a52a3 Thomas Thrainer
    instance_names = [inst.name for
1496 da4a52a3 Thomas Thrainer
                      inst in self.cfg.GetAllInstancesInfo().values()]
1497 da4a52a3 Thomas Thrainer
    if new_name in instance_names and new_name != instance.name:
1498 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
1499 22b7f6f8 Thomas Thrainer
                                 new_name, errors.ECODE_EXISTS)
1500 22b7f6f8 Thomas Thrainer
1501 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1502 22b7f6f8 Thomas Thrainer
    """Rename the instance.
1503 22b7f6f8 Thomas Thrainer

1504 22b7f6f8 Thomas Thrainer
    """
1505 d0d7d7cf Thomas Thrainer
    old_name = self.instance.name
1506 22b7f6f8 Thomas Thrainer
1507 22b7f6f8 Thomas Thrainer
    rename_file_storage = False
1508 d0d7d7cf Thomas Thrainer
    if (self.instance.disk_template in constants.DTS_FILEBASED and
1509 d0d7d7cf Thomas Thrainer
        self.op.new_name != self.instance.name):
1510 d0d7d7cf Thomas Thrainer
      old_file_storage_dir = os.path.dirname(
1511 d0d7d7cf Thomas Thrainer
                               self.instance.disks[0].logical_id[1])
1512 22b7f6f8 Thomas Thrainer
      rename_file_storage = True
1513 22b7f6f8 Thomas Thrainer
1514 da4a52a3 Thomas Thrainer
    self.cfg.RenameInstance(self.instance.uuid, self.op.new_name)
1515 22b7f6f8 Thomas Thrainer
    # Change the instance lock. This is definitely safe while we hold the BGL.
1516 22b7f6f8 Thomas Thrainer
    # Otherwise the new lock would have to be added in acquired mode.
1517 22b7f6f8 Thomas Thrainer
    assert self.REQ_BGL
1518 22b7f6f8 Thomas Thrainer
    assert locking.BGL in self.owned_locks(locking.LEVEL_CLUSTER)
1519 22b7f6f8 Thomas Thrainer
    self.glm.remove(locking.LEVEL_INSTANCE, old_name)
1520 22b7f6f8 Thomas Thrainer
    self.glm.add(locking.LEVEL_INSTANCE, self.op.new_name)
1521 22b7f6f8 Thomas Thrainer
1522 22b7f6f8 Thomas Thrainer
    # re-read the instance from the configuration after rename
1523 da4a52a3 Thomas Thrainer
    renamed_inst = self.cfg.GetInstanceInfo(self.instance.uuid)
1524 22b7f6f8 Thomas Thrainer
1525 22b7f6f8 Thomas Thrainer
    if rename_file_storage:
1526 d0d7d7cf Thomas Thrainer
      new_file_storage_dir = os.path.dirname(
1527 d0d7d7cf Thomas Thrainer
                               renamed_inst.disks[0].logical_id[1])
1528 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_file_storage_dir_rename(renamed_inst.primary_node,
1529 22b7f6f8 Thomas Thrainer
                                                     old_file_storage_dir,
1530 22b7f6f8 Thomas Thrainer
                                                     new_file_storage_dir)
1531 22b7f6f8 Thomas Thrainer
      result.Raise("Could not rename on node %s directory '%s' to '%s'"
1532 22b7f6f8 Thomas Thrainer
                   " (but the instance has been renamed in Ganeti)" %
1533 d0d7d7cf Thomas Thrainer
                   (self.cfg.GetNodeName(renamed_inst.primary_node),
1534 1c3231aa Thomas Thrainer
                    old_file_storage_dir, new_file_storage_dir))
1535 22b7f6f8 Thomas Thrainer
1536 d0d7d7cf Thomas Thrainer
    StartInstanceDisks(self, renamed_inst, None)
1537 22b7f6f8 Thomas Thrainer
    # update info on disks
1538 d0d7d7cf Thomas Thrainer
    info = GetInstanceInfoText(renamed_inst)
1539 d0d7d7cf Thomas Thrainer
    for (idx, disk) in enumerate(renamed_inst.disks):
1540 d0d7d7cf Thomas Thrainer
      for node_uuid in renamed_inst.all_nodes:
1541 1c3231aa Thomas Thrainer
        self.cfg.SetDiskID(disk, node_uuid)
1542 1c3231aa Thomas Thrainer
        result = self.rpc.call_blockdev_setinfo(node_uuid, disk, info)
1543 1c3231aa Thomas Thrainer
        result.Warn("Error setting info on node %s for disk %s" %
1544 1c3231aa Thomas Thrainer
                    (self.cfg.GetNodeName(node_uuid), idx), self.LogWarning)
1545 22b7f6f8 Thomas Thrainer
    try:
1546 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_instance_run_rename(renamed_inst.primary_node,
1547 d0d7d7cf Thomas Thrainer
                                                 renamed_inst, old_name,
1548 d0d7d7cf Thomas Thrainer
                                                 self.op.debug_level)
1549 c7dd65be Klaus Aehlig
      result.Warn("Could not run OS rename script for instance %s on node %s"
1550 c7dd65be Klaus Aehlig
                  " (but the instance has been renamed in Ganeti)" %
1551 d0d7d7cf Thomas Thrainer
                  (renamed_inst.name,
1552 d0d7d7cf Thomas Thrainer
                   self.cfg.GetNodeName(renamed_inst.primary_node)),
1553 1c3231aa Thomas Thrainer
                  self.LogWarning)
1554 22b7f6f8 Thomas Thrainer
    finally:
1555 d0d7d7cf Thomas Thrainer
      ShutdownInstanceDisks(self, renamed_inst)
1556 22b7f6f8 Thomas Thrainer
1557 d0d7d7cf Thomas Thrainer
    return renamed_inst.name
1558 22b7f6f8 Thomas Thrainer
1559 22b7f6f8 Thomas Thrainer
1560 22b7f6f8 Thomas Thrainer
class LUInstanceRemove(LogicalUnit):
1561 22b7f6f8 Thomas Thrainer
  """Remove an instance.
1562 22b7f6f8 Thomas Thrainer

1563 22b7f6f8 Thomas Thrainer
  """
1564 22b7f6f8 Thomas Thrainer
  HPATH = "instance-remove"
1565 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1566 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1567 22b7f6f8 Thomas Thrainer
1568 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1569 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1570 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
1571 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1572 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
1573 22b7f6f8 Thomas Thrainer
1574 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1575 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1576 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
1577 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1578 22b7f6f8 Thomas Thrainer
      # Copy node locks
1579 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1580 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1581 22b7f6f8 Thomas Thrainer
1582 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1583 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1584 22b7f6f8 Thomas Thrainer

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

1587 22b7f6f8 Thomas Thrainer
    """
1588 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1589 22b7f6f8 Thomas Thrainer
    env["SHUTDOWN_TIMEOUT"] = self.op.shutdown_timeout
1590 22b7f6f8 Thomas Thrainer
    return env
1591 22b7f6f8 Thomas Thrainer
1592 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1593 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1594 22b7f6f8 Thomas Thrainer

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

1603 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1604 22b7f6f8 Thomas Thrainer

1605 22b7f6f8 Thomas Thrainer
    """
1606 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1607 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1608 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1609 22b7f6f8 Thomas Thrainer
1610 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1611 22b7f6f8 Thomas Thrainer
    """Remove the instance.
1612 22b7f6f8 Thomas Thrainer

1613 22b7f6f8 Thomas Thrainer
    """
1614 d0d7d7cf Thomas Thrainer
    logging.info("Shutting down instance %s on node %s", self.instance.name,
1615 d0d7d7cf Thomas Thrainer
                 self.cfg.GetNodeName(self.instance.primary_node))
1616 22b7f6f8 Thomas Thrainer
1617 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_instance_shutdown(self.instance.primary_node,
1618 d0d7d7cf Thomas Thrainer
                                             self.instance,
1619 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1620 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1621 c7dd65be Klaus Aehlig
    if self.op.ignore_failures:
1622 c7dd65be Klaus Aehlig
      result.Warn("Warning: can't shutdown instance", feedback_fn)
1623 c7dd65be Klaus Aehlig
    else:
1624 c7dd65be Klaus Aehlig
      result.Raise("Could not shutdown instance %s on node %s" %
1625 d0d7d7cf Thomas Thrainer
                   (self.instance.name,
1626 d0d7d7cf Thomas Thrainer
                    self.cfg.GetNodeName(self.instance.primary_node)))
1627 22b7f6f8 Thomas Thrainer
1628 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1629 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1630 d0d7d7cf Thomas Thrainer
    assert not (set(self.instance.all_nodes) -
1631 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
1632 22b7f6f8 Thomas Thrainer
      "Not owning correct locks"
1633 22b7f6f8 Thomas Thrainer
1634 d0d7d7cf Thomas Thrainer
    RemoveInstance(self, feedback_fn, self.instance, self.op.ignore_failures)
1635 22b7f6f8 Thomas Thrainer
1636 22b7f6f8 Thomas Thrainer
1637 22b7f6f8 Thomas Thrainer
class LUInstanceMove(LogicalUnit):
1638 22b7f6f8 Thomas Thrainer
  """Move an instance by data-copying.
1639 22b7f6f8 Thomas Thrainer

1640 22b7f6f8 Thomas Thrainer
  """
1641 22b7f6f8 Thomas Thrainer
  HPATH = "instance-move"
1642 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1643 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1644 22b7f6f8 Thomas Thrainer
1645 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1646 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1647 1c3231aa Thomas Thrainer
    (self.op.target_node_uuid, self.op.target_node) = \
1648 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.target_node_uuid,
1649 1c3231aa Thomas Thrainer
                            self.op.target_node)
1650 b9aae98b Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = [self.op.target_node_uuid]
1651 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1652 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
1653 22b7f6f8 Thomas Thrainer
1654 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1655 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1656 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes(primary_only=True)
1657 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1658 22b7f6f8 Thomas Thrainer
      # Copy node locks
1659 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1660 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1661 22b7f6f8 Thomas Thrainer
1662 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1663 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1664 22b7f6f8 Thomas Thrainer

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

1667 22b7f6f8 Thomas Thrainer
    """
1668 22b7f6f8 Thomas Thrainer
    env = {
1669 22b7f6f8 Thomas Thrainer
      "TARGET_NODE": self.op.target_node,
1670 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
1671 22b7f6f8 Thomas Thrainer
      }
1672 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
1673 22b7f6f8 Thomas Thrainer
    return env
1674 22b7f6f8 Thomas Thrainer
1675 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1676 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1677 22b7f6f8 Thomas Thrainer

1678 22b7f6f8 Thomas Thrainer
    """
1679 22b7f6f8 Thomas Thrainer
    nl = [
1680 22b7f6f8 Thomas Thrainer
      self.cfg.GetMasterNode(),
1681 22b7f6f8 Thomas Thrainer
      self.instance.primary_node,
1682 1c3231aa Thomas Thrainer
      self.op.target_node_uuid,
1683 22b7f6f8 Thomas Thrainer
      ]
1684 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1685 22b7f6f8 Thomas Thrainer
1686 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1687 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1688 22b7f6f8 Thomas Thrainer

1689 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1690 22b7f6f8 Thomas Thrainer

1691 22b7f6f8 Thomas Thrainer
    """
1692 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1693 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1694 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1695 22b7f6f8 Thomas Thrainer
1696 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template not in constants.DTS_COPYABLE:
1697 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk template %s not suitable for copying" %
1698 d0d7d7cf Thomas Thrainer
                                 self.instance.disk_template,
1699 d0d7d7cf Thomas Thrainer
                                 errors.ECODE_STATE)
1700 22b7f6f8 Thomas Thrainer
1701 1c3231aa Thomas Thrainer
    target_node = self.cfg.GetNodeInfo(self.op.target_node_uuid)
1702 1c3231aa Thomas Thrainer
    assert target_node is not None, \
1703 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked node %s" % self.op.target_node
1704 22b7f6f8 Thomas Thrainer
1705 1c3231aa Thomas Thrainer
    self.target_node_uuid = target_node.uuid
1706 d0d7d7cf Thomas Thrainer
    if target_node.uuid == self.instance.primary_node:
1707 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance %s is already on the node %s" %
1708 d0d7d7cf Thomas Thrainer
                                 (self.instance.name, target_node.name),
1709 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
1710 22b7f6f8 Thomas Thrainer
1711 d0d7d7cf Thomas Thrainer
    bep = self.cfg.GetClusterInfo().FillBE(self.instance)
1712 22b7f6f8 Thomas Thrainer
1713 d0d7d7cf Thomas Thrainer
    for idx, dsk in enumerate(self.instance.disks):
1714 cd3b4ff4 Helga Velroyen
      if dsk.dev_type not in (constants.DT_PLAIN, constants.DT_FILE,
1715 cd3b4ff4 Helga Velroyen
                              constants.DT_SHARED_FILE):
1716 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance disk %d has a complex layout,"
1717 22b7f6f8 Thomas Thrainer
                                   " cannot copy" % idx, errors.ECODE_STATE)
1718 22b7f6f8 Thomas Thrainer
1719 1c3231aa Thomas Thrainer
    CheckNodeOnline(self, target_node.uuid)
1720 1c3231aa Thomas Thrainer
    CheckNodeNotDrained(self, target_node.uuid)
1721 1c3231aa Thomas Thrainer
    CheckNodeVmCapable(self, target_node.uuid)
1722 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
1723 1c3231aa Thomas Thrainer
    group_info = self.cfg.GetNodeGroup(target_node.group)
1724 22b7f6f8 Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster, group_info)
1725 d0d7d7cf Thomas Thrainer
    CheckTargetNodeIPolicy(self, ipolicy, self.instance, target_node, self.cfg,
1726 5eacbcae Thomas Thrainer
                           ignore=self.op.ignore_ipolicy)
1727 22b7f6f8 Thomas Thrainer
1728 d0d7d7cf Thomas Thrainer
    if self.instance.admin_state == constants.ADMINST_UP:
1729 22b7f6f8 Thomas Thrainer
      # check memory requirements on the secondary node
1730 a295eb80 Helga Velroyen
      CheckNodeFreeMemory(
1731 1c3231aa Thomas Thrainer
          self, target_node.uuid, "failing over instance %s" %
1732 d0d7d7cf Thomas Thrainer
          self.instance.name, bep[constants.BE_MAXMEM],
1733 d0d7d7cf Thomas Thrainer
          self.instance.hypervisor,
1734 d0d7d7cf Thomas Thrainer
          self.cfg.GetClusterInfo().hvparams[self.instance.hypervisor])
1735 22b7f6f8 Thomas Thrainer
    else:
1736 22b7f6f8 Thomas Thrainer
      self.LogInfo("Not checking memory on the secondary node as"
1737 22b7f6f8 Thomas Thrainer
                   " instance will not be started")
1738 22b7f6f8 Thomas Thrainer
1739 22b7f6f8 Thomas Thrainer
    # check bridge existance
1740 d0d7d7cf Thomas Thrainer
    CheckInstanceBridgesExist(self, self.instance, node_uuid=target_node.uuid)
1741 22b7f6f8 Thomas Thrainer
1742 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1743 22b7f6f8 Thomas Thrainer
    """Move an instance.
1744 22b7f6f8 Thomas Thrainer

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

1748 22b7f6f8 Thomas Thrainer
    """
1749 d0d7d7cf Thomas Thrainer
    source_node = self.cfg.GetNodeInfo(self.instance.primary_node)
1750 1c3231aa Thomas Thrainer
    target_node = self.cfg.GetNodeInfo(self.target_node_uuid)
1751 22b7f6f8 Thomas Thrainer
1752 22b7f6f8 Thomas Thrainer
    self.LogInfo("Shutting down instance %s on source node %s",
1753 d0d7d7cf Thomas Thrainer
                 self.instance.name, source_node.name)
1754 22b7f6f8 Thomas Thrainer
1755 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1756 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1757 22b7f6f8 Thomas Thrainer
1758 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_instance_shutdown(source_node.uuid, self.instance,
1759 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1760 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1761 c7dd65be Klaus Aehlig
    if self.op.ignore_consistency:
1762 c7dd65be Klaus Aehlig
      result.Warn("Could not shutdown instance %s on node %s. Proceeding"
1763 c7dd65be Klaus Aehlig
                  " anyway. Please make sure node %s is down. Error details" %
1764 d0d7d7cf Thomas Thrainer
                  (self.instance.name, source_node.name, source_node.name),
1765 1c3231aa Thomas Thrainer
                  self.LogWarning)
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, source_node.name))
1769 22b7f6f8 Thomas Thrainer
1770 22b7f6f8 Thomas Thrainer
    # create the target disks
1771 22b7f6f8 Thomas Thrainer
    try:
1772 d0d7d7cf Thomas Thrainer
      CreateDisks(self, self.instance, target_node_uuid=target_node.uuid)
1773 22b7f6f8 Thomas Thrainer
    except errors.OpExecError:
1774 22b7f6f8 Thomas Thrainer
      self.LogWarning("Device creation failed")
1775 da4a52a3 Thomas Thrainer
      self.cfg.ReleaseDRBDMinors(self.instance.uuid)
1776 22b7f6f8 Thomas Thrainer
      raise
1777 22b7f6f8 Thomas Thrainer
1778 22b7f6f8 Thomas Thrainer
    cluster_name = self.cfg.GetClusterInfo().cluster_name
1779 22b7f6f8 Thomas Thrainer
1780 22b7f6f8 Thomas Thrainer
    errs = []
1781 22b7f6f8 Thomas Thrainer
    # activate, get path, copy the data over
1782 d0d7d7cf Thomas Thrainer
    for idx, disk in enumerate(self.instance.disks):
1783 22b7f6f8 Thomas Thrainer
      self.LogInfo("Copying data for disk %d", idx)
1784 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_blockdev_assemble(
1785 d0d7d7cf Thomas Thrainer
                 target_node.uuid, (disk, self.instance), self.instance.name,
1786 d0d7d7cf Thomas Thrainer
                 True, idx)
1787 22b7f6f8 Thomas Thrainer
      if result.fail_msg:
1788 22b7f6f8 Thomas Thrainer
        self.LogWarning("Can't assemble newly created disk %d: %s",
1789 22b7f6f8 Thomas Thrainer
                        idx, result.fail_msg)
1790 22b7f6f8 Thomas Thrainer
        errs.append(result.fail_msg)
1791 22b7f6f8 Thomas Thrainer
        break
1792 22b7f6f8 Thomas Thrainer
      dev_path = result.payload
1793 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_blockdev_export(source_node.uuid, (disk,
1794 d0d7d7cf Thomas Thrainer
                                                                self.instance),
1795 1c3231aa Thomas Thrainer
                                             target_node.name, dev_path,
1796 22b7f6f8 Thomas Thrainer
                                             cluster_name)
1797 22b7f6f8 Thomas Thrainer
      if result.fail_msg:
1798 22b7f6f8 Thomas Thrainer
        self.LogWarning("Can't copy data over for disk %d: %s",
1799 22b7f6f8 Thomas Thrainer
                        idx, result.fail_msg)
1800 22b7f6f8 Thomas Thrainer
        errs.append(result.fail_msg)
1801 22b7f6f8 Thomas Thrainer
        break
1802 22b7f6f8 Thomas Thrainer
1803 22b7f6f8 Thomas Thrainer
    if errs:
1804 22b7f6f8 Thomas Thrainer
      self.LogWarning("Some disks failed to copy, aborting")
1805 22b7f6f8 Thomas Thrainer
      try:
1806 d0d7d7cf Thomas Thrainer
        RemoveDisks(self, self.instance, target_node_uuid=target_node.uuid)
1807 22b7f6f8 Thomas Thrainer
      finally:
1808 da4a52a3 Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(self.instance.uuid)
1809 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Errors during disk copy: %s" %
1810 22b7f6f8 Thomas Thrainer
                                 (",".join(errs),))
1811 22b7f6f8 Thomas Thrainer
1812 d0d7d7cf Thomas Thrainer
    self.instance.primary_node = target_node.uuid
1813 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
1814 22b7f6f8 Thomas Thrainer
1815 22b7f6f8 Thomas Thrainer
    self.LogInfo("Removing the disks on the original node")
1816 d0d7d7cf Thomas Thrainer
    RemoveDisks(self, self.instance, target_node_uuid=source_node.uuid)
1817 22b7f6f8 Thomas Thrainer
1818 22b7f6f8 Thomas Thrainer
    # Only start the instance if it's marked as up
1819 d0d7d7cf Thomas Thrainer
    if self.instance.admin_state == constants.ADMINST_UP:
1820 22b7f6f8 Thomas Thrainer
      self.LogInfo("Starting instance %s on node %s",
1821 d0d7d7cf Thomas Thrainer
                   self.instance.name, target_node.name)
1822 22b7f6f8 Thomas Thrainer
1823 d0d7d7cf Thomas Thrainer
      disks_ok, _ = AssembleInstanceDisks(self, self.instance,
1824 5eacbcae Thomas Thrainer
                                          ignore_secondaries=True)
1825 22b7f6f8 Thomas Thrainer
      if not disks_ok:
1826 d0d7d7cf Thomas Thrainer
        ShutdownInstanceDisks(self, self.instance)
1827 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Can't activate the instance's disks")
1828 22b7f6f8 Thomas Thrainer
1829 1c3231aa Thomas Thrainer
      result = self.rpc.call_instance_start(target_node.uuid,
1830 d0d7d7cf Thomas Thrainer
                                            (self.instance, None, None), False,
1831 22b7f6f8 Thomas Thrainer
                                            self.op.reason)
1832 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
1833 22b7f6f8 Thomas Thrainer
      if msg:
1834 d0d7d7cf Thomas Thrainer
        ShutdownInstanceDisks(self, self.instance)
1835 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not start instance %s on node %s: %s" %
1836 d0d7d7cf Thomas Thrainer
                                 (self.instance.name, target_node.name, msg))
1837 22b7f6f8 Thomas Thrainer
1838 22b7f6f8 Thomas Thrainer
1839 22b7f6f8 Thomas Thrainer
class LUInstanceMultiAlloc(NoHooksLU):
1840 22b7f6f8 Thomas Thrainer
  """Allocates multiple instances at the same time.
1841 22b7f6f8 Thomas Thrainer

1842 22b7f6f8 Thomas Thrainer
  """
1843 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1844 22b7f6f8 Thomas Thrainer
1845 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1846 22b7f6f8 Thomas Thrainer
    """Check arguments.
1847 22b7f6f8 Thomas Thrainer

1848 22b7f6f8 Thomas Thrainer
    """
1849 22b7f6f8 Thomas Thrainer
    nodes = []
1850 22b7f6f8 Thomas Thrainer
    for inst in self.op.instances:
1851 22b7f6f8 Thomas Thrainer
      if inst.iallocator is not None:
1852 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("iallocator are not allowed to be set on"
1853 22b7f6f8 Thomas Thrainer
                                   " instance objects", errors.ECODE_INVAL)
1854 22b7f6f8 Thomas Thrainer
      nodes.append(bool(inst.pnode))
1855 22b7f6f8 Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
1856 22b7f6f8 Thomas Thrainer
        nodes.append(bool(inst.snode))
1857 22b7f6f8 Thomas Thrainer
1858 22b7f6f8 Thomas Thrainer
    has_nodes = compat.any(nodes)
1859 22b7f6f8 Thomas Thrainer
    if compat.all(nodes) ^ has_nodes:
1860 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are instance objects providing"
1861 22b7f6f8 Thomas Thrainer
                                 " pnode/snode while others do not",
1862 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1863 22b7f6f8 Thomas Thrainer
1864 0c072225 Thomas Thrainer
    if not has_nodes and self.op.iallocator is None:
1865 22b7f6f8 Thomas Thrainer
      default_iallocator = self.cfg.GetDefaultIAllocator()
1866 0c072225 Thomas Thrainer
      if default_iallocator:
1867 22b7f6f8 Thomas Thrainer
        self.op.iallocator = default_iallocator
1868 22b7f6f8 Thomas Thrainer
      else:
1869 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No iallocator or nodes on the instances"
1870 22b7f6f8 Thomas Thrainer
                                   " given and no cluster-wide default"
1871 22b7f6f8 Thomas Thrainer
                                   " iallocator found; please specify either"
1872 22b7f6f8 Thomas Thrainer
                                   " an iallocator or nodes on the instances"
1873 22b7f6f8 Thomas Thrainer
                                   " or set a cluster-wide default iallocator",
1874 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1875 22b7f6f8 Thomas Thrainer
1876 22b7f6f8 Thomas Thrainer
    _CheckOpportunisticLocking(self.op)
1877 22b7f6f8 Thomas Thrainer
1878 22b7f6f8 Thomas Thrainer
    dups = utils.FindDuplicates([op.instance_name for op in self.op.instances])
1879 22b7f6f8 Thomas Thrainer
    if dups:
1880 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are duplicate instance names: %s" %
1881 22b7f6f8 Thomas Thrainer
                                 utils.CommaJoin(dups), errors.ECODE_INVAL)
1882 22b7f6f8 Thomas Thrainer
1883 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1884 22b7f6f8 Thomas Thrainer
    """Calculate the locks.
1885 22b7f6f8 Thomas Thrainer

1886 22b7f6f8 Thomas Thrainer
    """
1887 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1888 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
1889 22b7f6f8 Thomas Thrainer
      # iallocator will select nodes and even if no iallocator is used,
1890 22b7f6f8 Thomas Thrainer
      # collisions with LUInstanceCreate should be avoided
1891 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1892 22b7f6f8 Thomas Thrainer
      }
1893 22b7f6f8 Thomas Thrainer
1894 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
1895 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
1896 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = locking.ALL_SET
1897 22b7f6f8 Thomas Thrainer
1898 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
1899 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
1900 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE_RES] = True
1901 22b7f6f8 Thomas Thrainer
    else:
1902 22b7f6f8 Thomas Thrainer
      nodeslist = []
1903 22b7f6f8 Thomas Thrainer
      for inst in self.op.instances:
1904 1c3231aa Thomas Thrainer
        (inst.pnode_uuid, inst.pnode) = \
1905 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, inst.pnode_uuid, inst.pnode)
1906 6869f673 Thomas Thrainer
        nodeslist.append(inst.pnode_uuid)
1907 22b7f6f8 Thomas Thrainer
        if inst.snode is not None:
1908 1c3231aa Thomas Thrainer
          (inst.snode_uuid, inst.snode) = \
1909 1c3231aa Thomas Thrainer
            ExpandNodeUuidAndName(self.cfg, inst.snode_uuid, inst.snode)
1910 6869f673 Thomas Thrainer
          nodeslist.append(inst.snode_uuid)
1911 22b7f6f8 Thomas Thrainer
1912 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodeslist
1913 22b7f6f8 Thomas Thrainer
      # Lock resources of instance's primary and secondary nodes (copy to
1914 22b7f6f8 Thomas Thrainer
      # prevent accidential modification)
1915 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = list(nodeslist)
1916 22b7f6f8 Thomas Thrainer
1917 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1918 22b7f6f8 Thomas Thrainer
    """Check prerequisite.
1919 22b7f6f8 Thomas Thrainer

1920 22b7f6f8 Thomas Thrainer
    """
1921 0c072225 Thomas Thrainer
    if self.op.iallocator:
1922 0c072225 Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
1923 0c072225 Thomas Thrainer
      default_vg = self.cfg.GetVGName()
1924 0c072225 Thomas Thrainer
      ec_id = self.proc.GetECId()
1925 22b7f6f8 Thomas Thrainer
1926 0c072225 Thomas Thrainer
      if self.op.opportunistic_locking:
1927 0c072225 Thomas Thrainer
        # Only consider nodes for which a lock is held
1928 804d72eb Thomas Thrainer
        node_whitelist = self.cfg.GetNodeNames(
1929 804d72eb Thomas Thrainer
                           list(self.owned_locks(locking.LEVEL_NODE)))
1930 0c072225 Thomas Thrainer
      else:
1931 0c072225 Thomas Thrainer
        node_whitelist = None
1932 22b7f6f8 Thomas Thrainer
1933 0c072225 Thomas Thrainer
      insts = [_CreateInstanceAllocRequest(op, ComputeDisks(op, default_vg),
1934 0c072225 Thomas Thrainer
                                           _ComputeNics(op, cluster, None,
1935 0c072225 Thomas Thrainer
                                                        self.cfg, ec_id),
1936 0c072225 Thomas Thrainer
                                           _ComputeFullBeParams(op, cluster),
1937 0c072225 Thomas Thrainer
                                           node_whitelist)
1938 0c072225 Thomas Thrainer
               for op in self.op.instances]
1939 22b7f6f8 Thomas Thrainer
1940 0c072225 Thomas Thrainer
      req = iallocator.IAReqMultiInstanceAlloc(instances=insts)
1941 0c072225 Thomas Thrainer
      ial = iallocator.IAllocator(self.cfg, self.rpc, req)
1942 22b7f6f8 Thomas Thrainer
1943 0c072225 Thomas Thrainer
      ial.Run(self.op.iallocator)
1944 22b7f6f8 Thomas Thrainer
1945 0c072225 Thomas Thrainer
      if not ial.success:
1946 0c072225 Thomas Thrainer
        raise errors.OpPrereqError("Can't compute nodes using"
1947 0c072225 Thomas Thrainer
                                   " iallocator '%s': %s" %
1948 0c072225 Thomas Thrainer
                                   (self.op.iallocator, ial.info),
1949 0c072225 Thomas Thrainer
                                   errors.ECODE_NORES)
1950 22b7f6f8 Thomas Thrainer
1951 0c072225 Thomas Thrainer
      self.ia_result = ial.result
1952 22b7f6f8 Thomas Thrainer
1953 22b7f6f8 Thomas Thrainer
    if self.op.dry_run:
1954 22b7f6f8 Thomas Thrainer
      self.dry_run_result = objects.FillDict(self._ConstructPartialResult(), {
1955 22b7f6f8 Thomas Thrainer
        constants.JOB_IDS_KEY: [],
1956 22b7f6f8 Thomas Thrainer
        })
1957 22b7f6f8 Thomas Thrainer
1958 22b7f6f8 Thomas Thrainer
  def _ConstructPartialResult(self):
1959 22b7f6f8 Thomas Thrainer
    """Contructs the partial result.
1960 22b7f6f8 Thomas Thrainer

1961 22b7f6f8 Thomas Thrainer
    """
1962 0c072225 Thomas Thrainer
    if self.op.iallocator:
1963 0c072225 Thomas Thrainer
      (allocatable, failed_insts) = self.ia_result
1964 0c072225 Thomas Thrainer
      allocatable_insts = map(compat.fst, allocatable)
1965 0c072225 Thomas Thrainer
    else:
1966 0c072225 Thomas Thrainer
      allocatable_insts = [op.instance_name for op in self.op.instances]
1967 0c072225 Thomas Thrainer
      failed_insts = []
1968 0c072225 Thomas Thrainer
1969 22b7f6f8 Thomas Thrainer
    return {
1970 0c072225 Thomas Thrainer
      opcodes.OpInstanceMultiAlloc.ALLOCATABLE_KEY: allocatable_insts,
1971 0c072225 Thomas Thrainer
      opcodes.OpInstanceMultiAlloc.FAILED_KEY: failed_insts,
1972 22b7f6f8 Thomas Thrainer
      }
1973 22b7f6f8 Thomas Thrainer
1974 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1975 22b7f6f8 Thomas Thrainer
    """Executes the opcode.
1976 22b7f6f8 Thomas Thrainer

1977 22b7f6f8 Thomas Thrainer
    """
1978 22b7f6f8 Thomas Thrainer
    jobs = []
1979 0c072225 Thomas Thrainer
    if self.op.iallocator:
1980 0c072225 Thomas Thrainer
      op2inst = dict((op.instance_name, op) for op in self.op.instances)
1981 0c072225 Thomas Thrainer
      (allocatable, failed) = self.ia_result
1982 22b7f6f8 Thomas Thrainer
1983 804d72eb Thomas Thrainer
      for (name, node_names) in allocatable:
1984 0c072225 Thomas Thrainer
        op = op2inst.pop(name)
1985 22b7f6f8 Thomas Thrainer
1986 804d72eb Thomas Thrainer
        (op.pnode_uuid, op.pnode) = \
1987 804d72eb Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, None, node_names[0])
1988 804d72eb Thomas Thrainer
        if len(node_names) > 1:
1989 804d72eb Thomas Thrainer
          (op.snode_uuid, op.snode) = \
1990 804d72eb Thomas Thrainer
            ExpandNodeUuidAndName(self.cfg, None, node_names[1])
1991 22b7f6f8 Thomas Thrainer
1992 804d72eb Thomas Thrainer
          jobs.append([op])
1993 22b7f6f8 Thomas Thrainer
1994 804d72eb Thomas Thrainer
        missing = set(op2inst.keys()) - set(failed)
1995 804d72eb Thomas Thrainer
        assert not missing, \
1996 804d72eb Thomas Thrainer
          "Iallocator did return incomplete result: %s" % \
1997 804d72eb Thomas Thrainer
          utils.CommaJoin(missing)
1998 0c072225 Thomas Thrainer
    else:
1999 0c072225 Thomas Thrainer
      jobs.extend([op] for op in self.op.instances)
2000 22b7f6f8 Thomas Thrainer
2001 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs, **self._ConstructPartialResult())
2002 22b7f6f8 Thomas Thrainer
2003 22b7f6f8 Thomas Thrainer
2004 22b7f6f8 Thomas Thrainer
class _InstNicModPrivate:
2005 22b7f6f8 Thomas Thrainer
  """Data structure for network interface modifications.
2006 22b7f6f8 Thomas Thrainer

2007 22b7f6f8 Thomas Thrainer
  Used by L{LUInstanceSetParams}.
2008 22b7f6f8 Thomas Thrainer

2009 22b7f6f8 Thomas Thrainer
  """
2010 22b7f6f8 Thomas Thrainer
  def __init__(self):
2011 22b7f6f8 Thomas Thrainer
    self.params = None
2012 22b7f6f8 Thomas Thrainer
    self.filled = None
2013 22b7f6f8 Thomas Thrainer
2014 22b7f6f8 Thomas Thrainer
2015 5eacbcae Thomas Thrainer
def _PrepareContainerMods(mods, private_fn):
2016 22b7f6f8 Thomas Thrainer
  """Prepares a list of container modifications by adding a private data field.
2017 22b7f6f8 Thomas Thrainer

2018 22b7f6f8 Thomas Thrainer
  @type mods: list of tuples; (operation, index, parameters)
2019 22b7f6f8 Thomas Thrainer
  @param mods: List of modifications
2020 22b7f6f8 Thomas Thrainer
  @type private_fn: callable or None
2021 22b7f6f8 Thomas Thrainer
  @param private_fn: Callable for constructing a private data field for a
2022 22b7f6f8 Thomas Thrainer
    modification
2023 22b7f6f8 Thomas Thrainer
  @rtype: list
2024 22b7f6f8 Thomas Thrainer

2025 22b7f6f8 Thomas Thrainer
  """
2026 22b7f6f8 Thomas Thrainer
  if private_fn is None:
2027 22b7f6f8 Thomas Thrainer
    fn = lambda: None
2028 22b7f6f8 Thomas Thrainer
  else:
2029 22b7f6f8 Thomas Thrainer
    fn = private_fn
2030 22b7f6f8 Thomas Thrainer
2031 22b7f6f8 Thomas Thrainer
  return [(op, idx, params, fn()) for (op, idx, params) in mods]
2032 22b7f6f8 Thomas Thrainer
2033 22b7f6f8 Thomas Thrainer
2034 1c3231aa Thomas Thrainer
def _CheckNodesPhysicalCPUs(lu, node_uuids, requested, hypervisor_specs):
2035 22b7f6f8 Thomas Thrainer
  """Checks if nodes have enough physical CPUs
2036 22b7f6f8 Thomas Thrainer

2037 22b7f6f8 Thomas Thrainer
  This function checks if all given nodes have the needed number of
2038 22b7f6f8 Thomas Thrainer
  physical CPUs. In case any node has less CPUs or we cannot get the
2039 22b7f6f8 Thomas Thrainer
  information from the node, this function raises an OpPrereqError
2040 22b7f6f8 Thomas Thrainer
  exception.
2041 22b7f6f8 Thomas Thrainer

2042 22b7f6f8 Thomas Thrainer
  @type lu: C{LogicalUnit}
2043 22b7f6f8 Thomas Thrainer
  @param lu: a logical unit from which we get configuration data
2044 1c3231aa Thomas Thrainer
  @type node_uuids: C{list}
2045 1c3231aa Thomas Thrainer
  @param node_uuids: the list of node UUIDs to check
2046 22b7f6f8 Thomas Thrainer
  @type requested: C{int}
2047 22b7f6f8 Thomas Thrainer
  @param requested: the minimum acceptable number of physical CPUs
2048 a295eb80 Helga Velroyen
  @type hypervisor_specs: list of pairs (string, dict of strings)
2049 a295eb80 Helga Velroyen
  @param hypervisor_specs: list of hypervisor specifications in
2050 a295eb80 Helga Velroyen
      pairs (hypervisor_name, hvparams)
2051 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node doesn't have enough CPUs,
2052 22b7f6f8 Thomas Thrainer
      or we cannot check the node
2053 22b7f6f8 Thomas Thrainer

2054 22b7f6f8 Thomas Thrainer
  """
2055 da803ff1 Helga Velroyen
  nodeinfo = lu.rpc.call_node_info(node_uuids, None, hypervisor_specs)
2056 1c3231aa Thomas Thrainer
  for node_uuid in node_uuids:
2057 1c3231aa Thomas Thrainer
    info = nodeinfo[node_uuid]
2058 1c3231aa Thomas Thrainer
    node_name = lu.cfg.GetNodeName(node_uuid)
2059 1c3231aa Thomas Thrainer
    info.Raise("Cannot get current information from node %s" % node_name,
2060 22b7f6f8 Thomas Thrainer
               prereq=True, ecode=errors.ECODE_ENVIRON)
2061 22b7f6f8 Thomas Thrainer
    (_, _, (hv_info, )) = info.payload
2062 22b7f6f8 Thomas Thrainer
    num_cpus = hv_info.get("cpu_total", None)
2063 22b7f6f8 Thomas Thrainer
    if not isinstance(num_cpus, int):
2064 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute the number of physical CPUs"
2065 22b7f6f8 Thomas Thrainer
                                 " on node %s, result was '%s'" %
2066 1c3231aa Thomas Thrainer
                                 (node_name, num_cpus), errors.ECODE_ENVIRON)
2067 22b7f6f8 Thomas Thrainer
    if requested > num_cpus:
2068 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Node %s has %s physical CPUs, but %s are "
2069 1c3231aa Thomas Thrainer
                                 "required" % (node_name, num_cpus, requested),
2070 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_NORES)
2071 22b7f6f8 Thomas Thrainer
2072 22b7f6f8 Thomas Thrainer
2073 22b7f6f8 Thomas Thrainer
def GetItemFromContainer(identifier, kind, container):
2074 22b7f6f8 Thomas Thrainer
  """Return the item refered by the identifier.
2075 22b7f6f8 Thomas Thrainer

2076 22b7f6f8 Thomas Thrainer
  @type identifier: string
2077 22b7f6f8 Thomas Thrainer
  @param identifier: Item index or name or UUID
2078 22b7f6f8 Thomas Thrainer
  @type kind: string
2079 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2080 22b7f6f8 Thomas Thrainer
  @type container: list
2081 22b7f6f8 Thomas Thrainer
  @param container: Container to get the item from
2082 22b7f6f8 Thomas Thrainer

2083 22b7f6f8 Thomas Thrainer
  """
2084 22b7f6f8 Thomas Thrainer
  # Index
2085 22b7f6f8 Thomas Thrainer
  try:
2086 22b7f6f8 Thomas Thrainer
    idx = int(identifier)
2087 22b7f6f8 Thomas Thrainer
    if idx == -1:
2088 22b7f6f8 Thomas Thrainer
      # Append
2089 22b7f6f8 Thomas Thrainer
      absidx = len(container) - 1
2090 22b7f6f8 Thomas Thrainer
    elif idx < 0:
2091 22b7f6f8 Thomas Thrainer
      raise IndexError("Not accepting negative indices other than -1")
2092 22b7f6f8 Thomas Thrainer
    elif idx > len(container):
2093 22b7f6f8 Thomas Thrainer
      raise IndexError("Got %s index %s, but there are only %s" %
2094 22b7f6f8 Thomas Thrainer
                       (kind, idx, len(container)))
2095 22b7f6f8 Thomas Thrainer
    else:
2096 22b7f6f8 Thomas Thrainer
      absidx = idx
2097 22b7f6f8 Thomas Thrainer
    return (absidx, container[idx])
2098 22b7f6f8 Thomas Thrainer
  except ValueError:
2099 22b7f6f8 Thomas Thrainer
    pass
2100 22b7f6f8 Thomas Thrainer
2101 22b7f6f8 Thomas Thrainer
  for idx, item in enumerate(container):
2102 22b7f6f8 Thomas Thrainer
    if item.uuid == identifier or item.name == identifier:
2103 22b7f6f8 Thomas Thrainer
      return (idx, item)
2104 22b7f6f8 Thomas Thrainer
2105 22b7f6f8 Thomas Thrainer
  raise errors.OpPrereqError("Cannot find %s with identifier %s" %
2106 22b7f6f8 Thomas Thrainer
                             (kind, identifier), errors.ECODE_NOENT)
2107 22b7f6f8 Thomas Thrainer
2108 22b7f6f8 Thomas Thrainer
2109 5eacbcae Thomas Thrainer
def _ApplyContainerMods(kind, container, chgdesc, mods,
2110 5eacbcae Thomas Thrainer
                        create_fn, modify_fn, remove_fn):
2111 22b7f6f8 Thomas Thrainer
  """Applies descriptions in C{mods} to C{container}.
2112 22b7f6f8 Thomas Thrainer

2113 22b7f6f8 Thomas Thrainer
  @type kind: string
2114 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2115 22b7f6f8 Thomas Thrainer
  @type container: list
2116 22b7f6f8 Thomas Thrainer
  @param container: Container to modify
2117 22b7f6f8 Thomas Thrainer
  @type chgdesc: None or list
2118 22b7f6f8 Thomas Thrainer
  @param chgdesc: List of applied changes
2119 22b7f6f8 Thomas Thrainer
  @type mods: list
2120 5eacbcae Thomas Thrainer
  @param mods: Modifications as returned by L{_PrepareContainerMods}
2121 22b7f6f8 Thomas Thrainer
  @type create_fn: callable
2122 22b7f6f8 Thomas Thrainer
  @param create_fn: Callback for creating a new item (L{constants.DDM_ADD});
2123 22b7f6f8 Thomas Thrainer
    receives absolute item index, parameters and private data object as added
2124 5eacbcae Thomas Thrainer
    by L{_PrepareContainerMods}, returns tuple containing new item and changes
2125 22b7f6f8 Thomas Thrainer
    as list
2126 22b7f6f8 Thomas Thrainer
  @type modify_fn: callable
2127 22b7f6f8 Thomas Thrainer
  @param modify_fn: Callback for modifying an existing item
2128 22b7f6f8 Thomas Thrainer
    (L{constants.DDM_MODIFY}); receives absolute item index, item, parameters
2129 5eacbcae Thomas Thrainer
    and private data object as added by L{_PrepareContainerMods}, returns
2130 22b7f6f8 Thomas Thrainer
    changes as list
2131 22b7f6f8 Thomas Thrainer
  @type remove_fn: callable
2132 22b7f6f8 Thomas Thrainer
  @param remove_fn: Callback on removing item; receives absolute item index,
2133 5eacbcae Thomas Thrainer
    item and private data object as added by L{_PrepareContainerMods}
2134 22b7f6f8 Thomas Thrainer

2135 22b7f6f8 Thomas Thrainer
  """
2136 22b7f6f8 Thomas Thrainer
  for (op, identifier, params, private) in mods:
2137 22b7f6f8 Thomas Thrainer
    changes = None
2138 22b7f6f8 Thomas Thrainer
2139 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2140 22b7f6f8 Thomas Thrainer
      # Calculate where item will be added
2141 22b7f6f8 Thomas Thrainer
      # When adding an item, identifier can only be an index
2142 22b7f6f8 Thomas Thrainer
      try:
2143 22b7f6f8 Thomas Thrainer
        idx = int(identifier)
2144 22b7f6f8 Thomas Thrainer
      except ValueError:
2145 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Only possitive integer or -1 is accepted as"
2146 22b7f6f8 Thomas Thrainer
                                   " identifier for %s" % constants.DDM_ADD,
2147 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2148 22b7f6f8 Thomas Thrainer
      if idx == -1:
2149 22b7f6f8 Thomas Thrainer
        addidx = len(container)
2150 22b7f6f8 Thomas Thrainer
      else:
2151 22b7f6f8 Thomas Thrainer
        if idx < 0:
2152 22b7f6f8 Thomas Thrainer
          raise IndexError("Not accepting negative indices other than -1")
2153 22b7f6f8 Thomas Thrainer
        elif idx > len(container):
2154 22b7f6f8 Thomas Thrainer
          raise IndexError("Got %s index %s, but there are only %s" %
2155 22b7f6f8 Thomas Thrainer
                           (kind, idx, len(container)))
2156 22b7f6f8 Thomas Thrainer
        addidx = idx
2157 22b7f6f8 Thomas Thrainer
2158 22b7f6f8 Thomas Thrainer
      if create_fn is None:
2159 22b7f6f8 Thomas Thrainer
        item = params
2160 22b7f6f8 Thomas Thrainer
      else:
2161 22b7f6f8 Thomas Thrainer
        (item, changes) = create_fn(addidx, params, private)
2162 22b7f6f8 Thomas Thrainer
2163 22b7f6f8 Thomas Thrainer
      if idx == -1:
2164 22b7f6f8 Thomas Thrainer
        container.append(item)
2165 22b7f6f8 Thomas Thrainer
      else:
2166 22b7f6f8 Thomas Thrainer
        assert idx >= 0
2167 22b7f6f8 Thomas Thrainer
        assert idx <= len(container)
2168 22b7f6f8 Thomas Thrainer
        # list.insert does so before the specified index
2169 22b7f6f8 Thomas Thrainer
        container.insert(idx, item)
2170 22b7f6f8 Thomas Thrainer
    else:
2171 22b7f6f8 Thomas Thrainer
      # Retrieve existing item
2172 22b7f6f8 Thomas Thrainer
      (absidx, item) = GetItemFromContainer(identifier, kind, container)
2173 22b7f6f8 Thomas Thrainer
2174 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2175 22b7f6f8 Thomas Thrainer
        assert not params
2176 22b7f6f8 Thomas Thrainer
2177 22b7f6f8 Thomas Thrainer
        if remove_fn is not None:
2178 22b7f6f8 Thomas Thrainer
          remove_fn(absidx, item, private)
2179 22b7f6f8 Thomas Thrainer
2180 22b7f6f8 Thomas Thrainer
        changes = [("%s/%s" % (kind, absidx), "remove")]
2181 22b7f6f8 Thomas Thrainer
2182 22b7f6f8 Thomas Thrainer
        assert container[absidx] == item
2183 22b7f6f8 Thomas Thrainer
        del container[absidx]
2184 22b7f6f8 Thomas Thrainer
      elif op == constants.DDM_MODIFY:
2185 22b7f6f8 Thomas Thrainer
        if modify_fn is not None:
2186 22b7f6f8 Thomas Thrainer
          changes = modify_fn(absidx, item, params, private)
2187 22b7f6f8 Thomas Thrainer
      else:
2188 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
2189 22b7f6f8 Thomas Thrainer
2190 22b7f6f8 Thomas Thrainer
    assert _TApplyContModsCbChanges(changes)
2191 22b7f6f8 Thomas Thrainer
2192 22b7f6f8 Thomas Thrainer
    if not (chgdesc is None or changes is None):
2193 22b7f6f8 Thomas Thrainer
      chgdesc.extend(changes)
2194 22b7f6f8 Thomas Thrainer
2195 22b7f6f8 Thomas Thrainer
2196 22b7f6f8 Thomas Thrainer
def _UpdateIvNames(base_index, disks):
2197 22b7f6f8 Thomas Thrainer
  """Updates the C{iv_name} attribute of disks.
2198 22b7f6f8 Thomas Thrainer

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

2201 22b7f6f8 Thomas Thrainer
  """
2202 22b7f6f8 Thomas Thrainer
  for (idx, disk) in enumerate(disks):
2203 22b7f6f8 Thomas Thrainer
    disk.iv_name = "disk/%s" % (base_index + idx, )
2204 22b7f6f8 Thomas Thrainer
2205 22b7f6f8 Thomas Thrainer
2206 22b7f6f8 Thomas Thrainer
class LUInstanceSetParams(LogicalUnit):
2207 22b7f6f8 Thomas Thrainer
  """Modifies an instances's parameters.
2208 22b7f6f8 Thomas Thrainer

2209 22b7f6f8 Thomas Thrainer
  """
2210 22b7f6f8 Thomas Thrainer
  HPATH = "instance-modify"
2211 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2212 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2213 22b7f6f8 Thomas Thrainer
2214 22b7f6f8 Thomas Thrainer
  @staticmethod
2215 22b7f6f8 Thomas Thrainer
  def _UpgradeDiskNicMods(kind, mods, verify_fn):
2216 22b7f6f8 Thomas Thrainer
    assert ht.TList(mods)
2217 22b7f6f8 Thomas Thrainer
    assert not mods or len(mods[0]) in (2, 3)
2218 22b7f6f8 Thomas Thrainer
2219 22b7f6f8 Thomas Thrainer
    if mods and len(mods[0]) == 2:
2220 22b7f6f8 Thomas Thrainer
      result = []
2221 22b7f6f8 Thomas Thrainer
2222 22b7f6f8 Thomas Thrainer
      addremove = 0
2223 22b7f6f8 Thomas Thrainer
      for op, params in mods:
2224 22b7f6f8 Thomas Thrainer
        if op in (constants.DDM_ADD, constants.DDM_REMOVE):
2225 22b7f6f8 Thomas Thrainer
          result.append((op, -1, params))
2226 22b7f6f8 Thomas Thrainer
          addremove += 1
2227 22b7f6f8 Thomas Thrainer
2228 22b7f6f8 Thomas Thrainer
          if addremove > 1:
2229 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Only one %s add or remove operation is"
2230 22b7f6f8 Thomas Thrainer
                                       " supported at a time" % kind,
2231 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
2232 22b7f6f8 Thomas Thrainer
        else:
2233 22b7f6f8 Thomas Thrainer
          result.append((constants.DDM_MODIFY, op, params))
2234 22b7f6f8 Thomas Thrainer
2235 22b7f6f8 Thomas Thrainer
      assert verify_fn(result)
2236 22b7f6f8 Thomas Thrainer
    else:
2237 22b7f6f8 Thomas Thrainer
      result = mods
2238 22b7f6f8 Thomas Thrainer
2239 22b7f6f8 Thomas Thrainer
    return result
2240 22b7f6f8 Thomas Thrainer
2241 22b7f6f8 Thomas Thrainer
  @staticmethod
2242 22b7f6f8 Thomas Thrainer
  def _CheckMods(kind, mods, key_types, item_fn):
2243 22b7f6f8 Thomas Thrainer
    """Ensures requested disk/NIC modifications are valid.
2244 22b7f6f8 Thomas Thrainer

2245 22b7f6f8 Thomas Thrainer
    """
2246 22b7f6f8 Thomas Thrainer
    for (op, _, params) in mods:
2247 22b7f6f8 Thomas Thrainer
      assert ht.TDict(params)
2248 22b7f6f8 Thomas Thrainer
2249 22b7f6f8 Thomas Thrainer
      # If 'key_types' is an empty dict, we assume we have an
2250 22b7f6f8 Thomas Thrainer
      # 'ext' template and thus do not ForceDictType
2251 22b7f6f8 Thomas Thrainer
      if key_types:
2252 22b7f6f8 Thomas Thrainer
        utils.ForceDictType(params, key_types)
2253 22b7f6f8 Thomas Thrainer
2254 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2255 22b7f6f8 Thomas Thrainer
        if params:
2256 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("No settings should be passed when"
2257 22b7f6f8 Thomas Thrainer
                                     " removing a %s" % kind,
2258 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2259 22b7f6f8 Thomas Thrainer
      elif op in (constants.DDM_ADD, constants.DDM_MODIFY):
2260 22b7f6f8 Thomas Thrainer
        item_fn(op, params)
2261 22b7f6f8 Thomas Thrainer
      else:
2262 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
2263 22b7f6f8 Thomas Thrainer
2264 22b7f6f8 Thomas Thrainer
  @staticmethod
2265 3f3ea14c Bernardo Dal Seno
  def _VerifyDiskModification(op, params, excl_stor):
2266 22b7f6f8 Thomas Thrainer
    """Verifies a disk modification.
2267 22b7f6f8 Thomas Thrainer

2268 22b7f6f8 Thomas Thrainer
    """
2269 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2270 22b7f6f8 Thomas Thrainer
      mode = params.setdefault(constants.IDISK_MODE, constants.DISK_RDWR)
2271 22b7f6f8 Thomas Thrainer
      if mode not in constants.DISK_ACCESS_SET:
2272 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk access mode '%s'" % mode,
2273 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2274 22b7f6f8 Thomas Thrainer
2275 22b7f6f8 Thomas Thrainer
      size = params.get(constants.IDISK_SIZE, None)
2276 22b7f6f8 Thomas Thrainer
      if size is None:
2277 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Required disk parameter '%s' missing" %
2278 22b7f6f8 Thomas Thrainer
                                   constants.IDISK_SIZE, errors.ECODE_INVAL)
2279 22b7f6f8 Thomas Thrainer
2280 22b7f6f8 Thomas Thrainer
      try:
2281 22b7f6f8 Thomas Thrainer
        size = int(size)
2282 22b7f6f8 Thomas Thrainer
      except (TypeError, ValueError), err:
2283 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk size parameter: %s" % err,
2284 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2285 22b7f6f8 Thomas Thrainer
2286 22b7f6f8 Thomas Thrainer
      params[constants.IDISK_SIZE] = size
2287 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2288 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2289 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2290 22b7f6f8 Thomas Thrainer
2291 7c848a6a Bernardo Dal Seno
      CheckSpindlesExclusiveStorage(params, excl_stor, True)
2292 3f3ea14c Bernardo Dal Seno
2293 22b7f6f8 Thomas Thrainer
    elif op == constants.DDM_MODIFY:
2294 22b7f6f8 Thomas Thrainer
      if constants.IDISK_SIZE in params:
2295 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk size change not possible, use"
2296 22b7f6f8 Thomas Thrainer
                                   " grow-disk", errors.ECODE_INVAL)
2297 22b7f6f8 Thomas Thrainer
      if len(params) > 2:
2298 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk modification doesn't support"
2299 22b7f6f8 Thomas Thrainer
                                   " additional arbitrary parameters",
2300 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2301 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2302 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2303 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2304 22b7f6f8 Thomas Thrainer
2305 22b7f6f8 Thomas Thrainer
  @staticmethod
2306 22b7f6f8 Thomas Thrainer
  def _VerifyNicModification(op, params):
2307 22b7f6f8 Thomas Thrainer
    """Verifies a network interface modification.
2308 22b7f6f8 Thomas Thrainer

2309 22b7f6f8 Thomas Thrainer
    """
2310 22b7f6f8 Thomas Thrainer
    if op in (constants.DDM_ADD, constants.DDM_MODIFY):
2311 22b7f6f8 Thomas Thrainer
      ip = params.get(constants.INIC_IP, None)
2312 22b7f6f8 Thomas Thrainer
      name = params.get(constants.INIC_NAME, None)
2313 22b7f6f8 Thomas Thrainer
      req_net = params.get(constants.INIC_NETWORK, None)
2314 22b7f6f8 Thomas Thrainer
      link = params.get(constants.NIC_LINK, None)
2315 22b7f6f8 Thomas Thrainer
      mode = params.get(constants.NIC_MODE, None)
2316 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2317 22b7f6f8 Thomas Thrainer
        params[constants.INIC_NAME] = None
2318 22b7f6f8 Thomas Thrainer
      if req_net is not None:
2319 22b7f6f8 Thomas Thrainer
        if req_net.lower() == constants.VALUE_NONE:
2320 22b7f6f8 Thomas Thrainer
          params[constants.INIC_NETWORK] = None
2321 22b7f6f8 Thomas Thrainer
          req_net = None
2322 22b7f6f8 Thomas Thrainer
        elif link is not None or mode is not None:
2323 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("If network is given"
2324 22b7f6f8 Thomas Thrainer
                                     " mode or link should not",
2325 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2326 22b7f6f8 Thomas Thrainer
2327 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_ADD:
2328 22b7f6f8 Thomas Thrainer
        macaddr = params.get(constants.INIC_MAC, None)
2329 22b7f6f8 Thomas Thrainer
        if macaddr is None:
2330 22b7f6f8 Thomas Thrainer
          params[constants.INIC_MAC] = constants.VALUE_AUTO
2331 22b7f6f8 Thomas Thrainer
2332 22b7f6f8 Thomas Thrainer
      if ip is not None:
2333 22b7f6f8 Thomas Thrainer
        if ip.lower() == constants.VALUE_NONE:
2334 22b7f6f8 Thomas Thrainer
          params[constants.INIC_IP] = None
2335 22b7f6f8 Thomas Thrainer
        else:
2336 22b7f6f8 Thomas Thrainer
          if ip.lower() == constants.NIC_IP_POOL:
2337 22b7f6f8 Thomas Thrainer
            if op == constants.DDM_ADD and req_net is None:
2338 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("If ip=pool, parameter network"
2339 22b7f6f8 Thomas Thrainer
                                         " cannot be none",
2340 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_INVAL)
2341 22b7f6f8 Thomas Thrainer
          else:
2342 22b7f6f8 Thomas Thrainer
            if not netutils.IPAddress.IsValid(ip):
2343 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("Invalid IP address '%s'" % ip,
2344 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_INVAL)
2345 22b7f6f8 Thomas Thrainer
2346 22b7f6f8 Thomas Thrainer
      if constants.INIC_MAC in params:
2347 22b7f6f8 Thomas Thrainer
        macaddr = params[constants.INIC_MAC]
2348 22b7f6f8 Thomas Thrainer
        if macaddr not in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
2349 22b7f6f8 Thomas Thrainer
          macaddr = utils.NormalizeAndValidateMac(macaddr)
2350 22b7f6f8 Thomas Thrainer
2351 22b7f6f8 Thomas Thrainer
        if op == constants.DDM_MODIFY and macaddr == constants.VALUE_AUTO:
2352 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("'auto' is not a valid MAC address when"
2353 22b7f6f8 Thomas Thrainer
                                     " modifying an existing NIC",
2354 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2355 22b7f6f8 Thomas Thrainer
2356 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
2357 22b7f6f8 Thomas Thrainer
    if not (self.op.nics or self.op.disks or self.op.disk_template or
2358 22b7f6f8 Thomas Thrainer
            self.op.hvparams or self.op.beparams or self.op.os_name or
2359 5eae613c Thomas Thrainer
            self.op.osparams or self.op.offline is not None or
2360 5eae613c Thomas Thrainer
            self.op.runtime_mem or self.op.pnode):
2361 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("No changes submitted", errors.ECODE_INVAL)
2362 22b7f6f8 Thomas Thrainer
2363 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
2364 5eacbcae Thomas Thrainer
      CheckParamsNotGlobal(self.op.hvparams, constants.HVC_GLOBALS,
2365 5eacbcae Thomas Thrainer
                           "hypervisor", "instance", "cluster")
2366 22b7f6f8 Thomas Thrainer
2367 22b7f6f8 Thomas Thrainer
    self.op.disks = self._UpgradeDiskNicMods(
2368 22b7f6f8 Thomas Thrainer
      "disk", self.op.disks, opcodes.OpInstanceSetParams.TestDiskModifications)
2369 22b7f6f8 Thomas Thrainer
    self.op.nics = self._UpgradeDiskNicMods(
2370 22b7f6f8 Thomas Thrainer
      "NIC", self.op.nics, opcodes.OpInstanceSetParams.TestNicModifications)
2371 22b7f6f8 Thomas Thrainer
2372 22b7f6f8 Thomas Thrainer
    if self.op.disks and self.op.disk_template is not None:
2373 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk template conversion and other disk"
2374 22b7f6f8 Thomas Thrainer
                                 " changes not supported at the same time",
2375 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2376 22b7f6f8 Thomas Thrainer
2377 22b7f6f8 Thomas Thrainer
    if (self.op.disk_template and
2378 22b7f6f8 Thomas Thrainer
        self.op.disk_template in constants.DTS_INT_MIRROR and
2379 22b7f6f8 Thomas Thrainer
        self.op.remote_node is None):
2380 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Changing the disk template to a mirrored"
2381 22b7f6f8 Thomas Thrainer
                                 " one requires specifying a secondary node",
2382 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2383 22b7f6f8 Thomas Thrainer
2384 22b7f6f8 Thomas Thrainer
    # Check NIC modifications
2385 22b7f6f8 Thomas Thrainer
    self._CheckMods("NIC", self.op.nics, constants.INIC_PARAMS_TYPES,
2386 22b7f6f8 Thomas Thrainer
                    self._VerifyNicModification)
2387 22b7f6f8 Thomas Thrainer
2388 22b7f6f8 Thomas Thrainer
    if self.op.pnode:
2389 1c3231aa Thomas Thrainer
      (self.op.pnode_uuid, self.op.pnode) = \
2390 1c3231aa Thomas Thrainer
        ExpandNodeUuidAndName(self.cfg, self.op.pnode_uuid, self.op.pnode)
2391 22b7f6f8 Thomas Thrainer
2392 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2393 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2394 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODEGROUP] = []
2395 22b7f6f8 Thomas Thrainer
    # Can't even acquire node locks in shared mode as upcoming changes in
2396 22b7f6f8 Thomas Thrainer
    # Ganeti 2.6 will start to modify the node object on disk conversion
2397 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
2398 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
2399 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
2400 22b7f6f8 Thomas Thrainer
    # Look node group to look up the ipolicy
2401 22b7f6f8 Thomas Thrainer
    self.share_locks[locking.LEVEL_NODEGROUP] = 1
2402 22b7f6f8 Thomas Thrainer
2403 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
2404 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
2405 22b7f6f8 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
2406 22b7f6f8 Thomas Thrainer
      # Acquire locks for the instance's nodegroups optimistically. Needs
2407 22b7f6f8 Thomas Thrainer
      # to be verified in CheckPrereq
2408 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = \
2409 da4a52a3 Thomas Thrainer
        self.cfg.GetInstanceNodeGroups(self.op.instance_uuid)
2410 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE:
2411 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
2412 22b7f6f8 Thomas Thrainer
      if self.op.disk_template and self.op.remote_node:
2413 1c3231aa Thomas Thrainer
        (self.op.remote_node_uuid, self.op.remote_node) = \
2414 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, self.op.remote_node_uuid,
2415 1c3231aa Thomas Thrainer
                                self.op.remote_node)
2416 1c3231aa Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE].append(self.op.remote_node_uuid)
2417 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES and self.op.disk_template:
2418 22b7f6f8 Thomas Thrainer
      # Copy node locks
2419 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
2420 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
2421 22b7f6f8 Thomas Thrainer
2422 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
2423 22b7f6f8 Thomas Thrainer
    """Build hooks env.
2424 22b7f6f8 Thomas Thrainer

2425 22b7f6f8 Thomas Thrainer
    This runs on the master, primary and secondaries.
2426 22b7f6f8 Thomas Thrainer

2427 22b7f6f8 Thomas Thrainer
    """
2428 22b7f6f8 Thomas Thrainer
    args = {}
2429 22b7f6f8 Thomas Thrainer
    if constants.BE_MINMEM in self.be_new:
2430 22b7f6f8 Thomas Thrainer
      args["minmem"] = self.be_new[constants.BE_MINMEM]
2431 22b7f6f8 Thomas Thrainer
    if constants.BE_MAXMEM in self.be_new:
2432 22b7f6f8 Thomas Thrainer
      args["maxmem"] = self.be_new[constants.BE_MAXMEM]
2433 22b7f6f8 Thomas Thrainer
    if constants.BE_VCPUS in self.be_new:
2434 22b7f6f8 Thomas Thrainer
      args["vcpus"] = self.be_new[constants.BE_VCPUS]
2435 22b7f6f8 Thomas Thrainer
    # TODO: export disk changes. Note: _BuildInstanceHookEnv* don't export disk
2436 22b7f6f8 Thomas Thrainer
    # information at all.
2437 22b7f6f8 Thomas Thrainer
2438 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
2439 22b7f6f8 Thomas Thrainer
      nics = []
2440 22b7f6f8 Thomas Thrainer
2441 22b7f6f8 Thomas Thrainer
      for nic in self._new_nics:
2442 22b7f6f8 Thomas Thrainer
        n = copy.deepcopy(nic)
2443 22b7f6f8 Thomas Thrainer
        nicparams = self.cluster.SimpleFillNIC(n.nicparams)
2444 22b7f6f8 Thomas Thrainer
        n.nicparams = nicparams
2445 5eacbcae Thomas Thrainer
        nics.append(NICToTuple(self, n))
2446 22b7f6f8 Thomas Thrainer
2447 22b7f6f8 Thomas Thrainer
      args["nics"] = nics
2448 22b7f6f8 Thomas Thrainer
2449 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance, override=args)
2450 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
2451 22b7f6f8 Thomas Thrainer
      env["NEW_DISK_TEMPLATE"] = self.op.disk_template
2452 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
2453 22b7f6f8 Thomas Thrainer
      env["RUNTIME_MEMORY"] = self.op.runtime_mem
2454 22b7f6f8 Thomas Thrainer
2455 22b7f6f8 Thomas Thrainer
    return env
2456 22b7f6f8 Thomas Thrainer
2457 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2458 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2459 22b7f6f8 Thomas Thrainer

2460 22b7f6f8 Thomas Thrainer
    """
2461 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
2462 22b7f6f8 Thomas Thrainer
    return (nl, nl)
2463 22b7f6f8 Thomas Thrainer
2464 22b7f6f8 Thomas Thrainer
  def _PrepareNicModification(self, params, private, old_ip, old_net_uuid,
2465 1c3231aa Thomas Thrainer
                              old_params, cluster, pnode_uuid):
2466 22b7f6f8 Thomas Thrainer
2467 22b7f6f8 Thomas Thrainer
    update_params_dict = dict([(key, params[key])
2468 22b7f6f8 Thomas Thrainer
                               for key in constants.NICS_PARAMETERS
2469 22b7f6f8 Thomas Thrainer
                               if key in params])
2470 22b7f6f8 Thomas Thrainer
2471 22b7f6f8 Thomas Thrainer
    req_link = update_params_dict.get(constants.NIC_LINK, None)
2472 22b7f6f8 Thomas Thrainer
    req_mode = update_params_dict.get(constants.NIC_MODE, None)
2473 22b7f6f8 Thomas Thrainer
2474 22b7f6f8 Thomas Thrainer
    new_net_uuid = None
2475 22b7f6f8 Thomas Thrainer
    new_net_uuid_or_name = params.get(constants.INIC_NETWORK, old_net_uuid)
2476 22b7f6f8 Thomas Thrainer
    if new_net_uuid_or_name:
2477 22b7f6f8 Thomas Thrainer
      new_net_uuid = self.cfg.LookupNetwork(new_net_uuid_or_name)
2478 22b7f6f8 Thomas Thrainer
      new_net_obj = self.cfg.GetNetwork(new_net_uuid)
2479 22b7f6f8 Thomas Thrainer
2480 22b7f6f8 Thomas Thrainer
    if old_net_uuid:
2481 22b7f6f8 Thomas Thrainer
      old_net_obj = self.cfg.GetNetwork(old_net_uuid)
2482 22b7f6f8 Thomas Thrainer
2483 22b7f6f8 Thomas Thrainer
    if new_net_uuid:
2484 1c3231aa Thomas Thrainer
      netparams = self.cfg.GetGroupNetParams(new_net_uuid, pnode_uuid)
2485 22b7f6f8 Thomas Thrainer
      if not netparams:
2486 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No netparams found for the network"
2487 22b7f6f8 Thomas Thrainer
                                   " %s, probably not connected" %
2488 22b7f6f8 Thomas Thrainer
                                   new_net_obj.name, errors.ECODE_INVAL)
2489 22b7f6f8 Thomas Thrainer
      new_params = dict(netparams)
2490 22b7f6f8 Thomas Thrainer
    else:
2491 5eacbcae Thomas Thrainer
      new_params = GetUpdatedParams(old_params, update_params_dict)
2492 22b7f6f8 Thomas Thrainer
2493 22b7f6f8 Thomas Thrainer
    utils.ForceDictType(new_params, constants.NICS_PARAMETER_TYPES)
2494 22b7f6f8 Thomas Thrainer
2495 22b7f6f8 Thomas Thrainer
    new_filled_params = cluster.SimpleFillNIC(new_params)
2496 22b7f6f8 Thomas Thrainer
    objects.NIC.CheckParameterSyntax(new_filled_params)
2497 22b7f6f8 Thomas Thrainer
2498 22b7f6f8 Thomas Thrainer
    new_mode = new_filled_params[constants.NIC_MODE]
2499 22b7f6f8 Thomas Thrainer
    if new_mode == constants.NIC_MODE_BRIDGED:
2500 22b7f6f8 Thomas Thrainer
      bridge = new_filled_params[constants.NIC_LINK]
2501 1c3231aa Thomas Thrainer
      msg = self.rpc.call_bridges_exist(pnode_uuid, [bridge]).fail_msg
2502 22b7f6f8 Thomas Thrainer
      if msg:
2503 1c3231aa Thomas Thrainer
        msg = "Error checking bridges on node '%s': %s" % \
2504 1c3231aa Thomas Thrainer
                (self.cfg.GetNodeName(pnode_uuid), msg)
2505 22b7f6f8 Thomas Thrainer
        if self.op.force:
2506 22b7f6f8 Thomas Thrainer
          self.warn.append(msg)
2507 22b7f6f8 Thomas Thrainer
        else:
2508 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError(msg, errors.ECODE_ENVIRON)
2509 22b7f6f8 Thomas Thrainer
2510 22b7f6f8 Thomas Thrainer
    elif new_mode == constants.NIC_MODE_ROUTED:
2511 22b7f6f8 Thomas Thrainer
      ip = params.get(constants.INIC_IP, old_ip)
2512 22b7f6f8 Thomas Thrainer
      if ip is None:
2513 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cannot set the NIC IP address to None"
2514 22b7f6f8 Thomas Thrainer
                                   " on a routed NIC", errors.ECODE_INVAL)
2515 22b7f6f8 Thomas Thrainer
2516 22b7f6f8 Thomas Thrainer
    elif new_mode == constants.NIC_MODE_OVS:
2517 22b7f6f8 Thomas Thrainer
      # TODO: check OVS link
2518 22b7f6f8 Thomas Thrainer
      self.LogInfo("OVS links are currently not checked for correctness")
2519 22b7f6f8 Thomas Thrainer
2520 22b7f6f8 Thomas Thrainer
    if constants.INIC_MAC in params:
2521 22b7f6f8 Thomas Thrainer
      mac = params[constants.INIC_MAC]
2522 22b7f6f8 Thomas Thrainer
      if mac is None:
2523 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cannot unset the NIC MAC address",
2524 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2525 22b7f6f8 Thomas Thrainer
      elif mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
2526 22b7f6f8 Thomas Thrainer
        # otherwise generate the MAC address
2527 22b7f6f8 Thomas Thrainer
        params[constants.INIC_MAC] = \
2528 22b7f6f8 Thomas Thrainer
          self.cfg.GenerateMAC(new_net_uuid, self.proc.GetECId())
2529 22b7f6f8 Thomas Thrainer
      else:
2530 22b7f6f8 Thomas Thrainer
        # or validate/reserve the current one
2531 22b7f6f8 Thomas Thrainer
        try:
2532 22b7f6f8 Thomas Thrainer
          self.cfg.ReserveMAC(mac, self.proc.GetECId())
2533 22b7f6f8 Thomas Thrainer
        except errors.ReservationError:
2534 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("MAC address '%s' already in use"
2535 22b7f6f8 Thomas Thrainer
                                     " in cluster" % mac,
2536 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_NOTUNIQUE)
2537 22b7f6f8 Thomas Thrainer
    elif new_net_uuid != old_net_uuid:
2538 22b7f6f8 Thomas Thrainer
2539 22b7f6f8 Thomas Thrainer
      def get_net_prefix(net_uuid):
2540 22b7f6f8 Thomas Thrainer
        mac_prefix = None
2541 22b7f6f8 Thomas Thrainer
        if net_uuid:
2542 22b7f6f8 Thomas Thrainer
          nobj = self.cfg.GetNetwork(net_uuid)
2543 22b7f6f8 Thomas Thrainer
          mac_prefix = nobj.mac_prefix
2544 22b7f6f8 Thomas Thrainer
2545 22b7f6f8 Thomas Thrainer
        return mac_prefix
2546 22b7f6f8 Thomas Thrainer
2547 22b7f6f8 Thomas Thrainer
      new_prefix = get_net_prefix(new_net_uuid)
2548 22b7f6f8 Thomas Thrainer
      old_prefix = get_net_prefix(old_net_uuid)
2549 22b7f6f8 Thomas Thrainer
      if old_prefix != new_prefix:
2550 22b7f6f8 Thomas Thrainer
        params[constants.INIC_MAC] = \
2551 22b7f6f8 Thomas Thrainer
          self.cfg.GenerateMAC(new_net_uuid, self.proc.GetECId())
2552 22b7f6f8 Thomas Thrainer
2553 22b7f6f8 Thomas Thrainer
    # if there is a change in (ip, network) tuple
2554 22b7f6f8 Thomas Thrainer
    new_ip = params.get(constants.INIC_IP, old_ip)
2555 22b7f6f8 Thomas Thrainer
    if (new_ip, new_net_uuid) != (old_ip, old_net_uuid):
2556 22b7f6f8 Thomas Thrainer
      if new_ip:
2557 22b7f6f8 Thomas Thrainer
        # if IP is pool then require a network and generate one IP
2558 22b7f6f8 Thomas Thrainer
        if new_ip.lower() == constants.NIC_IP_POOL:
2559 22b7f6f8 Thomas Thrainer
          if new_net_uuid:
2560 22b7f6f8 Thomas Thrainer
            try:
2561 22b7f6f8 Thomas Thrainer
              new_ip = self.cfg.GenerateIp(new_net_uuid, self.proc.GetECId())
2562 22b7f6f8 Thomas Thrainer
            except errors.ReservationError:
2563 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("Unable to get a free IP"
2564 22b7f6f8 Thomas Thrainer
                                         " from the address pool",
2565 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_STATE)
2566 22b7f6f8 Thomas Thrainer
            self.LogInfo("Chose IP %s from network %s",
2567 22b7f6f8 Thomas Thrainer
                         new_ip,
2568 22b7f6f8 Thomas Thrainer
                         new_net_obj.name)
2569 22b7f6f8 Thomas Thrainer
            params[constants.INIC_IP] = new_ip
2570 22b7f6f8 Thomas Thrainer
          else:
2571 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("ip=pool, but no network found",
2572 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
2573 22b7f6f8 Thomas Thrainer
        # Reserve new IP if in the new network if any
2574 22b7f6f8 Thomas Thrainer
        elif new_net_uuid:
2575 22b7f6f8 Thomas Thrainer
          try:
2576 22b7f6f8 Thomas Thrainer
            self.cfg.ReserveIp(new_net_uuid, new_ip, self.proc.GetECId())
2577 22b7f6f8 Thomas Thrainer
            self.LogInfo("Reserving IP %s in network %s",
2578 22b7f6f8 Thomas Thrainer
                         new_ip, new_net_obj.name)
2579 22b7f6f8 Thomas Thrainer
          except errors.ReservationError:
2580 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("IP %s not available in network %s" %
2581 22b7f6f8 Thomas Thrainer
                                       (new_ip, new_net_obj.name),
2582 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_NOTUNIQUE)
2583 22b7f6f8 Thomas Thrainer
        # new network is None so check if new IP is a conflicting IP
2584 22b7f6f8 Thomas Thrainer
        elif self.op.conflicts_check:
2585 1c3231aa Thomas Thrainer
          _CheckForConflictingIp(self, new_ip, pnode_uuid)
2586 22b7f6f8 Thomas Thrainer
2587 22b7f6f8 Thomas Thrainer
      # release old IP if old network is not None
2588 22b7f6f8 Thomas Thrainer
      if old_ip and old_net_uuid:
2589 22b7f6f8 Thomas Thrainer
        try:
2590 22b7f6f8 Thomas Thrainer
          self.cfg.ReleaseIp(old_net_uuid, old_ip, self.proc.GetECId())
2591 22b7f6f8 Thomas Thrainer
        except errors.AddressPoolError:
2592 22b7f6f8 Thomas Thrainer
          logging.warning("Release IP %s not contained in network %s",
2593 22b7f6f8 Thomas Thrainer
                          old_ip, old_net_obj.name)
2594 22b7f6f8 Thomas Thrainer
2595 22b7f6f8 Thomas Thrainer
    # there are no changes in (ip, network) tuple and old network is not None
2596 22b7f6f8 Thomas Thrainer
    elif (old_net_uuid is not None and
2597 22b7f6f8 Thomas Thrainer
          (req_link is not None or req_mode is not None)):
2598 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Not allowed to change link or mode of"
2599 22b7f6f8 Thomas Thrainer
                                 " a NIC that is connected to a network",
2600 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2601 22b7f6f8 Thomas Thrainer
2602 22b7f6f8 Thomas Thrainer
    private.params = new_params
2603 22b7f6f8 Thomas Thrainer
    private.filled = new_filled_params
2604 22b7f6f8 Thomas Thrainer
2605 22b7f6f8 Thomas Thrainer
  def _PreCheckDiskTemplate(self, pnode_info):
2606 22b7f6f8 Thomas Thrainer
    """CheckPrereq checks related to a new disk template."""
2607 22b7f6f8 Thomas Thrainer
    # Arguments are passed to avoid configuration lookups
2608 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
2609 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template == self.op.disk_template:
2610 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance already has disk template %s" %
2611 d0d7d7cf Thomas Thrainer
                                 self.instance.disk_template,
2612 d0d7d7cf Thomas Thrainer
                                 errors.ECODE_INVAL)
2613 22b7f6f8 Thomas Thrainer
2614 7bb0c47f Thomas Thrainer
    if not self.cluster.IsDiskTemplateEnabled(self.op.disk_template):
2615 9d276e93 Helga Velroyen
      raise errors.OpPrereqError("Disk template '%s' is not enabled for this"
2616 7bb0c47f Thomas Thrainer
                                 " cluster." % self.op.disk_template)
2617 9d276e93 Helga Velroyen
2618 d0d7d7cf Thomas Thrainer
    if (self.instance.disk_template,
2619 22b7f6f8 Thomas Thrainer
        self.op.disk_template) not in self._DISK_CONVERSIONS:
2620 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Unsupported disk template conversion from"
2621 d0d7d7cf Thomas Thrainer
                                 " %s to %s" % (self.instance.disk_template,
2622 22b7f6f8 Thomas Thrainer
                                                self.op.disk_template),
2623 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2624 d0d7d7cf Thomas Thrainer
    CheckInstanceState(self, self.instance, INSTANCE_DOWN,
2625 5eacbcae Thomas Thrainer
                       msg="cannot change disk template")
2626 22b7f6f8 Thomas Thrainer
    if self.op.disk_template in constants.DTS_INT_MIRROR:
2627 1c3231aa Thomas Thrainer
      if self.op.remote_node_uuid == pnode_uuid:
2628 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Given new secondary node %s is the same"
2629 22b7f6f8 Thomas Thrainer
                                   " as the primary node of the instance" %
2630 22b7f6f8 Thomas Thrainer
                                   self.op.remote_node, errors.ECODE_STATE)
2631 1c3231aa Thomas Thrainer
      CheckNodeOnline(self, self.op.remote_node_uuid)
2632 1c3231aa Thomas Thrainer
      CheckNodeNotDrained(self, self.op.remote_node_uuid)
2633 22b7f6f8 Thomas Thrainer
      # FIXME: here we assume that the old instance type is DT_PLAIN
2634 d0d7d7cf Thomas Thrainer
      assert self.instance.disk_template == constants.DT_PLAIN
2635 22b7f6f8 Thomas Thrainer
      disks = [{constants.IDISK_SIZE: d.size,
2636 22b7f6f8 Thomas Thrainer
                constants.IDISK_VG: d.logical_id[0]}
2637 d0d7d7cf Thomas Thrainer
               for d in self.instance.disks]
2638 5eacbcae Thomas Thrainer
      required = ComputeDiskSizePerVG(self.op.disk_template, disks)
2639 1c3231aa Thomas Thrainer
      CheckNodesFreeDiskPerVG(self, [self.op.remote_node_uuid], required)
2640 22b7f6f8 Thomas Thrainer
2641 1c3231aa Thomas Thrainer
      snode_info = self.cfg.GetNodeInfo(self.op.remote_node_uuid)
2642 22b7f6f8 Thomas Thrainer
      snode_group = self.cfg.GetNodeGroup(snode_info.group)
2643 d0d7d7cf Thomas Thrainer
      ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(self.cluster,
2644 22b7f6f8 Thomas Thrainer
                                                              snode_group)
2645 d0d7d7cf Thomas Thrainer
      CheckTargetNodeIPolicy(self, ipolicy, self.instance, snode_info, self.cfg,
2646 5eacbcae Thomas Thrainer
                             ignore=self.op.ignore_ipolicy)
2647 22b7f6f8 Thomas Thrainer
      if pnode_info.group != snode_info.group:
2648 22b7f6f8 Thomas Thrainer
        self.LogWarning("The primary and secondary nodes are in two"
2649 22b7f6f8 Thomas Thrainer
                        " different node groups; the disk parameters"
2650 22b7f6f8 Thomas Thrainer
                        " from the first disk's node group will be"
2651 22b7f6f8 Thomas Thrainer
                        " used")
2652 22b7f6f8 Thomas Thrainer
2653 22b7f6f8 Thomas Thrainer
    if not self.op.disk_template in constants.DTS_EXCL_STORAGE:
2654 22b7f6f8 Thomas Thrainer
      # Make sure none of the nodes require exclusive storage
2655 22b7f6f8 Thomas Thrainer
      nodes = [pnode_info]
2656 22b7f6f8 Thomas Thrainer
      if self.op.disk_template in constants.DTS_INT_MIRROR:
2657 22b7f6f8 Thomas Thrainer
        assert snode_info
2658 22b7f6f8 Thomas Thrainer
        nodes.append(snode_info)
2659 5eacbcae Thomas Thrainer
      has_es = lambda n: IsExclusiveStorageEnabledNode(self.cfg, n)
2660 22b7f6f8 Thomas Thrainer
      if compat.any(map(has_es, nodes)):
2661 22b7f6f8 Thomas Thrainer
        errmsg = ("Cannot convert disk template from %s to %s when exclusive"
2662 d0d7d7cf Thomas Thrainer
                  " storage is enabled" % (self.instance.disk_template,
2663 22b7f6f8 Thomas Thrainer
                                           self.op.disk_template))
2664 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError(errmsg, errors.ECODE_STATE)
2665 22b7f6f8 Thomas Thrainer
2666 1bb99a33 Bernardo Dal Seno
  def _PreCheckDisks(self, ispec):
2667 1bb99a33 Bernardo Dal Seno
    """CheckPrereq checks related to disk changes.
2668 22b7f6f8 Thomas Thrainer

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

2672 22b7f6f8 Thomas Thrainer
    """
2673 d0d7d7cf Thomas Thrainer
    self.diskparams = self.cfg.GetInstanceDiskParams(self.instance)
2674 22b7f6f8 Thomas Thrainer
2675 3f3ea14c Bernardo Dal Seno
    excl_stor = compat.any(
2676 d0d7d7cf Thomas Thrainer
      rpc.GetExclusiveStorageForNodes(self.cfg,
2677 d0d7d7cf Thomas Thrainer
                                      self.instance.all_nodes).values()
2678 3f3ea14c Bernardo Dal Seno
      )
2679 3f3ea14c Bernardo Dal Seno
2680 22b7f6f8 Thomas Thrainer
    # Check disk modifications. This is done here and not in CheckArguments
2681 22b7f6f8 Thomas Thrainer
    # (as with NICs), because we need to know the instance's disk template
2682 3f3ea14c Bernardo Dal Seno
    ver_fn = lambda op, par: self._VerifyDiskModification(op, par, excl_stor)
2683 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template == constants.DT_EXT:
2684 3f3ea14c Bernardo Dal Seno
      self._CheckMods("disk", self.op.disks, {}, ver_fn)
2685 22b7f6f8 Thomas Thrainer
    else:
2686 22b7f6f8 Thomas Thrainer
      self._CheckMods("disk", self.op.disks, constants.IDISK_PARAMS_TYPES,
2687 3f3ea14c Bernardo Dal Seno
                      ver_fn)
2688 22b7f6f8 Thomas Thrainer
2689 5eacbcae Thomas Thrainer
    self.diskmod = _PrepareContainerMods(self.op.disks, None)
2690 22b7f6f8 Thomas Thrainer
2691 22b7f6f8 Thomas Thrainer
    # Check the validity of the `provider' parameter
2692 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DT_EXT:
2693 22b7f6f8 Thomas Thrainer
      for mod in self.diskmod:
2694 22b7f6f8 Thomas Thrainer
        ext_provider = mod[2].get(constants.IDISK_PROVIDER, None)
2695 22b7f6f8 Thomas Thrainer
        if mod[0] == constants.DDM_ADD:
2696 22b7f6f8 Thomas Thrainer
          if ext_provider is None:
2697 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Instance template is '%s' and parameter"
2698 22b7f6f8 Thomas Thrainer
                                       " '%s' missing, during disk add" %
2699 22b7f6f8 Thomas Thrainer
                                       (constants.DT_EXT,
2700 22b7f6f8 Thomas Thrainer
                                        constants.IDISK_PROVIDER),
2701 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_NOENT)
2702 22b7f6f8 Thomas Thrainer
        elif mod[0] == constants.DDM_MODIFY:
2703 22b7f6f8 Thomas Thrainer
          if ext_provider:
2704 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Parameter '%s' is invalid during disk"
2705 22b7f6f8 Thomas Thrainer
                                       " modification" %
2706 22b7f6f8 Thomas Thrainer
                                       constants.IDISK_PROVIDER,
2707 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
2708 22b7f6f8 Thomas Thrainer
    else:
2709 22b7f6f8 Thomas Thrainer
      for mod in self.diskmod:
2710 22b7f6f8 Thomas Thrainer
        ext_provider = mod[2].get(constants.IDISK_PROVIDER, None)
2711 22b7f6f8 Thomas Thrainer
        if ext_provider is not None:
2712 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("Parameter '%s' is only valid for"
2713 22b7f6f8 Thomas Thrainer
                                     " instances of type '%s'" %
2714 22b7f6f8 Thomas Thrainer
                                     (constants.IDISK_PROVIDER,
2715 22b7f6f8 Thomas Thrainer
                                      constants.DT_EXT),
2716 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2717 22b7f6f8 Thomas Thrainer
2718 d0d7d7cf Thomas Thrainer
    if self.op.disks and self.instance.disk_template == constants.DT_DISKLESS:
2719 1bb99a33 Bernardo Dal Seno
      raise errors.OpPrereqError("Disk operations not supported for"
2720 1bb99a33 Bernardo Dal Seno
                                 " diskless instances", errors.ECODE_INVAL)
2721 1bb99a33 Bernardo Dal Seno
2722 1bb99a33 Bernardo Dal Seno
    def _PrepareDiskMod(_, disk, params, __):
2723 1bb99a33 Bernardo Dal Seno
      disk.name = params.get(constants.IDISK_NAME, None)
2724 1bb99a33 Bernardo Dal Seno
2725 1bb99a33 Bernardo Dal Seno
    # Verify disk changes (operating on a copy)
2726 d0d7d7cf Thomas Thrainer
    disks = copy.deepcopy(self.instance.disks)
2727 1bb99a33 Bernardo Dal Seno
    _ApplyContainerMods("disk", disks, None, self.diskmod, None,
2728 1bb99a33 Bernardo Dal Seno
                        _PrepareDiskMod, None)
2729 1bb99a33 Bernardo Dal Seno
    utils.ValidateDeviceNames("disk", disks)
2730 1bb99a33 Bernardo Dal Seno
    if len(disks) > constants.MAX_DISKS:
2731 1bb99a33 Bernardo Dal Seno
      raise errors.OpPrereqError("Instance has too many disks (%d), cannot add"
2732 1bb99a33 Bernardo Dal Seno
                                 " more" % constants.MAX_DISKS,
2733 1bb99a33 Bernardo Dal Seno
                                 errors.ECODE_STATE)
2734 d0d7d7cf Thomas Thrainer
    disk_sizes = [disk.size for disk in self.instance.disks]
2735 1bb99a33 Bernardo Dal Seno
    disk_sizes.extend(params["size"] for (op, idx, params, private) in
2736 1bb99a33 Bernardo Dal Seno
                      self.diskmod if op == constants.DDM_ADD)
2737 1bb99a33 Bernardo Dal Seno
    ispec[constants.ISPEC_DISK_COUNT] = len(disk_sizes)
2738 1bb99a33 Bernardo Dal Seno
    ispec[constants.ISPEC_DISK_SIZE] = disk_sizes
2739 1bb99a33 Bernardo Dal Seno
2740 1bb99a33 Bernardo Dal Seno
    if self.op.offline is not None and self.op.offline:
2741 d0d7d7cf Thomas Thrainer
      CheckInstanceState(self, self.instance, CAN_CHANGE_INSTANCE_OFFLINE,
2742 1bb99a33 Bernardo Dal Seno
                         msg="can't change to offline")
2743 1bb99a33 Bernardo Dal Seno
2744 1bb99a33 Bernardo Dal Seno
  def CheckPrereq(self):
2745 1bb99a33 Bernardo Dal Seno
    """Check prerequisites.
2746 1bb99a33 Bernardo Dal Seno

2747 1bb99a33 Bernardo Dal Seno
    This only checks the instance list against the existing names.
2748 1bb99a33 Bernardo Dal Seno

2749 1bb99a33 Bernardo Dal Seno
    """
2750 1bb99a33 Bernardo Dal Seno
    assert self.op.instance_name in self.owned_locks(locking.LEVEL_INSTANCE)
2751 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
2752 d0d7d7cf Thomas Thrainer
    self.cluster = self.cfg.GetClusterInfo()
2753 1bb99a33 Bernardo Dal Seno
2754 1bb99a33 Bernardo Dal Seno
    assert self.instance is not None, \
2755 1bb99a33 Bernardo Dal Seno
      "Cannot retrieve locked instance %s" % self.op.instance_name
2756 1bb99a33 Bernardo Dal Seno
2757 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
2758 1bb99a33 Bernardo Dal Seno
2759 1bb99a33 Bernardo Dal Seno
    self.warn = []
2760 1bb99a33 Bernardo Dal Seno
2761 1c3231aa Thomas Thrainer
    if (self.op.pnode_uuid is not None and self.op.pnode_uuid != pnode_uuid and
2762 1bb99a33 Bernardo Dal Seno
        not self.op.force):
2763 1bb99a33 Bernardo Dal Seno
      # verify that the instance is not up
2764 0bbec3af Helga Velroyen
      instance_info = self.rpc.call_instance_info(
2765 d0d7d7cf Thomas Thrainer
          pnode_uuid, self.instance.name, self.instance.hypervisor,
2766 d0d7d7cf Thomas Thrainer
          self.instance.hvparams)
2767 1bb99a33 Bernardo Dal Seno
      if instance_info.fail_msg:
2768 1bb99a33 Bernardo Dal Seno
        self.warn.append("Can't get instance runtime information: %s" %
2769 1bb99a33 Bernardo Dal Seno
                         instance_info.fail_msg)
2770 1bb99a33 Bernardo Dal Seno
      elif instance_info.payload:
2771 1c3231aa Thomas Thrainer
        raise errors.OpPrereqError("Instance is still running on %s" %
2772 1c3231aa Thomas Thrainer
                                   self.cfg.GetNodeName(pnode_uuid),
2773 1bb99a33 Bernardo Dal Seno
                                   errors.ECODE_STATE)
2774 1bb99a33 Bernardo Dal Seno
2775 1c3231aa Thomas Thrainer
    assert pnode_uuid in self.owned_locks(locking.LEVEL_NODE)
2776 d0d7d7cf Thomas Thrainer
    node_uuids = list(self.instance.all_nodes)
2777 1c3231aa Thomas Thrainer
    pnode_info = self.cfg.GetNodeInfo(pnode_uuid)
2778 1bb99a33 Bernardo Dal Seno
2779 1bb99a33 Bernardo Dal Seno
    #_CheckInstanceNodeGroups(self.cfg, self.op.instance_name, owned_groups)
2780 1bb99a33 Bernardo Dal Seno
    assert pnode_info.group in self.owned_locks(locking.LEVEL_NODEGROUP)
2781 1bb99a33 Bernardo Dal Seno
    group_info = self.cfg.GetNodeGroup(pnode_info.group)
2782 1bb99a33 Bernardo Dal Seno
2783 1bb99a33 Bernardo Dal Seno
    # dictionary with instance information after the modification
2784 1bb99a33 Bernardo Dal Seno
    ispec = {}
2785 1bb99a33 Bernardo Dal Seno
2786 1bb99a33 Bernardo Dal Seno
    # Prepare NIC modifications
2787 1bb99a33 Bernardo Dal Seno
    self.nicmod = _PrepareContainerMods(self.op.nics, _InstNicModPrivate)
2788 1bb99a33 Bernardo Dal Seno
2789 22b7f6f8 Thomas Thrainer
    # OS change
2790 22b7f6f8 Thomas Thrainer
    if self.op.os_name and not self.op.force:
2791 d0d7d7cf Thomas Thrainer
      CheckNodeHasOS(self, self.instance.primary_node, self.op.os_name,
2792 5eacbcae Thomas Thrainer
                     self.op.force_variant)
2793 22b7f6f8 Thomas Thrainer
      instance_os = self.op.os_name
2794 22b7f6f8 Thomas Thrainer
    else:
2795 d0d7d7cf Thomas Thrainer
      instance_os = self.instance.os
2796 22b7f6f8 Thomas Thrainer
2797 22b7f6f8 Thomas Thrainer
    assert not (self.op.disk_template and self.op.disks), \
2798 22b7f6f8 Thomas Thrainer
      "Can't modify disk template and apply disk changes at the same time"
2799 22b7f6f8 Thomas Thrainer
2800 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
2801 22b7f6f8 Thomas Thrainer
      self._PreCheckDiskTemplate(pnode_info)
2802 22b7f6f8 Thomas Thrainer
2803 1bb99a33 Bernardo Dal Seno
    self._PreCheckDisks(ispec)
2804 1bb99a33 Bernardo Dal Seno
2805 22b7f6f8 Thomas Thrainer
    # hvparams processing
2806 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
2807 d0d7d7cf Thomas Thrainer
      hv_type = self.instance.hypervisor
2808 d0d7d7cf Thomas Thrainer
      i_hvdict = GetUpdatedParams(self.instance.hvparams, self.op.hvparams)
2809 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(i_hvdict, constants.HVS_PARAMETER_TYPES)
2810 d0d7d7cf Thomas Thrainer
      hv_new = self.cluster.SimpleFillHV(hv_type, self.instance.os, i_hvdict)
2811 22b7f6f8 Thomas Thrainer
2812 22b7f6f8 Thomas Thrainer
      # local check
2813 22b7f6f8 Thomas Thrainer
      hypervisor.GetHypervisorClass(hv_type).CheckParameterSyntax(hv_new)
2814 d0d7d7cf Thomas Thrainer
      CheckHVParams(self, node_uuids, self.instance.hypervisor, hv_new)
2815 22b7f6f8 Thomas Thrainer
      self.hv_proposed = self.hv_new = hv_new # the new actual values
2816 22b7f6f8 Thomas Thrainer
      self.hv_inst = i_hvdict # the new dict (without defaults)
2817 22b7f6f8 Thomas Thrainer
    else:
2818 d0d7d7cf Thomas Thrainer
      self.hv_proposed = self.cluster.SimpleFillHV(self.instance.hypervisor,
2819 d0d7d7cf Thomas Thrainer
                                                   self.instance.os,
2820 d0d7d7cf Thomas Thrainer
                                                   self.instance.hvparams)
2821 22b7f6f8 Thomas Thrainer
      self.hv_new = self.hv_inst = {}
2822 22b7f6f8 Thomas Thrainer
2823 22b7f6f8 Thomas Thrainer
    # beparams processing
2824 22b7f6f8 Thomas Thrainer
    if self.op.beparams:
2825 d0d7d7cf Thomas Thrainer
      i_bedict = GetUpdatedParams(self.instance.beparams, self.op.beparams,
2826 5eacbcae Thomas Thrainer
                                  use_none=True)
2827 22b7f6f8 Thomas Thrainer
      objects.UpgradeBeParams(i_bedict)
2828 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(i_bedict, constants.BES_PARAMETER_TYPES)
2829 d0d7d7cf Thomas Thrainer
      be_new = self.cluster.SimpleFillBE(i_bedict)
2830 22b7f6f8 Thomas Thrainer
      self.be_proposed = self.be_new = be_new # the new actual values
2831 22b7f6f8 Thomas Thrainer
      self.be_inst = i_bedict # the new dict (without defaults)
2832 22b7f6f8 Thomas Thrainer
    else:
2833 22b7f6f8 Thomas Thrainer
      self.be_new = self.be_inst = {}
2834 d0d7d7cf Thomas Thrainer
      self.be_proposed = self.cluster.SimpleFillBE(self.instance.beparams)
2835 d0d7d7cf Thomas Thrainer
    be_old = self.cluster.FillBE(self.instance)
2836 22b7f6f8 Thomas Thrainer
2837 22b7f6f8 Thomas Thrainer
    # CPU param validation -- checking every time a parameter is
2838 22b7f6f8 Thomas Thrainer
    # changed to cover all cases where either CPU mask or vcpus have
2839 22b7f6f8 Thomas Thrainer
    # changed
2840 22b7f6f8 Thomas Thrainer
    if (constants.BE_VCPUS in self.be_proposed and
2841 22b7f6f8 Thomas Thrainer
        constants.HV_CPU_MASK in self.hv_proposed):
2842 22b7f6f8 Thomas Thrainer
      cpu_list = \
2843 22b7f6f8 Thomas Thrainer
        utils.ParseMultiCpuMask(self.hv_proposed[constants.HV_CPU_MASK])
2844 22b7f6f8 Thomas Thrainer
      # Verify mask is consistent with number of vCPUs. Can skip this
2845 22b7f6f8 Thomas Thrainer
      # test if only 1 entry in the CPU mask, which means same mask
2846 22b7f6f8 Thomas Thrainer
      # is applied to all vCPUs.
2847 22b7f6f8 Thomas Thrainer
      if (len(cpu_list) > 1 and
2848 22b7f6f8 Thomas Thrainer
          len(cpu_list) != self.be_proposed[constants.BE_VCPUS]):
2849 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Number of vCPUs [%d] does not match the"
2850 22b7f6f8 Thomas Thrainer
                                   " CPU mask [%s]" %
2851 22b7f6f8 Thomas Thrainer
                                   (self.be_proposed[constants.BE_VCPUS],
2852 22b7f6f8 Thomas Thrainer
                                    self.hv_proposed[constants.HV_CPU_MASK]),
2853 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2854 22b7f6f8 Thomas Thrainer
2855 22b7f6f8 Thomas Thrainer
      # Only perform this test if a new CPU mask is given
2856 22b7f6f8 Thomas Thrainer
      if constants.HV_CPU_MASK in self.hv_new:
2857 22b7f6f8 Thomas Thrainer
        # Calculate the largest CPU number requested
2858 22b7f6f8 Thomas Thrainer
        max_requested_cpu = max(map(max, cpu_list))
2859 22b7f6f8 Thomas Thrainer
        # Check that all of the instance's nodes have enough physical CPUs to
2860 22b7f6f8 Thomas Thrainer
        # satisfy the requested CPU mask
2861 d0d7d7cf Thomas Thrainer
        hvspecs = [(self.instance.hypervisor,
2862 d0d7d7cf Thomas Thrainer
                    self.cfg.GetClusterInfo()
2863 d0d7d7cf Thomas Thrainer
                      .hvparams[self.instance.hypervisor])]
2864 d0d7d7cf Thomas Thrainer
        _CheckNodesPhysicalCPUs(self, self.instance.all_nodes,
2865 a295eb80 Helga Velroyen
                                max_requested_cpu + 1,
2866 a295eb80 Helga Velroyen
                                hvspecs)
2867 22b7f6f8 Thomas Thrainer
2868 22b7f6f8 Thomas Thrainer
    # osparams processing
2869 22b7f6f8 Thomas Thrainer
    if self.op.osparams:
2870 d0d7d7cf Thomas Thrainer
      i_osdict = GetUpdatedParams(self.instance.osparams, self.op.osparams)
2871 1c3231aa Thomas Thrainer
      CheckOSParams(self, True, node_uuids, instance_os, i_osdict)
2872 22b7f6f8 Thomas Thrainer
      self.os_inst = i_osdict # the new dict (without defaults)
2873 22b7f6f8 Thomas Thrainer
    else:
2874 22b7f6f8 Thomas Thrainer
      self.os_inst = {}
2875 22b7f6f8 Thomas Thrainer
2876 22b7f6f8 Thomas Thrainer
    #TODO(dynmem): do the appropriate check involving MINMEM
2877 22b7f6f8 Thomas Thrainer
    if (constants.BE_MAXMEM in self.op.beparams and not self.op.force and
2878 22b7f6f8 Thomas Thrainer
        be_new[constants.BE_MAXMEM] > be_old[constants.BE_MAXMEM]):
2879 1c3231aa Thomas Thrainer
      mem_check_list = [pnode_uuid]
2880 22b7f6f8 Thomas Thrainer
      if be_new[constants.BE_AUTO_BALANCE]:
2881 22b7f6f8 Thomas Thrainer
        # either we changed auto_balance to yes or it was from before
2882 d0d7d7cf Thomas Thrainer
        mem_check_list.extend(self.instance.secondary_nodes)
2883 a295eb80 Helga Velroyen
      instance_info = self.rpc.call_instance_info(
2884 d0d7d7cf Thomas Thrainer
          pnode_uuid, self.instance.name, self.instance.hypervisor,
2885 d0d7d7cf Thomas Thrainer
          self.instance.hvparams)
2886 d0d7d7cf Thomas Thrainer
      hvspecs = [(self.instance.hypervisor,
2887 d0d7d7cf Thomas Thrainer
                  self.cluster.hvparams[self.instance.hypervisor])]
2888 22b7f6f8 Thomas Thrainer
      nodeinfo = self.rpc.call_node_info(mem_check_list, None,
2889 da803ff1 Helga Velroyen
                                         hvspecs)
2890 1c3231aa Thomas Thrainer
      pninfo = nodeinfo[pnode_uuid]
2891 22b7f6f8 Thomas Thrainer
      msg = pninfo.fail_msg
2892 22b7f6f8 Thomas Thrainer
      if msg:
2893 22b7f6f8 Thomas Thrainer
        # Assume the primary node is unreachable and go ahead
2894 22b7f6f8 Thomas Thrainer
        self.warn.append("Can't get info from primary node %s: %s" %
2895 1c3231aa Thomas Thrainer
                         (self.cfg.GetNodeName(pnode_uuid), msg))
2896 22b7f6f8 Thomas Thrainer
      else:
2897 22b7f6f8 Thomas Thrainer
        (_, _, (pnhvinfo, )) = pninfo.payload
2898 22b7f6f8 Thomas Thrainer
        if not isinstance(pnhvinfo.get("memory_free", None), int):
2899 22b7f6f8 Thomas Thrainer
          self.warn.append("Node data from primary node %s doesn't contain"
2900 1c3231aa Thomas Thrainer
                           " free memory information" %
2901 1c3231aa Thomas Thrainer
                           self.cfg.GetNodeName(pnode_uuid))
2902 22b7f6f8 Thomas Thrainer
        elif instance_info.fail_msg:
2903 22b7f6f8 Thomas Thrainer
          self.warn.append("Can't get instance runtime information: %s" %
2904 22b7f6f8 Thomas Thrainer
                           instance_info.fail_msg)
2905 22b7f6f8 Thomas Thrainer
        else:
2906 22b7f6f8 Thomas Thrainer
          if instance_info.payload:
2907 22b7f6f8 Thomas Thrainer
            current_mem = int(instance_info.payload["memory"])
2908 22b7f6f8 Thomas Thrainer
          else:
2909 22b7f6f8 Thomas Thrainer
            # Assume instance not running
2910 22b7f6f8 Thomas Thrainer
            # (there is a slight race condition here, but it's not very
2911 22b7f6f8 Thomas Thrainer
            # probable, and we have no other way to check)
2912 22b7f6f8 Thomas Thrainer
            # TODO: Describe race condition
2913 22b7f6f8 Thomas Thrainer
            current_mem = 0
2914 22b7f6f8 Thomas Thrainer
          #TODO(dynmem): do the appropriate check involving MINMEM
2915 22b7f6f8 Thomas Thrainer
          miss_mem = (be_new[constants.BE_MAXMEM] - current_mem -
2916 22b7f6f8 Thomas Thrainer
                      pnhvinfo["memory_free"])
2917 22b7f6f8 Thomas Thrainer
          if miss_mem > 0:
2918 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("This change will prevent the instance"
2919 22b7f6f8 Thomas Thrainer
                                       " from starting, due to %d MB of memory"
2920 22b7f6f8 Thomas Thrainer
                                       " missing on its primary node" %
2921 22b7f6f8 Thomas Thrainer
                                       miss_mem, errors.ECODE_NORES)
2922 22b7f6f8 Thomas Thrainer
2923 22b7f6f8 Thomas Thrainer
      if be_new[constants.BE_AUTO_BALANCE]:
2924 1c3231aa Thomas Thrainer
        for node_uuid, nres in nodeinfo.items():
2925 d0d7d7cf Thomas Thrainer
          if node_uuid not in self.instance.secondary_nodes:
2926 22b7f6f8 Thomas Thrainer
            continue
2927 1c3231aa Thomas Thrainer
          nres.Raise("Can't get info from secondary node %s" %
2928 1c3231aa Thomas Thrainer
                     self.cfg.GetNodeName(node_uuid), prereq=True,
2929 1c3231aa Thomas Thrainer
                     ecode=errors.ECODE_STATE)
2930 22b7f6f8 Thomas Thrainer
          (_, _, (nhvinfo, )) = nres.payload
2931 22b7f6f8 Thomas Thrainer
          if not isinstance(nhvinfo.get("memory_free", None), int):
2932 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Secondary node %s didn't return free"
2933 1c3231aa Thomas Thrainer
                                       " memory information" %
2934 1c3231aa Thomas Thrainer
                                       self.cfg.GetNodeName(node_uuid),
2935 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_STATE)
2936 22b7f6f8 Thomas Thrainer
          #TODO(dynmem): do the appropriate check involving MINMEM
2937 22b7f6f8 Thomas Thrainer
          elif be_new[constants.BE_MAXMEM] > nhvinfo["memory_free"]:
2938 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("This change will prevent the instance"
2939 22b7f6f8 Thomas Thrainer
                                       " from failover to its secondary node"
2940 1c3231aa Thomas Thrainer
                                       " %s, due to not enough memory" %
2941 1c3231aa Thomas Thrainer
                                       self.cfg.GetNodeName(node_uuid),
2942 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_STATE)
2943 22b7f6f8 Thomas Thrainer
2944 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
2945 0bbec3af Helga Velroyen
      remote_info = self.rpc.call_instance_info(
2946 d0d7d7cf Thomas Thrainer
         self.instance.primary_node, self.instance.name,
2947 b666a94c Helga Velroyen
         self.instance.hypervisor,
2948 b666a94c Helga Velroyen
         self.cluster.hvparams[self.instance.hypervisor])
2949 1c3231aa Thomas Thrainer
      remote_info.Raise("Error checking node %s" %
2950 d0d7d7cf Thomas Thrainer
                        self.cfg.GetNodeName(self.instance.primary_node))
2951 22b7f6f8 Thomas Thrainer
      if not remote_info.payload: # not running already
2952 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s is not running" %
2953 d0d7d7cf Thomas Thrainer
                                   self.instance.name, errors.ECODE_STATE)
2954 22b7f6f8 Thomas Thrainer
2955 22b7f6f8 Thomas Thrainer
      current_memory = remote_info.payload["memory"]
2956 22b7f6f8 Thomas Thrainer
      if (not self.op.force and
2957 22b7f6f8 Thomas Thrainer
           (self.op.runtime_mem > self.be_proposed[constants.BE_MAXMEM] or
2958 22b7f6f8 Thomas Thrainer
            self.op.runtime_mem < self.be_proposed[constants.BE_MINMEM])):
2959 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s must have memory between %d"
2960 22b7f6f8 Thomas Thrainer
                                   " and %d MB of memory unless --force is"
2961 22b7f6f8 Thomas Thrainer
                                   " given" %
2962 d0d7d7cf Thomas Thrainer
                                   (self.instance.name,
2963 22b7f6f8 Thomas Thrainer
                                    self.be_proposed[constants.BE_MINMEM],
2964 22b7f6f8 Thomas Thrainer
                                    self.be_proposed[constants.BE_MAXMEM]),
2965 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2966 22b7f6f8 Thomas Thrainer
2967 22b7f6f8 Thomas Thrainer
      delta = self.op.runtime_mem - current_memory
2968 22b7f6f8 Thomas Thrainer
      if delta > 0:
2969 a295eb80 Helga Velroyen
        CheckNodeFreeMemory(
2970 d0d7d7cf Thomas Thrainer
            self, self.instance.primary_node,
2971 d0d7d7cf Thomas Thrainer
            "ballooning memory for instance %s" % self.instance.name, delta,
2972 d0d7d7cf Thomas Thrainer
            self.instance.hypervisor,
2973 d0d7d7cf Thomas Thrainer
            self.cfg.GetClusterInfo().hvparams[self.instance.hypervisor])
2974 d0d7d7cf Thomas Thrainer
2975 d0d7d7cf Thomas Thrainer
    # make self.cluster visible in the functions below
2976 d0d7d7cf Thomas Thrainer
    cluster = self.cluster
2977 22b7f6f8 Thomas Thrainer
2978 22b7f6f8 Thomas Thrainer
    def _PrepareNicCreate(_, params, private):
2979 22b7f6f8 Thomas Thrainer
      self._PrepareNicModification(params, private, None, None,
2980 1c3231aa Thomas Thrainer
                                   {}, cluster, pnode_uuid)
2981 22b7f6f8 Thomas Thrainer
      return (None, None)
2982 22b7f6f8 Thomas Thrainer
2983 22b7f6f8 Thomas Thrainer
    def _PrepareNicMod(_, nic, params, private):
2984 22b7f6f8 Thomas Thrainer
      self._PrepareNicModification(params, private, nic.ip, nic.network,
2985 1c3231aa Thomas Thrainer
                                   nic.nicparams, cluster, pnode_uuid)
2986 22b7f6f8 Thomas Thrainer
      return None
2987 22b7f6f8 Thomas Thrainer
2988 22b7f6f8 Thomas Thrainer
    def _PrepareNicRemove(_, params, __):
2989 22b7f6f8 Thomas Thrainer
      ip = params.ip
2990 22b7f6f8 Thomas Thrainer
      net = params.network
2991 22b7f6f8 Thomas Thrainer
      if net is not None and ip is not None:
2992 22b7f6f8 Thomas Thrainer
        self.cfg.ReleaseIp(net, ip, self.proc.GetECId())
2993 22b7f6f8 Thomas Thrainer
2994 22b7f6f8 Thomas Thrainer
    # Verify NIC changes (operating on copy)
2995 d0d7d7cf Thomas Thrainer
    nics = self.instance.nics[:]
2996 5eacbcae Thomas Thrainer
    _ApplyContainerMods("NIC", nics, None, self.nicmod,
2997 5eacbcae Thomas Thrainer
                        _PrepareNicCreate, _PrepareNicMod, _PrepareNicRemove)
2998 22b7f6f8 Thomas Thrainer
    if len(nics) > constants.MAX_NICS:
2999 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance has too many network interfaces"
3000 22b7f6f8 Thomas Thrainer
                                 " (%d), cannot add more" % constants.MAX_NICS,
3001 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
3002 22b7f6f8 Thomas Thrainer
3003 22b7f6f8 Thomas Thrainer
    # Pre-compute NIC changes (necessary to use result in hooks)
3004 22b7f6f8 Thomas Thrainer
    self._nic_chgdesc = []
3005 22b7f6f8 Thomas Thrainer
    if self.nicmod:
3006 22b7f6f8 Thomas Thrainer
      # Operate on copies as this is still in prereq
3007 d0d7d7cf Thomas Thrainer
      nics = [nic.Copy() for nic in self.instance.nics]
3008 5eacbcae Thomas Thrainer
      _ApplyContainerMods("NIC", nics, self._nic_chgdesc, self.nicmod,
3009 5eacbcae Thomas Thrainer
                          self._CreateNewNic, self._ApplyNicMods, None)
3010 22b7f6f8 Thomas Thrainer
      # Verify that NIC names are unique and valid
3011 22b7f6f8 Thomas Thrainer
      utils.ValidateDeviceNames("NIC", nics)
3012 22b7f6f8 Thomas Thrainer
      self._new_nics = nics
3013 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_NIC_COUNT] = len(self._new_nics)
3014 22b7f6f8 Thomas Thrainer
    else:
3015 22b7f6f8 Thomas Thrainer
      self._new_nics = None
3016 d0d7d7cf Thomas Thrainer
      ispec[constants.ISPEC_NIC_COUNT] = len(self.instance.nics)
3017 22b7f6f8 Thomas Thrainer
3018 22b7f6f8 Thomas Thrainer
    if not self.op.ignore_ipolicy:
3019 d0d7d7cf Thomas Thrainer
      ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(self.cluster,
3020 22b7f6f8 Thomas Thrainer
                                                              group_info)
3021 22b7f6f8 Thomas Thrainer
3022 22b7f6f8 Thomas Thrainer
      # Fill ispec with backend parameters
3023 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_SPINDLE_USE] = \
3024 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_SPINDLE_USE, None)
3025 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_CPU_COUNT] = self.be_new.get(constants.BE_VCPUS,
3026 22b7f6f8 Thomas Thrainer
                                                         None)
3027 22b7f6f8 Thomas Thrainer
3028 22b7f6f8 Thomas Thrainer
      # Copy ispec to verify parameters with min/max values separately
3029 22b7f6f8 Thomas Thrainer
      if self.op.disk_template:
3030 22b7f6f8 Thomas Thrainer
        new_disk_template = self.op.disk_template
3031 22b7f6f8 Thomas Thrainer
      else:
3032 d0d7d7cf Thomas Thrainer
        new_disk_template = self.instance.disk_template
3033 22b7f6f8 Thomas Thrainer
      ispec_max = ispec.copy()
3034 22b7f6f8 Thomas Thrainer
      ispec_max[constants.ISPEC_MEM_SIZE] = \
3035 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_MAXMEM, None)
3036 22b7f6f8 Thomas Thrainer
      res_max = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec_max,
3037 22b7f6f8 Thomas Thrainer
                                                     new_disk_template)
3038 22b7f6f8 Thomas Thrainer
      ispec_min = ispec.copy()
3039 22b7f6f8 Thomas Thrainer
      ispec_min[constants.ISPEC_MEM_SIZE] = \
3040 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_MINMEM, None)
3041 22b7f6f8 Thomas Thrainer
      res_min = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec_min,
3042 22b7f6f8 Thomas Thrainer
                                                     new_disk_template)
3043 22b7f6f8 Thomas Thrainer
3044 22b7f6f8 Thomas Thrainer
      if (res_max or res_min):
3045 22b7f6f8 Thomas Thrainer
        # FIXME: Improve error message by including information about whether
3046 22b7f6f8 Thomas Thrainer
        # the upper or lower limit of the parameter fails the ipolicy.
3047 22b7f6f8 Thomas Thrainer
        msg = ("Instance allocation to group %s (%s) violates policy: %s" %
3048 22b7f6f8 Thomas Thrainer
               (group_info, group_info.name,
3049 22b7f6f8 Thomas Thrainer
                utils.CommaJoin(set(res_max + res_min))))
3050 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
3051 22b7f6f8 Thomas Thrainer
3052 22b7f6f8 Thomas Thrainer
  def _ConvertPlainToDrbd(self, feedback_fn):
3053 22b7f6f8 Thomas Thrainer
    """Converts an instance from plain to drbd.
3054 22b7f6f8 Thomas Thrainer

3055 22b7f6f8 Thomas Thrainer
    """
3056 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to drbd")
3057 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3058 1c3231aa Thomas Thrainer
    snode_uuid = self.op.remote_node_uuid
3059 22b7f6f8 Thomas Thrainer
3060 d0d7d7cf Thomas Thrainer
    assert self.instance.disk_template == constants.DT_PLAIN
3061 22b7f6f8 Thomas Thrainer
3062 22b7f6f8 Thomas Thrainer
    # create a fake disk info for _GenerateDiskTemplate
3063 22b7f6f8 Thomas Thrainer
    disk_info = [{constants.IDISK_SIZE: d.size, constants.IDISK_MODE: d.mode,
3064 22b7f6f8 Thomas Thrainer
                  constants.IDISK_VG: d.logical_id[0],
3065 22b7f6f8 Thomas Thrainer
                  constants.IDISK_NAME: d.name}
3066 d0d7d7cf Thomas Thrainer
                 for d in self.instance.disks]
3067 5eacbcae Thomas Thrainer
    new_disks = GenerateDiskTemplate(self, self.op.disk_template,
3068 da4a52a3 Thomas Thrainer
                                     self.instance.uuid, pnode_uuid,
3069 d0d7d7cf Thomas Thrainer
                                     [snode_uuid], disk_info, None, None, 0,
3070 d0d7d7cf Thomas Thrainer
                                     feedback_fn, self.diskparams)
3071 22b7f6f8 Thomas Thrainer
    anno_disks = rpc.AnnotateDiskParams(constants.DT_DRBD8, new_disks,
3072 22b7f6f8 Thomas Thrainer
                                        self.diskparams)
3073 1c3231aa Thomas Thrainer
    p_excl_stor = IsExclusiveStorageEnabledNodeUuid(self.cfg, pnode_uuid)
3074 1c3231aa Thomas Thrainer
    s_excl_stor = IsExclusiveStorageEnabledNodeUuid(self.cfg, snode_uuid)
3075 d0d7d7cf Thomas Thrainer
    info = GetInstanceInfoText(self.instance)
3076 22b7f6f8 Thomas Thrainer
    feedback_fn("Creating additional volumes...")
3077 22b7f6f8 Thomas Thrainer
    # first, create the missing data and meta devices
3078 22b7f6f8 Thomas Thrainer
    for disk in anno_disks:
3079 22b7f6f8 Thomas Thrainer
      # unfortunately this is... not too nice
3080 d0d7d7cf Thomas Thrainer
      CreateSingleBlockDev(self, pnode_uuid, self.instance, disk.children[1],
3081 5eacbcae Thomas Thrainer
                           info, True, p_excl_stor)
3082 22b7f6f8 Thomas Thrainer
      for child in disk.children:
3083 d0d7d7cf Thomas Thrainer
        CreateSingleBlockDev(self, snode_uuid, self.instance, child, info, True,
3084 5eacbcae Thomas Thrainer
                             s_excl_stor)
3085 22b7f6f8 Thomas Thrainer
    # at this stage, all new LVs have been created, we can rename the
3086 22b7f6f8 Thomas Thrainer
    # old ones
3087 22b7f6f8 Thomas Thrainer
    feedback_fn("Renaming original volumes...")
3088 22b7f6f8 Thomas Thrainer
    rename_list = [(o, n.children[0].logical_id)
3089 d0d7d7cf Thomas Thrainer
                   for (o, n) in zip(self.instance.disks, new_disks)]
3090 1c3231aa Thomas Thrainer
    result = self.rpc.call_blockdev_rename(pnode_uuid, rename_list)
3091 22b7f6f8 Thomas Thrainer
    result.Raise("Failed to rename original LVs")
3092 22b7f6f8 Thomas Thrainer
3093 22b7f6f8 Thomas Thrainer
    feedback_fn("Initializing DRBD devices...")
3094 22b7f6f8 Thomas Thrainer
    # all child devices are in place, we can now create the DRBD devices
3095 22b7f6f8 Thomas Thrainer
    try:
3096 22b7f6f8 Thomas Thrainer
      for disk in anno_disks:
3097 1c3231aa Thomas Thrainer
        for (node_uuid, excl_stor) in [(pnode_uuid, p_excl_stor),
3098 1c3231aa Thomas Thrainer
                                       (snode_uuid, s_excl_stor)]:
3099 1c3231aa Thomas Thrainer
          f_create = node_uuid == pnode_uuid
3100 d0d7d7cf Thomas Thrainer
          CreateSingleBlockDev(self, node_uuid, self.instance, disk, info,
3101 d0d7d7cf Thomas Thrainer
                               f_create, excl_stor)
3102 22b7f6f8 Thomas Thrainer
    except errors.GenericError, e:
3103 22b7f6f8 Thomas Thrainer
      feedback_fn("Initializing of DRBD devices failed;"
3104 22b7f6f8 Thomas Thrainer
                  " renaming back original volumes...")
3105 22b7f6f8 Thomas Thrainer
      for disk in new_disks:
3106 1c3231aa Thomas Thrainer
        self.cfg.SetDiskID(disk, pnode_uuid)
3107 22b7f6f8 Thomas Thrainer
      rename_back_list = [(n.children[0], o.logical_id)
3108 d0d7d7cf Thomas Thrainer
                          for (n, o) in zip(new_disks, self.instance.disks)]
3109 1c3231aa Thomas Thrainer
      result = self.rpc.call_blockdev_rename(pnode_uuid, rename_back_list)
3110 22b7f6f8 Thomas Thrainer
      result.Raise("Failed to rename LVs back after error %s" % str(e))
3111 22b7f6f8 Thomas Thrainer
      raise
3112 22b7f6f8 Thomas Thrainer
3113 22b7f6f8 Thomas Thrainer
    # at this point, the instance has been modified
3114 d0d7d7cf Thomas Thrainer
    self.instance.disk_template = constants.DT_DRBD8
3115 d0d7d7cf Thomas Thrainer
    self.instance.disks = new_disks
3116 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
3117 22b7f6f8 Thomas Thrainer
3118 22b7f6f8 Thomas Thrainer
    # Release node locks while waiting for sync
3119 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3120 22b7f6f8 Thomas Thrainer
3121 22b7f6f8 Thomas Thrainer
    # disks are created, waiting for sync
3122 d0d7d7cf Thomas Thrainer
    disk_abort = not WaitForSync(self, self.instance,
3123 5eacbcae Thomas Thrainer
                                 oneshot=not self.op.wait_for_sync)
3124 22b7f6f8 Thomas Thrainer
    if disk_abort:
3125 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("There are some degraded disks for"
3126 22b7f6f8 Thomas Thrainer
                               " this instance, please cleanup manually")
3127 22b7f6f8 Thomas Thrainer
3128 22b7f6f8 Thomas Thrainer
    # Node resource locks will be released by caller
3129 22b7f6f8 Thomas Thrainer
3130 22b7f6f8 Thomas Thrainer
  def _ConvertDrbdToPlain(self, feedback_fn):
3131 22b7f6f8 Thomas Thrainer
    """Converts an instance from drbd to plain.
3132 22b7f6f8 Thomas Thrainer

3133 22b7f6f8 Thomas Thrainer
    """
3134 d0d7d7cf Thomas Thrainer
    assert len(self.instance.secondary_nodes) == 1
3135 d0d7d7cf Thomas Thrainer
    assert self.instance.disk_template == constants.DT_DRBD8
3136 22b7f6f8 Thomas Thrainer
3137 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3138 d0d7d7cf Thomas Thrainer
    snode_uuid = self.instance.secondary_nodes[0]
3139 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to plain")
3140 22b7f6f8 Thomas Thrainer
3141 d0d7d7cf Thomas Thrainer
    old_disks = AnnotateDiskParams(self.instance, self.instance.disks, self.cfg)
3142 d0d7d7cf Thomas Thrainer
    new_disks = [d.children[0] for d in self.instance.disks]
3143 22b7f6f8 Thomas Thrainer
3144 22b7f6f8 Thomas Thrainer
    # copy over size, mode and name
3145 22b7f6f8 Thomas Thrainer
    for parent, child in zip(old_disks, new_disks):
3146 22b7f6f8 Thomas Thrainer
      child.size = parent.size
3147 22b7f6f8 Thomas Thrainer
      child.mode = parent.mode
3148 22b7f6f8 Thomas Thrainer
      child.name = parent.name
3149 22b7f6f8 Thomas Thrainer
3150 22b7f6f8 Thomas Thrainer
    # this is a DRBD disk, return its port to the pool
3151 22b7f6f8 Thomas Thrainer
    # NOTE: this must be done right before the call to cfg.Update!
3152 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3153 22b7f6f8 Thomas Thrainer
      tcp_port = disk.logical_id[2]
3154 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(tcp_port)
3155 22b7f6f8 Thomas Thrainer
3156 22b7f6f8 Thomas Thrainer
    # update instance structure
3157 d0d7d7cf Thomas Thrainer
    self.instance.disks = new_disks
3158 d0d7d7cf Thomas Thrainer
    self.instance.disk_template = constants.DT_PLAIN
3159 d0d7d7cf Thomas Thrainer
    _UpdateIvNames(0, self.instance.disks)
3160 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
3161 22b7f6f8 Thomas Thrainer
3162 22b7f6f8 Thomas Thrainer
    # Release locks in case removing disks takes a while
3163 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3164 22b7f6f8 Thomas Thrainer
3165 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing volumes on the secondary node...")
3166 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3167 1c3231aa Thomas Thrainer
      self.cfg.SetDiskID(disk, snode_uuid)
3168 1c3231aa Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(snode_uuid, disk).fail_msg
3169 22b7f6f8 Thomas Thrainer
      if msg:
3170 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove block device %s on node %s,"
3171 1c3231aa Thomas Thrainer
                        " continuing anyway: %s", disk.iv_name,
3172 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(snode_uuid), msg)
3173 22b7f6f8 Thomas Thrainer
3174 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing unneeded volumes on the primary node...")
3175 22b7f6f8 Thomas Thrainer
    for idx, disk in enumerate(old_disks):
3176 22b7f6f8 Thomas Thrainer
      meta = disk.children[1]
3177 1c3231aa Thomas Thrainer
      self.cfg.SetDiskID(meta, pnode_uuid)
3178 1c3231aa Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(pnode_uuid, meta).fail_msg
3179 22b7f6f8 Thomas Thrainer
      if msg:
3180 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove metadata for disk %d on node %s,"
3181 1c3231aa Thomas Thrainer
                        " continuing anyway: %s", idx,
3182 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(pnode_uuid), msg)
3183 22b7f6f8 Thomas Thrainer
3184 22b7f6f8 Thomas Thrainer
  def _CreateNewDisk(self, idx, params, _):
3185 22b7f6f8 Thomas Thrainer
    """Creates a new disk.
3186 22b7f6f8 Thomas Thrainer

3187 22b7f6f8 Thomas Thrainer
    """
3188 22b7f6f8 Thomas Thrainer
    # add a new disk
3189 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DTS_FILEBASED:
3190 d0d7d7cf Thomas Thrainer
      (file_driver, file_path) = self.instance.disks[0].logical_id
3191 22b7f6f8 Thomas Thrainer
      file_path = os.path.dirname(file_path)
3192 22b7f6f8 Thomas Thrainer
    else:
3193 22b7f6f8 Thomas Thrainer
      file_driver = file_path = None
3194 22b7f6f8 Thomas Thrainer
3195 22b7f6f8 Thomas Thrainer
    disk = \
3196 d0d7d7cf Thomas Thrainer
      GenerateDiskTemplate(self, self.instance.disk_template,
3197 da4a52a3 Thomas Thrainer
                           self.instance.uuid, self.instance.primary_node,
3198 d0d7d7cf Thomas Thrainer
                           self.instance.secondary_nodes, [params], file_path,
3199 d0d7d7cf Thomas Thrainer
                           file_driver, idx, self.Log, self.diskparams)[0]
3200 22b7f6f8 Thomas Thrainer
3201 d0d7d7cf Thomas Thrainer
    new_disks = CreateDisks(self, self.instance, disks=[disk])
3202 22b7f6f8 Thomas Thrainer
3203 22b7f6f8 Thomas Thrainer
    if self.cluster.prealloc_wipe_disks:
3204 22b7f6f8 Thomas Thrainer
      # Wipe new disk
3205 d0d7d7cf Thomas Thrainer
      WipeOrCleanupDisks(self, self.instance,
3206 a365b47f Bernardo Dal Seno
                         disks=[(idx, disk, 0)],
3207 a365b47f Bernardo Dal Seno
                         cleanup=new_disks)
3208 22b7f6f8 Thomas Thrainer
3209 22b7f6f8 Thomas Thrainer
    return (disk, [
3210 22b7f6f8 Thomas Thrainer
      ("disk/%d" % idx, "add:size=%s,mode=%s" % (disk.size, disk.mode)),
3211 22b7f6f8 Thomas Thrainer
      ])
3212 22b7f6f8 Thomas Thrainer
3213 22b7f6f8 Thomas Thrainer
  @staticmethod
3214 22b7f6f8 Thomas Thrainer
  def _ModifyDisk(idx, disk, params, _):
3215 22b7f6f8 Thomas Thrainer
    """Modifies a disk.
3216 22b7f6f8 Thomas Thrainer

3217 22b7f6f8 Thomas Thrainer
    """
3218 22b7f6f8 Thomas Thrainer
    changes = []
3219 22b7f6f8 Thomas Thrainer
    mode = params.get(constants.IDISK_MODE, None)
3220 22b7f6f8 Thomas Thrainer
    if mode:
3221 22b7f6f8 Thomas Thrainer
      disk.mode = mode
3222 22b7f6f8 Thomas Thrainer
      changes.append(("disk.mode/%d" % idx, disk.mode))
3223 22b7f6f8 Thomas Thrainer
3224 22b7f6f8 Thomas Thrainer
    name = params.get(constants.IDISK_NAME, None)
3225 22b7f6f8 Thomas Thrainer
    disk.name = name
3226 22b7f6f8 Thomas Thrainer
    changes.append(("disk.name/%d" % idx, disk.name))
3227 22b7f6f8 Thomas Thrainer
3228 22b7f6f8 Thomas Thrainer
    return changes
3229 22b7f6f8 Thomas Thrainer
3230 22b7f6f8 Thomas Thrainer
  def _RemoveDisk(self, idx, root, _):
3231 22b7f6f8 Thomas Thrainer
    """Removes a disk.
3232 22b7f6f8 Thomas Thrainer

3233 22b7f6f8 Thomas Thrainer
    """
3234 5eacbcae Thomas Thrainer
    (anno_disk,) = AnnotateDiskParams(self.instance, [root], self.cfg)
3235 1c3231aa Thomas Thrainer
    for node_uuid, disk in anno_disk.ComputeNodeTree(
3236 1c3231aa Thomas Thrainer
                             self.instance.primary_node):
3237 1c3231aa Thomas Thrainer
      self.cfg.SetDiskID(disk, node_uuid)
3238 1c3231aa Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(node_uuid, disk).fail_msg
3239 22b7f6f8 Thomas Thrainer
      if msg:
3240 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove disk/%d on node '%s': %s,"
3241 1c3231aa Thomas Thrainer
                        " continuing anyway", idx,
3242 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
3243 22b7f6f8 Thomas Thrainer
3244 22b7f6f8 Thomas Thrainer
    # if this is a DRBD disk, return its port to the pool
3245 22b7f6f8 Thomas Thrainer
    if root.dev_type in constants.LDS_DRBD:
3246 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(root.logical_id[2])
3247 22b7f6f8 Thomas Thrainer
3248 22b7f6f8 Thomas Thrainer
  def _CreateNewNic(self, idx, params, private):
3249 22b7f6f8 Thomas Thrainer
    """Creates data structure for a new network interface.
3250 22b7f6f8 Thomas Thrainer

3251 22b7f6f8 Thomas Thrainer
    """
3252 22b7f6f8 Thomas Thrainer
    mac = params[constants.INIC_MAC]
3253 22b7f6f8 Thomas Thrainer
    ip = params.get(constants.INIC_IP, None)
3254 22b7f6f8 Thomas Thrainer
    net = params.get(constants.INIC_NETWORK, None)
3255 22b7f6f8 Thomas Thrainer
    name = params.get(constants.INIC_NAME, None)
3256 22b7f6f8 Thomas Thrainer
    net_uuid = self.cfg.LookupNetwork(net)
3257 22b7f6f8 Thomas Thrainer
    #TODO: not private.filled?? can a nic have no nicparams??
3258 22b7f6f8 Thomas Thrainer
    nicparams = private.filled
3259 22b7f6f8 Thomas Thrainer
    nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, name=name,
3260 22b7f6f8 Thomas Thrainer
                       nicparams=nicparams)
3261 22b7f6f8 Thomas Thrainer
    nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
3262 22b7f6f8 Thomas Thrainer
3263 22b7f6f8 Thomas Thrainer
    return (nobj, [
3264 22b7f6f8 Thomas Thrainer
      ("nic.%d" % idx,
3265 22b7f6f8 Thomas Thrainer
       "add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" %
3266 22b7f6f8 Thomas Thrainer
       (mac, ip, private.filled[constants.NIC_MODE],
3267 22b7f6f8 Thomas Thrainer
       private.filled[constants.NIC_LINK],
3268 22b7f6f8 Thomas Thrainer
       net)),
3269 22b7f6f8 Thomas Thrainer
      ])
3270 22b7f6f8 Thomas Thrainer
3271 22b7f6f8 Thomas Thrainer
  def _ApplyNicMods(self, idx, nic, params, private):
3272 22b7f6f8 Thomas Thrainer
    """Modifies a network interface.
3273 22b7f6f8 Thomas Thrainer

3274 22b7f6f8 Thomas Thrainer
    """
3275 22b7f6f8 Thomas Thrainer
    changes = []
3276 22b7f6f8 Thomas Thrainer
3277 22b7f6f8 Thomas Thrainer
    for key in [constants.INIC_MAC, constants.INIC_IP, constants.INIC_NAME]:
3278 22b7f6f8 Thomas Thrainer
      if key in params:
3279 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), params[key]))
3280 22b7f6f8 Thomas Thrainer
        setattr(nic, key, params[key])
3281 22b7f6f8 Thomas Thrainer
3282 22b7f6f8 Thomas Thrainer
    new_net = params.get(constants.INIC_NETWORK, nic.network)
3283 22b7f6f8 Thomas Thrainer
    new_net_uuid = self.cfg.LookupNetwork(new_net)
3284 22b7f6f8 Thomas Thrainer
    if new_net_uuid != nic.network:
3285 22b7f6f8 Thomas Thrainer
      changes.append(("nic.network/%d" % idx, new_net))
3286 22b7f6f8 Thomas Thrainer
      nic.network = new_net_uuid
3287 22b7f6f8 Thomas Thrainer
3288 22b7f6f8 Thomas Thrainer
    if private.filled:
3289 22b7f6f8 Thomas Thrainer
      nic.nicparams = private.filled
3290 22b7f6f8 Thomas Thrainer
3291 22b7f6f8 Thomas Thrainer
      for (key, val) in nic.nicparams.items():
3292 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), val))
3293 22b7f6f8 Thomas Thrainer
3294 22b7f6f8 Thomas Thrainer
    return changes
3295 22b7f6f8 Thomas Thrainer
3296 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3297 22b7f6f8 Thomas Thrainer
    """Modifies an instance.
3298 22b7f6f8 Thomas Thrainer

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

3301 22b7f6f8 Thomas Thrainer
    """
3302 22b7f6f8 Thomas Thrainer
    # Process here the warnings from CheckPrereq, as we don't have a
3303 22b7f6f8 Thomas Thrainer
    # feedback_fn there.
3304 22b7f6f8 Thomas Thrainer
    # TODO: Replace with self.LogWarning
3305 22b7f6f8 Thomas Thrainer
    for warn in self.warn:
3306 22b7f6f8 Thomas Thrainer
      feedback_fn("WARNING: %s" % warn)
3307 22b7f6f8 Thomas Thrainer
3308 22b7f6f8 Thomas Thrainer
    assert ((self.op.disk_template is None) ^
3309 22b7f6f8 Thomas Thrainer
            bool(self.owned_locks(locking.LEVEL_NODE_RES))), \
3310 22b7f6f8 Thomas Thrainer
      "Not owning any node resource locks"
3311 22b7f6f8 Thomas Thrainer
3312 22b7f6f8 Thomas Thrainer
    result = []
3313 22b7f6f8 Thomas Thrainer
3314 22b7f6f8 Thomas Thrainer
    # New primary node
3315 1c3231aa Thomas Thrainer
    if self.op.pnode_uuid:
3316 d0d7d7cf Thomas Thrainer
      self.instance.primary_node = self.op.pnode_uuid
3317 22b7f6f8 Thomas Thrainer
3318 22b7f6f8 Thomas Thrainer
    # runtime memory
3319 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
3320 d0d7d7cf Thomas Thrainer
      rpcres = self.rpc.call_instance_balloon_memory(self.instance.primary_node,
3321 d0d7d7cf Thomas Thrainer
                                                     self.instance,
3322 22b7f6f8 Thomas Thrainer
                                                     self.op.runtime_mem)
3323 22b7f6f8 Thomas Thrainer
      rpcres.Raise("Cannot modify instance runtime memory")
3324 22b7f6f8 Thomas Thrainer
      result.append(("runtime_memory", self.op.runtime_mem))
3325 22b7f6f8 Thomas Thrainer
3326 22b7f6f8 Thomas Thrainer
    # Apply disk changes
3327 d0d7d7cf Thomas Thrainer
    _ApplyContainerMods("disk", self.instance.disks, result, self.diskmod,
3328 5eacbcae Thomas Thrainer
                        self._CreateNewDisk, self._ModifyDisk,
3329 5eacbcae Thomas Thrainer
                        self._RemoveDisk)
3330 d0d7d7cf Thomas Thrainer
    _UpdateIvNames(0, self.instance.disks)
3331 22b7f6f8 Thomas Thrainer
3332 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
3333 22b7f6f8 Thomas Thrainer
      if __debug__:
3334 d0d7d7cf Thomas Thrainer
        check_nodes = set(self.instance.all_nodes)
3335 1c3231aa Thomas Thrainer
        if self.op.remote_node_uuid:
3336 1c3231aa Thomas Thrainer
          check_nodes.add(self.op.remote_node_uuid)
3337 22b7f6f8 Thomas Thrainer
        for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
3338 22b7f6f8 Thomas Thrainer
          owned = self.owned_locks(level)
3339 22b7f6f8 Thomas Thrainer
          assert not (check_nodes - owned), \
3340 22b7f6f8 Thomas Thrainer
            ("Not owning the correct locks, owning %r, expected at least %r" %
3341 22b7f6f8 Thomas Thrainer
             (owned, check_nodes))
3342 22b7f6f8 Thomas Thrainer
3343 d0d7d7cf Thomas Thrainer
      r_shut = ShutdownInstanceDisks(self, self.instance)
3344 22b7f6f8 Thomas Thrainer
      if not r_shut:
3345 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Cannot shutdown instance disks, unable to"
3346 22b7f6f8 Thomas Thrainer
                                 " proceed with disk template conversion")
3347 d0d7d7cf Thomas Thrainer
      mode = (self.instance.disk_template, self.op.disk_template)
3348 22b7f6f8 Thomas Thrainer
      try:
3349 22b7f6f8 Thomas Thrainer
        self._DISK_CONVERSIONS[mode](self, feedback_fn)
3350 22b7f6f8 Thomas Thrainer
      except:
3351 da4a52a3 Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(self.instance.uuid)
3352 22b7f6f8 Thomas Thrainer
        raise
3353 22b7f6f8 Thomas Thrainer
      result.append(("disk_template", self.op.disk_template))
3354 22b7f6f8 Thomas Thrainer
3355 d0d7d7cf Thomas Thrainer
      assert self.instance.disk_template == self.op.disk_template, \
3356 22b7f6f8 Thomas Thrainer
        ("Expected disk template '%s', found '%s'" %
3357 d0d7d7cf Thomas Thrainer
         (self.op.disk_template, self.instance.disk_template))
3358 22b7f6f8 Thomas Thrainer
3359 22b7f6f8 Thomas Thrainer
    # Release node and resource locks if there are any (they might already have
3360 22b7f6f8 Thomas Thrainer
    # been released during disk conversion)
3361 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3362 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE_RES)
3363 22b7f6f8 Thomas Thrainer
3364 22b7f6f8 Thomas Thrainer
    # Apply NIC changes
3365 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
3366 d0d7d7cf Thomas Thrainer
      self.instance.nics = self._new_nics
3367 22b7f6f8 Thomas Thrainer
      result.extend(self._nic_chgdesc)
3368 22b7f6f8 Thomas Thrainer
3369 22b7f6f8 Thomas Thrainer
    # hvparams changes
3370 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
3371 d0d7d7cf Thomas Thrainer
      self.instance.hvparams = self.hv_inst
3372 22b7f6f8 Thomas Thrainer
      for key, val in self.op.hvparams.iteritems():
3373 22b7f6f8 Thomas Thrainer
        result.append(("hv/%s" % key, val))
3374 22b7f6f8 Thomas Thrainer
3375 22b7f6f8 Thomas Thrainer
    # beparams changes
3376 22b7f6f8 Thomas Thrainer
    if self.op.beparams:
3377 d0d7d7cf Thomas Thrainer
      self.instance.beparams = self.be_inst
3378 22b7f6f8 Thomas Thrainer
      for key, val in self.op.beparams.iteritems():
3379 22b7f6f8 Thomas Thrainer
        result.append(("be/%s" % key, val))
3380 22b7f6f8 Thomas Thrainer
3381 22b7f6f8 Thomas Thrainer
    # OS change
3382 22b7f6f8 Thomas Thrainer
    if self.op.os_name:
3383 d0d7d7cf Thomas Thrainer
      self.instance.os = self.op.os_name
3384 22b7f6f8 Thomas Thrainer
3385 22b7f6f8 Thomas Thrainer
    # osparams changes
3386 22b7f6f8 Thomas Thrainer
    if self.op.osparams:
3387 d0d7d7cf Thomas Thrainer
      self.instance.osparams = self.os_inst
3388 22b7f6f8 Thomas Thrainer
      for key, val in self.op.osparams.iteritems():
3389 22b7f6f8 Thomas Thrainer
        result.append(("os/%s" % key, val))
3390 22b7f6f8 Thomas Thrainer
3391 22b7f6f8 Thomas Thrainer
    if self.op.offline is None:
3392 22b7f6f8 Thomas Thrainer
      # Ignore
3393 22b7f6f8 Thomas Thrainer
      pass
3394 22b7f6f8 Thomas Thrainer
    elif self.op.offline:
3395 22b7f6f8 Thomas Thrainer
      # Mark instance as offline
3396 da4a52a3 Thomas Thrainer
      self.cfg.MarkInstanceOffline(self.instance.uuid)
3397 22b7f6f8 Thomas Thrainer
      result.append(("admin_state", constants.ADMINST_OFFLINE))
3398 22b7f6f8 Thomas Thrainer
    else:
3399 22b7f6f8 Thomas Thrainer
      # Mark instance as online, but stopped
3400 da4a52a3 Thomas Thrainer
      self.cfg.MarkInstanceDown(self.instance.uuid)
3401 22b7f6f8 Thomas Thrainer
      result.append(("admin_state", constants.ADMINST_DOWN))
3402 22b7f6f8 Thomas Thrainer
3403 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn, self.proc.GetECId())
3404 22b7f6f8 Thomas Thrainer
3405 22b7f6f8 Thomas Thrainer
    assert not (self.owned_locks(locking.LEVEL_NODE_RES) or
3406 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
3407 22b7f6f8 Thomas Thrainer
      "All node locks should have been released by now"
3408 22b7f6f8 Thomas Thrainer
3409 22b7f6f8 Thomas Thrainer
    return result
3410 22b7f6f8 Thomas Thrainer
3411 22b7f6f8 Thomas Thrainer
  _DISK_CONVERSIONS = {
3412 22b7f6f8 Thomas Thrainer
    (constants.DT_PLAIN, constants.DT_DRBD8): _ConvertPlainToDrbd,
3413 22b7f6f8 Thomas Thrainer
    (constants.DT_DRBD8, constants.DT_PLAIN): _ConvertDrbdToPlain,
3414 22b7f6f8 Thomas Thrainer
    }
3415 22b7f6f8 Thomas Thrainer
3416 22b7f6f8 Thomas Thrainer
3417 22b7f6f8 Thomas Thrainer
class LUInstanceChangeGroup(LogicalUnit):
3418 22b7f6f8 Thomas Thrainer
  HPATH = "instance-change-group"
3419 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
3420 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
3421 22b7f6f8 Thomas Thrainer
3422 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
3423 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
3424 22b7f6f8 Thomas Thrainer
3425 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
3426 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODEGROUP: [],
3427 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE: [],
3428 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
3429 22b7f6f8 Thomas Thrainer
      }
3430 22b7f6f8 Thomas Thrainer
3431 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
3432 22b7f6f8 Thomas Thrainer
3433 22b7f6f8 Thomas Thrainer
    if self.op.target_groups:
3434 22b7f6f8 Thomas Thrainer
      self.req_target_uuids = map(self.cfg.LookupNodeGroup,
3435 22b7f6f8 Thomas Thrainer
                                  self.op.target_groups)
3436 22b7f6f8 Thomas Thrainer
    else:
3437 22b7f6f8 Thomas Thrainer
      self.req_target_uuids = None
3438 22b7f6f8 Thomas Thrainer
3439 5eacbcae Thomas Thrainer
    self.op.iallocator = GetDefaultIAllocator(self.cfg, self.op.iallocator)
3440 22b7f6f8 Thomas Thrainer
3441 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
3442 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
3443 22b7f6f8 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
3444 22b7f6f8 Thomas Thrainer
3445 22b7f6f8 Thomas Thrainer
      if self.req_target_uuids:
3446 22b7f6f8 Thomas Thrainer
        lock_groups = set(self.req_target_uuids)
3447 22b7f6f8 Thomas Thrainer
3448 22b7f6f8 Thomas Thrainer
        # Lock all groups used by instance optimistically; this requires going
3449 22b7f6f8 Thomas Thrainer
        # via the node before it's locked, requiring verification later on
3450 da4a52a3 Thomas Thrainer
        instance_groups = self.cfg.GetInstanceNodeGroups(self.op.instance_uuid)
3451 22b7f6f8 Thomas Thrainer
        lock_groups.update(instance_groups)
3452 22b7f6f8 Thomas Thrainer
      else:
3453 22b7f6f8 Thomas Thrainer
        # No target groups, need to lock all of them
3454 22b7f6f8 Thomas Thrainer
        lock_groups = locking.ALL_SET
3455 22b7f6f8 Thomas Thrainer
3456 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = lock_groups
3457 22b7f6f8 Thomas Thrainer
3458 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE:
3459 22b7f6f8 Thomas Thrainer
      if self.req_target_uuids:
3460 22b7f6f8 Thomas Thrainer
        # Lock all nodes used by instances
3461 22b7f6f8 Thomas Thrainer
        self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
3462 22b7f6f8 Thomas Thrainer
        self._LockInstancesNodes()
3463 22b7f6f8 Thomas Thrainer
3464 22b7f6f8 Thomas Thrainer
        # Lock all nodes in all potential target groups
3465 22b7f6f8 Thomas Thrainer
        lock_groups = (frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) -
3466 da4a52a3 Thomas Thrainer
                       self.cfg.GetInstanceNodeGroups(self.op.instance_uuid))
3467 1c3231aa Thomas Thrainer
        member_nodes = [node_uuid
3468 22b7f6f8 Thomas Thrainer
                        for group in lock_groups
3469 1c3231aa Thomas Thrainer
                        for node_uuid in self.cfg.GetNodeGroup(group).members]
3470 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE].extend(member_nodes)
3471 22b7f6f8 Thomas Thrainer
      else:
3472 22b7f6f8 Thomas Thrainer
        # Lock all nodes as all groups are potential targets
3473 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
3474 22b7f6f8 Thomas Thrainer
3475 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
3476 da4a52a3 Thomas Thrainer
    owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
3477 22b7f6f8 Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
3478 22b7f6f8 Thomas Thrainer
    owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE))
3479 22b7f6f8 Thomas Thrainer
3480 22b7f6f8 Thomas Thrainer
    assert (self.req_target_uuids is None or
3481 22b7f6f8 Thomas Thrainer
            owned_groups.issuperset(self.req_target_uuids))
3482 da4a52a3 Thomas Thrainer
    assert owned_instance_names == set([self.op.instance_name])
3483 22b7f6f8 Thomas Thrainer
3484 22b7f6f8 Thomas Thrainer
    # Get instance information
3485 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
3486 22b7f6f8 Thomas Thrainer
3487 22b7f6f8 Thomas Thrainer
    # Check if node groups for locked instance are still correct
3488 22b7f6f8 Thomas Thrainer
    assert owned_nodes.issuperset(self.instance.all_nodes), \
3489 22b7f6f8 Thomas Thrainer
      ("Instance %s's nodes changed while we kept the lock" %
3490 22b7f6f8 Thomas Thrainer
       self.op.instance_name)
3491 22b7f6f8 Thomas Thrainer
3492 da4a52a3 Thomas Thrainer
    inst_groups = CheckInstanceNodeGroups(self.cfg, self.op.instance_uuid,
3493 5eacbcae Thomas Thrainer
                                          owned_groups)
3494 22b7f6f8 Thomas Thrainer
3495 22b7f6f8 Thomas Thrainer
    if self.req_target_uuids:
3496 22b7f6f8 Thomas Thrainer
      # User requested specific target groups
3497 22b7f6f8 Thomas Thrainer
      self.target_uuids = frozenset(self.req_target_uuids)
3498 22b7f6f8 Thomas Thrainer
    else:
3499 22b7f6f8 Thomas Thrainer
      # All groups except those used by the instance are potential targets
3500 22b7f6f8 Thomas Thrainer
      self.target_uuids = owned_groups - inst_groups
3501 22b7f6f8 Thomas Thrainer
3502 22b7f6f8 Thomas Thrainer
    conflicting_groups = self.target_uuids & inst_groups
3503 22b7f6f8 Thomas Thrainer
    if conflicting_groups:
3504 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't use group(s) '%s' as targets, they are"
3505 22b7f6f8 Thomas Thrainer
                                 " used by the instance '%s'" %
3506 22b7f6f8 Thomas Thrainer
                                 (utils.CommaJoin(conflicting_groups),
3507 22b7f6f8 Thomas Thrainer
                                  self.op.instance_name),
3508 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3509 22b7f6f8 Thomas Thrainer
3510 22b7f6f8 Thomas Thrainer
    if not self.target_uuids:
3511 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are no possible target groups",
3512 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3513 22b7f6f8 Thomas Thrainer
3514 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
3515 22b7f6f8 Thomas Thrainer
    """Build hooks env.
3516 22b7f6f8 Thomas Thrainer

3517 22b7f6f8 Thomas Thrainer
    """
3518 22b7f6f8 Thomas Thrainer
    assert self.target_uuids
3519 22b7f6f8 Thomas Thrainer
3520 22b7f6f8 Thomas Thrainer
    env = {
3521 22b7f6f8 Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
3522 22b7f6f8 Thomas Thrainer
      }
3523 22b7f6f8 Thomas Thrainer
3524 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
3525 22b7f6f8 Thomas Thrainer
3526 22b7f6f8 Thomas Thrainer
    return env
3527 22b7f6f8 Thomas Thrainer
3528 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
3529 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
3530 22b7f6f8 Thomas Thrainer

3531 22b7f6f8 Thomas Thrainer
    """
3532 22b7f6f8 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
3533 22b7f6f8 Thomas Thrainer
    return ([mn], [mn])
3534 22b7f6f8 Thomas Thrainer
3535 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3536 22b7f6f8 Thomas Thrainer
    instances = list(self.owned_locks(locking.LEVEL_INSTANCE))
3537 22b7f6f8 Thomas Thrainer
3538 22b7f6f8 Thomas Thrainer
    assert instances == [self.op.instance_name], "Instance not locked"
3539 22b7f6f8 Thomas Thrainer
3540 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=instances,
3541 22b7f6f8 Thomas Thrainer
                                      target_groups=list(self.target_uuids))
3542 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
3543 22b7f6f8 Thomas Thrainer
3544 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
3545 22b7f6f8 Thomas Thrainer
3546 22b7f6f8 Thomas Thrainer
    if not ial.success:
3547 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute solution for changing group of"
3548 22b7f6f8 Thomas Thrainer
                                 " instance '%s' using iallocator '%s': %s" %
3549 22b7f6f8 Thomas Thrainer
                                 (self.op.instance_name, self.op.iallocator,
3550 22b7f6f8 Thomas Thrainer
                                  ial.info), errors.ECODE_NORES)
3551 22b7f6f8 Thomas Thrainer
3552 5eacbcae Thomas Thrainer
    jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
3553 22b7f6f8 Thomas Thrainer
3554 22b7f6f8 Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for changing group of"
3555 22b7f6f8 Thomas Thrainer
                 " instance '%s'", len(jobs), self.op.instance_name)
3556 22b7f6f8 Thomas Thrainer
3557 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs)