Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance.py @ 81c222af

History | View | Annotate | Download (150.1 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

853 22b7f6f8 Thomas Thrainer
    """
854 22b7f6f8 Thomas Thrainer
    # hvparams
855 22b7f6f8 Thomas Thrainer
    hv_defs = cluster.SimpleFillHV(self.op.hypervisor, self.op.os_type, {})
856 22b7f6f8 Thomas Thrainer
    for name in self.op.hvparams.keys():
857 22b7f6f8 Thomas Thrainer
      if name in hv_defs and hv_defs[name] == self.op.hvparams[name]:
858 22b7f6f8 Thomas Thrainer
        del self.op.hvparams[name]
859 22b7f6f8 Thomas Thrainer
    # beparams
860 22b7f6f8 Thomas Thrainer
    be_defs = cluster.SimpleFillBE({})
861 22b7f6f8 Thomas Thrainer
    for name in self.op.beparams.keys():
862 22b7f6f8 Thomas Thrainer
      if name in be_defs and be_defs[name] == self.op.beparams[name]:
863 22b7f6f8 Thomas Thrainer
        del self.op.beparams[name]
864 22b7f6f8 Thomas Thrainer
    # nic params
865 22b7f6f8 Thomas Thrainer
    nic_defs = cluster.SimpleFillNIC({})
866 22b7f6f8 Thomas Thrainer
    for nic in self.op.nics:
867 22b7f6f8 Thomas Thrainer
      for name in constants.NICS_PARAMETERS:
868 22b7f6f8 Thomas Thrainer
        if name in nic and name in nic_defs and nic[name] == nic_defs[name]:
869 22b7f6f8 Thomas Thrainer
          del nic[name]
870 22b7f6f8 Thomas Thrainer
    # osparams
871 22b7f6f8 Thomas Thrainer
    os_defs = cluster.SimpleFillOS(self.op.os_type, {})
872 22b7f6f8 Thomas Thrainer
    for name in self.op.osparams.keys():
873 22b7f6f8 Thomas Thrainer
      if name in os_defs and os_defs[name] == self.op.osparams[name]:
874 22b7f6f8 Thomas Thrainer
        del self.op.osparams[name]
875 22b7f6f8 Thomas Thrainer
876 22b7f6f8 Thomas Thrainer
  def _CalculateFileStorageDir(self):
877 22b7f6f8 Thomas Thrainer
    """Calculate final instance file storage dir.
878 22b7f6f8 Thomas Thrainer

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

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

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

1535 22b7f6f8 Thomas Thrainer
  """
1536 22b7f6f8 Thomas Thrainer
  HPATH = "instance-rename"
1537 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1538 22b7f6f8 Thomas Thrainer
1539 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1540 22b7f6f8 Thomas Thrainer
    """Check arguments.
1541 22b7f6f8 Thomas Thrainer

1542 22b7f6f8 Thomas Thrainer
    """
1543 22b7f6f8 Thomas Thrainer
    if self.op.ip_check and not self.op.name_check:
1544 22b7f6f8 Thomas Thrainer
      # TODO: make the ip check more flexible and not depend on the name check
1545 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("IP address check requires a name check",
1546 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1547 22b7f6f8 Thomas Thrainer
1548 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1549 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1550 22b7f6f8 Thomas Thrainer

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

1553 22b7f6f8 Thomas Thrainer
    """
1554 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1555 22b7f6f8 Thomas Thrainer
    env["INSTANCE_NEW_NAME"] = self.op.new_name
1556 22b7f6f8 Thomas Thrainer
    return env
1557 22b7f6f8 Thomas Thrainer
1558 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1559 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1560 22b7f6f8 Thomas Thrainer

1561 22b7f6f8 Thomas Thrainer
    """
1562 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
1563 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1564 22b7f6f8 Thomas Thrainer
1565 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1566 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1567 22b7f6f8 Thomas Thrainer

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

1570 22b7f6f8 Thomas Thrainer
    """
1571 da4a52a3 Thomas Thrainer
    (self.op.instance_uuid, self.op.instance_name) = \
1572 da4a52a3 Thomas Thrainer
      ExpandInstanceUuidAndName(self.cfg, self.op.instance_uuid,
1573 da4a52a3 Thomas Thrainer
                                self.op.instance_name)
1574 da4a52a3 Thomas Thrainer
    instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1575 22b7f6f8 Thomas Thrainer
    assert instance is not None
1576 1f7c8208 Helga Velroyen
1577 1f7c8208 Helga Velroyen
    # It should actually not happen that an instance is running with a disabled
1578 1f7c8208 Helga Velroyen
    # disk template, but in case it does, the renaming of file-based instances
1579 1f7c8208 Helga Velroyen
    # will fail horribly. Thus, we test it before.
1580 1f7c8208 Helga Velroyen
    if (instance.disk_template in constants.DTS_FILEBASED and
1581 1f7c8208 Helga Velroyen
        self.op.new_name != instance.name):
1582 1f7c8208 Helga Velroyen
      CheckDiskTemplateEnabled(self.cfg.GetClusterInfo(),
1583 1f7c8208 Helga Velroyen
                               instance.disk_template)
1584 1f7c8208 Helga Velroyen
1585 5eacbcae Thomas Thrainer
    CheckNodeOnline(self, instance.primary_node)
1586 5eacbcae Thomas Thrainer
    CheckInstanceState(self, instance, INSTANCE_NOT_RUNNING,
1587 5eacbcae Thomas Thrainer
                       msg="cannot rename")
1588 22b7f6f8 Thomas Thrainer
    self.instance = instance
1589 22b7f6f8 Thomas Thrainer
1590 22b7f6f8 Thomas Thrainer
    new_name = self.op.new_name
1591 22b7f6f8 Thomas Thrainer
    if self.op.name_check:
1592 22b7f6f8 Thomas Thrainer
      hostname = _CheckHostnameSane(self, new_name)
1593 22b7f6f8 Thomas Thrainer
      new_name = self.op.new_name = hostname.name
1594 22b7f6f8 Thomas Thrainer
      if (self.op.ip_check and
1595 22b7f6f8 Thomas Thrainer
          netutils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT)):
1596 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("IP %s of instance %s already in use" %
1597 22b7f6f8 Thomas Thrainer
                                   (hostname.ip, new_name),
1598 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
1599 22b7f6f8 Thomas Thrainer
1600 da4a52a3 Thomas Thrainer
    instance_names = [inst.name for
1601 da4a52a3 Thomas Thrainer
                      inst in self.cfg.GetAllInstancesInfo().values()]
1602 da4a52a3 Thomas Thrainer
    if new_name in instance_names and new_name != instance.name:
1603 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
1604 22b7f6f8 Thomas Thrainer
                                 new_name, errors.ECODE_EXISTS)
1605 22b7f6f8 Thomas Thrainer
1606 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1607 22b7f6f8 Thomas Thrainer
    """Rename the instance.
1608 22b7f6f8 Thomas Thrainer

1609 22b7f6f8 Thomas Thrainer
    """
1610 d0d7d7cf Thomas Thrainer
    old_name = self.instance.name
1611 22b7f6f8 Thomas Thrainer
1612 22b7f6f8 Thomas Thrainer
    rename_file_storage = False
1613 845b7ed1 Santi Raffa
    if (self.instance.disk_template in (constants.DT_FILE,
1614 845b7ed1 Santi Raffa
                                        constants.DT_SHARED_FILE) and
1615 d0d7d7cf Thomas Thrainer
        self.op.new_name != self.instance.name):
1616 d0d7d7cf Thomas Thrainer
      old_file_storage_dir = os.path.dirname(
1617 d0d7d7cf Thomas Thrainer
                               self.instance.disks[0].logical_id[1])
1618 22b7f6f8 Thomas Thrainer
      rename_file_storage = True
1619 22b7f6f8 Thomas Thrainer
1620 da4a52a3 Thomas Thrainer
    self.cfg.RenameInstance(self.instance.uuid, self.op.new_name)
1621 22b7f6f8 Thomas Thrainer
    # Change the instance lock. This is definitely safe while we hold the BGL.
1622 22b7f6f8 Thomas Thrainer
    # Otherwise the new lock would have to be added in acquired mode.
1623 22b7f6f8 Thomas Thrainer
    assert self.REQ_BGL
1624 22b7f6f8 Thomas Thrainer
    assert locking.BGL in self.owned_locks(locking.LEVEL_CLUSTER)
1625 22b7f6f8 Thomas Thrainer
    self.glm.remove(locking.LEVEL_INSTANCE, old_name)
1626 22b7f6f8 Thomas Thrainer
    self.glm.add(locking.LEVEL_INSTANCE, self.op.new_name)
1627 22b7f6f8 Thomas Thrainer
1628 22b7f6f8 Thomas Thrainer
    # re-read the instance from the configuration after rename
1629 da4a52a3 Thomas Thrainer
    renamed_inst = self.cfg.GetInstanceInfo(self.instance.uuid)
1630 22b7f6f8 Thomas Thrainer
1631 22b7f6f8 Thomas Thrainer
    if rename_file_storage:
1632 d0d7d7cf Thomas Thrainer
      new_file_storage_dir = os.path.dirname(
1633 d0d7d7cf Thomas Thrainer
                               renamed_inst.disks[0].logical_id[1])
1634 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_file_storage_dir_rename(renamed_inst.primary_node,
1635 22b7f6f8 Thomas Thrainer
                                                     old_file_storage_dir,
1636 22b7f6f8 Thomas Thrainer
                                                     new_file_storage_dir)
1637 22b7f6f8 Thomas Thrainer
      result.Raise("Could not rename on node %s directory '%s' to '%s'"
1638 22b7f6f8 Thomas Thrainer
                   " (but the instance has been renamed in Ganeti)" %
1639 d0d7d7cf Thomas Thrainer
                   (self.cfg.GetNodeName(renamed_inst.primary_node),
1640 1c3231aa Thomas Thrainer
                    old_file_storage_dir, new_file_storage_dir))
1641 22b7f6f8 Thomas Thrainer
1642 d0d7d7cf Thomas Thrainer
    StartInstanceDisks(self, renamed_inst, None)
1643 22b7f6f8 Thomas Thrainer
    # update info on disks
1644 d0d7d7cf Thomas Thrainer
    info = GetInstanceInfoText(renamed_inst)
1645 d0d7d7cf Thomas Thrainer
    for (idx, disk) in enumerate(renamed_inst.disks):
1646 d0d7d7cf Thomas Thrainer
      for node_uuid in renamed_inst.all_nodes:
1647 0c3d9c7c Thomas Thrainer
        result = self.rpc.call_blockdev_setinfo(node_uuid,
1648 0c3d9c7c Thomas Thrainer
                                                (disk, renamed_inst), info)
1649 1c3231aa Thomas Thrainer
        result.Warn("Error setting info on node %s for disk %s" %
1650 1c3231aa Thomas Thrainer
                    (self.cfg.GetNodeName(node_uuid), idx), self.LogWarning)
1651 22b7f6f8 Thomas Thrainer
    try:
1652 d0d7d7cf Thomas Thrainer
      result = self.rpc.call_instance_run_rename(renamed_inst.primary_node,
1653 d0d7d7cf Thomas Thrainer
                                                 renamed_inst, old_name,
1654 d0d7d7cf Thomas Thrainer
                                                 self.op.debug_level)
1655 c7dd65be Klaus Aehlig
      result.Warn("Could not run OS rename script for instance %s on node %s"
1656 c7dd65be Klaus Aehlig
                  " (but the instance has been renamed in Ganeti)" %
1657 d0d7d7cf Thomas Thrainer
                  (renamed_inst.name,
1658 d0d7d7cf Thomas Thrainer
                   self.cfg.GetNodeName(renamed_inst.primary_node)),
1659 1c3231aa Thomas Thrainer
                  self.LogWarning)
1660 22b7f6f8 Thomas Thrainer
    finally:
1661 d0d7d7cf Thomas Thrainer
      ShutdownInstanceDisks(self, renamed_inst)
1662 22b7f6f8 Thomas Thrainer
1663 d0d7d7cf Thomas Thrainer
    return renamed_inst.name
1664 22b7f6f8 Thomas Thrainer
1665 22b7f6f8 Thomas Thrainer
1666 22b7f6f8 Thomas Thrainer
class LUInstanceRemove(LogicalUnit):
1667 22b7f6f8 Thomas Thrainer
  """Remove an instance.
1668 22b7f6f8 Thomas Thrainer

1669 22b7f6f8 Thomas Thrainer
  """
1670 22b7f6f8 Thomas Thrainer
  HPATH = "instance-remove"
1671 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1672 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1673 22b7f6f8 Thomas Thrainer
1674 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1675 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1676 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
1677 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1678 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
1679 22b7f6f8 Thomas Thrainer
1680 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1681 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1682 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
1683 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1684 22b7f6f8 Thomas Thrainer
      # Copy node locks
1685 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1686 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1687 22b7f6f8 Thomas Thrainer
1688 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1689 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1690 22b7f6f8 Thomas Thrainer

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

1693 22b7f6f8 Thomas Thrainer
    """
1694 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1695 22b7f6f8 Thomas Thrainer
    env["SHUTDOWN_TIMEOUT"] = self.op.shutdown_timeout
1696 22b7f6f8 Thomas Thrainer
    return env
1697 22b7f6f8 Thomas Thrainer
1698 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1699 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1700 22b7f6f8 Thomas Thrainer

1701 22b7f6f8 Thomas Thrainer
    """
1702 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()]
1703 22b7f6f8 Thomas Thrainer
    nl_post = list(self.instance.all_nodes) + nl
1704 22b7f6f8 Thomas Thrainer
    return (nl, nl_post)
1705 22b7f6f8 Thomas Thrainer
1706 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1707 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1708 22b7f6f8 Thomas Thrainer

1709 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1710 22b7f6f8 Thomas Thrainer

1711 22b7f6f8 Thomas Thrainer
    """
1712 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1713 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1714 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1715 22b7f6f8 Thomas Thrainer
1716 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1717 22b7f6f8 Thomas Thrainer
    """Remove the instance.
1718 22b7f6f8 Thomas Thrainer

1719 22b7f6f8 Thomas Thrainer
    """
1720 d0d7d7cf Thomas Thrainer
    logging.info("Shutting down instance %s on node %s", self.instance.name,
1721 d0d7d7cf Thomas Thrainer
                 self.cfg.GetNodeName(self.instance.primary_node))
1722 22b7f6f8 Thomas Thrainer
1723 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_instance_shutdown(self.instance.primary_node,
1724 d0d7d7cf Thomas Thrainer
                                             self.instance,
1725 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1726 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1727 c7dd65be Klaus Aehlig
    if self.op.ignore_failures:
1728 c7dd65be Klaus Aehlig
      result.Warn("Warning: can't shutdown instance", feedback_fn)
1729 c7dd65be Klaus Aehlig
    else:
1730 c7dd65be Klaus Aehlig
      result.Raise("Could not shutdown instance %s on node %s" %
1731 d0d7d7cf Thomas Thrainer
                   (self.instance.name,
1732 d0d7d7cf Thomas Thrainer
                    self.cfg.GetNodeName(self.instance.primary_node)))
1733 22b7f6f8 Thomas Thrainer
1734 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1735 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1736 d0d7d7cf Thomas Thrainer
    assert not (set(self.instance.all_nodes) -
1737 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
1738 22b7f6f8 Thomas Thrainer
      "Not owning correct locks"
1739 22b7f6f8 Thomas Thrainer
1740 d0d7d7cf Thomas Thrainer
    RemoveInstance(self, feedback_fn, self.instance, self.op.ignore_failures)
1741 22b7f6f8 Thomas Thrainer
1742 22b7f6f8 Thomas Thrainer
1743 22b7f6f8 Thomas Thrainer
class LUInstanceMove(LogicalUnit):
1744 22b7f6f8 Thomas Thrainer
  """Move an instance by data-copying.
1745 22b7f6f8 Thomas Thrainer

1746 22b7f6f8 Thomas Thrainer
  """
1747 22b7f6f8 Thomas Thrainer
  HPATH = "instance-move"
1748 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1749 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1750 22b7f6f8 Thomas Thrainer
1751 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1752 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1753 1c3231aa Thomas Thrainer
    (self.op.target_node_uuid, self.op.target_node) = \
1754 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.target_node_uuid,
1755 1c3231aa Thomas Thrainer
                            self.op.target_node)
1756 b9aae98b Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = [self.op.target_node_uuid]
1757 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1758 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
1759 22b7f6f8 Thomas Thrainer
1760 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1761 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1762 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes(primary_only=True)
1763 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1764 22b7f6f8 Thomas Thrainer
      # Copy node locks
1765 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1766 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1767 22b7f6f8 Thomas Thrainer
1768 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1769 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1770 22b7f6f8 Thomas Thrainer

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

1773 22b7f6f8 Thomas Thrainer
    """
1774 22b7f6f8 Thomas Thrainer
    env = {
1775 22b7f6f8 Thomas Thrainer
      "TARGET_NODE": self.op.target_node,
1776 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
1777 22b7f6f8 Thomas Thrainer
      }
1778 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
1779 22b7f6f8 Thomas Thrainer
    return env
1780 22b7f6f8 Thomas Thrainer
1781 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1782 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1783 22b7f6f8 Thomas Thrainer

1784 22b7f6f8 Thomas Thrainer
    """
1785 22b7f6f8 Thomas Thrainer
    nl = [
1786 22b7f6f8 Thomas Thrainer
      self.cfg.GetMasterNode(),
1787 22b7f6f8 Thomas Thrainer
      self.instance.primary_node,
1788 1c3231aa Thomas Thrainer
      self.op.target_node_uuid,
1789 22b7f6f8 Thomas Thrainer
      ]
1790 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1791 22b7f6f8 Thomas Thrainer
1792 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1793 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1794 22b7f6f8 Thomas Thrainer

1795 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1796 22b7f6f8 Thomas Thrainer

1797 22b7f6f8 Thomas Thrainer
    """
1798 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1799 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1800 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1801 22b7f6f8 Thomas Thrainer
1802 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template not in constants.DTS_COPYABLE:
1803 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk template %s not suitable for copying" %
1804 d0d7d7cf Thomas Thrainer
                                 self.instance.disk_template,
1805 d0d7d7cf Thomas Thrainer
                                 errors.ECODE_STATE)
1806 22b7f6f8 Thomas Thrainer
1807 1c3231aa Thomas Thrainer
    target_node = self.cfg.GetNodeInfo(self.op.target_node_uuid)
1808 1c3231aa Thomas Thrainer
    assert target_node is not None, \
1809 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked node %s" % self.op.target_node
1810 22b7f6f8 Thomas Thrainer
1811 1c3231aa Thomas Thrainer
    self.target_node_uuid = target_node.uuid
1812 d0d7d7cf Thomas Thrainer
    if target_node.uuid == self.instance.primary_node:
1813 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance %s is already on the node %s" %
1814 d0d7d7cf Thomas Thrainer
                                 (self.instance.name, target_node.name),
1815 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
1816 22b7f6f8 Thomas Thrainer
1817 d29f52a6 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
1818 d29f52a6 Thomas Thrainer
    bep = cluster.FillBE(self.instance)
1819 22b7f6f8 Thomas Thrainer
1820 d0d7d7cf Thomas Thrainer
    for idx, dsk in enumerate(self.instance.disks):
1821 cd3b4ff4 Helga Velroyen
      if dsk.dev_type not in (constants.DT_PLAIN, constants.DT_FILE,
1822 8106dd64 Santi Raffa
                              constants.DT_SHARED_FILE, constants.DT_GLUSTER):
1823 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance disk %d has a complex layout,"
1824 22b7f6f8 Thomas Thrainer
                                   " cannot copy" % idx, errors.ECODE_STATE)
1825 22b7f6f8 Thomas Thrainer
1826 1c3231aa Thomas Thrainer
    CheckNodeOnline(self, target_node.uuid)
1827 1c3231aa Thomas Thrainer
    CheckNodeNotDrained(self, target_node.uuid)
1828 1c3231aa Thomas Thrainer
    CheckNodeVmCapable(self, target_node.uuid)
1829 1c3231aa Thomas Thrainer
    group_info = self.cfg.GetNodeGroup(target_node.group)
1830 22b7f6f8 Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster, group_info)
1831 d0d7d7cf Thomas Thrainer
    CheckTargetNodeIPolicy(self, ipolicy, self.instance, target_node, self.cfg,
1832 5eacbcae Thomas Thrainer
                           ignore=self.op.ignore_ipolicy)
1833 22b7f6f8 Thomas Thrainer
1834 d0d7d7cf Thomas Thrainer
    if self.instance.admin_state == constants.ADMINST_UP:
1835 83266db6 Thomas Thrainer
      # check memory requirements on the target node
1836 a295eb80 Helga Velroyen
      CheckNodeFreeMemory(
1837 1c3231aa Thomas Thrainer
          self, target_node.uuid, "failing over instance %s" %
1838 d0d7d7cf Thomas Thrainer
          self.instance.name, bep[constants.BE_MAXMEM],
1839 d0d7d7cf Thomas Thrainer
          self.instance.hypervisor,
1840 d29f52a6 Thomas Thrainer
          cluster.hvparams[self.instance.hypervisor])
1841 22b7f6f8 Thomas Thrainer
    else:
1842 22b7f6f8 Thomas Thrainer
      self.LogInfo("Not checking memory on the secondary node as"
1843 22b7f6f8 Thomas Thrainer
                   " instance will not be started")
1844 22b7f6f8 Thomas Thrainer
1845 22b7f6f8 Thomas Thrainer
    # check bridge existance
1846 d0d7d7cf Thomas Thrainer
    CheckInstanceBridgesExist(self, self.instance, node_uuid=target_node.uuid)
1847 22b7f6f8 Thomas Thrainer
1848 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1849 22b7f6f8 Thomas Thrainer
    """Move an instance.
1850 22b7f6f8 Thomas Thrainer

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

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

1946 22b7f6f8 Thomas Thrainer
  """
1947 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1948 22b7f6f8 Thomas Thrainer
1949 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1950 22b7f6f8 Thomas Thrainer
    """Check arguments.
1951 22b7f6f8 Thomas Thrainer

1952 22b7f6f8 Thomas Thrainer
    """
1953 22b7f6f8 Thomas Thrainer
    nodes = []
1954 22b7f6f8 Thomas Thrainer
    for inst in self.op.instances:
1955 22b7f6f8 Thomas Thrainer
      if inst.iallocator is not None:
1956 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("iallocator are not allowed to be set on"
1957 22b7f6f8 Thomas Thrainer
                                   " instance objects", errors.ECODE_INVAL)
1958 22b7f6f8 Thomas Thrainer
      nodes.append(bool(inst.pnode))
1959 22b7f6f8 Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
1960 22b7f6f8 Thomas Thrainer
        nodes.append(bool(inst.snode))
1961 22b7f6f8 Thomas Thrainer
1962 22b7f6f8 Thomas Thrainer
    has_nodes = compat.any(nodes)
1963 22b7f6f8 Thomas Thrainer
    if compat.all(nodes) ^ has_nodes:
1964 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are instance objects providing"
1965 22b7f6f8 Thomas Thrainer
                                 " pnode/snode while others do not",
1966 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1967 22b7f6f8 Thomas Thrainer
1968 0c072225 Thomas Thrainer
    if not has_nodes and self.op.iallocator is None:
1969 22b7f6f8 Thomas Thrainer
      default_iallocator = self.cfg.GetDefaultIAllocator()
1970 0c072225 Thomas Thrainer
      if default_iallocator:
1971 22b7f6f8 Thomas Thrainer
        self.op.iallocator = default_iallocator
1972 22b7f6f8 Thomas Thrainer
      else:
1973 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No iallocator or nodes on the instances"
1974 22b7f6f8 Thomas Thrainer
                                   " given and no cluster-wide default"
1975 22b7f6f8 Thomas Thrainer
                                   " iallocator found; please specify either"
1976 22b7f6f8 Thomas Thrainer
                                   " an iallocator or nodes on the instances"
1977 22b7f6f8 Thomas Thrainer
                                   " or set a cluster-wide default iallocator",
1978 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
1979 22b7f6f8 Thomas Thrainer
1980 22b7f6f8 Thomas Thrainer
    _CheckOpportunisticLocking(self.op)
1981 22b7f6f8 Thomas Thrainer
1982 22b7f6f8 Thomas Thrainer
    dups = utils.FindDuplicates([op.instance_name for op in self.op.instances])
1983 22b7f6f8 Thomas Thrainer
    if dups:
1984 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are duplicate instance names: %s" %
1985 22b7f6f8 Thomas Thrainer
                                 utils.CommaJoin(dups), errors.ECODE_INVAL)
1986 22b7f6f8 Thomas Thrainer
1987 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1988 22b7f6f8 Thomas Thrainer
    """Calculate the locks.
1989 22b7f6f8 Thomas Thrainer

1990 22b7f6f8 Thomas Thrainer
    """
1991 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1992 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
1993 22b7f6f8 Thomas Thrainer
      # iallocator will select nodes and even if no iallocator is used,
1994 22b7f6f8 Thomas Thrainer
      # collisions with LUInstanceCreate should be avoided
1995 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1996 22b7f6f8 Thomas Thrainer
      }
1997 22b7f6f8 Thomas Thrainer
1998 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
1999 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
2000 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = locking.ALL_SET
2001 22b7f6f8 Thomas Thrainer
2002 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
2003 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
2004 22b7f6f8 Thomas Thrainer
    else:
2005 22b7f6f8 Thomas Thrainer
      nodeslist = []
2006 22b7f6f8 Thomas Thrainer
      for inst in self.op.instances:
2007 1c3231aa Thomas Thrainer
        (inst.pnode_uuid, inst.pnode) = \
2008 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, inst.pnode_uuid, inst.pnode)
2009 6869f673 Thomas Thrainer
        nodeslist.append(inst.pnode_uuid)
2010 22b7f6f8 Thomas Thrainer
        if inst.snode is not None:
2011 1c3231aa Thomas Thrainer
          (inst.snode_uuid, inst.snode) = \
2012 1c3231aa Thomas Thrainer
            ExpandNodeUuidAndName(self.cfg, inst.snode_uuid, inst.snode)
2013 6869f673 Thomas Thrainer
          nodeslist.append(inst.snode_uuid)
2014 22b7f6f8 Thomas Thrainer
2015 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodeslist
2016 22b7f6f8 Thomas Thrainer
      # Lock resources of instance's primary and secondary nodes (copy to
2017 22b7f6f8 Thomas Thrainer
      # prevent accidential modification)
2018 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = list(nodeslist)
2019 22b7f6f8 Thomas Thrainer
2020 4289f617 Thomas Thrainer
  def DeclareLocks(self, level):
2021 4289f617 Thomas Thrainer
    if level == locking.LEVEL_NODE_RES and \
2022 4289f617 Thomas Thrainer
      self.opportunistic_locks[locking.LEVEL_NODE]:
2023 4289f617 Thomas Thrainer
      # Even when using opportunistic locking, we require the same set of
2024 4289f617 Thomas Thrainer
      # NODE_RES locks as we got NODE locks
2025 4289f617 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
2026 4289f617 Thomas Thrainer
        self.owned_locks(locking.LEVEL_NODE)
2027 4289f617 Thomas Thrainer
2028 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2029 22b7f6f8 Thomas Thrainer
    """Check prerequisite.
2030 22b7f6f8 Thomas Thrainer

2031 22b7f6f8 Thomas Thrainer
    """
2032 0c072225 Thomas Thrainer
    if self.op.iallocator:
2033 0c072225 Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
2034 0c072225 Thomas Thrainer
      default_vg = self.cfg.GetVGName()
2035 0c072225 Thomas Thrainer
      ec_id = self.proc.GetECId()
2036 22b7f6f8 Thomas Thrainer
2037 0c072225 Thomas Thrainer
      if self.op.opportunistic_locking:
2038 0c072225 Thomas Thrainer
        # Only consider nodes for which a lock is held
2039 804d72eb Thomas Thrainer
        node_whitelist = self.cfg.GetNodeNames(
2040 804d72eb Thomas Thrainer
                           list(self.owned_locks(locking.LEVEL_NODE)))
2041 0c072225 Thomas Thrainer
      else:
2042 0c072225 Thomas Thrainer
        node_whitelist = None
2043 22b7f6f8 Thomas Thrainer
2044 0c072225 Thomas Thrainer
      insts = [_CreateInstanceAllocRequest(op, ComputeDisks(op, default_vg),
2045 0c072225 Thomas Thrainer
                                           _ComputeNics(op, cluster, None,
2046 0c072225 Thomas Thrainer
                                                        self.cfg, ec_id),
2047 0c072225 Thomas Thrainer
                                           _ComputeFullBeParams(op, cluster),
2048 0c072225 Thomas Thrainer
                                           node_whitelist)
2049 0c072225 Thomas Thrainer
               for op in self.op.instances]
2050 22b7f6f8 Thomas Thrainer
2051 0c072225 Thomas Thrainer
      req = iallocator.IAReqMultiInstanceAlloc(instances=insts)
2052 0c072225 Thomas Thrainer
      ial = iallocator.IAllocator(self.cfg, self.rpc, req)
2053 22b7f6f8 Thomas Thrainer
2054 0c072225 Thomas Thrainer
      ial.Run(self.op.iallocator)
2055 22b7f6f8 Thomas Thrainer
2056 0c072225 Thomas Thrainer
      if not ial.success:
2057 0c072225 Thomas Thrainer
        raise errors.OpPrereqError("Can't compute nodes using"
2058 0c072225 Thomas Thrainer
                                   " iallocator '%s': %s" %
2059 0c072225 Thomas Thrainer
                                   (self.op.iallocator, ial.info),
2060 0c072225 Thomas Thrainer
                                   errors.ECODE_NORES)
2061 22b7f6f8 Thomas Thrainer
2062 0c072225 Thomas Thrainer
      self.ia_result = ial.result
2063 22b7f6f8 Thomas Thrainer
2064 22b7f6f8 Thomas Thrainer
    if self.op.dry_run:
2065 22b7f6f8 Thomas Thrainer
      self.dry_run_result = objects.FillDict(self._ConstructPartialResult(), {
2066 22b7f6f8 Thomas Thrainer
        constants.JOB_IDS_KEY: [],
2067 22b7f6f8 Thomas Thrainer
        })
2068 22b7f6f8 Thomas Thrainer
2069 22b7f6f8 Thomas Thrainer
  def _ConstructPartialResult(self):
2070 22b7f6f8 Thomas Thrainer
    """Contructs the partial result.
2071 22b7f6f8 Thomas Thrainer

2072 22b7f6f8 Thomas Thrainer
    """
2073 0c072225 Thomas Thrainer
    if self.op.iallocator:
2074 0c072225 Thomas Thrainer
      (allocatable, failed_insts) = self.ia_result
2075 0c072225 Thomas Thrainer
      allocatable_insts = map(compat.fst, allocatable)
2076 0c072225 Thomas Thrainer
    else:
2077 0c072225 Thomas Thrainer
      allocatable_insts = [op.instance_name for op in self.op.instances]
2078 0c072225 Thomas Thrainer
      failed_insts = []
2079 0c072225 Thomas Thrainer
2080 22b7f6f8 Thomas Thrainer
    return {
2081 1ca326c8 Thomas Thrainer
      constants.ALLOCATABLE_KEY: allocatable_insts,
2082 1ca326c8 Thomas Thrainer
      constants.FAILED_KEY: failed_insts,
2083 22b7f6f8 Thomas Thrainer
      }
2084 22b7f6f8 Thomas Thrainer
2085 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2086 22b7f6f8 Thomas Thrainer
    """Executes the opcode.
2087 22b7f6f8 Thomas Thrainer

2088 22b7f6f8 Thomas Thrainer
    """
2089 22b7f6f8 Thomas Thrainer
    jobs = []
2090 0c072225 Thomas Thrainer
    if self.op.iallocator:
2091 0c072225 Thomas Thrainer
      op2inst = dict((op.instance_name, op) for op in self.op.instances)
2092 0c072225 Thomas Thrainer
      (allocatable, failed) = self.ia_result
2093 22b7f6f8 Thomas Thrainer
2094 804d72eb Thomas Thrainer
      for (name, node_names) in allocatable:
2095 0c072225 Thomas Thrainer
        op = op2inst.pop(name)
2096 22b7f6f8 Thomas Thrainer
2097 804d72eb Thomas Thrainer
        (op.pnode_uuid, op.pnode) = \
2098 804d72eb Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, None, node_names[0])
2099 804d72eb Thomas Thrainer
        if len(node_names) > 1:
2100 804d72eb Thomas Thrainer
          (op.snode_uuid, op.snode) = \
2101 804d72eb Thomas Thrainer
            ExpandNodeUuidAndName(self.cfg, None, node_names[1])
2102 22b7f6f8 Thomas Thrainer
2103 804d72eb Thomas Thrainer
          jobs.append([op])
2104 22b7f6f8 Thomas Thrainer
2105 804d72eb Thomas Thrainer
        missing = set(op2inst.keys()) - set(failed)
2106 804d72eb Thomas Thrainer
        assert not missing, \
2107 804d72eb Thomas Thrainer
          "Iallocator did return incomplete result: %s" % \
2108 804d72eb Thomas Thrainer
          utils.CommaJoin(missing)
2109 0c072225 Thomas Thrainer
    else:
2110 0c072225 Thomas Thrainer
      jobs.extend([op] for op in self.op.instances)
2111 22b7f6f8 Thomas Thrainer
2112 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs, **self._ConstructPartialResult())
2113 22b7f6f8 Thomas Thrainer
2114 22b7f6f8 Thomas Thrainer
2115 22b7f6f8 Thomas Thrainer
class _InstNicModPrivate:
2116 22b7f6f8 Thomas Thrainer
  """Data structure for network interface modifications.
2117 22b7f6f8 Thomas Thrainer

2118 22b7f6f8 Thomas Thrainer
  Used by L{LUInstanceSetParams}.
2119 22b7f6f8 Thomas Thrainer

2120 22b7f6f8 Thomas Thrainer
  """
2121 22b7f6f8 Thomas Thrainer
  def __init__(self):
2122 22b7f6f8 Thomas Thrainer
    self.params = None
2123 22b7f6f8 Thomas Thrainer
    self.filled = None
2124 22b7f6f8 Thomas Thrainer
2125 22b7f6f8 Thomas Thrainer
2126 5eacbcae Thomas Thrainer
def _PrepareContainerMods(mods, private_fn):
2127 22b7f6f8 Thomas Thrainer
  """Prepares a list of container modifications by adding a private data field.
2128 22b7f6f8 Thomas Thrainer

2129 22b7f6f8 Thomas Thrainer
  @type mods: list of tuples; (operation, index, parameters)
2130 22b7f6f8 Thomas Thrainer
  @param mods: List of modifications
2131 22b7f6f8 Thomas Thrainer
  @type private_fn: callable or None
2132 22b7f6f8 Thomas Thrainer
  @param private_fn: Callable for constructing a private data field for a
2133 22b7f6f8 Thomas Thrainer
    modification
2134 22b7f6f8 Thomas Thrainer
  @rtype: list
2135 22b7f6f8 Thomas Thrainer

2136 22b7f6f8 Thomas Thrainer
  """
2137 22b7f6f8 Thomas Thrainer
  if private_fn is None:
2138 22b7f6f8 Thomas Thrainer
    fn = lambda: None
2139 22b7f6f8 Thomas Thrainer
  else:
2140 22b7f6f8 Thomas Thrainer
    fn = private_fn
2141 22b7f6f8 Thomas Thrainer
2142 22b7f6f8 Thomas Thrainer
  return [(op, idx, params, fn()) for (op, idx, params) in mods]
2143 22b7f6f8 Thomas Thrainer
2144 22b7f6f8 Thomas Thrainer
2145 1c3231aa Thomas Thrainer
def _CheckNodesPhysicalCPUs(lu, node_uuids, requested, hypervisor_specs):
2146 22b7f6f8 Thomas Thrainer
  """Checks if nodes have enough physical CPUs
2147 22b7f6f8 Thomas Thrainer

2148 22b7f6f8 Thomas Thrainer
  This function checks if all given nodes have the needed number of
2149 22b7f6f8 Thomas Thrainer
  physical CPUs. In case any node has less CPUs or we cannot get the
2150 22b7f6f8 Thomas Thrainer
  information from the node, this function raises an OpPrereqError
2151 22b7f6f8 Thomas Thrainer
  exception.
2152 22b7f6f8 Thomas Thrainer

2153 22b7f6f8 Thomas Thrainer
  @type lu: C{LogicalUnit}
2154 22b7f6f8 Thomas Thrainer
  @param lu: a logical unit from which we get configuration data
2155 1c3231aa Thomas Thrainer
  @type node_uuids: C{list}
2156 1c3231aa Thomas Thrainer
  @param node_uuids: the list of node UUIDs to check
2157 22b7f6f8 Thomas Thrainer
  @type requested: C{int}
2158 22b7f6f8 Thomas Thrainer
  @param requested: the minimum acceptable number of physical CPUs
2159 a295eb80 Helga Velroyen
  @type hypervisor_specs: list of pairs (string, dict of strings)
2160 a295eb80 Helga Velroyen
  @param hypervisor_specs: list of hypervisor specifications in
2161 a295eb80 Helga Velroyen
      pairs (hypervisor_name, hvparams)
2162 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node doesn't have enough CPUs,
2163 22b7f6f8 Thomas Thrainer
      or we cannot check the node
2164 22b7f6f8 Thomas Thrainer

2165 22b7f6f8 Thomas Thrainer
  """
2166 da803ff1 Helga Velroyen
  nodeinfo = lu.rpc.call_node_info(node_uuids, None, hypervisor_specs)
2167 1c3231aa Thomas Thrainer
  for node_uuid in node_uuids:
2168 1c3231aa Thomas Thrainer
    info = nodeinfo[node_uuid]
2169 1c3231aa Thomas Thrainer
    node_name = lu.cfg.GetNodeName(node_uuid)
2170 1c3231aa Thomas Thrainer
    info.Raise("Cannot get current information from node %s" % node_name,
2171 22b7f6f8 Thomas Thrainer
               prereq=True, ecode=errors.ECODE_ENVIRON)
2172 22b7f6f8 Thomas Thrainer
    (_, _, (hv_info, )) = info.payload
2173 22b7f6f8 Thomas Thrainer
    num_cpus = hv_info.get("cpu_total", None)
2174 22b7f6f8 Thomas Thrainer
    if not isinstance(num_cpus, int):
2175 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute the number of physical CPUs"
2176 22b7f6f8 Thomas Thrainer
                                 " on node %s, result was '%s'" %
2177 1c3231aa Thomas Thrainer
                                 (node_name, num_cpus), errors.ECODE_ENVIRON)
2178 22b7f6f8 Thomas Thrainer
    if requested > num_cpus:
2179 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Node %s has %s physical CPUs, but %s are "
2180 1c3231aa Thomas Thrainer
                                 "required" % (node_name, num_cpus, requested),
2181 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_NORES)
2182 22b7f6f8 Thomas Thrainer
2183 22b7f6f8 Thomas Thrainer
2184 22b7f6f8 Thomas Thrainer
def GetItemFromContainer(identifier, kind, container):
2185 22b7f6f8 Thomas Thrainer
  """Return the item refered by the identifier.
2186 22b7f6f8 Thomas Thrainer

2187 22b7f6f8 Thomas Thrainer
  @type identifier: string
2188 22b7f6f8 Thomas Thrainer
  @param identifier: Item index or name or UUID
2189 22b7f6f8 Thomas Thrainer
  @type kind: string
2190 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2191 22b7f6f8 Thomas Thrainer
  @type container: list
2192 22b7f6f8 Thomas Thrainer
  @param container: Container to get the item from
2193 22b7f6f8 Thomas Thrainer

2194 22b7f6f8 Thomas Thrainer
  """
2195 22b7f6f8 Thomas Thrainer
  # Index
2196 22b7f6f8 Thomas Thrainer
  try:
2197 22b7f6f8 Thomas Thrainer
    idx = int(identifier)
2198 22b7f6f8 Thomas Thrainer
    if idx == -1:
2199 22b7f6f8 Thomas Thrainer
      # Append
2200 22b7f6f8 Thomas Thrainer
      absidx = len(container) - 1
2201 22b7f6f8 Thomas Thrainer
    elif 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
    else:
2207 22b7f6f8 Thomas Thrainer
      absidx = idx
2208 22b7f6f8 Thomas Thrainer
    return (absidx, container[idx])
2209 22b7f6f8 Thomas Thrainer
  except ValueError:
2210 22b7f6f8 Thomas Thrainer
    pass
2211 22b7f6f8 Thomas Thrainer
2212 22b7f6f8 Thomas Thrainer
  for idx, item in enumerate(container):
2213 22b7f6f8 Thomas Thrainer
    if item.uuid == identifier or item.name == identifier:
2214 22b7f6f8 Thomas Thrainer
      return (idx, item)
2215 22b7f6f8 Thomas Thrainer
2216 22b7f6f8 Thomas Thrainer
  raise errors.OpPrereqError("Cannot find %s with identifier %s" %
2217 22b7f6f8 Thomas Thrainer
                             (kind, identifier), errors.ECODE_NOENT)
2218 22b7f6f8 Thomas Thrainer
2219 22b7f6f8 Thomas Thrainer
2220 5eacbcae Thomas Thrainer
def _ApplyContainerMods(kind, container, chgdesc, mods,
2221 922a9e65 Thomas Thrainer
                        create_fn, modify_fn, remove_fn,
2222 922a9e65 Thomas Thrainer
                        post_add_fn=None):
2223 22b7f6f8 Thomas Thrainer
  """Applies descriptions in C{mods} to C{container}.
2224 22b7f6f8 Thomas Thrainer

2225 22b7f6f8 Thomas Thrainer
  @type kind: string
2226 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2227 22b7f6f8 Thomas Thrainer
  @type container: list
2228 22b7f6f8 Thomas Thrainer
  @param container: Container to modify
2229 22b7f6f8 Thomas Thrainer
  @type chgdesc: None or list
2230 22b7f6f8 Thomas Thrainer
  @param chgdesc: List of applied changes
2231 22b7f6f8 Thomas Thrainer
  @type mods: list
2232 5eacbcae Thomas Thrainer
  @param mods: Modifications as returned by L{_PrepareContainerMods}
2233 22b7f6f8 Thomas Thrainer
  @type create_fn: callable
2234 22b7f6f8 Thomas Thrainer
  @param create_fn: Callback for creating a new item (L{constants.DDM_ADD});
2235 22b7f6f8 Thomas Thrainer
    receives absolute item index, parameters and private data object as added
2236 5eacbcae Thomas Thrainer
    by L{_PrepareContainerMods}, returns tuple containing new item and changes
2237 22b7f6f8 Thomas Thrainer
    as list
2238 22b7f6f8 Thomas Thrainer
  @type modify_fn: callable
2239 22b7f6f8 Thomas Thrainer
  @param modify_fn: Callback for modifying an existing item
2240 22b7f6f8 Thomas Thrainer
    (L{constants.DDM_MODIFY}); receives absolute item index, item, parameters
2241 5eacbcae Thomas Thrainer
    and private data object as added by L{_PrepareContainerMods}, returns
2242 22b7f6f8 Thomas Thrainer
    changes as list
2243 22b7f6f8 Thomas Thrainer
  @type remove_fn: callable
2244 22b7f6f8 Thomas Thrainer
  @param remove_fn: Callback on removing item; receives absolute item index,
2245 5eacbcae Thomas Thrainer
    item and private data object as added by L{_PrepareContainerMods}
2246 922a9e65 Thomas Thrainer
  @type post_add_fn: callable
2247 922a9e65 Thomas Thrainer
  @param post_add_fn: Callable for post-processing a newly created item after
2248 922a9e65 Thomas Thrainer
    it has been put into the container. It receives the index of the new item
2249 922a9e65 Thomas Thrainer
    and the new item as parameters.
2250 22b7f6f8 Thomas Thrainer

2251 22b7f6f8 Thomas Thrainer
  """
2252 22b7f6f8 Thomas Thrainer
  for (op, identifier, params, private) in mods:
2253 22b7f6f8 Thomas Thrainer
    changes = None
2254 22b7f6f8 Thomas Thrainer
2255 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2256 22b7f6f8 Thomas Thrainer
      # Calculate where item will be added
2257 22b7f6f8 Thomas Thrainer
      # When adding an item, identifier can only be an index
2258 22b7f6f8 Thomas Thrainer
      try:
2259 22b7f6f8 Thomas Thrainer
        idx = int(identifier)
2260 22b7f6f8 Thomas Thrainer
      except ValueError:
2261 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Only possitive integer or -1 is accepted as"
2262 22b7f6f8 Thomas Thrainer
                                   " identifier for %s" % constants.DDM_ADD,
2263 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2264 22b7f6f8 Thomas Thrainer
      if idx == -1:
2265 22b7f6f8 Thomas Thrainer
        addidx = len(container)
2266 22b7f6f8 Thomas Thrainer
      else:
2267 22b7f6f8 Thomas Thrainer
        if idx < 0:
2268 22b7f6f8 Thomas Thrainer
          raise IndexError("Not accepting negative indices other than -1")
2269 22b7f6f8 Thomas Thrainer
        elif idx > len(container):
2270 22b7f6f8 Thomas Thrainer
          raise IndexError("Got %s index %s, but there are only %s" %
2271 22b7f6f8 Thomas Thrainer
                           (kind, idx, len(container)))
2272 22b7f6f8 Thomas Thrainer
        addidx = idx
2273 22b7f6f8 Thomas Thrainer
2274 22b7f6f8 Thomas Thrainer
      if create_fn is None:
2275 22b7f6f8 Thomas Thrainer
        item = params
2276 22b7f6f8 Thomas Thrainer
      else:
2277 22b7f6f8 Thomas Thrainer
        (item, changes) = create_fn(addidx, params, private)
2278 22b7f6f8 Thomas Thrainer
2279 22b7f6f8 Thomas Thrainer
      if idx == -1:
2280 22b7f6f8 Thomas Thrainer
        container.append(item)
2281 22b7f6f8 Thomas Thrainer
      else:
2282 22b7f6f8 Thomas Thrainer
        assert idx >= 0
2283 22b7f6f8 Thomas Thrainer
        assert idx <= len(container)
2284 22b7f6f8 Thomas Thrainer
        # list.insert does so before the specified index
2285 22b7f6f8 Thomas Thrainer
        container.insert(idx, item)
2286 922a9e65 Thomas Thrainer
2287 922a9e65 Thomas Thrainer
      if post_add_fn is not None:
2288 922a9e65 Thomas Thrainer
        post_add_fn(addidx, item)
2289 922a9e65 Thomas Thrainer
2290 22b7f6f8 Thomas Thrainer
    else:
2291 22b7f6f8 Thomas Thrainer
      # Retrieve existing item
2292 22b7f6f8 Thomas Thrainer
      (absidx, item) = GetItemFromContainer(identifier, kind, container)
2293 22b7f6f8 Thomas Thrainer
2294 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2295 22b7f6f8 Thomas Thrainer
        assert not params
2296 22b7f6f8 Thomas Thrainer
2297 22b7f6f8 Thomas Thrainer
        changes = [("%s/%s" % (kind, absidx), "remove")]
2298 22b7f6f8 Thomas Thrainer
2299 e15a00dc Dimitris Aragiorgis
        if remove_fn is not None:
2300 e15a00dc Dimitris Aragiorgis
          msg = remove_fn(absidx, item, private)
2301 e15a00dc Dimitris Aragiorgis
          if msg:
2302 e15a00dc Dimitris Aragiorgis
            changes.append(("%s/%s" % (kind, absidx), msg))
2303 e15a00dc Dimitris Aragiorgis
2304 22b7f6f8 Thomas Thrainer
        assert container[absidx] == item
2305 22b7f6f8 Thomas Thrainer
        del container[absidx]
2306 22b7f6f8 Thomas Thrainer
      elif op == constants.DDM_MODIFY:
2307 22b7f6f8 Thomas Thrainer
        if modify_fn is not None:
2308 22b7f6f8 Thomas Thrainer
          changes = modify_fn(absidx, item, params, private)
2309 22b7f6f8 Thomas Thrainer
      else:
2310 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
2311 22b7f6f8 Thomas Thrainer
2312 22b7f6f8 Thomas Thrainer
    assert _TApplyContModsCbChanges(changes)
2313 22b7f6f8 Thomas Thrainer
2314 22b7f6f8 Thomas Thrainer
    if not (chgdesc is None or changes is None):
2315 22b7f6f8 Thomas Thrainer
      chgdesc.extend(changes)
2316 22b7f6f8 Thomas Thrainer
2317 22b7f6f8 Thomas Thrainer
2318 22b7f6f8 Thomas Thrainer
def _UpdateIvNames(base_index, disks):
2319 22b7f6f8 Thomas Thrainer
  """Updates the C{iv_name} attribute of disks.
2320 22b7f6f8 Thomas Thrainer

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

2323 22b7f6f8 Thomas Thrainer
  """
2324 22b7f6f8 Thomas Thrainer
  for (idx, disk) in enumerate(disks):
2325 22b7f6f8 Thomas Thrainer
    disk.iv_name = "disk/%s" % (base_index + idx, )
2326 22b7f6f8 Thomas Thrainer
2327 22b7f6f8 Thomas Thrainer
2328 22b7f6f8 Thomas Thrainer
class LUInstanceSetParams(LogicalUnit):
2329 22b7f6f8 Thomas Thrainer
  """Modifies an instances's parameters.
2330 22b7f6f8 Thomas Thrainer

2331 22b7f6f8 Thomas Thrainer
  """
2332 22b7f6f8 Thomas Thrainer
  HPATH = "instance-modify"
2333 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2334 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2335 22b7f6f8 Thomas Thrainer
2336 22b7f6f8 Thomas Thrainer
  @staticmethod
2337 22b7f6f8 Thomas Thrainer
  def _UpgradeDiskNicMods(kind, mods, verify_fn):
2338 22b7f6f8 Thomas Thrainer
    assert ht.TList(mods)
2339 22b7f6f8 Thomas Thrainer
    assert not mods or len(mods[0]) in (2, 3)
2340 22b7f6f8 Thomas Thrainer
2341 22b7f6f8 Thomas Thrainer
    if mods and len(mods[0]) == 2:
2342 22b7f6f8 Thomas Thrainer
      result = []
2343 22b7f6f8 Thomas Thrainer
2344 22b7f6f8 Thomas Thrainer
      addremove = 0
2345 22b7f6f8 Thomas Thrainer
      for op, params in mods:
2346 22b7f6f8 Thomas Thrainer
        if op in (constants.DDM_ADD, constants.DDM_REMOVE):
2347 22b7f6f8 Thomas Thrainer
          result.append((op, -1, params))
2348 22b7f6f8 Thomas Thrainer
          addremove += 1
2349 22b7f6f8 Thomas Thrainer
2350 22b7f6f8 Thomas Thrainer
          if addremove > 1:
2351 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Only one %s add or remove operation is"
2352 22b7f6f8 Thomas Thrainer
                                       " supported at a time" % kind,
2353 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
2354 22b7f6f8 Thomas Thrainer
        else:
2355 22b7f6f8 Thomas Thrainer
          result.append((constants.DDM_MODIFY, op, params))
2356 22b7f6f8 Thomas Thrainer
2357 22b7f6f8 Thomas Thrainer
      assert verify_fn(result)
2358 22b7f6f8 Thomas Thrainer
    else:
2359 22b7f6f8 Thomas Thrainer
      result = mods
2360 22b7f6f8 Thomas Thrainer
2361 22b7f6f8 Thomas Thrainer
    return result
2362 22b7f6f8 Thomas Thrainer
2363 22b7f6f8 Thomas Thrainer
  @staticmethod
2364 22b7f6f8 Thomas Thrainer
  def _CheckMods(kind, mods, key_types, item_fn):
2365 22b7f6f8 Thomas Thrainer
    """Ensures requested disk/NIC modifications are valid.
2366 22b7f6f8 Thomas Thrainer

2367 22b7f6f8 Thomas Thrainer
    """
2368 22b7f6f8 Thomas Thrainer
    for (op, _, params) in mods:
2369 22b7f6f8 Thomas Thrainer
      assert ht.TDict(params)
2370 22b7f6f8 Thomas Thrainer
2371 22b7f6f8 Thomas Thrainer
      # If 'key_types' is an empty dict, we assume we have an
2372 22b7f6f8 Thomas Thrainer
      # 'ext' template and thus do not ForceDictType
2373 22b7f6f8 Thomas Thrainer
      if key_types:
2374 22b7f6f8 Thomas Thrainer
        utils.ForceDictType(params, key_types)
2375 22b7f6f8 Thomas Thrainer
2376 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2377 22b7f6f8 Thomas Thrainer
        if params:
2378 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("No settings should be passed when"
2379 22b7f6f8 Thomas Thrainer
                                     " removing a %s" % kind,
2380 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2381 22b7f6f8 Thomas Thrainer
      elif op in (constants.DDM_ADD, constants.DDM_MODIFY):
2382 22b7f6f8 Thomas Thrainer
        item_fn(op, params)
2383 22b7f6f8 Thomas Thrainer
      else:
2384 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
2385 22b7f6f8 Thomas Thrainer
2386 affe1792 Klaus Aehlig
  def _VerifyDiskModification(self, op, params, excl_stor):
2387 22b7f6f8 Thomas Thrainer
    """Verifies a disk modification.
2388 22b7f6f8 Thomas Thrainer

2389 22b7f6f8 Thomas Thrainer
    """
2390 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2391 22b7f6f8 Thomas Thrainer
      mode = params.setdefault(constants.IDISK_MODE, constants.DISK_RDWR)
2392 22b7f6f8 Thomas Thrainer
      if mode not in constants.DISK_ACCESS_SET:
2393 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk access mode '%s'" % mode,
2394 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2395 22b7f6f8 Thomas Thrainer
2396 22b7f6f8 Thomas Thrainer
      size = params.get(constants.IDISK_SIZE, None)
2397 22b7f6f8 Thomas Thrainer
      if size is None:
2398 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Required disk parameter '%s' missing" %
2399 22b7f6f8 Thomas Thrainer
                                   constants.IDISK_SIZE, errors.ECODE_INVAL)
2400 34956ece Thomas Thrainer
      size = int(size)
2401 22b7f6f8 Thomas Thrainer
2402 22b7f6f8 Thomas Thrainer
      params[constants.IDISK_SIZE] = size
2403 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2404 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2405 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2406 22b7f6f8 Thomas Thrainer
2407 7c848a6a Bernardo Dal Seno
      CheckSpindlesExclusiveStorage(params, excl_stor, True)
2408 3f3ea14c Bernardo Dal Seno
2409 22b7f6f8 Thomas Thrainer
    elif op == constants.DDM_MODIFY:
2410 22b7f6f8 Thomas Thrainer
      if constants.IDISK_SIZE in params:
2411 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk size change not possible, use"
2412 22b7f6f8 Thomas Thrainer
                                   " grow-disk", errors.ECODE_INVAL)
2413 c5c72215 Dimitris Aragiorgis
2414 c5c72215 Dimitris Aragiorgis
      # Disk modification supports changing only the disk name and mode.
2415 c5c72215 Dimitris Aragiorgis
      # Changing arbitrary parameters is allowed only for ext disk template",
2416 c5c72215 Dimitris Aragiorgis
      if self.instance.disk_template != constants.DT_EXT:
2417 c5c72215 Dimitris Aragiorgis
        utils.ForceDictType(params, constants.MODIFIABLE_IDISK_PARAMS_TYPES)
2418 c5c72215 Dimitris Aragiorgis
2419 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2420 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2421 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2422 22b7f6f8 Thomas Thrainer
2423 22b7f6f8 Thomas Thrainer
  @staticmethod
2424 22b7f6f8 Thomas Thrainer
  def _VerifyNicModification(op, params):
2425 22b7f6f8 Thomas Thrainer
    """Verifies a network interface modification.
2426 22b7f6f8 Thomas Thrainer

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

2543 22b7f6f8 Thomas Thrainer
    This runs on the master, primary and secondaries.
2544 22b7f6f8 Thomas Thrainer

2545 22b7f6f8 Thomas Thrainer
    """
2546 22b7f6f8 Thomas Thrainer
    args = {}
2547 22b7f6f8 Thomas Thrainer
    if constants.BE_MINMEM in self.be_new:
2548 22b7f6f8 Thomas Thrainer
      args["minmem"] = self.be_new[constants.BE_MINMEM]
2549 22b7f6f8 Thomas Thrainer
    if constants.BE_MAXMEM in self.be_new:
2550 22b7f6f8 Thomas Thrainer
      args["maxmem"] = self.be_new[constants.BE_MAXMEM]
2551 22b7f6f8 Thomas Thrainer
    if constants.BE_VCPUS in self.be_new:
2552 22b7f6f8 Thomas Thrainer
      args["vcpus"] = self.be_new[constants.BE_VCPUS]
2553 22b7f6f8 Thomas Thrainer
    # TODO: export disk changes. Note: _BuildInstanceHookEnv* don't export disk
2554 22b7f6f8 Thomas Thrainer
    # information at all.
2555 22b7f6f8 Thomas Thrainer
2556 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
2557 22b7f6f8 Thomas Thrainer
      nics = []
2558 22b7f6f8 Thomas Thrainer
2559 22b7f6f8 Thomas Thrainer
      for nic in self._new_nics:
2560 22b7f6f8 Thomas Thrainer
        n = copy.deepcopy(nic)
2561 22b7f6f8 Thomas Thrainer
        nicparams = self.cluster.SimpleFillNIC(n.nicparams)
2562 22b7f6f8 Thomas Thrainer
        n.nicparams = nicparams
2563 5eacbcae Thomas Thrainer
        nics.append(NICToTuple(self, n))
2564 22b7f6f8 Thomas Thrainer
2565 22b7f6f8 Thomas Thrainer
      args["nics"] = nics
2566 22b7f6f8 Thomas Thrainer
2567 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance, override=args)
2568 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
2569 22b7f6f8 Thomas Thrainer
      env["NEW_DISK_TEMPLATE"] = self.op.disk_template
2570 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
2571 22b7f6f8 Thomas Thrainer
      env["RUNTIME_MEMORY"] = self.op.runtime_mem
2572 22b7f6f8 Thomas Thrainer
2573 22b7f6f8 Thomas Thrainer
    return env
2574 22b7f6f8 Thomas Thrainer
2575 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2576 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2577 22b7f6f8 Thomas Thrainer

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

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

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

2874 1bb99a33 Bernardo Dal Seno
    This only checks the instance list against the existing names.
2875 1bb99a33 Bernardo Dal Seno

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

3198 22b7f6f8 Thomas Thrainer
    """
3199 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to drbd")
3200 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3201 1c3231aa Thomas Thrainer
    snode_uuid = self.op.remote_node_uuid
3202 22b7f6f8 Thomas Thrainer
3203 d0d7d7cf Thomas Thrainer
    assert self.instance.disk_template == constants.DT_PLAIN
3204 22b7f6f8 Thomas Thrainer
3205 22b7f6f8 Thomas Thrainer
    # create a fake disk info for _GenerateDiskTemplate
3206 22b7f6f8 Thomas Thrainer
    disk_info = [{constants.IDISK_SIZE: d.size, constants.IDISK_MODE: d.mode,
3207 22b7f6f8 Thomas Thrainer
                  constants.IDISK_VG: d.logical_id[0],
3208 22b7f6f8 Thomas Thrainer
                  constants.IDISK_NAME: d.name}
3209 d0d7d7cf Thomas Thrainer
                 for d in self.instance.disks]
3210 5eacbcae Thomas Thrainer
    new_disks = GenerateDiskTemplate(self, self.op.disk_template,
3211 da4a52a3 Thomas Thrainer
                                     self.instance.uuid, pnode_uuid,
3212 d0d7d7cf Thomas Thrainer
                                     [snode_uuid], disk_info, None, None, 0,
3213 d0d7d7cf Thomas Thrainer
                                     feedback_fn, self.diskparams)
3214 0c3d9c7c Thomas Thrainer
    anno_disks = rpc.AnnotateDiskParams(new_disks, self.diskparams)
3215 1c3231aa Thomas Thrainer
    p_excl_stor = IsExclusiveStorageEnabledNodeUuid(self.cfg, pnode_uuid)
3216 1c3231aa Thomas Thrainer
    s_excl_stor = IsExclusiveStorageEnabledNodeUuid(self.cfg, snode_uuid)
3217 d0d7d7cf Thomas Thrainer
    info = GetInstanceInfoText(self.instance)
3218 22b7f6f8 Thomas Thrainer
    feedback_fn("Creating additional volumes...")
3219 22b7f6f8 Thomas Thrainer
    # first, create the missing data and meta devices
3220 22b7f6f8 Thomas Thrainer
    for disk in anno_disks:
3221 22b7f6f8 Thomas Thrainer
      # unfortunately this is... not too nice
3222 d0d7d7cf Thomas Thrainer
      CreateSingleBlockDev(self, pnode_uuid, self.instance, disk.children[1],
3223 5eacbcae Thomas Thrainer
                           info, True, p_excl_stor)
3224 22b7f6f8 Thomas Thrainer
      for child in disk.children:
3225 d0d7d7cf Thomas Thrainer
        CreateSingleBlockDev(self, snode_uuid, self.instance, child, info, True,
3226 5eacbcae Thomas Thrainer
                             s_excl_stor)
3227 22b7f6f8 Thomas Thrainer
    # at this stage, all new LVs have been created, we can rename the
3228 22b7f6f8 Thomas Thrainer
    # old ones
3229 22b7f6f8 Thomas Thrainer
    feedback_fn("Renaming original volumes...")
3230 22b7f6f8 Thomas Thrainer
    rename_list = [(o, n.children[0].logical_id)
3231 d0d7d7cf Thomas Thrainer
                   for (o, n) in zip(self.instance.disks, new_disks)]
3232 1c3231aa Thomas Thrainer
    result = self.rpc.call_blockdev_rename(pnode_uuid, rename_list)
3233 22b7f6f8 Thomas Thrainer
    result.Raise("Failed to rename original LVs")
3234 22b7f6f8 Thomas Thrainer
3235 22b7f6f8 Thomas Thrainer
    feedback_fn("Initializing DRBD devices...")
3236 22b7f6f8 Thomas Thrainer
    # all child devices are in place, we can now create the DRBD devices
3237 22b7f6f8 Thomas Thrainer
    try:
3238 22b7f6f8 Thomas Thrainer
      for disk in anno_disks:
3239 1c3231aa Thomas Thrainer
        for (node_uuid, excl_stor) in [(pnode_uuid, p_excl_stor),
3240 1c3231aa Thomas Thrainer
                                       (snode_uuid, s_excl_stor)]:
3241 1c3231aa Thomas Thrainer
          f_create = node_uuid == pnode_uuid
3242 d0d7d7cf Thomas Thrainer
          CreateSingleBlockDev(self, node_uuid, self.instance, disk, info,
3243 d0d7d7cf Thomas Thrainer
                               f_create, excl_stor)
3244 22b7f6f8 Thomas Thrainer
    except errors.GenericError, e:
3245 22b7f6f8 Thomas Thrainer
      feedback_fn("Initializing of DRBD devices failed;"
3246 22b7f6f8 Thomas Thrainer
                  " renaming back original volumes...")
3247 22b7f6f8 Thomas Thrainer
      rename_back_list = [(n.children[0], o.logical_id)
3248 d0d7d7cf Thomas Thrainer
                          for (n, o) in zip(new_disks, self.instance.disks)]
3249 1c3231aa Thomas Thrainer
      result = self.rpc.call_blockdev_rename(pnode_uuid, rename_back_list)
3250 22b7f6f8 Thomas Thrainer
      result.Raise("Failed to rename LVs back after error %s" % str(e))
3251 22b7f6f8 Thomas Thrainer
      raise
3252 22b7f6f8 Thomas Thrainer
3253 22b7f6f8 Thomas Thrainer
    # at this point, the instance has been modified
3254 d0d7d7cf Thomas Thrainer
    self.instance.disk_template = constants.DT_DRBD8
3255 d0d7d7cf Thomas Thrainer
    self.instance.disks = new_disks
3256 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
3257 22b7f6f8 Thomas Thrainer
3258 22b7f6f8 Thomas Thrainer
    # Release node locks while waiting for sync
3259 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3260 22b7f6f8 Thomas Thrainer
3261 22b7f6f8 Thomas Thrainer
    # disks are created, waiting for sync
3262 d0d7d7cf Thomas Thrainer
    disk_abort = not WaitForSync(self, self.instance,
3263 5eacbcae Thomas Thrainer
                                 oneshot=not self.op.wait_for_sync)
3264 22b7f6f8 Thomas Thrainer
    if disk_abort:
3265 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("There are some degraded disks for"
3266 22b7f6f8 Thomas Thrainer
                               " this instance, please cleanup manually")
3267 22b7f6f8 Thomas Thrainer
3268 22b7f6f8 Thomas Thrainer
    # Node resource locks will be released by caller
3269 22b7f6f8 Thomas Thrainer
3270 22b7f6f8 Thomas Thrainer
  def _ConvertDrbdToPlain(self, feedback_fn):
3271 22b7f6f8 Thomas Thrainer
    """Converts an instance from drbd to plain.
3272 22b7f6f8 Thomas Thrainer

3273 22b7f6f8 Thomas Thrainer
    """
3274 d0d7d7cf Thomas Thrainer
    assert len(self.instance.secondary_nodes) == 1
3275 d0d7d7cf Thomas Thrainer
    assert self.instance.disk_template == constants.DT_DRBD8
3276 22b7f6f8 Thomas Thrainer
3277 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3278 d0d7d7cf Thomas Thrainer
    snode_uuid = self.instance.secondary_nodes[0]
3279 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to plain")
3280 22b7f6f8 Thomas Thrainer
3281 d0d7d7cf Thomas Thrainer
    old_disks = AnnotateDiskParams(self.instance, self.instance.disks, self.cfg)
3282 d0d7d7cf Thomas Thrainer
    new_disks = [d.children[0] for d in self.instance.disks]
3283 22b7f6f8 Thomas Thrainer
3284 22b7f6f8 Thomas Thrainer
    # copy over size, mode and name
3285 22b7f6f8 Thomas Thrainer
    for parent, child in zip(old_disks, new_disks):
3286 22b7f6f8 Thomas Thrainer
      child.size = parent.size
3287 22b7f6f8 Thomas Thrainer
      child.mode = parent.mode
3288 22b7f6f8 Thomas Thrainer
      child.name = parent.name
3289 22b7f6f8 Thomas Thrainer
3290 22b7f6f8 Thomas Thrainer
    # this is a DRBD disk, return its port to the pool
3291 22b7f6f8 Thomas Thrainer
    # NOTE: this must be done right before the call to cfg.Update!
3292 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3293 22b7f6f8 Thomas Thrainer
      tcp_port = disk.logical_id[2]
3294 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(tcp_port)
3295 22b7f6f8 Thomas Thrainer
3296 22b7f6f8 Thomas Thrainer
    # update instance structure
3297 d0d7d7cf Thomas Thrainer
    self.instance.disks = new_disks
3298 d0d7d7cf Thomas Thrainer
    self.instance.disk_template = constants.DT_PLAIN
3299 d0d7d7cf Thomas Thrainer
    _UpdateIvNames(0, self.instance.disks)
3300 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
3301 22b7f6f8 Thomas Thrainer
3302 22b7f6f8 Thomas Thrainer
    # Release locks in case removing disks takes a while
3303 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3304 22b7f6f8 Thomas Thrainer
3305 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing volumes on the secondary node...")
3306 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3307 0c3d9c7c Thomas Thrainer
      result = self.rpc.call_blockdev_remove(snode_uuid, (disk, self.instance))
3308 aefc2f89 Thomas Thrainer
      result.Warn("Could not remove block device %s on node %s,"
3309 aefc2f89 Thomas Thrainer
                  " continuing anyway" %
3310 aefc2f89 Thomas Thrainer
                  (disk.iv_name, self.cfg.GetNodeName(snode_uuid)),
3311 aefc2f89 Thomas Thrainer
                  self.LogWarning)
3312 22b7f6f8 Thomas Thrainer
3313 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing unneeded volumes on the primary node...")
3314 22b7f6f8 Thomas Thrainer
    for idx, disk in enumerate(old_disks):
3315 22b7f6f8 Thomas Thrainer
      meta = disk.children[1]
3316 0c3d9c7c Thomas Thrainer
      result = self.rpc.call_blockdev_remove(pnode_uuid, (meta, self.instance))
3317 aefc2f89 Thomas Thrainer
      result.Warn("Could not remove metadata for disk %d on node %s,"
3318 aefc2f89 Thomas Thrainer
                  " continuing anyway" %
3319 aefc2f89 Thomas Thrainer
                  (idx, self.cfg.GetNodeName(pnode_uuid)),
3320 aefc2f89 Thomas Thrainer
                  self.LogWarning)
3321 22b7f6f8 Thomas Thrainer
3322 ba924970 Dimitris Aragiorgis
  def _HotplugDevice(self, action, dev_type, device, extra, seq):
3323 ba924970 Dimitris Aragiorgis
    self.LogInfo("Trying to hotplug device...")
3324 e15a00dc Dimitris Aragiorgis
    msg = "hotplug:"
3325 ba924970 Dimitris Aragiorgis
    result = self.rpc.call_hotplug_device(self.instance.primary_node,
3326 ba924970 Dimitris Aragiorgis
                                          self.instance, action, dev_type,
3327 51951d38 Dimitris Aragiorgis
                                          (device, self.instance),
3328 51951d38 Dimitris Aragiorgis
                                          extra, seq)
3329 ba924970 Dimitris Aragiorgis
    if result.fail_msg:
3330 ba924970 Dimitris Aragiorgis
      self.LogWarning("Could not hotplug device: %s" % result.fail_msg)
3331 ba924970 Dimitris Aragiorgis
      self.LogInfo("Continuing execution..")
3332 e15a00dc Dimitris Aragiorgis
      msg += "failed"
3333 ba924970 Dimitris Aragiorgis
    else:
3334 ba924970 Dimitris Aragiorgis
      self.LogInfo("Hotplug done.")
3335 e15a00dc Dimitris Aragiorgis
      msg += "done"
3336 e15a00dc Dimitris Aragiorgis
    return msg
3337 ba924970 Dimitris Aragiorgis
3338 22b7f6f8 Thomas Thrainer
  def _CreateNewDisk(self, idx, params, _):
3339 22b7f6f8 Thomas Thrainer
    """Creates a new disk.
3340 22b7f6f8 Thomas Thrainer

3341 22b7f6f8 Thomas Thrainer
    """
3342 22b7f6f8 Thomas Thrainer
    # add a new disk
3343 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DTS_FILEBASED:
3344 d0d7d7cf Thomas Thrainer
      (file_driver, file_path) = self.instance.disks[0].logical_id
3345 22b7f6f8 Thomas Thrainer
      file_path = os.path.dirname(file_path)
3346 22b7f6f8 Thomas Thrainer
    else:
3347 22b7f6f8 Thomas Thrainer
      file_driver = file_path = None
3348 22b7f6f8 Thomas Thrainer
3349 22b7f6f8 Thomas Thrainer
    disk = \
3350 d0d7d7cf Thomas Thrainer
      GenerateDiskTemplate(self, self.instance.disk_template,
3351 da4a52a3 Thomas Thrainer
                           self.instance.uuid, self.instance.primary_node,
3352 d0d7d7cf Thomas Thrainer
                           self.instance.secondary_nodes, [params], file_path,
3353 d0d7d7cf Thomas Thrainer
                           file_driver, idx, self.Log, self.diskparams)[0]
3354 22b7f6f8 Thomas Thrainer
3355 d0d7d7cf Thomas Thrainer
    new_disks = CreateDisks(self, self.instance, disks=[disk])
3356 22b7f6f8 Thomas Thrainer
3357 22b7f6f8 Thomas Thrainer
    if self.cluster.prealloc_wipe_disks:
3358 22b7f6f8 Thomas Thrainer
      # Wipe new disk
3359 d0d7d7cf Thomas Thrainer
      WipeOrCleanupDisks(self, self.instance,
3360 a365b47f Bernardo Dal Seno
                         disks=[(idx, disk, 0)],
3361 a365b47f Bernardo Dal Seno
                         cleanup=new_disks)
3362 22b7f6f8 Thomas Thrainer
3363 e15a00dc Dimitris Aragiorgis
    changes = [
3364 e15a00dc Dimitris Aragiorgis
      ("disk/%d" % idx,
3365 b15d5bd3 Petr Pudlak
       "add:size=%s,mode=%s" % (disk.size, disk.mode)),
3366 e15a00dc Dimitris Aragiorgis
      ]
3367 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3368 ba924970 Dimitris Aragiorgis
      result = self.rpc.call_blockdev_assemble(self.instance.primary_node,
3369 ba924970 Dimitris Aragiorgis
                                               (disk, self.instance),
3370 ba924970 Dimitris Aragiorgis
                                               self.instance.name, True, idx)
3371 ba924970 Dimitris Aragiorgis
      if result.fail_msg:
3372 e15a00dc Dimitris Aragiorgis
        changes.append(("disk/%d" % idx, "assemble:failed"))
3373 ba924970 Dimitris Aragiorgis
        self.LogWarning("Can't assemble newly created disk %d: %s",
3374 ba924970 Dimitris Aragiorgis
                        idx, result.fail_msg)
3375 ba924970 Dimitris Aragiorgis
      else:
3376 ba924970 Dimitris Aragiorgis
        _, link_name = result.payload
3377 e15a00dc Dimitris Aragiorgis
        msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
3378 e15a00dc Dimitris Aragiorgis
                                  constants.HOTPLUG_TARGET_DISK,
3379 e15a00dc Dimitris Aragiorgis
                                  disk, link_name, idx)
3380 e15a00dc Dimitris Aragiorgis
        changes.append(("disk/%d" % idx, msg))
3381 ba924970 Dimitris Aragiorgis
3382 e15a00dc Dimitris Aragiorgis
    return (disk, changes)
3383 22b7f6f8 Thomas Thrainer
3384 922a9e65 Thomas Thrainer
  def _PostAddDisk(self, _, disk):
3385 922a9e65 Thomas Thrainer
    if not WaitForSync(self, self.instance, disks=[disk],
3386 922a9e65 Thomas Thrainer
                       oneshot=not self.op.wait_for_sync):
3387 922a9e65 Thomas Thrainer
      raise errors.OpExecError("Failed to sync disks of %s" %
3388 922a9e65 Thomas Thrainer
                               self.instance.name)
3389 922a9e65 Thomas Thrainer
3390 3c260845 Thomas Thrainer
    # the disk is active at this point, so deactivate it if the instance disks
3391 3c260845 Thomas Thrainer
    # are supposed to be inactive
3392 3c260845 Thomas Thrainer
    if not self.instance.disks_active:
3393 3c260845 Thomas Thrainer
      ShutdownInstanceDisks(self, self.instance, disks=[disk])
3394 3c260845 Thomas Thrainer
3395 c5c72215 Dimitris Aragiorgis
  def _ModifyDisk(self, idx, disk, params, _):
3396 22b7f6f8 Thomas Thrainer
    """Modifies a disk.
3397 22b7f6f8 Thomas Thrainer

3398 22b7f6f8 Thomas Thrainer
    """
3399 22b7f6f8 Thomas Thrainer
    changes = []
3400 4eef428e Dimitris Aragiorgis
    if constants.IDISK_MODE in params:
3401 4eef428e Dimitris Aragiorgis
      disk.mode = params.get(constants.IDISK_MODE)
3402 22b7f6f8 Thomas Thrainer
      changes.append(("disk.mode/%d" % idx, disk.mode))
3403 22b7f6f8 Thomas Thrainer
3404 4eef428e Dimitris Aragiorgis
    if constants.IDISK_NAME in params:
3405 4eef428e Dimitris Aragiorgis
      disk.name = params.get(constants.IDISK_NAME)
3406 4eef428e Dimitris Aragiorgis
      changes.append(("disk.name/%d" % idx, disk.name))
3407 22b7f6f8 Thomas Thrainer
3408 c5c72215 Dimitris Aragiorgis
    # Modify arbitrary params in case instance template is ext
3409 c5c72215 Dimitris Aragiorgis
    for key, value in params.iteritems():
3410 c5c72215 Dimitris Aragiorgis
      if (key not in constants.MODIFIABLE_IDISK_PARAMS and
3411 c5c72215 Dimitris Aragiorgis
          self.instance.disk_template == constants.DT_EXT):
3412 e228ab9c Dimitris Aragiorgis
        # stolen from GetUpdatedParams: default means reset/delete
3413 e228ab9c Dimitris Aragiorgis
        if value.lower() == constants.VALUE_DEFAULT:
3414 e228ab9c Dimitris Aragiorgis
          try:
3415 e228ab9c Dimitris Aragiorgis
            del disk.params[key]
3416 e228ab9c Dimitris Aragiorgis
          except KeyError:
3417 e228ab9c Dimitris Aragiorgis
            pass
3418 e228ab9c Dimitris Aragiorgis
        else:
3419 e228ab9c Dimitris Aragiorgis
          disk.params[key] = value
3420 c5c72215 Dimitris Aragiorgis
        changes.append(("disk.params:%s/%d" % (key, idx), value))
3421 22b7f6f8 Thomas Thrainer
3422 22b7f6f8 Thomas Thrainer
    return changes
3423 22b7f6f8 Thomas Thrainer
3424 22b7f6f8 Thomas Thrainer
  def _RemoveDisk(self, idx, root, _):
3425 22b7f6f8 Thomas Thrainer
    """Removes a disk.
3426 22b7f6f8 Thomas Thrainer

3427 22b7f6f8 Thomas Thrainer
    """
3428 e15a00dc Dimitris Aragiorgis
    hotmsg = ""
3429 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3430 e15a00dc Dimitris Aragiorgis
      hotmsg = self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
3431 e15a00dc Dimitris Aragiorgis
                                   constants.HOTPLUG_TARGET_DISK,
3432 e15a00dc Dimitris Aragiorgis
                                   root, None, idx)
3433 ba924970 Dimitris Aragiorgis
      ShutdownInstanceDisks(self, self.instance, [root])
3434 ba924970 Dimitris Aragiorgis
3435 5eacbcae Thomas Thrainer
    (anno_disk,) = AnnotateDiskParams(self.instance, [root], self.cfg)
3436 1c3231aa Thomas Thrainer
    for node_uuid, disk in anno_disk.ComputeNodeTree(
3437 1c3231aa Thomas Thrainer
                             self.instance.primary_node):
3438 0c3d9c7c Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(node_uuid, (disk, self.instance)) \
3439 0c3d9c7c Thomas Thrainer
              .fail_msg
3440 22b7f6f8 Thomas Thrainer
      if msg:
3441 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove disk/%d on node '%s': %s,"
3442 1c3231aa Thomas Thrainer
                        " continuing anyway", idx,
3443 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
3444 22b7f6f8 Thomas Thrainer
3445 22b7f6f8 Thomas Thrainer
    # if this is a DRBD disk, return its port to the pool
3446 66a37e7a Helga Velroyen
    if root.dev_type in constants.DTS_DRBD:
3447 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(root.logical_id[2])
3448 22b7f6f8 Thomas Thrainer
3449 e15a00dc Dimitris Aragiorgis
    return hotmsg
3450 e15a00dc Dimitris Aragiorgis
3451 22b7f6f8 Thomas Thrainer
  def _CreateNewNic(self, idx, params, private):
3452 22b7f6f8 Thomas Thrainer
    """Creates data structure for a new network interface.
3453 22b7f6f8 Thomas Thrainer

3454 22b7f6f8 Thomas Thrainer
    """
3455 22b7f6f8 Thomas Thrainer
    mac = params[constants.INIC_MAC]
3456 22b7f6f8 Thomas Thrainer
    ip = params.get(constants.INIC_IP, None)
3457 22b7f6f8 Thomas Thrainer
    net = params.get(constants.INIC_NETWORK, None)
3458 22b7f6f8 Thomas Thrainer
    name = params.get(constants.INIC_NAME, None)
3459 22b7f6f8 Thomas Thrainer
    net_uuid = self.cfg.LookupNetwork(net)
3460 22b7f6f8 Thomas Thrainer
    #TODO: not private.filled?? can a nic have no nicparams??
3461 22b7f6f8 Thomas Thrainer
    nicparams = private.filled
3462 22b7f6f8 Thomas Thrainer
    nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, name=name,
3463 22b7f6f8 Thomas Thrainer
                       nicparams=nicparams)
3464 22b7f6f8 Thomas Thrainer
    nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
3465 22b7f6f8 Thomas Thrainer
3466 e15a00dc Dimitris Aragiorgis
    changes = [
3467 22b7f6f8 Thomas Thrainer
      ("nic.%d" % idx,
3468 22b7f6f8 Thomas Thrainer
       "add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" %
3469 22b7f6f8 Thomas Thrainer
       (mac, ip, private.filled[constants.NIC_MODE],
3470 ba924970 Dimitris Aragiorgis
       private.filled[constants.NIC_LINK], net)),
3471 ba924970 Dimitris Aragiorgis
      ]
3472 ba924970 Dimitris Aragiorgis
3473 e15a00dc Dimitris Aragiorgis
    if self.op.hotplug:
3474 e15a00dc Dimitris Aragiorgis
      msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
3475 e15a00dc Dimitris Aragiorgis
                                constants.HOTPLUG_TARGET_NIC,
3476 e15a00dc Dimitris Aragiorgis
                                nobj, None, idx)
3477 e15a00dc Dimitris Aragiorgis
      changes.append(("nic.%d" % idx, msg))
3478 e15a00dc Dimitris Aragiorgis
3479 e15a00dc Dimitris Aragiorgis
    return (nobj, changes)
3480 22b7f6f8 Thomas Thrainer
3481 22b7f6f8 Thomas Thrainer
  def _ApplyNicMods(self, idx, nic, params, private):
3482 22b7f6f8 Thomas Thrainer
    """Modifies a network interface.
3483 22b7f6f8 Thomas Thrainer

3484 22b7f6f8 Thomas Thrainer
    """
3485 22b7f6f8 Thomas Thrainer
    changes = []
3486 22b7f6f8 Thomas Thrainer
3487 22b7f6f8 Thomas Thrainer
    for key in [constants.INIC_MAC, constants.INIC_IP, constants.INIC_NAME]:
3488 22b7f6f8 Thomas Thrainer
      if key in params:
3489 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), params[key]))
3490 22b7f6f8 Thomas Thrainer
        setattr(nic, key, params[key])
3491 22b7f6f8 Thomas Thrainer
3492 22b7f6f8 Thomas Thrainer
    new_net = params.get(constants.INIC_NETWORK, nic.network)
3493 22b7f6f8 Thomas Thrainer
    new_net_uuid = self.cfg.LookupNetwork(new_net)
3494 22b7f6f8 Thomas Thrainer
    if new_net_uuid != nic.network:
3495 22b7f6f8 Thomas Thrainer
      changes.append(("nic.network/%d" % idx, new_net))
3496 22b7f6f8 Thomas Thrainer
      nic.network = new_net_uuid
3497 22b7f6f8 Thomas Thrainer
3498 22b7f6f8 Thomas Thrainer
    if private.filled:
3499 22b7f6f8 Thomas Thrainer
      nic.nicparams = private.filled
3500 22b7f6f8 Thomas Thrainer
3501 22b7f6f8 Thomas Thrainer
      for (key, val) in nic.nicparams.items():
3502 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), val))
3503 22b7f6f8 Thomas Thrainer
3504 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3505 e15a00dc Dimitris Aragiorgis
      msg = self._HotplugDevice(constants.HOTPLUG_ACTION_MODIFY,
3506 e15a00dc Dimitris Aragiorgis
                                constants.HOTPLUG_TARGET_NIC,
3507 e15a00dc Dimitris Aragiorgis
                                nic, None, idx)
3508 e15a00dc Dimitris Aragiorgis
      changes.append(("nic/%d" % idx, msg))
3509 ba924970 Dimitris Aragiorgis
3510 22b7f6f8 Thomas Thrainer
    return changes
3511 22b7f6f8 Thomas Thrainer
3512 ba924970 Dimitris Aragiorgis
  def _RemoveNic(self, idx, nic, _):
3513 ba924970 Dimitris Aragiorgis
    if self.op.hotplug:
3514 e15a00dc Dimitris Aragiorgis
      return self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
3515 e15a00dc Dimitris Aragiorgis
                                 constants.HOTPLUG_TARGET_NIC,
3516 e15a00dc Dimitris Aragiorgis
                                 nic, None, idx)
3517 ba924970 Dimitris Aragiorgis
3518 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3519 22b7f6f8 Thomas Thrainer
    """Modifies an instance.
3520 22b7f6f8 Thomas Thrainer

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

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

3739 22b7f6f8 Thomas Thrainer
    """
3740 22b7f6f8 Thomas Thrainer
    assert self.target_uuids
3741 22b7f6f8 Thomas Thrainer
3742 22b7f6f8 Thomas Thrainer
    env = {
3743 22b7f6f8 Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
3744 22b7f6f8 Thomas Thrainer
      }
3745 22b7f6f8 Thomas Thrainer
3746 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
3747 22b7f6f8 Thomas Thrainer
3748 22b7f6f8 Thomas Thrainer
    return env
3749 22b7f6f8 Thomas Thrainer
3750 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
3751 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
3752 22b7f6f8 Thomas Thrainer

3753 22b7f6f8 Thomas Thrainer
    """
3754 22b7f6f8 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
3755 22b7f6f8 Thomas Thrainer
    return ([mn], [mn])
3756 22b7f6f8 Thomas Thrainer
3757 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3758 22b7f6f8 Thomas Thrainer
    instances = list(self.owned_locks(locking.LEVEL_INSTANCE))
3759 22b7f6f8 Thomas Thrainer
3760 22b7f6f8 Thomas Thrainer
    assert instances == [self.op.instance_name], "Instance not locked"
3761 22b7f6f8 Thomas Thrainer
3762 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=instances,
3763 22b7f6f8 Thomas Thrainer
                                      target_groups=list(self.target_uuids))
3764 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
3765 22b7f6f8 Thomas Thrainer
3766 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
3767 22b7f6f8 Thomas Thrainer
3768 22b7f6f8 Thomas Thrainer
    if not ial.success:
3769 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute solution for changing group of"
3770 22b7f6f8 Thomas Thrainer
                                 " instance '%s' using iallocator '%s': %s" %
3771 22b7f6f8 Thomas Thrainer
                                 (self.op.instance_name, self.op.iallocator,
3772 22b7f6f8 Thomas Thrainer
                                  ial.info), errors.ECODE_NORES)
3773 22b7f6f8 Thomas Thrainer
3774 5eacbcae Thomas Thrainer
    jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
3775 22b7f6f8 Thomas Thrainer
3776 22b7f6f8 Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for changing group of"
3777 22b7f6f8 Thomas Thrainer
                 " instance '%s'", len(jobs), self.op.instance_name)
3778 22b7f6f8 Thomas Thrainer
3779 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs)