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 """Configuration management for Ganeti
32
33 This module provides the interface to the Ganeti cluster configuration.
34
35 The configuration data is stored on every node but is updated on the master
36 only. After each update, the master distributes the data to the other nodes.
37
38 Currently, the data storage format is JSON. YAML was slow and consuming too
39 much memory.
40
41 """
42
43
44
45
46 import copy
47 import os
48 import random
49 import logging
50 import time
51 import itertools
52
53 from ganeti import errors
54 from ganeti import locking
55 from ganeti import utils
56 from ganeti import constants
57 import ganeti.rpc.node as rpc
58 from ganeti import objects
59 from ganeti import serializer
60 from ganeti import uidpool
61 from ganeti import netutils
62 from ganeti import runtime
63 from ganeti import pathutils
64 from ganeti import network
65
66
67 _config_lock = locking.SharedLock("ConfigWriter")
68
69
70 _UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
85
88 """A temporary resource reservation manager.
89
90 This is used to reserve resources in a job, before using them, making sure
91 other jobs cannot get them in the meantime.
92
93 """
95 self._ec_reserved = {}
96
98 for holder_reserved in self._ec_reserved.values():
99 if resource in holder_reserved:
100 return True
101 return False
102
103 - def Reserve(self, ec_id, resource):
104 if self.Reserved(resource):
105 raise errors.ReservationError("Duplicate reservation for resource '%s'"
106 % str(resource))
107 if ec_id not in self._ec_reserved:
108 self._ec_reserved[ec_id] = set([resource])
109 else:
110 self._ec_reserved[ec_id].add(resource)
111
113 if ec_id in self._ec_reserved:
114 del self._ec_reserved[ec_id]
115
117 all_reserved = set()
118 for holder_reserved in self._ec_reserved.values():
119 all_reserved.update(holder_reserved)
120 return all_reserved
121
123 """ Used when you want to retrieve all reservations for a specific
124 execution context. E.g when commiting reserved IPs for a specific
125 network.
126
127 """
128 ec_reserved = set()
129 if ec_id in self._ec_reserved:
130 ec_reserved.update(self._ec_reserved[ec_id])
131 return ec_reserved
132
133 - def Generate(self, existing, generate_one_fn, ec_id):
134 """Generate a new resource of this type
135
136 """
137 assert callable(generate_one_fn)
138
139 all_elems = self.GetReserved()
140 all_elems.update(existing)
141 retries = 64
142 while retries > 0:
143 new_resource = generate_one_fn()
144 if new_resource is not None and new_resource not in all_elems:
145 break
146 else:
147 raise errors.ConfigurationError("Not able generate new resource"
148 " (last tried: %s)" % new_resource)
149 self.Reserve(ec_id, new_resource)
150 return new_resource
151
154 """Wrapper around L{utils.text.MatchNameComponent}.
155
156 """
157 return utils.MatchNameComponent(short_name, names, case_sensitive=False)
158
161 """Checks if instance's disks' C{iv_name} attributes are in order.
162
163 @type disks: list of L{objects.Disk}
164 @param disks: List of disks
165 @rtype: list of tuples; (int, string, string)
166 @return: List of wrongly named disks, each tuple contains disk index,
167 expected and actual name
168
169 """
170 result = []
171
172 for (idx, disk) in enumerate(disks):
173 exp_iv_name = "disk/%s" % idx
174 if disk.iv_name != exp_iv_name:
175 result.append((idx, exp_iv_name, disk.iv_name))
176
177 return result
178
181 """The interface to the cluster configuration.
182
183 @ivar _temporary_lvs: reservation manager for temporary LVs
184 @ivar _all_rms: a list of all temporary reservation managers
185
186 """
216
218 """Returns RPC runner for configuration.
219
220 """
221 return rpc.ConfigRunner(self._context, address_list)
222
223 - def SetContext(self, context):
224 """Sets Ganeti context.
225
226 """
227 self._context = context
228
229
230 @staticmethod
236
237 @locking.ssynchronized(_config_lock, shared=1)
239 """Get the node params populated with cluster defaults.
240
241 @type node: L{objects.Node}
242 @param node: The node we want to know the params for
243 @return: A dict with the filled in node params
244
245 """
246 nodegroup = self._UnlockedGetNodeGroup(node.group)
247 return self._config_data.cluster.FillND(node, nodegroup)
248
249 @locking.ssynchronized(_config_lock, shared=1)
251 """Get the node groups params populated with cluster defaults.
252
253 @type nodegroup: L{objects.NodeGroup}
254 @param nodegroup: The node group we want to know the params for
255 @return: A dict with the filled in node group params
256
257 """
258 return self._config_data.cluster.FillNDGroup(nodegroup)
259
260 @locking.ssynchronized(_config_lock, shared=1)
272
273 @locking.ssynchronized(_config_lock, shared=1)
275 """Get the disk params populated with inherit chain.
276
277 @type group: L{objects.NodeGroup}
278 @param group: The group we want to know the params for
279 @return: A dict with the filled in disk params
280
281 """
282 return self._UnlockedGetGroupDiskParams(group)
283
285 """Get the disk params populated with inherit chain down to node-group.
286
287 @type group: L{objects.NodeGroup}
288 @param group: The group we want to know the params for
289 @return: A dict with the filled in disk params
290
291 """
292 return self._config_data.cluster.SimpleFillDP(group.diskparams)
293
295 """Return the network mac prefix if it exists or the cluster level default.
296
297 """
298 prefix = None
299 if net_uuid:
300 nobj = self._UnlockedGetNetwork(net_uuid)
301 if nobj.mac_prefix:
302 prefix = nobj.mac_prefix
303
304 return prefix
305
307 """Return a function that randomly generates a MAC suffic
308 and appends it to the given prefix. If prefix is not given get
309 the cluster level default.
310
311 """
312 if not prefix:
313 prefix = self._config_data.cluster.mac_prefix
314
315 def GenMac():
316 byte1 = random.randrange(0, 256)
317 byte2 = random.randrange(0, 256)
318 byte3 = random.randrange(0, 256)
319 mac = "%s:%02x:%02x:%02x" % (prefix, byte1, byte2, byte3)
320 return mac
321
322 return GenMac
323
324 @locking.ssynchronized(_config_lock, shared=1)
326 """Generate a MAC for an instance.
327
328 This should check the current instances for duplicates.
329
330 """
331 existing = self._AllMACs()
332 prefix = self._UnlockedGetNetworkMACPrefix(net_uuid)
333 gen_mac = self._GenerateOneMAC(prefix)
334 return self._temporary_ids.Generate(existing, gen_mac, ec_id)
335
336 @locking.ssynchronized(_config_lock, shared=1)
338 """Reserve a MAC for an instance.
339
340 This only checks instances managed by this cluster, it does not
341 check for potential collisions elsewhere.
342
343 """
344 all_macs = self._AllMACs()
345 if mac in all_macs:
346 raise errors.ReservationError("mac already in use")
347 else:
348 self._temporary_macs.Reserve(ec_id, mac)
349
351 """Commit all reserved IP address to their respective pools
352
353 """
354 for action, address, net_uuid in self._temporary_ips.GetECReserved(ec_id):
355 self._UnlockedCommitIp(action, net_uuid, address)
356
369
371 """Give a specific IP address back to an IP pool.
372
373 The IP address is returned to the IP pool designated by pool_id and marked
374 as reserved.
375
376 """
377 self._temporary_ips.Reserve(ec_id,
378 (constants.RELEASE_ACTION, address, net_uuid))
379
380 @locking.ssynchronized(_config_lock, shared=1)
381 - def ReleaseIp(self, net_uuid, address, ec_id):
382 """Give a specified IP address back to an IP pool.
383
384 This is just a wrapper around _UnlockedReleaseIp.
385
386 """
387 if net_uuid:
388 self._UnlockedReleaseIp(net_uuid, address, ec_id)
389
390 @locking.ssynchronized(_config_lock, shared=1)
404
405 _, address, _ = self._temporary_ips.Generate([], gen_one, ec_id)
406 return address
407
427
428 @locking.ssynchronized(_config_lock, shared=1)
429 - def ReserveIp(self, net_uuid, address, ec_id, check=True):
430 """Reserve a given IPv4 address for use by an instance.
431
432 """
433 if net_uuid:
434 return self._UnlockedReserveIp(net_uuid, address, ec_id, check)
435
436 @locking.ssynchronized(_config_lock, shared=1)
438 """Reserve an VG/LV pair for an instance.
439
440 @type lv_name: string
441 @param lv_name: the logical volume name to reserve
442
443 """
444 all_lvs = self._AllLVs()
445 if lv_name in all_lvs:
446 raise errors.ReservationError("LV already in use")
447 else:
448 self._temporary_lvs.Reserve(ec_id, lv_name)
449
450 @locking.ssynchronized(_config_lock, shared=1)
460
462 """Compute the list of all LVs.
463
464 """
465 lvnames = set()
466 for instance in self._config_data.instances.values():
467 node_data = instance.MapLVsByNode()
468 for lv_list in node_data.values():
469 lvnames.update(lv_list)
470 return lvnames
471
473 """Compute the list of all Disks (recursively, including children).
474
475 """
476 def DiskAndAllChildren(disk):
477 """Returns a list containing the given disk and all of his children.
478
479 """
480 disks = [disk]
481 if disk.children:
482 for child_disk in disk.children:
483 disks.extend(DiskAndAllChildren(child_disk))
484 return disks
485
486 disks = []
487 for instance in self._config_data.instances.values():
488 for disk in instance.disks:
489 disks.extend(DiskAndAllChildren(disk))
490 return disks
491
493 """Compute the list of all NICs.
494
495 """
496 nics = []
497 for instance in self._config_data.instances.values():
498 nics.extend(instance.nics)
499 return nics
500
501 - def _AllIDs(self, include_temporary):
502 """Compute the list of all UUIDs and names we have.
503
504 @type include_temporary: boolean
505 @param include_temporary: whether to include the _temporary_ids set
506 @rtype: set
507 @return: a set of IDs
508
509 """
510 existing = set()
511 if include_temporary:
512 existing.update(self._temporary_ids.GetReserved())
513 existing.update(self._AllLVs())
514 existing.update(self._config_data.instances.keys())
515 existing.update(self._config_data.nodes.keys())
516 existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
517 return existing
518
520 """Generate an unique UUID.
521
522 This checks the current node, instances and disk names for
523 duplicates.
524
525 @rtype: string
526 @return: the unique id
527
528 """
529 existing = self._AllIDs(include_temporary=False)
530 return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
531
532 @locking.ssynchronized(_config_lock, shared=1)
534 """Generate an unique ID.
535
536 This is just a wrapper over the unlocked version.
537
538 @type ec_id: string
539 @param ec_id: unique id for the job to reserve the id to
540
541 """
542 return self._GenerateUniqueID(ec_id)
543
545 """Return all MACs present in the config.
546
547 @rtype: list
548 @return: the list of all MACs
549
550 """
551 result = []
552 for instance in self._config_data.instances.values():
553 for nic in instance.nics:
554 result.append(nic.mac)
555
556 return result
557
559 """Return all DRBD secrets present in the config.
560
561 @rtype: list
562 @return: the list of all DRBD secrets
563
564 """
565 def helper(disk, result):
566 """Recursively gather secrets from this disk."""
567 if disk.dev_type == constants.DT_DRBD8:
568 result.append(disk.logical_id[5])
569 if disk.children:
570 for child in disk.children:
571 helper(child, result)
572
573 result = []
574 for instance in self._config_data.instances.values():
575 for disk in instance.disks:
576 helper(disk, result)
577
578 return result
579
581 """Compute duplicate disk IDs
582
583 @type disk: L{objects.Disk}
584 @param disk: the disk at which to start searching
585 @type l_ids: list
586 @param l_ids: list of current logical ids
587 @rtype: list
588 @return: a list of error messages
589
590 """
591 result = []
592 if disk.logical_id is not None:
593 if disk.logical_id in l_ids:
594 result.append("duplicate logical id %s" % str(disk.logical_id))
595 else:
596 l_ids.append(disk.logical_id)
597
598 if disk.children:
599 for child in disk.children:
600 result.extend(self._CheckDiskIDs(child, l_ids))
601 return result
602
604 """Verify function.
605
606 @rtype: list
607 @return: a list of error messages; a non-empty list signifies
608 configuration errors
609
610 """
611
612 result = []
613 seen_macs = []
614 ports = {}
615 data = self._config_data
616 cluster = data.cluster
617 seen_lids = []
618
619
620 if not cluster.enabled_hypervisors:
621 result.append("enabled hypervisors list doesn't have any entries")
622 invalid_hvs = set(cluster.enabled_hypervisors) - constants.HYPER_TYPES
623 if invalid_hvs:
624 result.append("enabled hypervisors contains invalid entries: %s" %
625 utils.CommaJoin(invalid_hvs))
626 missing_hvp = (set(cluster.enabled_hypervisors) -
627 set(cluster.hvparams.keys()))
628 if missing_hvp:
629 result.append("hypervisor parameters missing for the enabled"
630 " hypervisor(s) %s" % utils.CommaJoin(missing_hvp))
631
632 if not cluster.enabled_disk_templates:
633 result.append("enabled disk templates list doesn't have any entries")
634 invalid_disk_templates = set(cluster.enabled_disk_templates) \
635 - constants.DISK_TEMPLATES
636 if invalid_disk_templates:
637 result.append("enabled disk templates list contains invalid entries:"
638 " %s" % utils.CommaJoin(invalid_disk_templates))
639
640 if cluster.master_node not in data.nodes:
641 result.append("cluster has invalid primary node '%s'" %
642 cluster.master_node)
643
644 def _helper(owner, attr, value, template):
645 try:
646 utils.ForceDictType(value, template)
647 except errors.GenericError, err:
648 result.append("%s has invalid %s: %s" % (owner, attr, err))
649
650 def _helper_nic(owner, params):
651 try:
652 objects.NIC.CheckParameterSyntax(params)
653 except errors.ConfigurationError, err:
654 result.append("%s has invalid nicparams: %s" % (owner, err))
655
656 def _helper_ipolicy(owner, ipolicy, iscluster):
657 try:
658 objects.InstancePolicy.CheckParameterSyntax(ipolicy, iscluster)
659 except errors.ConfigurationError, err:
660 result.append("%s has invalid instance policy: %s" % (owner, err))
661 for key, value in ipolicy.items():
662 if key == constants.ISPECS_MINMAX:
663 for k in range(len(value)):
664 _helper_ispecs(owner, "ipolicy/%s[%s]" % (key, k), value[k])
665 elif key == constants.ISPECS_STD:
666 _helper(owner, "ipolicy/" + key, value,
667 constants.ISPECS_PARAMETER_TYPES)
668 else:
669
670 if key in constants.IPOLICY_PARAMETERS:
671 exp_type = float
672
673 convertible_types = [int]
674 else:
675 exp_type = list
676 convertible_types = []
677
678 if any(isinstance(value, ct) for ct in convertible_types):
679 try:
680 value = exp_type(value)
681 ipolicy[key] = value
682 except ValueError:
683 pass
684 if not isinstance(value, exp_type):
685 result.append("%s has invalid instance policy: for %s,"
686 " expecting %s, got %s" %
687 (owner, key, exp_type.__name__, type(value)))
688
689 def _helper_ispecs(owner, parentkey, params):
690 for (key, value) in params.items():
691 fullkey = "/".join([parentkey, key])
692 _helper(owner, fullkey, value, constants.ISPECS_PARAMETER_TYPES)
693
694
695 _helper("cluster", "beparams", cluster.SimpleFillBE({}),
696 constants.BES_PARAMETER_TYPES)
697 _helper("cluster", "nicparams", cluster.SimpleFillNIC({}),
698 constants.NICS_PARAMETER_TYPES)
699 _helper_nic("cluster", cluster.SimpleFillNIC({}))
700 _helper("cluster", "ndparams", cluster.SimpleFillND({}),
701 constants.NDS_PARAMETER_TYPES)
702 _helper_ipolicy("cluster", cluster.ipolicy, True)
703
704 for disk_template in cluster.diskparams:
705 if disk_template not in constants.DTS_HAVE_ACCESS:
706 continue
707
708 access = cluster.diskparams[disk_template].get(constants.LDP_ACCESS,
709 constants.DISK_KERNELSPACE)
710 if access not in constants.DISK_VALID_ACCESS_MODES:
711 result.append(
712 "Invalid value of '%s:%s': '%s' (expected one of %s)" % (
713 disk_template, constants.LDP_ACCESS, access,
714 utils.CommaJoin(constants.DISK_VALID_ACCESS_MODES)
715 )
716 )
717
718
719 for instance_uuid in data.instances:
720 instance = data.instances[instance_uuid]
721 if instance.uuid != instance_uuid:
722 result.append("instance '%s' is indexed by wrong UUID '%s'" %
723 (instance.name, instance_uuid))
724 if instance.primary_node not in data.nodes:
725 result.append("instance '%s' has invalid primary node '%s'" %
726 (instance.name, instance.primary_node))
727 for snode in instance.secondary_nodes:
728 if snode not in data.nodes:
729 result.append("instance '%s' has invalid secondary node '%s'" %
730 (instance.name, snode))
731 for idx, nic in enumerate(instance.nics):
732 if nic.mac in seen_macs:
733 result.append("instance '%s' has NIC %d mac %s duplicate" %
734 (instance.name, idx, nic.mac))
735 else:
736 seen_macs.append(nic.mac)
737 if nic.nicparams:
738 filled = cluster.SimpleFillNIC(nic.nicparams)
739 owner = "instance %s nic %d" % (instance.name, idx)
740 _helper(owner, "nicparams",
741 filled, constants.NICS_PARAMETER_TYPES)
742 _helper_nic(owner, filled)
743
744
745 if not instance.disk_template in data.cluster.enabled_disk_templates:
746 result.append("instance '%s' uses the disabled disk template '%s'." %
747 (instance.name, instance.disk_template))
748
749
750 if instance.beparams:
751 _helper("instance %s" % instance.name, "beparams",
752 cluster.FillBE(instance), constants.BES_PARAMETER_TYPES)
753
754
755 for (idx, dsk) in enumerate(instance.disks):
756 if dsk.dev_type in constants.DTS_DRBD:
757 tcp_port = dsk.logical_id[2]
758 if tcp_port not in ports:
759 ports[tcp_port] = []
760 ports[tcp_port].append((instance.name, "drbd disk %s" % idx))
761
762 net_port = getattr(instance, "network_port", None)
763 if net_port is not None:
764 if net_port not in ports:
765 ports[net_port] = []
766 ports[net_port].append((instance.name, "network port"))
767
768
769 for idx, disk in enumerate(instance.disks):
770 result.extend(["instance '%s' disk %d error: %s" %
771 (instance.name, idx, msg) for msg in disk.Verify()])
772 result.extend(self._CheckDiskIDs(disk, seen_lids))
773
774 wrong_names = _CheckInstanceDiskIvNames(instance.disks)
775 if wrong_names:
776 tmp = "; ".join(("name of disk %s should be '%s', but is '%s'" %
777 (idx, exp_name, actual_name))
778 for (idx, exp_name, actual_name) in wrong_names)
779
780 result.append("Instance '%s' has wrongly named disks: %s" %
781 (instance.name, tmp))
782
783
784 for free_port in cluster.tcpudp_port_pool:
785 if free_port not in ports:
786 ports[free_port] = []
787 ports[free_port].append(("cluster", "port marked as free"))
788
789
790 keys = ports.keys()
791 keys.sort()
792 for pnum in keys:
793 pdata = ports[pnum]
794 if len(pdata) > 1:
795 txt = utils.CommaJoin(["%s/%s" % val for val in pdata])
796 result.append("tcp/udp port %s has duplicates: %s" % (pnum, txt))
797
798
799 if keys:
800 if keys[-1] > cluster.highest_used_port:
801 result.append("Highest used port mismatch, saved %s, computed %s" %
802 (cluster.highest_used_port, keys[-1]))
803
804 if not data.nodes[cluster.master_node].master_candidate:
805 result.append("Master node is not a master candidate")
806
807
808 mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats()
809 if mc_now < mc_max:
810 result.append("Not enough master candidates: actual %d, target %d" %
811 (mc_now, mc_max))
812
813
814 for node_uuid, node in data.nodes.items():
815 if node.uuid != node_uuid:
816 result.append("Node '%s' is indexed by wrong UUID '%s'" %
817 (node.name, node_uuid))
818 if [node.master_candidate, node.drained, node.offline].count(True) > 1:
819 result.append("Node %s state is invalid: master_candidate=%s,"
820 " drain=%s, offline=%s" %
821 (node.name, node.master_candidate, node.drained,
822 node.offline))
823 if node.group not in data.nodegroups:
824 result.append("Node '%s' has invalid group '%s'" %
825 (node.name, node.group))
826 else:
827 _helper("node %s" % node.name, "ndparams",
828 cluster.FillND(node, data.nodegroups[node.group]),
829 constants.NDS_PARAMETER_TYPES)
830 used_globals = constants.NDC_GLOBALS.intersection(node.ndparams)
831 if used_globals:
832 result.append("Node '%s' has some global parameters set: %s" %
833 (node.name, utils.CommaJoin(used_globals)))
834
835
836 nodegroups_names = set()
837 for nodegroup_uuid in data.nodegroups:
838 nodegroup = data.nodegroups[nodegroup_uuid]
839 if nodegroup.uuid != nodegroup_uuid:
840 result.append("node group '%s' (uuid: '%s') indexed by wrong uuid '%s'"
841 % (nodegroup.name, nodegroup.uuid, nodegroup_uuid))
842 if utils.UUID_RE.match(nodegroup.name.lower()):
843 result.append("node group '%s' (uuid: '%s') has uuid-like name" %
844 (nodegroup.name, nodegroup.uuid))
845 if nodegroup.name in nodegroups_names:
846 result.append("duplicate node group name '%s'" % nodegroup.name)
847 else:
848 nodegroups_names.add(nodegroup.name)
849 group_name = "group %s" % nodegroup.name
850 _helper_ipolicy(group_name, cluster.SimpleFillIPolicy(nodegroup.ipolicy),
851 False)
852 if nodegroup.ndparams:
853 _helper(group_name, "ndparams",
854 cluster.SimpleFillND(nodegroup.ndparams),
855 constants.NDS_PARAMETER_TYPES)
856
857
858 _, duplicates = self._UnlockedComputeDRBDMap()
859 for node, minor, instance_a, instance_b in duplicates:
860 result.append("DRBD minor %d on node %s is assigned twice to instances"
861 " %s and %s" % (minor, node, instance_a, instance_b))
862
863
864 default_nicparams = cluster.nicparams[constants.PP_DEFAULT]
865 ips = {}
866
867 def _AddIpAddress(ip, name):
868 ips.setdefault(ip, []).append(name)
869
870 _AddIpAddress(cluster.master_ip, "cluster_ip")
871
872 for node in data.nodes.values():
873 _AddIpAddress(node.primary_ip, "node:%s/primary" % node.name)
874 if node.secondary_ip != node.primary_ip:
875 _AddIpAddress(node.secondary_ip, "node:%s/secondary" % node.name)
876
877 for instance in data.instances.values():
878 for idx, nic in enumerate(instance.nics):
879 if nic.ip is None:
880 continue
881
882 nicparams = objects.FillDict(default_nicparams, nic.nicparams)
883 nic_mode = nicparams[constants.NIC_MODE]
884 nic_link = nicparams[constants.NIC_LINK]
885
886 if nic_mode == constants.NIC_MODE_BRIDGED:
887 link = "bridge:%s" % nic_link
888 elif nic_mode == constants.NIC_MODE_ROUTED:
889 link = "route:%s" % nic_link
890 elif nic_mode == constants.NIC_MODE_OVS:
891 link = "ovs:%s" % nic_link
892 else:
893 raise errors.ProgrammerError("NIC mode '%s' not handled" % nic_mode)
894
895 _AddIpAddress("%s/%s/%s" % (link, nic.ip, nic.network),
896 "instance:%s/nic:%d" % (instance.name, idx))
897
898 for ip, owners in ips.items():
899 if len(owners) > 1:
900 result.append("IP address %s is used by multiple owners: %s" %
901 (ip, utils.CommaJoin(owners)))
902
903 return result
904
905 @locking.ssynchronized(_config_lock, shared=1)
907 """Verify function.
908
909 This is just a wrapper over L{_UnlockedVerifyConfig}.
910
911 @rtype: list
912 @return: a list of error messages; a non-empty list signifies
913 configuration errors
914
915 """
916 return self._UnlockedVerifyConfig()
917
918 @locking.ssynchronized(_config_lock)
920 """Adds a new port to the available port pool.
921
922 @warning: this method does not "flush" the configuration (via
923 L{_WriteConfig}); callers should do that themselves once the
924 configuration is stable
925
926 """
927 if not isinstance(port, int):
928 raise errors.ProgrammerError("Invalid type passed for port")
929
930 self._config_data.cluster.tcpudp_port_pool.add(port)
931
932 @locking.ssynchronized(_config_lock, shared=1)
934 """Returns a copy of the current port list.
935
936 """
937 return self._config_data.cluster.tcpudp_port_pool.copy()
938
939 @locking.ssynchronized(_config_lock)
961
963 """Compute the used DRBD minor/nodes.
964
965 @rtype: (dict, list)
966 @return: dictionary of node_uuid: dict of minor: instance_uuid;
967 the returned dict will have all the nodes in it (even if with
968 an empty list), and a list of duplicates; if the duplicates
969 list is not empty, the configuration is corrupted and its caller
970 should raise an exception
971
972 """
973 def _AppendUsedMinors(get_node_name_fn, instance, disk, used):
974 duplicates = []
975 if disk.dev_type == constants.DT_DRBD8 and len(disk.logical_id) >= 5:
976 node_a, node_b, _, minor_a, minor_b = disk.logical_id[:5]
977 for node_uuid, minor in ((node_a, minor_a), (node_b, minor_b)):
978 assert node_uuid in used, \
979 ("Node '%s' of instance '%s' not found in node list" %
980 (get_node_name_fn(node_uuid), instance.name))
981 if minor in used[node_uuid]:
982 duplicates.append((node_uuid, minor, instance.uuid,
983 used[node_uuid][minor]))
984 else:
985 used[node_uuid][minor] = instance.uuid
986 if disk.children:
987 for child in disk.children:
988 duplicates.extend(_AppendUsedMinors(get_node_name_fn, instance, child,
989 used))
990 return duplicates
991
992 duplicates = []
993 my_dict = dict((node_uuid, {}) for node_uuid in self._config_data.nodes)
994 for instance in self._config_data.instances.itervalues():
995 for disk in instance.disks:
996 duplicates.extend(_AppendUsedMinors(self._UnlockedGetNodeName,
997 instance, disk, my_dict))
998 for (node_uuid, minor), inst_uuid in self._temporary_drbds.iteritems():
999 if minor in my_dict[node_uuid] and my_dict[node_uuid][minor] != inst_uuid:
1000 duplicates.append((node_uuid, minor, inst_uuid,
1001 my_dict[node_uuid][minor]))
1002 else:
1003 my_dict[node_uuid][minor] = inst_uuid
1004 return my_dict, duplicates
1005
1006 @locking.ssynchronized(_config_lock)
1008 """Compute the used DRBD minor/nodes.
1009
1010 This is just a wrapper over L{_UnlockedComputeDRBDMap}.
1011
1012 @return: dictionary of node_uuid: dict of minor: instance_uuid;
1013 the returned dict will have all the nodes in it (even if with
1014 an empty list).
1015
1016 """
1017 d_map, duplicates = self._UnlockedComputeDRBDMap()
1018 if duplicates:
1019 raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
1020 str(duplicates))
1021 return d_map
1022
1023 @locking.ssynchronized(_config_lock)
1025 """Allocate a drbd minor.
1026
1027 The free minor will be automatically computed from the existing
1028 devices. A node can be given multiple times in order to allocate
1029 multiple minors. The result is the list of minors, in the same
1030 order as the passed nodes.
1031
1032 @type inst_uuid: string
1033 @param inst_uuid: the instance for which we allocate minors
1034
1035 """
1036 assert isinstance(inst_uuid, basestring), \
1037 "Invalid argument '%s' passed to AllocateDRBDMinor" % inst_uuid
1038
1039 d_map, duplicates = self._UnlockedComputeDRBDMap()
1040 if duplicates:
1041 raise errors.ConfigurationError("Duplicate DRBD ports detected: %s" %
1042 str(duplicates))
1043 result = []
1044 for nuuid in node_uuids:
1045 ndata = d_map[nuuid]
1046 if not ndata:
1047
1048 result.append(0)
1049 ndata[0] = inst_uuid
1050 self._temporary_drbds[(nuuid, 0)] = inst_uuid
1051 continue
1052 keys = ndata.keys()
1053 keys.sort()
1054 ffree = utils.FirstFree(keys)
1055 if ffree is None:
1056
1057
1058 minor = keys[-1] + 1
1059 else:
1060 minor = ffree
1061
1062 assert minor not in d_map[nuuid], \
1063 ("Attempt to reuse allocated DRBD minor %d on node %s,"
1064 " already allocated to instance %s" %
1065 (minor, nuuid, d_map[nuuid][minor]))
1066 ndata[minor] = inst_uuid
1067
1068 r_key = (nuuid, minor)
1069 assert r_key not in self._temporary_drbds, \
1070 ("Attempt to reuse reserved DRBD minor %d on node %s,"
1071 " reserved for instance %s" %
1072 (minor, nuuid, self._temporary_drbds[r_key]))
1073 self._temporary_drbds[r_key] = inst_uuid
1074 result.append(minor)
1075 logging.debug("Request to allocate drbd minors, input: %s, returning %s",
1076 node_uuids, result)
1077 return result
1078
1080 """Release temporary drbd minors allocated for a given instance.
1081
1082 @type inst_uuid: string
1083 @param inst_uuid: the instance for which temporary minors should be
1084 released
1085
1086 """
1087 assert isinstance(inst_uuid, basestring), \
1088 "Invalid argument passed to ReleaseDRBDMinors"
1089 for key, uuid in self._temporary_drbds.items():
1090 if uuid == inst_uuid:
1091 del self._temporary_drbds[key]
1092
1093 @locking.ssynchronized(_config_lock)
1095 """Release temporary drbd minors allocated for a given instance.
1096
1097 This should be called on the error paths, on the success paths
1098 it's automatically called by the ConfigWriter add and update
1099 functions.
1100
1101 This function is just a wrapper over L{_UnlockedReleaseDRBDMinors}.
1102
1103 @type inst_uuid: string
1104 @param inst_uuid: the instance for which temporary minors should be
1105 released
1106
1107 """
1108 self._UnlockedReleaseDRBDMinors(inst_uuid)
1109
1110 @locking.ssynchronized(_config_lock, shared=1)
1112 """Get the configuration version.
1113
1114 @return: Config version
1115
1116 """
1117 return self._config_data.version
1118
1119 @locking.ssynchronized(_config_lock, shared=1)
1121 """Get cluster name.
1122
1123 @return: Cluster name
1124
1125 """
1126 return self._config_data.cluster.cluster_name
1127
1128 @locking.ssynchronized(_config_lock, shared=1)
1130 """Get the UUID of the master node for this cluster.
1131
1132 @return: Master node UUID
1133
1134 """
1135 return self._config_data.cluster.master_node
1136
1137 @locking.ssynchronized(_config_lock, shared=1)
1139 """Get the hostname of the master node for this cluster.
1140
1141 @return: Master node hostname
1142
1143 """
1144 return self._UnlockedGetNodeName(self._config_data.cluster.master_node)
1145
1146 @locking.ssynchronized(_config_lock, shared=1)
1148 """Get the master node information for this cluster.
1149
1150 @rtype: objects.Node
1151 @return: Master node L{objects.Node} object
1152
1153 """
1154 return self._UnlockedGetNodeInfo(self._config_data.cluster.master_node)
1155
1156 @locking.ssynchronized(_config_lock, shared=1)
1158 """Get the IP of the master node for this cluster.
1159
1160 @return: Master IP
1161
1162 """
1163 return self._config_data.cluster.master_ip
1164
1165 @locking.ssynchronized(_config_lock, shared=1)
1167 """Get the master network device for this cluster.
1168
1169 """
1170 return self._config_data.cluster.master_netdev
1171
1172 @locking.ssynchronized(_config_lock, shared=1)
1174 """Get the netmask of the master node for this cluster.
1175
1176 """
1177 return self._config_data.cluster.master_netmask
1178
1179 @locking.ssynchronized(_config_lock, shared=1)
1181 """Get flag representing whether to use the external master IP setup script.
1182
1183 """
1184 return self._config_data.cluster.use_external_mip_script
1185
1186 @locking.ssynchronized(_config_lock, shared=1)
1188 """Get the file storage dir for this cluster.
1189
1190 """
1191 return self._config_data.cluster.file_storage_dir
1192
1193 @locking.ssynchronized(_config_lock, shared=1)
1195 """Get the shared file storage dir for this cluster.
1196
1197 """
1198 return self._config_data.cluster.shared_file_storage_dir
1199
1200 @locking.ssynchronized(_config_lock, shared=1)
1202 """Get the Gluster storage dir for this cluster.
1203
1204 """
1205 return self._config_data.cluster.gluster_storage_dir
1206
1207 @locking.ssynchronized(_config_lock, shared=1)
1209 """Get the hypervisor type for this cluster.
1210
1211 """
1212 return self._config_data.cluster.enabled_hypervisors[0]
1213
1214 @locking.ssynchronized(_config_lock, shared=1)
1216 """Return the rsa hostkey from the config.
1217
1218 @rtype: string
1219 @return: the rsa hostkey
1220
1221 """
1222 return self._config_data.cluster.rsahostkeypub
1223
1224 @locking.ssynchronized(_config_lock, shared=1)
1226 """Return the dsa hostkey from the config.
1227
1228 @rtype: string
1229 @return: the dsa hostkey
1230
1231 """
1232 return self._config_data.cluster.dsahostkeypub
1233
1234 @locking.ssynchronized(_config_lock, shared=1)
1236 """Get the default instance allocator for this cluster.
1237
1238 """
1239 return self._config_data.cluster.default_iallocator
1240
1241 @locking.ssynchronized(_config_lock, shared=1)
1243 """Get the default instance allocator parameters for this cluster.
1244
1245 @rtype: dict
1246 @return: dict of iallocator parameters
1247
1248 """
1249 return self._config_data.cluster.default_iallocator_params
1250
1251 @locking.ssynchronized(_config_lock, shared=1)
1253 """Get cluster primary ip family.
1254
1255 @return: primary ip family
1256
1257 """
1258 return self._config_data.cluster.primary_ip_family
1259
1260 @locking.ssynchronized(_config_lock, shared=1)
1262 """Get network parameters of the master node.
1263
1264 @rtype: L{object.MasterNetworkParameters}
1265 @return: network parameters of the master node
1266
1267 """
1268 cluster = self._config_data.cluster
1269 result = objects.MasterNetworkParameters(
1270 uuid=cluster.master_node, ip=cluster.master_ip,
1271 netmask=cluster.master_netmask, netdev=cluster.master_netdev,
1272 ip_family=cluster.primary_ip_family)
1273
1274 return result
1275
1276 @locking.ssynchronized(_config_lock)
1278 """Add a node group to the configuration.
1279
1280 This method calls group.UpgradeConfig() to fill any missing attributes
1281 according to their default values.
1282
1283 @type group: L{objects.NodeGroup}
1284 @param group: the NodeGroup object to add
1285 @type ec_id: string
1286 @param ec_id: unique id for the job to use when creating a missing UUID
1287 @type check_uuid: bool
1288 @param check_uuid: add an UUID to the group if it doesn't have one or, if
1289 it does, ensure that it does not exist in the
1290 configuration already
1291
1292 """
1293 self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1294 self._WriteConfig()
1295
1324
1325 @locking.ssynchronized(_config_lock)
1327 """Remove a node group from the configuration.
1328
1329 @type group_uuid: string
1330 @param group_uuid: the UUID of the node group to remove
1331
1332 """
1333 logging.info("Removing node group %s from configuration", group_uuid)
1334
1335 if group_uuid not in self._config_data.nodegroups:
1336 raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
1337
1338 assert len(self._config_data.nodegroups) != 1, \
1339 "Group '%s' is the only group, cannot be removed" % group_uuid
1340
1341 del self._config_data.nodegroups[group_uuid]
1342 self._config_data.cluster.serial_no += 1
1343 self._WriteConfig()
1344
1346 """Lookup a node group's UUID.
1347
1348 @type target: string or None
1349 @param target: group name or UUID or None to look for the default
1350 @rtype: string
1351 @return: nodegroup UUID
1352 @raises errors.OpPrereqError: when the target group cannot be found
1353
1354 """
1355 if target is None:
1356 if len(self._config_data.nodegroups) != 1:
1357 raise errors.OpPrereqError("More than one node group exists. Target"
1358 " group must be specified explicitly.")
1359 else:
1360 return self._config_data.nodegroups.keys()[0]
1361 if target in self._config_data.nodegroups:
1362 return target
1363 for nodegroup in self._config_data.nodegroups.values():
1364 if nodegroup.name == target:
1365 return nodegroup.uuid
1366 raise errors.OpPrereqError("Node group '%s' not found" % target,
1367 errors.ECODE_NOENT)
1368
1369 @locking.ssynchronized(_config_lock, shared=1)
1371 """Lookup a node group's UUID.
1372
1373 This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1374
1375 @type target: string or None
1376 @param target: group name or UUID or None to look for the default
1377 @rtype: string
1378 @return: nodegroup UUID
1379
1380 """
1381 return self._UnlockedLookupNodeGroup(target)
1382
1384 """Lookup a node group.
1385
1386 @type uuid: string
1387 @param uuid: group UUID
1388 @rtype: L{objects.NodeGroup} or None
1389 @return: nodegroup object, or None if not found
1390
1391 """
1392 if uuid not in self._config_data.nodegroups:
1393 return None
1394
1395 return self._config_data.nodegroups[uuid]
1396
1397 @locking.ssynchronized(_config_lock, shared=1)
1399 """Lookup a node group.
1400
1401 @type uuid: string
1402 @param uuid: group UUID
1403 @rtype: L{objects.NodeGroup} or None
1404 @return: nodegroup object, or None if not found
1405
1406 """
1407 return self._UnlockedGetNodeGroup(uuid)
1408
1410 """Get the configuration of all node groups.
1411
1412 """
1413 return dict(self._config_data.nodegroups)
1414
1415 @locking.ssynchronized(_config_lock, shared=1)
1421
1422 @locking.ssynchronized(_config_lock, shared=1)
1424 """Get the configuration of all node groups expressed as a dictionary of
1425 dictionaries.
1426
1427 """
1428 return dict(map(lambda (uuid, ng): (uuid, ng.ToDict()),
1429 self._UnlockedGetAllNodeGroupsInfo().items()))
1430
1431 @locking.ssynchronized(_config_lock, shared=1)
1433 """Get a list of node groups.
1434
1435 """
1436 return self._config_data.nodegroups.keys()
1437
1438 @locking.ssynchronized(_config_lock, shared=1)
1440 """Get nodes which are member in the same nodegroups as the given nodes.
1441
1442 """
1443 ngfn = lambda node_uuid: self._UnlockedGetNodeInfo(node_uuid).group
1444 return frozenset(member_uuid
1445 for node_uuid in nodes
1446 for member_uuid in
1447 self._UnlockedGetNodeGroup(ngfn(node_uuid)).members)
1448
1449 @locking.ssynchronized(_config_lock, shared=1)
1451 """Get the configuration of multiple node groups.
1452
1453 @param group_uuids: List of node group UUIDs
1454 @rtype: list
1455 @return: List of tuples of (group_uuid, group_info)
1456
1457 """
1458 return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1459
1460 @locking.ssynchronized(_config_lock)
1493
1495 """Ensures a given object has a valid UUID.
1496
1497 @param item: the instance or node to be checked
1498 @param ec_id: the execution context id for the uuid reservation
1499
1500 """
1501 if not item.uuid:
1502 item.uuid = self._GenerateUniqueID(ec_id)
1503 else:
1504 self._CheckUniqueUUID(item, include_temporary=True)
1505
1507 """Checks that the UUID of the given object is unique.
1508
1509 @param item: the instance or node to be checked
1510 @param include_temporary: whether temporarily generated UUID's should be
1511 included in the check. If the UUID of the item to be checked is
1512 a temporarily generated one, this has to be C{False}.
1513
1514 """
1515 if not item.uuid:
1516 raise errors.ConfigurationError("'%s' must have an UUID" % (item.name,))
1517 if item.uuid in self._AllIDs(include_temporary=include_temporary):
1518 raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1519 " in use" % (item.name, item.uuid))
1520
1523 """Set the instance's status to a given value.
1524
1525 """
1526 if inst_uuid not in self._config_data.instances:
1527 raise errors.ConfigurationError("Unknown instance '%s'" %
1528 inst_uuid)
1529 instance = self._config_data.instances[inst_uuid]
1530
1531 if status is None:
1532 status = instance.admin_state
1533 if disks_active is None:
1534 disks_active = instance.disks_active
1535 if admin_state_source is None:
1536 admin_state_source = instance.admin_state_source
1537
1538 assert status in constants.ADMINST_ALL, \
1539 "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1540
1541 if instance.admin_state != status or \
1542 instance.disks_active != disks_active or \
1543 instance.admin_state_source != admin_state_source:
1544 instance.admin_state = status
1545 instance.disks_active = disks_active
1546 instance.admin_state_source = admin_state_source
1547 instance.serial_no += 1
1548 instance.mtime = time.time()
1549 self._WriteConfig()
1550
1551 @locking.ssynchronized(_config_lock)
1560
1561 @locking.ssynchronized(_config_lock)
1570
1571 @locking.ssynchronized(_config_lock)
1573 """Remove the instance from the configuration.
1574
1575 """
1576 if inst_uuid not in self._config_data.instances:
1577 raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1578
1579
1580
1581 inst = self._config_data.instances[inst_uuid]
1582 network_port = getattr(inst, "network_port", None)
1583 if network_port is not None:
1584 self._config_data.cluster.tcpudp_port_pool.add(network_port)
1585
1586 instance = self._UnlockedGetInstanceInfo(inst_uuid)
1587
1588 for nic in instance.nics:
1589 if nic.network and nic.ip:
1590
1591 self._UnlockedCommitIp(constants.RELEASE_ACTION, nic.network, nic.ip)
1592
1593 del self._config_data.instances[inst_uuid]
1594 self._config_data.cluster.serial_no += 1
1595 self._WriteConfig()
1596
1597 @locking.ssynchronized(_config_lock)
1599 """Rename an instance.
1600
1601 This needs to be done in ConfigWriter and not by RemoveInstance
1602 combined with AddInstance as only we can guarantee an atomic
1603 rename.
1604
1605 """
1606 if inst_uuid not in self._config_data.instances:
1607 raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1608
1609 inst = self._config_data.instances[inst_uuid]
1610 inst.name = new_name
1611
1612 for (_, disk) in enumerate(inst.disks):
1613 if disk.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1614
1615 file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1616 disk.logical_id = (disk.logical_id[0],
1617 utils.PathJoin(file_storage_dir, inst.name,
1618 os.path.basename(disk.logical_id[1])))
1619
1620
1621 self._config_data.cluster.serial_no += 1
1622
1623 self._WriteConfig()
1624
1625 @locking.ssynchronized(_config_lock)
1627 """Mark the status of an instance to down in the configuration.
1628
1629 This does not touch the instance disks active flag, as shut down instances
1630 can still have active disks.
1631
1632 """
1633 self._SetInstanceStatus(inst_uuid, constants.ADMINST_DOWN, None,
1634 constants.ADMIN_SOURCE)
1635
1636 @locking.ssynchronized(_config_lock)
1638 """Mark the status of an instance to user down in the configuration.
1639
1640 This does not touch the instance disks active flag, as user shut
1641 down instances can still have active disks.
1642
1643 """
1644
1645 self._SetInstanceStatus(inst_uuid, constants.ADMINST_DOWN, None,
1646 constants.USER_SOURCE)
1647
1648 @locking.ssynchronized(_config_lock)
1650 """Mark the status of instance disks active.
1651
1652 """
1653 self._SetInstanceStatus(inst_uuid, None, True, None)
1654
1655 @locking.ssynchronized(_config_lock)
1657 """Mark the status of instance disks inactive.
1658
1659 """
1660 self._SetInstanceStatus(inst_uuid, None, False, None)
1661
1663 """Get the list of instances.
1664
1665 This function is for internal use, when the config lock is already held.
1666
1667 """
1668 return self._config_data.instances.keys()
1669
1670 @locking.ssynchronized(_config_lock, shared=1)
1672 """Get the list of instances.
1673
1674 @return: array of instances, ex. ['instance2-uuid', 'instance1-uuid']
1675
1676 """
1677 return self._UnlockedGetInstanceList()
1678
1680 """Attempt to expand an incomplete instance name.
1681
1682 """
1683
1684 all_insts = self.GetAllInstancesInfo().values()
1685 expanded_name = _MatchNameComponentIgnoreCase(
1686 short_name, [inst.name for inst in all_insts])
1687
1688 if expanded_name is not None:
1689
1690 inst = (filter(lambda n: n.name == expanded_name, all_insts)[0])
1691 return (inst.uuid, inst.name)
1692 else:
1693 return (None, None)
1694
1696 """Returns information about an instance.
1697
1698 This function is for internal use, when the config lock is already held.
1699
1700 """
1701 if inst_uuid not in self._config_data.instances:
1702 return None
1703
1704 return self._config_data.instances[inst_uuid]
1705
1706 @locking.ssynchronized(_config_lock, shared=1)
1708 """Returns information about an instance.
1709
1710 It takes the information from the configuration file. Other information of
1711 an instance are taken from the live systems.
1712
1713 @param inst_uuid: UUID of the instance
1714
1715 @rtype: L{objects.Instance}
1716 @return: the instance object
1717
1718 """
1719 return self._UnlockedGetInstanceInfo(inst_uuid)
1720
1721 @locking.ssynchronized(_config_lock, shared=1)
1739
1740 @locking.ssynchronized(_config_lock, shared=1)
1757
1758 @locking.ssynchronized(_config_lock, shared=1)
1760 """Get the configuration of multiple instances.
1761
1762 @param inst_uuids: list of instance UUIDs
1763 @rtype: list
1764 @return: list of tuples (instance UUID, instance_info), where
1765 instance_info is what would GetInstanceInfo return for the
1766 node, while keeping the original order
1767
1768 """
1769 return [(uuid, self._UnlockedGetInstanceInfo(uuid)) for uuid in inst_uuids]
1770
1771 @locking.ssynchronized(_config_lock, shared=1)
1773 """Get the configuration of multiple instances.
1774
1775 @param inst_names: list of instance names
1776 @rtype: list
1777 @return: list of tuples (instance, instance_info), where
1778 instance_info is what would GetInstanceInfo return for the
1779 node, while keeping the original order
1780
1781 """
1782 result = []
1783 for name in inst_names:
1784 instance = self._UnlockedGetInstanceInfoByName(name)
1785 if instance:
1786 result.append((instance.uuid, instance))
1787 else:
1788 raise errors.ConfigurationError("Instance data of instance '%s'"
1789 " not found." % name)
1790 return result
1791
1792 @locking.ssynchronized(_config_lock, shared=1)
1794 """Get the configuration of all instances.
1795
1796 @rtype: dict
1797 @return: dict of (instance, instance_info), where instance_info is what
1798 would GetInstanceInfo return for the node
1799
1800 """
1801 return self._UnlockedGetAllInstancesInfo()
1802
1807
1808 @locking.ssynchronized(_config_lock, shared=1)
1810 """Get instance configuration with a filter.
1811
1812 @type filter_fn: callable
1813 @param filter_fn: Filter function receiving instance object as parameter,
1814 returning boolean. Important: this function is called while the
1815 configuration locks is held. It must not do any complex work or call
1816 functions potentially leading to a deadlock. Ideally it doesn't call any
1817 other functions and just compares instance attributes.
1818
1819 """
1820 return dict((uuid, inst)
1821 for (uuid, inst) in self._config_data.instances.items()
1822 if filter_fn(inst))
1823
1824 @locking.ssynchronized(_config_lock, shared=1)
1826 """Get the L{objects.Instance} object for a named instance.
1827
1828 @param inst_name: name of the instance to get information for
1829 @type inst_name: string
1830 @return: the corresponding L{objects.Instance} instance or None if no
1831 information is available
1832
1833 """
1834 return self._UnlockedGetInstanceInfoByName(inst_name)
1835
1841
1847
1848 @locking.ssynchronized(_config_lock, shared=1)
1850 """Gets the instance name for the passed instance.
1851
1852 @param inst_uuid: instance UUID to get name for
1853 @type inst_uuid: string
1854 @rtype: string
1855 @return: instance name
1856
1857 """
1858 return self._UnlockedGetInstanceName(inst_uuid)
1859
1860 @locking.ssynchronized(_config_lock, shared=1)
1862 """Gets the instance names for the passed list of nodes.
1863
1864 @param inst_uuids: list of instance UUIDs to get names for
1865 @type inst_uuids: list of strings
1866 @rtype: list of strings
1867 @return: list of instance names
1868
1869 """
1870 return self._UnlockedGetInstanceNames(inst_uuids)
1871
1874
1875 @locking.ssynchronized(_config_lock)
1893
1894 @locking.ssynchronized(_config_lock)
1896 """Remove a node from the configuration.
1897
1898 """
1899 logging.info("Removing node %s from configuration", node_uuid)
1900
1901 if node_uuid not in self._config_data.nodes:
1902 raise errors.ConfigurationError("Unknown node '%s'" % node_uuid)
1903
1904 self._UnlockedRemoveNodeFromGroup(self._config_data.nodes[node_uuid])
1905 del self._config_data.nodes[node_uuid]
1906 self._config_data.cluster.serial_no += 1
1907 self._WriteConfig()
1908
1924
1926 """Get the configuration of a node, as stored in the config.
1927
1928 This function is for internal use, when the config lock is already
1929 held.
1930
1931 @param node_uuid: the node UUID
1932
1933 @rtype: L{objects.Node}
1934 @return: the node object
1935
1936 """
1937 if node_uuid not in self._config_data.nodes:
1938 return None
1939
1940 return self._config_data.nodes[node_uuid]
1941
1942 @locking.ssynchronized(_config_lock, shared=1)
1944 """Get the configuration of a node, as stored in the config.
1945
1946 This is just a locked wrapper over L{_UnlockedGetNodeInfo}.
1947
1948 @param node_uuid: the node UUID
1949
1950 @rtype: L{objects.Node}
1951 @return: the node object
1952
1953 """
1954 return self._UnlockedGetNodeInfo(node_uuid)
1955
1956 @locking.ssynchronized(_config_lock, shared=1)
1958 """Get the instances of a node, as stored in the config.
1959
1960 @param node_uuid: the node UUID
1961
1962 @rtype: (list, list)
1963 @return: a tuple with two lists: the primary and the secondary instances
1964
1965 """
1966 pri = []
1967 sec = []
1968 for inst in self._config_data.instances.values():
1969 if inst.primary_node == node_uuid:
1970 pri.append(inst.uuid)
1971 if node_uuid in inst.secondary_nodes:
1972 sec.append(inst.uuid)
1973 return (pri, sec)
1974
1975 @locking.ssynchronized(_config_lock, shared=1)
1977 """Get the instances of a node group.
1978
1979 @param uuid: Node group UUID
1980 @param primary_only: Whether to only consider primary nodes
1981 @rtype: frozenset
1982 @return: List of instance UUIDs in node group
1983
1984 """
1985 if primary_only:
1986 nodes_fn = lambda inst: [inst.primary_node]
1987 else:
1988 nodes_fn = lambda inst: inst.all_nodes
1989
1990 return frozenset(inst.uuid
1991 for inst in self._config_data.instances.values()
1992 for node_uuid in nodes_fn(inst)
1993 if self._UnlockedGetNodeInfo(node_uuid).group == uuid)
1994
1996 """Return the string representation of the list of hyervisor parameters of
1997 the given hypervisor.
1998
1999 @see: C{GetHvparams}
2000
2001 """
2002 result = ""
2003 hvparams = self._config_data.cluster.hvparams[hvname]
2004 for key in hvparams:
2005 result += "%s=%s\n" % (key, hvparams[key])
2006 return result
2007
2008 @locking.ssynchronized(_config_lock, shared=1)
2010 """Return the hypervisor parameters of the given hypervisor.
2011
2012 @type hvname: string
2013 @param hvname: name of a hypervisor
2014 @rtype: string
2015 @return: string containing key-value-pairs, one pair on each line;
2016 format: KEY=VALUE
2017
2018 """
2019 return self._UnlockedGetHvparamsString(hvname)
2020
2022 """Return the list of nodes which are in the configuration.
2023
2024 This function is for internal use, when the config lock is already
2025 held.
2026
2027 @rtype: list
2028
2029 """
2030 return self._config_data.nodes.keys()
2031
2032 @locking.ssynchronized(_config_lock, shared=1)
2034 """Return the list of nodes which are in the configuration.
2035
2036 """
2037 return self._UnlockedGetNodeList()
2038
2046
2047 @locking.ssynchronized(_config_lock, shared=1)
2053
2054 @locking.ssynchronized(_config_lock, shared=1)
2062
2063 @locking.ssynchronized(_config_lock, shared=1)
2071
2072 @locking.ssynchronized(_config_lock, shared=1)
2080
2081 @locking.ssynchronized(_config_lock, shared=1)
2083 """Get the configuration of multiple nodes.
2084
2085 @param node_uuids: list of node UUIDs
2086 @rtype: list
2087 @return: list of tuples of (node, node_info), where node_info is
2088 what would GetNodeInfo return for the node, in the original
2089 order
2090
2091 """
2092 return [(uuid, self._UnlockedGetNodeInfo(uuid)) for uuid in node_uuids]
2093
2095 """Gets configuration of all nodes.
2096
2097 @note: See L{GetAllNodesInfo}
2098
2099 """
2100 return dict([(node_uuid, self._UnlockedGetNodeInfo(node_uuid))
2101 for node_uuid in self._UnlockedGetNodeList()])
2102
2103 @locking.ssynchronized(_config_lock, shared=1)
2105 """Get the configuration of all nodes.
2106
2107 @rtype: dict
2108 @return: dict of (node, node_info), where node_info is what
2109 would GetNodeInfo return for the node
2110
2111 """
2112 return self._UnlockedGetAllNodesInfo()
2113
2119
2120 @locking.ssynchronized(_config_lock, shared=1)
2122 """Get the L{objects.Node} object for a named node.
2123
2124 @param node_name: name of the node to get information for
2125 @type node_name: string
2126 @return: the corresponding L{objects.Node} instance or None if no
2127 information is available
2128
2129 """
2130 return self._UnlockedGetNodeInfoByName(node_name)
2131
2132 @locking.ssynchronized(_config_lock, shared=1)
2134 """Get the L{objects.NodeGroup} object for a named node group.
2135
2136 @param nodegroup_name: name of the node group to get information for
2137 @type nodegroup_name: string
2138 @return: the corresponding L{objects.NodeGroup} instance or None if no
2139 information is available
2140
2141 """
2142 for nodegroup in self._UnlockedGetAllNodeGroupsInfo().values():
2143 if nodegroup.name == nodegroup_name:
2144 return nodegroup
2145 return None
2146
2157
2158 @locking.ssynchronized(_config_lock, shared=1)
2160 """Gets the node name for the passed node.
2161
2162 @param node_spec: node to get names for
2163 @type node_spec: either node UUID or a L{objects.Node} object
2164 @rtype: string
2165 @return: node name
2166
2167 """
2168 return self._UnlockedGetNodeName(node_spec)
2169
2172
2173 @locking.ssynchronized(_config_lock, shared=1)
2175 """Gets the node names for the passed list of nodes.
2176
2177 @param node_specs: list of nodes to get names for
2178 @type node_specs: list of either node UUIDs or L{objects.Node} objects
2179 @rtype: list of strings
2180 @return: list of node names
2181
2182 """
2183 return self._UnlockedGetNodeNames(node_specs)
2184
2185 @locking.ssynchronized(_config_lock, shared=1)
2187 """Returns groups for a list of nodes.
2188
2189 @type node_uuids: list of string
2190 @param node_uuids: List of node UUIDs
2191 @rtype: frozenset
2192
2193 """
2194 return frozenset(self._UnlockedGetNodeInfo(uuid).group
2195 for uuid in node_uuids)
2196
2198 """Get the number of current and maximum desired and possible candidates.
2199
2200 @type exceptions: list
2201 @param exceptions: if passed, list of nodes that should be ignored
2202 @rtype: tuple
2203 @return: tuple of (current, desired and possible, possible)
2204
2205 """
2206 mc_now = mc_should = mc_max = 0
2207 for node in self._config_data.nodes.values():
2208 if exceptions and node.uuid in exceptions:
2209 continue
2210 if not (node.offline or node.drained) and node.master_capable:
2211 mc_max += 1
2212 if node.master_candidate:
2213 mc_now += 1
2214 mc_should = min(mc_max, self._config_data.cluster.candidate_pool_size)
2215 return (mc_now, mc_should, mc_max)
2216
2217 @locking.ssynchronized(_config_lock, shared=1)
2219 """Get the number of current and maximum possible candidates.
2220
2221 This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
2222
2223 @type exceptions: list
2224 @param exceptions: if passed, list of nodes that should be ignored
2225 @rtype: tuple
2226 @return: tuple of (current, max)
2227
2228 """
2229 return self._UnlockedGetMasterCandidateStats(exceptions)
2230
2231 @locking.ssynchronized(_config_lock)
2232 - def MaintainCandidatePool(self, exception_node_uuids):
2233 """Try to grow the candidate pool to the desired size.
2234
2235 @type exception_node_uuids: list
2236 @param exception_node_uuids: if passed, list of nodes that should be ignored
2237 @rtype: list
2238 @return: list with the adjusted nodes (L{objects.Node} instances)
2239
2240 """
2241 mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(
2242 exception_node_uuids)
2243 mod_list = []
2244 if mc_now < mc_max:
2245 node_list = self._config_data.nodes.keys()
2246 random.shuffle(node_list)
2247 for uuid in node_list:
2248 if mc_now >= mc_max:
2249 break
2250 node = self._config_data.nodes[uuid]
2251 if (node.master_candidate or node.offline or node.drained or
2252 node.uuid in exception_node_uuids or not node.master_capable):
2253 continue
2254 mod_list.append(node)
2255 node.master_candidate = True
2256 node.serial_no += 1
2257 mc_now += 1
2258 if mc_now != mc_max:
2259
2260 logging.warning("Warning: MaintainCandidatePool didn't manage to"
2261 " fill the candidate pool (%d/%d)", mc_now, mc_max)
2262 if mod_list:
2263 self._config_data.cluster.serial_no += 1
2264 self._WriteConfig()
2265
2266 return mod_list
2267
2269 """Add a given node to the specified group.
2270
2271 """
2272 if nodegroup_uuid not in self._config_data.nodegroups:
2273
2274
2275
2276
2277 raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
2278 if node_uuid not in self._config_data.nodegroups[nodegroup_uuid].members:
2279 self._config_data.nodegroups[nodegroup_uuid].members.append(node_uuid)
2280
2282 """Remove a given node from its group.
2283
2284 """
2285 nodegroup = node.group
2286 if nodegroup not in self._config_data.nodegroups:
2287 logging.warning("Warning: node '%s' has unknown node group '%s'"
2288 " (while being removed from it)", node.uuid, nodegroup)
2289 nodegroup_obj = self._config_data.nodegroups[nodegroup]
2290 if node.uuid not in nodegroup_obj.members:
2291 logging.warning("Warning: node '%s' not a member of its node group '%s'"
2292 " (while being removed from it)", node.uuid, nodegroup)
2293 else:
2294 nodegroup_obj.members.remove(node.uuid)
2295
2296 @locking.ssynchronized(_config_lock)
2298 """Changes the group of a number of nodes.
2299
2300 @type mods: list of tuples; (node name, new group UUID)
2301 @param mods: Node membership modifications
2302
2303 """
2304 groups = self._config_data.nodegroups
2305 nodes = self._config_data.nodes
2306
2307 resmod = []
2308
2309
2310 for (node_uuid, new_group_uuid) in mods:
2311 try:
2312 node = nodes[node_uuid]
2313 except KeyError:
2314 raise errors.ConfigurationError("Unable to find node '%s'" % node_uuid)
2315
2316 if node.group == new_group_uuid:
2317
2318 logging.debug("Node '%s' was assigned to its current group (%s)",
2319 node_uuid, node.group)
2320 continue
2321
2322
2323 try:
2324 old_group = groups[node.group]
2325 except KeyError:
2326 raise errors.ConfigurationError("Unable to find old group '%s'" %
2327 node.group)
2328
2329
2330 try:
2331 new_group = groups[new_group_uuid]
2332 except KeyError:
2333 raise errors.ConfigurationError("Unable to find new group '%s'" %
2334 new_group_uuid)
2335
2336 assert node.uuid in old_group.members, \
2337 ("Inconsistent configuration: node '%s' not listed in members for its"
2338 " old group '%s'" % (node.uuid, old_group.uuid))
2339 assert node.uuid not in new_group.members, \
2340 ("Inconsistent configuration: node '%s' already listed in members for"
2341 " its new group '%s'" % (node.uuid, new_group.uuid))
2342
2343 resmod.append((node, old_group, new_group))
2344
2345
2346 for (node, old_group, new_group) in resmod:
2347 assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
2348 "Assigning to current group is not possible"
2349
2350 node.group = new_group.uuid
2351
2352
2353 if node.uuid in old_group.members:
2354 old_group.members.remove(node.uuid)
2355 if node.uuid not in new_group.members:
2356 new_group.members.append(node.uuid)
2357
2358
2359 now = time.time()
2360 for obj in frozenset(itertools.chain(*resmod)):
2361 obj.serial_no += 1
2362 obj.mtime = now
2363
2364
2365 self._config_data.cluster.serial_no += 1
2366
2367 self._WriteConfig()
2368
2370 """Bump up the serial number of the config.
2371
2372 """
2373 self._config_data.serial_no += 1
2374 self._config_data.mtime = time.time()
2375
2377 """Returns all objects with uuid attributes.
2378
2379 """
2380 return (self._config_data.instances.values() +
2381 self._config_data.nodes.values() +
2382 self._config_data.nodegroups.values() +
2383 self._config_data.networks.values() +
2384 self._AllDisks() +
2385 self._AllNICs() +
2386 [self._config_data.cluster])
2387
2389 """Read the config data from disk.
2390
2391 """
2392 raw_data = utils.ReadFile(self._cfg_file)
2393
2394 try:
2395 data_dict = serializer.Load(raw_data)
2396
2397 _ValidateConfig(data_dict)
2398 data = objects.ConfigData.FromDict(data_dict)
2399 except errors.ConfigVersionMismatch:
2400 raise
2401 except Exception, err:
2402 raise errors.ConfigurationError(err)
2403
2404 if (not hasattr(data, "cluster") or
2405 not hasattr(data.cluster, "rsahostkeypub")):
2406 raise errors.ConfigurationError("Incomplete configuration"
2407 " (missing cluster.rsahostkeypub)")
2408
2409 if not data.cluster.master_node in data.nodes:
2410 msg = ("The configuration denotes node %s as master, but does not"
2411 " contain information about this node" %
2412 data.cluster.master_node)
2413 raise errors.ConfigurationError(msg)
2414
2415 master_info = data.nodes[data.cluster.master_node]
2416 if master_info.name != self._my_hostname and not accept_foreign:
2417 msg = ("The configuration denotes node %s as master, while my"
2418 " hostname is %s; opening a foreign configuration is only"
2419 " possible in accept_foreign mode" %
2420 (master_info.name, self._my_hostname))
2421 raise errors.ConfigurationError(msg)
2422
2423 self._config_data = data
2424
2425
2426 self._last_cluster_serial = -1
2427
2428
2429 self._UpgradeConfig()
2430
2431 self._cfg_id = utils.GetFileID(path=self._cfg_file)
2432
2434 """Run any upgrade steps.
2435
2436 This method performs both in-object upgrades and also update some data
2437 elements that need uniqueness across the whole configuration or interact
2438 with other objects.
2439
2440 @warning: this function will call L{_WriteConfig()}, but also
2441 L{DropECReservations} so it needs to be called only from a
2442 "safe" place (the constructor). If one wanted to call it with
2443 the lock held, a DropECReservationUnlocked would need to be
2444 created first, to avoid causing deadlock.
2445
2446 """
2447
2448
2449 oldconf = copy.deepcopy(self._config_data.ToDict())
2450
2451
2452 self._config_data.UpgradeConfig()
2453
2454 for item in self._AllUUIDObjects():
2455 if item.uuid is None:
2456 item.uuid = self._GenerateUniqueID(_UPGRADE_CONFIG_JID)
2457 if not self._config_data.nodegroups:
2458 default_nodegroup_name = constants.INITIAL_NODE_GROUP_NAME
2459 default_nodegroup = objects.NodeGroup(name=default_nodegroup_name,
2460 members=[])
2461 self._UnlockedAddNodeGroup(default_nodegroup, _UPGRADE_CONFIG_JID, True)
2462 for node in self._config_data.nodes.values():
2463 if not node.group:
2464 node.group = self.LookupNodeGroup(None)
2465
2466
2467
2468
2469 self._UnlockedAddNodeToGroup(node.uuid, node.group)
2470
2471 modified = (oldconf != self._config_data.ToDict())
2472 if modified:
2473 self._WriteConfig()
2474
2475
2476 self.DropECReservations(_UPGRADE_CONFIG_JID)
2477 else:
2478 config_errors = self._UnlockedVerifyConfig()
2479 if config_errors:
2480 errmsg = ("Loaded configuration data is not consistent: %s" %
2481 (utils.CommaJoin(config_errors)))
2482 logging.critical(errmsg)
2483
2485 """Distribute the configuration to the other nodes.
2486
2487 Currently, this only copies the configuration file. In the future,
2488 it could be used to encapsulate the 2/3-phase update mechanism.
2489
2490 """
2491 if self._offline:
2492 return True
2493
2494 bad = False
2495
2496 node_list = []
2497 addr_list = []
2498 myhostname = self._my_hostname
2499
2500
2501
2502
2503 for node_uuid in self._UnlockedGetNodeList():
2504 node_info = self._UnlockedGetNodeInfo(node_uuid)
2505 if node_info.name == myhostname or not node_info.master_candidate:
2506 continue
2507 node_list.append(node_info.name)
2508 addr_list.append(node_info.primary_ip)
2509
2510
2511 result = \
2512 self._GetRpc(addr_list).call_upload_file(node_list, self._cfg_file)
2513 for to_node, to_result in result.items():
2514 msg = to_result.fail_msg
2515 if msg:
2516 msg = ("Copy of file %s to node %s failed: %s" %
2517 (self._cfg_file, to_node, msg))
2518 logging.error(msg)
2519
2520 if feedback_fn:
2521 feedback_fn(msg)
2522
2523 bad = True
2524
2525 return not bad
2526
2527 - def _WriteConfig(self, destination=None, feedback_fn=None):
2528 """Write the configuration data to persistent storage.
2529
2530 """
2531 assert feedback_fn is None or callable(feedback_fn)
2532
2533
2534
2535
2536
2537 config_errors = self._UnlockedVerifyConfig()
2538 if config_errors:
2539 errmsg = ("Configuration data is not consistent: %s" %
2540 (utils.CommaJoin(config_errors)))
2541 logging.critical(errmsg)
2542 if feedback_fn:
2543 feedback_fn(errmsg)
2544
2545 if destination is None:
2546 destination = self._cfg_file
2547 self._BumpSerialNo()
2548 txt = serializer.Dump(self._config_data.ToDict())
2549
2550 getents = self._getents()
2551 try:
2552 fd = utils.SafeWriteFile(destination, self._cfg_id, data=txt,
2553 close=False, gid=getents.confd_gid, mode=0640)
2554 except errors.LockError:
2555 raise errors.ConfigurationError("The configuration file has been"
2556 " modified since the last write, cannot"
2557 " update")
2558 try:
2559 self._cfg_id = utils.GetFileID(fd=fd)
2560 finally:
2561 os.close(fd)
2562
2563 self.write_count += 1
2564
2565
2566 self._DistributeConfig(feedback_fn)
2567
2568
2569 if self._last_cluster_serial < self._config_data.cluster.serial_no:
2570 if not self._offline:
2571 result = self._GetRpc(None).call_write_ssconf_files(
2572 self._UnlockedGetNodeNames(self._UnlockedGetOnlineNodeList()),
2573 self._UnlockedGetSsconfValues())
2574
2575 for nname, nresu in result.items():
2576 msg = nresu.fail_msg
2577 if msg:
2578 errmsg = ("Error while uploading ssconf files to"
2579 " node %s: %s" % (nname, msg))
2580 logging.warning(errmsg)
2581
2582 if feedback_fn:
2583 feedback_fn(errmsg)
2584
2585 self._last_cluster_serial = self._config_data.cluster.serial_no
2586
2588 """Get the hvparams of all given hypervisors from the config.
2589
2590 @type hypervisors: list of string
2591 @param hypervisors: list of hypervisor names
2592 @rtype: dict of strings
2593 @returns: dictionary mapping the hypervisor name to a string representation
2594 of the hypervisor's hvparams
2595
2596 """
2597 hvparams = {}
2598 for hv in hypervisors:
2599 hvparams[hv] = self._UnlockedGetHvparamsString(hv)
2600 return hvparams
2601
2602 @staticmethod
2604 """Extends the ssconf_values dictionary by hvparams.
2605
2606 @type ssconf_values: dict of strings
2607 @param ssconf_values: dictionary mapping ssconf_keys to strings
2608 representing the content of ssconf files
2609 @type all_hvparams: dict of strings
2610 @param all_hvparams: dictionary mapping hypervisor names to a string
2611 representation of their hvparams
2612 @rtype: same as ssconf_values
2613 @returns: the ssconf_values dictionary extended by hvparams
2614
2615 """
2616 for hv in all_hvparams:
2617 ssconf_key = constants.SS_HVPARAMS_PREF + hv
2618 ssconf_values[ssconf_key] = all_hvparams[hv]
2619 return ssconf_values
2620
2622 """Return the values needed by ssconf.
2623
2624 @rtype: dict
2625 @return: a dictionary with keys the ssconf names and values their
2626 associated value
2627
2628 """
2629 fn = "\n".join
2630 instance_names = utils.NiceSort(
2631 [inst.name for inst in
2632 self._UnlockedGetAllInstancesInfo().values()])
2633 node_infos = self._UnlockedGetAllNodesInfo().values()
2634 node_names = [node.name for node in node_infos]
2635 node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
2636 for ninfo in node_infos]
2637 node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
2638 for ninfo in node_infos]
2639 node_vm_capable = ["%s=%s" % (ninfo.name, str(ninfo.vm_capable))
2640 for ninfo in node_infos]
2641
2642 instance_data = fn(instance_names)
2643 off_data = fn(node.name for node in node_infos if node.offline)
2644 on_data = fn(node.name for node in node_infos if not node.offline)
2645 mc_data = fn(node.name for node in node_infos if node.master_candidate)
2646 mc_ips_data = fn(node.primary_ip for node in node_infos
2647 if node.master_candidate)
2648 node_data = fn(node_names)
2649 node_pri_ips_data = fn(node_pri_ips)
2650 node_snd_ips_data = fn(node_snd_ips)
2651 node_vm_capable_data = fn(node_vm_capable)
2652
2653 cluster = self._config_data.cluster
2654 cluster_tags = fn(cluster.GetTags())
2655
2656 master_candidates_certs = fn("%s=%s" % (mc_uuid, mc_cert)
2657 for mc_uuid, mc_cert
2658 in cluster.candidate_certs.items())
2659
2660 hypervisor_list = fn(cluster.enabled_hypervisors)
2661 all_hvparams = self._GetAllHvparamsStrings(constants.HYPER_TYPES)
2662
2663 uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
2664
2665 nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
2666 self._config_data.nodegroups.values()]
2667 nodegroups_data = fn(utils.NiceSort(nodegroups))
2668 networks = ["%s %s" % (net.uuid, net.name) for net in
2669 self._config_data.networks.values()]
2670 networks_data = fn(utils.NiceSort(networks))
2671
2672 ssconf_values = {
2673 constants.SS_CLUSTER_NAME: cluster.cluster_name,
2674 constants.SS_CLUSTER_TAGS: cluster_tags,
2675 constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
2676 constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir,
2677 constants.SS_GLUSTER_STORAGE_DIR: cluster.gluster_storage_dir,
2678 constants.SS_MASTER_CANDIDATES: mc_data,
2679 constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
2680 constants.SS_MASTER_CANDIDATES_CERTS: master_candidates_certs,
2681 constants.SS_MASTER_IP: cluster.master_ip,
2682 constants.SS_MASTER_NETDEV: cluster.master_netdev,
2683 constants.SS_MASTER_NETMASK: str(cluster.master_netmask),
2684 constants.SS_MASTER_NODE: self._UnlockedGetNodeName(cluster.master_node),
2685 constants.SS_NODE_LIST: node_data,
2686 constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
2687 constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
2688 constants.SS_NODE_VM_CAPABLE: node_vm_capable_data,
2689 constants.SS_OFFLINE_NODES: off_data,
2690 constants.SS_ONLINE_NODES: on_data,
2691 constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
2692 constants.SS_INSTANCE_LIST: instance_data,
2693 constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
2694 constants.SS_HYPERVISOR_LIST: hypervisor_list,
2695 constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
2696 constants.SS_UID_POOL: uid_pool,
2697 constants.SS_NODEGROUPS: nodegroups_data,
2698 constants.SS_NETWORKS: networks_data,
2699 constants.SS_ENABLED_USER_SHUTDOWN: str(cluster.enabled_user_shutdown),
2700 }
2701 ssconf_values = self._ExtendByAllHvparamsStrings(ssconf_values,
2702 all_hvparams)
2703 bad_values = [(k, v) for k, v in ssconf_values.items()
2704 if not isinstance(v, (str, basestring))]
2705 if bad_values:
2706 err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values)
2707 raise errors.ConfigurationError("Some ssconf key(s) have non-string"
2708 " values: %s" % err)
2709 return ssconf_values
2710
2711 @locking.ssynchronized(_config_lock, shared=1)
2717
2718 @locking.ssynchronized(_config_lock, shared=1)
2720 """Return the volume group name.
2721
2722 """
2723 return self._config_data.cluster.volume_group_name
2724
2725 @locking.ssynchronized(_config_lock)
2727 """Set the volume group name.
2728
2729 """
2730 self._config_data.cluster.volume_group_name = vg_name
2731 self._config_data.cluster.serial_no += 1
2732 self._WriteConfig()
2733
2734 @locking.ssynchronized(_config_lock, shared=1)
2736 """Return DRBD usermode helper.
2737
2738 """
2739 return self._config_data.cluster.drbd_usermode_helper
2740
2741 @locking.ssynchronized(_config_lock)
2743 """Set DRBD usermode helper.
2744
2745 """
2746 self._config_data.cluster.drbd_usermode_helper = drbd_helper
2747 self._config_data.cluster.serial_no += 1
2748 self._WriteConfig()
2749
2750 @locking.ssynchronized(_config_lock, shared=1)
2752 """Return the mac prefix.
2753
2754 """
2755 return self._config_data.cluster.mac_prefix
2756
2757 @locking.ssynchronized(_config_lock, shared=1)
2759 """Returns information about the cluster
2760
2761 @rtype: L{objects.Cluster}
2762 @return: the cluster object
2763
2764 """
2765 return self._config_data.cluster
2766
2767 @locking.ssynchronized(_config_lock, shared=1)
2769 """Check if in there is at disk of the given type in the configuration.
2770
2771 """
2772 return self._config_data.HasAnyDiskOfType(dev_type)
2773
2774 @locking.ssynchronized(_config_lock)
2775 - def Update(self, target, feedback_fn, ec_id=None):
2776 """Notify function to be called after updates.
2777
2778 This function must be called when an object (as returned by
2779 GetInstanceInfo, GetNodeInfo, GetCluster) has been updated and the
2780 caller wants the modifications saved to the backing store. Note
2781 that all modified objects will be saved, but the target argument
2782 is the one the caller wants to ensure that it's saved.
2783
2784 @param target: an instance of either L{objects.Cluster},
2785 L{objects.Node} or L{objects.Instance} which is existing in
2786 the cluster
2787 @param feedback_fn: Callable feedback function
2788
2789 """
2790 if self._config_data is None:
2791 raise errors.ProgrammerError("Configuration file not read,"
2792 " cannot save.")
2793 update_serial = False
2794 if isinstance(target, objects.Cluster):
2795 test = target == self._config_data.cluster
2796 elif isinstance(target, objects.Node):
2797 test = target in self._config_data.nodes.values()
2798 update_serial = True
2799 elif isinstance(target, objects.Instance):
2800 test = target in self._config_data.instances.values()
2801 elif isinstance(target, objects.NodeGroup):
2802 test = target in self._config_data.nodegroups.values()
2803 elif isinstance(target, objects.Network):
2804 test = target in self._config_data.networks.values()
2805 else:
2806 raise errors.ProgrammerError("Invalid object type (%s) passed to"
2807 " ConfigWriter.Update" % type(target))
2808 if not test:
2809 raise errors.ConfigurationError("Configuration updated since object"
2810 " has been read or unknown object")
2811 target.serial_no += 1
2812 target.mtime = now = time.time()
2813
2814 if update_serial:
2815
2816 self._config_data.cluster.serial_no += 1
2817 self._config_data.cluster.mtime = now
2818
2819 if isinstance(target, objects.Instance):
2820 self._UnlockedReleaseDRBDMinors(target.uuid)
2821
2822 if ec_id is not None:
2823
2824 self._UnlockedCommitTemporaryIps(ec_id)
2825
2826 self._WriteConfig(feedback_fn=feedback_fn)
2827
2828 @locking.ssynchronized(_config_lock)
2830 """Drop per-execution-context reservations
2831
2832 """
2833 for rm in self._all_rms:
2834 rm.DropECReservations(ec_id)
2835
2836 @locking.ssynchronized(_config_lock, shared=1)
2838 """Get configuration info of all the networks.
2839
2840 """
2841 return dict(self._config_data.networks)
2842
2844 """Get the list of networks.
2845
2846 This function is for internal use, when the config lock is already held.
2847
2848 """
2849 return self._config_data.networks.keys()
2850
2851 @locking.ssynchronized(_config_lock, shared=1)
2853 """Get the list of networks.
2854
2855 @return: array of networks, ex. ["main", "vlan100", "200]
2856
2857 """
2858 return self._UnlockedGetNetworkList()
2859
2860 @locking.ssynchronized(_config_lock, shared=1)
2862 """Get a list of network names
2863
2864 """
2865 names = [net.name
2866 for net in self._config_data.networks.values()]
2867 return names
2868
2870 """Returns information about a network.
2871
2872 This function is for internal use, when the config lock is already held.
2873
2874 """
2875 if uuid not in self._config_data.networks:
2876 return None
2877
2878 return self._config_data.networks[uuid]
2879
2880 @locking.ssynchronized(_config_lock, shared=1)
2882 """Returns information about a network.
2883
2884 It takes the information from the configuration file.
2885
2886 @param uuid: UUID of the network
2887
2888 @rtype: L{objects.Network}
2889 @return: the network object
2890
2891 """
2892 return self._UnlockedGetNetwork(uuid)
2893
2894 @locking.ssynchronized(_config_lock)
2895 - def AddNetwork(self, net, ec_id, check_uuid=True):
2896 """Add a network to the configuration.
2897
2898 @type net: L{objects.Network}
2899 @param net: the Network object to add
2900 @type ec_id: string
2901 @param ec_id: unique id for the job to use when creating a missing UUID
2902
2903 """
2904 self._UnlockedAddNetwork(net, ec_id, check_uuid)
2905 self._WriteConfig()
2906
2908 """Add a network to the configuration.
2909
2910 """
2911 logging.info("Adding network %s to configuration", net.name)
2912
2913 if check_uuid:
2914 self._EnsureUUID(net, ec_id)
2915
2916 net.serial_no = 1
2917 net.ctime = net.mtime = time.time()
2918 self._config_data.networks[net.uuid] = net
2919 self._config_data.cluster.serial_no += 1
2920
2922 """Lookup a network's UUID.
2923
2924 @type target: string
2925 @param target: network name or UUID
2926 @rtype: string
2927 @return: network UUID
2928 @raises errors.OpPrereqError: when the target network cannot be found
2929
2930 """
2931 if target is None:
2932 return None
2933 if target in self._config_data.networks:
2934 return target
2935 for net in self._config_data.networks.values():
2936 if net.name == target:
2937 return net.uuid
2938 raise errors.OpPrereqError("Network '%s' not found" % target,
2939 errors.ECODE_NOENT)
2940
2941 @locking.ssynchronized(_config_lock, shared=1)
2943 """Lookup a network's UUID.
2944
2945 This function is just a wrapper over L{_UnlockedLookupNetwork}.
2946
2947 @type target: string
2948 @param target: network name or UUID
2949 @rtype: string
2950 @return: network UUID
2951
2952 """
2953 return self._UnlockedLookupNetwork(target)
2954
2955 @locking.ssynchronized(_config_lock)
2957 """Remove a network from the configuration.
2958
2959 @type network_uuid: string
2960 @param network_uuid: the UUID of the network to remove
2961
2962 """
2963 logging.info("Removing network %s from configuration", network_uuid)
2964
2965 if network_uuid not in self._config_data.networks:
2966 raise errors.ConfigurationError("Unknown network '%s'" % network_uuid)
2967
2968 del self._config_data.networks[network_uuid]
2969 self._config_data.cluster.serial_no += 1
2970 self._WriteConfig()
2971
2973 """Get the netparams (mode, link) of a network.
2974
2975 Get a network's netparams for a given node.
2976
2977 @type net_uuid: string
2978 @param net_uuid: network uuid
2979 @type node_uuid: string
2980 @param node_uuid: node UUID
2981 @rtype: dict or None
2982 @return: netparams
2983
2984 """
2985 node_info = self._UnlockedGetNodeInfo(node_uuid)
2986 nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
2987 netparams = nodegroup_info.networks.get(net_uuid, None)
2988
2989 return netparams
2990
2991 @locking.ssynchronized(_config_lock, shared=1)
2993 """Locking wrapper of _UnlockedGetGroupNetParams()
2994
2995 """
2996 return self._UnlockedGetGroupNetParams(net_uuid, node_uuid)
2997
2998 @locking.ssynchronized(_config_lock, shared=1)
3000 """Check IP uniqueness in nodegroup.
3001
3002 Check networks that are connected in the node's node group
3003 if ip is contained in any of them. Used when creating/adding
3004 a NIC to ensure uniqueness among nodegroups.
3005
3006 @type ip: string
3007 @param ip: ip address
3008 @type node_uuid: string
3009 @param node_uuid: node UUID
3010 @rtype: (string, dict) or (None, None)
3011 @return: (network name, netparams)
3012
3013 """
3014 if ip is None:
3015 return (None, None)
3016 node_info = self._UnlockedGetNodeInfo(node_uuid)
3017 nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
3018 for net_uuid in nodegroup_info.networks.keys():
3019 net_info = self._UnlockedGetNetwork(net_uuid)
3020 pool = network.AddressPool(net_info)
3021 if pool.Contains(ip):
3022 return (net_info.name, nodegroup_info.networks[net_uuid])
3023
3024 return (None, None)
3025