[ --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])
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
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=
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])
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
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
# 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
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
$(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) \
$(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
#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
#if WITH_STORAGE_SCSI
&virStorageBackendSCSI,
#endif
+#if WITH_STORAGE_MPATH
+ &virStorageBackendMpath,
+#endif
#if WITH_STORAGE_DISK
&virStorageBackendDisk,
#endif
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);
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,
--- /dev/null
+/*
+ * 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, ¶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,
+};
--- /dev/null
+/*
+ * 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__ */
VIR_STORAGE_POOL_LAST,
"dir", "fs", "netfs",
"logical", "disk", "iscsi",
- "scsi")
+ "scsi", "mpath")
VIR_ENUM_IMPL(virStoragePoolFormatFileSystem,
VIR_STORAGE_POOL_FS_LAST,
.formatToString = virStoragePoolFormatDiskTypeToString,
}
},
+ { .poolType = VIR_STORAGE_POOL_MPATH,
+ .volOptions = {
+ .formatToString = virStoragePoolFormatDiskTypeToString,
+ }
+ },
{ .poolType = VIR_STORAGE_POOL_DISK,
.poolOptions = {
.flags = (VIR_STORAGE_POOL_SOURCE_DEVICE),
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,
};