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  # All rights reserved. 
  6  # 
  7  # Redistribution and use in source and binary forms, with or without 
  8  # modification, are permitted provided that the following conditions are 
  9  # met: 
 10  # 
 11  # 1. Redistributions of source code must retain the above copyright notice, 
 12  # this list of conditions and the following disclaimer. 
 13  # 
 14  # 2. Redistributions in binary form must reproduce the above copyright 
 15  # notice, this list of conditions and the following disclaimer in the 
 16  # documentation and/or other materials provided with the distribution. 
 17  # 
 18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
 19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 29   
 30   
 31  """Global Configuration data for Ganeti. 
 32   
 33  This module provides the interface to a special case of cluster 
 34  configuration data, which is mostly static and available to all nodes. 
 35   
 36  """ 
 37   
 38  import sys 
 39  import errno 
 40  import logging 
 41   
 42  from ganeti import errors 
 43  from ganeti import constants 
 44  from ganeti import utils 
 45  from ganeti import netutils 
 46  from ganeti import pathutils 
 47   
 48   
 49  SSCONF_LOCK_TIMEOUT = 10 
 50   
 51  #: Valid ssconf keys 
 52  _VALID_KEYS = constants.VALID_SS_KEYS 
 53   
 54  #: Maximum size for ssconf files 
 55  _MAX_SIZE = 128 * 1024 
 56   
 57   
58 -def ReadSsconfFile(filename):
59 """Reads an ssconf file and verifies its size. 60 61 @type filename: string 62 @param filename: Path to file 63 @rtype: string 64 @return: File contents without newlines at the end 65 @raise RuntimeError: When the file size exceeds L{_MAX_SIZE} 66 67 """ 68 statcb = utils.FileStatHelper() 69 70 data = utils.ReadFile(filename, size=_MAX_SIZE, preread=statcb) 71 72 if statcb.st.st_size > _MAX_SIZE: 73 msg = ("File '%s' has a size of %s bytes (up to %s allowed)" % 74 (filename, statcb.st.st_size, _MAX_SIZE)) 75 raise RuntimeError(msg) 76 77 return data.rstrip("\n")
78 79
80 -class SimpleStore(object):
81 """Interface to static cluster data. 82 83 This is different that the config.ConfigWriter and 84 SimpleConfigReader classes in that it holds data that will always be 85 present, even on nodes which don't have all the cluster data. 86 87 Other particularities of the datastore: 88 - keys are restricted to predefined values 89 90 """
91 - def __init__(self, cfg_location=None, _lockfile=pathutils.SSCONF_LOCK_FILE):
92 if cfg_location is None: 93 self._cfg_dir = pathutils.DATA_DIR 94 else: 95 self._cfg_dir = cfg_location 96 97 self._lockfile = _lockfile
98
99 - def KeyToFilename(self, key):
100 """Convert a given key into filename. 101 102 """ 103 if key not in _VALID_KEYS: 104 raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'" 105 % str(key)) 106 107 filename = self._cfg_dir + "/" + constants.SSCONF_FILEPREFIX + key 108 return filename
109
110 - def _ReadFile(self, key, default=None):
111 """Generic routine to read keys. 112 113 This will read the file which holds the value requested. Errors 114 will be changed into ConfigurationErrors. 115 116 """ 117 filename = self.KeyToFilename(key) 118 try: 119 return ReadSsconfFile(filename) 120 except EnvironmentError, err: 121 if err.errno == errno.ENOENT and default is not None: 122 return default 123 raise errors.ConfigurationError("Can't read ssconf file %s: %s" % 124 (filename, str(err)))
125
126 - def ReadAll(self):
127 """Reads all keys and returns their values. 128 129 @rtype: dict 130 @return: Dictionary, ssconf key as key, value as value 131 132 """ 133 result = [] 134 135 for key in _VALID_KEYS: 136 try: 137 value = self._ReadFile(key) 138 except errors.ConfigurationError: 139 # Ignore non-existing files 140 pass 141 else: 142 result.append((key, value)) 143 144 return dict(result)
145
146 - def WriteFiles(self, values, dry_run=False):
147 """Writes ssconf files used by external scripts. 148 149 @type values: dict 150 @param values: Dictionary of (name, value) 151 @type dry_run boolean 152 @param dry_run: Whether to perform a dry run 153 154 """ 155 ssconf_lock = utils.FileLock.Open(self._lockfile) 156 157 # Get lock while writing files 158 ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT) 159 try: 160 for name, value in values.iteritems(): 161 if isinstance(value, (list, tuple)): 162 value = "\n".join(value) 163 if value and not value.endswith("\n"): 164 value += "\n" 165 166 if len(value) > _MAX_SIZE: 167 msg = ("Value '%s' has a length of %s bytes, but only up to %s are" 168 " allowed" % (name, len(value), _MAX_SIZE)) 169 raise errors.ConfigurationError(msg) 170 171 utils.WriteFile(self.KeyToFilename(name), data=value, 172 mode=constants.SS_FILE_PERMS, 173 dry_run=dry_run) 174 finally: 175 ssconf_lock.Unlock()
176
177 - def GetFileList(self):
178 """Return the list of all config files. 179 180 This is used for computing node replication data. 181 182 """ 183 return [self.KeyToFilename(key) for key in _VALID_KEYS]
184
185 - def GetClusterName(self):
186 """Get the cluster name. 187 188 """ 189 return self._ReadFile(constants.SS_CLUSTER_NAME)
190
191 - def GetFileStorageDir(self):
192 """Get the file storage dir. 193 194 """ 195 return self._ReadFile(constants.SS_FILE_STORAGE_DIR)
196
197 - def GetSharedFileStorageDir(self):
198 """Get the shared file storage dir. 199 200 """ 201 return self._ReadFile(constants.SS_SHARED_FILE_STORAGE_DIR)
202
203 - def GetGlusterStorageDir(self):
204 """Get the Gluster storage dir. 205 206 """ 207 return self._ReadFile(constants.SS_GLUSTER_STORAGE_DIR)
208
209 - def GetMasterCandidates(self):
210 """Return the list of master candidates. 211 212 """ 213 data = self._ReadFile(constants.SS_MASTER_CANDIDATES) 214 nl = data.splitlines(False) 215 return nl
216
218 """Return the list of master candidates' primary IP. 219 220 """ 221 data = self._ReadFile(constants.SS_MASTER_CANDIDATES_IPS) 222 nl = data.splitlines(False) 223 return nl
224
225 - def _GetDictOfSsconfMap(self, ss_file_key):
226 """Reads a file with lines like key=value and returns a dict. 227 228 This utility function reads a file containing ssconf values of 229 the form "key=value", splits the lines at "=" and returns a 230 dictionary mapping the keys to the values. 231 232 @type ss_file_key: string 233 @param ss_file_key: the constant referring to an ssconf file 234 @rtype: dict of string to string 235 @return: a dictionary mapping the keys to the values 236 237 """ 238 data = self._ReadFile(ss_file_key) 239 lines = data.splitlines(False) 240 mapping = {} 241 for line in lines: 242 (key, value) = line.split("=") 243 mapping[key] = value 244 return mapping
245
247 """Returns the map of master candidate UUIDs to ssl cert. 248 249 @rtype: dict of string to string 250 @return: dictionary mapping the master candidates' UUIDs 251 to their SSL certificate digests 252 253 """ 254 return self._GetDictOfSsconfMap(constants.SS_MASTER_CANDIDATES_CERTS)
255
256 - def GetSshPortMap(self):
257 """Returns the map of node names to SSH port. 258 259 @rtype: dict of string to string 260 @return: dictionary mapping the node names to their SSH port 261 262 """ 263 return dict([(node_name, int(ssh_port)) for 264 node_name, ssh_port in 265 self._GetDictOfSsconfMap(constants.SS_SSH_PORTS).items()])
266
267 - def GetMasterIP(self):
268 """Get the IP of the master node for this cluster. 269 270 """ 271 return self._ReadFile(constants.SS_MASTER_IP)
272
273 - def GetMasterNetdev(self):
274 """Get the netdev to which we'll add the master ip. 275 276 """ 277 return self._ReadFile(constants.SS_MASTER_NETDEV)
278
279 - def GetMasterNetmask(self):
280 """Get the master netmask. 281 282 """ 283 try: 284 return self._ReadFile(constants.SS_MASTER_NETMASK) 285 except errors.ConfigurationError: 286 family = self.GetPrimaryIPFamily() 287 ipcls = netutils.IPAddress.GetClassFromIpFamily(family) 288 return ipcls.iplen
289
290 - def GetMasterNode(self):
291 """Get the hostname of the master node for this cluster. 292 293 """ 294 return self._ReadFile(constants.SS_MASTER_NODE)
295
296 - def GetNodeList(self):
297 """Return the list of cluster nodes. 298 299 """ 300 data = self._ReadFile(constants.SS_NODE_LIST) 301 nl = data.splitlines(False) 302 return nl
303
304 - def GetOnlineNodeList(self):
305 """Return the list of online cluster nodes. 306 307 """ 308 data = self._ReadFile(constants.SS_ONLINE_NODES) 309 nl = data.splitlines(False) 310 return nl
311
312 - def GetNodePrimaryIPList(self):
313 """Return the list of cluster nodes' primary IP. 314 315 """ 316 data = self._ReadFile(constants.SS_NODE_PRIMARY_IPS) 317 nl = data.splitlines(False) 318 return nl
319
320 - def GetNodeSecondaryIPList(self):
321 """Return the list of cluster nodes' secondary IP. 322 323 """ 324 data = self._ReadFile(constants.SS_NODE_SECONDARY_IPS) 325 nl = data.splitlines(False) 326 return nl
327
328 - def GetNodesVmCapable(self):
329 """Return the cluster nodes' vm capable value. 330 331 @rtype: dict of string to bool 332 @return: mapping of node names to vm capable values 333 334 """ 335 data = self._ReadFile(constants.SS_NODE_VM_CAPABLE) 336 vm_capable = {} 337 for line in data.splitlines(False): 338 (node_uuid, node_vm_capable) = line.split("=") 339 vm_capable[node_uuid] = node_vm_capable == "True" 340 return vm_capable
341
342 - def GetNodegroupList(self):
343 """Return the list of nodegroups. 344 345 """ 346 data = self._ReadFile(constants.SS_NODEGROUPS) 347 nl = data.splitlines(False) 348 return nl
349
350 - def GetNetworkList(self):
351 """Return the list of networks. 352 353 """ 354 data = self._ReadFile(constants.SS_NETWORKS) 355 nl = data.splitlines(False) 356 return nl
357
358 - def GetClusterTags(self):
359 """Return the cluster tags. 360 361 """ 362 data = self._ReadFile(constants.SS_CLUSTER_TAGS) 363 nl = data.splitlines(False) 364 return nl
365
366 - def GetHypervisorList(self):
367 """Return the list of enabled hypervisors. 368 369 """ 370 data = self._ReadFile(constants.SS_HYPERVISOR_LIST) 371 nl = data.splitlines(False) 372 return nl
373
374 - def GetHvparamsForHypervisor(self, hvname):
375 """Return the hypervisor parameters of the given hypervisor. 376 377 @type hvname: string 378 @param hvname: name of the hypervisor, must be in C{constants.HYPER_TYPES} 379 @rtype: dict of strings 380 @returns: dictionary with hypervisor parameters 381 382 """ 383 return self._GetDictOfSsconfMap(constants.SS_HVPARAMS_PREF + hvname)
384
385 - def GetHvparams(self):
386 """Return the hypervisor parameters of all hypervisors. 387 388 @rtype: dict of dict of strings 389 @returns: dictionary mapping hypervisor names to hvparams 390 391 """ 392 all_hvparams = {} 393 for hv in constants.HYPER_TYPES: 394 all_hvparams[hv] = self.GetHvparamsForHypervisor(hv) 395 return all_hvparams
396
397 - def GetMaintainNodeHealth(self):
398 """Return the value of the maintain_node_health option. 399 400 """ 401 data = self._ReadFile(constants.SS_MAINTAIN_NODE_HEALTH) 402 # we rely on the bool serialization here 403 return data == "True"
404
405 - def GetUidPool(self):
406 """Return the user-id pool definition string. 407 408 The separator character is a newline. 409 410 The return value can be parsed using uidpool.ParseUidPool():: 411 412 ss = ssconf.SimpleStore() 413 uid_pool = uidpool.ParseUidPool(ss.GetUidPool(), separator="\\n") 414 415 """ 416 data = self._ReadFile(constants.SS_UID_POOL) 417 return data
418
419 - def GetPrimaryIPFamily(self):
420 """Return the cluster-wide primary address family. 421 422 """ 423 try: 424 return int(self._ReadFile(constants.SS_PRIMARY_IP_FAMILY, 425 default=netutils.IP4Address.family)) 426 except (ValueError, TypeError), err: 427 raise errors.ConfigurationError("Error while trying to parse primary IP" 428 " family: %s" % err)
429
430 - def GetEnabledUserShutdown(self):
431 """Return whether user shutdown is enabled. 432 433 @rtype: bool 434 @return: 'True' if user shutdown is enabled, 'False' otherwise 435 436 """ 437 return self._ReadFile(constants.SS_ENABLED_USER_SHUTDOWN) == "True"
438 439
440 -def WriteSsconfFiles(values, dry_run=False):
441 """Update all ssconf files. 442 443 Wrapper around L{SimpleStore.WriteFiles}. 444 445 """ 446 SimpleStore().WriteFiles(values, dry_run=dry_run)
447 448
449 -def GetMasterAndMyself(ss=None):
450 """Get the master node and my own hostname. 451 452 This can be either used for a 'soft' check (compared to CheckMaster, 453 which exits) or just for computing both at the same time. 454 455 The function does not handle any errors, these should be handled in 456 the caller (errors.ConfigurationError, errors.ResolverError). 457 458 @param ss: either a sstore.SimpleConfigReader or a 459 sstore.SimpleStore instance 460 @rtype: tuple 461 @return: a tuple (master node name, my own name) 462 463 """ 464 if ss is None: 465 ss = SimpleStore() 466 return ss.GetMasterNode(), netutils.Hostname.GetSysName()
467 468
469 -def CheckMaster(debug, ss=None):
470 """Checks the node setup. 471 472 If this is the master, the function will return. Otherwise it will 473 exit with an exit code based on the node status. 474 475 """ 476 try: 477 master_name, myself = GetMasterAndMyself(ss) 478 except errors.ConfigurationError, err: 479 print "Cluster configuration incomplete: '%s'" % str(err) 480 sys.exit(constants.EXIT_NODESETUP_ERROR) 481 except errors.ResolverError, err: 482 sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0]) 483 sys.exit(constants.EXIT_NODESETUP_ERROR) 484 485 if myself != master_name: 486 if debug: 487 sys.stderr.write("Not master, exiting.\n") 488 sys.exit(constants.EXIT_NOTMASTER)
489 490
491 -def VerifyClusterName(name, _cfg_location=None):
492 """Verifies cluster name against a local cluster name. 493 494 @type name: string 495 @param name: Cluster name 496 497 """ 498 sstore = SimpleStore(cfg_location=_cfg_location) 499 500 try: 501 local_name = sstore.GetClusterName() 502 except errors.ConfigurationError, err: 503 logging.debug("Can't get local cluster name: %s", err) 504 else: 505 if name != local_name: 506 raise errors.GenericError("Current cluster name is '%s'" % local_name)
507 508
509 -def VerifyKeys(keys):
510 """Raises an exception if unknown ssconf keys are given. 511 512 @type keys: sequence 513 @param keys: Key names to verify 514 @raise errors.GenericError: When invalid keys were found 515 516 """ 517 invalid = frozenset(keys) - _VALID_KEYS 518 if invalid: 519 raise errors.GenericError("Invalid ssconf keys: %s" % 520 utils.CommaJoin(sorted(invalid)))
521