Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance.py @ 37046199

History | View | Annotate | Download (145.7 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 pathutils
40 22b7f6f8 Thomas Thrainer
from ganeti import rpc
41 22b7f6f8 Thomas Thrainer
from ganeti import utils
42 22b7f6f8 Thomas Thrainer
43 8aa8f6b1 Thomas Thrainer
from ganeti.cmdlib.base import NoHooksLU, LogicalUnit, ResultWithJobs
44 22b7f6f8 Thomas Thrainer
45 13f6af81 Thomas Thrainer
from ganeti.cmdlib.common import INSTANCE_DOWN, \
46 5eacbcae Thomas Thrainer
  INSTANCE_NOT_RUNNING, CAN_CHANGE_INSTANCE_OFFLINE, CheckNodeOnline, \
47 5eacbcae Thomas Thrainer
  ShareAll, GetDefaultIAllocator, CheckInstanceNodeGroups, \
48 5eacbcae Thomas Thrainer
  LoadNodeEvacResult, CheckIAllocatorOrNode, CheckParamsNotGlobal, \
49 5eacbcae Thomas Thrainer
  IsExclusiveStorageEnabledNode, CheckHVParams, CheckOSParams, \
50 da4a52a3 Thomas Thrainer
  AnnotateDiskParams, GetUpdatedParams, ExpandInstanceUuidAndName, \
51 1f7c8208 Helga Velroyen
  ComputeIPolicySpecViolation, CheckInstanceState, ExpandNodeUuidAndName, \
52 294254b1 Raffa Santi
  CheckDiskTemplateEnabled, IsValidDiskAccessModeCombination
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 9f7d5fe4 Sebastian Gebhard
    vlan = nic.get(constants.INIC_VLAN, None)
177 22b7f6f8 Thomas Thrainer
178 22b7f6f8 Thomas Thrainer
    if net is None or net.lower() == constants.VALUE_NONE:
179 22b7f6f8 Thomas Thrainer
      net = None
180 22b7f6f8 Thomas Thrainer
    else:
181 22b7f6f8 Thomas Thrainer
      if nic_mode_req is not None or link is not None:
182 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("If network is given, no mode or link"
183 22b7f6f8 Thomas Thrainer
                                   " is allowed to be passed",
184 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
185 22b7f6f8 Thomas Thrainer
186 9f7d5fe4 Sebastian Gebhard
    if vlan is not None and nic_mode != constants.NIC_MODE_OVS:
187 9f7d5fe4 Sebastian Gebhard
      raise errors.OpPrereqError("VLAN is given, but network mode is not"
188 9f7d5fe4 Sebastian Gebhard
                                 " openvswitch", errors.ECODE_INVAL)
189 9f7d5fe4 Sebastian Gebhard
190 22b7f6f8 Thomas Thrainer
    # ip validity checks
191 22b7f6f8 Thomas Thrainer
    if ip is None or ip.lower() == constants.VALUE_NONE:
192 22b7f6f8 Thomas Thrainer
      nic_ip = None
193 22b7f6f8 Thomas Thrainer
    elif ip.lower() == constants.VALUE_AUTO:
194 22b7f6f8 Thomas Thrainer
      if not op.name_check:
195 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("IP address set to auto but name checks"
196 22b7f6f8 Thomas Thrainer
                                   " have been skipped",
197 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
198 22b7f6f8 Thomas Thrainer
      nic_ip = default_ip
199 22b7f6f8 Thomas Thrainer
    else:
200 22b7f6f8 Thomas Thrainer
      # We defer pool operations until later, so that the iallocator has
201 22b7f6f8 Thomas Thrainer
      # filled in the instance's node(s) dimara
202 22b7f6f8 Thomas Thrainer
      if ip.lower() == constants.NIC_IP_POOL:
203 22b7f6f8 Thomas Thrainer
        if net is None:
204 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("if ip=pool, parameter network"
205 22b7f6f8 Thomas Thrainer
                                     " must be passed too",
206 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
207 22b7f6f8 Thomas Thrainer
208 22b7f6f8 Thomas Thrainer
      elif not netutils.IPAddress.IsValid(ip):
209 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid IP address '%s'" % ip,
210 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
211 22b7f6f8 Thomas Thrainer
212 22b7f6f8 Thomas Thrainer
      nic_ip = ip
213 22b7f6f8 Thomas Thrainer
214 22b7f6f8 Thomas Thrainer
    # TODO: check the ip address for uniqueness
215 22b7f6f8 Thomas Thrainer
    if nic_mode == constants.NIC_MODE_ROUTED and not nic_ip:
216 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Routed nic mode requires an ip address",
217 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
218 22b7f6f8 Thomas Thrainer
219 22b7f6f8 Thomas Thrainer
    # MAC address verification
220 22b7f6f8 Thomas Thrainer
    mac = nic.get(constants.INIC_MAC, constants.VALUE_AUTO)
221 22b7f6f8 Thomas Thrainer
    if mac not in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
222 22b7f6f8 Thomas Thrainer
      mac = utils.NormalizeAndValidateMac(mac)
223 22b7f6f8 Thomas Thrainer
224 22b7f6f8 Thomas Thrainer
      try:
225 22b7f6f8 Thomas Thrainer
        # TODO: We need to factor this out
226 22b7f6f8 Thomas Thrainer
        cfg.ReserveMAC(mac, ec_id)
227 22b7f6f8 Thomas Thrainer
      except errors.ReservationError:
228 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("MAC address %s already in use"
229 22b7f6f8 Thomas Thrainer
                                   " in cluster" % mac,
230 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
231 22b7f6f8 Thomas Thrainer
232 22b7f6f8 Thomas Thrainer
    #  Build nic parameters
233 22b7f6f8 Thomas Thrainer
    nicparams = {}
234 22b7f6f8 Thomas Thrainer
    if nic_mode_req:
235 22b7f6f8 Thomas Thrainer
      nicparams[constants.NIC_MODE] = nic_mode
236 22b7f6f8 Thomas Thrainer
    if link:
237 22b7f6f8 Thomas Thrainer
      nicparams[constants.NIC_LINK] = link
238 9f7d5fe4 Sebastian Gebhard
    if vlan:
239 9f7d5fe4 Sebastian Gebhard
      nicparams[constants.NIC_VLAN] = vlan
240 22b7f6f8 Thomas Thrainer
241 22b7f6f8 Thomas Thrainer
    check_params = cluster.SimpleFillNIC(nicparams)
242 22b7f6f8 Thomas Thrainer
    objects.NIC.CheckParameterSyntax(check_params)
243 22b7f6f8 Thomas Thrainer
    net_uuid = cfg.LookupNetwork(net)
244 22b7f6f8 Thomas Thrainer
    name = nic.get(constants.INIC_NAME, None)
245 22b7f6f8 Thomas Thrainer
    if name is not None and name.lower() == constants.VALUE_NONE:
246 22b7f6f8 Thomas Thrainer
      name = None
247 22b7f6f8 Thomas Thrainer
    nic_obj = objects.NIC(mac=mac, ip=nic_ip, name=name,
248 22b7f6f8 Thomas Thrainer
                          network=net_uuid, nicparams=nicparams)
249 22b7f6f8 Thomas Thrainer
    nic_obj.uuid = cfg.GenerateUniqueID(ec_id)
250 22b7f6f8 Thomas Thrainer
    nics.append(nic_obj)
251 22b7f6f8 Thomas Thrainer
252 22b7f6f8 Thomas Thrainer
  return nics
253 22b7f6f8 Thomas Thrainer
254 22b7f6f8 Thomas Thrainer
255 1c3231aa Thomas Thrainer
def _CheckForConflictingIp(lu, ip, node_uuid):
256 22b7f6f8 Thomas Thrainer
  """In case of conflicting IP address raise error.
257 22b7f6f8 Thomas Thrainer

258 22b7f6f8 Thomas Thrainer
  @type ip: string
259 22b7f6f8 Thomas Thrainer
  @param ip: IP address
260 1c3231aa Thomas Thrainer
  @type node_uuid: string
261 1c3231aa Thomas Thrainer
  @param node_uuid: node UUID
262 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

552 22b7f6f8 Thomas Thrainer
    Figure out the right locks for instance creation.
553 22b7f6f8 Thomas Thrainer

554 22b7f6f8 Thomas Thrainer
    """
555 22b7f6f8 Thomas Thrainer
    self.needed_locks = {}
556 22b7f6f8 Thomas Thrainer
557 22b7f6f8 Thomas Thrainer
    # this is just a preventive check, but someone might still add this
558 22b7f6f8 Thomas Thrainer
    # instance in the meantime, and creation will fail at lock-add time
559 da4a52a3 Thomas Thrainer
    if self.op.instance_name in\
560 da4a52a3 Thomas Thrainer
      [inst.name for inst in self.cfg.GetAllInstancesInfo().values()]:
561 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
562 d0d7d7cf Thomas Thrainer
                                 self.op.instance_name, errors.ECODE_EXISTS)
563 22b7f6f8 Thomas Thrainer
564 d0d7d7cf Thomas Thrainer
    self.add_locks[locking.LEVEL_INSTANCE] = self.op.instance_name
565 22b7f6f8 Thomas Thrainer
566 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
567 22b7f6f8 Thomas Thrainer
      # TODO: Find a solution to not lock all nodes in the cluster, e.g. by
568 22b7f6f8 Thomas Thrainer
      # specifying a group on instance creation and then selecting nodes from
569 22b7f6f8 Thomas Thrainer
      # that group
570 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
571 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
572 22b7f6f8 Thomas Thrainer
573 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
574 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
575 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE_RES] = True
576 22b7f6f8 Thomas Thrainer
    else:
577 1c3231aa Thomas Thrainer
      (self.op.pnode_uuid, self.op.pnode) = \
578 1c3231aa Thomas Thrainer
        ExpandNodeUuidAndName(self.cfg, self.op.pnode_uuid, self.op.pnode)
579 1c3231aa Thomas Thrainer
      nodelist = [self.op.pnode_uuid]
580 22b7f6f8 Thomas Thrainer
      if self.op.snode is not None:
581 1c3231aa Thomas Thrainer
        (self.op.snode_uuid, self.op.snode) = \
582 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, self.op.snode_uuid, self.op.snode)
583 1c3231aa Thomas Thrainer
        nodelist.append(self.op.snode_uuid)
584 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodelist
585 22b7f6f8 Thomas Thrainer
586 22b7f6f8 Thomas Thrainer
    # in case of import lock the source node too
587 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
588 22b7f6f8 Thomas Thrainer
      src_node = self.op.src_node
589 22b7f6f8 Thomas Thrainer
      src_path = self.op.src_path
590 22b7f6f8 Thomas Thrainer
591 22b7f6f8 Thomas Thrainer
      if src_path is None:
592 22b7f6f8 Thomas Thrainer
        self.op.src_path = src_path = self.op.instance_name
593 22b7f6f8 Thomas Thrainer
594 22b7f6f8 Thomas Thrainer
      if src_node is None:
595 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
596 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
597 22b7f6f8 Thomas Thrainer
        self.op.src_node = None
598 22b7f6f8 Thomas Thrainer
        if os.path.isabs(src_path):
599 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("Importing an instance from a path"
600 22b7f6f8 Thomas Thrainer
                                     " requires a source node option",
601 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
602 22b7f6f8 Thomas Thrainer
      else:
603 1c3231aa Thomas Thrainer
        (self.op.src_node_uuid, self.op.src_node) = (_, src_node) = \
604 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, self.op.src_node_uuid, src_node)
605 22b7f6f8 Thomas Thrainer
        if self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET:
606 1c3231aa Thomas Thrainer
          self.needed_locks[locking.LEVEL_NODE].append(self.op.src_node_uuid)
607 22b7f6f8 Thomas Thrainer
        if not os.path.isabs(src_path):
608 a61a0813 Thomas Thrainer
          self.op.src_path = \
609 22b7f6f8 Thomas Thrainer
            utils.PathJoin(pathutils.EXPORT_DIR, src_path)
610 22b7f6f8 Thomas Thrainer
611 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = \
612 5eacbcae Thomas Thrainer
      CopyLockList(self.needed_locks[locking.LEVEL_NODE])
613 22b7f6f8 Thomas Thrainer
614 22b7f6f8 Thomas Thrainer
  def _RunAllocator(self):
615 22b7f6f8 Thomas Thrainer
    """Run the allocator based on input opcode.
616 22b7f6f8 Thomas Thrainer

617 22b7f6f8 Thomas Thrainer
    """
618 22b7f6f8 Thomas Thrainer
    if self.op.opportunistic_locking:
619 22b7f6f8 Thomas Thrainer
      # Only consider nodes for which a lock is held
620 4665b94f Thomas Thrainer
      node_name_whitelist = self.cfg.GetNodeNames(
621 4665b94f Thomas Thrainer
        self.owned_locks(locking.LEVEL_NODE))
622 22b7f6f8 Thomas Thrainer
    else:
623 4665b94f Thomas Thrainer
      node_name_whitelist = None
624 22b7f6f8 Thomas Thrainer
625 22b7f6f8 Thomas Thrainer
    req = _CreateInstanceAllocRequest(self.op, self.disks,
626 22b7f6f8 Thomas Thrainer
                                      self.nics, self.be_full,
627 4665b94f Thomas Thrainer
                                      node_name_whitelist)
628 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
629 22b7f6f8 Thomas Thrainer
630 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
631 22b7f6f8 Thomas Thrainer
632 22b7f6f8 Thomas Thrainer
    if not ial.success:
633 22b7f6f8 Thomas Thrainer
      # When opportunistic locks are used only a temporary failure is generated
634 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
635 22b7f6f8 Thomas Thrainer
        ecode = errors.ECODE_TEMP_NORES
636 22b7f6f8 Thomas Thrainer
      else:
637 22b7f6f8 Thomas Thrainer
        ecode = errors.ECODE_NORES
638 22b7f6f8 Thomas Thrainer
639 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute nodes using"
640 22b7f6f8 Thomas Thrainer
                                 " iallocator '%s': %s" %
641 22b7f6f8 Thomas Thrainer
                                 (self.op.iallocator, ial.info),
642 22b7f6f8 Thomas Thrainer
                                 ecode)
643 22b7f6f8 Thomas Thrainer
644 1c3231aa Thomas Thrainer
    (self.op.pnode_uuid, self.op.pnode) = \
645 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, None, ial.result[0])
646 22b7f6f8 Thomas Thrainer
    self.LogInfo("Selected nodes for instance %s via iallocator %s: %s",
647 22b7f6f8 Thomas Thrainer
                 self.op.instance_name, self.op.iallocator,
648 22b7f6f8 Thomas Thrainer
                 utils.CommaJoin(ial.result))
649 22b7f6f8 Thomas Thrainer
650 22b7f6f8 Thomas Thrainer
    assert req.RequiredNodes() in (1, 2), "Wrong node count from iallocator"
651 22b7f6f8 Thomas Thrainer
652 22b7f6f8 Thomas Thrainer
    if req.RequiredNodes() == 2:
653 1c3231aa Thomas Thrainer
      (self.op.snode_uuid, self.op.snode) = \
654 1c3231aa Thomas Thrainer
        ExpandNodeUuidAndName(self.cfg, None, ial.result[1])
655 22b7f6f8 Thomas Thrainer
656 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
657 22b7f6f8 Thomas Thrainer
    """Build hooks env.
658 22b7f6f8 Thomas Thrainer

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

661 22b7f6f8 Thomas Thrainer
    """
662 22b7f6f8 Thomas Thrainer
    env = {
663 22b7f6f8 Thomas Thrainer
      "ADD_MODE": self.op.mode,
664 22b7f6f8 Thomas Thrainer
      }
665 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
666 22b7f6f8 Thomas Thrainer
      env["SRC_NODE"] = self.op.src_node
667 22b7f6f8 Thomas Thrainer
      env["SRC_PATH"] = self.op.src_path
668 22b7f6f8 Thomas Thrainer
      env["SRC_IMAGES"] = self.src_images
669 22b7f6f8 Thomas Thrainer
670 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnv(
671 22b7f6f8 Thomas Thrainer
      name=self.op.instance_name,
672 1c3231aa Thomas Thrainer
      primary_node_name=self.op.pnode,
673 1c3231aa Thomas Thrainer
      secondary_node_names=self.cfg.GetNodeNames(self.secondaries),
674 22b7f6f8 Thomas Thrainer
      status=self.op.start,
675 22b7f6f8 Thomas Thrainer
      os_type=self.op.os_type,
676 22b7f6f8 Thomas Thrainer
      minmem=self.be_full[constants.BE_MINMEM],
677 22b7f6f8 Thomas Thrainer
      maxmem=self.be_full[constants.BE_MAXMEM],
678 22b7f6f8 Thomas Thrainer
      vcpus=self.be_full[constants.BE_VCPUS],
679 5eacbcae Thomas Thrainer
      nics=NICListToTuple(self, self.nics),
680 22b7f6f8 Thomas Thrainer
      disk_template=self.op.disk_template,
681 8a348b15 Christos Stavrakakis
      disks=[(d[constants.IDISK_NAME], d.get("uuid", ""),
682 8a348b15 Christos Stavrakakis
              d[constants.IDISK_SIZE], d[constants.IDISK_MODE])
683 8a348b15 Christos Stavrakakis
             for d in self.disks],
684 22b7f6f8 Thomas Thrainer
      bep=self.be_full,
685 22b7f6f8 Thomas Thrainer
      hvp=self.hv_full,
686 22b7f6f8 Thomas Thrainer
      hypervisor_name=self.op.hypervisor,
687 22b7f6f8 Thomas Thrainer
      tags=self.op.tags,
688 22b7f6f8 Thomas Thrainer
      ))
689 22b7f6f8 Thomas Thrainer
690 22b7f6f8 Thomas Thrainer
    return env
691 22b7f6f8 Thomas Thrainer
692 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
693 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
694 22b7f6f8 Thomas Thrainer

695 22b7f6f8 Thomas Thrainer
    """
696 1c3231aa Thomas Thrainer
    nl = [self.cfg.GetMasterNode(), self.op.pnode_uuid] + self.secondaries
697 22b7f6f8 Thomas Thrainer
    return nl, nl
698 22b7f6f8 Thomas Thrainer
699 22b7f6f8 Thomas Thrainer
  def _ReadExportInfo(self):
700 22b7f6f8 Thomas Thrainer
    """Reads the export information from disk.
701 22b7f6f8 Thomas Thrainer

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

705 22b7f6f8 Thomas Thrainer
    @return: the export information
706 22b7f6f8 Thomas Thrainer

707 22b7f6f8 Thomas Thrainer
    """
708 22b7f6f8 Thomas Thrainer
    assert self.op.mode == constants.INSTANCE_IMPORT
709 22b7f6f8 Thomas Thrainer
710 d0d7d7cf Thomas Thrainer
    if self.op.src_node_uuid is None:
711 22b7f6f8 Thomas Thrainer
      locked_nodes = self.owned_locks(locking.LEVEL_NODE)
712 22b7f6f8 Thomas Thrainer
      exp_list = self.rpc.call_export_list(locked_nodes)
713 22b7f6f8 Thomas Thrainer
      found = False
714 a61a0813 Thomas Thrainer
      for node_uuid in exp_list:
715 a61a0813 Thomas Thrainer
        if exp_list[node_uuid].fail_msg:
716 22b7f6f8 Thomas Thrainer
          continue
717 a61a0813 Thomas Thrainer
        if self.op.src_path in exp_list[node_uuid].payload:
718 22b7f6f8 Thomas Thrainer
          found = True
719 a61a0813 Thomas Thrainer
          self.op.src_node = self.cfg.GetNodeInfo(node_uuid).name
720 a61a0813 Thomas Thrainer
          self.op.src_node_uuid = node_uuid
721 d0d7d7cf Thomas Thrainer
          self.op.src_path = utils.PathJoin(pathutils.EXPORT_DIR,
722 d0d7d7cf Thomas Thrainer
                                            self.op.src_path)
723 22b7f6f8 Thomas Thrainer
          break
724 22b7f6f8 Thomas Thrainer
      if not found:
725 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No export found for relative path %s" %
726 d0d7d7cf Thomas Thrainer
                                   self.op.src_path, errors.ECODE_INVAL)
727 22b7f6f8 Thomas Thrainer
728 d0d7d7cf Thomas Thrainer
    CheckNodeOnline(self, self.op.src_node_uuid)
729 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_export_info(self.op.src_node_uuid, self.op.src_path)
730 d0d7d7cf Thomas Thrainer
    result.Raise("No export or invalid export found in dir %s" %
731 d0d7d7cf Thomas Thrainer
                 self.op.src_path)
732 22b7f6f8 Thomas Thrainer
733 22b7f6f8 Thomas Thrainer
    export_info = objects.SerializableConfigParser.Loads(str(result.payload))
734 22b7f6f8 Thomas Thrainer
    if not export_info.has_section(constants.INISECT_EXP):
735 22b7f6f8 Thomas Thrainer
      raise errors.ProgrammerError("Corrupted export config",
736 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_ENVIRON)
737 22b7f6f8 Thomas Thrainer
738 22b7f6f8 Thomas Thrainer
    ei_version = export_info.get(constants.INISECT_EXP, "version")
739 1c3231aa Thomas Thrainer
    if int(ei_version) != constants.EXPORT_VERSION:
740 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Wrong export version %s (wanted %d)" %
741 22b7f6f8 Thomas Thrainer
                                 (ei_version, constants.EXPORT_VERSION),
742 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_ENVIRON)
743 22b7f6f8 Thomas Thrainer
    return export_info
744 22b7f6f8 Thomas Thrainer
745 22b7f6f8 Thomas Thrainer
  def _ReadExportParams(self, einfo):
746 22b7f6f8 Thomas Thrainer
    """Use export parameters as defaults.
747 22b7f6f8 Thomas Thrainer

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

752 22b7f6f8 Thomas Thrainer
    """
753 22b7f6f8 Thomas Thrainer
    self.op.os_type = einfo.get(constants.INISECT_EXP, "os")
754 22b7f6f8 Thomas Thrainer
755 22b7f6f8 Thomas Thrainer
    if not self.op.disks:
756 22b7f6f8 Thomas Thrainer
      disks = []
757 22b7f6f8 Thomas Thrainer
      # TODO: import the disk iv_name too
758 22b7f6f8 Thomas Thrainer
      for idx in range(constants.MAX_DISKS):
759 22b7f6f8 Thomas Thrainer
        if einfo.has_option(constants.INISECT_INS, "disk%d_size" % idx):
760 22b7f6f8 Thomas Thrainer
          disk_sz = einfo.getint(constants.INISECT_INS, "disk%d_size" % idx)
761 22b7f6f8 Thomas Thrainer
          disks.append({constants.IDISK_SIZE: disk_sz})
762 22b7f6f8 Thomas Thrainer
      self.op.disks = disks
763 22b7f6f8 Thomas Thrainer
      if not disks and self.op.disk_template != constants.DT_DISKLESS:
764 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No disk info specified and the export"
765 22b7f6f8 Thomas Thrainer
                                   " is missing the disk information",
766 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
767 22b7f6f8 Thomas Thrainer
768 22b7f6f8 Thomas Thrainer
    if not self.op.nics:
769 22b7f6f8 Thomas Thrainer
      nics = []
770 22b7f6f8 Thomas Thrainer
      for idx in range(constants.MAX_NICS):
771 22b7f6f8 Thomas Thrainer
        if einfo.has_option(constants.INISECT_INS, "nic%d_mac" % idx):
772 22b7f6f8 Thomas Thrainer
          ndict = {}
773 22b7f6f8 Thomas Thrainer
          for name in list(constants.NICS_PARAMETERS) + ["ip", "mac"]:
774 a61a0813 Thomas Thrainer
            nic_param_name = "nic%d_%s" % (idx, name)
775 a61a0813 Thomas Thrainer
            if einfo.has_option(constants.INISECT_INS, nic_param_name):
776 a61a0813 Thomas Thrainer
              v = einfo.get(constants.INISECT_INS, nic_param_name)
777 a61a0813 Thomas Thrainer
              ndict[name] = v
778 22b7f6f8 Thomas Thrainer
          nics.append(ndict)
779 22b7f6f8 Thomas Thrainer
        else:
780 22b7f6f8 Thomas Thrainer
          break
781 22b7f6f8 Thomas Thrainer
      self.op.nics = nics
782 22b7f6f8 Thomas Thrainer
783 22b7f6f8 Thomas Thrainer
    if not self.op.tags and einfo.has_option(constants.INISECT_INS, "tags"):
784 22b7f6f8 Thomas Thrainer
      self.op.tags = einfo.get(constants.INISECT_INS, "tags").split()
785 22b7f6f8 Thomas Thrainer
786 22b7f6f8 Thomas Thrainer
    if (self.op.hypervisor is None and
787 22b7f6f8 Thomas Thrainer
        einfo.has_option(constants.INISECT_INS, "hypervisor")):
788 22b7f6f8 Thomas Thrainer
      self.op.hypervisor = einfo.get(constants.INISECT_INS, "hypervisor")
789 22b7f6f8 Thomas Thrainer
790 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_HYP):
791 22b7f6f8 Thomas Thrainer
      # use the export parameters but do not override the ones
792 22b7f6f8 Thomas Thrainer
      # specified by the user
793 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_HYP):
794 22b7f6f8 Thomas Thrainer
        if name not in self.op.hvparams:
795 22b7f6f8 Thomas Thrainer
          self.op.hvparams[name] = value
796 22b7f6f8 Thomas Thrainer
797 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_BEP):
798 22b7f6f8 Thomas Thrainer
      # use the parameters, without overriding
799 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_BEP):
800 22b7f6f8 Thomas Thrainer
        if name not in self.op.beparams:
801 22b7f6f8 Thomas Thrainer
          self.op.beparams[name] = value
802 22b7f6f8 Thomas Thrainer
        # Compatibility for the old "memory" be param
803 22b7f6f8 Thomas Thrainer
        if name == constants.BE_MEMORY:
804 22b7f6f8 Thomas Thrainer
          if constants.BE_MAXMEM not in self.op.beparams:
805 22b7f6f8 Thomas Thrainer
            self.op.beparams[constants.BE_MAXMEM] = value
806 22b7f6f8 Thomas Thrainer
          if constants.BE_MINMEM not in self.op.beparams:
807 22b7f6f8 Thomas Thrainer
            self.op.beparams[constants.BE_MINMEM] = value
808 22b7f6f8 Thomas Thrainer
    else:
809 22b7f6f8 Thomas Thrainer
      # try to read the parameters old style, from the main section
810 22b7f6f8 Thomas Thrainer
      for name in constants.BES_PARAMETERS:
811 22b7f6f8 Thomas Thrainer
        if (name not in self.op.beparams and
812 22b7f6f8 Thomas Thrainer
            einfo.has_option(constants.INISECT_INS, name)):
813 22b7f6f8 Thomas Thrainer
          self.op.beparams[name] = einfo.get(constants.INISECT_INS, name)
814 22b7f6f8 Thomas Thrainer
815 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_OSP):
816 22b7f6f8 Thomas Thrainer
      # use the parameters, without overriding
817 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_OSP):
818 22b7f6f8 Thomas Thrainer
        if name not in self.op.osparams:
819 22b7f6f8 Thomas Thrainer
          self.op.osparams[name] = value
820 22b7f6f8 Thomas Thrainer
821 22b7f6f8 Thomas Thrainer
  def _RevertToDefaults(self, cluster):
822 22b7f6f8 Thomas Thrainer
    """Revert the instance parameters to the default values.
823 22b7f6f8 Thomas Thrainer

824 22b7f6f8 Thomas Thrainer
    """
825 22b7f6f8 Thomas Thrainer
    # hvparams
826 22b7f6f8 Thomas Thrainer
    hv_defs = cluster.SimpleFillHV(self.op.hypervisor, self.op.os_type, {})
827 22b7f6f8 Thomas Thrainer
    for name in self.op.hvparams.keys():
828 22b7f6f8 Thomas Thrainer
      if name in hv_defs and hv_defs[name] == self.op.hvparams[name]:
829 22b7f6f8 Thomas Thrainer
        del self.op.hvparams[name]
830 22b7f6f8 Thomas Thrainer
    # beparams
831 22b7f6f8 Thomas Thrainer
    be_defs = cluster.SimpleFillBE({})
832 22b7f6f8 Thomas Thrainer
    for name in self.op.beparams.keys():
833 22b7f6f8 Thomas Thrainer
      if name in be_defs and be_defs[name] == self.op.beparams[name]:
834 22b7f6f8 Thomas Thrainer
        del self.op.beparams[name]
835 22b7f6f8 Thomas Thrainer
    # nic params
836 22b7f6f8 Thomas Thrainer
    nic_defs = cluster.SimpleFillNIC({})
837 22b7f6f8 Thomas Thrainer
    for nic in self.op.nics:
838 22b7f6f8 Thomas Thrainer
      for name in constants.NICS_PARAMETERS:
839 22b7f6f8 Thomas Thrainer
        if name in nic and name in nic_defs and nic[name] == nic_defs[name]:
840 22b7f6f8 Thomas Thrainer
          del nic[name]
841 22b7f6f8 Thomas Thrainer
    # osparams
842 22b7f6f8 Thomas Thrainer
    os_defs = cluster.SimpleFillOS(self.op.os_type, {})
843 22b7f6f8 Thomas Thrainer
    for name in self.op.osparams.keys():
844 22b7f6f8 Thomas Thrainer
      if name in os_defs and os_defs[name] == self.op.osparams[name]:
845 22b7f6f8 Thomas Thrainer
        del self.op.osparams[name]
846 22b7f6f8 Thomas Thrainer
847 22b7f6f8 Thomas Thrainer
  def _CalculateFileStorageDir(self):
848 22b7f6f8 Thomas Thrainer
    """Calculate final instance file storage dir.
849 22b7f6f8 Thomas Thrainer

850 22b7f6f8 Thomas Thrainer
    """
851 22b7f6f8 Thomas Thrainer
    # file storage dir calculation/check
852 22b7f6f8 Thomas Thrainer
    self.instance_file_storage_dir = None
853 22b7f6f8 Thomas Thrainer
    if self.op.disk_template in constants.DTS_FILEBASED:
854 22b7f6f8 Thomas Thrainer
      # build the full file storage dir path
855 22b7f6f8 Thomas Thrainer
      joinargs = []
856 22b7f6f8 Thomas Thrainer
857 22b7f6f8 Thomas Thrainer
      if self.op.disk_template == constants.DT_SHARED_FILE:
858 22b7f6f8 Thomas Thrainer
        get_fsd_fn = self.cfg.GetSharedFileStorageDir
859 22b7f6f8 Thomas Thrainer
      else:
860 22b7f6f8 Thomas Thrainer
        get_fsd_fn = self.cfg.GetFileStorageDir
861 22b7f6f8 Thomas Thrainer
862 22b7f6f8 Thomas Thrainer
      cfg_storagedir = get_fsd_fn()
863 22b7f6f8 Thomas Thrainer
      if not cfg_storagedir:
864 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cluster file storage dir not defined",
865 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
866 22b7f6f8 Thomas Thrainer
      joinargs.append(cfg_storagedir)
867 22b7f6f8 Thomas Thrainer
868 22b7f6f8 Thomas Thrainer
      if self.op.file_storage_dir is not None:
869 22b7f6f8 Thomas Thrainer
        joinargs.append(self.op.file_storage_dir)
870 22b7f6f8 Thomas Thrainer
871 22b7f6f8 Thomas Thrainer
      joinargs.append(self.op.instance_name)
872 22b7f6f8 Thomas Thrainer
873 22b7f6f8 Thomas Thrainer
      # pylint: disable=W0142
874 22b7f6f8 Thomas Thrainer
      self.instance_file_storage_dir = utils.PathJoin(*joinargs)
875 22b7f6f8 Thomas Thrainer
876 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self): # pylint: disable=R0914
877 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
878 22b7f6f8 Thomas Thrainer

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

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

1475 22b7f6f8 Thomas Thrainer
  """
1476 22b7f6f8 Thomas Thrainer
  HPATH = "instance-rename"
1477 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1478 22b7f6f8 Thomas Thrainer
1479 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1480 22b7f6f8 Thomas Thrainer
    """Check arguments.
1481 22b7f6f8 Thomas Thrainer

1482 22b7f6f8 Thomas Thrainer
    """
1483 22b7f6f8 Thomas Thrainer
    if self.op.ip_check and not self.op.name_check:
1484 22b7f6f8 Thomas Thrainer
      # TODO: make the ip check more flexible and not depend on the name check
1485 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("IP address check requires a name check",
1486 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1487 22b7f6f8 Thomas Thrainer
1488 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1489 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1490 22b7f6f8 Thomas Thrainer

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

1493 22b7f6f8 Thomas Thrainer
    """
1494 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1495 22b7f6f8 Thomas Thrainer
    env["INSTANCE_NEW_NAME"] = self.op.new_name
1496 22b7f6f8 Thomas Thrainer
    return env
1497 22b7f6f8 Thomas Thrainer
1498 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1499 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1500 22b7f6f8 Thomas Thrainer

1501 22b7f6f8 Thomas Thrainer
    """
1502 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
1503 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1504 22b7f6f8 Thomas Thrainer
1505 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1506 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1507 22b7f6f8 Thomas Thrainer

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

1510 22b7f6f8 Thomas Thrainer
    """
1511 da4a52a3 Thomas Thrainer
    (self.op.instance_uuid, self.op.instance_name) = \
1512 da4a52a3 Thomas Thrainer
      ExpandInstanceUuidAndName(self.cfg, self.op.instance_uuid,
1513 da4a52a3 Thomas Thrainer
                                self.op.instance_name)
1514 da4a52a3 Thomas Thrainer
    instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1515 22b7f6f8 Thomas Thrainer
    assert instance is not None
1516 1f7c8208 Helga Velroyen
1517 1f7c8208 Helga Velroyen
    # It should actually not happen that an instance is running with a disabled
1518 1f7c8208 Helga Velroyen
    # disk template, but in case it does, the renaming of file-based instances
1519 1f7c8208 Helga Velroyen
    # will fail horribly. Thus, we test it before.
1520 1f7c8208 Helga Velroyen
    if (instance.disk_template in constants.DTS_FILEBASED and
1521 1f7c8208 Helga Velroyen
        self.op.new_name != instance.name):
1522 1f7c8208 Helga Velroyen
      CheckDiskTemplateEnabled(self.cfg.GetClusterInfo(),
1523 1f7c8208 Helga Velroyen
                               instance.disk_template)
1524 1f7c8208 Helga Velroyen
1525 5eacbcae Thomas Thrainer
    CheckNodeOnline(self, instance.primary_node)
1526 5eacbcae Thomas Thrainer
    CheckInstanceState(self, instance, INSTANCE_NOT_RUNNING,
1527 5eacbcae Thomas Thrainer
                       msg="cannot rename")
1528 22b7f6f8 Thomas Thrainer
    self.instance = instance
1529 22b7f6f8 Thomas Thrainer
1530 22b7f6f8 Thomas Thrainer
    new_name = self.op.new_name
1531 22b7f6f8 Thomas Thrainer
    if self.op.name_check:
1532 22b7f6f8 Thomas Thrainer
      hostname = _CheckHostnameSane(self, new_name)
1533 22b7f6f8 Thomas Thrainer
      new_name = self.op.new_name = hostname.name
1534 22b7f6f8 Thomas Thrainer
      if (self.op.ip_check and
1535 22b7f6f8 Thomas Thrainer
          netutils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT)):
1536 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("IP %s of instance %s already in use" %
1537 22b7f6f8 Thomas Thrainer
                                   (hostname.ip, new_name),
1538 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
1539 22b7f6f8 Thomas Thrainer
1540 da4a52a3 Thomas Thrainer
    instance_names = [inst.name for
1541 da4a52a3 Thomas Thrainer
                      inst in self.cfg.GetAllInstancesInfo().values()]
1542 da4a52a3 Thomas Thrainer
    if new_name in instance_names and new_name != instance.name:
1543 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
1544 22b7f6f8 Thomas Thrainer
                                 new_name, errors.ECODE_EXISTS)
1545 22b7f6f8 Thomas Thrainer
1546 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1547 22b7f6f8 Thomas Thrainer
    """Rename the instance.
1548 22b7f6f8 Thomas Thrainer

1549 22b7f6f8 Thomas Thrainer
    """
1550 d0d7d7cf Thomas Thrainer
    old_name = self.instance.name
1551 22b7f6f8 Thomas Thrainer
1552 22b7f6f8 Thomas Thrainer
    rename_file_storage = False
1553 d0d7d7cf Thomas Thrainer
    if (self.instance.disk_template in constants.DTS_FILEBASED and
1554 d0d7d7cf Thomas Thrainer
        self.op.new_name != self.instance.name):
1555 d0d7d7cf Thomas Thrainer
      old_file_storage_dir = os.path.dirname(
1556 d0d7d7cf Thomas Thrainer
                               self.instance.disks[0].logical_id[1])
1557 22b7f6f8 Thomas Thrainer
      rename_file_storage = True
1558 22b7f6f8 Thomas Thrainer
1559 da4a52a3 Thomas Thrainer
    self.cfg.RenameInstance(self.instance.uuid, self.op.new_name)
1560 22b7f6f8 Thomas Thrainer
    # Change the instance lock. This is definitely safe while we hold the BGL.
1561 22b7f6f8 Thomas Thrainer
    # Otherwise the new lock would have to be added in acquired mode.
1562 22b7f6f8 Thomas Thrainer
    assert self.REQ_BGL
1563 22b7f6f8 Thomas Thrainer
    assert locking.BGL in self.owned_locks(locking.LEVEL_CLUSTER)
1564 22b7f6f8 Thomas Thrainer
    self.glm.remove(locking.LEVEL_INSTANCE, old_name)
1565 22b7f6f8 Thomas Thrainer
    self.glm.add(locking.LEVEL_INSTANCE, self.op.new_name)
1566 22b7f6f8 Thomas Thrainer
1567 22b7f6f8 Thomas Thrainer
    # re-read the instance from the configuration after rename
1568 da4a52a3 Thomas Thrainer
    renamed_inst = self.cfg.GetInstanceInfo(self.instance.uuid)
1569 22b7f6f8 Thomas Thrainer
1570 22b7f6f8 Thomas Thrainer
    if rename_file_storage:
1571 d0d7d7cf Thomas Thrainer
      new_file_storage_dir = os.path.dirname(
1572 d0d7d7cf Thomas Thrainer
                               renamed_inst.disks[0].logical_id[1])
1573 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_file_storage_dir_rename(renamed_inst.primary_node,
1574 22b7f6f8 Thomas Thrainer
                                                     old_file_storage_dir,
1575 22b7f6f8 Thomas Thrainer
                                                     new_file_storage_dir)
1576 22b7f6f8 Thomas Thrainer
      result.Raise("Could not rename on node %s directory '%s' to '%s'"
1577 22b7f6f8 Thomas Thrainer
                   " (but the instance has been renamed in Ganeti)" %
1578 d0d7d7cf Thomas Thrainer
                   (self.cfg.GetNodeName(renamed_inst.primary_node),
1579 1c3231aa Thomas Thrainer
                    old_file_storage_dir, new_file_storage_dir))
1580 22b7f6f8 Thomas Thrainer
1581 d0d7d7cf Thomas Thrainer
    StartInstanceDisks(self, renamed_inst, None)
1582 22b7f6f8 Thomas Thrainer
    # update info on disks
1583 d0d7d7cf Thomas Thrainer
    info = GetInstanceInfoText(renamed_inst)
1584 d0d7d7cf Thomas Thrainer
    for (idx, disk) in enumerate(renamed_inst.disks):
1585 d0d7d7cf Thomas Thrainer
      for node_uuid in renamed_inst.all_nodes:
1586 0c3d9c7c Thomas Thrainer
        result = self.rpc.call_blockdev_setinfo(node_uuid,
1587 0c3d9c7c Thomas Thrainer
                                                (disk, renamed_inst), info)
1588 1c3231aa Thomas Thrainer
        result.Warn("Error setting info on node %s for disk %s" %
1589 1c3231aa Thomas Thrainer
                    (self.cfg.GetNodeName(node_uuid), idx), self.LogWarning)
1590 22b7f6f8 Thomas Thrainer
    try:
1591 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_instance_run_rename(renamed_inst.primary_node,
1592 d0d7d7cf Thomas Thrainer
                                                 renamed_inst, old_name,
1593 d0d7d7cf Thomas Thrainer
                                                 self.op.debug_level)
1594 c7dd65be Klaus Aehlig
      result.Warn("Could not run OS rename script for instance %s on node %s"
1595 c7dd65be Klaus Aehlig
                  " (but the instance has been renamed in Ganeti)" %
1596 d0d7d7cf Thomas Thrainer
                  (renamed_inst.name,
1597 d0d7d7cf Thomas Thrainer
                   self.cfg.GetNodeName(renamed_inst.primary_node)),
1598 1c3231aa Thomas Thrainer
                  self.LogWarning)
1599 22b7f6f8 Thomas Thrainer
    finally:
1600 d0d7d7cf Thomas Thrainer
      ShutdownInstanceDisks(self, renamed_inst)
1601 22b7f6f8 Thomas Thrainer
1602 d0d7d7cf Thomas Thrainer
    return renamed_inst.name
1603 22b7f6f8 Thomas Thrainer
1604 22b7f6f8 Thomas Thrainer
1605 22b7f6f8 Thomas Thrainer
class LUInstanceRemove(LogicalUnit):
1606 22b7f6f8 Thomas Thrainer
  """Remove an instance.
1607 22b7f6f8 Thomas Thrainer

1608 22b7f6f8 Thomas Thrainer
  """
1609 22b7f6f8 Thomas Thrainer
  HPATH = "instance-remove"
1610 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1611 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1612 22b7f6f8 Thomas Thrainer
1613 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1614 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1615 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
1616 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1617 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
1618 22b7f6f8 Thomas Thrainer
1619 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1620 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1621 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
1622 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1623 22b7f6f8 Thomas Thrainer
      # Copy node locks
1624 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1625 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1626 22b7f6f8 Thomas Thrainer
1627 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1628 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1629 22b7f6f8 Thomas Thrainer

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

1632 22b7f6f8 Thomas Thrainer
    """
1633 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1634 22b7f6f8 Thomas Thrainer
    env["SHUTDOWN_TIMEOUT"] = self.op.shutdown_timeout
1635 22b7f6f8 Thomas Thrainer
    return env
1636 22b7f6f8 Thomas Thrainer
1637 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1638 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1639 22b7f6f8 Thomas Thrainer

1640 22b7f6f8 Thomas Thrainer
    """
1641 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()]
1642 22b7f6f8 Thomas Thrainer
    nl_post = list(self.instance.all_nodes) + nl
1643 22b7f6f8 Thomas Thrainer
    return (nl, nl_post)
1644 22b7f6f8 Thomas Thrainer
1645 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1646 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1647 22b7f6f8 Thomas Thrainer

1648 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1649 22b7f6f8 Thomas Thrainer

1650 22b7f6f8 Thomas Thrainer
    """
1651 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1652 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1653 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1654 22b7f6f8 Thomas Thrainer
1655 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1656 22b7f6f8 Thomas Thrainer
    """Remove the instance.
1657 22b7f6f8 Thomas Thrainer

1658 22b7f6f8 Thomas Thrainer
    """
1659 d0d7d7cf Thomas Thrainer
    logging.info("Shutting down instance %s on node %s", self.instance.name,
1660 d0d7d7cf Thomas Thrainer
                 self.cfg.GetNodeName(self.instance.primary_node))
1661 22b7f6f8 Thomas Thrainer
1662 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_instance_shutdown(self.instance.primary_node,
1663 d0d7d7cf Thomas Thrainer
                                             self.instance,
1664 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1665 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1666 c7dd65be Klaus Aehlig
    if self.op.ignore_failures:
1667 c7dd65be Klaus Aehlig
      result.Warn("Warning: can't shutdown instance", feedback_fn)
1668 c7dd65be Klaus Aehlig
    else:
1669 c7dd65be Klaus Aehlig
      result.Raise("Could not shutdown instance %s on node %s" %
1670 d0d7d7cf Thomas Thrainer
                   (self.instance.name,
1671 d0d7d7cf Thomas Thrainer
                    self.cfg.GetNodeName(self.instance.primary_node)))
1672 22b7f6f8 Thomas Thrainer
1673 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1674 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1675 d0d7d7cf Thomas Thrainer
    assert not (set(self.instance.all_nodes) -
1676 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
1677 22b7f6f8 Thomas Thrainer
      "Not owning correct locks"
1678 22b7f6f8 Thomas Thrainer
1679 d0d7d7cf Thomas Thrainer
    RemoveInstance(self, feedback_fn, self.instance, self.op.ignore_failures)
1680 22b7f6f8 Thomas Thrainer
1681 22b7f6f8 Thomas Thrainer
1682 22b7f6f8 Thomas Thrainer
class LUInstanceMove(LogicalUnit):
1683 22b7f6f8 Thomas Thrainer
  """Move an instance by data-copying.
1684 22b7f6f8 Thomas Thrainer

1685 22b7f6f8 Thomas Thrainer
  """
1686 22b7f6f8 Thomas Thrainer
  HPATH = "instance-move"
1687 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1688 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1689 22b7f6f8 Thomas Thrainer
1690 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1691 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1692 1c3231aa Thomas Thrainer
    (self.op.target_node_uuid, self.op.target_node) = \
1693 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.target_node_uuid,
1694 1c3231aa Thomas Thrainer
                            self.op.target_node)
1695 b9aae98b Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = [self.op.target_node_uuid]
1696 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1697 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
1698 22b7f6f8 Thomas Thrainer
1699 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1700 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1701 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes(primary_only=True)
1702 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1703 22b7f6f8 Thomas Thrainer
      # Copy node locks
1704 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1705 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1706 22b7f6f8 Thomas Thrainer
1707 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1708 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1709 22b7f6f8 Thomas Thrainer

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

1712 22b7f6f8 Thomas Thrainer
    """
1713 22b7f6f8 Thomas Thrainer
    env = {
1714 22b7f6f8 Thomas Thrainer
      "TARGET_NODE": self.op.target_node,
1715 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
1716 22b7f6f8 Thomas Thrainer
      }
1717 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
1718 22b7f6f8 Thomas Thrainer
    return env
1719 22b7f6f8 Thomas Thrainer
1720 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1721 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1722 22b7f6f8 Thomas Thrainer

1723 22b7f6f8 Thomas Thrainer
    """
1724 22b7f6f8 Thomas Thrainer
    nl = [
1725 22b7f6f8 Thomas Thrainer
      self.cfg.GetMasterNode(),
1726 22b7f6f8 Thomas Thrainer
      self.instance.primary_node,
1727 1c3231aa Thomas Thrainer
      self.op.target_node_uuid,
1728 22b7f6f8 Thomas Thrainer
      ]
1729 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1730 22b7f6f8 Thomas Thrainer
1731 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1732 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1733 22b7f6f8 Thomas Thrainer

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

1736 22b7f6f8 Thomas Thrainer
    """
1737 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1738 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1739 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1740 22b7f6f8 Thomas Thrainer
1741 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template not in constants.DTS_COPYABLE:
1742 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk template %s not suitable for copying" %
1743 d0d7d7cf Thomas Thrainer
                                 self.instance.disk_template,
1744 d0d7d7cf Thomas Thrainer
                                 errors.ECODE_STATE)
1745 22b7f6f8 Thomas Thrainer
1746 1c3231aa Thomas Thrainer
    target_node = self.cfg.GetNodeInfo(self.op.target_node_uuid)
1747 1c3231aa Thomas Thrainer
    assert target_node is not None, \
1748 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked node %s" % self.op.target_node
1749 22b7f6f8 Thomas Thrainer
1750 1c3231aa Thomas Thrainer
    self.target_node_uuid = target_node.uuid
1751 d0d7d7cf Thomas Thrainer
    if target_node.uuid == self.instance.primary_node:
1752 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance %s is already on the node %s" %
1753 d0d7d7cf Thomas Thrainer
                                 (self.instance.name, target_node.name),
1754 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
1755 22b7f6f8 Thomas Thrainer
1756 d0d7d7cf Thomas Thrainer
    bep = self.cfg.GetClusterInfo().FillBE(self.instance)
1757 22b7f6f8 Thomas Thrainer
1758 d0d7d7cf Thomas Thrainer
    for idx, dsk in enumerate(self.instance.disks):
1759 cd3b4ff4 Helga Velroyen
      if dsk.dev_type not in (constants.DT_PLAIN, constants.DT_FILE,
1760 cd3b4ff4 Helga Velroyen
                              constants.DT_SHARED_FILE):
1761 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance disk %d has a complex layout,"
1762 22b7f6f8 Thomas Thrainer
                                   " cannot copy" % idx, errors.ECODE_STATE)
1763 22b7f6f8 Thomas Thrainer
1764 1c3231aa Thomas Thrainer
    CheckNodeOnline(self, target_node.uuid)
1765 1c3231aa Thomas Thrainer
    CheckNodeNotDrained(self, target_node.uuid)
1766 1c3231aa Thomas Thrainer
    CheckNodeVmCapable(self, target_node.uuid)
1767 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
1768 1c3231aa Thomas Thrainer
    group_info = self.cfg.GetNodeGroup(target_node.group)
1769 22b7f6f8 Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster, group_info)
1770 d0d7d7cf Thomas Thrainer
    CheckTargetNodeIPolicy(self, ipolicy, self.instance, target_node, self.cfg,
1771 5eacbcae Thomas Thrainer
                           ignore=self.op.ignore_ipolicy)
1772 22b7f6f8 Thomas Thrainer
1773 d0d7d7cf Thomas Thrainer
    if self.instance.admin_state == constants.ADMINST_UP:
1774 22b7f6f8 Thomas Thrainer
      # check memory requirements on the secondary node
1775 a295eb80 Helga Velroyen
      CheckNodeFreeMemory(
1776 1c3231aa Thomas Thrainer
          self, target_node.uuid, "failing over instance %s" %
1777 d0d7d7cf Thomas Thrainer
          self.instance.name, bep[constants.BE_MAXMEM],
1778 d0d7d7cf Thomas Thrainer
          self.instance.hypervisor,
1779 d0d7d7cf Thomas Thrainer
          self.cfg.GetClusterInfo().hvparams[self.instance.hypervisor])
1780 22b7f6f8 Thomas Thrainer
    else:
1781 22b7f6f8 Thomas Thrainer
      self.LogInfo("Not checking memory on the secondary node as"
1782 22b7f6f8 Thomas Thrainer
                   " instance will not be started")
1783 22b7f6f8 Thomas Thrainer
1784 22b7f6f8 Thomas Thrainer
    # check bridge existance
1785 d0d7d7cf Thomas Thrainer
    CheckInstanceBridgesExist(self, self.instance, node_uuid=target_node.uuid)
1786 22b7f6f8 Thomas Thrainer
1787 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1788 22b7f6f8 Thomas Thrainer
    """Move an instance.
1789 22b7f6f8 Thomas Thrainer

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

1793 22b7f6f8 Thomas Thrainer
    """
1794 d0d7d7cf Thomas Thrainer
    source_node = self.cfg.GetNodeInfo(self.instance.primary_node)
1795 1c3231aa Thomas Thrainer
    target_node = self.cfg.GetNodeInfo(self.target_node_uuid)
1796 22b7f6f8 Thomas Thrainer
1797 22b7f6f8 Thomas Thrainer
    self.LogInfo("Shutting down instance %s on source node %s",
1798 d0d7d7cf Thomas Thrainer
                 self.instance.name, source_node.name)
1799 22b7f6f8 Thomas Thrainer
1800 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1801 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1802 22b7f6f8 Thomas Thrainer
1803 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_instance_shutdown(source_node.uuid, self.instance,
1804 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1805 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1806 c7dd65be Klaus Aehlig
    if self.op.ignore_consistency:
1807 c7dd65be Klaus Aehlig
      result.Warn("Could not shutdown instance %s on node %s. Proceeding"
1808 c7dd65be Klaus Aehlig
                  " anyway. Please make sure node %s is down. Error details" %
1809 d0d7d7cf Thomas Thrainer
                  (self.instance.name, source_node.name, source_node.name),
1810 1c3231aa Thomas Thrainer
                  self.LogWarning)
1811 c7dd65be Klaus Aehlig
    else:
1812 c7dd65be Klaus Aehlig
      result.Raise("Could not shutdown instance %s on node %s" %
1813 d0d7d7cf Thomas Thrainer
                   (self.instance.name, source_node.name))
1814 22b7f6f8 Thomas Thrainer
1815 22b7f6f8 Thomas Thrainer
    # create the target disks
1816 22b7f6f8 Thomas Thrainer
    try:
1817 d0d7d7cf Thomas Thrainer
      CreateDisks(self, self.instance, target_node_uuid=target_node.uuid)
1818 22b7f6f8 Thomas Thrainer
    except errors.OpExecError:
1819 22b7f6f8 Thomas Thrainer
      self.LogWarning("Device creation failed")
1820 da4a52a3 Thomas Thrainer
      self.cfg.ReleaseDRBDMinors(self.instance.uuid)
1821 22b7f6f8 Thomas Thrainer
      raise
1822 22b7f6f8 Thomas Thrainer
1823 22b7f6f8 Thomas Thrainer
    cluster_name = self.cfg.GetClusterInfo().cluster_name
1824 22b7f6f8 Thomas Thrainer
1825 22b7f6f8 Thomas Thrainer
    errs = []
1826 22b7f6f8 Thomas Thrainer
    # activate, get path, copy the data over
1827 d0d7d7cf Thomas Thrainer
    for idx, disk in enumerate(self.instance.disks):
1828 22b7f6f8 Thomas Thrainer
      self.LogInfo("Copying data for disk %d", idx)
1829 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_blockdev_assemble(
1830 d0d7d7cf Thomas Thrainer
                 target_node.uuid, (disk, self.instance), self.instance.name,
1831 d0d7d7cf Thomas Thrainer
                 True, idx)
1832 22b7f6f8 Thomas Thrainer
      if result.fail_msg:
1833 22b7f6f8 Thomas Thrainer
        self.LogWarning("Can't assemble newly created disk %d: %s",
1834 22b7f6f8 Thomas Thrainer
                        idx, result.fail_msg)
1835 22b7f6f8 Thomas Thrainer
        errs.append(result.fail_msg)
1836 22b7f6f8 Thomas Thrainer
        break
1837 71859b6f Dimitris Aragiorgis
      dev_path, _ = result.payload
1838 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_blockdev_export(source_node.uuid, (disk,
1839 d0d7d7cf Thomas Thrainer
                                                                self.instance),
1840 f56ab6d1 Thomas Thrainer
                                             target_node.secondary_ip,
1841 f56ab6d1 Thomas Thrainer
                                             dev_path, cluster_name)
1842 22b7f6f8 Thomas Thrainer
      if result.fail_msg:
1843 22b7f6f8 Thomas Thrainer
        self.LogWarning("Can't copy data over for disk %d: %s",
1844 22b7f6f8 Thomas Thrainer
                        idx, result.fail_msg)
1845 22b7f6f8 Thomas Thrainer
        errs.append(result.fail_msg)
1846 22b7f6f8 Thomas Thrainer
        break
1847 22b7f6f8 Thomas Thrainer
1848 22b7f6f8 Thomas Thrainer
    if errs:
1849 22b7f6f8 Thomas Thrainer
      self.LogWarning("Some disks failed to copy, aborting")
1850 22b7f6f8 Thomas Thrainer
      try:
1851 d0d7d7cf Thomas Thrainer
        RemoveDisks(self, self.instance, target_node_uuid=target_node.uuid)
1852 22b7f6f8 Thomas Thrainer
      finally:
1853 da4a52a3 Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(self.instance.uuid)
1854 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Errors during disk copy: %s" %
1855 22b7f6f8 Thomas Thrainer
                                 (",".join(errs),))
1856 22b7f6f8 Thomas Thrainer
1857 d0d7d7cf Thomas Thrainer
    self.instance.primary_node = target_node.uuid
1858 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
1859 22b7f6f8 Thomas Thrainer
1860 22b7f6f8 Thomas Thrainer
    self.LogInfo("Removing the disks on the original node")
1861 d0d7d7cf Thomas Thrainer
    RemoveDisks(self, self.instance, target_node_uuid=source_node.uuid)
1862 22b7f6f8 Thomas Thrainer
1863 22b7f6f8 Thomas Thrainer
    # Only start the instance if it's marked as up
1864 d0d7d7cf Thomas Thrainer
    if self.instance.admin_state == constants.ADMINST_UP:
1865 22b7f6f8 Thomas Thrainer
      self.LogInfo("Starting instance %s on node %s",
1866 d0d7d7cf Thomas Thrainer
                   self.instance.name, target_node.name)
1867 22b7f6f8 Thomas Thrainer
1868 d0d7d7cf Thomas Thrainer
      disks_ok, _ = AssembleInstanceDisks(self, self.instance,
1869 5eacbcae Thomas Thrainer
                                          ignore_secondaries=True)
1870 22b7f6f8 Thomas Thrainer
      if not disks_ok:
1871 d0d7d7cf Thomas Thrainer
        ShutdownInstanceDisks(self, self.instance)
1872 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Can't activate the instance's disks")
1873 22b7f6f8 Thomas Thrainer
1874 1c3231aa Thomas Thrainer
      result = self.rpc.call_instance_start(target_node.uuid,
1875 d0d7d7cf Thomas Thrainer
                                            (self.instance, None, None), False,
1876 22b7f6f8 Thomas Thrainer
                                            self.op.reason)
1877 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
1878 22b7f6f8 Thomas Thrainer
      if msg:
1879 d0d7d7cf Thomas Thrainer
        ShutdownInstanceDisks(self, self.instance)
1880 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not start instance %s on node %s: %s" %
1881 d0d7d7cf Thomas Thrainer
                                 (self.instance.name, target_node.name, msg))
1882 22b7f6f8 Thomas Thrainer
1883 22b7f6f8 Thomas Thrainer
1884 22b7f6f8 Thomas Thrainer
class LUInstanceMultiAlloc(NoHooksLU):
1885 22b7f6f8 Thomas Thrainer
  """Allocates multiple instances at the same time.
1886 22b7f6f8 Thomas Thrainer

1887 22b7f6f8 Thomas Thrainer
  """
1888 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1889 22b7f6f8 Thomas Thrainer
1890 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1891 22b7f6f8 Thomas Thrainer
    """Check arguments.
1892 22b7f6f8 Thomas Thrainer

1893 22b7f6f8 Thomas Thrainer
    """
1894 22b7f6f8 Thomas Thrainer
    nodes = []
1895 22b7f6f8 Thomas Thrainer
    for inst in self.op.instances:
1896 22b7f6f8 Thomas Thrainer
      if inst.iallocator is not None:
1897 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("iallocator are not allowed to be set on"
1898 22b7f6f8 Thomas Thrainer
                                   " instance objects", errors.ECODE_INVAL)
1899 22b7f6f8 Thomas Thrainer
      nodes.append(bool(inst.pnode))
1900 22b7f6f8 Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
1901 22b7f6f8 Thomas Thrainer
        nodes.append(bool(inst.snode))
1902 22b7f6f8 Thomas Thrainer
1903 22b7f6f8 Thomas Thrainer
    has_nodes = compat.any(nodes)
1904 22b7f6f8 Thomas Thrainer
    if compat.all(nodes) ^ has_nodes:
1905 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are instance objects providing"
1906 22b7f6f8 Thomas Thrainer
                                 " pnode/snode while others do not",
1907 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1908 22b7f6f8 Thomas Thrainer
1909 0c072225 Thomas Thrainer
    if not has_nodes and self.op.iallocator is None:
1910 22b7f6f8 Thomas Thrainer
      default_iallocator = self.cfg.GetDefaultIAllocator()
1911 0c072225 Thomas Thrainer
      if default_iallocator:
1912 22b7f6f8 Thomas Thrainer
        self.op.iallocator = default_iallocator
1913 22b7f6f8 Thomas Thrainer
      else:
1914 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No iallocator or nodes on the instances"
1915 22b7f6f8 Thomas Thrainer
                                   " given and no cluster-wide default"
1916 22b7f6f8 Thomas Thrainer
                                   " iallocator found; please specify either"
1917 22b7f6f8 Thomas Thrainer
                                   " an iallocator or nodes on the instances"
1918 22b7f6f8 Thomas Thrainer
                                   " or set a cluster-wide default iallocator",
1919 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1920 22b7f6f8 Thomas Thrainer
1921 22b7f6f8 Thomas Thrainer
    _CheckOpportunisticLocking(self.op)
1922 22b7f6f8 Thomas Thrainer
1923 22b7f6f8 Thomas Thrainer
    dups = utils.FindDuplicates([op.instance_name for op in self.op.instances])
1924 22b7f6f8 Thomas Thrainer
    if dups:
1925 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are duplicate instance names: %s" %
1926 22b7f6f8 Thomas Thrainer
                                 utils.CommaJoin(dups), errors.ECODE_INVAL)
1927 22b7f6f8 Thomas Thrainer
1928 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1929 22b7f6f8 Thomas Thrainer
    """Calculate the locks.
1930 22b7f6f8 Thomas Thrainer

1931 22b7f6f8 Thomas Thrainer
    """
1932 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1933 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
1934 22b7f6f8 Thomas Thrainer
      # iallocator will select nodes and even if no iallocator is used,
1935 22b7f6f8 Thomas Thrainer
      # collisions with LUInstanceCreate should be avoided
1936 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1937 22b7f6f8 Thomas Thrainer
      }
1938 22b7f6f8 Thomas Thrainer
1939 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
1940 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
1941 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = locking.ALL_SET
1942 22b7f6f8 Thomas Thrainer
1943 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
1944 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
1945 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE_RES] = True
1946 22b7f6f8 Thomas Thrainer
    else:
1947 22b7f6f8 Thomas Thrainer
      nodeslist = []
1948 22b7f6f8 Thomas Thrainer
      for inst in self.op.instances:
1949 1c3231aa Thomas Thrainer
        (inst.pnode_uuid, inst.pnode) = \
1950 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, inst.pnode_uuid, inst.pnode)
1951 6869f673 Thomas Thrainer
        nodeslist.append(inst.pnode_uuid)
1952 22b7f6f8 Thomas Thrainer
        if inst.snode is not None:
1953 1c3231aa Thomas Thrainer
          (inst.snode_uuid, inst.snode) = \
1954 1c3231aa Thomas Thrainer
            ExpandNodeUuidAndName(self.cfg, inst.snode_uuid, inst.snode)
1955 6869f673 Thomas Thrainer
          nodeslist.append(inst.snode_uuid)
1956 22b7f6f8 Thomas Thrainer
1957 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodeslist
1958 22b7f6f8 Thomas Thrainer
      # Lock resources of instance's primary and secondary nodes (copy to
1959 22b7f6f8 Thomas Thrainer
      # prevent accidential modification)
1960 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = list(nodeslist)
1961 22b7f6f8 Thomas Thrainer
1962 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1963 22b7f6f8 Thomas Thrainer
    """Check prerequisite.
1964 22b7f6f8 Thomas Thrainer

1965 22b7f6f8 Thomas Thrainer
    """
1966 0c072225 Thomas Thrainer
    if self.op.iallocator:
1967 0c072225 Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
1968 0c072225 Thomas Thrainer
      default_vg = self.cfg.GetVGName()
1969 0c072225 Thomas Thrainer
      ec_id = self.proc.GetECId()
1970 22b7f6f8 Thomas Thrainer
1971 0c072225 Thomas Thrainer
      if self.op.opportunistic_locking:
1972 0c072225 Thomas Thrainer
        # Only consider nodes for which a lock is held
1973 804d72eb Thomas Thrainer
        node_whitelist = self.cfg.GetNodeNames(
1974 804d72eb Thomas Thrainer
                           list(self.owned_locks(locking.LEVEL_NODE)))
1975 0c072225 Thomas Thrainer
      else:
1976 0c072225 Thomas Thrainer
        node_whitelist = None
1977 22b7f6f8 Thomas Thrainer
1978 0c072225 Thomas Thrainer
      insts = [_CreateInstanceAllocRequest(op, ComputeDisks(op, default_vg),
1979 0c072225 Thomas Thrainer
                                           _ComputeNics(op, cluster, None,
1980 0c072225 Thomas Thrainer
                                                        self.cfg, ec_id),
1981 0c072225 Thomas Thrainer
                                           _ComputeFullBeParams(op, cluster),
1982 0c072225 Thomas Thrainer
                                           node_whitelist)
1983 0c072225 Thomas Thrainer
               for op in self.op.instances]
1984 22b7f6f8 Thomas Thrainer
1985 0c072225 Thomas Thrainer
      req = iallocator.IAReqMultiInstanceAlloc(instances=insts)
1986 0c072225 Thomas Thrainer
      ial = iallocator.IAllocator(self.cfg, self.rpc, req)
1987 22b7f6f8 Thomas Thrainer
1988 0c072225 Thomas Thrainer
      ial.Run(self.op.iallocator)
1989 22b7f6f8 Thomas Thrainer
1990 0c072225 Thomas Thrainer
      if not ial.success:
1991 0c072225 Thomas Thrainer
        raise errors.OpPrereqError("Can't compute nodes using"
1992 0c072225 Thomas Thrainer
                                   " iallocator '%s': %s" %
1993 0c072225 Thomas Thrainer
                                   (self.op.iallocator, ial.info),
1994 0c072225 Thomas Thrainer
                                   errors.ECODE_NORES)
1995 22b7f6f8 Thomas Thrainer
1996 0c072225 Thomas Thrainer
      self.ia_result = ial.result
1997 22b7f6f8 Thomas Thrainer
1998 22b7f6f8 Thomas Thrainer
    if self.op.dry_run:
1999 22b7f6f8 Thomas Thrainer
      self.dry_run_result = objects.FillDict(self._ConstructPartialResult(), {
2000 22b7f6f8 Thomas Thrainer
        constants.JOB_IDS_KEY: [],
2001 22b7f6f8 Thomas Thrainer
        })
2002 22b7f6f8 Thomas Thrainer
2003 22b7f6f8 Thomas Thrainer
  def _ConstructPartialResult(self):
2004 22b7f6f8 Thomas Thrainer
    """Contructs the partial result.
2005 22b7f6f8 Thomas Thrainer

2006 22b7f6f8 Thomas Thrainer
    """
2007 0c072225 Thomas Thrainer
    if self.op.iallocator:
2008 0c072225 Thomas Thrainer
      (allocatable, failed_insts) = self.ia_result
2009 0c072225 Thomas Thrainer
      allocatable_insts = map(compat.fst, allocatable)
2010 0c072225 Thomas Thrainer
    else:
2011 0c072225 Thomas Thrainer
      allocatable_insts = [op.instance_name for op in self.op.instances]
2012 0c072225 Thomas Thrainer
      failed_insts = []
2013 0c072225 Thomas Thrainer
2014 22b7f6f8 Thomas Thrainer
    return {
2015 1ca326c8 Thomas Thrainer
      constants.ALLOCATABLE_KEY: allocatable_insts,
2016 1ca326c8 Thomas Thrainer
      constants.FAILED_KEY: failed_insts,
2017 22b7f6f8 Thomas Thrainer
      }
2018 22b7f6f8 Thomas Thrainer
2019 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2020 22b7f6f8 Thomas Thrainer
    """Executes the opcode.
2021 22b7f6f8 Thomas Thrainer

2022 22b7f6f8 Thomas Thrainer
    """
2023 22b7f6f8 Thomas Thrainer
    jobs = []
2024 0c072225 Thomas Thrainer
    if self.op.iallocator:
2025 0c072225 Thomas Thrainer
      op2inst = dict((op.instance_name, op) for op in self.op.instances)
2026 0c072225 Thomas Thrainer
      (allocatable, failed) = self.ia_result
2027 22b7f6f8 Thomas Thrainer
2028 804d72eb Thomas Thrainer
      for (name, node_names) in allocatable:
2029 0c072225 Thomas Thrainer
        op = op2inst.pop(name)
2030 22b7f6f8 Thomas Thrainer
2031 804d72eb Thomas Thrainer
        (op.pnode_uuid, op.pnode) = \
2032 804d72eb Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, None, node_names[0])
2033 804d72eb Thomas Thrainer
        if len(node_names) > 1:
2034 804d72eb Thomas Thrainer
          (op.snode_uuid, op.snode) = \
2035 804d72eb Thomas Thrainer
            ExpandNodeUuidAndName(self.cfg, None, node_names[1])
2036 22b7f6f8 Thomas Thrainer
2037 804d72eb Thomas Thrainer
          jobs.append([op])
2038 22b7f6f8 Thomas Thrainer
2039 804d72eb Thomas Thrainer
        missing = set(op2inst.keys()) - set(failed)
2040 804d72eb Thomas Thrainer
        assert not missing, \
2041 804d72eb Thomas Thrainer
          "Iallocator did return incomplete result: %s" % \
2042 804d72eb Thomas Thrainer
          utils.CommaJoin(missing)
2043 0c072225 Thomas Thrainer
    else:
2044 0c072225 Thomas Thrainer
      jobs.extend([op] for op in self.op.instances)
2045 22b7f6f8 Thomas Thrainer
2046 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs, **self._ConstructPartialResult())
2047 22b7f6f8 Thomas Thrainer
2048 22b7f6f8 Thomas Thrainer
2049 22b7f6f8 Thomas Thrainer
class _InstNicModPrivate:
2050 22b7f6f8 Thomas Thrainer
  """Data structure for network interface modifications.
2051 22b7f6f8 Thomas Thrainer

2052 22b7f6f8 Thomas Thrainer
  Used by L{LUInstanceSetParams}.
2053 22b7f6f8 Thomas Thrainer

2054 22b7f6f8 Thomas Thrainer
  """
2055 22b7f6f8 Thomas Thrainer
  def __init__(self):
2056 22b7f6f8 Thomas Thrainer
    self.params = None
2057 22b7f6f8 Thomas Thrainer
    self.filled = None
2058 22b7f6f8 Thomas Thrainer
2059 22b7f6f8 Thomas Thrainer
2060 5eacbcae Thomas Thrainer
def _PrepareContainerMods(mods, private_fn):
2061 22b7f6f8 Thomas Thrainer
  """Prepares a list of container modifications by adding a private data field.
2062 22b7f6f8 Thomas Thrainer

2063 22b7f6f8 Thomas Thrainer
  @type mods: list of tuples; (operation, index, parameters)
2064 22b7f6f8 Thomas Thrainer
  @param mods: List of modifications
2065 22b7f6f8 Thomas Thrainer
  @type private_fn: callable or None
2066 22b7f6f8 Thomas Thrainer
  @param private_fn: Callable for constructing a private data field for a
2067 22b7f6f8 Thomas Thrainer
    modification
2068 22b7f6f8 Thomas Thrainer
  @rtype: list
2069 22b7f6f8 Thomas Thrainer

2070 22b7f6f8 Thomas Thrainer
  """
2071 22b7f6f8 Thomas Thrainer
  if private_fn is None:
2072 22b7f6f8 Thomas Thrainer
    fn = lambda: None
2073 22b7f6f8 Thomas Thrainer
  else:
2074 22b7f6f8 Thomas Thrainer
    fn = private_fn
2075 22b7f6f8 Thomas Thrainer
2076 22b7f6f8 Thomas Thrainer
  return [(op, idx, params, fn()) for (op, idx, params) in mods]
2077 22b7f6f8 Thomas Thrainer
2078 22b7f6f8 Thomas Thrainer
2079 1c3231aa Thomas Thrainer
def _CheckNodesPhysicalCPUs(lu, node_uuids, requested, hypervisor_specs):
2080 22b7f6f8 Thomas Thrainer
  """Checks if nodes have enough physical CPUs
2081 22b7f6f8 Thomas Thrainer

2082 22b7f6f8 Thomas Thrainer
  This function checks if all given nodes have the needed number of
2083 22b7f6f8 Thomas Thrainer
  physical CPUs. In case any node has less CPUs or we cannot get the
2084 22b7f6f8 Thomas Thrainer
  information from the node, this function raises an OpPrereqError
2085 22b7f6f8 Thomas Thrainer
  exception.
2086 22b7f6f8 Thomas Thrainer

2087 22b7f6f8 Thomas Thrainer
  @type lu: C{LogicalUnit}
2088 22b7f6f8 Thomas Thrainer
  @param lu: a logical unit from which we get configuration data
2089 1c3231aa Thomas Thrainer
  @type node_uuids: C{list}
2090 1c3231aa Thomas Thrainer
  @param node_uuids: the list of node UUIDs to check
2091 22b7f6f8 Thomas Thrainer
  @type requested: C{int}
2092 22b7f6f8 Thomas Thrainer
  @param requested: the minimum acceptable number of physical CPUs
2093 a295eb80 Helga Velroyen
  @type hypervisor_specs: list of pairs (string, dict of strings)
2094 a295eb80 Helga Velroyen
  @param hypervisor_specs: list of hypervisor specifications in
2095 a295eb80 Helga Velroyen
      pairs (hypervisor_name, hvparams)
2096 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node doesn't have enough CPUs,
2097 22b7f6f8 Thomas Thrainer
      or we cannot check the node
2098 22b7f6f8 Thomas Thrainer

2099 22b7f6f8 Thomas Thrainer
  """
2100 da803ff1 Helga Velroyen
  nodeinfo = lu.rpc.call_node_info(node_uuids, None, hypervisor_specs)
2101 1c3231aa Thomas Thrainer
  for node_uuid in node_uuids:
2102 1c3231aa Thomas Thrainer
    info = nodeinfo[node_uuid]
2103 1c3231aa Thomas Thrainer
    node_name = lu.cfg.GetNodeName(node_uuid)
2104 1c3231aa Thomas Thrainer
    info.Raise("Cannot get current information from node %s" % node_name,
2105 22b7f6f8 Thomas Thrainer
               prereq=True, ecode=errors.ECODE_ENVIRON)
2106 22b7f6f8 Thomas Thrainer
    (_, _, (hv_info, )) = info.payload
2107 22b7f6f8 Thomas Thrainer
    num_cpus = hv_info.get("cpu_total", None)
2108 22b7f6f8 Thomas Thrainer
    if not isinstance(num_cpus, int):
2109 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute the number of physical CPUs"
2110 22b7f6f8 Thomas Thrainer
                                 " on node %s, result was '%s'" %
2111 1c3231aa Thomas Thrainer
                                 (node_name, num_cpus), errors.ECODE_ENVIRON)
2112 22b7f6f8 Thomas Thrainer
    if requested > num_cpus:
2113 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Node %s has %s physical CPUs, but %s are "
2114 1c3231aa Thomas Thrainer
                                 "required" % (node_name, num_cpus, requested),
2115 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_NORES)
2116 22b7f6f8 Thomas Thrainer
2117 22b7f6f8 Thomas Thrainer
2118 22b7f6f8 Thomas Thrainer
def GetItemFromContainer(identifier, kind, container):
2119 22b7f6f8 Thomas Thrainer
  """Return the item refered by the identifier.
2120 22b7f6f8 Thomas Thrainer

2121 22b7f6f8 Thomas Thrainer
  @type identifier: string
2122 22b7f6f8 Thomas Thrainer
  @param identifier: Item index or name or UUID
2123 22b7f6f8 Thomas Thrainer
  @type kind: string
2124 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2125 22b7f6f8 Thomas Thrainer
  @type container: list
2126 22b7f6f8 Thomas Thrainer
  @param container: Container to get the item from
2127 22b7f6f8 Thomas Thrainer

2128 22b7f6f8 Thomas Thrainer
  """
2129 22b7f6f8 Thomas Thrainer
  # Index
2130 22b7f6f8 Thomas Thrainer
  try:
2131 22b7f6f8 Thomas Thrainer
    idx = int(identifier)
2132 22b7f6f8 Thomas Thrainer
    if idx == -1:
2133 22b7f6f8 Thomas Thrainer
      # Append
2134 22b7f6f8 Thomas Thrainer
      absidx = len(container) - 1
2135 22b7f6f8 Thomas Thrainer
    elif idx < 0:
2136 22b7f6f8 Thomas Thrainer
      raise IndexError("Not accepting negative indices other than -1")
2137 22b7f6f8 Thomas Thrainer
    elif idx > len(container):
2138 22b7f6f8 Thomas Thrainer
      raise IndexError("Got %s index %s, but there are only %s" %
2139 22b7f6f8 Thomas Thrainer
                       (kind, idx, len(container)))
2140 22b7f6f8 Thomas Thrainer
    else:
2141 22b7f6f8 Thomas Thrainer
      absidx = idx
2142 22b7f6f8 Thomas Thrainer
    return (absidx, container[idx])
2143 22b7f6f8 Thomas Thrainer
  except ValueError:
2144 22b7f6f8 Thomas Thrainer
    pass
2145 22b7f6f8 Thomas Thrainer
2146 22b7f6f8 Thomas Thrainer
  for idx, item in enumerate(container):
2147 22b7f6f8 Thomas Thrainer
    if item.uuid == identifier or item.name == identifier:
2148 22b7f6f8 Thomas Thrainer
      return (idx, item)
2149 22b7f6f8 Thomas Thrainer
2150 22b7f6f8 Thomas Thrainer
  raise errors.OpPrereqError("Cannot find %s with identifier %s" %
2151 22b7f6f8 Thomas Thrainer
                             (kind, identifier), errors.ECODE_NOENT)
2152 22b7f6f8 Thomas Thrainer
2153 22b7f6f8 Thomas Thrainer
2154 5eacbcae Thomas Thrainer
def _ApplyContainerMods(kind, container, chgdesc, mods,
2155 922a9e65 Thomas Thrainer
                        create_fn, modify_fn, remove_fn,
2156 922a9e65 Thomas Thrainer
                        post_add_fn=None):
2157 22b7f6f8 Thomas Thrainer
  """Applies descriptions in C{mods} to C{container}.
2158 22b7f6f8 Thomas Thrainer

2159 22b7f6f8 Thomas Thrainer
  @type kind: string
2160 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2161 22b7f6f8 Thomas Thrainer
  @type container: list
2162 22b7f6f8 Thomas Thrainer
  @param container: Container to modify
2163 22b7f6f8 Thomas Thrainer
  @type chgdesc: None or list
2164 22b7f6f8 Thomas Thrainer
  @param chgdesc: List of applied changes
2165 22b7f6f8 Thomas Thrainer
  @type mods: list
2166 5eacbcae Thomas Thrainer
  @param mods: Modifications as returned by L{_PrepareContainerMods}
2167 22b7f6f8 Thomas Thrainer
  @type create_fn: callable
2168 22b7f6f8 Thomas Thrainer
  @param create_fn: Callback for creating a new item (L{constants.DDM_ADD});
2169 22b7f6f8 Thomas Thrainer
    receives absolute item index, parameters and private data object as added
2170 5eacbcae Thomas Thrainer
    by L{_PrepareContainerMods}, returns tuple containing new item and changes
2171 22b7f6f8 Thomas Thrainer
    as list
2172 22b7f6f8 Thomas Thrainer
  @type modify_fn: callable
2173 22b7f6f8 Thomas Thrainer
  @param modify_fn: Callback for modifying an existing item
2174 22b7f6f8 Thomas Thrainer
    (L{constants.DDM_MODIFY}); receives absolute item index, item, parameters
2175 5eacbcae Thomas Thrainer
    and private data object as added by L{_PrepareContainerMods}, returns
2176 22b7f6f8 Thomas Thrainer
    changes as list
2177 22b7f6f8 Thomas Thrainer
  @type remove_fn: callable
2178 22b7f6f8 Thomas Thrainer
  @param remove_fn: Callback on removing item; receives absolute item index,
2179 5eacbcae Thomas Thrainer
    item and private data object as added by L{_PrepareContainerMods}
2180 922a9e65 Thomas Thrainer
  @type post_add_fn: callable
2181 922a9e65 Thomas Thrainer
  @param post_add_fn: Callable for post-processing a newly created item after
2182 922a9e65 Thomas Thrainer
    it has been put into the container. It receives the index of the new item
2183 922a9e65 Thomas Thrainer
    and the new item as parameters.
2184 22b7f6f8 Thomas Thrainer

2185 22b7f6f8 Thomas Thrainer
  """
2186 22b7f6f8 Thomas Thrainer
  for (op, identifier, params, private) in mods:
2187 22b7f6f8 Thomas Thrainer
    changes = None
2188 22b7f6f8 Thomas Thrainer
2189 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2190 22b7f6f8 Thomas Thrainer
      # Calculate where item will be added
2191 22b7f6f8 Thomas Thrainer
      # When adding an item, identifier can only be an index
2192 22b7f6f8 Thomas Thrainer
      try:
2193 22b7f6f8 Thomas Thrainer
        idx = int(identifier)
2194 22b7f6f8 Thomas Thrainer
      except ValueError:
2195 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Only possitive integer or -1 is accepted as"
2196 22b7f6f8 Thomas Thrainer
                                   " identifier for %s" % constants.DDM_ADD,
2197 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2198 22b7f6f8 Thomas Thrainer
      if idx == -1:
2199 22b7f6f8 Thomas Thrainer
        addidx = len(container)
2200 22b7f6f8 Thomas Thrainer
      else:
2201 22b7f6f8 Thomas Thrainer
        if idx < 0:
2202 22b7f6f8 Thomas Thrainer
          raise IndexError("Not accepting negative indices other than -1")
2203 22b7f6f8 Thomas Thrainer
        elif idx > len(container):
2204 22b7f6f8 Thomas Thrainer
          raise IndexError("Got %s index %s, but there are only %s" %
2205 22b7f6f8 Thomas Thrainer
                           (kind, idx, len(container)))
2206 22b7f6f8 Thomas Thrainer
        addidx = idx
2207 22b7f6f8 Thomas Thrainer
2208 22b7f6f8 Thomas Thrainer
      if create_fn is None:
2209 22b7f6f8 Thomas Thrainer
        item = params
2210 22b7f6f8 Thomas Thrainer
      else:
2211 22b7f6f8 Thomas Thrainer
        (item, changes) = create_fn(addidx, params, private)
2212 22b7f6f8 Thomas Thrainer
2213 22b7f6f8 Thomas Thrainer
      if idx == -1:
2214 22b7f6f8 Thomas Thrainer
        container.append(item)
2215 22b7f6f8 Thomas Thrainer
      else:
2216 22b7f6f8 Thomas Thrainer
        assert idx >= 0
2217 22b7f6f8 Thomas Thrainer
        assert idx <= len(container)
2218 22b7f6f8 Thomas Thrainer
        # list.insert does so before the specified index
2219 22b7f6f8 Thomas Thrainer
        container.insert(idx, item)
2220 922a9e65 Thomas Thrainer
2221 922a9e65 Thomas Thrainer
      if post_add_fn is not None:
2222 922a9e65 Thomas Thrainer
        post_add_fn(addidx, item)
2223 922a9e65 Thomas Thrainer
2224 22b7f6f8 Thomas Thrainer
    else:
2225 22b7f6f8 Thomas Thrainer
      # Retrieve existing item
2226 22b7f6f8 Thomas Thrainer
      (absidx, item) = GetItemFromContainer(identifier, kind, container)
2227 22b7f6f8 Thomas Thrainer
2228 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2229 22b7f6f8 Thomas Thrainer
        assert not params
2230 22b7f6f8 Thomas Thrainer
2231 22b7f6f8 Thomas Thrainer
        changes = [("%s/%s" % (kind, absidx), "remove")]
2232 22b7f6f8 Thomas Thrainer
2233 37046199 Dimitris Aragiorgis
        if remove_fn is not None:
2234 37046199 Dimitris Aragiorgis
          msg = remove_fn(absidx, item, private)
2235 37046199 Dimitris Aragiorgis
          if msg:
2236 37046199 Dimitris Aragiorgis
            changes.append(("%s/%s" % (kind, absidx), msg))
2237 37046199 Dimitris Aragiorgis
2238 22b7f6f8 Thomas Thrainer
        assert container[absidx] == item
2239 22b7f6f8 Thomas Thrainer
        del container[absidx]
2240 22b7f6f8 Thomas Thrainer
      elif op == constants.DDM_MODIFY:
2241 22b7f6f8 Thomas Thrainer
        if modify_fn is not None:
2242 22b7f6f8 Thomas Thrainer
          changes = modify_fn(absidx, item, params, private)
2243 22b7f6f8 Thomas Thrainer
      else:
2244 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
2245 22b7f6f8 Thomas Thrainer
2246 22b7f6f8 Thomas Thrainer
    assert _TApplyContModsCbChanges(changes)
2247 22b7f6f8 Thomas Thrainer
2248 22b7f6f8 Thomas Thrainer
    if not (chgdesc is None or changes is None):
2249 22b7f6f8 Thomas Thrainer
      chgdesc.extend(changes)
2250 22b7f6f8 Thomas Thrainer
2251 22b7f6f8 Thomas Thrainer
2252 22b7f6f8 Thomas Thrainer
def _UpdateIvNames(base_index, disks):
2253 22b7f6f8 Thomas Thrainer
  """Updates the C{iv_name} attribute of disks.
2254 22b7f6f8 Thomas Thrainer

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

2257 22b7f6f8 Thomas Thrainer
  """
2258 22b7f6f8 Thomas Thrainer
  for (idx, disk) in enumerate(disks):
2259 22b7f6f8 Thomas Thrainer
    disk.iv_name = "disk/%s" % (base_index + idx, )
2260 22b7f6f8 Thomas Thrainer
2261 22b7f6f8 Thomas Thrainer
2262 22b7f6f8 Thomas Thrainer
class LUInstanceSetParams(LogicalUnit):
2263 22b7f6f8 Thomas Thrainer
  """Modifies an instances's parameters.
2264 22b7f6f8 Thomas Thrainer

2265 22b7f6f8 Thomas Thrainer
  """
2266 22b7f6f8 Thomas Thrainer
  HPATH = "instance-modify"
2267 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2268 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2269 22b7f6f8 Thomas Thrainer
2270 22b7f6f8 Thomas Thrainer
  @staticmethod
2271 22b7f6f8 Thomas Thrainer
  def _UpgradeDiskNicMods(kind, mods, verify_fn):
2272 22b7f6f8 Thomas Thrainer
    assert ht.TList(mods)
2273 22b7f6f8 Thomas Thrainer
    assert not mods or len(mods[0]) in (2, 3)
2274 22b7f6f8 Thomas Thrainer
2275 22b7f6f8 Thomas Thrainer
    if mods and len(mods[0]) == 2:
2276 22b7f6f8 Thomas Thrainer
      result = []
2277 22b7f6f8 Thomas Thrainer
2278 22b7f6f8 Thomas Thrainer
      addremove = 0
2279 22b7f6f8 Thomas Thrainer
      for op, params in mods:
2280 22b7f6f8 Thomas Thrainer
        if op in (constants.DDM_ADD, constants.DDM_REMOVE):
2281 22b7f6f8 Thomas Thrainer
          result.append((op, -1, params))
2282 22b7f6f8 Thomas Thrainer
          addremove += 1
2283 22b7f6f8 Thomas Thrainer
2284 22b7f6f8 Thomas Thrainer
          if addremove > 1:
2285 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Only one %s add or remove operation is"
2286 22b7f6f8 Thomas Thrainer
                                       " supported at a time" % kind,
2287 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
2288 22b7f6f8 Thomas Thrainer
        else:
2289 22b7f6f8 Thomas Thrainer
          result.append((constants.DDM_MODIFY, op, params))
2290 22b7f6f8 Thomas Thrainer
2291 22b7f6f8 Thomas Thrainer
      assert verify_fn(result)
2292 22b7f6f8 Thomas Thrainer
    else:
2293 22b7f6f8 Thomas Thrainer
      result = mods
2294 22b7f6f8 Thomas Thrainer
2295 22b7f6f8 Thomas Thrainer
    return result
2296 22b7f6f8 Thomas Thrainer
2297 22b7f6f8 Thomas Thrainer
  @staticmethod
2298 22b7f6f8 Thomas Thrainer
  def _CheckMods(kind, mods, key_types, item_fn):
2299 22b7f6f8 Thomas Thrainer
    """Ensures requested disk/NIC modifications are valid.
2300 22b7f6f8 Thomas Thrainer

2301 22b7f6f8 Thomas Thrainer
    """
2302 22b7f6f8 Thomas Thrainer
    for (op, _, params) in mods:
2303 22b7f6f8 Thomas Thrainer
      assert ht.TDict(params)
2304 22b7f6f8 Thomas Thrainer
2305 22b7f6f8 Thomas Thrainer
      # If 'key_types' is an empty dict, we assume we have an
2306 22b7f6f8 Thomas Thrainer
      # 'ext' template and thus do not ForceDictType
2307 22b7f6f8 Thomas Thrainer
      if key_types:
2308 22b7f6f8 Thomas Thrainer
        utils.ForceDictType(params, key_types)
2309 22b7f6f8 Thomas Thrainer
2310 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2311 22b7f6f8 Thomas Thrainer
        if params:
2312 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("No settings should be passed when"
2313 22b7f6f8 Thomas Thrainer
                                     " removing a %s" % kind,
2314 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2315 22b7f6f8 Thomas Thrainer
      elif op in (constants.DDM_ADD, constants.DDM_MODIFY):
2316 22b7f6f8 Thomas Thrainer
        item_fn(op, params)
2317 22b7f6f8 Thomas Thrainer
      else:
2318 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
2319 22b7f6f8 Thomas Thrainer
2320 22b7f6f8 Thomas Thrainer
  @staticmethod
2321 3f3ea14c Bernardo Dal Seno
  def _VerifyDiskModification(op, params, excl_stor):
2322 22b7f6f8 Thomas Thrainer
    """Verifies a disk modification.
2323 22b7f6f8 Thomas Thrainer

2324 22b7f6f8 Thomas Thrainer
    """
2325 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2326 22b7f6f8 Thomas Thrainer
      mode = params.setdefault(constants.IDISK_MODE, constants.DISK_RDWR)
2327 22b7f6f8 Thomas Thrainer
      if mode not in constants.DISK_ACCESS_SET:
2328 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk access mode '%s'" % mode,
2329 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2330 22b7f6f8 Thomas Thrainer
2331 22b7f6f8 Thomas Thrainer
      size = params.get(constants.IDISK_SIZE, None)
2332 22b7f6f8 Thomas Thrainer
      if size is None:
2333 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Required disk parameter '%s' missing" %
2334 22b7f6f8 Thomas Thrainer
                                   constants.IDISK_SIZE, errors.ECODE_INVAL)
2335 34956ece Thomas Thrainer
      size = int(size)
2336 22b7f6f8 Thomas Thrainer
2337 22b7f6f8 Thomas Thrainer
      params[constants.IDISK_SIZE] = size
2338 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2339 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2340 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2341 22b7f6f8 Thomas Thrainer
2342 7c848a6a Bernardo Dal Seno
      CheckSpindlesExclusiveStorage(params, excl_stor, True)
2343 3f3ea14c Bernardo Dal Seno
2344 22b7f6f8 Thomas Thrainer
    elif op == constants.DDM_MODIFY:
2345 22b7f6f8 Thomas Thrainer
      if constants.IDISK_SIZE in params:
2346 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk size change not possible, use"
2347 22b7f6f8 Thomas Thrainer
                                   " grow-disk", errors.ECODE_INVAL)
2348 22b7f6f8 Thomas Thrainer
      if len(params) > 2:
2349 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk modification doesn't support"
2350 22b7f6f8 Thomas Thrainer
                                   " additional arbitrary parameters",
2351 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2352 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2353 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2354 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2355 22b7f6f8 Thomas Thrainer
2356 22b7f6f8 Thomas Thrainer
  @staticmethod
2357 22b7f6f8 Thomas Thrainer
  def _VerifyNicModification(op, params):
2358 22b7f6f8 Thomas Thrainer
    """Verifies a network interface modification.
2359 22b7f6f8 Thomas Thrainer

2360 22b7f6f8 Thomas Thrainer
    """
2361 22b7f6f8 Thomas Thrainer
    if op in (constants.DDM_ADD, constants.DDM_MODIFY):
2362 22b7f6f8 Thomas Thrainer
      ip = params.get(constants.INIC_IP, None)
2363 22b7f6f8 Thomas Thrainer
      name = params.get(constants.INIC_NAME, None)
2364 22b7f6f8 Thomas Thrainer
      req_net = params.get(constants.INIC_NETWORK, None)
2365 22b7f6f8 Thomas Thrainer
      link = params.get(constants.NIC_LINK, None)
2366 22b7f6f8 Thomas Thrainer
      mode = params.get(constants.NIC_MODE, None)
2367 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2368 22b7f6f8 Thomas Thrainer
        params[constants.INIC_NAME] = None
2369 22b7f6f8 Thomas Thrainer
      if req_net is not None:
2370 22b7f6f8 Thomas Thrainer
        if req_net.lower() == constants.VALUE_NONE:
2371 22b7f6f8 Thomas Thrainer
          params[constants.INIC_NETWORK] = None
2372 22b7f6f8 Thomas Thrainer
          req_net = None
2373 22b7f6f8 Thomas Thrainer
        elif link is not None or mode is not None:
2374 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("If network is given"
2375 22b7f6f8 Thomas Thrainer
                                     " mode or link should not",
2376 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2377 22b7f6f8 Thomas Thrainer
2378 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_ADD:
2379 22b7f6f8 Thomas Thrainer
        macaddr = params.get(constants.INIC_MAC, None)
2380 22b7f6f8 Thomas Thrainer
        if macaddr is None:
2381 22b7f6f8 Thomas Thrainer
          params[constants.INIC_MAC] = constants.VALUE_AUTO
2382 22b7f6f8 Thomas Thrainer
2383 22b7f6f8 Thomas Thrainer
      if ip is not None:
2384 22b7f6f8 Thomas Thrainer
        if ip.lower() == constants.VALUE_NONE:
2385 22b7f6f8 Thomas Thrainer
          params[constants.INIC_IP] = None
2386 22b7f6f8 Thomas Thrainer
        else:
2387 22b7f6f8 Thomas Thrainer
          if ip.lower() == constants.NIC_IP_POOL:
2388 22b7f6f8 Thomas Thrainer
            if op == constants.DDM_ADD and req_net is None:
2389 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("If ip=pool, parameter network"
2390 22b7f6f8 Thomas Thrainer
                                         " cannot be none",
2391 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_INVAL)
2392 22b7f6f8 Thomas Thrainer
          else:
2393 22b7f6f8 Thomas Thrainer
            if not netutils.IPAddress.IsValid(ip):
2394 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("Invalid IP address '%s'" % ip,
2395 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_INVAL)
2396 22b7f6f8 Thomas Thrainer
2397 22b7f6f8 Thomas Thrainer
      if constants.INIC_MAC in params:
2398 22b7f6f8 Thomas Thrainer
        macaddr = params[constants.INIC_MAC]
2399 22b7f6f8 Thomas Thrainer
        if macaddr not in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
2400 22b7f6f8 Thomas Thrainer
          macaddr = utils.NormalizeAndValidateMac(macaddr)
2401 22b7f6f8 Thomas Thrainer
2402 22b7f6f8 Thomas Thrainer
        if op == constants.DDM_MODIFY and macaddr == constants.VALUE_AUTO:
2403 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("'auto' is not a valid MAC address when"
2404 22b7f6f8 Thomas Thrainer
                                     " modifying an existing NIC",
2405 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2406 22b7f6f8 Thomas Thrainer
2407 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
2408 22b7f6f8 Thomas Thrainer
    if not (self.op.nics or self.op.disks or self.op.disk_template or
2409 22b7f6f8 Thomas Thrainer
            self.op.hvparams or self.op.beparams or self.op.os_name or
2410 5eae613c Thomas Thrainer
            self.op.osparams or self.op.offline is not None or
2411 5eae613c Thomas Thrainer
            self.op.runtime_mem or self.op.pnode):
2412 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("No changes submitted", errors.ECODE_INVAL)
2413 22b7f6f8 Thomas Thrainer
2414 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
2415 5eacbcae Thomas Thrainer
      CheckParamsNotGlobal(self.op.hvparams, constants.HVC_GLOBALS,
2416 5eacbcae Thomas Thrainer
                           "hypervisor", "instance", "cluster")
2417 22b7f6f8 Thomas Thrainer
2418 22b7f6f8 Thomas Thrainer
    self.op.disks = self._UpgradeDiskNicMods(
2419 72cd5493 Jose A. Lopes
      "disk", self.op.disks, ht.TSetParamsMods(ht.TIDiskParams))
2420 22b7f6f8 Thomas Thrainer
    self.op.nics = self._UpgradeDiskNicMods(
2421 72cd5493 Jose A. Lopes
      "NIC", self.op.nics, ht.TSetParamsMods(ht.TINicParams))
2422 22b7f6f8 Thomas Thrainer
2423 22b7f6f8 Thomas Thrainer
    if self.op.disks and self.op.disk_template is not None:
2424 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk template conversion and other disk"
2425 22b7f6f8 Thomas Thrainer
                                 " changes not supported at the same time",
2426 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2427 22b7f6f8 Thomas Thrainer
2428 22b7f6f8 Thomas Thrainer
    if (self.op.disk_template and
2429 22b7f6f8 Thomas Thrainer
        self.op.disk_template in constants.DTS_INT_MIRROR and
2430 22b7f6f8 Thomas Thrainer
        self.op.remote_node is None):
2431 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Changing the disk template to a mirrored"
2432 22b7f6f8 Thomas Thrainer
                                 " one requires specifying a secondary node",
2433 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2434 22b7f6f8 Thomas Thrainer
2435 22b7f6f8 Thomas Thrainer
    # Check NIC modifications
2436 22b7f6f8 Thomas Thrainer
    self._CheckMods("NIC", self.op.nics, constants.INIC_PARAMS_TYPES,
2437 22b7f6f8 Thomas Thrainer
                    self._VerifyNicModification)
2438 22b7f6f8 Thomas Thrainer
2439 22b7f6f8 Thomas Thrainer
    if self.op.pnode:
2440 1c3231aa Thomas Thrainer
      (self.op.pnode_uuid, self.op.pnode) = \
2441 1c3231aa Thomas Thrainer
        ExpandNodeUuidAndName(self.cfg, self.op.pnode_uuid, self.op.pnode)
2442 22b7f6f8 Thomas Thrainer
2443 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2444 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2445 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODEGROUP] = []
2446 22b7f6f8 Thomas Thrainer
    # Can't even acquire node locks in shared mode as upcoming changes in
2447 22b7f6f8 Thomas Thrainer
    # Ganeti 2.6 will start to modify the node object on disk conversion
2448 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
2449 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
2450 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
2451 22b7f6f8 Thomas Thrainer
    # Look node group to look up the ipolicy
2452 22b7f6f8 Thomas Thrainer
    self.share_locks[locking.LEVEL_NODEGROUP] = 1
2453 22b7f6f8 Thomas Thrainer
2454 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
2455 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
2456 22b7f6f8 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
2457 22b7f6f8 Thomas Thrainer
      # Acquire locks for the instance's nodegroups optimistically. Needs
2458 22b7f6f8 Thomas Thrainer
      # to be verified in CheckPrereq
2459 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = \
2460 da4a52a3 Thomas Thrainer
        self.cfg.GetInstanceNodeGroups(self.op.instance_uuid)
2461 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE:
2462 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
2463 22b7f6f8 Thomas Thrainer
      if self.op.disk_template and self.op.remote_node:
2464 1c3231aa Thomas Thrainer
        (self.op.remote_node_uuid, self.op.remote_node) = \
2465 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, self.op.remote_node_uuid,
2466 1c3231aa Thomas Thrainer
                                self.op.remote_node)
2467 1c3231aa Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE].append(self.op.remote_node_uuid)
2468 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES and self.op.disk_template:
2469 22b7f6f8 Thomas Thrainer
      # Copy node locks
2470 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
2471 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
2472 22b7f6f8 Thomas Thrainer
2473 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
2474 22b7f6f8 Thomas Thrainer
    """Build hooks env.
2475 22b7f6f8 Thomas Thrainer

2476 22b7f6f8 Thomas Thrainer
    This runs on the master, primary and secondaries.
2477 22b7f6f8 Thomas Thrainer

2478 22b7f6f8 Thomas Thrainer
    """
2479 22b7f6f8 Thomas Thrainer
    args = {}
2480 22b7f6f8 Thomas Thrainer
    if constants.BE_MINMEM in self.be_new:
2481 22b7f6f8 Thomas Thrainer
      args["minmem"] = self.be_new[constants.BE_MINMEM]
2482 22b7f6f8 Thomas Thrainer
    if constants.BE_MAXMEM in self.be_new:
2483 22b7f6f8 Thomas Thrainer
      args["maxmem"] = self.be_new[constants.BE_MAXMEM]
2484 22b7f6f8 Thomas Thrainer
    if constants.BE_VCPUS in self.be_new:
2485 22b7f6f8 Thomas Thrainer
      args["vcpus"] = self.be_new[constants.BE_VCPUS]
2486 22b7f6f8 Thomas Thrainer
    # TODO: export disk changes. Note: _BuildInstanceHookEnv* don't export disk
2487 22b7f6f8 Thomas Thrainer
    # information at all.
2488 22b7f6f8 Thomas Thrainer
2489 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
2490 22b7f6f8 Thomas Thrainer
      nics = []
2491 22b7f6f8 Thomas Thrainer
2492 22b7f6f8 Thomas Thrainer
      for nic in self._new_nics:
2493 22b7f6f8 Thomas Thrainer
        n = copy.deepcopy(nic)
2494 22b7f6f8 Thomas Thrainer
        nicparams = self.cluster.SimpleFillNIC(n.nicparams)
2495 22b7f6f8 Thomas Thrainer
        n.nicparams = nicparams
2496 5eacbcae Thomas Thrainer
        nics.append(NICToTuple(self, n))
2497 22b7f6f8 Thomas Thrainer
2498 22b7f6f8 Thomas Thrainer
      args["nics"] = nics
2499 22b7f6f8 Thomas Thrainer
2500 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance, override=args)
2501 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
2502 22b7f6f8 Thomas Thrainer
      env["NEW_DISK_TEMPLATE"] = self.op.disk_template
2503 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
2504 22b7f6f8 Thomas Thrainer
      env["RUNTIME_MEMORY"] = self.op.runtime_mem
2505 22b7f6f8 Thomas Thrainer
2506 22b7f6f8 Thomas Thrainer
    return env
2507 22b7f6f8 Thomas Thrainer
2508 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2509 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2510 22b7f6f8 Thomas Thrainer

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

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

2723 22b7f6f8 Thomas Thrainer
    """
2724 d0d7d7cf Thomas Thrainer
    self.diskparams = self.cfg.GetInstanceDiskParams(self.instance)
2725 22b7f6f8 Thomas Thrainer
2726 3f3ea14c Bernardo Dal Seno
    excl_stor = compat.any(
2727 d0d7d7cf Thomas Thrainer
      rpc.GetExclusiveStorageForNodes(self.cfg,
2728 d0d7d7cf Thomas Thrainer
                                      self.instance.all_nodes).values()
2729 3f3ea14c Bernardo Dal Seno
      )
2730 3f3ea14c Bernardo Dal Seno
2731 22b7f6f8 Thomas Thrainer
    # Check disk modifications. This is done here and not in CheckArguments
2732 22b7f6f8 Thomas Thrainer
    # (as with NICs), because we need to know the instance's disk template
2733 3f3ea14c Bernardo Dal Seno
    ver_fn = lambda op, par: self._VerifyDiskModification(op, par, excl_stor)
2734 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template == constants.DT_EXT:
2735 3f3ea14c Bernardo Dal Seno
      self._CheckMods("disk", self.op.disks, {}, ver_fn)
2736 22b7f6f8 Thomas Thrainer
    else:
2737 22b7f6f8 Thomas Thrainer
      self._CheckMods("disk", self.op.disks, constants.IDISK_PARAMS_TYPES,
2738 3f3ea14c Bernardo Dal Seno
                      ver_fn)
2739 22b7f6f8 Thomas Thrainer
2740 5eacbcae Thomas Thrainer
    self.diskmod = _PrepareContainerMods(self.op.disks, None)
2741 22b7f6f8 Thomas Thrainer
2742 22b7f6f8 Thomas Thrainer
    # Check the validity of the `provider' parameter
2743 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DT_EXT:
2744 22b7f6f8 Thomas Thrainer
      for mod in self.diskmod:
2745 22b7f6f8 Thomas Thrainer
        ext_provider = mod[2].get(constants.IDISK_PROVIDER, None)
2746 22b7f6f8 Thomas Thrainer
        if mod[0] == constants.DDM_ADD:
2747 22b7f6f8 Thomas Thrainer
          if ext_provider is None:
2748 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Instance template is '%s' and parameter"
2749 22b7f6f8 Thomas Thrainer
                                       " '%s' missing, during disk add" %
2750 22b7f6f8 Thomas Thrainer
                                       (constants.DT_EXT,
2751 22b7f6f8 Thomas Thrainer
                                        constants.IDISK_PROVIDER),
2752 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_NOENT)
2753 22b7f6f8 Thomas Thrainer
        elif mod[0] == constants.DDM_MODIFY:
2754 22b7f6f8 Thomas Thrainer
          if ext_provider:
2755 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Parameter '%s' is invalid during disk"
2756 22b7f6f8 Thomas Thrainer
                                       " modification" %
2757 22b7f6f8 Thomas Thrainer
                                       constants.IDISK_PROVIDER,
2758 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
2759 22b7f6f8 Thomas Thrainer
    else:
2760 22b7f6f8 Thomas Thrainer
      for mod in self.diskmod:
2761 22b7f6f8 Thomas Thrainer
        ext_provider = mod[2].get(constants.IDISK_PROVIDER, None)
2762 22b7f6f8 Thomas Thrainer
        if ext_provider is not None:
2763 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("Parameter '%s' is only valid for"
2764 22b7f6f8 Thomas Thrainer
                                     " instances of type '%s'" %
2765 22b7f6f8 Thomas Thrainer
                                     (constants.IDISK_PROVIDER,
2766 22b7f6f8 Thomas Thrainer
                                      constants.DT_EXT),
2767 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2768 22b7f6f8 Thomas Thrainer
2769 3c260845 Thomas Thrainer
    if not self.op.wait_for_sync and self.instance.disks_active:
2770 3c260845 Thomas Thrainer
      for mod in self.diskmod:
2771 3c260845 Thomas Thrainer
        if mod[0] == constants.DDM_ADD:
2772 3c260845 Thomas Thrainer
          raise errors.OpPrereqError("Can't add a disk to an instance with"
2773 3c260845 Thomas Thrainer
                                     " activated disks and"
2774 3c260845 Thomas Thrainer
                                     " --no-wait-for-sync given.",
2775 3c260845 Thomas Thrainer
                                     errors.ECODE_INVAL)
2776 3c260845 Thomas Thrainer
2777 d0d7d7cf Thomas Thrainer
    if self.op.disks and self.instance.disk_template == constants.DT_DISKLESS:
2778 1bb99a33 Bernardo Dal Seno
      raise errors.OpPrereqError("Disk operations not supported for"
2779 1bb99a33 Bernardo Dal Seno
                                 " diskless instances", errors.ECODE_INVAL)
2780 1bb99a33 Bernardo Dal Seno
2781 1bb99a33 Bernardo Dal Seno
    def _PrepareDiskMod(_, disk, params, __):
2782 1bb99a33 Bernardo Dal Seno
      disk.name = params.get(constants.IDISK_NAME, None)
2783 1bb99a33 Bernardo Dal Seno
2784 1bb99a33 Bernardo Dal Seno
    # Verify disk changes (operating on a copy)
2785 d0d7d7cf Thomas Thrainer
    disks = copy.deepcopy(self.instance.disks)
2786 1bb99a33 Bernardo Dal Seno
    _ApplyContainerMods("disk", disks, None, self.diskmod, None,
2787 1bb99a33 Bernardo Dal Seno
                        _PrepareDiskMod, None)
2788 1bb99a33 Bernardo Dal Seno
    utils.ValidateDeviceNames("disk", disks)
2789 1bb99a33 Bernardo Dal Seno
    if len(disks) > constants.MAX_DISKS:
2790 1bb99a33 Bernardo Dal Seno
      raise errors.OpPrereqError("Instance has too many disks (%d), cannot add"
2791 1bb99a33 Bernardo Dal Seno
                                 " more" % constants.MAX_DISKS,
2792 1bb99a33 Bernardo Dal Seno
                                 errors.ECODE_STATE)
2793 d0d7d7cf Thomas Thrainer
    disk_sizes = [disk.size for disk in self.instance.disks]
2794 1bb99a33 Bernardo Dal Seno
    disk_sizes.extend(params["size"] for (op, idx, params, private) in
2795 1bb99a33 Bernardo Dal Seno
                      self.diskmod if op == constants.DDM_ADD)
2796 1bb99a33 Bernardo Dal Seno
    ispec[constants.ISPEC_DISK_COUNT] = len(disk_sizes)
2797 1bb99a33 Bernardo Dal Seno
    ispec[constants.ISPEC_DISK_SIZE] = disk_sizes
2798 1bb99a33 Bernardo Dal Seno
2799 1bb99a33 Bernardo Dal Seno
    if self.op.offline is not None and self.op.offline:
2800 d0d7d7cf Thomas Thrainer
      CheckInstanceState(self, self.instance, CAN_CHANGE_INSTANCE_OFFLINE,
2801 1bb99a33 Bernardo Dal Seno
                         msg="can't change to offline")
2802 1bb99a33 Bernardo Dal Seno
2803 1bb99a33 Bernardo Dal Seno
  def CheckPrereq(self):
2804 1bb99a33 Bernardo Dal Seno
    """Check prerequisites.
2805 1bb99a33 Bernardo Dal Seno

2806 1bb99a33 Bernardo Dal Seno
    This only checks the instance list against the existing names.
2807 1bb99a33 Bernardo Dal Seno

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

3115 22b7f6f8 Thomas Thrainer
    """
3116 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to drbd")
3117 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3118 1c3231aa Thomas Thrainer
    snode_uuid = self.op.remote_node_uuid
3119 22b7f6f8 Thomas Thrainer
3120 d0d7d7cf Thomas Thrainer
    assert self.instance.disk_template == constants.DT_PLAIN
3121 22b7f6f8 Thomas Thrainer
3122 22b7f6f8 Thomas Thrainer
    # create a fake disk info for _GenerateDiskTemplate
3123 22b7f6f8 Thomas Thrainer
    disk_info = [{constants.IDISK_SIZE: d.size, constants.IDISK_MODE: d.mode,
3124 22b7f6f8 Thomas Thrainer
                  constants.IDISK_VG: d.logical_id[0],
3125 22b7f6f8 Thomas Thrainer
                  constants.IDISK_NAME: d.name}
3126 d0d7d7cf Thomas Thrainer
                 for d in self.instance.disks]
3127 5eacbcae Thomas Thrainer
    new_disks = GenerateDiskTemplate(self, self.op.disk_template,
3128 da4a52a3 Thomas Thrainer
                                     self.instance.uuid, pnode_uuid,
3129 d0d7d7cf Thomas Thrainer
                                     [snode_uuid], disk_info, None, None, 0,
3130 d0d7d7cf Thomas Thrainer
                                     feedback_fn, self.diskparams)
3131 0c3d9c7c Thomas Thrainer
    anno_disks = rpc.AnnotateDiskParams(new_disks, self.diskparams)
3132 1c3231aa Thomas Thrainer
    p_excl_stor = IsExclusiveStorageEnabledNodeUuid(self.cfg, pnode_uuid)
3133 1c3231aa Thomas Thrainer
    s_excl_stor = IsExclusiveStorageEnabledNodeUuid(self.cfg, snode_uuid)
3134 d0d7d7cf Thomas Thrainer
    info = GetInstanceInfoText(self.instance)
3135 22b7f6f8 Thomas Thrainer
    feedback_fn("Creating additional volumes...")
3136 22b7f6f8 Thomas Thrainer
    # first, create the missing data and meta devices
3137 22b7f6f8 Thomas Thrainer
    for disk in anno_disks:
3138 22b7f6f8 Thomas Thrainer
      # unfortunately this is... not too nice
3139 d0d7d7cf Thomas Thrainer
      CreateSingleBlockDev(self, pnode_uuid, self.instance, disk.children[1],
3140 5eacbcae Thomas Thrainer
                           info, True, p_excl_stor)
3141 22b7f6f8 Thomas Thrainer
      for child in disk.children:
3142 d0d7d7cf Thomas Thrainer
        CreateSingleBlockDev(self, snode_uuid, self.instance, child, info, True,
3143 5eacbcae Thomas Thrainer
                             s_excl_stor)
3144 22b7f6f8 Thomas Thrainer
    # at this stage, all new LVs have been created, we can rename the
3145 22b7f6f8 Thomas Thrainer
    # old ones
3146 22b7f6f8 Thomas Thrainer
    feedback_fn("Renaming original volumes...")
3147 22b7f6f8 Thomas Thrainer
    rename_list = [(o, n.children[0].logical_id)
3148 d0d7d7cf Thomas Thrainer
                   for (o, n) in zip(self.instance.disks, new_disks)]
3149 1c3231aa Thomas Thrainer
    result = self.rpc.call_blockdev_rename(pnode_uuid, rename_list)
3150 22b7f6f8 Thomas Thrainer
    result.Raise("Failed to rename original LVs")
3151 22b7f6f8 Thomas Thrainer
3152 22b7f6f8 Thomas Thrainer
    feedback_fn("Initializing DRBD devices...")
3153 22b7f6f8 Thomas Thrainer
    # all child devices are in place, we can now create the DRBD devices
3154 22b7f6f8 Thomas Thrainer
    try:
3155 22b7f6f8 Thomas Thrainer
      for disk in anno_disks:
3156 1c3231aa Thomas Thrainer
        for (node_uuid, excl_stor) in [(pnode_uuid, p_excl_stor),
3157 1c3231aa Thomas Thrainer
                                       (snode_uuid, s_excl_stor)]:
3158 1c3231aa Thomas Thrainer
          f_create = node_uuid == pnode_uuid
3159 d0d7d7cf Thomas Thrainer
          CreateSingleBlockDev(self, node_uuid, self.instance, disk, info,
3160 d0d7d7cf Thomas Thrainer
                               f_create, excl_stor)
3161 22b7f6f8 Thomas Thrainer
    except errors.GenericError, e:
3162 22b7f6f8 Thomas Thrainer
      feedback_fn("Initializing of DRBD devices failed;"
3163 22b7f6f8 Thomas Thrainer
                  " renaming back original volumes...")
3164 22b7f6f8 Thomas Thrainer
      rename_back_list = [(n.children[0], o.logical_id)
3165 d0d7d7cf Thomas Thrainer
                          for (n, o) in zip(new_disks, self.instance.disks)]
3166 1c3231aa Thomas Thrainer
      result = self.rpc.call_blockdev_rename(pnode_uuid, rename_back_list)
3167 22b7f6f8 Thomas Thrainer
      result.Raise("Failed to rename LVs back after error %s" % str(e))
3168 22b7f6f8 Thomas Thrainer
      raise
3169 22b7f6f8 Thomas Thrainer
3170 22b7f6f8 Thomas Thrainer
    # at this point, the instance has been modified
3171 d0d7d7cf Thomas Thrainer
    self.instance.disk_template = constants.DT_DRBD8
3172 d0d7d7cf Thomas Thrainer
    self.instance.disks = new_disks
3173 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
3174 22b7f6f8 Thomas Thrainer
3175 22b7f6f8 Thomas Thrainer
    # Release node locks while waiting for sync
3176 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3177 22b7f6f8 Thomas Thrainer
3178 22b7f6f8 Thomas Thrainer
    # disks are created, waiting for sync
3179 d0d7d7cf Thomas Thrainer
    disk_abort = not WaitForSync(self, self.instance,
3180 5eacbcae Thomas Thrainer
                                 oneshot=not self.op.wait_for_sync)
3181 22b7f6f8 Thomas Thrainer
    if disk_abort:
3182 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("There are some degraded disks for"
3183 22b7f6f8 Thomas Thrainer
                               " this instance, please cleanup manually")
3184 22b7f6f8 Thomas Thrainer
3185 22b7f6f8 Thomas Thrainer
    # Node resource locks will be released by caller
3186 22b7f6f8 Thomas Thrainer
3187 22b7f6f8 Thomas Thrainer
  def _ConvertDrbdToPlain(self, feedback_fn):
3188 22b7f6f8 Thomas Thrainer
    """Converts an instance from drbd to plain.
3189 22b7f6f8 Thomas Thrainer

3190 22b7f6f8 Thomas Thrainer
    """
3191 d0d7d7cf Thomas Thrainer
    assert len(self.instance.secondary_nodes) == 1
3192 d0d7d7cf Thomas Thrainer
    assert self.instance.disk_template == constants.DT_DRBD8
3193 22b7f6f8 Thomas Thrainer
3194 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3195 d0d7d7cf Thomas Thrainer
    snode_uuid = self.instance.secondary_nodes[0]
3196 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to plain")
3197 22b7f6f8 Thomas Thrainer
3198 d0d7d7cf Thomas Thrainer
    old_disks = AnnotateDiskParams(self.instance, self.instance.disks, self.cfg)
3199 d0d7d7cf Thomas Thrainer
    new_disks = [d.children[0] for d in self.instance.disks]
3200 22b7f6f8 Thomas Thrainer
3201 22b7f6f8 Thomas Thrainer
    # copy over size, mode and name
3202 22b7f6f8 Thomas Thrainer
    for parent, child in zip(old_disks, new_disks):
3203 22b7f6f8 Thomas Thrainer
      child.size = parent.size
3204 22b7f6f8 Thomas Thrainer
      child.mode = parent.mode
3205 22b7f6f8 Thomas Thrainer
      child.name = parent.name
3206 22b7f6f8 Thomas Thrainer
3207 22b7f6f8 Thomas Thrainer
    # this is a DRBD disk, return its port to the pool
3208 22b7f6f8 Thomas Thrainer
    # NOTE: this must be done right before the call to cfg.Update!
3209 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3210 22b7f6f8 Thomas Thrainer
      tcp_port = disk.logical_id[2]
3211 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(tcp_port)
3212 22b7f6f8 Thomas Thrainer
3213 22b7f6f8 Thomas Thrainer
    # update instance structure
3214 d0d7d7cf Thomas Thrainer
    self.instance.disks = new_disks
3215 d0d7d7cf Thomas Thrainer
    self.instance.disk_template = constants.DT_PLAIN
3216 d0d7d7cf Thomas Thrainer
    _UpdateIvNames(0, self.instance.disks)
3217 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
3218 22b7f6f8 Thomas Thrainer
3219 22b7f6f8 Thomas Thrainer
    # Release locks in case removing disks takes a while
3220 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3221 22b7f6f8 Thomas Thrainer
3222 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing volumes on the secondary node...")
3223 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3224 0c3d9c7c Thomas Thrainer
      result = self.rpc.call_blockdev_remove(snode_uuid, (disk, self.instance))
3225 aefc2f89 Thomas Thrainer
      result.Warn("Could not remove block device %s on node %s,"
3226 aefc2f89 Thomas Thrainer
                  " continuing anyway" %
3227 aefc2f89 Thomas Thrainer
                  (disk.iv_name, self.cfg.GetNodeName(snode_uuid)),
3228 aefc2f89 Thomas Thrainer
                  self.LogWarning)
3229 22b7f6f8 Thomas Thrainer
3230 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing unneeded volumes on the primary node...")
3231 22b7f6f8 Thomas Thrainer
    for idx, disk in enumerate(old_disks):
3232 22b7f6f8 Thomas Thrainer
      meta = disk.children[1]
3233 0c3d9c7c Thomas Thrainer
      result = self.rpc.call_blockdev_remove(pnode_uuid, (meta, self.instance))
3234 aefc2f89 Thomas Thrainer
      result.Warn("Could not remove metadata for disk %d on node %s,"
3235 aefc2f89 Thomas Thrainer
                  " continuing anyway" %
3236 aefc2f89 Thomas Thrainer
                  (idx, self.cfg.GetNodeName(pnode_uuid)),
3237 aefc2f89 Thomas Thrainer
                  self.LogWarning)
3238 22b7f6f8 Thomas Thrainer
3239 71859b6f Dimitris Aragiorgis
  def _HotplugDevice(self, action, dev_type, device, extra, seq):
3240 71859b6f Dimitris Aragiorgis
    self.LogInfo("Trying to hotplug device...")
3241 37046199 Dimitris Aragiorgis
    msg = "hotplug:"
3242 71859b6f Dimitris Aragiorgis
    result = self.rpc.call_hotplug_device(self.instance.primary_node,
3243 71859b6f Dimitris Aragiorgis
                                          self.instance, action, dev_type,
3244 b786a420 Dimitris Aragiorgis
                                          (device, self.instance),
3245 b786a420 Dimitris Aragiorgis
                                          extra, seq)
3246 71859b6f Dimitris Aragiorgis
    if result.fail_msg:
3247 71859b6f Dimitris Aragiorgis
      self.LogWarning("Could not hotplug device: %s" % result.fail_msg)
3248 71859b6f Dimitris Aragiorgis
      self.LogInfo("Continuing execution..")
3249 37046199 Dimitris Aragiorgis
      msg += "failed"
3250 71859b6f Dimitris Aragiorgis
    else:
3251 71859b6f Dimitris Aragiorgis
      self.LogInfo("Hotplug done.")
3252 37046199 Dimitris Aragiorgis
      msg += "done"
3253 37046199 Dimitris Aragiorgis
    return msg
3254 71859b6f Dimitris Aragiorgis
3255 22b7f6f8 Thomas Thrainer
  def _CreateNewDisk(self, idx, params, _):
3256 22b7f6f8 Thomas Thrainer
    """Creates a new disk.
3257 22b7f6f8 Thomas Thrainer

3258 22b7f6f8 Thomas Thrainer
    """
3259 22b7f6f8 Thomas Thrainer
    # add a new disk
3260 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DTS_FILEBASED:
3261 d0d7d7cf Thomas Thrainer
      (file_driver, file_path) = self.instance.disks[0].logical_id
3262 22b7f6f8 Thomas Thrainer
      file_path = os.path.dirname(file_path)
3263 22b7f6f8 Thomas Thrainer
    else:
3264 22b7f6f8 Thomas Thrainer
      file_driver = file_path = None
3265 22b7f6f8 Thomas Thrainer
3266 22b7f6f8 Thomas Thrainer
    disk = \
3267 d0d7d7cf Thomas Thrainer
      GenerateDiskTemplate(self, self.instance.disk_template,
3268 da4a52a3 Thomas Thrainer
                           self.instance.uuid, self.instance.primary_node,
3269 d0d7d7cf Thomas Thrainer
                           self.instance.secondary_nodes, [params], file_path,
3270 d0d7d7cf Thomas Thrainer
                           file_driver, idx, self.Log, self.diskparams)[0]
3271 22b7f6f8 Thomas Thrainer
3272 d0d7d7cf Thomas Thrainer
    new_disks = CreateDisks(self, self.instance, disks=[disk])
3273 22b7f6f8 Thomas Thrainer
3274 22b7f6f8 Thomas Thrainer
    if self.cluster.prealloc_wipe_disks:
3275 22b7f6f8 Thomas Thrainer
      # Wipe new disk
3276 d0d7d7cf Thomas Thrainer
      WipeOrCleanupDisks(self, self.instance,
3277 a365b47f Bernardo Dal Seno
                         disks=[(idx, disk, 0)],
3278 a365b47f Bernardo Dal Seno
                         cleanup=new_disks)
3279 22b7f6f8 Thomas Thrainer
3280 37046199 Dimitris Aragiorgis
    changes = [
3281 37046199 Dimitris Aragiorgis
      ("disk/%d" % idx,
3282 37046199 Dimitris Aragiorgis
      "add:size=%s,mode=%s" % (disk.size, disk.mode)),
3283 37046199 Dimitris Aragiorgis
      ]
3284 37046199 Dimitris Aragiorgis
3285 71859b6f Dimitris Aragiorgis
    if self.op.hotplug:
3286 71859b6f Dimitris Aragiorgis
      result = self.rpc.call_blockdev_assemble(self.instance.primary_node,
3287 71859b6f Dimitris Aragiorgis
                                               (disk, self.instance),
3288 71859b6f Dimitris Aragiorgis
                                               self.instance.name, True, idx)
3289 71859b6f Dimitris Aragiorgis
      if result.fail_msg:
3290 71859b6f Dimitris Aragiorgis
        self.LogWarning("Can't assemble newly created disk %d: %s",
3291 71859b6f Dimitris Aragiorgis
                        idx, result.fail_msg)
3292 37046199 Dimitris Aragiorgis
        changes.append(("disk/%d" % idx, "assemble:failed"))
3293 71859b6f Dimitris Aragiorgis
      else:
3294 71859b6f Dimitris Aragiorgis
        _, link_name = result.payload
3295 37046199 Dimitris Aragiorgis
        msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
3296 37046199 Dimitris Aragiorgis
                                  constants.HOTPLUG_TARGET_DISK,
3297 37046199 Dimitris Aragiorgis
                                  disk, link_name, idx)
3298 37046199 Dimitris Aragiorgis
        changes.append(("disk/%d" % idx, msg))
3299 71859b6f Dimitris Aragiorgis
3300 37046199 Dimitris Aragiorgis
    return (disk, changes)
3301 22b7f6f8 Thomas Thrainer
3302 922a9e65 Thomas Thrainer
  def _PostAddDisk(self, _, disk):
3303 922a9e65 Thomas Thrainer
    if not WaitForSync(self, self.instance, disks=[disk],
3304 922a9e65 Thomas Thrainer
                       oneshot=not self.op.wait_for_sync):
3305 922a9e65 Thomas Thrainer
      raise errors.OpExecError("Failed to sync disks of %s" %
3306 922a9e65 Thomas Thrainer
                               self.instance.name)
3307 922a9e65 Thomas Thrainer
3308 3c260845 Thomas Thrainer
    # the disk is active at this point, so deactivate it if the instance disks
3309 3c260845 Thomas Thrainer
    # are supposed to be inactive
3310 3c260845 Thomas Thrainer
    if not self.instance.disks_active:
3311 3c260845 Thomas Thrainer
      ShutdownInstanceDisks(self, self.instance, disks=[disk])
3312 3c260845 Thomas Thrainer
3313 22b7f6f8 Thomas Thrainer
  @staticmethod
3314 22b7f6f8 Thomas Thrainer
  def _ModifyDisk(idx, disk, params, _):
3315 22b7f6f8 Thomas Thrainer
    """Modifies a disk.
3316 22b7f6f8 Thomas Thrainer

3317 22b7f6f8 Thomas Thrainer
    """
3318 22b7f6f8 Thomas Thrainer
    changes = []
3319 22b7f6f8 Thomas Thrainer
    mode = params.get(constants.IDISK_MODE, None)
3320 22b7f6f8 Thomas Thrainer
    if mode:
3321 22b7f6f8 Thomas Thrainer
      disk.mode = mode
3322 22b7f6f8 Thomas Thrainer
      changes.append(("disk.mode/%d" % idx, disk.mode))
3323 22b7f6f8 Thomas Thrainer
3324 22b7f6f8 Thomas Thrainer
    name = params.get(constants.IDISK_NAME, None)
3325 22b7f6f8 Thomas Thrainer
    disk.name = name
3326 22b7f6f8 Thomas Thrainer
    changes.append(("disk.name/%d" % idx, disk.name))
3327 22b7f6f8 Thomas Thrainer
3328 22b7f6f8 Thomas Thrainer
    return changes
3329 22b7f6f8 Thomas Thrainer
3330 22b7f6f8 Thomas Thrainer
  def _RemoveDisk(self, idx, root, _):
3331 22b7f6f8 Thomas Thrainer
    """Removes a disk.
3332 22b7f6f8 Thomas Thrainer

3333 22b7f6f8 Thomas Thrainer
    """
3334 37046199 Dimitris Aragiorgis
    hotmsg = ""
3335 71859b6f Dimitris Aragiorgis
    if self.op.hotplug:
3336 37046199 Dimitris Aragiorgis
      hotmsg = self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
3337 37046199 Dimitris Aragiorgis
                                   constants.HOTPLUG_TARGET_DISK,
3338 37046199 Dimitris Aragiorgis
                                   root, None, idx)
3339 71859b6f Dimitris Aragiorgis
      ShutdownInstanceDisks(self, self.instance, [root])
3340 71859b6f Dimitris Aragiorgis
3341 5eacbcae Thomas Thrainer
    (anno_disk,) = AnnotateDiskParams(self.instance, [root], self.cfg)
3342 1c3231aa Thomas Thrainer
    for node_uuid, disk in anno_disk.ComputeNodeTree(
3343 1c3231aa Thomas Thrainer
                             self.instance.primary_node):
3344 0c3d9c7c Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(node_uuid, (disk, self.instance)) \
3345 0c3d9c7c Thomas Thrainer
              .fail_msg
3346 22b7f6f8 Thomas Thrainer
      if msg:
3347 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove disk/%d on node '%s': %s,"
3348 1c3231aa Thomas Thrainer
                        " continuing anyway", idx,
3349 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
3350 22b7f6f8 Thomas Thrainer
3351 22b7f6f8 Thomas Thrainer
    # if this is a DRBD disk, return its port to the pool
3352 22b7f6f8 Thomas Thrainer
    if root.dev_type in constants.LDS_DRBD:
3353 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(root.logical_id[2])
3354 22b7f6f8 Thomas Thrainer
3355 37046199 Dimitris Aragiorgis
    return hotmsg
3356 37046199 Dimitris Aragiorgis
3357 22b7f6f8 Thomas Thrainer
  def _CreateNewNic(self, idx, params, private):
3358 22b7f6f8 Thomas Thrainer
    """Creates data structure for a new network interface.
3359 22b7f6f8 Thomas Thrainer

3360 22b7f6f8 Thomas Thrainer
    """
3361 22b7f6f8 Thomas Thrainer
    mac = params[constants.INIC_MAC]
3362 22b7f6f8 Thomas Thrainer
    ip = params.get(constants.INIC_IP, None)
3363 22b7f6f8 Thomas Thrainer
    net = params.get(constants.INIC_NETWORK, None)
3364 22b7f6f8 Thomas Thrainer
    name = params.get(constants.INIC_NAME, None)
3365 22b7f6f8 Thomas Thrainer
    net_uuid = self.cfg.LookupNetwork(net)
3366 22b7f6f8 Thomas Thrainer
    #TODO: not private.filled?? can a nic have no nicparams??
3367 22b7f6f8 Thomas Thrainer
    nicparams = private.filled
3368 22b7f6f8 Thomas Thrainer
    nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, name=name,
3369 22b7f6f8 Thomas Thrainer
                       nicparams=nicparams)
3370 22b7f6f8 Thomas Thrainer
    nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
3371 22b7f6f8 Thomas Thrainer
3372 37046199 Dimitris Aragiorgis
    changes = [
3373 22b7f6f8 Thomas Thrainer
      ("nic.%d" % idx,
3374 22b7f6f8 Thomas Thrainer
       "add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" %
3375 22b7f6f8 Thomas Thrainer
       (mac, ip, private.filled[constants.NIC_MODE],
3376 71859b6f Dimitris Aragiorgis
       private.filled[constants.NIC_LINK], net)),
3377 71859b6f Dimitris Aragiorgis
      ]
3378 71859b6f Dimitris Aragiorgis
3379 37046199 Dimitris Aragiorgis
    if self.op.hotplug:
3380 37046199 Dimitris Aragiorgis
      msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
3381 37046199 Dimitris Aragiorgis
                                constants.HOTPLUG_TARGET_NIC,
3382 37046199 Dimitris Aragiorgis
                                nobj, None, idx)
3383 37046199 Dimitris Aragiorgis
      changes.append(("nic.%d" % idx, msg))
3384 37046199 Dimitris Aragiorgis
3385 37046199 Dimitris Aragiorgis
    return (nobj, changes)
3386 22b7f6f8 Thomas Thrainer
3387 22b7f6f8 Thomas Thrainer
  def _ApplyNicMods(self, idx, nic, params, private):
3388 22b7f6f8 Thomas Thrainer
    """Modifies a network interface.
3389 22b7f6f8 Thomas Thrainer

3390 22b7f6f8 Thomas Thrainer
    """
3391 22b7f6f8 Thomas Thrainer
    changes = []
3392 22b7f6f8 Thomas Thrainer
3393 22b7f6f8 Thomas Thrainer
    for key in [constants.INIC_MAC, constants.INIC_IP, constants.INIC_NAME]:
3394 22b7f6f8 Thomas Thrainer
      if key in params:
3395 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), params[key]))
3396 22b7f6f8 Thomas Thrainer
        setattr(nic, key, params[key])
3397 22b7f6f8 Thomas Thrainer
3398 22b7f6f8 Thomas Thrainer
    new_net = params.get(constants.INIC_NETWORK, nic.network)
3399 22b7f6f8 Thomas Thrainer
    new_net_uuid = self.cfg.LookupNetwork(new_net)
3400 22b7f6f8 Thomas Thrainer
    if new_net_uuid != nic.network:
3401 22b7f6f8 Thomas Thrainer
      changes.append(("nic.network/%d" % idx, new_net))
3402 22b7f6f8 Thomas Thrainer
      nic.network = new_net_uuid
3403 22b7f6f8 Thomas Thrainer
3404 22b7f6f8 Thomas Thrainer
    if private.filled:
3405 22b7f6f8 Thomas Thrainer
      nic.nicparams = private.filled
3406 22b7f6f8 Thomas Thrainer
3407 22b7f6f8 Thomas Thrainer
      for (key, val) in nic.nicparams.items():
3408 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), val))
3409 22b7f6f8 Thomas Thrainer
3410 71859b6f Dimitris Aragiorgis
    if self.op.hotplug:
3411 37046199 Dimitris Aragiorgis
      msg = self._HotplugDevice(constants.HOTPLUG_ACTION_MODIFY,
3412 37046199 Dimitris Aragiorgis
                                constants.HOTPLUG_TARGET_NIC,
3413 37046199 Dimitris Aragiorgis
                                nic, None, idx)
3414 37046199 Dimitris Aragiorgis
      changes.append(("nic/%d" % idx, msg))
3415 71859b6f Dimitris Aragiorgis
3416 22b7f6f8 Thomas Thrainer
    return changes
3417 22b7f6f8 Thomas Thrainer
3418 71859b6f Dimitris Aragiorgis
  def _RemoveNic(self, idx, nic, _):
3419 71859b6f Dimitris Aragiorgis
    if self.op.hotplug:
3420 37046199 Dimitris Aragiorgis
      return self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
3421 37046199 Dimitris Aragiorgis
                                 constants.HOTPLUG_TARGET_NIC,
3422 37046199 Dimitris Aragiorgis
                                 nic, None, idx)
3423 71859b6f Dimitris Aragiorgis
3424 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3425 22b7f6f8 Thomas Thrainer
    """Modifies an instance.
3426 22b7f6f8 Thomas Thrainer

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

3429 22b7f6f8 Thomas Thrainer
    """
3430 22b7f6f8 Thomas Thrainer
    # Process here the warnings from CheckPrereq, as we don't have a
3431 22b7f6f8 Thomas Thrainer
    # feedback_fn there.
3432 22b7f6f8 Thomas Thrainer
    # TODO: Replace with self.LogWarning
3433 22b7f6f8 Thomas Thrainer
    for warn in self.warn:
3434 22b7f6f8 Thomas Thrainer
      feedback_fn("WARNING: %s" % warn)
3435 22b7f6f8 Thomas Thrainer
3436 22b7f6f8 Thomas Thrainer
    assert ((self.op.disk_template is None) ^
3437 22b7f6f8 Thomas Thrainer
            bool(self.owned_locks(locking.LEVEL_NODE_RES))), \
3438 22b7f6f8 Thomas Thrainer
      "Not owning any node resource locks"
3439 22b7f6f8 Thomas Thrainer
3440 22b7f6f8 Thomas Thrainer
    result = []
3441 22b7f6f8 Thomas Thrainer
3442 22b7f6f8 Thomas Thrainer
    # New primary node
3443 1c3231aa Thomas Thrainer
    if self.op.pnode_uuid:
3444 d0d7d7cf Thomas Thrainer
      self.instance.primary_node = self.op.pnode_uuid
3445 22b7f6f8 Thomas Thrainer
3446 22b7f6f8 Thomas Thrainer
    # runtime memory
3447 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
3448 d0d7d7cf Thomas Thrainer
      rpcres = self.rpc.call_instance_balloon_memory(self.instance.primary_node,
3449 d0d7d7cf Thomas Thrainer
                                                     self.instance,
3450 22b7f6f8 Thomas Thrainer
                                                     self.op.runtime_mem)
3451 22b7f6f8 Thomas Thrainer
      rpcres.Raise("Cannot modify instance runtime memory")
3452 22b7f6f8 Thomas Thrainer
      result.append(("runtime_memory", self.op.runtime_mem))
3453 22b7f6f8 Thomas Thrainer
3454 22b7f6f8 Thomas Thrainer
    # Apply disk changes
3455 d0d7d7cf Thomas Thrainer
    _ApplyContainerMods("disk", self.instance.disks, result, self.diskmod,
3456 5eacbcae Thomas Thrainer
                        self._CreateNewDisk, self._ModifyDisk,
3457 922a9e65 Thomas Thrainer
                        self._RemoveDisk, post_add_fn=self._PostAddDisk)
3458 d0d7d7cf Thomas Thrainer
    _UpdateIvNames(0, self.instance.disks)
3459 22b7f6f8 Thomas Thrainer
3460 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
3461 22b7f6f8 Thomas Thrainer
      if __debug__:
3462 d0d7d7cf Thomas Thrainer
        check_nodes = set(self.instance.all_nodes)
3463 1c3231aa Thomas Thrainer
        if self.op.remote_node_uuid:
3464 1c3231aa Thomas Thrainer
          check_nodes.add(self.op.remote_node_uuid)
3465 22b7f6f8 Thomas Thrainer
        for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
3466 22b7f6f8 Thomas Thrainer
          owned = self.owned_locks(level)
3467 22b7f6f8 Thomas Thrainer
          assert not (check_nodes - owned), \
3468 22b7f6f8 Thomas Thrainer
            ("Not owning the correct locks, owning %r, expected at least %r" %
3469 22b7f6f8 Thomas Thrainer
             (owned, check_nodes))
3470 22b7f6f8 Thomas Thrainer
3471 d0d7d7cf Thomas Thrainer
      r_shut = ShutdownInstanceDisks(self, self.instance)
3472 22b7f6f8 Thomas Thrainer
      if not r_shut:
3473 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Cannot shutdown instance disks, unable to"
3474 22b7f6f8 Thomas Thrainer
                                 " proceed with disk template conversion")
3475 d0d7d7cf Thomas Thrainer
      mode = (self.instance.disk_template, self.op.disk_template)
3476 22b7f6f8 Thomas Thrainer
      try:
3477 22b7f6f8 Thomas Thrainer
        self._DISK_CONVERSIONS[mode](self, feedback_fn)
3478 22b7f6f8 Thomas Thrainer
      except:
3479 da4a52a3 Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(self.instance.uuid)
3480 22b7f6f8 Thomas Thrainer
        raise
3481 22b7f6f8 Thomas Thrainer
      result.append(("disk_template", self.op.disk_template))
3482 22b7f6f8 Thomas Thrainer
3483 d0d7d7cf Thomas Thrainer
      assert self.instance.disk_template == self.op.disk_template, \
3484 22b7f6f8 Thomas Thrainer
        ("Expected disk template '%s', found '%s'" %
3485 d0d7d7cf Thomas Thrainer
         (self.op.disk_template, self.instance.disk_template))
3486 22b7f6f8 Thomas Thrainer
3487 22b7f6f8 Thomas Thrainer
    # Release node and resource locks if there are any (they might already have
3488 22b7f6f8 Thomas Thrainer
    # been released during disk conversion)
3489 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3490 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE_RES)
3491 22b7f6f8 Thomas Thrainer
3492 22b7f6f8 Thomas Thrainer
    # Apply NIC changes
3493 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
3494 d0d7d7cf Thomas Thrainer
      self.instance.nics = self._new_nics
3495 22b7f6f8 Thomas Thrainer
      result.extend(self._nic_chgdesc)
3496 22b7f6f8 Thomas Thrainer
3497 22b7f6f8 Thomas Thrainer
    # hvparams changes
3498 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
3499 d0d7d7cf Thomas Thrainer
      self.instance.hvparams = self.hv_inst
3500 22b7f6f8 Thomas Thrainer
      for key, val in self.op.hvparams.iteritems():
3501 22b7f6f8 Thomas Thrainer
        result.append(("hv/%s" % key, val))
3502 22b7f6f8 Thomas Thrainer
3503 22b7f6f8 Thomas Thrainer
    # beparams changes
3504 22b7f6f8 Thomas Thrainer
    if self.op.beparams:
3505 d0d7d7cf Thomas Thrainer
      self.instance.beparams = self.be_inst
3506 22b7f6f8 Thomas Thrainer
      for key, val in self.op.beparams.iteritems():
3507 22b7f6f8 Thomas Thrainer
        result.append(("be/%s" % key, val))
3508 22b7f6f8 Thomas Thrainer
3509 22b7f6f8 Thomas Thrainer
    # OS change
3510 22b7f6f8 Thomas Thrainer
    if self.op.os_name:
3511 d0d7d7cf Thomas Thrainer
      self.instance.os = self.op.os_name
3512 22b7f6f8 Thomas Thrainer
3513 22b7f6f8 Thomas Thrainer
    # osparams changes
3514 22b7f6f8 Thomas Thrainer
    if self.op.osparams:
3515 d0d7d7cf Thomas Thrainer
      self.instance.osparams = self.os_inst
3516 22b7f6f8 Thomas Thrainer
      for key, val in self.op.osparams.iteritems():
3517 22b7f6f8 Thomas Thrainer
        result.append(("os/%s" % key, val))
3518 22b7f6f8 Thomas Thrainer
3519 22b7f6f8 Thomas Thrainer
    if self.op.offline is None:
3520 22b7f6f8 Thomas Thrainer
      # Ignore
3521 22b7f6f8 Thomas Thrainer
      pass
3522 22b7f6f8 Thomas Thrainer
    elif self.op.offline:
3523 22b7f6f8 Thomas Thrainer
      # Mark instance as offline
3524 da4a52a3 Thomas Thrainer
      self.cfg.MarkInstanceOffline(self.instance.uuid)
3525 22b7f6f8 Thomas Thrainer
      result.append(("admin_state", constants.ADMINST_OFFLINE))
3526 22b7f6f8 Thomas Thrainer
    else:
3527 22b7f6f8 Thomas Thrainer
      # Mark instance as online, but stopped
3528 da4a52a3 Thomas Thrainer
      self.cfg.MarkInstanceDown(self.instance.uuid)
3529 22b7f6f8 Thomas Thrainer
      result.append(("admin_state", constants.ADMINST_DOWN))
3530 22b7f6f8 Thomas Thrainer
3531 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn, self.proc.GetECId())
3532 22b7f6f8 Thomas Thrainer
3533 22b7f6f8 Thomas Thrainer
    assert not (self.owned_locks(locking.LEVEL_NODE_RES) or
3534 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
3535 22b7f6f8 Thomas Thrainer
      "All node locks should have been released by now"
3536 22b7f6f8 Thomas Thrainer
3537 22b7f6f8 Thomas Thrainer
    return result
3538 22b7f6f8 Thomas Thrainer
3539 22b7f6f8 Thomas Thrainer
  _DISK_CONVERSIONS = {
3540 22b7f6f8 Thomas Thrainer
    (constants.DT_PLAIN, constants.DT_DRBD8): _ConvertPlainToDrbd,
3541 22b7f6f8 Thomas Thrainer
    (constants.DT_DRBD8, constants.DT_PLAIN): _ConvertDrbdToPlain,
3542 22b7f6f8 Thomas Thrainer
    }
3543 22b7f6f8 Thomas Thrainer
3544 22b7f6f8 Thomas Thrainer
3545 22b7f6f8 Thomas Thrainer
class LUInstanceChangeGroup(LogicalUnit):
3546 22b7f6f8 Thomas Thrainer
  HPATH = "instance-change-group"
3547 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
3548 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
3549 22b7f6f8 Thomas Thrainer
3550 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
3551 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
3552 22b7f6f8 Thomas Thrainer
3553 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
3554 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODEGROUP: [],
3555 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE: [],
3556 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
3557 22b7f6f8 Thomas Thrainer
      }
3558 22b7f6f8 Thomas Thrainer
3559 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
3560 22b7f6f8 Thomas Thrainer
3561 22b7f6f8 Thomas Thrainer
    if self.op.target_groups:
3562 22b7f6f8 Thomas Thrainer
      self.req_target_uuids = map(self.cfg.LookupNodeGroup,
3563 22b7f6f8 Thomas Thrainer
                                  self.op.target_groups)
3564 22b7f6f8 Thomas Thrainer
    else:
3565 22b7f6f8 Thomas Thrainer
      self.req_target_uuids = None
3566 22b7f6f8 Thomas Thrainer
3567 5eacbcae Thomas Thrainer
    self.op.iallocator = GetDefaultIAllocator(self.cfg, self.op.iallocator)
3568 22b7f6f8 Thomas Thrainer
3569 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
3570 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
3571 22b7f6f8 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
3572 22b7f6f8 Thomas Thrainer
3573 22b7f6f8 Thomas Thrainer
      if self.req_target_uuids:
3574 22b7f6f8 Thomas Thrainer
        lock_groups = set(self.req_target_uuids)
3575 22b7f6f8 Thomas Thrainer
3576 22b7f6f8 Thomas Thrainer
        # Lock all groups used by instance optimistically; this requires going
3577 22b7f6f8 Thomas Thrainer
        # via the node before it's locked, requiring verification later on
3578 da4a52a3 Thomas Thrainer
        instance_groups = self.cfg.GetInstanceNodeGroups(self.op.instance_uuid)
3579 22b7f6f8 Thomas Thrainer
        lock_groups.update(instance_groups)
3580 22b7f6f8 Thomas Thrainer
      else:
3581 22b7f6f8 Thomas Thrainer
        # No target groups, need to lock all of them
3582 22b7f6f8 Thomas Thrainer
        lock_groups = locking.ALL_SET
3583 22b7f6f8 Thomas Thrainer
3584 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = lock_groups
3585 22b7f6f8 Thomas Thrainer
3586 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE:
3587 22b7f6f8 Thomas Thrainer
      if self.req_target_uuids:
3588 22b7f6f8 Thomas Thrainer
        # Lock all nodes used by instances
3589 22b7f6f8 Thomas Thrainer
        self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
3590 22b7f6f8 Thomas Thrainer
        self._LockInstancesNodes()
3591 22b7f6f8 Thomas Thrainer
3592 22b7f6f8 Thomas Thrainer
        # Lock all nodes in all potential target groups
3593 22b7f6f8 Thomas Thrainer
        lock_groups = (frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) -
3594 da4a52a3 Thomas Thrainer
                       self.cfg.GetInstanceNodeGroups(self.op.instance_uuid))
3595 1c3231aa Thomas Thrainer
        member_nodes = [node_uuid
3596 22b7f6f8 Thomas Thrainer
                        for group in lock_groups
3597 1c3231aa Thomas Thrainer
                        for node_uuid in self.cfg.GetNodeGroup(group).members]
3598 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE].extend(member_nodes)
3599 22b7f6f8 Thomas Thrainer
      else:
3600 22b7f6f8 Thomas Thrainer
        # Lock all nodes as all groups are potential targets
3601 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
3602 22b7f6f8 Thomas Thrainer
3603 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
3604 da4a52a3 Thomas Thrainer
    owned_instance_names = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
3605 22b7f6f8 Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
3606 22b7f6f8 Thomas Thrainer
    owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE))
3607 22b7f6f8 Thomas Thrainer
3608 22b7f6f8 Thomas Thrainer
    assert (self.req_target_uuids is None or
3609 22b7f6f8 Thomas Thrainer
            owned_groups.issuperset(self.req_target_uuids))
3610 da4a52a3 Thomas Thrainer
    assert owned_instance_names == set([self.op.instance_name])
3611 22b7f6f8 Thomas Thrainer
3612 22b7f6f8 Thomas Thrainer
    # Get instance information
3613 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
3614 22b7f6f8 Thomas Thrainer
3615 22b7f6f8 Thomas Thrainer
    # Check if node groups for locked instance are still correct
3616 22b7f6f8 Thomas Thrainer
    assert owned_nodes.issuperset(self.instance.all_nodes), \
3617 22b7f6f8 Thomas Thrainer
      ("Instance %s's nodes changed while we kept the lock" %
3618 22b7f6f8 Thomas Thrainer
       self.op.instance_name)
3619 22b7f6f8 Thomas Thrainer
3620 da4a52a3 Thomas Thrainer
    inst_groups = CheckInstanceNodeGroups(self.cfg, self.op.instance_uuid,
3621 5eacbcae Thomas Thrainer
                                          owned_groups)
3622 22b7f6f8 Thomas Thrainer
3623 22b7f6f8 Thomas Thrainer
    if self.req_target_uuids:
3624 22b7f6f8 Thomas Thrainer
      # User requested specific target groups
3625 22b7f6f8 Thomas Thrainer
      self.target_uuids = frozenset(self.req_target_uuids)
3626 22b7f6f8 Thomas Thrainer
    else:
3627 22b7f6f8 Thomas Thrainer
      # All groups except those used by the instance are potential targets
3628 22b7f6f8 Thomas Thrainer
      self.target_uuids = owned_groups - inst_groups
3629 22b7f6f8 Thomas Thrainer
3630 22b7f6f8 Thomas Thrainer
    conflicting_groups = self.target_uuids & inst_groups
3631 22b7f6f8 Thomas Thrainer
    if conflicting_groups:
3632 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't use group(s) '%s' as targets, they are"
3633 22b7f6f8 Thomas Thrainer
                                 " used by the instance '%s'" %
3634 22b7f6f8 Thomas Thrainer
                                 (utils.CommaJoin(conflicting_groups),
3635 22b7f6f8 Thomas Thrainer
                                  self.op.instance_name),
3636 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3637 22b7f6f8 Thomas Thrainer
3638 22b7f6f8 Thomas Thrainer
    if not self.target_uuids:
3639 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are no possible target groups",
3640 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3641 22b7f6f8 Thomas Thrainer
3642 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
3643 22b7f6f8 Thomas Thrainer
    """Build hooks env.
3644 22b7f6f8 Thomas Thrainer

3645 22b7f6f8 Thomas Thrainer
    """
3646 22b7f6f8 Thomas Thrainer
    assert self.target_uuids
3647 22b7f6f8 Thomas Thrainer
3648 22b7f6f8 Thomas Thrainer
    env = {
3649 22b7f6f8 Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
3650 22b7f6f8 Thomas Thrainer
      }
3651 22b7f6f8 Thomas Thrainer
3652 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
3653 22b7f6f8 Thomas Thrainer
3654 22b7f6f8 Thomas Thrainer
    return env
3655 22b7f6f8 Thomas Thrainer
3656 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
3657 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
3658 22b7f6f8 Thomas Thrainer

3659 22b7f6f8 Thomas Thrainer
    """
3660 22b7f6f8 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
3661 22b7f6f8 Thomas Thrainer
    return ([mn], [mn])
3662 22b7f6f8 Thomas Thrainer
3663 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3664 22b7f6f8 Thomas Thrainer
    instances = list(self.owned_locks(locking.LEVEL_INSTANCE))
3665 22b7f6f8 Thomas Thrainer
3666 22b7f6f8 Thomas Thrainer
    assert instances == [self.op.instance_name], "Instance not locked"
3667 22b7f6f8 Thomas Thrainer
3668 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=instances,
3669 22b7f6f8 Thomas Thrainer
                                      target_groups=list(self.target_uuids))
3670 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
3671 22b7f6f8 Thomas Thrainer
3672 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
3673 22b7f6f8 Thomas Thrainer
3674 22b7f6f8 Thomas Thrainer
    if not ial.success:
3675 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute solution for changing group of"
3676 22b7f6f8 Thomas Thrainer
                                 " instance '%s' using iallocator '%s': %s" %
3677 22b7f6f8 Thomas Thrainer
                                 (self.op.instance_name, self.op.iallocator,
3678 22b7f6f8 Thomas Thrainer
                                  ial.info), errors.ECODE_NORES)
3679 22b7f6f8 Thomas Thrainer
3680 5eacbcae Thomas Thrainer
    jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
3681 22b7f6f8 Thomas Thrainer
3682 22b7f6f8 Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for changing group of"
3683 22b7f6f8 Thomas Thrainer
                 " instance '%s'", len(jobs), self.op.instance_name)
3684 22b7f6f8 Thomas Thrainer
3685 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs)