[libvirt] [PATCH 1/1] Add a simple pool type for multipath devices

David Allan dallan at redhat.com
Tue Jul 21 21:25:37 UTC 2009


This pool type contains volumes for the multipath devices that are present on the host.  It does not (yet) support any sort of multipath configuration, so that aspect of system administration must still be done at host build time.
---
 configure.in                |   24 +++
 po/POTFILES.in              |    1 +
 src/Makefile.am             |    9 +
 src/storage_backend.c       |   82 ++++++++++
 src/storage_backend.h       |    5 +-
 src/storage_backend_mpath.c |  344 +++++++++++++++++++++++++++++++++++++++++++
 src/storage_backend_mpath.h |   31 ++++
 src/storage_conf.c          |    7 +-
 src/storage_conf.h          |    1 +
 9 files changed, 502 insertions(+), 2 deletions(-)
 create mode 100644 src/storage_backend_mpath.c
 create mode 100644 src/storage_backend_mpath.h

diff --git a/configure.in b/configure.in
index 634e812..7a94373 100644
--- a/configure.in
+++ b/configure.in
@@ -906,6 +906,8 @@ AC_ARG_WITH([storage-iscsi],
 [  --with-storage-iscsi        with iSCSI backend for the storage driver (on)],[],[with_storage_iscsi=check])
 AC_ARG_WITH([storage-scsi],
 [  --with-storage-scsi         with SCSI backend for the storage driver (on)],[],[with_storage_scsi=check])
+AC_ARG_WITH([storage-mpath],
+[  --with-storage-mpath        with mpath backend for the storage driver (on)],[],[with_storage_mpath=check])
 AC_ARG_WITH([storage-disk],
 [  --with-storage-disk         with GPartd Disk backend for the storage driver (on)],[],[with_storage_disk=check])
 
@@ -916,6 +918,7 @@ if test "$with_libvirtd" = "no"; then
   with_storage_lvm=no
   with_storage_iscsi=no
   with_storage_scsi=no
+  with_storage_mpath=no
   with_storage_disk=no
 fi
 if test "$with_storage_dir" = "yes" ; then
@@ -1037,6 +1040,26 @@ if test "$with_storage_scsi" = "check"; then
 fi
 AM_CONDITIONAL([WITH_STORAGE_SCSI], [test "$with_storage_scsi" = "yes"])
 
+if test "$with_storage_mpath" = "check"; then
+   with_storage_mpath=yes
+
+   AC_DEFINE_UNQUOTED([WITH_STORAGE_MPATH], 1,
+     [whether mpath backend for storage driver is enabled])
+fi
+AM_CONDITIONAL([WITH_STORAGE_MPATH], [test "$with_storage_mpath" = "yes"])
+
+if test "$with_storage_mpath" = "yes"; then
+   DEVMAPPER_REQUIRED=0.0
+   DEVMAPPER_CFLAGS=
+   DEVMAPPER_LIBS=
+   PKG_CHECK_MODULES(DEVMAPPER, devmapper >= $DEVMAPPER_REQUIRED,
+    [], [
+    AC_MSG_ERROR(
+    [You must install device-mapper-devel >= $DEVMAPPER_REQUIRED to compile libvirt])
+    ])
+fi
+AC_SUBST([DEVMAPPER_CFLAGS])
+AC_SUBST([DEVMAPPER_LIBS])
 
 LIBPARTED_CFLAGS=
 LIBPARTED_LIBS=
@@ -1506,6 +1529,7 @@ AC_MSG_NOTICE([   NetFS: $with_storage_fs])
 AC_MSG_NOTICE([     LVM: $with_storage_lvm])
 AC_MSG_NOTICE([   iSCSI: $with_storage_iscsi])
 AC_MSG_NOTICE([    SCSI: $with_storage_scsi])
+AC_MSG_NOTICE([   mpath: $with_storage_mpath])
 AC_MSG_NOTICE([    Disk: $with_storage_disk])
 AC_MSG_NOTICE([])
 AC_MSG_NOTICE([Security Drivers])
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 7ccf3ab..f70936b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -36,6 +36,7 @@ src/storage_backend_fs.c
 src/storage_backend_iscsi.c
 src/storage_backend_logical.c
 src/storage_backend_scsi.c
+src/storage_backend_mpath.c
 src/storage_conf.c
 src/storage_driver.c
 src/test.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 79826b1..57bf9ba 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -184,6 +184,9 @@ STORAGE_DRIVER_ISCSI_SOURCES =					\
 STORAGE_DRIVER_SCSI_SOURCES =					\
 		storage_backend_scsi.h storage_backend_scsi.c
 
+STORAGE_DRIVER_MPATH_SOURCES =					\
+		storage_backend_mpath.h storage_backend_mpath.c
+
 STORAGE_DRIVER_DISK_SOURCES =					\
 		storage_backend_disk.h storage_backend_disk.c
 
@@ -436,6 +439,10 @@ if WITH_STORAGE_SCSI
 libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_SCSI_SOURCES)
 endif
 
+if WITH_STORAGE_MPATH
+libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_MPATH_SOURCES)
+endif
+
 if WITH_STORAGE_DISK
 libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_DISK_SOURCES)
 endif
@@ -495,6 +502,7 @@ EXTRA_DIST +=							\
 		$(STORAGE_DRIVER_LVM_SOURCES)			\
 		$(STORAGE_DRIVER_ISCSI_SOURCES)			\
 		$(STORAGE_DRIVER_SCSI_SOURCES)			\
+		$(STORAGE_DRIVER_MPATH_SOURCES)			\
 		$(STORAGE_DRIVER_DISK_SOURCES)			\
 		$(NODE_DEVICE_DRIVER_SOURCES)			\
 		$(NODE_DEVICE_DRIVER_HAL_SOURCES)		\
@@ -563,6 +571,7 @@ libvirt_la_LDFLAGS = $(VERSION_SCRIPT_FLAGS)libvirt.syms \
                     $(COVERAGE_CFLAGS:-f%=-Wc,-f%) \
                     $(LIBXML_LIBS) $(SELINUX_LIBS) \
 		    $(XEN_LIBS) $(DRIVER_MODULE_LIBS) \
+		    $(DEVMAPPER_LIBS) \
 		    @CYGWIN_EXTRA_LDFLAGS@ @MINGW_EXTRA_LDFLAGS@
 libvirt_la_CFLAGS = $(COVERAGE_CFLAGS) -DIN_LIBVIRT
 libvirt_la_DEPENDENCIES = $(libvirt_la_LIBADD) libvirt.syms
diff --git a/src/storage_backend.c b/src/storage_backend.c
index 1664804..0994fa0 100644
--- a/src/storage_backend.c
+++ b/src/storage_backend.c
@@ -59,6 +59,9 @@
 #if WITH_STORAGE_SCSI
 #include "storage_backend_scsi.h"
 #endif
+#if WITH_STORAGE_MPATH
+#include "storage_backend_mpath.h"
+#endif
 #if WITH_STORAGE_DISK
 #include "storage_backend_disk.h"
 #endif
@@ -89,6 +92,9 @@ static virStorageBackendPtr backends[] = {
 #if WITH_STORAGE_SCSI
     &virStorageBackendSCSI,
 #endif
+#if WITH_STORAGE_MPATH
+    &virStorageBackendMpath,
+#endif
 #if WITH_STORAGE_DISK
     &virStorageBackendDisk,
 #endif
@@ -770,6 +776,82 @@ virStorageBackendUpdateVolTargetInfoFD(virConnectPtr conn,
     return 0;
 }
 
+
+struct diskType {
+    int part_table_type;
+    unsigned short offset;
+    unsigned short length;
+    unsigned long long magic;
+};
+
+
+static struct diskType const disk_types[] = {
+    { VIR_STORAGE_POOL_DISK_LVM2, 0x218, 8, 0x31303020324D564CULL },
+    { VIR_STORAGE_POOL_DISK_GPT,  0x200, 8, 0x5452415020494645ULL },
+    { VIR_STORAGE_POOL_DISK_DVH,  0x0,   4, 0x41A9E50BULL },
+    { VIR_STORAGE_POOL_DISK_MAC,  0x0,   2, 0x5245ULL },
+    { VIR_STORAGE_POOL_DISK_BSD,  0x40,  4, 0x82564557ULL },
+    { VIR_STORAGE_POOL_DISK_SUN,  0x1fc, 2, 0xBEDAULL },
+    /*
+     * NOTE: pc98 is funky; the actual signature is 0x55AA (just like dos), so
+     * we can't use that.  At the moment I'm relying on the "dummy" IPL
+     * bootloader data that comes from parted.  Luckily, the chances of running
+     * into a pc98 machine running libvirt are approximately nil.
+     */
+    /*{ 0x1fe, 2, 0xAA55UL },*/
+    { VIR_STORAGE_POOL_DISK_PC98, 0x0,   8, 0x314C5049000000CBULL },
+    /*
+     * NOTE: the order is important here; some other disk types (like GPT and
+     * and PC98) also have 0x55AA at this offset.  For that reason, the DOS
+     * one must be the last one.
+     */
+    { VIR_STORAGE_POOL_DISK_DOS,  0x1fe, 2, 0xAA55ULL },
+    { -1,                         0x0,   0, 0x0ULL },
+};
+
+
+int
+virStorageBackendUpdateVolTargetFormatFD(virConnectPtr conn,
+                                         virStorageVolTargetPtr target,
+                                         int fd)
+{
+    int i;
+    off_t start;
+    unsigned char buffer[1024];
+    ssize_t bytes;
+
+    /* make sure to set the target format "unknown" to begin with */
+    target->format = VIR_STORAGE_POOL_DISK_UNKNOWN;
+
+    start = lseek(fd, 0, SEEK_SET);
+    if (start < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot seek to beginning of file '%s'"),
+                             target->path);
+        return -1;
+    }
+    bytes = saferead(fd, buffer, sizeof(buffer));
+    if (bytes < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot read beginning of file '%s'"),
+                             target->path);
+        return -1;
+    }
+
+    for (i = 0; disk_types[i].part_table_type != -1; i++) {
+        if (disk_types[i].offset + disk_types[i].length > bytes)
+            continue;
+        if (memcmp(buffer+disk_types[i].offset, &disk_types[i].magic,
+            disk_types[i].length) == 0) {
+            target->format = disk_types[i].part_table_type;
+            break;
+        }
+    }
+
+    return 0;
+}
+
+
 void virStorageBackendWaitForDevices(virConnectPtr conn)
 {
     virWaitForDevices(conn);
diff --git a/src/storage_backend.h b/src/storage_backend.h
index e80b05d..eb5bf87 100644
--- a/src/storage_backend.h
+++ b/src/storage_backend.h
@@ -91,7 +91,10 @@ int virStorageBackendUpdateVolTargetInfoFD(virConnectPtr conn,
                                            int fd,
                                            unsigned long long *allocation,
                                            unsigned long long *capacity);
-
+int
+virStorageBackendUpdateVolTargetFormatFD(virConnectPtr conn,
+                                         virStorageVolTargetPtr target,
+                                         int fd);
 void virStorageBackendWaitForDevices(virConnectPtr conn);
 
 char *virStorageBackendStablePath(virConnectPtr conn,
diff --git a/src/storage_backend_mpath.c b/src/storage_backend_mpath.c
new file mode 100644
index 0000000..8a2d90a
--- /dev/null
+++ b/src/storage_backend_mpath.c
@@ -0,0 +1,344 @@
+/*
+ * storage_backend_mpath.c: storage backend for multipath handling
+ *
+ * Copyright (C) 2007-2008 Red Hat, Inc.
+ * Copyright (C) 2007-2008 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange redhat com>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include <libdevmapper.h>
+
+#include "virterror_internal.h"
+#include "storage_conf.h"
+#include "storage_backend.h"
+#include "memory.h"
+#include "logging.h"
+
+#define VIR_FROM_THIS VIR_FROM_STORAGE
+
+static int
+virStorageBackendMpathUpdateVolTargetInfo(virConnectPtr conn,
+                                          virStorageVolTargetPtr target,
+                                          unsigned long long *allocation,
+                                          unsigned long long *capacity)
+{
+    int fd;
+
+    if ((fd = open(target->path, O_RDONLY)) < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot open volume '%s'"),
+                             target->path);
+        return -1;
+    }
+
+    if (virStorageBackendUpdateVolTargetInfoFD(conn,
+                                               target,
+                                               fd,
+                                               allocation,
+                                               capacity) < 0) {
+
+        VIR_ERROR(_("Failed to update volume target info for %s"),
+            target->path);
+
+        return -1;
+    }
+
+    if (virStorageBackendUpdateVolTargetFormatFD(conn,
+                                                 target,
+                                                 fd) < 0) {
+        VIR_ERROR(_("Failed to update volume target format for %s"),
+            target->path);
+
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+virStorageBackendMpathNewVol(virConnectPtr conn,
+                             virStoragePoolObjPtr pool,
+                             const int devnum,
+                             const char *dev)
+{
+    virStorageVolDefPtr vol;
+    int retval = 0;
+
+    if (VIR_ALLOC(vol) < 0) {
+        virReportOOMError(conn);
+        retval = -1;
+        goto out;
+    }
+
+    vol->type = VIR_STORAGE_VOL_BLOCK;
+
+    if (virAsprintf(&(vol->name), "dm-%u", devnum) < 0) {
+        virReportOOMError(conn);
+        retval = -1;
+        goto free_vol;
+    }
+
+    if (virAsprintf(&vol->target.path, "/dev/%s", dev) < 0) {
+        virReportOOMError(conn);
+        retval = -1;
+        goto free_vol;
+    }
+
+    if (virStorageBackendMpathUpdateVolTargetInfo(conn,
+                                                  &vol->target,
+                                                  &vol->allocation,
+                                                  &vol->capacity) < 0) {
+
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              _("Failed to update volume for '%s'"),
+                              vol->target.path);
+        retval = -1;
+        goto free_vol;
+    }
+
+    /* XXX should use logical unit's UUID instead */
+    vol->key = strdup(vol->target.path);
+    if (vol->key == NULL) {
+        virReportOOMError(conn);
+        retval = -1;
+        goto free_vol;
+    }
+
+    pool->def->capacity += vol->capacity;
+    pool->def->allocation += vol->allocation;
+
+    if (VIR_REALLOC_N(pool->volumes.objs,
+                      pool->volumes.count + 1) < 0) {
+        virReportOOMError(conn);
+        retval = -1;
+        goto free_vol;
+    }
+    pool->volumes.objs[pool->volumes.count++] = vol;
+
+    goto out;
+
+free_vol:
+    virStorageVolDefFree(vol);
+out:
+    return retval;
+}
+
+
+static int
+virStorageBackendIsMultipath(const char *devname)
+{
+    int ret = 0;
+    struct dm_task *dmt = NULL;
+    void *next = NULL;
+    uint64_t start, length;
+    char *target_type = NULL;
+    char *params = NULL;
+
+    dmt = dm_task_create(DM_DEVICE_TABLE);
+    if (dmt == NULL) {
+        ret = -1;
+        goto out;
+    }
+
+    if (dm_task_set_name(dmt, devname) == 0) {
+        ret = -1;
+        goto out;
+    }
+
+    dm_task_no_open_count(dmt);
+
+    if (!dm_task_run(dmt)) {
+        ret = -1;
+        goto out;
+    }
+
+    next = dm_get_next_target(dmt, next, &start, &length,
+                              &target_type, &params);
+
+    if (target_type == NULL) {
+        ret = -1;
+        goto out;
+    }
+
+    if (!(strcmp(target_type, "multipath"))) {
+        ret = 1;
+    }
+
+out:
+    if (dmt != NULL) {
+        dm_task_destroy(dmt);
+    }
+    return ret;
+}
+
+
+static int
+virStorageBackendGetMinorNumber(const char *devname, uint32_t *minor)
+{
+	int ret = -1;
+	struct dm_task *dmt;
+	struct dm_info info;
+
+	if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
+		goto out;
+	}
+
+	if (!dm_task_set_name(dmt, devname)) {
+		goto out;
+	}
+
+	if (!dm_task_run(dmt)) {
+		goto out;
+	}
+
+	if (!dm_task_get_info(dmt, &info)) {
+		goto out;
+	}
+
+	*minor = info.minor;
+	ret = 0;
+
+out:
+	if (dmt != NULL) {
+		dm_task_destroy(dmt);
+	}
+
+	return ret;
+}
+
+
+static int
+virStorageBackendCreateVols(virConnectPtr conn,
+                            virStoragePoolObjPtr pool,
+                            struct dm_names *names)
+{
+    int retval = 0, is_mpath = 0;
+    char *map_device = NULL;
+    uint32_t minor = -1;
+
+    do {
+        is_mpath = virStorageBackendIsMultipath(names->name);
+
+        if (is_mpath < 0) {
+            retval = -1;
+            goto out;
+        }
+
+        if (is_mpath == 1) {
+
+            if (virAsprintf(&map_device, "mapper/%s", names->name) < 0) {
+                virReportOOMError(conn);
+                retval = -1;
+                goto out;
+            }
+
+            if (virStorageBackendGetMinorNumber(names->name, &minor) < 0) {
+                retval = -1;
+                goto out;
+            }
+
+            virStorageBackendMpathNewVol(conn,
+                                         pool,
+                                         minor,
+                                         map_device);
+
+            VIR_FREE(map_device);
+        }
+
+        /* Given the way libdevmapper returns its data, I don't see
+         * any way to avoid this series of casts. */
+        names = (struct dm_names *)(((char *)names) + names->next);
+
+    } while (names->next);
+
+out:
+    return retval;
+}
+
+
+static int
+virStorageBackendGetMaps(virConnectPtr conn,
+                         virStoragePoolObjPtr pool)
+{
+    int retval = 0;
+    struct dm_task *dmt = NULL;
+    struct dm_names *names = NULL;
+
+    if (!(dmt = dm_task_create(DM_DEVICE_LIST))) {
+        retval = 1;
+        goto out;
+    }
+
+    dm_task_no_open_count(dmt);
+
+    if (!dm_task_run(dmt)) {
+        retval = 1;
+        goto out;
+    }
+
+    if (!(names = dm_task_get_names(dmt))) {
+        retval = 1;
+        goto out;
+    }
+
+    if (!names->dev) {
+        /* No devices found */
+        goto out;
+    }
+
+    virStorageBackendCreateVols(conn, pool, names);
+
+out:
+    if (dmt != NULL) {
+        dm_task_destroy (dmt);
+    }
+    return retval;
+}
+
+
+static int
+virStorageBackendMpathRefreshPool(virConnectPtr conn,
+                                  virStoragePoolObjPtr pool)
+{
+    int retval = 0;
+
+    VIR_ERROR(_("in %s"), __func__);
+
+    pool->def->allocation = pool->def->capacity = pool->def->available = 0;
+
+    virStorageBackendWaitForDevices(conn);
+
+    virStorageBackendGetMaps(conn, pool);
+
+    return retval;
+}
+
+
+virStorageBackend virStorageBackendMpath = {
+    .type = VIR_STORAGE_POOL_MPATH,
+
+    .refreshPool = virStorageBackendMpathRefreshPool,
+};
diff --git a/src/storage_backend_mpath.h b/src/storage_backend_mpath.h
new file mode 100644
index 0000000..9de2b79
--- /dev/null
+++ b/src/storage_backend_mpath.h
@@ -0,0 +1,31 @@
+/*
+ * storage_backend_scsi.h: storage backend for SCSI handling
+ *
+ * Copyright (C) 2007-2008 Red Hat, Inc.
+ * Copyright (C) 2007-2008 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange redhat com>
+ */
+
+#ifndef __VIR_STORAGE_BACKEND_MPATH_H__
+#define __VIR_STORAGE_BACKEND_MPATH_H__
+
+#include "storage_backend.h"
+
+extern virStorageBackend virStorageBackendMpath;
+
+#endif /* __VIR_STORAGE_BACKEND_MPATH_H__ */
diff --git a/src/storage_conf.c b/src/storage_conf.c
index 075279c..d94c425 100644
--- a/src/storage_conf.c
+++ b/src/storage_conf.c
@@ -58,7 +58,7 @@ VIR_ENUM_IMPL(virStoragePool,
               VIR_STORAGE_POOL_LAST,
               "dir", "fs", "netfs",
               "logical", "disk", "iscsi",
-              "scsi")
+              "scsi", "mpath")
 
 VIR_ENUM_IMPL(virStoragePoolFormatFileSystem,
               VIR_STORAGE_POOL_FS_LAST,
@@ -203,6 +203,11 @@ static virStoragePoolTypeInfo poolTypeInfo[] = {
             .formatToString = virStoragePoolFormatDiskTypeToString,
         }
     },
+    { .poolType = VIR_STORAGE_POOL_MPATH,
+      .volOptions = {
+            .formatToString = virStoragePoolFormatDiskTypeToString,
+        }
+    },
     { .poolType = VIR_STORAGE_POOL_DISK,
       .poolOptions = {
             .flags = (VIR_STORAGE_POOL_SOURCE_DEVICE),
diff --git a/src/storage_conf.h b/src/storage_conf.h
index a6c3650..59b8166 100644
--- a/src/storage_conf.h
+++ b/src/storage_conf.h
@@ -116,6 +116,7 @@ enum virStoragePoolType {
     VIR_STORAGE_POOL_DISK,     /* Disk partitions */
     VIR_STORAGE_POOL_ISCSI,    /* iSCSI targets */
     VIR_STORAGE_POOL_SCSI,     /* SCSI HBA */
+    VIR_STORAGE_POOL_MPATH,    /* Multipath devices */
 
     VIR_STORAGE_POOL_LAST,
 };
-- 
1.6.0.6




More information about the libvir-list mailing list