1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Transportable objects for Ganeti.
23
24 This module provides small, mostly data-only objects which are safe to
25 pass to and from external parties.
26
27 """
28
29
30
31
32
33
34
35
36 import ConfigParser
37 import re
38 import copy
39 from cStringIO import StringIO
40
41 from ganeti import errors
42 from ganeti import constants
43
44
45 __all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance",
46 "OS", "Node", "Cluster", "FillDict"]
47
48 _TIMESTAMPS = ["ctime", "mtime"]
49 _UUID = ["uuid"]
50
51
52 -def FillDict(defaults_dict, custom_dict, skip_keys=None):
53 """Basic function to apply settings on top a default dict.
54
55 @type defaults_dict: dict
56 @param defaults_dict: dictionary holding the default values
57 @type custom_dict: dict
58 @param custom_dict: dictionary holding customized value
59 @type skip_keys: list
60 @param skip_keys: which keys not to fill
61 @rtype: dict
62 @return: dict with the 'full' values
63
64 """
65 ret_dict = copy.deepcopy(defaults_dict)
66 ret_dict.update(custom_dict)
67 if skip_keys:
68 for k in skip_keys:
69 try:
70 del ret_dict[k]
71 except KeyError:
72 pass
73 return ret_dict
74
77 """Update all groups for the target parameter.
78
79 @type target: dict of dicts
80 @param target: {group: {parameter: value}}
81 @type defaults: dict
82 @param defaults: default parameter values
83
84 """
85 if target is None:
86 target = {constants.PP_DEFAULT: defaults}
87 else:
88 for group in target:
89 target[group] = FillDict(defaults, target[group])
90 return target
91
94 """A generic config object.
95
96 It has the following properties:
97
98 - provides somewhat safe recursive unpickling and pickling for its classes
99 - unset attributes which are defined in slots are always returned
100 as None instead of raising an error
101
102 Classes derived from this must always declare __slots__ (we use many
103 config objects and the memory reduction is useful)
104
105 """
106 __slots__ = []
107
109 for k, v in kwargs.iteritems():
110 setattr(self, k, v)
111
113 if name not in self._all_slots():
114 raise AttributeError("Invalid object attribute %s.%s" %
115 (type(self).__name__, name))
116 return None
117
119 slots = self._all_slots()
120 for name in state:
121 if name in slots:
122 setattr(self, name, state[name])
123
124 @classmethod
126 """Compute the list of all declared slots for a class.
127
128 """
129 slots = []
130 for parent in cls.__mro__:
131 slots.extend(getattr(parent, "__slots__", []))
132 return slots
133
135 """Convert to a dict holding only standard python types.
136
137 The generic routine just dumps all of this object's attributes in
138 a dict. It does not work if the class has children who are
139 ConfigObjects themselves (e.g. the nics list in an Instance), in
140 which case the object should subclass the function in order to
141 make sure all objects returned are only standard python types.
142
143 """
144 result = {}
145 for name in self._all_slots():
146 value = getattr(self, name, None)
147 if value is not None:
148 result[name] = value
149 return result
150
151 __getstate__ = ToDict
152
153 @classmethod
155 """Create an object from a dictionary.
156
157 This generic routine takes a dict, instantiates a new instance of
158 the given class, and sets attributes based on the dict content.
159
160 As for `ToDict`, this does not work if the class has children
161 who are ConfigObjects themselves (e.g. the nics list in an
162 Instance), in which case the object should subclass the function
163 and alter the objects.
164
165 """
166 if not isinstance(val, dict):
167 raise errors.ConfigurationError("Invalid object passed to FromDict:"
168 " expected dict, got %s" % type(val))
169 val_str = dict([(str(k), v) for k, v in val.iteritems()])
170 obj = cls(**val_str)
171 return obj
172
173 @staticmethod
175 """Convert the elements of a container to standard python types.
176
177 This method converts a container with elements derived from
178 ConfigData to standard python types. If the container is a dict,
179 we don't touch the keys, only the values.
180
181 """
182 if isinstance(container, dict):
183 ret = dict([(k, v.ToDict()) for k, v in container.iteritems()])
184 elif isinstance(container, (list, tuple, set, frozenset)):
185 ret = [elem.ToDict() for elem in container]
186 else:
187 raise TypeError("Invalid type %s passed to _ContainerToDicts" %
188 type(container))
189 return ret
190
191 @staticmethod
193 """Convert a container from standard python types.
194
195 This method converts a container with standard python types to
196 ConfigData objects. If the container is a dict, we don't touch the
197 keys, only the values.
198
199 """
200 if not isinstance(c_type, type):
201 raise TypeError("Container type %s passed to _ContainerFromDicts is"
202 " not a type" % type(c_type))
203 if c_type is dict:
204 ret = dict([(k, e_type.FromDict(v)) for k, v in source.iteritems()])
205 elif c_type in (list, tuple, set, frozenset):
206 ret = c_type([e_type.FromDict(elem) for elem in source])
207 else:
208 raise TypeError("Invalid container type %s passed to"
209 " _ContainerFromDicts" % c_type)
210 return ret
211
213 """Makes a deep copy of the current object and its children.
214
215 """
216 dict_form = self.ToDict()
217 clone_obj = self.__class__.FromDict(dict_form)
218 return clone_obj
219
221 """Implement __repr__ for ConfigObjects."""
222 return repr(self.ToDict())
223
225 """Fill defaults for missing configuration values.
226
227 This method will be called at configuration load time, and its
228 implementation will be object dependent.
229
230 """
231 pass
232
235 """An generic class supporting tags.
236
237 """
238 __slots__ = ["tags"]
239 VALID_TAG_RE = re.compile("^[\w.+*/:@-]+$")
240
241 @classmethod
243 """Check if a tag is valid.
244
245 If the tag is invalid, an errors.TagError will be raised. The
246 function has no return value.
247
248 """
249 if not isinstance(tag, basestring):
250 raise errors.TagError("Invalid tag type (not a string)")
251 if len(tag) > constants.MAX_TAG_LEN:
252 raise errors.TagError("Tag too long (>%d characters)" %
253 constants.MAX_TAG_LEN)
254 if not tag:
255 raise errors.TagError("Tags cannot be empty")
256 if not cls.VALID_TAG_RE.match(tag):
257 raise errors.TagError("Tag contains invalid characters")
258
267
277
288
290 """Taggable-object-specific conversion to standard python types.
291
292 This replaces the tags set with a list.
293
294 """
295 bo = super(TaggableObject, self).ToDict()
296
297 tags = bo.get("tags", None)
298 if isinstance(tags, set):
299 bo["tags"] = list(tags)
300 return bo
301
302 @classmethod
304 """Custom function for instances.
305
306 """
307 obj = super(TaggableObject, cls).FromDict(val)
308 if hasattr(obj, "tags") and isinstance(obj.tags, list):
309 obj.tags = set(obj.tags)
310 return obj
311
314 """Top-level config object."""
315 __slots__ = (["version", "cluster", "nodes", "instances", "serial_no"] +
316 _TIMESTAMPS)
317
319 """Custom function for top-level config data.
320
321 This just replaces the list of instances, nodes and the cluster
322 with standard python types.
323
324 """
325 mydict = super(ConfigData, self).ToDict()
326 mydict["cluster"] = mydict["cluster"].ToDict()
327 for key in "nodes", "instances":
328 mydict[key] = self._ContainerToDicts(mydict[key])
329
330 return mydict
331
332 @classmethod
342
344 """Check if in there is at disk of the given type in the configuration.
345
346 @type dev_type: L{constants.LDS_BLOCK}
347 @param dev_type: the type to look for
348 @rtype: boolean
349 @return: boolean indicating if a disk of the given type was found or not
350
351 """
352 for instance in self.instances.values():
353 for disk in instance.disks:
354 if disk.IsBasedOnDiskType(dev_type):
355 return True
356 return False
357
373
374
375 -class NIC(ConfigObject):
376 """Config object representing a network card."""
377 __slots__ = ["mac", "ip", "bridge", "nicparams"]
378
379 @classmethod
396
398 """Fill defaults for missing configuration values.
399
400 """
401 if self.nicparams is None:
402 self.nicparams = {}
403 if self.bridge is not None:
404 self.nicparams[constants.NIC_MODE] = constants.NIC_MODE_BRIDGED
405 self.nicparams[constants.NIC_LINK] = self.bridge
406
407
408
409 if self.bridge is not None:
410 self.bridge = None
411
412
413 -class Disk(ConfigObject):
414 """Config object representing a block device."""
415 __slots__ = ["dev_type", "logical_id", "physical_id",
416 "children", "iv_name", "size", "mode"]
417
421
425
427 """Test if this device needs to be opened on a secondary node."""
428 return self.dev_type in (constants.LD_LV,)
429
431 """Return the device path if this device type has a static one.
432
433 Some devices (LVM for example) live always at the same /dev/ path,
434 irrespective of their status. For such devices, we return this
435 path, for others we return None.
436
437 @warning: The path returned is not a normalized pathname; callers
438 should check that it is a valid path.
439
440 """
441 if self.dev_type == constants.LD_LV:
442 return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
443 return None
444
446 """Compute the needed number of children for activation.
447
448 This method will return either -1 (all children) or a positive
449 number denoting the minimum number of children needed for
450 activation (only mirrored devices will usually return >=0).
451
452 Currently, only DRBD8 supports diskless activation (therefore we
453 return 0), for all other we keep the previous semantics and return
454 -1.
455
456 """
457 if self.dev_type == constants.LD_DRBD8:
458 return 0
459 return -1
460
462 """Check if the disk or its children are based on the given type.
463
464 @type dev_type: L{constants.LDS_BLOCK}
465 @param dev_type: the type to look for
466 @rtype: boolean
467 @return: boolean indicating if a device of the given type was found or not
468
469 """
470 if self.children:
471 for child in self.children:
472 if child.IsBasedOnDiskType(dev_type):
473 return True
474 return self.dev_type == dev_type
475
477 """This function returns the nodes this device lives on.
478
479 Given the node on which the parent of the device lives on (or, in
480 case of a top-level device, the primary node of the devices'
481 instance), this function will return a list of nodes on which this
482 devices needs to (or can) be assembled.
483
484 """
485 if self.dev_type in [constants.LD_LV, constants.LD_FILE]:
486 result = [node]
487 elif self.dev_type in constants.LDS_DRBD:
488 result = [self.logical_id[0], self.logical_id[1]]
489 if node not in result:
490 raise errors.ConfigurationError("DRBD device passed unknown node")
491 else:
492 raise errors.ProgrammerError("Unhandled device type %s" % self.dev_type)
493 return result
494
496 """Compute the node/disk tree for this disk and its children.
497
498 This method, given the node on which the parent disk lives, will
499 return the list of all (node, disk) pairs which describe the disk
500 tree in the most compact way. For example, a drbd/lvm stack
501 will be returned as (primary_node, drbd) and (secondary_node, drbd)
502 which represents all the top-level devices on the nodes.
503
504 """
505 my_nodes = self.GetNodes(parent_node)
506 result = [(node, self) for node in my_nodes]
507 if not self.children:
508
509 return result
510 for node in my_nodes:
511 for child in self.children:
512 child_result = child.ComputeNodeTree(node)
513 if len(child_result) == 1:
514
515
516
517 continue
518 else:
519
520
521
522 for subnode, subdisk in child_result:
523 if subnode not in my_nodes:
524 result.append((subnode, subdisk))
525
526
527
528 return result
529
531 """Update the size of this disk after growth.
532
533 This method recurses over the disks's children and updates their
534 size correspondigly. The method needs to be kept in sync with the
535 actual algorithms from bdev.
536
537 """
538 if self.dev_type == constants.LD_LV or self.dev_type == constants.LD_FILE:
539 self.size += amount
540 elif self.dev_type == constants.LD_DRBD8:
541 if self.children:
542 self.children[0].RecordGrow(amount)
543 self.size += amount
544 else:
545 raise errors.ProgrammerError("Disk.RecordGrow called for unsupported"
546 " disk type %s" % self.dev_type)
547
549 """Sets recursively the size to zero for the disk and its children.
550
551 """
552 if self.children:
553 for child in self.children:
554 child.UnsetSize()
555 self.size = 0
556
558 """Convert the logical ID to the physical ID.
559
560 This is used only for drbd, which needs ip/port configuration.
561
562 The routine descends down and updates its children also, because
563 this helps when the only the top device is passed to the remote
564 node.
565
566 Arguments:
567 - target_node: the node we wish to configure for
568 - nodes_ip: a mapping of node name to ip
569
570 The target_node must exist in in nodes_ip, and must be one of the
571 nodes in the logical ID for each of the DRBD devices encountered
572 in the disk tree.
573
574 """
575 if self.children:
576 for child in self.children:
577 child.SetPhysicalID(target_node, nodes_ip)
578
579 if self.logical_id is None and self.physical_id is not None:
580 return
581 if self.dev_type in constants.LDS_DRBD:
582 pnode, snode, port, pminor, sminor, secret = self.logical_id
583 if target_node not in (pnode, snode):
584 raise errors.ConfigurationError("DRBD device not knowing node %s" %
585 target_node)
586 pnode_ip = nodes_ip.get(pnode, None)
587 snode_ip = nodes_ip.get(snode, None)
588 if pnode_ip is None or snode_ip is None:
589 raise errors.ConfigurationError("Can't find primary or secondary node"
590 " for %s" % str(self))
591 p_data = (pnode_ip, port)
592 s_data = (snode_ip, port)
593 if pnode == target_node:
594 self.physical_id = p_data + s_data + (pminor, secret)
595 else:
596 self.physical_id = s_data + p_data + (sminor, secret)
597 else:
598 self.physical_id = self.logical_id
599 return
600
602 """Disk-specific conversion to standard python types.
603
604 This replaces the children lists of objects with lists of
605 standard python types.
606
607 """
608 bo = super(Disk, self).ToDict()
609
610 for attr in ("children",):
611 alist = bo.get(attr, None)
612 if alist:
613 bo[attr] = self._ContainerToDicts(alist)
614 return bo
615
616 @classmethod
618 """Custom function for Disks
619
620 """
621 obj = super(Disk, cls).FromDict(val)
622 if obj.children:
623 obj.children = cls._ContainerFromDicts(obj.children, list, Disk)
624 if obj.logical_id and isinstance(obj.logical_id, list):
625 obj.logical_id = tuple(obj.logical_id)
626 if obj.physical_id and isinstance(obj.physical_id, list):
627 obj.physical_id = tuple(obj.physical_id)
628 if obj.dev_type in constants.LDS_DRBD:
629
630 if len(obj.logical_id) < 6:
631 obj.logical_id += (None,) * (6 - len(obj.logical_id))
632 return obj
633
635 """Custom str() formatter for disks.
636
637 """
638 if self.dev_type == constants.LD_LV:
639 val = "<LogicalVolume(/dev/%s/%s" % self.logical_id
640 elif self.dev_type in constants.LDS_DRBD:
641 node_a, node_b, port, minor_a, minor_b = self.logical_id[:5]
642 val = "<DRBD8("
643 if self.physical_id is None:
644 phy = "unconfigured"
645 else:
646 phy = ("configured as %s:%s %s:%s" %
647 (self.physical_id[0], self.physical_id[1],
648 self.physical_id[2], self.physical_id[3]))
649
650 val += ("hosts=%s/%d-%s/%d, port=%s, %s, " %
651 (node_a, minor_a, node_b, minor_b, port, phy))
652 if self.children and self.children.count(None) == 0:
653 val += "backend=%s, metadev=%s" % (self.children[0], self.children[1])
654 else:
655 val += "no local storage"
656 else:
657 val = ("<Disk(type=%s, logical_id=%s, physical_id=%s, children=%s" %
658 (self.dev_type, self.logical_id, self.physical_id, self.children))
659 if self.iv_name is None:
660 val += ", not visible"
661 else:
662 val += ", visible as /dev/%s" % self.iv_name
663 if isinstance(self.size, int):
664 val += ", size=%dm)>" % self.size
665 else:
666 val += ", size='%s')>" % (self.size,)
667 return val
668
670 """Checks that this disk is correctly configured.
671
672 """
673 all_errors = []
674 if self.mode not in constants.DISK_ACCESS_SET:
675 all_errors.append("Disk access mode '%s' is invalid" % (self.mode, ))
676 return all_errors
677
679 """Fill defaults for missing configuration values.
680
681 """
682 if self.children:
683 for child in self.children:
684 child.UpgradeConfig()
685
689 """Config object representing an instance."""
690 __slots__ = [
691 "name",
692 "primary_node",
693 "os",
694 "hypervisor",
695 "hvparams",
696 "beparams",
697 "osparams",
698 "admin_up",
699 "nics",
700 "disks",
701 "disk_template",
702 "network_port",
703 "serial_no",
704 ] + _TIMESTAMPS + _UUID
705
707 """Compute the list of secondary nodes.
708
709 This is a simple wrapper over _ComputeAllNodes.
710
711 """
712 all_nodes = set(self._ComputeAllNodes())
713 all_nodes.discard(self.primary_node)
714 return tuple(all_nodes)
715
716 secondary_nodes = property(_ComputeSecondaryNodes, None, None,
717 "List of secondary nodes")
718
720 """Compute the list of all nodes.
721
722 Since the data is already there (in the drbd disks), keeping it as
723 a separate normal attribute is redundant and if not properly
724 synchronised can cause problems. Thus it's better to compute it
725 dynamically.
726
727 """
728 def _Helper(nodes, device):
729 """Recursively computes nodes given a top device."""
730 if device.dev_type in constants.LDS_DRBD:
731 nodea, nodeb = device.logical_id[:2]
732 nodes.add(nodea)
733 nodes.add(nodeb)
734 if device.children:
735 for child in device.children:
736 _Helper(nodes, child)
737
738 all_nodes = set()
739 all_nodes.add(self.primary_node)
740 for device in self.disks:
741 _Helper(all_nodes, device)
742 return tuple(all_nodes)
743
744 all_nodes = property(_ComputeAllNodes, None, None,
745 "List of all nodes of the instance")
746
748 """Provide a mapping of nodes to LVs this instance owns.
749
750 This function figures out what logical volumes should belong on
751 which nodes, recursing through a device tree.
752
753 @param lvmap: optional dictionary to receive the
754 'node' : ['lv', ...] data.
755
756 @return: None if lvmap arg is given, otherwise, a dictionary
757 of the form { 'nodename' : ['volume1', 'volume2', ...], ... }
758
759 """
760 if node == None:
761 node = self.primary_node
762
763 if lvmap is None:
764 lvmap = { node : [] }
765 ret = lvmap
766 else:
767 if not node in lvmap:
768 lvmap[node] = []
769 ret = None
770
771 if not devs:
772 devs = self.disks
773
774 for dev in devs:
775 if dev.dev_type == constants.LD_LV:
776 lvmap[node].append(dev.logical_id[1])
777
778 elif dev.dev_type in constants.LDS_DRBD:
779 if dev.children:
780 self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
781 self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
782
783 elif dev.children:
784 self.MapLVsByNode(lvmap, dev.children, node)
785
786 return ret
787
789 """Find a disk given having a specified index.
790
791 This is just a wrapper that does validation of the index.
792
793 @type idx: int
794 @param idx: the disk index
795 @rtype: L{Disk}
796 @return: the corresponding disk
797 @raise errors.OpPrereqError: when the given index is not valid
798
799 """
800 try:
801 idx = int(idx)
802 return self.disks[idx]
803 except (TypeError, ValueError), err:
804 raise errors.OpPrereqError("Invalid disk index: '%s'" % str(err),
805 errors.ECODE_INVAL)
806 except IndexError:
807 raise errors.OpPrereqError("Invalid disk index: %d (instace has disks"
808 " 0 to %d" % (idx, len(self.disks)),
809 errors.ECODE_INVAL)
810
812 """Instance-specific conversion to standard python types.
813
814 This replaces the children lists of objects with lists of standard
815 python types.
816
817 """
818 bo = super(Instance, self).ToDict()
819
820 for attr in "nics", "disks":
821 alist = bo.get(attr, None)
822 if alist:
823 nlist = self._ContainerToDicts(alist)
824 else:
825 nlist = []
826 bo[attr] = nlist
827 return bo
828
829 @classmethod
838
840 """Fill defaults for missing configuration values.
841
842 """
843 for nic in self.nics:
844 nic.UpgradeConfig()
845 for disk in self.disks:
846 disk.UpgradeConfig()
847 if self.hvparams:
848 for key in constants.HVC_GLOBALS:
849 try:
850 del self.hvparams[key]
851 except KeyError:
852 pass
853 if self.osparams is None:
854 self.osparams = {}
855
856
857 -class OS(ConfigObject):
858 """Config object representing an operating system.
859
860 @type supported_parameters: list
861 @ivar supported_parameters: a list of tuples, name and description,
862 containing the supported parameters by this OS
863
864 @type VARIANT_DELIM: string
865 @cvar VARIANT_DELIM: the variant delimiter
866
867 """
868 __slots__ = [
869 "name",
870 "path",
871 "api_versions",
872 "create_script",
873 "export_script",
874 "import_script",
875 "rename_script",
876 "verify_script",
877 "supported_variants",
878 "supported_parameters",
879 ]
880
881 VARIANT_DELIM = "+"
882
883 @classmethod
885 """Splits the name into the proper name and variant.
886
887 @param name: the OS (unprocessed) name
888 @rtype: list
889 @return: a list of two elements; if the original name didn't
890 contain a variant, it's returned as an empty string
891
892 """
893 nv = name.split(cls.VARIANT_DELIM, 1)
894 if len(nv) == 1:
895 nv.append("")
896 return nv
897
898 @classmethod
900 """Returns the proper name of the os (without the variant).
901
902 @param name: the OS (unprocessed) name
903
904 """
905 return cls.SplitNameVariant(name)[0]
906
907 @classmethod
909 """Returns the variant the os (without the base name).
910
911 @param name: the OS (unprocessed) name
912
913 """
914 return cls.SplitNameVariant(name)[1]
915
916
917 -class Node(TaggableObject):
918 """Config object representing a node."""
919 __slots__ = [
920 "name",
921 "primary_ip",
922 "secondary_ip",
923 "serial_no",
924 "master_candidate",
925 "offline",
926 "drained",
927 ] + _TIMESTAMPS + _UUID
928
931 """Config object representing the cluster."""
932 __slots__ = [
933 "serial_no",
934 "rsahostkeypub",
935 "highest_used_port",
936 "tcpudp_port_pool",
937 "mac_prefix",
938 "volume_group_name",
939 "reserved_lvs",
940 "drbd_usermode_helper",
941 "default_bridge",
942 "default_hypervisor",
943 "master_node",
944 "master_ip",
945 "master_netdev",
946 "cluster_name",
947 "file_storage_dir",
948 "enabled_hypervisors",
949 "hvparams",
950 "os_hvp",
951 "beparams",
952 "osparams",
953 "nicparams",
954 "candidate_pool_size",
955 "modify_etc_hosts",
956 "modify_ssh_setup",
957 "maintain_node_health",
958 "uid_pool",
959 "default_iallocator",
960 "hidden_os",
961 "blacklisted_os",
962 ] + _TIMESTAMPS + _UUID
963
965 """Fill defaults for missing configuration values.
966
967 """
968
969
970 if self.hvparams is None:
971 self.hvparams = constants.HVC_DEFAULTS
972 else:
973 for hypervisor in self.hvparams:
974 self.hvparams[hypervisor] = FillDict(
975 constants.HVC_DEFAULTS[hypervisor], self.hvparams[hypervisor])
976
977 if self.os_hvp is None:
978 self.os_hvp = {}
979
980
981 if self.osparams is None:
982 self.osparams = {}
983
984 self.beparams = UpgradeGroupedParams(self.beparams,
985 constants.BEC_DEFAULTS)
986 migrate_default_bridge = not self.nicparams
987 self.nicparams = UpgradeGroupedParams(self.nicparams,
988 constants.NICC_DEFAULTS)
989 if migrate_default_bridge:
990 self.nicparams[constants.PP_DEFAULT][constants.NIC_LINK] = \
991 self.default_bridge
992
993 if self.modify_etc_hosts is None:
994 self.modify_etc_hosts = True
995
996 if self.modify_ssh_setup is None:
997 self.modify_ssh_setup = True
998
999
1000
1001
1002 if self.default_bridge is not None:
1003 self.default_bridge = None
1004
1005
1006
1007 if self.default_hypervisor is not None:
1008 self.enabled_hypervisors = ([self.default_hypervisor] +
1009 [hvname for hvname in self.enabled_hypervisors
1010 if hvname != self.default_hypervisor])
1011 self.default_hypervisor = None
1012
1013
1014 if self.maintain_node_health is None:
1015 self.maintain_node_health = False
1016
1017 if self.uid_pool is None:
1018 self.uid_pool = []
1019
1020 if self.default_iallocator is None:
1021 self.default_iallocator = ""
1022
1023
1024 if self.reserved_lvs is None:
1025 self.reserved_lvs = []
1026
1027
1028 if self.hidden_os is None:
1029 self.hidden_os = []
1030
1031 if self.blacklisted_os is None:
1032 self.blacklisted_os = []
1033
1035 """Custom function for cluster.
1036
1037 """
1038 mydict = super(Cluster, self).ToDict()
1039 mydict["tcpudp_port_pool"] = list(self.tcpudp_port_pool)
1040 return mydict
1041
1042 @classmethod
1044 """Custom function for cluster.
1045
1046 """
1047 obj = super(Cluster, cls).FromDict(val)
1048 if not isinstance(obj.tcpudp_port_pool, set):
1049 obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
1050 return obj
1051
1052 - def GetHVDefaults(self, hypervisor, os_name=None, skip_keys=None):
1053 """Get the default hypervisor parameters for the cluster.
1054
1055 @param hypervisor: the hypervisor name
1056 @param os_name: if specified, we'll also update the defaults for this OS
1057 @param skip_keys: if passed, list of keys not to use
1058 @return: the defaults dict
1059
1060 """
1061 if skip_keys is None:
1062 skip_keys = []
1063
1064 fill_stack = [self.hvparams.get(hypervisor, {})]
1065 if os_name is not None:
1066 os_hvp = self.os_hvp.get(os_name, {}).get(hypervisor, {})
1067 fill_stack.append(os_hvp)
1068
1069 ret_dict = {}
1070 for o_dict in fill_stack:
1071 ret_dict = FillDict(ret_dict, o_dict, skip_keys=skip_keys)
1072
1073 return ret_dict
1074
1075 - def SimpleFillHV(self, hv_name, os_name, hvparams, skip_globals=False):
1076 """Fill a given hvparams dict with cluster defaults.
1077
1078 @type hv_name: string
1079 @param hv_name: the hypervisor to use
1080 @type os_name: string
1081 @param os_name: the OS to use for overriding the hypervisor defaults
1082 @type skip_globals: boolean
1083 @param skip_globals: if True, the global hypervisor parameters will
1084 not be filled
1085 @rtype: dict
1086 @return: a copy of the given hvparams with missing keys filled from
1087 the cluster defaults
1088
1089 """
1090 if skip_globals:
1091 skip_keys = constants.HVC_GLOBALS
1092 else:
1093 skip_keys = []
1094
1095 def_dict = self.GetHVDefaults(hv_name, os_name, skip_keys=skip_keys)
1096 return FillDict(def_dict, hvparams, skip_keys=skip_keys)
1097
1098 - def FillHV(self, instance, skip_globals=False):
1099 """Fill an instance's hvparams dict with cluster defaults.
1100
1101 @type instance: L{objects.Instance}
1102 @param instance: the instance parameter to fill
1103 @type skip_globals: boolean
1104 @param skip_globals: if True, the global hypervisor parameters will
1105 not be filled
1106 @rtype: dict
1107 @return: a copy of the instance's hvparams with missing keys filled from
1108 the cluster defaults
1109
1110 """
1111 return self.SimpleFillHV(instance.hypervisor, instance.os,
1112 instance.hvparams, skip_globals)
1113
1115 """Fill a given beparams dict with cluster defaults.
1116
1117 @type beparams: dict
1118 @param beparams: the dict to fill
1119 @rtype: dict
1120 @return: a copy of the passed in beparams with missing keys filled
1121 from the cluster defaults
1122
1123 """
1124 return FillDict(self.beparams.get(constants.PP_DEFAULT, {}), beparams)
1125
1127 """Fill an instance's beparams dict with cluster defaults.
1128
1129 @type instance: L{objects.Instance}
1130 @param instance: the instance parameter to fill
1131 @rtype: dict
1132 @return: a copy of the instance's beparams with missing keys filled from
1133 the cluster defaults
1134
1135 """
1136 return self.SimpleFillBE(instance.beparams)
1137
1139 """Fill a given nicparams dict with cluster defaults.
1140
1141 @type nicparams: dict
1142 @param nicparams: the dict to fill
1143 @rtype: dict
1144 @return: a copy of the passed in nicparams with missing keys filled
1145 from the cluster defaults
1146
1147 """
1148 return FillDict(self.nicparams.get(constants.PP_DEFAULT, {}), nicparams)
1149
1151 """Fill an instance's osparams dict with cluster defaults.
1152
1153 @type os_name: string
1154 @param os_name: the OS name to use
1155 @type os_params: dict
1156 @param os_params: the dict to fill with default values
1157 @rtype: dict
1158 @return: a copy of the instance's osparams with missing keys filled from
1159 the cluster defaults
1160
1161 """
1162 name_only = os_name.split("+", 1)[0]
1163
1164 result = self.osparams.get(name_only, {})
1165
1166 result = FillDict(result, self.osparams.get(os_name, {}))
1167
1168 return FillDict(result, os_params)
1169
1172 """Config object representing the status of a block device."""
1173 __slots__ = [
1174 "dev_path",
1175 "major",
1176 "minor",
1177 "sync_percent",
1178 "estimated_time",
1179 "is_degraded",
1180 "ldisk_status",
1181 ]
1182
1185 """Config object representing the status of an import or export."""
1186 __slots__ = [
1187 "recent_output",
1188 "listen_port",
1189 "connected",
1190 "progress_mbytes",
1191 "progress_throughput",
1192 "progress_eta",
1193 "progress_percent",
1194 "exit_status",
1195 "error_message",
1196 ] + _TIMESTAMPS
1197
1200 """Options for import/export daemon
1201
1202 @ivar key_name: X509 key name (None for cluster certificate)
1203 @ivar ca_pem: Remote peer CA in PEM format (None for cluster certificate)
1204 @ivar compress: Compression method (one of L{constants.IEC_ALL})
1205 @ivar magic: Used to ensure the connection goes to the right disk
1206
1207 """
1208 __slots__ = [
1209 "key_name",
1210 "ca_pem",
1211 "compress",
1212 "magic",
1213 ]
1214
1217 """Object holding a confd request.
1218
1219 @ivar protocol: confd protocol version
1220 @ivar type: confd query type
1221 @ivar query: query request
1222 @ivar rsalt: requested reply salt
1223
1224 """
1225 __slots__ = [
1226 "protocol",
1227 "type",
1228 "query",
1229 "rsalt",
1230 ]
1231
1234 """Object holding a confd reply.
1235
1236 @ivar protocol: confd protocol version
1237 @ivar status: reply status code (ok, error)
1238 @ivar answer: confd query reply
1239 @ivar serial: configuration serial number
1240
1241 """
1242 __slots__ = [
1243 "protocol",
1244 "status",
1245 "answer",
1246 "serial",
1247 ]
1248
1251 """Simple wrapper over ConfigParse that allows serialization.
1252
1253 This class is basically ConfigParser.SafeConfigParser with two
1254 additional methods that allow it to serialize/unserialize to/from a
1255 buffer.
1256
1257 """
1259 """Dump this instance and return the string representation."""
1260 buf = StringIO()
1261 self.write(buf)
1262 return buf.getvalue()
1263
1264 @classmethod
1266 """Load data from a string."""
1267 buf = StringIO(data)
1268 cfp = cls()
1269 cfp.readfp(buf)
1270 return cfp
1271