Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance.py @ 9f7d5fe4

History | View | Annotate | Download (141.3 kB)

1 22b7f6f8 Thomas Thrainer
#
2 22b7f6f8 Thomas Thrainer
#
3 22b7f6f8 Thomas Thrainer
4 22b7f6f8 Thomas Thrainer
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5 22b7f6f8 Thomas Thrainer
#
6 22b7f6f8 Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 22b7f6f8 Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 22b7f6f8 Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 22b7f6f8 Thomas Thrainer
# (at your option) any later version.
10 22b7f6f8 Thomas Thrainer
#
11 22b7f6f8 Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 22b7f6f8 Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 22b7f6f8 Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 22b7f6f8 Thomas Thrainer
# General Public License for more details.
15 22b7f6f8 Thomas Thrainer
#
16 22b7f6f8 Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 22b7f6f8 Thomas Thrainer
# along with this program; if not, write to the Free Software
18 22b7f6f8 Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 22b7f6f8 Thomas Thrainer
# 02110-1301, USA.
20 22b7f6f8 Thomas Thrainer
21 22b7f6f8 Thomas Thrainer
22 22b7f6f8 Thomas Thrainer
"""Logical units dealing with instances."""
23 22b7f6f8 Thomas Thrainer
24 22b7f6f8 Thomas Thrainer
import OpenSSL
25 22b7f6f8 Thomas Thrainer
import copy
26 22b7f6f8 Thomas Thrainer
import logging
27 22b7f6f8 Thomas Thrainer
import os
28 22b7f6f8 Thomas Thrainer
29 22b7f6f8 Thomas Thrainer
from ganeti import compat
30 22b7f6f8 Thomas Thrainer
from ganeti import constants
31 22b7f6f8 Thomas Thrainer
from ganeti import errors
32 22b7f6f8 Thomas Thrainer
from ganeti import ht
33 22b7f6f8 Thomas Thrainer
from ganeti import hypervisor
34 22b7f6f8 Thomas Thrainer
from ganeti import locking
35 22b7f6f8 Thomas Thrainer
from ganeti.masterd import iallocator
36 22b7f6f8 Thomas Thrainer
from ganeti import masterd
37 22b7f6f8 Thomas Thrainer
from ganeti import netutils
38 22b7f6f8 Thomas Thrainer
from ganeti import objects
39 22b7f6f8 Thomas Thrainer
from ganeti import opcodes
40 22b7f6f8 Thomas Thrainer
from ganeti import pathutils
41 22b7f6f8 Thomas Thrainer
from ganeti import rpc
42 22b7f6f8 Thomas Thrainer
from ganeti import utils
43 22b7f6f8 Thomas Thrainer
44 8aa8f6b1 Thomas Thrainer
from ganeti.cmdlib.base import NoHooksLU, LogicalUnit, ResultWithJobs
45 22b7f6f8 Thomas Thrainer
46 13f6af81 Thomas Thrainer
from ganeti.cmdlib.common import INSTANCE_DOWN, \
47 5eacbcae Thomas Thrainer
  INSTANCE_NOT_RUNNING, CAN_CHANGE_INSTANCE_OFFLINE, CheckNodeOnline, \
48 5eacbcae Thomas Thrainer
  ShareAll, GetDefaultIAllocator, CheckInstanceNodeGroups, \
49 5eacbcae Thomas Thrainer
  LoadNodeEvacResult, CheckIAllocatorOrNode, CheckParamsNotGlobal, \
50 5eacbcae Thomas Thrainer
  IsExclusiveStorageEnabledNode, CheckHVParams, CheckOSParams, \
51 da4a52a3 Thomas Thrainer
  AnnotateDiskParams, GetUpdatedParams, ExpandInstanceUuidAndName, \
52 1f7c8208 Helga Velroyen
  ComputeIPolicySpecViolation, CheckInstanceState, ExpandNodeUuidAndName, \
53 1f7c8208 Helga Velroyen
  CheckDiskTemplateEnabled
54 5eacbcae Thomas Thrainer
from ganeti.cmdlib.instance_storage import CreateDisks, \
55 a365b47f Bernardo Dal Seno
  CheckNodesFreeDiskPerVG, WipeDisks, WipeOrCleanupDisks, WaitForSync, \
56 1c3231aa Thomas Thrainer
  IsExclusiveStorageEnabledNodeUuid, CreateSingleBlockDev, ComputeDisks, \
57 5eacbcae Thomas Thrainer
  CheckRADOSFreeSpace, ComputeDiskSizePerVG, GenerateDiskTemplate, \
58 3f3ea14c Bernardo Dal Seno
  StartInstanceDisks, ShutdownInstanceDisks, AssembleInstanceDisks, \
59 3f3ea14c Bernardo Dal Seno
  CheckSpindlesExclusiveStorage
60 5eacbcae Thomas Thrainer
from ganeti.cmdlib.instance_utils import BuildInstanceHookEnvByObject, \
61 5eacbcae Thomas Thrainer
  GetClusterDomainSecret, BuildInstanceHookEnv, NICListToTuple, \
62 5eacbcae Thomas Thrainer
  NICToTuple, CheckNodeNotDrained, RemoveInstance, CopyLockList, \
63 5eacbcae Thomas Thrainer
  ReleaseLocks, CheckNodeVmCapable, CheckTargetNodeIPolicy, \
64 5eacbcae Thomas Thrainer
  GetInstanceInfoText, RemoveDisks, CheckNodeFreeMemory, \
65 5eacbcae Thomas Thrainer
  CheckInstanceBridgesExist, CheckNicsBridgesExist, CheckNodeHasOS
66 22b7f6f8 Thomas Thrainer
67 22b7f6f8 Thomas Thrainer
import ganeti.masterd.instance
68 22b7f6f8 Thomas Thrainer
69 22b7f6f8 Thomas Thrainer
70 5eacbcae Thomas Thrainer
#: Type description for changes as returned by L{_ApplyContainerMods}'s
71 22b7f6f8 Thomas Thrainer
#: callbacks
72 22b7f6f8 Thomas Thrainer
_TApplyContModsCbChanges = \
73 22b7f6f8 Thomas Thrainer
  ht.TMaybeListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([
74 22b7f6f8 Thomas Thrainer
    ht.TNonEmptyString,
75 22b7f6f8 Thomas Thrainer
    ht.TAny,
76 22b7f6f8 Thomas Thrainer
    ])))
77 22b7f6f8 Thomas Thrainer
78 22b7f6f8 Thomas Thrainer
79 22b7f6f8 Thomas Thrainer
def _CheckHostnameSane(lu, name):
80 22b7f6f8 Thomas Thrainer
  """Ensures that a given hostname resolves to a 'sane' name.
81 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

550 22b7f6f8 Thomas Thrainer
    Figure out the right locks for instance creation.
551 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1463 22b7f6f8 Thomas Thrainer
  """
1464 22b7f6f8 Thomas Thrainer
  HPATH = "instance-rename"
1465 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1466 22b7f6f8 Thomas Thrainer
1467 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1468 22b7f6f8 Thomas Thrainer
    """Check arguments.
1469 22b7f6f8 Thomas Thrainer

1470 22b7f6f8 Thomas Thrainer
    """
1471 22b7f6f8 Thomas Thrainer
    if self.op.ip_check and not self.op.name_check:
1472 22b7f6f8 Thomas Thrainer
      # TODO: make the ip check more flexible and not depend on the name check
1473 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("IP address check requires a name check",
1474 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1475 22b7f6f8 Thomas Thrainer
1476 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1477 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1478 22b7f6f8 Thomas Thrainer

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

1481 22b7f6f8 Thomas Thrainer
    """
1482 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1483 22b7f6f8 Thomas Thrainer
    env["INSTANCE_NEW_NAME"] = self.op.new_name
1484 22b7f6f8 Thomas Thrainer
    return env
1485 22b7f6f8 Thomas Thrainer
1486 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1487 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1488 22b7f6f8 Thomas Thrainer

1489 22b7f6f8 Thomas Thrainer
    """
1490 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
1491 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1492 22b7f6f8 Thomas Thrainer
1493 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1494 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1495 22b7f6f8 Thomas Thrainer

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

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

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

1596 22b7f6f8 Thomas Thrainer
  """
1597 22b7f6f8 Thomas Thrainer
  HPATH = "instance-remove"
1598 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1599 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1600 22b7f6f8 Thomas Thrainer
1601 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1602 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1603 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
1604 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1605 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
1606 22b7f6f8 Thomas Thrainer
1607 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1608 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1609 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
1610 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1611 22b7f6f8 Thomas Thrainer
      # Copy node locks
1612 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1613 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1614 22b7f6f8 Thomas Thrainer
1615 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1616 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1617 22b7f6f8 Thomas Thrainer

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

1620 22b7f6f8 Thomas Thrainer
    """
1621 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1622 22b7f6f8 Thomas Thrainer
    env["SHUTDOWN_TIMEOUT"] = self.op.shutdown_timeout
1623 22b7f6f8 Thomas Thrainer
    return env
1624 22b7f6f8 Thomas Thrainer
1625 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1626 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1627 22b7f6f8 Thomas Thrainer

1628 22b7f6f8 Thomas Thrainer
    """
1629 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()]
1630 22b7f6f8 Thomas Thrainer
    nl_post = list(self.instance.all_nodes) + nl
1631 22b7f6f8 Thomas Thrainer
    return (nl, nl_post)
1632 22b7f6f8 Thomas Thrainer
1633 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1634 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1635 22b7f6f8 Thomas Thrainer

1636 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1637 22b7f6f8 Thomas Thrainer

1638 22b7f6f8 Thomas Thrainer
    """
1639 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1640 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1641 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1642 22b7f6f8 Thomas Thrainer
1643 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1644 22b7f6f8 Thomas Thrainer
    """Remove the instance.
1645 22b7f6f8 Thomas Thrainer

1646 22b7f6f8 Thomas Thrainer
    """
1647 d0d7d7cf Thomas Thrainer
    logging.info("Shutting down instance %s on node %s", self.instance.name,
1648 d0d7d7cf Thomas Thrainer
                 self.cfg.GetNodeName(self.instance.primary_node))
1649 22b7f6f8 Thomas Thrainer
1650 d0d7d7cf Thomas Thrainer
    result = self.rpc.call_instance_shutdown(self.instance.primary_node,
1651 d0d7d7cf Thomas Thrainer
                                             self.instance,
1652 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1653 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1654 c7dd65be Klaus Aehlig
    if self.op.ignore_failures:
1655 c7dd65be Klaus Aehlig
      result.Warn("Warning: can't shutdown instance", feedback_fn)
1656 c7dd65be Klaus Aehlig
    else:
1657 c7dd65be Klaus Aehlig
      result.Raise("Could not shutdown instance %s on node %s" %
1658 d0d7d7cf Thomas Thrainer
                   (self.instance.name,
1659 d0d7d7cf Thomas Thrainer
                    self.cfg.GetNodeName(self.instance.primary_node)))
1660 22b7f6f8 Thomas Thrainer
1661 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1662 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1663 d0d7d7cf Thomas Thrainer
    assert not (set(self.instance.all_nodes) -
1664 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
1665 22b7f6f8 Thomas Thrainer
      "Not owning correct locks"
1666 22b7f6f8 Thomas Thrainer
1667 d0d7d7cf Thomas Thrainer
    RemoveInstance(self, feedback_fn, self.instance, self.op.ignore_failures)
1668 22b7f6f8 Thomas Thrainer
1669 22b7f6f8 Thomas Thrainer
1670 22b7f6f8 Thomas Thrainer
class LUInstanceMove(LogicalUnit):
1671 22b7f6f8 Thomas Thrainer
  """Move an instance by data-copying.
1672 22b7f6f8 Thomas Thrainer

1673 22b7f6f8 Thomas Thrainer
  """
1674 22b7f6f8 Thomas Thrainer
  HPATH = "instance-move"
1675 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1676 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1677 22b7f6f8 Thomas Thrainer
1678 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1679 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1680 1c3231aa Thomas Thrainer
    (self.op.target_node_uuid, self.op.target_node) = \
1681 1c3231aa Thomas Thrainer
      ExpandNodeUuidAndName(self.cfg, self.op.target_node_uuid,
1682 1c3231aa Thomas Thrainer
                            self.op.target_node)
1683 1c3231aa Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = [self.op.target_node]
1684 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1685 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
1686 22b7f6f8 Thomas Thrainer
1687 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1688 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1689 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes(primary_only=True)
1690 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1691 22b7f6f8 Thomas Thrainer
      # Copy node locks
1692 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1693 5eacbcae Thomas Thrainer
        CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1694 22b7f6f8 Thomas Thrainer
1695 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1696 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1697 22b7f6f8 Thomas Thrainer

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

1700 22b7f6f8 Thomas Thrainer
    """
1701 22b7f6f8 Thomas Thrainer
    env = {
1702 22b7f6f8 Thomas Thrainer
      "TARGET_NODE": self.op.target_node,
1703 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
1704 22b7f6f8 Thomas Thrainer
      }
1705 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
1706 22b7f6f8 Thomas Thrainer
    return env
1707 22b7f6f8 Thomas Thrainer
1708 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1709 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1710 22b7f6f8 Thomas Thrainer

1711 22b7f6f8 Thomas Thrainer
    """
1712 22b7f6f8 Thomas Thrainer
    nl = [
1713 22b7f6f8 Thomas Thrainer
      self.cfg.GetMasterNode(),
1714 22b7f6f8 Thomas Thrainer
      self.instance.primary_node,
1715 1c3231aa Thomas Thrainer
      self.op.target_node_uuid,
1716 22b7f6f8 Thomas Thrainer
      ]
1717 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1718 22b7f6f8 Thomas Thrainer
1719 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1720 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1721 22b7f6f8 Thomas Thrainer

1722 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1723 22b7f6f8 Thomas Thrainer

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

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

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

1874 22b7f6f8 Thomas Thrainer
  """
1875 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1876 22b7f6f8 Thomas Thrainer
1877 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1878 22b7f6f8 Thomas Thrainer
    """Check arguments.
1879 22b7f6f8 Thomas Thrainer

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

1918 22b7f6f8 Thomas Thrainer
    """
1919 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1920 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
1921 22b7f6f8 Thomas Thrainer
      # iallocator will select nodes and even if no iallocator is used,
1922 22b7f6f8 Thomas Thrainer
      # collisions with LUInstanceCreate should be avoided
1923 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1924 22b7f6f8 Thomas Thrainer
      }
1925 22b7f6f8 Thomas Thrainer
1926 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
1927 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
1928 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = locking.ALL_SET
1929 22b7f6f8 Thomas Thrainer
1930 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
1931 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
1932 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE_RES] = True
1933 22b7f6f8 Thomas Thrainer
    else:
1934 22b7f6f8 Thomas Thrainer
      nodeslist = []
1935 22b7f6f8 Thomas Thrainer
      for inst in self.op.instances:
1936 1c3231aa Thomas Thrainer
        (inst.pnode_uuid, inst.pnode) = \
1937 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, inst.pnode_uuid, inst.pnode)
1938 22b7f6f8 Thomas Thrainer
        nodeslist.append(inst.pnode)
1939 22b7f6f8 Thomas Thrainer
        if inst.snode is not None:
1940 1c3231aa Thomas Thrainer
          (inst.snode_uuid, inst.snode) = \
1941 1c3231aa Thomas Thrainer
            ExpandNodeUuidAndName(self.cfg, inst.snode_uuid, inst.snode)
1942 22b7f6f8 Thomas Thrainer
          nodeslist.append(inst.snode)
1943 22b7f6f8 Thomas Thrainer
1944 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodeslist
1945 22b7f6f8 Thomas Thrainer
      # Lock resources of instance's primary and secondary nodes (copy to
1946 22b7f6f8 Thomas Thrainer
      # prevent accidential modification)
1947 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = list(nodeslist)
1948 22b7f6f8 Thomas Thrainer
1949 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1950 22b7f6f8 Thomas Thrainer
    """Check prerequisite.
1951 22b7f6f8 Thomas Thrainer

1952 22b7f6f8 Thomas Thrainer
    """
1953 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
1954 22b7f6f8 Thomas Thrainer
    default_vg = self.cfg.GetVGName()
1955 22b7f6f8 Thomas Thrainer
    ec_id = self.proc.GetECId()
1956 22b7f6f8 Thomas Thrainer
1957 22b7f6f8 Thomas Thrainer
    if self.op.opportunistic_locking:
1958 22b7f6f8 Thomas Thrainer
      # Only consider nodes for which a lock is held
1959 1c3231aa Thomas Thrainer
      node_whitelist = self.cfg.GetNodeNames(
1960 1c3231aa Thomas Thrainer
                         list(self.owned_locks(locking.LEVEL_NODE)))
1961 22b7f6f8 Thomas Thrainer
    else:
1962 22b7f6f8 Thomas Thrainer
      node_whitelist = None
1963 22b7f6f8 Thomas Thrainer
1964 5eacbcae Thomas Thrainer
    insts = [_CreateInstanceAllocRequest(op, ComputeDisks(op, default_vg),
1965 22b7f6f8 Thomas Thrainer
                                         _ComputeNics(op, cluster, None,
1966 22b7f6f8 Thomas Thrainer
                                                      self.cfg, ec_id),
1967 22b7f6f8 Thomas Thrainer
                                         _ComputeFullBeParams(op, cluster),
1968 22b7f6f8 Thomas Thrainer
                                         node_whitelist)
1969 22b7f6f8 Thomas Thrainer
             for op in self.op.instances]
1970 22b7f6f8 Thomas Thrainer
1971 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqMultiInstanceAlloc(instances=insts)
1972 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
1973 22b7f6f8 Thomas Thrainer
1974 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
1975 22b7f6f8 Thomas Thrainer
1976 22b7f6f8 Thomas Thrainer
    if not ial.success:
1977 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute nodes using"
1978 22b7f6f8 Thomas Thrainer
                                 " iallocator '%s': %s" %
1979 22b7f6f8 Thomas Thrainer
                                 (self.op.iallocator, ial.info),
1980 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_NORES)
1981 22b7f6f8 Thomas Thrainer
1982 22b7f6f8 Thomas Thrainer
    self.ia_result = ial.result
1983 22b7f6f8 Thomas Thrainer
1984 22b7f6f8 Thomas Thrainer
    if self.op.dry_run:
1985 22b7f6f8 Thomas Thrainer
      self.dry_run_result = objects.FillDict(self._ConstructPartialResult(), {
1986 22b7f6f8 Thomas Thrainer
        constants.JOB_IDS_KEY: [],
1987 22b7f6f8 Thomas Thrainer
        })
1988 22b7f6f8 Thomas Thrainer
1989 22b7f6f8 Thomas Thrainer
  def _ConstructPartialResult(self):
1990 22b7f6f8 Thomas Thrainer
    """Contructs the partial result.
1991 22b7f6f8 Thomas Thrainer

1992 22b7f6f8 Thomas Thrainer
    """
1993 22b7f6f8 Thomas Thrainer
    (allocatable, failed) = self.ia_result
1994 22b7f6f8 Thomas Thrainer
    return {
1995 22b7f6f8 Thomas Thrainer
      opcodes.OpInstanceMultiAlloc.ALLOCATABLE_KEY:
1996 22b7f6f8 Thomas Thrainer
        map(compat.fst, allocatable),
1997 22b7f6f8 Thomas Thrainer
      opcodes.OpInstanceMultiAlloc.FAILED_KEY: failed,
1998 22b7f6f8 Thomas Thrainer
      }
1999 22b7f6f8 Thomas Thrainer
2000 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2001 22b7f6f8 Thomas Thrainer
    """Executes the opcode.
2002 22b7f6f8 Thomas Thrainer

2003 22b7f6f8 Thomas Thrainer
    """
2004 22b7f6f8 Thomas Thrainer
    op2inst = dict((op.instance_name, op) for op in self.op.instances)
2005 22b7f6f8 Thomas Thrainer
    (allocatable, failed) = self.ia_result
2006 22b7f6f8 Thomas Thrainer
2007 22b7f6f8 Thomas Thrainer
    jobs = []
2008 1c3231aa Thomas Thrainer
    for (name, node_names) in allocatable:
2009 22b7f6f8 Thomas Thrainer
      op = op2inst.pop(name)
2010 22b7f6f8 Thomas Thrainer
2011 1c3231aa Thomas Thrainer
      (op.pnode_uuid, op.pnode) = \
2012 1c3231aa Thomas Thrainer
        ExpandNodeUuidAndName(self.cfg, None, node_names[0])
2013 1c3231aa Thomas Thrainer
      if len(node_names) > 1:
2014 1c3231aa Thomas Thrainer
        (op.snode_uuid, op.snode) = \
2015 1c3231aa Thomas Thrainer
          ExpandNodeUuidAndName(self.cfg, None, node_names[1])
2016 22b7f6f8 Thomas Thrainer
2017 22b7f6f8 Thomas Thrainer
      jobs.append([op])
2018 22b7f6f8 Thomas Thrainer
2019 22b7f6f8 Thomas Thrainer
    missing = set(op2inst.keys()) - set(failed)
2020 22b7f6f8 Thomas Thrainer
    assert not missing, \
2021 22b7f6f8 Thomas Thrainer
      "Iallocator did return incomplete result: %s" % utils.CommaJoin(missing)
2022 22b7f6f8 Thomas Thrainer
2023 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs, **self._ConstructPartialResult())
2024 22b7f6f8 Thomas Thrainer
2025 22b7f6f8 Thomas Thrainer
2026 22b7f6f8 Thomas Thrainer
class _InstNicModPrivate:
2027 22b7f6f8 Thomas Thrainer
  """Data structure for network interface modifications.
2028 22b7f6f8 Thomas Thrainer

2029 22b7f6f8 Thomas Thrainer
  Used by L{LUInstanceSetParams}.
2030 22b7f6f8 Thomas Thrainer

2031 22b7f6f8 Thomas Thrainer
  """
2032 22b7f6f8 Thomas Thrainer
  def __init__(self):
2033 22b7f6f8 Thomas Thrainer
    self.params = None
2034 22b7f6f8 Thomas Thrainer
    self.filled = None
2035 22b7f6f8 Thomas Thrainer
2036 22b7f6f8 Thomas Thrainer
2037 5eacbcae Thomas Thrainer
def _PrepareContainerMods(mods, private_fn):
2038 22b7f6f8 Thomas Thrainer
  """Prepares a list of container modifications by adding a private data field.
2039 22b7f6f8 Thomas Thrainer

2040 22b7f6f8 Thomas Thrainer
  @type mods: list of tuples; (operation, index, parameters)
2041 22b7f6f8 Thomas Thrainer
  @param mods: List of modifications
2042 22b7f6f8 Thomas Thrainer
  @type private_fn: callable or None
2043 22b7f6f8 Thomas Thrainer
  @param private_fn: Callable for constructing a private data field for a
2044 22b7f6f8 Thomas Thrainer
    modification
2045 22b7f6f8 Thomas Thrainer
  @rtype: list
2046 22b7f6f8 Thomas Thrainer

2047 22b7f6f8 Thomas Thrainer
  """
2048 22b7f6f8 Thomas Thrainer
  if private_fn is None:
2049 22b7f6f8 Thomas Thrainer
    fn = lambda: None
2050 22b7f6f8 Thomas Thrainer
  else:
2051 22b7f6f8 Thomas Thrainer
    fn = private_fn
2052 22b7f6f8 Thomas Thrainer
2053 22b7f6f8 Thomas Thrainer
  return [(op, idx, params, fn()) for (op, idx, params) in mods]
2054 22b7f6f8 Thomas Thrainer
2055 22b7f6f8 Thomas Thrainer
2056 1c3231aa Thomas Thrainer
def _CheckNodesPhysicalCPUs(lu, node_uuids, requested, hypervisor_specs):
2057 22b7f6f8 Thomas Thrainer
  """Checks if nodes have enough physical CPUs
2058 22b7f6f8 Thomas Thrainer

2059 22b7f6f8 Thomas Thrainer
  This function checks if all given nodes have the needed number of
2060 22b7f6f8 Thomas Thrainer
  physical CPUs. In case any node has less CPUs or we cannot get the
2061 22b7f6f8 Thomas Thrainer
  information from the node, this function raises an OpPrereqError
2062 22b7f6f8 Thomas Thrainer
  exception.
2063 22b7f6f8 Thomas Thrainer

2064 22b7f6f8 Thomas Thrainer
  @type lu: C{LogicalUnit}
2065 22b7f6f8 Thomas Thrainer
  @param lu: a logical unit from which we get configuration data
2066 1c3231aa Thomas Thrainer
  @type node_uuids: C{list}
2067 1c3231aa Thomas Thrainer
  @param node_uuids: the list of node UUIDs to check
2068 22b7f6f8 Thomas Thrainer
  @type requested: C{int}
2069 22b7f6f8 Thomas Thrainer
  @param requested: the minimum acceptable number of physical CPUs
2070 a295eb80 Helga Velroyen
  @type hypervisor_specs: list of pairs (string, dict of strings)
2071 a295eb80 Helga Velroyen
  @param hypervisor_specs: list of hypervisor specifications in
2072 a295eb80 Helga Velroyen
      pairs (hypervisor_name, hvparams)
2073 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node doesn't have enough CPUs,
2074 22b7f6f8 Thomas Thrainer
      or we cannot check the node
2075 22b7f6f8 Thomas Thrainer

2076 22b7f6f8 Thomas Thrainer
  """
2077 da803ff1 Helga Velroyen
  nodeinfo = lu.rpc.call_node_info(node_uuids, None, hypervisor_specs)
2078 1c3231aa Thomas Thrainer
  for node_uuid in node_uuids:
2079 1c3231aa Thomas Thrainer
    info = nodeinfo[node_uuid]
2080 1c3231aa Thomas Thrainer
    node_name = lu.cfg.GetNodeName(node_uuid)
2081 1c3231aa Thomas Thrainer
    info.Raise("Cannot get current information from node %s" % node_name,
2082 22b7f6f8 Thomas Thrainer
               prereq=True, ecode=errors.ECODE_ENVIRON)
2083 22b7f6f8 Thomas Thrainer
    (_, _, (hv_info, )) = info.payload
2084 22b7f6f8 Thomas Thrainer
    num_cpus = hv_info.get("cpu_total", None)
2085 22b7f6f8 Thomas Thrainer
    if not isinstance(num_cpus, int):
2086 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute the number of physical CPUs"
2087 22b7f6f8 Thomas Thrainer
                                 " on node %s, result was '%s'" %
2088 1c3231aa Thomas Thrainer
                                 (node_name, num_cpus), errors.ECODE_ENVIRON)
2089 22b7f6f8 Thomas Thrainer
    if requested > num_cpus:
2090 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Node %s has %s physical CPUs, but %s are "
2091 1c3231aa Thomas Thrainer
                                 "required" % (node_name, num_cpus, requested),
2092 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_NORES)
2093 22b7f6f8 Thomas Thrainer
2094 22b7f6f8 Thomas Thrainer
2095 22b7f6f8 Thomas Thrainer
def GetItemFromContainer(identifier, kind, container):
2096 22b7f6f8 Thomas Thrainer
  """Return the item refered by the identifier.
2097 22b7f6f8 Thomas Thrainer

2098 22b7f6f8 Thomas Thrainer
  @type identifier: string
2099 22b7f6f8 Thomas Thrainer
  @param identifier: Item index or name or UUID
2100 22b7f6f8 Thomas Thrainer
  @type kind: string
2101 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2102 22b7f6f8 Thomas Thrainer
  @type container: list
2103 22b7f6f8 Thomas Thrainer
  @param container: Container to get the item from
2104 22b7f6f8 Thomas Thrainer

2105 22b7f6f8 Thomas Thrainer
  """
2106 22b7f6f8 Thomas Thrainer
  # Index
2107 22b7f6f8 Thomas Thrainer
  try:
2108 22b7f6f8 Thomas Thrainer
    idx = int(identifier)
2109 22b7f6f8 Thomas Thrainer
    if idx == -1:
2110 22b7f6f8 Thomas Thrainer
      # Append
2111 22b7f6f8 Thomas Thrainer
      absidx = len(container) - 1
2112 22b7f6f8 Thomas Thrainer
    elif idx < 0:
2113 22b7f6f8 Thomas Thrainer
      raise IndexError("Not accepting negative indices other than -1")
2114 22b7f6f8 Thomas Thrainer
    elif idx > len(container):
2115 22b7f6f8 Thomas Thrainer
      raise IndexError("Got %s index %s, but there are only %s" %
2116 22b7f6f8 Thomas Thrainer
                       (kind, idx, len(container)))
2117 22b7f6f8 Thomas Thrainer
    else:
2118 22b7f6f8 Thomas Thrainer
      absidx = idx
2119 22b7f6f8 Thomas Thrainer
    return (absidx, container[idx])
2120 22b7f6f8 Thomas Thrainer
  except ValueError:
2121 22b7f6f8 Thomas Thrainer
    pass
2122 22b7f6f8 Thomas Thrainer
2123 22b7f6f8 Thomas Thrainer
  for idx, item in enumerate(container):
2124 22b7f6f8 Thomas Thrainer
    if item.uuid == identifier or item.name == identifier:
2125 22b7f6f8 Thomas Thrainer
      return (idx, item)
2126 22b7f6f8 Thomas Thrainer
2127 22b7f6f8 Thomas Thrainer
  raise errors.OpPrereqError("Cannot find %s with identifier %s" %
2128 22b7f6f8 Thomas Thrainer
                             (kind, identifier), errors.ECODE_NOENT)
2129 22b7f6f8 Thomas Thrainer
2130 22b7f6f8 Thomas Thrainer
2131 5eacbcae Thomas Thrainer
def _ApplyContainerMods(kind, container, chgdesc, mods,
2132 5eacbcae Thomas Thrainer
                        create_fn, modify_fn, remove_fn):
2133 22b7f6f8 Thomas Thrainer
  """Applies descriptions in C{mods} to C{container}.
2134 22b7f6f8 Thomas Thrainer

2135 22b7f6f8 Thomas Thrainer
  @type kind: string
2136 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2137 22b7f6f8 Thomas Thrainer
  @type container: list
2138 22b7f6f8 Thomas Thrainer
  @param container: Container to modify
2139 22b7f6f8 Thomas Thrainer
  @type chgdesc: None or list
2140 22b7f6f8 Thomas Thrainer
  @param chgdesc: List of applied changes
2141 22b7f6f8 Thomas Thrainer
  @type mods: list
2142 5eacbcae Thomas Thrainer
  @param mods: Modifications as returned by L{_PrepareContainerMods}
2143 22b7f6f8 Thomas Thrainer
  @type create_fn: callable
2144 22b7f6f8 Thomas Thrainer
  @param create_fn: Callback for creating a new item (L{constants.DDM_ADD});
2145 22b7f6f8 Thomas Thrainer
    receives absolute item index, parameters and private data object as added
2146 5eacbcae Thomas Thrainer
    by L{_PrepareContainerMods}, returns tuple containing new item and changes
2147 22b7f6f8 Thomas Thrainer
    as list
2148 22b7f6f8 Thomas Thrainer
  @type modify_fn: callable
2149 22b7f6f8 Thomas Thrainer
  @param modify_fn: Callback for modifying an existing item
2150 22b7f6f8 Thomas Thrainer
    (L{constants.DDM_MODIFY}); receives absolute item index, item, parameters
2151 5eacbcae Thomas Thrainer
    and private data object as added by L{_PrepareContainerMods}, returns
2152 22b7f6f8 Thomas Thrainer
    changes as list
2153 22b7f6f8 Thomas Thrainer
  @type remove_fn: callable
2154 22b7f6f8 Thomas Thrainer
  @param remove_fn: Callback on removing item; receives absolute item index,
2155 5eacbcae Thomas Thrainer
    item and private data object as added by L{_PrepareContainerMods}
2156 22b7f6f8 Thomas Thrainer

2157 22b7f6f8 Thomas Thrainer
  """
2158 22b7f6f8 Thomas Thrainer
  for (op, identifier, params, private) in mods:
2159 22b7f6f8 Thomas Thrainer
    changes = None
2160 22b7f6f8 Thomas Thrainer
2161 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2162 22b7f6f8 Thomas Thrainer
      # Calculate where item will be added
2163 22b7f6f8 Thomas Thrainer
      # When adding an item, identifier can only be an index
2164 22b7f6f8 Thomas Thrainer
      try:
2165 22b7f6f8 Thomas Thrainer
        idx = int(identifier)
2166 22b7f6f8 Thomas Thrainer
      except ValueError:
2167 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Only possitive integer or -1 is accepted as"
2168 22b7f6f8 Thomas Thrainer
                                   " identifier for %s" % constants.DDM_ADD,
2169 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2170 22b7f6f8 Thomas Thrainer
      if idx == -1:
2171 22b7f6f8 Thomas Thrainer
        addidx = len(container)
2172 22b7f6f8 Thomas Thrainer
      else:
2173 22b7f6f8 Thomas Thrainer
        if idx < 0:
2174 22b7f6f8 Thomas Thrainer
          raise IndexError("Not accepting negative indices other than -1")
2175 22b7f6f8 Thomas Thrainer
        elif idx > len(container):
2176 22b7f6f8 Thomas Thrainer
          raise IndexError("Got %s index %s, but there are only %s" %
2177 22b7f6f8 Thomas Thrainer
                           (kind, idx, len(container)))
2178 22b7f6f8 Thomas Thrainer
        addidx = idx
2179 22b7f6f8 Thomas Thrainer
2180 22b7f6f8 Thomas Thrainer
      if create_fn is None:
2181 22b7f6f8 Thomas Thrainer
        item = params
2182 22b7f6f8 Thomas Thrainer
      else:
2183 22b7f6f8 Thomas Thrainer
        (item, changes) = create_fn(addidx, params, private)
2184 22b7f6f8 Thomas Thrainer
2185 22b7f6f8 Thomas Thrainer
      if idx == -1:
2186 22b7f6f8 Thomas Thrainer
        container.append(item)
2187 22b7f6f8 Thomas Thrainer
      else:
2188 22b7f6f8 Thomas Thrainer
        assert idx >= 0
2189 22b7f6f8 Thomas Thrainer
        assert idx <= len(container)
2190 22b7f6f8 Thomas Thrainer
        # list.insert does so before the specified index
2191 22b7f6f8 Thomas Thrainer
        container.insert(idx, item)
2192 22b7f6f8 Thomas Thrainer
    else:
2193 22b7f6f8 Thomas Thrainer
      # Retrieve existing item
2194 22b7f6f8 Thomas Thrainer
      (absidx, item) = GetItemFromContainer(identifier, kind, container)
2195 22b7f6f8 Thomas Thrainer
2196 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2197 22b7f6f8 Thomas Thrainer
        assert not params
2198 22b7f6f8 Thomas Thrainer
2199 22b7f6f8 Thomas Thrainer
        if remove_fn is not None:
2200 22b7f6f8 Thomas Thrainer
          remove_fn(absidx, item, private)
2201 22b7f6f8 Thomas Thrainer
2202 22b7f6f8 Thomas Thrainer
        changes = [("%s/%s" % (kind, absidx), "remove")]
2203 22b7f6f8 Thomas Thrainer
2204 22b7f6f8 Thomas Thrainer
        assert container[absidx] == item
2205 22b7f6f8 Thomas Thrainer
        del container[absidx]
2206 22b7f6f8 Thomas Thrainer
      elif op == constants.DDM_MODIFY:
2207 22b7f6f8 Thomas Thrainer
        if modify_fn is not None:
2208 22b7f6f8 Thomas Thrainer
          changes = modify_fn(absidx, item, params, private)
2209 22b7f6f8 Thomas Thrainer
      else:
2210 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
2211 22b7f6f8 Thomas Thrainer
2212 22b7f6f8 Thomas Thrainer
    assert _TApplyContModsCbChanges(changes)
2213 22b7f6f8 Thomas Thrainer
2214 22b7f6f8 Thomas Thrainer
    if not (chgdesc is None or changes is None):
2215 22b7f6f8 Thomas Thrainer
      chgdesc.extend(changes)
2216 22b7f6f8 Thomas Thrainer
2217 22b7f6f8 Thomas Thrainer
2218 22b7f6f8 Thomas Thrainer
def _UpdateIvNames(base_index, disks):
2219 22b7f6f8 Thomas Thrainer
  """Updates the C{iv_name} attribute of disks.
2220 22b7f6f8 Thomas Thrainer

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

2223 22b7f6f8 Thomas Thrainer
  """
2224 22b7f6f8 Thomas Thrainer
  for (idx, disk) in enumerate(disks):
2225 22b7f6f8 Thomas Thrainer
    disk.iv_name = "disk/%s" % (base_index + idx, )
2226 22b7f6f8 Thomas Thrainer
2227 22b7f6f8 Thomas Thrainer
2228 22b7f6f8 Thomas Thrainer
class LUInstanceSetParams(LogicalUnit):
2229 22b7f6f8 Thomas Thrainer
  """Modifies an instances's parameters.
2230 22b7f6f8 Thomas Thrainer

2231 22b7f6f8 Thomas Thrainer
  """
2232 22b7f6f8 Thomas Thrainer
  HPATH = "instance-modify"
2233 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2234 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2235 22b7f6f8 Thomas Thrainer
2236 22b7f6f8 Thomas Thrainer
  @staticmethod
2237 22b7f6f8 Thomas Thrainer
  def _UpgradeDiskNicMods(kind, mods, verify_fn):
2238 22b7f6f8 Thomas Thrainer
    assert ht.TList(mods)
2239 22b7f6f8 Thomas Thrainer
    assert not mods or len(mods[0]) in (2, 3)
2240 22b7f6f8 Thomas Thrainer
2241 22b7f6f8 Thomas Thrainer
    if mods and len(mods[0]) == 2:
2242 22b7f6f8 Thomas Thrainer
      result = []
2243 22b7f6f8 Thomas Thrainer
2244 22b7f6f8 Thomas Thrainer
      addremove = 0
2245 22b7f6f8 Thomas Thrainer
      for op, params in mods:
2246 22b7f6f8 Thomas Thrainer
        if op in (constants.DDM_ADD, constants.DDM_REMOVE):
2247 22b7f6f8 Thomas Thrainer
          result.append((op, -1, params))
2248 22b7f6f8 Thomas Thrainer
          addremove += 1
2249 22b7f6f8 Thomas Thrainer
2250 22b7f6f8 Thomas Thrainer
          if addremove > 1:
2251 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Only one %s add or remove operation is"
2252 22b7f6f8 Thomas Thrainer
                                       " supported at a time" % kind,
2253 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
2254 22b7f6f8 Thomas Thrainer
        else:
2255 22b7f6f8 Thomas Thrainer
          result.append((constants.DDM_MODIFY, op, params))
2256 22b7f6f8 Thomas Thrainer
2257 22b7f6f8 Thomas Thrainer
      assert verify_fn(result)
2258 22b7f6f8 Thomas Thrainer
    else:
2259 22b7f6f8 Thomas Thrainer
      result = mods
2260 22b7f6f8 Thomas Thrainer
2261 22b7f6f8 Thomas Thrainer
    return result
2262 22b7f6f8 Thomas Thrainer
2263 22b7f6f8 Thomas Thrainer
  @staticmethod
2264 22b7f6f8 Thomas Thrainer
  def _CheckMods(kind, mods, key_types, item_fn):
2265 22b7f6f8 Thomas Thrainer
    """Ensures requested disk/NIC modifications are valid.
2266 22b7f6f8 Thomas Thrainer

2267 22b7f6f8 Thomas Thrainer
    """
2268 22b7f6f8 Thomas Thrainer
    for (op, _, params) in mods:
2269 22b7f6f8 Thomas Thrainer
      assert ht.TDict(params)
2270 22b7f6f8 Thomas Thrainer
2271 22b7f6f8 Thomas Thrainer
      # If 'key_types' is an empty dict, we assume we have an
2272 22b7f6f8 Thomas Thrainer
      # 'ext' template and thus do not ForceDictType
2273 22b7f6f8 Thomas Thrainer
      if key_types:
2274 22b7f6f8 Thomas Thrainer
        utils.ForceDictType(params, key_types)
2275 22b7f6f8 Thomas Thrainer
2276 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2277 22b7f6f8 Thomas Thrainer
        if params:
2278 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("No settings should be passed when"
2279 22b7f6f8 Thomas Thrainer
                                     " removing a %s" % kind,
2280 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
2281 22b7f6f8 Thomas Thrainer
      elif op in (constants.DDM_ADD, constants.DDM_MODIFY):
2282 22b7f6f8 Thomas Thrainer
        item_fn(op, params)
2283 22b7f6f8 Thomas Thrainer
      else:
2284 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
2285 22b7f6f8 Thomas Thrainer
2286 22b7f6f8 Thomas Thrainer
  @staticmethod
2287 3f3ea14c Bernardo Dal Seno
  def _VerifyDiskModification(op, params, excl_stor):
2288 22b7f6f8 Thomas Thrainer
    """Verifies a disk modification.
2289 22b7f6f8 Thomas Thrainer

2290 22b7f6f8 Thomas Thrainer
    """
2291 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2292 22b7f6f8 Thomas Thrainer
      mode = params.setdefault(constants.IDISK_MODE, constants.DISK_RDWR)
2293 22b7f6f8 Thomas Thrainer
      if mode not in constants.DISK_ACCESS_SET:
2294 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk access mode '%s'" % mode,
2295 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2296 22b7f6f8 Thomas Thrainer
2297 22b7f6f8 Thomas Thrainer
      size = params.get(constants.IDISK_SIZE, None)
2298 22b7f6f8 Thomas Thrainer
      if size is None:
2299 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Required disk parameter '%s' missing" %
2300 22b7f6f8 Thomas Thrainer
                                   constants.IDISK_SIZE, errors.ECODE_INVAL)
2301 22b7f6f8 Thomas Thrainer
2302 22b7f6f8 Thomas Thrainer
      try:
2303 22b7f6f8 Thomas Thrainer
        size = int(size)
2304 22b7f6f8 Thomas Thrainer
      except (TypeError, ValueError), err:
2305 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk size parameter: %s" % err,
2306 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2307 22b7f6f8 Thomas Thrainer
2308 22b7f6f8 Thomas Thrainer
      params[constants.IDISK_SIZE] = size
2309 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2310 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2311 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2312 22b7f6f8 Thomas Thrainer
2313 7c848a6a Bernardo Dal Seno
      CheckSpindlesExclusiveStorage(params, excl_stor, True)
2314 3f3ea14c Bernardo Dal Seno
2315 22b7f6f8 Thomas Thrainer
    elif op == constants.DDM_MODIFY:
2316 22b7f6f8 Thomas Thrainer
      if constants.IDISK_SIZE in params:
2317 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk size change not possible, use"
2318 22b7f6f8 Thomas Thrainer
                                   " grow-disk", errors.ECODE_INVAL)
2319 22b7f6f8 Thomas Thrainer
      if len(params) > 2:
2320 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk modification doesn't support"
2321 22b7f6f8 Thomas Thrainer
                                   " additional arbitrary parameters",
2322 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2323 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2324 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2325 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2326 22b7f6f8 Thomas Thrainer
2327 22b7f6f8 Thomas Thrainer
  @staticmethod
2328 22b7f6f8 Thomas Thrainer
  def _VerifyNicModification(op, params):
2329 22b7f6f8 Thomas Thrainer
    """Verifies a network interface modification.
2330 22b7f6f8 Thomas Thrainer

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

2447 22b7f6f8 Thomas Thrainer
    This runs on the master, primary and secondaries.
2448 22b7f6f8 Thomas Thrainer

2449 22b7f6f8 Thomas Thrainer
    """
2450 22b7f6f8 Thomas Thrainer
    args = {}
2451 22b7f6f8 Thomas Thrainer
    if constants.BE_MINMEM in self.be_new:
2452 22b7f6f8 Thomas Thrainer
      args["minmem"] = self.be_new[constants.BE_MINMEM]
2453 22b7f6f8 Thomas Thrainer
    if constants.BE_MAXMEM in self.be_new:
2454 22b7f6f8 Thomas Thrainer
      args["maxmem"] = self.be_new[constants.BE_MAXMEM]
2455 22b7f6f8 Thomas Thrainer
    if constants.BE_VCPUS in self.be_new:
2456 22b7f6f8 Thomas Thrainer
      args["vcpus"] = self.be_new[constants.BE_VCPUS]
2457 22b7f6f8 Thomas Thrainer
    # TODO: export disk changes. Note: _BuildInstanceHookEnv* don't export disk
2458 22b7f6f8 Thomas Thrainer
    # information at all.
2459 22b7f6f8 Thomas Thrainer
2460 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
2461 22b7f6f8 Thomas Thrainer
      nics = []
2462 22b7f6f8 Thomas Thrainer
2463 22b7f6f8 Thomas Thrainer
      for nic in self._new_nics:
2464 22b7f6f8 Thomas Thrainer
        n = copy.deepcopy(nic)
2465 22b7f6f8 Thomas Thrainer
        nicparams = self.cluster.SimpleFillNIC(n.nicparams)
2466 22b7f6f8 Thomas Thrainer
        n.nicparams = nicparams
2467 5eacbcae Thomas Thrainer
        nics.append(NICToTuple(self, n))
2468 22b7f6f8 Thomas Thrainer
2469 22b7f6f8 Thomas Thrainer
      args["nics"] = nics
2470 22b7f6f8 Thomas Thrainer
2471 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance, override=args)
2472 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
2473 22b7f6f8 Thomas Thrainer
      env["NEW_DISK_TEMPLATE"] = self.op.disk_template
2474 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
2475 22b7f6f8 Thomas Thrainer
      env["RUNTIME_MEMORY"] = self.op.runtime_mem
2476 22b7f6f8 Thomas Thrainer
2477 22b7f6f8 Thomas Thrainer
    return env
2478 22b7f6f8 Thomas Thrainer
2479 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2480 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2481 22b7f6f8 Thomas Thrainer

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

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

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

2769 1bb99a33 Bernardo Dal Seno
    This only checks the instance list against the existing names.
2770 1bb99a33 Bernardo Dal Seno

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

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

3155 22b7f6f8 Thomas Thrainer
    """
3156 d0d7d7cf Thomas Thrainer
    assert len(self.instance.secondary_nodes) == 1
3157 d0d7d7cf Thomas Thrainer
    assert self.instance.disk_template == constants.DT_DRBD8
3158 22b7f6f8 Thomas Thrainer
3159 d0d7d7cf Thomas Thrainer
    pnode_uuid = self.instance.primary_node
3160 d0d7d7cf Thomas Thrainer
    snode_uuid = self.instance.secondary_nodes[0]
3161 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to plain")
3162 22b7f6f8 Thomas Thrainer
3163 d0d7d7cf Thomas Thrainer
    old_disks = AnnotateDiskParams(self.instance, self.instance.disks, self.cfg)
3164 d0d7d7cf Thomas Thrainer
    new_disks = [d.children[0] for d in self.instance.disks]
3165 22b7f6f8 Thomas Thrainer
3166 22b7f6f8 Thomas Thrainer
    # copy over size, mode and name
3167 22b7f6f8 Thomas Thrainer
    for parent, child in zip(old_disks, new_disks):
3168 22b7f6f8 Thomas Thrainer
      child.size = parent.size
3169 22b7f6f8 Thomas Thrainer
      child.mode = parent.mode
3170 22b7f6f8 Thomas Thrainer
      child.name = parent.name
3171 22b7f6f8 Thomas Thrainer
3172 22b7f6f8 Thomas Thrainer
    # this is a DRBD disk, return its port to the pool
3173 22b7f6f8 Thomas Thrainer
    # NOTE: this must be done right before the call to cfg.Update!
3174 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3175 22b7f6f8 Thomas Thrainer
      tcp_port = disk.logical_id[2]
3176 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(tcp_port)
3177 22b7f6f8 Thomas Thrainer
3178 22b7f6f8 Thomas Thrainer
    # update instance structure
3179 d0d7d7cf Thomas Thrainer
    self.instance.disks = new_disks
3180 d0d7d7cf Thomas Thrainer
    self.instance.disk_template = constants.DT_PLAIN
3181 d0d7d7cf Thomas Thrainer
    _UpdateIvNames(0, self.instance.disks)
3182 d0d7d7cf Thomas Thrainer
    self.cfg.Update(self.instance, feedback_fn)
3183 22b7f6f8 Thomas Thrainer
3184 22b7f6f8 Thomas Thrainer
    # Release locks in case removing disks takes a while
3185 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3186 22b7f6f8 Thomas Thrainer
3187 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing volumes on the secondary node...")
3188 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3189 1c3231aa Thomas Thrainer
      self.cfg.SetDiskID(disk, snode_uuid)
3190 1c3231aa Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(snode_uuid, disk).fail_msg
3191 22b7f6f8 Thomas Thrainer
      if msg:
3192 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove block device %s on node %s,"
3193 1c3231aa Thomas Thrainer
                        " continuing anyway: %s", disk.iv_name,
3194 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(snode_uuid), msg)
3195 22b7f6f8 Thomas Thrainer
3196 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing unneeded volumes on the primary node...")
3197 22b7f6f8 Thomas Thrainer
    for idx, disk in enumerate(old_disks):
3198 22b7f6f8 Thomas Thrainer
      meta = disk.children[1]
3199 1c3231aa Thomas Thrainer
      self.cfg.SetDiskID(meta, pnode_uuid)
3200 1c3231aa Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(pnode_uuid, meta).fail_msg
3201 22b7f6f8 Thomas Thrainer
      if msg:
3202 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove metadata for disk %d on node %s,"
3203 1c3231aa Thomas Thrainer
                        " continuing anyway: %s", idx,
3204 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(pnode_uuid), msg)
3205 22b7f6f8 Thomas Thrainer
3206 22b7f6f8 Thomas Thrainer
  def _CreateNewDisk(self, idx, params, _):
3207 22b7f6f8 Thomas Thrainer
    """Creates a new disk.
3208 22b7f6f8 Thomas Thrainer

3209 22b7f6f8 Thomas Thrainer
    """
3210 22b7f6f8 Thomas Thrainer
    # add a new disk
3211 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DTS_FILEBASED:
3212 d0d7d7cf Thomas Thrainer
      (file_driver, file_path) = self.instance.disks[0].logical_id
3213 22b7f6f8 Thomas Thrainer
      file_path = os.path.dirname(file_path)
3214 22b7f6f8 Thomas Thrainer
    else:
3215 22b7f6f8 Thomas Thrainer
      file_driver = file_path = None
3216 22b7f6f8 Thomas Thrainer
3217 22b7f6f8 Thomas Thrainer
    disk = \
3218 d0d7d7cf Thomas Thrainer
      GenerateDiskTemplate(self, self.instance.disk_template,
3219 da4a52a3 Thomas Thrainer
                           self.instance.uuid, self.instance.primary_node,
3220 d0d7d7cf Thomas Thrainer
                           self.instance.secondary_nodes, [params], file_path,
3221 d0d7d7cf Thomas Thrainer
                           file_driver, idx, self.Log, self.diskparams)[0]
3222 22b7f6f8 Thomas Thrainer
3223 d0d7d7cf Thomas Thrainer
    new_disks = CreateDisks(self, self.instance, disks=[disk])
3224 22b7f6f8 Thomas Thrainer
3225 22b7f6f8 Thomas Thrainer
    if self.cluster.prealloc_wipe_disks:
3226 22b7f6f8 Thomas Thrainer
      # Wipe new disk
3227 d0d7d7cf Thomas Thrainer
      WipeOrCleanupDisks(self, self.instance,
3228 a365b47f Bernardo Dal Seno
                         disks=[(idx, disk, 0)],
3229 a365b47f Bernardo Dal Seno
                         cleanup=new_disks)
3230 22b7f6f8 Thomas Thrainer
3231 22b7f6f8 Thomas Thrainer
    return (disk, [
3232 22b7f6f8 Thomas Thrainer
      ("disk/%d" % idx, "add:size=%s,mode=%s" % (disk.size, disk.mode)),
3233 22b7f6f8 Thomas Thrainer
      ])
3234 22b7f6f8 Thomas Thrainer
3235 22b7f6f8 Thomas Thrainer
  @staticmethod
3236 22b7f6f8 Thomas Thrainer
  def _ModifyDisk(idx, disk, params, _):
3237 22b7f6f8 Thomas Thrainer
    """Modifies a disk.
3238 22b7f6f8 Thomas Thrainer

3239 22b7f6f8 Thomas Thrainer
    """
3240 22b7f6f8 Thomas Thrainer
    changes = []
3241 22b7f6f8 Thomas Thrainer
    mode = params.get(constants.IDISK_MODE, None)
3242 22b7f6f8 Thomas Thrainer
    if mode:
3243 22b7f6f8 Thomas Thrainer
      disk.mode = mode
3244 22b7f6f8 Thomas Thrainer
      changes.append(("disk.mode/%d" % idx, disk.mode))
3245 22b7f6f8 Thomas Thrainer
3246 22b7f6f8 Thomas Thrainer
    name = params.get(constants.IDISK_NAME, None)
3247 22b7f6f8 Thomas Thrainer
    disk.name = name
3248 22b7f6f8 Thomas Thrainer
    changes.append(("disk.name/%d" % idx, disk.name))
3249 22b7f6f8 Thomas Thrainer
3250 22b7f6f8 Thomas Thrainer
    return changes
3251 22b7f6f8 Thomas Thrainer
3252 22b7f6f8 Thomas Thrainer
  def _RemoveDisk(self, idx, root, _):
3253 22b7f6f8 Thomas Thrainer
    """Removes a disk.
3254 22b7f6f8 Thomas Thrainer

3255 22b7f6f8 Thomas Thrainer
    """
3256 5eacbcae Thomas Thrainer
    (anno_disk,) = AnnotateDiskParams(self.instance, [root], self.cfg)
3257 1c3231aa Thomas Thrainer
    for node_uuid, disk in anno_disk.ComputeNodeTree(
3258 1c3231aa Thomas Thrainer
                             self.instance.primary_node):
3259 1c3231aa Thomas Thrainer
      self.cfg.SetDiskID(disk, node_uuid)
3260 1c3231aa Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(node_uuid, disk).fail_msg
3261 22b7f6f8 Thomas Thrainer
      if msg:
3262 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove disk/%d on node '%s': %s,"
3263 1c3231aa Thomas Thrainer
                        " continuing anyway", idx,
3264 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
3265 22b7f6f8 Thomas Thrainer
3266 22b7f6f8 Thomas Thrainer
    # if this is a DRBD disk, return its port to the pool
3267 22b7f6f8 Thomas Thrainer
    if root.dev_type in constants.LDS_DRBD:
3268 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(root.logical_id[2])
3269 22b7f6f8 Thomas Thrainer
3270 22b7f6f8 Thomas Thrainer
  def _CreateNewNic(self, idx, params, private):
3271 22b7f6f8 Thomas Thrainer
    """Creates data structure for a new network interface.
3272 22b7f6f8 Thomas Thrainer

3273 22b7f6f8 Thomas Thrainer
    """
3274 22b7f6f8 Thomas Thrainer
    mac = params[constants.INIC_MAC]
3275 22b7f6f8 Thomas Thrainer
    ip = params.get(constants.INIC_IP, None)
3276 22b7f6f8 Thomas Thrainer
    net = params.get(constants.INIC_NETWORK, None)
3277 22b7f6f8 Thomas Thrainer
    name = params.get(constants.INIC_NAME, None)
3278 22b7f6f8 Thomas Thrainer
    net_uuid = self.cfg.LookupNetwork(net)
3279 22b7f6f8 Thomas Thrainer
    #TODO: not private.filled?? can a nic have no nicparams??
3280 22b7f6f8 Thomas Thrainer
    nicparams = private.filled
3281 22b7f6f8 Thomas Thrainer
    nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, name=name,
3282 22b7f6f8 Thomas Thrainer
                       nicparams=nicparams)
3283 22b7f6f8 Thomas Thrainer
    nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
3284 22b7f6f8 Thomas Thrainer
3285 22b7f6f8 Thomas Thrainer
    return (nobj, [
3286 22b7f6f8 Thomas Thrainer
      ("nic.%d" % idx,
3287 22b7f6f8 Thomas Thrainer
       "add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" %
3288 22b7f6f8 Thomas Thrainer
       (mac, ip, private.filled[constants.NIC_MODE],
3289 22b7f6f8 Thomas Thrainer
       private.filled[constants.NIC_LINK],
3290 22b7f6f8 Thomas Thrainer
       net)),
3291 22b7f6f8 Thomas Thrainer
      ])
3292 22b7f6f8 Thomas Thrainer
3293 22b7f6f8 Thomas Thrainer
  def _ApplyNicMods(self, idx, nic, params, private):
3294 22b7f6f8 Thomas Thrainer
    """Modifies a network interface.
3295 22b7f6f8 Thomas Thrainer

3296 22b7f6f8 Thomas Thrainer
    """
3297 22b7f6f8 Thomas Thrainer
    changes = []
3298 22b7f6f8 Thomas Thrainer
3299 22b7f6f8 Thomas Thrainer
    for key in [constants.INIC_MAC, constants.INIC_IP, constants.INIC_NAME]:
3300 22b7f6f8 Thomas Thrainer
      if key in params:
3301 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), params[key]))
3302 22b7f6f8 Thomas Thrainer
        setattr(nic, key, params[key])
3303 22b7f6f8 Thomas Thrainer
3304 22b7f6f8 Thomas Thrainer
    new_net = params.get(constants.INIC_NETWORK, nic.network)
3305 22b7f6f8 Thomas Thrainer
    new_net_uuid = self.cfg.LookupNetwork(new_net)
3306 22b7f6f8 Thomas Thrainer
    if new_net_uuid != nic.network:
3307 22b7f6f8 Thomas Thrainer
      changes.append(("nic.network/%d" % idx, new_net))
3308 22b7f6f8 Thomas Thrainer
      nic.network = new_net_uuid
3309 22b7f6f8 Thomas Thrainer
3310 22b7f6f8 Thomas Thrainer
    if private.filled:
3311 22b7f6f8 Thomas Thrainer
      nic.nicparams = private.filled
3312 22b7f6f8 Thomas Thrainer
3313 22b7f6f8 Thomas Thrainer
      for (key, val) in nic.nicparams.items():
3314 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), val))
3315 22b7f6f8 Thomas Thrainer
3316 22b7f6f8 Thomas Thrainer
    return changes
3317 22b7f6f8 Thomas Thrainer
3318 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3319 22b7f6f8 Thomas Thrainer
    """Modifies an instance.
3320 22b7f6f8 Thomas Thrainer

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

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

3539 22b7f6f8 Thomas Thrainer
    """
3540 22b7f6f8 Thomas Thrainer
    assert self.target_uuids
3541 22b7f6f8 Thomas Thrainer
3542 22b7f6f8 Thomas Thrainer
    env = {
3543 22b7f6f8 Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
3544 22b7f6f8 Thomas Thrainer
      }
3545 22b7f6f8 Thomas Thrainer
3546 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
3547 22b7f6f8 Thomas Thrainer
3548 22b7f6f8 Thomas Thrainer
    return env
3549 22b7f6f8 Thomas Thrainer
3550 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
3551 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
3552 22b7f6f8 Thomas Thrainer

3553 22b7f6f8 Thomas Thrainer
    """
3554 22b7f6f8 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
3555 22b7f6f8 Thomas Thrainer
    return ([mn], [mn])
3556 22b7f6f8 Thomas Thrainer
3557 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3558 22b7f6f8 Thomas Thrainer
    instances = list(self.owned_locks(locking.LEVEL_INSTANCE))
3559 22b7f6f8 Thomas Thrainer
3560 22b7f6f8 Thomas Thrainer
    assert instances == [self.op.instance_name], "Instance not locked"
3561 22b7f6f8 Thomas Thrainer
3562 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=instances,
3563 22b7f6f8 Thomas Thrainer
                                      target_groups=list(self.target_uuids))
3564 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
3565 22b7f6f8 Thomas Thrainer
3566 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
3567 22b7f6f8 Thomas Thrainer
3568 22b7f6f8 Thomas Thrainer
    if not ial.success:
3569 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute solution for changing group of"
3570 22b7f6f8 Thomas Thrainer
                                 " instance '%s' using iallocator '%s': %s" %
3571 22b7f6f8 Thomas Thrainer
                                 (self.op.instance_name, self.op.iallocator,
3572 22b7f6f8 Thomas Thrainer
                                  ial.info), errors.ECODE_NORES)
3573 22b7f6f8 Thomas Thrainer
3574 5eacbcae Thomas Thrainer
    jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
3575 22b7f6f8 Thomas Thrainer
3576 22b7f6f8 Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for changing group of"
3577 22b7f6f8 Thomas Thrainer
                 " instance '%s'", len(jobs), self.op.instance_name)
3578 22b7f6f8 Thomas Thrainer
3579 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs)