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