Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance.py @ 74676af4

History | View | Annotate | Download (142.9 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 74676af4 Dimitris Aragiorgis
def _DeviceHotplugable(dev):
80 74676af4 Dimitris Aragiorgis
81 74676af4 Dimitris Aragiorgis
  return dev.uuid is not None
82 74676af4 Dimitris Aragiorgis
83 74676af4 Dimitris Aragiorgis
84 22b7f6f8 Thomas Thrainer
def _CheckHostnameSane(lu, name):
85 22b7f6f8 Thomas Thrainer
  """Ensures that a given hostname resolves to a 'sane' name.
86 22b7f6f8 Thomas Thrainer

87 22b7f6f8 Thomas Thrainer
  The given name is required to be a prefix of the resolved hostname,
88 22b7f6f8 Thomas Thrainer
  to prevent accidental mismatches.
89 22b7f6f8 Thomas Thrainer

90 22b7f6f8 Thomas Thrainer
  @param lu: the logical unit on behalf of which we're checking
91 22b7f6f8 Thomas Thrainer
  @param name: the name we should resolve and check
92 22b7f6f8 Thomas Thrainer
  @return: the resolved hostname object
93 22b7f6f8 Thomas Thrainer

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

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

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

125 22b7f6f8 Thomas Thrainer
  @returns: A filled L{iallocator.IAReqInstanceAlloc}
126 22b7f6f8 Thomas Thrainer

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

145 22b7f6f8 Thomas Thrainer
  @param op: The instance opcode
146 22b7f6f8 Thomas Thrainer
  @param cluster: The cluster config object
147 22b7f6f8 Thomas Thrainer

148 22b7f6f8 Thomas Thrainer
  @return: The fully filled beparams
149 22b7f6f8 Thomas Thrainer

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

163 22b7f6f8 Thomas Thrainer
  @param op: The instance opcode
164 22b7f6f8 Thomas Thrainer
  @param cluster: Cluster configuration object
165 22b7f6f8 Thomas Thrainer
  @param default_ip: The default ip to assign
166 22b7f6f8 Thomas Thrainer
  @param cfg: An instance of the configuration object
167 22b7f6f8 Thomas Thrainer
  @param ec_id: Execution context ID
168 22b7f6f8 Thomas Thrainer

169 22b7f6f8 Thomas Thrainer
  @returns: The build up nics
170 22b7f6f8 Thomas Thrainer

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

264 22b7f6f8 Thomas Thrainer
  @type ip: string
265 22b7f6f8 Thomas Thrainer
  @param ip: IP address
266 1c3231aa Thomas Thrainer
  @type node_uuid: string
267 1c3231aa Thomas Thrainer
  @param node_uuid: node UUID
268 22b7f6f8 Thomas Thrainer

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

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

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

309 22b7f6f8 Thomas Thrainer
  @type os_obj: L{objects.OS}
310 22b7f6f8 Thomas Thrainer
  @param os_obj: OS object to check
311 22b7f6f8 Thomas Thrainer
  @type name: string
312 22b7f6f8 Thomas Thrainer
  @param name: OS name passed by the user, to check for validity
313 22b7f6f8 Thomas Thrainer

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

333 22b7f6f8 Thomas Thrainer
  """
334 22b7f6f8 Thomas Thrainer
  HPATH = "instance-add"
335 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
336 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
337 22b7f6f8 Thomas Thrainer
338 dab6ea3d Helga Velroyen
  def _CheckDiskTemplateValid(self):
339 dab6ea3d Helga Velroyen
    """Checks validity of disk template.
340 22b7f6f8 Thomas Thrainer

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

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

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

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

555 22b7f6f8 Thomas Thrainer
    Figure out the right locks for instance creation.
556 22b7f6f8 Thomas Thrainer

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

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

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

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

700 22b7f6f8 Thomas Thrainer
    """
701 1c3231aa Thomas Thrainer
    nl = [self.cfg.GetMasterNode(), self.op.pnode_uuid] + self.secondaries
702 22b7f6f8 Thomas Thrainer
    return nl, nl
703 22b7f6f8 Thomas Thrainer
704 22b7f6f8 Thomas Thrainer
  def _ReadExportInfo(self):
705 22b7f6f8 Thomas Thrainer
    """Reads the export information from disk.
706 22b7f6f8 Thomas Thrainer

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

710 22b7f6f8 Thomas Thrainer
    @return: the export information
711 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

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

1468 22b7f6f8 Thomas Thrainer
  """
1469 22b7f6f8 Thomas Thrainer
  HPATH = "instance-rename"
1470 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1471 22b7f6f8 Thomas Thrainer
1472 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1473 22b7f6f8 Thomas Thrainer
    """Check arguments.
1474 22b7f6f8 Thomas Thrainer

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

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

1486 22b7f6f8 Thomas Thrainer
    """
1487 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1488 22b7f6f8 Thomas Thrainer
    env["INSTANCE_NEW_NAME"] = self.op.new_name
1489 22b7f6f8 Thomas Thrainer
    return env
1490 22b7f6f8 Thomas Thrainer
1491 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1492 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1493 22b7f6f8 Thomas Thrainer

1494 22b7f6f8 Thomas Thrainer
    """
1495 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
1496 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1497 22b7f6f8 Thomas Thrainer
1498 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1499 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1500 22b7f6f8 Thomas Thrainer

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

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

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

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

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

1625 22b7f6f8 Thomas Thrainer
    """
1626 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1627 22b7f6f8 Thomas Thrainer
    env["SHUTDOWN_TIMEOUT"] = self.op.shutdown_timeout
1628 22b7f6f8 Thomas Thrainer
    return env
1629 22b7f6f8 Thomas Thrainer
1630 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1631 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1632 22b7f6f8 Thomas Thrainer

1633 22b7f6f8 Thomas Thrainer
    """
1634 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()]
1635 22b7f6f8 Thomas Thrainer
    nl_post = list(self.instance.all_nodes) + nl
1636 22b7f6f8 Thomas Thrainer
    return (nl, nl_post)
1637 22b7f6f8 Thomas Thrainer
1638 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1639 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1640 22b7f6f8 Thomas Thrainer

1641 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1642 22b7f6f8 Thomas Thrainer

1643 22b7f6f8 Thomas Thrainer
    """
1644 da4a52a3 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
1645 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1646 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1647 22b7f6f8 Thomas Thrainer
1648 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1649 22b7f6f8 Thomas Thrainer
    """Remove the instance.
1650 22b7f6f8 Thomas Thrainer

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

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

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

1705 22b7f6f8 Thomas Thrainer
    """
1706 22b7f6f8 Thomas Thrainer
    env = {
1707 22b7f6f8 Thomas Thrainer
      "TARGET_NODE": self.op.target_node,
1708 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
1709 22b7f6f8 Thomas Thrainer
      }
1710 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
1711 22b7f6f8 Thomas Thrainer
    return env
1712 22b7f6f8 Thomas Thrainer
1713 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1714 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1715 22b7f6f8 Thomas Thrainer

1716 22b7f6f8 Thomas Thrainer
    """
1717 22b7f6f8 Thomas Thrainer
    nl = [
1718 22b7f6f8 Thomas Thrainer
      self.cfg.GetMasterNode(),
1719 22b7f6f8 Thomas Thrainer
      self.instance.primary_node,
1720 1c3231aa Thomas Thrainer
      self.op.target_node_uuid,
1721 22b7f6f8 Thomas Thrainer
      ]
1722 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1723 22b7f6f8 Thomas Thrainer
1724 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1725 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1726 22b7f6f8 Thomas Thrainer

1727 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1728 22b7f6f8 Thomas Thrainer

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

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

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

1879 22b7f6f8 Thomas Thrainer
  """
1880 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1881 22b7f6f8 Thomas Thrainer
1882 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1883 22b7f6f8 Thomas Thrainer
    """Check arguments.
1884 22b7f6f8 Thomas Thrainer

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

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

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

1997 22b7f6f8 Thomas Thrainer
    """
1998 22b7f6f8 Thomas Thrainer
    (allocatable, failed) = self.ia_result
1999 22b7f6f8 Thomas Thrainer
    return {
2000 22b7f6f8 Thomas Thrainer
      opcodes.OpInstanceMultiAlloc.ALLOCATABLE_KEY:
2001 22b7f6f8 Thomas Thrainer
        map(compat.fst, allocatable),
2002 22b7f6f8 Thomas Thrainer
      opcodes.OpInstanceMultiAlloc.FAILED_KEY: failed,
2003 22b7f6f8 Thomas Thrainer
      }
2004 22b7f6f8 Thomas Thrainer
2005 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2006 22b7f6f8 Thomas Thrainer
    """Executes the opcode.
2007 22b7f6f8 Thomas Thrainer

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

2034 22b7f6f8 Thomas Thrainer
  Used by L{LUInstanceSetParams}.
2035 22b7f6f8 Thomas Thrainer

2036 22b7f6f8 Thomas Thrainer
  """
2037 22b7f6f8 Thomas Thrainer
  def __init__(self):
2038 22b7f6f8 Thomas Thrainer
    self.params = None
2039 22b7f6f8 Thomas Thrainer
    self.filled = None
2040 22b7f6f8 Thomas Thrainer
2041 22b7f6f8 Thomas Thrainer
2042 5eacbcae Thomas Thrainer
def _PrepareContainerMods(mods, private_fn):
2043 22b7f6f8 Thomas Thrainer
  """Prepares a list of container modifications by adding a private data field.
2044 22b7f6f8 Thomas Thrainer

2045 22b7f6f8 Thomas Thrainer
  @type mods: list of tuples; (operation, index, parameters)
2046 22b7f6f8 Thomas Thrainer
  @param mods: List of modifications
2047 22b7f6f8 Thomas Thrainer
  @type private_fn: callable or None
2048 22b7f6f8 Thomas Thrainer
  @param private_fn: Callable for constructing a private data field for a
2049 22b7f6f8 Thomas Thrainer
    modification
2050 22b7f6f8 Thomas Thrainer
  @rtype: list
2051 22b7f6f8 Thomas Thrainer

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

2064 22b7f6f8 Thomas Thrainer
  This function checks if all given nodes have the needed number of
2065 22b7f6f8 Thomas Thrainer
  physical CPUs. In case any node has less CPUs or we cannot get the
2066 22b7f6f8 Thomas Thrainer
  information from the node, this function raises an OpPrereqError
2067 22b7f6f8 Thomas Thrainer
  exception.
2068 22b7f6f8 Thomas Thrainer

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

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

2103 22b7f6f8 Thomas Thrainer
  @type identifier: string
2104 22b7f6f8 Thomas Thrainer
  @param identifier: Item index or name or UUID
2105 22b7f6f8 Thomas Thrainer
  @type kind: string
2106 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2107 22b7f6f8 Thomas Thrainer
  @type container: list
2108 22b7f6f8 Thomas Thrainer
  @param container: Container to get the item from
2109 22b7f6f8 Thomas Thrainer

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

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

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

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

2228 22b7f6f8 Thomas Thrainer
  """
2229 22b7f6f8 Thomas Thrainer
  for (idx, disk) in enumerate(disks):
2230 22b7f6f8 Thomas Thrainer
    disk.iv_name = "disk/%s" % (base_index + idx, )
2231 22b7f6f8 Thomas Thrainer
2232 22b7f6f8 Thomas Thrainer
2233 22b7f6f8 Thomas Thrainer
class LUInstanceSetParams(LogicalUnit):
2234 22b7f6f8 Thomas Thrainer
  """Modifies an instances's parameters.
2235 22b7f6f8 Thomas Thrainer

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

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

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

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

2452 22b7f6f8 Thomas Thrainer
    This runs on the master, primary and secondaries.
2453 22b7f6f8 Thomas Thrainer

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

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

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

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

2774 1bb99a33 Bernardo Dal Seno
    This only checks the instance list against the existing names.
2775 1bb99a33 Bernardo Dal Seno

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

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

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

3223 22b7f6f8 Thomas Thrainer
    """
3224 22b7f6f8 Thomas Thrainer
    # add a new disk
3225 d0d7d7cf Thomas Thrainer
    if self.instance.disk_template in constants.DTS_FILEBASED:
3226 d0d7d7cf Thomas Thrainer
      (file_driver, file_path) = self.instance.disks[0].logical_id
3227 22b7f6f8 Thomas Thrainer
      file_path = os.path.dirname(file_path)
3228 22b7f6f8 Thomas Thrainer
    else:
3229 22b7f6f8 Thomas Thrainer
      file_driver = file_path = None
3230 22b7f6f8 Thomas Thrainer
3231 22b7f6f8 Thomas Thrainer
    disk = \
3232 d0d7d7cf Thomas Thrainer
      GenerateDiskTemplate(self, self.instance.disk_template,
3233 da4a52a3 Thomas Thrainer
                           self.instance.uuid, self.instance.primary_node,
3234 d0d7d7cf Thomas Thrainer
                           self.instance.secondary_nodes, [params], file_path,
3235 d0d7d7cf Thomas Thrainer
                           file_driver, idx, self.Log, self.diskparams)[0]
3236 22b7f6f8 Thomas Thrainer
3237 d0d7d7cf Thomas Thrainer
    new_disks = CreateDisks(self, self.instance, disks=[disk])
3238 22b7f6f8 Thomas Thrainer
3239 22b7f6f8 Thomas Thrainer
    if self.cluster.prealloc_wipe_disks:
3240 22b7f6f8 Thomas Thrainer
      # Wipe new disk
3241 d0d7d7cf Thomas Thrainer
      WipeOrCleanupDisks(self, self.instance,
3242 a365b47f Bernardo Dal Seno
                         disks=[(idx, disk, 0)],
3243 a365b47f Bernardo Dal Seno
                         cleanup=new_disks)
3244 22b7f6f8 Thomas Thrainer
3245 74676af4 Dimitris Aragiorgis
    if self.op.hotplug:
3246 74676af4 Dimitris Aragiorgis
      _, device_info = AssembleInstanceDisks(self, self.instance,
3247 74676af4 Dimitris Aragiorgis
                                             [disk], check=False)
3248 74676af4 Dimitris Aragiorgis
      _, _, dev_path = device_info[0]
3249 74676af4 Dimitris Aragiorgis
      self._HotplugDevice(constants.HOTPLUG_ADD, constants.HOTPLUG_DISK,
3250 74676af4 Dimitris Aragiorgis
                          disk, dev_path, idx)
3251 74676af4 Dimitris Aragiorgis
3252 22b7f6f8 Thomas Thrainer
    return (disk, [
3253 22b7f6f8 Thomas Thrainer
      ("disk/%d" % idx, "add:size=%s,mode=%s" % (disk.size, disk.mode)),
3254 22b7f6f8 Thomas Thrainer
      ])
3255 22b7f6f8 Thomas Thrainer
3256 22b7f6f8 Thomas Thrainer
  @staticmethod
3257 22b7f6f8 Thomas Thrainer
  def _ModifyDisk(idx, disk, params, _):
3258 22b7f6f8 Thomas Thrainer
    """Modifies a disk.
3259 22b7f6f8 Thomas Thrainer

3260 22b7f6f8 Thomas Thrainer
    """
3261 22b7f6f8 Thomas Thrainer
    changes = []
3262 22b7f6f8 Thomas Thrainer
    mode = params.get(constants.IDISK_MODE, None)
3263 22b7f6f8 Thomas Thrainer
    if mode:
3264 22b7f6f8 Thomas Thrainer
      disk.mode = mode
3265 22b7f6f8 Thomas Thrainer
      changes.append(("disk.mode/%d" % idx, disk.mode))
3266 22b7f6f8 Thomas Thrainer
3267 22b7f6f8 Thomas Thrainer
    name = params.get(constants.IDISK_NAME, None)
3268 22b7f6f8 Thomas Thrainer
    disk.name = name
3269 22b7f6f8 Thomas Thrainer
    changes.append(("disk.name/%d" % idx, disk.name))
3270 22b7f6f8 Thomas Thrainer
3271 22b7f6f8 Thomas Thrainer
    return changes
3272 22b7f6f8 Thomas Thrainer
3273 22b7f6f8 Thomas Thrainer
  def _RemoveDisk(self, idx, root, _):
3274 22b7f6f8 Thomas Thrainer
    """Removes a disk.
3275 22b7f6f8 Thomas Thrainer

3276 22b7f6f8 Thomas Thrainer
    """
3277 74676af4 Dimitris Aragiorgis
    if self.op.hotplug and _DeviceHotplugable(root):
3278 74676af4 Dimitris Aragiorgis
      self._HotplugDevice(constants.HOTPLUG_REMOVE, constants.HOTPLUG_DISK,
3279 74676af4 Dimitris Aragiorgis
                          root, None, idx)
3280 74676af4 Dimitris Aragiorgis
      ShutdownInstanceDisks(self, self.instance, [root])
3281 74676af4 Dimitris Aragiorgis
3282 5eacbcae Thomas Thrainer
    (anno_disk,) = AnnotateDiskParams(self.instance, [root], self.cfg)
3283 1c3231aa Thomas Thrainer
    for node_uuid, disk in anno_disk.ComputeNodeTree(
3284 1c3231aa Thomas Thrainer
                             self.instance.primary_node):
3285 1c3231aa Thomas Thrainer
      self.cfg.SetDiskID(disk, node_uuid)
3286 1c3231aa Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(node_uuid, disk).fail_msg
3287 22b7f6f8 Thomas Thrainer
      if msg:
3288 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove disk/%d on node '%s': %s,"
3289 1c3231aa Thomas Thrainer
                        " continuing anyway", idx,
3290 1c3231aa Thomas Thrainer
                        self.cfg.GetNodeName(node_uuid), msg)
3291 22b7f6f8 Thomas Thrainer
3292 22b7f6f8 Thomas Thrainer
    # if this is a DRBD disk, return its port to the pool
3293 22b7f6f8 Thomas Thrainer
    if root.dev_type in constants.LDS_DRBD:
3294 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(root.logical_id[2])
3295 22b7f6f8 Thomas Thrainer
3296 22b7f6f8 Thomas Thrainer
  def _CreateNewNic(self, idx, params, private):
3297 22b7f6f8 Thomas Thrainer
    """Creates data structure for a new network interface.
3298 22b7f6f8 Thomas Thrainer

3299 22b7f6f8 Thomas Thrainer
    """
3300 22b7f6f8 Thomas Thrainer
    mac = params[constants.INIC_MAC]
3301 22b7f6f8 Thomas Thrainer
    ip = params.get(constants.INIC_IP, None)
3302 22b7f6f8 Thomas Thrainer
    net = params.get(constants.INIC_NETWORK, None)
3303 22b7f6f8 Thomas Thrainer
    name = params.get(constants.INIC_NAME, None)
3304 22b7f6f8 Thomas Thrainer
    net_uuid = self.cfg.LookupNetwork(net)
3305 22b7f6f8 Thomas Thrainer
    #TODO: not private.filled?? can a nic have no nicparams??
3306 22b7f6f8 Thomas Thrainer
    nicparams = private.filled
3307 22b7f6f8 Thomas Thrainer
    nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, name=name,
3308 22b7f6f8 Thomas Thrainer
                       nicparams=nicparams)
3309 22b7f6f8 Thomas Thrainer
    nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
3310 22b7f6f8 Thomas Thrainer
3311 74676af4 Dimitris Aragiorgis
    if self.op.hotplug:
3312 74676af4 Dimitris Aragiorgis
      self._HotplugDevice(constants.HOTPLUG_ADD, constants.HOTPLUG_NIC,
3313 74676af4 Dimitris Aragiorgis
                          nobj, None, idx)
3314 74676af4 Dimitris Aragiorgis
3315 74676af4 Dimitris Aragiorgis
    desc =  [
3316 22b7f6f8 Thomas Thrainer
      ("nic.%d" % idx,
3317 22b7f6f8 Thomas Thrainer
       "add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" %
3318 22b7f6f8 Thomas Thrainer
       (mac, ip, private.filled[constants.NIC_MODE],
3319 74676af4 Dimitris Aragiorgis
       private.filled[constants.NIC_LINK], net)),
3320 74676af4 Dimitris Aragiorgis
      ]
3321 74676af4 Dimitris Aragiorgis
3322 74676af4 Dimitris Aragiorgis
    return (nobj, desc)
3323 22b7f6f8 Thomas Thrainer
3324 22b7f6f8 Thomas Thrainer
  def _ApplyNicMods(self, idx, nic, params, private):
3325 22b7f6f8 Thomas Thrainer
    """Modifies a network interface.
3326 22b7f6f8 Thomas Thrainer

3327 22b7f6f8 Thomas Thrainer
    """
3328 22b7f6f8 Thomas Thrainer
    changes = []
3329 22b7f6f8 Thomas Thrainer
3330 22b7f6f8 Thomas Thrainer
    for key in [constants.INIC_MAC, constants.INIC_IP, constants.INIC_NAME]:
3331 22b7f6f8 Thomas Thrainer
      if key in params:
3332 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), params[key]))
3333 22b7f6f8 Thomas Thrainer
        setattr(nic, key, params[key])
3334 22b7f6f8 Thomas Thrainer
3335 22b7f6f8 Thomas Thrainer
    new_net = params.get(constants.INIC_NETWORK, nic.network)
3336 22b7f6f8 Thomas Thrainer
    new_net_uuid = self.cfg.LookupNetwork(new_net)
3337 22b7f6f8 Thomas Thrainer
    if new_net_uuid != nic.network:
3338 22b7f6f8 Thomas Thrainer
      changes.append(("nic.network/%d" % idx, new_net))
3339 22b7f6f8 Thomas Thrainer
      nic.network = new_net_uuid
3340 22b7f6f8 Thomas Thrainer
3341 22b7f6f8 Thomas Thrainer
    if private.filled:
3342 22b7f6f8 Thomas Thrainer
      nic.nicparams = private.filled
3343 22b7f6f8 Thomas Thrainer
3344 22b7f6f8 Thomas Thrainer
      for (key, val) in nic.nicparams.items():
3345 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), val))
3346 22b7f6f8 Thomas Thrainer
3347 74676af4 Dimitris Aragiorgis
    if self.op.hotplug and _DeviceHotplugable(nic):
3348 74676af4 Dimitris Aragiorgis
      self._HotplugDevice(constants.HOTPLUG_REMOVE, constants.HOTPLUG_NIC,
3349 74676af4 Dimitris Aragiorgis
                          nic, None, idx)
3350 74676af4 Dimitris Aragiorgis
      self._HotplugDevice(constants.HOTPLUG_ADD, constants.HOTPLUG_NIC,
3351 74676af4 Dimitris Aragiorgis
                          nic, None, idx)
3352 74676af4 Dimitris Aragiorgis
3353 22b7f6f8 Thomas Thrainer
    return changes
3354 22b7f6f8 Thomas Thrainer
3355 74676af4 Dimitris Aragiorgis
  def _RemoveNic(self, idx, nic, _):
3356 74676af4 Dimitris Aragiorgis
    if self.op.hotplug and _DeviceHotplugable(nic):
3357 74676af4 Dimitris Aragiorgis
      self._HotplugDevice(constants.HOTPLUG_REMOVE, constants.HOTPLUG_NIC,
3358 74676af4 Dimitris Aragiorgis
                          nic, None, idx)
3359 74676af4 Dimitris Aragiorgis
3360 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3361 22b7f6f8 Thomas Thrainer
    """Modifies an instance.
3362 22b7f6f8 Thomas Thrainer

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

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

3581 22b7f6f8 Thomas Thrainer
    """
3582 22b7f6f8 Thomas Thrainer
    assert self.target_uuids
3583 22b7f6f8 Thomas Thrainer
3584 22b7f6f8 Thomas Thrainer
    env = {
3585 22b7f6f8 Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
3586 22b7f6f8 Thomas Thrainer
      }
3587 22b7f6f8 Thomas Thrainer
3588 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
3589 22b7f6f8 Thomas Thrainer
3590 22b7f6f8 Thomas Thrainer
    return env
3591 22b7f6f8 Thomas Thrainer
3592 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
3593 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
3594 22b7f6f8 Thomas Thrainer

3595 22b7f6f8 Thomas Thrainer
    """
3596 22b7f6f8 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
3597 22b7f6f8 Thomas Thrainer
    return ([mn], [mn])
3598 22b7f6f8 Thomas Thrainer
3599 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3600 22b7f6f8 Thomas Thrainer
    instances = list(self.owned_locks(locking.LEVEL_INSTANCE))
3601 22b7f6f8 Thomas Thrainer
3602 22b7f6f8 Thomas Thrainer
    assert instances == [self.op.instance_name], "Instance not locked"
3603 22b7f6f8 Thomas Thrainer
3604 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=instances,
3605 22b7f6f8 Thomas Thrainer
                                      target_groups=list(self.target_uuids))
3606 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
3607 22b7f6f8 Thomas Thrainer
3608 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
3609 22b7f6f8 Thomas Thrainer
3610 22b7f6f8 Thomas Thrainer
    if not ial.success:
3611 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute solution for changing group of"
3612 22b7f6f8 Thomas Thrainer
                                 " instance '%s' using iallocator '%s': %s" %
3613 22b7f6f8 Thomas Thrainer
                                 (self.op.instance_name, self.op.iallocator,
3614 22b7f6f8 Thomas Thrainer
                                  ial.info), errors.ECODE_NORES)
3615 22b7f6f8 Thomas Thrainer
3616 5eacbcae Thomas Thrainer
    jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
3617 22b7f6f8 Thomas Thrainer
3618 22b7f6f8 Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for changing group of"
3619 22b7f6f8 Thomas Thrainer
                 " instance '%s'", len(jobs), self.op.instance_name)
3620 22b7f6f8 Thomas Thrainer
3621 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs)