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 errors
43 from ganeti import constants
44 from ganeti import utils
45 from ganeti import netutils
46 from ganeti import pathutils
47
48
49 SSCONF_LOCK_TIMEOUT = 10
50
51
52 _VALID_KEYS = constants.VALID_SS_KEYS
53
54
55 _MAX_SIZE = 128 * 1024
56
57
59 """Reads an ssconf file and verifies its size.
60
61 @type filename: string
62 @param filename: Path to file
63 @rtype: string
64 @return: File contents without newlines at the end
65 @raise RuntimeError: When the file size exceeds L{_MAX_SIZE}
66
67 """
68 statcb = utils.FileStatHelper()
69
70 data = utils.ReadFile(filename, size=_MAX_SIZE, preread=statcb)
71
72 if statcb.st.st_size > _MAX_SIZE:
73 msg = ("File '%s' has a size of %s bytes (up to %s allowed)" %
74 (filename, statcb.st.st_size, _MAX_SIZE))
75 raise RuntimeError(msg)
76
77 return data.rstrip("\n")
78
79
81 """Interface to static cluster data.
82
83 This is different that the config.ConfigWriter and
84 SimpleConfigReader classes in that it holds data that will always be
85 present, even on nodes which don't have all the cluster data.
86
87 Other particularities of the datastore:
88 - keys are restricted to predefined values
89
90 """
92 if cfg_location is None:
93 self._cfg_dir = pathutils.DATA_DIR
94 else:
95 self._cfg_dir = cfg_location
96
97 self._lockfile = _lockfile
98
109
111 """Generic routine to read keys.
112
113 This will read the file which holds the value requested. Errors
114 will be changed into ConfigurationErrors.
115
116 """
117 filename = self.KeyToFilename(key)
118 try:
119 return ReadSsconfFile(filename)
120 except EnvironmentError, err:
121 if err.errno == errno.ENOENT and default is not None:
122 return default
123 raise errors.ConfigurationError("Can't read ssconf file %s: %s" %
124 (filename, str(err)))
125
127 """Reads all keys and returns their values.
128
129 @rtype: dict
130 @return: Dictionary, ssconf key as key, value as value
131
132 """
133 result = []
134
135 for key in _VALID_KEYS:
136 try:
137 value = self._ReadFile(key)
138 except errors.ConfigurationError:
139
140 pass
141 else:
142 result.append((key, value))
143
144 return dict(result)
145
147 """Writes ssconf files used by external scripts.
148
149 @type values: dict
150 @param values: Dictionary of (name, value)
151 @type dry_run boolean
152 @param dry_run: Whether to perform a dry run
153
154 """
155 ssconf_lock = utils.FileLock.Open(self._lockfile)
156
157
158 ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
159 try:
160 for name, value in values.iteritems():
161 if isinstance(value, (list, tuple)):
162 value = "\n".join(value)
163 if value and not value.endswith("\n"):
164 value += "\n"
165
166 if len(value) > _MAX_SIZE:
167 msg = ("Value '%s' has a length of %s bytes, but only up to %s are"
168 " allowed" % (name, len(value), _MAX_SIZE))
169 raise errors.ConfigurationError(msg)
170
171 utils.WriteFile(self.KeyToFilename(name), data=value,
172 mode=constants.SS_FILE_PERMS,
173 dry_run=dry_run)
174 finally:
175 ssconf_lock.Unlock()
176
178 """Return the list of all config files.
179
180 This is used for computing node replication data.
181
182 """
183 return [self.KeyToFilename(key) for key in _VALID_KEYS]
184
190
196
202
208
216
224
226 """Reads a file with lines like key=value and returns a dict.
227
228 This utility function reads a file containing ssconf values of
229 the form "key=value", splits the lines at "=" and returns a
230 dictionary mapping the keys to the values.
231
232 @type ss_file_key: string
233 @param ss_file_key: the constant referring to an ssconf file
234 @rtype: dict of string to string
235 @return: a dictionary mapping the keys to the values
236
237 """
238 data = self._ReadFile(ss_file_key)
239 lines = data.splitlines(False)
240 mapping = {}
241 for line in lines:
242 (key, value) = line.split("=")
243 mapping[key] = value
244 return mapping
245
247 """Returns the map of master candidate UUIDs to ssl cert.
248
249 @rtype: dict of string to string
250 @return: dictionary mapping the master candidates' UUIDs
251 to their SSL certificate digests
252
253 """
254 return self._GetDictOfSsconfMap(constants.SS_MASTER_CANDIDATES_CERTS)
255
257 """Returns the map of node names to SSH port.
258
259 @rtype: dict of string to string
260 @return: dictionary mapping the node names to their SSH port
261
262 """
263 return dict([(node_name, int(ssh_port)) for
264 node_name, ssh_port in
265 self._GetDictOfSsconfMap(constants.SS_SSH_PORTS).items()])
266
272
278
289
295
303
311
319
327
329 """Return the cluster nodes' vm capable value.
330
331 @rtype: dict of string to bool
332 @return: mapping of node names to vm capable values
333
334 """
335 data = self._ReadFile(constants.SS_NODE_VM_CAPABLE)
336 vm_capable = {}
337 for line in data.splitlines(False):
338 (node_uuid, node_vm_capable) = line.split("=")
339 vm_capable[node_uuid] = node_vm_capable == "True"
340 return vm_capable
341
349
357
365
373
375 """Return the hypervisor parameters of the given hypervisor.
376
377 @type hvname: string
378 @param hvname: name of the hypervisor, must be in C{constants.HYPER_TYPES}
379 @rtype: dict of strings
380 @returns: dictionary with hypervisor parameters
381
382 """
383 return self._GetDictOfSsconfMap(constants.SS_HVPARAMS_PREF + hvname)
384
386 """Return the hypervisor parameters of all hypervisors.
387
388 @rtype: dict of dict of strings
389 @returns: dictionary mapping hypervisor names to hvparams
390
391 """
392 all_hvparams = {}
393 for hv in constants.HYPER_TYPES:
394 all_hvparams[hv] = self.GetHvparamsForHypervisor(hv)
395 return all_hvparams
396
398 """Return the value of the maintain_node_health option.
399
400 """
401 data = self._ReadFile(constants.SS_MAINTAIN_NODE_HEALTH)
402
403 return data == "True"
404
406 """Return the user-id pool definition string.
407
408 The separator character is a newline.
409
410 The return value can be parsed using uidpool.ParseUidPool()::
411
412 ss = ssconf.SimpleStore()
413 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\\n")
414
415 """
416 data = self._ReadFile(constants.SS_UID_POOL)
417 return data
418
429
431 """Return whether user shutdown is enabled.
432
433 @rtype: bool
434 @return: 'True' if user shutdown is enabled, 'False' otherwise
435
436 """
437 return self._ReadFile(constants.SS_ENABLED_USER_SHUTDOWN) == "True"
438
439
441 """Update all ssconf files.
442
443 Wrapper around L{SimpleStore.WriteFiles}.
444
445 """
446 SimpleStore().WriteFiles(values, dry_run=dry_run)
447
448
450 """Get the master node and my own hostname.
451
452 This can be either used for a 'soft' check (compared to CheckMaster,
453 which exits) or just for computing both at the same time.
454
455 The function does not handle any errors, these should be handled in
456 the caller (errors.ConfigurationError, errors.ResolverError).
457
458 @param ss: either a sstore.SimpleConfigReader or a
459 sstore.SimpleStore instance
460 @rtype: tuple
461 @return: a tuple (master node name, my own name)
462
463 """
464 if ss is None:
465 ss = SimpleStore()
466 return ss.GetMasterNode(), netutils.Hostname.GetSysName()
467
468
489
490
492 """Verifies cluster name against a local cluster name.
493
494 @type name: string
495 @param name: Cluster name
496
497 """
498 sstore = SimpleStore(cfg_location=_cfg_location)
499
500 try:
501 local_name = sstore.GetClusterName()
502 except errors.ConfigurationError, err:
503 logging.debug("Can't get local cluster name: %s", err)
504 else:
505 if name != local_name:
506 raise errors.GenericError("Current cluster name is '%s'" % local_name)
507
508
510 """Raises an exception if unknown ssconf keys are given.
511
512 @type keys: sequence
513 @param keys: Key names to verify
514 @raise errors.GenericError: When invalid keys were found
515
516 """
517 invalid = frozenset(keys) - _VALID_KEYS
518 if invalid:
519 raise errors.GenericError("Invalid ssconf keys: %s" %
520 utils.CommaJoin(sorted(invalid)))
521