Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance.py @ bd338fab

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

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

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

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

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

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

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

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

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

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

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

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

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

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

245 22b7f6f8 Thomas Thrainer
  @type ip: string
246 22b7f6f8 Thomas Thrainer
  @param ip: IP address
247 22b7f6f8 Thomas Thrainer
  @type node: string
248 22b7f6f8 Thomas Thrainer
  @param node: node name
249 22b7f6f8 Thomas Thrainer

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

266 22b7f6f8 Thomas Thrainer
  @type ipolicy: dict
267 22b7f6f8 Thomas Thrainer
  @param ipolicy: The ipolicy to verify against
268 22b7f6f8 Thomas Thrainer
  @param instance_spec: dict
269 22b7f6f8 Thomas Thrainer
  @param instance_spec: The instance spec to verify
270 22b7f6f8 Thomas Thrainer
  @type disk_template: string
271 22b7f6f8 Thomas Thrainer
  @param disk_template: the disk template of the instance
272 22b7f6f8 Thomas Thrainer
  @param _compute_fn: The function to verify ipolicy (unittest only)
273 5eacbcae Thomas Thrainer
  @see: L{ComputeIPolicySpecViolation}
274 22b7f6f8 Thomas Thrainer

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

290 22b7f6f8 Thomas Thrainer
  @type os_obj: L{objects.OS}
291 22b7f6f8 Thomas Thrainer
  @param os_obj: OS object to check
292 22b7f6f8 Thomas Thrainer
  @type name: string
293 22b7f6f8 Thomas Thrainer
  @param name: OS name passed by the user, to check for validity
294 22b7f6f8 Thomas Thrainer

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

314 22b7f6f8 Thomas Thrainer
  """
315 22b7f6f8 Thomas Thrainer
  HPATH = "instance-add"
316 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
317 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
318 22b7f6f8 Thomas Thrainer
319 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
320 22b7f6f8 Thomas Thrainer
    """Check arguments.
321 22b7f6f8 Thomas Thrainer

322 22b7f6f8 Thomas Thrainer
    """
323 22b7f6f8 Thomas Thrainer
    # do not require name_check to ease forward/backward compatibility
324 22b7f6f8 Thomas Thrainer
    # for tools
325 22b7f6f8 Thomas Thrainer
    if self.op.no_install and self.op.start:
326 22b7f6f8 Thomas Thrainer
      self.LogInfo("No-installation mode selected, disabling startup")
327 22b7f6f8 Thomas Thrainer
      self.op.start = False
328 22b7f6f8 Thomas Thrainer
    # validate/normalize the instance name
329 22b7f6f8 Thomas Thrainer
    self.op.instance_name = \
330 22b7f6f8 Thomas Thrainer
      netutils.Hostname.GetNormalizedName(self.op.instance_name)
331 22b7f6f8 Thomas Thrainer
332 22b7f6f8 Thomas Thrainer
    if self.op.ip_check and not self.op.name_check:
333 22b7f6f8 Thomas Thrainer
      # TODO: make the ip check more flexible and not depend on the name check
334 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot do IP address check without a name"
335 22b7f6f8 Thomas Thrainer
                                 " check", errors.ECODE_INVAL)
336 22b7f6f8 Thomas Thrainer
337 22b7f6f8 Thomas Thrainer
    # check nics' parameter names
338 22b7f6f8 Thomas Thrainer
    for nic in self.op.nics:
339 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(nic, constants.INIC_PARAMS_TYPES)
340 22b7f6f8 Thomas Thrainer
    # check that NIC's parameters names are unique and valid
341 22b7f6f8 Thomas Thrainer
    utils.ValidateDeviceNames("NIC", self.op.nics)
342 22b7f6f8 Thomas Thrainer
343 22b7f6f8 Thomas Thrainer
    # check that disk's names are unique and valid
344 22b7f6f8 Thomas Thrainer
    utils.ValidateDeviceNames("disk", self.op.disks)
345 22b7f6f8 Thomas Thrainer
346 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
347 22b7f6f8 Thomas Thrainer
    if not self.op.disk_template in cluster.enabled_disk_templates:
348 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Cannot create an instance with disk template"
349 22b7f6f8 Thomas Thrainer
                                 " '%s', because it is not enabled in the"
350 22b7f6f8 Thomas Thrainer
                                 " cluster. Enabled disk templates are: %s." %
351 22b7f6f8 Thomas Thrainer
                                 (self.op.disk_template,
352 22b7f6f8 Thomas Thrainer
                                  ",".join(cluster.enabled_disk_templates)))
353 22b7f6f8 Thomas Thrainer
354 22b7f6f8 Thomas Thrainer
    # check disks. parameter names and consistent adopt/no-adopt strategy
355 22b7f6f8 Thomas Thrainer
    has_adopt = has_no_adopt = False
356 22b7f6f8 Thomas Thrainer
    for disk in self.op.disks:
357 22b7f6f8 Thomas Thrainer
      if self.op.disk_template != constants.DT_EXT:
358 22b7f6f8 Thomas Thrainer
        utils.ForceDictType(disk, constants.IDISK_PARAMS_TYPES)
359 22b7f6f8 Thomas Thrainer
      if constants.IDISK_ADOPT in disk:
360 22b7f6f8 Thomas Thrainer
        has_adopt = True
361 22b7f6f8 Thomas Thrainer
      else:
362 22b7f6f8 Thomas Thrainer
        has_no_adopt = True
363 22b7f6f8 Thomas Thrainer
    if has_adopt and has_no_adopt:
364 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Either all disks are adopted or none is",
365 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
366 22b7f6f8 Thomas Thrainer
    if has_adopt:
367 22b7f6f8 Thomas Thrainer
      if self.op.disk_template not in constants.DTS_MAY_ADOPT:
368 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk adoption is not supported for the"
369 22b7f6f8 Thomas Thrainer
                                   " '%s' disk template" %
370 22b7f6f8 Thomas Thrainer
                                   self.op.disk_template,
371 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
372 22b7f6f8 Thomas Thrainer
      if self.op.iallocator is not None:
373 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk adoption not allowed with an"
374 22b7f6f8 Thomas Thrainer
                                   " iallocator script", errors.ECODE_INVAL)
375 22b7f6f8 Thomas Thrainer
      if self.op.mode == constants.INSTANCE_IMPORT:
376 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk adoption not allowed for"
377 22b7f6f8 Thomas Thrainer
                                   " instance import", errors.ECODE_INVAL)
378 22b7f6f8 Thomas Thrainer
    else:
379 22b7f6f8 Thomas Thrainer
      if self.op.disk_template in constants.DTS_MUST_ADOPT:
380 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk template %s requires disk adoption,"
381 22b7f6f8 Thomas Thrainer
                                   " but no 'adopt' parameter given" %
382 22b7f6f8 Thomas Thrainer
                                   self.op.disk_template,
383 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
384 22b7f6f8 Thomas Thrainer
385 22b7f6f8 Thomas Thrainer
    self.adopt_disks = has_adopt
386 22b7f6f8 Thomas Thrainer
387 22b7f6f8 Thomas Thrainer
    # instance name verification
388 22b7f6f8 Thomas Thrainer
    if self.op.name_check:
389 22b7f6f8 Thomas Thrainer
      self.hostname1 = _CheckHostnameSane(self, self.op.instance_name)
390 22b7f6f8 Thomas Thrainer
      self.op.instance_name = self.hostname1.name
391 22b7f6f8 Thomas Thrainer
      # used in CheckPrereq for ip ping check
392 22b7f6f8 Thomas Thrainer
      self.check_ip = self.hostname1.ip
393 22b7f6f8 Thomas Thrainer
    else:
394 22b7f6f8 Thomas Thrainer
      self.check_ip = None
395 22b7f6f8 Thomas Thrainer
396 22b7f6f8 Thomas Thrainer
    # file storage checks
397 22b7f6f8 Thomas Thrainer
    if (self.op.file_driver and
398 22b7f6f8 Thomas Thrainer
        not self.op.file_driver in constants.FILE_DRIVER):
399 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Invalid file driver name '%s'" %
400 22b7f6f8 Thomas Thrainer
                                 self.op.file_driver, errors.ECODE_INVAL)
401 22b7f6f8 Thomas Thrainer
402 ff34fb97 Michele Tartara
    # set default file_driver if unset and required
403 ff34fb97 Michele Tartara
    if (not self.op.file_driver and
404 ff34fb97 Michele Tartara
        self.op.disk_template in [constants.DT_FILE,
405 ff34fb97 Michele Tartara
                                  constants.DT_SHARED_FILE]):
406 77b0d264 Michele Tartara
      self.op.file_driver = constants.FD_DEFAULT
407 ff34fb97 Michele Tartara
408 22b7f6f8 Thomas Thrainer
    if self.op.disk_template == constants.DT_FILE:
409 22b7f6f8 Thomas Thrainer
      opcodes.RequireFileStorage()
410 22b7f6f8 Thomas Thrainer
    elif self.op.disk_template == constants.DT_SHARED_FILE:
411 22b7f6f8 Thomas Thrainer
      opcodes.RequireSharedFileStorage()
412 22b7f6f8 Thomas Thrainer
413 22b7f6f8 Thomas Thrainer
    ### Node/iallocator related checks
414 5eacbcae Thomas Thrainer
    CheckIAllocatorOrNode(self, "iallocator", "pnode")
415 22b7f6f8 Thomas Thrainer
416 22b7f6f8 Thomas Thrainer
    if self.op.pnode is not None:
417 22b7f6f8 Thomas Thrainer
      if self.op.disk_template in constants.DTS_INT_MIRROR:
418 22b7f6f8 Thomas Thrainer
        if self.op.snode is None:
419 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("The networked disk templates need"
420 22b7f6f8 Thomas Thrainer
                                     " a mirror node", errors.ECODE_INVAL)
421 22b7f6f8 Thomas Thrainer
      elif self.op.snode:
422 22b7f6f8 Thomas Thrainer
        self.LogWarning("Secondary node will be ignored on non-mirrored disk"
423 22b7f6f8 Thomas Thrainer
                        " template")
424 22b7f6f8 Thomas Thrainer
        self.op.snode = None
425 22b7f6f8 Thomas Thrainer
426 22b7f6f8 Thomas Thrainer
    _CheckOpportunisticLocking(self.op)
427 22b7f6f8 Thomas Thrainer
428 5eacbcae Thomas Thrainer
    self._cds = GetClusterDomainSecret()
429 22b7f6f8 Thomas Thrainer
430 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
431 22b7f6f8 Thomas Thrainer
      # On import force_variant must be True, because if we forced it at
432 22b7f6f8 Thomas Thrainer
      # initial install, our only chance when importing it back is that it
433 22b7f6f8 Thomas Thrainer
      # works again!
434 22b7f6f8 Thomas Thrainer
      self.op.force_variant = True
435 22b7f6f8 Thomas Thrainer
436 22b7f6f8 Thomas Thrainer
      if self.op.no_install:
437 22b7f6f8 Thomas Thrainer
        self.LogInfo("No-installation mode has no effect during import")
438 22b7f6f8 Thomas Thrainer
439 22b7f6f8 Thomas Thrainer
    elif self.op.mode == constants.INSTANCE_CREATE:
440 22b7f6f8 Thomas Thrainer
      if self.op.os_type is None:
441 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No guest OS specified",
442 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
443 22b7f6f8 Thomas Thrainer
      if self.op.os_type in self.cfg.GetClusterInfo().blacklisted_os:
444 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Guest OS '%s' is not allowed for"
445 22b7f6f8 Thomas Thrainer
                                   " installation" % self.op.os_type,
446 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
447 22b7f6f8 Thomas Thrainer
      if self.op.disk_template is None:
448 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No disk template specified",
449 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
450 22b7f6f8 Thomas Thrainer
451 22b7f6f8 Thomas Thrainer
    elif self.op.mode == constants.INSTANCE_REMOTE_IMPORT:
452 22b7f6f8 Thomas Thrainer
      # Check handshake to ensure both clusters have the same domain secret
453 22b7f6f8 Thomas Thrainer
      src_handshake = self.op.source_handshake
454 22b7f6f8 Thomas Thrainer
      if not src_handshake:
455 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing source handshake",
456 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
457 22b7f6f8 Thomas Thrainer
458 22b7f6f8 Thomas Thrainer
      errmsg = masterd.instance.CheckRemoteExportHandshake(self._cds,
459 22b7f6f8 Thomas Thrainer
                                                           src_handshake)
460 22b7f6f8 Thomas Thrainer
      if errmsg:
461 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid handshake: %s" % errmsg,
462 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
463 22b7f6f8 Thomas Thrainer
464 22b7f6f8 Thomas Thrainer
      # Load and check source CA
465 22b7f6f8 Thomas Thrainer
      self.source_x509_ca_pem = self.op.source_x509_ca
466 22b7f6f8 Thomas Thrainer
      if not self.source_x509_ca_pem:
467 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing source X509 CA",
468 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
469 22b7f6f8 Thomas Thrainer
470 22b7f6f8 Thomas Thrainer
      try:
471 22b7f6f8 Thomas Thrainer
        (cert, _) = utils.LoadSignedX509Certificate(self.source_x509_ca_pem,
472 22b7f6f8 Thomas Thrainer
                                                    self._cds)
473 22b7f6f8 Thomas Thrainer
      except OpenSSL.crypto.Error, err:
474 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Unable to load source X509 CA (%s)" %
475 22b7f6f8 Thomas Thrainer
                                   (err, ), errors.ECODE_INVAL)
476 22b7f6f8 Thomas Thrainer
477 22b7f6f8 Thomas Thrainer
      (errcode, msg) = utils.VerifyX509Certificate(cert, None, None)
478 22b7f6f8 Thomas Thrainer
      if errcode is not None:
479 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid source X509 CA (%s)" % (msg, ),
480 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
481 22b7f6f8 Thomas Thrainer
482 22b7f6f8 Thomas Thrainer
      self.source_x509_ca = cert
483 22b7f6f8 Thomas Thrainer
484 22b7f6f8 Thomas Thrainer
      src_instance_name = self.op.source_instance_name
485 22b7f6f8 Thomas Thrainer
      if not src_instance_name:
486 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Missing source instance name",
487 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
488 22b7f6f8 Thomas Thrainer
489 22b7f6f8 Thomas Thrainer
      self.source_instance_name = \
490 22b7f6f8 Thomas Thrainer
        netutils.GetHostname(name=src_instance_name).name
491 22b7f6f8 Thomas Thrainer
492 22b7f6f8 Thomas Thrainer
    else:
493 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Invalid instance creation mode %r" %
494 22b7f6f8 Thomas Thrainer
                                 self.op.mode, errors.ECODE_INVAL)
495 22b7f6f8 Thomas Thrainer
496 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
497 22b7f6f8 Thomas Thrainer
    """ExpandNames for CreateInstance.
498 22b7f6f8 Thomas Thrainer

499 22b7f6f8 Thomas Thrainer
    Figure out the right locks for instance creation.
500 22b7f6f8 Thomas Thrainer

501 22b7f6f8 Thomas Thrainer
    """
502 22b7f6f8 Thomas Thrainer
    self.needed_locks = {}
503 22b7f6f8 Thomas Thrainer
504 22b7f6f8 Thomas Thrainer
    instance_name = self.op.instance_name
505 22b7f6f8 Thomas Thrainer
    # this is just a preventive check, but someone might still add this
506 22b7f6f8 Thomas Thrainer
    # instance in the meantime, and creation will fail at lock-add time
507 22b7f6f8 Thomas Thrainer
    if instance_name in self.cfg.GetInstanceList():
508 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
509 22b7f6f8 Thomas Thrainer
                                 instance_name, errors.ECODE_EXISTS)
510 22b7f6f8 Thomas Thrainer
511 22b7f6f8 Thomas Thrainer
    self.add_locks[locking.LEVEL_INSTANCE] = instance_name
512 22b7f6f8 Thomas Thrainer
513 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
514 22b7f6f8 Thomas Thrainer
      # TODO: Find a solution to not lock all nodes in the cluster, e.g. by
515 22b7f6f8 Thomas Thrainer
      # specifying a group on instance creation and then selecting nodes from
516 22b7f6f8 Thomas Thrainer
      # that group
517 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
518 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
519 22b7f6f8 Thomas Thrainer
520 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
521 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
522 22b7f6f8 Thomas Thrainer
    else:
523 5eacbcae Thomas Thrainer
      self.op.pnode = ExpandNodeName(self.cfg, self.op.pnode)
524 22b7f6f8 Thomas Thrainer
      nodelist = [self.op.pnode]
525 22b7f6f8 Thomas Thrainer
      if self.op.snode is not None:
526 5eacbcae Thomas Thrainer
        self.op.snode = ExpandNodeName(self.cfg, self.op.snode)
527 22b7f6f8 Thomas Thrainer
        nodelist.append(self.op.snode)
528 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodelist
529 22b7f6f8 Thomas Thrainer
530 22b7f6f8 Thomas Thrainer
    # in case of import lock the source node too
531 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
532 22b7f6f8 Thomas Thrainer
      src_node = self.op.src_node
533 22b7f6f8 Thomas Thrainer
      src_path = self.op.src_path
534 22b7f6f8 Thomas Thrainer
535 22b7f6f8 Thomas Thrainer
      if src_path is None:
536 22b7f6f8 Thomas Thrainer
        self.op.src_path = src_path = self.op.instance_name
537 22b7f6f8 Thomas Thrainer
538 22b7f6f8 Thomas Thrainer
      if src_node is None:
539 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
540 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
541 22b7f6f8 Thomas Thrainer
        self.op.src_node = None
542 22b7f6f8 Thomas Thrainer
        if os.path.isabs(src_path):
543 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("Importing an instance from a path"
544 22b7f6f8 Thomas Thrainer
                                     " requires a source node option",
545 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
546 22b7f6f8 Thomas Thrainer
      else:
547 5eacbcae Thomas Thrainer
        self.op.src_node = src_node = ExpandNodeName(self.cfg, src_node)
548 22b7f6f8 Thomas Thrainer
        if self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET:
549 22b7f6f8 Thomas Thrainer
          self.needed_locks[locking.LEVEL_NODE].append(src_node)
550 22b7f6f8 Thomas Thrainer
        if not os.path.isabs(src_path):
551 22b7f6f8 Thomas Thrainer
          self.op.src_path = src_path = \
552 22b7f6f8 Thomas Thrainer
            utils.PathJoin(pathutils.EXPORT_DIR, src_path)
553 22b7f6f8 Thomas Thrainer
554 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = \
555 5eacbcae Thomas Thrainer
      CopyLockList(self.needed_locks[locking.LEVEL_NODE])
556 22b7f6f8 Thomas Thrainer
557 8b9887c5 Petr Pudlak
    # Optimistically acquire shared group locks (we're reading the
558 8b9887c5 Petr Pudlak
    # configuration).  We can't just call GetInstanceNodeGroups, because the
559 8b9887c5 Petr Pudlak
    # instance doesn't exist yet. Therefore we lock all node groups of all
560 8b9887c5 Petr Pudlak
    # nodes we have.
561 8b9887c5 Petr Pudlak
    if self.needed_locks[locking.LEVEL_NODE] == locking.ALL_SET:
562 8b9887c5 Petr Pudlak
      # In the case we lock all nodes for opportunistic allocation, we have no
563 8b9887c5 Petr Pudlak
      # choice than to lock all groups, because they're allocated before nodes.
564 8b9887c5 Petr Pudlak
      # This is sad, but true. At least we release all those we don't need in
565 8b9887c5 Petr Pudlak
      # CheckPrereq later.
566 8b9887c5 Petr Pudlak
      self.needed_locks[locking.LEVEL_NODEGROUP] = locking.ALL_SET
567 8b9887c5 Petr Pudlak
    else:
568 8b9887c5 Petr Pudlak
      self.needed_locks[locking.LEVEL_NODEGROUP] = \
569 8b9887c5 Petr Pudlak
        list(self.cfg.GetNodeGroupsFromNodes(
570 8b9887c5 Petr Pudlak
          self.needed_locks[locking.LEVEL_NODE]))
571 8b9887c5 Petr Pudlak
    self.share_locks[locking.LEVEL_NODEGROUP] = 1
572 8b9887c5 Petr Pudlak
573 4289f617 Thomas Thrainer
  def DeclareLocks(self, level):
574 4289f617 Thomas Thrainer
    if level == locking.LEVEL_NODE_RES and \
575 4289f617 Thomas Thrainer
      self.opportunistic_locks[locking.LEVEL_NODE]:
576 4289f617 Thomas Thrainer
      # Even when using opportunistic locking, we require the same set of
577 4289f617 Thomas Thrainer
      # NODE_RES locks as we got NODE locks
578 4289f617 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
579 4289f617 Thomas Thrainer
        self.owned_locks(locking.LEVEL_NODE)
580 4289f617 Thomas Thrainer
581 22b7f6f8 Thomas Thrainer
  def _RunAllocator(self):
582 22b7f6f8 Thomas Thrainer
    """Run the allocator based on input opcode.
583 22b7f6f8 Thomas Thrainer

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

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

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

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

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

671 22b7f6f8 Thomas Thrainer
    @return: the export information
672 22b7f6f8 Thomas Thrainer

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

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

719 22b7f6f8 Thomas Thrainer
    """
720 22b7f6f8 Thomas Thrainer
    self.op.os_type = einfo.get(constants.INISECT_EXP, "os")
721 22b7f6f8 Thomas Thrainer
722 22b7f6f8 Thomas Thrainer
    if self.op.disk_template is None:
723 22b7f6f8 Thomas Thrainer
      if einfo.has_option(constants.INISECT_INS, "disk_template"):
724 22b7f6f8 Thomas Thrainer
        self.op.disk_template = einfo.get(constants.INISECT_INS,
725 22b7f6f8 Thomas Thrainer
                                          "disk_template")
726 22b7f6f8 Thomas Thrainer
        if self.op.disk_template not in constants.DISK_TEMPLATES:
727 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("Disk template specified in configuration"
728 22b7f6f8 Thomas Thrainer
                                     " file is not one of the allowed values:"
729 22b7f6f8 Thomas Thrainer
                                     " %s" %
730 22b7f6f8 Thomas Thrainer
                                     " ".join(constants.DISK_TEMPLATES),
731 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
732 22b7f6f8 Thomas Thrainer
      else:
733 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No disk template specified and the export"
734 22b7f6f8 Thomas Thrainer
                                   " is missing the disk_template information",
735 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
736 22b7f6f8 Thomas Thrainer
737 22b7f6f8 Thomas Thrainer
    if not self.op.disks:
738 22b7f6f8 Thomas Thrainer
      disks = []
739 22b7f6f8 Thomas Thrainer
      # TODO: import the disk iv_name too
740 22b7f6f8 Thomas Thrainer
      for idx in range(constants.MAX_DISKS):
741 22b7f6f8 Thomas Thrainer
        if einfo.has_option(constants.INISECT_INS, "disk%d_size" % idx):
742 22b7f6f8 Thomas Thrainer
          disk_sz = einfo.getint(constants.INISECT_INS, "disk%d_size" % idx)
743 0f68f7fa Dimitris Aragiorgis
          disk_name = einfo.get(constants.INISECT_INS, "disk%d_name" % idx)
744 0f68f7fa Dimitris Aragiorgis
          disk = {
745 0f68f7fa Dimitris Aragiorgis
            constants.IDISK_SIZE: disk_sz,
746 0f68f7fa Dimitris Aragiorgis
            constants.IDISK_NAME: disk_name
747 0f68f7fa Dimitris Aragiorgis
            }
748 0f68f7fa Dimitris Aragiorgis
          disks.append(disk)
749 22b7f6f8 Thomas Thrainer
      self.op.disks = disks
750 22b7f6f8 Thomas Thrainer
      if not disks and self.op.disk_template != constants.DT_DISKLESS:
751 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No disk info specified and the export"
752 22b7f6f8 Thomas Thrainer
                                   " is missing the disk information",
753 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
754 22b7f6f8 Thomas Thrainer
755 22b7f6f8 Thomas Thrainer
    if not self.op.nics:
756 22b7f6f8 Thomas Thrainer
      nics = []
757 22b7f6f8 Thomas Thrainer
      for idx in range(constants.MAX_NICS):
758 22b7f6f8 Thomas Thrainer
        if einfo.has_option(constants.INISECT_INS, "nic%d_mac" % idx):
759 22b7f6f8 Thomas Thrainer
          ndict = {}
760 0f68f7fa Dimitris Aragiorgis
          for name in [constants.INIC_IP,
761 0f68f7fa Dimitris Aragiorgis
                       constants.INIC_MAC, constants.INIC_NAME]:
762 22b7f6f8 Thomas Thrainer
            v = einfo.get(constants.INISECT_INS, "nic%d_%s" % (idx, name))
763 22b7f6f8 Thomas Thrainer
            ndict[name] = v
764 0973f9ed Dimitris Aragiorgis
          network = einfo.get(constants.INISECT_INS,
765 0973f9ed Dimitris Aragiorgis
                              "nic%d_%s" % (idx, constants.INIC_NETWORK))
766 0973f9ed Dimitris Aragiorgis
          # in case network is given link and mode are inherited
767 0973f9ed Dimitris Aragiorgis
          # from nodegroup's netparams and thus should not be passed here
768 0973f9ed Dimitris Aragiorgis
          if network:
769 0973f9ed Dimitris Aragiorgis
            ndict[constants.INIC_NETWORK] = network
770 0973f9ed Dimitris Aragiorgis
          else:
771 0973f9ed Dimitris Aragiorgis
            for name in list(constants.NICS_PARAMETERS):
772 0973f9ed Dimitris Aragiorgis
              v = einfo.get(constants.INISECT_INS, "nic%d_%s" % (idx, name))
773 0973f9ed Dimitris Aragiorgis
              ndict[name] = v
774 22b7f6f8 Thomas Thrainer
          nics.append(ndict)
775 22b7f6f8 Thomas Thrainer
        else:
776 22b7f6f8 Thomas Thrainer
          break
777 22b7f6f8 Thomas Thrainer
      self.op.nics = nics
778 22b7f6f8 Thomas Thrainer
779 22b7f6f8 Thomas Thrainer
    if not self.op.tags and einfo.has_option(constants.INISECT_INS, "tags"):
780 22b7f6f8 Thomas Thrainer
      self.op.tags = einfo.get(constants.INISECT_INS, "tags").split()
781 22b7f6f8 Thomas Thrainer
782 22b7f6f8 Thomas Thrainer
    if (self.op.hypervisor is None and
783 22b7f6f8 Thomas Thrainer
        einfo.has_option(constants.INISECT_INS, "hypervisor")):
784 22b7f6f8 Thomas Thrainer
      self.op.hypervisor = einfo.get(constants.INISECT_INS, "hypervisor")
785 22b7f6f8 Thomas Thrainer
786 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_HYP):
787 22b7f6f8 Thomas Thrainer
      # use the export parameters but do not override the ones
788 22b7f6f8 Thomas Thrainer
      # specified by the user
789 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_HYP):
790 22b7f6f8 Thomas Thrainer
        if name not in self.op.hvparams:
791 22b7f6f8 Thomas Thrainer
          self.op.hvparams[name] = value
792 22b7f6f8 Thomas Thrainer
793 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_BEP):
794 22b7f6f8 Thomas Thrainer
      # use the parameters, without overriding
795 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_BEP):
796 22b7f6f8 Thomas Thrainer
        if name not in self.op.beparams:
797 22b7f6f8 Thomas Thrainer
          self.op.beparams[name] = value
798 22b7f6f8 Thomas Thrainer
        # Compatibility for the old "memory" be param
799 22b7f6f8 Thomas Thrainer
        if name == constants.BE_MEMORY:
800 22b7f6f8 Thomas Thrainer
          if constants.BE_MAXMEM not in self.op.beparams:
801 22b7f6f8 Thomas Thrainer
            self.op.beparams[constants.BE_MAXMEM] = value
802 22b7f6f8 Thomas Thrainer
          if constants.BE_MINMEM not in self.op.beparams:
803 22b7f6f8 Thomas Thrainer
            self.op.beparams[constants.BE_MINMEM] = value
804 22b7f6f8 Thomas Thrainer
    else:
805 22b7f6f8 Thomas Thrainer
      # try to read the parameters old style, from the main section
806 22b7f6f8 Thomas Thrainer
      for name in constants.BES_PARAMETERS:
807 22b7f6f8 Thomas Thrainer
        if (name not in self.op.beparams and
808 22b7f6f8 Thomas Thrainer
            einfo.has_option(constants.INISECT_INS, name)):
809 22b7f6f8 Thomas Thrainer
          self.op.beparams[name] = einfo.get(constants.INISECT_INS, name)
810 22b7f6f8 Thomas Thrainer
811 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_OSP):
812 22b7f6f8 Thomas Thrainer
      # use the parameters, without overriding
813 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_OSP):
814 22b7f6f8 Thomas Thrainer
        if name not in self.op.osparams:
815 22b7f6f8 Thomas Thrainer
          self.op.osparams[name] = value
816 22b7f6f8 Thomas Thrainer
817 22b7f6f8 Thomas Thrainer
  def _RevertToDefaults(self, cluster):
818 22b7f6f8 Thomas Thrainer
    """Revert the instance parameters to the default values.
819 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

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

1614 22b7f6f8 Thomas Thrainer
    """
1615 5eacbcae Thomas Thrainer
    env = BuildInstanceHookEnvByObject(self, self.instance)
1616 22b7f6f8 Thomas Thrainer
    env["SHUTDOWN_TIMEOUT"] = self.op.shutdown_timeout
1617 22b7f6f8 Thomas Thrainer
    return env
1618 22b7f6f8 Thomas Thrainer
1619 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1620 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1621 22b7f6f8 Thomas Thrainer

1622 22b7f6f8 Thomas Thrainer
    """
1623 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()]
1624 22b7f6f8 Thomas Thrainer
    nl_post = list(self.instance.all_nodes) + nl
1625 22b7f6f8 Thomas Thrainer
    return (nl, nl_post)
1626 22b7f6f8 Thomas Thrainer
1627 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1628 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1629 22b7f6f8 Thomas Thrainer

1630 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1631 22b7f6f8 Thomas Thrainer

1632 22b7f6f8 Thomas Thrainer
    """
1633 22b7f6f8 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
1634 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1635 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1636 22b7f6f8 Thomas Thrainer
1637 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1638 22b7f6f8 Thomas Thrainer
    """Remove the instance.
1639 22b7f6f8 Thomas Thrainer

1640 22b7f6f8 Thomas Thrainer
    """
1641 22b7f6f8 Thomas Thrainer
    instance = self.instance
1642 22b7f6f8 Thomas Thrainer
    logging.info("Shutting down instance %s on node %s",
1643 22b7f6f8 Thomas Thrainer
                 instance.name, instance.primary_node)
1644 22b7f6f8 Thomas Thrainer
1645 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_instance_shutdown(instance.primary_node, instance,
1646 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1647 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1648 22b7f6f8 Thomas Thrainer
    msg = result.fail_msg
1649 22b7f6f8 Thomas Thrainer
    if msg:
1650 22b7f6f8 Thomas Thrainer
      if self.op.ignore_failures:
1651 22b7f6f8 Thomas Thrainer
        feedback_fn("Warning: can't shutdown instance: %s" % msg)
1652 22b7f6f8 Thomas Thrainer
      else:
1653 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not shutdown instance %s on"
1654 22b7f6f8 Thomas Thrainer
                                 " node %s: %s" %
1655 22b7f6f8 Thomas Thrainer
                                 (instance.name, instance.primary_node, msg))
1656 22b7f6f8 Thomas Thrainer
1657 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1658 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1659 22b7f6f8 Thomas Thrainer
    assert not (set(instance.all_nodes) -
1660 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
1661 22b7f6f8 Thomas Thrainer
      "Not owning correct locks"
1662 22b7f6f8 Thomas Thrainer
1663 5eacbcae Thomas Thrainer
    RemoveInstance(self, feedback_fn, instance, self.op.ignore_failures)
1664 22b7f6f8 Thomas Thrainer
1665 22b7f6f8 Thomas Thrainer
1666 22b7f6f8 Thomas Thrainer
class LUInstanceMove(LogicalUnit):
1667 22b7f6f8 Thomas Thrainer
  """Move an instance by data-copying.
1668 22b7f6f8 Thomas Thrainer

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

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

1695 22b7f6f8 Thomas Thrainer
    """
1696 22b7f6f8 Thomas Thrainer
    env = {
1697 22b7f6f8 Thomas Thrainer
      "TARGET_NODE": self.op.target_node,
1698 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
1699 22b7f6f8 Thomas Thrainer
      }
1700 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
1701 22b7f6f8 Thomas Thrainer
    return env
1702 22b7f6f8 Thomas Thrainer
1703 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1704 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1705 22b7f6f8 Thomas Thrainer

1706 22b7f6f8 Thomas Thrainer
    """
1707 22b7f6f8 Thomas Thrainer
    nl = [
1708 22b7f6f8 Thomas Thrainer
      self.cfg.GetMasterNode(),
1709 22b7f6f8 Thomas Thrainer
      self.instance.primary_node,
1710 22b7f6f8 Thomas Thrainer
      self.op.target_node,
1711 22b7f6f8 Thomas Thrainer
      ]
1712 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1713 22b7f6f8 Thomas Thrainer
1714 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1715 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1716 22b7f6f8 Thomas Thrainer

1717 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1718 22b7f6f8 Thomas Thrainer

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

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

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

1871 22b7f6f8 Thomas Thrainer
  """
1872 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1873 22b7f6f8 Thomas Thrainer
1874 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1875 22b7f6f8 Thomas Thrainer
    """Check arguments.
1876 22b7f6f8 Thomas Thrainer

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

1915 22b7f6f8 Thomas Thrainer
    """
1916 5eacbcae Thomas Thrainer
    self.share_locks = ShareAll()
1917 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
1918 22b7f6f8 Thomas Thrainer
      # iallocator will select nodes and even if no iallocator is used,
1919 22b7f6f8 Thomas Thrainer
      # collisions with LUInstanceCreate should be avoided
1920 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
1921 22b7f6f8 Thomas Thrainer
      }
1922 22b7f6f8 Thomas Thrainer
1923 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
1924 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
1925 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = locking.ALL_SET
1926 22b7f6f8 Thomas Thrainer
1927 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
1928 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
1929 22b7f6f8 Thomas Thrainer
    else:
1930 22b7f6f8 Thomas Thrainer
      nodeslist = []
1931 22b7f6f8 Thomas Thrainer
      for inst in self.op.instances:
1932 5eacbcae Thomas Thrainer
        inst.pnode = ExpandNodeName(self.cfg, inst.pnode)
1933 22b7f6f8 Thomas Thrainer
        nodeslist.append(inst.pnode)
1934 22b7f6f8 Thomas Thrainer
        if inst.snode is not None:
1935 5eacbcae Thomas Thrainer
          inst.snode = ExpandNodeName(self.cfg, inst.snode)
1936 22b7f6f8 Thomas Thrainer
          nodeslist.append(inst.snode)
1937 22b7f6f8 Thomas Thrainer
1938 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodeslist
1939 22b7f6f8 Thomas Thrainer
      # Lock resources of instance's primary and secondary nodes (copy to
1940 22b7f6f8 Thomas Thrainer
      # prevent accidential modification)
1941 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = list(nodeslist)
1942 22b7f6f8 Thomas Thrainer
1943 4289f617 Thomas Thrainer
  def DeclareLocks(self, level):
1944 4289f617 Thomas Thrainer
    if level == locking.LEVEL_NODE_RES and \
1945 4289f617 Thomas Thrainer
      self.opportunistic_locks[locking.LEVEL_NODE]:
1946 4289f617 Thomas Thrainer
      # Even when using opportunistic locking, we require the same set of
1947 4289f617 Thomas Thrainer
      # NODE_RES locks as we got NODE locks
1948 4289f617 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1949 4289f617 Thomas Thrainer
        self.owned_locks(locking.LEVEL_NODE)
1950 4289f617 Thomas Thrainer
1951 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1952 22b7f6f8 Thomas Thrainer
    """Check prerequisite.
1953 22b7f6f8 Thomas Thrainer

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

1994 22b7f6f8 Thomas Thrainer
    """
1995 0c072225 Thomas Thrainer
    if self.op.iallocator:
1996 0c072225 Thomas Thrainer
      (allocatable, failed_insts) = self.ia_result
1997 0c072225 Thomas Thrainer
      allocatable_insts = map(compat.fst, allocatable)
1998 0c072225 Thomas Thrainer
    else:
1999 0c072225 Thomas Thrainer
      allocatable_insts = [op.instance_name for op in self.op.instances]
2000 0c072225 Thomas Thrainer
      failed_insts = []
2001 0c072225 Thomas Thrainer
2002 22b7f6f8 Thomas Thrainer
    return {
2003 0c072225 Thomas Thrainer
      opcodes.OpInstanceMultiAlloc.ALLOCATABLE_KEY: allocatable_insts,
2004 0c072225 Thomas Thrainer
      opcodes.OpInstanceMultiAlloc.FAILED_KEY: failed_insts,
2005 22b7f6f8 Thomas Thrainer
      }
2006 22b7f6f8 Thomas Thrainer
2007 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2008 22b7f6f8 Thomas Thrainer
    """Executes the opcode.
2009 22b7f6f8 Thomas Thrainer

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

2039 22b7f6f8 Thomas Thrainer
  Used by L{LUInstanceSetParams}.
2040 22b7f6f8 Thomas Thrainer

2041 22b7f6f8 Thomas Thrainer
  """
2042 22b7f6f8 Thomas Thrainer
  def __init__(self):
2043 22b7f6f8 Thomas Thrainer
    self.params = None
2044 22b7f6f8 Thomas Thrainer
    self.filled = None
2045 22b7f6f8 Thomas Thrainer
2046 22b7f6f8 Thomas Thrainer
2047 5eacbcae Thomas Thrainer
def _PrepareContainerMods(mods, private_fn):
2048 22b7f6f8 Thomas Thrainer
  """Prepares a list of container modifications by adding a private data field.
2049 22b7f6f8 Thomas Thrainer

2050 22b7f6f8 Thomas Thrainer
  @type mods: list of tuples; (operation, index, parameters)
2051 22b7f6f8 Thomas Thrainer
  @param mods: List of modifications
2052 22b7f6f8 Thomas Thrainer
  @type private_fn: callable or None
2053 22b7f6f8 Thomas Thrainer
  @param private_fn: Callable for constructing a private data field for a
2054 22b7f6f8 Thomas Thrainer
    modification
2055 22b7f6f8 Thomas Thrainer
  @rtype: list
2056 22b7f6f8 Thomas Thrainer

2057 22b7f6f8 Thomas Thrainer
  """
2058 22b7f6f8 Thomas Thrainer
  if private_fn is None:
2059 22b7f6f8 Thomas Thrainer
    fn = lambda: None
2060 22b7f6f8 Thomas Thrainer
  else:
2061 22b7f6f8 Thomas Thrainer
    fn = private_fn
2062 22b7f6f8 Thomas Thrainer
2063 22b7f6f8 Thomas Thrainer
  return [(op, idx, params, fn()) for (op, idx, params) in mods]
2064 22b7f6f8 Thomas Thrainer
2065 22b7f6f8 Thomas Thrainer
2066 22b7f6f8 Thomas Thrainer
def _CheckNodesPhysicalCPUs(lu, nodenames, requested, hypervisor_name):
2067 22b7f6f8 Thomas Thrainer
  """Checks if nodes have enough physical CPUs
2068 22b7f6f8 Thomas Thrainer

2069 22b7f6f8 Thomas Thrainer
  This function checks if all given nodes have the needed number of
2070 22b7f6f8 Thomas Thrainer
  physical CPUs. In case any node has less CPUs or we cannot get the
2071 22b7f6f8 Thomas Thrainer
  information from the node, this function raises an OpPrereqError
2072 22b7f6f8 Thomas Thrainer
  exception.
2073 22b7f6f8 Thomas Thrainer

2074 22b7f6f8 Thomas Thrainer
  @type lu: C{LogicalUnit}
2075 22b7f6f8 Thomas Thrainer
  @param lu: a logical unit from which we get configuration data
2076 22b7f6f8 Thomas Thrainer
  @type nodenames: C{list}
2077 22b7f6f8 Thomas Thrainer
  @param nodenames: the list of node names to check
2078 22b7f6f8 Thomas Thrainer
  @type requested: C{int}
2079 22b7f6f8 Thomas Thrainer
  @param requested: the minimum acceptable number of physical CPUs
2080 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node doesn't have enough CPUs,
2081 22b7f6f8 Thomas Thrainer
      or we cannot check the node
2082 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

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

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

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

2297 22b7f6f8 Thomas Thrainer
    """
2298 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2299 22b7f6f8 Thomas Thrainer
      mode = params.setdefault(constants.IDISK_MODE, constants.DISK_RDWR)
2300 22b7f6f8 Thomas Thrainer
      if mode not in constants.DISK_ACCESS_SET:
2301 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk access mode '%s'" % mode,
2302 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2303 22b7f6f8 Thomas Thrainer
2304 22b7f6f8 Thomas Thrainer
      size = params.get(constants.IDISK_SIZE, None)
2305 22b7f6f8 Thomas Thrainer
      if size is None:
2306 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Required disk parameter '%s' missing" %
2307 22b7f6f8 Thomas Thrainer
                                   constants.IDISK_SIZE, errors.ECODE_INVAL)
2308 22b7f6f8 Thomas Thrainer
2309 22b7f6f8 Thomas Thrainer
      try:
2310 22b7f6f8 Thomas Thrainer
        size = int(size)
2311 22b7f6f8 Thomas Thrainer
      except (TypeError, ValueError), err:
2312 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk size parameter: %s" % err,
2313 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2314 22b7f6f8 Thomas Thrainer
2315 22b7f6f8 Thomas Thrainer
      params[constants.IDISK_SIZE] = size
2316 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2317 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2318 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2319 22b7f6f8 Thomas Thrainer
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 c5c72215 Dimitris Aragiorgis
2325 c5c72215 Dimitris Aragiorgis
      # Disk modification supports changing only the disk name and mode.
2326 c5c72215 Dimitris Aragiorgis
      # Changing arbitrary parameters is allowed only for ext disk template",
2327 c5c72215 Dimitris Aragiorgis
      if self.instance.disk_template != constants.DT_EXT:
2328 c5c72215 Dimitris Aragiorgis
        utils.ForceDictType(params, constants.MODIFIABLE_IDISK_PARAMS_TYPES)
2329 c5c72215 Dimitris Aragiorgis
2330 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
2331 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
2332 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
2333 22b7f6f8 Thomas Thrainer
2334 22b7f6f8 Thomas Thrainer
  @staticmethod
2335 22b7f6f8 Thomas Thrainer
  def _VerifyNicModification(op, params):
2336 22b7f6f8 Thomas Thrainer
    """Verifies a network interface modification.
2337 22b7f6f8 Thomas Thrainer

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

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

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

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

2691 22b7f6f8 Thomas Thrainer
    This only checks the instance list against the existing names.
2692 22b7f6f8 Thomas Thrainer

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

3055 22b7f6f8 Thomas Thrainer
    """
3056 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to drbd")
3057 22b7f6f8 Thomas Thrainer
    instance = self.instance
3058 22b7f6f8 Thomas Thrainer
    pnode = instance.primary_node
3059 22b7f6f8 Thomas Thrainer
    snode = self.op.remote_node
3060 22b7f6f8 Thomas Thrainer
3061 22b7f6f8 Thomas Thrainer
    assert instance.disk_template == constants.DT_PLAIN
3062 22b7f6f8 Thomas Thrainer
3063 22b7f6f8 Thomas Thrainer
    # create a fake disk info for _GenerateDiskTemplate
3064 22b7f6f8 Thomas Thrainer
    disk_info = [{constants.IDISK_SIZE: d.size, constants.IDISK_MODE: d.mode,
3065 22b7f6f8 Thomas Thrainer
                  constants.IDISK_VG: d.logical_id[0],
3066 22b7f6f8 Thomas Thrainer
                  constants.IDISK_NAME: d.name}
3067 22b7f6f8 Thomas Thrainer
                 for d in instance.disks]
3068 5eacbcae Thomas Thrainer
    new_disks = GenerateDiskTemplate(self, self.op.disk_template,
3069 5eacbcae Thomas Thrainer
                                     instance.name, pnode, [snode],
3070 5eacbcae Thomas Thrainer
                                     disk_info, None, None, 0, feedback_fn,
3071 5eacbcae Thomas Thrainer
                                     self.diskparams)
3072 22b7f6f8 Thomas Thrainer
    anno_disks = rpc.AnnotateDiskParams(constants.DT_DRBD8, new_disks,
3073 22b7f6f8 Thomas Thrainer
                                        self.diskparams)
3074 5eacbcae Thomas Thrainer
    p_excl_stor = IsExclusiveStorageEnabledNodeName(self.cfg, pnode)
3075 5eacbcae Thomas Thrainer
    s_excl_stor = IsExclusiveStorageEnabledNodeName(self.cfg, snode)
3076 5eacbcae Thomas Thrainer
    info = GetInstanceInfoText(instance)
3077 22b7f6f8 Thomas Thrainer
    feedback_fn("Creating additional volumes...")
3078 22b7f6f8 Thomas Thrainer
    # first, create the missing data and meta devices
3079 22b7f6f8 Thomas Thrainer
    for disk in anno_disks:
3080 22b7f6f8 Thomas Thrainer
      # unfortunately this is... not too nice
3081 5eacbcae Thomas Thrainer
      CreateSingleBlockDev(self, pnode, instance, disk.children[1],
3082 5eacbcae Thomas Thrainer
                           info, True, p_excl_stor)
3083 22b7f6f8 Thomas Thrainer
      for child in disk.children:
3084 5eacbcae Thomas Thrainer
        CreateSingleBlockDev(self, snode, instance, child, info, True,
3085 5eacbcae Thomas Thrainer
                             s_excl_stor)
3086 22b7f6f8 Thomas Thrainer
    # at this stage, all new LVs have been created, we can rename the
3087 22b7f6f8 Thomas Thrainer
    # old ones
3088 22b7f6f8 Thomas Thrainer
    feedback_fn("Renaming original volumes...")
3089 22b7f6f8 Thomas Thrainer
    rename_list = [(o, n.children[0].logical_id)
3090 22b7f6f8 Thomas Thrainer
                   for (o, n) in zip(instance.disks, new_disks)]
3091 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_blockdev_rename(pnode, rename_list)
3092 22b7f6f8 Thomas Thrainer
    result.Raise("Failed to rename original LVs")
3093 22b7f6f8 Thomas Thrainer
3094 22b7f6f8 Thomas Thrainer
    feedback_fn("Initializing DRBD devices...")
3095 22b7f6f8 Thomas Thrainer
    # all child devices are in place, we can now create the DRBD devices
3096 22b7f6f8 Thomas Thrainer
    try:
3097 22b7f6f8 Thomas Thrainer
      for disk in anno_disks:
3098 22b7f6f8 Thomas Thrainer
        for (node, excl_stor) in [(pnode, p_excl_stor), (snode, s_excl_stor)]:
3099 22b7f6f8 Thomas Thrainer
          f_create = node == pnode
3100 5eacbcae Thomas Thrainer
          CreateSingleBlockDev(self, node, instance, disk, info, f_create,
3101 5eacbcae Thomas Thrainer
                               excl_stor)
3102 22b7f6f8 Thomas Thrainer
    except errors.GenericError, e:
3103 22b7f6f8 Thomas Thrainer
      feedback_fn("Initializing of DRBD devices failed;"
3104 22b7f6f8 Thomas Thrainer
                  " renaming back original volumes...")
3105 22b7f6f8 Thomas Thrainer
      for disk in new_disks:
3106 22b7f6f8 Thomas Thrainer
        self.cfg.SetDiskID(disk, pnode)
3107 22b7f6f8 Thomas Thrainer
      rename_back_list = [(n.children[0], o.logical_id)
3108 22b7f6f8 Thomas Thrainer
                          for (n, o) in zip(new_disks, instance.disks)]
3109 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_blockdev_rename(pnode, rename_back_list)
3110 22b7f6f8 Thomas Thrainer
      result.Raise("Failed to rename LVs back after error %s" % str(e))
3111 22b7f6f8 Thomas Thrainer
      raise
3112 22b7f6f8 Thomas Thrainer
3113 22b7f6f8 Thomas Thrainer
    # at this point, the instance has been modified
3114 22b7f6f8 Thomas Thrainer
    instance.disk_template = constants.DT_DRBD8
3115 22b7f6f8 Thomas Thrainer
    instance.disks = new_disks
3116 22b7f6f8 Thomas Thrainer
    self.cfg.Update(instance, feedback_fn)
3117 22b7f6f8 Thomas Thrainer
3118 22b7f6f8 Thomas Thrainer
    # Release node locks while waiting for sync
3119 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3120 22b7f6f8 Thomas Thrainer
3121 22b7f6f8 Thomas Thrainer
    # disks are created, waiting for sync
3122 5eacbcae Thomas Thrainer
    disk_abort = not WaitForSync(self, instance,
3123 5eacbcae Thomas Thrainer
                                 oneshot=not self.op.wait_for_sync)
3124 22b7f6f8 Thomas Thrainer
    if disk_abort:
3125 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("There are some degraded disks for"
3126 22b7f6f8 Thomas Thrainer
                               " this instance, please cleanup manually")
3127 22b7f6f8 Thomas Thrainer
3128 22b7f6f8 Thomas Thrainer
    # Node resource locks will be released by caller
3129 22b7f6f8 Thomas Thrainer
3130 22b7f6f8 Thomas Thrainer
  def _ConvertDrbdToPlain(self, feedback_fn):
3131 22b7f6f8 Thomas Thrainer
    """Converts an instance from drbd to plain.
3132 22b7f6f8 Thomas Thrainer

3133 22b7f6f8 Thomas Thrainer
    """
3134 22b7f6f8 Thomas Thrainer
    instance = self.instance
3135 22b7f6f8 Thomas Thrainer
3136 22b7f6f8 Thomas Thrainer
    assert len(instance.secondary_nodes) == 1
3137 22b7f6f8 Thomas Thrainer
    assert instance.disk_template == constants.DT_DRBD8
3138 22b7f6f8 Thomas Thrainer
3139 22b7f6f8 Thomas Thrainer
    pnode = instance.primary_node
3140 22b7f6f8 Thomas Thrainer
    snode = instance.secondary_nodes[0]
3141 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to plain")
3142 22b7f6f8 Thomas Thrainer
3143 5eacbcae Thomas Thrainer
    old_disks = AnnotateDiskParams(instance, instance.disks, self.cfg)
3144 22b7f6f8 Thomas Thrainer
    new_disks = [d.children[0] for d in instance.disks]
3145 22b7f6f8 Thomas Thrainer
3146 22b7f6f8 Thomas Thrainer
    # copy over size, mode and name
3147 22b7f6f8 Thomas Thrainer
    for parent, child in zip(old_disks, new_disks):
3148 22b7f6f8 Thomas Thrainer
      child.size = parent.size
3149 22b7f6f8 Thomas Thrainer
      child.mode = parent.mode
3150 22b7f6f8 Thomas Thrainer
      child.name = parent.name
3151 22b7f6f8 Thomas Thrainer
3152 22b7f6f8 Thomas Thrainer
    # this is a DRBD disk, return its port to the pool
3153 22b7f6f8 Thomas Thrainer
    # NOTE: this must be done right before the call to cfg.Update!
3154 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3155 22b7f6f8 Thomas Thrainer
      tcp_port = disk.logical_id[2]
3156 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(tcp_port)
3157 22b7f6f8 Thomas Thrainer
3158 22b7f6f8 Thomas Thrainer
    # update instance structure
3159 22b7f6f8 Thomas Thrainer
    instance.disks = new_disks
3160 22b7f6f8 Thomas Thrainer
    instance.disk_template = constants.DT_PLAIN
3161 22b7f6f8 Thomas Thrainer
    _UpdateIvNames(0, instance.disks)
3162 22b7f6f8 Thomas Thrainer
    self.cfg.Update(instance, feedback_fn)
3163 22b7f6f8 Thomas Thrainer
3164 22b7f6f8 Thomas Thrainer
    # Release locks in case removing disks takes a while
3165 5eacbcae Thomas Thrainer
    ReleaseLocks(self, locking.LEVEL_NODE)
3166 22b7f6f8 Thomas Thrainer
3167 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing volumes on the secondary node...")
3168 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3169 22b7f6f8 Thomas Thrainer
      self.cfg.SetDiskID(disk, snode)
3170 22b7f6f8 Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(snode, disk).fail_msg
3171 22b7f6f8 Thomas Thrainer
      if msg:
3172 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove block device %s on node %s,"
3173 22b7f6f8 Thomas Thrainer
                        " continuing anyway: %s", disk.iv_name, snode, msg)
3174 22b7f6f8 Thomas Thrainer
3175 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing unneeded volumes on the primary node...")
3176 22b7f6f8 Thomas Thrainer
    for idx, disk in enumerate(old_disks):
3177 22b7f6f8 Thomas Thrainer
      meta = disk.children[1]
3178 22b7f6f8 Thomas Thrainer
      self.cfg.SetDiskID(meta, pnode)
3179 22b7f6f8 Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(pnode, meta).fail_msg
3180 22b7f6f8 Thomas Thrainer
      if msg:
3181 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove metadata for disk %d on node %s,"
3182 22b7f6f8 Thomas Thrainer
                        " continuing anyway: %s", idx, pnode, msg)
3183 22b7f6f8 Thomas Thrainer
3184 d26af78e Dimitris Aragiorgis
  def _HotplugDevice(self, action, dev_type, device, extra, seq):
3185 d26af78e Dimitris Aragiorgis
    self.LogInfo("Trying to hotplug device...")
3186 d0d1f2c4 Dimitris Aragiorgis
    msg = "hotplug:"
3187 d26af78e Dimitris Aragiorgis
    result = self.rpc.call_hotplug_device(self.instance.primary_node,
3188 d26af78e Dimitris Aragiorgis
                                          self.instance, action, dev_type,
3189 014d9fac Dimitris Aragiorgis
                                          (device, self.instance),
3190 014d9fac Dimitris Aragiorgis
                                          extra, seq)
3191 d26af78e Dimitris Aragiorgis
    if result.fail_msg:
3192 d26af78e Dimitris Aragiorgis
      self.LogWarning("Could not hotplug device: %s" % result.fail_msg)
3193 d26af78e Dimitris Aragiorgis
      self.LogInfo("Continuing execution..")
3194 d0d1f2c4 Dimitris Aragiorgis
      msg += "failed"
3195 d26af78e Dimitris Aragiorgis
    else:
3196 d26af78e Dimitris Aragiorgis
      self.LogInfo("Hotplug done.")
3197 d0d1f2c4 Dimitris Aragiorgis
      msg += "done"
3198 d0d1f2c4 Dimitris Aragiorgis
    return msg
3199 d26af78e Dimitris Aragiorgis
3200 22b7f6f8 Thomas Thrainer
  def _CreateNewDisk(self, idx, params, _):
3201 22b7f6f8 Thomas Thrainer
    """Creates a new disk.
3202 22b7f6f8 Thomas Thrainer

3203 22b7f6f8 Thomas Thrainer
    """
3204 22b7f6f8 Thomas Thrainer
    instance = self.instance
3205 22b7f6f8 Thomas Thrainer
3206 22b7f6f8 Thomas Thrainer
    # add a new disk
3207 22b7f6f8 Thomas Thrainer
    if instance.disk_template in constants.DTS_FILEBASED:
3208 22b7f6f8 Thomas Thrainer
      (file_driver, file_path) = instance.disks[0].logical_id
3209 22b7f6f8 Thomas Thrainer
      file_path = os.path.dirname(file_path)
3210 22b7f6f8 Thomas Thrainer
    else:
3211 22b7f6f8 Thomas Thrainer
      file_driver = file_path = None
3212 22b7f6f8 Thomas Thrainer
3213 22b7f6f8 Thomas Thrainer
    disk = \
3214 5eacbcae Thomas Thrainer
      GenerateDiskTemplate(self, instance.disk_template, instance.name,
3215 5eacbcae Thomas Thrainer
                           instance.primary_node, instance.secondary_nodes,
3216 5eacbcae Thomas Thrainer
                           [params], file_path, file_driver, idx,
3217 5eacbcae Thomas Thrainer
                           self.Log, self.diskparams)[0]
3218 22b7f6f8 Thomas Thrainer
3219 a365b47f Bernardo Dal Seno
    new_disks = CreateDisks(self, instance, disks=[disk])
3220 22b7f6f8 Thomas Thrainer
3221 22b7f6f8 Thomas Thrainer
    if self.cluster.prealloc_wipe_disks:
3222 22b7f6f8 Thomas Thrainer
      # Wipe new disk
3223 a365b47f Bernardo Dal Seno
      WipeOrCleanupDisks(self, instance,
3224 a365b47f Bernardo Dal Seno
                         disks=[(idx, disk, 0)],
3225 a365b47f Bernardo Dal Seno
                         cleanup=new_disks)
3226 22b7f6f8 Thomas Thrainer
3227 d0d1f2c4 Dimitris Aragiorgis
    changes = [
3228 d0d1f2c4 Dimitris Aragiorgis
      ("disk/%d" % idx,
3229 d0d1f2c4 Dimitris Aragiorgis
      "add:size=%s,mode=%s" % (disk.size, disk.mode)),
3230 d0d1f2c4 Dimitris Aragiorgis
      ]
3231 d26af78e Dimitris Aragiorgis
    if self.op.hotplug:
3232 5398bd02 Dimitris Aragiorgis
      self.cfg.SetDiskID(disk, self.instance.primary_node)
3233 d26af78e Dimitris Aragiorgis
      result = self.rpc.call_blockdev_assemble(self.instance.primary_node,
3234 d26af78e Dimitris Aragiorgis
                                               (disk, self.instance),
3235 d26af78e Dimitris Aragiorgis
                                               self.instance.name, True, idx)
3236 d26af78e Dimitris Aragiorgis
      if result.fail_msg:
3237 d0d1f2c4 Dimitris Aragiorgis
        changes.append(("disk/%d" % idx, "assemble:failed"))
3238 d26af78e Dimitris Aragiorgis
        self.LogWarning("Can't assemble newly created disk %d: %s",
3239 d26af78e Dimitris Aragiorgis
                        idx, result.fail_msg)
3240 d26af78e Dimitris Aragiorgis
      else:
3241 d26af78e Dimitris Aragiorgis
        _, link_name = result.payload
3242 d0d1f2c4 Dimitris Aragiorgis
        msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
3243 d0d1f2c4 Dimitris Aragiorgis
                                  constants.HOTPLUG_TARGET_DISK,
3244 d0d1f2c4 Dimitris Aragiorgis
                                  disk, link_name, idx)
3245 d0d1f2c4 Dimitris Aragiorgis
        changes.append(("disk/%d" % idx, msg))
3246 d26af78e Dimitris Aragiorgis
3247 d0d1f2c4 Dimitris Aragiorgis
    return (disk, changes)
3248 22b7f6f8 Thomas Thrainer
3249 c5c72215 Dimitris Aragiorgis
  def _ModifyDisk(self, idx, disk, params, _):
3250 22b7f6f8 Thomas Thrainer
    """Modifies a disk.
3251 22b7f6f8 Thomas Thrainer

3252 22b7f6f8 Thomas Thrainer
    """
3253 22b7f6f8 Thomas Thrainer
    changes = []
3254 4eef428e Dimitris Aragiorgis
    if constants.IDISK_MODE in params:
3255 4eef428e Dimitris Aragiorgis
      disk.mode = params.get(constants.IDISK_MODE)
3256 22b7f6f8 Thomas Thrainer
      changes.append(("disk.mode/%d" % idx, disk.mode))
3257 22b7f6f8 Thomas Thrainer
3258 4eef428e Dimitris Aragiorgis
    if constants.IDISK_NAME in params:
3259 4eef428e Dimitris Aragiorgis
      disk.name = params.get(constants.IDISK_NAME)
3260 4eef428e Dimitris Aragiorgis
      changes.append(("disk.name/%d" % idx, disk.name))
3261 22b7f6f8 Thomas Thrainer
3262 c5c72215 Dimitris Aragiorgis
    # Modify arbitrary params in case instance template is ext
3263 c5c72215 Dimitris Aragiorgis
    for key, value in params.iteritems():
3264 c5c72215 Dimitris Aragiorgis
      if (key not in constants.MODIFIABLE_IDISK_PARAMS and
3265 c5c72215 Dimitris Aragiorgis
          self.instance.disk_template == constants.DT_EXT):
3266 e228ab9c Dimitris Aragiorgis
        # stolen from GetUpdatedParams: default means reset/delete
3267 e228ab9c Dimitris Aragiorgis
        if value.lower() == constants.VALUE_DEFAULT:
3268 e228ab9c Dimitris Aragiorgis
          try:
3269 e228ab9c Dimitris Aragiorgis
            del disk.params[key]
3270 e228ab9c Dimitris Aragiorgis
          except KeyError:
3271 e228ab9c Dimitris Aragiorgis
            pass
3272 e228ab9c Dimitris Aragiorgis
        else:
3273 e228ab9c Dimitris Aragiorgis
          disk.params[key] = value
3274 c5c72215 Dimitris Aragiorgis
        changes.append(("disk.params:%s/%d" % (key, idx), value))
3275 c5c72215 Dimitris Aragiorgis
3276 22b7f6f8 Thomas Thrainer
    return changes
3277 22b7f6f8 Thomas Thrainer
3278 22b7f6f8 Thomas Thrainer
  def _RemoveDisk(self, idx, root, _):
3279 22b7f6f8 Thomas Thrainer
    """Removes a disk.
3280 22b7f6f8 Thomas Thrainer

3281 22b7f6f8 Thomas Thrainer
    """
3282 d0d1f2c4 Dimitris Aragiorgis
    hotmsg = ""
3283 d26af78e Dimitris Aragiorgis
    if self.op.hotplug:
3284 d0d1f2c4 Dimitris Aragiorgis
      hotmsg = self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
3285 d0d1f2c4 Dimitris Aragiorgis
                                   constants.HOTPLUG_TARGET_DISK,
3286 d0d1f2c4 Dimitris Aragiorgis
                                   root, None, idx)
3287 d26af78e Dimitris Aragiorgis
      ShutdownInstanceDisks(self, self.instance, [root])
3288 d26af78e Dimitris Aragiorgis
3289 5eacbcae Thomas Thrainer
    (anno_disk,) = AnnotateDiskParams(self.instance, [root], self.cfg)
3290 22b7f6f8 Thomas Thrainer
    for node, disk in anno_disk.ComputeNodeTree(self.instance.primary_node):
3291 5831baf3 Dimitris Aragiorgis
      if self.op.keep_disks and disk.dev_type in constants.DT_EXT:
3292 5831baf3 Dimitris Aragiorgis
        continue
3293 22b7f6f8 Thomas Thrainer
      self.cfg.SetDiskID(disk, node)
3294 22b7f6f8 Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(node, disk).fail_msg
3295 22b7f6f8 Thomas Thrainer
      if msg:
3296 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove disk/%d on node '%s': %s,"
3297 22b7f6f8 Thomas Thrainer
                        " continuing anyway", idx, node, msg)
3298 22b7f6f8 Thomas Thrainer
3299 22b7f6f8 Thomas Thrainer
    # if this is a DRBD disk, return its port to the pool
3300 22b7f6f8 Thomas Thrainer
    if root.dev_type in constants.LDS_DRBD:
3301 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(root.logical_id[2])
3302 22b7f6f8 Thomas Thrainer
3303 d0d1f2c4 Dimitris Aragiorgis
    return hotmsg
3304 d0d1f2c4 Dimitris Aragiorgis
3305 22b7f6f8 Thomas Thrainer
  def _CreateNewNic(self, idx, params, private):
3306 22b7f6f8 Thomas Thrainer
    """Creates data structure for a new network interface.
3307 22b7f6f8 Thomas Thrainer

3308 22b7f6f8 Thomas Thrainer
    """
3309 22b7f6f8 Thomas Thrainer
    mac = params[constants.INIC_MAC]
3310 22b7f6f8 Thomas Thrainer
    ip = params.get(constants.INIC_IP, None)
3311 22b7f6f8 Thomas Thrainer
    net = params.get(constants.INIC_NETWORK, None)
3312 22b7f6f8 Thomas Thrainer
    name = params.get(constants.INIC_NAME, None)
3313 22b7f6f8 Thomas Thrainer
    net_uuid = self.cfg.LookupNetwork(net)
3314 22b7f6f8 Thomas Thrainer
    #TODO: not private.filled?? can a nic have no nicparams??
3315 22b7f6f8 Thomas Thrainer
    nicparams = private.filled
3316 22b7f6f8 Thomas Thrainer
    nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, name=name,
3317 22b7f6f8 Thomas Thrainer
                       nicparams=nicparams)
3318 22b7f6f8 Thomas Thrainer
    nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
3319 22b7f6f8 Thomas Thrainer
3320 d0d1f2c4 Dimitris Aragiorgis
    changes = [
3321 22b7f6f8 Thomas Thrainer
      ("nic.%d" % idx,
3322 22b7f6f8 Thomas Thrainer
       "add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" %
3323 22b7f6f8 Thomas Thrainer
       (mac, ip, private.filled[constants.NIC_MODE],
3324 d26af78e Dimitris Aragiorgis
       private.filled[constants.NIC_LINK], net)),
3325 d26af78e Dimitris Aragiorgis
      ]
3326 d26af78e Dimitris Aragiorgis
3327 d0d1f2c4 Dimitris Aragiorgis
    if self.op.hotplug:
3328 d0d1f2c4 Dimitris Aragiorgis
      msg = self._HotplugDevice(constants.HOTPLUG_ACTION_ADD,
3329 d0d1f2c4 Dimitris Aragiorgis
                                constants.HOTPLUG_TARGET_NIC,
3330 d0d1f2c4 Dimitris Aragiorgis
                                nobj, None, idx)
3331 d0d1f2c4 Dimitris Aragiorgis
      changes.append(("nic.%d" % idx, msg))
3332 d0d1f2c4 Dimitris Aragiorgis
3333 d0d1f2c4 Dimitris Aragiorgis
    return (nobj, changes)
3334 22b7f6f8 Thomas Thrainer
3335 22b7f6f8 Thomas Thrainer
  def _ApplyNicMods(self, idx, nic, params, private):
3336 22b7f6f8 Thomas Thrainer
    """Modifies a network interface.
3337 22b7f6f8 Thomas Thrainer

3338 22b7f6f8 Thomas Thrainer
    """
3339 22b7f6f8 Thomas Thrainer
    changes = []
3340 22b7f6f8 Thomas Thrainer
3341 22b7f6f8 Thomas Thrainer
    for key in [constants.INIC_MAC, constants.INIC_IP, constants.INIC_NAME]:
3342 22b7f6f8 Thomas Thrainer
      if key in params:
3343 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), params[key]))
3344 22b7f6f8 Thomas Thrainer
        setattr(nic, key, params[key])
3345 22b7f6f8 Thomas Thrainer
3346 22b7f6f8 Thomas Thrainer
    new_net = params.get(constants.INIC_NETWORK, nic.network)
3347 22b7f6f8 Thomas Thrainer
    new_net_uuid = self.cfg.LookupNetwork(new_net)
3348 22b7f6f8 Thomas Thrainer
    if new_net_uuid != nic.network:
3349 22b7f6f8 Thomas Thrainer
      changes.append(("nic.network/%d" % idx, new_net))
3350 22b7f6f8 Thomas Thrainer
      nic.network = new_net_uuid
3351 22b7f6f8 Thomas Thrainer
3352 22b7f6f8 Thomas Thrainer
    if private.filled:
3353 22b7f6f8 Thomas Thrainer
      nic.nicparams = private.filled
3354 22b7f6f8 Thomas Thrainer
3355 22b7f6f8 Thomas Thrainer
      for (key, val) in nic.nicparams.items():
3356 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), val))
3357 22b7f6f8 Thomas Thrainer
3358 d26af78e Dimitris Aragiorgis
    if self.op.hotplug:
3359 d0d1f2c4 Dimitris Aragiorgis
      msg = self._HotplugDevice(constants.HOTPLUG_ACTION_MODIFY,
3360 d0d1f2c4 Dimitris Aragiorgis
                                constants.HOTPLUG_TARGET_NIC,
3361 d0d1f2c4 Dimitris Aragiorgis
                                nic, None, idx)
3362 d0d1f2c4 Dimitris Aragiorgis
      changes.append(("nic/%d" % idx, msg))
3363 d26af78e Dimitris Aragiorgis
3364 22b7f6f8 Thomas Thrainer
    return changes
3365 22b7f6f8 Thomas Thrainer
3366 d26af78e Dimitris Aragiorgis
  def _RemoveNic(self, idx, nic, _):
3367 d26af78e Dimitris Aragiorgis
    if self.op.hotplug:
3368 d0d1f2c4 Dimitris Aragiorgis
      return self._HotplugDevice(constants.HOTPLUG_ACTION_REMOVE,
3369 d0d1f2c4 Dimitris Aragiorgis
                                 constants.HOTPLUG_TARGET_NIC,
3370 d0d1f2c4 Dimitris Aragiorgis
                                 nic, None, idx)
3371 d26af78e Dimitris Aragiorgis
3372 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3373 22b7f6f8 Thomas Thrainer
    """Modifies an instance.
3374 22b7f6f8 Thomas Thrainer

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

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

3594 22b7f6f8 Thomas Thrainer
    """
3595 22b7f6f8 Thomas Thrainer
    assert self.target_uuids
3596 22b7f6f8 Thomas Thrainer
3597 22b7f6f8 Thomas Thrainer
    env = {
3598 22b7f6f8 Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
3599 22b7f6f8 Thomas Thrainer
      }
3600 22b7f6f8 Thomas Thrainer
3601 5eacbcae Thomas Thrainer
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
3602 22b7f6f8 Thomas Thrainer
3603 22b7f6f8 Thomas Thrainer
    return env
3604 22b7f6f8 Thomas Thrainer
3605 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
3606 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
3607 22b7f6f8 Thomas Thrainer

3608 22b7f6f8 Thomas Thrainer
    """
3609 22b7f6f8 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
3610 22b7f6f8 Thomas Thrainer
    return ([mn], [mn])
3611 22b7f6f8 Thomas Thrainer
3612 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
3613 22b7f6f8 Thomas Thrainer
    instances = list(self.owned_locks(locking.LEVEL_INSTANCE))
3614 22b7f6f8 Thomas Thrainer
3615 22b7f6f8 Thomas Thrainer
    assert instances == [self.op.instance_name], "Instance not locked"
3616 22b7f6f8 Thomas Thrainer
3617 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=instances,
3618 22b7f6f8 Thomas Thrainer
                                      target_groups=list(self.target_uuids))
3619 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
3620 22b7f6f8 Thomas Thrainer
3621 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
3622 22b7f6f8 Thomas Thrainer
3623 22b7f6f8 Thomas Thrainer
    if not ial.success:
3624 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute solution for changing group of"
3625 22b7f6f8 Thomas Thrainer
                                 " instance '%s' using iallocator '%s': %s" %
3626 22b7f6f8 Thomas Thrainer
                                 (self.op.instance_name, self.op.iallocator,
3627 22b7f6f8 Thomas Thrainer
                                  ial.info), errors.ECODE_NORES)
3628 22b7f6f8 Thomas Thrainer
3629 5eacbcae Thomas Thrainer
    jobs = LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
3630 22b7f6f8 Thomas Thrainer
3631 22b7f6f8 Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for changing group of"
3632 22b7f6f8 Thomas Thrainer
                 " instance '%s'", len(jobs), self.op.instance_name)
3633 22b7f6f8 Thomas Thrainer
3634 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs)