Package ganeti :: Package cmdlib :: Module instance_utils
[hide private]
[frames] | no frames]

Source Code for Module ganeti.cmdlib.instance_utils

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc. 
  5  # 
  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. 
 10  # 
 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. 
 15  # 
 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 
 19  # 02110-1301, USA. 
 20   
 21   
 22  """Utility function mainly, but not only used by instance LU's.""" 
 23   
 24  import logging 
 25  import os 
 26   
 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 
 36   
 37   
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 42 43 This builds the hook environment from individual variables. 44 45 @type name: string 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 51 @type os_type: string 52 @param os_type: the name of the instance's OS 53 @type status: string 54 @param status: the desired status of the instance 55 @type minmem: string 56 @param minmem: the minimum memory size of the instance 57 @type maxmem: string 58 @param maxmem: the maximum memory size of the instance 59 @type vcpus: string 60 @param vcpus: the count of VCPUs the instance has 61 @type nics: list 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 66 @type disks: list 67 @param disks: list of tuples (name, uuid, size, mode) 68 @type bep: dict 69 @param bep: the backend parameters for the instance 70 @type hvp: dict 71 @param hvp: the hypervisor parameters for the instance 72 @type hypervisor_name: string 73 @param hypervisor_name: the hypervisor for the instance 74 @type tags: list 75 @param tags: list of instance tags as strings 76 @rtype: dict 77 @return: the hook environment for this instance 78 79 """ 80 env = { 81 "OP_TARGET": name, 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, 94 } 95 if nics: 96 nic_count = len(nics) 97 for idx, (name, uuid, ip, mac, mode, link, net, netinfo) in enumerate(nics): 98 if ip is None: 99 ip = "" 100 if name: 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 107 if netinfo: 108 nobj = objects.Network.FromDict(netinfo) 109 env.update(nobj.HooksDict("INSTANCE_NIC%d_" % idx)) 110 elif network: 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 117 else: 118 nic_count = 0 119 120 env["INSTANCE_NIC_COUNT"] = nic_count 121 122 if disks: 123 disk_count = len(disks) 124 for idx, (name, uuid, size, mode) in enumerate(disks): 125 if name: 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 130 else: 131 disk_count = 0 132 133 env["INSTANCE_DISK_COUNT"] = disk_count 134 135 if not tags: 136 tags = [] 137 138 env["INSTANCE_TAGS"] = " ".join(tags) 139 140 for source, kind in [(bep, "BE"), (hvp, "HV")]: 141 for key, value in source.items(): 142 env["INSTANCE_%s_%s" % (kind, key)] = value 143 144 return env
145 146
147 -def BuildInstanceHookEnvByObject(lu, instance, override=None):
148 """Builds instance related env variables for hooks from an object. 149 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 154 environment 155 @type override: dict 156 @param override: dictionary with key/values that will override 157 our values 158 @rtype: dict 159 @return: the hook environment dictionary 160 161 """ 162 cluster = lu.cfg.GetClusterInfo() 163 bep = cluster.FillBE(instance) 164 hvp = cluster.FillHV(instance) 165 args = { 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], 178 "bep": bep, 179 "hvp": hvp, 180 "hypervisor_name": instance.hypervisor, 181 "tags": instance.tags, 182 } 183 if override: 184 args.update(override) 185 return BuildInstanceHookEnv(**args) # pylint: disable=W0142
186 187
188 -def GetClusterDomainSecret():
189 """Reads the cluster domain secret. 190 191 """ 192 return utils.ReadOneLineFile(pathutils.CLUSTER_DOMAIN_SECRET_FILE, 193 strict=True)
194 195
196 -def CheckNodeNotDrained(lu, node_uuid):
197 """Ensure that a given node is not drained. 198 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 202 203 """ 204 node = lu.cfg.GetNodeInfo(node_uuid) 205 if node.drained: 206 raise errors.OpPrereqError("Can't use drained node %s" % node.name, 207 errors.ECODE_STATE)
208 209
210 -def CheckNodeVmCapable(lu, node_uuid):
211 """Ensure that a given node is vm capable. 212 213 @param lu: the LU on behalf of which we make the check 214 @param node_uuid: the node to check 215 @raise errors.OpPrereqError: if the node is not vm capable 216 217 """ 218 if not lu.cfg.GetNodeInfo(node_uuid).vm_capable: 219 raise errors.OpPrereqError("Can't use non-vm_capable node %s" % node_uuid, 220 errors.ECODE_STATE)
221 222
223 -def RemoveInstance(lu, feedback_fn, instance, ignore_failures):
224 """Utility function to remove an instance. 225 226 """ 227 logging.info("Removing block devices for instance %s", instance.name) 228 229 if not RemoveDisks(lu, instance, ignore_failures=ignore_failures): 230 if not ignore_failures: 231 raise errors.OpExecError("Can't remove instance's disks") 232 feedback_fn("Warning: can't remove instance's disks") 233 234 logging.info("Removing instance %s out of cluster config", instance.name) 235 236 lu.cfg.RemoveInstance(instance.uuid) 237 238 assert not lu.remove_locks.get(locking.LEVEL_INSTANCE), \ 239 "Instance lock removal conflict" 240 241 # Remove lock for the instance 242 lu.remove_locks[locking.LEVEL_INSTANCE] = instance.name
243 244
245 -def RemoveDisks(lu, instance, target_node_uuid=None, ignore_failures=False):
246 """Remove all disks for an instance. 247 248 This abstracts away some work from `AddInstance()` and 249 `RemoveInstance()`. Note that in case some of the devices couldn't 250 be removed, the removal will continue with the other ones. 251 252 @type lu: L{LogicalUnit} 253 @param lu: the logical unit on whose behalf we execute 254 @type instance: L{objects.Instance} 255 @param instance: the instance whose disks we should remove 256 @type target_node_uuid: string 257 @param target_node_uuid: used to override the node on which to remove the 258 disks 259 @rtype: boolean 260 @return: the success of the removal 261 262 """ 263 logging.info("Removing block devices for instance %s", instance.name) 264 265 all_result = True 266 ports_to_release = set() 267 anno_disks = AnnotateDiskParams(instance, instance.disks, lu.cfg) 268 for (idx, device) in enumerate(anno_disks): 269 if target_node_uuid: 270 edata = [(target_node_uuid, device)] 271 else: 272 edata = device.ComputeNodeTree(instance.primary_node) 273 for node_uuid, disk in edata: 274 lu.cfg.SetDiskID(disk, node_uuid) 275 result = lu.rpc.call_blockdev_remove(node_uuid, disk) 276 if result.fail_msg: 277 lu.LogWarning("Could not remove disk %s on node %s," 278 " continuing anyway: %s", idx, 279 lu.cfg.GetNodeName(node_uuid), result.fail_msg) 280 if not (result.offline and node_uuid != instance.primary_node): 281 all_result = False 282 283 # if this is a DRBD disk, return its port to the pool 284 if device.dev_type in constants.DTS_DRBD: 285 ports_to_release.add(device.logical_id[2]) 286 287 if all_result or ignore_failures: 288 for port in ports_to_release: 289 lu.cfg.AddTcpUdpPort(port) 290 291 CheckDiskTemplateEnabled(lu.cfg.GetClusterInfo(), instance.disk_template) 292 293 if instance.disk_template in constants.DTS_FILEBASED: 294 if len(instance.disks) > 0: 295 file_storage_dir = os.path.dirname(instance.disks[0].logical_id[1]) 296 else: 297 if instance.disk_template == constants.DT_SHARED_FILE: 298 file_storage_dir = utils.PathJoin(lu.cfg.GetSharedFileStorageDir(), 299 instance.name) 300 else: 301 file_storage_dir = utils.PathJoin(lu.cfg.GetFileStorageDir(), 302 instance.name) 303 if target_node_uuid: 304 tgt = target_node_uuid 305 else: 306 tgt = instance.primary_node 307 result = lu.rpc.call_file_storage_dir_remove(tgt, file_storage_dir) 308 if result.fail_msg: 309 lu.LogWarning("Could not remove directory '%s' on node %s: %s", 310 file_storage_dir, lu.cfg.GetNodeName(tgt), result.fail_msg) 311 all_result = False 312 313 return all_result
314 315
316 -def NICToTuple(lu, nic):
317 """Build a tupple of nic information. 318 319 @type lu: L{LogicalUnit} 320 @param lu: the logical unit on whose behalf we execute 321 @type nic: L{objects.NIC} 322 @param nic: nic to convert to hooks tuple 323 324 """ 325 cluster = lu.cfg.GetClusterInfo() 326 filled_params = cluster.SimpleFillNIC(nic.nicparams) 327 mode = filled_params[constants.NIC_MODE] 328 link = filled_params[constants.NIC_LINK] 329 netinfo = None 330 if nic.network: 331 nobj = lu.cfg.GetNetwork(nic.network) 332 netinfo = objects.Network.ToDict(nobj) 333 return (nic.name, nic.uuid, nic.ip, nic.mac, mode, link, nic.network, netinfo)
334 335
336 -def NICListToTuple(lu, nics):
337 """Build a list of nic information tuples. 338 339 This list is suitable to be passed to _BuildInstanceHookEnv or as a return 340 value in LUInstanceQueryData. 341 342 @type lu: L{LogicalUnit} 343 @param lu: the logical unit on whose behalf we execute 344 @type nics: list of L{objects.NIC} 345 @param nics: list of nics to convert to hooks tuples 346 347 """ 348 hooks_nics = [] 349 for nic in nics: 350 hooks_nics.append(NICToTuple(lu, nic)) 351 return hooks_nics
352 353
354 -def CopyLockList(names):
355 """Makes a copy of a list of lock names. 356 357 Handles L{locking.ALL_SET} correctly. 358 359 """ 360 if names == locking.ALL_SET: 361 return locking.ALL_SET 362 else: 363 return names[:]
364 365
366 -def ReleaseLocks(lu, level, names=None, keep=None):
367 """Releases locks owned by an LU. 368 369 @type lu: L{LogicalUnit} 370 @param level: Lock level 371 @type names: list or None 372 @param names: Names of locks to release 373 @type keep: list or None 374 @param keep: Names of locks to retain 375 376 """ 377 assert not (keep is not None and names is not None), \ 378 "Only one of the 'names' and the 'keep' parameters can be given" 379 380 if names is not None: 381 should_release = names.__contains__ 382 elif keep: 383 should_release = lambda name: name not in keep 384 else: 385 should_release = None 386 387 owned = lu.owned_locks(level) 388 if not owned: 389 # Not owning any lock at this level, do nothing 390 pass 391 392 elif should_release: 393 retain = [] 394 release = [] 395 396 # Determine which locks to release 397 for name in owned: 398 if should_release(name): 399 release.append(name) 400 else: 401 retain.append(name) 402 403 assert len(lu.owned_locks(level)) == (len(retain) + len(release)) 404 405 # Release just some locks 406 lu.glm.release(level, names=release) 407 408 assert frozenset(lu.owned_locks(level)) == frozenset(retain) 409 else: 410 # Release everything 411 lu.glm.release(level) 412 413 assert not lu.glm.is_owned(level), "No locks should be owned"
414 415
416 -def _ComputeIPolicyNodeViolation(ipolicy, instance, current_group, 417 target_group, cfg, 418 _compute_fn=ComputeIPolicyInstanceViolation):
419 """Compute if instance meets the specs of the new target group. 420 421 @param ipolicy: The ipolicy to verify 422 @param instance: The instance object to verify 423 @param current_group: The current group of the instance 424 @param target_group: The new group of the instance 425 @type cfg: L{config.ConfigWriter} 426 @param cfg: Cluster configuration 427 @param _compute_fn: The function to verify ipolicy (unittest only) 428 @see: L{ganeti.cmdlib.common.ComputeIPolicySpecViolation} 429 430 """ 431 if current_group == target_group: 432 return [] 433 else: 434 return _compute_fn(ipolicy, instance, cfg)
435 436
437 -def CheckTargetNodeIPolicy(lu, ipolicy, instance, node, cfg, ignore=False, 438 _compute_fn=_ComputeIPolicyNodeViolation):
439 """Checks that the target node is correct in terms of instance policy. 440 441 @param ipolicy: The ipolicy to verify 442 @param instance: The instance object to verify 443 @param node: The new node to relocate 444 @type cfg: L{config.ConfigWriter} 445 @param cfg: Cluster configuration 446 @param ignore: Ignore violations of the ipolicy 447 @param _compute_fn: The function to verify ipolicy (unittest only) 448 @see: L{ganeti.cmdlib.common.ComputeIPolicySpecViolation} 449 450 """ 451 primary_node = lu.cfg.GetNodeInfo(instance.primary_node) 452 res = _compute_fn(ipolicy, instance, primary_node.group, node.group, cfg) 453 454 if res: 455 msg = ("Instance does not meet target node group's (%s) instance" 456 " policy: %s") % (node.group, utils.CommaJoin(res)) 457 if ignore: 458 lu.LogWarning(msg) 459 else: 460 raise errors.OpPrereqError(msg, errors.ECODE_INVAL)
461 462
463 -def GetInstanceInfoText(instance):
464 """Compute that text that should be added to the disk's metadata. 465 466 """ 467 return "originstname+%s" % instance.name
468 469
470 -def CheckNodeFreeMemory(lu, node_uuid, reason, requested, hvname, hvparams):
471 """Checks if a node has enough free memory. 472 473 This function checks if a given node has the needed amount of free 474 memory. In case the node has less memory or we cannot get the 475 information from the node, this function raises an OpPrereqError 476 exception. 477 478 @type lu: C{LogicalUnit} 479 @param lu: a logical unit from which we get configuration data 480 @type node_uuid: C{str} 481 @param node_uuid: the node to check 482 @type reason: C{str} 483 @param reason: string to use in the error message 484 @type requested: C{int} 485 @param requested: the amount of memory in MiB to check for 486 @type hvname: string 487 @param hvname: the hypervisor's name 488 @type hvparams: dict of strings 489 @param hvparams: the hypervisor's parameters 490 @rtype: integer 491 @return: node current free memory 492 @raise errors.OpPrereqError: if the node doesn't have enough memory, or 493 we cannot check the node 494 495 """ 496 node_name = lu.cfg.GetNodeName(node_uuid) 497 nodeinfo = lu.rpc.call_node_info([node_uuid], None, [(hvname, hvparams)]) 498 nodeinfo[node_uuid].Raise("Can't get data from node %s" % node_name, 499 prereq=True, ecode=errors.ECODE_ENVIRON) 500 (_, _, (hv_info, )) = nodeinfo[node_uuid].payload 501 502 free_mem = hv_info.get("memory_free", None) 503 if not isinstance(free_mem, int): 504 raise errors.OpPrereqError("Can't compute free memory on node %s, result" 505 " was '%s'" % (node_name, free_mem), 506 errors.ECODE_ENVIRON) 507 if requested > free_mem: 508 raise errors.OpPrereqError("Not enough memory on node %s for %s:" 509 " needed %s MiB, available %s MiB" % 510 (node_name, reason, requested, free_mem), 511 errors.ECODE_NORES) 512 return free_mem
513 514
515 -def CheckInstanceBridgesExist(lu, instance, node_uuid=None):
516 """Check that the brigdes needed by an instance exist. 517 518 """ 519 if node_uuid is None: 520 node_uuid = instance.primary_node 521 CheckNicsBridgesExist(lu, instance.nics, node_uuid)
522 523
524 -def CheckNicsBridgesExist(lu, nics, node_uuid):
525 """Check that the brigdes needed by a list of nics exist. 526 527 """ 528 cluster = lu.cfg.GetClusterInfo() 529 paramslist = [cluster.SimpleFillNIC(nic.nicparams) for nic in nics] 530 brlist = [params[constants.NIC_LINK] for params in paramslist 531 if params[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED] 532 if brlist: 533 result = lu.rpc.call_bridges_exist(node_uuid, brlist) 534 result.Raise("Error checking bridges on destination node '%s'" % 535 lu.cfg.GetNodeName(node_uuid), prereq=True, 536 ecode=errors.ECODE_ENVIRON)
537 538
539 -def CheckNodeHasOS(lu, node_uuid, os_name, force_variant):
540 """Ensure that a node supports a given OS. 541 542 @param lu: the LU on behalf of which we make the check 543 @param node_uuid: the node to check 544 @param os_name: the OS to query about 545 @param force_variant: whether to ignore variant errors 546 @raise errors.OpPrereqError: if the node is not supporting the OS 547 548 """ 549 result = lu.rpc.call_os_get(node_uuid, os_name) 550 result.Raise("OS '%s' not in supported OS list for node %s" % 551 (os_name, lu.cfg.GetNodeName(node_uuid)), 552 prereq=True, ecode=errors.ECODE_INVAL) 553 if not force_variant: 554 _CheckOSVariant(result.payload, os_name)
555 556
557 -def _CheckOSVariant(os_obj, name):
558 """Check whether an OS name conforms to the os variants specification. 559 560 @type os_obj: L{objects.OS} 561 @param os_obj: OS object to check 562 @type name: string 563 @param name: OS name passed by the user, to check for validity 564 565 """ 566 variant = objects.OS.GetVariant(name) 567 if not os_obj.supported_variants: 568 if variant: 569 raise errors.OpPrereqError("OS '%s' doesn't support variants ('%s'" 570 " passed)" % (os_obj.name, variant), 571 errors.ECODE_INVAL) 572 return 573 if not variant: 574 raise errors.OpPrereqError("OS name must include a variant", 575 errors.ECODE_INVAL) 576 577 if variant not in os_obj.supported_variants: 578 raise errors.OpPrereqError("Unsupported OS variant", errors.ECODE_INVAL)
579