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