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