Package ganeti :: Module ssconf
[hide private]
[frames] | no frames]

Source Code for Module ganeti.ssconf

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2008 Google Inc. 
  5  # 
  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. 
 10  # 
 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. 
 15  # 
 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 
 19  # 02110-1301, USA. 
 20   
 21   
 22  """Global Configuration data for Ganeti. 
 23   
 24  This module provides the interface to a special case of cluster 
 25  configuration data, which is mostly static and available to all nodes. 
 26   
 27  """ 
 28   
 29  import sys 
 30  import re 
 31   
 32  from ganeti import errors 
 33  from ganeti import constants 
 34  from ganeti import utils 
 35  from ganeti import serializer 
 36   
 37   
 38  SSCONF_LOCK_TIMEOUT = 10 
 39   
 40  RE_VALID_SSCONF_NAME = re.compile(r'^[-_a-z0-9]+$') 
41 42 43 -class SimpleConfigReader(object):
44 """Simple class to read configuration file. 45 46 """
47 - def __init__(self, file_name=constants.CLUSTER_CONF_FILE):
48 """Initializes this class. 49 50 @type file_name: string 51 @param file_name: Configuration file path 52 53 """ 54 self._file_name = file_name 55 self._config_data = serializer.Load(utils.ReadFile(file_name))
56 # TODO: Error handling 57
58 - def GetClusterName(self):
59 return self._config_data["cluster"]["cluster_name"]
60
61 - def GetHostKey(self):
62 return self._config_data["cluster"]["rsahostkeypub"]
63
64 - def GetMasterNode(self):
65 return self._config_data["cluster"]["master_node"]
66
67 - def GetMasterIP(self):
68 return self._config_data["cluster"]["master_ip"]
69
70 - def GetMasterNetdev(self):
71 return self._config_data["cluster"]["master_netdev"]
72
73 - def GetFileStorageDir(self):
74 return self._config_data["cluster"]["file_storage_dir"]
75
76 - def GetHypervisorType(self):
77 return self._config_data["cluster"]["hypervisor"]
78
79 - def GetNodeList(self):
80 return self._config_data["nodes"].keys()
81 82 @classmethod
83 - def FromDict(cls, val, cfg_file=constants.CLUSTER_CONF_FILE):
84 """Alternative construction from a dictionary. 85 86 """ 87 obj = SimpleConfigReader.__new__(cls) 88 obj._config_data = val 89 obj._file_name = cfg_file 90 return obj
91
92 93 -class SimpleConfigWriter(SimpleConfigReader):
94 """Simple class to write configuration file. 95 96 """
97 - def Save(self):
98 """Writes configuration file. 99 100 Warning: Doesn't take care of locking or synchronizing with other 101 processes. 102 103 """ 104 utils.WriteFile(self._file_name, 105 data=serializer.Dump(self._config_data), 106 mode=0600)
107
108 109 -class SimpleStore(object):
110 """Interface to static cluster data. 111 112 This is different that the config.ConfigWriter and 113 SimpleConfigReader classes in that it holds data that will always be 114 present, even on nodes which don't have all the cluster data. 115 116 Other particularities of the datastore: 117 - keys are restricted to predefined values 118 119 """ 120 _SS_FILEPREFIX = "ssconf_" 121 _VALID_KEYS = ( 122 constants.SS_CLUSTER_NAME, 123 constants.SS_CLUSTER_TAGS, 124 constants.SS_FILE_STORAGE_DIR, 125 constants.SS_MASTER_CANDIDATES, 126 constants.SS_MASTER_IP, 127 constants.SS_MASTER_NETDEV, 128 constants.SS_MASTER_NODE, 129 constants.SS_NODE_LIST, 130 constants.SS_OFFLINE_NODES, 131 constants.SS_ONLINE_NODES, 132 constants.SS_INSTANCE_LIST, 133 constants.SS_RELEASE_VERSION, 134 ) 135 _MAX_SIZE = 131072 136
137 - def __init__(self, cfg_location=None):
138 if cfg_location is None: 139 self._cfg_dir = constants.DATA_DIR 140 else: 141 self._cfg_dir = cfg_location
142
143 - def KeyToFilename(self, key):
144 """Convert a given key into filename. 145 146 """ 147 if key not in self._VALID_KEYS: 148 raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'" 149 % str(key)) 150 151 filename = self._cfg_dir + '/' + self._SS_FILEPREFIX + key 152 return filename
153
154 - def _ReadFile(self, key):
155 """Generic routine to read keys. 156 157 This will read the file which holds the value requested. Errors 158 will be changed into ConfigurationErrors. 159 160 """ 161 filename = self.KeyToFilename(key) 162 try: 163 fh = file(filename, 'r') 164 try: 165 data = fh.read(self._MAX_SIZE) 166 data = data.rstrip('\n') 167 finally: 168 fh.close() 169 except EnvironmentError, err: 170 raise errors.ConfigurationError("Can't read from the ssconf file:" 171 " '%s'" % str(err)) 172 return data
173
174 - def WriteFiles(self, values):
175 """Writes ssconf files used by external scripts. 176 177 @type values: dict 178 @param values: Dictionary of (name, value) 179 180 """ 181 ssconf_lock = utils.FileLock(constants.SSCONF_LOCK_FILE) 182 183 # Get lock while writing files 184 ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT) 185 try: 186 for name, value in values.iteritems(): 187 if value and not value.endswith("\n"): 188 value += "\n" 189 utils.WriteFile(self.KeyToFilename(name), data=value, mode=0444) 190 finally: 191 ssconf_lock.Unlock()
192
193 - def GetFileList(self):
194 """Return the list of all config files. 195 196 This is used for computing node replication data. 197 198 """ 199 return [self.KeyToFilename(key) for key in self._VALID_KEYS]
200
201 - def GetClusterName(self):
202 """Get the cluster name. 203 204 """ 205 return self._ReadFile(constants.SS_CLUSTER_NAME)
206
207 - def GetFileStorageDir(self):
208 """Get the file storage dir. 209 210 """ 211 return self._ReadFile(constants.SS_FILE_STORAGE_DIR)
212
213 - def GetMasterCandidates(self):
214 """Return the list of master candidates. 215 216 """ 217 data = self._ReadFile(constants.SS_MASTER_CANDIDATES) 218 nl = data.splitlines(False) 219 return nl
220
221 - def GetMasterIP(self):
222 """Get the IP of the master node for this cluster. 223 224 """ 225 return self._ReadFile(constants.SS_MASTER_IP)
226
227 - def GetMasterNetdev(self):
228 """Get the netdev to which we'll add the master ip. 229 230 """ 231 return self._ReadFile(constants.SS_MASTER_NETDEV)
232
233 - def GetMasterNode(self):
234 """Get the hostname of the master node for this cluster. 235 236 """ 237 return self._ReadFile(constants.SS_MASTER_NODE)
238
239 - def GetNodeList(self):
240 """Return the list of cluster nodes. 241 242 """ 243 data = self._ReadFile(constants.SS_NODE_LIST) 244 nl = data.splitlines(False) 245 return nl
246
247 - def GetClusterTags(self):
248 """Return the cluster tags. 249 250 """ 251 data = self._ReadFile(constants.SS_CLUSTER_TAGS) 252 nl = data.splitlines(False) 253 return nl
254
255 256 -def GetMasterAndMyself(ss=None):
257 """Get the master node and my own hostname. 258 259 This can be either used for a 'soft' check (compared to CheckMaster, 260 which exits) or just for computing both at the same time. 261 262 The function does not handle any errors, these should be handled in 263 the caller (errors.ConfigurationError, errors.ResolverError). 264 265 @param ss: either a sstore.SimpleConfigReader or a 266 sstore.SimpleStore instance 267 @rtype: tuple 268 @return: a tuple (master node name, my own name) 269 270 """ 271 if ss is None: 272 ss = SimpleStore() 273 return ss.GetMasterNode(), utils.HostInfo().name
274
275 276 -def CheckMaster(debug, ss=None):
277 """Checks the node setup. 278 279 If this is the master, the function will return. Otherwise it will 280 exit with an exit code based on the node status. 281 282 """ 283 try: 284 master_name, myself = GetMasterAndMyself(ss) 285 except errors.ConfigurationError, err: 286 print "Cluster configuration incomplete: '%s'" % str(err) 287 sys.exit(constants.EXIT_NODESETUP_ERROR) 288 except errors.ResolverError, err: 289 sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0]) 290 sys.exit(constants.EXIT_NODESETUP_ERROR) 291 292 if myself != master_name: 293 if debug: 294 sys.stderr.write("Not master, exiting.\n") 295 sys.exit(constants.EXIT_NOTMASTER)
296