X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/06dc5b4438ecd7efb6aab75c83543ea7bfd73df6..dd7db36058e0e645c634402fe9c26cee69eef753:/lib/ssconf.py diff --git a/lib/ssconf.py b/lib/ssconf.py index f7bf49c..cce1141 100644 --- a/lib/ssconf.py +++ b/lib/ssconf.py @@ -26,8 +26,8 @@ configuration data, which is mostly static and available to all nodes. """ -import socket import sys +import re from ganeti import errors from ganeti import constants @@ -35,7 +35,12 @@ from ganeti import utils from ganeti import serializer -class SimpleConfigReader: +SSCONF_LOCK_TIMEOUT = 10 + +RE_VALID_SSCONF_NAME = re.compile(r'^[-_a-z0-9]+$') + + +class SimpleConfigReader(object): """Simple class to read configuration file. """ @@ -74,6 +79,16 @@ class SimpleConfigReader: def GetNodeList(self): return self._config_data["nodes"].keys() + @classmethod + def FromDict(cls, val, cfg_file=constants.CLUSTER_CONF_FILE): + """Alternative construction from a dictionary. + + """ + obj = SimpleConfigReader.__new__(cls) + obj._config_data = val + obj._file_name = cfg_file + return obj + class SimpleConfigWriter(SimpleConfigReader): """Simple class to write configuration file. @@ -97,6 +112,145 @@ class SimpleConfigWriter(SimpleConfigReader): mode=0600) +class SimpleStore(object): + """Interface to static cluster data. + + This is different that the config.ConfigWriter and + SimpleConfigReader classes in that it holds data that will always be + present, even on nodes which don't have all the cluster data. + + Other particularities of the datastore: + - keys are restricted to predefined values + + """ + _SS_FILEPREFIX = "ssconf_" + _VALID_KEYS = ( + constants.SS_CLUSTER_NAME, + constants.SS_CLUSTER_TAGS, + constants.SS_FILE_STORAGE_DIR, + constants.SS_MASTER_CANDIDATES, + constants.SS_MASTER_IP, + constants.SS_MASTER_NETDEV, + constants.SS_MASTER_NODE, + constants.SS_NODE_LIST, + constants.SS_OFFLINE_NODES, + constants.SS_ONLINE_NODES, + constants.SS_INSTANCE_LIST, + constants.SS_RELEASE_VERSION, + ) + _MAX_SIZE = 131072 + + def __init__(self, cfg_location=None): + if cfg_location is None: + self._cfg_dir = constants.DATA_DIR + else: + self._cfg_dir = cfg_location + + def KeyToFilename(self, key): + """Convert a given key into filename. + + """ + if key not in self._VALID_KEYS: + raise errors.ProgrammerError("Invalid key requested from SSConf: '%s'" + % str(key)) + + filename = self._cfg_dir + '/' + self._SS_FILEPREFIX + key + return filename + + def _ReadFile(self, key): + """Generic routine to read keys. + + This will read the file which holds the value requested. Errors + will be changed into ConfigurationErrors. + + """ + filename = self.KeyToFilename(key) + try: + fh = file(filename, 'r') + try: + data = fh.read(self._MAX_SIZE) + data = data.rstrip('\n') + finally: + fh.close() + except EnvironmentError, err: + raise errors.ConfigurationError("Can't read from the ssconf file:" + " '%s'" % str(err)) + return data + + def WriteFiles(self, values): + """Writes ssconf files used by external scripts. + + @type values: dict + @param values: Dictionary of (name, value) + + """ + ssconf_lock = utils.FileLock(constants.SSCONF_LOCK_FILE) + + # Get lock while writing files + ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT) + try: + for name, value in values.iteritems(): + if value and not value.endswith("\n"): + value += "\n" + utils.WriteFile(self.KeyToFilename(name), data=value, mode=0444) + finally: + ssconf_lock.Unlock() + + def GetFileList(self): + """Return the list of all config files. + + This is used for computing node replication data. + + """ + return [self.KeyToFilename(key) for key in self._VALID_KEYS] + + def GetClusterName(self): + """Get the cluster name. + + """ + return self._ReadFile(constants.SS_CLUSTER_NAME) + + def GetFileStorageDir(self): + """Get the file storage dir. + + """ + return self._ReadFile(constants.SS_FILE_STORAGE_DIR) + + def GetMasterCandidates(self): + """Return the list of master candidates. + + """ + data = self._ReadFile(constants.SS_MASTER_CANDIDATES) + nl = data.splitlines(False) + return nl + + def GetMasterIP(self): + """Get the IP of the master node for this cluster. + + """ + return self._ReadFile(constants.SS_MASTER_IP) + + def GetMasterNetdev(self): + """Get the netdev to which we'll add the master ip. + + """ + return self._ReadFile(constants.SS_MASTER_NETDEV) + + def GetMasterNode(self): + """Get the hostname of the master node for this cluster. + + """ + return self._ReadFile(constants.SS_MASTER_NODE) + + def GetNodeList(self): + """Return the list of cluster nodes. + + """ + data = self._ReadFile(constants.SS_NODE_LIST) + nl = data.splitlines(False) + return nl + + def GetMasterAndMyself(ss=None): """Get the master node and my own hostname. @@ -106,9 +260,14 @@ def GetMasterAndMyself(ss=None): The function does not handle any errors, these should be handled in the caller (errors.ConfigurationError, errors.ResolverError). + @param ss: either a sstore.SimpleConfigReader or a + sstore.SimpleStore instance + @rtype: tuple + @return: a tuple (master node name, my own name) + """ if ss is None: - ss = SimpleConfigReader() + ss = SimpleStore() return ss.GetMasterNode(), utils.HostInfo().name