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_ONLINE_NODES,
137 constants.SS_INSTANCE_LIST,
138 constants.SS_RELEASE_VERSION,
142 def __init__(self, cfg_location=None):
143 if cfg_location is None:
144 self._cfg_dir = constants.DATA_DIR
146 self._cfg_dir = cfg_location
148 def KeyToFilename(self, key):
149 """Convert a given key into filename.
152 if key not in self._VALID_KEYS:
153 raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'"
156 filename = self._cfg_dir + '/' + self._SS_FILEPREFIX + key
159 def _ReadFile(self, key):
160 """Generic routine to read keys.
162 This will read the file which holds the value requested. Errors
163 will be changed into ConfigurationErrors.
166 filename = self.KeyToFilename(key)
168 fh = file(filename, 'r')
170 data = fh.read(self._MAX_SIZE)
171 data = data.rstrip('\n')
174 except EnvironmentError, err:
175 raise errors.ConfigurationError("Can't read from the ssconf file:"
179 def WriteFiles(self, values):
180 """Writes ssconf files used by external scripts.
183 @param values: Dictionary of (name, value)
186 ssconf_lock = utils.FileLock(constants.SSCONF_LOCK_FILE)
188 # Get lock while writing files
189 ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
191 for name, value in values.iteritems():
192 if value and not value.endswith("\n"):
194 utils.WriteFile(self.KeyToFilename(name), data=value, mode=0444)
198 def GetFileList(self):
199 """Return the list of all config files.
201 This is used for computing node replication data.
204 return [self.KeyToFilename(key) for key in self._VALID_KEYS]
206 def GetClusterName(self):
207 """Get the cluster name.
210 return self._ReadFile(constants.SS_CLUSTER_NAME)
212 def GetFileStorageDir(self):
213 """Get the file storage dir.
216 return self._ReadFile(constants.SS_FILE_STORAGE_DIR)
218 def GetMasterCandidates(self):
219 """Return the list of master candidates.
222 data = self._ReadFile(constants.SS_MASTER_CANDIDATES)
223 nl = data.splitlines(False)
226 def GetMasterIP(self):
227 """Get the IP of the master node for this cluster.
230 return self._ReadFile(constants.SS_MASTER_IP)
232 def GetMasterNetdev(self):
233 """Get the netdev to which we'll add the master ip.
236 return self._ReadFile(constants.SS_MASTER_NETDEV)
238 def GetMasterNode(self):
239 """Get the hostname of the master node for this cluster.
242 return self._ReadFile(constants.SS_MASTER_NODE)
244 def GetNodeList(self):
245 """Return the list of cluster nodes.
248 data = self._ReadFile(constants.SS_NODE_LIST)
249 nl = data.splitlines(False)
253 def GetMasterAndMyself(ss=None):
254 """Get the master node and my own hostname.
256 This can be either used for a 'soft' check (compared to CheckMaster,
257 which exits) or just for computing both at the same time.
259 The function does not handle any errors, these should be handled in
260 the caller (errors.ConfigurationError, errors.ResolverError).
262 @param ss: either a sstore.SimpleConfigReader or a
263 sstore.SimpleStore instance
265 @return: a tuple (master node name, my own name)
270 return ss.GetMasterNode(), utils.HostInfo().name
273 def CheckMaster(debug, ss=None):
274 """Checks the node setup.
276 If this is the master, the function will return. Otherwise it will
277 exit with an exit code based on the node status.
281 master_name, myself = GetMasterAndMyself(ss)
282 except errors.ConfigurationError, err:
283 print "Cluster configuration incomplete: '%s'" % str(err)
284 sys.exit(constants.EXIT_NODESETUP_ERROR)
285 except errors.ResolverError, err:
286 sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0])
287 sys.exit(constants.EXIT_NODESETUP_ERROR)
289 if myself != master_name:
291 sys.stderr.write("Not master, exiting.\n")
292 sys.exit(constants.EXIT_NOTMASTER)