X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/fb4869694726715bf8dab399ceca8e3c7497bcda..5a93930f94c63f28be298cd6560899fb5dc19917:/lib/ssconf.py diff --git a/lib/ssconf.py b/lib/ssconf.py index 30c9d78..11af2a2 100644 --- a/lib/ssconf.py +++ b/lib/ssconf.py @@ -28,7 +28,9 @@ configuration data, which is mostly static and available to all nodes. import sys import errno +import logging +from ganeti import compat from ganeti import errors from ganeti import constants from ganeti import utils @@ -39,7 +41,7 @@ from ganeti import pathutils SSCONF_LOCK_TIMEOUT = 10 #: Valid ssconf keys -_VALID_KEYS = frozenset([ +_VALID_KEYS = compat.UniqueFrozenset([ constants.SS_CLUSTER_NAME, constants.SS_CLUSTER_TAGS, constants.SS_FILE_STORAGE_DIR, @@ -63,12 +65,40 @@ _VALID_KEYS = frozenset([ constants.SS_UID_POOL, constants.SS_NODEGROUPS, constants.SS_NETWORKS, + constants.SS_HVPARAMS_XEN_PVM, + constants.SS_HVPARAMS_XEN_FAKE, + constants.SS_HVPARAMS_XEN_HVM, + constants.SS_HVPARAMS_XEN_KVM, + constants.SS_HVPARAMS_XEN_CHROOT, + constants.SS_HVPARAMS_XEN_LXC, ]) #: Maximum size for ssconf files _MAX_SIZE = 128 * 1024 +def ReadSsconfFile(filename): + """Reads an ssconf file and verifies its size. + + @type filename: string + @param filename: Path to file + @rtype: string + @return: File contents without newlines at the end + @raise RuntimeError: When the file size exceeds L{_MAX_SIZE} + + """ + statcb = utils.FileStatHelper() + + data = utils.ReadFile(filename, size=_MAX_SIZE, preread=statcb) + + if statcb.st.st_size > _MAX_SIZE: + msg = ("File '%s' has a size of %s bytes (up to %s allowed)" % + (filename, statcb.st.st_size, _MAX_SIZE)) + raise RuntimeError(msg) + + return data.rstrip("\n") + + class SimpleStore(object): """Interface to static cluster data. @@ -80,12 +110,14 @@ class SimpleStore(object): - keys are restricted to predefined values """ - def __init__(self, cfg_location=None): + def __init__(self, cfg_location=None, _lockfile=pathutils.SSCONF_LOCK_FILE): if cfg_location is None: self._cfg_dir = pathutils.DATA_DIR else: self._cfg_dir = cfg_location + self._lockfile = _lockfile + def KeyToFilename(self, key): """Convert a given key into filename. @@ -106,23 +138,43 @@ class SimpleStore(object): """ filename = self.KeyToFilename(key) try: - data = utils.ReadFile(filename, size=_MAX_SIZE) + return ReadSsconfFile(filename) except EnvironmentError, err: if err.errno == errno.ENOENT and default is not None: return default raise errors.ConfigurationError("Can't read ssconf file %s: %s" % (filename, str(err))) - return data.rstrip("\n") + def ReadAll(self): + """Reads all keys and returns their values. + + @rtype: dict + @return: Dictionary, ssconf key as key, value as value - def WriteFiles(self, values): + """ + result = [] + + for key in _VALID_KEYS: + try: + value = self._ReadFile(key) + except errors.ConfigurationError: + # Ignore non-existing files + pass + else: + result.append((key, value)) + + return dict(result) + + def WriteFiles(self, values, dry_run=False): """Writes ssconf files used by external scripts. @type values: dict @param values: Dictionary of (name, value) + @type dry_run boolean + @param dry_run: Whether to perform a dry run """ - ssconf_lock = utils.FileLock.Open(pathutils.SSCONF_LOCK_FILE) + ssconf_lock = utils.FileLock.Open(self._lockfile) # Get lock while writing files ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT) @@ -130,11 +182,15 @@ class SimpleStore(object): for name, value in values.iteritems(): if value and not value.endswith("\n"): value += "\n" + if len(value) > _MAX_SIZE: - raise errors.ConfigurationError("ssconf file %s above maximum size" % - name) + msg = ("Value '%s' has a length of %s bytes, but only up to %s are" + " allowed" % (name, len(value), _MAX_SIZE)) + raise errors.ConfigurationError(msg) + utils.WriteFile(self.KeyToFilename(name), data=value, - mode=constants.SS_FILE_PERMS) + mode=constants.SS_FILE_PERMS, + dry_run=dry_run) finally: ssconf_lock.Unlock() @@ -265,6 +321,35 @@ class SimpleStore(object): nl = data.splitlines(False) return nl + def GetHvparamsForHypervisor(self, hvname): + """Return the hypervisor parameters of the given hypervisor. + + @type hvname: string + @param hvname: name of the hypervisor, must be in C{constants.HYPER_TYPES} + @rtype: dict of strings + @returns: dictionary with hypervisor parameters + + """ + data = self._ReadFile(constants.SS_HVPARAMS_PREF + hvname) + lines = data.splitlines(False) + hvparams = {} + for line in lines: + (key, value) = line.split("=") + hvparams[key] = value + return hvparams + + def GetHvparams(self): + """Return the hypervisor parameters of all hypervisors. + + @rtype: dict of dict of strings + @returns: dictionary mapping hypervisor names to hvparams + + """ + all_hvparams = {} + for hv in constants.HYPER_TYPES: + all_hvparams[hv] = self.GetHvparamsForHypervisor(hv) + return all_hvparams + def GetMaintainNodeHealth(self): """Return the value of the maintain_node_health option. @@ -299,13 +384,13 @@ class SimpleStore(object): " family: %s" % err) -def WriteSsconfFiles(values): +def WriteSsconfFiles(values, dry_run=False): """Update all ssconf files. Wrapper around L{SimpleStore.WriteFiles}. """ - SimpleStore().WriteFiles(values) + SimpleStore().WriteFiles(values, dry_run=dry_run) def GetMasterAndMyself(ss=None): @@ -348,3 +433,35 @@ def CheckMaster(debug, ss=None): if debug: sys.stderr.write("Not master, exiting.\n") sys.exit(constants.EXIT_NOTMASTER) + + +def VerifyClusterName(name, _cfg_location=None): + """Verifies cluster name against a local cluster name. + + @type name: string + @param name: Cluster name + + """ + sstore = SimpleStore(cfg_location=_cfg_location) + + try: + local_name = sstore.GetClusterName() + except errors.ConfigurationError, err: + logging.debug("Can't get local cluster name: %s", err) + else: + if name != local_name: + raise errors.GenericError("Current cluster name is '%s'" % local_name) + + +def VerifyKeys(keys): + """Raises an exception if unknown ssconf keys are given. + + @type keys: sequence + @param keys: Key names to verify + @raise errors.GenericError: When invalid keys were found + + """ + invalid = frozenset(keys) - _VALID_KEYS + if invalid: + raise errors.GenericError("Invalid ssconf keys: %s" % + utils.CommaJoin(sorted(invalid)))