diff --git a/configure.in b/configure.in index e457d866cd..05278fd72c 100644 --- a/configure.in +++ b/configure.in @@ -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]) diff --git a/po/POTFILES.in b/po/POTFILES.in index 0a53dab069..1586368b30 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index 8e186a6799..628edc5a01 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 diff --git a/src/storage_backend.c b/src/storage_backend.c index 77243ef576..5e04f35c14 100644 --- a/src/storage_backend.c +++ b/src/storage_backend.c @@ -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); diff --git a/src/storage_backend.h b/src/storage_backend.h index e80b05d07a..eb5bf87f19 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 0000000000..9afee5d810 --- /dev/null +++ b/src/storage_backend_mpath.c @@ -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 + */ + +#include + +#include +#include +#include +#include + +#include + +#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, ¶ms); + + 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 index 0000000000..04960fb786 --- /dev/null +++ b/src/storage_backend_mpath.h @@ -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 + */ + +#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 110f0ad3d2..cb063cc258 100644 --- a/src/storage_conf.c +++ b/src/storage_conf.c @@ -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), diff --git a/src/storage_conf.h b/src/storage_conf.h index bcf9b933cd..421d305f6f 100644 --- a/src/storage_conf.h +++ b/src/storage_conf.h @@ -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, };