4 # Copyright (C) 2006, 2007, 2008, 2010, 2011, 2012 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Global Configuration data for Ganeti.
24 This module provides the interface to a special case of cluster
25 configuration data, which is mostly static and available to all nodes.
33 from ganeti import compat
34 from ganeti import errors
35 from ganeti import constants
36 from ganeti import utils
37 from ganeti import netutils
38 from ganeti import pathutils
41 SSCONF_LOCK_TIMEOUT = 10
44 _VALID_KEYS = compat.UniqueFrozenset([
45 constants.SS_CLUSTER_NAME,
46 constants.SS_CLUSTER_TAGS,
47 constants.SS_FILE_STORAGE_DIR,
48 constants.SS_SHARED_FILE_STORAGE_DIR,
49 constants.SS_MASTER_CANDIDATES,
50 constants.SS_MASTER_CANDIDATES_IPS,
51 constants.SS_MASTER_IP,
52 constants.SS_MASTER_NETDEV,
53 constants.SS_MASTER_NETMASK,
54 constants.SS_MASTER_NODE,
55 constants.SS_NODE_LIST,
56 constants.SS_NODE_PRIMARY_IPS,
57 constants.SS_NODE_SECONDARY_IPS,
58 constants.SS_OFFLINE_NODES,
59 constants.SS_ONLINE_NODES,
60 constants.SS_PRIMARY_IP_FAMILY,
61 constants.SS_INSTANCE_LIST,
62 constants.SS_RELEASE_VERSION,
63 constants.SS_HYPERVISOR_LIST,
64 constants.SS_MAINTAIN_NODE_HEALTH,
65 constants.SS_UID_POOL,
66 constants.SS_NODEGROUPS,
67 constants.SS_NETWORKS,
68 constants.SS_HVPARAMS_XEN_PVM,
69 constants.SS_HVPARAMS_XEN_FAKE,
70 constants.SS_HVPARAMS_XEN_HVM,
71 constants.SS_HVPARAMS_XEN_KVM,
72 constants.SS_HVPARAMS_XEN_CHROOT,
73 constants.SS_HVPARAMS_XEN_LXC,
76 #: Maximum size for ssconf files
77 _MAX_SIZE = 128 * 1024
80 def ReadSsconfFile(filename):
81 """Reads an ssconf file and verifies its size.
83 @type filename: string
84 @param filename: Path to file
86 @return: File contents without newlines at the end
87 @raise RuntimeError: When the file size exceeds L{_MAX_SIZE}
90 statcb = utils.FileStatHelper()
92 data = utils.ReadFile(filename, size=_MAX_SIZE, preread=statcb)
94 if statcb.st.st_size > _MAX_SIZE:
95 msg = ("File '%s' has a size of %s bytes (up to %s allowed)" %
96 (filename, statcb.st.st_size, _MAX_SIZE))
97 raise RuntimeError(msg)
99 return data.rstrip("\n")
102 class SimpleStore(object):
103 """Interface to static cluster data.
105 This is different that the config.ConfigWriter and
106 SimpleConfigReader classes in that it holds data that will always be
107 present, even on nodes which don't have all the cluster data.
109 Other particularities of the datastore:
110 - keys are restricted to predefined values
113 def __init__(self, cfg_location=None, _lockfile=pathutils.SSCONF_LOCK_FILE):
114 if cfg_location is None:
115 self._cfg_dir = pathutils.DATA_DIR
117 self._cfg_dir = cfg_location
119 self._lockfile = _lockfile
121 def KeyToFilename(self, key):
122 """Convert a given key into filename.
125 if key not in _VALID_KEYS:
126 raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'"
129 filename = self._cfg_dir + "/" + constants.SSCONF_FILEPREFIX + key
132 def _ReadFile(self, key, default=None):
133 """Generic routine to read keys.
135 This will read the file which holds the value requested. Errors
136 will be changed into ConfigurationErrors.
139 filename = self.KeyToFilename(key)
141 return ReadSsconfFile(filename)
142 except EnvironmentError, err:
143 if err.errno == errno.ENOENT and default is not None:
145 raise errors.ConfigurationError("Can't read ssconf file %s: %s" %
146 (filename, str(err)))
149 """Reads all keys and returns their values.
152 @return: Dictionary, ssconf key as key, value as value
157 for key in _VALID_KEYS:
159 value = self._ReadFile(key)
160 except errors.ConfigurationError:
161 # Ignore non-existing files
164 result.append((key, value))
168 def WriteFiles(self, values, dry_run=False):
169 """Writes ssconf files used by external scripts.
172 @param values: Dictionary of (name, value)
173 @type dry_run boolean
174 @param dry_run: Whether to perform a dry run
177 ssconf_lock = utils.FileLock.Open(self._lockfile)
179 # Get lock while writing files
180 ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
182 for name, value in values.iteritems():
183 if value and not value.endswith("\n"):
186 if len(value) > _MAX_SIZE:
187 msg = ("Value '%s' has a length of %s bytes, but only up to %s are"
188 " allowed" % (name, len(value), _MAX_SIZE))
189 raise errors.ConfigurationError(msg)
191 utils.WriteFile(self.KeyToFilename(name), data=value,
192 mode=constants.SS_FILE_PERMS,
197 def GetFileList(self):
198 """Return the list of all config files.
200 This is used for computing node replication data.
203 return [self.KeyToFilename(key) for key in _VALID_KEYS]
205 def GetClusterName(self):
206 """Get the cluster name.
209 return self._ReadFile(constants.SS_CLUSTER_NAME)
211 def GetFileStorageDir(self):
212 """Get the file storage dir.
215 return self._ReadFile(constants.SS_FILE_STORAGE_DIR)
217 def GetSharedFileStorageDir(self):
218 """Get the shared file storage dir.
221 return self._ReadFile(constants.SS_SHARED_FILE_STORAGE_DIR)
223 def GetMasterCandidates(self):
224 """Return the list of master candidates.
227 data = self._ReadFile(constants.SS_MASTER_CANDIDATES)
228 nl = data.splitlines(False)
231 def GetMasterCandidatesIPList(self):
232 """Return the list of master candidates' primary IP.
235 data = self._ReadFile(constants.SS_MASTER_CANDIDATES_IPS)
236 nl = data.splitlines(False)
239 def GetMasterIP(self):
240 """Get the IP of the master node for this cluster.
243 return self._ReadFile(constants.SS_MASTER_IP)
245 def GetMasterNetdev(self):
246 """Get the netdev to which we'll add the master ip.
249 return self._ReadFile(constants.SS_MASTER_NETDEV)
251 def GetMasterNetmask(self):
252 """Get the master netmask.
256 return self._ReadFile(constants.SS_MASTER_NETMASK)
257 except errors.ConfigurationError:
258 family = self.GetPrimaryIPFamily()
259 ipcls = netutils.IPAddress.GetClassFromIpFamily(family)
262 def GetMasterNode(self):
263 """Get the hostname of the master node for this cluster.
266 return self._ReadFile(constants.SS_MASTER_NODE)
268 def GetNodeList(self):
269 """Return the list of cluster nodes.
272 data = self._ReadFile(constants.SS_NODE_LIST)
273 nl = data.splitlines(False)
276 def GetNodePrimaryIPList(self):
277 """Return the list of cluster nodes' primary IP.
280 data = self._ReadFile(constants.SS_NODE_PRIMARY_IPS)
281 nl = data.splitlines(False)
284 def GetNodeSecondaryIPList(self):
285 """Return the list of cluster nodes' secondary IP.
288 data = self._ReadFile(constants.SS_NODE_SECONDARY_IPS)
289 nl = data.splitlines(False)
292 def GetNodegroupList(self):
293 """Return the list of nodegroups.
296 data = self._ReadFile(constants.SS_NODEGROUPS)
297 nl = data.splitlines(False)
300 def GetNetworkList(self):
301 """Return the list of networks.
304 data = self._ReadFile(constants.SS_NETWORKS)
305 nl = data.splitlines(False)
308 def GetClusterTags(self):
309 """Return the cluster tags.
312 data = self._ReadFile(constants.SS_CLUSTER_TAGS)
313 nl = data.splitlines(False)
316 def GetHypervisorList(self):
317 """Return the list of enabled hypervisors.
320 data = self._ReadFile(constants.SS_HYPERVISOR_LIST)
321 nl = data.splitlines(False)
324 def GetHvparamsForHypervisor(self, hvname):
325 """Return the hypervisor parameters of the given hypervisor.
328 @param hvname: name of the hypervisor, must be in C{constants.HYPER_TYPES}
329 @rtype: dict of strings
330 @returns: dictionary with hypervisor parameters
333 data = self._ReadFile(constants.SS_HVPARAMS_PREF + hvname)
334 lines = data.splitlines(False)
337 (key, value) = line.split("=")
338 hvparams[key] = value
341 def GetHvparams(self):
342 """Return the hypervisor parameters of all hypervisors.
344 @rtype: dict of dict of strings
345 @returns: dictionary mapping hypervisor names to hvparams
349 for hv in constants.HYPER_TYPES:
350 all_hvparams[hv] = self.GetHvparamsForHypervisor(hv)
353 def GetMaintainNodeHealth(self):
354 """Return the value of the maintain_node_health option.
357 data = self._ReadFile(constants.SS_MAINTAIN_NODE_HEALTH)
358 # we rely on the bool serialization here
359 return data == "True"
361 def GetUidPool(self):
362 """Return the user-id pool definition string.
364 The separator character is a newline.
366 The return value can be parsed using uidpool.ParseUidPool()::
368 ss = ssconf.SimpleStore()
369 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\\n")
372 data = self._ReadFile(constants.SS_UID_POOL)
375 def GetPrimaryIPFamily(self):
376 """Return the cluster-wide primary address family.
380 return int(self._ReadFile(constants.SS_PRIMARY_IP_FAMILY,
381 default=netutils.IP4Address.family))
382 except (ValueError, TypeError), err:
383 raise errors.ConfigurationError("Error while trying to parse primary IP"
387 def WriteSsconfFiles(values, dry_run=False):
388 """Update all ssconf files.
390 Wrapper around L{SimpleStore.WriteFiles}.
393 SimpleStore().WriteFiles(values, dry_run=dry_run)
396 def GetMasterAndMyself(ss=None):
397 """Get the master node and my own hostname.
399 This can be either used for a 'soft' check (compared to CheckMaster,
400 which exits) or just for computing both at the same time.
402 The function does not handle any errors, these should be handled in
403 the caller (errors.ConfigurationError, errors.ResolverError).
405 @param ss: either a sstore.SimpleConfigReader or a
406 sstore.SimpleStore instance
408 @return: a tuple (master node name, my own name)
413 return ss.GetMasterNode(), netutils.Hostname.GetSysName()
416 def CheckMaster(debug, ss=None):
417 """Checks the node setup.
419 If this is the master, the function will return. Otherwise it will
420 exit with an exit code based on the node status.
424 master_name, myself = GetMasterAndMyself(ss)
425 except errors.ConfigurationError, err:
426 print "Cluster configuration incomplete: '%s'" % str(err)
427 sys.exit(constants.EXIT_NODESETUP_ERROR)
428 except errors.ResolverError, err:
429 sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0])
430 sys.exit(constants.EXIT_NODESETUP_ERROR)
432 if myself != master_name:
434 sys.stderr.write("Not master, exiting.\n")
435 sys.exit(constants.EXIT_NOTMASTER)
438 def VerifyClusterName(name, _cfg_location=None):
439 """Verifies cluster name against a local cluster name.
442 @param name: Cluster name
445 sstore = SimpleStore(cfg_location=_cfg_location)
448 local_name = sstore.GetClusterName()
449 except errors.ConfigurationError, err:
450 logging.debug("Can't get local cluster name: %s", err)
452 if name != local_name:
453 raise errors.GenericError("Current cluster name is '%s'" % local_name)
456 def VerifyKeys(keys):
457 """Raises an exception if unknown ssconf keys are given.
460 @param keys: Key names to verify
461 @raise errors.GenericError: When invalid keys were found
464 invalid = frozenset(keys) - _VALID_KEYS
466 raise errors.GenericError("Invalid ssconf keys: %s" %
467 utils.CommaJoin(sorted(invalid)))