diff --git a/ChangeLog b/ChangeLog index f9c0f997b6..b10caeec05 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +Wed Apr 1 16:50:22 BST 2009 Daniel P. Berrange + + SCSI HBA storage pool implementation (Dave Allan) + * configure.in: Add flag for SCSI storage pool support + * po/POTFILES.in: Add storage_backend_scsi.c + * src/Makefile.am: Add new SCSI storage backend + * src/storage_backend.c: Support for SCSI pool type + * src/storage_backend_iscsi.c: Refactor to re-use logic from + SCSI pool backend + * src/storage_backend_scsi.c, src/storage_backend_scsi.h: + Generic pool for Linux SCSI HBAs (or things which look + like SCSI HBAs) + * src/storage_conf.c, src/storage_conf.h: Add logic for + SCSI storage pool XML parsing options + Wed Apr 1 11:40:22 BST 2009 Daniel P. Berrange * configure.in: Check for libsasl.so as well as libsasl2.so diff --git a/configure.in b/configure.in index c5fe0b0711..392f2b9f05 100644 --- a/configure.in +++ b/configure.in @@ -792,6 +792,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]) @@ -801,6 +803,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 @@ -929,6 +932,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= @@ -1365,6 +1375,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([Security Drivers]) diff --git a/po/POTFILES.in b/po/POTFILES.in index 8b19b7d759..dc86835382 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -30,6 +30,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 d5aac11301..60b2d466fc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -159,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 @@ -353,6 +356,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 @@ -408,6 +415,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 787630c3af..71b80201cf 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_iscsi.c b/src/storage_backend_iscsi.c index d5b10e595f..b516add072 100644 --- a/src/storage_backend_iscsi.c +++ b/src/storage_backend_iscsi.c @@ -35,6 +35,7 @@ #include #include "virterror_internal.h" +#include "storage_backend_scsi.h" #include "storage_backend_iscsi.h" #include "util.h" #include "memory.h" @@ -169,343 +170,33 @@ virStorageBackendISCSIConnection(virConnectPtr conn, return 0; } -static int -virStorageBackendISCSINewLun(virConnectPtr conn, virStoragePoolObjPtr pool, - unsigned int lun, const char *dev) -{ - virStorageVolDefPtr vol; - int fd = -1; - char *devpath = NULL; - int opentries = 0; - - if (VIR_ALLOC(vol) < 0) { - virReportOOMError(conn); - goto cleanup; - } - - vol->type = VIR_STORAGE_VOL_BLOCK; - - if (virAsprintf(&(vol->name), "lun-%d", lun) < 0) { - virReportOOMError(conn); - goto cleanup; - } - - if (virAsprintf(&devpath, "/dev/%s", dev) < 0) { - virReportOOMError(conn); - goto cleanup; - } - - /* It can take a little while between logging into the ISCSI - * server and udev creating the /dev nodes, so if we get ENOENT - * we must retry a few times - they should eventually appear. - * We currently wait for upto 5 seconds. Is this good enough ? - * Perhaps not on a very heavily loaded system Any other - * options... ? - */ - reopen: - if ((fd = open(devpath, O_RDONLY)) < 0) { - opentries++; - if (errno == ENOENT && opentries < 50) { - usleep(100 * 1000); - goto reopen; - } - virReportSystemError(conn, errno, - _("cannot open '%s'"), - devpath); - 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, - devpath)) == NULL) - goto cleanup; - - VIR_FREE(devpath); - - if (virStorageBackendUpdateVolTargetInfoFD(conn, - &vol->target, - fd, - &vol->allocation, - &vol->capacity) < 0) - goto cleanup; - - /* XXX use unique iSCSI id instead */ - vol->key = strdup(vol->target.path); - if (vol->key == NULL) { - virReportOOMError(conn); - 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); - goto cleanup; - } - pool->volumes.objs[pool->volumes.count++] = vol; - - close(fd); - - return 0; - - cleanup: - if (fd != -1) close(fd); - VIR_FREE(devpath); - virStorageVolDefFree(vol); - return -1; -} - -static int notdotdir(const struct dirent *dir) -{ - return !(STREQLEN(dir->d_name, ".", 1) || STREQLEN(dir->d_name, "..", 2)); -} - -/* Function to check if the type file in the given sysfs_path is a - * Direct-Access device (i.e. type 0). Return -1 on failure, 0 if not - * a Direct-Access device, and 1 if a Direct-Access device - */ -static int directAccessDevice(const char *sysfs_path) -{ - char typestr[3]; - char *gottype, *p; - FILE *typefile; - int type; - - typefile = fopen(sysfs_path, "r"); - if (typefile == NULL) { - /* there was no type file; that doesn't seem right */ - return -1; - } - gottype = fgets(typestr, 3, typefile); - fclose(typefile); - - if (gottype == NULL) { - /* we couldn't read the type file; have to give up */ - return -1; - } - - /* we don't actually care about p, but if you pass NULL and the last - * character is not \0, virStrToLong_i complains - */ - if (virStrToLong_i(typestr, &p, 10, &type) < 0) { - /* Hm, type wasn't an integer; seems strange */ - return -1; - } - - if (type != 0) { - /* saw a device other than Direct-Access */ - return 0; - } - - return 1; -} static int -virStorageBackendISCSIFindLUNs(virConnectPtr conn, - virStoragePoolObjPtr pool, - const char *session) +virStorageBackendISCSIFindLUs(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char *session) { char sysfs_path[PATH_MAX]; - uint32_t host, bus, target, lun; - DIR *sysdir; - struct dirent *sys_dirent; - struct dirent **namelist; - int i, n, tries, retval, directaccess; - char *block, *scsidev, *block2; - - retval = 0; - block = NULL; - scsidev = NULL; + int retval = 0; + uint32_t host; snprintf(sysfs_path, PATH_MAX, "/sys/class/iscsi_session/session%s/device", session); - sysdir = opendir(sysfs_path); - if (sysdir == NULL) { + if (virStorageBackendSCSIGetHostNumber(conn, sysfs_path, &host) < 0) { virReportSystemError(conn, errno, - _("Failed to opendir sysfs path '%s'"), + _("Failed to get host number for iSCSI session " + "with path '%s'"), sysfs_path); - return -1; + retval = -1; } - while ((sys_dirent = readdir(sysdir))) { - /* double-negative, so we can use the same function for scandir below */ - if (!notdotdir(sys_dirent)) - continue; - if (STREQLEN(sys_dirent->d_name, "target", 6)) { - if (sscanf(sys_dirent->d_name, "target%u:%u:%u", - &host, &bus, &target) != 3) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to parse target from sysfs path %s/%s"), - sysfs_path, sys_dirent->d_name); - closedir(sysdir); - return -1; - } - break; - } - } - closedir(sysdir); - - /* we now have the host, bus, and target; let's scan for LUNs */ - snprintf(sysfs_path, PATH_MAX, - "/sys/class/iscsi_session/session%s/device/target%u:%u:%u", - session, host, bus, target); - - n = scandir(sysfs_path, &namelist, notdotdir, versionsort); - if (n <= 0) { - /* we didn't find any reasonable entries; return failure */ + if (virStorageBackendSCSIFindLUs(conn, pool, host) < 0) { virReportSystemError(conn, errno, - _("Failed to find any LUNs for session '%s'"), - session); - return -1; + _("Failed to find LUs on host %u"), host); + retval = -1; } - for (i=0; id_name, "%u:%u:%u:%u\n", - &host, &bus, &target, &lun) != 4) - continue; - - /* we found a LUN */ - /* Note, however, that just finding a LUN doesn't mean it is - * actually useful to us. There are a few different types of - * LUNs, enumerated in the linux kernel in - * drivers/scsi/scsi.c:scsi_device_types[]. Luckily, these device - * types form part of the ABI between the kernel and userland, so - * are unlikely to change. For now, we ignore everything that isn't - * type 0; that is, a Direct-Access device - */ - snprintf(sysfs_path, PATH_MAX, - "/sys/bus/scsi/devices/%u:%u:%u:%u/type", - host, bus, target, lun); - - directaccess = directAccessDevice(sysfs_path); - if (directaccess < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to determine if %u:%u:%u:%u is a Direct-Access LUN"), - host, bus, target, lun); - retval = -1; - goto namelist_cleanup; - } - else if (directaccess == 0) { - /* not a direct-access device; skip */ - continue; - } - /* implicit else if (access == 1); Direct-Access device */ - - /* It might take some time for the - * /sys/bus/scsi/devices/host:bus:target:lun/block{:sda,/sda} - * link to show up; wait up to 5 seconds for it, then give up - */ - tries = 0; - while (block == NULL && tries < 50) { - snprintf(sysfs_path, PATH_MAX, "/sys/bus/scsi/devices/%u:%u:%u:%u", - host, bus, target, lun); - - sysdir = opendir(sysfs_path); - if (sysdir == NULL) { - virReportSystemError(conn, errno, - _("Failed to opendir sysfs path '%s'"), - sysfs_path); - retval = -1; - goto namelist_cleanup; - } - while ((sys_dirent = readdir(sysdir))) { - if (!notdotdir(sys_dirent)) - continue; - if (STREQLEN(sys_dirent->d_name, "block", 5)) { - block = strdup(sys_dirent->d_name); - break; - } - } - closedir(sysdir); - tries++; - if (block == NULL) - usleep(100 * 1000); - } - - if (block == NULL) { - /* we couldn't find the device link for this device; fail */ - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to find device link for lun %d"), - lun); - retval = -1; - goto namelist_cleanup; - } - - if (strlen(block) == 5) { - /* OK, this is exactly "block"; must be new-style */ - snprintf(sysfs_path, PATH_MAX, - "/sys/bus/scsi/devices/%u:%u:%u:%u/block", - host, bus, target, lun); - sysdir = opendir(sysfs_path); - if (sysdir == NULL) { - virReportSystemError(conn, errno, - _("Failed to opendir sysfs path '%s'"), - sysfs_path); - retval = -1; - goto namelist_cleanup; - } - while ((sys_dirent = readdir(sysdir))) { - if (!notdotdir(sys_dirent)) - continue; - - scsidev = strdup(sys_dirent->d_name); - break; - } - closedir(sysdir); - } - else { - /* old-style; just parse out the sd */ - block2 = strrchr(block, ':'); - if (block2 == NULL) { - /* Hm, wasn't what we were expecting; have to give up */ - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to parse block path %s"), - block); - retval = -1; - goto namelist_cleanup; - } - block2++; - scsidev = strdup(block2); - } - if (scsidev == NULL) { - virReportOOMError(conn); - retval = -1; - goto namelist_cleanup; - } - - retval = virStorageBackendISCSINewLun(conn, pool, lun, scsidev); - if (retval < 0) - break; - VIR_FREE(scsidev); - VIR_FREE(block); - } - -namelist_cleanup: - /* we call these VIR_FREE here to make sure we don't leak memory on - * error cases; in the success case, these are already freed but NULL, - * which should be fine - */ - VIR_FREE(scsidev); - VIR_FREE(block); - - for (i=0; idef->allocation = pool->def->capacity = pool->def->available = 0; - virStorageBackendWaitForDevices(conn); - if ((session = virStorageBackendISCSISession(conn, pool, 0)) == NULL) goto cleanup; if (virStorageBackendISCSIRescanLUNs(conn, pool, session) < 0) goto cleanup; - if (virStorageBackendISCSIFindLUNs(conn, pool, session) < 0) + if (virStorageBackendISCSIFindLUs(conn, pool, session) < 0) goto cleanup; VIR_FREE(session); diff --git a/src/storage_backend_scsi.c b/src/storage_backend_scsi.c new file mode 100644 index 0000000000..a962d1c258 --- /dev/null +++ b/src/storage_backend_scsi.c @@ -0,0 +1,510 @@ +/* + * 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 + */ + +#include + +#include +#include +#include +#include + +#include "virterror_internal.h" +#include "storage_backend_scsi.h" +#include "memory.h" +#include "logging.h" + +#define VIR_FROM_THIS VIR_FROM_STORAGE + +/* Function to check if the type file in the given sysfs_path is a + * Direct-Access device (i.e. type 0). Return -1 on failure, type of + * the device otherwise. + */ +static int +getDeviceType(virConnectPtr conn, + uint32_t host, + uint32_t bus, + uint32_t target, + uint32_t lun, + int *type) +{ + char *type_path = NULL; + char typestr[3]; + char *gottype, *p; + FILE *typefile; + int retval = 0; + + if (virAsprintf(&type_path, "/sys/bus/scsi/devices/%u:%u:%u:%u/type", + host, bus, target, lun) < 0) { + virReportOOMError(conn); + goto out; + } + + typefile = fopen(type_path, "r"); + if (typefile == NULL) { + virReportSystemError(conn, errno, + _("Could not find typefile '%s'"), + type_path); + /* there was no type file; that doesn't seem right */ + retval = -1; + goto out; + } + + gottype = fgets(typestr, 3, typefile); + fclose(typefile); + + if (gottype == NULL) { + virReportSystemError(conn, errno, + _("Could not read typefile '%s'"), + type_path); + /* we couldn't read the type file; have to give up */ + retval = -1; + goto out; + } + + /* we don't actually care about p, but if you pass NULL and the last + * character is not \0, virStrToLong_i complains + */ + if (virStrToLong_i(typestr, &p, 10, type) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Device type '%s' is not an integer"), + typestr); + /* Hm, type wasn't an integer; seems strange */ + retval = -1; + goto out; + } + + VIR_DEBUG(_("Device type is %d"), *type); + +out: + VIR_FREE(type_path); + return retval; +} + + +static int +virStorageBackendSCSINewLun(virConnectPtr conn, + virStoragePoolObjPtr pool, + uint32_t host, + uint32_t bus, + uint32_t target, + uint32_t lun, + const char *dev) +{ + virStorageVolDefPtr vol; + char *devpath = NULL; + int retval = 0; + + if (VIR_ALLOC(vol) < 0) { + virReportOOMError(conn); + retval = -1; + goto out; + } + + vol->type = VIR_STORAGE_VOL_BLOCK; + + if (virAsprintf(&(vol->name), "%u.%u.%u.%u", host, bus, target, lun) < 0) { + virReportOOMError(conn); + retval = -1; + goto free_vol; + } + + if (virAsprintf(&devpath, "/dev/%s", dev) < 0) { + virReportOOMError(conn); + retval = -1; + goto free_vol; + } + + VIR_DEBUG(_("Trying to create volume for '%s'"), devpath); + + /* 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, + devpath)) == NULL) { + retval = -1; + goto free_vol; + } + + if (STREQLEN(devpath, vol->target.path, PATH_MAX) && + !(STREQ(pool->def->target.path, "/dev") || + STREQ(pool->def->target.path, "/dev/"))) { + + VIR_DEBUG(_("No stable path found for '%s' in '%s'"), + devpath, pool->def->target.path); + + retval = -1; + goto free_vol; + } + + if (virStorageBackendUpdateVolTargetInfo(conn, + &vol->target, + &vol->allocation, + &vol->capacity) < 0) { + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Failed to update volume for '%s'"), + devpath); + 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: + VIR_FREE(devpath); + return retval; +} + + +static int +getNewStyleBlockDevice(virConnectPtr conn, + const char *lun_path, + const char *block_name ATTRIBUTE_UNUSED, + char **block_device) +{ + char *block_path = NULL; + DIR *block_dir = NULL; + struct dirent *block_dirent = NULL; + int retval = 0; + + if (virAsprintf(&block_path, "%s/block", lun_path) < 0) { + virReportOOMError(conn); + goto out; + } + + VIR_DEBUG(_("Looking for block device in '%s'"), block_path); + + block_dir = opendir(block_path); + if (block_dir == NULL) { + virReportSystemError(conn, errno, + _("Failed to opendir sysfs path '%s'"), + block_path); + retval = -1; + goto out; + } + + while ((block_dirent = readdir(block_dir))) { + + if (STREQLEN(block_dirent->d_name, ".", 1)) { + continue; + } + + *block_device = strdup(block_dirent->d_name); + VIR_DEBUG(_("Block device is '%s'"), *block_device); + + break; + } + + closedir(block_dir); + +out: + VIR_FREE(block_path); + return retval; +} + + +static int +getOldStyleBlockDevice(virConnectPtr conn, + const char *lun_path ATTRIBUTE_UNUSED, + const char *block_name, + char **block_device) +{ + char *blockp = NULL; + int retval = 0; + + /* old-style; just parse out the sd */ + blockp = strrchr(block_name, ':'); + if (blockp == NULL) { + /* Hm, wasn't what we were expecting; have to give up */ + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Failed to parse block name %s"), + block_name); + retval = -1; + } else { + blockp++; + *block_device = strdup(blockp); + + VIR_DEBUG(_("Block device is '%s'"), *block_device); + } + + return retval; +} + + +static int +getBlockDevice(virConnectPtr conn, + uint32_t host, + uint32_t bus, + uint32_t target, + uint32_t lun, + char **block_device) +{ + char *lun_path = NULL; + DIR *lun_dir = NULL; + struct dirent *lun_dirent = NULL; + int retval = 0; + + if (virAsprintf(&lun_path, "/sys/bus/scsi/devices/%u:%u:%u:%u", + host, bus, target, lun) < 0) { + virReportOOMError(conn); + goto out; + } + + lun_dir = opendir(lun_path); + if (lun_dir == NULL) { + virReportSystemError(conn, errno, + _("Failed to opendir sysfs path '%s'"), + lun_path); + retval = -1; + goto out; + } + + while ((lun_dirent = readdir(lun_dir))) { + if (STREQLEN(lun_dirent->d_name, "block", 5)) { + if (strlen(lun_dirent->d_name) == 5) { + retval = getNewStyleBlockDevice(conn, + lun_path, + lun_dirent->d_name, + block_device); + } else { + retval = getOldStyleBlockDevice(conn, + lun_path, + lun_dirent->d_name, + block_device); + } + break; + } + } + + closedir(lun_dir); + +out: + VIR_FREE(lun_path); + return retval; +} + + +static int +processLU(virConnectPtr conn, + virStoragePoolObjPtr pool, + uint32_t host, + uint32_t bus, + uint32_t target, + uint32_t lun) +{ + char *type_path = NULL; + int retval = 0; + int device_type; + char *block_device = NULL; + + VIR_DEBUG(_("Processing LU %u:%u:%u:%u"), + host, bus, target, lun); + + if (getDeviceType(conn, host, bus, target, lun, &device_type) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Failed to determine if %u:%u:%u:%u is a Direct-Access LUN"), + host, bus, target, lun); + retval = -1; + goto out; + } + + /* We don't create volumes for devices other than disk and cdrom + * devices, but finding a device that isn't one of those types + * isn't an error, either. */ + if (!(device_type == VIR_STORAGE_DEVICE_TYPE_DISK || + device_type == VIR_STORAGE_DEVICE_TYPE_ROM)) + { + retval = 0; + goto out; + } + + VIR_DEBUG(_("%u:%u:%u:%u is a Direct-Access LUN"), + host, bus, target, lun); + + if (getBlockDevice(conn, host, bus, target, lun, &block_device) < 0) { + goto out; + } + + if (virStorageBackendSCSINewLun(conn, pool, + host, bus, target, lun, + block_device) < 0) { + VIR_DEBUG(_("Failed to create new storage volume for %u:%u:%u:%u"), + host, bus, target, lun); + retval = -1; + goto out; + } + + VIR_DEBUG(_("Created new storage volume for %u:%u:%u:%u successfully"), + host, bus, target, lun); + + VIR_FREE(type_path); + +out: + return retval; +} + + +int +virStorageBackendSCSIFindLUs(virConnectPtr conn, + virStoragePoolObjPtr pool, + uint32_t scanhost) +{ + int retval = 0; + uint32_t bus, target, lun; + char *device_path = NULL; + DIR *devicedir = NULL; + struct dirent *lun_dirent = NULL; + char devicepattern[64]; + + VIR_DEBUG(_("Discovering LUs on host %u"), scanhost); + + virStorageBackendWaitForDevices(conn); + + if (virAsprintf(&device_path, "/sys/bus/scsi/devices") < 0) { + virReportOOMError(conn); + goto out; + } + + devicedir = opendir(device_path); + + if (devicedir == NULL) { + virReportSystemError(conn, errno, + _("Failed to opendir path '%s'"), device_path); + retval = -1; + goto out; + } + + snprintf(devicepattern, sizeof(devicepattern), "%u:%%u:%%u:%%u\n", scanhost); + + while ((lun_dirent = readdir(devicedir))) { + if (sscanf(lun_dirent->d_name, devicepattern, + &bus, &target, &lun) != 3) { + continue; + } + + VIR_DEBUG(_("Found LU '%s'"), lun_dirent->d_name); + + processLU(conn, pool, scanhost, bus, target, lun); + } + + closedir(devicedir); + +out: + VIR_FREE(device_path); + return retval; +} + + +int +virStorageBackendSCSIGetHostNumber(virConnectPtr conn, + const char *sysfs_path, + uint32_t *host) +{ + int retval = 0; + DIR *sysdir = NULL; + struct dirent *dirent = NULL; + + VIR_DEBUG(_("Finding host number from '%s'"), sysfs_path); + + virStorageBackendWaitForDevices(conn); + + sysdir = opendir(sysfs_path); + + if (sysdir == NULL) { + virReportSystemError(conn, errno, + _("Failed to opendir path '%s'"), sysfs_path); + retval = -1; + goto out; + } + + while ((dirent = readdir(sysdir))) { + if (STREQLEN(dirent->d_name, "target", strlen("target"))) { + if (sscanf(dirent->d_name, + "target%u:", host) != 1) { + VIR_DEBUG(_("Failed to parse target '%s'"), dirent->d_name); + retval = -1; + break; + } + } + } + + closedir(sysdir); +out: + return retval; +} + + +static int +virStorageBackendSCSIRefreshPool(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + int retval = 0; + uint32_t host; + + pool->def->allocation = pool->def->capacity = pool->def->available = 0; + + if (sscanf(pool->def->source.adapter, "host%u", &host) != 1) { + VIR_DEBUG(_("Failed to get host number from '%s'"), pool->def->source.adapter); + retval = -1; + goto out; + } + + VIR_DEBUG(_("Scanning host%u"), host); + + virStorageBackendSCSIFindLUs(conn, pool, host); + +out: + return retval; +} + + +virStorageBackend virStorageBackendSCSI = { + .type = VIR_STORAGE_POOL_SCSI, + + .refreshPool = virStorageBackendSCSIRefreshPool, +}; diff --git a/src/storage_backend_scsi.h b/src/storage_backend_scsi.h new file mode 100644 index 0000000000..808d47bad7 --- /dev/null +++ b/src/storage_backend_scsi.h @@ -0,0 +1,43 @@ +/* + * 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 + */ + +#ifndef __VIR_STORAGE_BACKEND_SCSI_H__ +#define __VIR_STORAGE_BACKEND_SCSI_H__ + +#include "storage_backend.h" + +#define LINUX_SYSFS_SCSI_HOST_PREFIX "/sys/class/scsi_host" +#define LINUX_SYSFS_SCSI_HOST_POSTFIX "device" + +extern virStorageBackend virStorageBackendSCSI; + +int +virStorageBackendSCSIGetHostNumber(virConnectPtr conn, + const char *sysfs_path, + uint32_t *host); +int +virStorageBackendSCSIFindLUs(virConnectPtr conn, + virStoragePoolObjPtr pool, + uint32_t scanhost); + +#endif /* __VIR_STORAGE_BACKEND_SCSI_H__ */ diff --git a/src/storage_conf.c b/src/storage_conf.c index 323551839a..9e25ccbe79 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); @@ -573,6 +582,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) { diff --git a/src/storage_conf.h b/src/storage_conf.h index fc0fe0e787..4e35ccbc16 100644 --- a/src/storage_conf.h +++ b/src/storage_conf.h @@ -117,6 +117,13 @@ enum virStoragePoolType { VIR_ENUM_DECL(virStoragePool) +enum virStoragePoolDeviceType { + VIR_STORAGE_DEVICE_TYPE_DISK = 0x00, + VIR_STORAGE_DEVICE_TYPE_ROM = 0x05, + + VIR_STORAGE_DEVICE_TYPE_LAST, +}; + enum virStoragePoolAuthType { VIR_STORAGE_POOL_AUTH_NONE,