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 """Global Configuration data for Ganeti.
32
33 This module provides the interface to a special case of cluster
34 configuration data, which is mostly static and available to all nodes.
35
36 """
37
38 import sys
39 import errno
40 import logging
41
42 from ganeti import compat
43 from ganeti import errors
44 from ganeti import constants
45 from ganeti import utils
46 from ganeti import netutils
47 from ganeti import pathutils
48
49
50 SSCONF_LOCK_TIMEOUT = 10
51
52
53 _VALID_KEYS = compat.UniqueFrozenset([
54 constants.SS_CLUSTER_NAME,
55 constants.SS_CLUSTER_TAGS,
56 constants.SS_FILE_STORAGE_DIR,
57 constants.SS_SHARED_FILE_STORAGE_DIR,
58 constants.SS_GLUSTER_STORAGE_DIR,
59 constants.SS_MASTER_CANDIDATES,
60 constants.SS_MASTER_CANDIDATES_IPS,
61 constants.SS_MASTER_CANDIDATES_CERTS,
62 constants.SS_MASTER_IP,
63 constants.SS_MASTER_NETDEV,
64 constants.SS_MASTER_NETMASK,
65 constants.SS_MASTER_NODE,
66 constants.SS_NODE_LIST,
67 constants.SS_NODE_PRIMARY_IPS,
68 constants.SS_NODE_SECONDARY_IPS,
69 constants.SS_NODE_VM_CAPABLE,
70 constants.SS_OFFLINE_NODES,
71 constants.SS_ONLINE_NODES,
72 constants.SS_PRIMARY_IP_FAMILY,
73 constants.SS_INSTANCE_LIST,
74 constants.SS_RELEASE_VERSION,
75 constants.SS_HYPERVISOR_LIST,
76 constants.SS_MAINTAIN_NODE_HEALTH,
77 constants.SS_UID_POOL,
78 constants.SS_NODEGROUPS,
79 constants.SS_NETWORKS,
80 constants.SS_HVPARAMS_XEN_PVM,
81 constants.SS_HVPARAMS_XEN_FAKE,
82 constants.SS_HVPARAMS_XEN_HVM,
83 constants.SS_HVPARAMS_XEN_KVM,
84 constants.SS_HVPARAMS_XEN_CHROOT,
85 constants.SS_HVPARAMS_XEN_LXC,
86 constants.SS_ENABLED_USER_SHUTDOWN,
87 constants.SS_SSH_PORTS,
88 ])
89
90
91 _MAX_SIZE = 128 * 1024
92
93
95 """Reads an ssconf file and verifies its size.
96
97 @type filename: string
98 @param filename: Path to file
99 @rtype: string
100 @return: File contents without newlines at the end
101 @raise RuntimeError: When the file size exceeds L{_MAX_SIZE}
102
103 """
104 statcb = utils.FileStatHelper()
105
106 data = utils.ReadFile(filename, size=_MAX_SIZE, preread=statcb)
107
108 if statcb.st.st_size > _MAX_SIZE:
109 msg = ("File '%s' has a size of %s bytes (up to %s allowed)" %
110 (filename, statcb.st.st_size, _MAX_SIZE))
111 raise RuntimeError(msg)
112
113 return data.rstrip("\n")
114
115
117 """Interface to static cluster data.
118
119 This is different that the config.ConfigWriter and
120 SimpleConfigReader classes in that it holds data that will always be
121 present, even on nodes which don't have all the cluster data.
122
123 Other particularities of the datastore:
124 - keys are restricted to predefined values
125
126 """
128 if cfg_location is None:
129 self._cfg_dir = pathutils.DATA_DIR
130 else:
131 self._cfg_dir = cfg_location
132
133 self._lockfile = _lockfile
134
145
147 """Generic routine to read keys.
148
149 This will read the file which holds the value requested. Errors
150 will be changed into ConfigurationErrors.
151
152 """
153 filename = self.KeyToFilename(key)
154 try:
155 return ReadSsconfFile(filename)
156 except EnvironmentError, err:
157 if err.errno == errno.ENOENT and default is not None:
158 return default
159 raise errors.ConfigurationError("Can't read ssconf file %s: %s" %
160 (filename, str(err)))
161
163 """Reads all keys and returns their values.
164
165 @rtype: dict
166 @return: Dictionary, ssconf key as key, value as value
167
168 """
169 result = []
170
171 for key in _VALID_KEYS:
172 try:
173 value = self._ReadFile(key)
174 except errors.ConfigurationError:
175
176 pass
177 else:
178 result.append((key, value))
179
180 return dict(result)
181
183 """Writes ssconf files used by external scripts.
184
185 @type values: dict
186 @param values: Dictionary of (name, value)
187 @type dry_run boolean
188 @param dry_run: Whether to perform a dry run
189
190 """
191 ssconf_lock = utils.FileLock.Open(self._lockfile)
192
193
194 ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
195 try:
196 for name, value in values.iteritems():
197 if isinstance(value, (list, tuple)):
198 value = "\n".join(value)
199 if value and not value.endswith("\n"):
200 value += "\n"
201
202 if len(value) > _MAX_SIZE:
203 msg = ("Value '%s' has a length of %s bytes, but only up to %s are"
204 " allowed" % (name, len(value), _MAX_SIZE))
205 raise errors.ConfigurationError(msg)
206
207 utils.WriteFile(self.KeyToFilename(name), data=value,
208 mode=constants.SS_FILE_PERMS,
209 dry_run=dry_run)
210 finally:
211 ssconf_lock.Unlock()
212
214 """Return the list of all config files.
215
216 This is used for computing node replication data.
217
218 """
219 return [self.KeyToFilename(key) for key in _VALID_KEYS]
220
226
232
238
244
252
260
262 """Reads a file with lines like key=value and returns a dict.
263
264 This utility function reads a file containing ssconf values of
265 the form "key=value", splits the lines at "=" and returns a
266 dictionary mapping the keys to the values.
267
268 @type ss_file_key: string
269 @param ss_file_key: the constant referring to an ssconf file
270 @rtype: dict of string to string
271 @return: a dictionary mapping the keys to the values
272
273 """
274 data = self._ReadFile(ss_file_key)
275 lines = data.splitlines(False)
276 mapping = {}
277 for line in lines:
278 (key, value) = line.split("=")
279 mapping[key] = value
280 return mapping
281
283 """Returns the map of master candidate UUIDs to ssl cert.
284
285 @rtype: dict of string to string
286 @return: dictionary mapping the master candidates' UUIDs
287 to their SSL certificate digests
288
289 """
290 return self._GetDictOfSsconfMap(constants.SS_MASTER_CANDIDATES_CERTS)
291
293 """Returns the map of node names to SSH port.
294
295 @rtype: dict of string to string
296 @return: dictionary mapping the node names to their SSH port
297
298 """
299 return dict([(node_name, int(ssh_port)) for
300 node_name, ssh_port in
301 self._GetDictOfSsconfMap(constants.SS_SSH_PORTS).items()])
302
308
314
325
331
339
347
355
363
365 """Return the cluster nodes' vm capable value.
366
367 @rtype: dict of string to bool
368 @return: mapping of node names to vm capable values
369
370 """
371 data = self._ReadFile(constants.SS_NODE_VM_CAPABLE)
372 vm_capable = {}
373 for line in data.splitlines(False):
374 (node_uuid, node_vm_capable) = line.split("=")
375 vm_capable[node_uuid] = node_vm_capable == "True"
376 return vm_capable
377
385
393
401
409
411 """Return the hypervisor parameters of the given hypervisor.
412
413 @type hvname: string
414 @param hvname: name of the hypervisor, must be in C{constants.HYPER_TYPES}
415 @rtype: dict of strings
416 @returns: dictionary with hypervisor parameters
417
418 """
419 return self._GetDictOfSsconfMap(constants.SS_HVPARAMS_PREF + hvname)
420
422 """Return the hypervisor parameters of all hypervisors.
423
424 @rtype: dict of dict of strings
425 @returns: dictionary mapping hypervisor names to hvparams
426
427 """
428 all_hvparams = {}
429 for hv in constants.HYPER_TYPES:
430 all_hvparams[hv] = self.GetHvparamsForHypervisor(hv)
431 return all_hvparams
432
434 """Return the value of the maintain_node_health option.
435
436 """
437 data = self._ReadFile(constants.SS_MAINTAIN_NODE_HEALTH)
438
439 return data == "True"
440
442 """Return the user-id pool definition string.
443
444 The separator character is a newline.
445
446 The return value can be parsed using uidpool.ParseUidPool()::
447
448 ss = ssconf.SimpleStore()
449 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\\n")
450
451 """
452 data = self._ReadFile(constants.SS_UID_POOL)
453 return data
454
465
467 """Return whether user shutdown is enabled.
468
469 @rtype: bool
470 @return: 'True' if user shutdown is enabled, 'False' otherwise
471
472 """
473 return self._ReadFile(constants.SS_ENABLED_USER_SHUTDOWN) == "True"
474
475
477 """Update all ssconf files.
478
479 Wrapper around L{SimpleStore.WriteFiles}.
480
481 """
482 SimpleStore().WriteFiles(values, dry_run=dry_run)
483
484
486 """Get the master node and my own hostname.
487
488 This can be either used for a 'soft' check (compared to CheckMaster,
489 which exits) or just for computing both at the same time.
490
491 The function does not handle any errors, these should be handled in
492 the caller (errors.ConfigurationError, errors.ResolverError).
493
494 @param ss: either a sstore.SimpleConfigReader or a
495 sstore.SimpleStore instance
496 @rtype: tuple
497 @return: a tuple (master node name, my own name)
498
499 """
500 if ss is None:
501 ss = SimpleStore()
502 return ss.GetMasterNode(), netutils.Hostname.GetSysName()
503
504
525
526
528 """Verifies cluster name against a local cluster name.
529
530 @type name: string
531 @param name: Cluster name
532
533 """
534 sstore = SimpleStore(cfg_location=_cfg_location)
535
536 try:
537 local_name = sstore.GetClusterName()
538 except errors.ConfigurationError, err:
539 logging.debug("Can't get local cluster name: %s", err)
540 else:
541 if name != local_name:
542 raise errors.GenericError("Current cluster name is '%s'" % local_name)
543
544
546 """Raises an exception if unknown ssconf keys are given.
547
548 @type keys: sequence
549 @param keys: Key names to verify
550 @raise errors.GenericError: When invalid keys were found
551
552 """
553 invalid = frozenset(keys) - _VALID_KEYS
554 if invalid:
555 raise errors.GenericError("Invalid ssconf keys: %s" %
556 utils.CommaJoin(sorted(invalid)))
557