Make the disks_active flag queryable
[ganeti-local] / lib / ssconf.py
index 7e34b5d..fc79422 100644 (file)
@@ -30,6 +30,7 @@ import sys
 import errno
 import logging
 
+from ganeti import compat
 from ganeti import errors
 from ganeti import constants
 from ganeti import utils
@@ -40,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,
@@ -103,12 +104,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.
 
@@ -136,14 +139,36 @@ class SimpleStore(object):
       raise errors.ConfigurationError("Can't read ssconf file %s: %s" %
                                       (filename, str(err)))
 
-  def WriteFiles(self, values):
+  def ReadAll(self):
+    """Reads all keys and returns their values.
+
+    @rtype: dict
+    @return: Dictionary, ssconf key as key, value as value
+
+    """
+    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)
@@ -151,11 +176,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()
 
@@ -320,13 +349,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):
@@ -387,3 +416,17 @@ def VerifyClusterName(name, _cfg_location=None):
   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)))