- Add _PrepareInstanceRootFsBdev method which will create a partition mapping for block device and returns rootfs partition path. - Override CleanupInstance to perform cleanup on instance destruction. - Introduce _CleanupInstance method which is the actual implementation of the cleanup process with 'stash' argument. - Create 'stash' data store 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 | 100 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 14 deletions(-) diff --git a/lib/hypervisor/hv_lxc.py b/lib/hypervisor/hv_lxc.py index c33ed14..dfe094b 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 @@ -153,6 +154,35 @@ 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 instance cleanup procedure. + + @type instance_name: string + @param instance_name: instance name + @type stash: dict(string:any) + @param stash: dict that contains desired informations for instance cleanup + + """ + try: + if "loopback-device" in stash: + utils.ReleaseBdevPartitionMapping(stash["loopback-device"]) + 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. + + """ + try: + stash = self._LoadInstanceStash(instance_name) + except HypervisorError, err: + logging.warn("%s", err) + stash = {} + + self._CleanupInstance(instance_name, stash) + @classmethod def _GetCgroupMountPoint(cls): for _, mountpoint, fstype, _ in utils.GetMounts(): @@ -329,6 +359,29 @@ 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 partition mapping for storage_path and return the + first partition device path as a rootfs partition, and store allocated + loopback device path to stash. + If storage_path is not a multi-partition block device, just returns + 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["loopback-device"] = loop_dev_path + return dm_dev_paths[0] + def StartInstance(self, instance, block_devices, startup_paused): """Start an instance. @@ -336,6 +389,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 +405,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 an 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
