]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Multipath storage support module
authorDave Allan <dallan@redhat.com>
Tue, 8 Sep 2009 13:47:45 +0000 (15:47 +0200)
committerDaniel Veillard <veillard@redhat.com>
Tue, 8 Sep 2009 13:47:45 +0000 (15:47 +0200)
* configure.in src/Makefile.am src/storage_backend.[ch]
  src/storage_conf.[ch] src/storage_backend_mpath.[ch] po/POTFILES.in:
  add a new module for storage multipath, it requires device-mapper

configure.in
po/POTFILES.in
src/Makefile.am
src/storage_backend.c
src/storage_backend.h
src/storage_backend_mpath.c [new file with mode: 0644]
src/storage_backend_mpath.h [new file with mode: 0644]
src/storage_conf.c
src/storage_conf.h

index e457d866cd51a966d6eea958adacea281fe1ccc4..05278fd72c5c4499ed8bc37cf4b48a8790dce946 100644 (file)
@@ -1046,6 +1046,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])
 
@@ -1056,6 +1058,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
@@ -1177,6 +1180,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=
@@ -1677,6 +1700,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])
index 0a53dab069cf0ba37c3418722f164dbed6fcaa90..1586368b30511bed8faf52f908f4452057a06336 100644 (file)
@@ -38,6 +38,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/storage_encryption_conf.c
index 8e186a6799b3a362b06ab2e248272ae4ededf044..628edc5a018b60708fcc1d41781b26edd761bf33 100644 (file)
@@ -199,6 +199,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
 
@@ -451,6 +454,7 @@ endif
 
 # Needed to keep automake quiet about conditionals
 libvirt_driver_storage_la_SOURCES =
+libvirt_driver_storage_la_CFLAGS =
 if WITH_STORAGE_DIR
 if WITH_DRIVER_MODULES
 mod_LTLIBRARIES += libvirt_driver_storage.la
@@ -478,6 +482,11 @@ 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)
+libvirt_driver_storage_la_CFLAGS += $(DEVMAPPER_CFLAGS)
+endif
+
 if WITH_STORAGE_DISK
 libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_DISK_SOURCES)
 endif
@@ -539,6 +548,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)               \
@@ -607,6 +617,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
index 77243ef57645e28056c98c466ca084fa663ae415..5e04f35c147a907987040ca8c7af1cbe668bb804 100644 (file)
@@ -60,6 +60,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
@@ -90,6 +93,9 @@ static virStorageBackendPtr backends[] = {
 #if WITH_STORAGE_SCSI
     &virStorageBackendSCSI,
 #endif
+#if WITH_STORAGE_MPATH
+    &virStorageBackendMpath,
+#endif
 #if WITH_STORAGE_DISK
     &virStorageBackendDisk,
 #endif
@@ -811,6 +817,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);
index e80b05d07abe65ad9fb45211550b376a47bdb2a4..eb5bf87f19e21d32fb0220946b7733c0c1d3e613 100644 (file)
@@ -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 (file)
index 0000000..9afee5d
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * storage_backend_mpath.c: storage backend for multipath handling
+ *
+ * Copyright (C) 2009-2009 Red Hat, Inc.
+ * Copyright (C) 2009-2008 Dave Allan
+ *
+ * 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: Dave Allan <dallan@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 ret = 0;
+    int fd = -1;
+
+    if ((fd = open(target->path, O_RDONLY)) < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot open volume '%s'"),
+                             target->path);
+        ret = -1;
+        goto out;
+    }
+
+    if (virStorageBackendUpdateVolTargetInfoFD(conn,
+                                               target,
+                                               fd,
+                                               allocation,
+                                               capacity) < 0) {
+
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                          _("Failed to update volume target info for '%s'"),
+                          target->path);
+
+        ret = -1;
+        goto out;
+    }
+
+    if (virStorageBackendUpdateVolTargetFormatFD(conn,
+                                                 target,
+                                                 fd) < 0) {
+        virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                          _("Failed to update volume target format for '%s'"),
+                          target->path);
+
+        ret = -1;
+        goto out;
+    }
+
+out:
+    if (fd != -1) {
+        close(fd);
+    }
+    return ret;
+}
+
+
+static int
+virStorageBackendMpathNewVol(virConnectPtr conn,
+                             virStoragePoolObjPtr pool,
+                             const int devnum,
+                             const char *dev)
+{
+    virStorageVolDefPtr vol;
+    int ret = -1;
+
+    if (VIR_ALLOC(vol) < 0) {
+        virReportOOMError(conn);
+        goto cleanup;
+    }
+
+    vol->type = VIR_STORAGE_VOL_BLOCK;
+
+    if (virAsprintf(&(vol->name), "dm-%u", devnum) < 0) {
+        virReportOOMError(conn);
+        goto cleanup;
+    }
+
+    if (virAsprintf(&vol->target.path, "/dev/%s", dev) < 0) {
+        virReportOOMError(conn);
+        goto cleanup;
+    }
+
+    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);
+        goto cleanup;
+    }
+
+    /* XXX should use logical unit's UUID instead */
+    vol->key = strdup(vol->target.path);
+    if (vol->key == NULL) {
+        virReportOOMError(conn);
+        goto cleanup;
+    }
+
+    if (VIR_REALLOC_N(pool->volumes.objs,
+                      pool->volumes.count + 1) < 0) {
+        virReportOOMError(conn);
+        goto cleanup;
+    }
+    pool->volumes.objs[pool->volumes.count++] = vol;
+    pool->def->capacity += vol->capacity;
+    pool->def->allocation += vol->allocation;
+    ret = 0;
+
+cleanup:
+
+    if (ret != 0)
+        virStorageVolDefFree(vol);
+
+    return ret;
+}
+
+
+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 (STREQ(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 (file)
index 0000000..04960fb
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * storage_backend_scsi.h: storage backend for SCSI handling
+ *
+ * Copyright (C) 2009-2009 Red Hat, Inc.
+ * Copyright (C) 2009-2008 Dave Allan
+ *
+ * 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: Dave Allan <dallan@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__ */
index 110f0ad3d29d73958f37572a5f98fed9951f02f9..cb063cc2589bcf5632f216668819438f75190077 100644 (file)
@@ -53,7 +53,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,
@@ -198,6 +198,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),
index bcf9b933cd9c8e1b8eb6a5d3b149fdde34a10678..421d305f6f87fc7ffd9895a790b9b3229c7ffeb1 100644 (file)
@@ -119,6 +119,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,
 };