Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / common.py @ 5b0dfcef

History | View | Annotate | Download (35.5 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 5a13489b Bernardo Dal Seno
  ret = []
548 7352d33b Thomas Thrainer
  be_full = cfg.GetClusterInfo().FillBE(instance)
549 7352d33b Thomas Thrainer
  mem_size = be_full[constants.BE_MAXMEM]
550 7352d33b Thomas Thrainer
  cpu_count = be_full[constants.BE_VCPUS]
551 5a13489b Bernardo Dal Seno
  es_flags = rpc.GetExclusiveStorageForNodeNames(cfg, instance.all_nodes)
552 5a13489b Bernardo Dal Seno
  if any(es_flags.values()):
553 5a13489b Bernardo Dal Seno
    # With exclusive storage use the actual spindles
554 5a13489b Bernardo Dal Seno
    try:
555 5a13489b Bernardo Dal Seno
      spindle_use = sum([disk.spindles for disk in instance.disks])
556 5a13489b Bernardo Dal Seno
    except TypeError:
557 5a13489b Bernardo Dal Seno
      ret.append("Number of spindles not configured for disks of instance %s"
558 5a13489b Bernardo Dal Seno
                 " while exclusive storage is enabled, try running gnt-cluster"
559 5a13489b Bernardo Dal Seno
                 " repair-disk-sizes" % instance.name)
560 5a13489b Bernardo Dal Seno
      # _ComputeMinMaxSpec ignores 'None's
561 5a13489b Bernardo Dal Seno
      spindle_use = None
562 5a13489b Bernardo Dal Seno
  else:
563 5a13489b Bernardo Dal Seno
    spindle_use = be_full[constants.BE_SPINDLE_USE]
564 7352d33b Thomas Thrainer
  disk_count = len(instance.disks)
565 7352d33b Thomas Thrainer
  disk_sizes = [disk.size for disk in instance.disks]
566 7352d33b Thomas Thrainer
  nic_count = len(instance.nics)
567 7352d33b Thomas Thrainer
  disk_template = instance.disk_template
568 7352d33b Thomas Thrainer
569 5a13489b Bernardo Dal Seno
  return ret + _compute_fn(ipolicy, mem_size, cpu_count, disk_count, nic_count,
570 5a13489b Bernardo Dal Seno
                           disk_sizes, spindle_use, disk_template)
571 7352d33b Thomas Thrainer
572 7352d33b Thomas Thrainer
573 7352d33b Thomas Thrainer
def _ComputeViolatingInstances(ipolicy, instances, cfg):
574 7352d33b Thomas Thrainer
  """Computes a set of instances who violates given ipolicy.
575 7352d33b Thomas Thrainer

576 7352d33b Thomas Thrainer
  @param ipolicy: The ipolicy to verify
577 7352d33b Thomas Thrainer
  @type instances: L{objects.Instance}
578 7352d33b Thomas Thrainer
  @param instances: List of instances to verify
579 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
580 7352d33b Thomas Thrainer
  @param cfg: Cluster configuration
581 7352d33b Thomas Thrainer
  @return: A frozenset of instance names violating the ipolicy
582 7352d33b Thomas Thrainer

583 7352d33b Thomas Thrainer
  """
584 7352d33b Thomas Thrainer
  return frozenset([inst.name for inst in instances
585 5eacbcae Thomas Thrainer
                    if ComputeIPolicyInstanceViolation(ipolicy, inst, cfg)])
586 7352d33b Thomas Thrainer
587 7352d33b Thomas Thrainer
588 5eacbcae Thomas Thrainer
def ComputeNewInstanceViolations(old_ipolicy, new_ipolicy, instances, cfg):
589 7352d33b Thomas Thrainer
  """Computes a set of any instances that would violate the new ipolicy.
590 7352d33b Thomas Thrainer

591 7352d33b Thomas Thrainer
  @param old_ipolicy: The current (still in-place) ipolicy
592 7352d33b Thomas Thrainer
  @param new_ipolicy: The new (to become) ipolicy
593 7352d33b Thomas Thrainer
  @param instances: List of instances to verify
594 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
595 7352d33b Thomas Thrainer
  @param cfg: Cluster configuration
596 7352d33b Thomas Thrainer
  @return: A list of instances which violates the new ipolicy but
597 7352d33b Thomas Thrainer
      did not before
598 7352d33b Thomas Thrainer

599 7352d33b Thomas Thrainer
  """
600 7352d33b Thomas Thrainer
  return (_ComputeViolatingInstances(new_ipolicy, instances, cfg) -
601 7352d33b Thomas Thrainer
          _ComputeViolatingInstances(old_ipolicy, instances, cfg))
602 7352d33b Thomas Thrainer
603 7352d33b Thomas Thrainer
604 5eacbcae Thomas Thrainer
def GetUpdatedParams(old_params, update_dict,
605 7352d33b Thomas Thrainer
                      use_default=True, use_none=False):
606 7352d33b Thomas Thrainer
  """Return the new version of a parameter dictionary.
607 7352d33b Thomas Thrainer

608 7352d33b Thomas Thrainer
  @type old_params: dict
609 7352d33b Thomas Thrainer
  @param old_params: old parameters
610 7352d33b Thomas Thrainer
  @type update_dict: dict
611 7352d33b Thomas Thrainer
  @param update_dict: dict containing new parameter values, or
612 7352d33b Thomas Thrainer
      constants.VALUE_DEFAULT to reset the parameter to its default
613 7352d33b Thomas Thrainer
      value
614 7352d33b Thomas Thrainer
  @param use_default: boolean
615 7352d33b Thomas Thrainer
  @type use_default: whether to recognise L{constants.VALUE_DEFAULT}
616 7352d33b Thomas Thrainer
      values as 'to be deleted' values
617 7352d33b Thomas Thrainer
  @param use_none: boolean
618 7352d33b Thomas Thrainer
  @type use_none: whether to recognise C{None} values as 'to be
619 7352d33b Thomas Thrainer
      deleted' values
620 7352d33b Thomas Thrainer
  @rtype: dict
621 7352d33b Thomas Thrainer
  @return: the new parameter dictionary
622 7352d33b Thomas Thrainer

623 7352d33b Thomas Thrainer
  """
624 7352d33b Thomas Thrainer
  params_copy = copy.deepcopy(old_params)
625 7352d33b Thomas Thrainer
  for key, val in update_dict.iteritems():
626 7352d33b Thomas Thrainer
    if ((use_default and val == constants.VALUE_DEFAULT) or
627 7352d33b Thomas Thrainer
          (use_none and val is None)):
628 7352d33b Thomas Thrainer
      try:
629 7352d33b Thomas Thrainer
        del params_copy[key]
630 7352d33b Thomas Thrainer
      except KeyError:
631 7352d33b Thomas Thrainer
        pass
632 7352d33b Thomas Thrainer
    else:
633 7352d33b Thomas Thrainer
      params_copy[key] = val
634 7352d33b Thomas Thrainer
  return params_copy
635 7352d33b Thomas Thrainer
636 7352d33b Thomas Thrainer
637 5eacbcae Thomas Thrainer
def GetUpdatedIPolicy(old_ipolicy, new_ipolicy, group_policy=False):
638 7352d33b Thomas Thrainer
  """Return the new version of an instance policy.
639 7352d33b Thomas Thrainer

640 7352d33b Thomas Thrainer
  @param group_policy: whether this policy applies to a group and thus
641 7352d33b Thomas Thrainer
    we should support removal of policy entries
642 7352d33b Thomas Thrainer

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

694 7352d33b Thomas Thrainer
  @param instance: The instance object
695 7352d33b Thomas Thrainer
  @type devs: List of L{objects.Disk}
696 7352d33b Thomas Thrainer
  @param devs: The root devices (not any of its children!)
697 7352d33b Thomas Thrainer
  @param cfg: The config object
698 7352d33b Thomas Thrainer
  @returns The annotated disk copies
699 7352d33b Thomas Thrainer
  @see L{rpc.AnnotateDiskParams}
700 7352d33b Thomas Thrainer

701 7352d33b Thomas Thrainer
  """
702 7352d33b Thomas Thrainer
  return rpc.AnnotateDiskParams(instance.disk_template, devs,
703 7352d33b Thomas Thrainer
                                cfg.GetInstanceDiskParams(instance))
704 7352d33b Thomas Thrainer
705 7352d33b Thomas Thrainer
706 5eacbcae Thomas Thrainer
def SupportsOob(cfg, node):
707 7352d33b Thomas Thrainer
  """Tells if node supports OOB.
708 7352d33b Thomas Thrainer

709 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
710 7352d33b Thomas Thrainer
  @param cfg: The cluster configuration
711 7352d33b Thomas Thrainer
  @type node: L{objects.Node}
712 7352d33b Thomas Thrainer
  @param node: The node
713 7352d33b Thomas Thrainer
  @return: The OOB script if supported or an empty string otherwise
714 7352d33b Thomas Thrainer

715 7352d33b Thomas Thrainer
  """
716 7352d33b Thomas Thrainer
  return cfg.GetNdParams(node)[constants.ND_OOB_PROGRAM]
717 7352d33b Thomas Thrainer
718 7352d33b Thomas Thrainer
719 7352d33b Thomas Thrainer
def _UpdateAndVerifySubDict(base, updates, type_check):
720 7352d33b Thomas Thrainer
  """Updates and verifies a dict with sub dicts of the same type.
721 7352d33b Thomas Thrainer

722 7352d33b Thomas Thrainer
  @param base: The dict with the old data
723 7352d33b Thomas Thrainer
  @param updates: The dict with the new data
724 7352d33b Thomas Thrainer
  @param type_check: Dict suitable to ForceDictType to verify correct types
725 7352d33b Thomas Thrainer
  @returns: A new dict with updated and verified values
726 7352d33b Thomas Thrainer

727 7352d33b Thomas Thrainer
  """
728 7352d33b Thomas Thrainer
  def fn(old, value):
729 5eacbcae Thomas Thrainer
    new = GetUpdatedParams(old, value)
730 7352d33b Thomas Thrainer
    utils.ForceDictType(new, type_check)
731 7352d33b Thomas Thrainer
    return new
732 7352d33b Thomas Thrainer
733 7352d33b Thomas Thrainer
  ret = copy.deepcopy(base)
734 7352d33b Thomas Thrainer
  ret.update(dict((key, fn(base.get(key, {}), value))
735 7352d33b Thomas Thrainer
                  for key, value in updates.items()))
736 7352d33b Thomas Thrainer
  return ret
737 7352d33b Thomas Thrainer
738 7352d33b Thomas Thrainer
739 7352d33b Thomas Thrainer
def _FilterVmNodes(lu, nodenames):
740 7352d33b Thomas Thrainer
  """Filters out non-vm_capable nodes from a list.
741 7352d33b Thomas Thrainer

742 7352d33b Thomas Thrainer
  @type lu: L{LogicalUnit}
743 7352d33b Thomas Thrainer
  @param lu: the logical unit for which we check
744 7352d33b Thomas Thrainer
  @type nodenames: list
745 7352d33b Thomas Thrainer
  @param nodenames: the list of nodes on which we should check
746 7352d33b Thomas Thrainer
  @rtype: list
747 7352d33b Thomas Thrainer
  @return: the list of vm-capable nodes
748 7352d33b Thomas Thrainer

749 7352d33b Thomas Thrainer
  """
750 7352d33b Thomas Thrainer
  vm_nodes = frozenset(lu.cfg.GetNonVmCapableNodeList())
751 7352d33b Thomas Thrainer
  return [name for name in nodenames if name not in vm_nodes]
752 f380d53c Thomas Thrainer
753 f380d53c Thomas Thrainer
754 5eacbcae Thomas Thrainer
def GetDefaultIAllocator(cfg, ialloc):
755 f380d53c Thomas Thrainer
  """Decides on which iallocator to use.
756 f380d53c Thomas Thrainer

757 f380d53c Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
758 f380d53c Thomas Thrainer
  @param cfg: Cluster configuration object
759 f380d53c Thomas Thrainer
  @type ialloc: string or None
760 f380d53c Thomas Thrainer
  @param ialloc: Iallocator specified in opcode
761 f380d53c Thomas Thrainer
  @rtype: string
762 f380d53c Thomas Thrainer
  @return: Iallocator name
763 f380d53c Thomas Thrainer

764 f380d53c Thomas Thrainer
  """
765 f380d53c Thomas Thrainer
  if not ialloc:
766 f380d53c Thomas Thrainer
    # Use default iallocator
767 f380d53c Thomas Thrainer
    ialloc = cfg.GetDefaultIAllocator()
768 f380d53c Thomas Thrainer
769 f380d53c Thomas Thrainer
  if not ialloc:
770 f380d53c Thomas Thrainer
    raise errors.OpPrereqError("No iallocator was specified, neither in the"
771 f380d53c Thomas Thrainer
                               " opcode nor as a cluster-wide default",
772 f380d53c Thomas Thrainer
                               errors.ECODE_INVAL)
773 f380d53c Thomas Thrainer
774 f380d53c Thomas Thrainer
  return ialloc
775 f380d53c Thomas Thrainer
776 f380d53c Thomas Thrainer
777 5eacbcae Thomas Thrainer
def CheckInstancesNodeGroups(cfg, instances, owned_groups, owned_nodes,
778 5eacbcae Thomas Thrainer
                             cur_group_uuid):
779 f380d53c Thomas Thrainer
  """Checks if node groups for locked instances are still correct.
780 f380d53c Thomas Thrainer

781 f380d53c Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
782 f380d53c Thomas Thrainer
  @param cfg: Cluster configuration
783 f380d53c Thomas Thrainer
  @type instances: dict; string as key, L{objects.Instance} as value
784 f380d53c Thomas Thrainer
  @param instances: Dictionary, instance name as key, instance object as value
785 f380d53c Thomas Thrainer
  @type owned_groups: iterable of string
786 f380d53c Thomas Thrainer
  @param owned_groups: List of owned groups
787 f380d53c Thomas Thrainer
  @type owned_nodes: iterable of string
788 f380d53c Thomas Thrainer
  @param owned_nodes: List of owned nodes
789 f380d53c Thomas Thrainer
  @type cur_group_uuid: string or None
790 f380d53c Thomas Thrainer
  @param cur_group_uuid: Optional group UUID to check against instance's groups
791 f380d53c Thomas Thrainer

792 f380d53c Thomas Thrainer
  """
793 f380d53c Thomas Thrainer
  for (name, inst) in instances.items():
794 f380d53c Thomas Thrainer
    assert owned_nodes.issuperset(inst.all_nodes), \
795 f380d53c Thomas Thrainer
      "Instance %s's nodes changed while we kept the lock" % name
796 f380d53c Thomas Thrainer
797 5eacbcae Thomas Thrainer
    inst_groups = CheckInstanceNodeGroups(cfg, name, owned_groups)
798 f380d53c Thomas Thrainer
799 f380d53c Thomas Thrainer
    assert cur_group_uuid is None or cur_group_uuid in inst_groups, \
800 f380d53c Thomas Thrainer
      "Instance %s has no node in group %s" % (name, cur_group_uuid)
801 f380d53c Thomas Thrainer
802 f380d53c Thomas Thrainer
803 5eacbcae Thomas Thrainer
def CheckInstanceNodeGroups(cfg, instance_name, owned_groups,
804 5eacbcae Thomas Thrainer
                            primary_only=False):
805 f380d53c Thomas Thrainer
  """Checks if the owned node groups are still correct for an instance.
806 f380d53c Thomas Thrainer

807 f380d53c Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
808 f380d53c Thomas Thrainer
  @param cfg: The cluster configuration
809 f380d53c Thomas Thrainer
  @type instance_name: string
810 f380d53c Thomas Thrainer
  @param instance_name: Instance name
811 f380d53c Thomas Thrainer
  @type owned_groups: set or frozenset
812 f380d53c Thomas Thrainer
  @param owned_groups: List of currently owned node groups
813 f380d53c Thomas Thrainer
  @type primary_only: boolean
814 f380d53c Thomas Thrainer
  @param primary_only: Whether to check node groups for only the primary node
815 f380d53c Thomas Thrainer

816 f380d53c Thomas Thrainer
  """
817 f380d53c Thomas Thrainer
  inst_groups = cfg.GetInstanceNodeGroups(instance_name, primary_only)
818 f380d53c Thomas Thrainer
819 f380d53c Thomas Thrainer
  if not owned_groups.issuperset(inst_groups):
820 f380d53c Thomas Thrainer
    raise errors.OpPrereqError("Instance %s's node groups changed since"
821 f380d53c Thomas Thrainer
                               " locks were acquired, current groups are"
822 f380d53c Thomas Thrainer
                               " are '%s', owning groups '%s'; retry the"
823 f380d53c Thomas Thrainer
                               " operation" %
824 f380d53c Thomas Thrainer
                               (instance_name,
825 f380d53c Thomas Thrainer
                                utils.CommaJoin(inst_groups),
826 f380d53c Thomas Thrainer
                                utils.CommaJoin(owned_groups)),
827 f380d53c Thomas Thrainer
                               errors.ECODE_STATE)
828 f380d53c Thomas Thrainer
829 f380d53c Thomas Thrainer
  return inst_groups
830 f380d53c Thomas Thrainer
831 f380d53c Thomas Thrainer
832 5eacbcae Thomas Thrainer
def LoadNodeEvacResult(lu, alloc_result, early_release, use_nodes):
833 f380d53c Thomas Thrainer
  """Unpacks the result of change-group and node-evacuate iallocator requests.
834 f380d53c Thomas Thrainer

835 f380d53c Thomas Thrainer
  Iallocator modes L{constants.IALLOCATOR_MODE_NODE_EVAC} and
836 f380d53c Thomas Thrainer
  L{constants.IALLOCATOR_MODE_CHG_GROUP}.
837 f380d53c Thomas Thrainer

838 f380d53c Thomas Thrainer
  @type lu: L{LogicalUnit}
839 f380d53c Thomas Thrainer
  @param lu: Logical unit instance
840 f380d53c Thomas Thrainer
  @type alloc_result: tuple/list
841 f380d53c Thomas Thrainer
  @param alloc_result: Result from iallocator
842 f380d53c Thomas Thrainer
  @type early_release: bool
843 f380d53c Thomas Thrainer
  @param early_release: Whether to release locks early if possible
844 f380d53c Thomas Thrainer
  @type use_nodes: bool
845 f380d53c Thomas Thrainer
  @param use_nodes: Whether to display node names instead of groups
846 f380d53c Thomas Thrainer

847 f380d53c Thomas Thrainer
  """
848 f380d53c Thomas Thrainer
  (moved, failed, jobs) = alloc_result
849 f380d53c Thomas Thrainer
850 f380d53c Thomas Thrainer
  if failed:
851 f380d53c Thomas Thrainer
    failreason = utils.CommaJoin("%s (%s)" % (name, reason)
852 f380d53c Thomas Thrainer
                                 for (name, reason) in failed)
853 f380d53c Thomas Thrainer
    lu.LogWarning("Unable to evacuate instances %s", failreason)
854 f380d53c Thomas Thrainer
    raise errors.OpExecError("Unable to evacuate instances %s" % failreason)
855 f380d53c Thomas Thrainer
856 f380d53c Thomas Thrainer
  if moved:
857 f380d53c Thomas Thrainer
    lu.LogInfo("Instances to be moved: %s",
858 f380d53c Thomas Thrainer
               utils.CommaJoin("%s (to %s)" %
859 f380d53c Thomas Thrainer
                               (name, _NodeEvacDest(use_nodes, group, nodes))
860 f380d53c Thomas Thrainer
                               for (name, group, nodes) in moved))
861 f380d53c Thomas Thrainer
862 f380d53c Thomas Thrainer
  return [map(compat.partial(_SetOpEarlyRelease, early_release),
863 f380d53c Thomas Thrainer
              map(opcodes.OpCode.LoadOpCode, ops))
864 f380d53c Thomas Thrainer
          for ops in jobs]
865 f380d53c Thomas Thrainer
866 f380d53c Thomas Thrainer
867 f380d53c Thomas Thrainer
def _NodeEvacDest(use_nodes, group, nodes):
868 f380d53c Thomas Thrainer
  """Returns group or nodes depending on caller's choice.
869 f380d53c Thomas Thrainer

870 f380d53c Thomas Thrainer
  """
871 f380d53c Thomas Thrainer
  if use_nodes:
872 f380d53c Thomas Thrainer
    return utils.CommaJoin(nodes)
873 f380d53c Thomas Thrainer
  else:
874 f380d53c Thomas Thrainer
    return group
875 f380d53c Thomas Thrainer
876 f380d53c Thomas Thrainer
877 f380d53c Thomas Thrainer
def _SetOpEarlyRelease(early_release, op):
878 f380d53c Thomas Thrainer
  """Sets C{early_release} flag on opcodes if available.
879 f380d53c Thomas Thrainer

880 f380d53c Thomas Thrainer
  """
881 f380d53c Thomas Thrainer
  try:
882 f380d53c Thomas Thrainer
    op.early_release = early_release
883 f380d53c Thomas Thrainer
  except AttributeError:
884 f380d53c Thomas Thrainer
    assert not isinstance(op, opcodes.OpInstanceReplaceDisks)
885 f380d53c Thomas Thrainer
886 f380d53c Thomas Thrainer
  return op
887 f380d53c Thomas Thrainer
888 f380d53c Thomas Thrainer
889 5eacbcae Thomas Thrainer
def MapInstanceDisksToNodes(instances):
890 f380d53c Thomas Thrainer
  """Creates a map from (node, volume) to instance name.
891 f380d53c Thomas Thrainer

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

895 f380d53c Thomas Thrainer
  """
896 f380d53c Thomas Thrainer
  return dict(((node, vol), inst.name)
897 f380d53c Thomas Thrainer
              for inst in instances
898 f380d53c Thomas Thrainer
              for (node, vols) in inst.MapLVsByNode().items()
899 f380d53c Thomas Thrainer
              for vol in vols)
900 31b836b8 Thomas Thrainer
901 31b836b8 Thomas Thrainer
902 5eacbcae Thomas Thrainer
def CheckParamsNotGlobal(params, glob_pars, kind, bad_levels, good_levels):
903 31b836b8 Thomas Thrainer
  """Make sure that none of the given paramters is global.
904 31b836b8 Thomas Thrainer

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

908 31b836b8 Thomas Thrainer
  @type params: dictionary
909 31b836b8 Thomas Thrainer
  @param params: Parameters to check
910 31b836b8 Thomas Thrainer
  @type glob_pars: dictionary
911 31b836b8 Thomas Thrainer
  @param glob_pars: Forbidden parameters
912 31b836b8 Thomas Thrainer
  @type kind: string
913 31b836b8 Thomas Thrainer
  @param kind: Kind of parameters (e.g. "node")
914 31b836b8 Thomas Thrainer
  @type bad_levels: string
915 31b836b8 Thomas Thrainer
  @param bad_levels: Level(s) at which the parameters are forbidden (e.g.
916 31b836b8 Thomas Thrainer
      "instance")
917 31b836b8 Thomas Thrainer
  @type good_levels: strings
918 31b836b8 Thomas Thrainer
  @param good_levels: Level(s) at which the parameters are allowed (e.g.
919 31b836b8 Thomas Thrainer
      "cluster or group")
920 31b836b8 Thomas Thrainer

921 31b836b8 Thomas Thrainer
  """
922 31b836b8 Thomas Thrainer
  used_globals = glob_pars.intersection(params)
923 31b836b8 Thomas Thrainer
  if used_globals:
924 31b836b8 Thomas Thrainer
    msg = ("The following %s parameters are global and cannot"
925 31b836b8 Thomas Thrainer
           " be customized at %s level, please modify them at"
926 31b836b8 Thomas Thrainer
           " %s level: %s" %
927 31b836b8 Thomas Thrainer
           (kind, bad_levels, good_levels, utils.CommaJoin(used_globals)))
928 31b836b8 Thomas Thrainer
    raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
929 31b836b8 Thomas Thrainer
930 31b836b8 Thomas Thrainer
931 5eacbcae Thomas Thrainer
def IsExclusiveStorageEnabledNode(cfg, node):
932 31b836b8 Thomas Thrainer
  """Whether exclusive_storage is in effect for the given node.
933 31b836b8 Thomas Thrainer

934 31b836b8 Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
935 31b836b8 Thomas Thrainer
  @param cfg: The cluster configuration
936 31b836b8 Thomas Thrainer
  @type node: L{objects.Node}
937 31b836b8 Thomas Thrainer
  @param node: The node
938 31b836b8 Thomas Thrainer
  @rtype: bool
939 31b836b8 Thomas Thrainer
  @return: The effective value of exclusive_storage
940 31b836b8 Thomas Thrainer

941 31b836b8 Thomas Thrainer
  """
942 31b836b8 Thomas Thrainer
  return cfg.GetNdParams(node)[constants.ND_EXCLUSIVE_STORAGE]
943 31b836b8 Thomas Thrainer
944 31b836b8 Thomas Thrainer
945 5eacbcae Thomas Thrainer
def CheckInstanceState(lu, instance, req_states, msg=None):
946 31b836b8 Thomas Thrainer
  """Ensure that an instance is in one of the required states.
947 31b836b8 Thomas Thrainer

948 31b836b8 Thomas Thrainer
  @param lu: the LU on behalf of which we make the check
949 31b836b8 Thomas Thrainer
  @param instance: the instance to check
950 31b836b8 Thomas Thrainer
  @param msg: if passed, should be a message to replace the default one
951 31b836b8 Thomas Thrainer
  @raise errors.OpPrereqError: if the instance is not in the required state
952 31b836b8 Thomas Thrainer

953 31b836b8 Thomas Thrainer
  """
954 31b836b8 Thomas Thrainer
  if msg is None:
955 31b836b8 Thomas Thrainer
    msg = ("can't use instance from outside %s states" %
956 31b836b8 Thomas Thrainer
           utils.CommaJoin(req_states))
957 31b836b8 Thomas Thrainer
  if instance.admin_state not in req_states:
958 31b836b8 Thomas Thrainer
    raise errors.OpPrereqError("Instance '%s' is marked to be %s, %s" %
959 31b836b8 Thomas Thrainer
                               (instance.name, instance.admin_state, msg),
960 31b836b8 Thomas Thrainer
                               errors.ECODE_STATE)
961 31b836b8 Thomas Thrainer
962 31b836b8 Thomas Thrainer
  if constants.ADMINST_UP not in req_states:
963 31b836b8 Thomas Thrainer
    pnode = instance.primary_node
964 31b836b8 Thomas Thrainer
    if not lu.cfg.GetNodeInfo(pnode).offline:
965 8ac806e6 Helga Velroyen
      all_hvparams = lu.cfg.GetClusterInfo().hvparams
966 8ac806e6 Helga Velroyen
      ins_l = lu.rpc.call_instance_list([pnode], [instance.hypervisor],
967 8ac806e6 Helga Velroyen
                                        all_hvparams)[pnode]
968 31b836b8 Thomas Thrainer
      ins_l.Raise("Can't contact node %s for instance information" % pnode,
969 31b836b8 Thomas Thrainer
                  prereq=True, ecode=errors.ECODE_ENVIRON)
970 31b836b8 Thomas Thrainer
      if instance.name in ins_l.payload:
971 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s is running, %s" %
972 31b836b8 Thomas Thrainer
                                   (instance.name, msg), errors.ECODE_STATE)
973 31b836b8 Thomas Thrainer
    else:
974 31b836b8 Thomas Thrainer
      lu.LogWarning("Primary node offline, ignoring check that instance"
975 31b836b8 Thomas Thrainer
                     " is down")
976 31b836b8 Thomas Thrainer
977 31b836b8 Thomas Thrainer
978 5eacbcae Thomas Thrainer
def CheckIAllocatorOrNode(lu, iallocator_slot, node_slot):
979 31b836b8 Thomas Thrainer
  """Check the sanity of iallocator and node arguments and use the
980 31b836b8 Thomas Thrainer
  cluster-wide iallocator if appropriate.
981 31b836b8 Thomas Thrainer

982 31b836b8 Thomas Thrainer
  Check that at most one of (iallocator, node) is specified. If none is
983 31b836b8 Thomas Thrainer
  specified, or the iallocator is L{constants.DEFAULT_IALLOCATOR_SHORTCUT},
984 31b836b8 Thomas Thrainer
  then the LU's opcode's iallocator slot is filled with the cluster-wide
985 31b836b8 Thomas Thrainer
  default iallocator.
986 31b836b8 Thomas Thrainer

987 31b836b8 Thomas Thrainer
  @type iallocator_slot: string
988 31b836b8 Thomas Thrainer
  @param iallocator_slot: the name of the opcode iallocator slot
989 31b836b8 Thomas Thrainer
  @type node_slot: string
990 31b836b8 Thomas Thrainer
  @param node_slot: the name of the opcode target node slot
991 31b836b8 Thomas Thrainer

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

1036 22b7f6f8 Thomas Thrainer
  @param lu: the LU on behalf of which we make the check
1037 22b7f6f8 Thomas Thrainer
  @param node: the node to check
1038 22b7f6f8 Thomas Thrainer
  @param msg: if passed, should be a message to replace the default one
1039 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node is offline
1040 22b7f6f8 Thomas Thrainer

1041 22b7f6f8 Thomas Thrainer
  """
1042 22b7f6f8 Thomas Thrainer
  if msg is None:
1043 22b7f6f8 Thomas Thrainer
    msg = "Can't use offline node"
1044 22b7f6f8 Thomas Thrainer
  if lu.cfg.GetNodeInfo(node).offline:
1045 22b7f6f8 Thomas Thrainer
    raise errors.OpPrereqError("%s: %s" % (msg, node), errors.ECODE_STATE)