Make migration RPC non-blocking
[ganeti-local] / lib / hypervisor / hv_base.py
index 6bcf6d7..824842c 100644 (file)
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2006, 2007, 2008 Google Inc.
+# Copyright (C) 2006, 2007, 2008, 2009, 2010 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
@@ -47,6 +47,35 @@ from ganeti import utils
 from ganeti import constants
 
 
+def _IsCpuMaskWellFormed(cpu_mask):
+  """Verifies if the given single CPU mask is valid
+
+  The single CPU mask should be in the form "a,b,c,d", where each
+  letter is a positive number or range.
+
+  """
+  try:
+    cpu_list = utils.ParseCpuMask(cpu_mask)
+  except errors.ParseError, _:
+    return False
+  return isinstance(cpu_list, list) and len(cpu_list) > 0
+
+
+def _IsMultiCpuMaskWellFormed(cpu_mask):
+  """Verifies if the given multiple CPU mask is valid
+
+  A valid multiple CPU mask is in the form "a:b:c:d", where each
+  letter is a single CPU mask.
+
+  """
+  try:
+    utils.ParseMultiCpuMask(cpu_mask)
+  except errors.ParseError, _:
+    return False
+
+  return True
+
+
 # Read the BaseHypervisor.PARAMETERS docstring for the syntax of the
 # _CHECK values
 
@@ -58,13 +87,32 @@ _FILE_CHECK = (utils.IsNormAbsPath, "must be an absolute normalized path",
 _DIR_CHECK = (utils.IsNormAbsPath, "must be an absolute normalized path",
              os.path.isdir, "not found or not a directory")
 
+# CPU mask must be well-formed
+# TODO: implement node level check for the CPU mask
+_CPU_MASK_CHECK = (_IsCpuMaskWellFormed,
+                   "CPU mask definition is not well-formed",
+                   None, None)
+
+# Multiple CPU mask must be well-formed
+_MULTI_CPU_MASK_CHECK = (_IsMultiCpuMaskWellFormed,
+                         "Multiple CPU mask definition is not well-formed",
+                         None, None)
+
+# Check for validity of port number
+_NET_PORT_CHECK = (lambda x: 0 < x < 65535, "invalid port number",
+                   None, None)
+
 # nice wrappers for users
 REQ_FILE_CHECK = (True, ) + _FILE_CHECK
 OPT_FILE_CHECK = (False, ) + _FILE_CHECK
 REQ_DIR_CHECK = (True, ) + _DIR_CHECK
 OPT_DIR_CHECK = (False, ) + _DIR_CHECK
-NET_PORT_CHECK = (True, lambda x: x > 0 and x < 65535, "invalid port number",
-                  None, None)
+REQ_NET_PORT_CHECK = (True, ) + _NET_PORT_CHECK
+OPT_NET_PORT_CHECK = (False, ) + _NET_PORT_CHECK
+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
 
 # no checks at all
 NO_CHECK = (False, None, None, None, None)
@@ -73,8 +121,8 @@ NO_CHECK = (False, None, None, None, None)
 REQUIRED_CHECK = (True, None, None, None, None)
 
 # migration type
-MIGRATION_TYPE_CHECK = (True, lambda x: x in constants.HT_MIGRATION_TYPES,
-                        "invalid migration type", None, None)
+MIGRATION_MODE_CHECK = (True, lambda x: x in constants.HT_MIGRATION_MODES,
+                        "invalid migration mode", None, None)
 
 
 def ParamInSet(required, my_set):
@@ -118,7 +166,7 @@ class BaseHypervisor(object):
   def __init__(self):
     pass
 
-  def StartInstance(self, instance, block_devices):
+  def StartInstance(self, instance, block_devices, startup_paused):
     """Start an instance."""
     raise NotImplementedError
 
@@ -190,8 +238,8 @@ class BaseHypervisor(object):
     raise NotImplementedError
 
   @classmethod
-  def GetShellCommandForConsole(cls, instance, hvparams, beparams):
-    """Return a command for connecting to the console of an instance.
+  def GetInstanceConsole(cls, instance, hvparams, beparams):
+    """Return information for connecting to the console of an instance.
 
     """
     raise NotImplementedError
@@ -215,7 +263,7 @@ class BaseHypervisor(object):
     """
     raise NotImplementedError
 
-  def MigrationInfo(self, instance): # pylint: disable-msg=R0201,W0613
+  def MigrationInfo(self, instance): # pylint: disable=R0201,W0613
     """Get instance information to perform a migration.
 
     By default assume no information is needed.
@@ -226,7 +274,7 @@ class BaseHypervisor(object):
     @return: instance migration information - serialized form
 
     """
-    return ''
+    return ""
 
   def AcceptInstance(self, instance, info, target):
     """Prepare to accept an instance.
@@ -243,8 +291,8 @@ class BaseHypervisor(object):
     """
     pass
 
-  def FinalizeMigration(self, instance, info, success):
-    """Finalized an instance migration.
+  def FinalizeMigrationDst(self, instance, info, success):
+    """Finalize the instance migration on the target node.
 
     Should finalize or revert any preparation done to accept the instance.
     Since by default we do no preparation, we also don't have anything to do
@@ -272,6 +320,32 @@ class BaseHypervisor(object):
     """
     raise NotImplementedError
 
+  def FinalizeMigrationSource(self, instance, success, live):
+    """Finalize the instance migration on the source node.
+
+    @type instance: L{objects.Instance}
+    @param instance: the instance that was migrated
+    @type success: bool
+    @param success: whether the migration succeeded or not
+    @type live: bool
+    @param live: whether the user requested a live migration or not
+
+    """
+    pass
+
+  def GetMigrationStatus(self, instance):
+    """Get the migration status
+
+    @type instance: L{objects.Instance}
+    @param instance: the instance that is being migrated
+    @rtype: L{objects.MigrationStatus}
+    @return: the status of the current migration (one of
+             L{constants.HV_MIGRATION_VALID_STATUSES}), plus any additional
+             progress info that can be retrieved from the hypervisor
+
+    """
+    raise NotImplementedError
+
   @classmethod
   def CheckParameterSyntax(cls, hvparams):
     """Check the given parameters for validity.
@@ -365,16 +439,16 @@ class BaseHypervisor(object):
         if len(splitfields) > 1:
           key = splitfields[0].strip()
           val = splitfields[1].strip()
-          if key == 'MemTotal':
-            result['memory_total'] = int(val.split()[0])/1024
-          elif key in ('MemFree', 'Buffers', 'Cached'):
-            sum_free += int(val.split()[0])/1024
-          elif key == 'Active':
-            result['memory_dom0'] = int(val.split()[0])/1024
+          if key == "MemTotal":
+            result["memory_total"] = int(val.split()[0]) / 1024
+          elif key in ("MemFree", "Buffers", "Cached"):
+            sum_free += int(val.split()[0]) / 1024
+          elif key == "Active":
+            result["memory_dom0"] = int(val.split()[0]) / 1024
     except (ValueError, TypeError), err:
       raise errors.HypervisorError("Failed to compute memory usage: %s" %
                                    (err,))
-    result['memory_free'] = sum_free
+    result["memory_free"] = sum_free
 
     cpu_total = 0
     try:
@@ -386,10 +460,10 @@ class BaseHypervisor(object):
         fh.close()
     except EnvironmentError, err:
       raise errors.HypervisorError("Failed to list node info: %s" % (err,))
-    result['cpu_total'] = cpu_total
+    result["cpu_total"] = cpu_total
     # FIXME: export correct data here
-    result['cpu_nodes'] = 1
-    result['cpu_sockets'] = 1
+    result["cpu_nodes"] = 1
+    result["cpu_sockets"] = 1
 
     return result