Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / common.py @ 1c4910f7

History | View | Annotate | Download (47.6 kB)

1 1a732a74 Thomas Thrainer
#
2 1a732a74 Thomas Thrainer
#
3 1a732a74 Thomas Thrainer
4 e04860cc Jose A. Lopes
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 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 07e3c124 Santi Raffa
from ganeti.serializer import Private
36 4869595d Petr Pudlak
import ganeti.rpc.node as rpc
37 7352d33b Thomas Thrainer
from ganeti import ssconf
38 37dc17e3 Thomas Thrainer
from ganeti import utils
39 1a732a74 Thomas Thrainer
40 1a732a74 Thomas Thrainer
41 31b836b8 Thomas Thrainer
# States of instance
42 31b836b8 Thomas Thrainer
INSTANCE_DOWN = [constants.ADMINST_DOWN]
43 31b836b8 Thomas Thrainer
INSTANCE_ONLINE = [constants.ADMINST_DOWN, constants.ADMINST_UP]
44 31b836b8 Thomas Thrainer
INSTANCE_NOT_RUNNING = [constants.ADMINST_DOWN, constants.ADMINST_OFFLINE]
45 31b836b8 Thomas Thrainer
46 31b836b8 Thomas Thrainer
#: Instance status in which an instance can be marked as offline/online
47 31b836b8 Thomas Thrainer
CAN_CHANGE_INSTANCE_OFFLINE = (frozenset(INSTANCE_DOWN) | frozenset([
48 31b836b8 Thomas Thrainer
  constants.ADMINST_OFFLINE,
49 31b836b8 Thomas Thrainer
  ]))
50 31b836b8 Thomas Thrainer
51 31b836b8 Thomas Thrainer
52 1c3231aa Thomas Thrainer
def _ExpandItemName(expand_fn, name, kind):
53 1a732a74 Thomas Thrainer
  """Expand an item name.
54 1a732a74 Thomas Thrainer

55 1c3231aa Thomas Thrainer
  @param expand_fn: the function to use for expansion
56 1a732a74 Thomas Thrainer
  @param name: requested item name
57 1a732a74 Thomas Thrainer
  @param kind: text description ('Node' or 'Instance')
58 1c3231aa Thomas Thrainer
  @return: the result of the expand_fn, if successful
59 1a732a74 Thomas Thrainer
  @raise errors.OpPrereqError: if the item is not found
60 1a732a74 Thomas Thrainer

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

384 7352d33b Thomas Thrainer
  """
385 1c3231aa Thomas Thrainer
  node_uuids = _FilterVmNodes(lu, node_uuids)
386 07e3c124 Santi Raffa
387 07e3c124 Santi Raffa
  # Last chance to unwrap private elements.
388 07e3c124 Santi Raffa
  for key in osparams:
389 07e3c124 Santi Raffa
    if isinstance(osparams[key], Private):
390 07e3c124 Santi Raffa
      osparams[key] = osparams[key].Get()
391 07e3c124 Santi Raffa
392 1c4910f7 Jose A. Lopes
  if osname:
393 1c4910f7 Jose A. Lopes
    result = lu.rpc.call_os_validate(node_uuids, required, osname,
394 1c4910f7 Jose A. Lopes
                                     [constants.OS_VALIDATE_PARAMETERS],
395 1c4910f7 Jose A. Lopes
                                     osparams)
396 1c4910f7 Jose A. Lopes
    for node_uuid, nres in result.items():
397 1c4910f7 Jose A. Lopes
      # we don't check for offline cases since this should be run only
398 1c4910f7 Jose A. Lopes
      # against the master node and/or an instance's nodes
399 1c4910f7 Jose A. Lopes
      nres.Raise("OS Parameters validation failed on node %s" %
400 1c4910f7 Jose A. Lopes
                 lu.cfg.GetNodeName(node_uuid))
401 1c4910f7 Jose A. Lopes
      if not nres.payload:
402 1c4910f7 Jose A. Lopes
        lu.LogInfo("OS %s not found on node %s, validation skipped",
403 1c4910f7 Jose A. Lopes
                   osname, lu.cfg.GetNodeName(node_uuid))
404 7352d33b Thomas Thrainer
405 7352d33b Thomas Thrainer
406 7bc4d6ac Jose A. Lopes
def CheckOSImage(op):
407 7bc4d6ac Jose A. Lopes
  """Checks if the OS image in the OS parameters of an opcode is
408 7bc4d6ac Jose A. Lopes
  valid.
409 7bc4d6ac Jose A. Lopes

410 7bc4d6ac Jose A. Lopes
  This function can also be used in LUs as they carry an opcode.
411 7bc4d6ac Jose A. Lopes

412 7bc4d6ac Jose A. Lopes
  @type op: L{opcodes.OpCode}
413 7bc4d6ac Jose A. Lopes
  @param op: opcode containing the OS params
414 7bc4d6ac Jose A. Lopes

415 7bc4d6ac Jose A. Lopes
  @rtype: string or NoneType
416 7bc4d6ac Jose A. Lopes
  @return:
417 7bc4d6ac Jose A. Lopes
    None if the OS parameters in the opcode do not contain the OS
418 7bc4d6ac Jose A. Lopes
    image, otherwise the OS image value contained in the OS parameters
419 7bc4d6ac Jose A. Lopes
  @raise errors.OpPrereqError: if OS image is not a URL or an absolute path
420 7bc4d6ac Jose A. Lopes

421 7bc4d6ac Jose A. Lopes
  """
422 7bc4d6ac Jose A. Lopes
  os_image = objects.GetOSImage(op.osparams)
423 7bc4d6ac Jose A. Lopes
424 7bc4d6ac Jose A. Lopes
  if os_image is None:
425 7bc4d6ac Jose A. Lopes
    return None
426 7bc4d6ac Jose A. Lopes
  elif utils.IsUrl(os_image) or os.path.exists(os_image):
427 7bc4d6ac Jose A. Lopes
    return os_image
428 7bc4d6ac Jose A. Lopes
  else:
429 7bc4d6ac Jose A. Lopes
    raise errors.OpPrereqError("OS image must be a URL or an absolute path")
430 7bc4d6ac Jose A. Lopes
431 7bc4d6ac Jose A. Lopes
432 1c3231aa Thomas Thrainer
def CheckHVParams(lu, node_uuids, hvname, hvparams):
433 7352d33b Thomas Thrainer
  """Hypervisor parameter validation.
434 7352d33b Thomas Thrainer

435 7352d33b Thomas Thrainer
  This function abstract the hypervisor parameter validation to be
436 7352d33b Thomas Thrainer
  used in both instance create and instance modify.
437 7352d33b Thomas Thrainer

438 7352d33b Thomas Thrainer
  @type lu: L{LogicalUnit}
439 7352d33b Thomas Thrainer
  @param lu: the logical unit for which we check
440 1c3231aa Thomas Thrainer
  @type node_uuids: list
441 1c3231aa Thomas Thrainer
  @param node_uuids: the list of nodes on which we should check
442 7352d33b Thomas Thrainer
  @type hvname: string
443 7352d33b Thomas Thrainer
  @param hvname: the name of the hypervisor we should use
444 7352d33b Thomas Thrainer
  @type hvparams: dict
445 7352d33b Thomas Thrainer
  @param hvparams: the parameters which we need to check
446 7352d33b Thomas Thrainer
  @raise errors.OpPrereqError: if the parameters are not valid
447 7352d33b Thomas Thrainer

448 7352d33b Thomas Thrainer
  """
449 1c3231aa Thomas Thrainer
  node_uuids = _FilterVmNodes(lu, node_uuids)
450 7352d33b Thomas Thrainer
451 7352d33b Thomas Thrainer
  cluster = lu.cfg.GetClusterInfo()
452 7352d33b Thomas Thrainer
  hvfull = objects.FillDict(cluster.hvparams.get(hvname, {}), hvparams)
453 7352d33b Thomas Thrainer
454 1c3231aa Thomas Thrainer
  hvinfo = lu.rpc.call_hypervisor_validate_params(node_uuids, hvname, hvfull)
455 1c3231aa Thomas Thrainer
  for node_uuid in node_uuids:
456 1c3231aa Thomas Thrainer
    info = hvinfo[node_uuid]
457 7352d33b Thomas Thrainer
    if info.offline:
458 7352d33b Thomas Thrainer
      continue
459 1c3231aa Thomas Thrainer
    info.Raise("Hypervisor parameter validation failed on node %s" %
460 1c3231aa Thomas Thrainer
               lu.cfg.GetNodeName(node_uuid))
461 7352d33b Thomas Thrainer
462 7352d33b Thomas Thrainer
463 c1410048 Helga Velroyen
def AdjustCandidatePool(lu, exceptions, feedback_fn):
464 7352d33b Thomas Thrainer
  """Adjust the candidate pool after node operations.
465 7352d33b Thomas Thrainer

466 7352d33b Thomas Thrainer
  """
467 7352d33b Thomas Thrainer
  mod_list = lu.cfg.MaintainCandidatePool(exceptions)
468 7352d33b Thomas Thrainer
  if mod_list:
469 7352d33b Thomas Thrainer
    lu.LogInfo("Promoted nodes to master candidate role: %s",
470 7352d33b Thomas Thrainer
               utils.CommaJoin(node.name for node in mod_list))
471 1c3231aa Thomas Thrainer
    for node in mod_list:
472 1c3231aa Thomas Thrainer
      lu.context.ReaddNode(node)
473 c1410048 Helga Velroyen
      cluster = lu.cfg.GetClusterInfo()
474 c1410048 Helga Velroyen
      AddNodeCertToCandidateCerts(lu, node.uuid, cluster)
475 c1410048 Helga Velroyen
      lu.cfg.Update(cluster, feedback_fn)
476 7352d33b Thomas Thrainer
  mc_now, mc_max, _ = lu.cfg.GetMasterCandidateStats(exceptions)
477 7352d33b Thomas Thrainer
  if mc_now > mc_max:
478 7352d33b Thomas Thrainer
    lu.LogInfo("Note: more nodes are candidates (%d) than desired (%d)" %
479 7352d33b Thomas Thrainer
               (mc_now, mc_max))
480 7352d33b Thomas Thrainer
481 7352d33b Thomas Thrainer
482 5eacbcae Thomas Thrainer
def CheckNodePVs(nresult, exclusive_storage):
483 7352d33b Thomas Thrainer
  """Check node PVs.
484 7352d33b Thomas Thrainer

485 7352d33b Thomas Thrainer
  """
486 7352d33b Thomas Thrainer
  pvlist_dict = nresult.get(constants.NV_PVLIST, None)
487 7352d33b Thomas Thrainer
  if pvlist_dict is None:
488 7352d33b Thomas Thrainer
    return (["Can't get PV list from node"], None)
489 7352d33b Thomas Thrainer
  pvlist = map(objects.LvmPvInfo.FromDict, pvlist_dict)
490 7352d33b Thomas Thrainer
  errlist = []
491 7352d33b Thomas Thrainer
  # check that ':' is not present in PV names, since it's a
492 7352d33b Thomas Thrainer
  # special character for lvcreate (denotes the range of PEs to
493 7352d33b Thomas Thrainer
  # use on the PV)
494 7352d33b Thomas Thrainer
  for pv in pvlist:
495 7352d33b Thomas Thrainer
    if ":" in pv.name:
496 7352d33b Thomas Thrainer
      errlist.append("Invalid character ':' in PV '%s' of VG '%s'" %
497 7352d33b Thomas Thrainer
                     (pv.name, pv.vg_name))
498 7352d33b Thomas Thrainer
  es_pvinfo = None
499 7352d33b Thomas Thrainer
  if exclusive_storage:
500 7352d33b Thomas Thrainer
    (errmsgs, es_pvinfo) = utils.LvmExclusiveCheckNodePvs(pvlist)
501 7352d33b Thomas Thrainer
    errlist.extend(errmsgs)
502 7352d33b Thomas Thrainer
    shared_pvs = nresult.get(constants.NV_EXCLUSIVEPVS, None)
503 7352d33b Thomas Thrainer
    if shared_pvs:
504 7352d33b Thomas Thrainer
      for (pvname, lvlist) in shared_pvs:
505 7352d33b Thomas Thrainer
        # TODO: Check that LVs are really unrelated (snapshots, DRBD meta...)
506 7352d33b Thomas Thrainer
        errlist.append("PV %s is shared among unrelated LVs (%s)" %
507 7352d33b Thomas Thrainer
                       (pvname, utils.CommaJoin(lvlist)))
508 7352d33b Thomas Thrainer
  return (errlist, es_pvinfo)
509 7352d33b Thomas Thrainer
510 7352d33b Thomas Thrainer
511 7352d33b Thomas Thrainer
def _ComputeMinMaxSpec(name, qualifier, ispecs, value):
512 7352d33b Thomas Thrainer
  """Computes if value is in the desired range.
513 7352d33b Thomas Thrainer

514 7352d33b Thomas Thrainer
  @param name: name of the parameter for which we perform the check
515 7352d33b Thomas Thrainer
  @param qualifier: a qualifier used in the error message (e.g. 'disk/1',
516 7352d33b Thomas Thrainer
      not just 'disk')
517 7352d33b Thomas Thrainer
  @param ispecs: dictionary containing min and max values
518 7352d33b Thomas Thrainer
  @param value: actual value that we want to use
519 7352d33b Thomas Thrainer
  @return: None or an error string
520 7352d33b Thomas Thrainer

521 7352d33b Thomas Thrainer
  """
522 7352d33b Thomas Thrainer
  if value in [None, constants.VALUE_AUTO]:
523 7352d33b Thomas Thrainer
    return None
524 7352d33b Thomas Thrainer
  max_v = ispecs[constants.ISPECS_MAX].get(name, value)
525 7352d33b Thomas Thrainer
  min_v = ispecs[constants.ISPECS_MIN].get(name, value)
526 7352d33b Thomas Thrainer
  if value > max_v or min_v > value:
527 7352d33b Thomas Thrainer
    if qualifier:
528 7352d33b Thomas Thrainer
      fqn = "%s/%s" % (name, qualifier)
529 7352d33b Thomas Thrainer
    else:
530 7352d33b Thomas Thrainer
      fqn = name
531 7352d33b Thomas Thrainer
    return ("%s value %s is not in range [%s, %s]" %
532 7352d33b Thomas Thrainer
            (fqn, value, min_v, max_v))
533 7352d33b Thomas Thrainer
  return None
534 7352d33b Thomas Thrainer
535 7352d33b Thomas Thrainer
536 5eacbcae Thomas Thrainer
def ComputeIPolicySpecViolation(ipolicy, mem_size, cpu_count, disk_count,
537 5eacbcae Thomas Thrainer
                                nic_count, disk_sizes, spindle_use,
538 5eacbcae Thomas Thrainer
                                disk_template,
539 5eacbcae Thomas Thrainer
                                _compute_fn=_ComputeMinMaxSpec):
540 7352d33b Thomas Thrainer
  """Verifies ipolicy against provided specs.
541 7352d33b Thomas Thrainer

542 7352d33b Thomas Thrainer
  @type ipolicy: dict
543 7352d33b Thomas Thrainer
  @param ipolicy: The ipolicy
544 7352d33b Thomas Thrainer
  @type mem_size: int
545 7352d33b Thomas Thrainer
  @param mem_size: The memory size
546 7352d33b Thomas Thrainer
  @type cpu_count: int
547 7352d33b Thomas Thrainer
  @param cpu_count: Used cpu cores
548 7352d33b Thomas Thrainer
  @type disk_count: int
549 7352d33b Thomas Thrainer
  @param disk_count: Number of disks used
550 7352d33b Thomas Thrainer
  @type nic_count: int
551 7352d33b Thomas Thrainer
  @param nic_count: Number of nics used
552 7352d33b Thomas Thrainer
  @type disk_sizes: list of ints
553 7352d33b Thomas Thrainer
  @param disk_sizes: Disk sizes of used disk (len must match C{disk_count})
554 7352d33b Thomas Thrainer
  @type spindle_use: int
555 7352d33b Thomas Thrainer
  @param spindle_use: The number of spindles this instance uses
556 7352d33b Thomas Thrainer
  @type disk_template: string
557 7352d33b Thomas Thrainer
  @param disk_template: The disk template of the instance
558 7352d33b Thomas Thrainer
  @param _compute_fn: The compute function (unittest only)
559 7352d33b Thomas Thrainer
  @return: A list of violations, or an empty list of no violations are found
560 7352d33b Thomas Thrainer

561 7352d33b Thomas Thrainer
  """
562 7352d33b Thomas Thrainer
  assert disk_count == len(disk_sizes)
563 7352d33b Thomas Thrainer
564 7352d33b Thomas Thrainer
  test_settings = [
565 7352d33b Thomas Thrainer
    (constants.ISPEC_MEM_SIZE, "", mem_size),
566 7352d33b Thomas Thrainer
    (constants.ISPEC_CPU_COUNT, "", cpu_count),
567 7352d33b Thomas Thrainer
    (constants.ISPEC_NIC_COUNT, "", nic_count),
568 7352d33b Thomas Thrainer
    (constants.ISPEC_SPINDLE_USE, "", spindle_use),
569 7352d33b Thomas Thrainer
    ] + [(constants.ISPEC_DISK_SIZE, str(idx), d)
570 7352d33b Thomas Thrainer
         for idx, d in enumerate(disk_sizes)]
571 7352d33b Thomas Thrainer
  if disk_template != constants.DT_DISKLESS:
572 7352d33b Thomas Thrainer
    # This check doesn't make sense for diskless instances
573 7352d33b Thomas Thrainer
    test_settings.append((constants.ISPEC_DISK_COUNT, "", disk_count))
574 7352d33b Thomas Thrainer
  ret = []
575 7352d33b Thomas Thrainer
  allowed_dts = ipolicy[constants.IPOLICY_DTS]
576 7352d33b Thomas Thrainer
  if disk_template not in allowed_dts:
577 7352d33b Thomas Thrainer
    ret.append("Disk template %s is not allowed (allowed templates: %s)" %
578 7352d33b Thomas Thrainer
               (disk_template, utils.CommaJoin(allowed_dts)))
579 7352d33b Thomas Thrainer
580 7352d33b Thomas Thrainer
  min_errs = None
581 7352d33b Thomas Thrainer
  for minmax in ipolicy[constants.ISPECS_MINMAX]:
582 7352d33b Thomas Thrainer
    errs = filter(None,
583 7352d33b Thomas Thrainer
                  (_compute_fn(name, qualifier, minmax, value)
584 7352d33b Thomas Thrainer
                   for (name, qualifier, value) in test_settings))
585 7352d33b Thomas Thrainer
    if min_errs is None or len(errs) < len(min_errs):
586 7352d33b Thomas Thrainer
      min_errs = errs
587 7352d33b Thomas Thrainer
  assert min_errs is not None
588 7352d33b Thomas Thrainer
  return ret + min_errs
589 7352d33b Thomas Thrainer
590 7352d33b Thomas Thrainer
591 5eacbcae Thomas Thrainer
def ComputeIPolicyInstanceViolation(ipolicy, instance, cfg,
592 5eacbcae Thomas Thrainer
                                    _compute_fn=ComputeIPolicySpecViolation):
593 7352d33b Thomas Thrainer
  """Compute if instance meets the specs of ipolicy.
594 7352d33b Thomas Thrainer

595 7352d33b Thomas Thrainer
  @type ipolicy: dict
596 7352d33b Thomas Thrainer
  @param ipolicy: The ipolicy to verify against
597 7352d33b Thomas Thrainer
  @type instance: L{objects.Instance}
598 7352d33b Thomas Thrainer
  @param instance: The instance to verify
599 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
600 7352d33b Thomas Thrainer
  @param cfg: Cluster configuration
601 7352d33b Thomas Thrainer
  @param _compute_fn: The function to verify ipolicy (unittest only)
602 5eacbcae Thomas Thrainer
  @see: L{ComputeIPolicySpecViolation}
603 7352d33b Thomas Thrainer

604 7352d33b Thomas Thrainer
  """
605 5a13489b Bernardo Dal Seno
  ret = []
606 7352d33b Thomas Thrainer
  be_full = cfg.GetClusterInfo().FillBE(instance)
607 7352d33b Thomas Thrainer
  mem_size = be_full[constants.BE_MAXMEM]
608 7352d33b Thomas Thrainer
  cpu_count = be_full[constants.BE_VCPUS]
609 1c3231aa Thomas Thrainer
  es_flags = rpc.GetExclusiveStorageForNodes(cfg, instance.all_nodes)
610 5a13489b Bernardo Dal Seno
  if any(es_flags.values()):
611 5a13489b Bernardo Dal Seno
    # With exclusive storage use the actual spindles
612 5a13489b Bernardo Dal Seno
    try:
613 5a13489b Bernardo Dal Seno
      spindle_use = sum([disk.spindles for disk in instance.disks])
614 5a13489b Bernardo Dal Seno
    except TypeError:
615 5a13489b Bernardo Dal Seno
      ret.append("Number of spindles not configured for disks of instance %s"
616 5a13489b Bernardo Dal Seno
                 " while exclusive storage is enabled, try running gnt-cluster"
617 5a13489b Bernardo Dal Seno
                 " repair-disk-sizes" % instance.name)
618 5a13489b Bernardo Dal Seno
      # _ComputeMinMaxSpec ignores 'None's
619 5a13489b Bernardo Dal Seno
      spindle_use = None
620 5a13489b Bernardo Dal Seno
  else:
621 5a13489b Bernardo Dal Seno
    spindle_use = be_full[constants.BE_SPINDLE_USE]
622 7352d33b Thomas Thrainer
  disk_count = len(instance.disks)
623 7352d33b Thomas Thrainer
  disk_sizes = [disk.size for disk in instance.disks]
624 7352d33b Thomas Thrainer
  nic_count = len(instance.nics)
625 7352d33b Thomas Thrainer
  disk_template = instance.disk_template
626 7352d33b Thomas Thrainer
627 5a13489b Bernardo Dal Seno
  return ret + _compute_fn(ipolicy, mem_size, cpu_count, disk_count, nic_count,
628 5a13489b Bernardo Dal Seno
                           disk_sizes, spindle_use, disk_template)
629 7352d33b Thomas Thrainer
630 7352d33b Thomas Thrainer
631 7352d33b Thomas Thrainer
def _ComputeViolatingInstances(ipolicy, instances, cfg):
632 7352d33b Thomas Thrainer
  """Computes a set of instances who violates given ipolicy.
633 7352d33b Thomas Thrainer

634 7352d33b Thomas Thrainer
  @param ipolicy: The ipolicy to verify
635 7352d33b Thomas Thrainer
  @type instances: L{objects.Instance}
636 7352d33b Thomas Thrainer
  @param instances: List of instances to verify
637 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
638 7352d33b Thomas Thrainer
  @param cfg: Cluster configuration
639 7352d33b Thomas Thrainer
  @return: A frozenset of instance names violating the ipolicy
640 7352d33b Thomas Thrainer

641 7352d33b Thomas Thrainer
  """
642 7352d33b Thomas Thrainer
  return frozenset([inst.name for inst in instances
643 5eacbcae Thomas Thrainer
                    if ComputeIPolicyInstanceViolation(ipolicy, inst, cfg)])
644 7352d33b Thomas Thrainer
645 7352d33b Thomas Thrainer
646 5eacbcae Thomas Thrainer
def ComputeNewInstanceViolations(old_ipolicy, new_ipolicy, instances, cfg):
647 7352d33b Thomas Thrainer
  """Computes a set of any instances that would violate the new ipolicy.
648 7352d33b Thomas Thrainer

649 7352d33b Thomas Thrainer
  @param old_ipolicy: The current (still in-place) ipolicy
650 7352d33b Thomas Thrainer
  @param new_ipolicy: The new (to become) ipolicy
651 7352d33b Thomas Thrainer
  @param instances: List of instances to verify
652 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
653 7352d33b Thomas Thrainer
  @param cfg: Cluster configuration
654 7352d33b Thomas Thrainer
  @return: A list of instances which violates the new ipolicy but
655 7352d33b Thomas Thrainer
      did not before
656 7352d33b Thomas Thrainer

657 7352d33b Thomas Thrainer
  """
658 7352d33b Thomas Thrainer
  return (_ComputeViolatingInstances(new_ipolicy, instances, cfg) -
659 7352d33b Thomas Thrainer
          _ComputeViolatingInstances(old_ipolicy, instances, cfg))
660 7352d33b Thomas Thrainer
661 7352d33b Thomas Thrainer
662 5eacbcae Thomas Thrainer
def GetUpdatedParams(old_params, update_dict,
663 7352d33b Thomas Thrainer
                      use_default=True, use_none=False):
664 7352d33b Thomas Thrainer
  """Return the new version of a parameter dictionary.
665 7352d33b Thomas Thrainer

666 7352d33b Thomas Thrainer
  @type old_params: dict
667 7352d33b Thomas Thrainer
  @param old_params: old parameters
668 7352d33b Thomas Thrainer
  @type update_dict: dict
669 7352d33b Thomas Thrainer
  @param update_dict: dict containing new parameter values, or
670 7352d33b Thomas Thrainer
      constants.VALUE_DEFAULT to reset the parameter to its default
671 7352d33b Thomas Thrainer
      value
672 7352d33b Thomas Thrainer
  @param use_default: boolean
673 7352d33b Thomas Thrainer
  @type use_default: whether to recognise L{constants.VALUE_DEFAULT}
674 7352d33b Thomas Thrainer
      values as 'to be deleted' values
675 7352d33b Thomas Thrainer
  @param use_none: boolean
676 7352d33b Thomas Thrainer
  @type use_none: whether to recognise C{None} values as 'to be
677 7352d33b Thomas Thrainer
      deleted' values
678 7352d33b Thomas Thrainer
  @rtype: dict
679 7352d33b Thomas Thrainer
  @return: the new parameter dictionary
680 7352d33b Thomas Thrainer

681 7352d33b Thomas Thrainer
  """
682 7352d33b Thomas Thrainer
  params_copy = copy.deepcopy(old_params)
683 7352d33b Thomas Thrainer
  for key, val in update_dict.iteritems():
684 7352d33b Thomas Thrainer
    if ((use_default and val == constants.VALUE_DEFAULT) or
685 7352d33b Thomas Thrainer
          (use_none and val is None)):
686 7352d33b Thomas Thrainer
      try:
687 7352d33b Thomas Thrainer
        del params_copy[key]
688 7352d33b Thomas Thrainer
      except KeyError:
689 7352d33b Thomas Thrainer
        pass
690 7352d33b Thomas Thrainer
    else:
691 7352d33b Thomas Thrainer
      params_copy[key] = val
692 7352d33b Thomas Thrainer
  return params_copy
693 7352d33b Thomas Thrainer
694 7352d33b Thomas Thrainer
695 5eacbcae Thomas Thrainer
def GetUpdatedIPolicy(old_ipolicy, new_ipolicy, group_policy=False):
696 7352d33b Thomas Thrainer
  """Return the new version of an instance policy.
697 7352d33b Thomas Thrainer

698 7352d33b Thomas Thrainer
  @param group_policy: whether this policy applies to a group and thus
699 7352d33b Thomas Thrainer
    we should support removal of policy entries
700 7352d33b Thomas Thrainer

701 7352d33b Thomas Thrainer
  """
702 7352d33b Thomas Thrainer
  ipolicy = copy.deepcopy(old_ipolicy)
703 7352d33b Thomas Thrainer
  for key, value in new_ipolicy.items():
704 7352d33b Thomas Thrainer
    if key not in constants.IPOLICY_ALL_KEYS:
705 7352d33b Thomas Thrainer
      raise errors.OpPrereqError("Invalid key in new ipolicy: %s" % key,
706 7352d33b Thomas Thrainer
                                 errors.ECODE_INVAL)
707 7352d33b Thomas Thrainer
    if (not value or value == [constants.VALUE_DEFAULT] or
708 7352d33b Thomas Thrainer
            value == constants.VALUE_DEFAULT):
709 7352d33b Thomas Thrainer
      if group_policy:
710 7352d33b Thomas Thrainer
        if key in ipolicy:
711 7352d33b Thomas Thrainer
          del ipolicy[key]
712 7352d33b Thomas Thrainer
      else:
713 7352d33b Thomas Thrainer
        raise errors.OpPrereqError("Can't unset ipolicy attribute '%s'"
714 7352d33b Thomas Thrainer
                                   " on the cluster'" % key,
715 7352d33b Thomas Thrainer
                                   errors.ECODE_INVAL)
716 7352d33b Thomas Thrainer
    else:
717 7352d33b Thomas Thrainer
      if key in constants.IPOLICY_PARAMETERS:
718 7352d33b Thomas Thrainer
        # FIXME: we assume all such values are float
719 7352d33b Thomas Thrainer
        try:
720 7352d33b Thomas Thrainer
          ipolicy[key] = float(value)
721 7352d33b Thomas Thrainer
        except (TypeError, ValueError), err:
722 7352d33b Thomas Thrainer
          raise errors.OpPrereqError("Invalid value for attribute"
723 7352d33b Thomas Thrainer
                                     " '%s': '%s', error: %s" %
724 7352d33b Thomas Thrainer
                                     (key, value, err), errors.ECODE_INVAL)
725 7352d33b Thomas Thrainer
      elif key == constants.ISPECS_MINMAX:
726 7352d33b Thomas Thrainer
        for minmax in value:
727 7352d33b Thomas Thrainer
          for k in minmax.keys():
728 7352d33b Thomas Thrainer
            utils.ForceDictType(minmax[k], constants.ISPECS_PARAMETER_TYPES)
729 7352d33b Thomas Thrainer
        ipolicy[key] = value
730 7352d33b Thomas Thrainer
      elif key == constants.ISPECS_STD:
731 7352d33b Thomas Thrainer
        if group_policy:
732 7352d33b Thomas Thrainer
          msg = "%s cannot appear in group instance specs" % key
733 7352d33b Thomas Thrainer
          raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
734 5eacbcae Thomas Thrainer
        ipolicy[key] = GetUpdatedParams(old_ipolicy.get(key, {}), value,
735 5eacbcae Thomas Thrainer
                                        use_none=False, use_default=False)
736 7352d33b Thomas Thrainer
        utils.ForceDictType(ipolicy[key], constants.ISPECS_PARAMETER_TYPES)
737 7352d33b Thomas Thrainer
      else:
738 7352d33b Thomas Thrainer
        # FIXME: we assume all others are lists; this should be redone
739 7352d33b Thomas Thrainer
        # in a nicer way
740 7352d33b Thomas Thrainer
        ipolicy[key] = list(value)
741 7352d33b Thomas Thrainer
  try:
742 7352d33b Thomas Thrainer
    objects.InstancePolicy.CheckParameterSyntax(ipolicy, not group_policy)
743 7352d33b Thomas Thrainer
  except errors.ConfigurationError, err:
744 7352d33b Thomas Thrainer
    raise errors.OpPrereqError("Invalid instance policy: %s" % err,
745 7352d33b Thomas Thrainer
                               errors.ECODE_INVAL)
746 7352d33b Thomas Thrainer
  return ipolicy
747 7352d33b Thomas Thrainer
748 7352d33b Thomas Thrainer
749 5eacbcae Thomas Thrainer
def AnnotateDiskParams(instance, devs, cfg):
750 7352d33b Thomas Thrainer
  """Little helper wrapper to the rpc annotation method.
751 7352d33b Thomas Thrainer

752 7352d33b Thomas Thrainer
  @param instance: The instance object
753 7352d33b Thomas Thrainer
  @type devs: List of L{objects.Disk}
754 7352d33b Thomas Thrainer
  @param devs: The root devices (not any of its children!)
755 7352d33b Thomas Thrainer
  @param cfg: The config object
756 7352d33b Thomas Thrainer
  @returns The annotated disk copies
757 4e745e62 Santi Raffa
  @see L{rpc.node.AnnotateDiskParams}
758 7352d33b Thomas Thrainer

759 7352d33b Thomas Thrainer
  """
760 0c3d9c7c Thomas Thrainer
  return rpc.AnnotateDiskParams(devs, cfg.GetInstanceDiskParams(instance))
761 7352d33b Thomas Thrainer
762 7352d33b Thomas Thrainer
763 5eacbcae Thomas Thrainer
def SupportsOob(cfg, node):
764 7352d33b Thomas Thrainer
  """Tells if node supports OOB.
765 7352d33b Thomas Thrainer

766 7352d33b Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
767 7352d33b Thomas Thrainer
  @param cfg: The cluster configuration
768 7352d33b Thomas Thrainer
  @type node: L{objects.Node}
769 7352d33b Thomas Thrainer
  @param node: The node
770 7352d33b Thomas Thrainer
  @return: The OOB script if supported or an empty string otherwise
771 7352d33b Thomas Thrainer

772 7352d33b Thomas Thrainer
  """
773 7352d33b Thomas Thrainer
  return cfg.GetNdParams(node)[constants.ND_OOB_PROGRAM]
774 7352d33b Thomas Thrainer
775 7352d33b Thomas Thrainer
776 7352d33b Thomas Thrainer
def _UpdateAndVerifySubDict(base, updates, type_check):
777 7352d33b Thomas Thrainer
  """Updates and verifies a dict with sub dicts of the same type.
778 7352d33b Thomas Thrainer

779 7352d33b Thomas Thrainer
  @param base: The dict with the old data
780 7352d33b Thomas Thrainer
  @param updates: The dict with the new data
781 7352d33b Thomas Thrainer
  @param type_check: Dict suitable to ForceDictType to verify correct types
782 7352d33b Thomas Thrainer
  @returns: A new dict with updated and verified values
783 7352d33b Thomas Thrainer

784 7352d33b Thomas Thrainer
  """
785 7352d33b Thomas Thrainer
  def fn(old, value):
786 5eacbcae Thomas Thrainer
    new = GetUpdatedParams(old, value)
787 7352d33b Thomas Thrainer
    utils.ForceDictType(new, type_check)
788 7352d33b Thomas Thrainer
    return new
789 7352d33b Thomas Thrainer
790 7352d33b Thomas Thrainer
  ret = copy.deepcopy(base)
791 7352d33b Thomas Thrainer
  ret.update(dict((key, fn(base.get(key, {}), value))
792 7352d33b Thomas Thrainer
                  for key, value in updates.items()))
793 7352d33b Thomas Thrainer
  return ret
794 7352d33b Thomas Thrainer
795 7352d33b Thomas Thrainer
796 1c3231aa Thomas Thrainer
def _FilterVmNodes(lu, node_uuids):
797 7352d33b Thomas Thrainer
  """Filters out non-vm_capable nodes from a list.
798 7352d33b Thomas Thrainer

799 7352d33b Thomas Thrainer
  @type lu: L{LogicalUnit}
800 7352d33b Thomas Thrainer
  @param lu: the logical unit for which we check
801 1c3231aa Thomas Thrainer
  @type node_uuids: list
802 1c3231aa Thomas Thrainer
  @param node_uuids: the list of nodes on which we should check
803 7352d33b Thomas Thrainer
  @rtype: list
804 7352d33b Thomas Thrainer
  @return: the list of vm-capable nodes
805 7352d33b Thomas Thrainer

806 7352d33b Thomas Thrainer
  """
807 7352d33b Thomas Thrainer
  vm_nodes = frozenset(lu.cfg.GetNonVmCapableNodeList())
808 1c3231aa Thomas Thrainer
  return [uuid for uuid in node_uuids if uuid not in vm_nodes]
809 f380d53c Thomas Thrainer
810 f380d53c Thomas Thrainer
811 5eacbcae Thomas Thrainer
def GetDefaultIAllocator(cfg, ialloc):
812 f380d53c Thomas Thrainer
  """Decides on which iallocator to use.
813 f380d53c Thomas Thrainer

814 f380d53c Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
815 f380d53c Thomas Thrainer
  @param cfg: Cluster configuration object
816 f380d53c Thomas Thrainer
  @type ialloc: string or None
817 f380d53c Thomas Thrainer
  @param ialloc: Iallocator specified in opcode
818 f380d53c Thomas Thrainer
  @rtype: string
819 f380d53c Thomas Thrainer
  @return: Iallocator name
820 f380d53c Thomas Thrainer

821 f380d53c Thomas Thrainer
  """
822 f380d53c Thomas Thrainer
  if not ialloc:
823 f380d53c Thomas Thrainer
    # Use default iallocator
824 f380d53c Thomas Thrainer
    ialloc = cfg.GetDefaultIAllocator()
825 f380d53c Thomas Thrainer
826 f380d53c Thomas Thrainer
  if not ialloc:
827 f380d53c Thomas Thrainer
    raise errors.OpPrereqError("No iallocator was specified, neither in the"
828 f380d53c Thomas Thrainer
                               " opcode nor as a cluster-wide default",
829 f380d53c Thomas Thrainer
                               errors.ECODE_INVAL)
830 f380d53c Thomas Thrainer
831 f380d53c Thomas Thrainer
  return ialloc
832 f380d53c Thomas Thrainer
833 f380d53c Thomas Thrainer
834 1c3231aa Thomas Thrainer
def CheckInstancesNodeGroups(cfg, instances, owned_groups, owned_node_uuids,
835 5eacbcae Thomas Thrainer
                             cur_group_uuid):
836 f380d53c Thomas Thrainer
  """Checks if node groups for locked instances are still correct.
837 f380d53c Thomas Thrainer

838 f380d53c Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
839 f380d53c Thomas Thrainer
  @param cfg: Cluster configuration
840 f380d53c Thomas Thrainer
  @type instances: dict; string as key, L{objects.Instance} as value
841 da4a52a3 Thomas Thrainer
  @param instances: Dictionary, instance UUID as key, instance object as value
842 f380d53c Thomas Thrainer
  @type owned_groups: iterable of string
843 f380d53c Thomas Thrainer
  @param owned_groups: List of owned groups
844 1c3231aa Thomas Thrainer
  @type owned_node_uuids: iterable of string
845 1c3231aa Thomas Thrainer
  @param owned_node_uuids: List of owned nodes
846 f380d53c Thomas Thrainer
  @type cur_group_uuid: string or None
847 f380d53c Thomas Thrainer
  @param cur_group_uuid: Optional group UUID to check against instance's groups
848 f380d53c Thomas Thrainer

849 f380d53c Thomas Thrainer
  """
850 da4a52a3 Thomas Thrainer
  for (uuid, inst) in instances.items():
851 1c3231aa Thomas Thrainer
    assert owned_node_uuids.issuperset(inst.all_nodes), \
852 da4a52a3 Thomas Thrainer
      "Instance %s's nodes changed while we kept the lock" % inst.name
853 f380d53c Thomas Thrainer
854 da4a52a3 Thomas Thrainer
    inst_groups = CheckInstanceNodeGroups(cfg, uuid, owned_groups)
855 f380d53c Thomas Thrainer
856 f380d53c Thomas Thrainer
    assert cur_group_uuid is None or cur_group_uuid in inst_groups, \
857 da4a52a3 Thomas Thrainer
      "Instance %s has no node in group %s" % (inst.name, cur_group_uuid)
858 f380d53c Thomas Thrainer
859 f380d53c Thomas Thrainer
860 da4a52a3 Thomas Thrainer
def CheckInstanceNodeGroups(cfg, inst_uuid, owned_groups, primary_only=False):
861 f380d53c Thomas Thrainer
  """Checks if the owned node groups are still correct for an instance.
862 f380d53c Thomas Thrainer

863 f380d53c Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
864 f380d53c Thomas Thrainer
  @param cfg: The cluster configuration
865 da4a52a3 Thomas Thrainer
  @type inst_uuid: string
866 da4a52a3 Thomas Thrainer
  @param inst_uuid: Instance UUID
867 f380d53c Thomas Thrainer
  @type owned_groups: set or frozenset
868 f380d53c Thomas Thrainer
  @param owned_groups: List of currently owned node groups
869 f380d53c Thomas Thrainer
  @type primary_only: boolean
870 f380d53c Thomas Thrainer
  @param primary_only: Whether to check node groups for only the primary node
871 f380d53c Thomas Thrainer

872 f380d53c Thomas Thrainer
  """
873 da4a52a3 Thomas Thrainer
  inst_groups = cfg.GetInstanceNodeGroups(inst_uuid, primary_only)
874 f380d53c Thomas Thrainer
875 f380d53c Thomas Thrainer
  if not owned_groups.issuperset(inst_groups):
876 f380d53c Thomas Thrainer
    raise errors.OpPrereqError("Instance %s's node groups changed since"
877 f380d53c Thomas Thrainer
                               " locks were acquired, current groups are"
878 f380d53c Thomas Thrainer
                               " are '%s', owning groups '%s'; retry the"
879 f380d53c Thomas Thrainer
                               " operation" %
880 da4a52a3 Thomas Thrainer
                               (cfg.GetInstanceName(inst_uuid),
881 f380d53c Thomas Thrainer
                                utils.CommaJoin(inst_groups),
882 f380d53c Thomas Thrainer
                                utils.CommaJoin(owned_groups)),
883 f380d53c Thomas Thrainer
                               errors.ECODE_STATE)
884 f380d53c Thomas Thrainer
885 f380d53c Thomas Thrainer
  return inst_groups
886 f380d53c Thomas Thrainer
887 f380d53c Thomas Thrainer
888 5eacbcae Thomas Thrainer
def LoadNodeEvacResult(lu, alloc_result, early_release, use_nodes):
889 f380d53c Thomas Thrainer
  """Unpacks the result of change-group and node-evacuate iallocator requests.
890 f380d53c Thomas Thrainer

891 f380d53c Thomas Thrainer
  Iallocator modes L{constants.IALLOCATOR_MODE_NODE_EVAC} and
892 f380d53c Thomas Thrainer
  L{constants.IALLOCATOR_MODE_CHG_GROUP}.
893 f380d53c Thomas Thrainer

894 f380d53c Thomas Thrainer
  @type lu: L{LogicalUnit}
895 f380d53c Thomas Thrainer
  @param lu: Logical unit instance
896 f380d53c Thomas Thrainer
  @type alloc_result: tuple/list
897 f380d53c Thomas Thrainer
  @param alloc_result: Result from iallocator
898 f380d53c Thomas Thrainer
  @type early_release: bool
899 f380d53c Thomas Thrainer
  @param early_release: Whether to release locks early if possible
900 f380d53c Thomas Thrainer
  @type use_nodes: bool
901 f380d53c Thomas Thrainer
  @param use_nodes: Whether to display node names instead of groups
902 f380d53c Thomas Thrainer

903 f380d53c Thomas Thrainer
  """
904 f380d53c Thomas Thrainer
  (moved, failed, jobs) = alloc_result
905 f380d53c Thomas Thrainer
906 f380d53c Thomas Thrainer
  if failed:
907 f380d53c Thomas Thrainer
    failreason = utils.CommaJoin("%s (%s)" % (name, reason)
908 f380d53c Thomas Thrainer
                                 for (name, reason) in failed)
909 f380d53c Thomas Thrainer
    lu.LogWarning("Unable to evacuate instances %s", failreason)
910 f380d53c Thomas Thrainer
    raise errors.OpExecError("Unable to evacuate instances %s" % failreason)
911 f380d53c Thomas Thrainer
912 f380d53c Thomas Thrainer
  if moved:
913 f380d53c Thomas Thrainer
    lu.LogInfo("Instances to be moved: %s",
914 1c3231aa Thomas Thrainer
               utils.CommaJoin(
915 1c3231aa Thomas Thrainer
                 "%s (to %s)" %
916 1c3231aa Thomas Thrainer
                 (name, _NodeEvacDest(use_nodes, group, node_names))
917 1c3231aa Thomas Thrainer
                 for (name, group, node_names) in moved))
918 f380d53c Thomas Thrainer
919 f380d53c Thomas Thrainer
  return [map(compat.partial(_SetOpEarlyRelease, early_release),
920 f380d53c Thomas Thrainer
              map(opcodes.OpCode.LoadOpCode, ops))
921 f380d53c Thomas Thrainer
          for ops in jobs]
922 f380d53c Thomas Thrainer
923 f380d53c Thomas Thrainer
924 1c3231aa Thomas Thrainer
def _NodeEvacDest(use_nodes, group, node_names):
925 f380d53c Thomas Thrainer
  """Returns group or nodes depending on caller's choice.
926 f380d53c Thomas Thrainer

927 f380d53c Thomas Thrainer
  """
928 f380d53c Thomas Thrainer
  if use_nodes:
929 1c3231aa Thomas Thrainer
    return utils.CommaJoin(node_names)
930 f380d53c Thomas Thrainer
  else:
931 f380d53c Thomas Thrainer
    return group
932 f380d53c Thomas Thrainer
933 f380d53c Thomas Thrainer
934 f380d53c Thomas Thrainer
def _SetOpEarlyRelease(early_release, op):
935 f380d53c Thomas Thrainer
  """Sets C{early_release} flag on opcodes if available.
936 f380d53c Thomas Thrainer

937 f380d53c Thomas Thrainer
  """
938 f380d53c Thomas Thrainer
  try:
939 f380d53c Thomas Thrainer
    op.early_release = early_release
940 f380d53c Thomas Thrainer
  except AttributeError:
941 f380d53c Thomas Thrainer
    assert not isinstance(op, opcodes.OpInstanceReplaceDisks)
942 f380d53c Thomas Thrainer
943 f380d53c Thomas Thrainer
  return op
944 f380d53c Thomas Thrainer
945 f380d53c Thomas Thrainer
946 843094ad Thomas Thrainer
def MapInstanceLvsToNodes(instances):
947 f380d53c Thomas Thrainer
  """Creates a map from (node, volume) to instance name.
948 f380d53c Thomas Thrainer

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

953 f380d53c Thomas Thrainer
  """
954 843094ad Thomas Thrainer
  return dict(((node_uuid, vol), inst)
955 f380d53c Thomas Thrainer
              for inst in instances
956 1c3231aa Thomas Thrainer
              for (node_uuid, vols) in inst.MapLVsByNode().items()
957 f380d53c Thomas Thrainer
              for vol in vols)
958 31b836b8 Thomas Thrainer
959 31b836b8 Thomas Thrainer
960 5eacbcae Thomas Thrainer
def CheckParamsNotGlobal(params, glob_pars, kind, bad_levels, good_levels):
961 31b836b8 Thomas Thrainer
  """Make sure that none of the given paramters is global.
962 31b836b8 Thomas Thrainer

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

966 31b836b8 Thomas Thrainer
  @type params: dictionary
967 31b836b8 Thomas Thrainer
  @param params: Parameters to check
968 31b836b8 Thomas Thrainer
  @type glob_pars: dictionary
969 31b836b8 Thomas Thrainer
  @param glob_pars: Forbidden parameters
970 31b836b8 Thomas Thrainer
  @type kind: string
971 31b836b8 Thomas Thrainer
  @param kind: Kind of parameters (e.g. "node")
972 31b836b8 Thomas Thrainer
  @type bad_levels: string
973 31b836b8 Thomas Thrainer
  @param bad_levels: Level(s) at which the parameters are forbidden (e.g.
974 31b836b8 Thomas Thrainer
      "instance")
975 31b836b8 Thomas Thrainer
  @type good_levels: strings
976 31b836b8 Thomas Thrainer
  @param good_levels: Level(s) at which the parameters are allowed (e.g.
977 31b836b8 Thomas Thrainer
      "cluster or group")
978 31b836b8 Thomas Thrainer

979 31b836b8 Thomas Thrainer
  """
980 31b836b8 Thomas Thrainer
  used_globals = glob_pars.intersection(params)
981 31b836b8 Thomas Thrainer
  if used_globals:
982 31b836b8 Thomas Thrainer
    msg = ("The following %s parameters are global and cannot"
983 31b836b8 Thomas Thrainer
           " be customized at %s level, please modify them at"
984 31b836b8 Thomas Thrainer
           " %s level: %s" %
985 31b836b8 Thomas Thrainer
           (kind, bad_levels, good_levels, utils.CommaJoin(used_globals)))
986 31b836b8 Thomas Thrainer
    raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
987 31b836b8 Thomas Thrainer
988 31b836b8 Thomas Thrainer
989 5eacbcae Thomas Thrainer
def IsExclusiveStorageEnabledNode(cfg, node):
990 31b836b8 Thomas Thrainer
  """Whether exclusive_storage is in effect for the given node.
991 31b836b8 Thomas Thrainer

992 31b836b8 Thomas Thrainer
  @type cfg: L{config.ConfigWriter}
993 31b836b8 Thomas Thrainer
  @param cfg: The cluster configuration
994 31b836b8 Thomas Thrainer
  @type node: L{objects.Node}
995 31b836b8 Thomas Thrainer
  @param node: The node
996 31b836b8 Thomas Thrainer
  @rtype: bool
997 31b836b8 Thomas Thrainer
  @return: The effective value of exclusive_storage
998 31b836b8 Thomas Thrainer

999 31b836b8 Thomas Thrainer
  """
1000 31b836b8 Thomas Thrainer
  return cfg.GetNdParams(node)[constants.ND_EXCLUSIVE_STORAGE]
1001 31b836b8 Thomas Thrainer
1002 31b836b8 Thomas Thrainer
1003 5eacbcae Thomas Thrainer
def CheckInstanceState(lu, instance, req_states, msg=None):
1004 31b836b8 Thomas Thrainer
  """Ensure that an instance is in one of the required states.
1005 31b836b8 Thomas Thrainer

1006 31b836b8 Thomas Thrainer
  @param lu: the LU on behalf of which we make the check
1007 31b836b8 Thomas Thrainer
  @param instance: the instance to check
1008 31b836b8 Thomas Thrainer
  @param msg: if passed, should be a message to replace the default one
1009 31b836b8 Thomas Thrainer
  @raise errors.OpPrereqError: if the instance is not in the required state
1010 31b836b8 Thomas Thrainer

1011 31b836b8 Thomas Thrainer
  """
1012 31b836b8 Thomas Thrainer
  if msg is None:
1013 31b836b8 Thomas Thrainer
    msg = ("can't use instance from outside %s states" %
1014 31b836b8 Thomas Thrainer
           utils.CommaJoin(req_states))
1015 31b836b8 Thomas Thrainer
  if instance.admin_state not in req_states:
1016 31b836b8 Thomas Thrainer
    raise errors.OpPrereqError("Instance '%s' is marked to be %s, %s" %
1017 31b836b8 Thomas Thrainer
                               (instance.name, instance.admin_state, msg),
1018 31b836b8 Thomas Thrainer
                               errors.ECODE_STATE)
1019 31b836b8 Thomas Thrainer
1020 31b836b8 Thomas Thrainer
  if constants.ADMINST_UP not in req_states:
1021 1c3231aa Thomas Thrainer
    pnode_uuid = instance.primary_node
1022 1c3231aa Thomas Thrainer
    if not lu.cfg.GetNodeInfo(pnode_uuid).offline:
1023 8ac806e6 Helga Velroyen
      all_hvparams = lu.cfg.GetClusterInfo().hvparams
1024 1c3231aa Thomas Thrainer
      ins_l = lu.rpc.call_instance_list(
1025 1c3231aa Thomas Thrainer
                [pnode_uuid], [instance.hypervisor], all_hvparams)[pnode_uuid]
1026 1c3231aa Thomas Thrainer
      ins_l.Raise("Can't contact node %s for instance information" %
1027 1c3231aa Thomas Thrainer
                  lu.cfg.GetNodeName(pnode_uuid),
1028 31b836b8 Thomas Thrainer
                  prereq=True, ecode=errors.ECODE_ENVIRON)
1029 31b836b8 Thomas Thrainer
      if instance.name in ins_l.payload:
1030 31b836b8 Thomas Thrainer
        raise errors.OpPrereqError("Instance %s is running, %s" %
1031 31b836b8 Thomas Thrainer
                                   (instance.name, msg), errors.ECODE_STATE)
1032 31b836b8 Thomas Thrainer
    else:
1033 31b836b8 Thomas Thrainer
      lu.LogWarning("Primary node offline, ignoring check that instance"
1034 31b836b8 Thomas Thrainer
                     " is down")
1035 31b836b8 Thomas Thrainer
1036 31b836b8 Thomas Thrainer
1037 5eacbcae Thomas Thrainer
def CheckIAllocatorOrNode(lu, iallocator_slot, node_slot):
1038 31b836b8 Thomas Thrainer
  """Check the sanity of iallocator and node arguments and use the
1039 31b836b8 Thomas Thrainer
  cluster-wide iallocator if appropriate.
1040 31b836b8 Thomas Thrainer

1041 31b836b8 Thomas Thrainer
  Check that at most one of (iallocator, node) is specified. If none is
1042 31b836b8 Thomas Thrainer
  specified, or the iallocator is L{constants.DEFAULT_IALLOCATOR_SHORTCUT},
1043 31b836b8 Thomas Thrainer
  then the LU's opcode's iallocator slot is filled with the cluster-wide
1044 31b836b8 Thomas Thrainer
  default iallocator.
1045 31b836b8 Thomas Thrainer

1046 31b836b8 Thomas Thrainer
  @type iallocator_slot: string
1047 31b836b8 Thomas Thrainer
  @param iallocator_slot: the name of the opcode iallocator slot
1048 31b836b8 Thomas Thrainer
  @type node_slot: string
1049 31b836b8 Thomas Thrainer
  @param node_slot: the name of the opcode target node slot
1050 31b836b8 Thomas Thrainer

1051 31b836b8 Thomas Thrainer
  """
1052 31b836b8 Thomas Thrainer
  node = getattr(lu.op, node_slot, None)
1053 31b836b8 Thomas Thrainer
  ialloc = getattr(lu.op, iallocator_slot, None)
1054 31b836b8 Thomas Thrainer
  if node == []:
1055 31b836b8 Thomas Thrainer
    node = None
1056 31b836b8 Thomas Thrainer
1057 31b836b8 Thomas Thrainer
  if node is not None and ialloc is not None:
1058 31b836b8 Thomas Thrainer
    raise errors.OpPrereqError("Do not specify both, iallocator and node",
1059 31b836b8 Thomas Thrainer
                               errors.ECODE_INVAL)
1060 31b836b8 Thomas Thrainer
  elif ((node is None and ialloc is None) or
1061 31b836b8 Thomas Thrainer
        ialloc == constants.DEFAULT_IALLOCATOR_SHORTCUT):
1062 31b836b8 Thomas Thrainer
    default_iallocator = lu.cfg.GetDefaultIAllocator()
1063 31b836b8 Thomas Thrainer
    if default_iallocator:
1064 31b836b8 Thomas Thrainer
      setattr(lu.op, iallocator_slot, default_iallocator)
1065 31b836b8 Thomas Thrainer
    else:
1066 31b836b8 Thomas Thrainer
      raise errors.OpPrereqError("No iallocator or node given and no"
1067 31b836b8 Thomas Thrainer
                                 " cluster-wide default iallocator found;"
1068 31b836b8 Thomas Thrainer
                                 " please specify either an iallocator or a"
1069 31b836b8 Thomas Thrainer
                                 " node, or set a cluster-wide default"
1070 31b836b8 Thomas Thrainer
                                 " iallocator", errors.ECODE_INVAL)
1071 31b836b8 Thomas Thrainer
1072 31b836b8 Thomas Thrainer
1073 1c3231aa Thomas Thrainer
def FindFaultyInstanceDisks(cfg, rpc_runner, instance, node_uuid, prereq):
1074 31b836b8 Thomas Thrainer
  faulty = []
1075 31b836b8 Thomas Thrainer
1076 1c3231aa Thomas Thrainer
  result = rpc_runner.call_blockdev_getmirrorstatus(
1077 1c3231aa Thomas Thrainer
             node_uuid, (instance.disks, instance))
1078 1c3231aa Thomas Thrainer
  result.Raise("Failed to get disk status from node %s" %
1079 1c3231aa Thomas Thrainer
               cfg.GetNodeName(node_uuid),
1080 31b836b8 Thomas Thrainer
               prereq=prereq, ecode=errors.ECODE_ENVIRON)
1081 31b836b8 Thomas Thrainer
1082 31b836b8 Thomas Thrainer
  for idx, bdev_status in enumerate(result.payload):
1083 31b836b8 Thomas Thrainer
    if bdev_status and bdev_status.ldisk_status == constants.LDS_FAULTY:
1084 31b836b8 Thomas Thrainer
      faulty.append(idx)
1085 31b836b8 Thomas Thrainer
1086 31b836b8 Thomas Thrainer
  return faulty
1087 22b7f6f8 Thomas Thrainer
1088 22b7f6f8 Thomas Thrainer
1089 1c3231aa Thomas Thrainer
def CheckNodeOnline(lu, node_uuid, msg=None):
1090 22b7f6f8 Thomas Thrainer
  """Ensure that a given node is online.
1091 22b7f6f8 Thomas Thrainer

1092 22b7f6f8 Thomas Thrainer
  @param lu: the LU on behalf of which we make the check
1093 1c3231aa Thomas Thrainer
  @param node_uuid: the node to check
1094 22b7f6f8 Thomas Thrainer
  @param msg: if passed, should be a message to replace the default one
1095 22b7f6f8 Thomas Thrainer
  @raise errors.OpPrereqError: if the node is offline
1096 22b7f6f8 Thomas Thrainer

1097 22b7f6f8 Thomas Thrainer
  """
1098 22b7f6f8 Thomas Thrainer
  if msg is None:
1099 22b7f6f8 Thomas Thrainer
    msg = "Can't use offline node"
1100 1c3231aa Thomas Thrainer
  if lu.cfg.GetNodeInfo(node_uuid).offline:
1101 1c3231aa Thomas Thrainer
    raise errors.OpPrereqError("%s: %s" % (msg, lu.cfg.GetNodeName(node_uuid)),
1102 1c3231aa Thomas Thrainer
                               errors.ECODE_STATE)
1103 1f7c8208 Helga Velroyen
1104 1f7c8208 Helga Velroyen
1105 1f7c8208 Helga Velroyen
def CheckDiskTemplateEnabled(cluster, disk_template):
1106 1f7c8208 Helga Velroyen
  """Helper function to check if a disk template is enabled.
1107 1f7c8208 Helga Velroyen

1108 1f7c8208 Helga Velroyen
  @type cluster: C{objects.Cluster}
1109 1f7c8208 Helga Velroyen
  @param cluster: the cluster's configuration
1110 1f7c8208 Helga Velroyen
  @type disk_template: str
1111 1f7c8208 Helga Velroyen
  @param disk_template: the disk template to be checked
1112 1f7c8208 Helga Velroyen

1113 1f7c8208 Helga Velroyen
  """
1114 1f7c8208 Helga Velroyen
  assert disk_template is not None
1115 bfbffd55 Helga Velroyen
  if disk_template not in constants.DISK_TEMPLATES:
1116 bfbffd55 Helga Velroyen
    raise errors.OpPrereqError("'%s' is not a valid disk template."
1117 bfbffd55 Helga Velroyen
                               " Valid disk templates are: %s" %
1118 bfbffd55 Helga Velroyen
                               (disk_template,
1119 bfbffd55 Helga Velroyen
                                ",".join(constants.DISK_TEMPLATES)))
1120 1f7c8208 Helga Velroyen
  if not disk_template in cluster.enabled_disk_templates:
1121 1f7c8208 Helga Velroyen
    raise errors.OpPrereqError("Disk template '%s' is not enabled in cluster."
1122 1f7c8208 Helga Velroyen
                               " Enabled disk templates are: %s" %
1123 1f7c8208 Helga Velroyen
                               (disk_template,
1124 1f7c8208 Helga Velroyen
                                ",".join(cluster.enabled_disk_templates)))
1125 1f7c8208 Helga Velroyen
1126 9d276e93 Helga Velroyen
1127 9d276e93 Helga Velroyen
def CheckStorageTypeEnabled(cluster, storage_type):
1128 9d276e93 Helga Velroyen
  """Helper function to check if a storage type is enabled.
1129 9d276e93 Helga Velroyen

1130 9d276e93 Helga Velroyen
  @type cluster: C{objects.Cluster}
1131 9d276e93 Helga Velroyen
  @param cluster: the cluster's configuration
1132 9d276e93 Helga Velroyen
  @type storage_type: str
1133 9d276e93 Helga Velroyen
  @param storage_type: the storage type to be checked
1134 9d276e93 Helga Velroyen

1135 9d276e93 Helga Velroyen
  """
1136 9d276e93 Helga Velroyen
  assert storage_type is not None
1137 d8e55568 Helga Velroyen
  assert storage_type in constants.STORAGE_TYPES
1138 9d276e93 Helga Velroyen
  # special case for lvm-pv, because it cannot be enabled
1139 9d276e93 Helga Velroyen
  # via disk templates
1140 9d276e93 Helga Velroyen
  if storage_type == constants.ST_LVM_PV:
1141 9d276e93 Helga Velroyen
    CheckStorageTypeEnabled(cluster, constants.ST_LVM_VG)
1142 9d276e93 Helga Velroyen
  else:
1143 9d276e93 Helga Velroyen
    possible_disk_templates = \
1144 5a904197 Santi Raffa
        utils.storage.GetDiskTemplatesOfStorageTypes(storage_type)
1145 9d276e93 Helga Velroyen
    for disk_template in possible_disk_templates:
1146 9d276e93 Helga Velroyen
      if disk_template in cluster.enabled_disk_templates:
1147 9d276e93 Helga Velroyen
        return
1148 9d276e93 Helga Velroyen
    raise errors.OpPrereqError("No disk template of storage type '%s' is"
1149 9d276e93 Helga Velroyen
                               " enabled in this cluster. Enabled disk"
1150 9d276e93 Helga Velroyen
                               " templates are: %s" % (storage_type,
1151 9d276e93 Helga Velroyen
                               ",".join(cluster.enabled_disk_templates)))
1152 4e771a95 Helga Velroyen
1153 4e771a95 Helga Velroyen
1154 4e771a95 Helga Velroyen
def CheckIpolicyVsDiskTemplates(ipolicy, enabled_disk_templates):
1155 4e771a95 Helga Velroyen
  """Checks ipolicy disk templates against enabled disk tempaltes.
1156 4e771a95 Helga Velroyen

1157 4e771a95 Helga Velroyen
  @type ipolicy: dict
1158 4e771a95 Helga Velroyen
  @param ipolicy: the new ipolicy
1159 4e771a95 Helga Velroyen
  @type enabled_disk_templates: list of string
1160 4e771a95 Helga Velroyen
  @param enabled_disk_templates: list of enabled disk templates on the
1161 4e771a95 Helga Velroyen
    cluster
1162 4e771a95 Helga Velroyen
  @raises errors.OpPrereqError: if there is at least one allowed disk
1163 4e771a95 Helga Velroyen
    template that is not also enabled.
1164 4e771a95 Helga Velroyen

1165 4e771a95 Helga Velroyen
  """
1166 4e771a95 Helga Velroyen
  assert constants.IPOLICY_DTS in ipolicy
1167 4e771a95 Helga Velroyen
  allowed_disk_templates = ipolicy[constants.IPOLICY_DTS]
1168 4e771a95 Helga Velroyen
  not_enabled = set(allowed_disk_templates) - set(enabled_disk_templates)
1169 4e771a95 Helga Velroyen
  if not_enabled:
1170 4e771a95 Helga Velroyen
    raise errors.OpPrereqError("The following disk template are allowed"
1171 4e771a95 Helga Velroyen
                               " by the ipolicy, but not enabled on the"
1172 4e771a95 Helga Velroyen
                               " cluster: %s" % utils.CommaJoin(not_enabled))
1173 294254b1 Raffa Santi
1174 294254b1 Raffa Santi
1175 294254b1 Raffa Santi
def CheckDiskAccessModeValidity(parameters):
1176 294254b1 Raffa Santi
  """Checks if the access parameter is legal.
1177 294254b1 Raffa Santi

1178 294254b1 Raffa Santi
  @see: L{CheckDiskAccessModeConsistency} for cluster consistency checks.
1179 294254b1 Raffa Santi
  @raise errors.OpPrereqError: if the check fails.
1180 294254b1 Raffa Santi

1181 294254b1 Raffa Santi
  """
1182 6488e5bc Santi Raffa
  for disk_template in parameters:
1183 6488e5bc Santi Raffa
    access = parameters[disk_template].get(constants.LDP_ACCESS,
1184 6488e5bc Santi Raffa
                                           constants.DISK_KERNELSPACE)
1185 294254b1 Raffa Santi
    if access not in constants.DISK_VALID_ACCESS_MODES:
1186 294254b1 Raffa Santi
      valid_vals_str = utils.CommaJoin(constants.DISK_VALID_ACCESS_MODES)
1187 294254b1 Raffa Santi
      raise errors.OpPrereqError("Invalid value of '{d}:{a}': '{v}' (expected"
1188 6488e5bc Santi Raffa
                                 " one of {o})".format(d=disk_template,
1189 6488e5bc Santi Raffa
                                                       a=constants.LDP_ACCESS,
1190 294254b1 Raffa Santi
                                                       v=access,
1191 294254b1 Raffa Santi
                                                       o=valid_vals_str))
1192 294254b1 Raffa Santi
1193 294254b1 Raffa Santi
1194 294254b1 Raffa Santi
def CheckDiskAccessModeConsistency(parameters, cfg, group=None):
1195 294254b1 Raffa Santi
  """Checks if the access param is consistent with the cluster configuration.
1196 294254b1 Raffa Santi

1197 294254b1 Raffa Santi
  @note: requires a configuration lock to run.
1198 294254b1 Raffa Santi
  @param parameters: the parameters to validate
1199 294254b1 Raffa Santi
  @param cfg: the cfg object of the cluster
1200 294254b1 Raffa Santi
  @param group: if set, only check for consistency within this group.
1201 294254b1 Raffa Santi
  @raise errors.OpPrereqError: if the LU attempts to change the access parameter
1202 294254b1 Raffa Santi
                               to an invalid value, such as "pink bunny".
1203 294254b1 Raffa Santi
  @raise errors.OpPrereqError: if the LU attempts to change the access parameter
1204 294254b1 Raffa Santi
                               to an inconsistent value, such as asking for RBD
1205 294254b1 Raffa Santi
                               userspace access to the chroot hypervisor.
1206 294254b1 Raffa Santi

1207 294254b1 Raffa Santi
  """
1208 294254b1 Raffa Santi
  CheckDiskAccessModeValidity(parameters)
1209 294254b1 Raffa Santi
1210 6488e5bc Santi Raffa
  for disk_template in parameters:
1211 6488e5bc Santi Raffa
    access = parameters[disk_template].get(constants.LDP_ACCESS,
1212 6488e5bc Santi Raffa
                                           constants.DISK_KERNELSPACE)
1213 6488e5bc Santi Raffa
1214 5a904197 Santi Raffa
    if disk_template not in constants.DTS_HAVE_ACCESS:
1215 6488e5bc Santi Raffa
      continue
1216 294254b1 Raffa Santi
1217 294254b1 Raffa Santi
    #Check the combination of instance hypervisor, disk template and access
1218 294254b1 Raffa Santi
    #protocol is sane.
1219 294254b1 Raffa Santi
    inst_uuids = cfg.GetNodeGroupInstances(group) if group else \
1220 294254b1 Raffa Santi
                 cfg.GetInstanceList()
1221 294254b1 Raffa Santi
1222 294254b1 Raffa Santi
    for entry in inst_uuids:
1223 294254b1 Raffa Santi
      inst = cfg.GetInstanceInfo(entry)
1224 294254b1 Raffa Santi
      hv = inst.hypervisor
1225 294254b1 Raffa Santi
      dt = inst.disk_template
1226 294254b1 Raffa Santi
1227 294254b1 Raffa Santi
      if not IsValidDiskAccessModeCombination(hv, dt, access):
1228 294254b1 Raffa Santi
        raise errors.OpPrereqError("Instance {i}: cannot use '{a}' access"
1229 294254b1 Raffa Santi
                                   " setting with {h} hypervisor and {d} disk"
1230 294254b1 Raffa Santi
                                   " type.".format(i=inst.name,
1231 294254b1 Raffa Santi
                                                   a=access,
1232 294254b1 Raffa Santi
                                                   h=hv,
1233 294254b1 Raffa Santi
                                                   d=dt))
1234 294254b1 Raffa Santi
1235 294254b1 Raffa Santi
1236 294254b1 Raffa Santi
def IsValidDiskAccessModeCombination(hv, disk_template, mode):
1237 294254b1 Raffa Santi
  """Checks if an hypervisor can read a disk template with given mode.
1238 294254b1 Raffa Santi

1239 294254b1 Raffa Santi
  @param hv: the hypervisor that will access the data
1240 294254b1 Raffa Santi
  @param disk_template: the disk template the data is stored as
1241 294254b1 Raffa Santi
  @param mode: how the hypervisor should access the data
1242 294254b1 Raffa Santi
  @return: True if the hypervisor can read a given read disk_template
1243 294254b1 Raffa Santi
           in the specified mode.
1244 294254b1 Raffa Santi

1245 294254b1 Raffa Santi
  """
1246 294254b1 Raffa Santi
  if mode == constants.DISK_KERNELSPACE:
1247 294254b1 Raffa Santi
    return True
1248 294254b1 Raffa Santi
1249 294254b1 Raffa Santi
  if (hv == constants.HT_KVM and
1250 6488e5bc Santi Raffa
      disk_template in (constants.DT_RBD, constants.DT_GLUSTER) and
1251 294254b1 Raffa Santi
      mode == constants.DISK_USERSPACE):
1252 294254b1 Raffa Santi
    return True
1253 294254b1 Raffa Santi
1254 294254b1 Raffa Santi
  # Everything else:
1255 294254b1 Raffa Santi
  return False
1256 5b6f9e35 Helga Velroyen
1257 5b6f9e35 Helga Velroyen
1258 5b6f9e35 Helga Velroyen
def AddNodeCertToCandidateCerts(lu, node_uuid, cluster):
1259 5b6f9e35 Helga Velroyen
  """Add the node's client SSL certificate digest to the candidate certs.
1260 5b6f9e35 Helga Velroyen

1261 5b6f9e35 Helga Velroyen
  @type node_uuid: string
1262 5b6f9e35 Helga Velroyen
  @param node_uuid: the node's UUID
1263 5b6f9e35 Helga Velroyen
  @type cluster: C{object.Cluster}
1264 5b6f9e35 Helga Velroyen
  @param cluster: the cluster's configuration
1265 5b6f9e35 Helga Velroyen

1266 5b6f9e35 Helga Velroyen
  """
1267 5b6f9e35 Helga Velroyen
  result = lu.rpc.call_node_crypto_tokens(
1268 d722af8b Helga Velroyen
             node_uuid,
1269 d722af8b Helga Velroyen
             [(constants.CRYPTO_TYPE_SSL_DIGEST, constants.CRYPTO_ACTION_GET,
1270 d722af8b Helga Velroyen
               None)])
1271 5b6f9e35 Helga Velroyen
  result.Raise("Could not retrieve the node's (uuid %s) SSL digest."
1272 5b6f9e35 Helga Velroyen
               % node_uuid)
1273 5b6f9e35 Helga Velroyen
  ((crypto_type, digest), ) = result.payload
1274 5b6f9e35 Helga Velroyen
  assert crypto_type == constants.CRYPTO_TYPE_SSL_DIGEST
1275 5b6f9e35 Helga Velroyen
1276 5b6f9e35 Helga Velroyen
  utils.AddNodeToCandidateCerts(node_uuid, digest, cluster.candidate_certs)
1277 28756f80 Helga Velroyen
1278 28756f80 Helga Velroyen
1279 28756f80 Helga Velroyen
def RemoveNodeCertFromCandidateCerts(node_uuid, cluster):
1280 28756f80 Helga Velroyen
  """Removes the node's certificate from the candidate certificates list.
1281 28756f80 Helga Velroyen

1282 28756f80 Helga Velroyen
  @type node_uuid: string
1283 28756f80 Helga Velroyen
  @param node_uuid: the node's UUID
1284 28756f80 Helga Velroyen
  @type cluster: C{objects.Cluster}
1285 28756f80 Helga Velroyen
  @param cluster: the cluster's configuration
1286 28756f80 Helga Velroyen

1287 28756f80 Helga Velroyen
  """
1288 28756f80 Helga Velroyen
  utils.RemoveNodeFromCandidateCerts(node_uuid, cluster.candidate_certs)
1289 28756f80 Helga Velroyen
1290 28756f80 Helga Velroyen
1291 b3cc1646 Helga Velroyen
def CreateNewClientCert(lu, node_uuid, filename=None):
1292 28756f80 Helga Velroyen
  """Creates a new client SSL certificate for the node.
1293 28756f80 Helga Velroyen

1294 28756f80 Helga Velroyen
  @type node_uuid: string
1295 28756f80 Helga Velroyen
  @param node_uuid: the node's UUID
1296 28756f80 Helga Velroyen
  @type filename: string
1297 28756f80 Helga Velroyen
  @param filename: the certificate's filename
1298 28756f80 Helga Velroyen
  @rtype: string
1299 28756f80 Helga Velroyen
  @return: the digest of the newly created certificate
1300 28756f80 Helga Velroyen

1301 28756f80 Helga Velroyen
  """
1302 28756f80 Helga Velroyen
  options = {}
1303 28756f80 Helga Velroyen
  if filename:
1304 28756f80 Helga Velroyen
    options[constants.CRYPTO_OPTION_CERT_FILE] = filename
1305 ab4b1cf2 Helga Velroyen
  options[constants.CRYPTO_OPTION_SERIAL_NO] = utils.UuidToInt(node_uuid)
1306 b3cc1646 Helga Velroyen
  result = lu.rpc.call_node_crypto_tokens(
1307 28756f80 Helga Velroyen
             node_uuid,
1308 28756f80 Helga Velroyen
             [(constants.CRYPTO_TYPE_SSL_DIGEST,
1309 28756f80 Helga Velroyen
               constants.CRYPTO_ACTION_CREATE,
1310 28756f80 Helga Velroyen
               options)])
1311 28756f80 Helga Velroyen
  result.Raise("Could not create the node's (uuid %s) SSL client"
1312 28756f80 Helga Velroyen
               " certificate." % node_uuid)
1313 28756f80 Helga Velroyen
  ((crypto_type, new_digest), ) = result.payload
1314 28756f80 Helga Velroyen
  assert crypto_type == constants.CRYPTO_TYPE_SSL_DIGEST
1315 28756f80 Helga Velroyen
  return new_digest
1316 2ff6426b Jose A. Lopes
1317 2ff6426b Jose A. Lopes
1318 4b75f8a4 Jose A. Lopes
def AddInstanceCommunicationNetworkOp(network):
1319 4b75f8a4 Jose A. Lopes
  """Create an OpCode that adds the instance communication network.
1320 2ff6426b Jose A. Lopes

1321 2ff6426b Jose A. Lopes
  This OpCode contains the configuration necessary for the instance
1322 2ff6426b Jose A. Lopes
  communication network.
1323 2ff6426b Jose A. Lopes

1324 2ff6426b Jose A. Lopes
  @type network: string
1325 2ff6426b Jose A. Lopes
  @param network: name or UUID of the instance communication network
1326 2ff6426b Jose A. Lopes

1327 2ff6426b Jose A. Lopes
  @rtype: L{ganeti.opcodes.OpCode}
1328 2ff6426b Jose A. Lopes
  @return: OpCode that creates the instance communication network
1329 2ff6426b Jose A. Lopes

1330 2ff6426b Jose A. Lopes
  """
1331 2ff6426b Jose A. Lopes
  return opcodes.OpNetworkAdd(
1332 2ff6426b Jose A. Lopes
    network_name=network,
1333 2ff6426b Jose A. Lopes
    gateway=None,
1334 2ff6426b Jose A. Lopes
    network=constants.INSTANCE_COMMUNICATION_NETWORK4,
1335 2ff6426b Jose A. Lopes
    gateway6=None,
1336 2ff6426b Jose A. Lopes
    network6=constants.INSTANCE_COMMUNICATION_NETWORK6,
1337 2ff6426b Jose A. Lopes
    mac_prefix=constants.INSTANCE_COMMUNICATION_MAC_PREFIX,
1338 2ff6426b Jose A. Lopes
    add_reserved_ips=None,
1339 2ff6426b Jose A. Lopes
    conflicts_check=True,
1340 2ff6426b Jose A. Lopes
    tags=[])
1341 2ff6426b Jose A. Lopes
1342 2ff6426b Jose A. Lopes
1343 4b75f8a4 Jose A. Lopes
def ConnectInstanceCommunicationNetworkOp(group_uuid, network):
1344 4b75f8a4 Jose A. Lopes
  """Create an OpCode that connects a group to the instance
1345 4b75f8a4 Jose A. Lopes
  communication network.
1346 2ff6426b Jose A. Lopes

1347 2ff6426b Jose A. Lopes
  This OpCode contains the configuration necessary for the instance
1348 2ff6426b Jose A. Lopes
  communication network.
1349 2ff6426b Jose A. Lopes

1350 2ff6426b Jose A. Lopes
  @type group_uuid: string
1351 2ff6426b Jose A. Lopes
  @param group_uuid: UUID of the group to connect
1352 2ff6426b Jose A. Lopes

1353 2ff6426b Jose A. Lopes
  @type network: string
1354 2ff6426b Jose A. Lopes
  @param network: name or UUID of the network to connect to, i.e., the
1355 2ff6426b Jose A. Lopes
                  instance communication network
1356 2ff6426b Jose A. Lopes

1357 2ff6426b Jose A. Lopes
  @rtype: L{ganeti.opcodes.OpCode}
1358 2ff6426b Jose A. Lopes
  @return: OpCode that connects the group to the instance
1359 2ff6426b Jose A. Lopes
           communication network
1360 2ff6426b Jose A. Lopes

1361 2ff6426b Jose A. Lopes
  """
1362 2ff6426b Jose A. Lopes
  return opcodes.OpNetworkConnect(
1363 2ff6426b Jose A. Lopes
    group_name=group_uuid,
1364 2ff6426b Jose A. Lopes
    network_name=network,
1365 e04860cc Jose A. Lopes
    network_mode=constants.INSTANCE_COMMUNICATION_NETWORK_MODE,
1366 2ff6426b Jose A. Lopes
    network_link=constants.INSTANCE_COMMUNICATION_NETWORK_LINK,
1367 2ff6426b Jose A. Lopes
    conflicts_check=True)