]> xenbits.xensource.com Git - libvirt.git/commitdiff
SCSI HBA storage pool implementation (Dave Allan)
authorDaniel P. Berrange <berrange@redhat.com>
Wed, 1 Apr 2009 16:03:22 +0000 (16:03 +0000)
committerDaniel P. Berrange <berrange@redhat.com>
Wed, 1 Apr 2009 16:03:22 +0000 (16:03 +0000)
ChangeLog
configure.in
po/POTFILES.in
src/Makefile.am
src/storage_backend.c
src/storage_backend_iscsi.c
src/storage_backend_scsi.c [new file with mode: 0644]
src/storage_backend_scsi.h [new file with mode: 0644]
src/storage_conf.c
src/storage_conf.h

index f9c0f997b650ca6b64e252c442fe2c39aea00f9e..b10caeec05e13b4b4d6e0b51c97f61b6a5c332a0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+Wed Apr  1 16:50:22 BST 2009 Daniel P. Berrange <berrange@redhat.com>
+
+       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 <berrange@redhat.com>
 
        * configure.in: Check for libsasl.so as well as libsasl2.so
index c5fe0b0711322f603d881dcaaa78340bdd5f983d..392f2b9f054fab049e1c15aad27d27052b0026c7 100644 (file)
@@ -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])
index 8b19b7d7597ffd4e71eb85cfe5dc65409120ee17..dc86835382d9e03b1d3fe224f3fc7f995977d1bc 100644 (file)
@@ -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
index d5aac113012fd3c7ef89c076c09c59e5fdcaf87d..60b2d466fc96cb8dafea1627d754180da3d94862 100644 (file)
@@ -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)               \
index 787630c3af86fbc3f1baf15d5ab7e4e7cd9136f9..71b80201cf0c5a5b56a9be31b3f9c75b0c520639 100644 (file)
@@ -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
index d5b10e595f0d3475a678ebf96e5d35e29fa01487..b516add07257f0ca0d614aac2cc650d76c7cd3aa 100644 (file)
@@ -35,6 +35,7 @@
 #include <dirent.h>
 
 #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;
-    }
-
-    for (i=0; i<n; i++) {
-        block = NULL;
-        scsidev = NULL;
-
-        if (sscanf(namelist[i]->d_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);
+                             _("Failed to find LUs on host %u"), host);
+        retval = -1;
     }
 
-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; i<n; i++)
-        VIR_FREE(namelist[i]);
-
-    VIR_FREE(namelist);
-
     return retval;
 }
 
@@ -615,13 +306,11 @@ virStorageBackendISCSIRefreshPool(virConnectPtr conn,
 
     pool->def->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 (file)
index 0000000..a962d1c
--- /dev/null
@@ -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 <berrange redhat com>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#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 (file)
index 0000000..808d47b
--- /dev/null
@@ -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 <berrange redhat com>
+ */
+
+#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__ */
index 323551839ae5a88dda4183da128edec4dd4a36d7..9e25ccbe79056f0b180e74ca1d44ca7fa529fe45 100644 (file)
@@ -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) {
index fc0fe0e787bcd94fbec5ef93564e73f7a78ecdc9..4e35ccbc165ce7b0a201f6a2a611f2f99d93e9d3 100644 (file)
@@ -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,