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