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

Source Code for Module ganeti.ssconf

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2008, 2010, 2011, 2012 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 errno 
 31  import logging 
 32   
 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 
 39   
 40   
 41  SSCONF_LOCK_TIMEOUT = 10 
 42   
 43  #: Valid ssconf keys 
 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    ]) 
 69   
 70  #: Maximum size for ssconf files 
 71  _MAX_SIZE = 128 * 1024 
 72   
 73   
74 -def ReadSsconfFile(filename):
75 """Reads an ssconf file and verifies its size. 76 77 @type filename: string 78 @param filename: Path to file 79 @rtype: string 80 @return: File contents without newlines at the end 81 @raise RuntimeError: When the file size exceeds L{_MAX_SIZE} 82 83 """ 84 statcb = utils.FileStatHelper() 85 86 data = utils.ReadFile(filename, size=_MAX_SIZE, preread=statcb) 87 88 if statcb.st.st_size > _MAX_SIZE: 89 msg = ("File '%s' has a size of %s bytes (up to %s allowed)" % 90 (filename, statcb.st.st_size, _MAX_SIZE)) 91 raise RuntimeError(msg) 92 93 return data.rstrip("\n")
94 95
96 -class SimpleStore(object):
97 """Interface to static cluster data. 98 99 This is different that the config.ConfigWriter and 100 SimpleConfigReader classes in that it holds data that will always be 101 present, even on nodes which don't have all the cluster data. 102 103 Other particularities of the datastore: 104 - keys are restricted to predefined values 105 106 """
107 - def __init__(self, cfg_location=None, _lockfile=pathutils.SSCONF_LOCK_FILE):
108 if cfg_location is None: 109 self._cfg_dir = pathutils.DATA_DIR 110 else: 111 self._cfg_dir = cfg_location 112 113 self._lockfile = _lockfile
114
115 - def KeyToFilename(self, key):
116 """Convert a given key into filename. 117 118 """ 119 if key not in _VALID_KEYS: 120 raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'" 121 % str(key)) 122 123 filename = self._cfg_dir + "/" + constants.SSCONF_FILEPREFIX + key 124 return filename
125
126 - def _ReadFile(self, key, default=None):
127 """Generic routine to read keys. 128 129 This will read the file which holds the value requested. Errors 130 will be changed into ConfigurationErrors. 131 132 """ 133 filename = self.KeyToFilename(key) 134 try: 135 return ReadSsconfFile(filename) 136 except EnvironmentError, err: 137 if err.errno == errno.ENOENT and default is not None: 138 return default 139 raise errors.ConfigurationError("Can't read ssconf file %s: %s" % 140 (filename, str(err)))
141
142 - def ReadAll(self):
143 """Reads all keys and returns their values. 144 145 @rtype: dict 146 @return: Dictionary, ssconf key as key, value as value 147 148 """ 149 result = [] 150 151 for key in _VALID_KEYS: 152 try: 153 value = self._ReadFile(key) 154 except errors.ConfigurationError: 155 # Ignore non-existing files 156 pass 157 else: 158 result.append((key, value)) 159 160 return dict(result)
161
162 - def WriteFiles(self, values, dry_run=False):
163 """Writes ssconf files used by external scripts. 164 165 @type values: dict 166 @param values: Dictionary of (name, value) 167 @type dry_run boolean 168 @param dry_run: Whether to perform a dry run 169 170 """ 171 ssconf_lock = utils.FileLock.Open(self._lockfile) 172 173 # Get lock while writing files 174 ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT) 175 try: 176 for name, value in values.iteritems(): 177 if value and not value.endswith("\n"): 178 value += "\n" 179 180 if len(value) > _MAX_SIZE: 181 msg = ("Value '%s' has a length of %s bytes, but only up to %s are" 182 " allowed" % (name, len(value), _MAX_SIZE)) 183 raise errors.ConfigurationError(msg) 184 185 utils.WriteFile(self.KeyToFilename(name), data=value, 186 mode=constants.SS_FILE_PERMS, 187 dry_run=dry_run) 188 finally: 189 ssconf_lock.Unlock()
190
191 - def GetFileList(self):
192 """Return the list of all config files. 193 194 This is used for computing node replication data. 195 196 """ 197 return [self.KeyToFilename(key) for key in _VALID_KEYS]
198
199 - def GetClusterName(self):
200 """Get the cluster name. 201 202 """ 203 return self._ReadFile(constants.SS_CLUSTER_NAME)
204
205 - def GetFileStorageDir(self):
206 """Get the file storage dir. 207 208 """ 209 return self._ReadFile(constants.SS_FILE_STORAGE_DIR)
210
211 - def GetSharedFileStorageDir(self):
212 """Get the shared file storage dir. 213 214 """ 215 return self._ReadFile(constants.SS_SHARED_FILE_STORAGE_DIR)
216
217 - def GetMasterCandidates(self):
218 """Return the list of master candidates. 219 220 """ 221 data = self._ReadFile(constants.SS_MASTER_CANDIDATES) 222 nl = data.splitlines(False) 223 return nl
224
226 """Return the list of master candidates' primary IP. 227 228 """ 229 data = self._ReadFile(constants.SS_MASTER_CANDIDATES_IPS) 230 nl = data.splitlines(False) 231 return nl
232
233 - def GetMasterIP(self):
234 """Get the IP of the master node for this cluster. 235 236 """ 237 return self._ReadFile(constants.SS_MASTER_IP)
238
239 - def GetMasterNetdev(self):
240 """Get the netdev to which we'll add the master ip. 241 242 """ 243 return self._ReadFile(constants.SS_MASTER_NETDEV)
244
245 - def GetMasterNetmask(self):
246 """Get the master netmask. 247 248 """ 249 try: 250 return self._ReadFile(constants.SS_MASTER_NETMASK) 251 except errors.ConfigurationError: 252 family = self.GetPrimaryIPFamily() 253 ipcls = netutils.IPAddress.GetClassFromIpFamily(family) 254 return ipcls.iplen
255
256 - def GetMasterNode(self):
257 """Get the hostname of the master node for this cluster. 258 259 """ 260 return self._ReadFile(constants.SS_MASTER_NODE)
261
262 - def GetNodeList(self):
263 """Return the list of cluster nodes. 264 265 """ 266 data = self._ReadFile(constants.SS_NODE_LIST) 267 nl = data.splitlines(False) 268 return nl
269
270 - def GetNodePrimaryIPList(self):
271 """Return the list of cluster nodes' primary IP. 272 273 """ 274 data = self._ReadFile(constants.SS_NODE_PRIMARY_IPS) 275 nl = data.splitlines(False) 276 return nl
277
278 - def GetNodeSecondaryIPList(self):
279 """Return the list of cluster nodes' secondary IP. 280 281 """ 282 data = self._ReadFile(constants.SS_NODE_SECONDARY_IPS) 283 nl = data.splitlines(False) 284 return nl
285
286 - def GetNodegroupList(self):
287 """Return the list of nodegroups. 288 289 """ 290 data = self._ReadFile(constants.SS_NODEGROUPS) 291 nl = data.splitlines(False) 292 return nl
293
294 - def GetNetworkList(self):
295 """Return the list of networks. 296 297 """ 298 data = self._ReadFile(constants.SS_NETWORKS) 299 nl = data.splitlines(False) 300 return nl
301
302 - def GetClusterTags(self):
303 """Return the cluster tags. 304 305 """ 306 data = self._ReadFile(constants.SS_CLUSTER_TAGS) 307 nl = data.splitlines(False) 308 return nl
309
310 - def GetHypervisorList(self):
311 """Return the list of enabled hypervisors. 312 313 """ 314 data = self._ReadFile(constants.SS_HYPERVISOR_LIST) 315 nl = data.splitlines(False) 316 return nl
317
318 - def GetMaintainNodeHealth(self):
319 """Return the value of the maintain_node_health option. 320 321 """ 322 data = self._ReadFile(constants.SS_MAINTAIN_NODE_HEALTH) 323 # we rely on the bool serialization here 324 return data == "True"
325
326 - def GetUidPool(self):
327 """Return the user-id pool definition string. 328 329 The separator character is a newline. 330 331 The return value can be parsed using uidpool.ParseUidPool():: 332 333 ss = ssconf.SimpleStore() 334 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\\n") 335 336 """ 337 data = self._ReadFile(constants.SS_UID_POOL) 338 return data
339
340 - def GetPrimaryIPFamily(self):
341 """Return the cluster-wide primary address family. 342 343 """ 344 try: 345 return int(self._ReadFile(constants.SS_PRIMARY_IP_FAMILY, 346 default=netutils.IP4Address.family)) 347 except (ValueError, TypeError), err: 348 raise errors.ConfigurationError("Error while trying to parse primary IP" 349 " family: %s" % err)
350 351
352 -def WriteSsconfFiles(values, dry_run=False):
353 """Update all ssconf files. 354 355 Wrapper around L{SimpleStore.WriteFiles}. 356 357 """ 358 SimpleStore().WriteFiles(values, dry_run=dry_run)
359 360
361 -def GetMasterAndMyself(ss=None):
362 """Get the master node and my own hostname. 363 364 This can be either used for a 'soft' check (compared to CheckMaster, 365 which exits) or just for computing both at the same time. 366 367 The function does not handle any errors, these should be handled in 368 the caller (errors.ConfigurationError, errors.ResolverError). 369 370 @param ss: either a sstore.SimpleConfigReader or a 371 sstore.SimpleStore instance 372 @rtype: tuple 373 @return: a tuple (master node name, my own name) 374 375 """ 376 if ss is None: 377 ss = SimpleStore() 378 return ss.GetMasterNode(), netutils.Hostname.GetSysName()
379 380
381 -def CheckMaster(debug, ss=None):
382 """Checks the node setup. 383 384 If this is the master, the function will return. Otherwise it will 385 exit with an exit code based on the node status. 386 387 """ 388 try: 389 master_name, myself = GetMasterAndMyself(ss) 390 except errors.ConfigurationError, err: 391 print "Cluster configuration incomplete: '%s'" % str(err) 392 sys.exit(constants.EXIT_NODESETUP_ERROR) 393 except errors.ResolverError, err: 394 sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0]) 395 sys.exit(constants.EXIT_NODESETUP_ERROR) 396 397 if myself != master_name: 398 if debug: 399 sys.stderr.write("Not master, exiting.\n") 400 sys.exit(constants.EXIT_NOTMASTER)
401 402
403 -def VerifyClusterName(name, _cfg_location=None):
404 """Verifies cluster name against a local cluster name. 405 406 @type name: string 407 @param name: Cluster name 408 409 """ 410 sstore = SimpleStore(cfg_location=_cfg_location) 411 412 try: 413 local_name = sstore.GetClusterName() 414 except errors.ConfigurationError, err: 415 logging.debug("Can't get local cluster name: %s", err) 416 else: 417 if name != local_name: 418 raise errors.GenericError("Current cluster name is '%s'" % local_name)
419 420
421 -def VerifyKeys(keys):
422 """Raises an exception if unknown ssconf keys are given. 423 424 @type keys: sequence 425 @param keys: Key names to verify 426 @raise errors.GenericError: When invalid keys were found 427 428 """ 429 invalid = frozenset(keys) - _VALID_KEYS 430 if invalid: 431 raise errors.GenericError("Invalid ssconf keys: %s" % 432 utils.CommaJoin(sorted(invalid)))
433