Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / common.py @ 6bb43023

History | View | Annotate | Download (39 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 1c3231aa Thomas Thrainer
def _ExpandItemName(expand_fn, name, kind):
52 1a732a74 Thomas Thrainer
  """Expand an item name.
53 1a732a74 Thomas Thrainer

54 1c3231aa Thomas Thrainer
  @param expand_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 1c3231aa Thomas Thrainer
  @return: the result of the expand_fn, if successful
58 1a732a74 Thomas Thrainer
  @raise errors.OpPrereqError: if the item is not found
59 1a732a74 Thomas Thrainer

60 1a732a74 Thomas Thrainer
  """
61 738436bf Thomas Thrainer
  (uuid, full_name) = expand_fn(name)
62 738436bf Thomas Thrainer
  if uuid is None or 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 738436bf Thomas Thrainer
  return (uuid, full_name)
66 1a732a74 Thomas Thrainer
67 1a732a74 Thomas Thrainer
68 da4a52a3 Thomas Thrainer
def ExpandInstanceUuidAndName(cfg, expected_uuid, name):
69 1a732a74 Thomas Thrainer
  """Wrapper over L{_ExpandItemName} for instance."""
70 da4a52a3 Thomas Thrainer
  (uuid, full_name) = _ExpandItemName(cfg.ExpandInstanceName, name, "Instance")
71 da4a52a3 Thomas Thrainer
  if expected_uuid is not None and uuid != expected_uuid:
72 da4a52a3 Thomas Thrainer
    raise errors.OpPrereqError(
73 da4a52a3 Thomas Thrainer
      "The instances UUID '%s' does not match the expected UUID '%s' for"
74 da4a52a3 Thomas Thrainer
      " instance '%s'. Maybe the instance changed since you submitted this"
75 da4a52a3 Thomas Thrainer
      " job." % (uuid, expected_uuid, full_name), errors.ECODE_NOTUNIQUE)
76 da4a52a3 Thomas Thrainer
  return (uuid, full_name)
77 1a732a74 Thomas Thrainer
78 1a732a74 Thomas Thrainer
79 1c3231aa Thomas Thrainer
def ExpandNodeUuidAndName(cfg, expected_uuid, name):
80 1c3231aa Thomas Thrainer
  """Expand a short node name into the node UUID and full name.
81 1c3231aa Thomas Thrainer

82 1c3231aa Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
83 1c3231aa Thomas Thrainer
  @param cfg: The cluster configuration
84 1c3231aa Thomas Thrainer
  @type expected_uuid: string
85 1c3231aa Thomas Thrainer
  @param expected_uuid: expected UUID for the node (or None if there is no
86 1c3231aa Thomas Thrainer
        expectation). If it does not match, a L{errors.OpPrereqError} is
87 1c3231aa Thomas Thrainer
        raised.
88 1c3231aa Thomas Thrainer
  @type name: string
89 1c3231aa Thomas Thrainer
  @param name: the short node name
90 1c3231aa Thomas Thrainer

91 1c3231aa Thomas Thrainer
  """
92 1c3231aa Thomas Thrainer
  (uuid, full_name) = _ExpandItemName(cfg.ExpandNodeName, name, "Node")
93 1c3231aa Thomas Thrainer
  if expected_uuid is not None and uuid != expected_uuid:
94 1c3231aa Thomas Thrainer
    raise errors.OpPrereqError(
95 1c3231aa Thomas Thrainer
      "The nodes UUID '%s' does not match the expected UUID '%s' for node"
96 1c3231aa Thomas Thrainer
      " '%s'. Maybe the node changed since you submitted this job." %
97 1c3231aa Thomas Thrainer
      (uuid, expected_uuid, full_name), errors.ECODE_NOTUNIQUE)
98 1c3231aa Thomas Thrainer
  return (uuid, full_name)
99 fb3891d0 Thomas Thrainer
100 fb3891d0 Thomas Thrainer
101 5eacbcae Thomas Thrainer
def ShareAll():
102 fb3891d0 Thomas Thrainer
  """Returns a dict declaring all lock levels shared.
103 fb3891d0 Thomas Thrainer

104 fb3891d0 Thomas Thrainer
  """
105 fb3891d0 Thomas Thrainer
  return dict.fromkeys(locking.LEVELS, 1)
106 37dc17e3 Thomas Thrainer
107 37dc17e3 Thomas Thrainer
108 da4a52a3 Thomas Thrainer
def CheckNodeGroupInstances(cfg, group_uuid, owned_instance_names):
109 37dc17e3 Thomas Thrainer
  """Checks if the instances in a node group are still correct.
110 37dc17e3 Thomas Thrainer

111 37dc17e3 Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
112 37dc17e3 Thomas Thrainer
  @param cfg: The cluster configuration
113 37dc17e3 Thomas Thrainer
  @type group_uuid: string
114 37dc17e3 Thomas Thrainer
  @param group_uuid: Node group UUID
115 da4a52a3 Thomas Thrainer
  @type owned_instance_names: set or frozenset
116 da4a52a3 Thomas Thrainer
  @param owned_instance_names: List of currently owned instances
117 37dc17e3 Thomas Thrainer

118 37dc17e3 Thomas Thrainer
  """
119 da4a52a3 Thomas Thrainer
  wanted_instances = frozenset(cfg.GetInstanceNames(
120 da4a52a3 Thomas Thrainer
                                 cfg.GetNodeGroupInstances(group_uuid)))
121 da4a52a3 Thomas Thrainer
  if owned_instance_names != wanted_instances:
122 37dc17e3 Thomas Thrainer
    raise errors.OpPrereqError("Instances in node group '%s' changed since"
123 37dc17e3 Thomas Thrainer
                               " locks were acquired, wanted '%s', have '%s';"
124 37dc17e3 Thomas Thrainer
                               " retry the operation" %
125 37dc17e3 Thomas Thrainer
                               (group_uuid,
126 37dc17e3 Thomas Thrainer
                                utils.CommaJoin(wanted_instances),
127 da4a52a3 Thomas Thrainer
                                utils.CommaJoin(owned_instance_names)),
128 37dc17e3 Thomas Thrainer
                               errors.ECODE_STATE)
129 37dc17e3 Thomas Thrainer
130 37dc17e3 Thomas Thrainer
  return wanted_instances
131 1d870e0d Thomas Thrainer
132 1d870e0d Thomas Thrainer
133 1c3231aa Thomas Thrainer
def GetWantedNodes(lu, short_node_names):
134 1d870e0d Thomas Thrainer
  """Returns list of checked and expanded node names.
135 1d870e0d Thomas Thrainer

136 1d870e0d Thomas Thrainer
  @type lu: L{LogicalUnit}
137 1d870e0d Thomas Thrainer
  @param lu: the logical unit on whose behalf we execute
138 1c3231aa Thomas Thrainer
  @type short_node_names: list
139 1c3231aa Thomas Thrainer
  @param short_node_names: list of node names or None for all nodes
140 1c3231aa Thomas Thrainer
  @rtype: tuple of lists
141 1c3231aa Thomas Thrainer
  @return: tupe with (list of node UUIDs, list of node names)
142 1d870e0d Thomas Thrainer
  @raise errors.ProgrammerError: if the nodes parameter is wrong type
143 1d870e0d Thomas Thrainer

144 1d870e0d Thomas Thrainer
  """
145 1c3231aa Thomas Thrainer
  if short_node_names:
146 1c3231aa Thomas Thrainer
    node_uuids = [ExpandNodeUuidAndName(lu.cfg, None, name)[0]
147 1c3231aa Thomas Thrainer
                  for name in short_node_names]
148 1c3231aa Thomas Thrainer
  else:
149 1c3231aa Thomas Thrainer
    node_uuids = lu.cfg.GetNodeList()
150 1d870e0d Thomas Thrainer
151 b691385f Thomas Thrainer
  return (node_uuids, [lu.cfg.GetNodeName(uuid) for uuid in node_uuids])
152 1d870e0d Thomas Thrainer
153 1d870e0d Thomas Thrainer
154 da4a52a3 Thomas Thrainer
def GetWantedInstances(lu, short_inst_names):
155 1d870e0d Thomas Thrainer
  """Returns list of checked and expanded instance names.
156 1d870e0d Thomas Thrainer

157 1d870e0d Thomas Thrainer
  @type lu: L{LogicalUnit}
158 1d870e0d Thomas Thrainer
  @param lu: the logical unit on whose behalf we execute
159 da4a52a3 Thomas Thrainer
  @type short_inst_names: list
160 da4a52a3 Thomas Thrainer
  @param short_inst_names: list of instance names or None for all instances
161 da4a52a3 Thomas Thrainer
  @rtype: tuple of lists
162 da4a52a3 Thomas Thrainer
  @return: tuple of (instance UUIDs, instance names)
163 1d870e0d Thomas Thrainer
  @raise errors.OpPrereqError: if the instances parameter is wrong type
164 1d870e0d Thomas Thrainer
  @raise errors.OpPrereqError: if any of the passed instances is not found
165 1d870e0d Thomas Thrainer

166 1d870e0d Thomas Thrainer
  """
167 da4a52a3 Thomas Thrainer
  if short_inst_names:
168 da4a52a3 Thomas Thrainer
    inst_uuids = [ExpandInstanceUuidAndName(lu.cfg, None, name)[0]
169 da4a52a3 Thomas Thrainer
                  for name in short_inst_names]
170 1d870e0d Thomas Thrainer
  else:
171 da4a52a3 Thomas Thrainer
    inst_uuids = lu.cfg.GetInstanceList()
172 da4a52a3 Thomas Thrainer
  return (inst_uuids, [lu.cfg.GetInstanceName(uuid) for uuid in inst_uuids])
173 7352d33b Thomas Thrainer
174 7352d33b Thomas Thrainer
175 5eacbcae Thomas Thrainer
def RunPostHook(lu, node_name):
176 7352d33b Thomas Thrainer
  """Runs the post-hook for an opcode on a single node.
177 7352d33b Thomas Thrainer

178 7352d33b Thomas Thrainer
  """
179 7352d33b Thomas Thrainer
  hm = lu.proc.BuildHooksManager(lu)
180 7352d33b Thomas Thrainer
  try:
181 d0d7d7cf Thomas Thrainer
    hm.RunPhase(constants.HOOKS_PHASE_POST, node_names=[node_name])
182 7352d33b Thomas Thrainer
  except Exception, err: # pylint: disable=W0703
183 7352d33b Thomas Thrainer
    lu.LogWarning("Errors occurred running hooks on %s: %s",
184 7352d33b Thomas Thrainer
                  node_name, err)
185 7352d33b Thomas Thrainer
186 7352d33b Thomas Thrainer
187 1c3231aa Thomas Thrainer
def RedistributeAncillaryFiles(lu):
188 7352d33b Thomas Thrainer
  """Distribute additional files which are part of the cluster configuration.
189 7352d33b Thomas Thrainer

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

194 7352d33b Thomas Thrainer
  """
195 7352d33b Thomas Thrainer
  # Gather target nodes
196 7352d33b Thomas Thrainer
  cluster = lu.cfg.GetClusterInfo()
197 6bb43023 Thomas Thrainer
  master_info = lu.cfg.GetMasterNodeInfo()
198 7352d33b Thomas Thrainer
199 1c3231aa Thomas Thrainer
  online_node_uuids = lu.cfg.GetOnlineNodeList()
200 1c3231aa Thomas Thrainer
  online_node_uuid_set = frozenset(online_node_uuids)
201 1c3231aa Thomas Thrainer
  vm_node_uuids = list(online_node_uuid_set.intersection(
202 1c3231aa Thomas Thrainer
                         lu.cfg.GetVmCapableNodeList()))
203 7352d33b Thomas Thrainer
204 7352d33b Thomas Thrainer
  # Never distribute to master node
205 1c3231aa Thomas Thrainer
  for node_uuids in [online_node_uuids, vm_node_uuids]:
206 1c3231aa Thomas Thrainer
    if master_info.uuid in node_uuids:
207 1c3231aa Thomas Thrainer
      node_uuids.remove(master_info.uuid)
208 7352d33b Thomas Thrainer
209 7352d33b Thomas Thrainer
  # Gather file lists
210 7352d33b Thomas Thrainer
  (files_all, _, files_mc, files_vm) = \
211 5eacbcae Thomas Thrainer
    ComputeAncillaryFiles(cluster, True)
212 7352d33b Thomas Thrainer
213 7352d33b Thomas Thrainer
  # Never re-distribute configuration file from here
214 7352d33b Thomas Thrainer
  assert not (pathutils.CLUSTER_CONF_FILE in files_all or
215 7352d33b Thomas Thrainer
              pathutils.CLUSTER_CONF_FILE in files_vm)
216 7352d33b Thomas Thrainer
  assert not files_mc, "Master candidates not handled in this function"
217 7352d33b Thomas Thrainer
218 7352d33b Thomas Thrainer
  filemap = [
219 1c3231aa Thomas Thrainer
    (online_node_uuids, files_all),
220 1c3231aa Thomas Thrainer
    (vm_node_uuids, files_vm),
221 7352d33b Thomas Thrainer
    ]
222 7352d33b Thomas Thrainer
223 7352d33b Thomas Thrainer
  # Upload the files
224 1c3231aa Thomas Thrainer
  for (node_uuids, files) in filemap:
225 7352d33b Thomas Thrainer
    for fname in files:
226 1c3231aa Thomas Thrainer
      UploadHelper(lu, node_uuids, fname)
227 7352d33b Thomas Thrainer
228 7352d33b Thomas Thrainer
229 5eacbcae Thomas Thrainer
def ComputeAncillaryFiles(cluster, redist):
230 7352d33b Thomas Thrainer
  """Compute files external to Ganeti which need to be consistent.
231 7352d33b Thomas Thrainer

232 7352d33b Thomas Thrainer
  @type redist: boolean
233 7352d33b Thomas Thrainer
  @param redist: Whether to include files which need to be redistributed
234 7352d33b Thomas Thrainer

235 7352d33b Thomas Thrainer
  """
236 7352d33b Thomas Thrainer
  # Compute files for all nodes
237 7352d33b Thomas Thrainer
  files_all = set([
238 7352d33b Thomas Thrainer
    pathutils.SSH_KNOWN_HOSTS_FILE,
239 7352d33b Thomas Thrainer
    pathutils.CONFD_HMAC_KEY,
240 7352d33b Thomas Thrainer
    pathutils.CLUSTER_DOMAIN_SECRET_FILE,
241 7352d33b Thomas Thrainer
    pathutils.SPICE_CERT_FILE,
242 7352d33b Thomas Thrainer
    pathutils.SPICE_CACERT_FILE,
243 7352d33b Thomas Thrainer
    pathutils.RAPI_USERS_FILE,
244 7352d33b Thomas Thrainer
    ])
245 7352d33b Thomas Thrainer
246 7352d33b Thomas Thrainer
  if redist:
247 7352d33b Thomas Thrainer
    # we need to ship at least the RAPI certificate
248 7352d33b Thomas Thrainer
    files_all.add(pathutils.RAPI_CERT_FILE)
249 7352d33b Thomas Thrainer
  else:
250 7352d33b Thomas Thrainer
    files_all.update(pathutils.ALL_CERT_FILES)
251 7352d33b Thomas Thrainer
    files_all.update(ssconf.SimpleStore().GetFileList())
252 7352d33b Thomas Thrainer
253 7352d33b Thomas Thrainer
  if cluster.modify_etc_hosts:
254 7352d33b Thomas Thrainer
    files_all.add(pathutils.ETC_HOSTS)
255 7352d33b Thomas Thrainer
256 7352d33b Thomas Thrainer
  if cluster.use_external_mip_script:
257 7352d33b Thomas Thrainer
    files_all.add(pathutils.EXTERNAL_MASTER_SETUP_SCRIPT)
258 7352d33b Thomas Thrainer
259 7352d33b Thomas Thrainer
  # Files which are optional, these must:
260 7352d33b Thomas Thrainer
  # - be present in one other category as well
261 7352d33b Thomas Thrainer
  # - either exist or not exist on all nodes of that category (mc, vm all)
262 7352d33b Thomas Thrainer
  files_opt = set([
263 7352d33b Thomas Thrainer
    pathutils.RAPI_USERS_FILE,
264 7352d33b Thomas Thrainer
    ])
265 7352d33b Thomas Thrainer
266 7352d33b Thomas Thrainer
  # Files which should only be on master candidates
267 7352d33b Thomas Thrainer
  files_mc = set()
268 7352d33b Thomas Thrainer
269 7352d33b Thomas Thrainer
  if not redist:
270 7352d33b Thomas Thrainer
    files_mc.add(pathutils.CLUSTER_CONF_FILE)
271 7352d33b Thomas Thrainer
272 7352d33b Thomas Thrainer
  # File storage
273 850c53f1 Helga Velroyen
  if (not redist and (cluster.IsFileStorageEnabled() or
274 850c53f1 Helga Velroyen
                        cluster.IsSharedFileStorageEnabled())):
275 7352d33b Thomas Thrainer
    files_all.add(pathutils.FILE_STORAGE_PATHS_FILE)
276 7352d33b Thomas Thrainer
    files_opt.add(pathutils.FILE_STORAGE_PATHS_FILE)
277 7352d33b Thomas Thrainer
278 7352d33b Thomas Thrainer
  # Files which should only be on VM-capable nodes
279 7352d33b Thomas Thrainer
  files_vm = set(
280 7352d33b Thomas Thrainer
    filename
281 7352d33b Thomas Thrainer
    for hv_name in cluster.enabled_hypervisors
282 7352d33b Thomas Thrainer
    for filename in
283 7352d33b Thomas Thrainer
    hypervisor.GetHypervisorClass(hv_name).GetAncillaryFiles()[0])
284 7352d33b Thomas Thrainer
285 7352d33b Thomas Thrainer
  files_opt |= set(
286 7352d33b Thomas Thrainer
    filename
287 7352d33b Thomas Thrainer
    for hv_name in cluster.enabled_hypervisors
288 7352d33b Thomas Thrainer
    for filename in
289 7352d33b Thomas Thrainer
    hypervisor.GetHypervisorClass(hv_name).GetAncillaryFiles()[1])
290 7352d33b Thomas Thrainer
291 7352d33b Thomas Thrainer
  # Filenames in each category must be unique
292 7352d33b Thomas Thrainer
  all_files_set = files_all | files_mc | files_vm
293 7352d33b Thomas Thrainer
  assert (len(all_files_set) ==
294 7352d33b Thomas Thrainer
          sum(map(len, [files_all, files_mc, files_vm]))), \
295 7352d33b Thomas Thrainer
    "Found file listed in more than one file list"
296 7352d33b Thomas Thrainer
297 7352d33b Thomas Thrainer
  # Optional files must be present in one other category
298 7352d33b Thomas Thrainer
  assert all_files_set.issuperset(files_opt), \
299 7352d33b Thomas Thrainer
    "Optional file not in a different required list"
300 7352d33b Thomas Thrainer
301 7352d33b Thomas Thrainer
  # This one file should never ever be re-distributed via RPC
302 7352d33b Thomas Thrainer
  assert not (redist and
303 7352d33b Thomas Thrainer
              pathutils.FILE_STORAGE_PATHS_FILE in all_files_set)
304 7352d33b Thomas Thrainer
305 7352d33b Thomas Thrainer
  return (files_all, files_opt, files_mc, files_vm)
306 7352d33b Thomas Thrainer
307 7352d33b Thomas Thrainer
308 1c3231aa Thomas Thrainer
def UploadHelper(lu, node_uuids, fname):
309 7352d33b Thomas Thrainer
  """Helper for uploading a file and showing warnings.
310 7352d33b Thomas Thrainer

311 7352d33b Thomas Thrainer
  """
312 7352d33b Thomas Thrainer
  if os.path.exists(fname):
313 1c3231aa Thomas Thrainer
    result = lu.rpc.call_upload_file(node_uuids, fname)
314 1c3231aa Thomas Thrainer
    for to_node_uuids, to_result in result.items():
315 7352d33b Thomas Thrainer
      msg = to_result.fail_msg
316 7352d33b Thomas Thrainer
      if msg:
317 7352d33b Thomas Thrainer
        msg = ("Copy of file %s to node %s failed: %s" %
318 1c3231aa Thomas Thrainer
               (fname, lu.cfg.GetNodeName(to_node_uuids), msg))
319 7352d33b Thomas Thrainer
        lu.LogWarning(msg)
320 7352d33b Thomas Thrainer
321 7352d33b Thomas Thrainer
322 5eacbcae Thomas Thrainer
def MergeAndVerifyHvState(op_input, obj_input):
323 7352d33b Thomas Thrainer
  """Combines the hv state from an opcode with the one of the object
324 7352d33b Thomas Thrainer

325 7352d33b Thomas Thrainer
  @param op_input: The input dict from the opcode
326 7352d33b Thomas Thrainer
  @param obj_input: The input dict from the objects
327 7352d33b Thomas Thrainer
  @return: The verified and updated dict
328 7352d33b Thomas Thrainer

329 7352d33b Thomas Thrainer
  """
330 7352d33b Thomas Thrainer
  if op_input:
331 7352d33b Thomas Thrainer
    invalid_hvs = set(op_input) - constants.HYPER_TYPES
332 7352d33b Thomas Thrainer
    if invalid_hvs:
333 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Invalid hypervisor(s) in hypervisor state:"
334 7352d33b Thomas Thrainer
                                 " %s" % utils.CommaJoin(invalid_hvs),
335 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
336 7352d33b Thomas Thrainer
    if obj_input is None:
337 7352d33b Thomas Thrainer
      obj_input = {}
338 7352d33b Thomas Thrainer
    type_check = constants.HVSTS_PARAMETER_TYPES
339 7352d33b Thomas Thrainer
    return _UpdateAndVerifySubDict(obj_input, op_input, type_check)
340 7352d33b Thomas Thrainer
341 7352d33b Thomas Thrainer
  return None
342 7352d33b Thomas Thrainer
343 7352d33b Thomas Thrainer
344 5eacbcae Thomas Thrainer
def MergeAndVerifyDiskState(op_input, obj_input):
345 7352d33b Thomas Thrainer
  """Combines the disk state from an opcode with the one of the object
346 7352d33b Thomas Thrainer

347 7352d33b Thomas Thrainer
  @param op_input: The input dict from the opcode
348 7352d33b Thomas Thrainer
  @param obj_input: The input dict from the objects
349 7352d33b Thomas Thrainer
  @return: The verified and updated dict
350 7352d33b Thomas Thrainer
  """
351 7352d33b Thomas Thrainer
  if op_input:
352 7352d33b Thomas Thrainer
    invalid_dst = set(op_input) - constants.DS_VALID_TYPES
353 7352d33b Thomas Thrainer
    if invalid_dst:
354 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Invalid storage type(s) in disk state: %s" %
355 7352d33b Thomas Thrainer
                                 utils.CommaJoin(invalid_dst),
356 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
357 7352d33b Thomas Thrainer
    type_check = constants.DSS_PARAMETER_TYPES
358 7352d33b Thomas Thrainer
    if obj_input is None:
359 7352d33b Thomas Thrainer
      obj_input = {}
360 7352d33b Thomas Thrainer
    return dict((key, _UpdateAndVerifySubDict(obj_input.get(key, {}), value,
361 7352d33b Thomas Thrainer
                                              type_check))
362 7352d33b Thomas Thrainer
                for key, value in op_input.items())
363 7352d33b Thomas Thrainer
364 7352d33b Thomas Thrainer
  return None
365 7352d33b Thomas Thrainer
366 7352d33b Thomas Thrainer
367 1c3231aa Thomas Thrainer
def CheckOSParams(lu, required, node_uuids, osname, osparams):
368 7352d33b Thomas Thrainer
  """OS parameters validation.
369 7352d33b Thomas Thrainer

370 7352d33b Thomas Thrainer
  @type lu: L{LogicalUnit}
371 7352d33b Thomas Thrainer
  @param lu: the logical unit for which we check
372 7352d33b Thomas Thrainer
  @type required: boolean
373 7352d33b Thomas Thrainer
  @param required: whether the validation should fail if the OS is not
374 7352d33b Thomas Thrainer
      found
375 1c3231aa Thomas Thrainer
  @type node_uuids: list
376 1c3231aa Thomas Thrainer
  @param node_uuids: the list of nodes on which we should check
377 7352d33b Thomas Thrainer
  @type osname: string
378 7352d33b Thomas Thrainer
  @param osname: the name of the hypervisor we should use
379 7352d33b Thomas Thrainer
  @type osparams: dict
380 7352d33b Thomas Thrainer
  @param osparams: the parameters which we need to check
381 7352d33b Thomas Thrainer
  @raise errors.OpPrereqError: if the parameters are not valid
382 7352d33b Thomas Thrainer

383 7352d33b Thomas Thrainer
  """
384 1c3231aa Thomas Thrainer
  node_uuids = _FilterVmNodes(lu, node_uuids)
385 1c3231aa Thomas Thrainer
  result = lu.rpc.call_os_validate(node_uuids, required, osname,
386 7352d33b Thomas Thrainer
                                   [constants.OS_VALIDATE_PARAMETERS],
387 7352d33b Thomas Thrainer
                                   osparams)
388 1c3231aa Thomas Thrainer
  for node_uuid, nres in result.items():
389 7352d33b Thomas Thrainer
    # we don't check for offline cases since this should be run only
390 7352d33b Thomas Thrainer
    # against the master node and/or an instance's nodes
391 1c3231aa Thomas Thrainer
    nres.Raise("OS Parameters validation failed on node %s" %
392 1c3231aa Thomas Thrainer
               lu.cfg.GetNodeName(node_uuid))
393 7352d33b Thomas Thrainer
    if not nres.payload:
394 7352d33b Thomas Thrainer
      lu.LogInfo("OS %s not found on node %s, validation skipped",
395 1c3231aa Thomas Thrainer
                 osname, lu.cfg.GetNodeName(node_uuid))
396 7352d33b Thomas Thrainer
397 7352d33b Thomas Thrainer
398 1c3231aa Thomas Thrainer
def CheckHVParams(lu, node_uuids, hvname, hvparams):
399 7352d33b Thomas Thrainer
  """Hypervisor parameter validation.
400 7352d33b Thomas Thrainer

401 7352d33b Thomas Thrainer
  This function abstract the hypervisor parameter validation to be
402 7352d33b Thomas Thrainer
  used in both instance create and instance modify.
403 7352d33b Thomas Thrainer

404 7352d33b Thomas Thrainer
  @type lu: L{LogicalUnit}
405 7352d33b Thomas Thrainer
  @param lu: the logical unit for which we check
406 1c3231aa Thomas Thrainer
  @type node_uuids: list
407 1c3231aa Thomas Thrainer
  @param node_uuids: the list of nodes on which we should check
408 7352d33b Thomas Thrainer
  @type hvname: string
409 7352d33b Thomas Thrainer
  @param hvname: the name of the hypervisor we should use
410 7352d33b Thomas Thrainer
  @type hvparams: dict
411 7352d33b Thomas Thrainer
  @param hvparams: the parameters which we need to check
412 7352d33b Thomas Thrainer
  @raise errors.OpPrereqError: if the parameters are not valid
413 7352d33b Thomas Thrainer

414 7352d33b Thomas Thrainer
  """
415 1c3231aa Thomas Thrainer
  node_uuids = _FilterVmNodes(lu, node_uuids)
416 7352d33b Thomas Thrainer
417 7352d33b Thomas Thrainer
  cluster = lu.cfg.GetClusterInfo()
418 7352d33b Thomas Thrainer
  hvfull = objects.FillDict(cluster.hvparams.get(hvname, {}), hvparams)
419 7352d33b Thomas Thrainer
420 1c3231aa Thomas Thrainer
  hvinfo = lu.rpc.call_hypervisor_validate_params(node_uuids, hvname, hvfull)
421 1c3231aa Thomas Thrainer
  for node_uuid in node_uuids:
422 1c3231aa Thomas Thrainer
    info = hvinfo[node_uuid]
423 7352d33b Thomas Thrainer
    if info.offline:
424 7352d33b Thomas Thrainer
      continue
425 1c3231aa Thomas Thrainer
    info.Raise("Hypervisor parameter validation failed on node %s" %
426 1c3231aa Thomas Thrainer
               lu.cfg.GetNodeName(node_uuid))
427 7352d33b Thomas Thrainer
428 7352d33b Thomas Thrainer
429 5eacbcae Thomas Thrainer
def AdjustCandidatePool(lu, exceptions):
430 7352d33b Thomas Thrainer
  """Adjust the candidate pool after node operations.
431 7352d33b Thomas Thrainer

432 7352d33b Thomas Thrainer
  """
433 7352d33b Thomas Thrainer
  mod_list = lu.cfg.MaintainCandidatePool(exceptions)
434 7352d33b Thomas Thrainer
  if mod_list:
435 7352d33b Thomas Thrainer
    lu.LogInfo("Promoted nodes to master candidate role: %s",
436 7352d33b Thomas Thrainer
               utils.CommaJoin(node.name for node in mod_list))
437 1c3231aa Thomas Thrainer
    for node in mod_list:
438 1c3231aa Thomas Thrainer
      lu.context.ReaddNode(node)
439 7352d33b Thomas Thrainer
  mc_now, mc_max, _ = lu.cfg.GetMasterCandidateStats(exceptions)
440 7352d33b Thomas Thrainer
  if mc_now > mc_max:
441 7352d33b Thomas Thrainer
    lu.LogInfo("Note: more nodes are candidates (%d) than desired (%d)" %
442 7352d33b Thomas Thrainer
               (mc_now, mc_max))
443 7352d33b Thomas Thrainer
444 7352d33b Thomas Thrainer
445 5eacbcae Thomas Thrainer
def CheckNodePVs(nresult, exclusive_storage):
446 7352d33b Thomas Thrainer
  """Check node PVs.
447 7352d33b Thomas Thrainer

448 7352d33b Thomas Thrainer
  """
449 7352d33b Thomas Thrainer
  pvlist_dict = nresult.get(constants.NV_PVLIST, None)
450 7352d33b Thomas Thrainer
  if pvlist_dict is None:
451 7352d33b Thomas Thrainer
    return (["Can't get PV list from node"], None)
452 7352d33b Thomas Thrainer
  pvlist = map(objects.LvmPvInfo.FromDict, pvlist_dict)
453 7352d33b Thomas Thrainer
  errlist = []
454 7352d33b Thomas Thrainer
  # check that ':' is not present in PV names, since it's a
455 7352d33b Thomas Thrainer
  # special character for lvcreate (denotes the range of PEs to
456 7352d33b Thomas Thrainer
  # use on the PV)
457 7352d33b Thomas Thrainer
  for pv in pvlist:
458 7352d33b Thomas Thrainer
    if ":" in pv.name:
459 7352d33b Thomas Thrainer
      errlist.append("Invalid character ':' in PV '%s' of VG '%s'" %
460 7352d33b Thomas Thrainer
                     (pv.name, pv.vg_name))
461 7352d33b Thomas Thrainer
  es_pvinfo = None
462 7352d33b Thomas Thrainer
  if exclusive_storage:
463 7352d33b Thomas Thrainer
    (errmsgs, es_pvinfo) = utils.LvmExclusiveCheckNodePvs(pvlist)
464 7352d33b Thomas Thrainer
    errlist.extend(errmsgs)
465 7352d33b Thomas Thrainer
    shared_pvs = nresult.get(constants.NV_EXCLUSIVEPVS, None)
466 7352d33b Thomas Thrainer
    if shared_pvs:
467 7352d33b Thomas Thrainer
      for (pvname, lvlist) in shared_pvs:
468 7352d33b Thomas Thrainer
        # TODO: Check that LVs are really unrelated (snapshots, DRBD meta...)
469 7352d33b Thomas Thrainer
        errlist.append("PV %s is shared among unrelated LVs (%s)" %
470 7352d33b Thomas Thrainer
                       (pvname, utils.CommaJoin(lvlist)))
471 7352d33b Thomas Thrainer
  return (errlist, es_pvinfo)
472 7352d33b Thomas Thrainer
473 7352d33b Thomas Thrainer
474 7352d33b Thomas Thrainer
def _ComputeMinMaxSpec(name, qualifier, ispecs, value):
475 7352d33b Thomas Thrainer
  """Computes if value is in the desired range.
476 7352d33b Thomas Thrainer

477 7352d33b Thomas Thrainer
  @param name: name of the parameter for which we perform the check
478 7352d33b Thomas Thrainer
  @param qualifier: a qualifier used in the error message (e.g. 'disk/1',
479 7352d33b Thomas Thrainer
      not just 'disk')
480 7352d33b Thomas Thrainer
  @param ispecs: dictionary containing min and max values
481 7352d33b Thomas Thrainer
  @param value: actual value that we want to use
482 7352d33b Thomas Thrainer
  @return: None or an error string
483 7352d33b Thomas Thrainer

484 7352d33b Thomas Thrainer
  """
485 7352d33b Thomas Thrainer
  if value in [None, constants.VALUE_AUTO]:
486 7352d33b Thomas Thrainer
    return None
487 7352d33b Thomas Thrainer
  max_v = ispecs[constants.ISPECS_MAX].get(name, value)
488 7352d33b Thomas Thrainer
  min_v = ispecs[constants.ISPECS_MIN].get(name, value)
489 7352d33b Thomas Thrainer
  if value > max_v or min_v > value:
490 7352d33b Thomas Thrainer
    if qualifier:
491 7352d33b Thomas Thrainer
      fqn = "%s/%s" % (name, qualifier)
492 7352d33b Thomas Thrainer
    else:
493 7352d33b Thomas Thrainer
      fqn = name
494 7352d33b Thomas Thrainer
    return ("%s value %s is not in range [%s, %s]" %
495 7352d33b Thomas Thrainer
            (fqn, value, min_v, max_v))
496 7352d33b Thomas Thrainer
  return None
497 7352d33b Thomas Thrainer
498 7352d33b Thomas Thrainer
499 5eacbcae Thomas Thrainer
def ComputeIPolicySpecViolation(ipolicy, mem_size, cpu_count, disk_count,
500 5eacbcae Thomas Thrainer
                                nic_count, disk_sizes, spindle_use,
501 5eacbcae Thomas Thrainer
                                disk_template,
502 5eacbcae Thomas Thrainer
                                _compute_fn=_ComputeMinMaxSpec):
503 7352d33b Thomas Thrainer
  """Verifies ipolicy against provided specs.
504 7352d33b Thomas Thrainer

505 7352d33b Thomas Thrainer
  @type ipolicy: dict
506 7352d33b Thomas Thrainer
  @param ipolicy: The ipolicy
507 7352d33b Thomas Thrainer
  @type mem_size: int
508 7352d33b Thomas Thrainer
  @param mem_size: The memory size
509 7352d33b Thomas Thrainer
  @type cpu_count: int
510 7352d33b Thomas Thrainer
  @param cpu_count: Used cpu cores
511 7352d33b Thomas Thrainer
  @type disk_count: int
512 7352d33b Thomas Thrainer
  @param disk_count: Number of disks used
513 7352d33b Thomas Thrainer
  @type nic_count: int
514 7352d33b Thomas Thrainer
  @param nic_count: Number of nics used
515 7352d33b Thomas Thrainer
  @type disk_sizes: list of ints
516 7352d33b Thomas Thrainer
  @param disk_sizes: Disk sizes of used disk (len must match C{disk_count})
517 7352d33b Thomas Thrainer
  @type spindle_use: int
518 7352d33b Thomas Thrainer
  @param spindle_use: The number of spindles this instance uses
519 7352d33b Thomas Thrainer
  @type disk_template: string
520 7352d33b Thomas Thrainer
  @param disk_template: The disk template of the instance
521 7352d33b Thomas Thrainer
  @param _compute_fn: The compute function (unittest only)
522 7352d33b Thomas Thrainer
  @return: A list of violations, or an empty list of no violations are found
523 7352d33b Thomas Thrainer

524 7352d33b Thomas Thrainer
  """
525 7352d33b Thomas Thrainer
  assert disk_count == len(disk_sizes)
526 7352d33b Thomas Thrainer
527 7352d33b Thomas Thrainer
  test_settings = [
528 7352d33b Thomas Thrainer
    (constants.ISPEC_MEM_SIZE, "", mem_size),
529 7352d33b Thomas Thrainer
    (constants.ISPEC_CPU_COUNT, "", cpu_count),
530 7352d33b Thomas Thrainer
    (constants.ISPEC_NIC_COUNT, "", nic_count),
531 7352d33b Thomas Thrainer
    (constants.ISPEC_SPINDLE_USE, "", spindle_use),
532 7352d33b Thomas Thrainer
    ] + [(constants.ISPEC_DISK_SIZE, str(idx), d)
533 7352d33b Thomas Thrainer
         for idx, d in enumerate(disk_sizes)]
534 7352d33b Thomas Thrainer
  if disk_template != constants.DT_DISKLESS:
535 7352d33b Thomas Thrainer
    # This check doesn't make sense for diskless instances
536 7352d33b Thomas Thrainer
    test_settings.append((constants.ISPEC_DISK_COUNT, "", disk_count))
537 7352d33b Thomas Thrainer
  ret = []
538 7352d33b Thomas Thrainer
  allowed_dts = ipolicy[constants.IPOLICY_DTS]
539 7352d33b Thomas Thrainer
  if disk_template not in allowed_dts:
540 7352d33b Thomas Thrainer
    ret.append("Disk template %s is not allowed (allowed templates: %s)" %
541 7352d33b Thomas Thrainer
               (disk_template, utils.CommaJoin(allowed_dts)))
542 7352d33b Thomas Thrainer
543 7352d33b Thomas Thrainer
  min_errs = None
544 7352d33b Thomas Thrainer
  for minmax in ipolicy[constants.ISPECS_MINMAX]:
545 7352d33b Thomas Thrainer
    errs = filter(None,
546 7352d33b Thomas Thrainer
                  (_compute_fn(name, qualifier, minmax, value)
547 7352d33b Thomas Thrainer
                   for (name, qualifier, value) in test_settings))
548 7352d33b Thomas Thrainer
    if min_errs is None or len(errs) < len(min_errs):
549 7352d33b Thomas Thrainer
      min_errs = errs
550 7352d33b Thomas Thrainer
  assert min_errs is not None
551 7352d33b Thomas Thrainer
  return ret + min_errs
552 7352d33b Thomas Thrainer
553 7352d33b Thomas Thrainer
554 5eacbcae Thomas Thrainer
def ComputeIPolicyInstanceViolation(ipolicy, instance, cfg,
555 5eacbcae Thomas Thrainer
                                    _compute_fn=ComputeIPolicySpecViolation):
556 7352d33b Thomas Thrainer
  """Compute if instance meets the specs of ipolicy.
557 7352d33b Thomas Thrainer

558 7352d33b Thomas Thrainer
  @type ipolicy: dict
559 7352d33b Thomas Thrainer
  @param ipolicy: The ipolicy to verify against
560 7352d33b Thomas Thrainer
  @type instance: L{objects.Instance}
561 7352d33b Thomas Thrainer
  @param instance: The instance to verify
562 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
563 7352d33b Thomas Thrainer
  @param cfg: Cluster configuration
564 7352d33b Thomas Thrainer
  @param _compute_fn: The function to verify ipolicy (unittest only)
565 5eacbcae Thomas Thrainer
  @see: L{ComputeIPolicySpecViolation}
566 7352d33b Thomas Thrainer

567 7352d33b Thomas Thrainer
  """
568 5a13489b Bernardo Dal Seno
  ret = []
569 7352d33b Thomas Thrainer
  be_full = cfg.GetClusterInfo().FillBE(instance)
570 7352d33b Thomas Thrainer
  mem_size = be_full[constants.BE_MAXMEM]
571 7352d33b Thomas Thrainer
  cpu_count = be_full[constants.BE_VCPUS]
572 1c3231aa Thomas Thrainer
  es_flags = rpc.GetExclusiveStorageForNodes(cfg, instance.all_nodes)
573 5a13489b Bernardo Dal Seno
  if any(es_flags.values()):
574 5a13489b Bernardo Dal Seno
    # With exclusive storage use the actual spindles
575 5a13489b Bernardo Dal Seno
    try:
576 5a13489b Bernardo Dal Seno
      spindle_use = sum([disk.spindles for disk in instance.disks])
577 5a13489b Bernardo Dal Seno
    except TypeError:
578 5a13489b Bernardo Dal Seno
      ret.append("Number of spindles not configured for disks of instance %s"
579 5a13489b Bernardo Dal Seno
                 " while exclusive storage is enabled, try running gnt-cluster"
580 5a13489b Bernardo Dal Seno
                 " repair-disk-sizes" % instance.name)
581 5a13489b Bernardo Dal Seno
      # _ComputeMinMaxSpec ignores 'None's
582 5a13489b Bernardo Dal Seno
      spindle_use = None
583 5a13489b Bernardo Dal Seno
  else:
584 5a13489b Bernardo Dal Seno
    spindle_use = be_full[constants.BE_SPINDLE_USE]
585 7352d33b Thomas Thrainer
  disk_count = len(instance.disks)
586 7352d33b Thomas Thrainer
  disk_sizes = [disk.size for disk in instance.disks]
587 7352d33b Thomas Thrainer
  nic_count = len(instance.nics)
588 7352d33b Thomas Thrainer
  disk_template = instance.disk_template
589 7352d33b Thomas Thrainer
590 5a13489b Bernardo Dal Seno
  return ret + _compute_fn(ipolicy, mem_size, cpu_count, disk_count, nic_count,
591 5a13489b Bernardo Dal Seno
                           disk_sizes, spindle_use, disk_template)
592 7352d33b Thomas Thrainer
593 7352d33b Thomas Thrainer
594 7352d33b Thomas Thrainer
def _ComputeViolatingInstances(ipolicy, instances, cfg):
595 7352d33b Thomas Thrainer
  """Computes a set of instances who violates given ipolicy.
596 7352d33b Thomas Thrainer

597 7352d33b Thomas Thrainer
  @param ipolicy: The ipolicy to verify
598 7352d33b Thomas Thrainer
  @type instances: L{objects.Instance}
599 7352d33b Thomas Thrainer
  @param instances: List of instances to verify
600 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
601 7352d33b Thomas Thrainer
  @param cfg: Cluster configuration
602 7352d33b Thomas Thrainer
  @return: A frozenset of instance names violating the ipolicy
603 7352d33b Thomas Thrainer

604 7352d33b Thomas Thrainer
  """
605 7352d33b Thomas Thrainer
  return frozenset([inst.name for inst in instances
606 5eacbcae Thomas Thrainer
                    if ComputeIPolicyInstanceViolation(ipolicy, inst, cfg)])
607 7352d33b Thomas Thrainer
608 7352d33b Thomas Thrainer
609 5eacbcae Thomas Thrainer
def ComputeNewInstanceViolations(old_ipolicy, new_ipolicy, instances, cfg):
610 7352d33b Thomas Thrainer
  """Computes a set of any instances that would violate the new ipolicy.
611 7352d33b Thomas Thrainer

612 7352d33b Thomas Thrainer
  @param old_ipolicy: The current (still in-place) ipolicy
613 7352d33b Thomas Thrainer
  @param new_ipolicy: The new (to become) ipolicy
614 7352d33b Thomas Thrainer
  @param instances: List of instances to verify
615 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
616 7352d33b Thomas Thrainer
  @param cfg: Cluster configuration
617 7352d33b Thomas Thrainer
  @return: A list of instances which violates the new ipolicy but
618 7352d33b Thomas Thrainer
      did not before
619 7352d33b Thomas Thrainer

620 7352d33b Thomas Thrainer
  """
621 7352d33b Thomas Thrainer
  return (_ComputeViolatingInstances(new_ipolicy, instances, cfg) -
622 7352d33b Thomas Thrainer
          _ComputeViolatingInstances(old_ipolicy, instances, cfg))
623 7352d33b Thomas Thrainer
624 7352d33b Thomas Thrainer
625 5eacbcae Thomas Thrainer
def GetUpdatedParams(old_params, update_dict,
626 7352d33b Thomas Thrainer
                      use_default=True, use_none=False):
627 7352d33b Thomas Thrainer
  """Return the new version of a parameter dictionary.
628 7352d33b Thomas Thrainer

629 7352d33b Thomas Thrainer
  @type old_params: dict
630 7352d33b Thomas Thrainer
  @param old_params: old parameters
631 7352d33b Thomas Thrainer
  @type update_dict: dict
632 7352d33b Thomas Thrainer
  @param update_dict: dict containing new parameter values, or
633 7352d33b Thomas Thrainer
      constants.VALUE_DEFAULT to reset the parameter to its default
634 7352d33b Thomas Thrainer
      value
635 7352d33b Thomas Thrainer
  @param use_default: boolean
636 7352d33b Thomas Thrainer
  @type use_default: whether to recognise L{constants.VALUE_DEFAULT}
637 7352d33b Thomas Thrainer
      values as 'to be deleted' values
638 7352d33b Thomas Thrainer
  @param use_none: boolean
639 7352d33b Thomas Thrainer
  @type use_none: whether to recognise C{None} values as 'to be
640 7352d33b Thomas Thrainer
      deleted' values
641 7352d33b Thomas Thrainer
  @rtype: dict
642 7352d33b Thomas Thrainer
  @return: the new parameter dictionary
643 7352d33b Thomas Thrainer

644 7352d33b Thomas Thrainer
  """
645 7352d33b Thomas Thrainer
  params_copy = copy.deepcopy(old_params)
646 7352d33b Thomas Thrainer
  for key, val in update_dict.iteritems():
647 7352d33b Thomas Thrainer
    if ((use_default and val == constants.VALUE_DEFAULT) or
648 7352d33b Thomas Thrainer
          (use_none and val is None)):
649 7352d33b Thomas Thrainer
      try:
650 7352d33b Thomas Thrainer
        del params_copy[key]
651 7352d33b Thomas Thrainer
      except KeyError:
652 7352d33b Thomas Thrainer
        pass
653 7352d33b Thomas Thrainer
    else:
654 7352d33b Thomas Thrainer
      params_copy[key] = val
655 7352d33b Thomas Thrainer
  return params_copy
656 7352d33b Thomas Thrainer
657 7352d33b Thomas Thrainer
658 5eacbcae Thomas Thrainer
def GetUpdatedIPolicy(old_ipolicy, new_ipolicy, group_policy=False):
659 7352d33b Thomas Thrainer
  """Return the new version of an instance policy.
660 7352d33b Thomas Thrainer

661 7352d33b Thomas Thrainer
  @param group_policy: whether this policy applies to a group and thus
662 7352d33b Thomas Thrainer
    we should support removal of policy entries
663 7352d33b Thomas Thrainer

664 7352d33b Thomas Thrainer
  """
665 7352d33b Thomas Thrainer
  ipolicy = copy.deepcopy(old_ipolicy)
666 7352d33b Thomas Thrainer
  for key, value in new_ipolicy.items():
667 7352d33b Thomas Thrainer
    if key not in constants.IPOLICY_ALL_KEYS:
668 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Invalid key in new ipolicy: %s" % key,
669 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
670 7352d33b Thomas Thrainer
    if (not value or value == [constants.VALUE_DEFAULT] or
671 7352d33b Thomas Thrainer
            value == constants.VALUE_DEFAULT):
672 7352d33b Thomas Thrainer
      if group_policy:
673 7352d33b Thomas Thrainer
        if key in ipolicy:
674 7352d33b Thomas Thrainer
          del ipolicy[key]
675 7352d33b Thomas Thrainer
      else:
676 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Can't unset ipolicy attribute '%s'"
677 7352d33b Thomas Thrainer
                                   " on the cluster'" % key,
678 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
679 7352d33b Thomas Thrainer
    else:
680 7352d33b Thomas Thrainer
      if key in constants.IPOLICY_PARAMETERS:
681 7352d33b Thomas Thrainer
        # FIXME: we assume all such values are float
682 7352d33b Thomas Thrainer
        try:
683 7352d33b Thomas Thrainer
          ipolicy[key] = float(value)
684 7352d33b Thomas Thrainer
        except (TypeError, ValueError), err:
685 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Invalid value for attribute"
686 7352d33b Thomas Thrainer
                                     " '%s': '%s', error: %s" %
687 7352d33b Thomas Thrainer
                                     (key, value, err), errors.ECODE_INVAL)
688 7352d33b Thomas Thrainer
      elif key == constants.ISPECS_MINMAX:
689 7352d33b Thomas Thrainer
        for minmax in value:
690 7352d33b Thomas Thrainer
          for k in minmax.keys():
691 7352d33b Thomas Thrainer
            utils.ForceDictType(minmax[k], constants.ISPECS_PARAMETER_TYPES)
692 7352d33b Thomas Thrainer
        ipolicy[key] = value
693 7352d33b Thomas Thrainer
      elif key == constants.ISPECS_STD:
694 7352d33b Thomas Thrainer
        if group_policy:
695 7352d33b Thomas Thrainer
          msg = "%s cannot appear in group instance specs" % key
696 7352d33b Thomas Thrainer
          raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
697 5eacbcae Thomas Thrainer
        ipolicy[key] = GetUpdatedParams(old_ipolicy.get(key, {}), value,
698 5eacbcae Thomas Thrainer
                                        use_none=False, use_default=False)
699 7352d33b Thomas Thrainer
        utils.ForceDictType(ipolicy[key], constants.ISPECS_PARAMETER_TYPES)
700 7352d33b Thomas Thrainer
      else:
701 7352d33b Thomas Thrainer
        # FIXME: we assume all others are lists; this should be redone
702 7352d33b Thomas Thrainer
        # in a nicer way
703 7352d33b Thomas Thrainer
        ipolicy[key] = list(value)
704 7352d33b Thomas Thrainer
  try:
705 7352d33b Thomas Thrainer
    objects.InstancePolicy.CheckParameterSyntax(ipolicy, not group_policy)
706 7352d33b Thomas Thrainer
  except errors.ConfigurationError, err:
707 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("Invalid instance policy: %s" % err,
708 7352d33b Thomas Thrainer
                               errors.ECODE_INVAL)
709 7352d33b Thomas Thrainer
  return ipolicy
710 7352d33b Thomas Thrainer
711 7352d33b Thomas Thrainer
712 5eacbcae Thomas Thrainer
def AnnotateDiskParams(instance, devs, cfg):
713 7352d33b Thomas Thrainer
  """Little helper wrapper to the rpc annotation method.
714 7352d33b Thomas Thrainer

715 7352d33b Thomas Thrainer
  @param instance: The instance object
716 7352d33b Thomas Thrainer
  @type devs: List of L{objects.Disk}
717 7352d33b Thomas Thrainer
  @param devs: The root devices (not any of its children!)
718 7352d33b Thomas Thrainer
  @param cfg: The config object
719 7352d33b Thomas Thrainer
  @returns The annotated disk copies
720 7352d33b Thomas Thrainer
  @see L{rpc.AnnotateDiskParams}
721 7352d33b Thomas Thrainer

722 7352d33b Thomas Thrainer
  """
723 7352d33b Thomas Thrainer
  return rpc.AnnotateDiskParams(instance.disk_template, devs,
724 7352d33b Thomas Thrainer
                                cfg.GetInstanceDiskParams(instance))
725 7352d33b Thomas Thrainer
726 7352d33b Thomas Thrainer
727 5eacbcae Thomas Thrainer
def SupportsOob(cfg, node):
728 7352d33b Thomas Thrainer
  """Tells if node supports OOB.
729 7352d33b Thomas Thrainer

730 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
731 7352d33b Thomas Thrainer
  @param cfg: The cluster configuration
732 7352d33b Thomas Thrainer
  @type node: L{objects.Node}
733 7352d33b Thomas Thrainer
  @param node: The node
734 7352d33b Thomas Thrainer
  @return: The OOB script if supported or an empty string otherwise
735 7352d33b Thomas Thrainer

736 7352d33b Thomas Thrainer
  """
737 7352d33b Thomas Thrainer
  return cfg.GetNdParams(node)[constants.ND_OOB_PROGRAM]
738 7352d33b Thomas Thrainer
739 7352d33b Thomas Thrainer
740 7352d33b Thomas Thrainer
def _UpdateAndVerifySubDict(base, updates, type_check):
741 7352d33b Thomas Thrainer
  """Updates and verifies a dict with sub dicts of the same type.
742 7352d33b Thomas Thrainer

743 7352d33b Thomas Thrainer
  @param base: The dict with the old data
744 7352d33b Thomas Thrainer
  @param updates: The dict with the new data
745 7352d33b Thomas Thrainer
  @param type_check: Dict suitable to ForceDictType to verify correct types
746 7352d33b Thomas Thrainer
  @returns: A new dict with updated and verified values
747 7352d33b Thomas Thrainer

748 7352d33b Thomas Thrainer
  """
749 7352d33b Thomas Thrainer
  def fn(old, value):
750 5eacbcae Thomas Thrainer
    new = GetUpdatedParams(old, value)
751 7352d33b Thomas Thrainer
    utils.ForceDictType(new, type_check)
752 7352d33b Thomas Thrainer
    return new
753 7352d33b Thomas Thrainer
754 7352d33b Thomas Thrainer
  ret = copy.deepcopy(base)
755 7352d33b Thomas Thrainer
  ret.update(dict((key, fn(base.get(key, {}), value))
756 7352d33b Thomas Thrainer
                  for key, value in updates.items()))
757 7352d33b Thomas Thrainer
  return ret
758 7352d33b Thomas Thrainer
759 7352d33b Thomas Thrainer
760 1c3231aa Thomas Thrainer
def _FilterVmNodes(lu, node_uuids):
761 7352d33b Thomas Thrainer
  """Filters out non-vm_capable nodes from a list.
762 7352d33b Thomas Thrainer

763 7352d33b Thomas Thrainer
  @type lu: L{LogicalUnit}
764 7352d33b Thomas Thrainer
  @param lu: the logical unit for which we check
765 1c3231aa Thomas Thrainer
  @type node_uuids: list
766 1c3231aa Thomas Thrainer
  @param node_uuids: the list of nodes on which we should check
767 7352d33b Thomas Thrainer
  @rtype: list
768 7352d33b Thomas Thrainer
  @return: the list of vm-capable nodes
769 7352d33b Thomas Thrainer

770 7352d33b Thomas Thrainer
  """
771 7352d33b Thomas Thrainer
  vm_nodes = frozenset(lu.cfg.GetNonVmCapableNodeList())
772 1c3231aa Thomas Thrainer
  return [uuid for uuid in node_uuids if uuid not in vm_nodes]
773 f380d53c Thomas Thrainer
774 f380d53c Thomas Thrainer
775 5eacbcae Thomas Thrainer
def GetDefaultIAllocator(cfg, ialloc):
776 f380d53c Thomas Thrainer
  """Decides on which iallocator to use.
777 f380d53c Thomas Thrainer

778 f380d53c Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
779 f380d53c Thomas Thrainer
  @param cfg: Cluster configuration object
780 f380d53c Thomas Thrainer
  @type ialloc: string or None
781 f380d53c Thomas Thrainer
  @param ialloc: Iallocator specified in opcode
782 f380d53c Thomas Thrainer
  @rtype: string
783 f380d53c Thomas Thrainer
  @return: Iallocator name
784 f380d53c Thomas Thrainer

785 f380d53c Thomas Thrainer
  """
786 f380d53c Thomas Thrainer
  if not ialloc:
787 f380d53c Thomas Thrainer
    # Use default iallocator
788 f380d53c Thomas Thrainer
    ialloc = cfg.GetDefaultIAllocator()
789 f380d53c Thomas Thrainer
790 f380d53c Thomas Thrainer
  if not ialloc:
791 f380d53c Thomas Thrainer
    raise errors.OpPrereqError("No iallocator was specified, neither in the"
792 f380d53c Thomas Thrainer
                               " opcode nor as a cluster-wide default",
793 f380d53c Thomas Thrainer
                               errors.ECODE_INVAL)
794 f380d53c Thomas Thrainer
795 f380d53c Thomas Thrainer
  return ialloc
796 f380d53c Thomas Thrainer
797 f380d53c Thomas Thrainer
798 1c3231aa Thomas Thrainer
def CheckInstancesNodeGroups(cfg, instances, owned_groups, owned_node_uuids,
799 5eacbcae Thomas Thrainer
                             cur_group_uuid):
800 f380d53c Thomas Thrainer
  """Checks if node groups for locked instances are still correct.
801 f380d53c Thomas Thrainer

802 f380d53c Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
803 f380d53c Thomas Thrainer
  @param cfg: Cluster configuration
804 f380d53c Thomas Thrainer
  @type instances: dict; string as key, L{objects.Instance} as value
805 da4a52a3 Thomas Thrainer
  @param instances: Dictionary, instance UUID as key, instance object as value
806 f380d53c Thomas Thrainer
  @type owned_groups: iterable of string
807 f380d53c Thomas Thrainer
  @param owned_groups: List of owned groups
808 1c3231aa Thomas Thrainer
  @type owned_node_uuids: iterable of string
809 1c3231aa Thomas Thrainer
  @param owned_node_uuids: List of owned nodes
810 f380d53c Thomas Thrainer
  @type cur_group_uuid: string or None
811 f380d53c Thomas Thrainer
  @param cur_group_uuid: Optional group UUID to check against instance's groups
812 f380d53c Thomas Thrainer

813 f380d53c Thomas Thrainer
  """
814 da4a52a3 Thomas Thrainer
  for (uuid, inst) in instances.items():
815 1c3231aa Thomas Thrainer
    assert owned_node_uuids.issuperset(inst.all_nodes), \
816 da4a52a3 Thomas Thrainer
      "Instance %s's nodes changed while we kept the lock" % inst.name
817 f380d53c Thomas Thrainer
818 da4a52a3 Thomas Thrainer
    inst_groups = CheckInstanceNodeGroups(cfg, uuid, owned_groups)
819 f380d53c Thomas Thrainer
820 f380d53c Thomas Thrainer
    assert cur_group_uuid is None or cur_group_uuid in inst_groups, \
821 da4a52a3 Thomas Thrainer
      "Instance %s has no node in group %s" % (inst.name, cur_group_uuid)
822 f380d53c Thomas Thrainer
823 f380d53c Thomas Thrainer
824 da4a52a3 Thomas Thrainer
def CheckInstanceNodeGroups(cfg, inst_uuid, owned_groups, primary_only=False):
825 f380d53c Thomas Thrainer
  """Checks if the owned node groups are still correct for an instance.
826 f380d53c Thomas Thrainer

827 f380d53c Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
828 f380d53c Thomas Thrainer
  @param cfg: The cluster configuration
829 da4a52a3 Thomas Thrainer
  @type inst_uuid: string
830 da4a52a3 Thomas Thrainer
  @param inst_uuid: Instance UUID
831 f380d53c Thomas Thrainer
  @type owned_groups: set or frozenset
832 f380d53c Thomas Thrainer
  @param owned_groups: List of currently owned node groups
833 f380d53c Thomas Thrainer
  @type primary_only: boolean
834 f380d53c Thomas Thrainer
  @param primary_only: Whether to check node groups for only the primary node
835 f380d53c Thomas Thrainer

836 f380d53c Thomas Thrainer
  """
837 da4a52a3 Thomas Thrainer
  inst_groups = cfg.GetInstanceNodeGroups(inst_uuid, primary_only)
838 f380d53c Thomas Thrainer
839 f380d53c Thomas Thrainer
  if not owned_groups.issuperset(inst_groups):
840 f380d53c Thomas Thrainer
    raise errors.OpPrereqError("Instance %s's node groups changed since"
841 f380d53c Thomas Thrainer
                               " locks were acquired, current groups are"
842 f380d53c Thomas Thrainer
                               " are '%s', owning groups '%s'; retry the"
843 f380d53c Thomas Thrainer
                               " operation" %
844 da4a52a3 Thomas Thrainer
                               (cfg.GetInstanceName(inst_uuid),
845 f380d53c Thomas Thrainer
                                utils.CommaJoin(inst_groups),
846 f380d53c Thomas Thrainer
                                utils.CommaJoin(owned_groups)),
847 f380d53c Thomas Thrainer
                               errors.ECODE_STATE)
848 f380d53c Thomas Thrainer
849 f380d53c Thomas Thrainer
  return inst_groups
850 f380d53c Thomas Thrainer
851 f380d53c Thomas Thrainer
852 5eacbcae Thomas Thrainer
def LoadNodeEvacResult(lu, alloc_result, early_release, use_nodes):
853 f380d53c Thomas Thrainer
  """Unpacks the result of change-group and node-evacuate iallocator requests.
854 f380d53c Thomas Thrainer

855 f380d53c Thomas Thrainer
  Iallocator modes L{constants.IALLOCATOR_MODE_NODE_EVAC} and
856 f380d53c Thomas Thrainer
  L{constants.IALLOCATOR_MODE_CHG_GROUP}.
857 f380d53c Thomas Thrainer

858 f380d53c Thomas Thrainer
  @type lu: L{LogicalUnit}
859 f380d53c Thomas Thrainer
  @param lu: Logical unit instance
860 f380d53c Thomas Thrainer
  @type alloc_result: tuple/list
861 f380d53c Thomas Thrainer
  @param alloc_result: Result from iallocator
862 f380d53c Thomas Thrainer
  @type early_release: bool
863 f380d53c Thomas Thrainer
  @param early_release: Whether to release locks early if possible
864 f380d53c Thomas Thrainer
  @type use_nodes: bool
865 f380d53c Thomas Thrainer
  @param use_nodes: Whether to display node names instead of groups
866 f380d53c Thomas Thrainer

867 f380d53c Thomas Thrainer
  """
868 f380d53c Thomas Thrainer
  (moved, failed, jobs) = alloc_result
869 f380d53c Thomas Thrainer
870 f380d53c Thomas Thrainer
  if failed:
871 f380d53c Thomas Thrainer
    failreason = utils.CommaJoin("%s (%s)" % (name, reason)
872 f380d53c Thomas Thrainer
                                 for (name, reason) in failed)
873 f380d53c Thomas Thrainer
    lu.LogWarning("Unable to evacuate instances %s", failreason)
874 f380d53c Thomas Thrainer
    raise errors.OpExecError("Unable to evacuate instances %s" % failreason)
875 f380d53c Thomas Thrainer
876 f380d53c Thomas Thrainer
  if moved:
877 f380d53c Thomas Thrainer
    lu.LogInfo("Instances to be moved: %s",
878 1c3231aa Thomas Thrainer
               utils.CommaJoin(
879 1c3231aa Thomas Thrainer
                 "%s (to %s)" %
880 1c3231aa Thomas Thrainer
                 (name, _NodeEvacDest(use_nodes, group, node_names))
881 1c3231aa Thomas Thrainer
                 for (name, group, node_names) in moved))
882 f380d53c Thomas Thrainer
883 f380d53c Thomas Thrainer
  return [map(compat.partial(_SetOpEarlyRelease, early_release),
884 f380d53c Thomas Thrainer
              map(opcodes.OpCode.LoadOpCode, ops))
885 f380d53c Thomas Thrainer
          for ops in jobs]
886 f380d53c Thomas Thrainer
887 f380d53c Thomas Thrainer
888 1c3231aa Thomas Thrainer
def _NodeEvacDest(use_nodes, group, node_names):
889 f380d53c Thomas Thrainer
  """Returns group or nodes depending on caller's choice.
890 f380d53c Thomas Thrainer

891 f380d53c Thomas Thrainer
  """
892 f380d53c Thomas Thrainer
  if use_nodes:
893 1c3231aa Thomas Thrainer
    return utils.CommaJoin(node_names)
894 f380d53c Thomas Thrainer
  else:
895 f380d53c Thomas Thrainer
    return group
896 f380d53c Thomas Thrainer
897 f380d53c Thomas Thrainer
898 f380d53c Thomas Thrainer
def _SetOpEarlyRelease(early_release, op):
899 f380d53c Thomas Thrainer
  """Sets C{early_release} flag on opcodes if available.
900 f380d53c Thomas Thrainer

901 f380d53c Thomas Thrainer
  """
902 f380d53c Thomas Thrainer
  try:
903 f380d53c Thomas Thrainer
    op.early_release = early_release
904 f380d53c Thomas Thrainer
  except AttributeError:
905 f380d53c Thomas Thrainer
    assert not isinstance(op, opcodes.OpInstanceReplaceDisks)
906 f380d53c Thomas Thrainer
907 f380d53c Thomas Thrainer
  return op
908 f380d53c Thomas Thrainer
909 f380d53c Thomas Thrainer
910 843094ad Thomas Thrainer
def MapInstanceLvsToNodes(instances):
911 f380d53c Thomas Thrainer
  """Creates a map from (node, volume) to instance name.
912 f380d53c Thomas Thrainer

913 f380d53c Thomas Thrainer
  @type instances: list of L{objects.Instance}
914 843094ad Thomas Thrainer
  @rtype: dict; tuple of (node uuid, volume name) as key, L{objects.Instance}
915 843094ad Thomas Thrainer
          object as value
916 f380d53c Thomas Thrainer

917 f380d53c Thomas Thrainer
  """
918 843094ad Thomas Thrainer
  return dict(((node_uuid, vol), inst)
919 f380d53c Thomas Thrainer
              for inst in instances
920 1c3231aa Thomas Thrainer
              for (node_uuid, vols) in inst.MapLVsByNode().items()
921 f380d53c Thomas Thrainer
              for vol in vols)
922 31b836b8 Thomas Thrainer
923 31b836b8 Thomas Thrainer
924 5eacbcae Thomas Thrainer
def CheckParamsNotGlobal(params, glob_pars, kind, bad_levels, good_levels):
925 31b836b8 Thomas Thrainer
  """Make sure that none of the given paramters is global.
926 31b836b8 Thomas Thrainer

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

930 31b836b8 Thomas Thrainer
  @type params: dictionary
931 31b836b8 Thomas Thrainer
  @param params: Parameters to check
932 31b836b8 Thomas Thrainer
  @type glob_pars: dictionary
933 31b836b8 Thomas Thrainer
  @param glob_pars: Forbidden parameters
934 31b836b8 Thomas Thrainer
  @type kind: string
935 31b836b8 Thomas Thrainer
  @param kind: Kind of parameters (e.g. "node")
936 31b836b8 Thomas Thrainer
  @type bad_levels: string
937 31b836b8 Thomas Thrainer
  @param bad_levels: Level(s) at which the parameters are forbidden (e.g.
938 31b836b8 Thomas Thrainer
      "instance")
939 31b836b8 Thomas Thrainer
  @type good_levels: strings
940 31b836b8 Thomas Thrainer
  @param good_levels: Level(s) at which the parameters are allowed (e.g.
941 31b836b8 Thomas Thrainer
      "cluster or group")
942 31b836b8 Thomas Thrainer

943 31b836b8 Thomas Thrainer
  """
944 31b836b8 Thomas Thrainer
  used_globals = glob_pars.intersection(params)
945 31b836b8 Thomas Thrainer
  if used_globals:
946 31b836b8 Thomas Thrainer
    msg = ("The following %s parameters are global and cannot"
947 31b836b8 Thomas Thrainer
           " be customized at %s level, please modify them at"
948 31b836b8 Thomas Thrainer
           " %s level: %s" %
949 31b836b8 Thomas Thrainer
           (kind, bad_levels, good_levels, utils.CommaJoin(used_globals)))
950 31b836b8 Thomas Thrainer
    raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
951 31b836b8 Thomas Thrainer
952 31b836b8 Thomas Thrainer
953 5eacbcae Thomas Thrainer
def IsExclusiveStorageEnabledNode(cfg, node):
954 31b836b8 Thomas Thrainer
  """Whether exclusive_storage is in effect for the given node.
955 31b836b8 Thomas Thrainer

956 31b836b8 Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
957 31b836b8 Thomas Thrainer
  @param cfg: The cluster configuration
958 31b836b8 Thomas Thrainer
  @type node: L{objects.Node}
959 31b836b8 Thomas Thrainer
  @param node: The node
960 31b836b8 Thomas Thrainer
  @rtype: bool
961 31b836b8 Thomas Thrainer
  @return: The effective value of exclusive_storage
962 31b836b8 Thomas Thrainer

963 31b836b8 Thomas Thrainer
  """
964 31b836b8 Thomas Thrainer
  return cfg.GetNdParams(node)[constants.ND_EXCLUSIVE_STORAGE]
965 31b836b8 Thomas Thrainer
966 31b836b8 Thomas Thrainer
967 5eacbcae Thomas Thrainer
def CheckInstanceState(lu, instance, req_states, msg=None):
968 31b836b8 Thomas Thrainer
  """Ensure that an instance is in one of the required states.
969 31b836b8 Thomas Thrainer

970 31b836b8 Thomas Thrainer
  @param lu: the LU on behalf of which we make the check
971 31b836b8 Thomas Thrainer
  @param instance: the instance to check
972 31b836b8 Thomas Thrainer
  @param msg: if passed, should be a message to replace the default one
973 31b836b8 Thomas Thrainer
  @raise errors.OpPrereqError: if the instance is not in the required state
974 31b836b8 Thomas Thrainer

975 31b836b8 Thomas Thrainer
  """
976 31b836b8 Thomas Thrainer
  if msg is None:
977 31b836b8 Thomas Thrainer
    msg = ("can't use instance from outside %s states" %
978 31b836b8 Thomas Thrainer
           utils.CommaJoin(req_states))
979 31b836b8 Thomas Thrainer
  if instance.admin_state not in req_states:
980 31b836b8 Thomas Thrainer
    raise errors.OpPrereqError("Instance '%s' is marked to be %s, %s" %
981 31b836b8 Thomas Thrainer
                               (instance.name, instance.admin_state, msg),
982 31b836b8 Thomas Thrainer
                               errors.ECODE_STATE)
983 31b836b8 Thomas Thrainer
984 31b836b8 Thomas Thrainer
  if constants.ADMINST_UP not in req_states:
985 1c3231aa Thomas Thrainer
    pnode_uuid = instance.primary_node
986 1c3231aa Thomas Thrainer
    if not lu.cfg.GetNodeInfo(pnode_uuid).offline:
987 8ac806e6 Helga Velroyen
      all_hvparams = lu.cfg.GetClusterInfo().hvparams
988 1c3231aa Thomas Thrainer
      ins_l = lu.rpc.call_instance_list(
989 1c3231aa Thomas Thrainer
                [pnode_uuid], [instance.hypervisor], all_hvparams)[pnode_uuid]
990 1c3231aa Thomas Thrainer
      ins_l.Raise("Can't contact node %s for instance information" %
991 1c3231aa Thomas Thrainer
                  lu.cfg.GetNodeName(pnode_uuid),
992 31b836b8 Thomas Thrainer
                  prereq=True, ecode=errors.ECODE_ENVIRON)
993 31b836b8 Thomas Thrainer
      if instance.name in ins_l.payload:
994 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s is running, %s" %
995 31b836b8 Thomas Thrainer
                                   (instance.name, msg), errors.ECODE_STATE)
996 31b836b8 Thomas Thrainer
    else:
997 31b836b8 Thomas Thrainer
      lu.LogWarning("Primary node offline, ignoring check that instance"
998 31b836b8 Thomas Thrainer
                     " is down")
999 31b836b8 Thomas Thrainer
1000 31b836b8 Thomas Thrainer
1001 5eacbcae Thomas Thrainer
def CheckIAllocatorOrNode(lu, iallocator_slot, node_slot):
1002 31b836b8 Thomas Thrainer
  """Check the sanity of iallocator and node arguments and use the
1003 31b836b8 Thomas Thrainer
  cluster-wide iallocator if appropriate.
1004 31b836b8 Thomas Thrainer

1005 31b836b8 Thomas Thrainer
  Check that at most one of (iallocator, node) is specified. If none is
1006 31b836b8 Thomas Thrainer
  specified, or the iallocator is L{constants.DEFAULT_IALLOCATOR_SHORTCUT},
1007 31b836b8 Thomas Thrainer
  then the LU's opcode's iallocator slot is filled with the cluster-wide
1008 31b836b8 Thomas Thrainer
  default iallocator.
1009 31b836b8 Thomas Thrainer

1010 31b836b8 Thomas Thrainer
  @type iallocator_slot: string
1011 31b836b8 Thomas Thrainer
  @param iallocator_slot: the name of the opcode iallocator slot
1012 31b836b8 Thomas Thrainer
  @type node_slot: string
1013 31b836b8 Thomas Thrainer
  @param node_slot: the name of the opcode target node slot
1014 31b836b8 Thomas Thrainer

1015 31b836b8 Thomas Thrainer
  """
1016 31b836b8 Thomas Thrainer
  node = getattr(lu.op, node_slot, None)
1017 31b836b8 Thomas Thrainer
  ialloc = getattr(lu.op, iallocator_slot, None)
1018 31b836b8 Thomas Thrainer
  if node == []:
1019 31b836b8 Thomas Thrainer
    node = None
1020 31b836b8 Thomas Thrainer
1021 31b836b8 Thomas Thrainer
  if node is not None and ialloc is not None:
1022 31b836b8 Thomas Thrainer
    raise errors.OpPrereqError("Do not specify both, iallocator and node",
1023 31b836b8 Thomas Thrainer
                               errors.ECODE_INVAL)
1024 31b836b8 Thomas Thrainer
  elif ((node is None and ialloc is None) or
1025 31b836b8 Thomas Thrainer
        ialloc == constants.DEFAULT_IALLOCATOR_SHORTCUT):
1026 31b836b8 Thomas Thrainer
    default_iallocator = lu.cfg.GetDefaultIAllocator()
1027 31b836b8 Thomas Thrainer
    if default_iallocator:
1028 31b836b8 Thomas Thrainer
      setattr(lu.op, iallocator_slot, default_iallocator)
1029 31b836b8 Thomas Thrainer
    else:
1030 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("No iallocator or node given and no"
1031 31b836b8 Thomas Thrainer
                                 " cluster-wide default iallocator found;"
1032 31b836b8 Thomas Thrainer
                                 " please specify either an iallocator or a"
1033 31b836b8 Thomas Thrainer
                                 " node, or set a cluster-wide default"
1034 31b836b8 Thomas Thrainer
                                 " iallocator", errors.ECODE_INVAL)
1035 31b836b8 Thomas Thrainer
1036 31b836b8 Thomas Thrainer
1037 1c3231aa Thomas Thrainer
def FindFaultyInstanceDisks(cfg, rpc_runner, instance, node_uuid, prereq):
1038 31b836b8 Thomas Thrainer
  faulty = []
1039 31b836b8 Thomas Thrainer
1040 31b836b8 Thomas Thrainer
  for dev in instance.disks:
1041 1c3231aa Thomas Thrainer
    cfg.SetDiskID(dev, node_uuid)
1042 31b836b8 Thomas Thrainer
1043 1c3231aa Thomas Thrainer
  result = rpc_runner.call_blockdev_getmirrorstatus(
1044 1c3231aa Thomas Thrainer
             node_uuid, (instance.disks, instance))
1045 1c3231aa Thomas Thrainer
  result.Raise("Failed to get disk status from node %s" %
1046 1c3231aa Thomas Thrainer
               cfg.GetNodeName(node_uuid),
1047 31b836b8 Thomas Thrainer
               prereq=prereq, ecode=errors.ECODE_ENVIRON)
1048 31b836b8 Thomas Thrainer
1049 31b836b8 Thomas Thrainer
  for idx, bdev_status in enumerate(result.payload):
1050 31b836b8 Thomas Thrainer
    if bdev_status and bdev_status.ldisk_status == constants.LDS_FAULTY:
1051 31b836b8 Thomas Thrainer
      faulty.append(idx)
1052 31b836b8 Thomas Thrainer
1053 31b836b8 Thomas Thrainer
  return faulty
1054 22b7f6f8 Thomas Thrainer
1055 22b7f6f8 Thomas Thrainer
1056 1c3231aa Thomas Thrainer
def CheckNodeOnline(lu, node_uuid, msg=None):
1057 22b7f6f8 Thomas Thrainer
  """Ensure that a given node is online.
1058 22b7f6f8 Thomas Thrainer

1059 22b7f6f8 Thomas Thrainer
  @param lu: the LU on behalf of which we make the check
1060 1c3231aa Thomas Thrainer
  @param node_uuid: the node to check
1061 22b7f6f8 Thomas Thrainer
  @param msg: if passed, should be a message to replace the default one
1062 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node is offline
1063 22b7f6f8 Thomas Thrainer

1064 22b7f6f8 Thomas Thrainer
  """
1065 22b7f6f8 Thomas Thrainer
  if msg is None:
1066 22b7f6f8 Thomas Thrainer
    msg = "Can't use offline node"
1067 1c3231aa Thomas Thrainer
  if lu.cfg.GetNodeInfo(node_uuid).offline:
1068 1c3231aa Thomas Thrainer
    raise errors.OpPrereqError("%s: %s" % (msg, lu.cfg.GetNodeName(node_uuid)),
1069 1c3231aa Thomas Thrainer
                               errors.ECODE_STATE)
1070 1f7c8208 Helga Velroyen
1071 1f7c8208 Helga Velroyen
1072 1f7c8208 Helga Velroyen
def CheckDiskTemplateEnabled(cluster, disk_template):
1073 1f7c8208 Helga Velroyen
  """Helper function to check if a disk template is enabled.
1074 1f7c8208 Helga Velroyen

1075 1f7c8208 Helga Velroyen
  @type cluster: C{objects.Cluster}
1076 1f7c8208 Helga Velroyen
  @param cluster: the cluster's configuration
1077 1f7c8208 Helga Velroyen
  @type disk_template: str
1078 1f7c8208 Helga Velroyen
  @param disk_template: the disk template to be checked
1079 1f7c8208 Helga Velroyen

1080 1f7c8208 Helga Velroyen
  """
1081 1f7c8208 Helga Velroyen
  assert disk_template is not None
1082 bfbffd55 Helga Velroyen
  if disk_template not in constants.DISK_TEMPLATES:
1083 bfbffd55 Helga Velroyen
    raise errors.OpPrereqError("'%s' is not a valid disk template."
1084 bfbffd55 Helga Velroyen
                               " Valid disk templates are: %s" %
1085 bfbffd55 Helga Velroyen
                               (disk_template,
1086 bfbffd55 Helga Velroyen
                                ",".join(constants.DISK_TEMPLATES)))
1087 1f7c8208 Helga Velroyen
  if not disk_template in cluster.enabled_disk_templates:
1088 1f7c8208 Helga Velroyen
    raise errors.OpPrereqError("Disk template '%s' is not enabled in cluster."
1089 1f7c8208 Helga Velroyen
                               " Enabled disk templates are: %s" %
1090 1f7c8208 Helga Velroyen
                               (disk_template,
1091 1f7c8208 Helga Velroyen
                                ",".join(cluster.enabled_disk_templates)))
1092 1f7c8208 Helga Velroyen
1093 9d276e93 Helga Velroyen
1094 9d276e93 Helga Velroyen
def CheckStorageTypeEnabled(cluster, storage_type):
1095 9d276e93 Helga Velroyen
  """Helper function to check if a storage type is enabled.
1096 9d276e93 Helga Velroyen

1097 9d276e93 Helga Velroyen
  @type cluster: C{objects.Cluster}
1098 9d276e93 Helga Velroyen
  @param cluster: the cluster's configuration
1099 9d276e93 Helga Velroyen
  @type storage_type: str
1100 9d276e93 Helga Velroyen
  @param storage_type: the storage type to be checked
1101 9d276e93 Helga Velroyen

1102 9d276e93 Helga Velroyen
  """
1103 9d276e93 Helga Velroyen
  assert storage_type is not None
1104 d8e55568 Helga Velroyen
  assert storage_type in constants.STORAGE_TYPES
1105 9d276e93 Helga Velroyen
  # special case for lvm-pv, because it cannot be enabled
1106 9d276e93 Helga Velroyen
  # via disk templates
1107 9d276e93 Helga Velroyen
  if storage_type == constants.ST_LVM_PV:
1108 9d276e93 Helga Velroyen
    CheckStorageTypeEnabled(cluster, constants.ST_LVM_VG)
1109 9d276e93 Helga Velroyen
  else:
1110 9d276e93 Helga Velroyen
    possible_disk_templates = \
1111 9d276e93 Helga Velroyen
        utils.storage.GetDiskTemplatesOfStorageType(storage_type)
1112 9d276e93 Helga Velroyen
    for disk_template in possible_disk_templates:
1113 9d276e93 Helga Velroyen
      if disk_template in cluster.enabled_disk_templates:
1114 9d276e93 Helga Velroyen
        return
1115 9d276e93 Helga Velroyen
    raise errors.OpPrereqError("No disk template of storage type '%s' is"
1116 9d276e93 Helga Velroyen
                               " enabled in this cluster. Enabled disk"
1117 9d276e93 Helga Velroyen
                               " templates are: %s" % (storage_type,
1118 9d276e93 Helga Velroyen
                               ",".join(cluster.enabled_disk_templates)))