Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / common.py @ 3ad780e4

History | View | Annotate | Download (34.8 kB)

1 1a732a74 Thomas Thrainer
#
2 1a732a74 Thomas Thrainer
#
3 1a732a74 Thomas Thrainer
4 1a732a74 Thomas Thrainer
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5 1a732a74 Thomas Thrainer
#
6 1a732a74 Thomas Thrainer
# This program is free software; you can redistribute it and/or modify
7 1a732a74 Thomas Thrainer
# it under the terms of the GNU General Public License as published by
8 1a732a74 Thomas Thrainer
# the Free Software Foundation; either version 2 of the License, or
9 1a732a74 Thomas Thrainer
# (at your option) any later version.
10 1a732a74 Thomas Thrainer
#
11 1a732a74 Thomas Thrainer
# This program is distributed in the hope that it will be useful, but
12 1a732a74 Thomas Thrainer
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 1a732a74 Thomas Thrainer
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 1a732a74 Thomas Thrainer
# General Public License for more details.
15 1a732a74 Thomas Thrainer
#
16 1a732a74 Thomas Thrainer
# You should have received a copy of the GNU General Public License
17 1a732a74 Thomas Thrainer
# along with this program; if not, write to the Free Software
18 1a732a74 Thomas Thrainer
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 1a732a74 Thomas Thrainer
# 02110-1301, USA.
20 1a732a74 Thomas Thrainer
21 1a732a74 Thomas Thrainer
22 1a732a74 Thomas Thrainer
"""Common functions used by multiple logical units."""
23 f380d53c Thomas Thrainer
24 7352d33b Thomas Thrainer
import copy
25 7352d33b Thomas Thrainer
import os
26 1a732a74 Thomas Thrainer
27 f380d53c Thomas Thrainer
from ganeti import compat
28 7352d33b Thomas Thrainer
from ganeti import constants
29 1a732a74 Thomas Thrainer
from ganeti import errors
30 7352d33b Thomas Thrainer
from ganeti import hypervisor
31 fb3891d0 Thomas Thrainer
from ganeti import locking
32 7352d33b Thomas Thrainer
from ganeti import objects
33 f380d53c Thomas Thrainer
from ganeti import opcodes
34 7352d33b Thomas Thrainer
from ganeti import pathutils
35 7352d33b Thomas Thrainer
from ganeti import rpc
36 7352d33b Thomas Thrainer
from ganeti import ssconf
37 37dc17e3 Thomas Thrainer
from ganeti import utils
38 1a732a74 Thomas Thrainer
39 1a732a74 Thomas Thrainer
40 31b836b8 Thomas Thrainer
# States of instance
41 31b836b8 Thomas Thrainer
INSTANCE_DOWN = [constants.ADMINST_DOWN]
42 31b836b8 Thomas Thrainer
INSTANCE_ONLINE = [constants.ADMINST_DOWN, constants.ADMINST_UP]
43 31b836b8 Thomas Thrainer
INSTANCE_NOT_RUNNING = [constants.ADMINST_DOWN, constants.ADMINST_OFFLINE]
44 31b836b8 Thomas Thrainer
45 31b836b8 Thomas Thrainer
#: Instance status in which an instance can be marked as offline/online
46 31b836b8 Thomas Thrainer
CAN_CHANGE_INSTANCE_OFFLINE = (frozenset(INSTANCE_DOWN) | frozenset([
47 31b836b8 Thomas Thrainer
  constants.ADMINST_OFFLINE,
48 31b836b8 Thomas Thrainer
  ]))
49 31b836b8 Thomas Thrainer
50 31b836b8 Thomas Thrainer
51 1a732a74 Thomas Thrainer
def _ExpandItemName(fn, name, kind):
52 1a732a74 Thomas Thrainer
  """Expand an item name.
53 1a732a74 Thomas Thrainer

54 1a732a74 Thomas Thrainer
  @param fn: the function to use for expansion
55 1a732a74 Thomas Thrainer
  @param name: requested item name
56 1a732a74 Thomas Thrainer
  @param kind: text description ('Node' or 'Instance')
57 1a732a74 Thomas Thrainer
  @return: the resolved (full) name
58 1a732a74 Thomas Thrainer
  @raise errors.OpPrereqError: if the item is not found
59 1a732a74 Thomas Thrainer

60 1a732a74 Thomas Thrainer
  """
61 1a732a74 Thomas Thrainer
  full_name = fn(name)
62 1a732a74 Thomas Thrainer
  if full_name is None:
63 1a732a74 Thomas Thrainer
    raise errors.OpPrereqError("%s '%s' not known" % (kind, name),
64 1a732a74 Thomas Thrainer
                               errors.ECODE_NOENT)
65 1a732a74 Thomas Thrainer
  return full_name
66 1a732a74 Thomas Thrainer
67 1a732a74 Thomas Thrainer
68 5eacbcae Thomas Thrainer
def ExpandInstanceName(cfg, name):
69 1a732a74 Thomas Thrainer
  """Wrapper over L{_ExpandItemName} for instance."""
70 1a732a74 Thomas Thrainer
  return _ExpandItemName(cfg.ExpandInstanceName, name, "Instance")
71 1a732a74 Thomas Thrainer
72 1a732a74 Thomas Thrainer
73 5eacbcae Thomas Thrainer
def ExpandNodeName(cfg, name):
74 1a732a74 Thomas Thrainer
  """Wrapper over L{_ExpandItemName} for nodes."""
75 1a732a74 Thomas Thrainer
  return _ExpandItemName(cfg.ExpandNodeName, name, "Node")
76 fb3891d0 Thomas Thrainer
77 fb3891d0 Thomas Thrainer
78 5eacbcae Thomas Thrainer
def ShareAll():
79 fb3891d0 Thomas Thrainer
  """Returns a dict declaring all lock levels shared.
80 fb3891d0 Thomas Thrainer

81 fb3891d0 Thomas Thrainer
  """
82 fb3891d0 Thomas Thrainer
  return dict.fromkeys(locking.LEVELS, 1)
83 37dc17e3 Thomas Thrainer
84 37dc17e3 Thomas Thrainer
85 5eacbcae Thomas Thrainer
def CheckNodeGroupInstances(cfg, group_uuid, owned_instances):
86 37dc17e3 Thomas Thrainer
  """Checks if the instances in a node group are still correct.
87 37dc17e3 Thomas Thrainer

88 37dc17e3 Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
89 37dc17e3 Thomas Thrainer
  @param cfg: The cluster configuration
90 37dc17e3 Thomas Thrainer
  @type group_uuid: string
91 37dc17e3 Thomas Thrainer
  @param group_uuid: Node group UUID
92 37dc17e3 Thomas Thrainer
  @type owned_instances: set or frozenset
93 37dc17e3 Thomas Thrainer
  @param owned_instances: List of currently owned instances
94 37dc17e3 Thomas Thrainer

95 37dc17e3 Thomas Thrainer
  """
96 37dc17e3 Thomas Thrainer
  wanted_instances = cfg.GetNodeGroupInstances(group_uuid)
97 37dc17e3 Thomas Thrainer
  if owned_instances != wanted_instances:
98 37dc17e3 Thomas Thrainer
    raise errors.OpPrereqError("Instances in node group '%s' changed since"
99 37dc17e3 Thomas Thrainer
                               " locks were acquired, wanted '%s', have '%s';"
100 37dc17e3 Thomas Thrainer
                               " retry the operation" %
101 37dc17e3 Thomas Thrainer
                               (group_uuid,
102 37dc17e3 Thomas Thrainer
                                utils.CommaJoin(wanted_instances),
103 37dc17e3 Thomas Thrainer
                                utils.CommaJoin(owned_instances)),
104 37dc17e3 Thomas Thrainer
                               errors.ECODE_STATE)
105 37dc17e3 Thomas Thrainer
106 37dc17e3 Thomas Thrainer
  return wanted_instances
107 1d870e0d Thomas Thrainer
108 1d870e0d Thomas Thrainer
109 5eacbcae Thomas Thrainer
def GetWantedNodes(lu, nodes):
110 1d870e0d Thomas Thrainer
  """Returns list of checked and expanded node names.
111 1d870e0d Thomas Thrainer

112 1d870e0d Thomas Thrainer
  @type lu: L{LogicalUnit}
113 1d870e0d Thomas Thrainer
  @param lu: the logical unit on whose behalf we execute
114 1d870e0d Thomas Thrainer
  @type nodes: list
115 1d870e0d Thomas Thrainer
  @param nodes: list of node names or None for all nodes
116 1d870e0d Thomas Thrainer
  @rtype: list
117 1d870e0d Thomas Thrainer
  @return: the list of nodes, sorted
118 1d870e0d Thomas Thrainer
  @raise errors.ProgrammerError: if the nodes parameter is wrong type
119 1d870e0d Thomas Thrainer

120 1d870e0d Thomas Thrainer
  """
121 1d870e0d Thomas Thrainer
  if nodes:
122 5eacbcae Thomas Thrainer
    return [ExpandNodeName(lu.cfg, name) for name in nodes]
123 1d870e0d Thomas Thrainer
124 1d870e0d Thomas Thrainer
  return utils.NiceSort(lu.cfg.GetNodeList())
125 1d870e0d Thomas Thrainer
126 1d870e0d Thomas Thrainer
127 5eacbcae Thomas Thrainer
def GetWantedInstances(lu, instances):
128 1d870e0d Thomas Thrainer
  """Returns list of checked and expanded instance names.
129 1d870e0d Thomas Thrainer

130 1d870e0d Thomas Thrainer
  @type lu: L{LogicalUnit}
131 1d870e0d Thomas Thrainer
  @param lu: the logical unit on whose behalf we execute
132 1d870e0d Thomas Thrainer
  @type instances: list
133 1d870e0d Thomas Thrainer
  @param instances: list of instance names or None for all instances
134 1d870e0d Thomas Thrainer
  @rtype: list
135 1d870e0d Thomas Thrainer
  @return: the list of instances, sorted
136 1d870e0d Thomas Thrainer
  @raise errors.OpPrereqError: if the instances parameter is wrong type
137 1d870e0d Thomas Thrainer
  @raise errors.OpPrereqError: if any of the passed instances is not found
138 1d870e0d Thomas Thrainer

139 1d870e0d Thomas Thrainer
  """
140 1d870e0d Thomas Thrainer
  if instances:
141 5eacbcae Thomas Thrainer
    wanted = [ExpandInstanceName(lu.cfg, name) for name in instances]
142 1d870e0d Thomas Thrainer
  else:
143 1d870e0d Thomas Thrainer
    wanted = utils.NiceSort(lu.cfg.GetInstanceList())
144 1d870e0d Thomas Thrainer
  return wanted
145 7352d33b Thomas Thrainer
146 7352d33b Thomas Thrainer
147 5eacbcae Thomas Thrainer
def RunPostHook(lu, node_name):
148 7352d33b Thomas Thrainer
  """Runs the post-hook for an opcode on a single node.
149 7352d33b Thomas Thrainer

150 7352d33b Thomas Thrainer
  """
151 7352d33b Thomas Thrainer
  hm = lu.proc.BuildHooksManager(lu)
152 7352d33b Thomas Thrainer
  try:
153 7352d33b Thomas Thrainer
    hm.RunPhase(constants.HOOKS_PHASE_POST, nodes=[node_name])
154 7352d33b Thomas Thrainer
  except Exception, err: # pylint: disable=W0703
155 7352d33b Thomas Thrainer
    lu.LogWarning("Errors occurred running hooks on %s: %s",
156 7352d33b Thomas Thrainer
                  node_name, err)
157 7352d33b Thomas Thrainer
158 7352d33b Thomas Thrainer
159 5eacbcae Thomas Thrainer
def RedistributeAncillaryFiles(lu, additional_nodes=None, additional_vm=True):
160 7352d33b Thomas Thrainer
  """Distribute additional files which are part of the cluster configuration.
161 7352d33b Thomas Thrainer

162 7352d33b Thomas Thrainer
  ConfigWriter takes care of distributing the config and ssconf files, but
163 7352d33b Thomas Thrainer
  there are more files which should be distributed to all nodes. This function
164 7352d33b Thomas Thrainer
  makes sure those are copied.
165 7352d33b Thomas Thrainer

166 7352d33b Thomas Thrainer
  @param lu: calling logical unit
167 7352d33b Thomas Thrainer
  @param additional_nodes: list of nodes not in the config to distribute to
168 7352d33b Thomas Thrainer
  @type additional_vm: boolean
169 7352d33b Thomas Thrainer
  @param additional_vm: whether the additional nodes are vm-capable or not
170 7352d33b Thomas Thrainer

171 7352d33b Thomas Thrainer
  """
172 7352d33b Thomas Thrainer
  # Gather target nodes
173 7352d33b Thomas Thrainer
  cluster = lu.cfg.GetClusterInfo()
174 7352d33b Thomas Thrainer
  master_info = lu.cfg.GetNodeInfo(lu.cfg.GetMasterNode())
175 7352d33b Thomas Thrainer
176 7352d33b Thomas Thrainer
  online_nodes = lu.cfg.GetOnlineNodeList()
177 7352d33b Thomas Thrainer
  online_set = frozenset(online_nodes)
178 7352d33b Thomas Thrainer
  vm_nodes = list(online_set.intersection(lu.cfg.GetVmCapableNodeList()))
179 7352d33b Thomas Thrainer
180 7352d33b Thomas Thrainer
  if additional_nodes is not None:
181 7352d33b Thomas Thrainer
    online_nodes.extend(additional_nodes)
182 7352d33b Thomas Thrainer
    if additional_vm:
183 7352d33b Thomas Thrainer
      vm_nodes.extend(additional_nodes)
184 7352d33b Thomas Thrainer
185 7352d33b Thomas Thrainer
  # Never distribute to master node
186 7352d33b Thomas Thrainer
  for nodelist in [online_nodes, vm_nodes]:
187 7352d33b Thomas Thrainer
    if master_info.name in nodelist:
188 7352d33b Thomas Thrainer
      nodelist.remove(master_info.name)
189 7352d33b Thomas Thrainer
190 7352d33b Thomas Thrainer
  # Gather file lists
191 7352d33b Thomas Thrainer
  (files_all, _, files_mc, files_vm) = \
192 5eacbcae Thomas Thrainer
    ComputeAncillaryFiles(cluster, True)
193 7352d33b Thomas Thrainer
194 7352d33b Thomas Thrainer
  # Never re-distribute configuration file from here
195 7352d33b Thomas Thrainer
  assert not (pathutils.CLUSTER_CONF_FILE in files_all or
196 7352d33b Thomas Thrainer
              pathutils.CLUSTER_CONF_FILE in files_vm)
197 7352d33b Thomas Thrainer
  assert not files_mc, "Master candidates not handled in this function"
198 7352d33b Thomas Thrainer
199 7352d33b Thomas Thrainer
  filemap = [
200 7352d33b Thomas Thrainer
    (online_nodes, files_all),
201 7352d33b Thomas Thrainer
    (vm_nodes, files_vm),
202 7352d33b Thomas Thrainer
    ]
203 7352d33b Thomas Thrainer
204 7352d33b Thomas Thrainer
  # Upload the files
205 7352d33b Thomas Thrainer
  for (node_list, files) in filemap:
206 7352d33b Thomas Thrainer
    for fname in files:
207 5eacbcae Thomas Thrainer
      UploadHelper(lu, node_list, fname)
208 7352d33b Thomas Thrainer
209 7352d33b Thomas Thrainer
210 5eacbcae Thomas Thrainer
def ComputeAncillaryFiles(cluster, redist):
211 7352d33b Thomas Thrainer
  """Compute files external to Ganeti which need to be consistent.
212 7352d33b Thomas Thrainer

213 7352d33b Thomas Thrainer
  @type redist: boolean
214 7352d33b Thomas Thrainer
  @param redist: Whether to include files which need to be redistributed
215 7352d33b Thomas Thrainer

216 7352d33b Thomas Thrainer
  """
217 7352d33b Thomas Thrainer
  # Compute files for all nodes
218 7352d33b Thomas Thrainer
  files_all = set([
219 7352d33b Thomas Thrainer
    pathutils.SSH_KNOWN_HOSTS_FILE,
220 7352d33b Thomas Thrainer
    pathutils.CONFD_HMAC_KEY,
221 7352d33b Thomas Thrainer
    pathutils.CLUSTER_DOMAIN_SECRET_FILE,
222 7352d33b Thomas Thrainer
    pathutils.SPICE_CERT_FILE,
223 7352d33b Thomas Thrainer
    pathutils.SPICE_CACERT_FILE,
224 7352d33b Thomas Thrainer
    pathutils.RAPI_USERS_FILE,
225 7352d33b Thomas Thrainer
    ])
226 7352d33b Thomas Thrainer
227 7352d33b Thomas Thrainer
  if redist:
228 7352d33b Thomas Thrainer
    # we need to ship at least the RAPI certificate
229 7352d33b Thomas Thrainer
    files_all.add(pathutils.RAPI_CERT_FILE)
230 7352d33b Thomas Thrainer
  else:
231 7352d33b Thomas Thrainer
    files_all.update(pathutils.ALL_CERT_FILES)
232 7352d33b Thomas Thrainer
    files_all.update(ssconf.SimpleStore().GetFileList())
233 7352d33b Thomas Thrainer
234 7352d33b Thomas Thrainer
  if cluster.modify_etc_hosts:
235 7352d33b Thomas Thrainer
    files_all.add(pathutils.ETC_HOSTS)
236 7352d33b Thomas Thrainer
237 7352d33b Thomas Thrainer
  if cluster.use_external_mip_script:
238 7352d33b Thomas Thrainer
    files_all.add(pathutils.EXTERNAL_MASTER_SETUP_SCRIPT)
239 7352d33b Thomas Thrainer
240 7352d33b Thomas Thrainer
  # Files which are optional, these must:
241 7352d33b Thomas Thrainer
  # - be present in one other category as well
242 7352d33b Thomas Thrainer
  # - either exist or not exist on all nodes of that category (mc, vm all)
243 7352d33b Thomas Thrainer
  files_opt = set([
244 7352d33b Thomas Thrainer
    pathutils.RAPI_USERS_FILE,
245 7352d33b Thomas Thrainer
    ])
246 7352d33b Thomas Thrainer
247 7352d33b Thomas Thrainer
  # Files which should only be on master candidates
248 7352d33b Thomas Thrainer
  files_mc = set()
249 7352d33b Thomas Thrainer
250 7352d33b Thomas Thrainer
  if not redist:
251 7352d33b Thomas Thrainer
    files_mc.add(pathutils.CLUSTER_CONF_FILE)
252 7352d33b Thomas Thrainer
253 7352d33b Thomas Thrainer
  # File storage
254 7352d33b Thomas Thrainer
  if (not redist and (constants.ENABLE_FILE_STORAGE or
255 7352d33b Thomas Thrainer
                        constants.ENABLE_SHARED_FILE_STORAGE)):
256 7352d33b Thomas Thrainer
    files_all.add(pathutils.FILE_STORAGE_PATHS_FILE)
257 7352d33b Thomas Thrainer
    files_opt.add(pathutils.FILE_STORAGE_PATHS_FILE)
258 7352d33b Thomas Thrainer
259 7352d33b Thomas Thrainer
  # Files which should only be on VM-capable nodes
260 7352d33b Thomas Thrainer
  files_vm = set(
261 7352d33b Thomas Thrainer
    filename
262 7352d33b Thomas Thrainer
    for hv_name in cluster.enabled_hypervisors
263 7352d33b Thomas Thrainer
    for filename in
264 7352d33b Thomas Thrainer
    hypervisor.GetHypervisorClass(hv_name).GetAncillaryFiles()[0])
265 7352d33b Thomas Thrainer
266 7352d33b Thomas Thrainer
  files_opt |= set(
267 7352d33b Thomas Thrainer
    filename
268 7352d33b Thomas Thrainer
    for hv_name in cluster.enabled_hypervisors
269 7352d33b Thomas Thrainer
    for filename in
270 7352d33b Thomas Thrainer
    hypervisor.GetHypervisorClass(hv_name).GetAncillaryFiles()[1])
271 7352d33b Thomas Thrainer
272 7352d33b Thomas Thrainer
  # Filenames in each category must be unique
273 7352d33b Thomas Thrainer
  all_files_set = files_all | files_mc | files_vm
274 7352d33b Thomas Thrainer
  assert (len(all_files_set) ==
275 7352d33b Thomas Thrainer
          sum(map(len, [files_all, files_mc, files_vm]))), \
276 7352d33b Thomas Thrainer
    "Found file listed in more than one file list"
277 7352d33b Thomas Thrainer
278 7352d33b Thomas Thrainer
  # Optional files must be present in one other category
279 7352d33b Thomas Thrainer
  assert all_files_set.issuperset(files_opt), \
280 7352d33b Thomas Thrainer
    "Optional file not in a different required list"
281 7352d33b Thomas Thrainer
282 7352d33b Thomas Thrainer
  # This one file should never ever be re-distributed via RPC
283 7352d33b Thomas Thrainer
  assert not (redist and
284 7352d33b Thomas Thrainer
              pathutils.FILE_STORAGE_PATHS_FILE in all_files_set)
285 7352d33b Thomas Thrainer
286 7352d33b Thomas Thrainer
  return (files_all, files_opt, files_mc, files_vm)
287 7352d33b Thomas Thrainer
288 7352d33b Thomas Thrainer
289 5eacbcae Thomas Thrainer
def UploadHelper(lu, nodes, fname):
290 7352d33b Thomas Thrainer
  """Helper for uploading a file and showing warnings.
291 7352d33b Thomas Thrainer

292 7352d33b Thomas Thrainer
  """
293 7352d33b Thomas Thrainer
  if os.path.exists(fname):
294 7352d33b Thomas Thrainer
    result = lu.rpc.call_upload_file(nodes, fname)
295 7352d33b Thomas Thrainer
    for to_node, to_result in result.items():
296 7352d33b Thomas Thrainer
      msg = to_result.fail_msg
297 7352d33b Thomas Thrainer
      if msg:
298 7352d33b Thomas Thrainer
        msg = ("Copy of file %s to node %s failed: %s" %
299 7352d33b Thomas Thrainer
               (fname, to_node, msg))
300 7352d33b Thomas Thrainer
        lu.LogWarning(msg)
301 7352d33b Thomas Thrainer
302 7352d33b Thomas Thrainer
303 5eacbcae Thomas Thrainer
def MergeAndVerifyHvState(op_input, obj_input):
304 7352d33b Thomas Thrainer
  """Combines the hv state from an opcode with the one of the object
305 7352d33b Thomas Thrainer

306 7352d33b Thomas Thrainer
  @param op_input: The input dict from the opcode
307 7352d33b Thomas Thrainer
  @param obj_input: The input dict from the objects
308 7352d33b Thomas Thrainer
  @return: The verified and updated dict
309 7352d33b Thomas Thrainer

310 7352d33b Thomas Thrainer
  """
311 7352d33b Thomas Thrainer
  if op_input:
312 7352d33b Thomas Thrainer
    invalid_hvs = set(op_input) - constants.HYPER_TYPES
313 7352d33b Thomas Thrainer
    if invalid_hvs:
314 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Invalid hypervisor(s) in hypervisor state:"
315 7352d33b Thomas Thrainer
                                 " %s" % utils.CommaJoin(invalid_hvs),
316 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
317 7352d33b Thomas Thrainer
    if obj_input is None:
318 7352d33b Thomas Thrainer
      obj_input = {}
319 7352d33b Thomas Thrainer
    type_check = constants.HVSTS_PARAMETER_TYPES
320 7352d33b Thomas Thrainer
    return _UpdateAndVerifySubDict(obj_input, op_input, type_check)
321 7352d33b Thomas Thrainer
322 7352d33b Thomas Thrainer
  return None
323 7352d33b Thomas Thrainer
324 7352d33b Thomas Thrainer
325 5eacbcae Thomas Thrainer
def MergeAndVerifyDiskState(op_input, obj_input):
326 7352d33b Thomas Thrainer
  """Combines the disk state from an opcode with the one of the object
327 7352d33b Thomas Thrainer

328 7352d33b Thomas Thrainer
  @param op_input: The input dict from the opcode
329 7352d33b Thomas Thrainer
  @param obj_input: The input dict from the objects
330 7352d33b Thomas Thrainer
  @return: The verified and updated dict
331 7352d33b Thomas Thrainer
  """
332 7352d33b Thomas Thrainer
  if op_input:
333 7352d33b Thomas Thrainer
    invalid_dst = set(op_input) - constants.DS_VALID_TYPES
334 7352d33b Thomas Thrainer
    if invalid_dst:
335 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Invalid storage type(s) in disk state: %s" %
336 7352d33b Thomas Thrainer
                                 utils.CommaJoin(invalid_dst),
337 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
338 7352d33b Thomas Thrainer
    type_check = constants.DSS_PARAMETER_TYPES
339 7352d33b Thomas Thrainer
    if obj_input is None:
340 7352d33b Thomas Thrainer
      obj_input = {}
341 7352d33b Thomas Thrainer
    return dict((key, _UpdateAndVerifySubDict(obj_input.get(key, {}), value,
342 7352d33b Thomas Thrainer
                                              type_check))
343 7352d33b Thomas Thrainer
                for key, value in op_input.items())
344 7352d33b Thomas Thrainer
345 7352d33b Thomas Thrainer
  return None
346 7352d33b Thomas Thrainer
347 7352d33b Thomas Thrainer
348 5eacbcae Thomas Thrainer
def CheckOSParams(lu, required, nodenames, osname, osparams):
349 7352d33b Thomas Thrainer
  """OS parameters validation.
350 7352d33b Thomas Thrainer

351 7352d33b Thomas Thrainer
  @type lu: L{LogicalUnit}
352 7352d33b Thomas Thrainer
  @param lu: the logical unit for which we check
353 7352d33b Thomas Thrainer
  @type required: boolean
354 7352d33b Thomas Thrainer
  @param required: whether the validation should fail if the OS is not
355 7352d33b Thomas Thrainer
      found
356 7352d33b Thomas Thrainer
  @type nodenames: list
357 7352d33b Thomas Thrainer
  @param nodenames: the list of nodes on which we should check
358 7352d33b Thomas Thrainer
  @type osname: string
359 7352d33b Thomas Thrainer
  @param osname: the name of the hypervisor we should use
360 7352d33b Thomas Thrainer
  @type osparams: dict
361 7352d33b Thomas Thrainer
  @param osparams: the parameters which we need to check
362 7352d33b Thomas Thrainer
  @raise errors.OpPrereqError: if the parameters are not valid
363 7352d33b Thomas Thrainer

364 7352d33b Thomas Thrainer
  """
365 7352d33b Thomas Thrainer
  nodenames = _FilterVmNodes(lu, nodenames)
366 7352d33b Thomas Thrainer
  result = lu.rpc.call_os_validate(nodenames, required, osname,
367 7352d33b Thomas Thrainer
                                   [constants.OS_VALIDATE_PARAMETERS],
368 7352d33b Thomas Thrainer
                                   osparams)
369 7352d33b Thomas Thrainer
  for node, nres in result.items():
370 7352d33b Thomas Thrainer
    # we don't check for offline cases since this should be run only
371 7352d33b Thomas Thrainer
    # against the master node and/or an instance's nodes
372 7352d33b Thomas Thrainer
    nres.Raise("OS Parameters validation failed on node %s" % node)
373 7352d33b Thomas Thrainer
    if not nres.payload:
374 7352d33b Thomas Thrainer
      lu.LogInfo("OS %s not found on node %s, validation skipped",
375 7352d33b Thomas Thrainer
                 osname, node)
376 7352d33b Thomas Thrainer
377 7352d33b Thomas Thrainer
378 5eacbcae Thomas Thrainer
def CheckHVParams(lu, nodenames, hvname, hvparams):
379 7352d33b Thomas Thrainer
  """Hypervisor parameter validation.
380 7352d33b Thomas Thrainer

381 7352d33b Thomas Thrainer
  This function abstract the hypervisor parameter validation to be
382 7352d33b Thomas Thrainer
  used in both instance create and instance modify.
383 7352d33b Thomas Thrainer

384 7352d33b Thomas Thrainer
  @type lu: L{LogicalUnit}
385 7352d33b Thomas Thrainer
  @param lu: the logical unit for which we check
386 7352d33b Thomas Thrainer
  @type nodenames: list
387 7352d33b Thomas Thrainer
  @param nodenames: the list of nodes on which we should check
388 7352d33b Thomas Thrainer
  @type hvname: string
389 7352d33b Thomas Thrainer
  @param hvname: the name of the hypervisor we should use
390 7352d33b Thomas Thrainer
  @type hvparams: dict
391 7352d33b Thomas Thrainer
  @param hvparams: the parameters which we need to check
392 7352d33b Thomas Thrainer
  @raise errors.OpPrereqError: if the parameters are not valid
393 7352d33b Thomas Thrainer

394 7352d33b Thomas Thrainer
  """
395 7352d33b Thomas Thrainer
  nodenames = _FilterVmNodes(lu, nodenames)
396 7352d33b Thomas Thrainer
397 7352d33b Thomas Thrainer
  cluster = lu.cfg.GetClusterInfo()
398 7352d33b Thomas Thrainer
  hvfull = objects.FillDict(cluster.hvparams.get(hvname, {}), hvparams)
399 7352d33b Thomas Thrainer
400 7352d33b Thomas Thrainer
  hvinfo = lu.rpc.call_hypervisor_validate_params(nodenames, hvname, hvfull)
401 7352d33b Thomas Thrainer
  for node in nodenames:
402 7352d33b Thomas Thrainer
    info = hvinfo[node]
403 7352d33b Thomas Thrainer
    if info.offline:
404 7352d33b Thomas Thrainer
      continue
405 7352d33b Thomas Thrainer
    info.Raise("Hypervisor parameter validation failed on node %s" % node)
406 7352d33b Thomas Thrainer
407 7352d33b Thomas Thrainer
408 5eacbcae Thomas Thrainer
def AdjustCandidatePool(lu, exceptions):
409 7352d33b Thomas Thrainer
  """Adjust the candidate pool after node operations.
410 7352d33b Thomas Thrainer

411 7352d33b Thomas Thrainer
  """
412 7352d33b Thomas Thrainer
  mod_list = lu.cfg.MaintainCandidatePool(exceptions)
413 7352d33b Thomas Thrainer
  if mod_list:
414 7352d33b Thomas Thrainer
    lu.LogInfo("Promoted nodes to master candidate role: %s",
415 7352d33b Thomas Thrainer
               utils.CommaJoin(node.name for node in mod_list))
416 7352d33b Thomas Thrainer
    for name in mod_list:
417 7352d33b Thomas Thrainer
      lu.context.ReaddNode(name)
418 7352d33b Thomas Thrainer
  mc_now, mc_max, _ = lu.cfg.GetMasterCandidateStats(exceptions)
419 7352d33b Thomas Thrainer
  if mc_now > mc_max:
420 7352d33b Thomas Thrainer
    lu.LogInfo("Note: more nodes are candidates (%d) than desired (%d)" %
421 7352d33b Thomas Thrainer
               (mc_now, mc_max))
422 7352d33b Thomas Thrainer
423 7352d33b Thomas Thrainer
424 5eacbcae Thomas Thrainer
def CheckNodePVs(nresult, exclusive_storage):
425 7352d33b Thomas Thrainer
  """Check node PVs.
426 7352d33b Thomas Thrainer

427 7352d33b Thomas Thrainer
  """
428 7352d33b Thomas Thrainer
  pvlist_dict = nresult.get(constants.NV_PVLIST, None)
429 7352d33b Thomas Thrainer
  if pvlist_dict is None:
430 7352d33b Thomas Thrainer
    return (["Can't get PV list from node"], None)
431 7352d33b Thomas Thrainer
  pvlist = map(objects.LvmPvInfo.FromDict, pvlist_dict)
432 7352d33b Thomas Thrainer
  errlist = []
433 7352d33b Thomas Thrainer
  # check that ':' is not present in PV names, since it's a
434 7352d33b Thomas Thrainer
  # special character for lvcreate (denotes the range of PEs to
435 7352d33b Thomas Thrainer
  # use on the PV)
436 7352d33b Thomas Thrainer
  for pv in pvlist:
437 7352d33b Thomas Thrainer
    if ":" in pv.name:
438 7352d33b Thomas Thrainer
      errlist.append("Invalid character ':' in PV '%s' of VG '%s'" %
439 7352d33b Thomas Thrainer
                     (pv.name, pv.vg_name))
440 7352d33b Thomas Thrainer
  es_pvinfo = None
441 7352d33b Thomas Thrainer
  if exclusive_storage:
442 7352d33b Thomas Thrainer
    (errmsgs, es_pvinfo) = utils.LvmExclusiveCheckNodePvs(pvlist)
443 7352d33b Thomas Thrainer
    errlist.extend(errmsgs)
444 7352d33b Thomas Thrainer
    shared_pvs = nresult.get(constants.NV_EXCLUSIVEPVS, None)
445 7352d33b Thomas Thrainer
    if shared_pvs:
446 7352d33b Thomas Thrainer
      for (pvname, lvlist) in shared_pvs:
447 7352d33b Thomas Thrainer
        # TODO: Check that LVs are really unrelated (snapshots, DRBD meta...)
448 7352d33b Thomas Thrainer
        errlist.append("PV %s is shared among unrelated LVs (%s)" %
449 7352d33b Thomas Thrainer
                       (pvname, utils.CommaJoin(lvlist)))
450 7352d33b Thomas Thrainer
  return (errlist, es_pvinfo)
451 7352d33b Thomas Thrainer
452 7352d33b Thomas Thrainer
453 7352d33b Thomas Thrainer
def _ComputeMinMaxSpec(name, qualifier, ispecs, value):
454 7352d33b Thomas Thrainer
  """Computes if value is in the desired range.
455 7352d33b Thomas Thrainer

456 7352d33b Thomas Thrainer
  @param name: name of the parameter for which we perform the check
457 7352d33b Thomas Thrainer
  @param qualifier: a qualifier used in the error message (e.g. 'disk/1',
458 7352d33b Thomas Thrainer
      not just 'disk')
459 7352d33b Thomas Thrainer
  @param ispecs: dictionary containing min and max values
460 7352d33b Thomas Thrainer
  @param value: actual value that we want to use
461 7352d33b Thomas Thrainer
  @return: None or an error string
462 7352d33b Thomas Thrainer

463 7352d33b Thomas Thrainer
  """
464 7352d33b Thomas Thrainer
  if value in [None, constants.VALUE_AUTO]:
465 7352d33b Thomas Thrainer
    return None
466 7352d33b Thomas Thrainer
  max_v = ispecs[constants.ISPECS_MAX].get(name, value)
467 7352d33b Thomas Thrainer
  min_v = ispecs[constants.ISPECS_MIN].get(name, value)
468 7352d33b Thomas Thrainer
  if value > max_v or min_v > value:
469 7352d33b Thomas Thrainer
    if qualifier:
470 7352d33b Thomas Thrainer
      fqn = "%s/%s" % (name, qualifier)
471 7352d33b Thomas Thrainer
    else:
472 7352d33b Thomas Thrainer
      fqn = name
473 7352d33b Thomas Thrainer
    return ("%s value %s is not in range [%s, %s]" %
474 7352d33b Thomas Thrainer
            (fqn, value, min_v, max_v))
475 7352d33b Thomas Thrainer
  return None
476 7352d33b Thomas Thrainer
477 7352d33b Thomas Thrainer
478 5eacbcae Thomas Thrainer
def ComputeIPolicySpecViolation(ipolicy, mem_size, cpu_count, disk_count,
479 5eacbcae Thomas Thrainer
                                nic_count, disk_sizes, spindle_use,
480 5eacbcae Thomas Thrainer
                                disk_template,
481 5eacbcae Thomas Thrainer
                                _compute_fn=_ComputeMinMaxSpec):
482 7352d33b Thomas Thrainer
  """Verifies ipolicy against provided specs.
483 7352d33b Thomas Thrainer

484 7352d33b Thomas Thrainer
  @type ipolicy: dict
485 7352d33b Thomas Thrainer
  @param ipolicy: The ipolicy
486 7352d33b Thomas Thrainer
  @type mem_size: int
487 7352d33b Thomas Thrainer
  @param mem_size: The memory size
488 7352d33b Thomas Thrainer
  @type cpu_count: int
489 7352d33b Thomas Thrainer
  @param cpu_count: Used cpu cores
490 7352d33b Thomas Thrainer
  @type disk_count: int
491 7352d33b Thomas Thrainer
  @param disk_count: Number of disks used
492 7352d33b Thomas Thrainer
  @type nic_count: int
493 7352d33b Thomas Thrainer
  @param nic_count: Number of nics used
494 7352d33b Thomas Thrainer
  @type disk_sizes: list of ints
495 7352d33b Thomas Thrainer
  @param disk_sizes: Disk sizes of used disk (len must match C{disk_count})
496 7352d33b Thomas Thrainer
  @type spindle_use: int
497 7352d33b Thomas Thrainer
  @param spindle_use: The number of spindles this instance uses
498 7352d33b Thomas Thrainer
  @type disk_template: string
499 7352d33b Thomas Thrainer
  @param disk_template: The disk template of the instance
500 7352d33b Thomas Thrainer
  @param _compute_fn: The compute function (unittest only)
501 7352d33b Thomas Thrainer
  @return: A list of violations, or an empty list of no violations are found
502 7352d33b Thomas Thrainer

503 7352d33b Thomas Thrainer
  """
504 7352d33b Thomas Thrainer
  assert disk_count == len(disk_sizes)
505 7352d33b Thomas Thrainer
506 7352d33b Thomas Thrainer
  test_settings = [
507 7352d33b Thomas Thrainer
    (constants.ISPEC_MEM_SIZE, "", mem_size),
508 7352d33b Thomas Thrainer
    (constants.ISPEC_CPU_COUNT, "", cpu_count),
509 7352d33b Thomas Thrainer
    (constants.ISPEC_NIC_COUNT, "", nic_count),
510 7352d33b Thomas Thrainer
    (constants.ISPEC_SPINDLE_USE, "", spindle_use),
511 7352d33b Thomas Thrainer
    ] + [(constants.ISPEC_DISK_SIZE, str(idx), d)
512 7352d33b Thomas Thrainer
         for idx, d in enumerate(disk_sizes)]
513 7352d33b Thomas Thrainer
  if disk_template != constants.DT_DISKLESS:
514 7352d33b Thomas Thrainer
    # This check doesn't make sense for diskless instances
515 7352d33b Thomas Thrainer
    test_settings.append((constants.ISPEC_DISK_COUNT, "", disk_count))
516 7352d33b Thomas Thrainer
  ret = []
517 7352d33b Thomas Thrainer
  allowed_dts = ipolicy[constants.IPOLICY_DTS]
518 7352d33b Thomas Thrainer
  if disk_template not in allowed_dts:
519 7352d33b Thomas Thrainer
    ret.append("Disk template %s is not allowed (allowed templates: %s)" %
520 7352d33b Thomas Thrainer
               (disk_template, utils.CommaJoin(allowed_dts)))
521 7352d33b Thomas Thrainer
522 7352d33b Thomas Thrainer
  min_errs = None
523 7352d33b Thomas Thrainer
  for minmax in ipolicy[constants.ISPECS_MINMAX]:
524 7352d33b Thomas Thrainer
    errs = filter(None,
525 7352d33b Thomas Thrainer
                  (_compute_fn(name, qualifier, minmax, value)
526 7352d33b Thomas Thrainer
                   for (name, qualifier, value) in test_settings))
527 7352d33b Thomas Thrainer
    if min_errs is None or len(errs) < len(min_errs):
528 7352d33b Thomas Thrainer
      min_errs = errs
529 7352d33b Thomas Thrainer
  assert min_errs is not None
530 7352d33b Thomas Thrainer
  return ret + min_errs
531 7352d33b Thomas Thrainer
532 7352d33b Thomas Thrainer
533 5eacbcae Thomas Thrainer
def ComputeIPolicyInstanceViolation(ipolicy, instance, cfg,
534 5eacbcae Thomas Thrainer
                                    _compute_fn=ComputeIPolicySpecViolation):
535 7352d33b Thomas Thrainer
  """Compute if instance meets the specs of ipolicy.
536 7352d33b Thomas Thrainer

537 7352d33b Thomas Thrainer
  @type ipolicy: dict
538 7352d33b Thomas Thrainer
  @param ipolicy: The ipolicy to verify against
539 7352d33b Thomas Thrainer
  @type instance: L{objects.Instance}
540 7352d33b Thomas Thrainer
  @param instance: The instance to verify
541 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
542 7352d33b Thomas Thrainer
  @param cfg: Cluster configuration
543 7352d33b Thomas Thrainer
  @param _compute_fn: The function to verify ipolicy (unittest only)
544 5eacbcae Thomas Thrainer
  @see: L{ComputeIPolicySpecViolation}
545 7352d33b Thomas Thrainer

546 7352d33b Thomas Thrainer
  """
547 7352d33b Thomas Thrainer
  be_full = cfg.GetClusterInfo().FillBE(instance)
548 7352d33b Thomas Thrainer
  mem_size = be_full[constants.BE_MAXMEM]
549 7352d33b Thomas Thrainer
  cpu_count = be_full[constants.BE_VCPUS]
550 7352d33b Thomas Thrainer
  spindle_use = be_full[constants.BE_SPINDLE_USE]
551 7352d33b Thomas Thrainer
  disk_count = len(instance.disks)
552 7352d33b Thomas Thrainer
  disk_sizes = [disk.size for disk in instance.disks]
553 7352d33b Thomas Thrainer
  nic_count = len(instance.nics)
554 7352d33b Thomas Thrainer
  disk_template = instance.disk_template
555 7352d33b Thomas Thrainer
556 7352d33b Thomas Thrainer
  return _compute_fn(ipolicy, mem_size, cpu_count, disk_count, nic_count,
557 7352d33b Thomas Thrainer
                     disk_sizes, spindle_use, disk_template)
558 7352d33b Thomas Thrainer
559 7352d33b Thomas Thrainer
560 7352d33b Thomas Thrainer
def _ComputeViolatingInstances(ipolicy, instances, cfg):
561 7352d33b Thomas Thrainer
  """Computes a set of instances who violates given ipolicy.
562 7352d33b Thomas Thrainer

563 7352d33b Thomas Thrainer
  @param ipolicy: The ipolicy to verify
564 7352d33b Thomas Thrainer
  @type instances: L{objects.Instance}
565 7352d33b Thomas Thrainer
  @param instances: List of instances to verify
566 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
567 7352d33b Thomas Thrainer
  @param cfg: Cluster configuration
568 7352d33b Thomas Thrainer
  @return: A frozenset of instance names violating the ipolicy
569 7352d33b Thomas Thrainer

570 7352d33b Thomas Thrainer
  """
571 7352d33b Thomas Thrainer
  return frozenset([inst.name for inst in instances
572 5eacbcae Thomas Thrainer
                    if ComputeIPolicyInstanceViolation(ipolicy, inst, cfg)])
573 7352d33b Thomas Thrainer
574 7352d33b Thomas Thrainer
575 5eacbcae Thomas Thrainer
def ComputeNewInstanceViolations(old_ipolicy, new_ipolicy, instances, cfg):
576 7352d33b Thomas Thrainer
  """Computes a set of any instances that would violate the new ipolicy.
577 7352d33b Thomas Thrainer

578 7352d33b Thomas Thrainer
  @param old_ipolicy: The current (still in-place) ipolicy
579 7352d33b Thomas Thrainer
  @param new_ipolicy: The new (to become) ipolicy
580 7352d33b Thomas Thrainer
  @param instances: List of instances to verify
581 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
582 7352d33b Thomas Thrainer
  @param cfg: Cluster configuration
583 7352d33b Thomas Thrainer
  @return: A list of instances which violates the new ipolicy but
584 7352d33b Thomas Thrainer
      did not before
585 7352d33b Thomas Thrainer

586 7352d33b Thomas Thrainer
  """
587 7352d33b Thomas Thrainer
  return (_ComputeViolatingInstances(new_ipolicy, instances, cfg) -
588 7352d33b Thomas Thrainer
          _ComputeViolatingInstances(old_ipolicy, instances, cfg))
589 7352d33b Thomas Thrainer
590 7352d33b Thomas Thrainer
591 5eacbcae Thomas Thrainer
def GetUpdatedParams(old_params, update_dict,
592 7352d33b Thomas Thrainer
                      use_default=True, use_none=False):
593 7352d33b Thomas Thrainer
  """Return the new version of a parameter dictionary.
594 7352d33b Thomas Thrainer

595 7352d33b Thomas Thrainer
  @type old_params: dict
596 7352d33b Thomas Thrainer
  @param old_params: old parameters
597 7352d33b Thomas Thrainer
  @type update_dict: dict
598 7352d33b Thomas Thrainer
  @param update_dict: dict containing new parameter values, or
599 7352d33b Thomas Thrainer
      constants.VALUE_DEFAULT to reset the parameter to its default
600 7352d33b Thomas Thrainer
      value
601 7352d33b Thomas Thrainer
  @param use_default: boolean
602 7352d33b Thomas Thrainer
  @type use_default: whether to recognise L{constants.VALUE_DEFAULT}
603 7352d33b Thomas Thrainer
      values as 'to be deleted' values
604 7352d33b Thomas Thrainer
  @param use_none: boolean
605 7352d33b Thomas Thrainer
  @type use_none: whether to recognise C{None} values as 'to be
606 7352d33b Thomas Thrainer
      deleted' values
607 7352d33b Thomas Thrainer
  @rtype: dict
608 7352d33b Thomas Thrainer
  @return: the new parameter dictionary
609 7352d33b Thomas Thrainer

610 7352d33b Thomas Thrainer
  """
611 7352d33b Thomas Thrainer
  params_copy = copy.deepcopy(old_params)
612 7352d33b Thomas Thrainer
  for key, val in update_dict.iteritems():
613 7352d33b Thomas Thrainer
    if ((use_default and val == constants.VALUE_DEFAULT) or
614 7352d33b Thomas Thrainer
          (use_none and val is None)):
615 7352d33b Thomas Thrainer
      try:
616 7352d33b Thomas Thrainer
        del params_copy[key]
617 7352d33b Thomas Thrainer
      except KeyError:
618 7352d33b Thomas Thrainer
        pass
619 7352d33b Thomas Thrainer
    else:
620 7352d33b Thomas Thrainer
      params_copy[key] = val
621 7352d33b Thomas Thrainer
  return params_copy
622 7352d33b Thomas Thrainer
623 7352d33b Thomas Thrainer
624 5eacbcae Thomas Thrainer
def GetUpdatedIPolicy(old_ipolicy, new_ipolicy, group_policy=False):
625 7352d33b Thomas Thrainer
  """Return the new version of an instance policy.
626 7352d33b Thomas Thrainer

627 7352d33b Thomas Thrainer
  @param group_policy: whether this policy applies to a group and thus
628 7352d33b Thomas Thrainer
    we should support removal of policy entries
629 7352d33b Thomas Thrainer

630 7352d33b Thomas Thrainer
  """
631 7352d33b Thomas Thrainer
  ipolicy = copy.deepcopy(old_ipolicy)
632 7352d33b Thomas Thrainer
  for key, value in new_ipolicy.items():
633 7352d33b Thomas Thrainer
    if key not in constants.IPOLICY_ALL_KEYS:
634 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Invalid key in new ipolicy: %s" % key,
635 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
636 7352d33b Thomas Thrainer
    if (not value or value == [constants.VALUE_DEFAULT] or
637 7352d33b Thomas Thrainer
            value == constants.VALUE_DEFAULT):
638 7352d33b Thomas Thrainer
      if group_policy:
639 7352d33b Thomas Thrainer
        if key in ipolicy:
640 7352d33b Thomas Thrainer
          del ipolicy[key]
641 7352d33b Thomas Thrainer
      else:
642 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Can't unset ipolicy attribute '%s'"
643 7352d33b Thomas Thrainer
                                   " on the cluster'" % key,
644 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
645 7352d33b Thomas Thrainer
    else:
646 7352d33b Thomas Thrainer
      if key in constants.IPOLICY_PARAMETERS:
647 7352d33b Thomas Thrainer
        # FIXME: we assume all such values are float
648 7352d33b Thomas Thrainer
        try:
649 7352d33b Thomas Thrainer
          ipolicy[key] = float(value)
650 7352d33b Thomas Thrainer
        except (TypeError, ValueError), err:
651 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Invalid value for attribute"
652 7352d33b Thomas Thrainer
                                     " '%s': '%s', error: %s" %
653 7352d33b Thomas Thrainer
                                     (key, value, err), errors.ECODE_INVAL)
654 7352d33b Thomas Thrainer
      elif key == constants.ISPECS_MINMAX:
655 7352d33b Thomas Thrainer
        for minmax in value:
656 7352d33b Thomas Thrainer
          for k in minmax.keys():
657 7352d33b Thomas Thrainer
            utils.ForceDictType(minmax[k], constants.ISPECS_PARAMETER_TYPES)
658 7352d33b Thomas Thrainer
        ipolicy[key] = value
659 7352d33b Thomas Thrainer
      elif key == constants.ISPECS_STD:
660 7352d33b Thomas Thrainer
        if group_policy:
661 7352d33b Thomas Thrainer
          msg = "%s cannot appear in group instance specs" % key
662 7352d33b Thomas Thrainer
          raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
663 5eacbcae Thomas Thrainer
        ipolicy[key] = GetUpdatedParams(old_ipolicy.get(key, {}), value,
664 5eacbcae Thomas Thrainer
                                        use_none=False, use_default=False)
665 7352d33b Thomas Thrainer
        utils.ForceDictType(ipolicy[key], constants.ISPECS_PARAMETER_TYPES)
666 7352d33b Thomas Thrainer
      else:
667 7352d33b Thomas Thrainer
        # FIXME: we assume all others are lists; this should be redone
668 7352d33b Thomas Thrainer
        # in a nicer way
669 7352d33b Thomas Thrainer
        ipolicy[key] = list(value)
670 7352d33b Thomas Thrainer
  try:
671 7352d33b Thomas Thrainer
    objects.InstancePolicy.CheckParameterSyntax(ipolicy, not group_policy)
672 7352d33b Thomas Thrainer
  except errors.ConfigurationError, err:
673 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("Invalid instance policy: %s" % err,
674 7352d33b Thomas Thrainer
                               errors.ECODE_INVAL)
675 7352d33b Thomas Thrainer
  return ipolicy
676 7352d33b Thomas Thrainer
677 7352d33b Thomas Thrainer
678 5eacbcae Thomas Thrainer
def AnnotateDiskParams(instance, devs, cfg):
679 7352d33b Thomas Thrainer
  """Little helper wrapper to the rpc annotation method.
680 7352d33b Thomas Thrainer

681 7352d33b Thomas Thrainer
  @param instance: The instance object
682 7352d33b Thomas Thrainer
  @type devs: List of L{objects.Disk}
683 7352d33b Thomas Thrainer
  @param devs: The root devices (not any of its children!)
684 7352d33b Thomas Thrainer
  @param cfg: The config object
685 7352d33b Thomas Thrainer
  @returns The annotated disk copies
686 7352d33b Thomas Thrainer
  @see L{rpc.AnnotateDiskParams}
687 7352d33b Thomas Thrainer

688 7352d33b Thomas Thrainer
  """
689 7352d33b Thomas Thrainer
  return rpc.AnnotateDiskParams(instance.disk_template, devs,
690 7352d33b Thomas Thrainer
                                cfg.GetInstanceDiskParams(instance))
691 7352d33b Thomas Thrainer
692 7352d33b Thomas Thrainer
693 5eacbcae Thomas Thrainer
def SupportsOob(cfg, node):
694 7352d33b Thomas Thrainer
  """Tells if node supports OOB.
695 7352d33b Thomas Thrainer

696 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
697 7352d33b Thomas Thrainer
  @param cfg: The cluster configuration
698 7352d33b Thomas Thrainer
  @type node: L{objects.Node}
699 7352d33b Thomas Thrainer
  @param node: The node
700 7352d33b Thomas Thrainer
  @return: The OOB script if supported or an empty string otherwise
701 7352d33b Thomas Thrainer

702 7352d33b Thomas Thrainer
  """
703 7352d33b Thomas Thrainer
  return cfg.GetNdParams(node)[constants.ND_OOB_PROGRAM]
704 7352d33b Thomas Thrainer
705 7352d33b Thomas Thrainer
706 7352d33b Thomas Thrainer
def _UpdateAndVerifySubDict(base, updates, type_check):
707 7352d33b Thomas Thrainer
  """Updates and verifies a dict with sub dicts of the same type.
708 7352d33b Thomas Thrainer

709 7352d33b Thomas Thrainer
  @param base: The dict with the old data
710 7352d33b Thomas Thrainer
  @param updates: The dict with the new data
711 7352d33b Thomas Thrainer
  @param type_check: Dict suitable to ForceDictType to verify correct types
712 7352d33b Thomas Thrainer
  @returns: A new dict with updated and verified values
713 7352d33b Thomas Thrainer

714 7352d33b Thomas Thrainer
  """
715 7352d33b Thomas Thrainer
  def fn(old, value):
716 5eacbcae Thomas Thrainer
    new = GetUpdatedParams(old, value)
717 7352d33b Thomas Thrainer
    utils.ForceDictType(new, type_check)
718 7352d33b Thomas Thrainer
    return new
719 7352d33b Thomas Thrainer
720 7352d33b Thomas Thrainer
  ret = copy.deepcopy(base)
721 7352d33b Thomas Thrainer
  ret.update(dict((key, fn(base.get(key, {}), value))
722 7352d33b Thomas Thrainer
                  for key, value in updates.items()))
723 7352d33b Thomas Thrainer
  return ret
724 7352d33b Thomas Thrainer
725 7352d33b Thomas Thrainer
726 7352d33b Thomas Thrainer
def _FilterVmNodes(lu, nodenames):
727 7352d33b Thomas Thrainer
  """Filters out non-vm_capable nodes from a list.
728 7352d33b Thomas Thrainer

729 7352d33b Thomas Thrainer
  @type lu: L{LogicalUnit}
730 7352d33b Thomas Thrainer
  @param lu: the logical unit for which we check
731 7352d33b Thomas Thrainer
  @type nodenames: list
732 7352d33b Thomas Thrainer
  @param nodenames: the list of nodes on which we should check
733 7352d33b Thomas Thrainer
  @rtype: list
734 7352d33b Thomas Thrainer
  @return: the list of vm-capable nodes
735 7352d33b Thomas Thrainer

736 7352d33b Thomas Thrainer
  """
737 7352d33b Thomas Thrainer
  vm_nodes = frozenset(lu.cfg.GetNonVmCapableNodeList())
738 7352d33b Thomas Thrainer
  return [name for name in nodenames if name not in vm_nodes]
739 f380d53c Thomas Thrainer
740 f380d53c Thomas Thrainer
741 5eacbcae Thomas Thrainer
def GetDefaultIAllocator(cfg, ialloc):
742 f380d53c Thomas Thrainer
  """Decides on which iallocator to use.
743 f380d53c Thomas Thrainer

744 f380d53c Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
745 f380d53c Thomas Thrainer
  @param cfg: Cluster configuration object
746 f380d53c Thomas Thrainer
  @type ialloc: string or None
747 f380d53c Thomas Thrainer
  @param ialloc: Iallocator specified in opcode
748 f380d53c Thomas Thrainer
  @rtype: string
749 f380d53c Thomas Thrainer
  @return: Iallocator name
750 f380d53c Thomas Thrainer

751 f380d53c Thomas Thrainer
  """
752 f380d53c Thomas Thrainer
  if not ialloc:
753 f380d53c Thomas Thrainer
    # Use default iallocator
754 f380d53c Thomas Thrainer
    ialloc = cfg.GetDefaultIAllocator()
755 f380d53c Thomas Thrainer
756 f380d53c Thomas Thrainer
  if not ialloc:
757 f380d53c Thomas Thrainer
    raise errors.OpPrereqError("No iallocator was specified, neither in the"
758 f380d53c Thomas Thrainer
                               " opcode nor as a cluster-wide default",
759 f380d53c Thomas Thrainer
                               errors.ECODE_INVAL)
760 f380d53c Thomas Thrainer
761 f380d53c Thomas Thrainer
  return ialloc
762 f380d53c Thomas Thrainer
763 f380d53c Thomas Thrainer
764 5eacbcae Thomas Thrainer
def CheckInstancesNodeGroups(cfg, instances, owned_groups, owned_nodes,
765 5eacbcae Thomas Thrainer
                             cur_group_uuid):
766 f380d53c Thomas Thrainer
  """Checks if node groups for locked instances are still correct.
767 f380d53c Thomas Thrainer

768 f380d53c Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
769 f380d53c Thomas Thrainer
  @param cfg: Cluster configuration
770 f380d53c Thomas Thrainer
  @type instances: dict; string as key, L{objects.Instance} as value
771 f380d53c Thomas Thrainer
  @param instances: Dictionary, instance name as key, instance object as value
772 f380d53c Thomas Thrainer
  @type owned_groups: iterable of string
773 f380d53c Thomas Thrainer
  @param owned_groups: List of owned groups
774 f380d53c Thomas Thrainer
  @type owned_nodes: iterable of string
775 f380d53c Thomas Thrainer
  @param owned_nodes: List of owned nodes
776 f380d53c Thomas Thrainer
  @type cur_group_uuid: string or None
777 f380d53c Thomas Thrainer
  @param cur_group_uuid: Optional group UUID to check against instance's groups
778 f380d53c Thomas Thrainer

779 f380d53c Thomas Thrainer
  """
780 f380d53c Thomas Thrainer
  for (name, inst) in instances.items():
781 f380d53c Thomas Thrainer
    assert owned_nodes.issuperset(inst.all_nodes), \
782 f380d53c Thomas Thrainer
      "Instance %s's nodes changed while we kept the lock" % name
783 f380d53c Thomas Thrainer
784 5eacbcae Thomas Thrainer
    inst_groups = CheckInstanceNodeGroups(cfg, name, owned_groups)
785 f380d53c Thomas Thrainer
786 f380d53c Thomas Thrainer
    assert cur_group_uuid is None or cur_group_uuid in inst_groups, \
787 f380d53c Thomas Thrainer
      "Instance %s has no node in group %s" % (name, cur_group_uuid)
788 f380d53c Thomas Thrainer
789 f380d53c Thomas Thrainer
790 5eacbcae Thomas Thrainer
def CheckInstanceNodeGroups(cfg, instance_name, owned_groups,
791 5eacbcae Thomas Thrainer
                            primary_only=False):
792 f380d53c Thomas Thrainer
  """Checks if the owned node groups are still correct for an instance.
793 f380d53c Thomas Thrainer

794 f380d53c Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
795 f380d53c Thomas Thrainer
  @param cfg: The cluster configuration
796 f380d53c Thomas Thrainer
  @type instance_name: string
797 f380d53c Thomas Thrainer
  @param instance_name: Instance name
798 f380d53c Thomas Thrainer
  @type owned_groups: set or frozenset
799 f380d53c Thomas Thrainer
  @param owned_groups: List of currently owned node groups
800 f380d53c Thomas Thrainer
  @type primary_only: boolean
801 f380d53c Thomas Thrainer
  @param primary_only: Whether to check node groups for only the primary node
802 f380d53c Thomas Thrainer

803 f380d53c Thomas Thrainer
  """
804 f380d53c Thomas Thrainer
  inst_groups = cfg.GetInstanceNodeGroups(instance_name, primary_only)
805 f380d53c Thomas Thrainer
806 f380d53c Thomas Thrainer
  if not owned_groups.issuperset(inst_groups):
807 f380d53c Thomas Thrainer
    raise errors.OpPrereqError("Instance %s's node groups changed since"
808 f380d53c Thomas Thrainer
                               " locks were acquired, current groups are"
809 f380d53c Thomas Thrainer
                               " are '%s', owning groups '%s'; retry the"
810 f380d53c Thomas Thrainer
                               " operation" %
811 f380d53c Thomas Thrainer
                               (instance_name,
812 f380d53c Thomas Thrainer
                                utils.CommaJoin(inst_groups),
813 f380d53c Thomas Thrainer
                                utils.CommaJoin(owned_groups)),
814 f380d53c Thomas Thrainer
                               errors.ECODE_STATE)
815 f380d53c Thomas Thrainer
816 f380d53c Thomas Thrainer
  return inst_groups
817 f380d53c Thomas Thrainer
818 f380d53c Thomas Thrainer
819 5eacbcae Thomas Thrainer
def LoadNodeEvacResult(lu, alloc_result, early_release, use_nodes):
820 f380d53c Thomas Thrainer
  """Unpacks the result of change-group and node-evacuate iallocator requests.
821 f380d53c Thomas Thrainer

822 f380d53c Thomas Thrainer
  Iallocator modes L{constants.IALLOCATOR_MODE_NODE_EVAC} and
823 f380d53c Thomas Thrainer
  L{constants.IALLOCATOR_MODE_CHG_GROUP}.
824 f380d53c Thomas Thrainer

825 f380d53c Thomas Thrainer
  @type lu: L{LogicalUnit}
826 f380d53c Thomas Thrainer
  @param lu: Logical unit instance
827 f380d53c Thomas Thrainer
  @type alloc_result: tuple/list
828 f380d53c Thomas Thrainer
  @param alloc_result: Result from iallocator
829 f380d53c Thomas Thrainer
  @type early_release: bool
830 f380d53c Thomas Thrainer
  @param early_release: Whether to release locks early if possible
831 f380d53c Thomas Thrainer
  @type use_nodes: bool
832 f380d53c Thomas Thrainer
  @param use_nodes: Whether to display node names instead of groups
833 f380d53c Thomas Thrainer

834 f380d53c Thomas Thrainer
  """
835 f380d53c Thomas Thrainer
  (moved, failed, jobs) = alloc_result
836 f380d53c Thomas Thrainer
837 f380d53c Thomas Thrainer
  if failed:
838 f380d53c Thomas Thrainer
    failreason = utils.CommaJoin("%s (%s)" % (name, reason)
839 f380d53c Thomas Thrainer
                                 for (name, reason) in failed)
840 f380d53c Thomas Thrainer
    lu.LogWarning("Unable to evacuate instances %s", failreason)
841 f380d53c Thomas Thrainer
    raise errors.OpExecError("Unable to evacuate instances %s" % failreason)
842 f380d53c Thomas Thrainer
843 f380d53c Thomas Thrainer
  if moved:
844 f380d53c Thomas Thrainer
    lu.LogInfo("Instances to be moved: %s",
845 f380d53c Thomas Thrainer
               utils.CommaJoin("%s (to %s)" %
846 f380d53c Thomas Thrainer
                               (name, _NodeEvacDest(use_nodes, group, nodes))
847 f380d53c Thomas Thrainer
                               for (name, group, nodes) in moved))
848 f380d53c Thomas Thrainer
849 f380d53c Thomas Thrainer
  return [map(compat.partial(_SetOpEarlyRelease, early_release),
850 f380d53c Thomas Thrainer
              map(opcodes.OpCode.LoadOpCode, ops))
851 f380d53c Thomas Thrainer
          for ops in jobs]
852 f380d53c Thomas Thrainer
853 f380d53c Thomas Thrainer
854 f380d53c Thomas Thrainer
def _NodeEvacDest(use_nodes, group, nodes):
855 f380d53c Thomas Thrainer
  """Returns group or nodes depending on caller's choice.
856 f380d53c Thomas Thrainer

857 f380d53c Thomas Thrainer
  """
858 f380d53c Thomas Thrainer
  if use_nodes:
859 f380d53c Thomas Thrainer
    return utils.CommaJoin(nodes)
860 f380d53c Thomas Thrainer
  else:
861 f380d53c Thomas Thrainer
    return group
862 f380d53c Thomas Thrainer
863 f380d53c Thomas Thrainer
864 f380d53c Thomas Thrainer
def _SetOpEarlyRelease(early_release, op):
865 f380d53c Thomas Thrainer
  """Sets C{early_release} flag on opcodes if available.
866 f380d53c Thomas Thrainer

867 f380d53c Thomas Thrainer
  """
868 f380d53c Thomas Thrainer
  try:
869 f380d53c Thomas Thrainer
    op.early_release = early_release
870 f380d53c Thomas Thrainer
  except AttributeError:
871 f380d53c Thomas Thrainer
    assert not isinstance(op, opcodes.OpInstanceReplaceDisks)
872 f380d53c Thomas Thrainer
873 f380d53c Thomas Thrainer
  return op
874 f380d53c Thomas Thrainer
875 f380d53c Thomas Thrainer
876 5eacbcae Thomas Thrainer
def MapInstanceDisksToNodes(instances):
877 f380d53c Thomas Thrainer
  """Creates a map from (node, volume) to instance name.
878 f380d53c Thomas Thrainer

879 f380d53c Thomas Thrainer
  @type instances: list of L{objects.Instance}
880 f380d53c Thomas Thrainer
  @rtype: dict; tuple of (node name, volume name) as key, instance name as value
881 f380d53c Thomas Thrainer

882 f380d53c Thomas Thrainer
  """
883 f380d53c Thomas Thrainer
  return dict(((node, vol), inst.name)
884 f380d53c Thomas Thrainer
              for inst in instances
885 f380d53c Thomas Thrainer
              for (node, vols) in inst.MapLVsByNode().items()
886 f380d53c Thomas Thrainer
              for vol in vols)
887 31b836b8 Thomas Thrainer
888 31b836b8 Thomas Thrainer
889 5eacbcae Thomas Thrainer
def CheckParamsNotGlobal(params, glob_pars, kind, bad_levels, good_levels):
890 31b836b8 Thomas Thrainer
  """Make sure that none of the given paramters is global.
891 31b836b8 Thomas Thrainer

892 31b836b8 Thomas Thrainer
  If a global parameter is found, an L{errors.OpPrereqError} exception is
893 31b836b8 Thomas Thrainer
  raised. This is used to avoid setting global parameters for individual nodes.
894 31b836b8 Thomas Thrainer

895 31b836b8 Thomas Thrainer
  @type params: dictionary
896 31b836b8 Thomas Thrainer
  @param params: Parameters to check
897 31b836b8 Thomas Thrainer
  @type glob_pars: dictionary
898 31b836b8 Thomas Thrainer
  @param glob_pars: Forbidden parameters
899 31b836b8 Thomas Thrainer
  @type kind: string
900 31b836b8 Thomas Thrainer
  @param kind: Kind of parameters (e.g. "node")
901 31b836b8 Thomas Thrainer
  @type bad_levels: string
902 31b836b8 Thomas Thrainer
  @param bad_levels: Level(s) at which the parameters are forbidden (e.g.
903 31b836b8 Thomas Thrainer
      "instance")
904 31b836b8 Thomas Thrainer
  @type good_levels: strings
905 31b836b8 Thomas Thrainer
  @param good_levels: Level(s) at which the parameters are allowed (e.g.
906 31b836b8 Thomas Thrainer
      "cluster or group")
907 31b836b8 Thomas Thrainer

908 31b836b8 Thomas Thrainer
  """
909 31b836b8 Thomas Thrainer
  used_globals = glob_pars.intersection(params)
910 31b836b8 Thomas Thrainer
  if used_globals:
911 31b836b8 Thomas Thrainer
    msg = ("The following %s parameters are global and cannot"
912 31b836b8 Thomas Thrainer
           " be customized at %s level, please modify them at"
913 31b836b8 Thomas Thrainer
           " %s level: %s" %
914 31b836b8 Thomas Thrainer
           (kind, bad_levels, good_levels, utils.CommaJoin(used_globals)))
915 31b836b8 Thomas Thrainer
    raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
916 31b836b8 Thomas Thrainer
917 31b836b8 Thomas Thrainer
918 5eacbcae Thomas Thrainer
def IsExclusiveStorageEnabledNode(cfg, node):
919 31b836b8 Thomas Thrainer
  """Whether exclusive_storage is in effect for the given node.
920 31b836b8 Thomas Thrainer

921 31b836b8 Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
922 31b836b8 Thomas Thrainer
  @param cfg: The cluster configuration
923 31b836b8 Thomas Thrainer
  @type node: L{objects.Node}
924 31b836b8 Thomas Thrainer
  @param node: The node
925 31b836b8 Thomas Thrainer
  @rtype: bool
926 31b836b8 Thomas Thrainer
  @return: The effective value of exclusive_storage
927 31b836b8 Thomas Thrainer

928 31b836b8 Thomas Thrainer
  """
929 31b836b8 Thomas Thrainer
  return cfg.GetNdParams(node)[constants.ND_EXCLUSIVE_STORAGE]
930 31b836b8 Thomas Thrainer
931 31b836b8 Thomas Thrainer
932 5eacbcae Thomas Thrainer
def CheckInstanceState(lu, instance, req_states, msg=None):
933 31b836b8 Thomas Thrainer
  """Ensure that an instance is in one of the required states.
934 31b836b8 Thomas Thrainer

935 31b836b8 Thomas Thrainer
  @param lu: the LU on behalf of which we make the check
936 31b836b8 Thomas Thrainer
  @param instance: the instance to check
937 31b836b8 Thomas Thrainer
  @param msg: if passed, should be a message to replace the default one
938 31b836b8 Thomas Thrainer
  @raise errors.OpPrereqError: if the instance is not in the required state
939 31b836b8 Thomas Thrainer

940 31b836b8 Thomas Thrainer
  """
941 31b836b8 Thomas Thrainer
  if msg is None:
942 31b836b8 Thomas Thrainer
    msg = ("can't use instance from outside %s states" %
943 31b836b8 Thomas Thrainer
           utils.CommaJoin(req_states))
944 31b836b8 Thomas Thrainer
  if instance.admin_state not in req_states:
945 31b836b8 Thomas Thrainer
    raise errors.OpPrereqError("Instance '%s' is marked to be %s, %s" %
946 31b836b8 Thomas Thrainer
                               (instance.name, instance.admin_state, msg),
947 31b836b8 Thomas Thrainer
                               errors.ECODE_STATE)
948 31b836b8 Thomas Thrainer
949 31b836b8 Thomas Thrainer
  if constants.ADMINST_UP not in req_states:
950 31b836b8 Thomas Thrainer
    pnode = instance.primary_node
951 31b836b8 Thomas Thrainer
    if not lu.cfg.GetNodeInfo(pnode).offline:
952 31b836b8 Thomas Thrainer
      ins_l = lu.rpc.call_instance_list([pnode], [instance.hypervisor])[pnode]
953 31b836b8 Thomas Thrainer
      ins_l.Raise("Can't contact node %s for instance information" % pnode,
954 31b836b8 Thomas Thrainer
                  prereq=True, ecode=errors.ECODE_ENVIRON)
955 31b836b8 Thomas Thrainer
      if instance.name in ins_l.payload:
956 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s is running, %s" %
957 31b836b8 Thomas Thrainer
                                   (instance.name, msg), errors.ECODE_STATE)
958 31b836b8 Thomas Thrainer
    else:
959 31b836b8 Thomas Thrainer
      lu.LogWarning("Primary node offline, ignoring check that instance"
960 31b836b8 Thomas Thrainer
                     " is down")
961 31b836b8 Thomas Thrainer
962 31b836b8 Thomas Thrainer
963 5eacbcae Thomas Thrainer
def CheckIAllocatorOrNode(lu, iallocator_slot, node_slot):
964 31b836b8 Thomas Thrainer
  """Check the sanity of iallocator and node arguments and use the
965 31b836b8 Thomas Thrainer
  cluster-wide iallocator if appropriate.
966 31b836b8 Thomas Thrainer

967 31b836b8 Thomas Thrainer
  Check that at most one of (iallocator, node) is specified. If none is
968 31b836b8 Thomas Thrainer
  specified, or the iallocator is L{constants.DEFAULT_IALLOCATOR_SHORTCUT},
969 31b836b8 Thomas Thrainer
  then the LU's opcode's iallocator slot is filled with the cluster-wide
970 31b836b8 Thomas Thrainer
  default iallocator.
971 31b836b8 Thomas Thrainer

972 31b836b8 Thomas Thrainer
  @type iallocator_slot: string
973 31b836b8 Thomas Thrainer
  @param iallocator_slot: the name of the opcode iallocator slot
974 31b836b8 Thomas Thrainer
  @type node_slot: string
975 31b836b8 Thomas Thrainer
  @param node_slot: the name of the opcode target node slot
976 31b836b8 Thomas Thrainer

977 31b836b8 Thomas Thrainer
  """
978 31b836b8 Thomas Thrainer
  node = getattr(lu.op, node_slot, None)
979 31b836b8 Thomas Thrainer
  ialloc = getattr(lu.op, iallocator_slot, None)
980 31b836b8 Thomas Thrainer
  if node == []:
981 31b836b8 Thomas Thrainer
    node = None
982 31b836b8 Thomas Thrainer
983 31b836b8 Thomas Thrainer
  if node is not None and ialloc is not None:
984 31b836b8 Thomas Thrainer
    raise errors.OpPrereqError("Do not specify both, iallocator and node",
985 31b836b8 Thomas Thrainer
                               errors.ECODE_INVAL)
986 31b836b8 Thomas Thrainer
  elif ((node is None and ialloc is None) or
987 31b836b8 Thomas Thrainer
        ialloc == constants.DEFAULT_IALLOCATOR_SHORTCUT):
988 31b836b8 Thomas Thrainer
    default_iallocator = lu.cfg.GetDefaultIAllocator()
989 31b836b8 Thomas Thrainer
    if default_iallocator:
990 31b836b8 Thomas Thrainer
      setattr(lu.op, iallocator_slot, default_iallocator)
991 31b836b8 Thomas Thrainer
    else:
992 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("No iallocator or node given and no"
993 31b836b8 Thomas Thrainer
                                 " cluster-wide default iallocator found;"
994 31b836b8 Thomas Thrainer
                                 " please specify either an iallocator or a"
995 31b836b8 Thomas Thrainer
                                 " node, or set a cluster-wide default"
996 31b836b8 Thomas Thrainer
                                 " iallocator", errors.ECODE_INVAL)
997 31b836b8 Thomas Thrainer
998 31b836b8 Thomas Thrainer
999 5eacbcae Thomas Thrainer
def FindFaultyInstanceDisks(cfg, rpc_runner, instance, node_name, prereq):
1000 31b836b8 Thomas Thrainer
  faulty = []
1001 31b836b8 Thomas Thrainer
1002 31b836b8 Thomas Thrainer
  for dev in instance.disks:
1003 31b836b8 Thomas Thrainer
    cfg.SetDiskID(dev, node_name)
1004 31b836b8 Thomas Thrainer
1005 22b7f6f8 Thomas Thrainer
  result = rpc_runner.call_blockdev_getmirrorstatus(node_name,
1006 22b7f6f8 Thomas Thrainer
                                                    (instance.disks,
1007 22b7f6f8 Thomas Thrainer
                                                     instance))
1008 31b836b8 Thomas Thrainer
  result.Raise("Failed to get disk status from node %s" % node_name,
1009 31b836b8 Thomas Thrainer
               prereq=prereq, ecode=errors.ECODE_ENVIRON)
1010 31b836b8 Thomas Thrainer
1011 31b836b8 Thomas Thrainer
  for idx, bdev_status in enumerate(result.payload):
1012 31b836b8 Thomas Thrainer
    if bdev_status and bdev_status.ldisk_status == constants.LDS_FAULTY:
1013 31b836b8 Thomas Thrainer
      faulty.append(idx)
1014 31b836b8 Thomas Thrainer
1015 31b836b8 Thomas Thrainer
  return faulty
1016 22b7f6f8 Thomas Thrainer
1017 22b7f6f8 Thomas Thrainer
1018 5eacbcae Thomas Thrainer
def CheckNodeOnline(lu, node, msg=None):
1019 22b7f6f8 Thomas Thrainer
  """Ensure that a given node is online.
1020 22b7f6f8 Thomas Thrainer

1021 22b7f6f8 Thomas Thrainer
  @param lu: the LU on behalf of which we make the check
1022 22b7f6f8 Thomas Thrainer
  @param node: the node to check
1023 22b7f6f8 Thomas Thrainer
  @param msg: if passed, should be a message to replace the default one
1024 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node is offline
1025 22b7f6f8 Thomas Thrainer

1026 22b7f6f8 Thomas Thrainer
  """
1027 22b7f6f8 Thomas Thrainer
  if msg is None:
1028 22b7f6f8 Thomas Thrainer
    msg = "Can't use offline node"
1029 22b7f6f8 Thomas Thrainer
  if lu.cfg.GetNodeInfo(node).offline:
1030 22b7f6f8 Thomas Thrainer
    raise errors.OpPrereqError("%s: %s" % (msg, node), errors.ECODE_STATE)