Added two functions to the utils.storage namespace. - CreateBdevPartitionMapping will allocate a loopback device and a device-mapper device to provide a mapping device for each partition of the block device. - ReleaseBdevPartitionMapping is the inverse function of CreateBdevPartitionMapping. This function call is required to release resources allocated by CreateBdevPartitionMapping. These functions depend on losetup(1) and kpartx(1).
Signed-off-by: Yuto KAWAMURA(kawamuray) <[email protected]> --- lib/utils/storage.py | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/lib/utils/storage.py b/lib/utils/storage.py index 64e2ec2..13bd563 100644 --- a/lib/utils/storage.py +++ b/lib/utils/storage.py @@ -25,7 +25,9 @@ import logging from ganeti import constants - +from ganeti import errors +from ganeti.utils import io as utils_io +from ganeti.utils import process as utils_process def GetDiskTemplatesOfStorageTypes(*storage_types): """Given the storage type, returns a list of disk templates based on that @@ -206,3 +208,68 @@ def GetDiskLabels(prefix, num_disks, start=0): for i in range(start, num_disks): yield prefix + _GetDiskSuffix(i) + + +def CreateBdevPartitionMapping(image_path): + """Create dm device for each partition of disk image. + + This operation will allocate a loopback and a device-mapper device to map + partitions. + You must call L{ReleaseBdevPartitionMapping} to clean up resources allocated + by this function call. + + @type image_path: string + @param image_path: path of multi-partition disk image + @rtype: tuple(string, list(string)) or NoneType + @return: returns the tuple(loopback_device, list(device_mapper_files)) if + image_path is a multi-partition disk image. otherwise, returns None. + + """ + # Unfortunately, there are two different losetup commands in this world. + # One has the '-s' switch and the other has the '--show' switch to provide the + # same functionality. + result = utils_process.RunCmd(["losetup", "-f", "-s", image_path]) + if result.failed and "invalid option -- 's'" in result.stderr: + result = utils_process.RunCmd(["losetup", "-f", "--show", image_path]) + if result.failed: + raise errors.CommandError("Failed to setup loop device for %s: %s" % + (image_path, result.output)) + loop_dev_path = result.stdout.strip() + logging.debug("Loop dev %s allocated for %s", loop_dev_path, image_path) + + result = utils_process.RunCmd(["kpartx", "-a", "-v", loop_dev_path]) + if result.failed: + # Just try to cleanup allocated loop device + utils_process.RunCmd(["losetup", "-d", loop_dev_path]) + raise errors.CommandError("Failed to add partition mapping for %s: %s" % + (image_path, result.output)) + dm_devs = [x.split(" ") for x in result.stdout.split("\n") if x] + if dm_devs: + dm_dev_paths = [utils_io.PathJoin("/dev/mapper", x[2]) for x in dm_devs] + return (loop_dev_path, dm_dev_paths) + else: + # image_path is not a multi partition disk image, no need to use + # device-mapper. + logging.debug("Release loop dev %s allocated for %s", + loop_dev_path, image_path) + ReleaseBdevPartitionMapping(loop_dev_path) + return None + + +def ReleaseBdevPartitionMapping(loop_dev_path): + """Release allocated dm devices and loopback devices. + + @type loop_dev_path: string + @param loop_dev_path: path of loopback device returned by + L{CreateBdevPartitionMapping} + + """ + result = utils_process.RunCmd(["kpartx", "-d", loop_dev_path]) + if result.failed: + raise errors.CommandError("Failed to release partition mapping of %s: %s" % + (loop_dev_path, result.output)) + + result = utils_process.RunCmd(["losetup", "-d", loop_dev_path]) + if result.failed: + raise errors.CommandError("Failed to detach %s: %s" % + (loop_dev_path, result.output)) -- 1.8.5.5
