Verify: xen toolstack, hypervisor and hvparams
[ganeti-local] / lib / hypervisor / hv_base.py
index 824842c..5135b4e 100644 (file)
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2006, 2007, 2008, 2009, 2010 Google Inc.
+# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2012, 2013 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -81,11 +81,11 @@ def _IsMultiCpuMaskWellFormed(cpu_mask):
 
 # must be afile
 _FILE_CHECK = (utils.IsNormAbsPath, "must be an absolute normalized path",
-              os.path.isfile, "not found or not a file")
+               os.path.isfile, "not found or not a file")
 
 # must be a directory
 _DIR_CHECK = (utils.IsNormAbsPath, "must be an absolute normalized path",
-             os.path.isdir, "not found or not a directory")
+              os.path.isdir, "not found or not a directory")
 
 # CPU mask must be well-formed
 # TODO: implement node level check for the CPU mask
@@ -102,6 +102,9 @@ _MULTI_CPU_MASK_CHECK = (_IsMultiCpuMaskWellFormed,
 _NET_PORT_CHECK = (lambda x: 0 < x < 65535, "invalid port number",
                    None, None)
 
+# Check that an integer is non negative
+_NONNEGATIVE_INT_CHECK = (lambda x: x >= 0, "cannot be negative", None, None)
+
 # nice wrappers for users
 REQ_FILE_CHECK = (True, ) + _FILE_CHECK
 OPT_FILE_CHECK = (False, ) + _FILE_CHECK
@@ -113,6 +116,8 @@ REQ_CPU_MASK_CHECK = (True, ) + _CPU_MASK_CHECK
 OPT_CPU_MASK_CHECK = (False, ) + _CPU_MASK_CHECK
 REQ_MULTI_CPU_MASK_CHECK = (True, ) + _MULTI_CPU_MASK_CHECK
 OPT_MULTI_CPU_MASK_CHECK = (False, ) + _MULTI_CPU_MASK_CHECK
+REQ_NONNEGATIVE_INT_CHECK = (True, ) + _NONNEGATIVE_INT_CHECK
+OPT_NONNEGATIVE_INT_CHECK = (False, ) + _NONNEGATIVE_INT_CHECK
 
 # no checks at all
 NO_CHECK = (False, None, None, None, None)
@@ -161,11 +166,9 @@ class BaseHypervisor(object):
   """
   PARAMETERS = {}
   ANCILLARY_FILES = []
+  ANCILLARY_FILES_OPT = []
   CAN_MIGRATE = False
 
-  def __init__(self):
-    pass
-
   def StartInstance(self, instance, block_devices, startup_paused):
     """Start an instance."""
     raise NotImplementedError
@@ -203,7 +206,7 @@ class BaseHypervisor(object):
     """Reboot an instance."""
     raise NotImplementedError
 
-  def ListInstances(self):
+  def ListInstances(self, hvparams=None):
     """Get the list of running instances."""
     raise NotImplementedError
 
@@ -249,17 +252,25 @@ class BaseHypervisor(object):
     """Return a list of ancillary files to be copied to all nodes as ancillary
     configuration files.
 
-    @rtype: list of strings
-    @return: list of absolute paths of files to ship cluster-wide
+    @rtype: (list of absolute paths, list of absolute paths)
+    @return: (all files, optional files)
 
     """
     # By default we return a member variable, so that if an hypervisor has just
     # a static list of files it doesn't have to override this function.
-    return cls.ANCILLARY_FILES
+    assert set(cls.ANCILLARY_FILES).issuperset(cls.ANCILLARY_FILES_OPT), \
+      "Optional ancillary files must be a subset of ancillary files"
 
-  def Verify(self):
+    return (cls.ANCILLARY_FILES, cls.ANCILLARY_FILES_OPT)
+
+  def Verify(self, hvparams=None):
     """Verify the hypervisor.
 
+    @type hvparams: dict of strings
+    @param hvparams: hypervisor parameters to be verified against
+
+    @return: Problem description if something is wrong, C{None} otherwise
+
     """
     raise NotImplementedError
 
@@ -291,6 +302,17 @@ class BaseHypervisor(object):
     """
     pass
 
+  def BalloonInstanceMemory(self, instance, mem):
+    """Balloon an instance memory to a certain value.
+
+    @type instance: L{objects.Instance}
+    @param instance: instance to be accepted
+    @type mem: int
+    @param mem: actual memory size to use for instance runtime
+
+    """
+    raise NotImplementedError
+
   def FinalizeMigrationDst(self, instance, info, success):
     """Finalize the instance migration on the target node.
 
@@ -346,6 +368,24 @@ class BaseHypervisor(object):
     """
     raise NotImplementedError
 
+  def _InstanceStartupMemory(self, instance):
+    """Get the correct startup memory for an instance
+
+    This function calculates how much memory an instance should be started
+    with, making sure it's a value between the minimum and the maximum memory,
+    but also trying to use no more than the current free memory on the node.
+
+    @type instance: L{objects.Instance}
+    @param instance: the instance that is being started
+    @rtype: integer
+    @return: memory the instance should be started with
+
+    """
+    free_memory = self.GetNodeInfo()["memory_free"]
+    max_start_mem = min(instance.beparams[constants.BE_MAXMEM], free_memory)
+    start_mem = max(instance.beparams[constants.BE_MINMEM], max_start_mem)
+    return start_mem
+
   @classmethod
   def CheckParameterSyntax(cls, hvparams):
     """Check the given parameters for validity.
@@ -483,3 +523,17 @@ class BaseHypervisor(object):
       result = utils.RunCmd(["reboot", "-n", "-f"])
       if not result:
         logging.error("Can't run shutdown: %s", result.output)
+
+  @staticmethod
+  def _FormatVerifyResults(msgs):
+    """Formats the verification results, given a list of errors.
+
+    @param msgs: list of errors, possibly empty
+    @return: overall problem description if something is wrong,
+        C{None} otherwise
+
+    """
+    if msgs:
+      return "; ".join(msgs)
+    else:
+      return None