Package ganeti :: Module objects
[hide private]
[frames] | no frames]

Source Code for Module ganeti.objects

   1  # 
   2  # 
   3   
   4  # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Google Inc. 
   5  # All rights reserved. 
   6  # 
   7  # Redistribution and use in source and binary forms, with or without 
   8  # modification, are permitted provided that the following conditions are 
   9  # met: 
  10  # 
  11  # 1. Redistributions of source code must retain the above copyright notice, 
  12  # this list of conditions and the following disclaimer. 
  13  # 
  14  # 2. Redistributions in binary form must reproduce the above copyright 
  15  # notice, this list of conditions and the following disclaimer in the 
  16  # documentation and/or other materials provided with the distribution. 
  17  # 
  18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
  19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
  20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
  22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
  23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
  24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
  25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
  26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
  27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
  28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  29   
  30   
  31  """Transportable objects for Ganeti. 
  32   
  33  This module provides small, mostly data-only objects which are safe to 
  34  pass to and from external parties. 
  35   
  36  """ 
  37   
  38  # pylint: disable=E0203,W0201,R0902 
  39   
  40  # E0203: Access to member %r before its definition, since we use 
  41  # objects.py which doesn't explicitly initialise its members 
  42   
  43  # W0201: Attribute '%s' defined outside __init__ 
  44   
  45  # R0902: Allow instances of these objects to have more than 20 attributes 
  46   
  47  import ConfigParser 
  48  import re 
  49  import copy 
  50  import logging 
  51  import time 
  52  from cStringIO import StringIO 
  53   
  54  from ganeti import errors 
  55  from ganeti import constants 
  56  from ganeti import netutils 
  57  from ganeti import outils 
  58  from ganeti import utils 
  59  from ganeti import serializer 
  60   
  61  from socket import AF_INET 
  62   
  63   
  64  __all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance", 
  65             "OS", "Node", "NodeGroup", "Cluster", "FillDict", "Network", 
  66             "Filter"] 
  67   
  68  _TIMESTAMPS = ["ctime", "mtime"] 
  69  _UUID = ["uuid"] 
70 71 72 -def FillDict(defaults_dict, custom_dict, skip_keys=None):
73 """Basic function to apply settings on top a default dict. 74 75 @type defaults_dict: dict 76 @param defaults_dict: dictionary holding the default values 77 @type custom_dict: dict 78 @param custom_dict: dictionary holding customized value 79 @type skip_keys: list 80 @param skip_keys: which keys not to fill 81 @rtype: dict 82 @return: dict with the 'full' values 83 84 """ 85 ret_dict = copy.deepcopy(defaults_dict) 86 ret_dict.update(custom_dict) 87 if skip_keys: 88 for k in skip_keys: 89 if k in ret_dict: 90 del ret_dict[k] 91 return ret_dict
92
93 94 -def FillIPolicy(default_ipolicy, custom_ipolicy):
95 """Fills an instance policy with defaults. 96 97 """ 98 assert frozenset(default_ipolicy.keys()) == constants.IPOLICY_ALL_KEYS 99 ret_dict = copy.deepcopy(custom_ipolicy) 100 for key in default_ipolicy: 101 if key not in ret_dict: 102 ret_dict[key] = copy.deepcopy(default_ipolicy[key]) 103 elif key == constants.ISPECS_STD: 104 ret_dict[key] = FillDict(default_ipolicy[key], ret_dict[key]) 105 return ret_dict
106
107 108 -def FillDiskParams(default_dparams, custom_dparams, skip_keys=None):
109 """Fills the disk parameter defaults. 110 111 @see: L{FillDict} for parameters and return value 112 113 """ 114 return dict((dt, FillDict(default_dparams.get(dt, {}), 115 custom_dparams.get(dt, {}), 116 skip_keys=skip_keys)) 117 for dt in constants.DISK_TEMPLATES)
118
119 120 -def UpgradeGroupedParams(target, defaults):
121 """Update all groups for the target parameter. 122 123 @type target: dict of dicts 124 @param target: {group: {parameter: value}} 125 @type defaults: dict 126 @param defaults: default parameter values 127 128 """ 129 if target is None: 130 target = {constants.PP_DEFAULT: defaults} 131 else: 132 for group in target: 133 target[group] = FillDict(defaults, target[group]) 134 return target
135
136 137 -def UpgradeBeParams(target):
138 """Update the be parameters dict to the new format. 139 140 @type target: dict 141 @param target: "be" parameters dict 142 143 """ 144 if constants.BE_MEMORY in target: 145 memory = target[constants.BE_MEMORY] 146 target[constants.BE_MAXMEM] = memory 147 target[constants.BE_MINMEM] = memory 148 del target[constants.BE_MEMORY]
149
150 151 -def UpgradeDiskParams(diskparams):
152 """Upgrade the disk parameters. 153 154 @type diskparams: dict 155 @param diskparams: disk parameters to upgrade 156 @rtype: dict 157 @return: the upgraded disk parameters dict 158 159 """ 160 if not diskparams: 161 result = {} 162 else: 163 result = FillDiskParams(constants.DISK_DT_DEFAULTS, diskparams) 164 165 return result
166
167 168 -def UpgradeNDParams(ndparams):
169 """Upgrade ndparams structure. 170 171 @type ndparams: dict 172 @param ndparams: disk parameters to upgrade 173 @rtype: dict 174 @return: the upgraded node parameters dict 175 176 """ 177 if ndparams is None: 178 ndparams = {} 179 180 if (constants.ND_OOB_PROGRAM in ndparams and 181 ndparams[constants.ND_OOB_PROGRAM] is None): 182 # will be reset by the line below 183 del ndparams[constants.ND_OOB_PROGRAM] 184 return FillDict(constants.NDC_DEFAULTS, ndparams)
185
186 187 -def MakeEmptyIPolicy():
188 """Create empty IPolicy dictionary. 189 190 """ 191 return {}
192
193 194 -class ConfigObject(outils.ValidatedSlots):
195 """A generic config object. 196 197 It has the following properties: 198 199 - provides somewhat safe recursive unpickling and pickling for its classes 200 - unset attributes which are defined in slots are always returned 201 as None instead of raising an error 202 203 Classes derived from this must always declare __slots__ (we use many 204 config objects and the memory reduction is useful) 205 206 """ 207 __slots__ = [] 208
209 - def __getattr__(self, name):
210 if name not in self.GetAllSlots(): 211 raise AttributeError("Invalid object attribute %s.%s" % 212 (type(self).__name__, name)) 213 return None
214
215 - def __setstate__(self, state):
216 slots = self.GetAllSlots() 217 for name in state: 218 if name in slots: 219 setattr(self, name, state[name])
220
221 - def Validate(self):
222 """Validates the slots. 223 224 This method returns L{None} if the validation succeeds, or raises 225 an exception otherwise. 226 227 This method must be implemented by the child classes. 228 229 @rtype: NoneType 230 @return: L{None}, if the validation succeeds 231 232 @raise Exception: validation fails 233 234 """
235
236 - def ToDict(self, _with_private=False):
237 """Convert to a dict holding only standard python types. 238 239 The generic routine just dumps all of this object's attributes in 240 a dict. It does not work if the class has children who are 241 ConfigObjects themselves (e.g. the nics list in an Instance), in 242 which case the object should subclass the function in order to 243 make sure all objects returned are only standard python types. 244 245 Private fields can be included or not with the _with_private switch. 246 The actual implementation of this switch is left for those subclassses 247 with private fields to implement. 248 249 @type _with_private: bool 250 @param _with_private: if True, the object will leak its private fields in 251 the dictionary representation. If False, the values 252 will be replaced with None. 253 254 """ 255 result = {} 256 for name in self.GetAllSlots(): 257 value = getattr(self, name, None) 258 if value is not None: 259 result[name] = value 260 return result
261 262 __getstate__ = ToDict 263 264 @classmethod
265 - def FromDict(cls, val):
266 """Create an object from a dictionary. 267 268 This generic routine takes a dict, instantiates a new instance of 269 the given class, and sets attributes based on the dict content. 270 271 As for `ToDict`, this does not work if the class has children 272 who are ConfigObjects themselves (e.g. the nics list in an 273 Instance), in which case the object should subclass the function 274 and alter the objects. 275 276 """ 277 if not isinstance(val, dict): 278 raise errors.ConfigurationError("Invalid object passed to FromDict:" 279 " expected dict, got %s" % type(val)) 280 val_str = dict([(str(k), v) for k, v in val.iteritems()]) 281 obj = cls(**val_str) # pylint: disable=W0142 282 return obj
283
284 - def Copy(self):
285 """Makes a deep copy of the current object and its children. 286 287 """ 288 dict_form = self.ToDict() 289 clone_obj = self.__class__.FromDict(dict_form) 290 return clone_obj
291
292 - def __repr__(self):
293 """Implement __repr__ for ConfigObjects.""" 294 return repr(self.ToDict())
295
296 - def __eq__(self, other):
297 """Implement __eq__ for ConfigObjects.""" 298 return isinstance(other, self.__class__) and self.ToDict() == other.ToDict()
299
300 - def UpgradeConfig(self):
301 """Fill defaults for missing configuration values. 302 303 This method will be called at configuration load time, and its 304 implementation will be object dependent. 305 306 """ 307 pass
308
309 310 -class TaggableObject(ConfigObject):
311 """An generic class supporting tags. 312 313 """ 314 __slots__ = ["tags"] 315 VALID_TAG_RE = re.compile(r"^[\w.+*/:@-]+$") 316 317 @classmethod
318 - def ValidateTag(cls, tag):
319 """Check if a tag is valid. 320 321 If the tag is invalid, an errors.TagError will be raised. The 322 function has no return value. 323 324 """ 325 if not isinstance(tag, basestring): 326 raise errors.TagError("Invalid tag type (not a string)") 327 if len(tag) > constants.MAX_TAG_LEN: 328 raise errors.TagError("Tag too long (>%d characters)" % 329 constants.MAX_TAG_LEN) 330 if not tag: 331 raise errors.TagError("Tags cannot be empty") 332 if not cls.VALID_TAG_RE.match(tag): 333 raise errors.TagError("Tag contains invalid characters")
334
335 - def GetTags(self):
336 """Return the tags list. 337 338 """ 339 tags = getattr(self, "tags", None) 340 if tags is None: 341 tags = self.tags = set() 342 return tags
343
344 - def AddTag(self, tag):
345 """Add a new tag. 346 347 """ 348 self.ValidateTag(tag) 349 tags = self.GetTags() 350 if len(tags) >= constants.MAX_TAGS_PER_OBJ: 351 raise errors.TagError("Too many tags") 352 self.GetTags().add(tag)
353
354 - def RemoveTag(self, tag):
355 """Remove a tag. 356 357 """ 358 self.ValidateTag(tag) 359 tags = self.GetTags() 360 try: 361 tags.remove(tag) 362 except KeyError: 363 raise errors.TagError("Tag not found")
364
365 - def ToDict(self, _with_private=False):
366 """Taggable-object-specific conversion to standard python types. 367 368 This replaces the tags set with a list. 369 370 """ 371 bo = super(TaggableObject, self).ToDict(_with_private=_with_private) 372 373 tags = bo.get("tags", None) 374 if isinstance(tags, set): 375 bo["tags"] = list(tags) 376 return bo
377 378 @classmethod
379 - def FromDict(cls, val):
380 """Custom function for instances. 381 382 """ 383 obj = super(TaggableObject, cls).FromDict(val) 384 if hasattr(obj, "tags") and isinstance(obj.tags, list): 385 obj.tags = set(obj.tags) 386 return obj
387
388 389 -class MasterNetworkParameters(ConfigObject):
390 """Network configuration parameters for the master 391 392 @ivar uuid: master nodes UUID 393 @ivar ip: master IP 394 @ivar netmask: master netmask 395 @ivar netdev: master network device 396 @ivar ip_family: master IP family 397 398 """ 399 __slots__ = [ 400 "uuid", 401 "ip", 402 "netmask", 403 "netdev", 404 "ip_family", 405 ]
406
407 408 -class ConfigData(ConfigObject):
409 """Top-level config object.""" 410 __slots__ = [ 411 "version", 412 "cluster", 413 "nodes", 414 "nodegroups", 415 "instances", 416 "networks", 417 "disks", 418 "filters", 419 "serial_no", 420 ] + _TIMESTAMPS 421
422 - def ToDict(self, _with_private=False):
423 """Custom function for top-level config data. 424 425 This just replaces the list of nodes, instances, nodegroups, 426 networks, disks and the cluster with standard python types. 427 428 """ 429 mydict = super(ConfigData, self).ToDict(_with_private=_with_private) 430 mydict["cluster"] = mydict["cluster"].ToDict() 431 for key in ("nodes", "instances", "nodegroups", "networks", "disks", 432 "filters"): 433 mydict[key] = outils.ContainerToDicts(mydict[key]) 434 435 return mydict
436 437 @classmethod
438 - def FromDict(cls, val):
439 """Custom function for top-level config data 440 441 """ 442 obj = super(ConfigData, cls).FromDict(val) 443 obj.cluster = Cluster.FromDict(obj.cluster) 444 obj.nodes = outils.ContainerFromDicts(obj.nodes, dict, Node) 445 obj.instances = \ 446 outils.ContainerFromDicts(obj.instances, dict, Instance) 447 obj.nodegroups = \ 448 outils.ContainerFromDicts(obj.nodegroups, dict, NodeGroup) 449 obj.networks = outils.ContainerFromDicts(obj.networks, dict, Network) 450 obj.disks = outils.ContainerFromDicts(obj.disks, dict, Disk) 451 obj.filters = outils.ContainerFromDicts(obj.filters, dict, Filter) 452 return obj
453
454 - def DisksOfType(self, dev_type):
455 """Check if in there is at disk of the given type in the configuration. 456 457 @type dev_type: L{constants.DTS_BLOCK} 458 @param dev_type: the type to look for 459 @rtype: list of disks 460 @return: all disks of the dev_type 461 462 """ 463 464 return [disk for disk in self.disks.values() 465 if disk.IsBasedOnDiskType(dev_type)]
466
467 - def UpgradeConfig(self):
468 """Fill defaults for missing configuration values. 469 470 """ 471 self.cluster.UpgradeConfig() 472 for node in self.nodes.values(): 473 node.UpgradeConfig() 474 for instance in self.instances.values(): 475 instance.UpgradeConfig() 476 self._UpgradeEnabledDiskTemplates() 477 if self.nodegroups is None: 478 self.nodegroups = {} 479 for nodegroup in self.nodegroups.values(): 480 nodegroup.UpgradeConfig() 481 InstancePolicy.UpgradeDiskTemplates( 482 nodegroup.ipolicy, self.cluster.enabled_disk_templates) 483 if self.cluster.drbd_usermode_helper is None: 484 if self.cluster.IsDiskTemplateEnabled(constants.DT_DRBD8): 485 self.cluster.drbd_usermode_helper = constants.DEFAULT_DRBD_HELPER 486 if self.networks is None: 487 self.networks = {} 488 for network in self.networks.values(): 489 network.UpgradeConfig() 490 for disk in self.disks.values(): 491 disk.UpgradeConfig() 492 if self.filters is None: 493 self.filters = {}
494
496 """Upgrade the cluster's enabled disk templates by inspecting the currently 497 enabled and/or used disk templates. 498 499 """ 500 if not self.cluster.enabled_disk_templates: 501 template_set = \ 502 set([d.dev_type for d in self.disks.values()]) 503 if any(not inst.disks for inst in self.instances.values()): 504 template_set.add(constants.DT_DISKLESS) 505 # Add drbd and plain, if lvm is enabled (by specifying a volume group) 506 if self.cluster.volume_group_name: 507 template_set.add(constants.DT_DRBD8) 508 template_set.add(constants.DT_PLAIN) 509 # Set enabled_disk_templates to the inferred disk templates. Order them 510 # according to a preference list that is based on Ganeti's history of 511 # supported disk templates. 512 self.cluster.enabled_disk_templates = [] 513 for preferred_template in constants.DISK_TEMPLATE_PREFERENCE: 514 if preferred_template in template_set: 515 self.cluster.enabled_disk_templates.append(preferred_template) 516 template_set.remove(preferred_template) 517 self.cluster.enabled_disk_templates.extend(list(template_set)) 518 InstancePolicy.UpgradeDiskTemplates( 519 self.cluster.ipolicy, self.cluster.enabled_disk_templates)
520
521 522 -class NIC(ConfigObject):
523 """Config object representing a network card.""" 524 __slots__ = ["name", "mac", "ip", "network", 525 "nicparams", "netinfo", "pci", "hvinfo"] + _UUID 526 527 @classmethod
528 - def CheckParameterSyntax(cls, nicparams):
529 """Check the given parameters for validity. 530 531 @type nicparams: dict 532 @param nicparams: dictionary with parameter names/value 533 @raise errors.ConfigurationError: when a parameter is not valid 534 535 """ 536 mode = nicparams[constants.NIC_MODE] 537 if (mode not in constants.NIC_VALID_MODES and 538 mode != constants.VALUE_AUTO): 539 raise errors.ConfigurationError("Invalid NIC mode '%s'" % mode) 540 541 if (mode == constants.NIC_MODE_BRIDGED and 542 not nicparams[constants.NIC_LINK]): 543 raise errors.ConfigurationError("Missing bridged NIC link")
544
545 546 -class Filter(ConfigObject):
547 """Config object representing a filter rule.""" 548 __slots__ = ["watermark", "priority", 549 "predicates", "action", "reason_trail"] + _UUID
550
551 552 -class Disk(ConfigObject):
553 """Config object representing a block device.""" 554 __slots__ = [ 555 "forthcoming", 556 "name", 557 "dev_type", 558 "logical_id", 559 "children", 560 "nodes", 561 "iv_name", 562 "size", 563 "mode", 564 "params", 565 "spindles", 566 "pci", 567 "hvinfo", 568 "serial_no", 569 # dynamic_params is special. It depends on the node this instance 570 # is sent to, and should not be persisted. 571 "dynamic_params" 572 ] + _UUID + _TIMESTAMPS 573
574 - def _ComputeAllNodes(self):
575 """Compute the list of all nodes covered by a device and its children.""" 576 def _Helper(nodes, device): 577 """Recursively compute nodes given a top device.""" 578 if device.dev_type in constants.DTS_DRBD: 579 nodes.extend(device.logical_id[:2]) 580 if device.children: 581 for child in device.children: 582 _Helper(nodes, child)
583 584 all_nodes = list() 585 _Helper(all_nodes, self) 586 return tuple(set(all_nodes))
587 588 all_nodes = property(_ComputeAllNodes, None, None, 589 "List of names of all the nodes of a disk") 590
591 - def CreateOnSecondary(self):
592 """Test if this device needs to be created on a secondary node.""" 593 return self.dev_type in (constants.DT_DRBD8, constants.DT_PLAIN)
594
595 - def AssembleOnSecondary(self):
596 """Test if this device needs to be assembled on a secondary node.""" 597 return self.dev_type in (constants.DT_DRBD8, constants.DT_PLAIN)
598
599 - def OpenOnSecondary(self):
600 """Test if this device needs to be opened on a secondary node.""" 601 return self.dev_type in (constants.DT_PLAIN,)
602
603 - def SupportsSnapshots(self):
604 """Test if this device supports snapshots.""" 605 return self.dev_type in constants.DTS_SNAPSHOT_CAPABLE
606
607 - def StaticDevPath(self):
608 """Return the device path if this device type has a static one. 609 610 Some devices (LVM for example) live always at the same /dev/ path, 611 irrespective of their status. For such devices, we return this 612 path, for others we return None. 613 614 @warning: The path returned is not a normalized pathname; callers 615 should check that it is a valid path. 616 617 """ 618 if self.dev_type == constants.DT_PLAIN: 619 return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1]) 620 elif self.dev_type == constants.DT_BLOCK: 621 return self.logical_id[1] 622 elif self.dev_type == constants.DT_RBD: 623 return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1]) 624 return None
625
626 - def ChildrenNeeded(self):
627 """Compute the needed number of children for activation. 628 629 This method will return either -1 (all children) or a positive 630 number denoting the minimum number of children needed for 631 activation (only mirrored devices will usually return >=0). 632 633 Currently, only DRBD8 supports diskless activation (therefore we 634 return 0), for all other we keep the previous semantics and return 635 -1. 636 637 """ 638 if self.dev_type == constants.DT_DRBD8: 639 return 0 640 return -1
641
642 - def IsBasedOnDiskType(self, dev_type):
643 """Check if the disk or its children are based on the given type. 644 645 @type dev_type: L{constants.DTS_BLOCK} 646 @param dev_type: the type to look for 647 @rtype: boolean 648 @return: boolean indicating if a device of the given type was found or not 649 650 """ 651 if self.children: 652 for child in self.children: 653 if child.IsBasedOnDiskType(dev_type): 654 return True 655 return self.dev_type == dev_type
656
657 - def GetNodes(self, node_uuid):
658 """This function returns the nodes this device lives on. 659 660 Given the node on which the parent of the device lives on (or, in 661 case of a top-level device, the primary node of the devices' 662 instance), this function will return a list of nodes on which this 663 devices needs to (or can) be assembled. 664 665 """ 666 if self.dev_type in [constants.DT_PLAIN, constants.DT_FILE, 667 constants.DT_BLOCK, constants.DT_RBD, 668 constants.DT_EXT, constants.DT_SHARED_FILE, 669 constants.DT_GLUSTER]: 670 result = [node_uuid] 671 elif self.dev_type in constants.DTS_DRBD: 672 result = [self.logical_id[0], self.logical_id[1]] 673 if node_uuid not in result: 674 raise errors.ConfigurationError("DRBD device passed unknown node") 675 else: 676 raise errors.ProgrammerError("Unhandled device type %s" % self.dev_type) 677 return result
678
679 - def GetPrimaryNode(self, node_uuid):
680 """This function returns the primary node of the device. 681 682 If the device is not a DRBD device, we still return the node the device 683 lives on. 684 685 """ 686 if self.dev_type in constants.DTS_DRBD: 687 return self.logical_id[0] 688 return node_uuid
689
690 - def ComputeNodeTree(self, parent_node_uuid):
691 """Compute the node/disk tree for this disk and its children. 692 693 This method, given the node on which the parent disk lives, will 694 return the list of all (node UUID, disk) pairs which describe the disk 695 tree in the most compact way. For example, a drbd/lvm stack 696 will be returned as (primary_node, drbd) and (secondary_node, drbd) 697 which represents all the top-level devices on the nodes. 698 699 """ 700 my_nodes = self.GetNodes(parent_node_uuid) 701 result = [(node, self) for node in my_nodes] 702 if not self.children: 703 # leaf device 704 return result 705 for node in my_nodes: 706 for child in self.children: 707 child_result = child.ComputeNodeTree(node) 708 if len(child_result) == 1: 709 # child (and all its descendants) is simple, doesn't split 710 # over multiple hosts, so we don't need to describe it, our 711 # own entry for this node describes it completely 712 continue 713 else: 714 # check if child nodes differ from my nodes; note that 715 # subdisk can differ from the child itself, and be instead 716 # one of its descendants 717 for subnode, subdisk in child_result: 718 if subnode not in my_nodes: 719 result.append((subnode, subdisk)) 720 # otherwise child is under our own node, so we ignore this 721 # entry (but probably the other results in the list will 722 # be different) 723 return result
724
725 - def ComputeGrowth(self, amount):
726 """Compute the per-VG growth requirements. 727 728 This only works for VG-based disks. 729 730 @type amount: integer 731 @param amount: the desired increase in (user-visible) disk space 732 @rtype: dict 733 @return: a dictionary of volume-groups and the required size 734 735 """ 736 if self.dev_type == constants.DT_PLAIN: 737 return {self.logical_id[0]: amount} 738 elif self.dev_type == constants.DT_DRBD8: 739 if self.children: 740 return self.children[0].ComputeGrowth(amount) 741 else: 742 return {} 743 else: 744 # Other disk types do not require VG space 745 return {}
746
747 - def RecordGrow(self, amount):
748 """Update the size of this disk after growth. 749 750 This method recurses over the disks's children and updates their 751 size correspondigly. The method needs to be kept in sync with the 752 actual algorithms from bdev. 753 754 """ 755 if self.dev_type in (constants.DT_PLAIN, constants.DT_FILE, 756 constants.DT_RBD, constants.DT_EXT, 757 constants.DT_SHARED_FILE, constants.DT_GLUSTER): 758 self.size += amount 759 elif self.dev_type == constants.DT_DRBD8: 760 if self.children: 761 self.children[0].RecordGrow(amount) 762 self.size += amount 763 else: 764 raise errors.ProgrammerError("Disk.RecordGrow called for unsupported" 765 " disk type %s" % self.dev_type)
766
767 - def Update(self, size=None, mode=None, spindles=None):
768 """Apply changes to size, spindles and mode. 769 770 """ 771 if self.dev_type == constants.DT_DRBD8: 772 if self.children: 773 self.children[0].Update(size=size, mode=mode) 774 else: 775 assert not self.children 776 777 if size is not None: 778 self.size = size 779 if mode is not None: 780 self.mode = mode 781 if spindles is not None: 782 self.spindles = spindles
783
784 - def UnsetSize(self):
785 """Sets recursively the size to zero for the disk and its children. 786 787 """ 788 if self.children: 789 for child in self.children: 790 child.UnsetSize() 791 self.size = 0
792
793 - def UpdateDynamicDiskParams(self, target_node_uuid, nodes_ip):
794 """Updates the dynamic disk params for the given node. 795 796 This is mainly used for drbd, which needs ip/port configuration. 797 798 Arguments: 799 - target_node_uuid: the node UUID we wish to configure for 800 - nodes_ip: a mapping of node name to ip 801 802 The target_node must exist in nodes_ip, and should be one of the 803 nodes in the logical ID if this device is a DRBD device. 804 805 """ 806 if self.children: 807 for child in self.children: 808 child.UpdateDynamicDiskParams(target_node_uuid, nodes_ip) 809 810 dyn_disk_params = {} 811 if self.logical_id is not None and self.dev_type in constants.DTS_DRBD: 812 pnode_uuid, snode_uuid, _, pminor, sminor, _ = self.logical_id 813 if target_node_uuid not in (pnode_uuid, snode_uuid): 814 # disk object is being sent to neither the primary nor the secondary 815 # node. reset the dynamic parameters, the target node is not 816 # supposed to use them. 817 self.dynamic_params = dyn_disk_params 818 return 819 820 pnode_ip = nodes_ip.get(pnode_uuid, None) 821 snode_ip = nodes_ip.get(snode_uuid, None) 822 if pnode_ip is None or snode_ip is None: 823 raise errors.ConfigurationError("Can't find primary or secondary node" 824 " for %s" % str(self)) 825 if pnode_uuid == target_node_uuid: 826 dyn_disk_params[constants.DDP_LOCAL_IP] = pnode_ip 827 dyn_disk_params[constants.DDP_REMOTE_IP] = snode_ip 828 dyn_disk_params[constants.DDP_LOCAL_MINOR] = pminor 829 dyn_disk_params[constants.DDP_REMOTE_MINOR] = sminor 830 else: # it must be secondary, we tested above 831 dyn_disk_params[constants.DDP_LOCAL_IP] = snode_ip 832 dyn_disk_params[constants.DDP_REMOTE_IP] = pnode_ip 833 dyn_disk_params[constants.DDP_LOCAL_MINOR] = sminor 834 dyn_disk_params[constants.DDP_REMOTE_MINOR] = pminor 835 836 self.dynamic_params = dyn_disk_params
837 838 # pylint: disable=W0221
839 - def ToDict(self, include_dynamic_params=False, 840 _with_private=False):
841 """Disk-specific conversion to standard python types. 842 843 This replaces the children lists of objects with lists of 844 standard python types. 845 846 """ 847 bo = super(Disk, self).ToDict(_with_private=_with_private) 848 if not include_dynamic_params and "dynamic_params" in bo: 849 del bo["dynamic_params"] 850 851 if _with_private and "logical_id" in bo: 852 mutable_id = list(bo["logical_id"]) 853 mutable_id[5] = mutable_id[5].Get() 854 bo["logical_id"] = tuple(mutable_id) 855 856 for attr in ("children",): 857 alist = bo.get(attr, None) 858 if alist: 859 bo[attr] = outils.ContainerToDicts(alist) 860 return bo
861 862 @classmethod
863 - def FromDict(cls, val):
864 """Custom function for Disks 865 866 """ 867 obj = super(Disk, cls).FromDict(val) 868 if obj.children: 869 obj.children = outils.ContainerFromDicts(obj.children, list, Disk) 870 if obj.logical_id and isinstance(obj.logical_id, list): 871 obj.logical_id = tuple(obj.logical_id) 872 if obj.dev_type in constants.DTS_DRBD: 873 # we need a tuple of length six here 874 if len(obj.logical_id) < 6: 875 obj.logical_id += (None,) * (6 - len(obj.logical_id)) 876 # If we do have a tuple of length 6, make the last entry (secret key) 877 # private 878 elif (len(obj.logical_id) == 6 and 879 not isinstance(obj.logical_id[-1], serializer.Private)): 880 obj.logical_id = obj.logical_id[:-1] + \ 881 (serializer.Private(obj.logical_id[-1]),) 882 return obj
883
884 - def __str__(self):
885 """Custom str() formatter for disks. 886 887 """ 888 if self.dev_type == constants.DT_PLAIN: 889 val = "<LogicalVolume(/dev/%s/%s" % self.logical_id 890 elif self.dev_type in constants.DTS_DRBD: 891 node_a, node_b, port, minor_a, minor_b = self.logical_id[:5] 892 val = "<DRBD8(" 893 894 val += ("hosts=%s/%d-%s/%d, port=%s, " % 895 (node_a, minor_a, node_b, minor_b, port)) 896 if self.children and self.children.count(None) == 0: 897 val += "backend=%s, metadev=%s" % (self.children[0], self.children[1]) 898 else: 899 val += "no local storage" 900 else: 901 val = ("<Disk(type=%s, logical_id=%s, children=%s" % 902 (self.dev_type, self.logical_id, self.children)) 903 if self.iv_name is None: 904 val += ", not visible" 905 else: 906 val += ", visible as /dev/%s" % self.iv_name 907 if self.spindles is not None: 908 val += ", spindles=%s" % self.spindles 909 if isinstance(self.size, int): 910 val += ", size=%dm)>" % self.size 911 else: 912 val += ", size='%s')>" % (self.size,) 913 return val
914
915 - def Verify(self):
916 """Checks that this disk is correctly configured. 917 918 """ 919 all_errors = [] 920 if self.mode not in constants.DISK_ACCESS_SET: 921 all_errors.append("Disk access mode '%s' is invalid" % (self.mode, )) 922 return all_errors
923
924 - def UpgradeConfig(self):
925 """Fill defaults for missing configuration values. 926 927 """ 928 if self.children: 929 for child in self.children: 930 child.UpgradeConfig() 931 932 # FIXME: Make this configurable in Ganeti 2.7 933 # Params should be an empty dict that gets filled any time needed 934 # In case of ext template we allow arbitrary params that should not 935 # be overrided during a config reload/upgrade. 936 if not self.params or not isinstance(self.params, dict): 937 self.params = {} 938 939 # add here config upgrade for this disk 940 if self.serial_no is None: 941 self.serial_no = 1 942 if self.mtime is None: 943 self.mtime = time.time() 944 if self.ctime is None: 945 self.ctime = time.time() 946 947 # map of legacy device types (mapping differing LD constants to new 948 # DT constants) 949 LEG_DEV_TYPE_MAP = {"lvm": constants.DT_PLAIN, "drbd8": constants.DT_DRBD8} 950 if self.dev_type in LEG_DEV_TYPE_MAP: 951 self.dev_type = LEG_DEV_TYPE_MAP[self.dev_type]
952 953 @staticmethod
954 - def ComputeLDParams(disk_template, disk_params):
955 """Computes Logical Disk parameters from Disk Template parameters. 956 957 @type disk_template: string 958 @param disk_template: disk template, one of L{constants.DISK_TEMPLATES} 959 @type disk_params: dict 960 @param disk_params: disk template parameters; 961 dict(template_name -> parameters 962 @rtype: list(dict) 963 @return: a list of dicts, one for each node of the disk hierarchy. Each dict 964 contains the LD parameters of the node. The tree is flattened in-order. 965 966 """ 967 if disk_template not in constants.DISK_TEMPLATES: 968 raise errors.ProgrammerError("Unknown disk template %s" % disk_template) 969 970 assert disk_template in disk_params 971 972 result = list() 973 dt_params = disk_params[disk_template] 974 975 if disk_template == constants.DT_DRBD8: 976 result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.DT_DRBD8], { 977 constants.LDP_RESYNC_RATE: dt_params[constants.DRBD_RESYNC_RATE], 978 constants.LDP_BARRIERS: dt_params[constants.DRBD_DISK_BARRIERS], 979 constants.LDP_NO_META_FLUSH: dt_params[constants.DRBD_META_BARRIERS], 980 constants.LDP_DEFAULT_METAVG: dt_params[constants.DRBD_DEFAULT_METAVG], 981 constants.LDP_DISK_CUSTOM: dt_params[constants.DRBD_DISK_CUSTOM], 982 constants.LDP_NET_CUSTOM: dt_params[constants.DRBD_NET_CUSTOM], 983 constants.LDP_PROTOCOL: dt_params[constants.DRBD_PROTOCOL], 984 constants.LDP_DYNAMIC_RESYNC: dt_params[constants.DRBD_DYNAMIC_RESYNC], 985 constants.LDP_PLAN_AHEAD: dt_params[constants.DRBD_PLAN_AHEAD], 986 constants.LDP_FILL_TARGET: dt_params[constants.DRBD_FILL_TARGET], 987 constants.LDP_DELAY_TARGET: dt_params[constants.DRBD_DELAY_TARGET], 988 constants.LDP_MAX_RATE: dt_params[constants.DRBD_MAX_RATE], 989 constants.LDP_MIN_RATE: dt_params[constants.DRBD_MIN_RATE], 990 })) 991 992 # data LV 993 result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.DT_PLAIN], { 994 constants.LDP_STRIPES: dt_params[constants.DRBD_DATA_STRIPES], 995 })) 996 997 # metadata LV 998 result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.DT_PLAIN], { 999 constants.LDP_STRIPES: dt_params[constants.DRBD_META_STRIPES], 1000 })) 1001 1002 else: 1003 defaults = constants.DISK_LD_DEFAULTS[disk_template] 1004 values = {} 1005 for field in defaults: 1006 values[field] = dt_params[field] 1007 result.append(FillDict(defaults, values)) 1008 1009 return result
1010
1011 1012 -class InstancePolicy(ConfigObject):
1013 """Config object representing instance policy limits dictionary. 1014 1015 Note that this object is not actually used in the config, it's just 1016 used as a placeholder for a few functions. 1017 1018 """ 1019 @classmethod
1020 - def UpgradeDiskTemplates(cls, ipolicy, enabled_disk_templates):
1021 """Upgrades the ipolicy configuration.""" 1022 if constants.IPOLICY_DTS in ipolicy: 1023 if not set(ipolicy[constants.IPOLICY_DTS]).issubset( 1024 set(enabled_disk_templates)): 1025 ipolicy[constants.IPOLICY_DTS] = list( 1026 set(ipolicy[constants.IPOLICY_DTS]) & set(enabled_disk_templates))
1027 1028 @classmethod
1029 - def CheckParameterSyntax(cls, ipolicy, check_std):
1030 """ Check the instance policy for validity. 1031 1032 @type ipolicy: dict 1033 @param ipolicy: dictionary with min/max/std specs and policies 1034 @type check_std: bool 1035 @param check_std: Whether to check std value or just assume compliance 1036 @raise errors.ConfigurationError: when the policy is not legal 1037 1038 """ 1039 InstancePolicy.CheckISpecSyntax(ipolicy, check_std) 1040 if constants.IPOLICY_DTS in ipolicy: 1041 InstancePolicy.CheckDiskTemplates(ipolicy[constants.IPOLICY_DTS]) 1042 for key in constants.IPOLICY_PARAMETERS: 1043 if key in ipolicy: 1044 InstancePolicy.CheckParameter(key, ipolicy[key]) 1045 wrong_keys = frozenset(ipolicy.keys()) - constants.IPOLICY_ALL_KEYS 1046 if wrong_keys: 1047 raise errors.ConfigurationError("Invalid keys in ipolicy: %s" % 1048 utils.CommaJoin(wrong_keys))
1049 1050 @classmethod
1051 - def _CheckIncompleteSpec(cls, spec, keyname):
1052 missing_params = constants.ISPECS_PARAMETERS - frozenset(spec.keys()) 1053 if missing_params: 1054 msg = ("Missing instance specs parameters for %s: %s" % 1055 (keyname, utils.CommaJoin(missing_params))) 1056 raise errors.ConfigurationError(msg)
1057 1058 @classmethod
1059 - def CheckISpecSyntax(cls, ipolicy, check_std):
1060 """Check the instance policy specs for validity. 1061 1062 @type ipolicy: dict 1063 @param ipolicy: dictionary with min/max/std specs 1064 @type check_std: bool 1065 @param check_std: Whether to check std value or just assume compliance 1066 @raise errors.ConfigurationError: when specs are not valid 1067 1068 """ 1069 if constants.ISPECS_MINMAX not in ipolicy: 1070 # Nothing to check 1071 return 1072 1073 if check_std and constants.ISPECS_STD not in ipolicy: 1074 msg = "Missing key in ipolicy: %s" % constants.ISPECS_STD 1075 raise errors.ConfigurationError(msg) 1076 stdspec = ipolicy.get(constants.ISPECS_STD) 1077 if check_std: 1078 InstancePolicy._CheckIncompleteSpec(stdspec, constants.ISPECS_STD) 1079 1080 if not ipolicy[constants.ISPECS_MINMAX]: 1081 raise errors.ConfigurationError("Empty minmax specifications") 1082 std_is_good = False 1083 for minmaxspecs in ipolicy[constants.ISPECS_MINMAX]: 1084 missing = constants.ISPECS_MINMAX_KEYS - frozenset(minmaxspecs.keys()) 1085 if missing: 1086 msg = "Missing instance specification: %s" % utils.CommaJoin(missing) 1087 raise errors.ConfigurationError(msg) 1088 for (key, spec) in minmaxspecs.items(): 1089 InstancePolicy._CheckIncompleteSpec(spec, key) 1090 1091 spec_std_ok = True 1092 for param in constants.ISPECS_PARAMETERS: 1093 par_std_ok = InstancePolicy._CheckISpecParamSyntax(minmaxspecs, stdspec, 1094 param, check_std) 1095 spec_std_ok = spec_std_ok and par_std_ok 1096 std_is_good = std_is_good or spec_std_ok 1097 if not std_is_good: 1098 raise errors.ConfigurationError("Invalid std specifications")
1099 1100 @classmethod
1101 - def _CheckISpecParamSyntax(cls, minmaxspecs, stdspec, name, check_std):
1102 """Check the instance policy specs for validity on a given key. 1103 1104 We check if the instance specs makes sense for a given key, that is 1105 if minmaxspecs[min][name] <= stdspec[name] <= minmaxspec[max][name]. 1106 1107 @type minmaxspecs: dict 1108 @param minmaxspecs: dictionary with min and max instance spec 1109 @type stdspec: dict 1110 @param stdspec: dictionary with standard instance spec 1111 @type name: string 1112 @param name: what are the limits for 1113 @type check_std: bool 1114 @param check_std: Whether to check std value or just assume compliance 1115 @rtype: bool 1116 @return: C{True} when specs are valid, C{False} when standard spec for the 1117 given name is not valid 1118 @raise errors.ConfigurationError: when min/max specs for the given name 1119 are not valid 1120 1121 """ 1122 minspec = minmaxspecs[constants.ISPECS_MIN] 1123 maxspec = minmaxspecs[constants.ISPECS_MAX] 1124 min_v = minspec[name] 1125 max_v = maxspec[name] 1126 1127 if min_v > max_v: 1128 err = ("Invalid specification of min/max values for %s: %s/%s" % 1129 (name, min_v, max_v)) 1130 raise errors.ConfigurationError(err) 1131 elif check_std: 1132 std_v = stdspec.get(name, min_v) 1133 return std_v >= min_v and std_v <= max_v 1134 else: 1135 return True
1136 1137 @classmethod
1138 - def CheckDiskTemplates(cls, disk_templates):
1139 """Checks the disk templates for validity. 1140 1141 """ 1142 if not disk_templates: 1143 raise errors.ConfigurationError("Instance policy must contain" + 1144 " at least one disk template") 1145 wrong = frozenset(disk_templates).difference(constants.DISK_TEMPLATES) 1146 if wrong: 1147 raise errors.ConfigurationError("Invalid disk template(s) %s" % 1148 utils.CommaJoin(wrong))
1149 1150 @classmethod
1151 - def CheckParameter(cls, key, value):
1152 """Checks a parameter. 1153 1154 Currently we expect all parameters to be float values. 1155 1156 """ 1157 try: 1158 float(value) 1159 except (TypeError, ValueError), err: 1160 raise errors.ConfigurationError("Invalid value for key" " '%s':" 1161 " '%s', error: %s" % (key, value, err))
1162
1163 1164 -def GetOSImage(osparams):
1165 """Gets the OS image value from the OS parameters. 1166 1167 @type osparams: L{dict} or NoneType 1168 @param osparams: OS parameters or None 1169 1170 @rtype: string or NoneType 1171 @return: 1172 value of OS image contained in OS parameters, or None if the OS 1173 parameters are None or the OS parameters do not contain an OS 1174 image 1175 1176 """ 1177 if osparams is None: 1178 return None 1179 else: 1180 return osparams.get("os-image", None)
1181
1182 1183 -def PutOSImage(osparams, os_image):
1184 """Update OS image value in the OS parameters 1185 1186 @type osparams: L{dict} 1187 @param osparams: OS parameters 1188 1189 @type os_image: string 1190 @param os_image: OS image 1191 1192 @rtype: NoneType 1193 @return: None 1194 1195 """ 1196 osparams["os-image"] = os_image
1197
1198 1199 -class Instance(TaggableObject):
1200 """Config object representing an instance.""" 1201 __slots__ = [ 1202 "forthcoming", 1203 "name", 1204 "primary_node", 1205 "secondary_nodes", 1206 "os", 1207 "hypervisor", 1208 "hvparams", 1209 "beparams", 1210 "osparams", 1211 "osparams_private", 1212 "admin_state", 1213 "admin_state_source", 1214 "nics", 1215 "disks", 1216 "disks_info", 1217 "disk_template", 1218 "disks_active", 1219 "network_port", 1220 "serial_no", 1221 ] + _TIMESTAMPS + _UUID 1222
1223 - def FindDisk(self, idx):
1224 """Find a disk given having a specified index. 1225 1226 This is just a wrapper that does validation of the index. 1227 1228 @type idx: int 1229 @param idx: the disk index 1230 @rtype: string 1231 @return: the corresponding disk's uuid 1232 @raise errors.OpPrereqError: when the given index is not valid 1233 1234 """ 1235 try: 1236 idx = int(idx) 1237 return self.disks[idx] 1238 except (TypeError, ValueError), err: 1239 raise errors.OpPrereqError("Invalid disk index: '%s'" % str(err), 1240 errors.ECODE_INVAL) 1241 except IndexError: 1242 raise errors.OpPrereqError("Invalid disk index: %d (instace has disks" 1243 " 0 to %d" % (idx, len(self.disks) - 1), 1244 errors.ECODE_INVAL)
1245
1246 - def ToDict(self, _with_private=False):
1247 """Instance-specific conversion to standard python types. 1248 1249 This replaces the children lists of objects with lists of standard 1250 python types. 1251 1252 """ 1253 bo = super(Instance, self).ToDict(_with_private=_with_private) 1254 1255 if _with_private: 1256 bo["osparams_private"] = self.osparams_private.Unprivate() 1257 1258 for attr in "nics", : 1259 alist = bo.get(attr, None) 1260 if alist: 1261 nlist = outils.ContainerToDicts(alist) 1262 else: 1263 nlist = [] 1264 bo[attr] = nlist 1265 1266 if 'disk_template' in bo: 1267 del bo['disk_template'] 1268 1269 return bo
1270 1271 @classmethod
1272 - def FromDict(cls, val):
1273 """Custom function for instances. 1274 1275 """ 1276 if "admin_state" not in val: 1277 if val.get("admin_up", False): 1278 val["admin_state"] = constants.ADMINST_UP 1279 else: 1280 val["admin_state"] = constants.ADMINST_DOWN 1281 if "admin_up" in val: 1282 del val["admin_up"] 1283 obj = super(Instance, cls).FromDict(val) 1284 obj.nics = outils.ContainerFromDicts(obj.nics, list, NIC) 1285 1286 # attribute 'disks_info' is only present when deserializing from a RPC 1287 # call in the backend 1288 disks_info = getattr(obj, "disks_info", None) 1289 if disks_info: 1290 obj.disks_info = outils.ContainerFromDicts(disks_info, list, Disk) 1291 1292 return obj
1293
1294 - def UpgradeConfig(self):
1295 """Fill defaults for missing configuration values. 1296 1297 """ 1298 if self.admin_state_source is None: 1299 self.admin_state_source = constants.ADMIN_SOURCE 1300 for nic in self.nics: 1301 nic.UpgradeConfig() 1302 if self.disks is None: 1303 self.disks = [] 1304 if self.hvparams: 1305 for key in constants.HVC_GLOBALS: 1306 try: 1307 del self.hvparams[key] 1308 except KeyError: 1309 pass 1310 if self.osparams is None: 1311 self.osparams = {} 1312 if self.osparams_private is None: 1313 self.osparams_private = serializer.PrivateDict() 1314 UpgradeBeParams(self.beparams) 1315 if self.disks_active is None: 1316 self.disks_active = self.admin_state == constants.ADMINST_UP
1317
1318 1319 -class OS(ConfigObject):
1320 """Config object representing an operating system. 1321 1322 @type supported_parameters: list 1323 @ivar supported_parameters: a list of tuples, name and description, 1324 containing the supported parameters by this OS 1325 1326 @type VARIANT_DELIM: string 1327 @cvar VARIANT_DELIM: the variant delimiter 1328 1329 """ 1330 __slots__ = [ 1331 "name", 1332 "path", 1333 "api_versions", 1334 "create_script", 1335 "create_script_untrusted", 1336 "export_script", 1337 "import_script", 1338 "rename_script", 1339 "verify_script", 1340 "supported_variants", 1341 "supported_parameters", 1342 ] 1343 1344 VARIANT_DELIM = "+" 1345 1346 @classmethod
1347 - def SplitNameVariant(cls, name):
1348 """Splits the name into the proper name and variant. 1349 1350 @param name: the OS (unprocessed) name 1351 @rtype: list 1352 @return: a list of two elements; if the original name didn't 1353 contain a variant, it's returned as an empty string 1354 1355 """ 1356 nv = name.split(cls.VARIANT_DELIM, 1) 1357 if len(nv) == 1: 1358 nv.append("") 1359 return nv
1360 1361 @classmethod
1362 - def GetName(cls, name):
1363 """Returns the proper name of the os (without the variant). 1364 1365 @param name: the OS (unprocessed) name 1366 1367 """ 1368 return cls.SplitNameVariant(name)[0]
1369 1370 @classmethod
1371 - def GetVariant(cls, name):
1372 """Returns the variant the os (without the base name). 1373 1374 @param name: the OS (unprocessed) name 1375 1376 """ 1377 return cls.SplitNameVariant(name)[1]
1378
1379 - def IsTrusted(self):
1380 """Returns whether this OS is trusted. 1381 1382 @rtype: bool 1383 @return: L{True} if this OS is trusted, L{False} otherwise 1384 1385 """ 1386 return not self.create_script_untrusted
1387
1388 1389 -class ExtStorage(ConfigObject):
1390 """Config object representing an External Storage Provider. 1391 1392 """ 1393 __slots__ = [ 1394 "name", 1395 "path", 1396 "create_script", 1397 "remove_script", 1398 "grow_script", 1399 "attach_script", 1400 "detach_script", 1401 "setinfo_script", 1402 "verify_script", 1403 "snapshot_script", 1404 "open_script", 1405 "close_script", 1406 "supported_parameters", 1407 ]
1408
1409 1410 -class NodeHvState(ConfigObject):
1411 """Hypvervisor state on a node. 1412 1413 @ivar mem_total: Total amount of memory 1414 @ivar mem_node: Memory used by, or reserved for, the node itself (not always 1415 available) 1416 @ivar mem_hv: Memory used by hypervisor or lost due to instance allocation 1417 rounding 1418 @ivar mem_inst: Memory used by instances living on node 1419 @ivar cpu_total: Total node CPU core count 1420 @ivar cpu_node: Number of CPU cores reserved for the node itself 1421 1422 """ 1423 __slots__ = [ 1424 "mem_total", 1425 "mem_node", 1426 "mem_hv", 1427 "mem_inst", 1428 "cpu_total", 1429 "cpu_node", 1430 ] + _TIMESTAMPS
1431
1432 1433 -class NodeDiskState(ConfigObject):
1434 """Disk state on a node. 1435 1436 """ 1437 __slots__ = [ 1438 "total", 1439 "reserved", 1440 "overhead", 1441 ] + _TIMESTAMPS
1442
1443 1444 -class Node(TaggableObject):
1445 """Config object representing a node. 1446 1447 @ivar hv_state: Hypervisor state (e.g. number of CPUs) 1448 @ivar hv_state_static: Hypervisor state overriden by user 1449 @ivar disk_state: Disk state (e.g. free space) 1450 @ivar disk_state_static: Disk state overriden by user 1451 1452 """ 1453 __slots__ = [ 1454 "name", 1455 "primary_ip", 1456 "secondary_ip", 1457 "serial_no", 1458 "master_candidate", 1459 "offline", 1460 "drained", 1461 "group", 1462 "master_capable", 1463 "vm_capable", 1464 "ndparams", 1465 "powered", 1466 "hv_state", 1467 "hv_state_static", 1468 "disk_state", 1469 "disk_state_static", 1470 ] + _TIMESTAMPS + _UUID 1471
1472 - def UpgradeConfig(self):
1473 """Fill defaults for missing configuration values. 1474 1475 """ 1476 # pylint: disable=E0203 1477 # because these are "defined" via slots, not manually 1478 if self.master_capable is None: 1479 self.master_capable = True 1480 1481 if self.vm_capable is None: 1482 self.vm_capable = True 1483 1484 if self.ndparams is None: 1485 self.ndparams = {} 1486 # And remove any global parameter 1487 for key in constants.NDC_GLOBALS: 1488 if key in self.ndparams: 1489 logging.warning("Ignoring %s node parameter for node %s", 1490 key, self.name) 1491 del self.ndparams[key] 1492 1493 if self.powered is None: 1494 self.powered = True
1495
1496 - def ToDict(self, _with_private=False):
1497 """Custom function for serializing. 1498 1499 """ 1500 data = super(Node, self).ToDict(_with_private=_with_private) 1501 1502 hv_state = data.get("hv_state", None) 1503 if hv_state is not None: 1504 data["hv_state"] = outils.ContainerToDicts(hv_state) 1505 1506 disk_state = data.get("disk_state", None) 1507 if disk_state is not None: 1508 data["disk_state"] = \ 1509 dict((key, outils.ContainerToDicts(value)) 1510 for (key, value) in disk_state.items()) 1511 1512 return data
1513 1514 @classmethod
1515 - def FromDict(cls, val):
1516 """Custom function for deserializing. 1517 1518 """ 1519 obj = super(Node, cls).FromDict(val) 1520 1521 if obj.hv_state is not None: 1522 obj.hv_state = \ 1523 outils.ContainerFromDicts(obj.hv_state, dict, NodeHvState) 1524 1525 if obj.disk_state is not None: 1526 obj.disk_state = \ 1527 dict((key, outils.ContainerFromDicts(value, dict, NodeDiskState)) 1528 for (key, value) in obj.disk_state.items()) 1529 1530 return obj
1531
1532 1533 -class NodeGroup(TaggableObject):
1534 """Config object representing a node group.""" 1535 __slots__ = [ 1536 "name", 1537 "members", 1538 "ndparams", 1539 "diskparams", 1540 "ipolicy", 1541 "serial_no", 1542 "hv_state_static", 1543 "disk_state_static", 1544 "alloc_policy", 1545 "networks", 1546 ] + _TIMESTAMPS + _UUID 1547
1548 - def ToDict(self, _with_private=False):
1549 """Custom function for nodegroup. 1550 1551 This discards the members object, which gets recalculated and is only kept 1552 in memory. 1553 1554 """ 1555 mydict = super(NodeGroup, self).ToDict(_with_private=_with_private) 1556 del mydict["members"] 1557 return mydict
1558 1559 @classmethod
1560 - def FromDict(cls, val):
1561 """Custom function for nodegroup. 1562 1563 The members slot is initialized to an empty list, upon deserialization. 1564 1565 """ 1566 obj = super(NodeGroup, cls).FromDict(val) 1567 obj.members = [] 1568 return obj
1569
1570 - def UpgradeConfig(self):
1571 """Fill defaults for missing configuration values. 1572 1573 """ 1574 if self.ndparams is None: 1575 self.ndparams = {} 1576 1577 if self.serial_no is None: 1578 self.serial_no = 1 1579 1580 if self.alloc_policy is None: 1581 self.alloc_policy = constants.ALLOC_POLICY_PREFERRED 1582 1583 # We only update mtime, and not ctime, since we would not be able 1584 # to provide a correct value for creation time. 1585 if self.mtime is None: 1586 self.mtime = time.time() 1587 1588 if self.diskparams is None: 1589 self.diskparams = {} 1590 if self.ipolicy is None: 1591 self.ipolicy = MakeEmptyIPolicy() 1592 1593 if self.networks is None: 1594 self.networks = {} 1595 1596 for network, netparams in self.networks.items(): 1597 self.networks[network] = FillDict(constants.NICC_DEFAULTS, netparams)
1598
1599 - def FillND(self, node):
1600 """Return filled out ndparams for L{objects.Node} 1601 1602 @type node: L{objects.Node} 1603 @param node: A Node object to fill 1604 @return a copy of the node's ndparams with defaults filled 1605 1606 """ 1607 return self.SimpleFillND(node.ndparams)
1608
1609 - def SimpleFillND(self, ndparams):
1610 """Fill a given ndparams dict with defaults. 1611 1612 @type ndparams: dict 1613 @param ndparams: the dict to fill 1614 @rtype: dict 1615 @return: a copy of the passed in ndparams with missing keys filled 1616 from the node group defaults 1617 1618 """ 1619 return FillDict(self.ndparams, ndparams)
1620
1621 1622 -class Cluster(TaggableObject):
1623 """Config object representing the cluster.""" 1624 __slots__ = [ 1625 "serial_no", 1626 "rsahostkeypub", 1627 "dsahostkeypub", 1628 "highest_used_port", 1629 "tcpudp_port_pool", 1630 "mac_prefix", 1631 "volume_group_name", 1632 "reserved_lvs", 1633 "drbd_usermode_helper", 1634 "default_bridge", 1635 "default_hypervisor", 1636 "master_node", 1637 "master_ip", 1638 "master_netdev", 1639 "master_netmask", 1640 "use_external_mip_script", 1641 "cluster_name", 1642 "file_storage_dir", 1643 "shared_file_storage_dir", 1644 "gluster_storage_dir", 1645 "enabled_hypervisors", 1646 "hvparams", 1647 "ipolicy", 1648 "os_hvp", 1649 "beparams", 1650 "osparams", 1651 "osparams_private_cluster", 1652 "nicparams", 1653 "ndparams", 1654 "diskparams", 1655 "candidate_pool_size", 1656 "modify_etc_hosts", 1657 "modify_ssh_setup", 1658 "maintain_node_health", 1659 "uid_pool", 1660 "default_iallocator", 1661 "default_iallocator_params", 1662 "hidden_os", 1663 "blacklisted_os", 1664 "primary_ip_family", 1665 "prealloc_wipe_disks", 1666 "hv_state_static", 1667 "disk_state_static", 1668 "enabled_disk_templates", 1669 "candidate_certs", 1670 "max_running_jobs", 1671 "max_tracked_jobs", 1672 "install_image", 1673 "instance_communication_network", 1674 "zeroing_image", 1675 "compression_tools", 1676 "enabled_user_shutdown", 1677 "data_collectors", 1678 "ssh_key_type", 1679 "ssh_key_bits", 1680 ] + _TIMESTAMPS + _UUID 1681
1682 - def UpgradeConfig(self):
1683 """Fill defaults for missing configuration values. 1684 1685 """ 1686 # pylint: disable=E0203 1687 # because these are "defined" via slots, not manually 1688 if self.hvparams is None: 1689 self.hvparams = constants.HVC_DEFAULTS 1690 else: 1691 for hypervisor in constants.HYPER_TYPES: 1692 try: 1693 existing_params = self.hvparams[hypervisor] 1694 except KeyError: 1695 existing_params = {} 1696 self.hvparams[hypervisor] = FillDict( 1697 constants.HVC_DEFAULTS[hypervisor], existing_params) 1698 1699 if self.os_hvp is None: 1700 self.os_hvp = {} 1701 1702 if self.osparams is None: 1703 self.osparams = {} 1704 # osparams_private_cluster added in 2.12 1705 if self.osparams_private_cluster is None: 1706 self.osparams_private_cluster = {} 1707 1708 self.ndparams = UpgradeNDParams(self.ndparams) 1709 1710 self.beparams = UpgradeGroupedParams(self.beparams, 1711 constants.BEC_DEFAULTS) 1712 for beparams_group in self.beparams: 1713 UpgradeBeParams(self.beparams[beparams_group]) 1714 1715 migrate_default_bridge = not self.nicparams 1716 self.nicparams = UpgradeGroupedParams(self.nicparams, 1717 constants.NICC_DEFAULTS) 1718 if migrate_default_bridge: 1719 self.nicparams[constants.PP_DEFAULT][constants.NIC_LINK] = \ 1720 self.default_bridge 1721 1722 if self.modify_etc_hosts is None: 1723 self.modify_etc_hosts = True 1724 1725 if self.modify_ssh_setup is None: 1726 self.modify_ssh_setup = True 1727 1728 # default_bridge is no longer used in 2.1. The slot is left there to 1729 # support auto-upgrading. It can be removed once we decide to deprecate 1730 # upgrading straight from 2.0. 1731 if self.default_bridge is not None: 1732 self.default_bridge = None 1733 1734 # default_hypervisor is just the first enabled one in 2.1. This slot and 1735 # code can be removed once upgrading straight from 2.0 is deprecated. 1736 if self.default_hypervisor is not None: 1737 self.enabled_hypervisors = ([self.default_hypervisor] + 1738 [hvname for hvname in self.enabled_hypervisors 1739 if hvname != self.default_hypervisor]) 1740 self.default_hypervisor = None 1741 1742 # maintain_node_health added after 2.1.1 1743 if self.maintain_node_health is None: 1744 self.maintain_node_health = False 1745 1746 if self.uid_pool is None: 1747 self.uid_pool = [] 1748 1749 if self.default_iallocator is None: 1750 self.default_iallocator = "" 1751 1752 if self.default_iallocator_params is None: 1753 self.default_iallocator_params = {} 1754 1755 # reserved_lvs added before 2.2 1756 if self.reserved_lvs is None: 1757 self.reserved_lvs = [] 1758 1759 # hidden and blacklisted operating systems added before 2.2.1 1760 if self.hidden_os is None: 1761 self.hidden_os = [] 1762 1763 if self.blacklisted_os is None: 1764 self.blacklisted_os = [] 1765 1766 # primary_ip_family added before 2.3 1767 if self.primary_ip_family is None: 1768 self.primary_ip_family = AF_INET 1769 1770 if self.master_netmask is None: 1771 ipcls = netutils.IPAddress.GetClassFromIpFamily(self.primary_ip_family) 1772 self.master_netmask = ipcls.iplen 1773 1774 if self.prealloc_wipe_disks is None: 1775 self.prealloc_wipe_disks = False 1776 1777 # shared_file_storage_dir added before 2.5 1778 if self.shared_file_storage_dir is None: 1779 self.shared_file_storage_dir = "" 1780 1781 # gluster_storage_dir added in 2.11 1782 if self.gluster_storage_dir is None: 1783 self.gluster_storage_dir = "" 1784 1785 if self.use_external_mip_script is None: 1786 self.use_external_mip_script = False 1787 1788 if self.diskparams: 1789 self.diskparams = UpgradeDiskParams(self.diskparams) 1790 else: 1791 self.diskparams = constants.DISK_DT_DEFAULTS.copy() 1792 1793 # instance policy added before 2.6 1794 if self.ipolicy is None: 1795 self.ipolicy = FillIPolicy(constants.IPOLICY_DEFAULTS, {}) 1796 else: 1797 # we can either make sure to upgrade the ipolicy always, or only 1798 # do it in some corner cases (e.g. missing keys); note that this 1799 # will break any removal of keys from the ipolicy dict 1800 wrongkeys = frozenset(self.ipolicy.keys()) - constants.IPOLICY_ALL_KEYS 1801 if wrongkeys: 1802 # These keys would be silently removed by FillIPolicy() 1803 msg = ("Cluster instance policy contains spurious keys: %s" % 1804 utils.CommaJoin(wrongkeys)) 1805 raise errors.ConfigurationError(msg) 1806 self.ipolicy = FillIPolicy(constants.IPOLICY_DEFAULTS, self.ipolicy) 1807 1808 # hv_state_static added in 2.7 1809 if self.hv_state_static is None: 1810 self.hv_state_static = {} 1811 if self.disk_state_static is None: 1812 self.disk_state_static = {} 1813 1814 if self.candidate_certs is None: 1815 self.candidate_certs = {} 1816 1817 if self.max_running_jobs is None: 1818 self.max_running_jobs = constants.LUXID_MAXIMAL_RUNNING_JOBS_DEFAULT 1819 1820 if self.max_tracked_jobs is None: 1821 self.max_tracked_jobs = constants.LUXID_MAXIMAL_TRACKED_JOBS_DEFAULT 1822 1823 if self.instance_communication_network is None: 1824 self.instance_communication_network = "" 1825 1826 if self.install_image is None: 1827 self.install_image = "" 1828 1829 if self.compression_tools is None: 1830 self.compression_tools = constants.IEC_DEFAULT_TOOLS 1831 1832 if self.enabled_user_shutdown is None: 1833 self.enabled_user_shutdown = False 1834 1835 if self.ssh_key_type is None: 1836 self.ssh_key_type = constants.SSH_DEFAULT_KEY_TYPE 1837 1838 if self.ssh_key_bits is None: 1839 self.ssh_key_bits = constants.SSH_DEFAULT_KEY_BITS
1840 1841 @property
1842 - def primary_hypervisor(self):
1843 """The first hypervisor is the primary. 1844 1845 Useful, for example, for L{Node}'s hv/disk state. 1846 1847 """ 1848 return self.enabled_hypervisors[0]
1849
1850 - def ToDict(self, _with_private=False):
1851 """Custom function for cluster. 1852 1853 """ 1854 mydict = super(Cluster, self).ToDict(_with_private=_with_private) 1855 1856 # Explicitly save private parameters. 1857 if _with_private: 1858 for os in mydict["osparams_private_cluster"]: 1859 mydict["osparams_private_cluster"][os] = \ 1860 self.osparams_private_cluster[os].Unprivate() 1861 1862 if self.tcpudp_port_pool is None: 1863 tcpudp_port_pool = [] 1864 else: 1865 tcpudp_port_pool = list(self.tcpudp_port_pool) 1866 1867 mydict["tcpudp_port_pool"] = tcpudp_port_pool 1868 1869 return mydict
1870 1871 @classmethod
1872 - def FromDict(cls, val):
1873 """Custom function for cluster. 1874 1875 """ 1876 obj = super(Cluster, cls).FromDict(val) 1877 1878 if obj.tcpudp_port_pool is None: 1879 obj.tcpudp_port_pool = set() 1880 elif not isinstance(obj.tcpudp_port_pool, set): 1881 obj.tcpudp_port_pool = set(obj.tcpudp_port_pool) 1882 1883 return obj
1884
1885 - def SimpleFillDP(self, diskparams):
1886 """Fill a given diskparams dict with cluster defaults. 1887 1888 @param diskparams: The diskparams 1889 @return: The defaults dict 1890 1891 """ 1892 return FillDiskParams(self.diskparams, diskparams)
1893
1894 - def GetHVDefaults(self, hypervisor, os_name=None, skip_keys=None):
1895 """Get the default hypervisor parameters for the cluster. 1896 1897 @param hypervisor: the hypervisor name 1898 @param os_name: if specified, we'll also update the defaults for this OS 1899 @param skip_keys: if passed, list of keys not to use 1900 @return: the defaults dict 1901 1902 """ 1903 if skip_keys is None: 1904 skip_keys = [] 1905 1906 fill_stack = [self.hvparams.get(hypervisor, {})] 1907 if os_name is not None: 1908 os_hvp = self.os_hvp.get(os_name, {}).get(hypervisor, {}) 1909 fill_stack.append(os_hvp) 1910 1911 ret_dict = {} 1912 for o_dict in fill_stack: 1913 ret_dict = FillDict(ret_dict, o_dict, skip_keys=skip_keys) 1914 1915 return ret_dict
1916
1917 - def SimpleFillHV(self, hv_name, os_name, hvparams, skip_globals=False):
1918 """Fill a given hvparams dict with cluster defaults. 1919 1920 @type hv_name: string 1921 @param hv_name: the hypervisor to use 1922 @type os_name: string 1923 @param os_name: the OS to use for overriding the hypervisor defaults 1924 @type skip_globals: boolean 1925 @param skip_globals: if True, the global hypervisor parameters will 1926 not be filled 1927 @rtype: dict 1928 @return: a copy of the given hvparams with missing keys filled from 1929 the cluster defaults 1930 1931 """ 1932 if skip_globals: 1933 skip_keys = constants.HVC_GLOBALS 1934 else: 1935 skip_keys = [] 1936 1937 def_dict = self.GetHVDefaults(hv_name, os_name, skip_keys=skip_keys) 1938 return FillDict(def_dict, hvparams, skip_keys=skip_keys)
1939
1940 - def FillHV(self, instance, skip_globals=False):
1941 """Fill an instance's hvparams dict with cluster defaults. 1942 1943 @type instance: L{objects.Instance} 1944 @param instance: the instance parameter to fill 1945 @type skip_globals: boolean 1946 @param skip_globals: if True, the global hypervisor parameters will 1947 not be filled 1948 @rtype: dict 1949 @return: a copy of the instance's hvparams with missing keys filled from 1950 the cluster defaults 1951 1952 """ 1953 return self.SimpleFillHV(instance.hypervisor, instance.os, 1954 instance.hvparams, skip_globals)
1955
1956 - def SimpleFillBE(self, beparams):
1957 """Fill a given beparams dict with cluster defaults. 1958 1959 @type beparams: dict 1960 @param beparams: the dict to fill 1961 @rtype: dict 1962 @return: a copy of the passed in beparams with missing keys filled 1963 from the cluster defaults 1964 1965 """ 1966 return FillDict(self.beparams.get(constants.PP_DEFAULT, {}), beparams)
1967
1968 - def FillBE(self, instance):
1969 """Fill an instance's beparams dict with cluster defaults. 1970 1971 @type instance: L{objects.Instance} 1972 @param instance: the instance parameter to fill 1973 @rtype: dict 1974 @return: a copy of the instance's beparams with missing keys filled from 1975 the cluster defaults 1976 1977 """ 1978 return self.SimpleFillBE(instance.beparams)
1979
1980 - def SimpleFillNIC(self, nicparams):
1981 """Fill a given nicparams dict with cluster defaults. 1982 1983 @type nicparams: dict 1984 @param nicparams: the dict to fill 1985 @rtype: dict 1986 @return: a copy of the passed in nicparams with missing keys filled 1987 from the cluster defaults 1988 1989 """ 1990 return FillDict(self.nicparams.get(constants.PP_DEFAULT, {}), nicparams)
1991
1992 - def SimpleFillOS(self, os_name, 1993 os_params_public, 1994 os_params_private=None, 1995 os_params_secret=None):
1996 """Fill an instance's osparams dict with cluster defaults. 1997 1998 @type os_name: string 1999 @param os_name: the OS name to use 2000 @type os_params_public: dict 2001 @param os_params_public: the dict to fill with default values 2002 @type os_params_private: dict 2003 @param os_params_private: the dict with private fields to fill 2004 with default values. Not passing this field 2005 results in no private fields being added to the 2006 return value. Private fields will be wrapped in 2007 L{Private} objects. 2008 @type os_params_secret: dict 2009 @param os_params_secret: the dict with secret fields to fill 2010 with default values. Not passing this field 2011 results in no secret fields being added to the 2012 return value. Private fields will be wrapped in 2013 L{Private} objects. 2014 @rtype: dict 2015 @return: a copy of the instance's osparams with missing keys filled from 2016 the cluster defaults. Private and secret parameters are not included 2017 unless the respective optional parameters are supplied. 2018 2019 """ 2020 if os_name is None: 2021 name_only = None 2022 else: 2023 name_only = OS.GetName(os_name) 2024 2025 defaults_base_public = self.osparams.get(name_only, {}) 2026 defaults_public = FillDict(defaults_base_public, 2027 self.osparams.get(os_name, {})) 2028 params_public = FillDict(defaults_public, os_params_public) 2029 2030 if os_params_private is not None: 2031 defaults_base_private = self.osparams_private_cluster.get(name_only, {}) 2032 defaults_private = FillDict(defaults_base_private, 2033 self.osparams_private_cluster.get(os_name, 2034 {})) 2035 params_private = FillDict(defaults_private, os_params_private) 2036 else: 2037 params_private = {} 2038 2039 if os_params_secret is not None: 2040 # There can't be default secret settings, so there's nothing to be done. 2041 params_secret = os_params_secret 2042 else: 2043 params_secret = {} 2044 2045 # Enforce that the set of keys be distinct: 2046 duplicate_keys = utils.GetRepeatedKeys(params_public, 2047 params_private, 2048 params_secret) 2049 if not duplicate_keys: 2050 2051 # Actually update them: 2052 params_public.update(params_private) 2053 params_public.update(params_secret) 2054 2055 return params_public 2056 2057 else: 2058 2059 def formatter(keys): 2060 return utils.CommaJoin(sorted(map(repr, keys))) if keys else "(none)"
2061 2062 #Lose the values. 2063 params_public = set(params_public) 2064 params_private = set(params_private) 2065 params_secret = set(params_secret) 2066 2067 msg = """Cannot assign multiple values to OS parameters. 2068 2069 Conflicting OS parameters that would have been set by this operation: 2070 - at public visibility: {public} 2071 - at private visibility: {private} 2072 - at secret visibility: {secret} 2073 """.format(dupes=formatter(duplicate_keys), 2074 public=formatter(params_public & duplicate_keys), 2075 private=formatter(params_private & duplicate_keys), 2076 secret=formatter(params_secret & duplicate_keys)) 2077 raise errors.OpPrereqError(msg)
2078 2079 @staticmethod
2080 - def SimpleFillHvState(hv_state):
2081 """Fill an hv_state sub dict with cluster defaults. 2082 2083 """ 2084 return FillDict(constants.HVST_DEFAULTS, hv_state)
2085 2086 @staticmethod
2087 - def SimpleFillDiskState(disk_state):
2088 """Fill an disk_state sub dict with cluster defaults. 2089 2090 """ 2091 return FillDict(constants.DS_DEFAULTS, disk_state)
2092
2093 - def FillND(self, node, nodegroup):
2094 """Return filled out ndparams for L{objects.NodeGroup} and L{objects.Node} 2095 2096 @type node: L{objects.Node} 2097 @param node: A Node object to fill 2098 @type nodegroup: L{objects.NodeGroup} 2099 @param nodegroup: A Node object to fill 2100 @return a copy of the node's ndparams with defaults filled 2101 2102 """ 2103 return self.SimpleFillND(nodegroup.FillND(node))
2104
2105 - def FillNDGroup(self, nodegroup):
2106 """Return filled out ndparams for just L{objects.NodeGroup} 2107 2108 @type nodegroup: L{objects.NodeGroup} 2109 @param nodegroup: A Node object to fill 2110 @return a copy of the node group's ndparams with defaults filled 2111 2112 """ 2113 return self.SimpleFillND(nodegroup.SimpleFillND({}))
2114
2115 - def SimpleFillND(self, ndparams):
2116 """Fill a given ndparams dict with defaults. 2117 2118 @type ndparams: dict 2119 @param ndparams: the dict to fill 2120 @rtype: dict 2121 @return: a copy of the passed in ndparams with missing keys filled 2122 from the cluster defaults 2123 2124 """ 2125 return FillDict(self.ndparams, ndparams)
2126
2127 - def SimpleFillIPolicy(self, ipolicy):
2128 """ Fill instance policy dict with defaults. 2129 2130 @type ipolicy: dict 2131 @param ipolicy: the dict to fill 2132 @rtype: dict 2133 @return: a copy of passed ipolicy with missing keys filled from 2134 the cluster defaults 2135 2136 """ 2137 return FillIPolicy(self.ipolicy, ipolicy)
2138
2139 - def IsDiskTemplateEnabled(self, disk_template):
2140 """Checks if a particular disk template is enabled. 2141 2142 """ 2143 return utils.storage.IsDiskTemplateEnabled( 2144 disk_template, self.enabled_disk_templates)
2145
2146 - def IsFileStorageEnabled(self):
2147 """Checks if file storage is enabled. 2148 2149 """ 2150 return utils.storage.IsFileStorageEnabled(self.enabled_disk_templates)
2151
2152 - def IsSharedFileStorageEnabled(self):
2153 """Checks if shared file storage is enabled. 2154 2155 """ 2156 return utils.storage.IsSharedFileStorageEnabled( 2157 self.enabled_disk_templates)
2158
2159 2160 -class BlockDevStatus(ConfigObject):
2161 """Config object representing the status of a block device.""" 2162 __slots__ = [ 2163 "dev_path", 2164 "major", 2165 "minor", 2166 "sync_percent", 2167 "estimated_time", 2168 "is_degraded", 2169 "ldisk_status", 2170 ]
2171
2172 2173 -class ImportExportStatus(ConfigObject):
2174 """Config object representing the status of an import or export.""" 2175 __slots__ = [ 2176 "recent_output", 2177 "listen_port", 2178 "connected", 2179 "progress_mbytes", 2180 "progress_throughput", 2181 "progress_eta", 2182 "progress_percent", 2183 "exit_status", 2184 "error_message", 2185 ] + _TIMESTAMPS
2186
2187 2188 -class ImportExportOptions(ConfigObject):
2189 """Options for import/export daemon 2190 2191 @ivar key_name: X509 key name (None for cluster certificate) 2192 @ivar ca_pem: Remote peer CA in PEM format (None for cluster certificate) 2193 @ivar compress: Compression tool to use 2194 @ivar magic: Used to ensure the connection goes to the right disk 2195 @ivar ipv6: Whether to use IPv6 2196 @ivar connect_timeout: Number of seconds for establishing connection 2197 2198 """ 2199 __slots__ = [ 2200 "key_name", 2201 "ca_pem", 2202 "compress", 2203 "magic", 2204 "ipv6", 2205 "connect_timeout", 2206 ]
2207
2208 2209 -class ConfdRequest(ConfigObject):
2210 """Object holding a confd request. 2211 2212 @ivar protocol: confd protocol version 2213 @ivar type: confd query type 2214 @ivar query: query request 2215 @ivar rsalt: requested reply salt 2216 2217 """ 2218 __slots__ = [ 2219 "protocol", 2220 "type", 2221 "query", 2222 "rsalt", 2223 ]
2224
2225 2226 -class ConfdReply(ConfigObject):
2227 """Object holding a confd reply. 2228 2229 @ivar protocol: confd protocol version 2230 @ivar status: reply status code (ok, error) 2231 @ivar answer: confd query reply 2232 @ivar serial: configuration serial number 2233 2234 """ 2235 __slots__ = [ 2236 "protocol", 2237 "status", 2238 "answer", 2239 "serial", 2240 ]
2241
2242 2243 -class QueryFieldDefinition(ConfigObject):
2244 """Object holding a query field definition. 2245 2246 @ivar name: Field name 2247 @ivar title: Human-readable title 2248 @ivar kind: Field type 2249 @ivar doc: Human-readable description 2250 2251 """ 2252 __slots__ = [ 2253 "name", 2254 "title", 2255 "kind", 2256 "doc", 2257 ]
2258
2259 2260 -class _QueryResponseBase(ConfigObject):
2261 __slots__ = [ 2262 "fields", 2263 ] 2264
2265 - def ToDict(self, _with_private=False):
2266 """Custom function for serializing. 2267 2268 """ 2269 mydict = super(_QueryResponseBase, self).ToDict() 2270 mydict["fields"] = outils.ContainerToDicts(mydict["fields"]) 2271 return mydict
2272 2273 @classmethod
2274 - def FromDict(cls, val):
2275 """Custom function for de-serializing. 2276 2277 """ 2278 obj = super(_QueryResponseBase, cls).FromDict(val) 2279 obj.fields = \ 2280 outils.ContainerFromDicts(obj.fields, list, QueryFieldDefinition) 2281 return obj
2282
2283 2284 -class QueryResponse(_QueryResponseBase):
2285 """Object holding the response to a query. 2286 2287 @ivar fields: List of L{QueryFieldDefinition} objects 2288 @ivar data: Requested data 2289 2290 """ 2291 __slots__ = [ 2292 "data", 2293 ]
2294
2295 2296 -class QueryFieldsRequest(ConfigObject):
2297 """Object holding a request for querying available fields. 2298 2299 """ 2300 __slots__ = [ 2301 "what", 2302 "fields", 2303 ]
2304
2305 2306 -class QueryFieldsResponse(_QueryResponseBase):
2307 """Object holding the response to a query for fields. 2308 2309 @ivar fields: List of L{QueryFieldDefinition} objects 2310 2311 """ 2312 __slots__ = []
2313
2314 2315 -class MigrationStatus(ConfigObject):
2316 """Object holding the status of a migration. 2317 2318 """ 2319 __slots__ = [ 2320 "status", 2321 "transferred_ram", 2322 "total_ram", 2323 ]
2324
2325 2326 -class InstanceConsole(ConfigObject):
2327 """Object describing how to access the console of an instance. 2328 2329 """ 2330 __slots__ = [ 2331 "instance", 2332 "kind", 2333 "message", 2334 "host", 2335 "port", 2336 "user", 2337 "command", 2338 "display", 2339 ] 2340
2341 - def Validate(self):
2342 """Validates contents of this object. 2343 2344 """ 2345 assert self.kind in constants.CONS_ALL, "Unknown console type" 2346 assert self.instance, "Missing instance name" 2347 assert self.message or self.kind in [constants.CONS_SSH, 2348 constants.CONS_SPICE, 2349 constants.CONS_VNC] 2350 assert self.host or self.kind == constants.CONS_MESSAGE 2351 assert self.port or self.kind in [constants.CONS_MESSAGE, 2352 constants.CONS_SSH] 2353 assert self.user or self.kind in [constants.CONS_MESSAGE, 2354 constants.CONS_SPICE, 2355 constants.CONS_VNC] 2356 assert self.command or self.kind in [constants.CONS_MESSAGE, 2357 constants.CONS_SPICE, 2358 constants.CONS_VNC] 2359 assert self.display or self.kind in [constants.CONS_MESSAGE, 2360 constants.CONS_SPICE, 2361 constants.CONS_SSH]
2362
2363 2364 -class Network(TaggableObject):
2365 """Object representing a network definition for ganeti. 2366 2367 """ 2368 __slots__ = [ 2369 "name", 2370 "serial_no", 2371 "mac_prefix", 2372 "network", 2373 "network6", 2374 "gateway", 2375 "gateway6", 2376 "reservations", 2377 "ext_reservations", 2378 ] + _TIMESTAMPS + _UUID 2379
2380 - def HooksDict(self, prefix=""):
2381 """Export a dictionary used by hooks with a network's information. 2382 2383 @type prefix: String 2384 @param prefix: Prefix to prepend to the dict entries 2385 2386 """ 2387 result = { 2388 "%sNETWORK_NAME" % prefix: self.name, 2389 "%sNETWORK_UUID" % prefix: self.uuid, 2390 "%sNETWORK_TAGS" % prefix: " ".join(self.GetTags()), 2391 } 2392 if self.network: 2393 result["%sNETWORK_SUBNET" % prefix] = self.network 2394 if self.gateway: 2395 result["%sNETWORK_GATEWAY" % prefix] = self.gateway 2396 if self.network6: 2397 result["%sNETWORK_SUBNET6" % prefix] = self.network6 2398 if self.gateway6: 2399 result["%sNETWORK_GATEWAY6" % prefix] = self.gateway6 2400 if self.mac_prefix: 2401 result["%sNETWORK_MAC_PREFIX" % prefix] = self.mac_prefix 2402 2403 return result
2404 2405 @classmethod
2406 - def FromDict(cls, val):
2407 """Custom function for networks. 2408 2409 Remove deprecated network_type and family. 2410 2411 """ 2412 if "network_type" in val: 2413 del val["network_type"] 2414 if "family" in val: 2415 del val["family"] 2416 obj = super(Network, cls).FromDict(val) 2417 return obj
2418
2419 2420 # need to inherit object in order to use super() 2421 -class SerializableConfigParser(ConfigParser.SafeConfigParser, object):
2422 """Simple wrapper over ConfigParse that allows serialization. 2423 2424 This class is basically ConfigParser.SafeConfigParser with two 2425 additional methods that allow it to serialize/unserialize to/from a 2426 buffer. 2427 2428 """
2429 - def Dumps(self):
2430 """Dump this instance and return the string representation.""" 2431 buf = StringIO() 2432 self.write(buf) 2433 return buf.getvalue()
2434 2435 @classmethod
2436 - def Loads(cls, data):
2437 """Load data from a string.""" 2438 buf = StringIO(data) 2439 cfp = cls() 2440 cfp.readfp(buf) 2441 return cfp
2442
2443 - def get(self, section, option, **kwargs):
2444 value = None 2445 try: 2446 value = super(SerializableConfigParser, self).get(section, option, 2447 **kwargs) 2448 if value.lower() == constants.VALUE_NONE: 2449 value = None 2450 except ConfigParser.NoOptionError: 2451 r = re.compile(r"(disk|nic)\d+_name|nic\d+_(network|vlan)") 2452 match = r.match(option) 2453 if match: 2454 pass 2455 else: 2456 raise 2457 2458 return value
2459
2460 2461 -class LvmPvInfo(ConfigObject):
2462 """Information about an LVM physical volume (PV). 2463 2464 @type name: string 2465 @ivar name: name of the PV 2466 @type vg_name: string 2467 @ivar vg_name: name of the volume group containing the PV 2468 @type size: float 2469 @ivar size: size of the PV in MiB 2470 @type free: float 2471 @ivar free: free space in the PV, in MiB 2472 @type attributes: string 2473 @ivar attributes: PV attributes 2474 @type lv_list: list of strings 2475 @ivar lv_list: names of the LVs hosted on the PV 2476 """ 2477 __slots__ = [ 2478 "name", 2479 "vg_name", 2480 "size", 2481 "free", 2482 "attributes", 2483 "lv_list" 2484 ] 2485
2486 - def IsEmpty(self):
2487 """Is this PV empty? 2488 2489 """ 2490 return self.size <= (self.free + 1)
2491
2492 - def IsAllocatable(self):
2493 """Is this PV allocatable? 2494 2495 """ 2496 return ("a" in self.attributes)
2497