Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / common.py @ 178ad717

History | View | Annotate | Download (45 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 c1410048 Helga Velroyen
def AdjustCandidatePool(lu, exceptions, feedback_fn):
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 c1410048 Helga Velroyen
      cluster = lu.cfg.GetClusterInfo()
440 c1410048 Helga Velroyen
      AddNodeCertToCandidateCerts(lu, node.uuid, cluster)
441 c1410048 Helga Velroyen
      lu.cfg.Update(cluster, feedback_fn)
442 7352d33b Thomas Thrainer
  mc_now, mc_max, _ = lu.cfg.GetMasterCandidateStats(exceptions)
443 7352d33b Thomas Thrainer
  if mc_now > mc_max:
444 7352d33b Thomas Thrainer
    lu.LogInfo("Note: more nodes are candidates (%d) than desired (%d)" %
445 7352d33b Thomas Thrainer
               (mc_now, mc_max))
446 7352d33b Thomas Thrainer
447 7352d33b Thomas Thrainer
448 5eacbcae Thomas Thrainer
def CheckNodePVs(nresult, exclusive_storage):
449 7352d33b Thomas Thrainer
  """Check node PVs.
450 7352d33b Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

664 7352d33b Thomas Thrainer
  @param group_policy: whether this policy applies to a group and thus
665 7352d33b Thomas Thrainer
    we should support removal of policy entries
666 7352d33b Thomas Thrainer

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

718 7352d33b Thomas Thrainer
  @param instance: The instance object
719 7352d33b Thomas Thrainer
  @type devs: List of L{objects.Disk}
720 7352d33b Thomas Thrainer
  @param devs: The root devices (not any of its children!)
721 7352d33b Thomas Thrainer
  @param cfg: The config object
722 7352d33b Thomas Thrainer
  @returns The annotated disk copies
723 4e745e62 Santi Raffa
  @see L{rpc.node.AnnotateDiskParams}
724 7352d33b Thomas Thrainer

725 7352d33b Thomas Thrainer
  """
726 0c3d9c7c Thomas Thrainer
  return rpc.AnnotateDiskParams(devs, cfg.GetInstanceDiskParams(instance))
727 7352d33b Thomas Thrainer
728 7352d33b Thomas Thrainer
729 5eacbcae Thomas Thrainer
def SupportsOob(cfg, node):
730 7352d33b Thomas Thrainer
  """Tells if node supports OOB.
731 7352d33b Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

857 f380d53c Thomas Thrainer
  Iallocator modes L{constants.IALLOCATOR_MODE_NODE_EVAC} and
858 f380d53c Thomas Thrainer
  L{constants.IALLOCATOR_MODE_CHG_GROUP}.
859 f380d53c Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1144 294254b1 Raffa Santi
  @see: L{CheckDiskAccessModeConsistency} for cluster consistency checks.
1145 294254b1 Raffa Santi
  @raise errors.OpPrereqError: if the check fails.
1146 294254b1 Raffa Santi

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

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

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

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

1211 294254b1 Raffa Santi
  """
1212 294254b1 Raffa Santi
  if mode == constants.DISK_KERNELSPACE:
1213 294254b1 Raffa Santi
    return True
1214 294254b1 Raffa Santi
1215 294254b1 Raffa Santi
  if (hv == constants.HT_KVM and
1216 6488e5bc Santi Raffa
      disk_template in (constants.DT_RBD, constants.DT_GLUSTER) and
1217 294254b1 Raffa Santi
      mode == constants.DISK_USERSPACE):
1218 294254b1 Raffa Santi
    return True
1219 294254b1 Raffa Santi
1220 294254b1 Raffa Santi
  # Everything else:
1221 294254b1 Raffa Santi
  return False
1222 5b6f9e35 Helga Velroyen
1223 5b6f9e35 Helga Velroyen
1224 5b6f9e35 Helga Velroyen
def AddNodeCertToCandidateCerts(lu, node_uuid, cluster):
1225 5b6f9e35 Helga Velroyen
  """Add the node's client SSL certificate digest to the candidate certs.
1226 5b6f9e35 Helga Velroyen

1227 5b6f9e35 Helga Velroyen
  @type node_uuid: string
1228 5b6f9e35 Helga Velroyen
  @param node_uuid: the node's UUID
1229 5b6f9e35 Helga Velroyen
  @type cluster: C{object.Cluster}
1230 5b6f9e35 Helga Velroyen
  @param cluster: the cluster's configuration
1231 5b6f9e35 Helga Velroyen

1232 5b6f9e35 Helga Velroyen
  """
1233 5b6f9e35 Helga Velroyen
  result = lu.rpc.call_node_crypto_tokens(
1234 d722af8b Helga Velroyen
             node_uuid,
1235 d722af8b Helga Velroyen
             [(constants.CRYPTO_TYPE_SSL_DIGEST, constants.CRYPTO_ACTION_GET,
1236 d722af8b Helga Velroyen
               None)])
1237 5b6f9e35 Helga Velroyen
  result.Raise("Could not retrieve the node's (uuid %s) SSL digest."
1238 5b6f9e35 Helga Velroyen
               % node_uuid)
1239 5b6f9e35 Helga Velroyen
  ((crypto_type, digest), ) = result.payload
1240 5b6f9e35 Helga Velroyen
  assert crypto_type == constants.CRYPTO_TYPE_SSL_DIGEST
1241 5b6f9e35 Helga Velroyen
1242 5b6f9e35 Helga Velroyen
  utils.AddNodeToCandidateCerts(node_uuid, digest, cluster.candidate_certs)
1243 28756f80 Helga Velroyen
1244 28756f80 Helga Velroyen
1245 28756f80 Helga Velroyen
def RemoveNodeCertFromCandidateCerts(node_uuid, cluster):
1246 28756f80 Helga Velroyen
  """Removes the node's certificate from the candidate certificates list.
1247 28756f80 Helga Velroyen

1248 28756f80 Helga Velroyen
  @type node_uuid: string
1249 28756f80 Helga Velroyen
  @param node_uuid: the node's UUID
1250 28756f80 Helga Velroyen
  @type cluster: C{objects.Cluster}
1251 28756f80 Helga Velroyen
  @param cluster: the cluster's configuration
1252 28756f80 Helga Velroyen

1253 28756f80 Helga Velroyen
  """
1254 28756f80 Helga Velroyen
  utils.RemoveNodeFromCandidateCerts(node_uuid, cluster.candidate_certs)
1255 28756f80 Helga Velroyen
1256 28756f80 Helga Velroyen
1257 b3cc1646 Helga Velroyen
def CreateNewClientCert(lu, node_uuid, filename=None):
1258 28756f80 Helga Velroyen
  """Creates a new client SSL certificate for the node.
1259 28756f80 Helga Velroyen

1260 28756f80 Helga Velroyen
  @type node_uuid: string
1261 28756f80 Helga Velroyen
  @param node_uuid: the node's UUID
1262 28756f80 Helga Velroyen
  @type filename: string
1263 28756f80 Helga Velroyen
  @param filename: the certificate's filename
1264 28756f80 Helga Velroyen
  @rtype: string
1265 28756f80 Helga Velroyen
  @return: the digest of the newly created certificate
1266 28756f80 Helga Velroyen

1267 28756f80 Helga Velroyen
  """
1268 28756f80 Helga Velroyen
  options = {}
1269 28756f80 Helga Velroyen
  if filename:
1270 28756f80 Helga Velroyen
    options[constants.CRYPTO_OPTION_CERT_FILE] = filename
1271 b3cc1646 Helga Velroyen
  result = lu.rpc.call_node_crypto_tokens(
1272 28756f80 Helga Velroyen
             node_uuid,
1273 28756f80 Helga Velroyen
             [(constants.CRYPTO_TYPE_SSL_DIGEST,
1274 28756f80 Helga Velroyen
               constants.CRYPTO_ACTION_CREATE,
1275 28756f80 Helga Velroyen
               options)])
1276 28756f80 Helga Velroyen
  result.Raise("Could not create the node's (uuid %s) SSL client"
1277 28756f80 Helga Velroyen
               " certificate." % node_uuid)
1278 28756f80 Helga Velroyen
  ((crypto_type, new_digest), ) = result.payload
1279 28756f80 Helga Velroyen
  assert crypto_type == constants.CRYPTO_TYPE_SSL_DIGEST
1280 28756f80 Helga Velroyen
  return new_digest