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