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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Dave Allan wrote:
Daniel P. Berrange wrote:
On Mon, Mar 30, 2009 at 05:50:38PM -0400, Dave Allan wrote:
Daniel P. Berrange wrote:
This works now on RHEL-5, F9 and F10.

I still had to disable this code

   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;
   }

because it breaks pools configured with a target dir of /dev/
That's a good point. Let's allow people to use non-stable paths by specifying a target path of /dev. That preserves the behavior I want, which is to prevent people from accidentally using non-stable paths when they asked for by-id, etc, but lets them use unstable paths if they want to. That's a one-line change that I have implemented in the attached patch.

And add device_type == 5 to allow CDROMs to work

   if (device_type != 0 &&
       device_type != 5) {
       retval = 0;
       goto out;
   }
The more I think about this question, the more I think we need to ensure that within a particular pool there is only one device type, also implemented in the attached patch. The problem with mixing device types within the pool is that it forces the consumer application to do device type checking every time it uses a volume from the pool, because the different device types cannot be used identically in all cases. I'm not entirely in agreement that we should allow device types other than disk in this API, but if we ensure that each pool contains only devices of a particular type, I don't see any harm in it.

I don't think it makes sense to restrict a pool to only contain devices
of a single type. It is perfectly reasonable for an IDE controller to
have a disk and a CDROM device on it, and restricting this would mean
users needing to create 2 separate pools for the same controller just
to be able to see all the devices.

Having the consumer create a pool for each device type is the way I was envisioning its being used.

Other pool types are quite happy to mix volume types, eg raw, qcow2,
vmdk files in filesystem based pool, and I see no reason not to allow
this for HBA based pools.

That is true.  My assumption was that they were all being used by the
VMs as disks.  Is that not the case, i.e, are people using storage
pools to back virtual MMC devices as well? If people are already mixing device types (from the guest's perspective) within pools, then making a distinction here is less important. It still does weird things to pool capacity, and volume creation fails unless there's media in the drive.

I guess what it comes down to is I disagree with allowing anything not a disk in the pool, but if people are already doing it, then I don't disagree enough to continue to argue about it.

diff --git a/src/storage_backend_scsi.c b/src/storage_backend_scsi.c
index 2c5168a..9f6a085 100644
--- a/src/storage_backend_scsi.c
+++ b/src/storage_backend_scsi.c
@@ -149,8 +149,11 @@ virStorageBackendSCSINewLun(virConnectPtr conn,
         goto free_vol;
     }
- if (STREQLEN(devpath, vol->target.path, PATH_MAX)) {
-        VIR_DEBUG(_("No stable path found for '%s' in '%s'"),
+    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;

ACK to this chunk.

Cool.  Thanks.

@@ -343,16 +346,19 @@ processLU(virConnectPtr conn,
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"),
+                              _("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) {
+    /* Check to see if the discovered device is the correct type for
+     * the pool. */
+    if (device_type != pool->def->deviceType) {
         retval = 0;
+ VIR_DEBUG(_("Found a device of type %d but pool device type is %d"),
+                  device_type, pool->def->deviceType);
         goto out;
     }

IMHo we should just be allowing types 0 & 5 directly here, with no
other restriction.

Attached is what I think is a final version of the scsi host pool code. It's the set of patches we've been discussing rolled up into a single patch, so it should look ok, but let me know if you have additional comments. I allowed both disk and rom and took out the XML enhancement for device type.

Dave
>From 50801da32e9f7db0be1d56aedff8343f95fed1b4 Mon Sep 17 00:00:00 2001
From: David Allan <dallan@xxxxxxxxxx>
Date: Tue, 3 Mar 2009 16:53:39 -0500
Subject: [PATCH 1/1] Add SCSI pool support.

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

It does not add a volume if it cannot find a stable path for a particular LU and the user specifies a stable target path.  A user may request unstable paths by specifying a target path of /dev.

Fixed LU scan logic so that it now hopefully works for all transport types on both RHEL5 and F10.
---
 configure.in                |   11 +
 po/POTFILES.in              |    1 +
 src/Makefile.am             |    8 +
 src/storage_backend.c       |    6 +
 src/storage_backend_iscsi.c |  339 ++---------------------------
 src/storage_backend_scsi.c  |  510 +++++++++++++++++++++++++++++++++++++++++++
 src/storage_backend_scsi.h  |   43 ++++
 src/storage_conf.c          |   18 ++
 src/storage_conf.h          |    7 +
 9 files changed, 618 insertions(+), 325 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..b516add 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,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
index 0000000..a962d1c
--- /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 <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
index 0000000..808d47b
--- /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 <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__ */
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) {
diff --git a/src/storage_conf.h b/src/storage_conf.h
index fc0fe0e..4e35ccb 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,
-- 
1.6.0.6

--
Libvir-list mailing list
Libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list

[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]