- Add the _PrepareInstanceRootFsBdev method which will create a
  partition mapping for a block device and return the rootfs partition
  path.
- Override CleanupInstance to perform cleanup on instance destruction.
- Introduce the _CleanupInstance method which is the actual
  implementation of the cleanup process with a 'stash' argument.
- Create the instance stash in StartInstance, and ensure cleanup is
  performed on exit or on error.

Signed-off-by: Yuto KAWAMURA(kawamuray) <[email protected]>
---
 lib/hypervisor/hv_lxc.py | 98 +++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 84 insertions(+), 14 deletions(-)

diff --git a/lib/hypervisor/hv_lxc.py b/lib/hypervisor/hv_lxc.py
index a39854d..6f883e9 100644
--- a/lib/hypervisor/hv_lxc.py
+++ b/lib/hypervisor/hv_lxc.py
@@ -26,6 +26,7 @@
 import os
 import os.path
 import logging
+import sys
 
 from ganeti import constants
 from ganeti import errors # pylint: disable=W0611
@@ -75,6 +76,7 @@ class LXCHypervisor(hv_base.BaseHypervisor):
     ]
   _DIR_MODE = 0755
   _UNIQ_SUFFIX = ".conf"
+  _STASH_KEY_ALLOCATED_LOOP_DEV = "allocated_loopdev"
 
   PARAMETERS = {
     constants.HV_CPU_MASK: hv_base.OPT_CPU_MASK_CHECK,
@@ -154,6 +156,31 @@ class LXCHypervisor(hv_base.BaseHypervisor):
       raise HypervisorError("Failed to load instance stash file %s : %s" %
                             (stash_file, err))
 
+  def _CleanupInstance(self, instance_name, stash):
+    """Actual implementation of the instance cleanup procedure.
+
+    @type instance_name: string
+    @param instance_name: instance name
+    @type stash: dict(string:any)
+    @param stash: dict that contains desired information for instance cleanup
+
+    """
+    try:
+      if self._STASH_KEY_ALLOCATED_LOOP_DEV in stash:
+        loop_dev_path = stash[self._STASH_KEY_ALLOCATED_LOOP_DEV]
+        utils.ReleaseBdevPartitionMapping(loop_dev_path)
+    except errors.CommandError, err:
+      raise HypervisorError("Failed to cleanup partition mapping : %s" % err)
+
+    utils.RemoveFile(self._InstanceStashFilePath(instance_name))
+
+  def CleanupInstance(self, instance_name):
+    """Cleanup after a stopped instance.
+
+    """
+    stash = self._LoadInstanceStash(instance_name)
+    self._CleanupInstance(instance_name, stash)
+
   @classmethod
   def _GetCgroupMountPoint(cls):
     for _, mountpoint, fstype, _ in utils.GetMounts():
@@ -329,6 +356,30 @@ class LXCHypervisor(hv_base.BaseHypervisor):
 
     return "\n".join(out) + "\n"
 
+  @classmethod
+  def _PrepareInstanceRootFsBdev(cls, storage_path, stash):
+    """Return mountable path for storage_path.
+
+    This function creates a partition mapping for storage_path and returns the
+    first partition device path as a rootfs partition, and stashes the loopback
+    device path.
+    If storage_path is not a multi-partition block device, just return
+    storage_path.
+
+    """
+    try:
+      ret = utils.CreateBdevPartitionMapping(storage_path)
+    except errors.CommandError, err:
+      raise HypervisorError("Failed to create partition mapping for %s"
+                            ": %s" % (storage_path, err))
+
+    if ret is None:
+      return storage_path
+    else:
+      loop_dev_path, dm_dev_paths = ret
+      stash[cls._STASH_KEY_ALLOCATED_LOOP_DEV] = loop_dev_path
+      return dm_dev_paths[0]
+
   def StartInstance(self, instance, block_devices, startup_paused):
     """Start an instance.
 
@@ -336,6 +387,7 @@ class LXCHypervisor(hv_base.BaseHypervisor):
     We use volatile containers.
 
     """
+    stash = {}
     root_dir = self._InstanceDir(instance.name)
     try:
       utils.EnsureDirs([(root_dir, self._DIR_MODE)])
@@ -351,21 +403,39 @@ class LXCHypervisor(hv_base.BaseHypervisor):
                                      " instance %s failed: %s" %
                                      (log_file, instance.name, err))
 
-    if not block_devices:
-      raise HypervisorError("LXC needs at least one disk")
-
-    sda_dev_path = block_devices[0][1]
-    conf_file = self._InstanceConfFile(instance.name)
-    conf = self._CreateConfigFile(instance, sda_dev_path)
-    utils.WriteFile(conf_file, data=conf)
+    try:
+      if not block_devices:
+        raise HypervisorError("LXC needs at least one disk")
+
+      sda_dev_path = block_devices[0][1]
+      # LXC needs to use partition mapping devices to access each partition
+      # of the storage
+      sda_dev_path = self._PrepareInstanceRootFsBdev(sda_dev_path, stash)
+      conf_file = self._InstanceConfFile(instance.name)
+      conf = self._CreateConfigFile(instance, sda_dev_path)
+      utils.WriteFile(conf_file, data=conf)
+
+      logging.info("Running lxc-start")
+      result = utils.RunCmd(["lxc-start",
+                             "-n", instance.name,
+                             "-o", log_file,
+                             "-l", "DEBUG",
+                             "-f", conf_file,
+                             "-d"])
+      if result.failed:
+        raise HypervisorError("Running the lxc-start failed: %s" %
+                              result.output)
+    except:
+      # Save the original error
+      exc_info = sys.exc_info()
+      try:
+        self._CleanupInstance(instance.name, stash)
+      except HypervisorError, err:
+        logging.warn("Cleanup for instance %s incomplete: %s",
+                     instance.name, err)
+      raise exc_info[0], exc_info[1], exc_info[2]
 
-    result = utils.RunCmd(["lxc-start", "-n", instance.name,
-                           "-o", log_file,
-                           "-l", "DEBUG",
-                           "-f", conf_file, "-d"])
-    if result.failed:
-      raise HypervisorError("Running the lxc-start script failed: %s" %
-                            result.output)
+    self._SaveInstanceStash(instance.name, stash)
 
   def StopInstance(self, instance, force=False, retry=False, name=None,
                    timeout=None):
-- 
1.8.5.5

Reply via email to