4 # Copyright (C) 2006, 2007, 2008 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.
32 from ganeti import errors
33 from ganeti import constants
34 from ganeti import utils
35 from ganeti import serializer
38 SSCONF_LOCK_TIMEOUT = 10
40 RE_VALID_SSCONF_NAME = re.compile(r'^[-_a-z0-9]+$')
43 class SimpleConfigReader(object):
44 """Simple class to read configuration file.
47 def __init__(self, file_name=constants.CLUSTER_CONF_FILE):
48 """Initializes this class.
50 @type file_name: string
51 @param file_name: Configuration file path
54 self._file_name = file_name
55 self._config_data = serializer.Load(utils.ReadFile(file_name))
56 # TODO: Error handling
58 def GetClusterName(self):
59 return self._config_data["cluster"]["cluster_name"]
62 return self._config_data["cluster"]["rsahostkeypub"]
64 def GetMasterNode(self):
65 return self._config_data["cluster"]["master_node"]
67 def GetMasterIP(self):
68 return self._config_data["cluster"]["master_ip"]
70 def GetMasterNetdev(self):
71 return self._config_data["cluster"]["master_netdev"]
73 def GetFileStorageDir(self):
74 return self._config_data["cluster"]["file_storage_dir"]
76 def GetHypervisorType(self):
77 return self._config_data["cluster"]["hypervisor"]
79 def GetNodeList(self):
80 return self._config_data["nodes"].keys()
83 def FromDict(cls, val, cfg_file=constants.CLUSTER_CONF_FILE):
84 """Alternative construction from a dictionary.
87 obj = SimpleConfigReader.__new__(cls)
88 obj._config_data = val
89 obj._file_name = cfg_file
93 class SimpleConfigWriter(SimpleConfigReader):
94 """Simple class to write configuration file.
97 def SetMasterNode(self, node):
98 """Change master node.
101 self._config_data["cluster"]["master_node"] = node
104 """Writes configuration file.
106 Warning: Doesn't take care of locking or synchronizing with other
110 utils.WriteFile(self._file_name,
111 data=serializer.Dump(self._config_data),
115 class SimpleStore(object):
116 """Interface to static cluster data.
118 This is different that the config.ConfigWriter and
119 SimpleConfigReader classes in that it holds data that will always be
120 present, even on nodes which don't have all the cluster data.
122 Other particularities of the datastore:
123 - keys are restricted to predefined values
126 _SS_FILEPREFIX = "ssconf_"
128 constants.SS_CLUSTER_NAME,
129 constants.SS_FILE_STORAGE_DIR,
130 constants.SS_MASTER_CANDIDATES,
131 constants.SS_MASTER_IP,
132 constants.SS_MASTER_NETDEV,
133 constants.SS_MASTER_NODE,
134 constants.SS_NODE_LIST,
135 constants.SS_OFFLINE_NODES,
136 constants.SS_RELEASE_VERSION,
140 def __init__(self, cfg_location=None):
141 if cfg_location is None:
142 self._cfg_dir = constants.DATA_DIR
144 self._cfg_dir = cfg_location
146 def KeyToFilename(self, key):
147 """Convert a given key into filename.
150 if key not in self._VALID_KEYS:
151 raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'"
154 filename = self._cfg_dir + '/' + self._SS_FILEPREFIX + key
157 def _ReadFile(self, key):
158 """Generic routine to read keys.
160 This will read the file which holds the value requested. Errors
161 will be changed into ConfigurationErrors.
164 filename = self.KeyToFilename(key)
166 fh = file(filename, 'r')
168 data = fh.read(self._MAX_SIZE)
169 data = data.rstrip('\n')
172 except EnvironmentError, err:
173 raise errors.ConfigurationError("Can't read from the ssconf file:"
177 def WriteFiles(self, values):
178 """Writes ssconf files used by external scripts.
181 @param values: Dictionary of (name, value)
184 ssconf_lock = utils.FileLock(constants.SSCONF_LOCK_FILE)
186 # Get lock while writing files
187 ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
189 for name, value in values.iteritems():
190 if value and not value.endswith("\n"):
192 utils.WriteFile(self.KeyToFilename(name), data=value)
196 def GetFileList(self):
197 """Return the list of all config files.
199 This is used for computing node replication data.
202 return [self.KeyToFilename(key) for key in self._VALID_KEYS]
204 def GetClusterName(self):
205 """Get the cluster name.
208 return self._ReadFile(constants.SS_CLUSTER_NAME)
210 def GetFileStorageDir(self):
211 """Get the file storage dir.
214 return self._ReadFile(constants.SS_FILE_STORAGE_DIR)
216 def GetMasterCandidates(self):
217 """Return the list of master candidates.
220 data = self._ReadFile(constants.SS_MASTER_CANDIDATES)
221 nl = data.splitlines(False)
224 def GetMasterIP(self):
225 """Get the IP of the master node for this cluster.
228 return self._ReadFile(constants.SS_MASTER_IP)
230 def GetMasterNetdev(self):
231 """Get the netdev to which we'll add the master ip.
234 return self._ReadFile(constants.SS_MASTER_NETDEV)
236 def GetMasterNode(self):
237 """Get the hostname of the master node for this cluster.
240 return self._ReadFile(constants.SS_MASTER_NODE)
242 def GetNodeList(self):
243 """Return the list of cluster nodes.
246 data = self._ReadFile(constants.SS_NODE_LIST)
247 nl = data.splitlines(False)
251 def GetMasterAndMyself(ss=None):
252 """Get the master node and my own hostname.
254 This can be either used for a 'soft' check (compared to CheckMaster,
255 which exits) or just for computing both at the same time.
257 The function does not handle any errors, these should be handled in
258 the caller (errors.ConfigurationError, errors.ResolverError).
260 @param ss: either a sstore.SimpleConfigReader or a
261 sstore.SimpleStore instance
263 @return: a tuple (master node name, my own name)
268 return ss.GetMasterNode(), utils.HostInfo().name
271 def CheckMaster(debug, ss=None):
272 """Checks the node setup.
274 If this is the master, the function will return. Otherwise it will
275 exit with an exit code based on the node status.
279 master_name, myself = GetMasterAndMyself(ss)
280 except errors.ConfigurationError, err:
281 print "Cluster configuration incomplete: '%s'" % str(err)
282 sys.exit(constants.EXIT_NODESETUP_ERROR)
283 except errors.ResolverError, err:
284 sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0])
285 sys.exit(constants.EXIT_NODESETUP_ERROR)
287 if myself != master_name:
289 sys.stderr.write("Not master, exiting.\n")
290 sys.exit(constants.EXIT_NOTMASTER)