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
38 def BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status,
39 minmem, maxmem, vcpus, nics, disk_template, disks,
40 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: string
48 @param primary_node: the name of the instance's primary node
49 @type secondary_nodes: list
50 @param secondary_nodes: 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,
84 "INSTANCE_SECONDARIES": " ".join(secondary_nodes),
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": instance.primary_node,
168 "secondary_nodes": 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):
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: the node to check
201 @raise errors.OpPrereqError: if the node is drained
204 if lu.cfg.GetNodeInfo(node).drained:
205 raise errors.OpPrereqError("Can't use drained node %s" % node,
209 def CheckNodeVmCapable(lu, node):
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: the node to check
214 @raise errors.OpPrereqError: if the node is not vm capable
217 if not lu.cfg.GetNodeInfo(node).vm_capable:
218 raise errors.OpPrereqError("Can't use non-vm_capable node %s" % node,
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.name)
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=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: string
256 @param target_node: used to override the node on which to remove the disks
258 @return: the success of the removal
261 logging.info("Removing block devices for instance %s", instance.name)
264 ports_to_release = set()
265 anno_disks = AnnotateDiskParams(instance, instance.disks, lu.cfg)
266 for (idx, device) in enumerate(anno_disks):
268 edata = [(target_node, device)]
270 edata = device.ComputeNodeTree(instance.primary_node)
271 for node, disk in edata:
272 lu.cfg.SetDiskID(disk, node)
273 result = lu.rpc.call_blockdev_remove(node, disk)
275 lu.LogWarning("Could not remove disk %s on node %s,"
276 " continuing anyway: %s", idx, node, result.fail_msg)
277 if not (result.offline and node != instance.primary_node):
280 # if this is a DRBD disk, return its port to the pool
281 if device.dev_type in constants.LDS_DRBD:
282 ports_to_release.add(device.logical_id[2])
284 if all_result or ignore_failures:
285 for port in ports_to_release:
286 lu.cfg.AddTcpUdpPort(port)
288 if instance.disk_template in constants.DTS_FILEBASED:
289 file_storage_dir = os.path.dirname(instance.disks[0].logical_id[1])
293 tgt = instance.primary_node
294 result = lu.rpc.call_file_storage_dir_remove(tgt, file_storage_dir)
296 lu.LogWarning("Could not remove directory '%s' on node %s: %s",
297 file_storage_dir, instance.primary_node, result.fail_msg)
303 def NICToTuple(lu, nic):
304 """Build a tupple of nic information.
306 @type lu: L{LogicalUnit}
307 @param lu: the logical unit on whose behalf we execute
308 @type nic: L{objects.NIC}
309 @param nic: nic to convert to hooks tuple
312 cluster = lu.cfg.GetClusterInfo()
313 filled_params = cluster.SimpleFillNIC(nic.nicparams)
314 mode = filled_params[constants.NIC_MODE]
315 link = filled_params[constants.NIC_LINK]
318 nobj = lu.cfg.GetNetwork(nic.network)
319 netinfo = objects.Network.ToDict(nobj)
320 return (nic.name, nic.uuid, nic.ip, nic.mac, mode, link, nic.network, netinfo)
323 def NICListToTuple(lu, nics):
324 """Build a list of nic information tuples.
326 This list is suitable to be passed to _BuildInstanceHookEnv or as a return
327 value in LUInstanceQueryData.
329 @type lu: L{LogicalUnit}
330 @param lu: the logical unit on whose behalf we execute
331 @type nics: list of L{objects.NIC}
332 @param nics: list of nics to convert to hooks tuples
337 hooks_nics.append(NICToTuple(lu, nic))
341 def CopyLockList(names):
342 """Makes a copy of a list of lock names.
344 Handles L{locking.ALL_SET} correctly.
347 if names == locking.ALL_SET:
348 return locking.ALL_SET
353 def ReleaseLocks(lu, level, names=None, keep=None):
354 """Releases locks owned by an LU.
356 @type lu: L{LogicalUnit}
357 @param level: Lock level
358 @type names: list or None
359 @param names: Names of locks to release
360 @type keep: list or None
361 @param keep: Names of locks to retain
364 assert not (keep is not None and names is not None), \
365 "Only one of the 'names' and the 'keep' parameters can be given"
367 if names is not None:
368 should_release = names.__contains__
370 should_release = lambda name: name not in keep
372 should_release = None
374 owned = lu.owned_locks(level)
376 # Not owning any lock at this level, do nothing
383 # Determine which locks to release
385 if should_release(name):
390 assert len(lu.owned_locks(level)) == (len(retain) + len(release))
392 # Release just some locks
393 lu.glm.release(level, names=release)
395 assert frozenset(lu.owned_locks(level)) == frozenset(retain)
398 lu.glm.release(level)
400 assert not lu.glm.is_owned(level), "No locks should be owned"
403 def _ComputeIPolicyNodeViolation(ipolicy, instance, current_group,
405 _compute_fn=ComputeIPolicyInstanceViolation):
406 """Compute if instance meets the specs of the new target group.
408 @param ipolicy: The ipolicy to verify
409 @param instance: The instance object to verify
410 @param current_group: The current group of the instance
411 @param target_group: The new group of the instance
412 @type cfg: L{config.ConfigWriter}
413 @param cfg: Cluster configuration
414 @param _compute_fn: The function to verify ipolicy (unittest only)
415 @see: L{ganeti.cmdlib.common.ComputeIPolicySpecViolation}
418 if current_group == target_group:
421 return _compute_fn(ipolicy, instance, cfg)
424 def CheckTargetNodeIPolicy(lu, ipolicy, instance, node, cfg, ignore=False,
425 _compute_fn=_ComputeIPolicyNodeViolation):
426 """Checks that the target node is correct in terms of instance policy.
428 @param ipolicy: The ipolicy to verify
429 @param instance: The instance object to verify
430 @param node: The new node to relocate
431 @type cfg: L{config.ConfigWriter}
432 @param cfg: Cluster configuration
433 @param ignore: Ignore violations of the ipolicy
434 @param _compute_fn: The function to verify ipolicy (unittest only)
435 @see: L{ganeti.cmdlib.common.ComputeIPolicySpecViolation}
438 primary_node = lu.cfg.GetNodeInfo(instance.primary_node)
439 res = _compute_fn(ipolicy, instance, primary_node.group, node.group, cfg)
442 msg = ("Instance does not meet target node group's (%s) instance"
443 " policy: %s") % (node.group, utils.CommaJoin(res))
447 raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
450 def GetInstanceInfoText(instance):
451 """Compute that text that should be added to the disk's metadata.
454 return "originstname+%s" % instance.name
457 def CheckNodeFreeMemory(lu, node, reason, requested, hypervisor_name):
458 """Checks if a node has enough free memory.
460 This function checks if a given node has the needed amount of free
461 memory. In case the node has less memory or we cannot get the
462 information from the node, this function raises an OpPrereqError
465 @type lu: C{LogicalUnit}
466 @param lu: a logical unit from which we get configuration data
468 @param node: the node to check
470 @param reason: string to use in the error message
471 @type requested: C{int}
472 @param requested: the amount of memory in MiB to check for
473 @type hypervisor_name: C{str}
474 @param hypervisor_name: the hypervisor to ask for memory stats
476 @return: node current free memory
477 @raise errors.OpPrereqError: if the node doesn't have enough memory, or
478 we cannot check the node
481 nodeinfo = lu.rpc.call_node_info([node], None, [hypervisor_name], False)
482 nodeinfo[node].Raise("Can't get data from node %s" % node,
483 prereq=True, ecode=errors.ECODE_ENVIRON)
484 (_, _, (hv_info, )) = nodeinfo[node].payload
486 free_mem = hv_info.get("memory_free", None)
487 if not isinstance(free_mem, int):
488 raise errors.OpPrereqError("Can't compute free memory on node %s, result"
489 " was '%s'" % (node, free_mem),
490 errors.ECODE_ENVIRON)
491 if requested > free_mem:
492 raise errors.OpPrereqError("Not enough memory on node %s for %s:"
493 " needed %s MiB, available %s MiB" %
494 (node, reason, requested, free_mem),
499 def CheckInstanceBridgesExist(lu, instance, node=None):
500 """Check that the brigdes needed by an instance exist.
504 node = instance.primary_node
505 CheckNicsBridgesExist(lu, instance.nics, node)
508 def CheckNicsBridgesExist(lu, target_nics, target_node):
509 """Check that the brigdes needed by a list of nics exist.
512 cluster = lu.cfg.GetClusterInfo()
513 paramslist = [cluster.SimpleFillNIC(nic.nicparams) for nic in target_nics]
514 brlist = [params[constants.NIC_LINK] for params in paramslist
515 if params[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED]
517 result = lu.rpc.call_bridges_exist(target_node, brlist)
518 result.Raise("Error checking bridges on destination node '%s'" %
519 target_node, prereq=True, ecode=errors.ECODE_ENVIRON)
522 def CheckNodeHasOS(lu, node, os_name, force_variant):
523 """Ensure that a node supports a given OS.
525 @param lu: the LU on behalf of which we make the check
526 @param node: the node to check
527 @param os_name: the OS to query about
528 @param force_variant: whether to ignore variant errors
529 @raise errors.OpPrereqError: if the node is not supporting the OS
532 result = lu.rpc.call_os_get(node, os_name)
533 result.Raise("OS '%s' not in supported OS list for node %s" %
535 prereq=True, ecode=errors.ECODE_INVAL)
536 if not force_variant:
537 _CheckOSVariant(result.payload, os_name)
540 def _CheckOSVariant(os_obj, name):
541 """Check whether an OS name conforms to the os variants specification.
543 @type os_obj: L{objects.OS}
544 @param os_obj: OS object to check
546 @param name: OS name passed by the user, to check for validity
549 variant = objects.OS.GetVariant(name)
550 if not os_obj.supported_variants:
552 raise errors.OpPrereqError("OS '%s' doesn't support variants ('%s'"
553 " passed)" % (os_obj.name, variant),
557 raise errors.OpPrereqError("OS name must include a variant",
560 if variant not in os_obj.supported_variants:
561 raise errors.OpPrereqError("Unsupported OS variant", errors.ECODE_INVAL)