Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance.py @ 72080db1

History | View | Annotate | Download (139.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 1c3231aa Thomas Thrainer
  ComputeIPolicySpecViolation, CheckInstanceState, ExpandNodeUuidAndName
53 5eacbcae Thomas Thrainer
from ganeti.cmdlib.instance_storage import CreateDisks, \
54 a365b47f Bernardo Dal Seno
  CheckNodesFreeDiskPerVG, WipeDisks, WipeOrCleanupDisks, WaitForSync, \
55 1c3231aa Thomas Thrainer
  IsExclusiveStorageEnabledNodeUuid, CreateSingleBlockDev, ComputeDisks, \
56 5eacbcae Thomas Thrainer
  CheckRADOSFreeSpace, ComputeDiskSizePerVG, GenerateDiskTemplate, \
57 3f3ea14c Bernardo Dal Seno
  StartInstanceDisks, ShutdownInstanceDisks, AssembleInstanceDisks, \
58 3f3ea14c Bernardo Dal Seno
  CheckSpindlesExclusiveStorage
59 5eacbcae Thomas Thrainer
from ganeti.cmdlib.instance_utils import BuildInstanceHookEnvByObject, \
60 5eacbcae Thomas Thrainer
  GetClusterDomainSecret, BuildInstanceHookEnv, NICListToTuple, \
61 5eacbcae Thomas Thrainer
  NICToTuple, CheckNodeNotDrained, RemoveInstance, CopyLockList, \
62 5eacbcae Thomas Thrainer
  ReleaseLocks, CheckNodeVmCapable, CheckTargetNodeIPolicy, \
63 5eacbcae Thomas Thrainer
  GetInstanceInfoText, RemoveDisks, CheckNodeFreeMemory, \
64 5eacbcae Thomas Thrainer
  CheckInstanceBridgesExist, CheckNicsBridgesExist, CheckNodeHasOS
65 22b7f6f8 Thomas Thrainer
66 22b7f6f8 Thomas Thrainer
import ganeti.masterd.instance
67 22b7f6f8 Thomas Thrainer
68 22b7f6f8 Thomas Thrainer
69 5eacbcae Thomas Thrainer
#: Type description for changes as returned by L{_ApplyContainerMods}'s
70 22b7f6f8 Thomas Thrainer
#: callbacks
71 22b7f6f8 Thomas Thrainer
_TApplyContModsCbChanges = \
72 22b7f6f8 Thomas Thrainer
  ht.TMaybeListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([
73 22b7f6f8 Thomas Thrainer
    ht.TNonEmptyString,
74 22b7f6f8 Thomas Thrainer
    ht.TAny,
75 22b7f6f8 Thomas Thrainer
    ])))
76 22b7f6f8 Thomas Thrainer
77 22b7f6f8 Thomas Thrainer
78 22b7f6f8 Thomas Thrainer
def _CheckHostnameSane(lu, name):
79 22b7f6f8 Thomas Thrainer
  """Ensures that a given hostname resolves to a 'sane' name.
80 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

328 22b7f6f8 Thomas Thrainer
    """
329 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
330 72080db1 Helga Velroyen
    if self.op.disk_template is None:
331 72080db1 Helga Velroyen
      # FIXME: It would be better to take the default disk template from the
332 72080db1 Helga Velroyen
      # ipolicy, but for the ipolicy we need the primary node, which we get from
333 72080db1 Helga Velroyen
      # the iallocator, which wants the disk template as input. To solve this
334 72080db1 Helga Velroyen
      # chicken-and-egg problem, it should be possible to specify just a node
335 72080db1 Helga Velroyen
      # group from the iallocator and take the ipolicy from that.
336 72080db1 Helga Velroyen
      self.op.disk_template = cluster.enabled_disk_templates[0]
337 22b7f6f8 Thomas Thrainer
    if not self.op.disk_template in cluster.enabled_disk_templates:
338 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot create an instance with disk template"
339 22b7f6f8 Thomas Thrainer
                                 " '%s', because it is not enabled in the"
340 22b7f6f8 Thomas Thrainer
                                 " cluster. Enabled disk templates are: %s." %
341 22b7f6f8 Thomas Thrainer
                                 (self.op.disk_template,
342 22b7f6f8 Thomas Thrainer
                                  ",".join(cluster.enabled_disk_templates)))
343 22b7f6f8 Thomas Thrainer
344 dab6ea3d Helga Velroyen
  def _CheckDiskArguments(self):
345 dab6ea3d Helga Velroyen
    """Checks validity of disk-related arguments.
346 dab6ea3d Helga Velroyen

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

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

518 22b7f6f8 Thomas Thrainer
    Figure out the right locks for instance creation.
519 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

673 22b7f6f8 Thomas Thrainer
    @return: the export information
674 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

1466 22b7f6f8 Thomas Thrainer
    """
1467 da4a52a3 Thomas Thrainer
    (self.op.instance_uuid, self.op.instance_name) = \
1468 da4a52a3 Thomas Thrainer
      ExpandInstanceUuidAndName(self.cfg, self.op.instance_uuid,
1469 da4a52a3 Thomas Thrainer
                                self.op.instance_name)
1470 da4a52a3 Thomas Thrainer
    instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1471 22b7f6f8 Thomas Thrainer
    assert instance is not None
1472 5eacbcae Thomas Thrainer
    CheckNodeOnline(self, instance.primary_node)
1473 5eacbcae Thomas Thrainer
    CheckInstanceState(self, instance, INSTANCE_NOT_RUNNING,
1474 5eacbcae Thomas Thrainer
                       msg="cannot rename")
1475 22b7f6f8 Thomas Thrainer
    self.instance = instance
1476 22b7f6f8 Thomas Thrainer
1477 22b7f6f8 Thomas Thrainer
    new_name = self.op.new_name
1478 22b7f6f8 Thomas Thrainer
    if self.op.name_check:
1479 22b7f6f8 Thomas Thrainer
      hostname = _CheckHostnameSane(self, new_name)
1480 22b7f6f8 Thomas Thrainer
      new_name = self.op.new_name = hostname.name
1481 22b7f6f8 Thomas Thrainer
      if (self.op.ip_check and
1482 22b7f6f8 Thomas Thrainer
          netutils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT)):
1483 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("IP %s of instance %s already in use" %
1484 22b7f6f8 Thomas Thrainer
                                   (hostname.ip, new_name),
1485 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
1486 22b7f6f8 Thomas Thrainer
1487 da4a52a3 Thomas Thrainer
    instance_names = [inst.name for
1488 da4a52a3 Thomas Thrainer
                      inst in self.cfg.GetAllInstancesInfo().values()]
1489 da4a52a3 Thomas Thrainer
    if new_name in instance_names and new_name != instance.name:
1490 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
1491 22b7f6f8 Thomas Thrainer
                                 new_name, errors.ECODE_EXISTS)
1492 22b7f6f8 Thomas Thrainer
1493 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1494 22b7f6f8 Thomas Thrainer
    """Rename the instance.
1495 22b7f6f8 Thomas Thrainer

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

1555 22b7f6f8 Thomas Thrainer
  """
1556 22b7f6f8 Thomas Thrainer
  HPATH = "instance-remove"
1557 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1558 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1559 22b7f6f8 Thomas Thrainer
1560 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1561 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1562 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
1563 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1564 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
1565 22b7f6f8 Thomas Thrainer
1566 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1567 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1568 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
1569 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1570 22b7f6f8 Thomas Thrainer
      # Copy node locks
1571 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1572 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1573 22b7f6f8 Thomas Thrainer
1574 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1575 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1576 22b7f6f8 Thomas Thrainer

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

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

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

1595 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1596 22b7f6f8 Thomas Thrainer

1597 22b7f6f8 Thomas Thrainer
    """
1598 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1599 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1600 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1601 22b7f6f8 Thomas Thrainer
1602 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1603 22b7f6f8 Thomas Thrainer
    """Remove the instance.
1604 22b7f6f8 Thomas Thrainer

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

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

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

1659 22b7f6f8 Thomas Thrainer
    """
1660 22b7f6f8 Thomas Thrainer
    env = {
1661 22b7f6f8 Thomas Thrainer
      "TARGET_NODE": self.op.target_node,
1662 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
1663 22b7f6f8 Thomas Thrainer
      }
1664 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
1665 22b7f6f8 Thomas Thrainer
    return env
1666 22b7f6f8 Thomas Thrainer
1667 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1668 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1669 22b7f6f8 Thomas Thrainer

1670 22b7f6f8 Thomas Thrainer
    """
1671 22b7f6f8 Thomas Thrainer
    nl = [
1672 22b7f6f8 Thomas Thrainer
      self.cfg.GetMasterNode(),
1673 22b7f6f8 Thomas Thrainer
      self.instance.primary_node,
1674 1c3231aa Thomas Thrainer
      self.op.target_node_uuid,
1675 22b7f6f8 Thomas Thrainer
      ]
1676 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1677 22b7f6f8 Thomas Thrainer
1678 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1679 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1680 22b7f6f8 Thomas Thrainer

1681 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1682 22b7f6f8 Thomas Thrainer

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

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

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

1833 22b7f6f8 Thomas Thrainer
  """
1834 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1835 22b7f6f8 Thomas Thrainer
1836 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1837 22b7f6f8 Thomas Thrainer
    """Check arguments.
1838 22b7f6f8 Thomas Thrainer

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

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

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

1951 22b7f6f8 Thomas Thrainer
    """
1952 22b7f6f8 Thomas Thrainer
    (allocatable, failed) = self.ia_result
1953 22b7f6f8 Thomas Thrainer
    return {
1954 22b7f6f8 Thomas Thrainer
      opcodes.OpInstanceMultiAlloc.ALLOCATABLE_KEY:
1955 22b7f6f8 Thomas Thrainer
        map(compat.fst, allocatable),
1956 22b7f6f8 Thomas Thrainer
      opcodes.OpInstanceMultiAlloc.FAILED_KEY: failed,
1957 22b7f6f8 Thomas Thrainer
      }
1958 22b7f6f8 Thomas Thrainer
1959 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1960 22b7f6f8 Thomas Thrainer
    """Executes the opcode.
1961 22b7f6f8 Thomas Thrainer

1962 22b7f6f8 Thomas Thrainer
    """
1963 22b7f6f8 Thomas Thrainer
    op2inst = dict((op.instance_name, op) for op in self.op.instances)
1964 22b7f6f8 Thomas Thrainer
    (allocatable, failed) = self.ia_result
1965 22b7f6f8 Thomas Thrainer
1966 22b7f6f8 Thomas Thrainer
    jobs = []
1967 1c3231aa Thomas Thrainer
    for (name, node_names) in allocatable:
1968 22b7f6f8 Thomas Thrainer
      op = op2inst.pop(name)
1969 22b7f6f8 Thomas Thrainer
1970 1c3231aa Thomas Thrainer
      (op.pnode_uuid, op.pnode) = \
1971 1c3231aa Thomas Thrainer
        ExpandNodeUuidAndName(self.cfg, None, node_names[0])
1972 1c3231aa Thomas Thrainer
      if len(node_names) > 1:
1973 1c3231aa Thomas Thrainer
        (op.snode_uuid, op.snode) = \
1974 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, None, node_names[1])
1975 22b7f6f8 Thomas Thrainer
1976 22b7f6f8 Thomas Thrainer
      jobs.append([op])
1977 22b7f6f8 Thomas Thrainer
1978 22b7f6f8 Thomas Thrainer
    missing = set(op2inst.keys()) - set(failed)
1979 22b7f6f8 Thomas Thrainer
    assert not missing, \
1980 22b7f6f8 Thomas Thrainer
      "Iallocator did return incomplete result: %s" % utils.CommaJoin(missing)
1981 22b7f6f8 Thomas Thrainer
1982 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs, **self._ConstructPartialResult())
1983 22b7f6f8 Thomas Thrainer
1984 22b7f6f8 Thomas Thrainer
1985 22b7f6f8 Thomas Thrainer
class _InstNicModPrivate:
1986 22b7f6f8 Thomas Thrainer
  """Data structure for network interface modifications.
1987 22b7f6f8 Thomas Thrainer

1988 22b7f6f8 Thomas Thrainer
  Used by L{LUInstanceSetParams}.
1989 22b7f6f8 Thomas Thrainer

1990 22b7f6f8 Thomas Thrainer
  """
1991 22b7f6f8 Thomas Thrainer
  def __init__(self):
1992 22b7f6f8 Thomas Thrainer
    self.params = None
1993 22b7f6f8 Thomas Thrainer
    self.filled = None
1994 22b7f6f8 Thomas Thrainer
1995 22b7f6f8 Thomas Thrainer
1996 5eacbcae Thomas Thrainer
def _PrepareContainerMods(mods, private_fn):
1997 22b7f6f8 Thomas Thrainer
  """Prepares a list of container modifications by adding a private data field.
1998 22b7f6f8 Thomas Thrainer

1999 22b7f6f8 Thomas Thrainer
  @type mods: list of tuples; (operation, index, parameters)
2000 22b7f6f8 Thomas Thrainer
  @param mods: List of modifications
2001 22b7f6f8 Thomas Thrainer
  @type private_fn: callable or None
2002 22b7f6f8 Thomas Thrainer
  @param private_fn: Callable for constructing a private data field for a
2003 22b7f6f8 Thomas Thrainer
    modification
2004 22b7f6f8 Thomas Thrainer
  @rtype: list
2005 22b7f6f8 Thomas Thrainer

2006 22b7f6f8 Thomas Thrainer
  """
2007 22b7f6f8 Thomas Thrainer
  if private_fn is None:
2008 22b7f6f8 Thomas Thrainer
    fn = lambda: None
2009 22b7f6f8 Thomas Thrainer
  else:
2010 22b7f6f8 Thomas Thrainer
    fn = private_fn
2011 22b7f6f8 Thomas Thrainer
2012 22b7f6f8 Thomas Thrainer
  return [(op, idx, params, fn()) for (op, idx, params) in mods]
2013 22b7f6f8 Thomas Thrainer
2014 22b7f6f8 Thomas Thrainer
2015 1c3231aa Thomas Thrainer
def _CheckNodesPhysicalCPUs(lu, node_uuids, requested, hypervisor_specs):
2016 22b7f6f8 Thomas Thrainer
  """Checks if nodes have enough physical CPUs
2017 22b7f6f8 Thomas Thrainer

2018 22b7f6f8 Thomas Thrainer
  This function checks if all given nodes have the needed number of
2019 22b7f6f8 Thomas Thrainer
  physical CPUs. In case any node has less CPUs or we cannot get the
2020 22b7f6f8 Thomas Thrainer
  information from the node, this function raises an OpPrereqError
2021 22b7f6f8 Thomas Thrainer
  exception.
2022 22b7f6f8 Thomas Thrainer

2023 22b7f6f8 Thomas Thrainer
  @type lu: C{LogicalUnit}
2024 22b7f6f8 Thomas Thrainer
  @param lu: a logical unit from which we get configuration data
2025 1c3231aa Thomas Thrainer
  @type node_uuids: C{list}
2026 1c3231aa Thomas Thrainer
  @param node_uuids: the list of node UUIDs to check
2027 22b7f6f8 Thomas Thrainer
  @type requested: C{int}
2028 22b7f6f8 Thomas Thrainer
  @param requested: the minimum acceptable number of physical CPUs
2029 a295eb80 Helga Velroyen
  @type hypervisor_specs: list of pairs (string, dict of strings)
2030 a295eb80 Helga Velroyen
  @param hypervisor_specs: list of hypervisor specifications in
2031 a295eb80 Helga Velroyen
      pairs (hypervisor_name, hvparams)
2032 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node doesn't have enough CPUs,
2033 22b7f6f8 Thomas Thrainer
      or we cannot check the node
2034 22b7f6f8 Thomas Thrainer

2035 22b7f6f8 Thomas Thrainer
  """
2036 da803ff1 Helga Velroyen
  nodeinfo = lu.rpc.call_node_info(node_uuids, None, hypervisor_specs)
2037 1c3231aa Thomas Thrainer
  for node_uuid in node_uuids:
2038 1c3231aa Thomas Thrainer
    info = nodeinfo[node_uuid]
2039 1c3231aa Thomas Thrainer
    node_name = lu.cfg.GetNodeName(node_uuid)
2040 1c3231aa Thomas Thrainer
    info.Raise("Cannot get current information from node %s" % node_name,
2041 22b7f6f8 Thomas Thrainer
               prereq=True, ecode=errors.ECODE_ENVIRON)
2042 22b7f6f8 Thomas Thrainer
    (_, _, (hv_info, )) = info.payload
2043 22b7f6f8 Thomas Thrainer
    num_cpus = hv_info.get("cpu_total", None)
2044 22b7f6f8 Thomas Thrainer
    if not isinstance(num_cpus, int):
2045 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute the number of physical CPUs"
2046 22b7f6f8 Thomas Thrainer
                                 " on node %s, result was '%s'" %
2047 1c3231aa Thomas Thrainer
                                 (node_name, num_cpus), errors.ECODE_ENVIRON)
2048 22b7f6f8 Thomas Thrainer
    if requested > num_cpus:
2049 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Node %s has %s physical CPUs, but %s are "
2050 1c3231aa Thomas Thrainer
                                 "required" % (node_name, num_cpus, requested),
2051 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_NORES)
2052 22b7f6f8 Thomas Thrainer
2053 22b7f6f8 Thomas Thrainer
2054 22b7f6f8 Thomas Thrainer
def GetItemFromContainer(identifier, kind, container):
2055 22b7f6f8 Thomas Thrainer
  """Return the item refered by the identifier.
2056 22b7f6f8 Thomas Thrainer

2057 22b7f6f8 Thomas Thrainer
  @type identifier: string
2058 22b7f6f8 Thomas Thrainer
  @param identifier: Item index or name or UUID
2059 22b7f6f8 Thomas Thrainer
  @type kind: string
2060 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2061 22b7f6f8 Thomas Thrainer
  @type container: list
2062 22b7f6f8 Thomas Thrainer
  @param container: Container to get the item from
2063 22b7f6f8 Thomas Thrainer

2064 22b7f6f8 Thomas Thrainer
  """
2065 22b7f6f8 Thomas Thrainer
  # Index
2066 22b7f6f8 Thomas Thrainer
  try:
2067 22b7f6f8 Thomas Thrainer
    idx = int(identifier)
2068 22b7f6f8 Thomas Thrainer
    if idx == -1:
2069 22b7f6f8 Thomas Thrainer
      # Append
2070 22b7f6f8 Thomas Thrainer
      absidx = len(container) - 1
2071 22b7f6f8 Thomas Thrainer
    elif idx < 0:
2072 22b7f6f8 Thomas Thrainer
      raise IndexError("Not accepting negative indices other than -1")
2073 22b7f6f8 Thomas Thrainer
    elif idx > len(container):
2074 22b7f6f8 Thomas Thrainer
      raise IndexError("Got %s index %s, but there are only %s" %
2075 22b7f6f8 Thomas Thrainer
                       (kind, idx, len(container)))
2076 22b7f6f8 Thomas Thrainer
    else:
2077 22b7f6f8 Thomas Thrainer
      absidx = idx
2078 22b7f6f8 Thomas Thrainer
    return (absidx, container[idx])
2079 22b7f6f8 Thomas Thrainer
  except ValueError:
2080 22b7f6f8 Thomas Thrainer
    pass
2081 22b7f6f8 Thomas Thrainer
2082 22b7f6f8 Thomas Thrainer
  for idx, item in enumerate(container):
2083 22b7f6f8 Thomas Thrainer
    if item.uuid == identifier or item.name == identifier:
2084 22b7f6f8 Thomas Thrainer
      return (idx, item)
2085 22b7f6f8 Thomas Thrainer
2086 22b7f6f8 Thomas Thrainer
  raise errors.OpPrereqError("Cannot find %s with identifier %s" %
2087 22b7f6f8 Thomas Thrainer
                             (kind, identifier), errors.ECODE_NOENT)
2088 22b7f6f8 Thomas Thrainer
2089 22b7f6f8 Thomas Thrainer
2090 5eacbcae Thomas Thrainer
def _ApplyContainerMods(kind, container, chgdesc, mods,
2091 5eacbcae Thomas Thrainer
                        create_fn, modify_fn, remove_fn):
2092 22b7f6f8 Thomas Thrainer
  """Applies descriptions in C{mods} to C{container}.
2093 22b7f6f8 Thomas Thrainer

2094 22b7f6f8 Thomas Thrainer
  @type kind: string
2095 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2096 22b7f6f8 Thomas Thrainer
  @type container: list
2097 22b7f6f8 Thomas Thrainer
  @param container: Container to modify
2098 22b7f6f8 Thomas Thrainer
  @type chgdesc: None or list
2099 22b7f6f8 Thomas Thrainer
  @param chgdesc: List of applied changes
2100 22b7f6f8 Thomas Thrainer
  @type mods: list
2101 5eacbcae Thomas Thrainer
  @param mods: Modifications as returned by L{_PrepareContainerMods}
2102 22b7f6f8 Thomas Thrainer
  @type create_fn: callable
2103 22b7f6f8 Thomas Thrainer
  @param create_fn: Callback for creating a new item (L{constants.DDM_ADD});
2104 22b7f6f8 Thomas Thrainer
    receives absolute item index, parameters and private data object as added
2105 5eacbcae Thomas Thrainer
    by L{_PrepareContainerMods}, returns tuple containing new item and changes
2106 22b7f6f8 Thomas Thrainer
    as list
2107 22b7f6f8 Thomas Thrainer
  @type modify_fn: callable
2108 22b7f6f8 Thomas Thrainer
  @param modify_fn: Callback for modifying an existing item
2109 22b7f6f8 Thomas Thrainer
    (L{constants.DDM_MODIFY}); receives absolute item index, item, parameters
2110 5eacbcae Thomas Thrainer
    and private data object as added by L{_PrepareContainerMods}, returns
2111 22b7f6f8 Thomas Thrainer
    changes as list
2112 22b7f6f8 Thomas Thrainer
  @type remove_fn: callable
2113 22b7f6f8 Thomas Thrainer
  @param remove_fn: Callback on removing item; receives absolute item index,
2114 5eacbcae Thomas Thrainer
    item and private data object as added by L{_PrepareContainerMods}
2115 22b7f6f8 Thomas Thrainer

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

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

2182 22b7f6f8 Thomas Thrainer
  """
2183 22b7f6f8 Thomas Thrainer
  for (idx, disk) in enumerate(disks):
2184 22b7f6f8 Thomas Thrainer
    disk.iv_name = "disk/%s" % (base_index + idx, )
2185 22b7f6f8 Thomas Thrainer
2186 22b7f6f8 Thomas Thrainer
2187 22b7f6f8 Thomas Thrainer
class LUInstanceSetParams(LogicalUnit):
2188 22b7f6f8 Thomas Thrainer
  """Modifies an instances's parameters.
2189 22b7f6f8 Thomas Thrainer

2190 22b7f6f8 Thomas Thrainer
  """
2191 22b7f6f8 Thomas Thrainer
  HPATH = "instance-modify"
2192 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2193 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2194 22b7f6f8 Thomas Thrainer
2195 22b7f6f8 Thomas Thrainer
  @staticmethod
2196 22b7f6f8 Thomas Thrainer
  def _UpgradeDiskNicMods(kind, mods, verify_fn):
2197 22b7f6f8 Thomas Thrainer
    assert ht.TList(mods)
2198 22b7f6f8 Thomas Thrainer
    assert not mods or len(mods[0]) in (2, 3)
2199 22b7f6f8 Thomas Thrainer
2200 22b7f6f8 Thomas Thrainer
    if mods and len(mods[0]) == 2:
2201 22b7f6f8 Thomas Thrainer
      result = []
2202 22b7f6f8 Thomas Thrainer
2203 22b7f6f8 Thomas Thrainer
      addremove = 0
2204 22b7f6f8 Thomas Thrainer
      for op, params in mods:
2205 22b7f6f8 Thomas Thrainer
        if op in (constants.DDM_ADD, constants.DDM_REMOVE):
2206 22b7f6f8 Thomas Thrainer
          result.append((op, -1, params))
2207 22b7f6f8 Thomas Thrainer
          addremove += 1
2208 22b7f6f8 Thomas Thrainer
2209 22b7f6f8 Thomas Thrainer
          if addremove > 1:
2210 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Only one %s add or remove operation is"
2211 22b7f6f8 Thomas Thrainer
                                       " supported at a time" % kind,
2212 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
2213 22b7f6f8 Thomas Thrainer
        else:
2214 22b7f6f8 Thomas Thrainer
          result.append((constants.DDM_MODIFY, op, params))
2215 22b7f6f8 Thomas Thrainer
2216 22b7f6f8 Thomas Thrainer
      assert verify_fn(result)
2217 22b7f6f8 Thomas Thrainer
    else:
2218 22b7f6f8 Thomas Thrainer
      result = mods
2219 22b7f6f8 Thomas Thrainer
2220 22b7f6f8 Thomas Thrainer
    return result
2221 22b7f6f8 Thomas Thrainer
2222 22b7f6f8 Thomas Thrainer
  @staticmethod
2223 22b7f6f8 Thomas Thrainer
  def _CheckMods(kind, mods, key_types, item_fn):
2224 22b7f6f8 Thomas Thrainer
    """Ensures requested disk/NIC modifications are valid.
2225 22b7f6f8 Thomas Thrainer

2226 22b7f6f8 Thomas Thrainer
    """
2227 22b7f6f8 Thomas Thrainer
    for (op, _, params) in mods:
2228 22b7f6f8 Thomas Thrainer
      assert ht.TDict(params)
2229 22b7f6f8 Thomas Thrainer
2230 22b7f6f8 Thomas Thrainer
      # If 'key_types' is an empty dict, we assume we have an
2231 22b7f6f8 Thomas Thrainer
      # 'ext' template and thus do not ForceDictType
2232 22b7f6f8 Thomas Thrainer
      if key_types:
2233 22b7f6f8 Thomas Thrainer
        utils.ForceDictType(params, key_types)
2234 22b7f6f8 Thomas Thrainer
2235 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2236 22b7f6f8 Thomas Thrainer
        if params:
2237 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("No settings should be passed when"
2238 22b7f6f8 Thomas Thrainer
                                     " removing a %s" % kind,
2239 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2240 22b7f6f8 Thomas Thrainer
      elif op in (constants.DDM_ADD, constants.DDM_MODIFY):
2241 22b7f6f8 Thomas Thrainer
        item_fn(op, params)
2242 22b7f6f8 Thomas Thrainer
      else:
2243 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
2244 22b7f6f8 Thomas Thrainer
2245 22b7f6f8 Thomas Thrainer
  @staticmethod
2246 3f3ea14c Bernardo Dal Seno
  def _VerifyDiskModification(op, params, excl_stor):
2247 22b7f6f8 Thomas Thrainer
    """Verifies a disk modification.
2248 22b7f6f8 Thomas Thrainer

2249 22b7f6f8 Thomas Thrainer
    """
2250 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2251 22b7f6f8 Thomas Thrainer
      mode = params.setdefault(constants.IDISK_MODE, constants.DISK_RDWR)
2252 22b7f6f8 Thomas Thrainer
      if mode not in constants.DISK_ACCESS_SET:
2253 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk access mode '%s'" % mode,
2254 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2255 22b7f6f8 Thomas Thrainer
2256 22b7f6f8 Thomas Thrainer
      size = params.get(constants.IDISK_SIZE, None)
2257 22b7f6f8 Thomas Thrainer
      if size is None:
2258 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Required disk parameter '%s' missing" %
2259 22b7f6f8 Thomas Thrainer
                                   constants.IDISK_SIZE, errors.ECODE_INVAL)
2260 22b7f6f8 Thomas Thrainer
2261 22b7f6f8 Thomas Thrainer
      try:
2262 22b7f6f8 Thomas Thrainer
        size = int(size)
2263 22b7f6f8 Thomas Thrainer
      except (TypeError, ValueError), err:
2264 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk size parameter: %s" % err,
2265 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2266 22b7f6f8 Thomas Thrainer
2267 22b7f6f8 Thomas Thrainer
      params[constants.IDISK_SIZE] = size
2268 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2269 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2270 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2271 22b7f6f8 Thomas Thrainer
2272 7c848a6a Bernardo Dal Seno
      CheckSpindlesExclusiveStorage(params, excl_stor, True)
2273 3f3ea14c Bernardo Dal Seno
2274 22b7f6f8 Thomas Thrainer
    elif op == constants.DDM_MODIFY:
2275 22b7f6f8 Thomas Thrainer
      if constants.IDISK_SIZE in params:
2276 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk size change not possible, use"
2277 22b7f6f8 Thomas Thrainer
                                   " grow-disk", errors.ECODE_INVAL)
2278 22b7f6f8 Thomas Thrainer
      if len(params) > 2:
2279 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk modification doesn't support"
2280 22b7f6f8 Thomas Thrainer
                                   " additional arbitrary parameters",
2281 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2282 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2283 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2284 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2285 22b7f6f8 Thomas Thrainer
2286 22b7f6f8 Thomas Thrainer
  @staticmethod
2287 22b7f6f8 Thomas Thrainer
  def _VerifyNicModification(op, params):
2288 22b7f6f8 Thomas Thrainer
    """Verifies a network interface modification.
2289 22b7f6f8 Thomas Thrainer

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

2406 22b7f6f8 Thomas Thrainer
    This runs on the master, primary and secondaries.
2407 22b7f6f8 Thomas Thrainer

2408 22b7f6f8 Thomas Thrainer
    """
2409 22b7f6f8 Thomas Thrainer
    args = {}
2410 22b7f6f8 Thomas Thrainer
    if constants.BE_MINMEM in self.be_new:
2411 22b7f6f8 Thomas Thrainer
      args["minmem"] = self.be_new[constants.BE_MINMEM]
2412 22b7f6f8 Thomas Thrainer
    if constants.BE_MAXMEM in self.be_new:
2413 22b7f6f8 Thomas Thrainer
      args["maxmem"] = self.be_new[constants.BE_MAXMEM]
2414 22b7f6f8 Thomas Thrainer
    if constants.BE_VCPUS in self.be_new:
2415 22b7f6f8 Thomas Thrainer
      args["vcpus"] = self.be_new[constants.BE_VCPUS]
2416 22b7f6f8 Thomas Thrainer
    # TODO: export disk changes. Note: _BuildInstanceHookEnv* don't export disk
2417 22b7f6f8 Thomas Thrainer
    # information at all.
2418 22b7f6f8 Thomas Thrainer
2419 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
2420 22b7f6f8 Thomas Thrainer
      nics = []
2421 22b7f6f8 Thomas Thrainer
2422 22b7f6f8 Thomas Thrainer
      for nic in self._new_nics:
2423 22b7f6f8 Thomas Thrainer
        n = copy.deepcopy(nic)
2424 22b7f6f8 Thomas Thrainer
        nicparams = self.cluster.SimpleFillNIC(n.nicparams)
2425 22b7f6f8 Thomas Thrainer
        n.nicparams = nicparams
2426 5eacbcae Thomas Thrainer
        nics.append(NICToTuple(self, n))
2427 22b7f6f8 Thomas Thrainer
2428 22b7f6f8 Thomas Thrainer
      args["nics"] = nics
2429 22b7f6f8 Thomas Thrainer
2430 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance, override=args)
2431 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
2432 22b7f6f8 Thomas Thrainer
      env["NEW_DISK_TEMPLATE"] = self.op.disk_template
2433 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
2434 22b7f6f8 Thomas Thrainer
      env["RUNTIME_MEMORY"] = self.op.runtime_mem
2435 22b7f6f8 Thomas Thrainer
2436 22b7f6f8 Thomas Thrainer
    return env
2437 22b7f6f8 Thomas Thrainer
2438 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2439 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2440 22b7f6f8 Thomas Thrainer

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

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

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

2724 1bb99a33 Bernardo Dal Seno
    This only checks the instance list against the existing names.
2725 1bb99a33 Bernardo Dal Seno

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

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

3110 22b7f6f8 Thomas Thrainer
    """
3111 d0d7d7cf Thomas Thrainer
    assert len(self.instance.secondary_nodes) == 1
3112 d0d7d7cf Thomas Thrainer
    assert self.instance.disk_template == constants.DT_DRBD8
3113 22b7f6f8 Thomas Thrainer
3114 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3115 d0d7d7cf Thomas Thrainer
    snode_uuid = self.instance.secondary_nodes[0]
3116 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to plain")
3117 22b7f6f8 Thomas Thrainer
3118 d0d7d7cf Thomas Thrainer
    old_disks = AnnotateDiskParams(self.instance, self.instance.disks, self.cfg)
3119 d0d7d7cf Thomas Thrainer
    new_disks = [d.children[0] for d in self.instance.disks]
3120 22b7f6f8 Thomas Thrainer
3121 22b7f6f8 Thomas Thrainer
    # copy over size, mode and name
3122 22b7f6f8 Thomas Thrainer
    for parent, child in zip(old_disks, new_disks):
3123 22b7f6f8 Thomas Thrainer
      child.size = parent.size
3124 22b7f6f8 Thomas Thrainer
      child.mode = parent.mode
3125 22b7f6f8 Thomas Thrainer
      child.name = parent.name
3126 22b7f6f8 Thomas Thrainer
3127 22b7f6f8 Thomas Thrainer
    # this is a DRBD disk, return its port to the pool
3128 22b7f6f8 Thomas Thrainer
    # NOTE: this must be done right before the call to cfg.Update!
3129 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3130 22b7f6f8 Thomas Thrainer
      tcp_port = disk.logical_id[2]
3131 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(tcp_port)
3132 22b7f6f8 Thomas Thrainer
3133 22b7f6f8 Thomas Thrainer
    # update instance structure
3134 d0d7d7cf Thomas Thrainer
    self.instance.disks = new_disks
3135 d0d7d7cf Thomas Thrainer
    self.instance.disk_template = constants.DT_PLAIN
3136 d0d7d7cf Thomas Thrainer
    _UpdateIvNames(0, self.instance.disks)
3137 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
3138 22b7f6f8 Thomas Thrainer
3139 22b7f6f8 Thomas Thrainer
    # Release locks in case removing disks takes a while
3140 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3141 22b7f6f8 Thomas Thrainer
3142 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing volumes on the secondary node...")
3143 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3144 1c3231aa Thomas Thrainer
      self.cfg.SetDiskID(disk, snode_uuid)
3145 1c3231aa Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(snode_uuid, disk).fail_msg
3146 22b7f6f8 Thomas Thrainer
      if msg:
3147 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove block device %s on node %s,"
3148 1c3231aa Thomas Thrainer
                        " continuing anyway: %s", disk.iv_name,
3149 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(snode_uuid), msg)
3150 22b7f6f8 Thomas Thrainer
3151 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing unneeded volumes on the primary node...")
3152 22b7f6f8 Thomas Thrainer
    for idx, disk in enumerate(old_disks):
3153 22b7f6f8 Thomas Thrainer
      meta = disk.children[1]
3154 1c3231aa Thomas Thrainer
      self.cfg.SetDiskID(meta, pnode_uuid)
3155 1c3231aa Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(pnode_uuid, meta).fail_msg
3156 22b7f6f8 Thomas Thrainer
      if msg:
3157 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove metadata for disk %d on node %s,"
3158 1c3231aa Thomas Thrainer
                        " continuing anyway: %s", idx,
3159 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(pnode_uuid), msg)
3160 22b7f6f8 Thomas Thrainer
3161 22b7f6f8 Thomas Thrainer
  def _CreateNewDisk(self, idx, params, _):
3162 22b7f6f8 Thomas Thrainer
    """Creates a new disk.
3163 22b7f6f8 Thomas Thrainer

3164 22b7f6f8 Thomas Thrainer
    """
3165 22b7f6f8 Thomas Thrainer
    # add a new disk
3166 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DTS_FILEBASED:
3167 d0d7d7cf Thomas Thrainer
      (file_driver, file_path) = self.instance.disks[0].logical_id
3168 22b7f6f8 Thomas Thrainer
      file_path = os.path.dirname(file_path)
3169 22b7f6f8 Thomas Thrainer
    else:
3170 22b7f6f8 Thomas Thrainer
      file_driver = file_path = None
3171 22b7f6f8 Thomas Thrainer
3172 22b7f6f8 Thomas Thrainer
    disk = \
3173 d0d7d7cf Thomas Thrainer
      GenerateDiskTemplate(self, self.instance.disk_template,
3174 da4a52a3 Thomas Thrainer
                           self.instance.uuid, self.instance.primary_node,
3175 d0d7d7cf Thomas Thrainer
                           self.instance.secondary_nodes, [params], file_path,
3176 d0d7d7cf Thomas Thrainer
                           file_driver, idx, self.Log, self.diskparams)[0]
3177 22b7f6f8 Thomas Thrainer
3178 d0d7d7cf Thomas Thrainer
    new_disks = CreateDisks(self, self.instance, disks=[disk])
3179 22b7f6f8 Thomas Thrainer
3180 22b7f6f8 Thomas Thrainer
    if self.cluster.prealloc_wipe_disks:
3181 22b7f6f8 Thomas Thrainer
      # Wipe new disk
3182 d0d7d7cf Thomas Thrainer
      WipeOrCleanupDisks(self, self.instance,
3183 a365b47f Bernardo Dal Seno
                         disks=[(idx, disk, 0)],
3184 a365b47f Bernardo Dal Seno
                         cleanup=new_disks)
3185 22b7f6f8 Thomas Thrainer
3186 22b7f6f8 Thomas Thrainer
    return (disk, [
3187 22b7f6f8 Thomas Thrainer
      ("disk/%d" % idx, "add:size=%s,mode=%s" % (disk.size, disk.mode)),
3188 22b7f6f8 Thomas Thrainer
      ])
3189 22b7f6f8 Thomas Thrainer
3190 22b7f6f8 Thomas Thrainer
  @staticmethod
3191 22b7f6f8 Thomas Thrainer
  def _ModifyDisk(idx, disk, params, _):
3192 22b7f6f8 Thomas Thrainer
    """Modifies a disk.
3193 22b7f6f8 Thomas Thrainer

3194 22b7f6f8 Thomas Thrainer
    """
3195 22b7f6f8 Thomas Thrainer
    changes = []
3196 22b7f6f8 Thomas Thrainer
    mode = params.get(constants.IDISK_MODE, None)
3197 22b7f6f8 Thomas Thrainer
    if mode:
3198 22b7f6f8 Thomas Thrainer
      disk.mode = mode
3199 22b7f6f8 Thomas Thrainer
      changes.append(("disk.mode/%d" % idx, disk.mode))
3200 22b7f6f8 Thomas Thrainer
3201 22b7f6f8 Thomas Thrainer
    name = params.get(constants.IDISK_NAME, None)
3202 22b7f6f8 Thomas Thrainer
    disk.name = name
3203 22b7f6f8 Thomas Thrainer
    changes.append(("disk.name/%d" % idx, disk.name))
3204 22b7f6f8 Thomas Thrainer
3205 22b7f6f8 Thomas Thrainer
    return changes
3206 22b7f6f8 Thomas Thrainer
3207 22b7f6f8 Thomas Thrainer
  def _RemoveDisk(self, idx, root, _):
3208 22b7f6f8 Thomas Thrainer
    """Removes a disk.
3209 22b7f6f8 Thomas Thrainer

3210 22b7f6f8 Thomas Thrainer
    """
3211 5eacbcae Thomas Thrainer
    (anno_disk,) = AnnotateDiskParams(self.instance, [root], self.cfg)
3212 1c3231aa Thomas Thrainer
    for node_uuid, disk in anno_disk.ComputeNodeTree(
3213 1c3231aa Thomas Thrainer
                             self.instance.primary_node):
3214 1c3231aa Thomas Thrainer
      self.cfg.SetDiskID(disk, node_uuid)
3215 1c3231aa Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(node_uuid, disk).fail_msg
3216 22b7f6f8 Thomas Thrainer
      if msg:
3217 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove disk/%d on node '%s': %s,"
3218 1c3231aa Thomas Thrainer
                        " continuing anyway", idx,
3219 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
3220 22b7f6f8 Thomas Thrainer
3221 22b7f6f8 Thomas Thrainer
    # if this is a DRBD disk, return its port to the pool
3222 22b7f6f8 Thomas Thrainer
    if root.dev_type in constants.LDS_DRBD:
3223 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(root.logical_id[2])
3224 22b7f6f8 Thomas Thrainer
3225 22b7f6f8 Thomas Thrainer
  def _CreateNewNic(self, idx, params, private):
3226 22b7f6f8 Thomas Thrainer
    """Creates data structure for a new network interface.
3227 22b7f6f8 Thomas Thrainer

3228 22b7f6f8 Thomas Thrainer
    """
3229 22b7f6f8 Thomas Thrainer
    mac = params[constants.INIC_MAC]
3230 22b7f6f8 Thomas Thrainer
    ip = params.get(constants.INIC_IP, None)
3231 22b7f6f8 Thomas Thrainer
    net = params.get(constants.INIC_NETWORK, None)
3232 22b7f6f8 Thomas Thrainer
    name = params.get(constants.INIC_NAME, None)
3233 22b7f6f8 Thomas Thrainer
    net_uuid = self.cfg.LookupNetwork(net)
3234 22b7f6f8 Thomas Thrainer
    #TODO: not private.filled?? can a nic have no nicparams??
3235 22b7f6f8 Thomas Thrainer
    nicparams = private.filled
3236 22b7f6f8 Thomas Thrainer
    nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, name=name,
3237 22b7f6f8 Thomas Thrainer
                       nicparams=nicparams)
3238 22b7f6f8 Thomas Thrainer
    nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
3239 22b7f6f8 Thomas Thrainer
3240 22b7f6f8 Thomas Thrainer
    return (nobj, [
3241 22b7f6f8 Thomas Thrainer
      ("nic.%d" % idx,
3242 22b7f6f8 Thomas Thrainer
       "add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" %
3243 22b7f6f8 Thomas Thrainer
       (mac, ip, private.filled[constants.NIC_MODE],
3244 22b7f6f8 Thomas Thrainer
       private.filled[constants.NIC_LINK],
3245 22b7f6f8 Thomas Thrainer
       net)),
3246 22b7f6f8 Thomas Thrainer
      ])
3247 22b7f6f8 Thomas Thrainer
3248 22b7f6f8 Thomas Thrainer
  def _ApplyNicMods(self, idx, nic, params, private):
3249 22b7f6f8 Thomas Thrainer
    """Modifies a network interface.
3250 22b7f6f8 Thomas Thrainer

3251 22b7f6f8 Thomas Thrainer
    """
3252 22b7f6f8 Thomas Thrainer
    changes = []
3253 22b7f6f8 Thomas Thrainer
3254 22b7f6f8 Thomas Thrainer
    for key in [constants.INIC_MAC, constants.INIC_IP, constants.INIC_NAME]:
3255 22b7f6f8 Thomas Thrainer
      if key in params:
3256 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), params[key]))
3257 22b7f6f8 Thomas Thrainer
        setattr(nic, key, params[key])
3258 22b7f6f8 Thomas Thrainer
3259 22b7f6f8 Thomas Thrainer
    new_net = params.get(constants.INIC_NETWORK, nic.network)
3260 22b7f6f8 Thomas Thrainer
    new_net_uuid = self.cfg.LookupNetwork(new_net)
3261 22b7f6f8 Thomas Thrainer
    if new_net_uuid != nic.network:
3262 22b7f6f8 Thomas Thrainer
      changes.append(("nic.network/%d" % idx, new_net))
3263 22b7f6f8 Thomas Thrainer
      nic.network = new_net_uuid
3264 22b7f6f8 Thomas Thrainer
3265 22b7f6f8 Thomas Thrainer
    if private.filled:
3266 22b7f6f8 Thomas Thrainer
      nic.nicparams = private.filled
3267 22b7f6f8 Thomas Thrainer
3268 22b7f6f8 Thomas Thrainer
      for (key, val) in nic.nicparams.items():
3269 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), val))
3270 22b7f6f8 Thomas Thrainer
3271 22b7f6f8 Thomas Thrainer
    return changes
3272 22b7f6f8 Thomas Thrainer
3273 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3274 22b7f6f8 Thomas Thrainer
    """Modifies an instance.
3275 22b7f6f8 Thomas Thrainer

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

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

3494 22b7f6f8 Thomas Thrainer
    """
3495 22b7f6f8 Thomas Thrainer
    assert self.target_uuids
3496 22b7f6f8 Thomas Thrainer
3497 22b7f6f8 Thomas Thrainer
    env = {
3498 22b7f6f8 Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
3499 22b7f6f8 Thomas Thrainer
      }
3500 22b7f6f8 Thomas Thrainer
3501 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
3502 22b7f6f8 Thomas Thrainer
3503 22b7f6f8 Thomas Thrainer
    return env
3504 22b7f6f8 Thomas Thrainer
3505 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
3506 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
3507 22b7f6f8 Thomas Thrainer

3508 22b7f6f8 Thomas Thrainer
    """
3509 22b7f6f8 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
3510 22b7f6f8 Thomas Thrainer
    return ([mn], [mn])
3511 22b7f6f8 Thomas Thrainer
3512 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3513 22b7f6f8 Thomas Thrainer
    instances = list(self.owned_locks(locking.LEVEL_INSTANCE))
3514 22b7f6f8 Thomas Thrainer
3515 22b7f6f8 Thomas Thrainer
    assert instances == [self.op.instance_name], "Instance not locked"
3516 22b7f6f8 Thomas Thrainer
3517 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=instances,
3518 22b7f6f8 Thomas Thrainer
                                      target_groups=list(self.target_uuids))
3519 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
3520 22b7f6f8 Thomas Thrainer
3521 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
3522 22b7f6f8 Thomas Thrainer
3523 22b7f6f8 Thomas Thrainer
    if not ial.success:
3524 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute solution for changing group of"
3525 22b7f6f8 Thomas Thrainer
                                 " instance '%s' using iallocator '%s': %s" %
3526 22b7f6f8 Thomas Thrainer
                                 (self.op.instance_name, self.op.iallocator,
3527 22b7f6f8 Thomas Thrainer
                                  ial.info), errors.ECODE_NORES)
3528 22b7f6f8 Thomas Thrainer
3529 5eacbcae Thomas Thrainer
    jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
3530 22b7f6f8 Thomas Thrainer
3531 22b7f6f8 Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for changing group of"
3532 22b7f6f8 Thomas Thrainer
                 " instance '%s'", len(jobs), self.op.instance_name)
3533 22b7f6f8 Thomas Thrainer
3534 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs)