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 threading
52 import itertools
53
54 from ganeti import errors
55 from ganeti import utils
56 from ganeti import constants
57 import ganeti.wconfd as wc
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 -def GetWConfdContext(ec_id, livelock):
68 """Prepare a context for communication with WConfd.
69
70 WConfd needs to know the identity of each caller to properly manage locks and
71 detect job death. This helper function prepares the identity object given a
72 job ID (optional) and a livelock file.
73
74 @type ec_id: int, or None
75 @param ec_id: the job ID or None, if the caller isn't a job
76 @type livelock: L{ganeti.utils.livelock.LiveLock}
77 @param livelock: a livelock object holding the lockfile needed for WConfd
78 @return: the WConfd context
79
80 """
81 if ec_id is None:
82 return (threading.current_thread().getName(),
83 livelock.GetPath(), os.getpid())
84 else:
85 return (ec_id,
86 livelock.GetPath(), os.getpid())
87
90 """A utility function for constructing instances of ConfigWriter.
91
92 It prepares a WConfd context and uses it to create a ConfigWriter instance.
93
94 @type ec_id: int, or None
95 @param ec_id: the job ID or None, if the caller isn't a job
96 @type livelock: L{ganeti.utils.livelock.LiveLock}
97 @param livelock: a livelock object holding the lockfile needed for WConfd
98 @type kwargs: dict
99 @param kwargs: Any additional arguments for the ConfigWriter constructor
100 @rtype: L{ConfigWriter}
101 @return: the ConfigWriter context
102
103 """
104 kwargs['wconfdcontext'] = GetWConfdContext(ec_id, livelock)
105
106
107
108 accept_foreign = kwargs.get('accept_foreign', False)
109 kwargs['wconfd'] = wc.Client(allow_non_master=accept_foreign)
110
111 return ConfigWriter(**kwargs)
112
115 """Configuration synchronization decorator.
116
117 """
118 def wrap(fn):
119 def sync_function(*args, **kwargs):
120 with args[0].GetConfigManager(shared):
121 return fn(*args, **kwargs)
122 return sync_function
123 return wrap
124
125
126 _UPGRADE_CONFIG_JID = "jid-cfg-upgrade"
141
144 """A temporary resource reservation manager.
145
146 This is used to reserve resources in a job, before using them, making sure
147 other jobs cannot get them in the meantime.
148
149 """
151 self._ec_reserved = {}
152
154 for holder_reserved in self._ec_reserved.values():
155 if resource in holder_reserved:
156 return True
157 return False
158
159 - def Reserve(self, ec_id, resource):
160 if self.Reserved(resource):
161 raise errors.ReservationError("Duplicate reservation for resource '%s'"
162 % str(resource))
163 if ec_id not in self._ec_reserved:
164 self._ec_reserved[ec_id] = set([resource])
165 else:
166 self._ec_reserved[ec_id].add(resource)
167
169 if ec_id in self._ec_reserved:
170 del self._ec_reserved[ec_id]
171
173 all_reserved = set()
174 for holder_reserved in self._ec_reserved.values():
175 all_reserved.update(holder_reserved)
176 return all_reserved
177
179 """ Used when you want to retrieve all reservations for a specific
180 execution context. E.g when commiting reserved IPs for a specific
181 network.
182
183 """
184 ec_reserved = set()
185 if ec_id in self._ec_reserved:
186 ec_reserved.update(self._ec_reserved[ec_id])
187 return ec_reserved
188
189 - def Generate(self, existing, generate_one_fn, ec_id):
190 """Generate a new resource of this type
191
192 """
193 assert callable(generate_one_fn)
194
195 all_elems = self.GetReserved()
196 all_elems.update(existing)
197 retries = 64
198 while retries > 0:
199 new_resource = generate_one_fn()
200 if new_resource is not None and new_resource not in all_elems:
201 break
202 else:
203 raise errors.ConfigurationError("Not able generate new resource"
204 " (last tried: %s)" % new_resource)
205 self.Reserve(ec_id, new_resource)
206 return new_resource
207
210 """Wrapper around L{utils.text.MatchNameComponent}.
211
212 """
213 return utils.MatchNameComponent(short_name, names, case_sensitive=False)
214
217 """Checks if instance's disks' C{iv_name} attributes are in order.
218
219 @type disks: list of L{objects.Disk}
220 @param disks: List of disks
221 @rtype: list of tuples; (int, string, string)
222 @return: List of wrongly named disks, each tuple contains disk index,
223 expected and actual name
224
225 """
226 result = []
227
228 for (idx, disk) in enumerate(disks):
229 exp_iv_name = "disk/%s" % idx
230 if disk.iv_name != exp_iv_name:
231 result.append((idx, exp_iv_name, disk.iv_name))
232
233 return result
234
237 """Locks the configuration and exposes it to be read or modified.
238
239 """
240 - def __init__(self, config_writer, shared=False):
241 assert isinstance(config_writer, ConfigWriter), \
242 "invalid argument: Not a ConfigWriter"
243 self._config_writer = config_writer
244 self._shared = shared
245
247 try:
248 self._config_writer._OpenConfig(self._shared)
249 except Exception:
250 logging.debug("Opening configuration failed")
251 try:
252 self._config_writer._CloseConfig(False)
253 except Exception:
254 logging.debug("Closing configuration failed as well")
255 raise
256
257 - def __exit__(self, exc_type, exc_value, traceback):
258
259 if exc_type is not None:
260 logging.debug("Configuration operation failed,"
261 " the changes will not be saved")
262
263 self._config_writer._CloseConfig(not self._shared and exc_type is None)
264 return False
265
268 """Update the C{iv_name} attribute of disks.
269
270 @type disks: list of L{objects.Disk}
271
272 """
273 for (idx, disk) in enumerate(disks):
274 disk.iv_name = "disk/%s" % (base_idx + idx)
275
278 """The interface to the cluster configuration.
279
280 WARNING: The class is no longer thread-safe!
281 Each thread must construct a separate instance.
282
283 @ivar _all_rms: a list of all temporary reservation managers
284
285 """
286 - def __init__(self, cfg_file=None, offline=False, _getents=runtime.GetEnts,
287 accept_foreign=False, wconfdcontext=None, wconfd=None):
288 self.write_count = 0
289 self._config_data = None
290 self._SetConfigData(None)
291 self._offline = offline
292 if cfg_file is None:
293 self._cfg_file = pathutils.CLUSTER_CONF_FILE
294 else:
295 self._cfg_file = cfg_file
296 self._getents = _getents
297 self._temporary_ids = TemporaryReservationManager()
298 self._all_rms = [self._temporary_ids]
299
300
301
302
303 self._my_hostname = netutils.Hostname.GetSysName()
304 self._cfg_id = None
305 self._wconfdcontext = wconfdcontext
306 self._wconfd = wconfd
307 self._accept_foreign = accept_foreign
308 self._lock_count = 0
309 self._lock_current_shared = None
310
312 return self._config_data
313
315 self._config_data = None
316
318 self._config_data = cfg
319
321 return self._wconfdcontext
322
323
324 @staticmethod
330
334
335 @_ConfigSync(shared=1)
337 """Get the node params populated with cluster defaults.
338
339 @type node: L{objects.Node}
340 @param node: The node we want to know the params for
341 @return: A dict with the filled in node params
342
343 """
344 return self._UnlockedGetNdParams(node)
345
346 @_ConfigSync(shared=1)
348 """Get the node groups params populated with cluster defaults.
349
350 @type nodegroup: L{objects.NodeGroup}
351 @param nodegroup: The node group we want to know the params for
352 @return: A dict with the filled in node group params
353
354 """
355 return self._ConfigData().cluster.FillNDGroup(nodegroup)
356
357 @_ConfigSync(shared=1)
369
371 """Return the disks' info for the given instance
372
373 @type inst_uuid: string
374 @param inst_uuid: The UUID of the instance we want to know the disks for
375
376 @rtype: List of L{objects.Disk}
377 @return: A list with all the disks' info
378
379 """
380 instance = self._UnlockedGetInstanceInfo(inst_uuid)
381 if instance is None:
382 raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
383
384 return [self._UnlockedGetDiskInfo(disk_uuid)
385 for disk_uuid in instance.disks]
386
387 @_ConfigSync(shared=1)
389 """Return the disks' info for the given instance
390
391 This is a simple wrapper over L{_UnlockedGetInstanceDisks}.
392
393 """
394 return self._UnlockedGetInstanceDisks(inst_uuid)
395
397 """Add a disk to the config.
398
399 @type disk: L{objects.Disk}
400 @param disk: The disk object
401
402 """
403 if not isinstance(disk, objects.Disk):
404 raise errors.ProgrammerError("Invalid type passed to _UnlockedAddDisk")
405
406 logging.info("Adding disk %s to configuration", disk.uuid)
407
408 self._CheckUniqueUUID(disk, include_temporary=False)
409 disk.serial_no = 1
410 disk.ctime = disk.mtime = time.time()
411 disk.UpgradeConfig()
412 self._ConfigData().disks[disk.uuid] = disk
413 self._ConfigData().cluster.serial_no += 1
414
416 """Attach a disk to an instance.
417
418 @type inst_uuid: string
419 @param inst_uuid: The UUID of the instance object
420 @type disk_uuid: string
421 @param disk_uuid: The UUID of the disk object
422 @type idx: int
423 @param idx: the index of the newly attached disk; if not
424 passed, the disk will be attached as the last one.
425
426 """
427 instance = self._UnlockedGetInstanceInfo(inst_uuid)
428 if instance is None:
429 raise errors.ConfigurationError("Instance %s doesn't exist"
430 % inst_uuid)
431 if disk_uuid not in self._ConfigData().disks:
432 raise errors.ConfigurationError("Disk %s doesn't exist" % disk_uuid)
433
434 if idx is None:
435 idx = len(instance.disks)
436 else:
437 if idx < 0:
438 raise IndexError("Not accepting negative indices other than -1")
439 elif idx > len(instance.disks):
440 raise IndexError("Got disk index %s, but there are only %s" %
441 (idx, len(instance.disks)))
442
443
444 for inst in self._ConfigData().instances.values():
445 if disk_uuid in inst.disks:
446 raise errors.ReservationError("Disk %s already attached to instance %s"
447 % (disk_uuid, inst.name))
448
449 instance.disks.insert(idx, disk_uuid)
450 instance_disks = self._UnlockedGetInstanceDisks(inst_uuid)
451 _UpdateIvNames(idx, instance_disks[idx:])
452 instance.serial_no += 1
453 instance.mtime = time.time()
454
455 @_ConfigSync()
457 """Add a disk to the config and attach it to instance.
458
459 This is a simple wrapper over L{_UnlockedAddDisk} and
460 L{_UnlockedAttachInstanceDisk}.
461
462 """
463 self._UnlockedAddDisk(disk)
464 self._UnlockedAttachInstanceDisk(inst_uuid, disk.uuid, idx)
465
493
495 """Remove the disk from the configuration.
496
497 @type disk_uuid: string
498 @param disk_uuid: The UUID of the disk object
499
500 """
501 if disk_uuid not in self._ConfigData().disks:
502 raise errors.ConfigurationError("Disk %s doesn't exist" % disk_uuid)
503
504
505 for inst in self._ConfigData().instances.values():
506 if disk_uuid in inst.disks:
507 raise errors.ReservationError("Cannot remove disk %s. Disk is"
508 " attached to instance %s"
509 % (disk_uuid, inst.name))
510
511
512 del self._ConfigData().disks[disk_uuid]
513 self._ConfigData().cluster.serial_no += 1
514
515 @_ConfigSync()
517 """Detach a disk from an instance and remove it from the config.
518
519 This is a simple wrapper over L{_UnlockedDetachInstanceDisk} and
520 L{_UnlockedRemoveDisk}.
521
522 """
523 self._UnlockedDetachInstanceDisk(inst_uuid, disk_uuid)
524 self._UnlockedRemoveDisk(disk_uuid)
525
527 """Returns information about a disk.
528
529 It takes the information from the configuration file.
530
531 @param disk_uuid: UUID of the disk
532
533 @rtype: L{objects.Disk}
534 @return: the disk object
535
536 """
537 if disk_uuid not in self._ConfigData().disks:
538 return None
539
540 return self._ConfigData().disks[disk_uuid]
541
542 @_ConfigSync(shared=1)
544 """Returns information about a disk.
545
546 This is a simple wrapper over L{_UnlockedGetDiskInfo}.
547
548 """
549 return self._UnlockedGetDiskInfo(disk_uuid)
550
552 """Compute the set of all disk-related nodes for an instance.
553
554 This abstracts away some work from '_UnlockedGetInstanceNodes'
555 and '_UnlockedGetInstanceSecondaryNodes'.
556
557 @type inst_uuid: string
558 @param inst_uuid: The UUID of the instance we want to get nodes for
559 @rtype: set of strings
560 @return: A set of names for all the nodes of the instance
561
562 """
563 instance = self._UnlockedGetInstanceInfo(inst_uuid)
564 if instance is None:
565 raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
566
567 instance_disks = self._UnlockedGetInstanceDisks(inst_uuid)
568 all_nodes = []
569 for disk in instance_disks:
570 all_nodes.extend(disk.all_nodes)
571 return (set(all_nodes), instance)
572
574 """Get all disk-related nodes for an instance.
575
576 For non-DRBD, this will be empty, for DRBD it will contain both
577 the primary and the secondaries.
578
579 @type inst_uuid: string
580 @param inst_uuid: The UUID of the instance we want to get nodes for
581 @rtype: list of strings
582 @return: A list of names for all the nodes of the instance
583
584 """
585 (all_nodes, instance) = self._AllInstanceNodes(inst_uuid)
586
587 all_nodes.discard(instance.primary_node)
588 return (instance.primary_node, ) + tuple(all_nodes)
589
590 @_ConfigSync(shared=1)
592 """Get all disk-related nodes for an instance.
593
594 This is just a wrapper over L{_UnlockedGetInstanceNodes}
595
596 """
597 return self._UnlockedGetInstanceNodes(inst_uuid)
598
600 """Get the list of secondary nodes.
601
602 @type inst_uuid: string
603 @param inst_uuid: The UUID of the instance we want to get nodes for
604 @rtype: list of strings
605 @return: A list of names for all the secondary nodes of the instance
606
607 """
608 (all_nodes, instance) = self._AllInstanceNodes(inst_uuid)
609 all_nodes.discard(instance.primary_node)
610 return tuple(all_nodes)
611
612 @_ConfigSync(shared=1)
614 """Get the list of secondary nodes.
615
616 This is a simple wrapper over L{_UnlockedGetInstanceSecondaryNodes}.
617
618 """
619 return self._UnlockedGetInstanceSecondaryNodes(inst_uuid)
620
622 """Provide a mapping of node to LVs a given instance owns.
623
624 @type inst_uuid: string
625 @param inst_uuid: The UUID of the instance we want to
626 compute the LVsByNode for
627 @type lvmap: dict
628 @param lvmap: Optional dictionary to receive the
629 'node' : ['lv', ...] data.
630 @rtype: dict or None
631 @return: None if lvmap arg is given, otherwise, a dictionary of
632 the form { 'node_uuid' : ['volume1', 'volume2', ...], ... };
633 volumeN is of the form "vg_name/lv_name", compatible with
634 GetVolumeList()
635
636 """
637 def _MapLVsByNode(lvmap, devices, node_uuid):
638 """Recursive helper function."""
639 if not node_uuid in lvmap:
640 lvmap[node_uuid] = []
641
642 for dev in devices:
643 if dev.dev_type == constants.DT_PLAIN:
644 lvmap[node_uuid].append(dev.logical_id[0] + "/" + dev.logical_id[1])
645
646 elif dev.dev_type in constants.DTS_DRBD:
647 if dev.children:
648 _MapLVsByNode(lvmap, dev.children, dev.logical_id[0])
649 _MapLVsByNode(lvmap, dev.children, dev.logical_id[1])
650
651 elif dev.children:
652 _MapLVsByNode(lvmap, dev.children, node_uuid)
653
654 instance = self._UnlockedGetInstanceInfo(inst_uuid)
655 if instance is None:
656 raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
657
658 if lvmap is None:
659 lvmap = {}
660 ret = lvmap
661 else:
662 ret = None
663
664 _MapLVsByNode(lvmap,
665 self._UnlockedGetInstanceDisks(instance.uuid),
666 instance.primary_node)
667 return ret
668
669 @_ConfigSync(shared=1)
671 """Provide a mapping of node to LVs a given instance owns.
672
673 This is a simple wrapper over L{_UnlockedGetInstanceLVsByNode}
674
675 """
676 return self._UnlockedGetInstanceLVsByNode(inst_uuid, lvmap=lvmap)
677
678 @_ConfigSync(shared=1)
680 """Get the disk params populated with inherit chain.
681
682 @type group: L{objects.NodeGroup}
683 @param group: The group we want to know the params for
684 @return: A dict with the filled in disk params
685
686 """
687 return self._UnlockedGetGroupDiskParams(group)
688
690 """Get the disk params populated with inherit chain down to node-group.
691
692 @type group: L{objects.NodeGroup}
693 @param group: The group we want to know the params for
694 @return: A dict with the filled in disk params
695
696 """
697 data = self._ConfigData().cluster.SimpleFillDP(group.diskparams)
698 assert isinstance(data, dict), "Not a dictionary: " + str(data)
699 return data
700
702 """Generate a MAC for an instance.
703
704 This should check the current instances for duplicates.
705
706 """
707 return self._wconfd.GenerateMAC(self._GetWConfdContext(), net_uuid)
708
710 """Reserve a MAC for an instance.
711
712 This only checks instances managed by this cluster, it does not
713 check for potential collisions elsewhere.
714
715 """
716 self._wconfd.ReserveMAC(self._GetWConfdContext(), mac)
717
728
743
744 - def ReleaseIp(self, net_uuid, address, _ec_id):
745 """Give a specific IP address back to an IP pool.
746
747 The IP address is returned to the IP pool and marked as reserved.
748
749 """
750 if net_uuid:
751 if self._offline:
752 raise errors.ProgrammerError("Can't call ReleaseIp in offline mode")
753 self._wconfd.ReleaseIp(self._GetWConfdContext(), net_uuid, address)
754
762
763 - def ReserveIp(self, net_uuid, address, _ec_id, check=True):
764 """Reserve a given IPv4 address for use by an instance.
765
766 """
767 if self._offline:
768 raise errors.ProgrammerError("Can't call ReserveIp in offline mode")
769 return self._wconfd.ReserveIp(self._GetWConfdContext(), net_uuid, address,
770 check)
771
773 """Reserve an VG/LV pair for an instance.
774
775 @type lv_name: string
776 @param lv_name: the logical volume name to reserve
777
778 """
779 return self._wconfd.ReserveLV(self._GetWConfdContext(), lv_name)
780
788
789
800
802 """Compute the list of all NICs.
803
804 """
805 nics = []
806 for instance in self._ConfigData().instances.values():
807 nics.extend(instance.nics)
808 return nics
809
810 - def _AllIDs(self, include_temporary):
811 """Compute the list of all UUIDs and names we have.
812
813 @type include_temporary: boolean
814 @param include_temporary: whether to include the _temporary_ids set
815 @rtype: set
816 @return: a set of IDs
817
818 """
819 existing = set()
820 if include_temporary:
821 existing.update(self._temporary_ids.GetReserved())
822 existing.update(self._AllLVs())
823 existing.update(self._ConfigData().instances.keys())
824 existing.update(self._ConfigData().nodes.keys())
825 existing.update([i.uuid for i in self._AllUUIDObjects() if i.uuid])
826 return existing
827
829 """Generate an unique UUID.
830
831 This checks the current node, instances and disk names for
832 duplicates.
833
834 @rtype: string
835 @return: the unique id
836
837 """
838 existing = self._AllIDs(include_temporary=False)
839 return self._temporary_ids.Generate(existing, utils.NewUUID, ec_id)
840
841 @_ConfigSync(shared=1)
843 """Generate an unique ID.
844
845 This is just a wrapper over the unlocked version.
846
847 @type ec_id: string
848 @param ec_id: unique id for the job to reserve the id to
849
850 """
851 return self._GenerateUniqueID(ec_id)
852
854 """Return all MACs present in the config.
855
856 @rtype: list
857 @return: the list of all MACs
858
859 """
860 result = []
861 for instance in self._ConfigData().instances.values():
862 for nic in instance.nics:
863 result.append(nic.mac)
864
865 return result
866
868 """Return all DRBD secrets present in the config.
869
870 @rtype: list
871 @return: the list of all DRBD secrets
872
873 """
874 def helper(disk, result):
875 """Recursively gather secrets from this disk."""
876 if disk.dev_type == constants.DT_DRBD8:
877 result.append(disk.logical_id[5])
878 if disk.children:
879 for child in disk.children:
880 helper(child, result)
881
882 result = []
883 for disk in self._ConfigData().disks.values():
884 helper(disk, result)
885
886 return result
887
888 @staticmethod
890 """Per-disk verification checks
891
892 Extends L{result} with diagnostic information about the disks.
893
894 @type data: see L{_ConfigData}
895 @param data: configuration data
896
897 @type result: list of strings
898 @param result: list containing diagnostic messages
899
900 """
901 instance_disk_uuids = [d for insts in data.instances.values()
902 for d in insts.disks]
903 for disk_uuid in data.disks:
904 disk = data.disks[disk_uuid]
905 result.extend(["disk %s error: %s" % (disk.uuid, msg)
906 for msg in disk.Verify()])
907 if disk.uuid != disk_uuid:
908 result.append("disk '%s' is indexed by wrong UUID '%s'" %
909 (disk.name, disk_uuid))
910 if disk.uuid not in instance_disk_uuids:
911 result.append("disk '%s' is not attached to any instance" %
912 disk.uuid)
913
915 """Verify function.
916
917 @rtype: list
918 @return: a list of error messages; a non-empty list signifies
919 configuration errors
920
921 """
922
923 result = []
924 seen_macs = []
925 ports = {}
926 data = self._ConfigData()
927 cluster = data.cluster
928
929
930 if not self._offline:
931 try:
932 self._wconfd.VerifyConfig()
933 except errors.ConfigVerifyError, err:
934 try:
935 for msg in err.args[1]:
936 result.append(msg)
937 except IndexError:
938 pass
939
940 def _helper(owner, attr, value, template):
941 try:
942 utils.ForceDictType(value, template)
943 except errors.GenericError, err:
944 result.append("%s has invalid %s: %s" % (owner, attr, err))
945
946 def _helper_nic(owner, params):
947 try:
948 objects.NIC.CheckParameterSyntax(params)
949 except errors.ConfigurationError, err:
950 result.append("%s has invalid nicparams: %s" % (owner, err))
951
952 def _helper_ipolicy(owner, ipolicy, iscluster):
953 try:
954 objects.InstancePolicy.CheckParameterSyntax(ipolicy, iscluster)
955 except errors.ConfigurationError, err:
956 result.append("%s has invalid instance policy: %s" % (owner, err))
957 for key, value in ipolicy.items():
958 if key == constants.ISPECS_MINMAX:
959 for k in range(len(value)):
960 _helper_ispecs(owner, "ipolicy/%s[%s]" % (key, k), value[k])
961 elif key == constants.ISPECS_STD:
962 _helper(owner, "ipolicy/" + key, value,
963 constants.ISPECS_PARAMETER_TYPES)
964 else:
965
966 if key in constants.IPOLICY_PARAMETERS:
967 exp_type = float
968
969 convertible_types = [int]
970 else:
971 exp_type = list
972 convertible_types = []
973
974 if any(isinstance(value, ct) for ct in convertible_types):
975 try:
976 value = exp_type(value)
977 ipolicy[key] = value
978 except ValueError:
979 pass
980 if not isinstance(value, exp_type):
981 result.append("%s has invalid instance policy: for %s,"
982 " expecting %s, got %s" %
983 (owner, key, exp_type.__name__, type(value)))
984
985 def _helper_ispecs(owner, parentkey, params):
986 for (key, value) in params.items():
987 fullkey = "/".join([parentkey, key])
988 _helper(owner, fullkey, value, constants.ISPECS_PARAMETER_TYPES)
989
990
991 _helper("cluster", "beparams", cluster.SimpleFillBE({}),
992 constants.BES_PARAMETER_TYPES)
993 _helper("cluster", "nicparams", cluster.SimpleFillNIC({}),
994 constants.NICS_PARAMETER_TYPES)
995 _helper_nic("cluster", cluster.SimpleFillNIC({}))
996 _helper("cluster", "ndparams", cluster.SimpleFillND({}),
997 constants.NDS_PARAMETER_TYPES)
998 _helper_ipolicy("cluster", cluster.ipolicy, True)
999
1000 for disk_template in cluster.diskparams:
1001 if disk_template not in constants.DTS_HAVE_ACCESS:
1002 continue
1003
1004 access = cluster.diskparams[disk_template].get(constants.LDP_ACCESS,
1005 constants.DISK_KERNELSPACE)
1006 if access not in constants.DISK_VALID_ACCESS_MODES:
1007 result.append(
1008 "Invalid value of '%s:%s': '%s' (expected one of %s)" % (
1009 disk_template, constants.LDP_ACCESS, access,
1010 utils.CommaJoin(constants.DISK_VALID_ACCESS_MODES)
1011 )
1012 )
1013
1014 self._VerifyDisks(data, result)
1015
1016
1017 for instance_uuid in data.instances:
1018 instance = data.instances[instance_uuid]
1019 if instance.uuid != instance_uuid:
1020 result.append("instance '%s' is indexed by wrong UUID '%s'" %
1021 (instance.name, instance_uuid))
1022 if instance.primary_node not in data.nodes:
1023 result.append("instance '%s' has invalid primary node '%s'" %
1024 (instance.name, instance.primary_node))
1025 for snode in self._UnlockedGetInstanceSecondaryNodes(instance.uuid):
1026 if snode not in data.nodes:
1027 result.append("instance '%s' has invalid secondary node '%s'" %
1028 (instance.name, snode))
1029 for idx, nic in enumerate(instance.nics):
1030 if nic.mac in seen_macs:
1031 result.append("instance '%s' has NIC %d mac %s duplicate" %
1032 (instance.name, idx, nic.mac))
1033 else:
1034 seen_macs.append(nic.mac)
1035 if nic.nicparams:
1036 filled = cluster.SimpleFillNIC(nic.nicparams)
1037 owner = "instance %s nic %d" % (instance.name, idx)
1038 _helper(owner, "nicparams",
1039 filled, constants.NICS_PARAMETER_TYPES)
1040 _helper_nic(owner, filled)
1041
1042
1043 if not instance.disk_template in data.cluster.enabled_disk_templates:
1044 result.append("instance '%s' uses the disabled disk template '%s'." %
1045 (instance.name, instance.disk_template))
1046
1047
1048 if instance.beparams:
1049 _helper("instance %s" % instance.name, "beparams",
1050 cluster.FillBE(instance), constants.BES_PARAMETER_TYPES)
1051
1052
1053 for disk_uuid in instance.disks:
1054 if disk_uuid not in data.disks:
1055 result.append("Instance '%s' has invalid disk '%s'" %
1056 (instance.name, disk_uuid))
1057
1058 instance_disks = self._UnlockedGetInstanceDisks(instance.uuid)
1059
1060 for (idx, dsk) in enumerate(instance_disks):
1061 if dsk.dev_type in constants.DTS_DRBD:
1062 tcp_port = dsk.logical_id[2]
1063 if tcp_port not in ports:
1064 ports[tcp_port] = []
1065 ports[tcp_port].append((instance.name, "drbd disk %s" % idx))
1066
1067 net_port = getattr(instance, "network_port", None)
1068 if net_port is not None:
1069 if net_port not in ports:
1070 ports[net_port] = []
1071 ports[net_port].append((instance.name, "network port"))
1072
1073 wrong_names = _CheckInstanceDiskIvNames(instance_disks)
1074 if wrong_names:
1075 tmp = "; ".join(("name of disk %s should be '%s', but is '%s'" %
1076 (idx, exp_name, actual_name))
1077 for (idx, exp_name, actual_name) in wrong_names)
1078
1079 result.append("Instance '%s' has wrongly named disks: %s" %
1080 (instance.name, tmp))
1081
1082
1083 for free_port in cluster.tcpudp_port_pool:
1084 if free_port not in ports:
1085 ports[free_port] = []
1086 ports[free_port].append(("cluster", "port marked as free"))
1087
1088
1089 keys = ports.keys()
1090 keys.sort()
1091 for pnum in keys:
1092 pdata = ports[pnum]
1093 if len(pdata) > 1:
1094 txt = utils.CommaJoin(["%s/%s" % val for val in pdata])
1095 result.append("tcp/udp port %s has duplicates: %s" % (pnum, txt))
1096
1097
1098 if keys:
1099 if keys[-1] > cluster.highest_used_port:
1100 result.append("Highest used port mismatch, saved %s, computed %s" %
1101 (cluster.highest_used_port, keys[-1]))
1102
1103 if not data.nodes[cluster.master_node].master_candidate:
1104 result.append("Master node is not a master candidate")
1105
1106
1107 mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats()
1108 if mc_now < mc_max:
1109 result.append("Not enough master candidates: actual %d, target %d" %
1110 (mc_now, mc_max))
1111
1112
1113 for node_uuid, node in data.nodes.items():
1114 if node.uuid != node_uuid:
1115 result.append("Node '%s' is indexed by wrong UUID '%s'" %
1116 (node.name, node_uuid))
1117 if [node.master_candidate, node.drained, node.offline].count(True) > 1:
1118 result.append("Node %s state is invalid: master_candidate=%s,"
1119 " drain=%s, offline=%s" %
1120 (node.name, node.master_candidate, node.drained,
1121 node.offline))
1122 if node.group not in data.nodegroups:
1123 result.append("Node '%s' has invalid group '%s'" %
1124 (node.name, node.group))
1125 else:
1126 _helper("node %s" % node.name, "ndparams",
1127 cluster.FillND(node, data.nodegroups[node.group]),
1128 constants.NDS_PARAMETER_TYPES)
1129 used_globals = constants.NDC_GLOBALS.intersection(node.ndparams)
1130 if used_globals:
1131 result.append("Node '%s' has some global parameters set: %s" %
1132 (node.name, utils.CommaJoin(used_globals)))
1133
1134
1135 nodegroups_names = set()
1136 for nodegroup_uuid in data.nodegroups:
1137 nodegroup = data.nodegroups[nodegroup_uuid]
1138 if nodegroup.uuid != nodegroup_uuid:
1139 result.append("node group '%s' (uuid: '%s') indexed by wrong uuid '%s'"
1140 % (nodegroup.name, nodegroup.uuid, nodegroup_uuid))
1141 if utils.UUID_RE.match(nodegroup.name.lower()):
1142 result.append("node group '%s' (uuid: '%s') has uuid-like name" %
1143 (nodegroup.name, nodegroup.uuid))
1144 if nodegroup.name in nodegroups_names:
1145 result.append("duplicate node group name '%s'" % nodegroup.name)
1146 else:
1147 nodegroups_names.add(nodegroup.name)
1148 group_name = "group %s" % nodegroup.name
1149 _helper_ipolicy(group_name, cluster.SimpleFillIPolicy(nodegroup.ipolicy),
1150 False)
1151 if nodegroup.ndparams:
1152 _helper(group_name, "ndparams",
1153 cluster.SimpleFillND(nodegroup.ndparams),
1154 constants.NDS_PARAMETER_TYPES)
1155
1156
1157
1158
1159
1160 default_nicparams = cluster.nicparams[constants.PP_DEFAULT]
1161 ips = {}
1162
1163 def _AddIpAddress(ip, name):
1164 ips.setdefault(ip, []).append(name)
1165
1166 _AddIpAddress(cluster.master_ip, "cluster_ip")
1167
1168 for node in data.nodes.values():
1169 _AddIpAddress(node.primary_ip, "node:%s/primary" % node.name)
1170 if node.secondary_ip != node.primary_ip:
1171 _AddIpAddress(node.secondary_ip, "node:%s/secondary" % node.name)
1172
1173 for instance in data.instances.values():
1174 for idx, nic in enumerate(instance.nics):
1175 if nic.ip is None:
1176 continue
1177
1178 nicparams = objects.FillDict(default_nicparams, nic.nicparams)
1179 nic_mode = nicparams[constants.NIC_MODE]
1180 nic_link = nicparams[constants.NIC_LINK]
1181
1182 if nic_mode == constants.NIC_MODE_BRIDGED:
1183 link = "bridge:%s" % nic_link
1184 elif nic_mode == constants.NIC_MODE_ROUTED:
1185 link = "route:%s" % nic_link
1186 elif nic_mode == constants.NIC_MODE_OVS:
1187 link = "ovs:%s" % nic_link
1188 else:
1189 raise errors.ProgrammerError("NIC mode '%s' not handled" % nic_mode)
1190
1191 _AddIpAddress("%s/%s/%s" % (link, nic.ip, nic.network),
1192 "instance:%s/nic:%d" % (instance.name, idx))
1193
1194 for ip, owners in ips.items():
1195 if len(owners) > 1:
1196 result.append("IP address %s is used by multiple owners: %s" %
1197 (ip, utils.CommaJoin(owners)))
1198
1199 return result
1200
1202 """Verify the configuration and log any errors.
1203
1204 The errors get logged as critical errors and also to the feedback function,
1205 if given.
1206
1207 @param feedback_fn: Callable feedback function
1208 @rtype: list
1209 @return: a list of error messages; a non-empty list signifies
1210 configuration errors
1211
1212 """
1213 assert feedback_fn is None or callable(feedback_fn)
1214
1215
1216
1217
1218
1219 config_errors = self._UnlockedVerifyConfig()
1220 if config_errors:
1221 errmsg = ("Configuration data is not consistent: %s" %
1222 (utils.CommaJoin(config_errors)))
1223 logging.critical(errmsg)
1224 if feedback_fn:
1225 feedback_fn(errmsg)
1226 return config_errors
1227
1228 @_ConfigSync(shared=1)
1230 """Verify function.
1231
1232 This is just a wrapper over L{_UnlockedVerifyConfig}.
1233
1234 @rtype: list
1235 @return: a list of error messages; a non-empty list signifies
1236 configuration errors
1237
1238 """
1239 return self._UnlockedVerifyConfig()
1240
1241 @_ConfigSync()
1243 """Adds a new port to the available port pool.
1244
1245 @warning: this method does not "flush" the configuration (via
1246 L{_WriteConfig}); callers should do that themselves once the
1247 configuration is stable
1248
1249 """
1250 if not isinstance(port, int):
1251 raise errors.ProgrammerError("Invalid type passed for port")
1252
1253 self._ConfigData().cluster.tcpudp_port_pool.add(port)
1254
1255 @_ConfigSync(shared=1)
1257 """Returns a copy of the current port list.
1258
1259 """
1260 return self._ConfigData().cluster.tcpudp_port_pool.copy()
1261
1262 @_ConfigSync()
1282
1283 @_ConfigSync(shared=1)
1285 """Compute the used DRBD minor/nodes.
1286
1287 This is just a wrapper over a call to WConfd.
1288
1289 @return: dictionary of node_uuid: dict of minor: instance_uuid;
1290 the returned dict will have all the nodes in it (even if with
1291 an empty list).
1292
1293 """
1294 if self._offline:
1295 raise errors.ProgrammerError("Can't call ComputeDRBDMap in offline mode")
1296 else:
1297 return dict(map(lambda (k, v): (k, dict(v)),
1298 self._wconfd.ComputeDRBDMap()))
1299
1301 """Allocate a drbd minor.
1302
1303 This is just a wrapper over a call to WConfd.
1304
1305 The free minor will be automatically computed from the existing
1306 devices. A node can be given multiple times in order to allocate
1307 multiple minors. The result is the list of minors, in the same
1308 order as the passed nodes.
1309
1310 @type inst_uuid: string
1311 @param inst_uuid: the instance for which we allocate minors
1312
1313 """
1314 assert isinstance(inst_uuid, basestring), \
1315 "Invalid argument '%s' passed to AllocateDRBDMinor" % inst_uuid
1316
1317 if self._offline:
1318 raise errors.ProgrammerError("Can't call AllocateDRBDMinor"
1319 " in offline mode")
1320
1321 result = self._wconfd.AllocateDRBDMinor(inst_uuid, node_uuids)
1322 logging.debug("Request to allocate drbd minors, input: %s, returning %s",
1323 node_uuids, result)
1324 return result
1325
1327 """Release temporary drbd minors allocated for a given instance.
1328
1329 This is just a wrapper over a call to WConfd.
1330
1331 @type inst_uuid: string
1332 @param inst_uuid: the instance for which temporary minors should be
1333 released
1334
1335 """
1336 assert isinstance(inst_uuid, basestring), \
1337 "Invalid argument passed to ReleaseDRBDMinors"
1338
1339
1340
1341 if not self._offline:
1342 self._wconfd.ReleaseDRBDMinors(inst_uuid)
1343
1344 @_ConfigSync()
1346 """Release temporary drbd minors allocated for a given instance.
1347
1348 This should be called on the error paths, on the success paths
1349 it's automatically called by the ConfigWriter add and update
1350 functions.
1351
1352 This function is just a wrapper over L{_UnlockedReleaseDRBDMinors}.
1353
1354 @type inst_uuid: string
1355 @param inst_uuid: the instance for which temporary minors should be
1356 released
1357
1358 """
1359 self._UnlockedReleaseDRBDMinors(inst_uuid)
1360
1361 @_ConfigSync(shared=1)
1363 """Get the configuration version.
1364
1365 @return: Config version
1366
1367 """
1368 return self._ConfigData().version
1369
1370 @_ConfigSync(shared=1)
1372 """Get cluster name.
1373
1374 @return: Cluster name
1375
1376 """
1377 return self._ConfigData().cluster.cluster_name
1378
1379 @_ConfigSync(shared=1)
1381 """Get the UUID of the master node for this cluster.
1382
1383 @return: Master node UUID
1384
1385 """
1386 return self._ConfigData().cluster.master_node
1387
1388 @_ConfigSync(shared=1)
1390 """Get the hostname of the master node for this cluster.
1391
1392 @return: Master node hostname
1393
1394 """
1395 return self._UnlockedGetNodeName(self._ConfigData().cluster.master_node)
1396
1397 @_ConfigSync(shared=1)
1399 """Get the master node information for this cluster.
1400
1401 @rtype: objects.Node
1402 @return: Master node L{objects.Node} object
1403
1404 """
1405 return self._UnlockedGetNodeInfo(self._ConfigData().cluster.master_node)
1406
1407 @_ConfigSync(shared=1)
1409 """Get the IP of the master node for this cluster.
1410
1411 @return: Master IP
1412
1413 """
1414 return self._ConfigData().cluster.master_ip
1415
1416 @_ConfigSync(shared=1)
1418 """Get the master network device for this cluster.
1419
1420 """
1421 return self._ConfigData().cluster.master_netdev
1422
1423 @_ConfigSync(shared=1)
1425 """Get the netmask of the master node for this cluster.
1426
1427 """
1428 return self._ConfigData().cluster.master_netmask
1429
1430 @_ConfigSync(shared=1)
1432 """Get flag representing whether to use the external master IP setup script.
1433
1434 """
1435 return self._ConfigData().cluster.use_external_mip_script
1436
1437 @_ConfigSync(shared=1)
1439 """Get the file storage dir for this cluster.
1440
1441 """
1442 return self._ConfigData().cluster.file_storage_dir
1443
1444 @_ConfigSync(shared=1)
1446 """Get the shared file storage dir for this cluster.
1447
1448 """
1449 return self._ConfigData().cluster.shared_file_storage_dir
1450
1451 @_ConfigSync(shared=1)
1453 """Get the Gluster storage dir for this cluster.
1454
1455 """
1456 return self._ConfigData().cluster.gluster_storage_dir
1457
1458 @_ConfigSync(shared=1)
1460 """Get the hypervisor type for this cluster.
1461
1462 """
1463 return self._ConfigData().cluster.enabled_hypervisors[0]
1464
1465 @_ConfigSync(shared=1)
1467 """Return the rsa hostkey from the config.
1468
1469 @rtype: string
1470 @return: the rsa hostkey
1471
1472 """
1473 return self._ConfigData().cluster.rsahostkeypub
1474
1475 @_ConfigSync(shared=1)
1477 """Return the dsa hostkey from the config.
1478
1479 @rtype: string
1480 @return: the dsa hostkey
1481
1482 """
1483 return self._ConfigData().cluster.dsahostkeypub
1484
1485 @_ConfigSync(shared=1)
1487 """Get the default instance allocator for this cluster.
1488
1489 """
1490 return self._ConfigData().cluster.default_iallocator
1491
1492 @_ConfigSync(shared=1)
1494 """Get the default instance allocator parameters for this cluster.
1495
1496 @rtype: dict
1497 @return: dict of iallocator parameters
1498
1499 """
1500 return self._ConfigData().cluster.default_iallocator_params
1501
1502 @_ConfigSync(shared=1)
1504 """Get cluster primary ip family.
1505
1506 @return: primary ip family
1507
1508 """
1509 return self._ConfigData().cluster.primary_ip_family
1510
1511 @_ConfigSync(shared=1)
1526
1527 @_ConfigSync(shared=1)
1529 """Get the install image location
1530
1531 @rtype: string
1532 @return: location of the install image
1533
1534 """
1535 return self._ConfigData().cluster.install_image
1536
1537 @_ConfigSync()
1539 """Set the install image location
1540
1541 @type install_image: string
1542 @param install_image: location of the install image
1543
1544 """
1545 self._ConfigData().cluster.install_image = install_image
1546
1547 @_ConfigSync(shared=1)
1549 """Get cluster instance communication network
1550
1551 @rtype: string
1552 @return: instance communication network, which is the name of the
1553 network used for instance communication
1554
1555 """
1556 return self._ConfigData().cluster.instance_communication_network
1557
1558 @_ConfigSync()
1560 """Set cluster instance communication network
1561
1562 @type network_name: string
1563 @param network_name: instance communication network, which is the name of
1564 the network used for instance communication
1565
1566 """
1567 self._ConfigData().cluster.instance_communication_network = network_name
1568
1569 @_ConfigSync(shared=1)
1571 """Get the zeroing image location
1572
1573 @rtype: string
1574 @return: the location of the zeroing image
1575
1576 """
1577 return self._config_data.cluster.zeroing_image
1578
1579 @_ConfigSync(shared=1)
1589
1590 @_ConfigSync()
1600
1601 @_ConfigSync()
1603 """Add a node group to the configuration.
1604
1605 This method calls group.UpgradeConfig() to fill any missing attributes
1606 according to their default values.
1607
1608 @type group: L{objects.NodeGroup}
1609 @param group: the NodeGroup object to add
1610 @type ec_id: string
1611 @param ec_id: unique id for the job to use when creating a missing UUID
1612 @type check_uuid: bool
1613 @param check_uuid: add an UUID to the group if it doesn't have one or, if
1614 it does, ensure that it does not exist in the
1615 configuration already
1616
1617 """
1618 self._UnlockedAddNodeGroup(group, ec_id, check_uuid)
1619
1648
1649 @_ConfigSync()
1651 """Remove a node group from the configuration.
1652
1653 @type group_uuid: string
1654 @param group_uuid: the UUID of the node group to remove
1655
1656 """
1657 logging.info("Removing node group %s from configuration", group_uuid)
1658
1659 if group_uuid not in self._ConfigData().nodegroups:
1660 raise errors.ConfigurationError("Unknown node group '%s'" % group_uuid)
1661
1662 assert len(self._ConfigData().nodegroups) != 1, \
1663 "Group '%s' is the only group, cannot be removed" % group_uuid
1664
1665 del self._ConfigData().nodegroups[group_uuid]
1666 self._ConfigData().cluster.serial_no += 1
1667
1669 """Lookup a node group's UUID.
1670
1671 @type target: string or None
1672 @param target: group name or UUID or None to look for the default
1673 @rtype: string
1674 @return: nodegroup UUID
1675 @raises errors.OpPrereqError: when the target group cannot be found
1676
1677 """
1678 if target is None:
1679 if len(self._ConfigData().nodegroups) != 1:
1680 raise errors.OpPrereqError("More than one node group exists. Target"
1681 " group must be specified explicitly.")
1682 else:
1683 return self._ConfigData().nodegroups.keys()[0]
1684 if target in self._ConfigData().nodegroups:
1685 return target
1686 for nodegroup in self._ConfigData().nodegroups.values():
1687 if nodegroup.name == target:
1688 return nodegroup.uuid
1689 raise errors.OpPrereqError("Node group '%s' not found" % target,
1690 errors.ECODE_NOENT)
1691
1692 @_ConfigSync(shared=1)
1694 """Lookup a node group's UUID.
1695
1696 This function is just a wrapper over L{_UnlockedLookupNodeGroup}.
1697
1698 @type target: string or None
1699 @param target: group name or UUID or None to look for the default
1700 @rtype: string
1701 @return: nodegroup UUID
1702
1703 """
1704 return self._UnlockedLookupNodeGroup(target)
1705
1707 """Lookup a node group.
1708
1709 @type uuid: string
1710 @param uuid: group UUID
1711 @rtype: L{objects.NodeGroup} or None
1712 @return: nodegroup object, or None if not found
1713
1714 """
1715 if uuid not in self._ConfigData().nodegroups:
1716 return None
1717
1718 return self._ConfigData().nodegroups[uuid]
1719
1720 @_ConfigSync(shared=1)
1722 """Lookup a node group.
1723
1724 @type uuid: string
1725 @param uuid: group UUID
1726 @rtype: L{objects.NodeGroup} or None
1727 @return: nodegroup object, or None if not found
1728
1729 """
1730 return self._UnlockedGetNodeGroup(uuid)
1731
1733 """Get the configuration of all node groups.
1734
1735 """
1736 return dict(self._ConfigData().nodegroups)
1737
1738 @_ConfigSync(shared=1)
1744
1745 @_ConfigSync(shared=1)
1747 """Get the configuration of all node groups expressed as a dictionary of
1748 dictionaries.
1749
1750 """
1751 return dict(map(lambda (uuid, ng): (uuid, ng.ToDict()),
1752 self._UnlockedGetAllNodeGroupsInfo().items()))
1753
1754 @_ConfigSync(shared=1)
1756 """Get a list of node groups.
1757
1758 """
1759 return self._ConfigData().nodegroups.keys()
1760
1761 @_ConfigSync(shared=1)
1763 """Get nodes which are member in the same nodegroups as the given nodes.
1764
1765 """
1766 ngfn = lambda node_uuid: self._UnlockedGetNodeInfo(node_uuid).group
1767 return frozenset(member_uuid
1768 for node_uuid in nodes
1769 for member_uuid in
1770 self._UnlockedGetNodeGroup(ngfn(node_uuid)).members)
1771
1772 @_ConfigSync(shared=1)
1774 """Get the configuration of multiple node groups.
1775
1776 @param group_uuids: List of node group UUIDs
1777 @rtype: list
1778 @return: List of tuples of (group_uuid, group_info)
1779
1780 """
1781 return [(uuid, self._UnlockedGetNodeGroup(uuid)) for uuid in group_uuids]
1782
1783 @_ConfigSync()
1813
1815 """Ensures a given object has a valid UUID.
1816
1817 @param item: the instance or node to be checked
1818 @param ec_id: the execution context id for the uuid reservation
1819
1820 """
1821 if not item.uuid:
1822 item.uuid = self._GenerateUniqueID(ec_id)
1823 else:
1824 self._CheckUniqueUUID(item, include_temporary=True)
1825
1827 """Checks that the UUID of the given object is unique.
1828
1829 @param item: the instance or node to be checked
1830 @param include_temporary: whether temporarily generated UUID's should be
1831 included in the check. If the UUID of the item to be checked is
1832 a temporarily generated one, this has to be C{False}.
1833
1834 """
1835 if not item.uuid:
1836 raise errors.ConfigurationError("'%s' must have an UUID" % (item.name,))
1837 if item.uuid in self._AllIDs(include_temporary=include_temporary):
1838 raise errors.ConfigurationError("Cannot add '%s': UUID %s already"
1839 " in use" % (item.name, item.uuid))
1840
1843 """Set the instance's status to a given value.
1844
1845 @rtype: L{objects.Instance}
1846 @return: the updated instance object
1847
1848 """
1849 if inst_uuid not in self._ConfigData().instances:
1850 raise errors.ConfigurationError("Unknown instance '%s'" %
1851 inst_uuid)
1852 instance = self._ConfigData().instances[inst_uuid]
1853
1854 if status is None:
1855 status = instance.admin_state
1856 if disks_active is None:
1857 disks_active = instance.disks_active
1858 if admin_state_source is None:
1859 admin_state_source = instance.admin_state_source
1860
1861 assert status in constants.ADMINST_ALL, \
1862 "Invalid status '%s' passed to SetInstanceStatus" % (status,)
1863
1864 if instance.admin_state != status or \
1865 instance.disks_active != disks_active or \
1866 instance.admin_state_source != admin_state_source:
1867 instance.admin_state = status
1868 instance.disks_active = disks_active
1869 instance.admin_state_source = admin_state_source
1870 instance.serial_no += 1
1871 instance.mtime = time.time()
1872 return instance
1873
1874 @_ConfigSync()
1876 """Mark the instance status to up in the config.
1877
1878 This also sets the instance disks active flag.
1879
1880 @rtype: L{objects.Instance}
1881 @return: the updated instance object
1882
1883 """
1884 return self._SetInstanceStatus(inst_uuid, constants.ADMINST_UP, True,
1885 constants.ADMIN_SOURCE)
1886
1887 @_ConfigSync()
1889 """Mark the instance status to down in the config.
1890
1891 This also clears the instance disks active flag.
1892
1893 @rtype: L{objects.Instance}
1894 @return: the updated instance object
1895
1896 """
1897 return self._SetInstanceStatus(inst_uuid, constants.ADMINST_OFFLINE, False,
1898 constants.ADMIN_SOURCE)
1899
1900 @_ConfigSync()
1926
1927 @_ConfigSync()
1929 """Rename an instance.
1930
1931 This needs to be done in ConfigWriter and not by RemoveInstance
1932 combined with AddInstance as only we can guarantee an atomic
1933 rename.
1934
1935 """
1936 if inst_uuid not in self._ConfigData().instances:
1937 raise errors.ConfigurationError("Unknown instance '%s'" % inst_uuid)
1938
1939 inst = self._ConfigData().instances[inst_uuid]
1940 inst.name = new_name
1941
1942 instance_disks = self._UnlockedGetInstanceDisks(inst_uuid)
1943 for (_, disk) in enumerate(instance_disks):
1944 if disk.dev_type in [constants.DT_FILE, constants.DT_SHARED_FILE]:
1945
1946 file_storage_dir = os.path.dirname(os.path.dirname(disk.logical_id[1]))
1947 disk.logical_id = (disk.logical_id[0],
1948 utils.PathJoin(file_storage_dir, inst.name,
1949 os.path.basename(disk.logical_id[1])))
1950
1951
1952 self._ConfigData().cluster.serial_no += 1
1953
1954 @_ConfigSync()
1956 """Mark the status of an instance to down in the configuration.
1957
1958 This does not touch the instance disks active flag, as shut down instances
1959 can still have active disks.
1960
1961 @rtype: L{objects.Instance}
1962 @return: the updated instance object
1963
1964 """
1965 return self._SetInstanceStatus(inst_uuid, constants.ADMINST_DOWN, None,
1966 constants.ADMIN_SOURCE)
1967
1968 @_ConfigSync()
1970 """Mark the status of an instance to user down in the configuration.
1971
1972 This does not touch the instance disks active flag, as user shut
1973 down instances can still have active disks.
1974
1975 """
1976
1977 self._SetInstanceStatus(inst_uuid, constants.ADMINST_DOWN, None,
1978 constants.USER_SOURCE)
1979
1980 @_ConfigSync()
1982 """Mark the status of instance disks active.
1983
1984 @rtype: L{objects.Instance}
1985 @return: the updated instance object
1986
1987 """
1988 return self._SetInstanceStatus(inst_uuid, None, True, None)
1989
1990 @_ConfigSync()
1992 """Mark the status of instance disks inactive.
1993
1994 @rtype: L{objects.Instance}
1995 @return: the updated instance object
1996
1997 """
1998 return self._SetInstanceStatus(inst_uuid, None, False, None)
1999
2001 """Get the list of instances.
2002
2003 This function is for internal use, when the config lock is already held.
2004
2005 """
2006 return self._ConfigData().instances.keys()
2007
2008 @_ConfigSync(shared=1)
2010 """Get the list of instances.
2011
2012 @return: array of instances, ex. ['instance2-uuid', 'instance1-uuid']
2013
2014 """
2015 return self._UnlockedGetInstanceList()
2016
2018 """Attempt to expand an incomplete instance name.
2019
2020 """
2021
2022 all_insts = self.GetAllInstancesInfo().values()
2023 expanded_name = _MatchNameComponentIgnoreCase(
2024 short_name, [inst.name for inst in all_insts])
2025
2026 if expanded_name is not None:
2027
2028 inst = (filter(lambda n: n.name == expanded_name, all_insts)[0])
2029 return (inst.uuid, inst.name)
2030 else:
2031 return (None, None)
2032
2034 """Returns information about an instance.
2035
2036 This function is for internal use, when the config lock is already held.
2037
2038 """
2039 if inst_uuid not in self._ConfigData().instances:
2040 return None
2041
2042 return self._ConfigData().instances[inst_uuid]
2043
2044 @_ConfigSync(shared=1)
2046 """Returns information about an instance.
2047
2048 It takes the information from the configuration file. Other information of
2049 an instance are taken from the live systems.
2050
2051 @param inst_uuid: UUID of the instance
2052
2053 @rtype: L{objects.Instance}
2054 @return: the instance object
2055
2056 """
2057 return self._UnlockedGetInstanceInfo(inst_uuid)
2058
2059 @_ConfigSync(shared=1)
2077
2078 @_ConfigSync(shared=1)
2095
2096 @_ConfigSync(shared=1)
2098 """Get the configuration of multiple instances.
2099
2100 @param inst_uuids: list of instance UUIDs
2101 @rtype: list
2102 @return: list of tuples (instance UUID, instance_info), where
2103 instance_info is what would GetInstanceInfo return for the
2104 node, while keeping the original order
2105
2106 """
2107 return [(uuid, self._UnlockedGetInstanceInfo(uuid)) for uuid in inst_uuids]
2108
2109 @_ConfigSync(shared=1)
2111 """Get the configuration of multiple instances.
2112
2113 @param inst_names: list of instance names
2114 @rtype: list
2115 @return: list of tuples (instance, instance_info), where
2116 instance_info is what would GetInstanceInfo return for the
2117 node, while keeping the original order
2118
2119 """
2120 result = []
2121 for name in inst_names:
2122 instance = self._UnlockedGetInstanceInfoByName(name)
2123 if instance:
2124 result.append((instance.uuid, instance))
2125 else:
2126 raise errors.ConfigurationError("Instance data of instance '%s'"
2127 " not found." % name)
2128 return result
2129
2130 @_ConfigSync(shared=1)
2132 """Get the configuration of all instances.
2133
2134 @rtype: dict
2135 @return: dict of (instance, instance_info), where instance_info is what
2136 would GetInstanceInfo return for the node
2137
2138 """
2139 return self._UnlockedGetAllInstancesInfo()
2140
2145
2146 @_ConfigSync(shared=1)
2148 """Get instance configuration with a filter.
2149
2150 @type filter_fn: callable
2151 @param filter_fn: Filter function receiving instance object as parameter,
2152 returning boolean. Important: this function is called while the
2153 configuration locks is held. It must not do any complex work or call
2154 functions potentially leading to a deadlock. Ideally it doesn't call any
2155 other functions and just compares instance attributes.
2156
2157 """
2158 return dict((uuid, inst)
2159 for (uuid, inst) in self._ConfigData().instances.items()
2160 if filter_fn(inst))
2161
2162 @_ConfigSync(shared=1)
2164 """Get the L{objects.Instance} object for a named instance.
2165
2166 @param inst_name: name of the instance to get information for
2167 @type inst_name: string
2168 @return: the corresponding L{objects.Instance} instance or None if no
2169 information is available
2170
2171 """
2172 return self._UnlockedGetInstanceInfoByName(inst_name)
2173
2179
2185
2186 @_ConfigSync(shared=1)
2188 """Gets the instance name for the passed instance.
2189
2190 @param inst_uuid: instance UUID to get name for
2191 @type inst_uuid: string
2192 @rtype: string
2193 @return: instance name
2194
2195 """
2196 return self._UnlockedGetInstanceName(inst_uuid)
2197
2198 @_ConfigSync(shared=1)
2200 """Gets the instance names for the passed list of nodes.
2201
2202 @param inst_uuids: list of instance UUIDs to get names for
2203 @type inst_uuids: list of strings
2204 @rtype: list of strings
2205 @return: list of instance names
2206
2207 """
2208 return self._UnlockedGetInstanceNames(inst_uuids)
2209
2210 @_ConfigSync()
2212 """Sets the primary node of an existing instance
2213
2214 @param inst_uuid: instance UUID
2215 @type inst_uuid: string
2216 @param target_node_uuid: the new primary node UUID
2217 @type target_node_uuid: string
2218
2219 """
2220 self._UnlockedGetInstanceInfo(inst_uuid).primary_node = target_node_uuid
2221
2224
2242
2243 @_ConfigSync()
2245 """Add a node to the configuration.
2246
2247 @type node: L{objects.Node}
2248 @param node: a Node instance
2249
2250 """
2251 self._UnlockedAddNode(node, ec_id)
2252
2253 @_ConfigSync()
2266
2282
2284 """Get the configuration of a node, as stored in the config.
2285
2286 This function is for internal use, when the config lock is already
2287 held.
2288
2289 @param node_uuid: the node UUID
2290
2291 @rtype: L{objects.Node}
2292 @return: the node object
2293
2294 """
2295 if node_uuid not in self._ConfigData().nodes:
2296 return None
2297
2298 return self._ConfigData().nodes[node_uuid]
2299
2300 @_ConfigSync(shared=1)
2302 """Get the configuration of a node, as stored in the config.
2303
2304 This is just a locked wrapper over L{_UnlockedGetNodeInfo}.
2305
2306 @param node_uuid: the node UUID
2307
2308 @rtype: L{objects.Node}
2309 @return: the node object
2310
2311 """
2312 return self._UnlockedGetNodeInfo(node_uuid)
2313
2314 @_ConfigSync(shared=1)
2316 """Get the instances of a node, as stored in the config.
2317
2318 @param node_uuid: the node UUID
2319
2320 @rtype: (list, list)
2321 @return: a tuple with two lists: the primary and the secondary instances
2322
2323 """
2324 pri = []
2325 sec = []
2326 for inst in self._ConfigData().instances.values():
2327 if inst.primary_node == node_uuid:
2328 pri.append(inst.uuid)
2329 if node_uuid in self._UnlockedGetInstanceSecondaryNodes(inst.uuid):
2330 sec.append(inst.uuid)
2331 return (pri, sec)
2332
2333 @_ConfigSync(shared=1)
2335 """Get the instances of a node group.
2336
2337 @param uuid: Node group UUID
2338 @param primary_only: Whether to only consider primary nodes
2339 @rtype: frozenset
2340 @return: List of instance UUIDs in node group
2341
2342 """
2343 if primary_only:
2344 nodes_fn = lambda inst: [inst.primary_node]
2345 else:
2346 nodes_fn = lambda inst: self._UnlockedGetInstanceNodes(inst.uuid)
2347
2348 return frozenset(inst.uuid
2349 for inst in self._ConfigData().instances.values()
2350 for node_uuid in nodes_fn(inst)
2351 if self._UnlockedGetNodeInfo(node_uuid).group == uuid)
2352
2354 """Return the string representation of the list of hyervisor parameters of
2355 the given hypervisor.
2356
2357 @see: C{GetHvparams}
2358
2359 """
2360 result = ""
2361 hvparams = self._ConfigData().cluster.hvparams[hvname]
2362 for key in hvparams:
2363 result += "%s=%s\n" % (key, hvparams[key])
2364 return result
2365
2366 @_ConfigSync(shared=1)
2368 """Return the hypervisor parameters of the given hypervisor.
2369
2370 @type hvname: string
2371 @param hvname: name of a hypervisor
2372 @rtype: string
2373 @return: string containing key-value-pairs, one pair on each line;
2374 format: KEY=VALUE
2375
2376 """
2377 return self._UnlockedGetHvparamsString(hvname)
2378
2380 """Return the list of nodes which are in the configuration.
2381
2382 This function is for internal use, when the config lock is already
2383 held.
2384
2385 @rtype: list
2386
2387 """
2388 return self._ConfigData().nodes.keys()
2389
2390 @_ConfigSync(shared=1)
2392 """Return the list of nodes which are in the configuration.
2393
2394 """
2395 return self._UnlockedGetNodeList()
2396
2404
2405 @_ConfigSync(shared=1)
2411
2412 @_ConfigSync(shared=1)
2420
2421 @_ConfigSync(shared=1)
2429
2430 @_ConfigSync(shared=1)
2438
2439 @_ConfigSync(shared=1)
2441 """Get the configuration of multiple nodes.
2442
2443 @param node_uuids: list of node UUIDs
2444 @rtype: list
2445 @return: list of tuples of (node, node_info), where node_info is
2446 what would GetNodeInfo return for the node, in the original
2447 order
2448
2449 """
2450 return [(uuid, self._UnlockedGetNodeInfo(uuid)) for uuid in node_uuids]
2451
2453 """Gets configuration of all nodes.
2454
2455 @note: See L{GetAllNodesInfo}
2456
2457 """
2458 return dict([(node_uuid, self._UnlockedGetNodeInfo(node_uuid))
2459 for node_uuid in self._UnlockedGetNodeList()])
2460
2461 @_ConfigSync(shared=1)
2463 """Get the configuration of all nodes.
2464
2465 @rtype: dict
2466 @return: dict of (node, node_info), where node_info is what
2467 would GetNodeInfo return for the node
2468
2469 """
2470 return self._UnlockedGetAllNodesInfo()
2471
2477
2478 @_ConfigSync(shared=1)
2480 """Get the L{objects.Node} object for a named node.
2481
2482 @param node_name: name of the node to get information for
2483 @type node_name: string
2484 @return: the corresponding L{objects.Node} instance or None if no
2485 information is available
2486
2487 """
2488 return self._UnlockedGetNodeInfoByName(node_name)
2489
2490 @_ConfigSync(shared=1)
2492 """Get the L{objects.NodeGroup} object for a named node group.
2493
2494 @param nodegroup_name: name of the node group to get information for
2495 @type nodegroup_name: string
2496 @return: the corresponding L{objects.NodeGroup} instance or None if no
2497 information is available
2498
2499 """
2500 for nodegroup in self._UnlockedGetAllNodeGroupsInfo().values():
2501 if nodegroup.name == nodegroup_name:
2502 return nodegroup
2503 return None
2504
2515
2516 @_ConfigSync(shared=1)
2518 """Gets the node name for the passed node.
2519
2520 @param node_spec: node to get names for
2521 @type node_spec: either node UUID or a L{objects.Node} object
2522 @rtype: string
2523 @return: node name
2524
2525 """
2526 return self._UnlockedGetNodeName(node_spec)
2527
2530
2531 @_ConfigSync(shared=1)
2533 """Gets the node names for the passed list of nodes.
2534
2535 @param node_specs: list of nodes to get names for
2536 @type node_specs: list of either node UUIDs or L{objects.Node} objects
2537 @rtype: list of strings
2538 @return: list of node names
2539
2540 """
2541 return self._UnlockedGetNodeNames(node_specs)
2542
2543 @_ConfigSync(shared=1)
2545 """Returns groups for a list of nodes.
2546
2547 @type node_uuids: list of string
2548 @param node_uuids: List of node UUIDs
2549 @rtype: frozenset
2550
2551 """
2552 return frozenset(self._UnlockedGetNodeInfo(uuid).group
2553 for uuid in node_uuids)
2554
2556 """Get the number of current and maximum desired and possible candidates.
2557
2558 @type exceptions: list
2559 @param exceptions: if passed, list of nodes that should be ignored
2560 @rtype: tuple
2561 @return: tuple of (current, desired and possible, possible)
2562
2563 """
2564 mc_now = mc_should = mc_max = 0
2565 for node in self._ConfigData().nodes.values():
2566 if exceptions and node.uuid in exceptions:
2567 continue
2568 if not (node.offline or node.drained) and node.master_capable:
2569 mc_max += 1
2570 if node.master_candidate:
2571 mc_now += 1
2572 mc_should = min(mc_max, self._ConfigData().cluster.candidate_pool_size)
2573 return (mc_now, mc_should, mc_max)
2574
2575 @_ConfigSync(shared=1)
2577 """Get the number of current and maximum possible candidates.
2578
2579 This is just a wrapper over L{_UnlockedGetMasterCandidateStats}.
2580
2581 @type exceptions: list
2582 @param exceptions: if passed, list of nodes that should be ignored
2583 @rtype: tuple
2584 @return: tuple of (current, max)
2585
2586 """
2587 return self._UnlockedGetMasterCandidateStats(exceptions)
2588
2589 @_ConfigSync()
2590 - def MaintainCandidatePool(self, exception_node_uuids):
2591 """Try to grow the candidate pool to the desired size.
2592
2593 @type exception_node_uuids: list
2594 @param exception_node_uuids: if passed, list of nodes that should be ignored
2595 @rtype: list
2596 @return: list with the adjusted nodes (L{objects.Node} instances)
2597
2598 """
2599 mc_now, mc_max, _ = self._UnlockedGetMasterCandidateStats(
2600 exception_node_uuids)
2601 mod_list = []
2602 if mc_now < mc_max:
2603 node_list = self._ConfigData().nodes.keys()
2604 random.shuffle(node_list)
2605 for uuid in node_list:
2606 if mc_now >= mc_max:
2607 break
2608 node = self._ConfigData().nodes[uuid]
2609 if (node.master_candidate or node.offline or node.drained or
2610 node.uuid in exception_node_uuids or not node.master_capable):
2611 continue
2612 mod_list.append(node)
2613 node.master_candidate = True
2614 node.serial_no += 1
2615 mc_now += 1
2616 if mc_now != mc_max:
2617
2618 logging.warning("Warning: MaintainCandidatePool didn't manage to"
2619 " fill the candidate pool (%d/%d)", mc_now, mc_max)
2620 if mod_list:
2621 self._ConfigData().cluster.serial_no += 1
2622
2623 return mod_list
2624
2626 """Add a given node to the specified group.
2627
2628 """
2629 if nodegroup_uuid not in self._ConfigData().nodegroups:
2630
2631
2632
2633
2634 raise errors.OpExecError("Unknown node group: %s" % nodegroup_uuid)
2635 if node_uuid not in self._ConfigData().nodegroups[nodegroup_uuid].members:
2636 self._ConfigData().nodegroups[nodegroup_uuid].members.append(node_uuid)
2637
2639 """Remove a given node from its group.
2640
2641 """
2642 nodegroup = node.group
2643 if nodegroup not in self._ConfigData().nodegroups:
2644 logging.warning("Warning: node '%s' has unknown node group '%s'"
2645 " (while being removed from it)", node.uuid, nodegroup)
2646 nodegroup_obj = self._ConfigData().nodegroups[nodegroup]
2647 if node.uuid not in nodegroup_obj.members:
2648 logging.warning("Warning: node '%s' not a member of its node group '%s'"
2649 " (while being removed from it)", node.uuid, nodegroup)
2650 else:
2651 nodegroup_obj.members.remove(node.uuid)
2652
2653 @_ConfigSync()
2655 """Changes the group of a number of nodes.
2656
2657 @type mods: list of tuples; (node name, new group UUID)
2658 @param mods: Node membership modifications
2659
2660 """
2661 groups = self._ConfigData().nodegroups
2662 nodes = self._ConfigData().nodes
2663
2664 resmod = []
2665
2666
2667 for (node_uuid, new_group_uuid) in mods:
2668 try:
2669 node = nodes[node_uuid]
2670 except KeyError:
2671 raise errors.ConfigurationError("Unable to find node '%s'" % node_uuid)
2672
2673 if node.group == new_group_uuid:
2674
2675 logging.debug("Node '%s' was assigned to its current group (%s)",
2676 node_uuid, node.group)
2677 continue
2678
2679
2680 try:
2681 old_group = groups[node.group]
2682 except KeyError:
2683 raise errors.ConfigurationError("Unable to find old group '%s'" %
2684 node.group)
2685
2686
2687 try:
2688 new_group = groups[new_group_uuid]
2689 except KeyError:
2690 raise errors.ConfigurationError("Unable to find new group '%s'" %
2691 new_group_uuid)
2692
2693 assert node.uuid in old_group.members, \
2694 ("Inconsistent configuration: node '%s' not listed in members for its"
2695 " old group '%s'" % (node.uuid, old_group.uuid))
2696 assert node.uuid not in new_group.members, \
2697 ("Inconsistent configuration: node '%s' already listed in members for"
2698 " its new group '%s'" % (node.uuid, new_group.uuid))
2699
2700 resmod.append((node, old_group, new_group))
2701
2702
2703 for (node, old_group, new_group) in resmod:
2704 assert node.uuid != new_group.uuid and old_group.uuid != new_group.uuid, \
2705 "Assigning to current group is not possible"
2706
2707 node.group = new_group.uuid
2708
2709
2710 if node.uuid in old_group.members:
2711 old_group.members.remove(node.uuid)
2712 if node.uuid not in new_group.members:
2713 new_group.members.append(node.uuid)
2714
2715
2716 now = time.time()
2717 for obj in frozenset(itertools.chain(*resmod)):
2718 obj.serial_no += 1
2719 obj.mtime = now
2720
2721
2722 self._ConfigData().cluster.serial_no += 1
2723
2725 """Bump up the serial number of the config.
2726
2727 """
2728 self._ConfigData().serial_no += 1
2729 self._ConfigData().mtime = time.time()
2730
2742
2744 """Returns a ConfigManager, which is suitable to perform a synchronized
2745 block of configuration operations.
2746
2747 WARNING: This blocks all other configuration operations, so anything that
2748 runs inside the block should be very fast, preferably not using any IO.
2749 """
2750
2751 return ConfigManager(self, shared)
2752
2754 self._lock_count += count
2755 return self._lock_count
2756
2758 return self._lock_count
2759
2761 """Read the config data from WConfd or disk.
2762
2763 """
2764 if self._AddLockCount(1) > 1:
2765 if self._lock_current_shared and not shared:
2766 self._AddLockCount(-1)
2767 raise errors.ConfigurationError("Can't request an exclusive"
2768 " configuration lock while holding"
2769 " shared")
2770 else:
2771 return
2772 else:
2773 self._lock_current_shared = shared
2774
2775
2776 if self._offline:
2777 try:
2778 raw_data = utils.ReadFile(self._cfg_file)
2779 data_dict = serializer.Load(raw_data)
2780
2781 _ValidateConfig(data_dict)
2782 data = objects.ConfigData.FromDict(data_dict)
2783 except errors.ConfigVersionMismatch:
2784 raise
2785 except Exception, err:
2786 raise errors.ConfigurationError(err)
2787
2788 self._cfg_id = utils.GetFileID(path=self._cfg_file)
2789
2790 if (not hasattr(data, "cluster") or
2791 not hasattr(data.cluster, "rsahostkeypub")):
2792 raise errors.ConfigurationError("Incomplete configuration"
2793 " (missing cluster.rsahostkeypub)")
2794
2795 if not data.cluster.master_node in data.nodes:
2796 msg = ("The configuration denotes node %s as master, but does not"
2797 " contain information about this node" %
2798 data.cluster.master_node)
2799 raise errors.ConfigurationError(msg)
2800
2801 master_info = data.nodes[data.cluster.master_node]
2802 if master_info.name != self._my_hostname and not self._accept_foreign:
2803 msg = ("The configuration denotes node %s as master, while my"
2804 " hostname is %s; opening a foreign configuration is only"
2805 " possible in accept_foreign mode" %
2806 (master_info.name, self._my_hostname))
2807 raise errors.ConfigurationError(msg)
2808
2809 self._SetConfigData(data)
2810
2811
2812 self._UpgradeConfig(saveafter=True)
2813 else:
2814 if shared:
2815 if self._config_data is None:
2816 logging.debug("Requesting config, as I have no up-to-date copy")
2817 dict_data = self._wconfd.ReadConfig()
2818 else:
2819 logging.debug("My config copy is up to date.")
2820 dict_data = None
2821 else:
2822
2823 while True:
2824 dict_data = \
2825 self._wconfd.LockConfig(self._GetWConfdContext(), bool(shared))
2826 logging.debug("Received config from WConfd.LockConfig [shared=%s]",
2827 bool(shared))
2828 if dict_data is not None:
2829 break
2830 time.sleep(random.random())
2831
2832 try:
2833 if dict_data is not None:
2834 self._SetConfigData(objects.ConfigData.FromDict(dict_data))
2835 self._UpgradeConfig()
2836 except Exception, err:
2837 raise errors.ConfigurationError(err)
2838
2840 """Release resources relating the config data.
2841
2842 """
2843 if self._AddLockCount(-1) > 0:
2844 return
2845 try:
2846 if save:
2847 self._WriteConfig()
2848 except Exception, err:
2849 logging.critical("Can't write the configuration: %s", str(err))
2850 raise
2851 finally:
2852 if not self._offline and not self._lock_current_shared:
2853 try:
2854 self._wconfd.UnlockConfig(self._GetWConfdContext())
2855 except AttributeError:
2856
2857 pass
2858 logging.debug("Configuration in WConfd unlocked")
2859
2860
2904
2943
2945 """Get the hvparams of all given hypervisors from the config.
2946
2947 @type hypervisors: list of string
2948 @param hypervisors: list of hypervisor names
2949 @rtype: dict of strings
2950 @returns: dictionary mapping the hypervisor name to a string representation
2951 of the hypervisor's hvparams
2952
2953 """
2954 hvparams = {}
2955 for hv in hypervisors:
2956 hvparams[hv] = self._UnlockedGetHvparamsString(hv)
2957 return hvparams
2958
2959 @staticmethod
2961 """Extends the ssconf_values dictionary by hvparams.
2962
2963 @type ssconf_values: dict of strings
2964 @param ssconf_values: dictionary mapping ssconf_keys to strings
2965 representing the content of ssconf files
2966 @type all_hvparams: dict of strings
2967 @param all_hvparams: dictionary mapping hypervisor names to a string
2968 representation of their hvparams
2969 @rtype: same as ssconf_values
2970 @returns: the ssconf_values dictionary extended by hvparams
2971
2972 """
2973 for hv in all_hvparams:
2974 ssconf_key = constants.SS_HVPARAMS_PREF + hv
2975 ssconf_values[ssconf_key] = all_hvparams[hv]
2976 return ssconf_values
2977
2984
2986 """Return the values needed by ssconf.
2987
2988 @rtype: dict
2989 @return: a dictionary with keys the ssconf names and values their
2990 associated value
2991
2992 """
2993 fn = "\n".join
2994 instance_names = utils.NiceSort(
2995 [inst.name for inst in
2996 self._UnlockedGetAllInstancesInfo().values()])
2997 node_infos = self._UnlockedGetAllNodesInfo().values()
2998 node_names = [node.name for node in node_infos]
2999 node_pri_ips = ["%s %s" % (ninfo.name, ninfo.primary_ip)
3000 for ninfo in node_infos]
3001 node_snd_ips = ["%s %s" % (ninfo.name, ninfo.secondary_ip)
3002 for ninfo in node_infos]
3003 node_vm_capable = ["%s=%s" % (ninfo.name, str(ninfo.vm_capable))
3004 for ninfo in node_infos]
3005
3006 instance_data = fn(instance_names)
3007 off_data = fn(node.name for node in node_infos if node.offline)
3008 on_data = fn(node.name for node in node_infos if not node.offline)
3009 mc_data = fn(node.name for node in node_infos if node.master_candidate)
3010 mc_ips_data = fn(node.primary_ip for node in node_infos
3011 if node.master_candidate)
3012 node_data = fn(node_names)
3013 node_pri_ips_data = fn(node_pri_ips)
3014 node_snd_ips_data = fn(node_snd_ips)
3015 node_vm_capable_data = fn(node_vm_capable)
3016
3017 cluster = self._ConfigData().cluster
3018 cluster_tags = fn(cluster.GetTags())
3019
3020 master_candidates_certs = fn("%s=%s" % (mc_uuid, mc_cert)
3021 for mc_uuid, mc_cert
3022 in cluster.candidate_certs.items())
3023
3024 hypervisor_list = fn(cluster.enabled_hypervisors)
3025 all_hvparams = self._GetAllHvparamsStrings(constants.HYPER_TYPES)
3026
3027 uid_pool = uidpool.FormatUidPool(cluster.uid_pool, separator="\n")
3028
3029 nodegroups = ["%s %s" % (nodegroup.uuid, nodegroup.name) for nodegroup in
3030 self._ConfigData().nodegroups.values()]
3031 nodegroups_data = fn(utils.NiceSort(nodegroups))
3032 networks = ["%s %s" % (net.uuid, net.name) for net in
3033 self._ConfigData().networks.values()]
3034 networks_data = fn(utils.NiceSort(networks))
3035
3036 ssh_ports = fn("%s=%s" % (node_name, port)
3037 for node_name, port
3038 in self._UnlockedGetSshPortMap(node_infos).items())
3039
3040 ssconf_values = {
3041 constants.SS_CLUSTER_NAME: cluster.cluster_name,
3042 constants.SS_CLUSTER_TAGS: cluster_tags,
3043 constants.SS_FILE_STORAGE_DIR: cluster.file_storage_dir,
3044 constants.SS_SHARED_FILE_STORAGE_DIR: cluster.shared_file_storage_dir,
3045 constants.SS_GLUSTER_STORAGE_DIR: cluster.gluster_storage_dir,
3046 constants.SS_MASTER_CANDIDATES: mc_data,
3047 constants.SS_MASTER_CANDIDATES_IPS: mc_ips_data,
3048 constants.SS_MASTER_CANDIDATES_CERTS: master_candidates_certs,
3049 constants.SS_MASTER_IP: cluster.master_ip,
3050 constants.SS_MASTER_NETDEV: cluster.master_netdev,
3051 constants.SS_MASTER_NETMASK: str(cluster.master_netmask),
3052 constants.SS_MASTER_NODE: self._UnlockedGetNodeName(cluster.master_node),
3053 constants.SS_NODE_LIST: node_data,
3054 constants.SS_NODE_PRIMARY_IPS: node_pri_ips_data,
3055 constants.SS_NODE_SECONDARY_IPS: node_snd_ips_data,
3056 constants.SS_NODE_VM_CAPABLE: node_vm_capable_data,
3057 constants.SS_OFFLINE_NODES: off_data,
3058 constants.SS_ONLINE_NODES: on_data,
3059 constants.SS_PRIMARY_IP_FAMILY: str(cluster.primary_ip_family),
3060 constants.SS_INSTANCE_LIST: instance_data,
3061 constants.SS_RELEASE_VERSION: constants.RELEASE_VERSION,
3062 constants.SS_HYPERVISOR_LIST: hypervisor_list,
3063 constants.SS_MAINTAIN_NODE_HEALTH: str(cluster.maintain_node_health),
3064 constants.SS_UID_POOL: uid_pool,
3065 constants.SS_NODEGROUPS: nodegroups_data,
3066 constants.SS_NETWORKS: networks_data,
3067 constants.SS_ENABLED_USER_SHUTDOWN: str(cluster.enabled_user_shutdown),
3068 constants.SS_SSH_PORTS: ssh_ports,
3069 }
3070 ssconf_values = self._ExtendByAllHvparamsStrings(ssconf_values,
3071 all_hvparams)
3072 bad_values = [(k, v) for k, v in ssconf_values.items()
3073 if not isinstance(v, (str, basestring))]
3074 if bad_values:
3075 err = utils.CommaJoin("%s=%s" % (k, v) for k, v in bad_values)
3076 raise errors.ConfigurationError("Some ssconf key(s) have non-string"
3077 " values: %s" % err)
3078 return ssconf_values
3079
3080 @_ConfigSync(shared=1)
3086
3087 @_ConfigSync(shared=1)
3089 """Return the volume group name.
3090
3091 """
3092 return self._ConfigData().cluster.volume_group_name
3093
3094 @_ConfigSync()
3101
3102 @_ConfigSync(shared=1)
3104 """Return DRBD usermode helper.
3105
3106 """
3107 return self._ConfigData().cluster.drbd_usermode_helper
3108
3109 @_ConfigSync()
3116
3117 @_ConfigSync(shared=1)
3119 """Return the mac prefix.
3120
3121 """
3122 return self._ConfigData().cluster.mac_prefix
3123
3124 @_ConfigSync(shared=1)
3126 """Returns information about the cluster
3127
3128 @rtype: L{objects.Cluster}
3129 @return: the cluster object
3130
3131 """
3132 return self._ConfigData().cluster
3133
3134 @_ConfigSync(shared=1)
3136 """Check if in there is at disk of the given type in the configuration.
3137
3138 """
3139 return self._ConfigData().HasAnyDiskOfType(dev_type)
3140
3141 @_ConfigSync(shared=1)
3143 """Returns a detached version of a ConfigManager, which represents
3144 a read-only snapshot of the configuration at this particular time.
3145
3146 """
3147 return DetachedConfig(self._ConfigData())
3148
3149 @_ConfigSync()
3150 - def Update(self, target, feedback_fn, ec_id=None):
3151 """Notify function to be called after updates.
3152
3153 This function must be called when an object (as returned by
3154 GetInstanceInfo, GetNodeInfo, GetCluster) has been updated and the
3155 caller wants the modifications saved to the backing store. Note
3156 that all modified objects will be saved, but the target argument
3157 is the one the caller wants to ensure that it's saved.
3158
3159 @param target: an instance of either L{objects.Cluster},
3160 L{objects.Node} or L{objects.Instance} which is existing in
3161 the cluster
3162 @param feedback_fn: Callable feedback function
3163
3164 """
3165 if self._ConfigData() is None:
3166 raise errors.ProgrammerError("Configuration file not read,"
3167 " cannot save.")
3168
3169 def check_serial(target, current):
3170 if current is None:
3171 raise errors.ConfigurationError("Configuration object unknown")
3172 elif current.serial_no != target.serial_no:
3173 raise errors.ConfigurationError("Configuration object updated since"
3174 " it has been read: %d != %d",
3175 current.serial_no, target.serial_no)
3176
3177 def replace_in(target, tdict):
3178 check_serial(target, tdict.get(target.uuid))
3179 tdict[target.uuid] = target
3180
3181 update_serial = False
3182 if isinstance(target, objects.Cluster):
3183 check_serial(target, self._ConfigData().cluster)
3184 self._ConfigData().cluster = target
3185 elif isinstance(target, objects.Node):
3186 replace_in(target, self._ConfigData().nodes)
3187 update_serial = True
3188 elif isinstance(target, objects.Instance):
3189 replace_in(target, self._ConfigData().instances)
3190 elif isinstance(target, objects.NodeGroup):
3191 replace_in(target, self._ConfigData().nodegroups)
3192 elif isinstance(target, objects.Network):
3193 replace_in(target, self._ConfigData().networks)
3194 elif isinstance(target, objects.Disk):
3195 replace_in(target, self._ConfigData().disks)
3196 else:
3197 raise errors.ProgrammerError("Invalid object type (%s) passed to"
3198 " ConfigWriter.Update" % type(target))
3199 target.serial_no += 1
3200 target.mtime = now = time.time()
3201
3202 if update_serial:
3203
3204 self._ConfigData().cluster.serial_no += 1
3205 self._ConfigData().cluster.mtime = now
3206
3207 if isinstance(target, objects.Instance):
3208 self._UnlockedReleaseDRBDMinors(target.uuid)
3209
3210 if ec_id is not None:
3211
3212
3213
3214 self._UnlockedCommitTemporaryIps(ec_id)
3215
3216
3217
3218 self._UnlockedVerifyConfigAndLog(feedback_fn=feedback_fn)
3219
3230
3233
3234 @_ConfigSync(shared=1)
3236 """Get configuration info of all the networks.
3237
3238 """
3239 return dict(self._ConfigData().networks)
3240
3242 """Get the list of networks.
3243
3244 This function is for internal use, when the config lock is already held.
3245
3246 """
3247 return self._ConfigData().networks.keys()
3248
3249 @_ConfigSync(shared=1)
3251 """Get the list of networks.
3252
3253 @return: array of networks, ex. ["main", "vlan100", "200]
3254
3255 """
3256 return self._UnlockedGetNetworkList()
3257
3258 @_ConfigSync(shared=1)
3260 """Get a list of network names
3261
3262 """
3263 names = [net.name
3264 for net in self._ConfigData().networks.values()]
3265 return names
3266
3268 """Returns information about a network.
3269
3270 This function is for internal use, when the config lock is already held.
3271
3272 """
3273 if uuid not in self._ConfigData().networks:
3274 return None
3275
3276 return self._ConfigData().networks[uuid]
3277
3278 @_ConfigSync(shared=1)
3280 """Returns information about a network.
3281
3282 It takes the information from the configuration file.
3283
3284 @param uuid: UUID of the network
3285
3286 @rtype: L{objects.Network}
3287 @return: the network object
3288
3289 """
3290 return self._UnlockedGetNetwork(uuid)
3291
3292 @_ConfigSync()
3293 - def AddNetwork(self, net, ec_id, check_uuid=True):
3294 """Add a network to the configuration.
3295
3296 @type net: L{objects.Network}
3297 @param net: the Network object to add
3298 @type ec_id: string
3299 @param ec_id: unique id for the job to use when creating a missing UUID
3300
3301 """
3302 self._UnlockedAddNetwork(net, ec_id, check_uuid)
3303
3305 """Add a network to the configuration.
3306
3307 """
3308 logging.info("Adding network %s to configuration", net.name)
3309
3310 if check_uuid:
3311 self._EnsureUUID(net, ec_id)
3312
3313 net.serial_no = 1
3314 net.ctime = net.mtime = time.time()
3315 self._ConfigData().networks[net.uuid] = net
3316 self._ConfigData().cluster.serial_no += 1
3317
3319 """Lookup a network's UUID.
3320
3321 @type target: string
3322 @param target: network name or UUID
3323 @rtype: string
3324 @return: network UUID
3325 @raises errors.OpPrereqError: when the target network cannot be found
3326
3327 """
3328 if target is None:
3329 return None
3330 if target in self._ConfigData().networks:
3331 return target
3332 for net in self._ConfigData().networks.values():
3333 if net.name == target:
3334 return net.uuid
3335 raise errors.OpPrereqError("Network '%s' not found" % target,
3336 errors.ECODE_NOENT)
3337
3338 @_ConfigSync(shared=1)
3340 """Lookup a network's UUID.
3341
3342 This function is just a wrapper over L{_UnlockedLookupNetwork}.
3343
3344 @type target: string
3345 @param target: network name or UUID
3346 @rtype: string
3347 @return: network UUID
3348
3349 """
3350 return self._UnlockedLookupNetwork(target)
3351
3352 @_ConfigSync()
3354 """Remove a network from the configuration.
3355
3356 @type network_uuid: string
3357 @param network_uuid: the UUID of the network to remove
3358
3359 """
3360 logging.info("Removing network %s from configuration", network_uuid)
3361
3362 if network_uuid not in self._ConfigData().networks:
3363 raise errors.ConfigurationError("Unknown network '%s'" % network_uuid)
3364
3365 del self._ConfigData().networks[network_uuid]
3366 self._ConfigData().cluster.serial_no += 1
3367
3369 """Get the netparams (mode, link) of a network.
3370
3371 Get a network's netparams for a given node.
3372
3373 @type net_uuid: string
3374 @param net_uuid: network uuid
3375 @type node_uuid: string
3376 @param node_uuid: node UUID
3377 @rtype: dict or None
3378 @return: netparams
3379
3380 """
3381 node_info = self._UnlockedGetNodeInfo(node_uuid)
3382 nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
3383 netparams = nodegroup_info.networks.get(net_uuid, None)
3384
3385 return netparams
3386
3387 @_ConfigSync(shared=1)
3389 """Locking wrapper of _UnlockedGetGroupNetParams()
3390
3391 """
3392 return self._UnlockedGetGroupNetParams(net_uuid, node_uuid)
3393
3394 @_ConfigSync(shared=1)
3396 """Check IP uniqueness in nodegroup.
3397
3398 Check networks that are connected in the node's node group
3399 if ip is contained in any of them. Used when creating/adding
3400 a NIC to ensure uniqueness among nodegroups.
3401
3402 @type ip: string
3403 @param ip: ip address
3404 @type node_uuid: string
3405 @param node_uuid: node UUID
3406 @rtype: (string, dict) or (None, None)
3407 @return: (network name, netparams)
3408
3409 """
3410 if ip is None:
3411 return (None, None)
3412 node_info = self._UnlockedGetNodeInfo(node_uuid)
3413 nodegroup_info = self._UnlockedGetNodeGroup(node_info.group)
3414 for net_uuid in nodegroup_info.networks.keys():
3415 net_info = self._UnlockedGetNetwork(net_uuid)
3416 pool = network.AddressPool(net_info)
3417 if pool.Contains(ip):
3418 return (net_info.name, nodegroup_info.networks[net_uuid])
3419
3420 return (None, None)
3421
3422 @_ConfigSync(shared=1)
3424 """Returns the candidate certificate map.
3425
3426 """
3427 return self._ConfigData().cluster.candidate_certs
3428
3429 @_ConfigSync()
3431 """Replaces the master candidate cert list with the new values.
3432
3433 @type certs: dict of string to string
3434 @param certs: map of node UUIDs to SSL client certificate digests.
3435
3436 """
3437 self._ConfigData().cluster.candidate_certs = certs
3438
3439 @_ConfigSync()
3442 """Adds an entry to the candidate certificate map.
3443
3444 @type node_uuid: string
3445 @param node_uuid: the node's UUID
3446 @type cert_digest: string
3447 @param cert_digest: the digest of the node's client SSL certificate
3448 @type info_fn: function
3449 @param info_fn: logging function for information messages
3450 @type warn_fn: function
3451 @param warn_fn: logging function for warning messages
3452
3453 """
3454 cluster = self._ConfigData().cluster
3455 if node_uuid in cluster.candidate_certs:
3456 old_cert_digest = cluster.candidate_certs[node_uuid]
3457 if old_cert_digest == cert_digest:
3458 if info_fn is not None:
3459 info_fn("Certificate digest for node %s already in config."
3460 "Not doing anything." % node_uuid)
3461 return
3462 else:
3463 if warn_fn is not None:
3464 warn_fn("Overriding differing certificate digest for node %s"
3465 % node_uuid)
3466 cluster.candidate_certs[node_uuid] = cert_digest
3467
3468 @_ConfigSync()
3471 """Removes the entry of the given node in the certificate map.
3472
3473 @type node_uuid: string
3474 @param node_uuid: the node's UUID
3475 @type warn_fn: function
3476 @param warn_fn: logging function for warning messages
3477
3478 """
3479 cluster = self._ConfigData().cluster
3480 if node_uuid not in cluster.candidate_certs:
3481 if warn_fn is not None:
3482 warn_fn("Cannot remove certifcate for node %s, because it's not"
3483 " in the candidate map." % node_uuid)
3484 return
3485 del cluster.candidate_certs[node_uuid]
3486
3488 """Force the distribution of configuration to master candidates.
3489
3490 It is not necessary to hold a lock for this operation, it is handled
3491 internally by WConfd.
3492
3493 """
3494 if not self._offline:
3495 self._wconfd.FlushConfig()
3496
3502
3503 @staticmethod
3507
3511
3515