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