4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Utility function mainly, but not only used by instance LU's."""
27 from ganeti import constants
28 from ganeti import errors
29 from ganeti import locking
30 from ganeti import network
31 from ganeti import objects
32 from ganeti import pathutils
33 from ganeti import utils
34 from ganeti.cmdlib.common import AnnotateDiskParams, \
35 ComputeIPolicyInstanceViolation, CheckDiskTemplateEnabled
38 def BuildInstanceHookEnv(name, primary_node_name, secondary_node_names, os_type,
39 status, minmem, maxmem, vcpus, nics, disk_template,
40 disks, bep, hvp, hypervisor_name, tags):
41 """Builds instance related env variables for hooks
43 This builds the hook environment from individual variables.
46 @param name: the name of the instance
47 @type primary_node_name: string
48 @param primary_node_name: the name of the instance's primary node
49 @type secondary_node_names: list
50 @param secondary_node_names: list of secondary nodes as strings
52 @param os_type: the name of the instance's OS
54 @param status: the desired status of the instance
56 @param minmem: the minimum memory size of the instance
58 @param maxmem: the maximum memory size of the instance
60 @param vcpus: the count of VCPUs the instance has
62 @param nics: list of tuples (name, uuid, ip, mac, mode, link, net, netinfo)
63 representing the NICs the instance has
64 @type disk_template: string
65 @param disk_template: the disk template of the instance
67 @param disks: list of tuples (name, uuid, size, mode)
69 @param bep: the backend parameters for the instance
71 @param hvp: the hypervisor parameters for the instance
72 @type hypervisor_name: string
73 @param hypervisor_name: the hypervisor for the instance
75 @param tags: list of instance tags as strings
77 @return: the hook environment for this instance
82 "INSTANCE_NAME": name,
83 "INSTANCE_PRIMARY": primary_node_name,
84 "INSTANCE_SECONDARIES": " ".join(secondary_node_names),
85 "INSTANCE_OS_TYPE": os_type,
86 "INSTANCE_STATUS": status,
87 "INSTANCE_MINMEM": minmem,
88 "INSTANCE_MAXMEM": maxmem,
89 # TODO(2.9) remove deprecated "memory" value
90 "INSTANCE_MEMORY": maxmem,
91 "INSTANCE_VCPUS": vcpus,
92 "INSTANCE_DISK_TEMPLATE": disk_template,
93 "INSTANCE_HYPERVISOR": hypervisor_name,
97 for idx, (name, uuid, ip, mac, mode, link, net, netinfo) in enumerate(nics):
101 env["INSTANCE_NIC%d_NAME" % idx] = name
102 env["INSTANCE_NIC%d_UUID" % idx] = uuid
103 env["INSTANCE_NIC%d_IP" % idx] = ip
104 env["INSTANCE_NIC%d_MAC" % idx] = mac
105 env["INSTANCE_NIC%d_MODE" % idx] = mode
106 env["INSTANCE_NIC%d_LINK" % idx] = link
108 nobj = objects.Network.FromDict(netinfo)
109 env.update(nobj.HooksDict("INSTANCE_NIC%d_" % idx))
111 # FIXME: broken network reference: the instance NIC specifies a
112 # network, but the relevant network entry was not in the config. This
113 # should be made impossible.
114 env["INSTANCE_NIC%d_NETWORK_NAME" % idx] = net
115 if mode == constants.NIC_MODE_BRIDGED:
116 env["INSTANCE_NIC%d_BRIDGE" % idx] = link
120 env["INSTANCE_NIC_COUNT"] = nic_count
123 disk_count = len(disks)
124 for idx, (name, uuid, size, mode) in enumerate(disks):
126 env["INSTANCE_DISK%d_NAME" % idx] = name
127 env["INSTANCE_DISK%d_UUID" % idx] = uuid
128 env["INSTANCE_DISK%d_SIZE" % idx] = size
129 env["INSTANCE_DISK%d_MODE" % idx] = mode
133 env["INSTANCE_DISK_COUNT"] = disk_count
138 env["INSTANCE_TAGS"] = " ".join(tags)
140 for source, kind in [(bep, "BE"), (hvp, "HV")]:
141 for key, value in source.items():
142 env["INSTANCE_%s_%s" % (kind, key)] = value
147 def BuildInstanceHookEnvByObject(lu, instance, override=None):
148 """Builds instance related env variables for hooks from an object.
150 @type lu: L{LogicalUnit}
151 @param lu: the logical unit on whose behalf we execute
152 @type instance: L{objects.Instance}
153 @param instance: the instance for which we should build the
156 @param override: dictionary with key/values that will override
159 @return: the hook environment dictionary
162 cluster = lu.cfg.GetClusterInfo()
163 bep = cluster.FillBE(instance)
164 hvp = cluster.FillHV(instance)
166 "name": instance.name,
167 "primary_node_name": lu.cfg.GetNodeName(instance.primary_node),
168 "secondary_node_names": lu.cfg.GetNodeNames(instance.secondary_nodes),
169 "os_type": instance.os,
170 "status": instance.admin_state,
171 "maxmem": bep[constants.BE_MAXMEM],
172 "minmem": bep[constants.BE_MINMEM],
173 "vcpus": bep[constants.BE_VCPUS],
174 "nics": NICListToTuple(lu, instance.nics),
175 "disk_template": instance.disk_template,
176 "disks": [(disk.name, disk.uuid, disk.size, disk.mode)
177 for disk in instance.disks],
180 "hypervisor_name": instance.hypervisor,
181 "tags": instance.tags,
184 args.update(override)
185 return BuildInstanceHookEnv(**args) # pylint: disable=W0142
188 def GetClusterDomainSecret():
189 """Reads the cluster domain secret.
192 return utils.ReadOneLineFile(pathutils.CLUSTER_DOMAIN_SECRET_FILE,
196 def CheckNodeNotDrained(lu, node_uuid):
197 """Ensure that a given node is not drained.
199 @param lu: the LU on behalf of which we make the check
200 @param node_uuid: the node to check
201 @raise errors.OpPrereqError: if the node is drained
204 if lu.cfg.GetNodeInfo(node_uuid).drained:
205 raise errors.OpPrereqError("Can't use drained node %s" % node_uuid,
209 def CheckNodeVmCapable(lu, node_uuid):
210 """Ensure that a given node is vm capable.
212 @param lu: the LU on behalf of which we make the check
213 @param node_uuid: the node to check
214 @raise errors.OpPrereqError: if the node is not vm capable
217 if not lu.cfg.GetNodeInfo(node_uuid).vm_capable:
218 raise errors.OpPrereqError("Can't use non-vm_capable node %s" % node_uuid,
222 def RemoveInstance(lu, feedback_fn, instance, ignore_failures):
223 """Utility function to remove an instance.
226 logging.info("Removing block devices for instance %s", instance.name)
228 if not RemoveDisks(lu, instance, ignore_failures=ignore_failures):
229 if not ignore_failures:
230 raise errors.OpExecError("Can't remove instance's disks")
231 feedback_fn("Warning: can't remove instance's disks")
233 logging.info("Removing instance %s out of cluster config", instance.name)
235 lu.cfg.RemoveInstance(instance.uuid)
237 assert not lu.remove_locks.get(locking.LEVEL_INSTANCE), \
238 "Instance lock removal conflict"
240 # Remove lock for the instance
241 lu.remove_locks[locking.LEVEL_INSTANCE] = instance.name
244 def RemoveDisks(lu, instance, target_node_uuid=None, ignore_failures=False):
245 """Remove all disks for an instance.
247 This abstracts away some work from `AddInstance()` and
248 `RemoveInstance()`. Note that in case some of the devices couldn't
249 be removed, the removal will continue with the other ones.
251 @type lu: L{LogicalUnit}
252 @param lu: the logical unit on whose behalf we execute
253 @type instance: L{objects.Instance}
254 @param instance: the instance whose disks we should remove
255 @type target_node_uuid: string
256 @param target_node_uuid: used to override the node on which to remove the
259 @return: the success of the removal
262 logging.info("Removing block devices for instance %s", instance.name)
265 ports_to_release = set()
266 anno_disks = AnnotateDiskParams(instance, instance.disks, lu.cfg)
267 for (idx, device) in enumerate(anno_disks):
269 edata = [(target_node_uuid, device)]
271 edata = device.ComputeNodeTree(instance.primary_node)
272 for node_uuid, disk in edata:
273 lu.cfg.SetDiskID(disk, node_uuid)
274 result = lu.rpc.call_blockdev_remove(node_uuid, disk)
276 lu.LogWarning("Could not remove disk %s on node %s,"
277 " continuing anyway: %s", idx,
278 lu.cfg.GetNodeName(node_uuid), result.fail_msg)
279 if not (result.offline and node_uuid != instance.primary_node):
282 # if this is a DRBD disk, return its port to the pool
283 if device.dev_type in constants.LDS_DRBD:
284 ports_to_release.add(device.logical_id[2])
286 if all_result or ignore_failures:
287 for port in ports_to_release:
288 lu.cfg.AddTcpUdpPort(port)
290 CheckDiskTemplateEnabled(lu.cfg.GetClusterInfo(), instance.disk_template)
292 if instance.disk_template in constants.DTS_FILEBASED:
293 file_storage_dir = os.path.dirname(instance.disks[0].logical_id[1])
295 tgt = target_node_uuid
297 tgt = instance.primary_node
298 result = lu.rpc.call_file_storage_dir_remove(tgt, file_storage_dir)
300 lu.LogWarning("Could not remove directory '%s' on node %s: %s",
301 file_storage_dir, lu.cfg.GetNodeName(tgt), result.fail_msg)
307 def NICToTuple(lu, nic):
308 """Build a tupple of nic information.
310 @type lu: L{LogicalUnit}
311 @param lu: the logical unit on whose behalf we execute
312 @type nic: L{objects.NIC}
313 @param nic: nic to convert to hooks tuple
316 cluster = lu.cfg.GetClusterInfo()
317 filled_params = cluster.SimpleFillNIC(nic.nicparams)
318 mode = filled_params[constants.NIC_MODE]
319 link = filled_params[constants.NIC_LINK]
322 nobj = lu.cfg.GetNetwork(nic.network)
323 netinfo = objects.Network.ToDict(nobj)
324 return (nic.name, nic.uuid, nic.ip, nic.mac, mode, link, nic.network, netinfo)
327 def NICListToTuple(lu, nics):
328 """Build a list of nic information tuples.
330 This list is suitable to be passed to _BuildInstanceHookEnv or as a return
331 value in LUInstanceQueryData.
333 @type lu: L{LogicalUnit}
334 @param lu: the logical unit on whose behalf we execute
335 @type nics: list of L{objects.NIC}
336 @param nics: list of nics to convert to hooks tuples
341 hooks_nics.append(NICToTuple(lu, nic))
345 def CopyLockList(names):
346 """Makes a copy of a list of lock names.
348 Handles L{locking.ALL_SET} correctly.
351 if names == locking.ALL_SET:
352 return locking.ALL_SET
357 def ReleaseLocks(lu, level, names=None, keep=None):
358 """Releases locks owned by an LU.
360 @type lu: L{LogicalUnit}
361 @param level: Lock level
362 @type names: list or None
363 @param names: Names of locks to release
364 @type keep: list or None
365 @param keep: Names of locks to retain
368 assert not (keep is not None and names is not None), \
369 "Only one of the 'names' and the 'keep' parameters can be given"
371 if names is not None:
372 should_release = names.__contains__
374 should_release = lambda name: name not in keep
376 should_release = None
378 owned = lu.owned_locks(level)
380 # Not owning any lock at this level, do nothing
387 # Determine which locks to release
389 if should_release(name):
394 assert len(lu.owned_locks(level)) == (len(retain) + len(release))
396 # Release just some locks
397 lu.glm.release(level, names=release)
399 assert frozenset(lu.owned_locks(level)) == frozenset(retain)
402 lu.glm.release(level)
404 assert not lu.glm.is_owned(level), "No locks should be owned"
407 def _ComputeIPolicyNodeViolation(ipolicy, instance, current_group,
409 _compute_fn=ComputeIPolicyInstanceViolation):
410 """Compute if instance meets the specs of the new target group.
412 @param ipolicy: The ipolicy to verify
413 @param instance: The instance object to verify
414 @param current_group: The current group of the instance
415 @param target_group: The new group of the instance
416 @type cfg: L{config.ConfigWriter}
417 @param cfg: Cluster configuration
418 @param _compute_fn: The function to verify ipolicy (unittest only)
419 @see: L{ganeti.cmdlib.common.ComputeIPolicySpecViolation}
422 if current_group == target_group:
425 return _compute_fn(ipolicy, instance, cfg)
428 def CheckTargetNodeIPolicy(lu, ipolicy, instance, node, cfg, ignore=False,
429 _compute_fn=_ComputeIPolicyNodeViolation):
430 """Checks that the target node is correct in terms of instance policy.
432 @param ipolicy: The ipolicy to verify
433 @param instance: The instance object to verify
434 @param node: The new node to relocate
435 @type cfg: L{config.ConfigWriter}
436 @param cfg: Cluster configuration
437 @param ignore: Ignore violations of the ipolicy
438 @param _compute_fn: The function to verify ipolicy (unittest only)
439 @see: L{ganeti.cmdlib.common.ComputeIPolicySpecViolation}
442 primary_node = lu.cfg.GetNodeInfo(instance.primary_node)
443 res = _compute_fn(ipolicy, instance, primary_node.group, node.group, cfg)
446 msg = ("Instance does not meet target node group's (%s) instance"
447 " policy: %s") % (node.group, utils.CommaJoin(res))
451 raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
454 def GetInstanceInfoText(instance):
455 """Compute that text that should be added to the disk's metadata.
458 return "originstname+%s" % instance.name
461 def CheckNodeFreeMemory(lu, node_uuid, reason, requested, hvname, hvparams):
462 """Checks if a node has enough free memory.
464 This function checks if a given node has the needed amount of free
465 memory. In case the node has less memory or we cannot get the
466 information from the node, this function raises an OpPrereqError
469 @type lu: C{LogicalUnit}
470 @param lu: a logical unit from which we get configuration data
471 @type node_uuid: C{str}
472 @param node_uuid: the node to check
474 @param reason: string to use in the error message
475 @type requested: C{int}
476 @param requested: the amount of memory in MiB to check for
478 @param hvname: the hypervisor's name
479 @type hvparams: dict of strings
480 @param hvparams: the hypervisor's parameters
482 @return: node current free memory
483 @raise errors.OpPrereqError: if the node doesn't have enough memory, or
484 we cannot check the node
487 node_name = lu.cfg.GetNodeName(node_uuid)
488 nodeinfo = lu.rpc.call_node_info([node_uuid], None, [(hvname, hvparams)])
489 nodeinfo[node_uuid].Raise("Can't get data from node %s" % node_name,
490 prereq=True, ecode=errors.ECODE_ENVIRON)
491 (_, _, (hv_info, )) = nodeinfo[node_uuid].payload
493 free_mem = hv_info.get("memory_free", None)
494 if not isinstance(free_mem, int):
495 raise errors.OpPrereqError("Can't compute free memory on node %s, result"
496 " was '%s'" % (node_name, free_mem),
497 errors.ECODE_ENVIRON)
498 if requested > free_mem:
499 raise errors.OpPrereqError("Not enough memory on node %s for %s:"
500 " needed %s MiB, available %s MiB" %
501 (node_name, reason, requested, free_mem),
506 def CheckInstanceBridgesExist(lu, instance, node_uuid=None):
507 """Check that the brigdes needed by an instance exist.
510 if node_uuid is None:
511 node_uuid = instance.primary_node
512 CheckNicsBridgesExist(lu, instance.nics, node_uuid)
515 def CheckNicsBridgesExist(lu, nics, node_uuid):
516 """Check that the brigdes needed by a list of nics exist.
519 cluster = lu.cfg.GetClusterInfo()
520 paramslist = [cluster.SimpleFillNIC(nic.nicparams) for nic in nics]
521 brlist = [params[constants.NIC_LINK] for params in paramslist
522 if params[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED]
524 result = lu.rpc.call_bridges_exist(node_uuid, brlist)
525 result.Raise("Error checking bridges on destination node '%s'" %
526 lu.cfg.GetNodeName(node_uuid), prereq=True,
527 ecode=errors.ECODE_ENVIRON)
530 def CheckNodeHasOS(lu, node_uuid, os_name, force_variant):
531 """Ensure that a node supports a given OS.
533 @param lu: the LU on behalf of which we make the check
534 @param node_uuid: the node to check
535 @param os_name: the OS to query about
536 @param force_variant: whether to ignore variant errors
537 @raise errors.OpPrereqError: if the node is not supporting the OS
540 result = lu.rpc.call_os_get(node_uuid, os_name)
541 result.Raise("OS '%s' not in supported OS list for node %s" %
542 (os_name, lu.cfg.GetNodeName(node_uuid)),
543 prereq=True, ecode=errors.ECODE_INVAL)
544 if not force_variant:
545 _CheckOSVariant(result.payload, os_name)
548 def _CheckOSVariant(os_obj, name):
549 """Check whether an OS name conforms to the os variants specification.
551 @type os_obj: L{objects.OS}
552 @param os_obj: OS object to check
554 @param name: OS name passed by the user, to check for validity
557 variant = objects.OS.GetVariant(name)
558 if not os_obj.supported_variants:
560 raise errors.OpPrereqError("OS '%s' doesn't support variants ('%s'"
561 " passed)" % (os_obj.name, variant),
565 raise errors.OpPrereqError("OS name must include a variant",
568 if variant not in os_obj.supported_variants:
569 raise errors.OpPrereqError("Unsupported OS variant", errors.ECODE_INVAL)