Last March, DanPB posted code to create pools from SCSI HBAs. This patch is simply bringing that code into the current tree. * src/storage_backend_scsi.[ch] contain the pool implementataion * There are small changes to several other source files to integrate the new pool type. --- configure.in | 13 ++- po/POTFILES.in | 1 + src/Makefile.am | 9 + src/storage_backend.c | 6 + src/storage_backend_scsi.c | 471 ++++++++++++++++++++++++++++++++++++++++++++ src/storage_backend_scsi.h | 45 +++++ src/storage_conf.c | 18 ++ 7 files changed, 562 insertions(+), 1 deletions(-) create mode 100644 src/storage_backend_scsi.c create mode 100644 src/storage_backend_scsi.h diff --git a/configure.in b/configure.in index d2e8252..b88a7d5 100644 --- a/configure.in +++ b/configure.in @@ -739,6 +739,8 @@ AC_ARG_WITH([storage-lvm], [ --with-storage-lvm with LVM backend for the storage driver (on)],[],[with_storage_lvm=check]) 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-disk], [ --with-storage-disk with GPartd Disk backend for the storage driver (on)],[],[with_storage_disk=check]) @@ -748,6 +750,7 @@ if test "$with_libvirtd" = "no"; then with_storage_fs=no with_storage_lvm=no with_storage_iscsi=no + with_storage_scsi=no with_storage_disk=no fi if test "$with_storage_dir" = "yes" ; then @@ -876,6 +879,13 @@ if test "$with_storage_iscsi" = "yes" -o "$with_storage_iscsi" = "check"; then fi AM_CONDITIONAL([WITH_STORAGE_ISCSI], [test "$with_storage_iscsi" = "yes"]) +if test "$with_storage_scsi" = "check"; then + with_storage_scsi=yes + + AC_DEFINE_UNQUOTED([WITH_STORAGE_SCSI], 1, + [whether SCSI backend for storage driver is enabled]) + AM_CONDITIONAL([WITH_STORAGE_SCSI], [test "$with_storage_scsi" = "yes"]) +fi LIBPARTED_CFLAGS= @@ -1158,7 +1168,7 @@ LV_LIBTOOL_OBJDIR=${lt_cv_objdir-.} AC_SUBST([LV_LIBTOOL_OBJDIR]) dnl HAL or DeviceKit library for host device enumeration -HAL_REQUIRED=0.0 +HAL_REQUIRED=0.5.0 HAL_CFLAGS= HAL_LIBS= AC_ARG_WITH([hal], @@ -1312,6 +1322,7 @@ AC_MSG_NOTICE([ FS: $with_storage_fs]) 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([ Disk: $with_storage_disk]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([Driver Loadable Modules]) diff --git a/po/POTFILES.in b/po/POTFILES.in index 7461f93..4433183 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -27,6 +27,7 @@ src/storage_backend_disk.c src/storage_backend_fs.c src/storage_backend_iscsi.c src/storage_backend_logical.c +src/storage_backend_scsi.c src/storage_conf.c src/storage_driver.c src/test.c diff --git a/src/Makefile.am b/src/Makefile.am index 3a798d2..f1dd789 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,6 +9,7 @@ INCLUDES = \ $(XEN_CFLAGS) \ $(SELINUX_CFLAGS) \ $(DRIVER_MODULE_CFLAGS) \ + $(POLKIT_CFLAGS) \ -DLIBDIR=\""$(libdir)"\" \ -DBINDIR=\""$(libexecdir)"\" \ -DSBINDIR=\""$(sbindir)"\" \ @@ -158,6 +159,9 @@ STORAGE_DRIVER_LVM_SOURCES = \ STORAGE_DRIVER_ISCSI_SOURCES = \ storage_backend_iscsi.h storage_backend_iscsi.c +STORAGE_DRIVER_SCSI_SOURCES = \ + storage_backend_scsi.h storage_backend_scsi.c + STORAGE_DRIVER_DISK_SOURCES = \ storage_backend_disk.h storage_backend_disk.c @@ -344,6 +348,10 @@ if WITH_STORAGE_ISCSI libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_ISCSI_SOURCES) endif +if WITH_STORAGE_SCSI +libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_SCSI_SOURCES) +endif + if WITH_STORAGE_DISK libvirt_driver_storage_la_SOURCES += $(STORAGE_DRIVER_DISK_SOURCES) endif @@ -392,6 +400,7 @@ EXTRA_DIST += \ $(STORAGE_DRIVER_FS_SOURCES) \ $(STORAGE_DRIVER_LVM_SOURCES) \ $(STORAGE_DRIVER_ISCSI_SOURCES) \ + $(STORAGE_DRIVER_SCSI_SOURCES) \ $(STORAGE_DRIVER_DISK_SOURCES) \ $(NODE_DEVICE_DRIVER_SOURCES) \ $(NODE_DEVICE_DRIVER_HAL_SOURCES) \ diff --git a/src/storage_backend.c b/src/storage_backend.c index 8408f34..95173fa 100644 --- a/src/storage_backend.c +++ b/src/storage_backend.c @@ -54,6 +54,9 @@ #if WITH_STORAGE_ISCSI #include "storage_backend_iscsi.h" #endif +#if WITH_STORAGE_SCSI +#include "storage_backend_scsi.h" +#endif #if WITH_STORAGE_DISK #include "storage_backend_disk.h" #endif @@ -78,6 +81,9 @@ static virStorageBackendPtr backends[] = { #if WITH_STORAGE_ISCSI &virStorageBackendISCSI, #endif +#if WITH_STORAGE_SCSI + &virStorageBackendSCSI, +#endif #if WITH_STORAGE_DISK &virStorageBackendDisk, #endif diff --git a/src/storage_backend_scsi.c b/src/storage_backend_scsi.c new file mode 100644 index 0000000..236d894 --- /dev/null +++ b/src/storage_backend_scsi.c @@ -0,0 +1,471 @@ +/* + * storage_backend_scsi.c: 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> + */ + +#include <config.h> + +#include <c-ctype.h> +#include <unistd.h> +#include <stdio.h> +#include <hal/libhal.h> + +#include "virterror_internal.h" +#include "storage_backend_scsi.h" +#include "memory.h" + +#define VIR_FROM_THIS VIR_FROM_STORAGE + +#define LINUX_SYSFS_SCSI_HOST_PREFIX "/sys/class/scsi_host/" +#define LINUX_SYSFS_SCSI_HOST_POSTFIX "/device" + + +/** + * virStorageBackendSCSIMakeHostDevice: + * @conn: report errors agains + * @pool: pool to resolve + * @buf: pre-allocated buffer to fill with path name + * @buflen: size of buffer + * + * Resolve a HBA name to HBA device path + * + * Given a pool object configured for a SCSI HBA, resolve the HBA name + * into the canonical sysfs path for the HBA device. This can then be + * used to lookup the device in HAL. + * + * Returns 0 on success, -1 on failure + */ +static int virStorageBackendSCSIMakeHostDevice(virConnectPtr conn, + virStoragePoolObjPtr pool, + char *buf, + size_t buflen) +{ + char *dev = NULL; + char *tmp = pool->def->source.adapter; + char relLink[PATH_MAX], absLink[PATH_MAX]; + ssize_t len; + + if (!tmp) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Missing adapter name for pool")); + return -1; + } + + /* Sanity check host adapter name - should only be 0-9, a-z. + * Anything else is bogus & so we reject it, to prevent them + * making us read arbitrary paths on host + */ + while (*tmp) { + if (!c_isalnum(*tmp)) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Invalid character in pool adapter name '%s'"), + pool->def->source.adapter); + return -1; + } + tmp++; + } + + /* + * First get the class based path eg + * + * /sys/class/scsi_host/host1/device + */ + if ((dev = malloc(sizeof(LINUX_SYSFS_SCSI_HOST_PREFIX) + + strlen(pool->def->source.adapter) + + sizeof(LINUX_SYSFS_SCSI_HOST_POSTFIX) + + 1)) == NULL) { + virReportOOMError(conn); + return -1; + } + + strcpy(dev, LINUX_SYSFS_SCSI_HOST_PREFIX); + strcat(dev, pool->def->source.adapter); + strcat(dev, LINUX_SYSFS_SCSI_HOST_POSTFIX); + + /* + * Now resolve the class based path symlink to the real + * device path, which is likely under PCI bus hierarchy + * and is the path tracked by HAL + */ + /* Readlink does not null terminate, so we reserve one byte */ + if ((len = readlink(dev, relLink, sizeof(relLink)-1)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot find SCSI host adapter '%s' at '%s'"), + pool->def->source.adapter, dev); + free(dev); + return -1; + } + relLink[len] = '\0'; + + + /* + * The symlink is relative, so now we have to turn it + * into a absolute path + */ + if ((tmp = realloc(dev, + sizeof(LINUX_SYSFS_SCSI_HOST_PREFIX) + + strlen(pool->def->source.adapter) + + 1 + + strlen(relLink) + + 1)) == NULL) { + virReportOOMError(conn); + free(dev); + return -1; + } + dev = tmp; + + strcpy(dev, LINUX_SYSFS_SCSI_HOST_PREFIX); + strcat(dev, pool->def->source.adapter); + strcat(dev, "/"); + strcat(dev, relLink); + + /* + * And finally canonicalize the absolute path to remove the + * copious .. components + */ + if (!realpath(dev, absLink)) { + char ebuf[1024]; + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot canonicalize link: %s"), + virStrerror(errno, ebuf, sizeof ebuf)); + free(dev); + return -1; + } + free(dev); + + if (strlen(absLink) >= buflen) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("link too long for buffer")); + return -1; + } + strcpy(buf, absLink); + + return 0; +} + + +/** + * virStorageBackendSCSICreateVol + * + * Allocate a virStorageVolDef object for the specified + * metadata and hook it into the pool + * + * Returns 0 on success, -1 on failure + */ +static int +virStorageBackendSCSICreateVol(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char *name, + const char *path, + const char *key, + unsigned long long size) +{ + virStorageVolDefPtr vol; + char *tmppath; + + if (VIR_ALLOC(vol) < 0) { + virReportOOMError(conn); + goto cleanup; + } + + vol->type = VIR_STORAGE_VOL_BLOCK; + + tmppath = strdup(path); + vol->name = strdup(name); + vol->key = strdup(key); + vol->allocation = vol->capacity = size; + + if ((vol->name == NULL) || + (vol->key == NULL) || + (tmppath == NULL)) { + virReportOOMError(conn); + goto cleanup; + } + + /* Now figure out the stable path + * + * XXX this method is O(N) because it scans the pool target + * dir every time its run. Should figure out a more efficient + * way of doing this... + */ + if ((vol->target.path = virStorageBackendStablePath(conn, + pool, + tmppath)) == NULL) + goto cleanup; + + if (tmppath != vol->target.path) + free(tmppath); + tmppath = NULL; + + if (virStorageBackendUpdateVolInfo(conn, vol, 1) < 0) + goto cleanup; + + pool->def->capacity += vol->capacity; + pool->def->allocation += vol->allocation; + + if (VIR_REALLOC_N(pool->volumes.objs, + pool->volumes.count+1) < 0) { + virReportOOMError(conn); + virStorageVolDefFree(vol); + goto cleanup; + } + pool->volumes.objs[pool->volumes.count++] = vol; + + return 0; + +cleanup: + free(tmppath); + virStorageVolDefFree(vol); + return -1; +} + + +/** + * virStorageBackendSCSIAddLUN: + * + * @conn: connection to report errors against + * @pool: pool to register volume with + * @ctx: HAL context + * @devname: HAL device name for LUN + * + * Given a HAL device supported 'block' and 'storage' capabilities + * register it as a volume in the pool + * + * Returns 0 on success, -1 on error + */ +static int +virStorageBackendSCSIAddLUN(virConnectPtr conn, + virStoragePoolObjPtr pool, + LibHalContext *ctx, + const char *devname) +{ + char **strdevs = NULL; + int numstrdevs = 0; + char *dev = NULL, *key = NULL; + unsigned long long size; + int host, bus, target, lun; + int n = -1, i; + int ret = -1; + char name[100]; + DBusError derr = DBUS_ERROR_INIT; + + if ((strdevs = libhal_manager_find_device_string_match(ctx, + "info.parent", + devname, + &numstrdevs, + &derr)) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("HAL failed to lookup LUNs for '%s': %s"), + devname, derr.message); + goto cleanup; + } + for (i = 0 ; i < numstrdevs && n == -1 ; i++) { + char *cat = libhal_device_get_property_string(ctx, + strdevs[0], + "info.category", + NULL); + /* XXX derr */ + if (cat != NULL) { + if (STREQ(cat, "storage")) + n = i; + free(cat); + } + } + /* No storage vol for device - probably a removable vol so ignore */ + if (n == -1) { + ret = 0; + goto cleanup; + } + +#define HAL_GET_PROPERTY(path, name, err, type, value) \ + (value) = libhal_device_get_property_ ## type(ctx, (path), (name), (err)); \ + if (dbus_error_is_set((err))) { \ + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, \ + "unable to lookup '%s' property on %s: %s", \ + (name), (path), derr.message); \ + goto cleanup; \ + } + + /* These are props of the physical device */ + HAL_GET_PROPERTY(devname, "scsi.host", &derr, int, host); + HAL_GET_PROPERTY(devname, "scsi.bus", &derr, int, bus); + HAL_GET_PROPERTY(devname, "scsi.target", &derr, int, target); + HAL_GET_PROPERTY(devname, "scsi.lun", &derr, int, lun); + + /* These are props of the logic device */ + HAL_GET_PROPERTY(strdevs[0], "block.device", &derr, string, dev); + /* + * XXX storage.serial is not actually unique if they have + * multipath on the fibre channel adapter + */ + HAL_GET_PROPERTY(strdevs[0], "storage.serial", &derr, string, key); + HAL_GET_PROPERTY(strdevs[0], "storage.size", &derr, uint64, size); + +#undef HAL_GET_PROPERTY + + snprintf(name, sizeof(name), "%d:%d:%d:%d", host, bus, target, lun); + name[sizeof(name)-1] = '\0'; + + if (virStorageBackendSCSICreateVol(conn, pool, name, dev, key, size) < 0) + goto cleanup; + ret = 0; + +cleanup: + for (i = 0 ; strdevs && i < numstrdevs ; i++) + free(strdevs[i]); + free(strdevs); + free(dev); + free(key); + if (dbus_error_is_set(&derr)) + dbus_error_free(&derr); + return ret; +} + + +/** + * virStorageBackendSCSIRefreshPool: + * + * Query HAL for all storage devices associated with a specific + * SCSI HBA. + * + * XXX, currently we only support regular devices in /dev/, + * or /dev/disk/by-XXX. In future we also need to be able to + * map to multipath devices setup under /dev/mpath/XXXX. + */ +static int +virStorageBackendSCSIRefreshPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + char hostdevice[PATH_MAX]; + LibHalContext *ctx = NULL; + DBusConnection *sysbus = NULL; + DBusError derr = DBUS_ERROR_INIT; + char **hbadevs = NULL, **blkdevs = NULL; + int numhbadevs = 0, numblkdevs = 0; + int i, ret = -1; + + /* Get the canonical sysfs path for the HBA device */ + if (virStorageBackendSCSIMakeHostDevice(conn, pool, + hostdevice, sizeof(hostdevice)) < 0) + goto cleanup; + + if ((ctx = libhal_ctx_new()) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to allocate HAL context")); + goto cleanup; + } + + sysbus = dbus_bus_get(DBUS_BUS_SYSTEM, &derr); + if (sysbus == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to get dbus system bus: '%s'"), + derr.message); + goto cleanup; + } + + if (!libhal_ctx_set_dbus_connection(ctx, sysbus)) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to set HAL dbus connection")); + goto cleanup; + } + + + if (!libhal_ctx_init(ctx, &derr)) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to initialize HAL context: '%s'"), + derr.message); + goto cleanup; + } + + if ((hbadevs = libhal_manager_find_device_string_match(ctx, + "linux.sysfs_path", + hostdevice, + &numhbadevs, + &derr)) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("HAL failed to lookup device '%s': %s"), + hostdevice, derr.message); + goto cleanup; + } + + if (numhbadevs != 1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected number of HBA devices from HAL " + "(expected 1, got %d) when looking up device " + "'%s'"), numhbadevs, hostdevice); + goto cleanup; + } + + if ((blkdevs = libhal_manager_find_device_string_match(ctx, + "info.parent", + hbadevs[0], + &numblkdevs, + &derr)) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("failed to lookup LUNs for %s with HAL: %s"), + hbadevs[0], derr.message); + goto cleanup; + } + + for (i = 0 ; i < numblkdevs ; i++) + virStorageBackendSCSIAddLUN(conn, + pool, + ctx, + blkdevs[i]); + + ret = 0; + +cleanup: + for (i = 0 ; hbadevs && i < numhbadevs ; i++) + free(hbadevs[i]); + free(hbadevs); + for (i = 0 ; blkdevs && i < numblkdevs ; i++) + free(blkdevs[i]); + free(blkdevs); + libhal_ctx_shutdown(ctx, NULL); + libhal_ctx_free(ctx); + if (sysbus) + dbus_connection_unref(sysbus); + if (dbus_error_is_set(&derr)) + dbus_error_free(&derr); + return ret; +} + + + +virStorageBackend virStorageBackendSCSI = { + .type = VIR_STORAGE_POOL_SCSI, + .refreshPool = virStorageBackendSCSIRefreshPool, +}; + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/src/storage_backend_scsi.h b/src/storage_backend_scsi.h new file mode 100644 index 0000000..c912269 --- /dev/null +++ b/src/storage_backend_scsi.h @@ -0,0 +1,45 @@ +/* + * 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_SCSI_H__ +#define __VIR_STORAGE_BACKEND_SCSI_H__ + +#include "storage_backend.h" + +extern virStorageBackend virStorageBackendSCSI; + +#endif /* __VIR_STORAGE_BACKEND_SCSI_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/src/storage_conf.c b/src/storage_conf.c index 70107a2..2ea6f8e 100644 --- a/src/storage_conf.c +++ b/src/storage_conf.c @@ -187,6 +187,14 @@ static virStoragePoolTypeInfo poolTypeInfo[] = { .formatToString = virStoragePoolFormatDiskTypeToString, } }, + { .poolType = VIR_STORAGE_POOL_SCSI, + .poolOptions = { + .flags = (VIR_STORAGE_POOL_SOURCE_ADAPTER), + }, + .volOptions = { + .formatToString = virStoragePoolFormatDiskTypeToString, + } + }, { .poolType = VIR_STORAGE_POOL_DISK, .poolOptions = { .flags = (VIR_STORAGE_POOL_SOURCE_DEVICE), @@ -269,6 +277,7 @@ virStoragePoolSourceFree(virStoragePoolSourcePtr source) { VIR_FREE(source->devices); VIR_FREE(source->dir); VIR_FREE(source->name); + VIR_FREE(source->adapter); if (source->authType == VIR_STORAGE_POOL_AUTH_CHAP) { VIR_FREE(source->auth.chap.login); @@ -550,6 +559,15 @@ virStoragePoolDefParseDoc(virConnectPtr conn, } } + if (options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER) { + if ((ret->source.adapter = virXPathString(conn, + "string(/pool/source/adapter/@name)", + ctxt)) == NULL) { + virStorageReportError(conn, VIR_ERR_XML_ERROR, + "%s", _("missing storage pool source adapter name")); + goto cleanup; + } + } authType = virXPathString(conn, "string(/pool/source/auth/@type)", ctxt); if (authType == NULL) { -- 1.6.0.6 -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list