storage: Fix virStorageBackendDiskDeleteVol for device mapper

Commit id 'df1011ca8' modified virStorageBackendDiskDeleteVol to use
"dmsetup remove --force" to remove the volume, but left things in an
inconsistent state since the partition still existed on the disk and
only the device mapper device (/dev/dm-#) was removed.

Prior to commit '1895b421' (or '1ffd82bb' and '471e1c4e'), this could
go unnoticed since virStorageBackendDiskRefreshPool wasn't called.
However, the pool would be unusable since the /dev/dm-# device would
be removed even though the partition was not removed unless a multipathd
restart reset the link. That would of course make the volume appear again
in the pool after a refresh or pool start after libvirt reload.

This patch removes the 'dmsetup' logic and re-implements the partition
deletion logic for device mapper devices. The removal of the partition
via 'parted rm --script #' will cause udev device change logic to allow
multipathd to handle removing the dm-* device associated with the partition.
This commit is contained in:
John Ferlan
2016-04-26 08:53:57 -04:00
parent e7bde8d319
commit 8cdff0b93f
3 changed files with 70 additions and 46 deletions

View File

@@ -1958,19 +1958,12 @@ LIBPARTED_LIBS=
if test "$with_storage_disk" = "yes" || if test "$with_storage_disk" = "yes" ||
test "$with_storage_disk" = "check"; then test "$with_storage_disk" = "check"; then
AC_PATH_PROG([PARTED], [parted], [], [$PATH:/sbin:/usr/sbin]) AC_PATH_PROG([PARTED], [parted], [], [$PATH:/sbin:/usr/sbin])
AC_PATH_PROG([DMSETUP], [dmsetup], [], [$PATH:/sbin:/usr/sbin])
if test -z "$PARTED" ; then if test -z "$PARTED" ; then
PARTED_FOUND=no PARTED_FOUND=no
else else
PARTED_FOUND=yes PARTED_FOUND=yes
fi fi
if test -z "$DMSETUP" ; then
DMSETUP_FOUND=no
else
DMSETUP_FOUND=yes
fi
if test "$PARTED_FOUND" = "yes" && test "x$PKG_CONFIG" != "x" ; then if test "$PARTED_FOUND" = "yes" && test "x$PKG_CONFIG" != "x" ; then
PKG_CHECK_MODULES([LIBPARTED], [libparted >= $PARTED_REQUIRED], [], PKG_CHECK_MODULES([LIBPARTED], [libparted >= $PARTED_REQUIRED], [],
[PARTED_FOUND=no]) [PARTED_FOUND=no])
@@ -1989,12 +1982,12 @@ if test "$with_storage_disk" = "yes" ||
fi fi
if test "$with_storage_disk" = "yes" && if test "$with_storage_disk" = "yes" &&
test "$PARTED_FOUND:$DMSETUP_FOUND" != "yes:yes"; then test "$PARTED_FOUND" != "yes"; then
AC_MSG_ERROR([Need both parted and dmsetup for disk storage driver]) AC_MSG_ERROR([Need parted for disk storage driver])
fi fi
if test "$with_storage_disk" = "check"; then if test "$with_storage_disk" = "check"; then
if test "$PARTED_FOUND:$DMSETUP_FOUND" != "yes:yes"; then if test "$PARTED_FOUND" != "yes"; then
with_storage_disk=no with_storage_disk=no
else else
with_storage_disk=yes with_storage_disk=yes
@@ -2006,8 +1999,6 @@ if test "$with_storage_disk" = "yes" ||
[whether Disk backend for storage driver is enabled]) [whether Disk backend for storage driver is enabled])
AC_DEFINE_UNQUOTED([PARTED],["$PARTED"], AC_DEFINE_UNQUOTED([PARTED],["$PARTED"],
[Location or name of the parted program]) [Location or name of the parted program])
AC_DEFINE_UNQUOTED([DMSETUP],["$DMSETUP"],
[Location or name of the dmsetup program])
fi fi
fi fi
AM_CONDITIONAL([WITH_STORAGE_DISK], [test "$with_storage_disk" = "yes"]) AM_CONDITIONAL([WITH_STORAGE_DISK], [test "$with_storage_disk" = "yes"])

View File

@@ -83,6 +83,8 @@ int main(int argc, char **argv)
return 1; return 1;
} }
/* NB: Changes to the following algorithm will need corresponding
* changes to virStorageBackendDiskDeleteVol */
path = argv[1]; path = argv[1];
if (virIsDevMapperDevice(path)) { if (virIsDevMapperDevice(path)) {
/* If the path ends with a number or we explicitly request it for /* If the path ends with a number or we explicitly request it for

View File

@@ -799,6 +799,31 @@ virStorageBackendDiskPartBoundaries(virStoragePoolObjPtr pool,
} }
/* virStorageBackendDiskDeleteVol
* @conn: Pointer to a libvirt connection
* @pool: Pointer to the storage pool
* @vol: Pointer to the volume definition
* @flags: flags (unused for now)
*
* This API will remove the disk volume partition either from direct
* API call or as an error path during creation when the partition
* name provided during create doesn't match the name read from
* virStorageBackendDiskReadPartitions.
*
* For a device mapper device, device respresentation is dependant upon
* device mapper configuration, but the general rule of thumb is that at
* creation if a device name ends with a number, then a partition separator
* "p" is added to the created name; otherwise, if the device name doesn't
* end with a number, then there is no partition separator. This name is
* what ends up in the vol->target.path. This ends up being a link to a
* /dev/mapper/dm-# device which cannot be used in the algorithm to determine
* which partition to remove, but a properly handled target.path can be.
*
* For non device mapper devices, just need to resolve the link of the
* vol->target.path in order to get the path.
*
* Returns 0 on success, -1 on failure with error message set.
*/
static int static int
virStorageBackendDiskDeleteVol(virConnectPtr conn, virStorageBackendDiskDeleteVol(virConnectPtr conn,
virStoragePoolObjPtr pool, virStoragePoolObjPtr pool,
@@ -807,7 +832,9 @@ virStorageBackendDiskDeleteVol(virConnectPtr conn,
{ {
char *part_num = NULL; char *part_num = NULL;
char *devpath = NULL; char *devpath = NULL;
char *dev_name, *srcname; char *dev_name;
char *src_path = pool->def->source.devices[0].path;
char *srcname = last_component(src_path);
virCommandPtr cmd = NULL; virCommandPtr cmd = NULL;
bool isDevMapperDevice; bool isDevMapperDevice;
int rc = -1; int rc = -1;
@@ -817,56 +844,60 @@ virStorageBackendDiskDeleteVol(virConnectPtr conn,
if (!vol->target.path) { if (!vol->target.path) {
virReportError(VIR_ERR_INVALID_ARG, virReportError(VIR_ERR_INVALID_ARG,
_("volume target path empty for source path '%s'"), _("volume target path empty for source path '%s'"),
pool->def->source.devices[0].path); src_path);
return -1; return -1;
} }
if (virFileResolveLink(vol->target.path, &devpath) < 0) { /* NB: This is the corollary to the algorithm in libvirt_parthelper
virReportSystemError(errno, * (parthelper.c) that is used to generate the target.path name
_("Couldn't read volume target path '%s'"), * for use by libvirt. Changes to either, need to be reflected
vol->target.path); * in both places */
goto cleanup; isDevMapperDevice = virIsDevMapperDevice(vol->target.path);
if (isDevMapperDevice) {
dev_name = last_component(vol->target.path);
} else {
if (virFileResolveLink(vol->target.path, &devpath) < 0) {
virReportSystemError(errno,
_("Couldn't read volume target path '%s'"),
vol->target.path);
goto cleanup;
}
dev_name = last_component(devpath);
} }
dev_name = last_component(devpath);
srcname = last_component(pool->def->source.devices[0].path);
VIR_DEBUG("dev_name=%s, srcname=%s", dev_name, srcname); VIR_DEBUG("dev_name=%s, srcname=%s", dev_name, srcname);
isDevMapperDevice = virIsDevMapperDevice(devpath); if (!STRPREFIX(dev_name, srcname)) {
if (!isDevMapperDevice && !STRPREFIX(dev_name, srcname)) {
virReportError(VIR_ERR_INTERNAL_ERROR, virReportError(VIR_ERR_INTERNAL_ERROR,
_("Volume path '%s' did not start with parent " _("Volume path '%s' did not start with parent "
"pool source device name."), dev_name); "pool source device name."), dev_name);
goto cleanup; goto cleanup;
} }
if (!isDevMapperDevice) { part_num = dev_name + strlen(srcname);
part_num = dev_name + strlen(srcname);
if (*part_num == 0) { /* For device mapper and we have a partition character 'p' as the
virReportError(VIR_ERR_INTERNAL_ERROR, * current character, let's move beyond that before checking part_num */
_("cannot parse partition number from target " if (isDevMapperDevice && *part_num == 'p')
"'%s'"), dev_name); part_num++;
goto cleanup;
}
/* eg parted /dev/sda rm 2 */ if (*part_num == 0) {
cmd = virCommandNewArgList(PARTED, virReportError(VIR_ERR_INTERNAL_ERROR,
pool->def->source.devices[0].path, _("cannot parse partition number from target "
"rm", "'%s'"), dev_name);
"--script", goto cleanup;
part_num,
NULL);
if (virCommandRun(cmd, NULL) < 0)
goto cleanup;
} else {
cmd = virCommandNewArgList(DMSETUP, "remove", "--force", devpath, NULL);
if (virCommandRun(cmd, NULL) < 0)
goto cleanup;
} }
/* eg parted /dev/sda rm 2 or /dev/mapper/mpathc rm 2 */
cmd = virCommandNewArgList(PARTED,
src_path,
"rm",
"--script",
part_num,
NULL);
if (virCommandRun(cmd, NULL) < 0)
goto cleanup;
/* Refreshing the pool is the easiest option as LOGICAL and EXTENDED /* Refreshing the pool is the easiest option as LOGICAL and EXTENDED
* partition allocation/capacity management is handled within * partition allocation/capacity management is handled within
* virStorageBackendDiskMakeDataVol and trying to redo that logic * virStorageBackendDiskMakeDataVol and trying to redo that logic