Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / common.py @ 4869595d

History | View | Annotate | Download (43 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 4869595d Petr Pudlak
import ganeti.rpc.node as 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 0c3d9c7c Thomas Thrainer
  return rpc.AnnotateDiskParams(devs, cfg.GetInstanceDiskParams(instance))
724 7352d33b Thomas Thrainer
725 7352d33b Thomas Thrainer
726 5eacbcae Thomas Thrainer
def SupportsOob(cfg, node):
727 7352d33b Thomas Thrainer
  """Tells if node supports OOB.
728 7352d33b Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1071 1f7c8208 Helga Velroyen
  @type cluster: C{objects.Cluster}
1072 1f7c8208 Helga Velroyen
  @param cluster: the cluster's configuration
1073 1f7c8208 Helga Velroyen
  @type disk_template: str
1074 1f7c8208 Helga Velroyen
  @param disk_template: the disk template to be checked
1075 1f7c8208 Helga Velroyen

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

1093 9d276e93 Helga Velroyen
  @type cluster: C{objects.Cluster}
1094 9d276e93 Helga Velroyen
  @param cluster: the cluster's configuration
1095 9d276e93 Helga Velroyen
  @type storage_type: str
1096 9d276e93 Helga Velroyen
  @param storage_type: the storage type to be checked
1097 9d276e93 Helga Velroyen

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

1120 4e771a95 Helga Velroyen
  @type ipolicy: dict
1121 4e771a95 Helga Velroyen
  @param ipolicy: the new ipolicy
1122 4e771a95 Helga Velroyen
  @type enabled_disk_templates: list of string
1123 4e771a95 Helga Velroyen
  @param enabled_disk_templates: list of enabled disk templates on the
1124 4e771a95 Helga Velroyen
    cluster
1125 4e771a95 Helga Velroyen
  @raises errors.OpPrereqError: if there is at least one allowed disk
1126 4e771a95 Helga Velroyen
    template that is not also enabled.
1127 4e771a95 Helga Velroyen

1128 4e771a95 Helga Velroyen
  """
1129 4e771a95 Helga Velroyen
  assert constants.IPOLICY_DTS in ipolicy
1130 4e771a95 Helga Velroyen
  allowed_disk_templates = ipolicy[constants.IPOLICY_DTS]
1131 4e771a95 Helga Velroyen
  not_enabled = set(allowed_disk_templates) - set(enabled_disk_templates)
1132 4e771a95 Helga Velroyen
  if not_enabled:
1133 4e771a95 Helga Velroyen
    raise errors.OpPrereqError("The following disk template are allowed"
1134 4e771a95 Helga Velroyen
                               " by the ipolicy, but not enabled on the"
1135 4e771a95 Helga Velroyen
                               " cluster: %s" % utils.CommaJoin(not_enabled))
1136 294254b1 Raffa Santi
1137 294254b1 Raffa Santi
1138 294254b1 Raffa Santi
def CheckDiskAccessModeValidity(parameters):
1139 294254b1 Raffa Santi
  """Checks if the access parameter is legal.
1140 294254b1 Raffa Santi

1141 294254b1 Raffa Santi
  @see: L{CheckDiskAccessModeConsistency} for cluster consistency checks.
1142 294254b1 Raffa Santi
  @raise errors.OpPrereqError: if the check fails.
1143 294254b1 Raffa Santi

1144 294254b1 Raffa Santi
  """
1145 6488e5bc Santi Raffa
  for disk_template in parameters:
1146 6488e5bc Santi Raffa
    access = parameters[disk_template].get(constants.LDP_ACCESS,
1147 6488e5bc Santi Raffa
                                           constants.DISK_KERNELSPACE)
1148 294254b1 Raffa Santi
    if access not in constants.DISK_VALID_ACCESS_MODES:
1149 294254b1 Raffa Santi
      valid_vals_str = utils.CommaJoin(constants.DISK_VALID_ACCESS_MODES)
1150 294254b1 Raffa Santi
      raise errors.OpPrereqError("Invalid value of '{d}:{a}': '{v}' (expected"
1151 6488e5bc Santi Raffa
                                 " one of {o})".format(d=disk_template,
1152 6488e5bc Santi Raffa
                                                       a=constants.LDP_ACCESS,
1153 294254b1 Raffa Santi
                                                       v=access,
1154 294254b1 Raffa Santi
                                                       o=valid_vals_str))
1155 294254b1 Raffa Santi
1156 294254b1 Raffa Santi
1157 294254b1 Raffa Santi
def CheckDiskAccessModeConsistency(parameters, cfg, group=None):
1158 294254b1 Raffa Santi
  """Checks if the access param is consistent with the cluster configuration.
1159 294254b1 Raffa Santi

1160 294254b1 Raffa Santi
  @note: requires a configuration lock to run.
1161 294254b1 Raffa Santi
  @param parameters: the parameters to validate
1162 294254b1 Raffa Santi
  @param cfg: the cfg object of the cluster
1163 294254b1 Raffa Santi
  @param group: if set, only check for consistency within this group.
1164 294254b1 Raffa Santi
  @raise errors.OpPrereqError: if the LU attempts to change the access parameter
1165 294254b1 Raffa Santi
                               to an invalid value, such as "pink bunny".
1166 294254b1 Raffa Santi
  @raise errors.OpPrereqError: if the LU attempts to change the access parameter
1167 294254b1 Raffa Santi
                               to an inconsistent value, such as asking for RBD
1168 294254b1 Raffa Santi
                               userspace access to the chroot hypervisor.
1169 294254b1 Raffa Santi

1170 294254b1 Raffa Santi
  """
1171 294254b1 Raffa Santi
  CheckDiskAccessModeValidity(parameters)
1172 294254b1 Raffa Santi
1173 6488e5bc Santi Raffa
  for disk_template in parameters:
1174 6488e5bc Santi Raffa
    access = parameters[disk_template].get(constants.LDP_ACCESS,
1175 6488e5bc Santi Raffa
                                           constants.DISK_KERNELSPACE)
1176 6488e5bc Santi Raffa
1177 5a904197 Santi Raffa
    if disk_template not in constants.DTS_HAVE_ACCESS:
1178 6488e5bc Santi Raffa
      continue
1179 294254b1 Raffa Santi
1180 294254b1 Raffa Santi
    #Check the combination of instance hypervisor, disk template and access
1181 294254b1 Raffa Santi
    #protocol is sane.
1182 294254b1 Raffa Santi
    inst_uuids = cfg.GetNodeGroupInstances(group) if group else \
1183 294254b1 Raffa Santi
                 cfg.GetInstanceList()
1184 294254b1 Raffa Santi
1185 294254b1 Raffa Santi
    for entry in inst_uuids:
1186 294254b1 Raffa Santi
      inst = cfg.GetInstanceInfo(entry)
1187 294254b1 Raffa Santi
      hv = inst.hypervisor
1188 294254b1 Raffa Santi
      dt = inst.disk_template
1189 294254b1 Raffa Santi
1190 294254b1 Raffa Santi
      if not IsValidDiskAccessModeCombination(hv, dt, access):
1191 294254b1 Raffa Santi
        raise errors.OpPrereqError("Instance {i}: cannot use '{a}' access"
1192 294254b1 Raffa Santi
                                   " setting with {h} hypervisor and {d} disk"
1193 294254b1 Raffa Santi
                                   " type.".format(i=inst.name,
1194 294254b1 Raffa Santi
                                                   a=access,
1195 294254b1 Raffa Santi
                                                   h=hv,
1196 294254b1 Raffa Santi
                                                   d=dt))
1197 294254b1 Raffa Santi
1198 294254b1 Raffa Santi
1199 294254b1 Raffa Santi
def IsValidDiskAccessModeCombination(hv, disk_template, mode):
1200 294254b1 Raffa Santi
  """Checks if an hypervisor can read a disk template with given mode.
1201 294254b1 Raffa Santi

1202 294254b1 Raffa Santi
  @param hv: the hypervisor that will access the data
1203 294254b1 Raffa Santi
  @param disk_template: the disk template the data is stored as
1204 294254b1 Raffa Santi
  @param mode: how the hypervisor should access the data
1205 294254b1 Raffa Santi
  @return: True if the hypervisor can read a given read disk_template
1206 294254b1 Raffa Santi
           in the specified mode.
1207 294254b1 Raffa Santi

1208 294254b1 Raffa Santi
  """
1209 294254b1 Raffa Santi
  if mode == constants.DISK_KERNELSPACE:
1210 294254b1 Raffa Santi
    return True
1211 294254b1 Raffa Santi
1212 294254b1 Raffa Santi
  if (hv == constants.HT_KVM and
1213 6488e5bc Santi Raffa
      disk_template in (constants.DT_RBD, constants.DT_GLUSTER) and
1214 294254b1 Raffa Santi
      mode == constants.DISK_USERSPACE):
1215 294254b1 Raffa Santi
    return True
1216 294254b1 Raffa Santi
1217 294254b1 Raffa Santi
  # Everything else:
1218 294254b1 Raffa Santi
  return False