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