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
37
38 import ConfigParser
39 import re
40 import copy
41 import logging
42 import time
43 from cStringIO import StringIO
44
45 from ganeti import errors
46 from ganeti import constants
47 from ganeti import netutils
48 from ganeti import outils
49 from ganeti import utils
50
51 from socket import AF_INET
52
53
54 __all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance",
55 "OS", "Node", "NodeGroup", "Cluster", "FillDict", "Network"]
56
57 _TIMESTAMPS = ["ctime", "mtime"]
58 _UUID = ["uuid"]
59
60
61 -def FillDict(defaults_dict, custom_dict, skip_keys=None):
62 """Basic function to apply settings on top a default dict.
63
64 @type defaults_dict: dict
65 @param defaults_dict: dictionary holding the default values
66 @type custom_dict: dict
67 @param custom_dict: dictionary holding customized value
68 @type skip_keys: list
69 @param skip_keys: which keys not to fill
70 @rtype: dict
71 @return: dict with the 'full' values
72
73 """
74 ret_dict = copy.deepcopy(defaults_dict)
75 ret_dict.update(custom_dict)
76 if skip_keys:
77 for k in skip_keys:
78 try:
79 del ret_dict[k]
80 except KeyError:
81 pass
82 return ret_dict
83
84
85 -def FillIPolicy(default_ipolicy, custom_ipolicy, skip_keys=None):
103
104
105 -def FillDiskParams(default_dparams, custom_dparams, skip_keys=None):
106 """Fills the disk parameter defaults.
107
108 @see: L{FillDict} for parameters and return value
109
110 """
111 assert frozenset(default_dparams.keys()) == constants.DISK_TEMPLATES
112
113 return dict((dt, FillDict(default_dparams[dt], custom_dparams.get(dt, {}),
114 skip_keys=skip_keys))
115 for dt in constants.DISK_TEMPLATES)
116
119 """Update all groups for the target parameter.
120
121 @type target: dict of dicts
122 @param target: {group: {parameter: value}}
123 @type defaults: dict
124 @param defaults: default parameter values
125
126 """
127 if target is None:
128 target = {constants.PP_DEFAULT: defaults}
129 else:
130 for group in target:
131 target[group] = FillDict(defaults, target[group])
132 return target
133
147
150 """Upgrade the disk parameters.
151
152 @type diskparams: dict
153 @param diskparams: disk parameters to upgrade
154 @rtype: dict
155 @return: the upgraded disk parameters dict
156
157 """
158 if not diskparams:
159 result = {}
160 else:
161 result = FillDiskParams(constants.DISK_DT_DEFAULTS, diskparams)
162
163 return result
164
183
194
197 """A generic config object.
198
199 It has the following properties:
200
201 - provides somewhat safe recursive unpickling and pickling for its classes
202 - unset attributes which are defined in slots are always returned
203 as None instead of raising an error
204
205 Classes derived from this must always declare __slots__ (we use many
206 config objects and the memory reduction is useful)
207
208 """
209 __slots__ = []
210
212 if name not in self.GetAllSlots():
213 raise AttributeError("Invalid object attribute %s.%s" %
214 (type(self).__name__, name))
215 return None
216
222
224 """Validates the slots.
225
226 """
227
229 """Convert to a dict holding only standard python types.
230
231 The generic routine just dumps all of this object's attributes in
232 a dict. It does not work if the class has children who are
233 ConfigObjects themselves (e.g. the nics list in an Instance), in
234 which case the object should subclass the function in order to
235 make sure all objects returned are only standard python types.
236
237 """
238 result = {}
239 for name in self.GetAllSlots():
240 value = getattr(self, name, None)
241 if value is not None:
242 result[name] = value
243 return result
244
245 __getstate__ = ToDict
246
247 @classmethod
249 """Create an object from a dictionary.
250
251 This generic routine takes a dict, instantiates a new instance of
252 the given class, and sets attributes based on the dict content.
253
254 As for `ToDict`, this does not work if the class has children
255 who are ConfigObjects themselves (e.g. the nics list in an
256 Instance), in which case the object should subclass the function
257 and alter the objects.
258
259 """
260 if not isinstance(val, dict):
261 raise errors.ConfigurationError("Invalid object passed to FromDict:"
262 " expected dict, got %s" % type(val))
263 val_str = dict([(str(k), v) for k, v in val.iteritems()])
264 obj = cls(**val_str)
265 return obj
266
267 @staticmethod
269 """Convert the elements of a container to standard python types.
270
271 This method converts a container with elements derived from
272 ConfigData to standard python types. If the container is a dict,
273 we don't touch the keys, only the values.
274
275 """
276 if isinstance(container, dict):
277 ret = dict([(k, v.ToDict()) for k, v in container.iteritems()])
278 elif isinstance(container, (list, tuple, set, frozenset)):
279 ret = [elem.ToDict() for elem in container]
280 else:
281 raise TypeError("Invalid type %s passed to _ContainerToDicts" %
282 type(container))
283 return ret
284
285 @staticmethod
287 """Convert a container from standard python types.
288
289 This method converts a container with standard python types to
290 ConfigData objects. If the container is a dict, we don't touch the
291 keys, only the values.
292
293 """
294 if not isinstance(c_type, type):
295 raise TypeError("Container type %s passed to _ContainerFromDicts is"
296 " not a type" % type(c_type))
297 if source is None:
298 source = c_type()
299 if c_type is dict:
300 ret = dict([(k, e_type.FromDict(v)) for k, v in source.iteritems()])
301 elif c_type in (list, tuple, set, frozenset):
302 ret = c_type([e_type.FromDict(elem) for elem in source])
303 else:
304 raise TypeError("Invalid container type %s passed to"
305 " _ContainerFromDicts" % c_type)
306 return ret
307
309 """Makes a deep copy of the current object and its children.
310
311 """
312 dict_form = self.ToDict()
313 clone_obj = self.__class__.FromDict(dict_form)
314 return clone_obj
315
317 """Implement __repr__ for ConfigObjects."""
318 return repr(self.ToDict())
319
321 """Fill defaults for missing configuration values.
322
323 This method will be called at configuration load time, and its
324 implementation will be object dependent.
325
326 """
327 pass
328
331 """An generic class supporting tags.
332
333 """
334 __slots__ = ["tags"]
335 VALID_TAG_RE = re.compile("^[\w.+*/:@-]+$")
336
337 @classmethod
339 """Check if a tag is valid.
340
341 If the tag is invalid, an errors.TagError will be raised. The
342 function has no return value.
343
344 """
345 if not isinstance(tag, basestring):
346 raise errors.TagError("Invalid tag type (not a string)")
347 if len(tag) > constants.MAX_TAG_LEN:
348 raise errors.TagError("Tag too long (>%d characters)" %
349 constants.MAX_TAG_LEN)
350 if not tag:
351 raise errors.TagError("Tags cannot be empty")
352 if not cls.VALID_TAG_RE.match(tag):
353 raise errors.TagError("Tag contains invalid characters")
354
363
373
384
386 """Taggable-object-specific conversion to standard python types.
387
388 This replaces the tags set with a list.
389
390 """
391 bo = super(TaggableObject, self).ToDict()
392
393 tags = bo.get("tags", None)
394 if isinstance(tags, set):
395 bo["tags"] = list(tags)
396 return bo
397
398 @classmethod
400 """Custom function for instances.
401
402 """
403 obj = super(TaggableObject, cls).FromDict(val)
404 if hasattr(obj, "tags") and isinstance(obj.tags, list):
405 obj.tags = set(obj.tags)
406 return obj
407
410 """Network configuration parameters for the master
411
412 @ivar name: master name
413 @ivar ip: master IP
414 @ivar netmask: master netmask
415 @ivar netdev: master network device
416 @ivar ip_family: master IP family
417
418 """
419 __slots__ = [
420 "name",
421 "ip",
422 "netmask",
423 "netdev",
424 "ip_family",
425 ]
426
429 """Top-level config object."""
430 __slots__ = [
431 "version",
432 "cluster",
433 "nodes",
434 "nodegroups",
435 "instances",
436 "networks",
437 "serial_no",
438 ] + _TIMESTAMPS
439
441 """Custom function for top-level config data.
442
443 This just replaces the list of instances, nodes and the cluster
444 with standard python types.
445
446 """
447 mydict = super(ConfigData, self).ToDict()
448 mydict["cluster"] = mydict["cluster"].ToDict()
449 for key in "nodes", "instances", "nodegroups", "networks":
450 mydict[key] = self._ContainerToDicts(mydict[key])
451
452 return mydict
453
454 @classmethod
466
468 """Check if in there is at disk of the given type in the configuration.
469
470 @type dev_type: L{constants.LDS_BLOCK}
471 @param dev_type: the type to look for
472 @rtype: boolean
473 @return: boolean indicating if a disk of the given type was found or not
474
475 """
476 for instance in self.instances.values():
477 for disk in instance.disks:
478 if disk.IsBasedOnDiskType(dev_type):
479 return True
480 return False
481
505
506
507 -class NIC(ConfigObject):
508 """Config object representing a network card."""
509 __slots__ = ["mac", "ip", "network", "nicparams", "netinfo"]
510
511 @classmethod
528
529
530 -class Disk(ConfigObject):
531 """Config object representing a block device."""
532 __slots__ = ["dev_type", "logical_id", "physical_id",
533 "children", "iv_name", "size", "mode", "params"]
534
538
542
544 """Test if this device needs to be opened on a secondary node."""
545 return self.dev_type in (constants.LD_LV,)
546
548 """Return the device path if this device type has a static one.
549
550 Some devices (LVM for example) live always at the same /dev/ path,
551 irrespective of their status. For such devices, we return this
552 path, for others we return None.
553
554 @warning: The path returned is not a normalized pathname; callers
555 should check that it is a valid path.
556
557 """
558 if self.dev_type == constants.LD_LV:
559 return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
560 elif self.dev_type == constants.LD_BLOCKDEV:
561 return self.logical_id[1]
562 elif self.dev_type == constants.LD_RBD:
563 return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
564 return None
565
567 """Compute the needed number of children for activation.
568
569 This method will return either -1 (all children) or a positive
570 number denoting the minimum number of children needed for
571 activation (only mirrored devices will usually return >=0).
572
573 Currently, only DRBD8 supports diskless activation (therefore we
574 return 0), for all other we keep the previous semantics and return
575 -1.
576
577 """
578 if self.dev_type == constants.LD_DRBD8:
579 return 0
580 return -1
581
583 """Check if the disk or its children are based on the given type.
584
585 @type dev_type: L{constants.LDS_BLOCK}
586 @param dev_type: the type to look for
587 @rtype: boolean
588 @return: boolean indicating if a device of the given type was found or not
589
590 """
591 if self.children:
592 for child in self.children:
593 if child.IsBasedOnDiskType(dev_type):
594 return True
595 return self.dev_type == dev_type
596
598 """This function returns the nodes this device lives on.
599
600 Given the node on which the parent of the device lives on (or, in
601 case of a top-level device, the primary node of the devices'
602 instance), this function will return a list of nodes on which this
603 devices needs to (or can) be assembled.
604
605 """
606 if self.dev_type in [constants.LD_LV, constants.LD_FILE,
607 constants.LD_BLOCKDEV, constants.LD_RBD,
608 constants.LD_EXT]:
609 result = [node]
610 elif self.dev_type in constants.LDS_DRBD:
611 result = [self.logical_id[0], self.logical_id[1]]
612 if node not in result:
613 raise errors.ConfigurationError("DRBD device passed unknown node")
614 else:
615 raise errors.ProgrammerError("Unhandled device type %s" % self.dev_type)
616 return result
617
619 """Compute the node/disk tree for this disk and its children.
620
621 This method, given the node on which the parent disk lives, will
622 return the list of all (node, disk) pairs which describe the disk
623 tree in the most compact way. For example, a drbd/lvm stack
624 will be returned as (primary_node, drbd) and (secondary_node, drbd)
625 which represents all the top-level devices on the nodes.
626
627 """
628 my_nodes = self.GetNodes(parent_node)
629 result = [(node, self) for node in my_nodes]
630 if not self.children:
631
632 return result
633 for node in my_nodes:
634 for child in self.children:
635 child_result = child.ComputeNodeTree(node)
636 if len(child_result) == 1:
637
638
639
640 continue
641 else:
642
643
644
645 for subnode, subdisk in child_result:
646 if subnode not in my_nodes:
647 result.append((subnode, subdisk))
648
649
650
651 return result
652
654 """Compute the per-VG growth requirements.
655
656 This only works for VG-based disks.
657
658 @type amount: integer
659 @param amount: the desired increase in (user-visible) disk space
660 @rtype: dict
661 @return: a dictionary of volume-groups and the required size
662
663 """
664 if self.dev_type == constants.LD_LV:
665 return {self.logical_id[0]: amount}
666 elif self.dev_type == constants.LD_DRBD8:
667 if self.children:
668 return self.children[0].ComputeGrowth(amount)
669 else:
670 return {}
671 else:
672
673 return {}
674
676 """Update the size of this disk after growth.
677
678 This method recurses over the disks's children and updates their
679 size correspondigly. The method needs to be kept in sync with the
680 actual algorithms from bdev.
681
682 """
683 if self.dev_type in (constants.LD_LV, constants.LD_FILE,
684 constants.LD_RBD, constants.LD_EXT):
685 self.size += amount
686 elif self.dev_type == constants.LD_DRBD8:
687 if self.children:
688 self.children[0].RecordGrow(amount)
689 self.size += amount
690 else:
691 raise errors.ProgrammerError("Disk.RecordGrow called for unsupported"
692 " disk type %s" % self.dev_type)
693
694 - def Update(self, size=None, mode=None):
695 """Apply changes to size and mode.
696
697 """
698 if self.dev_type == constants.LD_DRBD8:
699 if self.children:
700 self.children[0].Update(size=size, mode=mode)
701 else:
702 assert not self.children
703
704 if size is not None:
705 self.size = size
706 if mode is not None:
707 self.mode = mode
708
710 """Sets recursively the size to zero for the disk and its children.
711
712 """
713 if self.children:
714 for child in self.children:
715 child.UnsetSize()
716 self.size = 0
717
719 """Convert the logical ID to the physical ID.
720
721 This is used only for drbd, which needs ip/port configuration.
722
723 The routine descends down and updates its children also, because
724 this helps when the only the top device is passed to the remote
725 node.
726
727 Arguments:
728 - target_node: the node we wish to configure for
729 - nodes_ip: a mapping of node name to ip
730
731 The target_node must exist in in nodes_ip, and must be one of the
732 nodes in the logical ID for each of the DRBD devices encountered
733 in the disk tree.
734
735 """
736 if self.children:
737 for child in self.children:
738 child.SetPhysicalID(target_node, nodes_ip)
739
740 if self.logical_id is None and self.physical_id is not None:
741 return
742 if self.dev_type in constants.LDS_DRBD:
743 pnode, snode, port, pminor, sminor, secret = self.logical_id
744 if target_node not in (pnode, snode):
745 raise errors.ConfigurationError("DRBD device not knowing node %s" %
746 target_node)
747 pnode_ip = nodes_ip.get(pnode, None)
748 snode_ip = nodes_ip.get(snode, None)
749 if pnode_ip is None or snode_ip is None:
750 raise errors.ConfigurationError("Can't find primary or secondary node"
751 " for %s" % str(self))
752 p_data = (pnode_ip, port)
753 s_data = (snode_ip, port)
754 if pnode == target_node:
755 self.physical_id = p_data + s_data + (pminor, secret)
756 else:
757 self.physical_id = s_data + p_data + (sminor, secret)
758 else:
759 self.physical_id = self.logical_id
760 return
761
763 """Disk-specific conversion to standard python types.
764
765 This replaces the children lists of objects with lists of
766 standard python types.
767
768 """
769 bo = super(Disk, self).ToDict()
770
771 for attr in ("children",):
772 alist = bo.get(attr, None)
773 if alist:
774 bo[attr] = self._ContainerToDicts(alist)
775 return bo
776
777 @classmethod
779 """Custom function for Disks
780
781 """
782 obj = super(Disk, cls).FromDict(val)
783 if obj.children:
784 obj.children = cls._ContainerFromDicts(obj.children, list, Disk)
785 if obj.logical_id and isinstance(obj.logical_id, list):
786 obj.logical_id = tuple(obj.logical_id)
787 if obj.physical_id and isinstance(obj.physical_id, list):
788 obj.physical_id = tuple(obj.physical_id)
789 if obj.dev_type in constants.LDS_DRBD:
790
791 if len(obj.logical_id) < 6:
792 obj.logical_id += (None,) * (6 - len(obj.logical_id))
793 return obj
794
796 """Custom str() formatter for disks.
797
798 """
799 if self.dev_type == constants.LD_LV:
800 val = "<LogicalVolume(/dev/%s/%s" % self.logical_id
801 elif self.dev_type in constants.LDS_DRBD:
802 node_a, node_b, port, minor_a, minor_b = self.logical_id[:5]
803 val = "<DRBD8("
804 if self.physical_id is None:
805 phy = "unconfigured"
806 else:
807 phy = ("configured as %s:%s %s:%s" %
808 (self.physical_id[0], self.physical_id[1],
809 self.physical_id[2], self.physical_id[3]))
810
811 val += ("hosts=%s/%d-%s/%d, port=%s, %s, " %
812 (node_a, minor_a, node_b, minor_b, port, phy))
813 if self.children and self.children.count(None) == 0:
814 val += "backend=%s, metadev=%s" % (self.children[0], self.children[1])
815 else:
816 val += "no local storage"
817 else:
818 val = ("<Disk(type=%s, logical_id=%s, physical_id=%s, children=%s" %
819 (self.dev_type, self.logical_id, self.physical_id, self.children))
820 if self.iv_name is None:
821 val += ", not visible"
822 else:
823 val += ", visible as /dev/%s" % self.iv_name
824 if isinstance(self.size, int):
825 val += ", size=%dm)>" % self.size
826 else:
827 val += ", size='%s')>" % (self.size,)
828 return val
829
831 """Checks that this disk is correctly configured.
832
833 """
834 all_errors = []
835 if self.mode not in constants.DISK_ACCESS_SET:
836 all_errors.append("Disk access mode '%s' is invalid" % (self.mode, ))
837 return all_errors
838
840 """Fill defaults for missing configuration values.
841
842 """
843 if self.children:
844 for child in self.children:
845 child.UpgradeConfig()
846
847
848 self.params = {}
849
850
851 @staticmethod
853 """Computes Logical Disk parameters from Disk Template parameters.
854
855 @type disk_template: string
856 @param disk_template: disk template, one of L{constants.DISK_TEMPLATES}
857 @type disk_params: dict
858 @param disk_params: disk template parameters;
859 dict(template_name -> parameters
860 @rtype: list(dict)
861 @return: a list of dicts, one for each node of the disk hierarchy. Each dict
862 contains the LD parameters of the node. The tree is flattened in-order.
863
864 """
865 if disk_template not in constants.DISK_TEMPLATES:
866 raise errors.ProgrammerError("Unknown disk template %s" % disk_template)
867
868 assert disk_template in disk_params
869
870 result = list()
871 dt_params = disk_params[disk_template]
872 if disk_template == constants.DT_DRBD8:
873 result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_DRBD8], {
874 constants.LDP_RESYNC_RATE: dt_params[constants.DRBD_RESYNC_RATE],
875 constants.LDP_BARRIERS: dt_params[constants.DRBD_DISK_BARRIERS],
876 constants.LDP_NO_META_FLUSH: dt_params[constants.DRBD_META_BARRIERS],
877 constants.LDP_DEFAULT_METAVG: dt_params[constants.DRBD_DEFAULT_METAVG],
878 constants.LDP_DISK_CUSTOM: dt_params[constants.DRBD_DISK_CUSTOM],
879 constants.LDP_NET_CUSTOM: dt_params[constants.DRBD_NET_CUSTOM],
880 constants.LDP_DYNAMIC_RESYNC: dt_params[constants.DRBD_DYNAMIC_RESYNC],
881 constants.LDP_PLAN_AHEAD: dt_params[constants.DRBD_PLAN_AHEAD],
882 constants.LDP_FILL_TARGET: dt_params[constants.DRBD_FILL_TARGET],
883 constants.LDP_DELAY_TARGET: dt_params[constants.DRBD_DELAY_TARGET],
884 constants.LDP_MAX_RATE: dt_params[constants.DRBD_MAX_RATE],
885 constants.LDP_MIN_RATE: dt_params[constants.DRBD_MIN_RATE],
886 }))
887
888
889 result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV], {
890 constants.LDP_STRIPES: dt_params[constants.DRBD_DATA_STRIPES],
891 }))
892
893
894 result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV], {
895 constants.LDP_STRIPES: dt_params[constants.DRBD_META_STRIPES],
896 }))
897
898 elif disk_template in (constants.DT_FILE, constants.DT_SHARED_FILE):
899 result.append(constants.DISK_LD_DEFAULTS[constants.LD_FILE])
900
901 elif disk_template == constants.DT_PLAIN:
902 result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_LV], {
903 constants.LDP_STRIPES: dt_params[constants.LV_STRIPES],
904 }))
905
906 elif disk_template == constants.DT_BLOCK:
907 result.append(constants.DISK_LD_DEFAULTS[constants.LD_BLOCKDEV])
908
909 elif disk_template == constants.DT_RBD:
910 result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.LD_RBD], {
911 constants.LDP_POOL: dt_params[constants.RBD_POOL],
912 }))
913
914 elif disk_template == constants.DT_EXT:
915 result.append(constants.DISK_LD_DEFAULTS[constants.LD_EXT])
916
917 return result
918
921 """Config object representing instance policy limits dictionary.
922
923
924 Note that this object is not actually used in the config, it's just
925 used as a placeholder for a few functions.
926
927 """
928 @classmethod
944
945 @classmethod
947 """Check the instance policy for validity on a given key.
948
949 We check if the instance policy makes sense for a given key, that is
950 if ipolicy[min][name] <= ipolicy[std][name] <= ipolicy[max][name].
951
952 @type ipolicy: dict
953 @param ipolicy: dictionary with min, max, std specs
954 @type name: string
955 @param name: what are the limits for
956 @type check_std: bool
957 @param check_std: Whether to check std value or just assume compliance
958 @raise errors.ConfigureError: when specs for given name are not valid
959
960 """
961 min_v = ipolicy[constants.ISPECS_MIN].get(name, 0)
962
963 if check_std:
964 std_v = ipolicy[constants.ISPECS_STD].get(name, min_v)
965 std_msg = std_v
966 else:
967 std_v = min_v
968 std_msg = "-"
969
970 max_v = ipolicy[constants.ISPECS_MAX].get(name, std_v)
971 err = ("Invalid specification of min/max/std values for %s: %s/%s/%s" %
972 (name,
973 ipolicy[constants.ISPECS_MIN].get(name, "-"),
974 ipolicy[constants.ISPECS_MAX].get(name, "-"),
975 std_msg))
976 if min_v > std_v or std_v > max_v:
977 raise errors.ConfigurationError(err)
978
979 @classmethod
991
992 @classmethod
994 """Checks a parameter.
995
996 Currently we expect all parameters to be float values.
997
998 """
999 try:
1000 float(value)
1001 except (TypeError, ValueError), err:
1002 raise errors.ConfigurationError("Invalid value for key" " '%s':"
1003 " '%s', error: %s" % (key, value, err))
1004
1007 """Config object representing an instance."""
1008 __slots__ = [
1009 "name",
1010 "primary_node",
1011 "os",
1012 "hypervisor",
1013 "hvparams",
1014 "beparams",
1015 "osparams",
1016 "admin_state",
1017 "nics",
1018 "disks",
1019 "disk_template",
1020 "network_port",
1021 "serial_no",
1022 ] + _TIMESTAMPS + _UUID
1023
1025 """Compute the list of secondary nodes.
1026
1027 This is a simple wrapper over _ComputeAllNodes.
1028
1029 """
1030 all_nodes = set(self._ComputeAllNodes())
1031 all_nodes.discard(self.primary_node)
1032 return tuple(all_nodes)
1033
1034 secondary_nodes = property(_ComputeSecondaryNodes, None, None,
1035 "List of names of secondary nodes")
1036
1038 """Compute the list of all nodes.
1039
1040 Since the data is already there (in the drbd disks), keeping it as
1041 a separate normal attribute is redundant and if not properly
1042 synchronised can cause problems. Thus it's better to compute it
1043 dynamically.
1044
1045 """
1046 def _Helper(nodes, device):
1047 """Recursively computes nodes given a top device."""
1048 if device.dev_type in constants.LDS_DRBD:
1049 nodea, nodeb = device.logical_id[:2]
1050 nodes.add(nodea)
1051 nodes.add(nodeb)
1052 if device.children:
1053 for child in device.children:
1054 _Helper(nodes, child)
1055
1056 all_nodes = set()
1057 all_nodes.add(self.primary_node)
1058 for device in self.disks:
1059 _Helper(all_nodes, device)
1060 return tuple(all_nodes)
1061
1062 all_nodes = property(_ComputeAllNodes, None, None,
1063 "List of names of all the nodes of the instance")
1064
1066 """Provide a mapping of nodes to LVs this instance owns.
1067
1068 This function figures out what logical volumes should belong on
1069 which nodes, recursing through a device tree.
1070
1071 @param lvmap: optional dictionary to receive the
1072 'node' : ['lv', ...] data.
1073
1074 @return: None if lvmap arg is given, otherwise, a dictionary of
1075 the form { 'nodename' : ['volume1', 'volume2', ...], ... };
1076 volumeN is of the form "vg_name/lv_name", compatible with
1077 GetVolumeList()
1078
1079 """
1080 if node is None:
1081 node = self.primary_node
1082
1083 if lvmap is None:
1084 lvmap = {
1085 node: [],
1086 }
1087 ret = lvmap
1088 else:
1089 if not node in lvmap:
1090 lvmap[node] = []
1091 ret = None
1092
1093 if not devs:
1094 devs = self.disks
1095
1096 for dev in devs:
1097 if dev.dev_type == constants.LD_LV:
1098 lvmap[node].append(dev.logical_id[0] + "/" + dev.logical_id[1])
1099
1100 elif dev.dev_type in constants.LDS_DRBD:
1101 if dev.children:
1102 self.MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
1103 self.MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
1104
1105 elif dev.children:
1106 self.MapLVsByNode(lvmap, dev.children, node)
1107
1108 return ret
1109
1111 """Find a disk given having a specified index.
1112
1113 This is just a wrapper that does validation of the index.
1114
1115 @type idx: int
1116 @param idx: the disk index
1117 @rtype: L{Disk}
1118 @return: the corresponding disk
1119 @raise errors.OpPrereqError: when the given index is not valid
1120
1121 """
1122 try:
1123 idx = int(idx)
1124 return self.disks[idx]
1125 except (TypeError, ValueError), err:
1126 raise errors.OpPrereqError("Invalid disk index: '%s'" % str(err),
1127 errors.ECODE_INVAL)
1128 except IndexError:
1129 raise errors.OpPrereqError("Invalid disk index: %d (instace has disks"
1130 " 0 to %d" % (idx, len(self.disks) - 1),
1131 errors.ECODE_INVAL)
1132
1134 """Instance-specific conversion to standard python types.
1135
1136 This replaces the children lists of objects with lists of standard
1137 python types.
1138
1139 """
1140 bo = super(Instance, self).ToDict()
1141
1142 for attr in "nics", "disks":
1143 alist = bo.get(attr, None)
1144 if alist:
1145 nlist = self._ContainerToDicts(alist)
1146 else:
1147 nlist = []
1148 bo[attr] = nlist
1149 return bo
1150
1151 @classmethod
1167
1169 """Fill defaults for missing configuration values.
1170
1171 """
1172 for nic in self.nics:
1173 nic.UpgradeConfig()
1174 for disk in self.disks:
1175 disk.UpgradeConfig()
1176 if self.hvparams:
1177 for key in constants.HVC_GLOBALS:
1178 try:
1179 del self.hvparams[key]
1180 except KeyError:
1181 pass
1182 if self.osparams is None:
1183 self.osparams = {}
1184 UpgradeBeParams(self.beparams)
1185
1186
1187 -class OS(ConfigObject):
1188 """Config object representing an operating system.
1189
1190 @type supported_parameters: list
1191 @ivar supported_parameters: a list of tuples, name and description,
1192 containing the supported parameters by this OS
1193
1194 @type VARIANT_DELIM: string
1195 @cvar VARIANT_DELIM: the variant delimiter
1196
1197 """
1198 __slots__ = [
1199 "name",
1200 "path",
1201 "api_versions",
1202 "create_script",
1203 "export_script",
1204 "import_script",
1205 "rename_script",
1206 "verify_script",
1207 "supported_variants",
1208 "supported_parameters",
1209 ]
1210
1211 VARIANT_DELIM = "+"
1212
1213 @classmethod
1215 """Splits the name into the proper name and variant.
1216
1217 @param name: the OS (unprocessed) name
1218 @rtype: list
1219 @return: a list of two elements; if the original name didn't
1220 contain a variant, it's returned as an empty string
1221
1222 """
1223 nv = name.split(cls.VARIANT_DELIM, 1)
1224 if len(nv) == 1:
1225 nv.append("")
1226 return nv
1227
1228 @classmethod
1230 """Returns the proper name of the os (without the variant).
1231
1232 @param name: the OS (unprocessed) name
1233
1234 """
1235 return cls.SplitNameVariant(name)[0]
1236
1237 @classmethod
1239 """Returns the variant the os (without the base name).
1240
1241 @param name: the OS (unprocessed) name
1242
1243 """
1244 return cls.SplitNameVariant(name)[1]
1245
1248 """Config object representing an External Storage Provider.
1249
1250 """
1251 __slots__ = [
1252 "name",
1253 "path",
1254 "create_script",
1255 "remove_script",
1256 "grow_script",
1257 "attach_script",
1258 "detach_script",
1259 "setinfo_script",
1260 "verify_script",
1261 "supported_parameters",
1262 ]
1263
1266 """Hypvervisor state on a node.
1267
1268 @ivar mem_total: Total amount of memory
1269 @ivar mem_node: Memory used by, or reserved for, the node itself (not always
1270 available)
1271 @ivar mem_hv: Memory used by hypervisor or lost due to instance allocation
1272 rounding
1273 @ivar mem_inst: Memory used by instances living on node
1274 @ivar cpu_total: Total node CPU core count
1275 @ivar cpu_node: Number of CPU cores reserved for the node itself
1276
1277 """
1278 __slots__ = [
1279 "mem_total",
1280 "mem_node",
1281 "mem_hv",
1282 "mem_inst",
1283 "cpu_total",
1284 "cpu_node",
1285 ] + _TIMESTAMPS
1286
1289 """Disk state on a node.
1290
1291 """
1292 __slots__ = [
1293 "total",
1294 "reserved",
1295 "overhead",
1296 ] + _TIMESTAMPS
1297
1298
1299 -class Node(TaggableObject):
1300 """Config object representing a node.
1301
1302 @ivar hv_state: Hypervisor state (e.g. number of CPUs)
1303 @ivar hv_state_static: Hypervisor state overriden by user
1304 @ivar disk_state: Disk state (e.g. free space)
1305 @ivar disk_state_static: Disk state overriden by user
1306
1307 """
1308 __slots__ = [
1309 "name",
1310 "primary_ip",
1311 "secondary_ip",
1312 "serial_no",
1313 "master_candidate",
1314 "offline",
1315 "drained",
1316 "group",
1317 "master_capable",
1318 "vm_capable",
1319 "ndparams",
1320 "powered",
1321 "hv_state",
1322 "hv_state_static",
1323 "disk_state",
1324 "disk_state_static",
1325 ] + _TIMESTAMPS + _UUID
1326
1328 """Fill defaults for missing configuration values.
1329
1330 """
1331
1332
1333 if self.master_capable is None:
1334 self.master_capable = True
1335
1336 if self.vm_capable is None:
1337 self.vm_capable = True
1338
1339 if self.ndparams is None:
1340 self.ndparams = {}
1341
1342 for key in constants.NDC_GLOBALS:
1343 if key in self.ndparams:
1344 logging.warning("Ignoring %s node parameter for node %s",
1345 key, self.name)
1346 del self.ndparams[key]
1347
1348 if self.powered is None:
1349 self.powered = True
1350
1352 """Custom function for serializing.
1353
1354 """
1355 data = super(Node, self).ToDict()
1356
1357 hv_state = data.get("hv_state", None)
1358 if hv_state is not None:
1359 data["hv_state"] = self._ContainerToDicts(hv_state)
1360
1361 disk_state = data.get("disk_state", None)
1362 if disk_state is not None:
1363 data["disk_state"] = \
1364 dict((key, self._ContainerToDicts(value))
1365 for (key, value) in disk_state.items())
1366
1367 return data
1368
1369 @classmethod
1371 """Custom function for deserializing.
1372
1373 """
1374 obj = super(Node, cls).FromDict(val)
1375
1376 if obj.hv_state is not None:
1377 obj.hv_state = cls._ContainerFromDicts(obj.hv_state, dict, NodeHvState)
1378
1379 if obj.disk_state is not None:
1380 obj.disk_state = \
1381 dict((key, cls._ContainerFromDicts(value, dict, NodeDiskState))
1382 for (key, value) in obj.disk_state.items())
1383
1384 return obj
1385
1388 """Config object representing a node group."""
1389 __slots__ = [
1390 "name",
1391 "members",
1392 "ndparams",
1393 "diskparams",
1394 "ipolicy",
1395 "serial_no",
1396 "hv_state_static",
1397 "disk_state_static",
1398 "alloc_policy",
1399 "networks",
1400 ] + _TIMESTAMPS + _UUID
1401
1403 """Custom function for nodegroup.
1404
1405 This discards the members object, which gets recalculated and is only kept
1406 in memory.
1407
1408 """
1409 mydict = super(NodeGroup, self).ToDict()
1410 del mydict["members"]
1411 return mydict
1412
1413 @classmethod
1415 """Custom function for nodegroup.
1416
1417 The members slot is initialized to an empty list, upon deserialization.
1418
1419 """
1420 obj = super(NodeGroup, cls).FromDict(val)
1421 obj.members = []
1422 return obj
1423
1425 """Fill defaults for missing configuration values.
1426
1427 """
1428 if self.ndparams is None:
1429 self.ndparams = {}
1430
1431 if self.serial_no is None:
1432 self.serial_no = 1
1433
1434 if self.alloc_policy is None:
1435 self.alloc_policy = constants.ALLOC_POLICY_PREFERRED
1436
1437
1438
1439 if self.mtime is None:
1440 self.mtime = time.time()
1441
1442 if self.diskparams is None:
1443 self.diskparams = {}
1444 if self.ipolicy is None:
1445 self.ipolicy = MakeEmptyIPolicy()
1446
1447 if self.networks is None:
1448 self.networks = {}
1449
1451 """Return filled out ndparams for L{objects.Node}
1452
1453 @type node: L{objects.Node}
1454 @param node: A Node object to fill
1455 @return a copy of the node's ndparams with defaults filled
1456
1457 """
1458 return self.SimpleFillND(node.ndparams)
1459
1461 """Fill a given ndparams dict with defaults.
1462
1463 @type ndparams: dict
1464 @param ndparams: the dict to fill
1465 @rtype: dict
1466 @return: a copy of the passed in ndparams with missing keys filled
1467 from the node group defaults
1468
1469 """
1470 return FillDict(self.ndparams, ndparams)
1471
1472
1473 -class Cluster(TaggableObject):
1474 """Config object representing the cluster."""
1475 __slots__ = [
1476 "serial_no",
1477 "rsahostkeypub",
1478 "highest_used_port",
1479 "tcpudp_port_pool",
1480 "mac_prefix",
1481 "volume_group_name",
1482 "reserved_lvs",
1483 "drbd_usermode_helper",
1484 "default_bridge",
1485 "default_hypervisor",
1486 "master_node",
1487 "master_ip",
1488 "master_netdev",
1489 "master_netmask",
1490 "use_external_mip_script",
1491 "cluster_name",
1492 "file_storage_dir",
1493 "shared_file_storage_dir",
1494 "enabled_hypervisors",
1495 "hvparams",
1496 "ipolicy",
1497 "os_hvp",
1498 "beparams",
1499 "osparams",
1500 "nicparams",
1501 "ndparams",
1502 "diskparams",
1503 "candidate_pool_size",
1504 "modify_etc_hosts",
1505 "modify_ssh_setup",
1506 "maintain_node_health",
1507 "uid_pool",
1508 "default_iallocator",
1509 "hidden_os",
1510 "blacklisted_os",
1511 "primary_ip_family",
1512 "prealloc_wipe_disks",
1513 "hv_state_static",
1514 "disk_state_static",
1515 ] + _TIMESTAMPS + _UUID
1516
1518 """Fill defaults for missing configuration values.
1519
1520 """
1521
1522
1523 if self.hvparams is None:
1524 self.hvparams = constants.HVC_DEFAULTS
1525 else:
1526 for hypervisor in self.hvparams:
1527 self.hvparams[hypervisor] = FillDict(
1528 constants.HVC_DEFAULTS[hypervisor], self.hvparams[hypervisor])
1529
1530 if self.os_hvp is None:
1531 self.os_hvp = {}
1532
1533
1534 if self.osparams is None:
1535 self.osparams = {}
1536
1537 self.ndparams = UpgradeNDParams(self.ndparams)
1538
1539 self.beparams = UpgradeGroupedParams(self.beparams,
1540 constants.BEC_DEFAULTS)
1541 for beparams_group in self.beparams:
1542 UpgradeBeParams(self.beparams[beparams_group])
1543
1544 migrate_default_bridge = not self.nicparams
1545 self.nicparams = UpgradeGroupedParams(self.nicparams,
1546 constants.NICC_DEFAULTS)
1547 if migrate_default_bridge:
1548 self.nicparams[constants.PP_DEFAULT][constants.NIC_LINK] = \
1549 self.default_bridge
1550
1551 if self.modify_etc_hosts is None:
1552 self.modify_etc_hosts = True
1553
1554 if self.modify_ssh_setup is None:
1555 self.modify_ssh_setup = True
1556
1557
1558
1559
1560 if self.default_bridge is not None:
1561 self.default_bridge = None
1562
1563
1564
1565 if self.default_hypervisor is not None:
1566 self.enabled_hypervisors = ([self.default_hypervisor] +
1567 [hvname for hvname in self.enabled_hypervisors
1568 if hvname != self.default_hypervisor])
1569 self.default_hypervisor = None
1570
1571
1572 if self.maintain_node_health is None:
1573 self.maintain_node_health = False
1574
1575 if self.uid_pool is None:
1576 self.uid_pool = []
1577
1578 if self.default_iallocator is None:
1579 self.default_iallocator = ""
1580
1581
1582 if self.reserved_lvs is None:
1583 self.reserved_lvs = []
1584
1585
1586 if self.hidden_os is None:
1587 self.hidden_os = []
1588
1589 if self.blacklisted_os is None:
1590 self.blacklisted_os = []
1591
1592
1593 if self.primary_ip_family is None:
1594 self.primary_ip_family = AF_INET
1595
1596 if self.master_netmask is None:
1597 ipcls = netutils.IPAddress.GetClassFromIpFamily(self.primary_ip_family)
1598 self.master_netmask = ipcls.iplen
1599
1600 if self.prealloc_wipe_disks is None:
1601 self.prealloc_wipe_disks = False
1602
1603
1604 if self.shared_file_storage_dir is None:
1605 self.shared_file_storage_dir = ""
1606
1607 if self.use_external_mip_script is None:
1608 self.use_external_mip_script = False
1609
1610 if self.diskparams:
1611 self.diskparams = UpgradeDiskParams(self.diskparams)
1612 else:
1613 self.diskparams = constants.DISK_DT_DEFAULTS.copy()
1614
1615
1616 if self.ipolicy is None:
1617 self.ipolicy = FillIPolicy(constants.IPOLICY_DEFAULTS, {})
1618 else:
1619
1620
1621
1622 wrongkeys = frozenset(self.ipolicy.keys()) - constants.IPOLICY_ALL_KEYS
1623 if wrongkeys:
1624
1625 msg = ("Cluster instance policy contains spurious keys: %s" %
1626 utils.CommaJoin(wrongkeys))
1627 raise errors.ConfigurationError(msg)
1628 self.ipolicy = FillIPolicy(constants.IPOLICY_DEFAULTS, self.ipolicy)
1629
1630 @property
1632 """The first hypervisor is the primary.
1633
1634 Useful, for example, for L{Node}'s hv/disk state.
1635
1636 """
1637 return self.enabled_hypervisors[0]
1638
1640 """Custom function for cluster.
1641
1642 """
1643 mydict = super(Cluster, self).ToDict()
1644 mydict["tcpudp_port_pool"] = list(self.tcpudp_port_pool)
1645 return mydict
1646
1647 @classmethod
1649 """Custom function for cluster.
1650
1651 """
1652 obj = super(Cluster, cls).FromDict(val)
1653 if not isinstance(obj.tcpudp_port_pool, set):
1654 obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
1655 return obj
1656
1658 """Fill a given diskparams dict with cluster defaults.
1659
1660 @param diskparams: The diskparams
1661 @return: The defaults dict
1662
1663 """
1664 return FillDiskParams(self.diskparams, diskparams)
1665
1666 - def GetHVDefaults(self, hypervisor, os_name=None, skip_keys=None):
1667 """Get the default hypervisor parameters for the cluster.
1668
1669 @param hypervisor: the hypervisor name
1670 @param os_name: if specified, we'll also update the defaults for this OS
1671 @param skip_keys: if passed, list of keys not to use
1672 @return: the defaults dict
1673
1674 """
1675 if skip_keys is None:
1676 skip_keys = []
1677
1678 fill_stack = [self.hvparams.get(hypervisor, {})]
1679 if os_name is not None:
1680 os_hvp = self.os_hvp.get(os_name, {}).get(hypervisor, {})
1681 fill_stack.append(os_hvp)
1682
1683 ret_dict = {}
1684 for o_dict in fill_stack:
1685 ret_dict = FillDict(ret_dict, o_dict, skip_keys=skip_keys)
1686
1687 return ret_dict
1688
1689 - def SimpleFillHV(self, hv_name, os_name, hvparams, skip_globals=False):
1690 """Fill a given hvparams dict with cluster defaults.
1691
1692 @type hv_name: string
1693 @param hv_name: the hypervisor to use
1694 @type os_name: string
1695 @param os_name: the OS to use for overriding the hypervisor defaults
1696 @type skip_globals: boolean
1697 @param skip_globals: if True, the global hypervisor parameters will
1698 not be filled
1699 @rtype: dict
1700 @return: a copy of the given hvparams with missing keys filled from
1701 the cluster defaults
1702
1703 """
1704 if skip_globals:
1705 skip_keys = constants.HVC_GLOBALS
1706 else:
1707 skip_keys = []
1708
1709 def_dict = self.GetHVDefaults(hv_name, os_name, skip_keys=skip_keys)
1710 return FillDict(def_dict, hvparams, skip_keys=skip_keys)
1711
1712 - def FillHV(self, instance, skip_globals=False):
1713 """Fill an instance's hvparams dict with cluster defaults.
1714
1715 @type instance: L{objects.Instance}
1716 @param instance: the instance parameter to fill
1717 @type skip_globals: boolean
1718 @param skip_globals: if True, the global hypervisor parameters will
1719 not be filled
1720 @rtype: dict
1721 @return: a copy of the instance's hvparams with missing keys filled from
1722 the cluster defaults
1723
1724 """
1725 return self.SimpleFillHV(instance.hypervisor, instance.os,
1726 instance.hvparams, skip_globals)
1727
1729 """Fill a given beparams dict with cluster defaults.
1730
1731 @type beparams: dict
1732 @param beparams: the dict to fill
1733 @rtype: dict
1734 @return: a copy of the passed in beparams with missing keys filled
1735 from the cluster defaults
1736
1737 """
1738 return FillDict(self.beparams.get(constants.PP_DEFAULT, {}), beparams)
1739
1741 """Fill an instance's beparams dict with cluster defaults.
1742
1743 @type instance: L{objects.Instance}
1744 @param instance: the instance parameter to fill
1745 @rtype: dict
1746 @return: a copy of the instance's beparams with missing keys filled from
1747 the cluster defaults
1748
1749 """
1750 return self.SimpleFillBE(instance.beparams)
1751
1753 """Fill a given nicparams dict with cluster defaults.
1754
1755 @type nicparams: dict
1756 @param nicparams: the dict to fill
1757 @rtype: dict
1758 @return: a copy of the passed in nicparams with missing keys filled
1759 from the cluster defaults
1760
1761 """
1762 return FillDict(self.nicparams.get(constants.PP_DEFAULT, {}), nicparams)
1763
1765 """Fill an instance's osparams dict with cluster defaults.
1766
1767 @type os_name: string
1768 @param os_name: the OS name to use
1769 @type os_params: dict
1770 @param os_params: the dict to fill with default values
1771 @rtype: dict
1772 @return: a copy of the instance's osparams with missing keys filled from
1773 the cluster defaults
1774
1775 """
1776 name_only = os_name.split("+", 1)[0]
1777
1778 result = self.osparams.get(name_only, {})
1779
1780 result = FillDict(result, self.osparams.get(os_name, {}))
1781
1782 return FillDict(result, os_params)
1783
1784 @staticmethod
1790
1791 @staticmethod
1797
1798 - def FillND(self, node, nodegroup):
1799 """Return filled out ndparams for L{objects.NodeGroup} and L{objects.Node}
1800
1801 @type node: L{objects.Node}
1802 @param node: A Node object to fill
1803 @type nodegroup: L{objects.NodeGroup}
1804 @param nodegroup: A Node object to fill
1805 @return a copy of the node's ndparams with defaults filled
1806
1807 """
1808 return self.SimpleFillND(nodegroup.FillND(node))
1809
1811 """Fill a given ndparams dict with defaults.
1812
1813 @type ndparams: dict
1814 @param ndparams: the dict to fill
1815 @rtype: dict
1816 @return: a copy of the passed in ndparams with missing keys filled
1817 from the cluster defaults
1818
1819 """
1820 return FillDict(self.ndparams, ndparams)
1821
1823 """ Fill instance policy dict with defaults.
1824
1825 @type ipolicy: dict
1826 @param ipolicy: the dict to fill
1827 @rtype: dict
1828 @return: a copy of passed ipolicy with missing keys filled from
1829 the cluster defaults
1830
1831 """
1832 return FillIPolicy(self.ipolicy, ipolicy)
1833
1836 """Config object representing the status of a block device."""
1837 __slots__ = [
1838 "dev_path",
1839 "major",
1840 "minor",
1841 "sync_percent",
1842 "estimated_time",
1843 "is_degraded",
1844 "ldisk_status",
1845 ]
1846
1849 """Config object representing the status of an import or export."""
1850 __slots__ = [
1851 "recent_output",
1852 "listen_port",
1853 "connected",
1854 "progress_mbytes",
1855 "progress_throughput",
1856 "progress_eta",
1857 "progress_percent",
1858 "exit_status",
1859 "error_message",
1860 ] + _TIMESTAMPS
1861
1864 """Options for import/export daemon
1865
1866 @ivar key_name: X509 key name (None for cluster certificate)
1867 @ivar ca_pem: Remote peer CA in PEM format (None for cluster certificate)
1868 @ivar compress: Compression method (one of L{constants.IEC_ALL})
1869 @ivar magic: Used to ensure the connection goes to the right disk
1870 @ivar ipv6: Whether to use IPv6
1871 @ivar connect_timeout: Number of seconds for establishing connection
1872
1873 """
1874 __slots__ = [
1875 "key_name",
1876 "ca_pem",
1877 "compress",
1878 "magic",
1879 "ipv6",
1880 "connect_timeout",
1881 ]
1882
1885 """Object holding a confd request.
1886
1887 @ivar protocol: confd protocol version
1888 @ivar type: confd query type
1889 @ivar query: query request
1890 @ivar rsalt: requested reply salt
1891
1892 """
1893 __slots__ = [
1894 "protocol",
1895 "type",
1896 "query",
1897 "rsalt",
1898 ]
1899
1902 """Object holding a confd reply.
1903
1904 @ivar protocol: confd protocol version
1905 @ivar status: reply status code (ok, error)
1906 @ivar answer: confd query reply
1907 @ivar serial: configuration serial number
1908
1909 """
1910 __slots__ = [
1911 "protocol",
1912 "status",
1913 "answer",
1914 "serial",
1915 ]
1916
1919 """Object holding a query field definition.
1920
1921 @ivar name: Field name
1922 @ivar title: Human-readable title
1923 @ivar kind: Field type
1924 @ivar doc: Human-readable description
1925
1926 """
1927 __slots__ = [
1928 "name",
1929 "title",
1930 "kind",
1931 "doc",
1932 ]
1933
1936 __slots__ = [
1937 "fields",
1938 ]
1939
1947
1948 @classmethod
1956
1959 """Object holding the response to a query.
1960
1961 @ivar fields: List of L{QueryFieldDefinition} objects
1962 @ivar data: Requested data
1963
1964 """
1965 __slots__ = [
1966 "data",
1967 ]
1968
1971 """Object holding a request for querying available fields.
1972
1973 """
1974 __slots__ = [
1975 "what",
1976 "fields",
1977 ]
1978
1981 """Object holding the response to a query for fields.
1982
1983 @ivar fields: List of L{QueryFieldDefinition} objects
1984
1985 """
1986 __slots__ = []
1987
1990 """Object holding the status of a migration.
1991
1992 """
1993 __slots__ = [
1994 "status",
1995 "transferred_ram",
1996 "total_ram",
1997 ]
1998
2001 """Object describing how to access the console of an instance.
2002
2003 """
2004 __slots__ = [
2005 "instance",
2006 "kind",
2007 "message",
2008 "host",
2009 "port",
2010 "user",
2011 "command",
2012 "display",
2013 ]
2014
2016 """Validates contents of this object.
2017
2018 """
2019 assert self.kind in constants.CONS_ALL, "Unknown console type"
2020 assert self.instance, "Missing instance name"
2021 assert self.message or self.kind in [constants.CONS_SSH,
2022 constants.CONS_SPICE,
2023 constants.CONS_VNC]
2024 assert self.host or self.kind == constants.CONS_MESSAGE
2025 assert self.port or self.kind in [constants.CONS_MESSAGE,
2026 constants.CONS_SSH]
2027 assert self.user or self.kind in [constants.CONS_MESSAGE,
2028 constants.CONS_SPICE,
2029 constants.CONS_VNC]
2030 assert self.command or self.kind in [constants.CONS_MESSAGE,
2031 constants.CONS_SPICE,
2032 constants.CONS_VNC]
2033 assert self.display or self.kind in [constants.CONS_MESSAGE,
2034 constants.CONS_SPICE,
2035 constants.CONS_SSH]
2036 return True
2037
2038
2039 -class Network(TaggableObject):
2040 """Object representing a network definition for ganeti.
2041
2042 """
2043 __slots__ = [
2044 "name",
2045 "serial_no",
2046 "mac_prefix",
2047 "network",
2048 "network6",
2049 "gateway",
2050 "gateway6",
2051 "reservations",
2052 "ext_reservations",
2053 ] + _TIMESTAMPS + _UUID
2054
2056 """Export a dictionary used by hooks with a network's information.
2057
2058 @type prefix: String
2059 @param prefix: Prefix to prepend to the dict entries
2060
2061 """
2062 result = {
2063 "%sNETWORK_NAME" % prefix: self.name,
2064 "%sNETWORK_UUID" % prefix: self.uuid,
2065 "%sNETWORK_TAGS" % prefix: " ".join(self.GetTags()),
2066 }
2067 if self.network:
2068 result["%sNETWORK_SUBNET" % prefix] = self.network
2069 if self.gateway:
2070 result["%sNETWORK_GATEWAY" % prefix] = self.gateway
2071 if self.network6:
2072 result["%sNETWORK_SUBNET6" % prefix] = self.network6
2073 if self.gateway6:
2074 result["%sNETWORK_GATEWAY6" % prefix] = self.gateway6
2075 if self.mac_prefix:
2076 result["%sNETWORK_MAC_PREFIX" % prefix] = self.mac_prefix
2077
2078 return result
2079
2080 @classmethod
2082 """Custom function for networks.
2083
2084 Remove deprecated network_type and family.
2085
2086 """
2087 if "network_type" in val:
2088 del val["network_type"]
2089 if "family" in val:
2090 del val["family"]
2091 obj = super(Network, cls).FromDict(val)
2092 return obj
2093
2096 """Simple wrapper over ConfigParse that allows serialization.
2097
2098 This class is basically ConfigParser.SafeConfigParser with two
2099 additional methods that allow it to serialize/unserialize to/from a
2100 buffer.
2101
2102 """
2104 """Dump this instance and return the string representation."""
2105 buf = StringIO()
2106 self.write(buf)
2107 return buf.getvalue()
2108
2109 @classmethod
2111 """Load data from a string."""
2112 buf = StringIO(data)
2113 cfp = cls()
2114 cfp.readfp(buf)
2115 return cfp
2116
2119 """Information about an LVM physical volume (PV).
2120
2121 @type name: string
2122 @ivar name: name of the PV
2123 @type vg_name: string
2124 @ivar vg_name: name of the volume group containing the PV
2125 @type size: float
2126 @ivar size: size of the PV in MiB
2127 @type free: float
2128 @ivar free: free space in the PV, in MiB
2129 @type attributes: string
2130 @ivar attributes: PV attributes
2131 @type lv_list: list of strings
2132 @ivar lv_list: names of the LVs hosted on the PV
2133 """
2134 __slots__ = [
2135 "name",
2136 "vg_name",
2137 "size",
2138 "free",
2139 "attributes",
2140 "lv_list"
2141 ]
2142
2144 """Is this PV empty?
2145
2146 """
2147 return self.size <= (self.free + 1)
2148
2150 """Is this PV allocatable?
2151
2152 """
2153 return ("a" in self.attributes)
2154