cluster-verify checks uniformity of PV sizes
[ganeti-local] / lib / utils / __init__.py
index 9f7cb24..d3319c1 100644 (file)
@@ -41,12 +41,14 @@ import signal
 from ganeti import errors
 from ganeti import constants
 from ganeti import compat
+from ganeti import pathutils
 
 from ganeti.utils.algo import *
 from ganeti.utils.filelock import *
 from ganeti.utils.hash import *
 from ganeti.utils.io import *
 from ganeti.utils.log import *
+from ganeti.utils.lvm import *
 from ganeti.utils.mlock import *
 from ganeti.utils.nodesetup import *
 from ganeti.utils.process import *
@@ -58,8 +60,7 @@ from ganeti.utils.x509 import *
 
 _VALID_SERVICE_NAME_RE = re.compile("^[-_.a-zA-Z0-9]{1,128}$")
 
-UUID_RE = re.compile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-"
-                     "[a-f0-9]{4}-[a-f0-9]{12}$")
+UUID_RE = re.compile(constants.UUID_REGEX)
 
 
 def ForceDictType(target, key_types, allowed_values=None):
@@ -155,6 +156,46 @@ def ValidateServiceName(name):
   return name
 
 
+def _ComputeMissingKeys(key_path, options, defaults):
+  """Helper functions to compute which keys a invalid.
+
+  @param key_path: The current key path (if any)
+  @param options: The user provided options
+  @param defaults: The default dictionary
+  @return: A list of invalid keys
+
+  """
+  defaults_keys = frozenset(defaults.keys())
+  invalid = []
+  for key, value in options.items():
+    if key_path:
+      new_path = "%s/%s" % (key_path, key)
+    else:
+      new_path = key
+
+    if key not in defaults_keys:
+      invalid.append(new_path)
+    elif isinstance(value, dict):
+      invalid.extend(_ComputeMissingKeys(new_path, value, defaults[key]))
+
+  return invalid
+
+
+def VerifyDictOptions(options, defaults):
+  """Verify a dict has only keys set which also are in the defaults dict.
+
+  @param options: The user provided options
+  @param defaults: The default dictionary
+  @raise error.OpPrereqError: If one of the keys is not supported
+
+  """
+  invalid = _ComputeMissingKeys("", options, defaults)
+
+  if invalid:
+    raise errors.OpPrereqError("Provided option keys not supported: %s" %
+                               CommaJoin(invalid), errors.ECODE_INVAL)
+
+
 def ListVolumeGroups():
   """List volume groups and their size
 
@@ -287,7 +328,7 @@ def GetHomeDir(user, default=None):
 
   The user can be passed either as a string (denoting the name) or as
   an integer (denoting the user id). If the user is not found, the
-  'default' argument is returned, which defaults to None.
+  C{default} argument is returned, which defaults to C{None}.
 
   """
   try:
@@ -426,7 +467,7 @@ def EnsureDaemon(name):
   """Check for and start daemon if not alive.
 
   """
-  result = RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
+  result = RunCmd([pathutils.DAEMON_UTIL, "check-and-start", name])
   if result.failed:
     logging.error("Can't start daemon '%s', failure %s, output: %s",
                   name, result.fail_reason, result.output)
@@ -439,7 +480,7 @@ def StopDaemon(name):
   """Stop daemon
 
   """
-  result = RunCmd([constants.DAEMON_UTIL, "stop", name])
+  result = RunCmd([pathutils.DAEMON_UTIL, "stop", name])
   if result.failed:
     logging.error("Can't stop daemon '%s', failure %s, output: %s",
                   name, result.fail_reason, result.output)
@@ -448,31 +489,6 @@ def StopDaemon(name):
   return True
 
 
-def CheckVolumeGroupSize(vglist, vgname, minsize):
-  """Checks if the volume group list is valid.
-
-  The function will check if a given volume group is in the list of
-  volume groups and has a minimum size.
-
-  @type vglist: dict
-  @param vglist: dictionary of volume group names and their size
-  @type vgname: str
-  @param vgname: the volume group we should check
-  @type minsize: int
-  @param minsize: the minimum size we accept
-  @rtype: None or str
-  @return: None for success, otherwise the error message
-
-  """
-  vgsize = vglist.get(vgname, None)
-  if vgsize is None:
-    return "volume group '%s' missing" % vgname
-  elif vgsize < minsize:
-    return ("volume group '%s' too small (%s MiB required, %d MiB found)" %
-            (vgname, minsize, vgsize))
-  return None
-
-
 def SplitTime(value):
   """Splits time as floating point number into a tuple.
 
@@ -607,9 +623,11 @@ class SignalWakeupFd(object):
     _set_wakeup_fd_fn = signal.set_wakeup_fd
   except AttributeError:
     # Not supported
+
     def _SetWakeupFd(self, _): # pylint: disable=R0201
       return -1
   else:
+
     def _SetWakeupFd(self, fd):
       return self._set_wakeup_fd_fn(fd)