Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance.py @ 87e25be1

History | View | Annotate | Download (163.6 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 itertools
27 22b7f6f8 Thomas Thrainer
import logging
28 22b7f6f8 Thomas Thrainer
import operator
29 22b7f6f8 Thomas Thrainer
import os
30 22b7f6f8 Thomas Thrainer
31 22b7f6f8 Thomas Thrainer
from ganeti import compat
32 22b7f6f8 Thomas Thrainer
from ganeti import constants
33 22b7f6f8 Thomas Thrainer
from ganeti import errors
34 22b7f6f8 Thomas Thrainer
from ganeti import ht
35 22b7f6f8 Thomas Thrainer
from ganeti import hypervisor
36 22b7f6f8 Thomas Thrainer
from ganeti import locking
37 22b7f6f8 Thomas Thrainer
from ganeti.masterd import iallocator
38 22b7f6f8 Thomas Thrainer
from ganeti import masterd
39 22b7f6f8 Thomas Thrainer
from ganeti import netutils
40 22b7f6f8 Thomas Thrainer
from ganeti import objects
41 22b7f6f8 Thomas Thrainer
from ganeti import opcodes
42 22b7f6f8 Thomas Thrainer
from ganeti import pathutils
43 22b7f6f8 Thomas Thrainer
from ganeti import qlang
44 22b7f6f8 Thomas Thrainer
from ganeti import rpc
45 22b7f6f8 Thomas Thrainer
from ganeti import utils
46 22b7f6f8 Thomas Thrainer
from ganeti import query
47 22b7f6f8 Thomas Thrainer
48 22b7f6f8 Thomas Thrainer
from ganeti.cmdlib.base import NoHooksLU, LogicalUnit, _QueryBase, \
49 87e25be1 Thomas Thrainer
  ResultWithJobs
50 22b7f6f8 Thomas Thrainer
51 22b7f6f8 Thomas Thrainer
from ganeti.cmdlib.common import INSTANCE_ONLINE, INSTANCE_DOWN, \
52 22b7f6f8 Thomas Thrainer
  INSTANCE_NOT_RUNNING, CAN_CHANGE_INSTANCE_OFFLINE, _CheckNodeOnline, \
53 22b7f6f8 Thomas Thrainer
  _ShareAll, _GetDefaultIAllocator, _CheckInstanceNodeGroups, \
54 22b7f6f8 Thomas Thrainer
  _LoadNodeEvacResult, _CheckIAllocatorOrNode, _CheckParamsNotGlobal, \
55 22b7f6f8 Thomas Thrainer
  _IsExclusiveStorageEnabledNode, _CheckHVParams, _CheckOSParams, \
56 22b7f6f8 Thomas Thrainer
  _GetWantedInstances, _CheckInstancesNodeGroups, _AnnotateDiskParams, \
57 763ad5be Thomas Thrainer
  _GetUpdatedParams, _ExpandInstanceName, _ComputeIPolicySpecViolation, \
58 22b7f6f8 Thomas Thrainer
  _CheckInstanceState, _ExpandNodeName
59 763ad5be Thomas Thrainer
from ganeti.cmdlib.instance_storage import _CreateDisks, \
60 87e25be1 Thomas Thrainer
  _CheckNodesFreeDiskPerVG, _WipeDisks, _WaitForSync, \
61 763ad5be Thomas Thrainer
  _IsExclusiveStorageEnabledNodeName, _CreateSingleBlockDev, _ComputeDisks, \
62 763ad5be Thomas Thrainer
  _CheckRADOSFreeSpace, _ComputeDiskSizePerVG, _GenerateDiskTemplate, \
63 763ad5be Thomas Thrainer
  _CreateBlockDev, _StartInstanceDisks, _ShutdownInstanceDisks, \
64 87e25be1 Thomas Thrainer
  _AssembleInstanceDisks
65 763ad5be Thomas Thrainer
from ganeti.cmdlib.instance_utils import _BuildInstanceHookEnvByObject, \
66 763ad5be Thomas Thrainer
  _GetClusterDomainSecret, _BuildInstanceHookEnv, _NICListToTuple, \
67 763ad5be Thomas Thrainer
  _NICToTuple, _CheckNodeNotDrained, _RemoveInstance, _CopyLockList, \
68 763ad5be Thomas Thrainer
  _ReleaseLocks, _CheckNodeVmCapable, _CheckTargetNodeIPolicy, \
69 87e25be1 Thomas Thrainer
  _GetInstanceInfoText, _RemoveDisks, _CheckNodeFreeMemory, \
70 87e25be1 Thomas Thrainer
  _CheckInstanceBridgesExist, _CheckNicsBridgesExist
71 22b7f6f8 Thomas Thrainer
72 22b7f6f8 Thomas Thrainer
import ganeti.masterd.instance
73 22b7f6f8 Thomas Thrainer
74 22b7f6f8 Thomas Thrainer
75 22b7f6f8 Thomas Thrainer
#: Type description for changes as returned by L{ApplyContainerMods}'s
76 22b7f6f8 Thomas Thrainer
#: callbacks
77 22b7f6f8 Thomas Thrainer
_TApplyContModsCbChanges = \
78 22b7f6f8 Thomas Thrainer
  ht.TMaybeListOf(ht.TAnd(ht.TIsLength(2), ht.TItems([
79 22b7f6f8 Thomas Thrainer
    ht.TNonEmptyString,
80 22b7f6f8 Thomas Thrainer
    ht.TAny,
81 22b7f6f8 Thomas Thrainer
    ])))
82 22b7f6f8 Thomas Thrainer
83 22b7f6f8 Thomas Thrainer
84 22b7f6f8 Thomas Thrainer
def _CheckHostnameSane(lu, name):
85 22b7f6f8 Thomas Thrainer
  """Ensures that a given hostname resolves to a 'sane' name.
86 22b7f6f8 Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

257 22b7f6f8 Thomas Thrainer
  @type ip: string
258 22b7f6f8 Thomas Thrainer
  @param ip: IP address
259 22b7f6f8 Thomas Thrainer
  @type node: string
260 22b7f6f8 Thomas Thrainer
  @param node: node name
261 22b7f6f8 Thomas Thrainer

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

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

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

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

307 22b7f6f8 Thomas Thrainer
  """
308 22b7f6f8 Thomas Thrainer
  variant = objects.OS.GetVariant(name)
309 22b7f6f8 Thomas Thrainer
  if not os_obj.supported_variants:
310 22b7f6f8 Thomas Thrainer
    if variant:
311 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("OS '%s' doesn't support variants ('%s'"
312 22b7f6f8 Thomas Thrainer
                                 " passed)" % (os_obj.name, variant),
313 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
314 22b7f6f8 Thomas Thrainer
    return
315 22b7f6f8 Thomas Thrainer
  if not variant:
316 22b7f6f8 Thomas Thrainer
    raise errors.OpPrereqError("OS name must include a variant",
317 22b7f6f8 Thomas Thrainer
                               errors.ECODE_INVAL)
318 22b7f6f8 Thomas Thrainer
319 22b7f6f8 Thomas Thrainer
  if variant not in os_obj.supported_variants:
320 22b7f6f8 Thomas Thrainer
    raise errors.OpPrereqError("Unsupported OS variant", errors.ECODE_INVAL)
321 22b7f6f8 Thomas Thrainer
322 22b7f6f8 Thomas Thrainer
323 22b7f6f8 Thomas Thrainer
def _CheckNodeHasOS(lu, node, os_name, force_variant):
324 22b7f6f8 Thomas Thrainer
  """Ensure that a node supports a given OS.
325 22b7f6f8 Thomas Thrainer

326 22b7f6f8 Thomas Thrainer
  @param lu: the LU on behalf of which we make the check
327 22b7f6f8 Thomas Thrainer
  @param node: the node to check
328 22b7f6f8 Thomas Thrainer
  @param os_name: the OS to query about
329 22b7f6f8 Thomas Thrainer
  @param force_variant: whether to ignore variant errors
330 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node is not supporting the OS
331 22b7f6f8 Thomas Thrainer

332 22b7f6f8 Thomas Thrainer
  """
333 22b7f6f8 Thomas Thrainer
  result = lu.rpc.call_os_get(node, os_name)
334 22b7f6f8 Thomas Thrainer
  result.Raise("OS '%s' not in supported OS list for node %s" %
335 22b7f6f8 Thomas Thrainer
               (os_name, node),
336 22b7f6f8 Thomas Thrainer
               prereq=True, ecode=errors.ECODE_INVAL)
337 22b7f6f8 Thomas Thrainer
  if not force_variant:
338 22b7f6f8 Thomas Thrainer
    _CheckOSVariant(result.payload, os_name)
339 22b7f6f8 Thomas Thrainer
340 22b7f6f8 Thomas Thrainer
341 22b7f6f8 Thomas Thrainer
class LUInstanceCreate(LogicalUnit):
342 22b7f6f8 Thomas Thrainer
  """Create an instance.
343 22b7f6f8 Thomas Thrainer

344 22b7f6f8 Thomas Thrainer
  """
345 22b7f6f8 Thomas Thrainer
  HPATH = "instance-add"
346 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
347 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
348 22b7f6f8 Thomas Thrainer
349 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
350 22b7f6f8 Thomas Thrainer
    """Check arguments.
351 22b7f6f8 Thomas Thrainer

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

523 22b7f6f8 Thomas Thrainer
    Figure out the right locks for instance creation.
524 22b7f6f8 Thomas Thrainer

525 22b7f6f8 Thomas Thrainer
    """
526 22b7f6f8 Thomas Thrainer
    self.needed_locks = {}
527 22b7f6f8 Thomas Thrainer
528 22b7f6f8 Thomas Thrainer
    instance_name = self.op.instance_name
529 22b7f6f8 Thomas Thrainer
    # this is just a preventive check, but someone might still add this
530 22b7f6f8 Thomas Thrainer
    # instance in the meantime, and creation will fail at lock-add time
531 22b7f6f8 Thomas Thrainer
    if instance_name in self.cfg.GetInstanceList():
532 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
533 22b7f6f8 Thomas Thrainer
                                 instance_name, errors.ECODE_EXISTS)
534 22b7f6f8 Thomas Thrainer
535 22b7f6f8 Thomas Thrainer
    self.add_locks[locking.LEVEL_INSTANCE] = instance_name
536 22b7f6f8 Thomas Thrainer
537 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
538 22b7f6f8 Thomas Thrainer
      # TODO: Find a solution to not lock all nodes in the cluster, e.g. by
539 22b7f6f8 Thomas Thrainer
      # specifying a group on instance creation and then selecting nodes from
540 22b7f6f8 Thomas Thrainer
      # that group
541 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
542 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
543 22b7f6f8 Thomas Thrainer
544 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
545 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
546 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE_RES] = True
547 22b7f6f8 Thomas Thrainer
    else:
548 22b7f6f8 Thomas Thrainer
      self.op.pnode = _ExpandNodeName(self.cfg, self.op.pnode)
549 22b7f6f8 Thomas Thrainer
      nodelist = [self.op.pnode]
550 22b7f6f8 Thomas Thrainer
      if self.op.snode is not None:
551 22b7f6f8 Thomas Thrainer
        self.op.snode = _ExpandNodeName(self.cfg, self.op.snode)
552 22b7f6f8 Thomas Thrainer
        nodelist.append(self.op.snode)
553 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodelist
554 22b7f6f8 Thomas Thrainer
555 22b7f6f8 Thomas Thrainer
    # in case of import lock the source node too
556 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
557 22b7f6f8 Thomas Thrainer
      src_node = self.op.src_node
558 22b7f6f8 Thomas Thrainer
      src_path = self.op.src_path
559 22b7f6f8 Thomas Thrainer
560 22b7f6f8 Thomas Thrainer
      if src_path is None:
561 22b7f6f8 Thomas Thrainer
        self.op.src_path = src_path = self.op.instance_name
562 22b7f6f8 Thomas Thrainer
563 22b7f6f8 Thomas Thrainer
      if src_node is None:
564 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
565 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE_ALLOC] = locking.ALL_SET
566 22b7f6f8 Thomas Thrainer
        self.op.src_node = None
567 22b7f6f8 Thomas Thrainer
        if os.path.isabs(src_path):
568 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("Importing an instance from a path"
569 22b7f6f8 Thomas Thrainer
                                     " requires a source node option",
570 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
571 22b7f6f8 Thomas Thrainer
      else:
572 22b7f6f8 Thomas Thrainer
        self.op.src_node = src_node = _ExpandNodeName(self.cfg, src_node)
573 22b7f6f8 Thomas Thrainer
        if self.needed_locks[locking.LEVEL_NODE] is not locking.ALL_SET:
574 22b7f6f8 Thomas Thrainer
          self.needed_locks[locking.LEVEL_NODE].append(src_node)
575 22b7f6f8 Thomas Thrainer
        if not os.path.isabs(src_path):
576 22b7f6f8 Thomas Thrainer
          self.op.src_path = src_path = \
577 22b7f6f8 Thomas Thrainer
            utils.PathJoin(pathutils.EXPORT_DIR, src_path)
578 22b7f6f8 Thomas Thrainer
579 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = \
580 22b7f6f8 Thomas Thrainer
      _CopyLockList(self.needed_locks[locking.LEVEL_NODE])
581 22b7f6f8 Thomas Thrainer
582 22b7f6f8 Thomas Thrainer
  def _RunAllocator(self):
583 22b7f6f8 Thomas Thrainer
    """Run the allocator based on input opcode.
584 22b7f6f8 Thomas Thrainer

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

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

628 22b7f6f8 Thomas Thrainer
    """
629 22b7f6f8 Thomas Thrainer
    env = {
630 22b7f6f8 Thomas Thrainer
      "ADD_MODE": self.op.mode,
631 22b7f6f8 Thomas Thrainer
      }
632 22b7f6f8 Thomas Thrainer
    if self.op.mode == constants.INSTANCE_IMPORT:
633 22b7f6f8 Thomas Thrainer
      env["SRC_NODE"] = self.op.src_node
634 22b7f6f8 Thomas Thrainer
      env["SRC_PATH"] = self.op.src_path
635 22b7f6f8 Thomas Thrainer
      env["SRC_IMAGES"] = self.src_images
636 22b7f6f8 Thomas Thrainer
637 22b7f6f8 Thomas Thrainer
    env.update(_BuildInstanceHookEnv(
638 22b7f6f8 Thomas Thrainer
      name=self.op.instance_name,
639 22b7f6f8 Thomas Thrainer
      primary_node=self.op.pnode,
640 22b7f6f8 Thomas Thrainer
      secondary_nodes=self.secondaries,
641 22b7f6f8 Thomas Thrainer
      status=self.op.start,
642 22b7f6f8 Thomas Thrainer
      os_type=self.op.os_type,
643 22b7f6f8 Thomas Thrainer
      minmem=self.be_full[constants.BE_MINMEM],
644 22b7f6f8 Thomas Thrainer
      maxmem=self.be_full[constants.BE_MAXMEM],
645 22b7f6f8 Thomas Thrainer
      vcpus=self.be_full[constants.BE_VCPUS],
646 22b7f6f8 Thomas Thrainer
      nics=_NICListToTuple(self, self.nics),
647 22b7f6f8 Thomas Thrainer
      disk_template=self.op.disk_template,
648 22b7f6f8 Thomas Thrainer
      disks=[(d[constants.IDISK_NAME], d[constants.IDISK_SIZE],
649 22b7f6f8 Thomas Thrainer
              d[constants.IDISK_MODE]) 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 22b7f6f8 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 22b7f6f8 Thomas Thrainer
          disks.append({constants.IDISK_SIZE: disk_sz})
744 22b7f6f8 Thomas Thrainer
      self.op.disks = disks
745 22b7f6f8 Thomas Thrainer
      if not disks and self.op.disk_template != constants.DT_DISKLESS:
746 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No disk info specified and the export"
747 22b7f6f8 Thomas Thrainer
                                   " is missing the disk information",
748 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
749 22b7f6f8 Thomas Thrainer
750 22b7f6f8 Thomas Thrainer
    if not self.op.nics:
751 22b7f6f8 Thomas Thrainer
      nics = []
752 22b7f6f8 Thomas Thrainer
      for idx in range(constants.MAX_NICS):
753 22b7f6f8 Thomas Thrainer
        if einfo.has_option(constants.INISECT_INS, "nic%d_mac" % idx):
754 22b7f6f8 Thomas Thrainer
          ndict = {}
755 22b7f6f8 Thomas Thrainer
          for name in list(constants.NICS_PARAMETERS) + ["ip", "mac"]:
756 22b7f6f8 Thomas Thrainer
            v = einfo.get(constants.INISECT_INS, "nic%d_%s" % (idx, name))
757 22b7f6f8 Thomas Thrainer
            ndict[name] = v
758 22b7f6f8 Thomas Thrainer
          nics.append(ndict)
759 22b7f6f8 Thomas Thrainer
        else:
760 22b7f6f8 Thomas Thrainer
          break
761 22b7f6f8 Thomas Thrainer
      self.op.nics = nics
762 22b7f6f8 Thomas Thrainer
763 22b7f6f8 Thomas Thrainer
    if not self.op.tags and einfo.has_option(constants.INISECT_INS, "tags"):
764 22b7f6f8 Thomas Thrainer
      self.op.tags = einfo.get(constants.INISECT_INS, "tags").split()
765 22b7f6f8 Thomas Thrainer
766 22b7f6f8 Thomas Thrainer
    if (self.op.hypervisor is None and
767 22b7f6f8 Thomas Thrainer
        einfo.has_option(constants.INISECT_INS, "hypervisor")):
768 22b7f6f8 Thomas Thrainer
      self.op.hypervisor = einfo.get(constants.INISECT_INS, "hypervisor")
769 22b7f6f8 Thomas Thrainer
770 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_HYP):
771 22b7f6f8 Thomas Thrainer
      # use the export parameters but do not override the ones
772 22b7f6f8 Thomas Thrainer
      # specified by the user
773 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_HYP):
774 22b7f6f8 Thomas Thrainer
        if name not in self.op.hvparams:
775 22b7f6f8 Thomas Thrainer
          self.op.hvparams[name] = value
776 22b7f6f8 Thomas Thrainer
777 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_BEP):
778 22b7f6f8 Thomas Thrainer
      # use the parameters, without overriding
779 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_BEP):
780 22b7f6f8 Thomas Thrainer
        if name not in self.op.beparams:
781 22b7f6f8 Thomas Thrainer
          self.op.beparams[name] = value
782 22b7f6f8 Thomas Thrainer
        # Compatibility for the old "memory" be param
783 22b7f6f8 Thomas Thrainer
        if name == constants.BE_MEMORY:
784 22b7f6f8 Thomas Thrainer
          if constants.BE_MAXMEM not in self.op.beparams:
785 22b7f6f8 Thomas Thrainer
            self.op.beparams[constants.BE_MAXMEM] = value
786 22b7f6f8 Thomas Thrainer
          if constants.BE_MINMEM not in self.op.beparams:
787 22b7f6f8 Thomas Thrainer
            self.op.beparams[constants.BE_MINMEM] = value
788 22b7f6f8 Thomas Thrainer
    else:
789 22b7f6f8 Thomas Thrainer
      # try to read the parameters old style, from the main section
790 22b7f6f8 Thomas Thrainer
      for name in constants.BES_PARAMETERS:
791 22b7f6f8 Thomas Thrainer
        if (name not in self.op.beparams and
792 22b7f6f8 Thomas Thrainer
            einfo.has_option(constants.INISECT_INS, name)):
793 22b7f6f8 Thomas Thrainer
          self.op.beparams[name] = einfo.get(constants.INISECT_INS, name)
794 22b7f6f8 Thomas Thrainer
795 22b7f6f8 Thomas Thrainer
    if einfo.has_section(constants.INISECT_OSP):
796 22b7f6f8 Thomas Thrainer
      # use the parameters, without overriding
797 22b7f6f8 Thomas Thrainer
      for name, value in einfo.items(constants.INISECT_OSP):
798 22b7f6f8 Thomas Thrainer
        if name not in self.op.osparams:
799 22b7f6f8 Thomas Thrainer
          self.op.osparams[name] = value
800 22b7f6f8 Thomas Thrainer
801 22b7f6f8 Thomas Thrainer
  def _RevertToDefaults(self, cluster):
802 22b7f6f8 Thomas Thrainer
    """Revert the instance parameters to the default values.
803 22b7f6f8 Thomas Thrainer

804 22b7f6f8 Thomas Thrainer
    """
805 22b7f6f8 Thomas Thrainer
    # hvparams
806 22b7f6f8 Thomas Thrainer
    hv_defs = cluster.SimpleFillHV(self.op.hypervisor, self.op.os_type, {})
807 22b7f6f8 Thomas Thrainer
    for name in self.op.hvparams.keys():
808 22b7f6f8 Thomas Thrainer
      if name in hv_defs and hv_defs[name] == self.op.hvparams[name]:
809 22b7f6f8 Thomas Thrainer
        del self.op.hvparams[name]
810 22b7f6f8 Thomas Thrainer
    # beparams
811 22b7f6f8 Thomas Thrainer
    be_defs = cluster.SimpleFillBE({})
812 22b7f6f8 Thomas Thrainer
    for name in self.op.beparams.keys():
813 22b7f6f8 Thomas Thrainer
      if name in be_defs and be_defs[name] == self.op.beparams[name]:
814 22b7f6f8 Thomas Thrainer
        del self.op.beparams[name]
815 22b7f6f8 Thomas Thrainer
    # nic params
816 22b7f6f8 Thomas Thrainer
    nic_defs = cluster.SimpleFillNIC({})
817 22b7f6f8 Thomas Thrainer
    for nic in self.op.nics:
818 22b7f6f8 Thomas Thrainer
      for name in constants.NICS_PARAMETERS:
819 22b7f6f8 Thomas Thrainer
        if name in nic and name in nic_defs and nic[name] == nic_defs[name]:
820 22b7f6f8 Thomas Thrainer
          del nic[name]
821 22b7f6f8 Thomas Thrainer
    # osparams
822 22b7f6f8 Thomas Thrainer
    os_defs = cluster.SimpleFillOS(self.op.os_type, {})
823 22b7f6f8 Thomas Thrainer
    for name in self.op.osparams.keys():
824 22b7f6f8 Thomas Thrainer
      if name in os_defs and os_defs[name] == self.op.osparams[name]:
825 22b7f6f8 Thomas Thrainer
        del self.op.osparams[name]
826 22b7f6f8 Thomas Thrainer
827 22b7f6f8 Thomas Thrainer
  def _CalculateFileStorageDir(self):
828 22b7f6f8 Thomas Thrainer
    """Calculate final instance file storage dir.
829 22b7f6f8 Thomas Thrainer

830 22b7f6f8 Thomas Thrainer
    """
831 22b7f6f8 Thomas Thrainer
    # file storage dir calculation/check
832 22b7f6f8 Thomas Thrainer
    self.instance_file_storage_dir = None
833 22b7f6f8 Thomas Thrainer
    if self.op.disk_template in constants.DTS_FILEBASED:
834 22b7f6f8 Thomas Thrainer
      # build the full file storage dir path
835 22b7f6f8 Thomas Thrainer
      joinargs = []
836 22b7f6f8 Thomas Thrainer
837 22b7f6f8 Thomas Thrainer
      if self.op.disk_template == constants.DT_SHARED_FILE:
838 22b7f6f8 Thomas Thrainer
        get_fsd_fn = self.cfg.GetSharedFileStorageDir
839 22b7f6f8 Thomas Thrainer
      else:
840 22b7f6f8 Thomas Thrainer
        get_fsd_fn = self.cfg.GetFileStorageDir
841 22b7f6f8 Thomas Thrainer
842 22b7f6f8 Thomas Thrainer
      cfg_storagedir = get_fsd_fn()
843 22b7f6f8 Thomas Thrainer
      if not cfg_storagedir:
844 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cluster file storage dir not defined",
845 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
846 22b7f6f8 Thomas Thrainer
      joinargs.append(cfg_storagedir)
847 22b7f6f8 Thomas Thrainer
848 22b7f6f8 Thomas Thrainer
      if self.op.file_storage_dir is not None:
849 22b7f6f8 Thomas Thrainer
        joinargs.append(self.op.file_storage_dir)
850 22b7f6f8 Thomas Thrainer
851 22b7f6f8 Thomas Thrainer
      joinargs.append(self.op.instance_name)
852 22b7f6f8 Thomas Thrainer
853 22b7f6f8 Thomas Thrainer
      # pylint: disable=W0142
854 22b7f6f8 Thomas Thrainer
      self.instance_file_storage_dir = utils.PathJoin(*joinargs)
855 22b7f6f8 Thomas Thrainer
856 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self): # pylint: disable=R0914
857 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
858 22b7f6f8 Thomas Thrainer

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

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

1429 22b7f6f8 Thomas Thrainer
  """
1430 22b7f6f8 Thomas Thrainer
  HPATH = "instance-rename"
1431 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1432 22b7f6f8 Thomas Thrainer
1433 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
1434 22b7f6f8 Thomas Thrainer
    """Check arguments.
1435 22b7f6f8 Thomas Thrainer

1436 22b7f6f8 Thomas Thrainer
    """
1437 22b7f6f8 Thomas Thrainer
    if self.op.ip_check and not self.op.name_check:
1438 22b7f6f8 Thomas Thrainer
      # TODO: make the ip check more flexible and not depend on the name check
1439 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("IP address check requires a name check",
1440 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
1441 22b7f6f8 Thomas Thrainer
1442 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1443 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1444 22b7f6f8 Thomas Thrainer

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

1447 22b7f6f8 Thomas Thrainer
    """
1448 22b7f6f8 Thomas Thrainer
    env = _BuildInstanceHookEnvByObject(self, self.instance)
1449 22b7f6f8 Thomas Thrainer
    env["INSTANCE_NEW_NAME"] = self.op.new_name
1450 22b7f6f8 Thomas Thrainer
    return env
1451 22b7f6f8 Thomas Thrainer
1452 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1453 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1454 22b7f6f8 Thomas Thrainer

1455 22b7f6f8 Thomas Thrainer
    """
1456 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
1457 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1458 22b7f6f8 Thomas Thrainer
1459 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1460 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1461 22b7f6f8 Thomas Thrainer

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

1464 22b7f6f8 Thomas Thrainer
    """
1465 22b7f6f8 Thomas Thrainer
    self.op.instance_name = _ExpandInstanceName(self.cfg,
1466 22b7f6f8 Thomas Thrainer
                                                self.op.instance_name)
1467 22b7f6f8 Thomas Thrainer
    instance = self.cfg.GetInstanceInfo(self.op.instance_name)
1468 22b7f6f8 Thomas Thrainer
    assert instance is not None
1469 22b7f6f8 Thomas Thrainer
    _CheckNodeOnline(self, instance.primary_node)
1470 22b7f6f8 Thomas Thrainer
    _CheckInstanceState(self, instance, INSTANCE_NOT_RUNNING,
1471 22b7f6f8 Thomas Thrainer
                        msg="cannot rename")
1472 22b7f6f8 Thomas Thrainer
    self.instance = instance
1473 22b7f6f8 Thomas Thrainer
1474 22b7f6f8 Thomas Thrainer
    new_name = self.op.new_name
1475 22b7f6f8 Thomas Thrainer
    if self.op.name_check:
1476 22b7f6f8 Thomas Thrainer
      hostname = _CheckHostnameSane(self, new_name)
1477 22b7f6f8 Thomas Thrainer
      new_name = self.op.new_name = hostname.name
1478 22b7f6f8 Thomas Thrainer
      if (self.op.ip_check and
1479 22b7f6f8 Thomas Thrainer
          netutils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT)):
1480 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("IP %s of instance %s already in use" %
1481 22b7f6f8 Thomas Thrainer
                                   (hostname.ip, new_name),
1482 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_NOTUNIQUE)
1483 22b7f6f8 Thomas Thrainer
1484 22b7f6f8 Thomas Thrainer
    instance_list = self.cfg.GetInstanceList()
1485 22b7f6f8 Thomas Thrainer
    if new_name in instance_list and new_name != instance.name:
1486 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' is already in the cluster" %
1487 22b7f6f8 Thomas Thrainer
                                 new_name, errors.ECODE_EXISTS)
1488 22b7f6f8 Thomas Thrainer
1489 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1490 22b7f6f8 Thomas Thrainer
    """Rename the instance.
1491 22b7f6f8 Thomas Thrainer

1492 22b7f6f8 Thomas Thrainer
    """
1493 22b7f6f8 Thomas Thrainer
    inst = self.instance
1494 22b7f6f8 Thomas Thrainer
    old_name = inst.name
1495 22b7f6f8 Thomas Thrainer
1496 22b7f6f8 Thomas Thrainer
    rename_file_storage = False
1497 22b7f6f8 Thomas Thrainer
    if (inst.disk_template in constants.DTS_FILEBASED and
1498 22b7f6f8 Thomas Thrainer
        self.op.new_name != inst.name):
1499 22b7f6f8 Thomas Thrainer
      old_file_storage_dir = os.path.dirname(inst.disks[0].logical_id[1])
1500 22b7f6f8 Thomas Thrainer
      rename_file_storage = True
1501 22b7f6f8 Thomas Thrainer
1502 22b7f6f8 Thomas Thrainer
    self.cfg.RenameInstance(inst.name, self.op.new_name)
1503 22b7f6f8 Thomas Thrainer
    # Change the instance lock. This is definitely safe while we hold the BGL.
1504 22b7f6f8 Thomas Thrainer
    # Otherwise the new lock would have to be added in acquired mode.
1505 22b7f6f8 Thomas Thrainer
    assert self.REQ_BGL
1506 22b7f6f8 Thomas Thrainer
    assert locking.BGL in self.owned_locks(locking.LEVEL_CLUSTER)
1507 22b7f6f8 Thomas Thrainer
    self.glm.remove(locking.LEVEL_INSTANCE, old_name)
1508 22b7f6f8 Thomas Thrainer
    self.glm.add(locking.LEVEL_INSTANCE, self.op.new_name)
1509 22b7f6f8 Thomas Thrainer
1510 22b7f6f8 Thomas Thrainer
    # re-read the instance from the configuration after rename
1511 22b7f6f8 Thomas Thrainer
    inst = self.cfg.GetInstanceInfo(self.op.new_name)
1512 22b7f6f8 Thomas Thrainer
1513 22b7f6f8 Thomas Thrainer
    if rename_file_storage:
1514 22b7f6f8 Thomas Thrainer
      new_file_storage_dir = os.path.dirname(inst.disks[0].logical_id[1])
1515 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_file_storage_dir_rename(inst.primary_node,
1516 22b7f6f8 Thomas Thrainer
                                                     old_file_storage_dir,
1517 22b7f6f8 Thomas Thrainer
                                                     new_file_storage_dir)
1518 22b7f6f8 Thomas Thrainer
      result.Raise("Could not rename on node %s directory '%s' to '%s'"
1519 22b7f6f8 Thomas Thrainer
                   " (but the instance has been renamed in Ganeti)" %
1520 22b7f6f8 Thomas Thrainer
                   (inst.primary_node, old_file_storage_dir,
1521 22b7f6f8 Thomas Thrainer
                    new_file_storage_dir))
1522 22b7f6f8 Thomas Thrainer
1523 22b7f6f8 Thomas Thrainer
    _StartInstanceDisks(self, inst, None)
1524 22b7f6f8 Thomas Thrainer
    # update info on disks
1525 22b7f6f8 Thomas Thrainer
    info = _GetInstanceInfoText(inst)
1526 22b7f6f8 Thomas Thrainer
    for (idx, disk) in enumerate(inst.disks):
1527 22b7f6f8 Thomas Thrainer
      for node in inst.all_nodes:
1528 22b7f6f8 Thomas Thrainer
        self.cfg.SetDiskID(disk, node)
1529 22b7f6f8 Thomas Thrainer
        result = self.rpc.call_blockdev_setinfo(node, disk, info)
1530 22b7f6f8 Thomas Thrainer
        if result.fail_msg:
1531 22b7f6f8 Thomas Thrainer
          self.LogWarning("Error setting info on node %s for disk %s: %s",
1532 22b7f6f8 Thomas Thrainer
                          node, idx, result.fail_msg)
1533 22b7f6f8 Thomas Thrainer
    try:
1534 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_run_rename(inst.primary_node, inst,
1535 22b7f6f8 Thomas Thrainer
                                                 old_name, self.op.debug_level)
1536 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
1537 22b7f6f8 Thomas Thrainer
      if msg:
1538 22b7f6f8 Thomas Thrainer
        msg = ("Could not run OS rename script for instance %s on node %s"
1539 22b7f6f8 Thomas Thrainer
               " (but the instance has been renamed in Ganeti): %s" %
1540 22b7f6f8 Thomas Thrainer
               (inst.name, inst.primary_node, msg))
1541 22b7f6f8 Thomas Thrainer
        self.LogWarning(msg)
1542 22b7f6f8 Thomas Thrainer
    finally:
1543 22b7f6f8 Thomas Thrainer
      _ShutdownInstanceDisks(self, inst)
1544 22b7f6f8 Thomas Thrainer
1545 22b7f6f8 Thomas Thrainer
    return inst.name
1546 22b7f6f8 Thomas Thrainer
1547 22b7f6f8 Thomas Thrainer
1548 22b7f6f8 Thomas Thrainer
class LUInstanceRemove(LogicalUnit):
1549 22b7f6f8 Thomas Thrainer
  """Remove an instance.
1550 22b7f6f8 Thomas Thrainer

1551 22b7f6f8 Thomas Thrainer
  """
1552 22b7f6f8 Thomas Thrainer
  HPATH = "instance-remove"
1553 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1554 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1555 22b7f6f8 Thomas Thrainer
1556 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1557 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1558 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
1559 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1560 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
1561 22b7f6f8 Thomas Thrainer
1562 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1563 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1564 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
1565 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1566 22b7f6f8 Thomas Thrainer
      # Copy node locks
1567 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1568 22b7f6f8 Thomas Thrainer
        _CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1569 22b7f6f8 Thomas Thrainer
1570 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1571 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1572 22b7f6f8 Thomas Thrainer

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

1575 22b7f6f8 Thomas Thrainer
    """
1576 22b7f6f8 Thomas Thrainer
    env = _BuildInstanceHookEnvByObject(self, self.instance)
1577 22b7f6f8 Thomas Thrainer
    env["SHUTDOWN_TIMEOUT"] = self.op.shutdown_timeout
1578 22b7f6f8 Thomas Thrainer
    return env
1579 22b7f6f8 Thomas Thrainer
1580 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1581 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1582 22b7f6f8 Thomas Thrainer

1583 22b7f6f8 Thomas Thrainer
    """
1584 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()]
1585 22b7f6f8 Thomas Thrainer
    nl_post = list(self.instance.all_nodes) + nl
1586 22b7f6f8 Thomas Thrainer
    return (nl, nl_post)
1587 22b7f6f8 Thomas Thrainer
1588 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1589 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1590 22b7f6f8 Thomas Thrainer

1591 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1592 22b7f6f8 Thomas Thrainer

1593 22b7f6f8 Thomas Thrainer
    """
1594 22b7f6f8 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
1595 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1596 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1597 22b7f6f8 Thomas Thrainer
1598 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1599 22b7f6f8 Thomas Thrainer
    """Remove the instance.
1600 22b7f6f8 Thomas Thrainer

1601 22b7f6f8 Thomas Thrainer
    """
1602 22b7f6f8 Thomas Thrainer
    instance = self.instance
1603 22b7f6f8 Thomas Thrainer
    logging.info("Shutting down instance %s on node %s",
1604 22b7f6f8 Thomas Thrainer
                 instance.name, instance.primary_node)
1605 22b7f6f8 Thomas Thrainer
1606 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_instance_shutdown(instance.primary_node, instance,
1607 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1608 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1609 22b7f6f8 Thomas Thrainer
    msg = result.fail_msg
1610 22b7f6f8 Thomas Thrainer
    if msg:
1611 22b7f6f8 Thomas Thrainer
      if self.op.ignore_failures:
1612 22b7f6f8 Thomas Thrainer
        feedback_fn("Warning: can't shutdown instance: %s" % msg)
1613 22b7f6f8 Thomas Thrainer
      else:
1614 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not shutdown instance %s on"
1615 22b7f6f8 Thomas Thrainer
                                 " node %s: %s" %
1616 22b7f6f8 Thomas Thrainer
                                 (instance.name, instance.primary_node, msg))
1617 22b7f6f8 Thomas Thrainer
1618 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1619 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1620 22b7f6f8 Thomas Thrainer
    assert not (set(instance.all_nodes) -
1621 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
1622 22b7f6f8 Thomas Thrainer
      "Not owning correct locks"
1623 22b7f6f8 Thomas Thrainer
1624 22b7f6f8 Thomas Thrainer
    _RemoveInstance(self, feedback_fn, instance, self.op.ignore_failures)
1625 22b7f6f8 Thomas Thrainer
1626 22b7f6f8 Thomas Thrainer
1627 22b7f6f8 Thomas Thrainer
class LUInstanceMove(LogicalUnit):
1628 22b7f6f8 Thomas Thrainer
  """Move an instance by data-copying.
1629 22b7f6f8 Thomas Thrainer

1630 22b7f6f8 Thomas Thrainer
  """
1631 22b7f6f8 Thomas Thrainer
  HPATH = "instance-move"
1632 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
1633 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
1634 22b7f6f8 Thomas Thrainer
1635 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
1636 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
1637 22b7f6f8 Thomas Thrainer
    target_node = _ExpandNodeName(self.cfg, self.op.target_node)
1638 22b7f6f8 Thomas Thrainer
    self.op.target_node = target_node
1639 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = [target_node]
1640 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
1641 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
1642 22b7f6f8 Thomas Thrainer
1643 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
1644 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE:
1645 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes(primary_only=True)
1646 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES:
1647 22b7f6f8 Thomas Thrainer
      # Copy node locks
1648 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
1649 22b7f6f8 Thomas Thrainer
        _CopyLockList(self.needed_locks[locking.LEVEL_NODE])
1650 22b7f6f8 Thomas Thrainer
1651 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
1652 22b7f6f8 Thomas Thrainer
    """Build hooks env.
1653 22b7f6f8 Thomas Thrainer

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

1656 22b7f6f8 Thomas Thrainer
    """
1657 22b7f6f8 Thomas Thrainer
    env = {
1658 22b7f6f8 Thomas Thrainer
      "TARGET_NODE": self.op.target_node,
1659 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
1660 22b7f6f8 Thomas Thrainer
      }
1661 22b7f6f8 Thomas Thrainer
    env.update(_BuildInstanceHookEnvByObject(self, self.instance))
1662 22b7f6f8 Thomas Thrainer
    return env
1663 22b7f6f8 Thomas Thrainer
1664 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
1665 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
1666 22b7f6f8 Thomas Thrainer

1667 22b7f6f8 Thomas Thrainer
    """
1668 22b7f6f8 Thomas Thrainer
    nl = [
1669 22b7f6f8 Thomas Thrainer
      self.cfg.GetMasterNode(),
1670 22b7f6f8 Thomas Thrainer
      self.instance.primary_node,
1671 22b7f6f8 Thomas Thrainer
      self.op.target_node,
1672 22b7f6f8 Thomas Thrainer
      ]
1673 22b7f6f8 Thomas Thrainer
    return (nl, nl)
1674 22b7f6f8 Thomas Thrainer
1675 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
1676 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
1677 22b7f6f8 Thomas Thrainer

1678 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
1679 22b7f6f8 Thomas Thrainer

1680 22b7f6f8 Thomas Thrainer
    """
1681 22b7f6f8 Thomas Thrainer
    self.instance = instance = self.cfg.GetInstanceInfo(self.op.instance_name)
1682 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
1683 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
1684 22b7f6f8 Thomas Thrainer
1685 22b7f6f8 Thomas Thrainer
    if instance.disk_template not in constants.DTS_COPYABLE:
1686 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk template %s not suitable for copying" %
1687 22b7f6f8 Thomas Thrainer
                                 instance.disk_template, errors.ECODE_STATE)
1688 22b7f6f8 Thomas Thrainer
1689 22b7f6f8 Thomas Thrainer
    node = self.cfg.GetNodeInfo(self.op.target_node)
1690 22b7f6f8 Thomas Thrainer
    assert node is not None, \
1691 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked node %s" % self.op.target_node
1692 22b7f6f8 Thomas Thrainer
1693 22b7f6f8 Thomas Thrainer
    self.target_node = target_node = node.name
1694 22b7f6f8 Thomas Thrainer
1695 22b7f6f8 Thomas Thrainer
    if target_node == instance.primary_node:
1696 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance %s is already on the node %s" %
1697 22b7f6f8 Thomas Thrainer
                                 (instance.name, target_node),
1698 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
1699 22b7f6f8 Thomas Thrainer
1700 22b7f6f8 Thomas Thrainer
    bep = self.cfg.GetClusterInfo().FillBE(instance)
1701 22b7f6f8 Thomas Thrainer
1702 22b7f6f8 Thomas Thrainer
    for idx, dsk in enumerate(instance.disks):
1703 22b7f6f8 Thomas Thrainer
      if dsk.dev_type not in (constants.LD_LV, constants.LD_FILE):
1704 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance disk %d has a complex layout,"
1705 22b7f6f8 Thomas Thrainer
                                   " cannot copy" % idx, errors.ECODE_STATE)
1706 22b7f6f8 Thomas Thrainer
1707 22b7f6f8 Thomas Thrainer
    _CheckNodeOnline(self, target_node)
1708 22b7f6f8 Thomas Thrainer
    _CheckNodeNotDrained(self, target_node)
1709 22b7f6f8 Thomas Thrainer
    _CheckNodeVmCapable(self, target_node)
1710 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
1711 22b7f6f8 Thomas Thrainer
    group_info = self.cfg.GetNodeGroup(node.group)
1712 22b7f6f8 Thomas Thrainer
    ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster, group_info)
1713 22b7f6f8 Thomas Thrainer
    _CheckTargetNodeIPolicy(self, ipolicy, instance, node, self.cfg,
1714 22b7f6f8 Thomas Thrainer
                            ignore=self.op.ignore_ipolicy)
1715 22b7f6f8 Thomas Thrainer
1716 22b7f6f8 Thomas Thrainer
    if instance.admin_state == constants.ADMINST_UP:
1717 22b7f6f8 Thomas Thrainer
      # check memory requirements on the secondary node
1718 22b7f6f8 Thomas Thrainer
      _CheckNodeFreeMemory(self, target_node,
1719 22b7f6f8 Thomas Thrainer
                           "failing over instance %s" %
1720 22b7f6f8 Thomas Thrainer
                           instance.name, bep[constants.BE_MAXMEM],
1721 22b7f6f8 Thomas Thrainer
                           instance.hypervisor)
1722 22b7f6f8 Thomas Thrainer
    else:
1723 22b7f6f8 Thomas Thrainer
      self.LogInfo("Not checking memory on the secondary node as"
1724 22b7f6f8 Thomas Thrainer
                   " instance will not be started")
1725 22b7f6f8 Thomas Thrainer
1726 22b7f6f8 Thomas Thrainer
    # check bridge existance
1727 22b7f6f8 Thomas Thrainer
    _CheckInstanceBridgesExist(self, instance, node=target_node)
1728 22b7f6f8 Thomas Thrainer
1729 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
1730 22b7f6f8 Thomas Thrainer
    """Move an instance.
1731 22b7f6f8 Thomas Thrainer

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

1735 22b7f6f8 Thomas Thrainer
    """
1736 22b7f6f8 Thomas Thrainer
    instance = self.instance
1737 22b7f6f8 Thomas Thrainer
1738 22b7f6f8 Thomas Thrainer
    source_node = instance.primary_node
1739 22b7f6f8 Thomas Thrainer
    target_node = self.target_node
1740 22b7f6f8 Thomas Thrainer
1741 22b7f6f8 Thomas Thrainer
    self.LogInfo("Shutting down instance %s on source node %s",
1742 22b7f6f8 Thomas Thrainer
                 instance.name, source_node)
1743 22b7f6f8 Thomas Thrainer
1744 22b7f6f8 Thomas Thrainer
    assert (self.owned_locks(locking.LEVEL_NODE) ==
1745 22b7f6f8 Thomas Thrainer
            self.owned_locks(locking.LEVEL_NODE_RES))
1746 22b7f6f8 Thomas Thrainer
1747 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_instance_shutdown(source_node, instance,
1748 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout,
1749 22b7f6f8 Thomas Thrainer
                                             self.op.reason)
1750 22b7f6f8 Thomas Thrainer
    msg = result.fail_msg
1751 22b7f6f8 Thomas Thrainer
    if msg:
1752 22b7f6f8 Thomas Thrainer
      if self.op.ignore_consistency:
1753 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not shutdown instance %s on node %s."
1754 22b7f6f8 Thomas Thrainer
                        " Proceeding anyway. Please make sure node"
1755 22b7f6f8 Thomas Thrainer
                        " %s is down. Error details: %s",
1756 22b7f6f8 Thomas Thrainer
                        instance.name, source_node, source_node, msg)
1757 22b7f6f8 Thomas Thrainer
      else:
1758 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not shutdown instance %s on"
1759 22b7f6f8 Thomas Thrainer
                                 " node %s: %s" %
1760 22b7f6f8 Thomas Thrainer
                                 (instance.name, source_node, msg))
1761 22b7f6f8 Thomas Thrainer
1762 22b7f6f8 Thomas Thrainer
    # create the target disks
1763 22b7f6f8 Thomas Thrainer
    try:
1764 22b7f6f8 Thomas Thrainer
      _CreateDisks(self, instance, target_node=target_node)
1765 22b7f6f8 Thomas Thrainer
    except errors.OpExecError:
1766 22b7f6f8 Thomas Thrainer
      self.LogWarning("Device creation failed")
1767 22b7f6f8 Thomas Thrainer
      self.cfg.ReleaseDRBDMinors(instance.name)
1768 22b7f6f8 Thomas Thrainer
      raise
1769 22b7f6f8 Thomas Thrainer
1770 22b7f6f8 Thomas Thrainer
    cluster_name = self.cfg.GetClusterInfo().cluster_name
1771 22b7f6f8 Thomas Thrainer
1772 22b7f6f8 Thomas Thrainer
    errs = []
1773 22b7f6f8 Thomas Thrainer
    # activate, get path, copy the data over
1774 22b7f6f8 Thomas Thrainer
    for idx, disk in enumerate(instance.disks):
1775 22b7f6f8 Thomas Thrainer
      self.LogInfo("Copying data for disk %d", idx)
1776 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_blockdev_assemble(target_node, (disk, instance),
1777 22b7f6f8 Thomas Thrainer
                                               instance.name, True, idx)
1778 22b7f6f8 Thomas Thrainer
      if result.fail_msg:
1779 22b7f6f8 Thomas Thrainer
        self.LogWarning("Can't assemble newly created disk %d: %s",
1780 22b7f6f8 Thomas Thrainer
                        idx, result.fail_msg)
1781 22b7f6f8 Thomas Thrainer
        errs.append(result.fail_msg)
1782 22b7f6f8 Thomas Thrainer
        break
1783 22b7f6f8 Thomas Thrainer
      dev_path = result.payload
1784 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_blockdev_export(source_node, (disk, instance),
1785 22b7f6f8 Thomas Thrainer
                                             target_node, dev_path,
1786 22b7f6f8 Thomas Thrainer
                                             cluster_name)
1787 22b7f6f8 Thomas Thrainer
      if result.fail_msg:
1788 22b7f6f8 Thomas Thrainer
        self.LogWarning("Can't copy data over for disk %d: %s",
1789 22b7f6f8 Thomas Thrainer
                        idx, result.fail_msg)
1790 22b7f6f8 Thomas Thrainer
        errs.append(result.fail_msg)
1791 22b7f6f8 Thomas Thrainer
        break
1792 22b7f6f8 Thomas Thrainer
1793 22b7f6f8 Thomas Thrainer
    if errs:
1794 22b7f6f8 Thomas Thrainer
      self.LogWarning("Some disks failed to copy, aborting")
1795 22b7f6f8 Thomas Thrainer
      try:
1796 22b7f6f8 Thomas Thrainer
        _RemoveDisks(self, instance, target_node=target_node)
1797 22b7f6f8 Thomas Thrainer
      finally:
1798 22b7f6f8 Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(instance.name)
1799 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Errors during disk copy: %s" %
1800 22b7f6f8 Thomas Thrainer
                                 (",".join(errs),))
1801 22b7f6f8 Thomas Thrainer
1802 22b7f6f8 Thomas Thrainer
    instance.primary_node = target_node
1803 22b7f6f8 Thomas Thrainer
    self.cfg.Update(instance, feedback_fn)
1804 22b7f6f8 Thomas Thrainer
1805 22b7f6f8 Thomas Thrainer
    self.LogInfo("Removing the disks on the original node")
1806 22b7f6f8 Thomas Thrainer
    _RemoveDisks(self, instance, target_node=source_node)
1807 22b7f6f8 Thomas Thrainer
1808 22b7f6f8 Thomas Thrainer
    # Only start the instance if it's marked as up
1809 22b7f6f8 Thomas Thrainer
    if instance.admin_state == constants.ADMINST_UP:
1810 22b7f6f8 Thomas Thrainer
      self.LogInfo("Starting instance %s on node %s",
1811 22b7f6f8 Thomas Thrainer
                   instance.name, target_node)
1812 22b7f6f8 Thomas Thrainer
1813 22b7f6f8 Thomas Thrainer
      disks_ok, _ = _AssembleInstanceDisks(self, instance,
1814 22b7f6f8 Thomas Thrainer
                                           ignore_secondaries=True)
1815 22b7f6f8 Thomas Thrainer
      if not disks_ok:
1816 22b7f6f8 Thomas Thrainer
        _ShutdownInstanceDisks(self, instance)
1817 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Can't activate the instance's disks")
1818 22b7f6f8 Thomas Thrainer
1819 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_start(target_node,
1820 22b7f6f8 Thomas Thrainer
                                            (instance, None, None), False,
1821 22b7f6f8 Thomas Thrainer
                                            self.op.reason)
1822 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
1823 22b7f6f8 Thomas Thrainer
      if msg:
1824 22b7f6f8 Thomas Thrainer
        _ShutdownInstanceDisks(self, instance)
1825 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not start instance %s on node %s: %s" %
1826 22b7f6f8 Thomas Thrainer
                                 (instance.name, target_node, msg))
1827 22b7f6f8 Thomas Thrainer
1828 22b7f6f8 Thomas Thrainer
1829 22b7f6f8 Thomas Thrainer
def _GetInstanceConsole(cluster, instance):
1830 22b7f6f8 Thomas Thrainer
  """Returns console information for an instance.
1831 22b7f6f8 Thomas Thrainer

1832 22b7f6f8 Thomas Thrainer
  @type cluster: L{objects.Cluster}
1833 22b7f6f8 Thomas Thrainer
  @type instance: L{objects.Instance}
1834 22b7f6f8 Thomas Thrainer
  @rtype: dict
1835 22b7f6f8 Thomas Thrainer

1836 22b7f6f8 Thomas Thrainer
  """
1837 22b7f6f8 Thomas Thrainer
  hyper = hypervisor.GetHypervisorClass(instance.hypervisor)
1838 22b7f6f8 Thomas Thrainer
  # beparams and hvparams are passed separately, to avoid editing the
1839 22b7f6f8 Thomas Thrainer
  # instance and then saving the defaults in the instance itself.
1840 22b7f6f8 Thomas Thrainer
  hvparams = cluster.FillHV(instance)
1841 22b7f6f8 Thomas Thrainer
  beparams = cluster.FillBE(instance)
1842 22b7f6f8 Thomas Thrainer
  console = hyper.GetInstanceConsole(instance, hvparams, beparams)
1843 22b7f6f8 Thomas Thrainer
1844 22b7f6f8 Thomas Thrainer
  assert console.instance == instance.name
1845 22b7f6f8 Thomas Thrainer
  assert console.Validate()
1846 22b7f6f8 Thomas Thrainer
1847 22b7f6f8 Thomas Thrainer
  return console.ToDict()
1848 22b7f6f8 Thomas Thrainer
1849 22b7f6f8 Thomas Thrainer
1850 22b7f6f8 Thomas Thrainer
class _InstanceQuery(_QueryBase):
1851 22b7f6f8 Thomas Thrainer
  FIELDS = query.INSTANCE_FIELDS
1852 22b7f6f8 Thomas Thrainer
1853 22b7f6f8 Thomas Thrainer
  def ExpandNames(self, lu):
1854 22b7f6f8 Thomas Thrainer
    lu.needed_locks = {}
1855 22b7f6f8 Thomas Thrainer
    lu.share_locks = _ShareAll()
1856 22b7f6f8 Thomas Thrainer
1857 22b7f6f8 Thomas Thrainer
    if self.names:
1858 22b7f6f8 Thomas Thrainer
      self.wanted = _GetWantedInstances(lu, self.names)
1859 22b7f6f8 Thomas Thrainer
    else:
1860 22b7f6f8 Thomas Thrainer
      self.wanted = locking.ALL_SET
1861 22b7f6f8 Thomas Thrainer
1862 22b7f6f8 Thomas Thrainer
    self.do_locking = (self.use_locking and
1863 22b7f6f8 Thomas Thrainer
                       query.IQ_LIVE in self.requested_data)
1864 22b7f6f8 Thomas Thrainer
    if self.do_locking:
1865 22b7f6f8 Thomas Thrainer
      lu.needed_locks[locking.LEVEL_INSTANCE] = self.wanted
1866 22b7f6f8 Thomas Thrainer
      lu.needed_locks[locking.LEVEL_NODEGROUP] = []
1867 22b7f6f8 Thomas Thrainer
      lu.needed_locks[locking.LEVEL_NODE] = []
1868 22b7f6f8 Thomas Thrainer
      lu.needed_locks[locking.LEVEL_NETWORK] = []
1869 22b7f6f8 Thomas Thrainer
      lu.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
1870 22b7f6f8 Thomas Thrainer
1871 22b7f6f8 Thomas Thrainer
    self.do_grouplocks = (self.do_locking and
1872 22b7f6f8 Thomas Thrainer
                          query.IQ_NODES in self.requested_data)
1873 22b7f6f8 Thomas Thrainer
1874 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, lu, level):
1875 22b7f6f8 Thomas Thrainer
    if self.do_locking:
1876 22b7f6f8 Thomas Thrainer
      if level == locking.LEVEL_NODEGROUP and self.do_grouplocks:
1877 22b7f6f8 Thomas Thrainer
        assert not lu.needed_locks[locking.LEVEL_NODEGROUP]
1878 22b7f6f8 Thomas Thrainer
1879 22b7f6f8 Thomas Thrainer
        # Lock all groups used by instances optimistically; this requires going
1880 22b7f6f8 Thomas Thrainer
        # via the node before it's locked, requiring verification later on
1881 22b7f6f8 Thomas Thrainer
        lu.needed_locks[locking.LEVEL_NODEGROUP] = \
1882 22b7f6f8 Thomas Thrainer
          set(group_uuid
1883 22b7f6f8 Thomas Thrainer
              for instance_name in lu.owned_locks(locking.LEVEL_INSTANCE)
1884 22b7f6f8 Thomas Thrainer
              for group_uuid in lu.cfg.GetInstanceNodeGroups(instance_name))
1885 22b7f6f8 Thomas Thrainer
      elif level == locking.LEVEL_NODE:
1886 22b7f6f8 Thomas Thrainer
        lu._LockInstancesNodes() # pylint: disable=W0212
1887 22b7f6f8 Thomas Thrainer
1888 22b7f6f8 Thomas Thrainer
      elif level == locking.LEVEL_NETWORK:
1889 22b7f6f8 Thomas Thrainer
        lu.needed_locks[locking.LEVEL_NETWORK] = \
1890 22b7f6f8 Thomas Thrainer
          frozenset(net_uuid
1891 22b7f6f8 Thomas Thrainer
                    for instance_name in lu.owned_locks(locking.LEVEL_INSTANCE)
1892 22b7f6f8 Thomas Thrainer
                    for net_uuid in lu.cfg.GetInstanceNetworks(instance_name))
1893 22b7f6f8 Thomas Thrainer
1894 22b7f6f8 Thomas Thrainer
  @staticmethod
1895 22b7f6f8 Thomas Thrainer
  def _CheckGroupLocks(lu):
1896 22b7f6f8 Thomas Thrainer
    owned_instances = frozenset(lu.owned_locks(locking.LEVEL_INSTANCE))
1897 22b7f6f8 Thomas Thrainer
    owned_groups = frozenset(lu.owned_locks(locking.LEVEL_NODEGROUP))
1898 22b7f6f8 Thomas Thrainer
1899 22b7f6f8 Thomas Thrainer
    # Check if node groups for locked instances are still correct
1900 22b7f6f8 Thomas Thrainer
    for instance_name in owned_instances:
1901 22b7f6f8 Thomas Thrainer
      _CheckInstanceNodeGroups(lu.cfg, instance_name, owned_groups)
1902 22b7f6f8 Thomas Thrainer
1903 22b7f6f8 Thomas Thrainer
  def _GetQueryData(self, lu):
1904 22b7f6f8 Thomas Thrainer
    """Computes the list of instances and their attributes.
1905 22b7f6f8 Thomas Thrainer

1906 22b7f6f8 Thomas Thrainer
    """
1907 22b7f6f8 Thomas Thrainer
    if self.do_grouplocks:
1908 22b7f6f8 Thomas Thrainer
      self._CheckGroupLocks(lu)
1909 22b7f6f8 Thomas Thrainer
1910 22b7f6f8 Thomas Thrainer
    cluster = lu.cfg.GetClusterInfo()
1911 22b7f6f8 Thomas Thrainer
    all_info = lu.cfg.GetAllInstancesInfo()
1912 22b7f6f8 Thomas Thrainer
1913 22b7f6f8 Thomas Thrainer
    instance_names = self._GetNames(lu, all_info.keys(), locking.LEVEL_INSTANCE)
1914 22b7f6f8 Thomas Thrainer
1915 22b7f6f8 Thomas Thrainer
    instance_list = [all_info[name] for name in instance_names]
1916 22b7f6f8 Thomas Thrainer
    nodes = frozenset(itertools.chain(*(inst.all_nodes
1917 22b7f6f8 Thomas Thrainer
                                        for inst in instance_list)))
1918 22b7f6f8 Thomas Thrainer
    hv_list = list(set([inst.hypervisor for inst in instance_list]))
1919 22b7f6f8 Thomas Thrainer
    bad_nodes = []
1920 22b7f6f8 Thomas Thrainer
    offline_nodes = []
1921 22b7f6f8 Thomas Thrainer
    wrongnode_inst = set()
1922 22b7f6f8 Thomas Thrainer
1923 22b7f6f8 Thomas Thrainer
    # Gather data as requested
1924 22b7f6f8 Thomas Thrainer
    if self.requested_data & set([query.IQ_LIVE, query.IQ_CONSOLE]):
1925 22b7f6f8 Thomas Thrainer
      live_data = {}
1926 22b7f6f8 Thomas Thrainer
      node_data = lu.rpc.call_all_instances_info(nodes, hv_list)
1927 22b7f6f8 Thomas Thrainer
      for name in nodes:
1928 22b7f6f8 Thomas Thrainer
        result = node_data[name]
1929 22b7f6f8 Thomas Thrainer
        if result.offline:
1930 22b7f6f8 Thomas Thrainer
          # offline nodes will be in both lists
1931 22b7f6f8 Thomas Thrainer
          assert result.fail_msg
1932 22b7f6f8 Thomas Thrainer
          offline_nodes.append(name)
1933 22b7f6f8 Thomas Thrainer
        if result.fail_msg:
1934 22b7f6f8 Thomas Thrainer
          bad_nodes.append(name)
1935 22b7f6f8 Thomas Thrainer
        elif result.payload:
1936 22b7f6f8 Thomas Thrainer
          for inst in result.payload:
1937 22b7f6f8 Thomas Thrainer
            if inst in all_info:
1938 22b7f6f8 Thomas Thrainer
              if all_info[inst].primary_node == name:
1939 22b7f6f8 Thomas Thrainer
                live_data.update(result.payload)
1940 22b7f6f8 Thomas Thrainer
              else:
1941 22b7f6f8 Thomas Thrainer
                wrongnode_inst.add(inst)
1942 22b7f6f8 Thomas Thrainer
            else:
1943 22b7f6f8 Thomas Thrainer
              # orphan instance; we don't list it here as we don't
1944 22b7f6f8 Thomas Thrainer
              # handle this case yet in the output of instance listing
1945 22b7f6f8 Thomas Thrainer
              logging.warning("Orphan instance '%s' found on node %s",
1946 22b7f6f8 Thomas Thrainer
                              inst, name)
1947 22b7f6f8 Thomas Thrainer
              # else no instance is alive
1948 22b7f6f8 Thomas Thrainer
    else:
1949 22b7f6f8 Thomas Thrainer
      live_data = {}
1950 22b7f6f8 Thomas Thrainer
1951 22b7f6f8 Thomas Thrainer
    if query.IQ_DISKUSAGE in self.requested_data:
1952 22b7f6f8 Thomas Thrainer
      gmi = ganeti.masterd.instance
1953 22b7f6f8 Thomas Thrainer
      disk_usage = dict((inst.name,
1954 22b7f6f8 Thomas Thrainer
                         gmi.ComputeDiskSize(inst.disk_template,
1955 22b7f6f8 Thomas Thrainer
                                             [{constants.IDISK_SIZE: disk.size}
1956 22b7f6f8 Thomas Thrainer
                                              for disk in inst.disks]))
1957 22b7f6f8 Thomas Thrainer
                        for inst in instance_list)
1958 22b7f6f8 Thomas Thrainer
    else:
1959 22b7f6f8 Thomas Thrainer
      disk_usage = None
1960 22b7f6f8 Thomas Thrainer
1961 22b7f6f8 Thomas Thrainer
    if query.IQ_CONSOLE in self.requested_data:
1962 22b7f6f8 Thomas Thrainer
      consinfo = {}
1963 22b7f6f8 Thomas Thrainer
      for inst in instance_list:
1964 22b7f6f8 Thomas Thrainer
        if inst.name in live_data:
1965 22b7f6f8 Thomas Thrainer
          # Instance is running
1966 22b7f6f8 Thomas Thrainer
          consinfo[inst.name] = _GetInstanceConsole(cluster, inst)
1967 22b7f6f8 Thomas Thrainer
        else:
1968 22b7f6f8 Thomas Thrainer
          consinfo[inst.name] = None
1969 22b7f6f8 Thomas Thrainer
      assert set(consinfo.keys()) == set(instance_names)
1970 22b7f6f8 Thomas Thrainer
    else:
1971 22b7f6f8 Thomas Thrainer
      consinfo = None
1972 22b7f6f8 Thomas Thrainer
1973 22b7f6f8 Thomas Thrainer
    if query.IQ_NODES in self.requested_data:
1974 22b7f6f8 Thomas Thrainer
      node_names = set(itertools.chain(*map(operator.attrgetter("all_nodes"),
1975 22b7f6f8 Thomas Thrainer
                                            instance_list)))
1976 22b7f6f8 Thomas Thrainer
      nodes = dict(lu.cfg.GetMultiNodeInfo(node_names))
1977 22b7f6f8 Thomas Thrainer
      groups = dict((uuid, lu.cfg.GetNodeGroup(uuid))
1978 22b7f6f8 Thomas Thrainer
                    for uuid in set(map(operator.attrgetter("group"),
1979 22b7f6f8 Thomas Thrainer
                                        nodes.values())))
1980 22b7f6f8 Thomas Thrainer
    else:
1981 22b7f6f8 Thomas Thrainer
      nodes = None
1982 22b7f6f8 Thomas Thrainer
      groups = None
1983 22b7f6f8 Thomas Thrainer
1984 22b7f6f8 Thomas Thrainer
    if query.IQ_NETWORKS in self.requested_data:
1985 22b7f6f8 Thomas Thrainer
      net_uuids = itertools.chain(*(lu.cfg.GetInstanceNetworks(i.name)
1986 22b7f6f8 Thomas Thrainer
                                    for i in instance_list))
1987 22b7f6f8 Thomas Thrainer
      networks = dict((uuid, lu.cfg.GetNetwork(uuid)) for uuid in net_uuids)
1988 22b7f6f8 Thomas Thrainer
    else:
1989 22b7f6f8 Thomas Thrainer
      networks = None
1990 22b7f6f8 Thomas Thrainer
1991 22b7f6f8 Thomas Thrainer
    return query.InstanceQueryData(instance_list, lu.cfg.GetClusterInfo(),
1992 22b7f6f8 Thomas Thrainer
                                   disk_usage, offline_nodes, bad_nodes,
1993 22b7f6f8 Thomas Thrainer
                                   live_data, wrongnode_inst, consinfo,
1994 22b7f6f8 Thomas Thrainer
                                   nodes, groups, networks)
1995 22b7f6f8 Thomas Thrainer
1996 22b7f6f8 Thomas Thrainer
1997 22b7f6f8 Thomas Thrainer
class LUInstanceQuery(NoHooksLU):
1998 22b7f6f8 Thomas Thrainer
  """Logical unit for querying instances.
1999 22b7f6f8 Thomas Thrainer

2000 22b7f6f8 Thomas Thrainer
  """
2001 22b7f6f8 Thomas Thrainer
  # pylint: disable=W0142
2002 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2003 22b7f6f8 Thomas Thrainer
2004 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
2005 22b7f6f8 Thomas Thrainer
    self.iq = _InstanceQuery(qlang.MakeSimpleFilter("name", self.op.names),
2006 22b7f6f8 Thomas Thrainer
                             self.op.output_fields, self.op.use_locking)
2007 22b7f6f8 Thomas Thrainer
2008 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2009 22b7f6f8 Thomas Thrainer
    self.iq.ExpandNames(self)
2010 22b7f6f8 Thomas Thrainer
2011 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
2012 22b7f6f8 Thomas Thrainer
    self.iq.DeclareLocks(self, level)
2013 22b7f6f8 Thomas Thrainer
2014 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2015 22b7f6f8 Thomas Thrainer
    return self.iq.OldStyleQuery(self)
2016 22b7f6f8 Thomas Thrainer
2017 22b7f6f8 Thomas Thrainer
2018 22b7f6f8 Thomas Thrainer
class LUInstanceQueryData(NoHooksLU):
2019 22b7f6f8 Thomas Thrainer
  """Query runtime instance data.
2020 22b7f6f8 Thomas Thrainer

2021 22b7f6f8 Thomas Thrainer
  """
2022 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2023 22b7f6f8 Thomas Thrainer
2024 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2025 22b7f6f8 Thomas Thrainer
    self.needed_locks = {}
2026 22b7f6f8 Thomas Thrainer
2027 22b7f6f8 Thomas Thrainer
    # Use locking if requested or when non-static information is wanted
2028 22b7f6f8 Thomas Thrainer
    if not (self.op.static or self.op.use_locking):
2029 22b7f6f8 Thomas Thrainer
      self.LogWarning("Non-static data requested, locks need to be acquired")
2030 22b7f6f8 Thomas Thrainer
      self.op.use_locking = True
2031 22b7f6f8 Thomas Thrainer
2032 22b7f6f8 Thomas Thrainer
    if self.op.instances or not self.op.use_locking:
2033 22b7f6f8 Thomas Thrainer
      # Expand instance names right here
2034 22b7f6f8 Thomas Thrainer
      self.wanted_names = _GetWantedInstances(self, self.op.instances)
2035 22b7f6f8 Thomas Thrainer
    else:
2036 22b7f6f8 Thomas Thrainer
      # Will use acquired locks
2037 22b7f6f8 Thomas Thrainer
      self.wanted_names = None
2038 22b7f6f8 Thomas Thrainer
2039 22b7f6f8 Thomas Thrainer
    if self.op.use_locking:
2040 22b7f6f8 Thomas Thrainer
      self.share_locks = _ShareAll()
2041 22b7f6f8 Thomas Thrainer
2042 22b7f6f8 Thomas Thrainer
      if self.wanted_names is None:
2043 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_INSTANCE] = locking.ALL_SET
2044 22b7f6f8 Thomas Thrainer
      else:
2045 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_INSTANCE] = self.wanted_names
2046 22b7f6f8 Thomas Thrainer
2047 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = []
2048 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = []
2049 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NETWORK] = []
2050 22b7f6f8 Thomas Thrainer
      self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
2051 22b7f6f8 Thomas Thrainer
2052 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
2053 22b7f6f8 Thomas Thrainer
    if self.op.use_locking:
2054 22b7f6f8 Thomas Thrainer
      owned_instances = self.owned_locks(locking.LEVEL_INSTANCE)
2055 22b7f6f8 Thomas Thrainer
      if level == locking.LEVEL_NODEGROUP:
2056 22b7f6f8 Thomas Thrainer
2057 22b7f6f8 Thomas Thrainer
        # Lock all groups used by instances optimistically; this requires going
2058 22b7f6f8 Thomas Thrainer
        # via the node before it's locked, requiring verification later on
2059 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODEGROUP] = \
2060 22b7f6f8 Thomas Thrainer
          frozenset(group_uuid
2061 22b7f6f8 Thomas Thrainer
                    for instance_name in owned_instances
2062 22b7f6f8 Thomas Thrainer
                    for group_uuid in
2063 22b7f6f8 Thomas Thrainer
                    self.cfg.GetInstanceNodeGroups(instance_name))
2064 22b7f6f8 Thomas Thrainer
2065 22b7f6f8 Thomas Thrainer
      elif level == locking.LEVEL_NODE:
2066 22b7f6f8 Thomas Thrainer
        self._LockInstancesNodes()
2067 22b7f6f8 Thomas Thrainer
2068 22b7f6f8 Thomas Thrainer
      elif level == locking.LEVEL_NETWORK:
2069 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NETWORK] = \
2070 22b7f6f8 Thomas Thrainer
          frozenset(net_uuid
2071 22b7f6f8 Thomas Thrainer
                    for instance_name in owned_instances
2072 22b7f6f8 Thomas Thrainer
                    for net_uuid in
2073 22b7f6f8 Thomas Thrainer
                    self.cfg.GetInstanceNetworks(instance_name))
2074 22b7f6f8 Thomas Thrainer
2075 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2076 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
2077 22b7f6f8 Thomas Thrainer

2078 22b7f6f8 Thomas Thrainer
    This only checks the optional instance list against the existing names.
2079 22b7f6f8 Thomas Thrainer

2080 22b7f6f8 Thomas Thrainer
    """
2081 22b7f6f8 Thomas Thrainer
    owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
2082 22b7f6f8 Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
2083 22b7f6f8 Thomas Thrainer
    owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE))
2084 22b7f6f8 Thomas Thrainer
    owned_networks = frozenset(self.owned_locks(locking.LEVEL_NETWORK))
2085 22b7f6f8 Thomas Thrainer
2086 22b7f6f8 Thomas Thrainer
    if self.wanted_names is None:
2087 22b7f6f8 Thomas Thrainer
      assert self.op.use_locking, "Locking was not used"
2088 22b7f6f8 Thomas Thrainer
      self.wanted_names = owned_instances
2089 22b7f6f8 Thomas Thrainer
2090 22b7f6f8 Thomas Thrainer
    instances = dict(self.cfg.GetMultiInstanceInfo(self.wanted_names))
2091 22b7f6f8 Thomas Thrainer
2092 22b7f6f8 Thomas Thrainer
    if self.op.use_locking:
2093 22b7f6f8 Thomas Thrainer
      _CheckInstancesNodeGroups(self.cfg, instances, owned_groups, owned_nodes,
2094 22b7f6f8 Thomas Thrainer
                                None)
2095 22b7f6f8 Thomas Thrainer
    else:
2096 22b7f6f8 Thomas Thrainer
      assert not (owned_instances or owned_groups or
2097 22b7f6f8 Thomas Thrainer
                  owned_nodes or owned_networks)
2098 22b7f6f8 Thomas Thrainer
2099 22b7f6f8 Thomas Thrainer
    self.wanted_instances = instances.values()
2100 22b7f6f8 Thomas Thrainer
2101 22b7f6f8 Thomas Thrainer
  def _ComputeBlockdevStatus(self, node, instance, dev):
2102 22b7f6f8 Thomas Thrainer
    """Returns the status of a block device
2103 22b7f6f8 Thomas Thrainer

2104 22b7f6f8 Thomas Thrainer
    """
2105 22b7f6f8 Thomas Thrainer
    if self.op.static or not node:
2106 22b7f6f8 Thomas Thrainer
      return None
2107 22b7f6f8 Thomas Thrainer
2108 22b7f6f8 Thomas Thrainer
    self.cfg.SetDiskID(dev, node)
2109 22b7f6f8 Thomas Thrainer
2110 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_blockdev_find(node, dev)
2111 22b7f6f8 Thomas Thrainer
    if result.offline:
2112 22b7f6f8 Thomas Thrainer
      return None
2113 22b7f6f8 Thomas Thrainer
2114 22b7f6f8 Thomas Thrainer
    result.Raise("Can't compute disk status for %s" % instance.name)
2115 22b7f6f8 Thomas Thrainer
2116 22b7f6f8 Thomas Thrainer
    status = result.payload
2117 22b7f6f8 Thomas Thrainer
    if status is None:
2118 22b7f6f8 Thomas Thrainer
      return None
2119 22b7f6f8 Thomas Thrainer
2120 22b7f6f8 Thomas Thrainer
    return (status.dev_path, status.major, status.minor,
2121 22b7f6f8 Thomas Thrainer
            status.sync_percent, status.estimated_time,
2122 22b7f6f8 Thomas Thrainer
            status.is_degraded, status.ldisk_status)
2123 22b7f6f8 Thomas Thrainer
2124 22b7f6f8 Thomas Thrainer
  def _ComputeDiskStatus(self, instance, snode, dev):
2125 22b7f6f8 Thomas Thrainer
    """Compute block device status.
2126 22b7f6f8 Thomas Thrainer

2127 22b7f6f8 Thomas Thrainer
    """
2128 22b7f6f8 Thomas Thrainer
    (anno_dev,) = _AnnotateDiskParams(instance, [dev], self.cfg)
2129 22b7f6f8 Thomas Thrainer
2130 22b7f6f8 Thomas Thrainer
    return self._ComputeDiskStatusInner(instance, snode, anno_dev)
2131 22b7f6f8 Thomas Thrainer
2132 22b7f6f8 Thomas Thrainer
  def _ComputeDiskStatusInner(self, instance, snode, dev):
2133 22b7f6f8 Thomas Thrainer
    """Compute block device status.
2134 22b7f6f8 Thomas Thrainer

2135 22b7f6f8 Thomas Thrainer
    @attention: The device has to be annotated already.
2136 22b7f6f8 Thomas Thrainer

2137 22b7f6f8 Thomas Thrainer
    """
2138 22b7f6f8 Thomas Thrainer
    if dev.dev_type in constants.LDS_DRBD:
2139 22b7f6f8 Thomas Thrainer
      # we change the snode then (otherwise we use the one passed in)
2140 22b7f6f8 Thomas Thrainer
      if dev.logical_id[0] == instance.primary_node:
2141 22b7f6f8 Thomas Thrainer
        snode = dev.logical_id[1]
2142 22b7f6f8 Thomas Thrainer
      else:
2143 22b7f6f8 Thomas Thrainer
        snode = dev.logical_id[0]
2144 22b7f6f8 Thomas Thrainer
2145 22b7f6f8 Thomas Thrainer
    dev_pstatus = self._ComputeBlockdevStatus(instance.primary_node,
2146 22b7f6f8 Thomas Thrainer
                                              instance, dev)
2147 22b7f6f8 Thomas Thrainer
    dev_sstatus = self._ComputeBlockdevStatus(snode, instance, dev)
2148 22b7f6f8 Thomas Thrainer
2149 22b7f6f8 Thomas Thrainer
    if dev.children:
2150 22b7f6f8 Thomas Thrainer
      dev_children = map(compat.partial(self._ComputeDiskStatusInner,
2151 22b7f6f8 Thomas Thrainer
                                        instance, snode),
2152 22b7f6f8 Thomas Thrainer
                         dev.children)
2153 22b7f6f8 Thomas Thrainer
    else:
2154 22b7f6f8 Thomas Thrainer
      dev_children = []
2155 22b7f6f8 Thomas Thrainer
2156 22b7f6f8 Thomas Thrainer
    return {
2157 22b7f6f8 Thomas Thrainer
      "iv_name": dev.iv_name,
2158 22b7f6f8 Thomas Thrainer
      "dev_type": dev.dev_type,
2159 22b7f6f8 Thomas Thrainer
      "logical_id": dev.logical_id,
2160 22b7f6f8 Thomas Thrainer
      "physical_id": dev.physical_id,
2161 22b7f6f8 Thomas Thrainer
      "pstatus": dev_pstatus,
2162 22b7f6f8 Thomas Thrainer
      "sstatus": dev_sstatus,
2163 22b7f6f8 Thomas Thrainer
      "children": dev_children,
2164 22b7f6f8 Thomas Thrainer
      "mode": dev.mode,
2165 22b7f6f8 Thomas Thrainer
      "size": dev.size,
2166 22b7f6f8 Thomas Thrainer
      "name": dev.name,
2167 22b7f6f8 Thomas Thrainer
      "uuid": dev.uuid,
2168 22b7f6f8 Thomas Thrainer
      }
2169 22b7f6f8 Thomas Thrainer
2170 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2171 22b7f6f8 Thomas Thrainer
    """Gather and return data"""
2172 22b7f6f8 Thomas Thrainer
    result = {}
2173 22b7f6f8 Thomas Thrainer
2174 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
2175 22b7f6f8 Thomas Thrainer
2176 763ad5be Thomas Thrainer
    node_names = itertools.chain(*(i.all_nodes for i in self.wanted_instances))
2177 763ad5be Thomas Thrainer
    nodes = dict(self.cfg.GetMultiNodeInfo(node_names))
2178 22b7f6f8 Thomas Thrainer
2179 763ad5be Thomas Thrainer
    groups = dict(self.cfg.GetMultiNodeGroupInfo(node.group
2180 763ad5be Thomas Thrainer
                                                 for node in nodes.values()))
2181 22b7f6f8 Thomas Thrainer
2182 763ad5be Thomas Thrainer
    group2name_fn = lambda uuid: groups[uuid].name
2183 763ad5be Thomas Thrainer
    for instance in self.wanted_instances:
2184 763ad5be Thomas Thrainer
      pnode = nodes[instance.primary_node]
2185 22b7f6f8 Thomas Thrainer
2186 763ad5be Thomas Thrainer
      if self.op.static or pnode.offline:
2187 763ad5be Thomas Thrainer
        remote_state = None
2188 763ad5be Thomas Thrainer
        if pnode.offline:
2189 763ad5be Thomas Thrainer
          self.LogWarning("Primary node %s is marked offline, returning static"
2190 763ad5be Thomas Thrainer
                          " information only for instance %s" %
2191 763ad5be Thomas Thrainer
                          (pnode.name, instance.name))
2192 763ad5be Thomas Thrainer
      else:
2193 763ad5be Thomas Thrainer
        remote_info = self.rpc.call_instance_info(instance.primary_node,
2194 763ad5be Thomas Thrainer
                                                  instance.name,
2195 763ad5be Thomas Thrainer
                                                  instance.hypervisor)
2196 763ad5be Thomas Thrainer
        remote_info.Raise("Error checking node %s" % instance.primary_node)
2197 763ad5be Thomas Thrainer
        remote_info = remote_info.payload
2198 763ad5be Thomas Thrainer
        if remote_info and "state" in remote_info:
2199 763ad5be Thomas Thrainer
          remote_state = "up"
2200 763ad5be Thomas Thrainer
        else:
2201 763ad5be Thomas Thrainer
          if instance.admin_state == constants.ADMINST_UP:
2202 763ad5be Thomas Thrainer
            remote_state = "down"
2203 763ad5be Thomas Thrainer
          else:
2204 763ad5be Thomas Thrainer
            remote_state = instance.admin_state
2205 22b7f6f8 Thomas Thrainer
2206 763ad5be Thomas Thrainer
      disks = map(compat.partial(self._ComputeDiskStatus, instance, None),
2207 763ad5be Thomas Thrainer
                  instance.disks)
2208 22b7f6f8 Thomas Thrainer
2209 763ad5be Thomas Thrainer
      snodes_group_uuids = [nodes[snode_name].group
2210 763ad5be Thomas Thrainer
                            for snode_name in instance.secondary_nodes]
2211 22b7f6f8 Thomas Thrainer
2212 763ad5be Thomas Thrainer
      result[instance.name] = {
2213 763ad5be Thomas Thrainer
        "name": instance.name,
2214 763ad5be Thomas Thrainer
        "config_state": instance.admin_state,
2215 763ad5be Thomas Thrainer
        "run_state": remote_state,
2216 763ad5be Thomas Thrainer
        "pnode": instance.primary_node,
2217 763ad5be Thomas Thrainer
        "pnode_group_uuid": pnode.group,
2218 763ad5be Thomas Thrainer
        "pnode_group_name": group2name_fn(pnode.group),
2219 763ad5be Thomas Thrainer
        "snodes": instance.secondary_nodes,
2220 763ad5be Thomas Thrainer
        "snodes_group_uuids": snodes_group_uuids,
2221 763ad5be Thomas Thrainer
        "snodes_group_names": map(group2name_fn, snodes_group_uuids),
2222 763ad5be Thomas Thrainer
        "os": instance.os,
2223 763ad5be Thomas Thrainer
        # this happens to be the same format used for hooks
2224 763ad5be Thomas Thrainer
        "nics": _NICListToTuple(self, instance.nics),
2225 763ad5be Thomas Thrainer
        "disk_template": instance.disk_template,
2226 763ad5be Thomas Thrainer
        "disks": disks,
2227 763ad5be Thomas Thrainer
        "hypervisor": instance.hypervisor,
2228 763ad5be Thomas Thrainer
        "network_port": instance.network_port,
2229 763ad5be Thomas Thrainer
        "hv_instance": instance.hvparams,
2230 763ad5be Thomas Thrainer
        "hv_actual": cluster.FillHV(instance, skip_globals=True),
2231 763ad5be Thomas Thrainer
        "be_instance": instance.beparams,
2232 763ad5be Thomas Thrainer
        "be_actual": cluster.FillBE(instance),
2233 763ad5be Thomas Thrainer
        "os_instance": instance.osparams,
2234 763ad5be Thomas Thrainer
        "os_actual": cluster.SimpleFillOS(instance.os, instance.osparams),
2235 763ad5be Thomas Thrainer
        "serial_no": instance.serial_no,
2236 763ad5be Thomas Thrainer
        "mtime": instance.mtime,
2237 763ad5be Thomas Thrainer
        "ctime": instance.ctime,
2238 763ad5be Thomas Thrainer
        "uuid": instance.uuid,
2239 763ad5be Thomas Thrainer
        }
2240 763ad5be Thomas Thrainer
2241 763ad5be Thomas Thrainer
    return result
2242 22b7f6f8 Thomas Thrainer
2243 22b7f6f8 Thomas Thrainer
2244 22b7f6f8 Thomas Thrainer
class LUInstanceStartup(LogicalUnit):
2245 22b7f6f8 Thomas Thrainer
  """Starts an instance.
2246 22b7f6f8 Thomas Thrainer

2247 22b7f6f8 Thomas Thrainer
  """
2248 22b7f6f8 Thomas Thrainer
  HPATH = "instance-start"
2249 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2250 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2251 22b7f6f8 Thomas Thrainer
2252 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
2253 22b7f6f8 Thomas Thrainer
    # extra beparams
2254 22b7f6f8 Thomas Thrainer
    if self.op.beparams:
2255 22b7f6f8 Thomas Thrainer
      # fill the beparams dict
2256 22b7f6f8 Thomas Thrainer
      objects.UpgradeBeParams(self.op.beparams)
2257 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
2258 22b7f6f8 Thomas Thrainer
2259 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2260 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2261 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE_RES] = constants.LOCKS_REPLACE
2262 22b7f6f8 Thomas Thrainer
2263 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
2264 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODE_RES:
2265 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes(primary_only=True, level=locking.LEVEL_NODE_RES)
2266 22b7f6f8 Thomas Thrainer
2267 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
2268 22b7f6f8 Thomas Thrainer
    """Build hooks env.
2269 22b7f6f8 Thomas Thrainer

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

2272 22b7f6f8 Thomas Thrainer
    """
2273 22b7f6f8 Thomas Thrainer
    env = {
2274 22b7f6f8 Thomas Thrainer
      "FORCE": self.op.force,
2275 22b7f6f8 Thomas Thrainer
      }
2276 22b7f6f8 Thomas Thrainer
2277 22b7f6f8 Thomas Thrainer
    env.update(_BuildInstanceHookEnvByObject(self, self.instance))
2278 22b7f6f8 Thomas Thrainer
2279 22b7f6f8 Thomas Thrainer
    return env
2280 22b7f6f8 Thomas Thrainer
2281 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2282 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2283 22b7f6f8 Thomas Thrainer

2284 22b7f6f8 Thomas Thrainer
    """
2285 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
2286 22b7f6f8 Thomas Thrainer
    return (nl, nl)
2287 22b7f6f8 Thomas Thrainer
2288 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2289 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
2290 22b7f6f8 Thomas Thrainer

2291 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
2292 22b7f6f8 Thomas Thrainer

2293 22b7f6f8 Thomas Thrainer
    """
2294 22b7f6f8 Thomas Thrainer
    self.instance = instance = self.cfg.GetInstanceInfo(self.op.instance_name)
2295 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
2296 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
2297 22b7f6f8 Thomas Thrainer
2298 22b7f6f8 Thomas Thrainer
    # extra hvparams
2299 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
2300 22b7f6f8 Thomas Thrainer
      # check hypervisor parameter syntax (locally)
2301 22b7f6f8 Thomas Thrainer
      cluster = self.cfg.GetClusterInfo()
2302 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(self.op.hvparams, constants.HVS_PARAMETER_TYPES)
2303 22b7f6f8 Thomas Thrainer
      filled_hvp = cluster.FillHV(instance)
2304 22b7f6f8 Thomas Thrainer
      filled_hvp.update(self.op.hvparams)
2305 22b7f6f8 Thomas Thrainer
      hv_type = hypervisor.GetHypervisorClass(instance.hypervisor)
2306 22b7f6f8 Thomas Thrainer
      hv_type.CheckParameterSyntax(filled_hvp)
2307 22b7f6f8 Thomas Thrainer
      _CheckHVParams(self, instance.all_nodes, instance.hypervisor, filled_hvp)
2308 22b7f6f8 Thomas Thrainer
2309 22b7f6f8 Thomas Thrainer
    _CheckInstanceState(self, instance, INSTANCE_ONLINE)
2310 22b7f6f8 Thomas Thrainer
2311 22b7f6f8 Thomas Thrainer
    self.primary_offline = self.cfg.GetNodeInfo(instance.primary_node).offline
2312 22b7f6f8 Thomas Thrainer
2313 22b7f6f8 Thomas Thrainer
    if self.primary_offline and self.op.ignore_offline_nodes:
2314 22b7f6f8 Thomas Thrainer
      self.LogWarning("Ignoring offline primary node")
2315 22b7f6f8 Thomas Thrainer
2316 22b7f6f8 Thomas Thrainer
      if self.op.hvparams or self.op.beparams:
2317 22b7f6f8 Thomas Thrainer
        self.LogWarning("Overridden parameters are ignored")
2318 22b7f6f8 Thomas Thrainer
    else:
2319 22b7f6f8 Thomas Thrainer
      _CheckNodeOnline(self, instance.primary_node)
2320 22b7f6f8 Thomas Thrainer
2321 22b7f6f8 Thomas Thrainer
      bep = self.cfg.GetClusterInfo().FillBE(instance)
2322 22b7f6f8 Thomas Thrainer
      bep.update(self.op.beparams)
2323 22b7f6f8 Thomas Thrainer
2324 22b7f6f8 Thomas Thrainer
      # check bridges existence
2325 22b7f6f8 Thomas Thrainer
      _CheckInstanceBridgesExist(self, instance)
2326 22b7f6f8 Thomas Thrainer
2327 22b7f6f8 Thomas Thrainer
      remote_info = self.rpc.call_instance_info(instance.primary_node,
2328 22b7f6f8 Thomas Thrainer
                                                instance.name,
2329 22b7f6f8 Thomas Thrainer
                                                instance.hypervisor)
2330 22b7f6f8 Thomas Thrainer
      remote_info.Raise("Error checking node %s" % instance.primary_node,
2331 22b7f6f8 Thomas Thrainer
                        prereq=True, ecode=errors.ECODE_ENVIRON)
2332 22b7f6f8 Thomas Thrainer
      if not remote_info.payload: # not running already
2333 22b7f6f8 Thomas Thrainer
        _CheckNodeFreeMemory(self, instance.primary_node,
2334 22b7f6f8 Thomas Thrainer
                             "starting instance %s" % instance.name,
2335 22b7f6f8 Thomas Thrainer
                             bep[constants.BE_MINMEM], instance.hypervisor)
2336 22b7f6f8 Thomas Thrainer
2337 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2338 22b7f6f8 Thomas Thrainer
    """Start the instance.
2339 22b7f6f8 Thomas Thrainer

2340 22b7f6f8 Thomas Thrainer
    """
2341 22b7f6f8 Thomas Thrainer
    instance = self.instance
2342 22b7f6f8 Thomas Thrainer
    force = self.op.force
2343 22b7f6f8 Thomas Thrainer
    reason = self.op.reason
2344 22b7f6f8 Thomas Thrainer
2345 22b7f6f8 Thomas Thrainer
    if not self.op.no_remember:
2346 22b7f6f8 Thomas Thrainer
      self.cfg.MarkInstanceUp(instance.name)
2347 22b7f6f8 Thomas Thrainer
2348 22b7f6f8 Thomas Thrainer
    if self.primary_offline:
2349 22b7f6f8 Thomas Thrainer
      assert self.op.ignore_offline_nodes
2350 22b7f6f8 Thomas Thrainer
      self.LogInfo("Primary node offline, marked instance as started")
2351 22b7f6f8 Thomas Thrainer
    else:
2352 22b7f6f8 Thomas Thrainer
      node_current = instance.primary_node
2353 22b7f6f8 Thomas Thrainer
2354 22b7f6f8 Thomas Thrainer
      _StartInstanceDisks(self, instance, force)
2355 22b7f6f8 Thomas Thrainer
2356 22b7f6f8 Thomas Thrainer
      result = \
2357 22b7f6f8 Thomas Thrainer
        self.rpc.call_instance_start(node_current,
2358 22b7f6f8 Thomas Thrainer
                                     (instance, self.op.hvparams,
2359 22b7f6f8 Thomas Thrainer
                                      self.op.beparams),
2360 22b7f6f8 Thomas Thrainer
                                     self.op.startup_paused, reason)
2361 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
2362 22b7f6f8 Thomas Thrainer
      if msg:
2363 22b7f6f8 Thomas Thrainer
        _ShutdownInstanceDisks(self, instance)
2364 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not start instance: %s" % msg)
2365 22b7f6f8 Thomas Thrainer
2366 22b7f6f8 Thomas Thrainer
2367 22b7f6f8 Thomas Thrainer
class LUInstanceShutdown(LogicalUnit):
2368 22b7f6f8 Thomas Thrainer
  """Shutdown an instance.
2369 22b7f6f8 Thomas Thrainer

2370 22b7f6f8 Thomas Thrainer
  """
2371 22b7f6f8 Thomas Thrainer
  HPATH = "instance-stop"
2372 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2373 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2374 22b7f6f8 Thomas Thrainer
2375 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2376 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2377 22b7f6f8 Thomas Thrainer
2378 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
2379 22b7f6f8 Thomas Thrainer
    """Build hooks env.
2380 22b7f6f8 Thomas Thrainer

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

2383 22b7f6f8 Thomas Thrainer
    """
2384 22b7f6f8 Thomas Thrainer
    env = _BuildInstanceHookEnvByObject(self, self.instance)
2385 22b7f6f8 Thomas Thrainer
    env["TIMEOUT"] = self.op.timeout
2386 22b7f6f8 Thomas Thrainer
    return env
2387 22b7f6f8 Thomas Thrainer
2388 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2389 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2390 22b7f6f8 Thomas Thrainer

2391 22b7f6f8 Thomas Thrainer
    """
2392 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
2393 22b7f6f8 Thomas Thrainer
    return (nl, nl)
2394 22b7f6f8 Thomas Thrainer
2395 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2396 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
2397 22b7f6f8 Thomas Thrainer

2398 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
2399 22b7f6f8 Thomas Thrainer

2400 22b7f6f8 Thomas Thrainer
    """
2401 22b7f6f8 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
2402 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
2403 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
2404 22b7f6f8 Thomas Thrainer
2405 22b7f6f8 Thomas Thrainer
    if not self.op.force:
2406 22b7f6f8 Thomas Thrainer
      _CheckInstanceState(self, self.instance, INSTANCE_ONLINE)
2407 22b7f6f8 Thomas Thrainer
    else:
2408 22b7f6f8 Thomas Thrainer
      self.LogWarning("Ignoring offline instance check")
2409 22b7f6f8 Thomas Thrainer
2410 22b7f6f8 Thomas Thrainer
    self.primary_offline = \
2411 22b7f6f8 Thomas Thrainer
      self.cfg.GetNodeInfo(self.instance.primary_node).offline
2412 22b7f6f8 Thomas Thrainer
2413 22b7f6f8 Thomas Thrainer
    if self.primary_offline and self.op.ignore_offline_nodes:
2414 22b7f6f8 Thomas Thrainer
      self.LogWarning("Ignoring offline primary node")
2415 22b7f6f8 Thomas Thrainer
    else:
2416 22b7f6f8 Thomas Thrainer
      _CheckNodeOnline(self, self.instance.primary_node)
2417 22b7f6f8 Thomas Thrainer
2418 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2419 22b7f6f8 Thomas Thrainer
    """Shutdown the instance.
2420 22b7f6f8 Thomas Thrainer

2421 22b7f6f8 Thomas Thrainer
    """
2422 22b7f6f8 Thomas Thrainer
    instance = self.instance
2423 22b7f6f8 Thomas Thrainer
    node_current = instance.primary_node
2424 22b7f6f8 Thomas Thrainer
    timeout = self.op.timeout
2425 22b7f6f8 Thomas Thrainer
    reason = self.op.reason
2426 22b7f6f8 Thomas Thrainer
2427 22b7f6f8 Thomas Thrainer
    # If the instance is offline we shouldn't mark it as down, as that
2428 22b7f6f8 Thomas Thrainer
    # resets the offline flag.
2429 22b7f6f8 Thomas Thrainer
    if not self.op.no_remember and instance.admin_state in INSTANCE_ONLINE:
2430 22b7f6f8 Thomas Thrainer
      self.cfg.MarkInstanceDown(instance.name)
2431 22b7f6f8 Thomas Thrainer
2432 22b7f6f8 Thomas Thrainer
    if self.primary_offline:
2433 22b7f6f8 Thomas Thrainer
      assert self.op.ignore_offline_nodes
2434 22b7f6f8 Thomas Thrainer
      self.LogInfo("Primary node offline, marked instance as stopped")
2435 22b7f6f8 Thomas Thrainer
    else:
2436 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_shutdown(node_current, instance, timeout,
2437 22b7f6f8 Thomas Thrainer
                                               reason)
2438 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
2439 22b7f6f8 Thomas Thrainer
      if msg:
2440 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not shutdown instance: %s", msg)
2441 22b7f6f8 Thomas Thrainer
2442 22b7f6f8 Thomas Thrainer
      _ShutdownInstanceDisks(self, instance)
2443 22b7f6f8 Thomas Thrainer
2444 22b7f6f8 Thomas Thrainer
2445 22b7f6f8 Thomas Thrainer
class LUInstanceReinstall(LogicalUnit):
2446 22b7f6f8 Thomas Thrainer
  """Reinstall an instance.
2447 22b7f6f8 Thomas Thrainer

2448 22b7f6f8 Thomas Thrainer
  """
2449 22b7f6f8 Thomas Thrainer
  HPATH = "instance-reinstall"
2450 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2451 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2452 22b7f6f8 Thomas Thrainer
2453 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2454 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2455 22b7f6f8 Thomas Thrainer
2456 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
2457 22b7f6f8 Thomas Thrainer
    """Build hooks env.
2458 22b7f6f8 Thomas Thrainer

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

2461 22b7f6f8 Thomas Thrainer
    """
2462 22b7f6f8 Thomas Thrainer
    return _BuildInstanceHookEnvByObject(self, self.instance)
2463 22b7f6f8 Thomas Thrainer
2464 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2465 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2466 22b7f6f8 Thomas Thrainer

2467 22b7f6f8 Thomas Thrainer
    """
2468 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
2469 22b7f6f8 Thomas Thrainer
    return (nl, nl)
2470 22b7f6f8 Thomas Thrainer
2471 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2472 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
2473 22b7f6f8 Thomas Thrainer

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

2476 22b7f6f8 Thomas Thrainer
    """
2477 22b7f6f8 Thomas Thrainer
    instance = self.cfg.GetInstanceInfo(self.op.instance_name)
2478 22b7f6f8 Thomas Thrainer
    assert instance is not None, \
2479 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
2480 22b7f6f8 Thomas Thrainer
    _CheckNodeOnline(self, instance.primary_node, "Instance primary node"
2481 22b7f6f8 Thomas Thrainer
                     " offline, cannot reinstall")
2482 22b7f6f8 Thomas Thrainer
2483 22b7f6f8 Thomas Thrainer
    if instance.disk_template == constants.DT_DISKLESS:
2484 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance '%s' has no disks" %
2485 22b7f6f8 Thomas Thrainer
                                 self.op.instance_name,
2486 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2487 22b7f6f8 Thomas Thrainer
    _CheckInstanceState(self, instance, INSTANCE_DOWN, msg="cannot reinstall")
2488 22b7f6f8 Thomas Thrainer
2489 22b7f6f8 Thomas Thrainer
    if self.op.os_type is not None:
2490 22b7f6f8 Thomas Thrainer
      # OS verification
2491 22b7f6f8 Thomas Thrainer
      pnode = _ExpandNodeName(self.cfg, instance.primary_node)
2492 22b7f6f8 Thomas Thrainer
      _CheckNodeHasOS(self, pnode, self.op.os_type, self.op.force_variant)
2493 22b7f6f8 Thomas Thrainer
      instance_os = self.op.os_type
2494 22b7f6f8 Thomas Thrainer
    else:
2495 22b7f6f8 Thomas Thrainer
      instance_os = instance.os
2496 22b7f6f8 Thomas Thrainer
2497 22b7f6f8 Thomas Thrainer
    nodelist = list(instance.all_nodes)
2498 22b7f6f8 Thomas Thrainer
2499 22b7f6f8 Thomas Thrainer
    if self.op.osparams:
2500 22b7f6f8 Thomas Thrainer
      i_osdict = _GetUpdatedParams(instance.osparams, self.op.osparams)
2501 22b7f6f8 Thomas Thrainer
      _CheckOSParams(self, True, nodelist, instance_os, i_osdict)
2502 22b7f6f8 Thomas Thrainer
      self.os_inst = i_osdict # the new dict (without defaults)
2503 22b7f6f8 Thomas Thrainer
    else:
2504 22b7f6f8 Thomas Thrainer
      self.os_inst = None
2505 22b7f6f8 Thomas Thrainer
2506 22b7f6f8 Thomas Thrainer
    self.instance = instance
2507 22b7f6f8 Thomas Thrainer
2508 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2509 22b7f6f8 Thomas Thrainer
    """Reinstall the instance.
2510 22b7f6f8 Thomas Thrainer

2511 22b7f6f8 Thomas Thrainer
    """
2512 22b7f6f8 Thomas Thrainer
    inst = self.instance
2513 22b7f6f8 Thomas Thrainer
2514 22b7f6f8 Thomas Thrainer
    if self.op.os_type is not None:
2515 22b7f6f8 Thomas Thrainer
      feedback_fn("Changing OS to '%s'..." % self.op.os_type)
2516 22b7f6f8 Thomas Thrainer
      inst.os = self.op.os_type
2517 22b7f6f8 Thomas Thrainer
      # Write to configuration
2518 22b7f6f8 Thomas Thrainer
      self.cfg.Update(inst, feedback_fn)
2519 22b7f6f8 Thomas Thrainer
2520 22b7f6f8 Thomas Thrainer
    _StartInstanceDisks(self, inst, None)
2521 22b7f6f8 Thomas Thrainer
    try:
2522 22b7f6f8 Thomas Thrainer
      feedback_fn("Running the instance OS create scripts...")
2523 22b7f6f8 Thomas Thrainer
      # FIXME: pass debug option from opcode to backend
2524 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_os_add(inst.primary_node,
2525 22b7f6f8 Thomas Thrainer
                                             (inst, self.os_inst), True,
2526 22b7f6f8 Thomas Thrainer
                                             self.op.debug_level)
2527 22b7f6f8 Thomas Thrainer
      result.Raise("Could not install OS for instance %s on node %s" %
2528 22b7f6f8 Thomas Thrainer
                   (inst.name, inst.primary_node))
2529 22b7f6f8 Thomas Thrainer
    finally:
2530 22b7f6f8 Thomas Thrainer
      _ShutdownInstanceDisks(self, inst)
2531 22b7f6f8 Thomas Thrainer
2532 22b7f6f8 Thomas Thrainer
2533 22b7f6f8 Thomas Thrainer
class LUInstanceReboot(LogicalUnit):
2534 22b7f6f8 Thomas Thrainer
  """Reboot an instance.
2535 22b7f6f8 Thomas Thrainer

2536 22b7f6f8 Thomas Thrainer
  """
2537 22b7f6f8 Thomas Thrainer
  HPATH = "instance-reboot"
2538 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
2539 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2540 22b7f6f8 Thomas Thrainer
2541 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2542 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2543 22b7f6f8 Thomas Thrainer
2544 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
2545 22b7f6f8 Thomas Thrainer
    """Build hooks env.
2546 22b7f6f8 Thomas Thrainer

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

2549 22b7f6f8 Thomas Thrainer
    """
2550 22b7f6f8 Thomas Thrainer
    env = {
2551 22b7f6f8 Thomas Thrainer
      "IGNORE_SECONDARIES": self.op.ignore_secondaries,
2552 22b7f6f8 Thomas Thrainer
      "REBOOT_TYPE": self.op.reboot_type,
2553 22b7f6f8 Thomas Thrainer
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
2554 22b7f6f8 Thomas Thrainer
      }
2555 22b7f6f8 Thomas Thrainer
2556 22b7f6f8 Thomas Thrainer
    env.update(_BuildInstanceHookEnvByObject(self, self.instance))
2557 22b7f6f8 Thomas Thrainer
2558 22b7f6f8 Thomas Thrainer
    return env
2559 22b7f6f8 Thomas Thrainer
2560 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
2561 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
2562 22b7f6f8 Thomas Thrainer

2563 22b7f6f8 Thomas Thrainer
    """
2564 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
2565 22b7f6f8 Thomas Thrainer
    return (nl, nl)
2566 22b7f6f8 Thomas Thrainer
2567 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2568 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
2569 22b7f6f8 Thomas Thrainer

2570 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
2571 22b7f6f8 Thomas Thrainer

2572 22b7f6f8 Thomas Thrainer
    """
2573 22b7f6f8 Thomas Thrainer
    self.instance = instance = self.cfg.GetInstanceInfo(self.op.instance_name)
2574 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
2575 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
2576 22b7f6f8 Thomas Thrainer
    _CheckInstanceState(self, instance, INSTANCE_ONLINE)
2577 22b7f6f8 Thomas Thrainer
    _CheckNodeOnline(self, instance.primary_node)
2578 22b7f6f8 Thomas Thrainer
2579 22b7f6f8 Thomas Thrainer
    # check bridges existence
2580 22b7f6f8 Thomas Thrainer
    _CheckInstanceBridgesExist(self, instance)
2581 22b7f6f8 Thomas Thrainer
2582 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2583 22b7f6f8 Thomas Thrainer
    """Reboot the instance.
2584 22b7f6f8 Thomas Thrainer

2585 22b7f6f8 Thomas Thrainer
    """
2586 22b7f6f8 Thomas Thrainer
    instance = self.instance
2587 22b7f6f8 Thomas Thrainer
    ignore_secondaries = self.op.ignore_secondaries
2588 22b7f6f8 Thomas Thrainer
    reboot_type = self.op.reboot_type
2589 22b7f6f8 Thomas Thrainer
    reason = self.op.reason
2590 22b7f6f8 Thomas Thrainer
2591 22b7f6f8 Thomas Thrainer
    remote_info = self.rpc.call_instance_info(instance.primary_node,
2592 22b7f6f8 Thomas Thrainer
                                              instance.name,
2593 22b7f6f8 Thomas Thrainer
                                              instance.hypervisor)
2594 22b7f6f8 Thomas Thrainer
    remote_info.Raise("Error checking node %s" % instance.primary_node)
2595 22b7f6f8 Thomas Thrainer
    instance_running = bool(remote_info.payload)
2596 22b7f6f8 Thomas Thrainer
2597 22b7f6f8 Thomas Thrainer
    node_current = instance.primary_node
2598 22b7f6f8 Thomas Thrainer
2599 22b7f6f8 Thomas Thrainer
    if instance_running and reboot_type in [constants.INSTANCE_REBOOT_SOFT,
2600 22b7f6f8 Thomas Thrainer
                                            constants.INSTANCE_REBOOT_HARD]:
2601 22b7f6f8 Thomas Thrainer
      for disk in instance.disks:
2602 22b7f6f8 Thomas Thrainer
        self.cfg.SetDiskID(disk, node_current)
2603 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_reboot(node_current, instance,
2604 22b7f6f8 Thomas Thrainer
                                             reboot_type,
2605 22b7f6f8 Thomas Thrainer
                                             self.op.shutdown_timeout, reason)
2606 22b7f6f8 Thomas Thrainer
      result.Raise("Could not reboot instance")
2607 22b7f6f8 Thomas Thrainer
    else:
2608 22b7f6f8 Thomas Thrainer
      if instance_running:
2609 22b7f6f8 Thomas Thrainer
        result = self.rpc.call_instance_shutdown(node_current, instance,
2610 22b7f6f8 Thomas Thrainer
                                                 self.op.shutdown_timeout,
2611 22b7f6f8 Thomas Thrainer
                                                 reason)
2612 22b7f6f8 Thomas Thrainer
        result.Raise("Could not shutdown instance for full reboot")
2613 22b7f6f8 Thomas Thrainer
        _ShutdownInstanceDisks(self, instance)
2614 22b7f6f8 Thomas Thrainer
      else:
2615 22b7f6f8 Thomas Thrainer
        self.LogInfo("Instance %s was already stopped, starting now",
2616 22b7f6f8 Thomas Thrainer
                     instance.name)
2617 22b7f6f8 Thomas Thrainer
      _StartInstanceDisks(self, instance, ignore_secondaries)
2618 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_instance_start(node_current,
2619 22b7f6f8 Thomas Thrainer
                                            (instance, None, None), False,
2620 22b7f6f8 Thomas Thrainer
                                            reason)
2621 22b7f6f8 Thomas Thrainer
      msg = result.fail_msg
2622 22b7f6f8 Thomas Thrainer
      if msg:
2623 22b7f6f8 Thomas Thrainer
        _ShutdownInstanceDisks(self, instance)
2624 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Could not start instance for"
2625 22b7f6f8 Thomas Thrainer
                                 " full reboot: %s" % msg)
2626 22b7f6f8 Thomas Thrainer
2627 22b7f6f8 Thomas Thrainer
    self.cfg.MarkInstanceUp(instance.name)
2628 22b7f6f8 Thomas Thrainer
2629 22b7f6f8 Thomas Thrainer
2630 22b7f6f8 Thomas Thrainer
class LUInstanceConsole(NoHooksLU):
2631 22b7f6f8 Thomas Thrainer
  """Connect to an instance's console.
2632 22b7f6f8 Thomas Thrainer

2633 22b7f6f8 Thomas Thrainer
  This is somewhat special in that it returns the command line that
2634 22b7f6f8 Thomas Thrainer
  you need to run on the master node in order to connect to the
2635 22b7f6f8 Thomas Thrainer
  console.
2636 22b7f6f8 Thomas Thrainer

2637 22b7f6f8 Thomas Thrainer
  """
2638 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2639 22b7f6f8 Thomas Thrainer
2640 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2641 22b7f6f8 Thomas Thrainer
    self.share_locks = _ShareAll()
2642 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
2643 22b7f6f8 Thomas Thrainer
2644 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2645 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
2646 22b7f6f8 Thomas Thrainer

2647 22b7f6f8 Thomas Thrainer
    This checks that the instance is in the cluster.
2648 22b7f6f8 Thomas Thrainer

2649 22b7f6f8 Thomas Thrainer
    """
2650 22b7f6f8 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
2651 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
2652 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
2653 22b7f6f8 Thomas Thrainer
    _CheckNodeOnline(self, self.instance.primary_node)
2654 22b7f6f8 Thomas Thrainer
2655 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2656 22b7f6f8 Thomas Thrainer
    """Connect to the console of an instance
2657 22b7f6f8 Thomas Thrainer

2658 22b7f6f8 Thomas Thrainer
    """
2659 22b7f6f8 Thomas Thrainer
    instance = self.instance
2660 22b7f6f8 Thomas Thrainer
    node = instance.primary_node
2661 22b7f6f8 Thomas Thrainer
2662 22b7f6f8 Thomas Thrainer
    node_insts = self.rpc.call_instance_list([node],
2663 22b7f6f8 Thomas Thrainer
                                             [instance.hypervisor])[node]
2664 22b7f6f8 Thomas Thrainer
    node_insts.Raise("Can't get node information from %s" % node)
2665 22b7f6f8 Thomas Thrainer
2666 22b7f6f8 Thomas Thrainer
    if instance.name not in node_insts.payload:
2667 22b7f6f8 Thomas Thrainer
      if instance.admin_state == constants.ADMINST_UP:
2668 22b7f6f8 Thomas Thrainer
        state = constants.INSTST_ERRORDOWN
2669 22b7f6f8 Thomas Thrainer
      elif instance.admin_state == constants.ADMINST_DOWN:
2670 22b7f6f8 Thomas Thrainer
        state = constants.INSTST_ADMINDOWN
2671 22b7f6f8 Thomas Thrainer
      else:
2672 22b7f6f8 Thomas Thrainer
        state = constants.INSTST_ADMINOFFLINE
2673 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("Instance %s is not running (state %s)" %
2674 22b7f6f8 Thomas Thrainer
                               (instance.name, state))
2675 22b7f6f8 Thomas Thrainer
2676 22b7f6f8 Thomas Thrainer
    logging.debug("Connecting to console of %s on %s", instance.name, node)
2677 22b7f6f8 Thomas Thrainer
2678 22b7f6f8 Thomas Thrainer
    return _GetInstanceConsole(self.cfg.GetClusterInfo(), instance)
2679 22b7f6f8 Thomas Thrainer
2680 22b7f6f8 Thomas Thrainer
2681 22b7f6f8 Thomas Thrainer
class LUInstanceMultiAlloc(NoHooksLU):
2682 22b7f6f8 Thomas Thrainer
  """Allocates multiple instances at the same time.
2683 22b7f6f8 Thomas Thrainer

2684 22b7f6f8 Thomas Thrainer
  """
2685 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
2686 22b7f6f8 Thomas Thrainer
2687 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
2688 22b7f6f8 Thomas Thrainer
    """Check arguments.
2689 22b7f6f8 Thomas Thrainer

2690 22b7f6f8 Thomas Thrainer
    """
2691 22b7f6f8 Thomas Thrainer
    nodes = []
2692 22b7f6f8 Thomas Thrainer
    for inst in self.op.instances:
2693 22b7f6f8 Thomas Thrainer
      if inst.iallocator is not None:
2694 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("iallocator are not allowed to be set on"
2695 22b7f6f8 Thomas Thrainer
                                   " instance objects", errors.ECODE_INVAL)
2696 22b7f6f8 Thomas Thrainer
      nodes.append(bool(inst.pnode))
2697 22b7f6f8 Thomas Thrainer
      if inst.disk_template in constants.DTS_INT_MIRROR:
2698 22b7f6f8 Thomas Thrainer
        nodes.append(bool(inst.snode))
2699 22b7f6f8 Thomas Thrainer
2700 22b7f6f8 Thomas Thrainer
    has_nodes = compat.any(nodes)
2701 22b7f6f8 Thomas Thrainer
    if compat.all(nodes) ^ has_nodes:
2702 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are instance objects providing"
2703 22b7f6f8 Thomas Thrainer
                                 " pnode/snode while others do not",
2704 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
2705 22b7f6f8 Thomas Thrainer
2706 22b7f6f8 Thomas Thrainer
    if self.op.iallocator is None:
2707 22b7f6f8 Thomas Thrainer
      default_iallocator = self.cfg.GetDefaultIAllocator()
2708 22b7f6f8 Thomas Thrainer
      if default_iallocator and has_nodes:
2709 22b7f6f8 Thomas Thrainer
        self.op.iallocator = default_iallocator
2710 22b7f6f8 Thomas Thrainer
      else:
2711 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No iallocator or nodes on the instances"
2712 22b7f6f8 Thomas Thrainer
                                   " given and no cluster-wide default"
2713 22b7f6f8 Thomas Thrainer
                                   " iallocator found; please specify either"
2714 22b7f6f8 Thomas Thrainer
                                   " an iallocator or nodes on the instances"
2715 22b7f6f8 Thomas Thrainer
                                   " or set a cluster-wide default iallocator",
2716 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2717 22b7f6f8 Thomas Thrainer
2718 22b7f6f8 Thomas Thrainer
    _CheckOpportunisticLocking(self.op)
2719 22b7f6f8 Thomas Thrainer
2720 22b7f6f8 Thomas Thrainer
    dups = utils.FindDuplicates([op.instance_name for op in self.op.instances])
2721 22b7f6f8 Thomas Thrainer
    if dups:
2722 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are duplicate instance names: %s" %
2723 22b7f6f8 Thomas Thrainer
                                 utils.CommaJoin(dups), errors.ECODE_INVAL)
2724 22b7f6f8 Thomas Thrainer
2725 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
2726 22b7f6f8 Thomas Thrainer
    """Calculate the locks.
2727 22b7f6f8 Thomas Thrainer

2728 22b7f6f8 Thomas Thrainer
    """
2729 22b7f6f8 Thomas Thrainer
    self.share_locks = _ShareAll()
2730 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
2731 22b7f6f8 Thomas Thrainer
      # iallocator will select nodes and even if no iallocator is used,
2732 22b7f6f8 Thomas Thrainer
      # collisions with LUInstanceCreate should be avoided
2733 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
2734 22b7f6f8 Thomas Thrainer
      }
2735 22b7f6f8 Thomas Thrainer
2736 22b7f6f8 Thomas Thrainer
    if self.op.iallocator:
2737 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
2738 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = locking.ALL_SET
2739 22b7f6f8 Thomas Thrainer
2740 22b7f6f8 Thomas Thrainer
      if self.op.opportunistic_locking:
2741 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE] = True
2742 22b7f6f8 Thomas Thrainer
        self.opportunistic_locks[locking.LEVEL_NODE_RES] = True
2743 22b7f6f8 Thomas Thrainer
    else:
2744 22b7f6f8 Thomas Thrainer
      nodeslist = []
2745 22b7f6f8 Thomas Thrainer
      for inst in self.op.instances:
2746 22b7f6f8 Thomas Thrainer
        inst.pnode = _ExpandNodeName(self.cfg, inst.pnode)
2747 22b7f6f8 Thomas Thrainer
        nodeslist.append(inst.pnode)
2748 22b7f6f8 Thomas Thrainer
        if inst.snode is not None:
2749 22b7f6f8 Thomas Thrainer
          inst.snode = _ExpandNodeName(self.cfg, inst.snode)
2750 22b7f6f8 Thomas Thrainer
          nodeslist.append(inst.snode)
2751 22b7f6f8 Thomas Thrainer
2752 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE] = nodeslist
2753 22b7f6f8 Thomas Thrainer
      # Lock resources of instance's primary and secondary nodes (copy to
2754 22b7f6f8 Thomas Thrainer
      # prevent accidential modification)
2755 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = list(nodeslist)
2756 22b7f6f8 Thomas Thrainer
2757 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
2758 22b7f6f8 Thomas Thrainer
    """Check prerequisite.
2759 22b7f6f8 Thomas Thrainer

2760 22b7f6f8 Thomas Thrainer
    """
2761 22b7f6f8 Thomas Thrainer
    cluster = self.cfg.GetClusterInfo()
2762 22b7f6f8 Thomas Thrainer
    default_vg = self.cfg.GetVGName()
2763 22b7f6f8 Thomas Thrainer
    ec_id = self.proc.GetECId()
2764 22b7f6f8 Thomas Thrainer
2765 22b7f6f8 Thomas Thrainer
    if self.op.opportunistic_locking:
2766 22b7f6f8 Thomas Thrainer
      # Only consider nodes for which a lock is held
2767 22b7f6f8 Thomas Thrainer
      node_whitelist = list(self.owned_locks(locking.LEVEL_NODE))
2768 22b7f6f8 Thomas Thrainer
    else:
2769 22b7f6f8 Thomas Thrainer
      node_whitelist = None
2770 22b7f6f8 Thomas Thrainer
2771 22b7f6f8 Thomas Thrainer
    insts = [_CreateInstanceAllocRequest(op, _ComputeDisks(op, default_vg),
2772 22b7f6f8 Thomas Thrainer
                                         _ComputeNics(op, cluster, None,
2773 22b7f6f8 Thomas Thrainer
                                                      self.cfg, ec_id),
2774 22b7f6f8 Thomas Thrainer
                                         _ComputeFullBeParams(op, cluster),
2775 22b7f6f8 Thomas Thrainer
                                         node_whitelist)
2776 22b7f6f8 Thomas Thrainer
             for op in self.op.instances]
2777 22b7f6f8 Thomas Thrainer
2778 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqMultiInstanceAlloc(instances=insts)
2779 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
2780 22b7f6f8 Thomas Thrainer
2781 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
2782 22b7f6f8 Thomas Thrainer
2783 22b7f6f8 Thomas Thrainer
    if not ial.success:
2784 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute nodes using"
2785 22b7f6f8 Thomas Thrainer
                                 " iallocator '%s': %s" %
2786 22b7f6f8 Thomas Thrainer
                                 (self.op.iallocator, ial.info),
2787 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_NORES)
2788 22b7f6f8 Thomas Thrainer
2789 22b7f6f8 Thomas Thrainer
    self.ia_result = ial.result
2790 22b7f6f8 Thomas Thrainer
2791 22b7f6f8 Thomas Thrainer
    if self.op.dry_run:
2792 22b7f6f8 Thomas Thrainer
      self.dry_run_result = objects.FillDict(self._ConstructPartialResult(), {
2793 22b7f6f8 Thomas Thrainer
        constants.JOB_IDS_KEY: [],
2794 22b7f6f8 Thomas Thrainer
        })
2795 22b7f6f8 Thomas Thrainer
2796 22b7f6f8 Thomas Thrainer
  def _ConstructPartialResult(self):
2797 22b7f6f8 Thomas Thrainer
    """Contructs the partial result.
2798 22b7f6f8 Thomas Thrainer

2799 22b7f6f8 Thomas Thrainer
    """
2800 22b7f6f8 Thomas Thrainer
    (allocatable, failed) = self.ia_result
2801 22b7f6f8 Thomas Thrainer
    return {
2802 22b7f6f8 Thomas Thrainer
      opcodes.OpInstanceMultiAlloc.ALLOCATABLE_KEY:
2803 22b7f6f8 Thomas Thrainer
        map(compat.fst, allocatable),
2804 22b7f6f8 Thomas Thrainer
      opcodes.OpInstanceMultiAlloc.FAILED_KEY: failed,
2805 22b7f6f8 Thomas Thrainer
      }
2806 22b7f6f8 Thomas Thrainer
2807 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
2808 22b7f6f8 Thomas Thrainer
    """Executes the opcode.
2809 22b7f6f8 Thomas Thrainer

2810 22b7f6f8 Thomas Thrainer
    """
2811 22b7f6f8 Thomas Thrainer
    op2inst = dict((op.instance_name, op) for op in self.op.instances)
2812 22b7f6f8 Thomas Thrainer
    (allocatable, failed) = self.ia_result
2813 22b7f6f8 Thomas Thrainer
2814 22b7f6f8 Thomas Thrainer
    jobs = []
2815 22b7f6f8 Thomas Thrainer
    for (name, nodes) in allocatable:
2816 22b7f6f8 Thomas Thrainer
      op = op2inst.pop(name)
2817 22b7f6f8 Thomas Thrainer
2818 22b7f6f8 Thomas Thrainer
      if len(nodes) > 1:
2819 22b7f6f8 Thomas Thrainer
        (op.pnode, op.snode) = nodes
2820 22b7f6f8 Thomas Thrainer
      else:
2821 22b7f6f8 Thomas Thrainer
        (op.pnode,) = nodes
2822 22b7f6f8 Thomas Thrainer
2823 22b7f6f8 Thomas Thrainer
      jobs.append([op])
2824 22b7f6f8 Thomas Thrainer
2825 22b7f6f8 Thomas Thrainer
    missing = set(op2inst.keys()) - set(failed)
2826 22b7f6f8 Thomas Thrainer
    assert not missing, \
2827 22b7f6f8 Thomas Thrainer
      "Iallocator did return incomplete result: %s" % utils.CommaJoin(missing)
2828 22b7f6f8 Thomas Thrainer
2829 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs, **self._ConstructPartialResult())
2830 22b7f6f8 Thomas Thrainer
2831 22b7f6f8 Thomas Thrainer
2832 22b7f6f8 Thomas Thrainer
class _InstNicModPrivate:
2833 22b7f6f8 Thomas Thrainer
  """Data structure for network interface modifications.
2834 22b7f6f8 Thomas Thrainer

2835 22b7f6f8 Thomas Thrainer
  Used by L{LUInstanceSetParams}.
2836 22b7f6f8 Thomas Thrainer

2837 22b7f6f8 Thomas Thrainer
  """
2838 22b7f6f8 Thomas Thrainer
  def __init__(self):
2839 22b7f6f8 Thomas Thrainer
    self.params = None
2840 22b7f6f8 Thomas Thrainer
    self.filled = None
2841 22b7f6f8 Thomas Thrainer
2842 22b7f6f8 Thomas Thrainer
2843 22b7f6f8 Thomas Thrainer
def PrepareContainerMods(mods, private_fn):
2844 22b7f6f8 Thomas Thrainer
  """Prepares a list of container modifications by adding a private data field.
2845 22b7f6f8 Thomas Thrainer

2846 22b7f6f8 Thomas Thrainer
  @type mods: list of tuples; (operation, index, parameters)
2847 22b7f6f8 Thomas Thrainer
  @param mods: List of modifications
2848 22b7f6f8 Thomas Thrainer
  @type private_fn: callable or None
2849 22b7f6f8 Thomas Thrainer
  @param private_fn: Callable for constructing a private data field for a
2850 22b7f6f8 Thomas Thrainer
    modification
2851 22b7f6f8 Thomas Thrainer
  @rtype: list
2852 22b7f6f8 Thomas Thrainer

2853 22b7f6f8 Thomas Thrainer
  """
2854 22b7f6f8 Thomas Thrainer
  if private_fn is None:
2855 22b7f6f8 Thomas Thrainer
    fn = lambda: None
2856 22b7f6f8 Thomas Thrainer
  else:
2857 22b7f6f8 Thomas Thrainer
    fn = private_fn
2858 22b7f6f8 Thomas Thrainer
2859 22b7f6f8 Thomas Thrainer
  return [(op, idx, params, fn()) for (op, idx, params) in mods]
2860 22b7f6f8 Thomas Thrainer
2861 22b7f6f8 Thomas Thrainer
2862 22b7f6f8 Thomas Thrainer
def _CheckNodesPhysicalCPUs(lu, nodenames, requested, hypervisor_name):
2863 22b7f6f8 Thomas Thrainer
  """Checks if nodes have enough physical CPUs
2864 22b7f6f8 Thomas Thrainer

2865 22b7f6f8 Thomas Thrainer
  This function checks if all given nodes have the needed number of
2866 22b7f6f8 Thomas Thrainer
  physical CPUs. In case any node has less CPUs or we cannot get the
2867 22b7f6f8 Thomas Thrainer
  information from the node, this function raises an OpPrereqError
2868 22b7f6f8 Thomas Thrainer
  exception.
2869 22b7f6f8 Thomas Thrainer

2870 22b7f6f8 Thomas Thrainer
  @type lu: C{LogicalUnit}
2871 22b7f6f8 Thomas Thrainer
  @param lu: a logical unit from which we get configuration data
2872 22b7f6f8 Thomas Thrainer
  @type nodenames: C{list}
2873 22b7f6f8 Thomas Thrainer
  @param nodenames: the list of node names to check
2874 22b7f6f8 Thomas Thrainer
  @type requested: C{int}
2875 22b7f6f8 Thomas Thrainer
  @param requested: the minimum acceptable number of physical CPUs
2876 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node doesn't have enough CPUs,
2877 22b7f6f8 Thomas Thrainer
      or we cannot check the node
2878 22b7f6f8 Thomas Thrainer

2879 22b7f6f8 Thomas Thrainer
  """
2880 22b7f6f8 Thomas Thrainer
  nodeinfo = lu.rpc.call_node_info(nodenames, None, [hypervisor_name], None)
2881 22b7f6f8 Thomas Thrainer
  for node in nodenames:
2882 22b7f6f8 Thomas Thrainer
    info = nodeinfo[node]
2883 22b7f6f8 Thomas Thrainer
    info.Raise("Cannot get current information from node %s" % node,
2884 22b7f6f8 Thomas Thrainer
               prereq=True, ecode=errors.ECODE_ENVIRON)
2885 22b7f6f8 Thomas Thrainer
    (_, _, (hv_info, )) = info.payload
2886 22b7f6f8 Thomas Thrainer
    num_cpus = hv_info.get("cpu_total", None)
2887 22b7f6f8 Thomas Thrainer
    if not isinstance(num_cpus, int):
2888 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute the number of physical CPUs"
2889 22b7f6f8 Thomas Thrainer
                                 " on node %s, result was '%s'" %
2890 22b7f6f8 Thomas Thrainer
                                 (node, num_cpus), errors.ECODE_ENVIRON)
2891 22b7f6f8 Thomas Thrainer
    if requested > num_cpus:
2892 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Node %s has %s physical CPUs, but %s are "
2893 22b7f6f8 Thomas Thrainer
                                 "required" % (node, num_cpus, requested),
2894 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_NORES)
2895 22b7f6f8 Thomas Thrainer
2896 22b7f6f8 Thomas Thrainer
2897 22b7f6f8 Thomas Thrainer
def GetItemFromContainer(identifier, kind, container):
2898 22b7f6f8 Thomas Thrainer
  """Return the item refered by the identifier.
2899 22b7f6f8 Thomas Thrainer

2900 22b7f6f8 Thomas Thrainer
  @type identifier: string
2901 22b7f6f8 Thomas Thrainer
  @param identifier: Item index or name or UUID
2902 22b7f6f8 Thomas Thrainer
  @type kind: string
2903 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2904 22b7f6f8 Thomas Thrainer
  @type container: list
2905 22b7f6f8 Thomas Thrainer
  @param container: Container to get the item from
2906 22b7f6f8 Thomas Thrainer

2907 22b7f6f8 Thomas Thrainer
  """
2908 22b7f6f8 Thomas Thrainer
  # Index
2909 22b7f6f8 Thomas Thrainer
  try:
2910 22b7f6f8 Thomas Thrainer
    idx = int(identifier)
2911 22b7f6f8 Thomas Thrainer
    if idx == -1:
2912 22b7f6f8 Thomas Thrainer
      # Append
2913 22b7f6f8 Thomas Thrainer
      absidx = len(container) - 1
2914 22b7f6f8 Thomas Thrainer
    elif idx < 0:
2915 22b7f6f8 Thomas Thrainer
      raise IndexError("Not accepting negative indices other than -1")
2916 22b7f6f8 Thomas Thrainer
    elif idx > len(container):
2917 22b7f6f8 Thomas Thrainer
      raise IndexError("Got %s index %s, but there are only %s" %
2918 22b7f6f8 Thomas Thrainer
                       (kind, idx, len(container)))
2919 22b7f6f8 Thomas Thrainer
    else:
2920 22b7f6f8 Thomas Thrainer
      absidx = idx
2921 22b7f6f8 Thomas Thrainer
    return (absidx, container[idx])
2922 22b7f6f8 Thomas Thrainer
  except ValueError:
2923 22b7f6f8 Thomas Thrainer
    pass
2924 22b7f6f8 Thomas Thrainer
2925 22b7f6f8 Thomas Thrainer
  for idx, item in enumerate(container):
2926 22b7f6f8 Thomas Thrainer
    if item.uuid == identifier or item.name == identifier:
2927 22b7f6f8 Thomas Thrainer
      return (idx, item)
2928 22b7f6f8 Thomas Thrainer
2929 22b7f6f8 Thomas Thrainer
  raise errors.OpPrereqError("Cannot find %s with identifier %s" %
2930 22b7f6f8 Thomas Thrainer
                             (kind, identifier), errors.ECODE_NOENT)
2931 22b7f6f8 Thomas Thrainer
2932 22b7f6f8 Thomas Thrainer
2933 22b7f6f8 Thomas Thrainer
def ApplyContainerMods(kind, container, chgdesc, mods,
2934 22b7f6f8 Thomas Thrainer
                       create_fn, modify_fn, remove_fn):
2935 22b7f6f8 Thomas Thrainer
  """Applies descriptions in C{mods} to C{container}.
2936 22b7f6f8 Thomas Thrainer

2937 22b7f6f8 Thomas Thrainer
  @type kind: string
2938 22b7f6f8 Thomas Thrainer
  @param kind: One-word item description
2939 22b7f6f8 Thomas Thrainer
  @type container: list
2940 22b7f6f8 Thomas Thrainer
  @param container: Container to modify
2941 22b7f6f8 Thomas Thrainer
  @type chgdesc: None or list
2942 22b7f6f8 Thomas Thrainer
  @param chgdesc: List of applied changes
2943 22b7f6f8 Thomas Thrainer
  @type mods: list
2944 22b7f6f8 Thomas Thrainer
  @param mods: Modifications as returned by L{PrepareContainerMods}
2945 22b7f6f8 Thomas Thrainer
  @type create_fn: callable
2946 22b7f6f8 Thomas Thrainer
  @param create_fn: Callback for creating a new item (L{constants.DDM_ADD});
2947 22b7f6f8 Thomas Thrainer
    receives absolute item index, parameters and private data object as added
2948 22b7f6f8 Thomas Thrainer
    by L{PrepareContainerMods}, returns tuple containing new item and changes
2949 22b7f6f8 Thomas Thrainer
    as list
2950 22b7f6f8 Thomas Thrainer
  @type modify_fn: callable
2951 22b7f6f8 Thomas Thrainer
  @param modify_fn: Callback for modifying an existing item
2952 22b7f6f8 Thomas Thrainer
    (L{constants.DDM_MODIFY}); receives absolute item index, item, parameters
2953 22b7f6f8 Thomas Thrainer
    and private data object as added by L{PrepareContainerMods}, returns
2954 22b7f6f8 Thomas Thrainer
    changes as list
2955 22b7f6f8 Thomas Thrainer
  @type remove_fn: callable
2956 22b7f6f8 Thomas Thrainer
  @param remove_fn: Callback on removing item; receives absolute item index,
2957 22b7f6f8 Thomas Thrainer
    item and private data object as added by L{PrepareContainerMods}
2958 22b7f6f8 Thomas Thrainer

2959 22b7f6f8 Thomas Thrainer
  """
2960 22b7f6f8 Thomas Thrainer
  for (op, identifier, params, private) in mods:
2961 22b7f6f8 Thomas Thrainer
    changes = None
2962 22b7f6f8 Thomas Thrainer
2963 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
2964 22b7f6f8 Thomas Thrainer
      # Calculate where item will be added
2965 22b7f6f8 Thomas Thrainer
      # When adding an item, identifier can only be an index
2966 22b7f6f8 Thomas Thrainer
      try:
2967 22b7f6f8 Thomas Thrainer
        idx = int(identifier)
2968 22b7f6f8 Thomas Thrainer
      except ValueError:
2969 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Only possitive integer or -1 is accepted as"
2970 22b7f6f8 Thomas Thrainer
                                   " identifier for %s" % constants.DDM_ADD,
2971 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
2972 22b7f6f8 Thomas Thrainer
      if idx == -1:
2973 22b7f6f8 Thomas Thrainer
        addidx = len(container)
2974 22b7f6f8 Thomas Thrainer
      else:
2975 22b7f6f8 Thomas Thrainer
        if idx < 0:
2976 22b7f6f8 Thomas Thrainer
          raise IndexError("Not accepting negative indices other than -1")
2977 22b7f6f8 Thomas Thrainer
        elif idx > len(container):
2978 22b7f6f8 Thomas Thrainer
          raise IndexError("Got %s index %s, but there are only %s" %
2979 22b7f6f8 Thomas Thrainer
                           (kind, idx, len(container)))
2980 22b7f6f8 Thomas Thrainer
        addidx = idx
2981 22b7f6f8 Thomas Thrainer
2982 22b7f6f8 Thomas Thrainer
      if create_fn is None:
2983 22b7f6f8 Thomas Thrainer
        item = params
2984 22b7f6f8 Thomas Thrainer
      else:
2985 22b7f6f8 Thomas Thrainer
        (item, changes) = create_fn(addidx, params, private)
2986 22b7f6f8 Thomas Thrainer
2987 22b7f6f8 Thomas Thrainer
      if idx == -1:
2988 22b7f6f8 Thomas Thrainer
        container.append(item)
2989 22b7f6f8 Thomas Thrainer
      else:
2990 22b7f6f8 Thomas Thrainer
        assert idx >= 0
2991 22b7f6f8 Thomas Thrainer
        assert idx <= len(container)
2992 22b7f6f8 Thomas Thrainer
        # list.insert does so before the specified index
2993 22b7f6f8 Thomas Thrainer
        container.insert(idx, item)
2994 22b7f6f8 Thomas Thrainer
    else:
2995 22b7f6f8 Thomas Thrainer
      # Retrieve existing item
2996 22b7f6f8 Thomas Thrainer
      (absidx, item) = GetItemFromContainer(identifier, kind, container)
2997 22b7f6f8 Thomas Thrainer
2998 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
2999 22b7f6f8 Thomas Thrainer
        assert not params
3000 22b7f6f8 Thomas Thrainer
3001 22b7f6f8 Thomas Thrainer
        if remove_fn is not None:
3002 22b7f6f8 Thomas Thrainer
          remove_fn(absidx, item, private)
3003 22b7f6f8 Thomas Thrainer
3004 22b7f6f8 Thomas Thrainer
        changes = [("%s/%s" % (kind, absidx), "remove")]
3005 22b7f6f8 Thomas Thrainer
3006 22b7f6f8 Thomas Thrainer
        assert container[absidx] == item
3007 22b7f6f8 Thomas Thrainer
        del container[absidx]
3008 22b7f6f8 Thomas Thrainer
      elif op == constants.DDM_MODIFY:
3009 22b7f6f8 Thomas Thrainer
        if modify_fn is not None:
3010 22b7f6f8 Thomas Thrainer
          changes = modify_fn(absidx, item, params, private)
3011 22b7f6f8 Thomas Thrainer
      else:
3012 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
3013 22b7f6f8 Thomas Thrainer
3014 22b7f6f8 Thomas Thrainer
    assert _TApplyContModsCbChanges(changes)
3015 22b7f6f8 Thomas Thrainer
3016 22b7f6f8 Thomas Thrainer
    if not (chgdesc is None or changes is None):
3017 22b7f6f8 Thomas Thrainer
      chgdesc.extend(changes)
3018 22b7f6f8 Thomas Thrainer
3019 22b7f6f8 Thomas Thrainer
3020 22b7f6f8 Thomas Thrainer
def _UpdateIvNames(base_index, disks):
3021 22b7f6f8 Thomas Thrainer
  """Updates the C{iv_name} attribute of disks.
3022 22b7f6f8 Thomas Thrainer

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

3025 22b7f6f8 Thomas Thrainer
  """
3026 22b7f6f8 Thomas Thrainer
  for (idx, disk) in enumerate(disks):
3027 22b7f6f8 Thomas Thrainer
    disk.iv_name = "disk/%s" % (base_index + idx, )
3028 22b7f6f8 Thomas Thrainer
3029 22b7f6f8 Thomas Thrainer
3030 22b7f6f8 Thomas Thrainer
class LUInstanceSetParams(LogicalUnit):
3031 22b7f6f8 Thomas Thrainer
  """Modifies an instances's parameters.
3032 22b7f6f8 Thomas Thrainer

3033 22b7f6f8 Thomas Thrainer
  """
3034 22b7f6f8 Thomas Thrainer
  HPATH = "instance-modify"
3035 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
3036 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
3037 22b7f6f8 Thomas Thrainer
3038 22b7f6f8 Thomas Thrainer
  @staticmethod
3039 22b7f6f8 Thomas Thrainer
  def _UpgradeDiskNicMods(kind, mods, verify_fn):
3040 22b7f6f8 Thomas Thrainer
    assert ht.TList(mods)
3041 22b7f6f8 Thomas Thrainer
    assert not mods or len(mods[0]) in (2, 3)
3042 22b7f6f8 Thomas Thrainer
3043 22b7f6f8 Thomas Thrainer
    if mods and len(mods[0]) == 2:
3044 22b7f6f8 Thomas Thrainer
      result = []
3045 22b7f6f8 Thomas Thrainer
3046 22b7f6f8 Thomas Thrainer
      addremove = 0
3047 22b7f6f8 Thomas Thrainer
      for op, params in mods:
3048 22b7f6f8 Thomas Thrainer
        if op in (constants.DDM_ADD, constants.DDM_REMOVE):
3049 22b7f6f8 Thomas Thrainer
          result.append((op, -1, params))
3050 22b7f6f8 Thomas Thrainer
          addremove += 1
3051 22b7f6f8 Thomas Thrainer
3052 22b7f6f8 Thomas Thrainer
          if addremove > 1:
3053 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Only one %s add or remove operation is"
3054 22b7f6f8 Thomas Thrainer
                                       " supported at a time" % kind,
3055 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
3056 22b7f6f8 Thomas Thrainer
        else:
3057 22b7f6f8 Thomas Thrainer
          result.append((constants.DDM_MODIFY, op, params))
3058 22b7f6f8 Thomas Thrainer
3059 22b7f6f8 Thomas Thrainer
      assert verify_fn(result)
3060 22b7f6f8 Thomas Thrainer
    else:
3061 22b7f6f8 Thomas Thrainer
      result = mods
3062 22b7f6f8 Thomas Thrainer
3063 22b7f6f8 Thomas Thrainer
    return result
3064 22b7f6f8 Thomas Thrainer
3065 22b7f6f8 Thomas Thrainer
  @staticmethod
3066 22b7f6f8 Thomas Thrainer
  def _CheckMods(kind, mods, key_types, item_fn):
3067 22b7f6f8 Thomas Thrainer
    """Ensures requested disk/NIC modifications are valid.
3068 22b7f6f8 Thomas Thrainer

3069 22b7f6f8 Thomas Thrainer
    """
3070 22b7f6f8 Thomas Thrainer
    for (op, _, params) in mods:
3071 22b7f6f8 Thomas Thrainer
      assert ht.TDict(params)
3072 22b7f6f8 Thomas Thrainer
3073 22b7f6f8 Thomas Thrainer
      # If 'key_types' is an empty dict, we assume we have an
3074 22b7f6f8 Thomas Thrainer
      # 'ext' template and thus do not ForceDictType
3075 22b7f6f8 Thomas Thrainer
      if key_types:
3076 22b7f6f8 Thomas Thrainer
        utils.ForceDictType(params, key_types)
3077 22b7f6f8 Thomas Thrainer
3078 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_REMOVE:
3079 22b7f6f8 Thomas Thrainer
        if params:
3080 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("No settings should be passed when"
3081 22b7f6f8 Thomas Thrainer
                                     " removing a %s" % kind,
3082 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
3083 22b7f6f8 Thomas Thrainer
      elif op in (constants.DDM_ADD, constants.DDM_MODIFY):
3084 22b7f6f8 Thomas Thrainer
        item_fn(op, params)
3085 22b7f6f8 Thomas Thrainer
      else:
3086 22b7f6f8 Thomas Thrainer
        raise errors.ProgrammerError("Unhandled operation '%s'" % op)
3087 22b7f6f8 Thomas Thrainer
3088 22b7f6f8 Thomas Thrainer
  @staticmethod
3089 22b7f6f8 Thomas Thrainer
  def _VerifyDiskModification(op, params):
3090 22b7f6f8 Thomas Thrainer
    """Verifies a disk modification.
3091 22b7f6f8 Thomas Thrainer

3092 22b7f6f8 Thomas Thrainer
    """
3093 22b7f6f8 Thomas Thrainer
    if op == constants.DDM_ADD:
3094 22b7f6f8 Thomas Thrainer
      mode = params.setdefault(constants.IDISK_MODE, constants.DISK_RDWR)
3095 22b7f6f8 Thomas Thrainer
      if mode not in constants.DISK_ACCESS_SET:
3096 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk access mode '%s'" % mode,
3097 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3098 22b7f6f8 Thomas Thrainer
3099 22b7f6f8 Thomas Thrainer
      size = params.get(constants.IDISK_SIZE, None)
3100 22b7f6f8 Thomas Thrainer
      if size is None:
3101 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Required disk parameter '%s' missing" %
3102 22b7f6f8 Thomas Thrainer
                                   constants.IDISK_SIZE, errors.ECODE_INVAL)
3103 22b7f6f8 Thomas Thrainer
3104 22b7f6f8 Thomas Thrainer
      try:
3105 22b7f6f8 Thomas Thrainer
        size = int(size)
3106 22b7f6f8 Thomas Thrainer
      except (TypeError, ValueError), err:
3107 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Invalid disk size parameter: %s" % err,
3108 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3109 22b7f6f8 Thomas Thrainer
3110 22b7f6f8 Thomas Thrainer
      params[constants.IDISK_SIZE] = size
3111 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
3112 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
3113 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
3114 22b7f6f8 Thomas Thrainer
3115 22b7f6f8 Thomas Thrainer
    elif op == constants.DDM_MODIFY:
3116 22b7f6f8 Thomas Thrainer
      if constants.IDISK_SIZE in params:
3117 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk size change not possible, use"
3118 22b7f6f8 Thomas Thrainer
                                   " grow-disk", errors.ECODE_INVAL)
3119 22b7f6f8 Thomas Thrainer
      if len(params) > 2:
3120 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Disk modification doesn't support"
3121 22b7f6f8 Thomas Thrainer
                                   " additional arbitrary parameters",
3122 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3123 22b7f6f8 Thomas Thrainer
      name = params.get(constants.IDISK_NAME, None)
3124 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
3125 22b7f6f8 Thomas Thrainer
        params[constants.IDISK_NAME] = None
3126 22b7f6f8 Thomas Thrainer
3127 22b7f6f8 Thomas Thrainer
  @staticmethod
3128 22b7f6f8 Thomas Thrainer
  def _VerifyNicModification(op, params):
3129 22b7f6f8 Thomas Thrainer
    """Verifies a network interface modification.
3130 22b7f6f8 Thomas Thrainer

3131 22b7f6f8 Thomas Thrainer
    """
3132 22b7f6f8 Thomas Thrainer
    if op in (constants.DDM_ADD, constants.DDM_MODIFY):
3133 22b7f6f8 Thomas Thrainer
      ip = params.get(constants.INIC_IP, None)
3134 22b7f6f8 Thomas Thrainer
      name = params.get(constants.INIC_NAME, None)
3135 22b7f6f8 Thomas Thrainer
      req_net = params.get(constants.INIC_NETWORK, None)
3136 22b7f6f8 Thomas Thrainer
      link = params.get(constants.NIC_LINK, None)
3137 22b7f6f8 Thomas Thrainer
      mode = params.get(constants.NIC_MODE, None)
3138 22b7f6f8 Thomas Thrainer
      if name is not None and name.lower() == constants.VALUE_NONE:
3139 22b7f6f8 Thomas Thrainer
        params[constants.INIC_NAME] = None
3140 22b7f6f8 Thomas Thrainer
      if req_net is not None:
3141 22b7f6f8 Thomas Thrainer
        if req_net.lower() == constants.VALUE_NONE:
3142 22b7f6f8 Thomas Thrainer
          params[constants.INIC_NETWORK] = None
3143 22b7f6f8 Thomas Thrainer
          req_net = None
3144 22b7f6f8 Thomas Thrainer
        elif link is not None or mode is not None:
3145 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("If network is given"
3146 22b7f6f8 Thomas Thrainer
                                     " mode or link should not",
3147 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
3148 22b7f6f8 Thomas Thrainer
3149 22b7f6f8 Thomas Thrainer
      if op == constants.DDM_ADD:
3150 22b7f6f8 Thomas Thrainer
        macaddr = params.get(constants.INIC_MAC, None)
3151 22b7f6f8 Thomas Thrainer
        if macaddr is None:
3152 22b7f6f8 Thomas Thrainer
          params[constants.INIC_MAC] = constants.VALUE_AUTO
3153 22b7f6f8 Thomas Thrainer
3154 22b7f6f8 Thomas Thrainer
      if ip is not None:
3155 22b7f6f8 Thomas Thrainer
        if ip.lower() == constants.VALUE_NONE:
3156 22b7f6f8 Thomas Thrainer
          params[constants.INIC_IP] = None
3157 22b7f6f8 Thomas Thrainer
        else:
3158 22b7f6f8 Thomas Thrainer
          if ip.lower() == constants.NIC_IP_POOL:
3159 22b7f6f8 Thomas Thrainer
            if op == constants.DDM_ADD and req_net is None:
3160 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("If ip=pool, parameter network"
3161 22b7f6f8 Thomas Thrainer
                                         " cannot be none",
3162 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_INVAL)
3163 22b7f6f8 Thomas Thrainer
          else:
3164 22b7f6f8 Thomas Thrainer
            if not netutils.IPAddress.IsValid(ip):
3165 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("Invalid IP address '%s'" % ip,
3166 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_INVAL)
3167 22b7f6f8 Thomas Thrainer
3168 22b7f6f8 Thomas Thrainer
      if constants.INIC_MAC in params:
3169 22b7f6f8 Thomas Thrainer
        macaddr = params[constants.INIC_MAC]
3170 22b7f6f8 Thomas Thrainer
        if macaddr not in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
3171 22b7f6f8 Thomas Thrainer
          macaddr = utils.NormalizeAndValidateMac(macaddr)
3172 22b7f6f8 Thomas Thrainer
3173 22b7f6f8 Thomas Thrainer
        if op == constants.DDM_MODIFY and macaddr == constants.VALUE_AUTO:
3174 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("'auto' is not a valid MAC address when"
3175 22b7f6f8 Thomas Thrainer
                                     " modifying an existing NIC",
3176 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
3177 22b7f6f8 Thomas Thrainer
3178 22b7f6f8 Thomas Thrainer
  def CheckArguments(self):
3179 22b7f6f8 Thomas Thrainer
    if not (self.op.nics or self.op.disks or self.op.disk_template or
3180 22b7f6f8 Thomas Thrainer
            self.op.hvparams or self.op.beparams or self.op.os_name or
3181 22b7f6f8 Thomas Thrainer
            self.op.offline is not None or self.op.runtime_mem or
3182 22b7f6f8 Thomas Thrainer
            self.op.pnode):
3183 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("No changes submitted", errors.ECODE_INVAL)
3184 22b7f6f8 Thomas Thrainer
3185 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
3186 22b7f6f8 Thomas Thrainer
      _CheckParamsNotGlobal(self.op.hvparams, constants.HVC_GLOBALS,
3187 22b7f6f8 Thomas Thrainer
                            "hypervisor", "instance", "cluster")
3188 22b7f6f8 Thomas Thrainer
3189 22b7f6f8 Thomas Thrainer
    self.op.disks = self._UpgradeDiskNicMods(
3190 22b7f6f8 Thomas Thrainer
      "disk", self.op.disks, opcodes.OpInstanceSetParams.TestDiskModifications)
3191 22b7f6f8 Thomas Thrainer
    self.op.nics = self._UpgradeDiskNicMods(
3192 22b7f6f8 Thomas Thrainer
      "NIC", self.op.nics, opcodes.OpInstanceSetParams.TestNicModifications)
3193 22b7f6f8 Thomas Thrainer
3194 22b7f6f8 Thomas Thrainer
    if self.op.disks and self.op.disk_template is not None:
3195 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk template conversion and other disk"
3196 22b7f6f8 Thomas Thrainer
                                 " changes not supported at the same time",
3197 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3198 22b7f6f8 Thomas Thrainer
3199 22b7f6f8 Thomas Thrainer
    if (self.op.disk_template and
3200 22b7f6f8 Thomas Thrainer
        self.op.disk_template in constants.DTS_INT_MIRROR and
3201 22b7f6f8 Thomas Thrainer
        self.op.remote_node is None):
3202 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Changing the disk template to a mirrored"
3203 22b7f6f8 Thomas Thrainer
                                 " one requires specifying a secondary node",
3204 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3205 22b7f6f8 Thomas Thrainer
3206 22b7f6f8 Thomas Thrainer
    # Check NIC modifications
3207 22b7f6f8 Thomas Thrainer
    self._CheckMods("NIC", self.op.nics, constants.INIC_PARAMS_TYPES,
3208 22b7f6f8 Thomas Thrainer
                    self._VerifyNicModification)
3209 22b7f6f8 Thomas Thrainer
3210 22b7f6f8 Thomas Thrainer
    if self.op.pnode:
3211 22b7f6f8 Thomas Thrainer
      self.op.pnode = _ExpandNodeName(self.cfg, self.op.pnode)
3212 22b7f6f8 Thomas Thrainer
3213 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
3214 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
3215 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODEGROUP] = []
3216 22b7f6f8 Thomas Thrainer
    # Can't even acquire node locks in shared mode as upcoming changes in
3217 22b7f6f8 Thomas Thrainer
    # Ganeti 2.6 will start to modify the node object on disk conversion
3218 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE] = []
3219 22b7f6f8 Thomas Thrainer
    self.needed_locks[locking.LEVEL_NODE_RES] = []
3220 22b7f6f8 Thomas Thrainer
    self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
3221 22b7f6f8 Thomas Thrainer
    # Look node group to look up the ipolicy
3222 22b7f6f8 Thomas Thrainer
    self.share_locks[locking.LEVEL_NODEGROUP] = 1
3223 22b7f6f8 Thomas Thrainer
3224 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
3225 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
3226 22b7f6f8 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
3227 22b7f6f8 Thomas Thrainer
      # Acquire locks for the instance's nodegroups optimistically. Needs
3228 22b7f6f8 Thomas Thrainer
      # to be verified in CheckPrereq
3229 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = \
3230 22b7f6f8 Thomas Thrainer
        self.cfg.GetInstanceNodeGroups(self.op.instance_name)
3231 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE:
3232 22b7f6f8 Thomas Thrainer
      self._LockInstancesNodes()
3233 22b7f6f8 Thomas Thrainer
      if self.op.disk_template and self.op.remote_node:
3234 22b7f6f8 Thomas Thrainer
        self.op.remote_node = _ExpandNodeName(self.cfg, self.op.remote_node)
3235 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE].append(self.op.remote_node)
3236 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE_RES and self.op.disk_template:
3237 22b7f6f8 Thomas Thrainer
      # Copy node locks
3238 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODE_RES] = \
3239 22b7f6f8 Thomas Thrainer
        _CopyLockList(self.needed_locks[locking.LEVEL_NODE])
3240 22b7f6f8 Thomas Thrainer
3241 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
3242 22b7f6f8 Thomas Thrainer
    """Build hooks env.
3243 22b7f6f8 Thomas Thrainer

3244 22b7f6f8 Thomas Thrainer
    This runs on the master, primary and secondaries.
3245 22b7f6f8 Thomas Thrainer

3246 22b7f6f8 Thomas Thrainer
    """
3247 22b7f6f8 Thomas Thrainer
    args = {}
3248 22b7f6f8 Thomas Thrainer
    if constants.BE_MINMEM in self.be_new:
3249 22b7f6f8 Thomas Thrainer
      args["minmem"] = self.be_new[constants.BE_MINMEM]
3250 22b7f6f8 Thomas Thrainer
    if constants.BE_MAXMEM in self.be_new:
3251 22b7f6f8 Thomas Thrainer
      args["maxmem"] = self.be_new[constants.BE_MAXMEM]
3252 22b7f6f8 Thomas Thrainer
    if constants.BE_VCPUS in self.be_new:
3253 22b7f6f8 Thomas Thrainer
      args["vcpus"] = self.be_new[constants.BE_VCPUS]
3254 22b7f6f8 Thomas Thrainer
    # TODO: export disk changes. Note: _BuildInstanceHookEnv* don't export disk
3255 22b7f6f8 Thomas Thrainer
    # information at all.
3256 22b7f6f8 Thomas Thrainer
3257 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
3258 22b7f6f8 Thomas Thrainer
      nics = []
3259 22b7f6f8 Thomas Thrainer
3260 22b7f6f8 Thomas Thrainer
      for nic in self._new_nics:
3261 22b7f6f8 Thomas Thrainer
        n = copy.deepcopy(nic)
3262 22b7f6f8 Thomas Thrainer
        nicparams = self.cluster.SimpleFillNIC(n.nicparams)
3263 22b7f6f8 Thomas Thrainer
        n.nicparams = nicparams
3264 22b7f6f8 Thomas Thrainer
        nics.append(_NICToTuple(self, n))
3265 22b7f6f8 Thomas Thrainer
3266 22b7f6f8 Thomas Thrainer
      args["nics"] = nics
3267 22b7f6f8 Thomas Thrainer
3268 22b7f6f8 Thomas Thrainer
    env = _BuildInstanceHookEnvByObject(self, self.instance, override=args)
3269 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
3270 22b7f6f8 Thomas Thrainer
      env["NEW_DISK_TEMPLATE"] = self.op.disk_template
3271 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
3272 22b7f6f8 Thomas Thrainer
      env["RUNTIME_MEMORY"] = self.op.runtime_mem
3273 22b7f6f8 Thomas Thrainer
3274 22b7f6f8 Thomas Thrainer
    return env
3275 22b7f6f8 Thomas Thrainer
3276 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
3277 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
3278 22b7f6f8 Thomas Thrainer

3279 22b7f6f8 Thomas Thrainer
    """
3280 22b7f6f8 Thomas Thrainer
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
3281 22b7f6f8 Thomas Thrainer
    return (nl, nl)
3282 22b7f6f8 Thomas Thrainer
3283 22b7f6f8 Thomas Thrainer
  def _PrepareNicModification(self, params, private, old_ip, old_net_uuid,
3284 22b7f6f8 Thomas Thrainer
                              old_params, cluster, pnode):
3285 22b7f6f8 Thomas Thrainer
3286 22b7f6f8 Thomas Thrainer
    update_params_dict = dict([(key, params[key])
3287 22b7f6f8 Thomas Thrainer
                               for key in constants.NICS_PARAMETERS
3288 22b7f6f8 Thomas Thrainer
                               if key in params])
3289 22b7f6f8 Thomas Thrainer
3290 22b7f6f8 Thomas Thrainer
    req_link = update_params_dict.get(constants.NIC_LINK, None)
3291 22b7f6f8 Thomas Thrainer
    req_mode = update_params_dict.get(constants.NIC_MODE, None)
3292 22b7f6f8 Thomas Thrainer
3293 22b7f6f8 Thomas Thrainer
    new_net_uuid = None
3294 22b7f6f8 Thomas Thrainer
    new_net_uuid_or_name = params.get(constants.INIC_NETWORK, old_net_uuid)
3295 22b7f6f8 Thomas Thrainer
    if new_net_uuid_or_name:
3296 22b7f6f8 Thomas Thrainer
      new_net_uuid = self.cfg.LookupNetwork(new_net_uuid_or_name)
3297 22b7f6f8 Thomas Thrainer
      new_net_obj = self.cfg.GetNetwork(new_net_uuid)
3298 22b7f6f8 Thomas Thrainer
3299 22b7f6f8 Thomas Thrainer
    if old_net_uuid:
3300 22b7f6f8 Thomas Thrainer
      old_net_obj = self.cfg.GetNetwork(old_net_uuid)
3301 22b7f6f8 Thomas Thrainer
3302 22b7f6f8 Thomas Thrainer
    if new_net_uuid:
3303 22b7f6f8 Thomas Thrainer
      netparams = self.cfg.GetGroupNetParams(new_net_uuid, pnode)
3304 22b7f6f8 Thomas Thrainer
      if not netparams:
3305 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("No netparams found for the network"
3306 22b7f6f8 Thomas Thrainer
                                   " %s, probably not connected" %
3307 22b7f6f8 Thomas Thrainer
                                   new_net_obj.name, errors.ECODE_INVAL)
3308 22b7f6f8 Thomas Thrainer
      new_params = dict(netparams)
3309 22b7f6f8 Thomas Thrainer
    else:
3310 22b7f6f8 Thomas Thrainer
      new_params = _GetUpdatedParams(old_params, update_params_dict)
3311 22b7f6f8 Thomas Thrainer
3312 22b7f6f8 Thomas Thrainer
    utils.ForceDictType(new_params, constants.NICS_PARAMETER_TYPES)
3313 22b7f6f8 Thomas Thrainer
3314 22b7f6f8 Thomas Thrainer
    new_filled_params = cluster.SimpleFillNIC(new_params)
3315 22b7f6f8 Thomas Thrainer
    objects.NIC.CheckParameterSyntax(new_filled_params)
3316 22b7f6f8 Thomas Thrainer
3317 22b7f6f8 Thomas Thrainer
    new_mode = new_filled_params[constants.NIC_MODE]
3318 22b7f6f8 Thomas Thrainer
    if new_mode == constants.NIC_MODE_BRIDGED:
3319 22b7f6f8 Thomas Thrainer
      bridge = new_filled_params[constants.NIC_LINK]
3320 22b7f6f8 Thomas Thrainer
      msg = self.rpc.call_bridges_exist(pnode, [bridge]).fail_msg
3321 22b7f6f8 Thomas Thrainer
      if msg:
3322 22b7f6f8 Thomas Thrainer
        msg = "Error checking bridges on node '%s': %s" % (pnode, msg)
3323 22b7f6f8 Thomas Thrainer
        if self.op.force:
3324 22b7f6f8 Thomas Thrainer
          self.warn.append(msg)
3325 22b7f6f8 Thomas Thrainer
        else:
3326 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError(msg, errors.ECODE_ENVIRON)
3327 22b7f6f8 Thomas Thrainer
3328 22b7f6f8 Thomas Thrainer
    elif new_mode == constants.NIC_MODE_ROUTED:
3329 22b7f6f8 Thomas Thrainer
      ip = params.get(constants.INIC_IP, old_ip)
3330 22b7f6f8 Thomas Thrainer
      if ip is None:
3331 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cannot set the NIC IP address to None"
3332 22b7f6f8 Thomas Thrainer
                                   " on a routed NIC", errors.ECODE_INVAL)
3333 22b7f6f8 Thomas Thrainer
3334 22b7f6f8 Thomas Thrainer
    elif new_mode == constants.NIC_MODE_OVS:
3335 22b7f6f8 Thomas Thrainer
      # TODO: check OVS link
3336 22b7f6f8 Thomas Thrainer
      self.LogInfo("OVS links are currently not checked for correctness")
3337 22b7f6f8 Thomas Thrainer
3338 22b7f6f8 Thomas Thrainer
    if constants.INIC_MAC in params:
3339 22b7f6f8 Thomas Thrainer
      mac = params[constants.INIC_MAC]
3340 22b7f6f8 Thomas Thrainer
      if mac is None:
3341 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Cannot unset the NIC MAC address",
3342 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3343 22b7f6f8 Thomas Thrainer
      elif mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
3344 22b7f6f8 Thomas Thrainer
        # otherwise generate the MAC address
3345 22b7f6f8 Thomas Thrainer
        params[constants.INIC_MAC] = \
3346 22b7f6f8 Thomas Thrainer
          self.cfg.GenerateMAC(new_net_uuid, self.proc.GetECId())
3347 22b7f6f8 Thomas Thrainer
      else:
3348 22b7f6f8 Thomas Thrainer
        # or validate/reserve the current one
3349 22b7f6f8 Thomas Thrainer
        try:
3350 22b7f6f8 Thomas Thrainer
          self.cfg.ReserveMAC(mac, self.proc.GetECId())
3351 22b7f6f8 Thomas Thrainer
        except errors.ReservationError:
3352 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("MAC address '%s' already in use"
3353 22b7f6f8 Thomas Thrainer
                                     " in cluster" % mac,
3354 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_NOTUNIQUE)
3355 22b7f6f8 Thomas Thrainer
    elif new_net_uuid != old_net_uuid:
3356 22b7f6f8 Thomas Thrainer
3357 22b7f6f8 Thomas Thrainer
      def get_net_prefix(net_uuid):
3358 22b7f6f8 Thomas Thrainer
        mac_prefix = None
3359 22b7f6f8 Thomas Thrainer
        if net_uuid:
3360 22b7f6f8 Thomas Thrainer
          nobj = self.cfg.GetNetwork(net_uuid)
3361 22b7f6f8 Thomas Thrainer
          mac_prefix = nobj.mac_prefix
3362 22b7f6f8 Thomas Thrainer
3363 22b7f6f8 Thomas Thrainer
        return mac_prefix
3364 22b7f6f8 Thomas Thrainer
3365 22b7f6f8 Thomas Thrainer
      new_prefix = get_net_prefix(new_net_uuid)
3366 22b7f6f8 Thomas Thrainer
      old_prefix = get_net_prefix(old_net_uuid)
3367 22b7f6f8 Thomas Thrainer
      if old_prefix != new_prefix:
3368 22b7f6f8 Thomas Thrainer
        params[constants.INIC_MAC] = \
3369 22b7f6f8 Thomas Thrainer
          self.cfg.GenerateMAC(new_net_uuid, self.proc.GetECId())
3370 22b7f6f8 Thomas Thrainer
3371 22b7f6f8 Thomas Thrainer
    # if there is a change in (ip, network) tuple
3372 22b7f6f8 Thomas Thrainer
    new_ip = params.get(constants.INIC_IP, old_ip)
3373 22b7f6f8 Thomas Thrainer
    if (new_ip, new_net_uuid) != (old_ip, old_net_uuid):
3374 22b7f6f8 Thomas Thrainer
      if new_ip:
3375 22b7f6f8 Thomas Thrainer
        # if IP is pool then require a network and generate one IP
3376 22b7f6f8 Thomas Thrainer
        if new_ip.lower() == constants.NIC_IP_POOL:
3377 22b7f6f8 Thomas Thrainer
          if new_net_uuid:
3378 22b7f6f8 Thomas Thrainer
            try:
3379 22b7f6f8 Thomas Thrainer
              new_ip = self.cfg.GenerateIp(new_net_uuid, self.proc.GetECId())
3380 22b7f6f8 Thomas Thrainer
            except errors.ReservationError:
3381 22b7f6f8 Thomas Thrainer
              raise errors.OpPrereqError("Unable to get a free IP"
3382 22b7f6f8 Thomas Thrainer
                                         " from the address pool",
3383 22b7f6f8 Thomas Thrainer
                                         errors.ECODE_STATE)
3384 22b7f6f8 Thomas Thrainer
            self.LogInfo("Chose IP %s from network %s",
3385 22b7f6f8 Thomas Thrainer
                         new_ip,
3386 22b7f6f8 Thomas Thrainer
                         new_net_obj.name)
3387 22b7f6f8 Thomas Thrainer
            params[constants.INIC_IP] = new_ip
3388 22b7f6f8 Thomas Thrainer
          else:
3389 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("ip=pool, but no network found",
3390 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
3391 22b7f6f8 Thomas Thrainer
        # Reserve new IP if in the new network if any
3392 22b7f6f8 Thomas Thrainer
        elif new_net_uuid:
3393 22b7f6f8 Thomas Thrainer
          try:
3394 22b7f6f8 Thomas Thrainer
            self.cfg.ReserveIp(new_net_uuid, new_ip, self.proc.GetECId())
3395 22b7f6f8 Thomas Thrainer
            self.LogInfo("Reserving IP %s in network %s",
3396 22b7f6f8 Thomas Thrainer
                         new_ip, new_net_obj.name)
3397 22b7f6f8 Thomas Thrainer
          except errors.ReservationError:
3398 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("IP %s not available in network %s" %
3399 22b7f6f8 Thomas Thrainer
                                       (new_ip, new_net_obj.name),
3400 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_NOTUNIQUE)
3401 22b7f6f8 Thomas Thrainer
        # new network is None so check if new IP is a conflicting IP
3402 22b7f6f8 Thomas Thrainer
        elif self.op.conflicts_check:
3403 22b7f6f8 Thomas Thrainer
          _CheckForConflictingIp(self, new_ip, pnode)
3404 22b7f6f8 Thomas Thrainer
3405 22b7f6f8 Thomas Thrainer
      # release old IP if old network is not None
3406 22b7f6f8 Thomas Thrainer
      if old_ip and old_net_uuid:
3407 22b7f6f8 Thomas Thrainer
        try:
3408 22b7f6f8 Thomas Thrainer
          self.cfg.ReleaseIp(old_net_uuid, old_ip, self.proc.GetECId())
3409 22b7f6f8 Thomas Thrainer
        except errors.AddressPoolError:
3410 22b7f6f8 Thomas Thrainer
          logging.warning("Release IP %s not contained in network %s",
3411 22b7f6f8 Thomas Thrainer
                          old_ip, old_net_obj.name)
3412 22b7f6f8 Thomas Thrainer
3413 22b7f6f8 Thomas Thrainer
    # there are no changes in (ip, network) tuple and old network is not None
3414 22b7f6f8 Thomas Thrainer
    elif (old_net_uuid is not None and
3415 22b7f6f8 Thomas Thrainer
          (req_link is not None or req_mode is not None)):
3416 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Not allowed to change link or mode of"
3417 22b7f6f8 Thomas Thrainer
                                 " a NIC that is connected to a network",
3418 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3419 22b7f6f8 Thomas Thrainer
3420 22b7f6f8 Thomas Thrainer
    private.params = new_params
3421 22b7f6f8 Thomas Thrainer
    private.filled = new_filled_params
3422 22b7f6f8 Thomas Thrainer
3423 22b7f6f8 Thomas Thrainer
  def _PreCheckDiskTemplate(self, pnode_info):
3424 22b7f6f8 Thomas Thrainer
    """CheckPrereq checks related to a new disk template."""
3425 22b7f6f8 Thomas Thrainer
    # Arguments are passed to avoid configuration lookups
3426 22b7f6f8 Thomas Thrainer
    instance = self.instance
3427 22b7f6f8 Thomas Thrainer
    pnode = instance.primary_node
3428 22b7f6f8 Thomas Thrainer
    cluster = self.cluster
3429 22b7f6f8 Thomas Thrainer
    if instance.disk_template == self.op.disk_template:
3430 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance already has disk template %s" %
3431 22b7f6f8 Thomas Thrainer
                                 instance.disk_template, errors.ECODE_INVAL)
3432 22b7f6f8 Thomas Thrainer
3433 22b7f6f8 Thomas Thrainer
    if (instance.disk_template,
3434 22b7f6f8 Thomas Thrainer
        self.op.disk_template) not in self._DISK_CONVERSIONS:
3435 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Unsupported disk template conversion from"
3436 22b7f6f8 Thomas Thrainer
                                 " %s to %s" % (instance.disk_template,
3437 22b7f6f8 Thomas Thrainer
                                                self.op.disk_template),
3438 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
3439 22b7f6f8 Thomas Thrainer
    _CheckInstanceState(self, instance, INSTANCE_DOWN,
3440 22b7f6f8 Thomas Thrainer
                        msg="cannot change disk template")
3441 22b7f6f8 Thomas Thrainer
    if self.op.disk_template in constants.DTS_INT_MIRROR:
3442 22b7f6f8 Thomas Thrainer
      if self.op.remote_node == pnode:
3443 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Given new secondary node %s is the same"
3444 22b7f6f8 Thomas Thrainer
                                   " as the primary node of the instance" %
3445 22b7f6f8 Thomas Thrainer
                                   self.op.remote_node, errors.ECODE_STATE)
3446 22b7f6f8 Thomas Thrainer
      _CheckNodeOnline(self, self.op.remote_node)
3447 22b7f6f8 Thomas Thrainer
      _CheckNodeNotDrained(self, self.op.remote_node)
3448 22b7f6f8 Thomas Thrainer
      # FIXME: here we assume that the old instance type is DT_PLAIN
3449 22b7f6f8 Thomas Thrainer
      assert instance.disk_template == constants.DT_PLAIN
3450 22b7f6f8 Thomas Thrainer
      disks = [{constants.IDISK_SIZE: d.size,
3451 22b7f6f8 Thomas Thrainer
                constants.IDISK_VG: d.logical_id[0]}
3452 22b7f6f8 Thomas Thrainer
               for d in instance.disks]
3453 22b7f6f8 Thomas Thrainer
      required = _ComputeDiskSizePerVG(self.op.disk_template, disks)
3454 22b7f6f8 Thomas Thrainer
      _CheckNodesFreeDiskPerVG(self, [self.op.remote_node], required)
3455 22b7f6f8 Thomas Thrainer
3456 22b7f6f8 Thomas Thrainer
      snode_info = self.cfg.GetNodeInfo(self.op.remote_node)
3457 22b7f6f8 Thomas Thrainer
      snode_group = self.cfg.GetNodeGroup(snode_info.group)
3458 22b7f6f8 Thomas Thrainer
      ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
3459 22b7f6f8 Thomas Thrainer
                                                              snode_group)
3460 22b7f6f8 Thomas Thrainer
      _CheckTargetNodeIPolicy(self, ipolicy, instance, snode_info, self.cfg,
3461 22b7f6f8 Thomas Thrainer
                              ignore=self.op.ignore_ipolicy)
3462 22b7f6f8 Thomas Thrainer
      if pnode_info.group != snode_info.group:
3463 22b7f6f8 Thomas Thrainer
        self.LogWarning("The primary and secondary nodes are in two"
3464 22b7f6f8 Thomas Thrainer
                        " different node groups; the disk parameters"
3465 22b7f6f8 Thomas Thrainer
                        " from the first disk's node group will be"
3466 22b7f6f8 Thomas Thrainer
                        " used")
3467 22b7f6f8 Thomas Thrainer
3468 22b7f6f8 Thomas Thrainer
    if not self.op.disk_template in constants.DTS_EXCL_STORAGE:
3469 22b7f6f8 Thomas Thrainer
      # Make sure none of the nodes require exclusive storage
3470 22b7f6f8 Thomas Thrainer
      nodes = [pnode_info]
3471 22b7f6f8 Thomas Thrainer
      if self.op.disk_template in constants.DTS_INT_MIRROR:
3472 22b7f6f8 Thomas Thrainer
        assert snode_info
3473 22b7f6f8 Thomas Thrainer
        nodes.append(snode_info)
3474 22b7f6f8 Thomas Thrainer
      has_es = lambda n: _IsExclusiveStorageEnabledNode(self.cfg, n)
3475 22b7f6f8 Thomas Thrainer
      if compat.any(map(has_es, nodes)):
3476 22b7f6f8 Thomas Thrainer
        errmsg = ("Cannot convert disk template from %s to %s when exclusive"
3477 22b7f6f8 Thomas Thrainer
                  " storage is enabled" % (instance.disk_template,
3478 22b7f6f8 Thomas Thrainer
                                           self.op.disk_template))
3479 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError(errmsg, errors.ECODE_STATE)
3480 22b7f6f8 Thomas Thrainer
3481 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
3482 22b7f6f8 Thomas Thrainer
    """Check prerequisites.
3483 22b7f6f8 Thomas Thrainer

3484 22b7f6f8 Thomas Thrainer
    This only checks the instance list against the existing names.
3485 22b7f6f8 Thomas Thrainer

3486 22b7f6f8 Thomas Thrainer
    """
3487 22b7f6f8 Thomas Thrainer
    assert self.op.instance_name in self.owned_locks(locking.LEVEL_INSTANCE)
3488 22b7f6f8 Thomas Thrainer
    instance = self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
3489 22b7f6f8 Thomas Thrainer
3490 22b7f6f8 Thomas Thrainer
    cluster = self.cluster = self.cfg.GetClusterInfo()
3491 22b7f6f8 Thomas Thrainer
    assert self.instance is not None, \
3492 22b7f6f8 Thomas Thrainer
      "Cannot retrieve locked instance %s" % self.op.instance_name
3493 22b7f6f8 Thomas Thrainer
3494 22b7f6f8 Thomas Thrainer
    pnode = instance.primary_node
3495 22b7f6f8 Thomas Thrainer
3496 22b7f6f8 Thomas Thrainer
    self.warn = []
3497 22b7f6f8 Thomas Thrainer
3498 22b7f6f8 Thomas Thrainer
    if (self.op.pnode is not None and self.op.pnode != pnode and
3499 22b7f6f8 Thomas Thrainer
        not self.op.force):
3500 22b7f6f8 Thomas Thrainer
      # verify that the instance is not up
3501 22b7f6f8 Thomas Thrainer
      instance_info = self.rpc.call_instance_info(pnode, instance.name,
3502 22b7f6f8 Thomas Thrainer
                                                  instance.hypervisor)
3503 22b7f6f8 Thomas Thrainer
      if instance_info.fail_msg:
3504 22b7f6f8 Thomas Thrainer
        self.warn.append("Can't get instance runtime information: %s" %
3505 22b7f6f8 Thomas Thrainer
                         instance_info.fail_msg)
3506 22b7f6f8 Thomas Thrainer
      elif instance_info.payload:
3507 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance is still running on %s" % pnode,
3508 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_STATE)
3509 22b7f6f8 Thomas Thrainer
3510 22b7f6f8 Thomas Thrainer
    assert pnode in self.owned_locks(locking.LEVEL_NODE)
3511 22b7f6f8 Thomas Thrainer
    nodelist = list(instance.all_nodes)
3512 22b7f6f8 Thomas Thrainer
    pnode_info = self.cfg.GetNodeInfo(pnode)
3513 22b7f6f8 Thomas Thrainer
    self.diskparams = self.cfg.GetInstanceDiskParams(instance)
3514 22b7f6f8 Thomas Thrainer
3515 22b7f6f8 Thomas Thrainer
    #_CheckInstanceNodeGroups(self.cfg, self.op.instance_name, owned_groups)
3516 22b7f6f8 Thomas Thrainer
    assert pnode_info.group in self.owned_locks(locking.LEVEL_NODEGROUP)
3517 22b7f6f8 Thomas Thrainer
    group_info = self.cfg.GetNodeGroup(pnode_info.group)
3518 22b7f6f8 Thomas Thrainer
3519 22b7f6f8 Thomas Thrainer
    # dictionary with instance information after the modification
3520 22b7f6f8 Thomas Thrainer
    ispec = {}
3521 22b7f6f8 Thomas Thrainer
3522 22b7f6f8 Thomas Thrainer
    # Check disk modifications. This is done here and not in CheckArguments
3523 22b7f6f8 Thomas Thrainer
    # (as with NICs), because we need to know the instance's disk template
3524 22b7f6f8 Thomas Thrainer
    if instance.disk_template == constants.DT_EXT:
3525 22b7f6f8 Thomas Thrainer
      self._CheckMods("disk", self.op.disks, {},
3526 22b7f6f8 Thomas Thrainer
                      self._VerifyDiskModification)
3527 22b7f6f8 Thomas Thrainer
    else:
3528 22b7f6f8 Thomas Thrainer
      self._CheckMods("disk", self.op.disks, constants.IDISK_PARAMS_TYPES,
3529 22b7f6f8 Thomas Thrainer
                      self._VerifyDiskModification)
3530 22b7f6f8 Thomas Thrainer
3531 22b7f6f8 Thomas Thrainer
    # Prepare disk/NIC modifications
3532 22b7f6f8 Thomas Thrainer
    self.diskmod = PrepareContainerMods(self.op.disks, None)
3533 22b7f6f8 Thomas Thrainer
    self.nicmod = PrepareContainerMods(self.op.nics, _InstNicModPrivate)
3534 22b7f6f8 Thomas Thrainer
3535 22b7f6f8 Thomas Thrainer
    # Check the validity of the `provider' parameter
3536 22b7f6f8 Thomas Thrainer
    if instance.disk_template in constants.DT_EXT:
3537 22b7f6f8 Thomas Thrainer
      for mod in self.diskmod:
3538 22b7f6f8 Thomas Thrainer
        ext_provider = mod[2].get(constants.IDISK_PROVIDER, None)
3539 22b7f6f8 Thomas Thrainer
        if mod[0] == constants.DDM_ADD:
3540 22b7f6f8 Thomas Thrainer
          if ext_provider is None:
3541 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Instance template is '%s' and parameter"
3542 22b7f6f8 Thomas Thrainer
                                       " '%s' missing, during disk add" %
3543 22b7f6f8 Thomas Thrainer
                                       (constants.DT_EXT,
3544 22b7f6f8 Thomas Thrainer
                                        constants.IDISK_PROVIDER),
3545 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_NOENT)
3546 22b7f6f8 Thomas Thrainer
        elif mod[0] == constants.DDM_MODIFY:
3547 22b7f6f8 Thomas Thrainer
          if ext_provider:
3548 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Parameter '%s' is invalid during disk"
3549 22b7f6f8 Thomas Thrainer
                                       " modification" %
3550 22b7f6f8 Thomas Thrainer
                                       constants.IDISK_PROVIDER,
3551 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_INVAL)
3552 22b7f6f8 Thomas Thrainer
    else:
3553 22b7f6f8 Thomas Thrainer
      for mod in self.diskmod:
3554 22b7f6f8 Thomas Thrainer
        ext_provider = mod[2].get(constants.IDISK_PROVIDER, None)
3555 22b7f6f8 Thomas Thrainer
        if ext_provider is not None:
3556 22b7f6f8 Thomas Thrainer
          raise errors.OpPrereqError("Parameter '%s' is only valid for"
3557 22b7f6f8 Thomas Thrainer
                                     " instances of type '%s'" %
3558 22b7f6f8 Thomas Thrainer
                                     (constants.IDISK_PROVIDER,
3559 22b7f6f8 Thomas Thrainer
                                      constants.DT_EXT),
3560 22b7f6f8 Thomas Thrainer
                                     errors.ECODE_INVAL)
3561 22b7f6f8 Thomas Thrainer
3562 22b7f6f8 Thomas Thrainer
    # OS change
3563 22b7f6f8 Thomas Thrainer
    if self.op.os_name and not self.op.force:
3564 22b7f6f8 Thomas Thrainer
      _CheckNodeHasOS(self, instance.primary_node, self.op.os_name,
3565 22b7f6f8 Thomas Thrainer
                      self.op.force_variant)
3566 22b7f6f8 Thomas Thrainer
      instance_os = self.op.os_name
3567 22b7f6f8 Thomas Thrainer
    else:
3568 22b7f6f8 Thomas Thrainer
      instance_os = instance.os
3569 22b7f6f8 Thomas Thrainer
3570 22b7f6f8 Thomas Thrainer
    assert not (self.op.disk_template and self.op.disks), \
3571 22b7f6f8 Thomas Thrainer
      "Can't modify disk template and apply disk changes at the same time"
3572 22b7f6f8 Thomas Thrainer
3573 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
3574 22b7f6f8 Thomas Thrainer
      self._PreCheckDiskTemplate(pnode_info)
3575 22b7f6f8 Thomas Thrainer
3576 22b7f6f8 Thomas Thrainer
    # hvparams processing
3577 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
3578 22b7f6f8 Thomas Thrainer
      hv_type = instance.hypervisor
3579 22b7f6f8 Thomas Thrainer
      i_hvdict = _GetUpdatedParams(instance.hvparams, self.op.hvparams)
3580 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(i_hvdict, constants.HVS_PARAMETER_TYPES)
3581 22b7f6f8 Thomas Thrainer
      hv_new = cluster.SimpleFillHV(hv_type, instance.os, i_hvdict)
3582 22b7f6f8 Thomas Thrainer
3583 22b7f6f8 Thomas Thrainer
      # local check
3584 22b7f6f8 Thomas Thrainer
      hypervisor.GetHypervisorClass(hv_type).CheckParameterSyntax(hv_new)
3585 22b7f6f8 Thomas Thrainer
      _CheckHVParams(self, nodelist, instance.hypervisor, hv_new)
3586 22b7f6f8 Thomas Thrainer
      self.hv_proposed = self.hv_new = hv_new # the new actual values
3587 22b7f6f8 Thomas Thrainer
      self.hv_inst = i_hvdict # the new dict (without defaults)
3588 22b7f6f8 Thomas Thrainer
    else:
3589 22b7f6f8 Thomas Thrainer
      self.hv_proposed = cluster.SimpleFillHV(instance.hypervisor, instance.os,
3590 22b7f6f8 Thomas Thrainer
                                              instance.hvparams)
3591 22b7f6f8 Thomas Thrainer
      self.hv_new = self.hv_inst = {}
3592 22b7f6f8 Thomas Thrainer
3593 22b7f6f8 Thomas Thrainer
    # beparams processing
3594 22b7f6f8 Thomas Thrainer
    if self.op.beparams:
3595 22b7f6f8 Thomas Thrainer
      i_bedict = _GetUpdatedParams(instance.beparams, self.op.beparams,
3596 22b7f6f8 Thomas Thrainer
                                   use_none=True)
3597 22b7f6f8 Thomas Thrainer
      objects.UpgradeBeParams(i_bedict)
3598 22b7f6f8 Thomas Thrainer
      utils.ForceDictType(i_bedict, constants.BES_PARAMETER_TYPES)
3599 22b7f6f8 Thomas Thrainer
      be_new = cluster.SimpleFillBE(i_bedict)
3600 22b7f6f8 Thomas Thrainer
      self.be_proposed = self.be_new = be_new # the new actual values
3601 22b7f6f8 Thomas Thrainer
      self.be_inst = i_bedict # the new dict (without defaults)
3602 22b7f6f8 Thomas Thrainer
    else:
3603 22b7f6f8 Thomas Thrainer
      self.be_new = self.be_inst = {}
3604 22b7f6f8 Thomas Thrainer
      self.be_proposed = cluster.SimpleFillBE(instance.beparams)
3605 22b7f6f8 Thomas Thrainer
    be_old = cluster.FillBE(instance)
3606 22b7f6f8 Thomas Thrainer
3607 22b7f6f8 Thomas Thrainer
    # CPU param validation -- checking every time a parameter is
3608 22b7f6f8 Thomas Thrainer
    # changed to cover all cases where either CPU mask or vcpus have
3609 22b7f6f8 Thomas Thrainer
    # changed
3610 22b7f6f8 Thomas Thrainer
    if (constants.BE_VCPUS in self.be_proposed and
3611 22b7f6f8 Thomas Thrainer
        constants.HV_CPU_MASK in self.hv_proposed):
3612 22b7f6f8 Thomas Thrainer
      cpu_list = \
3613 22b7f6f8 Thomas Thrainer
        utils.ParseMultiCpuMask(self.hv_proposed[constants.HV_CPU_MASK])
3614 22b7f6f8 Thomas Thrainer
      # Verify mask is consistent with number of vCPUs. Can skip this
3615 22b7f6f8 Thomas Thrainer
      # test if only 1 entry in the CPU mask, which means same mask
3616 22b7f6f8 Thomas Thrainer
      # is applied to all vCPUs.
3617 22b7f6f8 Thomas Thrainer
      if (len(cpu_list) > 1 and
3618 22b7f6f8 Thomas Thrainer
          len(cpu_list) != self.be_proposed[constants.BE_VCPUS]):
3619 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Number of vCPUs [%d] does not match the"
3620 22b7f6f8 Thomas Thrainer
                                   " CPU mask [%s]" %
3621 22b7f6f8 Thomas Thrainer
                                   (self.be_proposed[constants.BE_VCPUS],
3622 22b7f6f8 Thomas Thrainer
                                    self.hv_proposed[constants.HV_CPU_MASK]),
3623 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3624 22b7f6f8 Thomas Thrainer
3625 22b7f6f8 Thomas Thrainer
      # Only perform this test if a new CPU mask is given
3626 22b7f6f8 Thomas Thrainer
      if constants.HV_CPU_MASK in self.hv_new:
3627 22b7f6f8 Thomas Thrainer
        # Calculate the largest CPU number requested
3628 22b7f6f8 Thomas Thrainer
        max_requested_cpu = max(map(max, cpu_list))
3629 22b7f6f8 Thomas Thrainer
        # Check that all of the instance's nodes have enough physical CPUs to
3630 22b7f6f8 Thomas Thrainer
        # satisfy the requested CPU mask
3631 22b7f6f8 Thomas Thrainer
        _CheckNodesPhysicalCPUs(self, instance.all_nodes,
3632 22b7f6f8 Thomas Thrainer
                                max_requested_cpu + 1, instance.hypervisor)
3633 22b7f6f8 Thomas Thrainer
3634 22b7f6f8 Thomas Thrainer
    # osparams processing
3635 22b7f6f8 Thomas Thrainer
    if self.op.osparams:
3636 22b7f6f8 Thomas Thrainer
      i_osdict = _GetUpdatedParams(instance.osparams, self.op.osparams)
3637 22b7f6f8 Thomas Thrainer
      _CheckOSParams(self, True, nodelist, instance_os, i_osdict)
3638 22b7f6f8 Thomas Thrainer
      self.os_inst = i_osdict # the new dict (without defaults)
3639 22b7f6f8 Thomas Thrainer
    else:
3640 22b7f6f8 Thomas Thrainer
      self.os_inst = {}
3641 22b7f6f8 Thomas Thrainer
3642 22b7f6f8 Thomas Thrainer
    #TODO(dynmem): do the appropriate check involving MINMEM
3643 22b7f6f8 Thomas Thrainer
    if (constants.BE_MAXMEM in self.op.beparams and not self.op.force and
3644 22b7f6f8 Thomas Thrainer
        be_new[constants.BE_MAXMEM] > be_old[constants.BE_MAXMEM]):
3645 22b7f6f8 Thomas Thrainer
      mem_check_list = [pnode]
3646 22b7f6f8 Thomas Thrainer
      if be_new[constants.BE_AUTO_BALANCE]:
3647 22b7f6f8 Thomas Thrainer
        # either we changed auto_balance to yes or it was from before
3648 22b7f6f8 Thomas Thrainer
        mem_check_list.extend(instance.secondary_nodes)
3649 22b7f6f8 Thomas Thrainer
      instance_info = self.rpc.call_instance_info(pnode, instance.name,
3650 22b7f6f8 Thomas Thrainer
                                                  instance.hypervisor)
3651 22b7f6f8 Thomas Thrainer
      nodeinfo = self.rpc.call_node_info(mem_check_list, None,
3652 22b7f6f8 Thomas Thrainer
                                         [instance.hypervisor], False)
3653 22b7f6f8 Thomas Thrainer
      pninfo = nodeinfo[pnode]
3654 22b7f6f8 Thomas Thrainer
      msg = pninfo.fail_msg
3655 22b7f6f8 Thomas Thrainer
      if msg:
3656 22b7f6f8 Thomas Thrainer
        # Assume the primary node is unreachable and go ahead
3657 22b7f6f8 Thomas Thrainer
        self.warn.append("Can't get info from primary node %s: %s" %
3658 22b7f6f8 Thomas Thrainer
                         (pnode, msg))
3659 22b7f6f8 Thomas Thrainer
      else:
3660 22b7f6f8 Thomas Thrainer
        (_, _, (pnhvinfo, )) = pninfo.payload
3661 22b7f6f8 Thomas Thrainer
        if not isinstance(pnhvinfo.get("memory_free", None), int):
3662 22b7f6f8 Thomas Thrainer
          self.warn.append("Node data from primary node %s doesn't contain"
3663 22b7f6f8 Thomas Thrainer
                           " free memory information" % pnode)
3664 22b7f6f8 Thomas Thrainer
        elif instance_info.fail_msg:
3665 22b7f6f8 Thomas Thrainer
          self.warn.append("Can't get instance runtime information: %s" %
3666 22b7f6f8 Thomas Thrainer
                           instance_info.fail_msg)
3667 22b7f6f8 Thomas Thrainer
        else:
3668 22b7f6f8 Thomas Thrainer
          if instance_info.payload:
3669 22b7f6f8 Thomas Thrainer
            current_mem = int(instance_info.payload["memory"])
3670 22b7f6f8 Thomas Thrainer
          else:
3671 22b7f6f8 Thomas Thrainer
            # Assume instance not running
3672 22b7f6f8 Thomas Thrainer
            # (there is a slight race condition here, but it's not very
3673 22b7f6f8 Thomas Thrainer
            # probable, and we have no other way to check)
3674 22b7f6f8 Thomas Thrainer
            # TODO: Describe race condition
3675 22b7f6f8 Thomas Thrainer
            current_mem = 0
3676 22b7f6f8 Thomas Thrainer
          #TODO(dynmem): do the appropriate check involving MINMEM
3677 22b7f6f8 Thomas Thrainer
          miss_mem = (be_new[constants.BE_MAXMEM] - current_mem -
3678 22b7f6f8 Thomas Thrainer
                      pnhvinfo["memory_free"])
3679 22b7f6f8 Thomas Thrainer
          if miss_mem > 0:
3680 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("This change will prevent the instance"
3681 22b7f6f8 Thomas Thrainer
                                       " from starting, due to %d MB of memory"
3682 22b7f6f8 Thomas Thrainer
                                       " missing on its primary node" %
3683 22b7f6f8 Thomas Thrainer
                                       miss_mem, errors.ECODE_NORES)
3684 22b7f6f8 Thomas Thrainer
3685 22b7f6f8 Thomas Thrainer
      if be_new[constants.BE_AUTO_BALANCE]:
3686 22b7f6f8 Thomas Thrainer
        for node, nres in nodeinfo.items():
3687 22b7f6f8 Thomas Thrainer
          if node not in instance.secondary_nodes:
3688 22b7f6f8 Thomas Thrainer
            continue
3689 22b7f6f8 Thomas Thrainer
          nres.Raise("Can't get info from secondary node %s" % node,
3690 22b7f6f8 Thomas Thrainer
                     prereq=True, ecode=errors.ECODE_STATE)
3691 22b7f6f8 Thomas Thrainer
          (_, _, (nhvinfo, )) = nres.payload
3692 22b7f6f8 Thomas Thrainer
          if not isinstance(nhvinfo.get("memory_free", None), int):
3693 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("Secondary node %s didn't return free"
3694 22b7f6f8 Thomas Thrainer
                                       " memory information" % node,
3695 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_STATE)
3696 22b7f6f8 Thomas Thrainer
          #TODO(dynmem): do the appropriate check involving MINMEM
3697 22b7f6f8 Thomas Thrainer
          elif be_new[constants.BE_MAXMEM] > nhvinfo["memory_free"]:
3698 22b7f6f8 Thomas Thrainer
            raise errors.OpPrereqError("This change will prevent the instance"
3699 22b7f6f8 Thomas Thrainer
                                       " from failover to its secondary node"
3700 22b7f6f8 Thomas Thrainer
                                       " %s, due to not enough memory" % node,
3701 22b7f6f8 Thomas Thrainer
                                       errors.ECODE_STATE)
3702 22b7f6f8 Thomas Thrainer
3703 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
3704 22b7f6f8 Thomas Thrainer
      remote_info = self.rpc.call_instance_info(instance.primary_node,
3705 22b7f6f8 Thomas Thrainer
                                                instance.name,
3706 22b7f6f8 Thomas Thrainer
                                                instance.hypervisor)
3707 22b7f6f8 Thomas Thrainer
      remote_info.Raise("Error checking node %s" % instance.primary_node)
3708 22b7f6f8 Thomas Thrainer
      if not remote_info.payload: # not running already
3709 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s is not running" %
3710 22b7f6f8 Thomas Thrainer
                                   instance.name, errors.ECODE_STATE)
3711 22b7f6f8 Thomas Thrainer
3712 22b7f6f8 Thomas Thrainer
      current_memory = remote_info.payload["memory"]
3713 22b7f6f8 Thomas Thrainer
      if (not self.op.force and
3714 22b7f6f8 Thomas Thrainer
           (self.op.runtime_mem > self.be_proposed[constants.BE_MAXMEM] or
3715 22b7f6f8 Thomas Thrainer
            self.op.runtime_mem < self.be_proposed[constants.BE_MINMEM])):
3716 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s must have memory between %d"
3717 22b7f6f8 Thomas Thrainer
                                   " and %d MB of memory unless --force is"
3718 22b7f6f8 Thomas Thrainer
                                   " given" %
3719 22b7f6f8 Thomas Thrainer
                                   (instance.name,
3720 22b7f6f8 Thomas Thrainer
                                    self.be_proposed[constants.BE_MINMEM],
3721 22b7f6f8 Thomas Thrainer
                                    self.be_proposed[constants.BE_MAXMEM]),
3722 22b7f6f8 Thomas Thrainer
                                   errors.ECODE_INVAL)
3723 22b7f6f8 Thomas Thrainer
3724 22b7f6f8 Thomas Thrainer
      delta = self.op.runtime_mem - current_memory
3725 22b7f6f8 Thomas Thrainer
      if delta > 0:
3726 22b7f6f8 Thomas Thrainer
        _CheckNodeFreeMemory(self, instance.primary_node,
3727 22b7f6f8 Thomas Thrainer
                             "ballooning memory for instance %s" %
3728 22b7f6f8 Thomas Thrainer
                             instance.name, delta, instance.hypervisor)
3729 22b7f6f8 Thomas Thrainer
3730 22b7f6f8 Thomas Thrainer
    if self.op.disks and instance.disk_template == constants.DT_DISKLESS:
3731 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Disk operations not supported for"
3732 22b7f6f8 Thomas Thrainer
                                 " diskless instances", errors.ECODE_INVAL)
3733 22b7f6f8 Thomas Thrainer
3734 22b7f6f8 Thomas Thrainer
    def _PrepareNicCreate(_, params, private):
3735 22b7f6f8 Thomas Thrainer
      self._PrepareNicModification(params, private, None, None,
3736 22b7f6f8 Thomas Thrainer
                                   {}, cluster, pnode)
3737 22b7f6f8 Thomas Thrainer
      return (None, None)
3738 22b7f6f8 Thomas Thrainer
3739 22b7f6f8 Thomas Thrainer
    def _PrepareNicMod(_, nic, params, private):
3740 22b7f6f8 Thomas Thrainer
      self._PrepareNicModification(params, private, nic.ip, nic.network,
3741 22b7f6f8 Thomas Thrainer
                                   nic.nicparams, cluster, pnode)
3742 22b7f6f8 Thomas Thrainer
      return None
3743 22b7f6f8 Thomas Thrainer
3744 22b7f6f8 Thomas Thrainer
    def _PrepareNicRemove(_, params, __):
3745 22b7f6f8 Thomas Thrainer
      ip = params.ip
3746 22b7f6f8 Thomas Thrainer
      net = params.network
3747 22b7f6f8 Thomas Thrainer
      if net is not None and ip is not None:
3748 22b7f6f8 Thomas Thrainer
        self.cfg.ReleaseIp(net, ip, self.proc.GetECId())
3749 22b7f6f8 Thomas Thrainer
3750 22b7f6f8 Thomas Thrainer
    # Verify NIC changes (operating on copy)
3751 22b7f6f8 Thomas Thrainer
    nics = instance.nics[:]
3752 22b7f6f8 Thomas Thrainer
    ApplyContainerMods("NIC", nics, None, self.nicmod,
3753 22b7f6f8 Thomas Thrainer
                       _PrepareNicCreate, _PrepareNicMod, _PrepareNicRemove)
3754 22b7f6f8 Thomas Thrainer
    if len(nics) > constants.MAX_NICS:
3755 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance has too many network interfaces"
3756 22b7f6f8 Thomas Thrainer
                                 " (%d), cannot add more" % constants.MAX_NICS,
3757 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
3758 22b7f6f8 Thomas Thrainer
3759 22b7f6f8 Thomas Thrainer
    def _PrepareDiskMod(_, disk, params, __):
3760 22b7f6f8 Thomas Thrainer
      disk.name = params.get(constants.IDISK_NAME, None)
3761 22b7f6f8 Thomas Thrainer
3762 22b7f6f8 Thomas Thrainer
    # Verify disk changes (operating on a copy)
3763 22b7f6f8 Thomas Thrainer
    disks = copy.deepcopy(instance.disks)
3764 22b7f6f8 Thomas Thrainer
    ApplyContainerMods("disk", disks, None, self.diskmod, None, _PrepareDiskMod,
3765 22b7f6f8 Thomas Thrainer
                       None)
3766 22b7f6f8 Thomas Thrainer
    utils.ValidateDeviceNames("disk", disks)
3767 22b7f6f8 Thomas Thrainer
    if len(disks) > constants.MAX_DISKS:
3768 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Instance has too many disks (%d), cannot add"
3769 22b7f6f8 Thomas Thrainer
                                 " more" % constants.MAX_DISKS,
3770 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_STATE)
3771 22b7f6f8 Thomas Thrainer
    disk_sizes = [disk.size for disk in instance.disks]
3772 22b7f6f8 Thomas Thrainer
    disk_sizes.extend(params["size"] for (op, idx, params, private) in
3773 22b7f6f8 Thomas Thrainer
                      self.diskmod if op == constants.DDM_ADD)
3774 22b7f6f8 Thomas Thrainer
    ispec[constants.ISPEC_DISK_COUNT] = len(disk_sizes)
3775 22b7f6f8 Thomas Thrainer
    ispec[constants.ISPEC_DISK_SIZE] = disk_sizes
3776 22b7f6f8 Thomas Thrainer
3777 22b7f6f8 Thomas Thrainer
    if self.op.offline is not None and self.op.offline:
3778 22b7f6f8 Thomas Thrainer
      _CheckInstanceState(self, instance, CAN_CHANGE_INSTANCE_OFFLINE,
3779 22b7f6f8 Thomas Thrainer
                          msg="can't change to offline")
3780 22b7f6f8 Thomas Thrainer
3781 22b7f6f8 Thomas Thrainer
    # Pre-compute NIC changes (necessary to use result in hooks)
3782 22b7f6f8 Thomas Thrainer
    self._nic_chgdesc = []
3783 22b7f6f8 Thomas Thrainer
    if self.nicmod:
3784 22b7f6f8 Thomas Thrainer
      # Operate on copies as this is still in prereq
3785 22b7f6f8 Thomas Thrainer
      nics = [nic.Copy() for nic in instance.nics]
3786 22b7f6f8 Thomas Thrainer
      ApplyContainerMods("NIC", nics, self._nic_chgdesc, self.nicmod,
3787 22b7f6f8 Thomas Thrainer
                         self._CreateNewNic, self._ApplyNicMods, None)
3788 22b7f6f8 Thomas Thrainer
      # Verify that NIC names are unique and valid
3789 22b7f6f8 Thomas Thrainer
      utils.ValidateDeviceNames("NIC", nics)
3790 22b7f6f8 Thomas Thrainer
      self._new_nics = nics
3791 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_NIC_COUNT] = len(self._new_nics)
3792 22b7f6f8 Thomas Thrainer
    else:
3793 22b7f6f8 Thomas Thrainer
      self._new_nics = None
3794 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_NIC_COUNT] = len(instance.nics)
3795 22b7f6f8 Thomas Thrainer
3796 22b7f6f8 Thomas Thrainer
    if not self.op.ignore_ipolicy:
3797 22b7f6f8 Thomas Thrainer
      ipolicy = ganeti.masterd.instance.CalculateGroupIPolicy(cluster,
3798 22b7f6f8 Thomas Thrainer
                                                              group_info)
3799 22b7f6f8 Thomas Thrainer
3800 22b7f6f8 Thomas Thrainer
      # Fill ispec with backend parameters
3801 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_SPINDLE_USE] = \
3802 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_SPINDLE_USE, None)
3803 22b7f6f8 Thomas Thrainer
      ispec[constants.ISPEC_CPU_COUNT] = self.be_new.get(constants.BE_VCPUS,
3804 22b7f6f8 Thomas Thrainer
                                                         None)
3805 22b7f6f8 Thomas Thrainer
3806 22b7f6f8 Thomas Thrainer
      # Copy ispec to verify parameters with min/max values separately
3807 22b7f6f8 Thomas Thrainer
      if self.op.disk_template:
3808 22b7f6f8 Thomas Thrainer
        new_disk_template = self.op.disk_template
3809 22b7f6f8 Thomas Thrainer
      else:
3810 22b7f6f8 Thomas Thrainer
        new_disk_template = instance.disk_template
3811 22b7f6f8 Thomas Thrainer
      ispec_max = ispec.copy()
3812 22b7f6f8 Thomas Thrainer
      ispec_max[constants.ISPEC_MEM_SIZE] = \
3813 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_MAXMEM, None)
3814 22b7f6f8 Thomas Thrainer
      res_max = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec_max,
3815 22b7f6f8 Thomas Thrainer
                                                     new_disk_template)
3816 22b7f6f8 Thomas Thrainer
      ispec_min = ispec.copy()
3817 22b7f6f8 Thomas Thrainer
      ispec_min[constants.ISPEC_MEM_SIZE] = \
3818 22b7f6f8 Thomas Thrainer
        self.be_new.get(constants.BE_MINMEM, None)
3819 22b7f6f8 Thomas Thrainer
      res_min = _ComputeIPolicyInstanceSpecViolation(ipolicy, ispec_min,
3820 22b7f6f8 Thomas Thrainer
                                                     new_disk_template)
3821 22b7f6f8 Thomas Thrainer
3822 22b7f6f8 Thomas Thrainer
      if (res_max or res_min):
3823 22b7f6f8 Thomas Thrainer
        # FIXME: Improve error message by including information about whether
3824 22b7f6f8 Thomas Thrainer
        # the upper or lower limit of the parameter fails the ipolicy.
3825 22b7f6f8 Thomas Thrainer
        msg = ("Instance allocation to group %s (%s) violates policy: %s" %
3826 22b7f6f8 Thomas Thrainer
               (group_info, group_info.name,
3827 22b7f6f8 Thomas Thrainer
                utils.CommaJoin(set(res_max + res_min))))
3828 22b7f6f8 Thomas Thrainer
        raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
3829 22b7f6f8 Thomas Thrainer
3830 22b7f6f8 Thomas Thrainer
  def _ConvertPlainToDrbd(self, feedback_fn):
3831 22b7f6f8 Thomas Thrainer
    """Converts an instance from plain to drbd.
3832 22b7f6f8 Thomas Thrainer

3833 22b7f6f8 Thomas Thrainer
    """
3834 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to drbd")
3835 22b7f6f8 Thomas Thrainer
    instance = self.instance
3836 22b7f6f8 Thomas Thrainer
    pnode = instance.primary_node
3837 22b7f6f8 Thomas Thrainer
    snode = self.op.remote_node
3838 22b7f6f8 Thomas Thrainer
3839 22b7f6f8 Thomas Thrainer
    assert instance.disk_template == constants.DT_PLAIN
3840 22b7f6f8 Thomas Thrainer
3841 22b7f6f8 Thomas Thrainer
    # create a fake disk info for _GenerateDiskTemplate
3842 22b7f6f8 Thomas Thrainer
    disk_info = [{constants.IDISK_SIZE: d.size, constants.IDISK_MODE: d.mode,
3843 22b7f6f8 Thomas Thrainer
                  constants.IDISK_VG: d.logical_id[0],
3844 22b7f6f8 Thomas Thrainer
                  constants.IDISK_NAME: d.name}
3845 22b7f6f8 Thomas Thrainer
                 for d in instance.disks]
3846 22b7f6f8 Thomas Thrainer
    new_disks = _GenerateDiskTemplate(self, self.op.disk_template,
3847 22b7f6f8 Thomas Thrainer
                                      instance.name, pnode, [snode],
3848 22b7f6f8 Thomas Thrainer
                                      disk_info, None, None, 0, feedback_fn,
3849 22b7f6f8 Thomas Thrainer
                                      self.diskparams)
3850 22b7f6f8 Thomas Thrainer
    anno_disks = rpc.AnnotateDiskParams(constants.DT_DRBD8, new_disks,
3851 22b7f6f8 Thomas Thrainer
                                        self.diskparams)
3852 22b7f6f8 Thomas Thrainer
    p_excl_stor = _IsExclusiveStorageEnabledNodeName(self.cfg, pnode)
3853 22b7f6f8 Thomas Thrainer
    s_excl_stor = _IsExclusiveStorageEnabledNodeName(self.cfg, snode)
3854 22b7f6f8 Thomas Thrainer
    info = _GetInstanceInfoText(instance)
3855 22b7f6f8 Thomas Thrainer
    feedback_fn("Creating additional volumes...")
3856 22b7f6f8 Thomas Thrainer
    # first, create the missing data and meta devices
3857 22b7f6f8 Thomas Thrainer
    for disk in anno_disks:
3858 22b7f6f8 Thomas Thrainer
      # unfortunately this is... not too nice
3859 22b7f6f8 Thomas Thrainer
      _CreateSingleBlockDev(self, pnode, instance, disk.children[1],
3860 22b7f6f8 Thomas Thrainer
                            info, True, p_excl_stor)
3861 22b7f6f8 Thomas Thrainer
      for child in disk.children:
3862 22b7f6f8 Thomas Thrainer
        _CreateSingleBlockDev(self, snode, instance, child, info, True,
3863 22b7f6f8 Thomas Thrainer
                              s_excl_stor)
3864 22b7f6f8 Thomas Thrainer
    # at this stage, all new LVs have been created, we can rename the
3865 22b7f6f8 Thomas Thrainer
    # old ones
3866 22b7f6f8 Thomas Thrainer
    feedback_fn("Renaming original volumes...")
3867 22b7f6f8 Thomas Thrainer
    rename_list = [(o, n.children[0].logical_id)
3868 22b7f6f8 Thomas Thrainer
                   for (o, n) in zip(instance.disks, new_disks)]
3869 22b7f6f8 Thomas Thrainer
    result = self.rpc.call_blockdev_rename(pnode, rename_list)
3870 22b7f6f8 Thomas Thrainer
    result.Raise("Failed to rename original LVs")
3871 22b7f6f8 Thomas Thrainer
3872 22b7f6f8 Thomas Thrainer
    feedback_fn("Initializing DRBD devices...")
3873 22b7f6f8 Thomas Thrainer
    # all child devices are in place, we can now create the DRBD devices
3874 22b7f6f8 Thomas Thrainer
    try:
3875 22b7f6f8 Thomas Thrainer
      for disk in anno_disks:
3876 22b7f6f8 Thomas Thrainer
        for (node, excl_stor) in [(pnode, p_excl_stor), (snode, s_excl_stor)]:
3877 22b7f6f8 Thomas Thrainer
          f_create = node == pnode
3878 22b7f6f8 Thomas Thrainer
          _CreateSingleBlockDev(self, node, instance, disk, info, f_create,
3879 22b7f6f8 Thomas Thrainer
                                excl_stor)
3880 22b7f6f8 Thomas Thrainer
    except errors.GenericError, e:
3881 22b7f6f8 Thomas Thrainer
      feedback_fn("Initializing of DRBD devices failed;"
3882 22b7f6f8 Thomas Thrainer
                  " renaming back original volumes...")
3883 22b7f6f8 Thomas Thrainer
      for disk in new_disks:
3884 22b7f6f8 Thomas Thrainer
        self.cfg.SetDiskID(disk, pnode)
3885 22b7f6f8 Thomas Thrainer
      rename_back_list = [(n.children[0], o.logical_id)
3886 22b7f6f8 Thomas Thrainer
                          for (n, o) in zip(new_disks, instance.disks)]
3887 22b7f6f8 Thomas Thrainer
      result = self.rpc.call_blockdev_rename(pnode, rename_back_list)
3888 22b7f6f8 Thomas Thrainer
      result.Raise("Failed to rename LVs back after error %s" % str(e))
3889 22b7f6f8 Thomas Thrainer
      raise
3890 22b7f6f8 Thomas Thrainer
3891 22b7f6f8 Thomas Thrainer
    # at this point, the instance has been modified
3892 22b7f6f8 Thomas Thrainer
    instance.disk_template = constants.DT_DRBD8
3893 22b7f6f8 Thomas Thrainer
    instance.disks = new_disks
3894 22b7f6f8 Thomas Thrainer
    self.cfg.Update(instance, feedback_fn)
3895 22b7f6f8 Thomas Thrainer
3896 22b7f6f8 Thomas Thrainer
    # Release node locks while waiting for sync
3897 22b7f6f8 Thomas Thrainer
    _ReleaseLocks(self, locking.LEVEL_NODE)
3898 22b7f6f8 Thomas Thrainer
3899 22b7f6f8 Thomas Thrainer
    # disks are created, waiting for sync
3900 22b7f6f8 Thomas Thrainer
    disk_abort = not _WaitForSync(self, instance,
3901 22b7f6f8 Thomas Thrainer
                                  oneshot=not self.op.wait_for_sync)
3902 22b7f6f8 Thomas Thrainer
    if disk_abort:
3903 22b7f6f8 Thomas Thrainer
      raise errors.OpExecError("There are some degraded disks for"
3904 22b7f6f8 Thomas Thrainer
                               " this instance, please cleanup manually")
3905 22b7f6f8 Thomas Thrainer
3906 22b7f6f8 Thomas Thrainer
    # Node resource locks will be released by caller
3907 22b7f6f8 Thomas Thrainer
3908 22b7f6f8 Thomas Thrainer
  def _ConvertDrbdToPlain(self, feedback_fn):
3909 22b7f6f8 Thomas Thrainer
    """Converts an instance from drbd to plain.
3910 22b7f6f8 Thomas Thrainer

3911 22b7f6f8 Thomas Thrainer
    """
3912 22b7f6f8 Thomas Thrainer
    instance = self.instance
3913 22b7f6f8 Thomas Thrainer
3914 22b7f6f8 Thomas Thrainer
    assert len(instance.secondary_nodes) == 1
3915 22b7f6f8 Thomas Thrainer
    assert instance.disk_template == constants.DT_DRBD8
3916 22b7f6f8 Thomas Thrainer
3917 22b7f6f8 Thomas Thrainer
    pnode = instance.primary_node
3918 22b7f6f8 Thomas Thrainer
    snode = instance.secondary_nodes[0]
3919 22b7f6f8 Thomas Thrainer
    feedback_fn("Converting template to plain")
3920 22b7f6f8 Thomas Thrainer
3921 22b7f6f8 Thomas Thrainer
    old_disks = _AnnotateDiskParams(instance, instance.disks, self.cfg)
3922 22b7f6f8 Thomas Thrainer
    new_disks = [d.children[0] for d in instance.disks]
3923 22b7f6f8 Thomas Thrainer
3924 22b7f6f8 Thomas Thrainer
    # copy over size, mode and name
3925 22b7f6f8 Thomas Thrainer
    for parent, child in zip(old_disks, new_disks):
3926 22b7f6f8 Thomas Thrainer
      child.size = parent.size
3927 22b7f6f8 Thomas Thrainer
      child.mode = parent.mode
3928 22b7f6f8 Thomas Thrainer
      child.name = parent.name
3929 22b7f6f8 Thomas Thrainer
3930 22b7f6f8 Thomas Thrainer
    # this is a DRBD disk, return its port to the pool
3931 22b7f6f8 Thomas Thrainer
    # NOTE: this must be done right before the call to cfg.Update!
3932 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3933 22b7f6f8 Thomas Thrainer
      tcp_port = disk.logical_id[2]
3934 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(tcp_port)
3935 22b7f6f8 Thomas Thrainer
3936 22b7f6f8 Thomas Thrainer
    # update instance structure
3937 22b7f6f8 Thomas Thrainer
    instance.disks = new_disks
3938 22b7f6f8 Thomas Thrainer
    instance.disk_template = constants.DT_PLAIN
3939 22b7f6f8 Thomas Thrainer
    _UpdateIvNames(0, instance.disks)
3940 22b7f6f8 Thomas Thrainer
    self.cfg.Update(instance, feedback_fn)
3941 22b7f6f8 Thomas Thrainer
3942 22b7f6f8 Thomas Thrainer
    # Release locks in case removing disks takes a while
3943 22b7f6f8 Thomas Thrainer
    _ReleaseLocks(self, locking.LEVEL_NODE)
3944 22b7f6f8 Thomas Thrainer
3945 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing volumes on the secondary node...")
3946 22b7f6f8 Thomas Thrainer
    for disk in old_disks:
3947 22b7f6f8 Thomas Thrainer
      self.cfg.SetDiskID(disk, snode)
3948 22b7f6f8 Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(snode, disk).fail_msg
3949 22b7f6f8 Thomas Thrainer
      if msg:
3950 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove block device %s on node %s,"
3951 22b7f6f8 Thomas Thrainer
                        " continuing anyway: %s", disk.iv_name, snode, msg)
3952 22b7f6f8 Thomas Thrainer
3953 22b7f6f8 Thomas Thrainer
    feedback_fn("Removing unneeded volumes on the primary node...")
3954 22b7f6f8 Thomas Thrainer
    for idx, disk in enumerate(old_disks):
3955 22b7f6f8 Thomas Thrainer
      meta = disk.children[1]
3956 22b7f6f8 Thomas Thrainer
      self.cfg.SetDiskID(meta, pnode)
3957 22b7f6f8 Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(pnode, meta).fail_msg
3958 22b7f6f8 Thomas Thrainer
      if msg:
3959 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove metadata for disk %d on node %s,"
3960 22b7f6f8 Thomas Thrainer
                        " continuing anyway: %s", idx, pnode, msg)
3961 22b7f6f8 Thomas Thrainer
3962 22b7f6f8 Thomas Thrainer
  def _CreateNewDisk(self, idx, params, _):
3963 22b7f6f8 Thomas Thrainer
    """Creates a new disk.
3964 22b7f6f8 Thomas Thrainer

3965 22b7f6f8 Thomas Thrainer
    """
3966 22b7f6f8 Thomas Thrainer
    instance = self.instance
3967 22b7f6f8 Thomas Thrainer
3968 22b7f6f8 Thomas Thrainer
    # add a new disk
3969 22b7f6f8 Thomas Thrainer
    if instance.disk_template in constants.DTS_FILEBASED:
3970 22b7f6f8 Thomas Thrainer
      (file_driver, file_path) = instance.disks[0].logical_id
3971 22b7f6f8 Thomas Thrainer
      file_path = os.path.dirname(file_path)
3972 22b7f6f8 Thomas Thrainer
    else:
3973 22b7f6f8 Thomas Thrainer
      file_driver = file_path = None
3974 22b7f6f8 Thomas Thrainer
3975 22b7f6f8 Thomas Thrainer
    disk = \
3976 22b7f6f8 Thomas Thrainer
      _GenerateDiskTemplate(self, instance.disk_template, instance.name,
3977 22b7f6f8 Thomas Thrainer
                            instance.primary_node, instance.secondary_nodes,
3978 22b7f6f8 Thomas Thrainer
                            [params], file_path, file_driver, idx,
3979 22b7f6f8 Thomas Thrainer
                            self.Log, self.diskparams)[0]
3980 22b7f6f8 Thomas Thrainer
3981 22b7f6f8 Thomas Thrainer
    info = _GetInstanceInfoText(instance)
3982 22b7f6f8 Thomas Thrainer
3983 22b7f6f8 Thomas Thrainer
    logging.info("Creating volume %s for instance %s",
3984 22b7f6f8 Thomas Thrainer
                 disk.iv_name, instance.name)
3985 22b7f6f8 Thomas Thrainer
    # Note: this needs to be kept in sync with _CreateDisks
3986 22b7f6f8 Thomas Thrainer
    #HARDCODE
3987 22b7f6f8 Thomas Thrainer
    for node in instance.all_nodes:
3988 22b7f6f8 Thomas Thrainer
      f_create = (node == instance.primary_node)
3989 22b7f6f8 Thomas Thrainer
      try:
3990 22b7f6f8 Thomas Thrainer
        _CreateBlockDev(self, node, instance, disk, f_create, info, f_create)
3991 22b7f6f8 Thomas Thrainer
      except errors.OpExecError, err:
3992 22b7f6f8 Thomas Thrainer
        self.LogWarning("Failed to create volume %s (%s) on node '%s': %s",
3993 22b7f6f8 Thomas Thrainer
                        disk.iv_name, disk, node, err)
3994 22b7f6f8 Thomas Thrainer
3995 22b7f6f8 Thomas Thrainer
    if self.cluster.prealloc_wipe_disks:
3996 22b7f6f8 Thomas Thrainer
      # Wipe new disk
3997 22b7f6f8 Thomas Thrainer
      _WipeDisks(self, instance,
3998 22b7f6f8 Thomas Thrainer
                 disks=[(idx, disk, 0)])
3999 22b7f6f8 Thomas Thrainer
4000 22b7f6f8 Thomas Thrainer
    return (disk, [
4001 22b7f6f8 Thomas Thrainer
      ("disk/%d" % idx, "add:size=%s,mode=%s" % (disk.size, disk.mode)),
4002 22b7f6f8 Thomas Thrainer
      ])
4003 22b7f6f8 Thomas Thrainer
4004 22b7f6f8 Thomas Thrainer
  @staticmethod
4005 22b7f6f8 Thomas Thrainer
  def _ModifyDisk(idx, disk, params, _):
4006 22b7f6f8 Thomas Thrainer
    """Modifies a disk.
4007 22b7f6f8 Thomas Thrainer

4008 22b7f6f8 Thomas Thrainer
    """
4009 22b7f6f8 Thomas Thrainer
    changes = []
4010 22b7f6f8 Thomas Thrainer
    mode = params.get(constants.IDISK_MODE, None)
4011 22b7f6f8 Thomas Thrainer
    if mode:
4012 22b7f6f8 Thomas Thrainer
      disk.mode = mode
4013 22b7f6f8 Thomas Thrainer
      changes.append(("disk.mode/%d" % idx, disk.mode))
4014 22b7f6f8 Thomas Thrainer
4015 22b7f6f8 Thomas Thrainer
    name = params.get(constants.IDISK_NAME, None)
4016 22b7f6f8 Thomas Thrainer
    disk.name = name
4017 22b7f6f8 Thomas Thrainer
    changes.append(("disk.name/%d" % idx, disk.name))
4018 22b7f6f8 Thomas Thrainer
4019 22b7f6f8 Thomas Thrainer
    return changes
4020 22b7f6f8 Thomas Thrainer
4021 22b7f6f8 Thomas Thrainer
  def _RemoveDisk(self, idx, root, _):
4022 22b7f6f8 Thomas Thrainer
    """Removes a disk.
4023 22b7f6f8 Thomas Thrainer

4024 22b7f6f8 Thomas Thrainer
    """
4025 22b7f6f8 Thomas Thrainer
    (anno_disk,) = _AnnotateDiskParams(self.instance, [root], self.cfg)
4026 22b7f6f8 Thomas Thrainer
    for node, disk in anno_disk.ComputeNodeTree(self.instance.primary_node):
4027 22b7f6f8 Thomas Thrainer
      self.cfg.SetDiskID(disk, node)
4028 22b7f6f8 Thomas Thrainer
      msg = self.rpc.call_blockdev_remove(node, disk).fail_msg
4029 22b7f6f8 Thomas Thrainer
      if msg:
4030 22b7f6f8 Thomas Thrainer
        self.LogWarning("Could not remove disk/%d on node '%s': %s,"
4031 22b7f6f8 Thomas Thrainer
                        " continuing anyway", idx, node, msg)
4032 22b7f6f8 Thomas Thrainer
4033 22b7f6f8 Thomas Thrainer
    # if this is a DRBD disk, return its port to the pool
4034 22b7f6f8 Thomas Thrainer
    if root.dev_type in constants.LDS_DRBD:
4035 22b7f6f8 Thomas Thrainer
      self.cfg.AddTcpUdpPort(root.logical_id[2])
4036 22b7f6f8 Thomas Thrainer
4037 22b7f6f8 Thomas Thrainer
  def _CreateNewNic(self, idx, params, private):
4038 22b7f6f8 Thomas Thrainer
    """Creates data structure for a new network interface.
4039 22b7f6f8 Thomas Thrainer

4040 22b7f6f8 Thomas Thrainer
    """
4041 22b7f6f8 Thomas Thrainer
    mac = params[constants.INIC_MAC]
4042 22b7f6f8 Thomas Thrainer
    ip = params.get(constants.INIC_IP, None)
4043 22b7f6f8 Thomas Thrainer
    net = params.get(constants.INIC_NETWORK, None)
4044 22b7f6f8 Thomas Thrainer
    name = params.get(constants.INIC_NAME, None)
4045 22b7f6f8 Thomas Thrainer
    net_uuid = self.cfg.LookupNetwork(net)
4046 22b7f6f8 Thomas Thrainer
    #TODO: not private.filled?? can a nic have no nicparams??
4047 22b7f6f8 Thomas Thrainer
    nicparams = private.filled
4048 22b7f6f8 Thomas Thrainer
    nobj = objects.NIC(mac=mac, ip=ip, network=net_uuid, name=name,
4049 22b7f6f8 Thomas Thrainer
                       nicparams=nicparams)
4050 22b7f6f8 Thomas Thrainer
    nobj.uuid = self.cfg.GenerateUniqueID(self.proc.GetECId())
4051 22b7f6f8 Thomas Thrainer
4052 22b7f6f8 Thomas Thrainer
    return (nobj, [
4053 22b7f6f8 Thomas Thrainer
      ("nic.%d" % idx,
4054 22b7f6f8 Thomas Thrainer
       "add:mac=%s,ip=%s,mode=%s,link=%s,network=%s" %
4055 22b7f6f8 Thomas Thrainer
       (mac, ip, private.filled[constants.NIC_MODE],
4056 22b7f6f8 Thomas Thrainer
       private.filled[constants.NIC_LINK],
4057 22b7f6f8 Thomas Thrainer
       net)),
4058 22b7f6f8 Thomas Thrainer
      ])
4059 22b7f6f8 Thomas Thrainer
4060 22b7f6f8 Thomas Thrainer
  def _ApplyNicMods(self, idx, nic, params, private):
4061 22b7f6f8 Thomas Thrainer
    """Modifies a network interface.
4062 22b7f6f8 Thomas Thrainer

4063 22b7f6f8 Thomas Thrainer
    """
4064 22b7f6f8 Thomas Thrainer
    changes = []
4065 22b7f6f8 Thomas Thrainer
4066 22b7f6f8 Thomas Thrainer
    for key in [constants.INIC_MAC, constants.INIC_IP, constants.INIC_NAME]:
4067 22b7f6f8 Thomas Thrainer
      if key in params:
4068 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), params[key]))
4069 22b7f6f8 Thomas Thrainer
        setattr(nic, key, params[key])
4070 22b7f6f8 Thomas Thrainer
4071 22b7f6f8 Thomas Thrainer
    new_net = params.get(constants.INIC_NETWORK, nic.network)
4072 22b7f6f8 Thomas Thrainer
    new_net_uuid = self.cfg.LookupNetwork(new_net)
4073 22b7f6f8 Thomas Thrainer
    if new_net_uuid != nic.network:
4074 22b7f6f8 Thomas Thrainer
      changes.append(("nic.network/%d" % idx, new_net))
4075 22b7f6f8 Thomas Thrainer
      nic.network = new_net_uuid
4076 22b7f6f8 Thomas Thrainer
4077 22b7f6f8 Thomas Thrainer
    if private.filled:
4078 22b7f6f8 Thomas Thrainer
      nic.nicparams = private.filled
4079 22b7f6f8 Thomas Thrainer
4080 22b7f6f8 Thomas Thrainer
      for (key, val) in nic.nicparams.items():
4081 22b7f6f8 Thomas Thrainer
        changes.append(("nic.%s/%d" % (key, idx), val))
4082 22b7f6f8 Thomas Thrainer
4083 22b7f6f8 Thomas Thrainer
    return changes
4084 22b7f6f8 Thomas Thrainer
4085 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
4086 22b7f6f8 Thomas Thrainer
    """Modifies an instance.
4087 22b7f6f8 Thomas Thrainer

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

4090 22b7f6f8 Thomas Thrainer
    """
4091 22b7f6f8 Thomas Thrainer
    # Process here the warnings from CheckPrereq, as we don't have a
4092 22b7f6f8 Thomas Thrainer
    # feedback_fn there.
4093 22b7f6f8 Thomas Thrainer
    # TODO: Replace with self.LogWarning
4094 22b7f6f8 Thomas Thrainer
    for warn in self.warn:
4095 22b7f6f8 Thomas Thrainer
      feedback_fn("WARNING: %s" % warn)
4096 22b7f6f8 Thomas Thrainer
4097 22b7f6f8 Thomas Thrainer
    assert ((self.op.disk_template is None) ^
4098 22b7f6f8 Thomas Thrainer
            bool(self.owned_locks(locking.LEVEL_NODE_RES))), \
4099 22b7f6f8 Thomas Thrainer
      "Not owning any node resource locks"
4100 22b7f6f8 Thomas Thrainer
4101 22b7f6f8 Thomas Thrainer
    result = []
4102 22b7f6f8 Thomas Thrainer
    instance = self.instance
4103 22b7f6f8 Thomas Thrainer
4104 22b7f6f8 Thomas Thrainer
    # New primary node
4105 22b7f6f8 Thomas Thrainer
    if self.op.pnode:
4106 22b7f6f8 Thomas Thrainer
      instance.primary_node = self.op.pnode
4107 22b7f6f8 Thomas Thrainer
4108 22b7f6f8 Thomas Thrainer
    # runtime memory
4109 22b7f6f8 Thomas Thrainer
    if self.op.runtime_mem:
4110 22b7f6f8 Thomas Thrainer
      rpcres = self.rpc.call_instance_balloon_memory(instance.primary_node,
4111 22b7f6f8 Thomas Thrainer
                                                     instance,
4112 22b7f6f8 Thomas Thrainer
                                                     self.op.runtime_mem)
4113 22b7f6f8 Thomas Thrainer
      rpcres.Raise("Cannot modify instance runtime memory")
4114 22b7f6f8 Thomas Thrainer
      result.append(("runtime_memory", self.op.runtime_mem))
4115 22b7f6f8 Thomas Thrainer
4116 22b7f6f8 Thomas Thrainer
    # Apply disk changes
4117 22b7f6f8 Thomas Thrainer
    ApplyContainerMods("disk", instance.disks, result, self.diskmod,
4118 22b7f6f8 Thomas Thrainer
                       self._CreateNewDisk, self._ModifyDisk, self._RemoveDisk)
4119 22b7f6f8 Thomas Thrainer
    _UpdateIvNames(0, instance.disks)
4120 22b7f6f8 Thomas Thrainer
4121 22b7f6f8 Thomas Thrainer
    if self.op.disk_template:
4122 22b7f6f8 Thomas Thrainer
      if __debug__:
4123 22b7f6f8 Thomas Thrainer
        check_nodes = set(instance.all_nodes)
4124 22b7f6f8 Thomas Thrainer
        if self.op.remote_node:
4125 22b7f6f8 Thomas Thrainer
          check_nodes.add(self.op.remote_node)
4126 22b7f6f8 Thomas Thrainer
        for level in [locking.LEVEL_NODE, locking.LEVEL_NODE_RES]:
4127 22b7f6f8 Thomas Thrainer
          owned = self.owned_locks(level)
4128 22b7f6f8 Thomas Thrainer
          assert not (check_nodes - owned), \
4129 22b7f6f8 Thomas Thrainer
            ("Not owning the correct locks, owning %r, expected at least %r" %
4130 22b7f6f8 Thomas Thrainer
             (owned, check_nodes))
4131 22b7f6f8 Thomas Thrainer
4132 22b7f6f8 Thomas Thrainer
      r_shut = _ShutdownInstanceDisks(self, instance)
4133 22b7f6f8 Thomas Thrainer
      if not r_shut:
4134 22b7f6f8 Thomas Thrainer
        raise errors.OpExecError("Cannot shutdown instance disks, unable to"
4135 22b7f6f8 Thomas Thrainer
                                 " proceed with disk template conversion")
4136 22b7f6f8 Thomas Thrainer
      mode = (instance.disk_template, self.op.disk_template)
4137 22b7f6f8 Thomas Thrainer
      try:
4138 22b7f6f8 Thomas Thrainer
        self._DISK_CONVERSIONS[mode](self, feedback_fn)
4139 22b7f6f8 Thomas Thrainer
      except:
4140 22b7f6f8 Thomas Thrainer
        self.cfg.ReleaseDRBDMinors(instance.name)
4141 22b7f6f8 Thomas Thrainer
        raise
4142 22b7f6f8 Thomas Thrainer
      result.append(("disk_template", self.op.disk_template))
4143 22b7f6f8 Thomas Thrainer
4144 22b7f6f8 Thomas Thrainer
      assert instance.disk_template == self.op.disk_template, \
4145 22b7f6f8 Thomas Thrainer
        ("Expected disk template '%s', found '%s'" %
4146 22b7f6f8 Thomas Thrainer
         (self.op.disk_template, instance.disk_template))
4147 22b7f6f8 Thomas Thrainer
4148 22b7f6f8 Thomas Thrainer
    # Release node and resource locks if there are any (they might already have
4149 22b7f6f8 Thomas Thrainer
    # been released during disk conversion)
4150 22b7f6f8 Thomas Thrainer
    _ReleaseLocks(self, locking.LEVEL_NODE)
4151 22b7f6f8 Thomas Thrainer
    _ReleaseLocks(self, locking.LEVEL_NODE_RES)
4152 22b7f6f8 Thomas Thrainer
4153 22b7f6f8 Thomas Thrainer
    # Apply NIC changes
4154 22b7f6f8 Thomas Thrainer
    if self._new_nics is not None:
4155 22b7f6f8 Thomas Thrainer
      instance.nics = self._new_nics
4156 22b7f6f8 Thomas Thrainer
      result.extend(self._nic_chgdesc)
4157 22b7f6f8 Thomas Thrainer
4158 22b7f6f8 Thomas Thrainer
    # hvparams changes
4159 22b7f6f8 Thomas Thrainer
    if self.op.hvparams:
4160 22b7f6f8 Thomas Thrainer
      instance.hvparams = self.hv_inst
4161 22b7f6f8 Thomas Thrainer
      for key, val in self.op.hvparams.iteritems():
4162 22b7f6f8 Thomas Thrainer
        result.append(("hv/%s" % key, val))
4163 22b7f6f8 Thomas Thrainer
4164 22b7f6f8 Thomas Thrainer
    # beparams changes
4165 22b7f6f8 Thomas Thrainer
    if self.op.beparams:
4166 22b7f6f8 Thomas Thrainer
      instance.beparams = self.be_inst
4167 22b7f6f8 Thomas Thrainer
      for key, val in self.op.beparams.iteritems():
4168 22b7f6f8 Thomas Thrainer
        result.append(("be/%s" % key, val))
4169 22b7f6f8 Thomas Thrainer
4170 22b7f6f8 Thomas Thrainer
    # OS change
4171 22b7f6f8 Thomas Thrainer
    if self.op.os_name:
4172 22b7f6f8 Thomas Thrainer
      instance.os = self.op.os_name
4173 22b7f6f8 Thomas Thrainer
4174 22b7f6f8 Thomas Thrainer
    # osparams changes
4175 22b7f6f8 Thomas Thrainer
    if self.op.osparams:
4176 22b7f6f8 Thomas Thrainer
      instance.osparams = self.os_inst
4177 22b7f6f8 Thomas Thrainer
      for key, val in self.op.osparams.iteritems():
4178 22b7f6f8 Thomas Thrainer
        result.append(("os/%s" % key, val))
4179 22b7f6f8 Thomas Thrainer
4180 22b7f6f8 Thomas Thrainer
    if self.op.offline is None:
4181 22b7f6f8 Thomas Thrainer
      # Ignore
4182 22b7f6f8 Thomas Thrainer
      pass
4183 22b7f6f8 Thomas Thrainer
    elif self.op.offline:
4184 22b7f6f8 Thomas Thrainer
      # Mark instance as offline
4185 22b7f6f8 Thomas Thrainer
      self.cfg.MarkInstanceOffline(instance.name)
4186 22b7f6f8 Thomas Thrainer
      result.append(("admin_state", constants.ADMINST_OFFLINE))
4187 22b7f6f8 Thomas Thrainer
    else:
4188 22b7f6f8 Thomas Thrainer
      # Mark instance as online, but stopped
4189 22b7f6f8 Thomas Thrainer
      self.cfg.MarkInstanceDown(instance.name)
4190 22b7f6f8 Thomas Thrainer
      result.append(("admin_state", constants.ADMINST_DOWN))
4191 22b7f6f8 Thomas Thrainer
4192 22b7f6f8 Thomas Thrainer
    self.cfg.Update(instance, feedback_fn, self.proc.GetECId())
4193 22b7f6f8 Thomas Thrainer
4194 22b7f6f8 Thomas Thrainer
    assert not (self.owned_locks(locking.LEVEL_NODE_RES) or
4195 22b7f6f8 Thomas Thrainer
                self.owned_locks(locking.LEVEL_NODE)), \
4196 22b7f6f8 Thomas Thrainer
      "All node locks should have been released by now"
4197 22b7f6f8 Thomas Thrainer
4198 22b7f6f8 Thomas Thrainer
    return result
4199 22b7f6f8 Thomas Thrainer
4200 22b7f6f8 Thomas Thrainer
  _DISK_CONVERSIONS = {
4201 22b7f6f8 Thomas Thrainer
    (constants.DT_PLAIN, constants.DT_DRBD8): _ConvertPlainToDrbd,
4202 22b7f6f8 Thomas Thrainer
    (constants.DT_DRBD8, constants.DT_PLAIN): _ConvertDrbdToPlain,
4203 22b7f6f8 Thomas Thrainer
    }
4204 22b7f6f8 Thomas Thrainer
4205 22b7f6f8 Thomas Thrainer
4206 22b7f6f8 Thomas Thrainer
class LUInstanceChangeGroup(LogicalUnit):
4207 22b7f6f8 Thomas Thrainer
  HPATH = "instance-change-group"
4208 22b7f6f8 Thomas Thrainer
  HTYPE = constants.HTYPE_INSTANCE
4209 22b7f6f8 Thomas Thrainer
  REQ_BGL = False
4210 22b7f6f8 Thomas Thrainer
4211 22b7f6f8 Thomas Thrainer
  def ExpandNames(self):
4212 22b7f6f8 Thomas Thrainer
    self.share_locks = _ShareAll()
4213 22b7f6f8 Thomas Thrainer
4214 22b7f6f8 Thomas Thrainer
    self.needed_locks = {
4215 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODEGROUP: [],
4216 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE: [],
4217 22b7f6f8 Thomas Thrainer
      locking.LEVEL_NODE_ALLOC: locking.ALL_SET,
4218 22b7f6f8 Thomas Thrainer
      }
4219 22b7f6f8 Thomas Thrainer
4220 22b7f6f8 Thomas Thrainer
    self._ExpandAndLockInstance()
4221 22b7f6f8 Thomas Thrainer
4222 22b7f6f8 Thomas Thrainer
    if self.op.target_groups:
4223 22b7f6f8 Thomas Thrainer
      self.req_target_uuids = map(self.cfg.LookupNodeGroup,
4224 22b7f6f8 Thomas Thrainer
                                  self.op.target_groups)
4225 22b7f6f8 Thomas Thrainer
    else:
4226 22b7f6f8 Thomas Thrainer
      self.req_target_uuids = None
4227 22b7f6f8 Thomas Thrainer
4228 22b7f6f8 Thomas Thrainer
    self.op.iallocator = _GetDefaultIAllocator(self.cfg, self.op.iallocator)
4229 22b7f6f8 Thomas Thrainer
4230 22b7f6f8 Thomas Thrainer
  def DeclareLocks(self, level):
4231 22b7f6f8 Thomas Thrainer
    if level == locking.LEVEL_NODEGROUP:
4232 22b7f6f8 Thomas Thrainer
      assert not self.needed_locks[locking.LEVEL_NODEGROUP]
4233 22b7f6f8 Thomas Thrainer
4234 22b7f6f8 Thomas Thrainer
      if self.req_target_uuids:
4235 22b7f6f8 Thomas Thrainer
        lock_groups = set(self.req_target_uuids)
4236 22b7f6f8 Thomas Thrainer
4237 22b7f6f8 Thomas Thrainer
        # Lock all groups used by instance optimistically; this requires going
4238 22b7f6f8 Thomas Thrainer
        # via the node before it's locked, requiring verification later on
4239 22b7f6f8 Thomas Thrainer
        instance_groups = self.cfg.GetInstanceNodeGroups(self.op.instance_name)
4240 22b7f6f8 Thomas Thrainer
        lock_groups.update(instance_groups)
4241 22b7f6f8 Thomas Thrainer
      else:
4242 22b7f6f8 Thomas Thrainer
        # No target groups, need to lock all of them
4243 22b7f6f8 Thomas Thrainer
        lock_groups = locking.ALL_SET
4244 22b7f6f8 Thomas Thrainer
4245 22b7f6f8 Thomas Thrainer
      self.needed_locks[locking.LEVEL_NODEGROUP] = lock_groups
4246 22b7f6f8 Thomas Thrainer
4247 22b7f6f8 Thomas Thrainer
    elif level == locking.LEVEL_NODE:
4248 22b7f6f8 Thomas Thrainer
      if self.req_target_uuids:
4249 22b7f6f8 Thomas Thrainer
        # Lock all nodes used by instances
4250 22b7f6f8 Thomas Thrainer
        self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_APPEND
4251 22b7f6f8 Thomas Thrainer
        self._LockInstancesNodes()
4252 22b7f6f8 Thomas Thrainer
4253 22b7f6f8 Thomas Thrainer
        # Lock all nodes in all potential target groups
4254 22b7f6f8 Thomas Thrainer
        lock_groups = (frozenset(self.owned_locks(locking.LEVEL_NODEGROUP)) -
4255 22b7f6f8 Thomas Thrainer
                       self.cfg.GetInstanceNodeGroups(self.op.instance_name))
4256 22b7f6f8 Thomas Thrainer
        member_nodes = [node_name
4257 22b7f6f8 Thomas Thrainer
                        for group in lock_groups
4258 22b7f6f8 Thomas Thrainer
                        for node_name in self.cfg.GetNodeGroup(group).members]
4259 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE].extend(member_nodes)
4260 22b7f6f8 Thomas Thrainer
      else:
4261 22b7f6f8 Thomas Thrainer
        # Lock all nodes as all groups are potential targets
4262 22b7f6f8 Thomas Thrainer
        self.needed_locks[locking.LEVEL_NODE] = locking.ALL_SET
4263 22b7f6f8 Thomas Thrainer
4264 22b7f6f8 Thomas Thrainer
  def CheckPrereq(self):
4265 22b7f6f8 Thomas Thrainer
    owned_instances = frozenset(self.owned_locks(locking.LEVEL_INSTANCE))
4266 22b7f6f8 Thomas Thrainer
    owned_groups = frozenset(self.owned_locks(locking.LEVEL_NODEGROUP))
4267 22b7f6f8 Thomas Thrainer
    owned_nodes = frozenset(self.owned_locks(locking.LEVEL_NODE))
4268 22b7f6f8 Thomas Thrainer
4269 22b7f6f8 Thomas Thrainer
    assert (self.req_target_uuids is None or
4270 22b7f6f8 Thomas Thrainer
            owned_groups.issuperset(self.req_target_uuids))
4271 22b7f6f8 Thomas Thrainer
    assert owned_instances == set([self.op.instance_name])
4272 22b7f6f8 Thomas Thrainer
4273 22b7f6f8 Thomas Thrainer
    # Get instance information
4274 22b7f6f8 Thomas Thrainer
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
4275 22b7f6f8 Thomas Thrainer
4276 22b7f6f8 Thomas Thrainer
    # Check if node groups for locked instance are still correct
4277 22b7f6f8 Thomas Thrainer
    assert owned_nodes.issuperset(self.instance.all_nodes), \
4278 22b7f6f8 Thomas Thrainer
      ("Instance %s's nodes changed while we kept the lock" %
4279 22b7f6f8 Thomas Thrainer
       self.op.instance_name)
4280 22b7f6f8 Thomas Thrainer
4281 22b7f6f8 Thomas Thrainer
    inst_groups = _CheckInstanceNodeGroups(self.cfg, self.op.instance_name,
4282 22b7f6f8 Thomas Thrainer
                                           owned_groups)
4283 22b7f6f8 Thomas Thrainer
4284 22b7f6f8 Thomas Thrainer
    if self.req_target_uuids:
4285 22b7f6f8 Thomas Thrainer
      # User requested specific target groups
4286 22b7f6f8 Thomas Thrainer
      self.target_uuids = frozenset(self.req_target_uuids)
4287 22b7f6f8 Thomas Thrainer
    else:
4288 22b7f6f8 Thomas Thrainer
      # All groups except those used by the instance are potential targets
4289 22b7f6f8 Thomas Thrainer
      self.target_uuids = owned_groups - inst_groups
4290 22b7f6f8 Thomas Thrainer
4291 22b7f6f8 Thomas Thrainer
    conflicting_groups = self.target_uuids & inst_groups
4292 22b7f6f8 Thomas Thrainer
    if conflicting_groups:
4293 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't use group(s) '%s' as targets, they are"
4294 22b7f6f8 Thomas Thrainer
                                 " used by the instance '%s'" %
4295 22b7f6f8 Thomas Thrainer
                                 (utils.CommaJoin(conflicting_groups),
4296 22b7f6f8 Thomas Thrainer
                                  self.op.instance_name),
4297 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
4298 22b7f6f8 Thomas Thrainer
4299 22b7f6f8 Thomas Thrainer
    if not self.target_uuids:
4300 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("There are no possible target groups",
4301 22b7f6f8 Thomas Thrainer
                                 errors.ECODE_INVAL)
4302 22b7f6f8 Thomas Thrainer
4303 22b7f6f8 Thomas Thrainer
  def BuildHooksEnv(self):
4304 22b7f6f8 Thomas Thrainer
    """Build hooks env.
4305 22b7f6f8 Thomas Thrainer

4306 22b7f6f8 Thomas Thrainer
    """
4307 22b7f6f8 Thomas Thrainer
    assert self.target_uuids
4308 22b7f6f8 Thomas Thrainer
4309 22b7f6f8 Thomas Thrainer
    env = {
4310 22b7f6f8 Thomas Thrainer
      "TARGET_GROUPS": " ".join(self.target_uuids),
4311 22b7f6f8 Thomas Thrainer
      }
4312 22b7f6f8 Thomas Thrainer
4313 22b7f6f8 Thomas Thrainer
    env.update(_BuildInstanceHookEnvByObject(self, self.instance))
4314 22b7f6f8 Thomas Thrainer
4315 22b7f6f8 Thomas Thrainer
    return env
4316 22b7f6f8 Thomas Thrainer
4317 22b7f6f8 Thomas Thrainer
  def BuildHooksNodes(self):
4318 22b7f6f8 Thomas Thrainer
    """Build hooks nodes.
4319 22b7f6f8 Thomas Thrainer

4320 22b7f6f8 Thomas Thrainer
    """
4321 22b7f6f8 Thomas Thrainer
    mn = self.cfg.GetMasterNode()
4322 22b7f6f8 Thomas Thrainer
    return ([mn], [mn])
4323 22b7f6f8 Thomas Thrainer
4324 22b7f6f8 Thomas Thrainer
  def Exec(self, feedback_fn):
4325 22b7f6f8 Thomas Thrainer
    instances = list(self.owned_locks(locking.LEVEL_INSTANCE))
4326 22b7f6f8 Thomas Thrainer
4327 22b7f6f8 Thomas Thrainer
    assert instances == [self.op.instance_name], "Instance not locked"
4328 22b7f6f8 Thomas Thrainer
4329 22b7f6f8 Thomas Thrainer
    req = iallocator.IAReqGroupChange(instances=instances,
4330 22b7f6f8 Thomas Thrainer
                                      target_groups=list(self.target_uuids))
4331 22b7f6f8 Thomas Thrainer
    ial = iallocator.IAllocator(self.cfg, self.rpc, req)
4332 22b7f6f8 Thomas Thrainer
4333 22b7f6f8 Thomas Thrainer
    ial.Run(self.op.iallocator)
4334 22b7f6f8 Thomas Thrainer
4335 22b7f6f8 Thomas Thrainer
    if not ial.success:
4336 22b7f6f8 Thomas Thrainer
      raise errors.OpPrereqError("Can't compute solution for changing group of"
4337 22b7f6f8 Thomas Thrainer
                                 " instance '%s' using iallocator '%s': %s" %
4338 22b7f6f8 Thomas Thrainer
                                 (self.op.instance_name, self.op.iallocator,
4339 22b7f6f8 Thomas Thrainer
                                  ial.info), errors.ECODE_NORES)
4340 22b7f6f8 Thomas Thrainer
4341 22b7f6f8 Thomas Thrainer
    jobs = _LoadNodeEvacResult(self, ial.result, self.op.early_release, False)
4342 22b7f6f8 Thomas Thrainer
4343 22b7f6f8 Thomas Thrainer
    self.LogInfo("Iallocator returned %s job(s) for changing group of"
4344 22b7f6f8 Thomas Thrainer
                 " instance '%s'", len(jobs), self.op.instance_name)
4345 22b7f6f8 Thomas Thrainer
4346 22b7f6f8 Thomas Thrainer
    return ResultWithJobs(jobs)