[libvirt] [PATCH 1/1] Add SCSI pool support.

David Allan dallan at redhat.com
Thu Mar 26 21:55:48 UTC 2009


This version of the code scans a particular host for targets and LUs and adds any LUs it finds as storage volumes.

It does not attempt to add a volume if it cannot find a stable path for a particular LU, for example, if the user specifies a target path by-id and the LU is one path of a multipath device.
---
 configure.in                |   11 +
 po/POTFILES.in              |    1 +
 src/Makefile.am             |    8 +
 src/storage_backend.c       |    6 +
 src/storage_backend_iscsi.c |  327 +---------------------------
 src/storage_backend_scsi.c  |  508 +++++++++++++++++++++++++++++++++++++++++++
 src/storage_backend_scsi.h  |   54 +++++
 src/storage_conf.c          |   18 ++
 8 files changed, 611 insertions(+), 322 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 6b2bb5e..c941b7e 100644
--- a/configure.in
+++ b/configure.in
@@ -784,6 +784,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])
 
@@ -793,6 +795,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
@@ -921,6 +924,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=
@@ -1357,6 +1367,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 8b19b7d..dc86835 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 d5aac11..60b2d46 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 787630c..71b8020 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 d5b10e5..2ececdd 100644
--- a/src/storage_backend_iscsi.c
+++ b/src/storage_backend_iscsi.c
@@ -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,145 +170,6 @@ 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,
@@ -315,196 +177,17 @@ virStorageBackendISCSIFindLUNs(virConnectPtr conn,
                                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;
 
     snprintf(sysfs_path, PATH_MAX,
              "/sys/class/iscsi_session/session%s/device", session);
 
-    sysdir = opendir(sysfs_path);
-    if (sysdir == NULL) {
+    if (virStorageBackendSCSIFindTargets(conn, pool, sysfs_path, "target") < 0) {
         virReportSystemError(conn, errno,
-                             _("Failed to opendir sysfs path '%s'"),
+                             _("Failed to get target list for 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 */
-        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);
-    }
-
-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;
 }
diff --git a/src/storage_backend_scsi.c b/src/storage_backend_scsi.c
new file mode 100644
index 0000000..62c05ae
--- /dev/null
+++ b/src/storage_backend_scsi.c
@@ -0,0 +1,508 @@
+/*
+ * 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)) {
+        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 use anything except Direct-Access devices, but finding
+     * one isn't an error, either. */
+    if (device_type != 0) {
+        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;
+}
+
+
+static int
+virStorageBackendSCSIFindLUs(virConnectPtr conn,
+                             virStoragePoolObjPtr pool,
+                             uint32_t scanhost,
+                             uint32_t scanbus,
+                             uint32_t scantarget)
+{
+    int retval = 0;
+    uint32_t host, bus, target, lun;
+    char *target_path = NULL;
+    DIR *targetdir = NULL;
+    struct dirent *lun_dirent = NULL;
+
+    VIR_DEBUG(_("Discovering LUs on host %u bus %u target %u"),
+              scanhost, scanbus, scantarget);
+
+    if (virAsprintf(&target_path, "/sys/bus/scsi/devices/target%u:%u:%u",
+             scanhost, scanbus, scantarget) < 0) {
+        virReportOOMError(conn);
+        goto out;
+    }
+
+    targetdir = opendir(target_path);
+
+    if (targetdir == NULL) {
+        virReportSystemError(conn, errno,
+                             _("Failed to opendir path '%s'"), target_path);
+        retval = -1;
+        goto out;
+    }
+
+    while ((lun_dirent = readdir(targetdir))) {
+        if (sscanf(lun_dirent->d_name, "%u:%u:%u:%u\n",
+                   &host, &bus, &target, &lun) != 4) {
+            continue;
+        }
+
+        VIR_DEBUG(_("Found LU '%s'"), lun_dirent->d_name);
+
+        processLU(conn, pool, host, bus, target, lun);
+    }
+
+    closedir(targetdir);
+
+out:
+    VIR_FREE(target_path);
+    return retval;
+}
+
+
+int
+virStorageBackendSCSIFindTargets(virConnectPtr conn,
+                                 virStoragePoolObjPtr pool,
+                                 const char *sysfs_path,
+                                 const char *pattern)
+{
+    int retval = 0;
+    uint32_t host, bus, target;
+    DIR *sysdir = NULL;
+    struct dirent *dirent = NULL;
+
+    VIR_DEBUG(_("Discovering targets in '%s'"), sysfs_path);
+
+    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, pattern, strlen(pattern))) {
+            if (sscanf(dirent->d_name,
+                       "target%u:%u:%u", &host, &bus, &target) != 3) {
+                VIR_DEBUG(_("Failed to parse target '%s'"), dirent->d_name);
+                retval = -1;
+                break;
+            }
+            virStorageBackendSCSIFindLUs(conn, pool, host, bus, target);
+        }
+    }
+
+    closedir(sysdir);
+out:
+    return retval;
+}
+
+
+static int
+virStorageBackendSCSIRefreshPool(virConnectPtr conn,
+                                 virStoragePoolObjPtr pool)
+{
+    char targetN[64];
+    int retval = 0;
+    uint32_t host;
+
+    pool->def->allocation = pool->def->capacity = pool->def->available = 0;
+
+    virStorageBackendWaitForDevices(conn);
+
+    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);
+
+    snprintf(targetN, sizeof(targetN), "target%u", host);
+
+    virStorageBackendSCSIFindTargets(conn, pool, "/sys/bus/scsi/devices", targetN);
+
+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 0000000..678ccd6
--- /dev/null
+++ b/src/storage_backend_scsi.h
@@ -0,0 +1,54 @@
+/*
+ * 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"
+#include "hash.h"
+
+#define LINUX_SYSFS_SCSI_HOST_PREFIX "/sys/class/scsi_host"
+#define LINUX_SYSFS_SCSI_HOST_POSTFIX "device"
+
+struct _virDirectoryWalkData {
+    virConnectPtr conn;
+    virStoragePoolObjPtr pool;
+    const char *dir_path;
+    const char *pattern_to_match;
+    size_t expected_matches;
+    virHashTablePtr matches; // will be created by walk function
+    virHashIterator iterator;
+    virHashDeallocator deallocator;
+};
+typedef struct _virDirectoryWalkData virDirectoryWalkData;
+typedef virDirectoryWalkData *virDirectoryWalkDataPtr;
+
+extern virStorageBackend virStorageBackendSCSI;
+
+int
+virStorageBackendSCSIFindTargets(virConnectPtr conn,
+                                 virStoragePoolObjPtr pool,
+                                 const char *sysfs_path,
+                                 const char *pattern);
+
+#endif /* __VIR_STORAGE_BACKEND_SCSI_H__ */
diff --git a/src/storage_conf.c b/src/storage_conf.c
index 1c9a4e5..e2870fd 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);
@@ -572,6 +581,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




More information about the libvir-list mailing list