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
67 _TIMESTAMPS = ["ctime", "mtime"]
68 _UUID = ["uuid"]
69
70
71 -def FillDict(defaults_dict, custom_dict, skip_keys=None):
72 """Basic function to apply settings on top a default dict.
73
74 @type defaults_dict: dict
75 @param defaults_dict: dictionary holding the default values
76 @type custom_dict: dict
77 @param custom_dict: dictionary holding customized value
78 @type skip_keys: list
79 @param skip_keys: which keys not to fill
80 @rtype: dict
81 @return: dict with the 'full' values
82
83 """
84 ret_dict = copy.deepcopy(defaults_dict)
85 ret_dict.update(custom_dict)
86 if skip_keys:
87 for k in skip_keys:
88 if k in ret_dict:
89 del ret_dict[k]
90 return ret_dict
91
94 """Fills an instance policy with defaults.
95
96 """
97 assert frozenset(default_ipolicy.keys()) == constants.IPOLICY_ALL_KEYS
98 ret_dict = copy.deepcopy(custom_ipolicy)
99 for key in default_ipolicy:
100 if key not in ret_dict:
101 ret_dict[key] = copy.deepcopy(default_ipolicy[key])
102 elif key == constants.ISPECS_STD:
103 ret_dict[key] = FillDict(default_ipolicy[key], ret_dict[key])
104 return ret_dict
105
106
107 -def FillDiskParams(default_dparams, custom_dparams, skip_keys=None):
108 """Fills the disk parameter defaults.
109
110 @see: L{FillDict} for parameters and return value
111
112 """
113 assert frozenset(default_dparams.keys()) == constants.DISK_TEMPLATES
114
115 return dict((dt, FillDict(default_dparams[dt], 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 "serial_no",
419 ] + _TIMESTAMPS
420
421 - def ToDict(self, _with_private=False):
422 """Custom function for top-level config data.
423
424 This just replaces the list of nodes, instances, nodegroups,
425 networks, disks and the cluster with standard python types.
426
427 """
428 mydict = super(ConfigData, self).ToDict(_with_private=_with_private)
429 mydict["cluster"] = mydict["cluster"].ToDict()
430 for key in "nodes", "instances", "nodegroups", "networks", "disks":
431 mydict[key] = outils.ContainerToDicts(mydict[key])
432
433 return mydict
434
435 @classmethod
437 """Custom function for top-level config data
438
439 """
440 obj = super(ConfigData, cls).FromDict(val)
441 obj.cluster = Cluster.FromDict(obj.cluster)
442 obj.nodes = outils.ContainerFromDicts(obj.nodes, dict, Node)
443 obj.instances = \
444 outils.ContainerFromDicts(obj.instances, dict, Instance)
445 obj.nodegroups = \
446 outils.ContainerFromDicts(obj.nodegroups, dict, NodeGroup)
447 obj.networks = outils.ContainerFromDicts(obj.networks, dict, Network)
448 obj.disks = outils.ContainerFromDicts(obj.disks, dict, Disk)
449 return obj
450
452 """Check if in there is at disk of the given type in the configuration.
453
454 @type dev_type: L{constants.DTS_BLOCK}
455 @param dev_type: the type to look for
456 @rtype: boolean
457 @return: boolean indicating if a disk of the given type was found or not
458
459 """
460 for disk in self.disks.values():
461 if disk.IsBasedOnDiskType(dev_type):
462 return True
463 return False
464
490
492 """Upgrade the cluster's enabled disk templates by inspecting the currently
493 enabled and/or used disk templates.
494
495 """
496 if not self.cluster.enabled_disk_templates:
497 template_set = \
498 set([inst.disk_template for inst in self.instances.values()])
499
500 if self.cluster.volume_group_name:
501 template_set.add(constants.DT_DRBD8)
502 template_set.add(constants.DT_PLAIN)
503
504
505
506 self.cluster.enabled_disk_templates = []
507 for preferred_template in constants.DISK_TEMPLATE_PREFERENCE:
508 if preferred_template in template_set:
509 self.cluster.enabled_disk_templates.append(preferred_template)
510 template_set.remove(preferred_template)
511 self.cluster.enabled_disk_templates.extend(list(template_set))
512 InstancePolicy.UpgradeDiskTemplates(
513 self.cluster.ipolicy, self.cluster.enabled_disk_templates)
514
515
516 -class NIC(ConfigObject):
517 """Config object representing a network card."""
518 __slots__ = ["name", "mac", "ip", "network",
519 "nicparams", "netinfo", "pci"] + _UUID
520
521 @classmethod
538
539
540 -class Disk(ConfigObject):
541 """Config object representing a block device."""
542 __slots__ = [
543 "name",
544 "dev_type",
545 "logical_id",
546 "children",
547 "iv_name",
548 "size",
549 "mode",
550 "params",
551 "spindles",
552 "pci",
553 "serial_no",
554
555
556 "dynamic_params"
557 ] + _UUID + _TIMESTAMPS
558
560 """Compute the list of all nodes covered by a device and its children."""
561 def _Helper(nodes, device):
562 """Recursively compute nodes given a top device."""
563 if device.dev_type in constants.DTS_DRBD:
564 nodes.extend(device.logical_id[:2])
565 if device.children:
566 for child in device.children:
567 _Helper(nodes, child)
568
569 all_nodes = list()
570 _Helper(all_nodes, self)
571 return tuple(set(all_nodes))
572
573 all_nodes = property(_ComputeAllNodes, None, None,
574 "List of names of all the nodes of a disk")
575
579
583
585 """Test if this device needs to be opened on a secondary node."""
586 return self.dev_type in (constants.DT_PLAIN,)
587
589 """Return the device path if this device type has a static one.
590
591 Some devices (LVM for example) live always at the same /dev/ path,
592 irrespective of their status. For such devices, we return this
593 path, for others we return None.
594
595 @warning: The path returned is not a normalized pathname; callers
596 should check that it is a valid path.
597
598 """
599 if self.dev_type == constants.DT_PLAIN:
600 return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
601 elif self.dev_type == constants.DT_BLOCK:
602 return self.logical_id[1]
603 elif self.dev_type == constants.DT_RBD:
604 return "/dev/%s/%s" % (self.logical_id[0], self.logical_id[1])
605 return None
606
608 """Compute the needed number of children for activation.
609
610 This method will return either -1 (all children) or a positive
611 number denoting the minimum number of children needed for
612 activation (only mirrored devices will usually return >=0).
613
614 Currently, only DRBD8 supports diskless activation (therefore we
615 return 0), for all other we keep the previous semantics and return
616 -1.
617
618 """
619 if self.dev_type == constants.DT_DRBD8:
620 return 0
621 return -1
622
624 """Check if the disk or its children are based on the given type.
625
626 @type dev_type: L{constants.DTS_BLOCK}
627 @param dev_type: the type to look for
628 @rtype: boolean
629 @return: boolean indicating if a device of the given type was found or not
630
631 """
632 if self.children:
633 for child in self.children:
634 if child.IsBasedOnDiskType(dev_type):
635 return True
636 return self.dev_type == dev_type
637
639 """This function returns the nodes this device lives on.
640
641 Given the node on which the parent of the device lives on (or, in
642 case of a top-level device, the primary node of the devices'
643 instance), this function will return a list of nodes on which this
644 devices needs to (or can) be assembled.
645
646 """
647 if self.dev_type in [constants.DT_PLAIN, constants.DT_FILE,
648 constants.DT_BLOCK, constants.DT_RBD,
649 constants.DT_EXT, constants.DT_SHARED_FILE,
650 constants.DT_GLUSTER]:
651 result = [node_uuid]
652 elif self.dev_type in constants.DTS_DRBD:
653 result = [self.logical_id[0], self.logical_id[1]]
654 if node_uuid not in result:
655 raise errors.ConfigurationError("DRBD device passed unknown node")
656 else:
657 raise errors.ProgrammerError("Unhandled device type %s" % self.dev_type)
658 return result
659
661 """Compute the node/disk tree for this disk and its children.
662
663 This method, given the node on which the parent disk lives, will
664 return the list of all (node UUID, disk) pairs which describe the disk
665 tree in the most compact way. For example, a drbd/lvm stack
666 will be returned as (primary_node, drbd) and (secondary_node, drbd)
667 which represents all the top-level devices on the nodes.
668
669 """
670 my_nodes = self.GetNodes(parent_node_uuid)
671 result = [(node, self) for node in my_nodes]
672 if not self.children:
673
674 return result
675 for node in my_nodes:
676 for child in self.children:
677 child_result = child.ComputeNodeTree(node)
678 if len(child_result) == 1:
679
680
681
682 continue
683 else:
684
685
686
687 for subnode, subdisk in child_result:
688 if subnode not in my_nodes:
689 result.append((subnode, subdisk))
690
691
692
693 return result
694
696 """Compute the per-VG growth requirements.
697
698 This only works for VG-based disks.
699
700 @type amount: integer
701 @param amount: the desired increase in (user-visible) disk space
702 @rtype: dict
703 @return: a dictionary of volume-groups and the required size
704
705 """
706 if self.dev_type == constants.DT_PLAIN:
707 return {self.logical_id[0]: amount}
708 elif self.dev_type == constants.DT_DRBD8:
709 if self.children:
710 return self.children[0].ComputeGrowth(amount)
711 else:
712 return {}
713 else:
714
715 return {}
716
736
737 - def Update(self, size=None, mode=None, spindles=None):
738 """Apply changes to size, spindles and mode.
739
740 """
741 if self.dev_type == constants.DT_DRBD8:
742 if self.children:
743 self.children[0].Update(size=size, mode=mode)
744 else:
745 assert not self.children
746
747 if size is not None:
748 self.size = size
749 if mode is not None:
750 self.mode = mode
751 if spindles is not None:
752 self.spindles = spindles
753
755 """Sets recursively the size to zero for the disk and its children.
756
757 """
758 if self.children:
759 for child in self.children:
760 child.UnsetSize()
761 self.size = 0
762
764 """Updates the dynamic disk params for the given node.
765
766 This is mainly used for drbd, which needs ip/port configuration.
767
768 Arguments:
769 - target_node_uuid: the node UUID we wish to configure for
770 - nodes_ip: a mapping of node name to ip
771
772 The target_node must exist in nodes_ip, and should be one of the
773 nodes in the logical ID if this device is a DRBD device.
774
775 """
776 if self.children:
777 for child in self.children:
778 child.UpdateDynamicDiskParams(target_node_uuid, nodes_ip)
779
780 dyn_disk_params = {}
781 if self.logical_id is not None and self.dev_type in constants.DTS_DRBD:
782 pnode_uuid, snode_uuid, _, pminor, sminor, _ = self.logical_id
783 if target_node_uuid not in (pnode_uuid, snode_uuid):
784
785
786
787 self.dynamic_params = dyn_disk_params
788 return
789
790 pnode_ip = nodes_ip.get(pnode_uuid, None)
791 snode_ip = nodes_ip.get(snode_uuid, None)
792 if pnode_ip is None or snode_ip is None:
793 raise errors.ConfigurationError("Can't find primary or secondary node"
794 " for %s" % str(self))
795 if pnode_uuid == target_node_uuid:
796 dyn_disk_params[constants.DDP_LOCAL_IP] = pnode_ip
797 dyn_disk_params[constants.DDP_REMOTE_IP] = snode_ip
798 dyn_disk_params[constants.DDP_LOCAL_MINOR] = pminor
799 dyn_disk_params[constants.DDP_REMOTE_MINOR] = sminor
800 else:
801 dyn_disk_params[constants.DDP_LOCAL_IP] = snode_ip
802 dyn_disk_params[constants.DDP_REMOTE_IP] = pnode_ip
803 dyn_disk_params[constants.DDP_LOCAL_MINOR] = sminor
804 dyn_disk_params[constants.DDP_REMOTE_MINOR] = pminor
805
806 self.dynamic_params = dyn_disk_params
807
808
809 - def ToDict(self, include_dynamic_params=False,
810 _with_private=False):
811 """Disk-specific conversion to standard python types.
812
813 This replaces the children lists of objects with lists of
814 standard python types.
815
816 """
817 bo = super(Disk, self).ToDict(_with_private=_with_private)
818 if not include_dynamic_params and "dynamic_params" in bo:
819 del bo["dynamic_params"]
820
821 if _with_private and "logical_id" in bo:
822 mutable_id = list(bo["logical_id"])
823 mutable_id[5] = mutable_id[5].Get()
824 bo["logical_id"] = tuple(mutable_id)
825
826 for attr in ("children",):
827 alist = bo.get(attr, None)
828 if alist:
829 bo[attr] = outils.ContainerToDicts(alist)
830 return bo
831
832 @classmethod
834 """Custom function for Disks
835
836 """
837 obj = super(Disk, cls).FromDict(val)
838 if obj.children:
839 obj.children = outils.ContainerFromDicts(obj.children, list, Disk)
840 if obj.logical_id and isinstance(obj.logical_id, list):
841 obj.logical_id = tuple(obj.logical_id)
842 if obj.dev_type in constants.DTS_DRBD:
843
844 if len(obj.logical_id) < 6:
845 obj.logical_id += (None,) * (6 - len(obj.logical_id))
846
847
848 elif (len(obj.logical_id) == 6 and
849 not isinstance(obj.logical_id[-1], serializer.Private)):
850 obj.logical_id = obj.logical_id[:-1] + \
851 (serializer.Private(obj.logical_id[-1]),)
852 return obj
853
855 """Custom str() formatter for disks.
856
857 """
858 if self.dev_type == constants.DT_PLAIN:
859 val = "<LogicalVolume(/dev/%s/%s" % self.logical_id
860 elif self.dev_type in constants.DTS_DRBD:
861 node_a, node_b, port, minor_a, minor_b = self.logical_id[:5]
862 val = "<DRBD8("
863
864 val += ("hosts=%s/%d-%s/%d, port=%s, " %
865 (node_a, minor_a, node_b, minor_b, port))
866 if self.children and self.children.count(None) == 0:
867 val += "backend=%s, metadev=%s" % (self.children[0], self.children[1])
868 else:
869 val += "no local storage"
870 else:
871 val = ("<Disk(type=%s, logical_id=%s, children=%s" %
872 (self.dev_type, self.logical_id, self.children))
873 if self.iv_name is None:
874 val += ", not visible"
875 else:
876 val += ", visible as /dev/%s" % self.iv_name
877 if self.spindles is not None:
878 val += ", spindles=%s" % self.spindles
879 if isinstance(self.size, int):
880 val += ", size=%dm)>" % self.size
881 else:
882 val += ", size='%s')>" % (self.size,)
883 return val
884
886 """Checks that this disk is correctly configured.
887
888 """
889 all_errors = []
890 if self.mode not in constants.DISK_ACCESS_SET:
891 all_errors.append("Disk access mode '%s' is invalid" % (self.mode, ))
892 return all_errors
893
895 """Fill defaults for missing configuration values.
896
897 """
898 if self.children:
899 for child in self.children:
900 child.UpgradeConfig()
901
902
903
904
905
906 if not self.params or not isinstance(self.params, dict):
907 self.params = {}
908
909
910 if self.serial_no is None:
911 self.serial_no = 1
912 if self.mtime is None:
913 self.mtime = time.time()
914 if self.ctime is None:
915 self.ctime = time.time()
916
917
918
919 LEG_DEV_TYPE_MAP = {"lvm": constants.DT_PLAIN, "drbd8": constants.DT_DRBD8}
920 if self.dev_type in LEG_DEV_TYPE_MAP:
921 self.dev_type = LEG_DEV_TYPE_MAP[self.dev_type]
922
923 @staticmethod
925 """Computes Logical Disk parameters from Disk Template parameters.
926
927 @type disk_template: string
928 @param disk_template: disk template, one of L{constants.DISK_TEMPLATES}
929 @type disk_params: dict
930 @param disk_params: disk template parameters;
931 dict(template_name -> parameters
932 @rtype: list(dict)
933 @return: a list of dicts, one for each node of the disk hierarchy. Each dict
934 contains the LD parameters of the node. The tree is flattened in-order.
935
936 """
937 if disk_template not in constants.DISK_TEMPLATES:
938 raise errors.ProgrammerError("Unknown disk template %s" % disk_template)
939
940 assert disk_template in disk_params
941
942 result = list()
943 dt_params = disk_params[disk_template]
944
945 if disk_template == constants.DT_DRBD8:
946 result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.DT_DRBD8], {
947 constants.LDP_RESYNC_RATE: dt_params[constants.DRBD_RESYNC_RATE],
948 constants.LDP_BARRIERS: dt_params[constants.DRBD_DISK_BARRIERS],
949 constants.LDP_NO_META_FLUSH: dt_params[constants.DRBD_META_BARRIERS],
950 constants.LDP_DEFAULT_METAVG: dt_params[constants.DRBD_DEFAULT_METAVG],
951 constants.LDP_DISK_CUSTOM: dt_params[constants.DRBD_DISK_CUSTOM],
952 constants.LDP_NET_CUSTOM: dt_params[constants.DRBD_NET_CUSTOM],
953 constants.LDP_PROTOCOL: dt_params[constants.DRBD_PROTOCOL],
954 constants.LDP_DYNAMIC_RESYNC: dt_params[constants.DRBD_DYNAMIC_RESYNC],
955 constants.LDP_PLAN_AHEAD: dt_params[constants.DRBD_PLAN_AHEAD],
956 constants.LDP_FILL_TARGET: dt_params[constants.DRBD_FILL_TARGET],
957 constants.LDP_DELAY_TARGET: dt_params[constants.DRBD_DELAY_TARGET],
958 constants.LDP_MAX_RATE: dt_params[constants.DRBD_MAX_RATE],
959 constants.LDP_MIN_RATE: dt_params[constants.DRBD_MIN_RATE],
960 }))
961
962
963 result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.DT_PLAIN], {
964 constants.LDP_STRIPES: dt_params[constants.DRBD_DATA_STRIPES],
965 }))
966
967
968 result.append(FillDict(constants.DISK_LD_DEFAULTS[constants.DT_PLAIN], {
969 constants.LDP_STRIPES: dt_params[constants.DRBD_META_STRIPES],
970 }))
971
972 else:
973 defaults = constants.DISK_LD_DEFAULTS[disk_template]
974 values = {}
975 for field in defaults:
976 values[field] = dt_params[field]
977 result.append(FillDict(defaults, values))
978
979 return result
980
983 """Config object representing instance policy limits dictionary.
984
985 Note that this object is not actually used in the config, it's just
986 used as a placeholder for a few functions.
987
988 """
989 @classmethod
997
998 @classmethod
1019
1020 @classmethod
1027
1028 @classmethod
1069
1070 @classmethod
1072 """Check the instance policy specs for validity on a given key.
1073
1074 We check if the instance specs makes sense for a given key, that is
1075 if minmaxspecs[min][name] <= stdspec[name] <= minmaxspec[max][name].
1076
1077 @type minmaxspecs: dict
1078 @param minmaxspecs: dictionary with min and max instance spec
1079 @type stdspec: dict
1080 @param stdspec: dictionary with standard instance spec
1081 @type name: string
1082 @param name: what are the limits for
1083 @type check_std: bool
1084 @param check_std: Whether to check std value or just assume compliance
1085 @rtype: bool
1086 @return: C{True} when specs are valid, C{False} when standard spec for the
1087 given name is not valid
1088 @raise errors.ConfigurationError: when min/max specs for the given name
1089 are not valid
1090
1091 """
1092 minspec = minmaxspecs[constants.ISPECS_MIN]
1093 maxspec = minmaxspecs[constants.ISPECS_MAX]
1094 min_v = minspec[name]
1095 max_v = maxspec[name]
1096
1097 if min_v > max_v:
1098 err = ("Invalid specification of min/max values for %s: %s/%s" %
1099 (name, min_v, max_v))
1100 raise errors.ConfigurationError(err)
1101 elif check_std:
1102 std_v = stdspec.get(name, min_v)
1103 return std_v >= min_v and std_v <= max_v
1104 else:
1105 return True
1106
1107 @classmethod
1109 """Checks the disk templates for validity.
1110
1111 """
1112 if not disk_templates:
1113 raise errors.ConfigurationError("Instance policy must contain" +
1114 " at least one disk template")
1115 wrong = frozenset(disk_templates).difference(constants.DISK_TEMPLATES)
1116 if wrong:
1117 raise errors.ConfigurationError("Invalid disk template(s) %s" %
1118 utils.CommaJoin(wrong))
1119
1120 @classmethod
1122 """Checks a parameter.
1123
1124 Currently we expect all parameters to be float values.
1125
1126 """
1127 try:
1128 float(value)
1129 except (TypeError, ValueError), err:
1130 raise errors.ConfigurationError("Invalid value for key" " '%s':"
1131 " '%s', error: %s" % (key, value, err))
1132
1135 """Gets the OS image value from the OS parameters.
1136
1137 @type osparams: L{dict} or NoneType
1138 @param osparams: OS parameters or None
1139
1140 @rtype: string or NoneType
1141 @return:
1142 value of OS image contained in OS parameters, or None if the OS
1143 parameters are None or the OS parameters do not contain an OS
1144 image
1145
1146 """
1147 if osparams is None:
1148 return None
1149 else:
1150 return osparams.get("os-image", None)
1151
1154 """Update OS image value in the OS parameters
1155
1156 @type osparams: L{dict}
1157 @param osparams: OS parameters
1158
1159 @type os_image: string
1160 @param os_image: OS image
1161
1162 @rtype: NoneType
1163 @return: None
1164
1165 """
1166 osparams["os-image"] = os_image
1167
1170 """Config object representing an instance."""
1171 __slots__ = [
1172 "name",
1173 "primary_node",
1174 "secondary_nodes",
1175 "os",
1176 "hypervisor",
1177 "hvparams",
1178 "beparams",
1179 "osparams",
1180 "osparams_private",
1181 "admin_state",
1182 "admin_state_source",
1183 "nics",
1184 "disks",
1185 "disks_info",
1186 "disk_template",
1187 "disks_active",
1188 "network_port",
1189 "serial_no",
1190 ] + _TIMESTAMPS + _UUID
1191
1193 """Find a disk given having a specified index.
1194
1195 This is just a wrapper that does validation of the index.
1196
1197 @type idx: int
1198 @param idx: the disk index
1199 @rtype: string
1200 @return: the corresponding disk's uuid
1201 @raise errors.OpPrereqError: when the given index is not valid
1202
1203 """
1204 try:
1205 idx = int(idx)
1206 return self.disks[idx]
1207 except (TypeError, ValueError), err:
1208 raise errors.OpPrereqError("Invalid disk index: '%s'" % str(err),
1209 errors.ECODE_INVAL)
1210 except IndexError:
1211 raise errors.OpPrereqError("Invalid disk index: %d (instace has disks"
1212 " 0 to %d" % (idx, len(self.disks) - 1),
1213 errors.ECODE_INVAL)
1214
1215 - def ToDict(self, _with_private=False):
1216 """Instance-specific conversion to standard python types.
1217
1218 This replaces the children lists of objects with lists of standard
1219 python types.
1220
1221 """
1222 bo = super(Instance, self).ToDict(_with_private=_with_private)
1223
1224 if _with_private:
1225 bo["osparams_private"] = self.osparams_private.Unprivate()
1226
1227 for attr in "nics", :
1228 alist = bo.get(attr, None)
1229 if alist:
1230 nlist = outils.ContainerToDicts(alist)
1231 else:
1232 nlist = []
1233 bo[attr] = nlist
1234 return bo
1235
1236 @classmethod
1258
1260 """Fill defaults for missing configuration values.
1261
1262 """
1263 if self.admin_state_source is None:
1264 self.admin_state_source = constants.ADMIN_SOURCE
1265 for nic in self.nics:
1266 nic.UpgradeConfig()
1267 if self.disks is None:
1268 self.disks = []
1269 if self.hvparams:
1270 for key in constants.HVC_GLOBALS:
1271 try:
1272 del self.hvparams[key]
1273 except KeyError:
1274 pass
1275 if self.osparams is None:
1276 self.osparams = {}
1277 if self.osparams_private is None:
1278 self.osparams_private = serializer.PrivateDict()
1279 UpgradeBeParams(self.beparams)
1280 if self.disks_active is None:
1281 self.disks_active = self.admin_state == constants.ADMINST_UP
1282
1283
1284 -class OS(ConfigObject):
1285 """Config object representing an operating system.
1286
1287 @type supported_parameters: list
1288 @ivar supported_parameters: a list of tuples, name and description,
1289 containing the supported parameters by this OS
1290
1291 @type VARIANT_DELIM: string
1292 @cvar VARIANT_DELIM: the variant delimiter
1293
1294 """
1295 __slots__ = [
1296 "name",
1297 "path",
1298 "api_versions",
1299 "create_script",
1300 "create_script_untrusted",
1301 "export_script",
1302 "import_script",
1303 "rename_script",
1304 "verify_script",
1305 "supported_variants",
1306 "supported_parameters",
1307 ]
1308
1309 VARIANT_DELIM = "+"
1310
1311 @classmethod
1313 """Splits the name into the proper name and variant.
1314
1315 @param name: the OS (unprocessed) name
1316 @rtype: list
1317 @return: a list of two elements; if the original name didn't
1318 contain a variant, it's returned as an empty string
1319
1320 """
1321 nv = name.split(cls.VARIANT_DELIM, 1)
1322 if len(nv) == 1:
1323 nv.append("")
1324 return nv
1325
1326 @classmethod
1328 """Returns the proper name of the os (without the variant).
1329
1330 @param name: the OS (unprocessed) name
1331
1332 """
1333 return cls.SplitNameVariant(name)[0]
1334
1335 @classmethod
1337 """Returns the variant the os (without the base name).
1338
1339 @param name: the OS (unprocessed) name
1340
1341 """
1342 return cls.SplitNameVariant(name)[1]
1343
1345 """Returns whether this OS is trusted.
1346
1347 @rtype: bool
1348 @return: L{True} if this OS is trusted, L{False} otherwise
1349
1350 """
1351 return not self.create_script_untrusted
1352
1355 """Config object representing an External Storage Provider.
1356
1357 """
1358 __slots__ = [
1359 "name",
1360 "path",
1361 "create_script",
1362 "remove_script",
1363 "grow_script",
1364 "attach_script",
1365 "detach_script",
1366 "setinfo_script",
1367 "verify_script",
1368 "supported_parameters",
1369 ]
1370
1373 """Hypvervisor state on a node.
1374
1375 @ivar mem_total: Total amount of memory
1376 @ivar mem_node: Memory used by, or reserved for, the node itself (not always
1377 available)
1378 @ivar mem_hv: Memory used by hypervisor or lost due to instance allocation
1379 rounding
1380 @ivar mem_inst: Memory used by instances living on node
1381 @ivar cpu_total: Total node CPU core count
1382 @ivar cpu_node: Number of CPU cores reserved for the node itself
1383
1384 """
1385 __slots__ = [
1386 "mem_total",
1387 "mem_node",
1388 "mem_hv",
1389 "mem_inst",
1390 "cpu_total",
1391 "cpu_node",
1392 ] + _TIMESTAMPS
1393
1396 """Disk state on a node.
1397
1398 """
1399 __slots__ = [
1400 "total",
1401 "reserved",
1402 "overhead",
1403 ] + _TIMESTAMPS
1404
1405
1406 -class Node(TaggableObject):
1407 """Config object representing a node.
1408
1409 @ivar hv_state: Hypervisor state (e.g. number of CPUs)
1410 @ivar hv_state_static: Hypervisor state overriden by user
1411 @ivar disk_state: Disk state (e.g. free space)
1412 @ivar disk_state_static: Disk state overriden by user
1413
1414 """
1415 __slots__ = [
1416 "name",
1417 "primary_ip",
1418 "secondary_ip",
1419 "serial_no",
1420 "master_candidate",
1421 "offline",
1422 "drained",
1423 "group",
1424 "master_capable",
1425 "vm_capable",
1426 "ndparams",
1427 "powered",
1428 "hv_state",
1429 "hv_state_static",
1430 "disk_state",
1431 "disk_state_static",
1432 ] + _TIMESTAMPS + _UUID
1433
1435 """Fill defaults for missing configuration values.
1436
1437 """
1438
1439
1440 if self.master_capable is None:
1441 self.master_capable = True
1442
1443 if self.vm_capable is None:
1444 self.vm_capable = True
1445
1446 if self.ndparams is None:
1447 self.ndparams = {}
1448
1449 for key in constants.NDC_GLOBALS:
1450 if key in self.ndparams:
1451 logging.warning("Ignoring %s node parameter for node %s",
1452 key, self.name)
1453 del self.ndparams[key]
1454
1455 if self.powered is None:
1456 self.powered = True
1457
1458 - def ToDict(self, _with_private=False):
1459 """Custom function for serializing.
1460
1461 """
1462 data = super(Node, self).ToDict(_with_private=_with_private)
1463
1464 hv_state = data.get("hv_state", None)
1465 if hv_state is not None:
1466 data["hv_state"] = outils.ContainerToDicts(hv_state)
1467
1468 disk_state = data.get("disk_state", None)
1469 if disk_state is not None:
1470 data["disk_state"] = \
1471 dict((key, outils.ContainerToDicts(value))
1472 for (key, value) in disk_state.items())
1473
1474 return data
1475
1476 @classmethod
1478 """Custom function for deserializing.
1479
1480 """
1481 obj = super(Node, cls).FromDict(val)
1482
1483 if obj.hv_state is not None:
1484 obj.hv_state = \
1485 outils.ContainerFromDicts(obj.hv_state, dict, NodeHvState)
1486
1487 if obj.disk_state is not None:
1488 obj.disk_state = \
1489 dict((key, outils.ContainerFromDicts(value, dict, NodeDiskState))
1490 for (key, value) in obj.disk_state.items())
1491
1492 return obj
1493
1496 """Config object representing a node group."""
1497 __slots__ = [
1498 "name",
1499 "members",
1500 "ndparams",
1501 "diskparams",
1502 "ipolicy",
1503 "serial_no",
1504 "hv_state_static",
1505 "disk_state_static",
1506 "alloc_policy",
1507 "networks",
1508 ] + _TIMESTAMPS + _UUID
1509
1510 - def ToDict(self, _with_private=False):
1511 """Custom function for nodegroup.
1512
1513 This discards the members object, which gets recalculated and is only kept
1514 in memory.
1515
1516 """
1517 mydict = super(NodeGroup, self).ToDict(_with_private=_with_private)
1518 del mydict["members"]
1519 return mydict
1520
1521 @classmethod
1523 """Custom function for nodegroup.
1524
1525 The members slot is initialized to an empty list, upon deserialization.
1526
1527 """
1528 obj = super(NodeGroup, cls).FromDict(val)
1529 obj.members = []
1530 return obj
1531
1533 """Fill defaults for missing configuration values.
1534
1535 """
1536 if self.ndparams is None:
1537 self.ndparams = {}
1538
1539 if self.serial_no is None:
1540 self.serial_no = 1
1541
1542 if self.alloc_policy is None:
1543 self.alloc_policy = constants.ALLOC_POLICY_PREFERRED
1544
1545
1546
1547 if self.mtime is None:
1548 self.mtime = time.time()
1549
1550 if self.diskparams is None:
1551 self.diskparams = {}
1552 if self.ipolicy is None:
1553 self.ipolicy = MakeEmptyIPolicy()
1554
1555 if self.networks is None:
1556 self.networks = {}
1557
1558 for network, netparams in self.networks.items():
1559 self.networks[network] = FillDict(constants.NICC_DEFAULTS, netparams)
1560
1562 """Return filled out ndparams for L{objects.Node}
1563
1564 @type node: L{objects.Node}
1565 @param node: A Node object to fill
1566 @return a copy of the node's ndparams with defaults filled
1567
1568 """
1569 return self.SimpleFillND(node.ndparams)
1570
1572 """Fill a given ndparams dict with defaults.
1573
1574 @type ndparams: dict
1575 @param ndparams: the dict to fill
1576 @rtype: dict
1577 @return: a copy of the passed in ndparams with missing keys filled
1578 from the node group defaults
1579
1580 """
1581 return FillDict(self.ndparams, ndparams)
1582
1583
1584 -class Cluster(TaggableObject):
1585 """Config object representing the cluster."""
1586 __slots__ = [
1587 "serial_no",
1588 "rsahostkeypub",
1589 "dsahostkeypub",
1590 "highest_used_port",
1591 "tcpudp_port_pool",
1592 "mac_prefix",
1593 "volume_group_name",
1594 "reserved_lvs",
1595 "drbd_usermode_helper",
1596 "default_bridge",
1597 "default_hypervisor",
1598 "master_node",
1599 "master_ip",
1600 "master_netdev",
1601 "master_netmask",
1602 "use_external_mip_script",
1603 "cluster_name",
1604 "file_storage_dir",
1605 "shared_file_storage_dir",
1606 "gluster_storage_dir",
1607 "enabled_hypervisors",
1608 "hvparams",
1609 "ipolicy",
1610 "os_hvp",
1611 "beparams",
1612 "osparams",
1613 "osparams_private_cluster",
1614 "nicparams",
1615 "ndparams",
1616 "diskparams",
1617 "candidate_pool_size",
1618 "modify_etc_hosts",
1619 "modify_ssh_setup",
1620 "maintain_node_health",
1621 "uid_pool",
1622 "default_iallocator",
1623 "default_iallocator_params",
1624 "hidden_os",
1625 "blacklisted_os",
1626 "primary_ip_family",
1627 "prealloc_wipe_disks",
1628 "hv_state_static",
1629 "disk_state_static",
1630 "enabled_disk_templates",
1631 "candidate_certs",
1632 "max_running_jobs",
1633 "max_tracked_jobs",
1634 "install_image",
1635 "instance_communication_network",
1636 "zeroing_image",
1637 "compression_tools",
1638 "enabled_user_shutdown",
1639 ] + _TIMESTAMPS + _UUID
1640
1642 """Fill defaults for missing configuration values.
1643
1644 """
1645
1646
1647 if self.hvparams is None:
1648 self.hvparams = constants.HVC_DEFAULTS
1649 else:
1650 for hypervisor in constants.HYPER_TYPES:
1651 try:
1652 existing_params = self.hvparams[hypervisor]
1653 except KeyError:
1654 existing_params = {}
1655 self.hvparams[hypervisor] = FillDict(
1656 constants.HVC_DEFAULTS[hypervisor], existing_params)
1657
1658 if self.os_hvp is None:
1659 self.os_hvp = {}
1660
1661 if self.osparams is None:
1662 self.osparams = {}
1663
1664 if self.osparams_private_cluster is None:
1665 self.osparams_private_cluster = {}
1666
1667 self.ndparams = UpgradeNDParams(self.ndparams)
1668
1669 self.beparams = UpgradeGroupedParams(self.beparams,
1670 constants.BEC_DEFAULTS)
1671 for beparams_group in self.beparams:
1672 UpgradeBeParams(self.beparams[beparams_group])
1673
1674 migrate_default_bridge = not self.nicparams
1675 self.nicparams = UpgradeGroupedParams(self.nicparams,
1676 constants.NICC_DEFAULTS)
1677 if migrate_default_bridge:
1678 self.nicparams[constants.PP_DEFAULT][constants.NIC_LINK] = \
1679 self.default_bridge
1680
1681 if self.modify_etc_hosts is None:
1682 self.modify_etc_hosts = True
1683
1684 if self.modify_ssh_setup is None:
1685 self.modify_ssh_setup = True
1686
1687
1688
1689
1690 if self.default_bridge is not None:
1691 self.default_bridge = None
1692
1693
1694
1695 if self.default_hypervisor is not None:
1696 self.enabled_hypervisors = ([self.default_hypervisor] +
1697 [hvname for hvname in self.enabled_hypervisors
1698 if hvname != self.default_hypervisor])
1699 self.default_hypervisor = None
1700
1701
1702 if self.maintain_node_health is None:
1703 self.maintain_node_health = False
1704
1705 if self.uid_pool is None:
1706 self.uid_pool = []
1707
1708 if self.default_iallocator is None:
1709 self.default_iallocator = ""
1710
1711 if self.default_iallocator_params is None:
1712 self.default_iallocator_params = {}
1713
1714
1715 if self.reserved_lvs is None:
1716 self.reserved_lvs = []
1717
1718
1719 if self.hidden_os is None:
1720 self.hidden_os = []
1721
1722 if self.blacklisted_os is None:
1723 self.blacklisted_os = []
1724
1725
1726 if self.primary_ip_family is None:
1727 self.primary_ip_family = AF_INET
1728
1729 if self.master_netmask is None:
1730 ipcls = netutils.IPAddress.GetClassFromIpFamily(self.primary_ip_family)
1731 self.master_netmask = ipcls.iplen
1732
1733 if self.prealloc_wipe_disks is None:
1734 self.prealloc_wipe_disks = False
1735
1736
1737 if self.shared_file_storage_dir is None:
1738 self.shared_file_storage_dir = ""
1739
1740
1741 if self.gluster_storage_dir is None:
1742 self.gluster_storage_dir = ""
1743
1744 if self.use_external_mip_script is None:
1745 self.use_external_mip_script = False
1746
1747 if self.diskparams:
1748 self.diskparams = UpgradeDiskParams(self.diskparams)
1749 else:
1750 self.diskparams = constants.DISK_DT_DEFAULTS.copy()
1751
1752
1753 if self.ipolicy is None:
1754 self.ipolicy = FillIPolicy(constants.IPOLICY_DEFAULTS, {})
1755 else:
1756
1757
1758
1759 wrongkeys = frozenset(self.ipolicy.keys()) - constants.IPOLICY_ALL_KEYS
1760 if wrongkeys:
1761
1762 msg = ("Cluster instance policy contains spurious keys: %s" %
1763 utils.CommaJoin(wrongkeys))
1764 raise errors.ConfigurationError(msg)
1765 self.ipolicy = FillIPolicy(constants.IPOLICY_DEFAULTS, self.ipolicy)
1766
1767
1768 if self.hv_state_static is None:
1769 self.hv_state_static = {}
1770 if self.disk_state_static is None:
1771 self.disk_state_static = {}
1772
1773 if self.candidate_certs is None:
1774 self.candidate_certs = {}
1775
1776 if self.max_running_jobs is None:
1777 self.max_running_jobs = constants.LUXID_MAXIMAL_RUNNING_JOBS_DEFAULT
1778
1779 if self.max_tracked_jobs is None:
1780 self.max_tracked_jobs = constants.LUXID_MAXIMAL_TRACKED_JOBS_DEFAULT
1781
1782 if self.instance_communication_network is None:
1783 self.instance_communication_network = ""
1784
1785 if self.install_image is None:
1786 self.install_image = ""
1787
1788 if self.compression_tools is None:
1789 self.compression_tools = constants.IEC_DEFAULT_TOOLS
1790
1791 if self.enabled_user_shutdown is None:
1792 self.enabled_user_shutdown = False
1793
1794 @property
1796 """The first hypervisor is the primary.
1797
1798 Useful, for example, for L{Node}'s hv/disk state.
1799
1800 """
1801 return self.enabled_hypervisors[0]
1802
1803 - def ToDict(self, _with_private=False):
1804 """Custom function for cluster.
1805
1806 """
1807 mydict = super(Cluster, self).ToDict(_with_private=_with_private)
1808
1809
1810 if _with_private:
1811 for os in mydict["osparams_private_cluster"]:
1812 mydict["osparams_private_cluster"][os] = \
1813 self.osparams_private_cluster[os].Unprivate()
1814
1815 if self.tcpudp_port_pool is None:
1816 tcpudp_port_pool = []
1817 else:
1818 tcpudp_port_pool = list(self.tcpudp_port_pool)
1819
1820 mydict["tcpudp_port_pool"] = tcpudp_port_pool
1821
1822 return mydict
1823
1824 @classmethod
1826 """Custom function for cluster.
1827
1828 """
1829 obj = super(Cluster, cls).FromDict(val)
1830
1831 if obj.tcpudp_port_pool is None:
1832 obj.tcpudp_port_pool = set()
1833 elif not isinstance(obj.tcpudp_port_pool, set):
1834 obj.tcpudp_port_pool = set(obj.tcpudp_port_pool)
1835
1836 return obj
1837
1839 """Fill a given diskparams dict with cluster defaults.
1840
1841 @param diskparams: The diskparams
1842 @return: The defaults dict
1843
1844 """
1845 return FillDiskParams(self.diskparams, diskparams)
1846
1847 - def GetHVDefaults(self, hypervisor, os_name=None, skip_keys=None):
1848 """Get the default hypervisor parameters for the cluster.
1849
1850 @param hypervisor: the hypervisor name
1851 @param os_name: if specified, we'll also update the defaults for this OS
1852 @param skip_keys: if passed, list of keys not to use
1853 @return: the defaults dict
1854
1855 """
1856 if skip_keys is None:
1857 skip_keys = []
1858
1859 fill_stack = [self.hvparams.get(hypervisor, {})]
1860 if os_name is not None:
1861 os_hvp = self.os_hvp.get(os_name, {}).get(hypervisor, {})
1862 fill_stack.append(os_hvp)
1863
1864 ret_dict = {}
1865 for o_dict in fill_stack:
1866 ret_dict = FillDict(ret_dict, o_dict, skip_keys=skip_keys)
1867
1868 return ret_dict
1869
1870 - def SimpleFillHV(self, hv_name, os_name, hvparams, skip_globals=False):
1871 """Fill a given hvparams dict with cluster defaults.
1872
1873 @type hv_name: string
1874 @param hv_name: the hypervisor to use
1875 @type os_name: string
1876 @param os_name: the OS to use for overriding the hypervisor defaults
1877 @type skip_globals: boolean
1878 @param skip_globals: if True, the global hypervisor parameters will
1879 not be filled
1880 @rtype: dict
1881 @return: a copy of the given hvparams with missing keys filled from
1882 the cluster defaults
1883
1884 """
1885 if skip_globals:
1886 skip_keys = constants.HVC_GLOBALS
1887 else:
1888 skip_keys = []
1889
1890 def_dict = self.GetHVDefaults(hv_name, os_name, skip_keys=skip_keys)
1891 return FillDict(def_dict, hvparams, skip_keys=skip_keys)
1892
1893 - def FillHV(self, instance, skip_globals=False):
1894 """Fill an instance's hvparams dict with cluster defaults.
1895
1896 @type instance: L{objects.Instance}
1897 @param instance: the instance parameter to fill
1898 @type skip_globals: boolean
1899 @param skip_globals: if True, the global hypervisor parameters will
1900 not be filled
1901 @rtype: dict
1902 @return: a copy of the instance's hvparams with missing keys filled from
1903 the cluster defaults
1904
1905 """
1906 return self.SimpleFillHV(instance.hypervisor, instance.os,
1907 instance.hvparams, skip_globals)
1908
1910 """Fill a given beparams dict with cluster defaults.
1911
1912 @type beparams: dict
1913 @param beparams: the dict to fill
1914 @rtype: dict
1915 @return: a copy of the passed in beparams with missing keys filled
1916 from the cluster defaults
1917
1918 """
1919 return FillDict(self.beparams.get(constants.PP_DEFAULT, {}), beparams)
1920
1922 """Fill an instance's beparams dict with cluster defaults.
1923
1924 @type instance: L{objects.Instance}
1925 @param instance: the instance parameter to fill
1926 @rtype: dict
1927 @return: a copy of the instance's beparams with missing keys filled from
1928 the cluster defaults
1929
1930 """
1931 return self.SimpleFillBE(instance.beparams)
1932
1934 """Fill a given nicparams dict with cluster defaults.
1935
1936 @type nicparams: dict
1937 @param nicparams: the dict to fill
1938 @rtype: dict
1939 @return: a copy of the passed in nicparams with missing keys filled
1940 from the cluster defaults
1941
1942 """
1943 return FillDict(self.nicparams.get(constants.PP_DEFAULT, {}), nicparams)
1944
1945 - def SimpleFillOS(self, os_name,
1946 os_params_public,
1947 os_params_private=None,
1948 os_params_secret=None):
1949 """Fill an instance's osparams dict with cluster defaults.
1950
1951 @type os_name: string
1952 @param os_name: the OS name to use
1953 @type os_params_public: dict
1954 @param os_params_public: the dict to fill with default values
1955 @type os_params_private: dict
1956 @param os_params_private: the dict with private fields to fill
1957 with default values. Not passing this field
1958 results in no private fields being added to the
1959 return value. Private fields will be wrapped in
1960 L{Private} objects.
1961 @type os_params_secret: dict
1962 @param os_params_secret: the dict with secret fields to fill
1963 with default values. Not passing this field
1964 results in no secret fields being added to the
1965 return value. Private fields will be wrapped in
1966 L{Private} objects.
1967 @rtype: dict
1968 @return: a copy of the instance's osparams with missing keys filled from
1969 the cluster defaults. Private and secret parameters are not included
1970 unless the respective optional parameters are supplied.
1971
1972 """
1973 if os_name is None:
1974 name_only = None
1975 else:
1976 name_only = OS.GetName(os_name)
1977
1978 defaults_base_public = self.osparams.get(name_only, {})
1979 defaults_public = FillDict(defaults_base_public,
1980 self.osparams.get(os_name, {}))
1981 params_public = FillDict(defaults_public, os_params_public)
1982
1983 if os_params_private is not None:
1984 defaults_base_private = self.osparams_private_cluster.get(name_only, {})
1985 defaults_private = FillDict(defaults_base_private,
1986 self.osparams_private_cluster.get(os_name,
1987 {}))
1988 params_private = FillDict(defaults_private, os_params_private)
1989 else:
1990 params_private = {}
1991
1992 if os_params_secret is not None:
1993
1994 params_secret = os_params_secret
1995 else:
1996 params_secret = {}
1997
1998
1999 duplicate_keys = utils.GetRepeatedKeys(params_public,
2000 params_private,
2001 params_secret)
2002 if not duplicate_keys:
2003
2004
2005 params_public.update(params_private)
2006 params_public.update(params_secret)
2007
2008 return params_public
2009
2010 else:
2011
2012 def formatter(keys):
2013 return utils.CommaJoin(sorted(map(repr, keys))) if keys else "(none)"
2014
2015
2016 params_public = set(params_public)
2017 params_private = set(params_private)
2018 params_secret = set(params_secret)
2019
2020 msg = """Cannot assign multiple values to OS parameters.
2021
2022 Conflicting OS parameters that would have been set by this operation:
2023 - at public visibility: {public}
2024 - at private visibility: {private}
2025 - at secret visibility: {secret}
2026 """.format(dupes=formatter(duplicate_keys),
2027 public=formatter(params_public & duplicate_keys),
2028 private=formatter(params_private & duplicate_keys),
2029 secret=formatter(params_secret & duplicate_keys))
2030 raise errors.OpPrereqError(msg)
2031
2032 @staticmethod
2038
2039 @staticmethod
2045
2046 - def FillND(self, node, nodegroup):
2047 """Return filled out ndparams for L{objects.NodeGroup} and L{objects.Node}
2048
2049 @type node: L{objects.Node}
2050 @param node: A Node object to fill
2051 @type nodegroup: L{objects.NodeGroup}
2052 @param nodegroup: A Node object to fill
2053 @return a copy of the node's ndparams with defaults filled
2054
2055 """
2056 return self.SimpleFillND(nodegroup.FillND(node))
2057
2059 """Return filled out ndparams for just L{objects.NodeGroup}
2060
2061 @type nodegroup: L{objects.NodeGroup}
2062 @param nodegroup: A Node object to fill
2063 @return a copy of the node group's ndparams with defaults filled
2064
2065 """
2066 return self.SimpleFillND(nodegroup.SimpleFillND({}))
2067
2069 """Fill a given ndparams dict with defaults.
2070
2071 @type ndparams: dict
2072 @param ndparams: the dict to fill
2073 @rtype: dict
2074 @return: a copy of the passed in ndparams with missing keys filled
2075 from the cluster defaults
2076
2077 """
2078 return FillDict(self.ndparams, ndparams)
2079
2081 """ Fill instance policy dict with defaults.
2082
2083 @type ipolicy: dict
2084 @param ipolicy: the dict to fill
2085 @rtype: dict
2086 @return: a copy of passed ipolicy with missing keys filled from
2087 the cluster defaults
2088
2089 """
2090 return FillIPolicy(self.ipolicy, ipolicy)
2091
2093 """Checks if a particular disk template is enabled.
2094
2095 """
2096 return utils.storage.IsDiskTemplateEnabled(
2097 disk_template, self.enabled_disk_templates)
2098
2104
2111
2114 """Config object representing the status of a block device."""
2115 __slots__ = [
2116 "dev_path",
2117 "major",
2118 "minor",
2119 "sync_percent",
2120 "estimated_time",
2121 "is_degraded",
2122 "ldisk_status",
2123 ]
2124
2127 """Config object representing the status of an import or export."""
2128 __slots__ = [
2129 "recent_output",
2130 "listen_port",
2131 "connected",
2132 "progress_mbytes",
2133 "progress_throughput",
2134 "progress_eta",
2135 "progress_percent",
2136 "exit_status",
2137 "error_message",
2138 ] + _TIMESTAMPS
2139
2142 """Options for import/export daemon
2143
2144 @ivar key_name: X509 key name (None for cluster certificate)
2145 @ivar ca_pem: Remote peer CA in PEM format (None for cluster certificate)
2146 @ivar compress: Compression tool to use
2147 @ivar magic: Used to ensure the connection goes to the right disk
2148 @ivar ipv6: Whether to use IPv6
2149 @ivar connect_timeout: Number of seconds for establishing connection
2150
2151 """
2152 __slots__ = [
2153 "key_name",
2154 "ca_pem",
2155 "compress",
2156 "magic",
2157 "ipv6",
2158 "connect_timeout",
2159 ]
2160
2163 """Object holding a confd request.
2164
2165 @ivar protocol: confd protocol version
2166 @ivar type: confd query type
2167 @ivar query: query request
2168 @ivar rsalt: requested reply salt
2169
2170 """
2171 __slots__ = [
2172 "protocol",
2173 "type",
2174 "query",
2175 "rsalt",
2176 ]
2177
2180 """Object holding a confd reply.
2181
2182 @ivar protocol: confd protocol version
2183 @ivar status: reply status code (ok, error)
2184 @ivar answer: confd query reply
2185 @ivar serial: configuration serial number
2186
2187 """
2188 __slots__ = [
2189 "protocol",
2190 "status",
2191 "answer",
2192 "serial",
2193 ]
2194
2197 """Object holding a query field definition.
2198
2199 @ivar name: Field name
2200 @ivar title: Human-readable title
2201 @ivar kind: Field type
2202 @ivar doc: Human-readable description
2203
2204 """
2205 __slots__ = [
2206 "name",
2207 "title",
2208 "kind",
2209 "doc",
2210 ]
2211
2214 __slots__ = [
2215 "fields",
2216 ]
2217
2218 - def ToDict(self, _with_private=False):
2225
2226 @classmethod
2235
2238 """Object holding the response to a query.
2239
2240 @ivar fields: List of L{QueryFieldDefinition} objects
2241 @ivar data: Requested data
2242
2243 """
2244 __slots__ = [
2245 "data",
2246 ]
2247
2250 """Object holding a request for querying available fields.
2251
2252 """
2253 __slots__ = [
2254 "what",
2255 "fields",
2256 ]
2257
2260 """Object holding the response to a query for fields.
2261
2262 @ivar fields: List of L{QueryFieldDefinition} objects
2263
2264 """
2265 __slots__ = []
2266
2269 """Object holding the status of a migration.
2270
2271 """
2272 __slots__ = [
2273 "status",
2274 "transferred_ram",
2275 "total_ram",
2276 ]
2277
2280 """Object describing how to access the console of an instance.
2281
2282 """
2283 __slots__ = [
2284 "instance",
2285 "kind",
2286 "message",
2287 "host",
2288 "port",
2289 "user",
2290 "command",
2291 "display",
2292 ]
2293
2295 """Validates contents of this object.
2296
2297 """
2298 assert self.kind in constants.CONS_ALL, "Unknown console type"
2299 assert self.instance, "Missing instance name"
2300 assert self.message or self.kind in [constants.CONS_SSH,
2301 constants.CONS_SPICE,
2302 constants.CONS_VNC]
2303 assert self.host or self.kind == constants.CONS_MESSAGE
2304 assert self.port or self.kind in [constants.CONS_MESSAGE,
2305 constants.CONS_SSH]
2306 assert self.user or self.kind in [constants.CONS_MESSAGE,
2307 constants.CONS_SPICE,
2308 constants.CONS_VNC]
2309 assert self.command or self.kind in [constants.CONS_MESSAGE,
2310 constants.CONS_SPICE,
2311 constants.CONS_VNC]
2312 assert self.display or self.kind in [constants.CONS_MESSAGE,
2313 constants.CONS_SPICE,
2314 constants.CONS_SSH]
2315
2316
2317 -class Network(TaggableObject):
2318 """Object representing a network definition for ganeti.
2319
2320 """
2321 __slots__ = [
2322 "name",
2323 "serial_no",
2324 "mac_prefix",
2325 "network",
2326 "network6",
2327 "gateway",
2328 "gateway6",
2329 "reservations",
2330 "ext_reservations",
2331 ] + _TIMESTAMPS + _UUID
2332
2334 """Export a dictionary used by hooks with a network's information.
2335
2336 @type prefix: String
2337 @param prefix: Prefix to prepend to the dict entries
2338
2339 """
2340 result = {
2341 "%sNETWORK_NAME" % prefix: self.name,
2342 "%sNETWORK_UUID" % prefix: self.uuid,
2343 "%sNETWORK_TAGS" % prefix: " ".join(self.GetTags()),
2344 }
2345 if self.network:
2346 result["%sNETWORK_SUBNET" % prefix] = self.network
2347 if self.gateway:
2348 result["%sNETWORK_GATEWAY" % prefix] = self.gateway
2349 if self.network6:
2350 result["%sNETWORK_SUBNET6" % prefix] = self.network6
2351 if self.gateway6:
2352 result["%sNETWORK_GATEWAY6" % prefix] = self.gateway6
2353 if self.mac_prefix:
2354 result["%sNETWORK_MAC_PREFIX" % prefix] = self.mac_prefix
2355
2356 return result
2357
2358 @classmethod
2360 """Custom function for networks.
2361
2362 Remove deprecated network_type and family.
2363
2364 """
2365 if "network_type" in val:
2366 del val["network_type"]
2367 if "family" in val:
2368 del val["family"]
2369 obj = super(Network, cls).FromDict(val)
2370 return obj
2371
2375 """Simple wrapper over ConfigParse that allows serialization.
2376
2377 This class is basically ConfigParser.SafeConfigParser with two
2378 additional methods that allow it to serialize/unserialize to/from a
2379 buffer.
2380
2381 """
2383 """Dump this instance and return the string representation."""
2384 buf = StringIO()
2385 self.write(buf)
2386 return buf.getvalue()
2387
2388 @classmethod
2390 """Load data from a string."""
2391 buf = StringIO(data)
2392 cfp = cls()
2393 cfp.readfp(buf)
2394 return cfp
2395
2396 - def get(self, section, option, **kwargs):
2397 value = None
2398 try:
2399 value = super(SerializableConfigParser, self).get(section, option,
2400 **kwargs)
2401 if value.lower() == constants.VALUE_NONE:
2402 value = None
2403 except ConfigParser.NoOptionError:
2404 r = re.compile(r"(disk|nic)\d+_name|nic\d+_(network|vlan)")
2405 match = r.match(option)
2406 if match:
2407 pass
2408 else:
2409 raise
2410
2411 return value
2412
2415 """Information about an LVM physical volume (PV).
2416
2417 @type name: string
2418 @ivar name: name of the PV
2419 @type vg_name: string
2420 @ivar vg_name: name of the volume group containing the PV
2421 @type size: float
2422 @ivar size: size of the PV in MiB
2423 @type free: float
2424 @ivar free: free space in the PV, in MiB
2425 @type attributes: string
2426 @ivar attributes: PV attributes
2427 @type lv_list: list of strings
2428 @ivar lv_list: names of the LVs hosted on the PV
2429 """
2430 __slots__ = [
2431 "name",
2432 "vg_name",
2433 "size",
2434 "free",
2435 "attributes",
2436 "lv_list"
2437 ]
2438
2440 """Is this PV empty?
2441
2442 """
2443 return self.size <= (self.free + 1)
2444
2446 """Is this PV allocatable?
2447
2448 """
2449 return ("a" in self.attributes)
2450