[libvirt] [PATCH 5/8] virutil: Introduce virReadSCSIUniqueId

John Ferlan jferlan at redhat.com
Tue Jun 10 19:03:54 UTC 2014


Introduce a new function to read the current scsi_host entry and return
the value found in the 'unique_id' file.

Add a 'scsihosttest' test (similar to the fchosttest, but incorporating some
of the concepts of the mocked pci test library) in order to read the
unique_id file like would be found in the /sys/class/scsi_host tree.

Signed-off-by: John Ferlan <jferlan at redhat.com>
---
 src/libvirt_private.syms |   1 +
 src/util/virutil.c       |  53 ++++++++++
 src/util/virutil.h       |   4 +
 tests/Makefile.am        |   7 ++
 tests/scsihosttest.c     | 254 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 319 insertions(+)
 create mode 100644 tests/scsihosttest.c

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index d1d6ff3..a51e456 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2079,6 +2079,7 @@ virParseOwnershipIds;
 virParseVersionString;
 virPipeReadUntilEOF;
 virReadFCHost;
+virReadSCSIUniqueId;
 virScaleInteger;
 virSetBlocking;
 virSetCloseExec;
diff --git a/src/util/virutil.c b/src/util/virutil.c
index 95d1ff9..c73ce06 100644
--- a/src/util/virutil.c
+++ b/src/util/virutil.c
@@ -1681,6 +1681,50 @@ virGetDeviceUnprivSGIO(const char *path,
 # define SYSFS_FC_HOST_PATH "/sys/class/fc_host/"
 # define SYSFS_SCSI_HOST_PATH "/sys/class/scsi_host/"
 
+/* virReadSCSIUniqueId:
+ * @sysfs_prefix: "scsi_host" sysfs path, defaults to SYSFS_SCSI_HOST_PATH
+ * @host: Host number, E.g. 5 of "scsi_host/host5"
+ * @result: Return the entry value as an unsigned int
+ *
+ * Read the value of the "scsi_host" unique_id file.
+ *
+ * Returns 0 on success, and @result is filled with the unique_id value
+ * Otherwise returns -1
+ */
+int
+virReadSCSIUniqueId(const char *sysfs_prefix,
+                    int host,
+                    int *result)
+{
+    char *sysfs_path = NULL;
+    char *p = NULL;
+    int ret = -1;
+    char *buf = NULL;
+    int unique_id;
+
+    if (virAsprintf(&sysfs_path, "%s/host%d/unique_id",
+                    sysfs_prefix ? sysfs_prefix : SYSFS_SCSI_HOST_PATH,
+                    host) < 0)
+        goto cleanup;
+
+    if (virFileReadAll(sysfs_path, 1024, &buf) < 0)
+        goto cleanup;
+
+    if ((p = strchr(buf, '\n')))
+        *p = '\0';
+
+    if (virStrToLong_i(buf, NULL, 10, &unique_id) < 0)
+        goto cleanup;
+
+    *result = unique_id;
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(sysfs_path);
+    VIR_FREE(buf);
+    return ret;
+}
+
 /* virReadFCHost:
  * @sysfs_prefix: "fc_host" sysfs path, defaults to SYSFS_FC_HOST_PATH
  * @host: Host number, E.g. 5 of "fc_host/host5"
@@ -2034,6 +2078,15 @@ virFindFCHostCapableVport(const char *sysfs_prefix)
 }
 #else
 int
+virReadSCSIUniqueId(const char *sysfs_prefix ATTRIBUTE_UNUSED,
+                    int host ATTRIBUTE_UNUSED,
+                    unsigned int *result ATTRIBUTE_UNUSED)
+{
+    virReportSystemError(ENOSYS, "%s", _("Not supported on this platform"));
+    return -1;
+}
+
+int
 virReadFCHost(const char *sysfs_prefix ATTRIBUTE_UNUSED,
               int host ATTRIBUTE_UNUSED,
               const char *entry ATTRIBUTE_UNUSED,
diff --git a/src/util/virutil.h b/src/util/virutil.h
index 2bb74e2..1407dfd 100644
--- a/src/util/virutil.h
+++ b/src/util/virutil.h
@@ -164,6 +164,10 @@ int virGetDeviceUnprivSGIO(const char *path,
                            int *unpriv_sgio);
 char *virGetUnprivSGIOSysfsPath(const char *path,
                                 const char *sysfs_dir);
+int virReadSCSIUniqueId(const char *sysfs_prefix,
+                        int host,
+                        int *result)
+    ATTRIBUTE_NONNULL(3);
 int virReadFCHost(const char *sysfs_prefix,
                   int host,
                   const char *entry,
diff --git a/tests/Makefile.am b/tests/Makefile.am
index c999061..a15bd81 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -81,6 +81,7 @@ EXTRA_DIST =		\
 	domainsnapshotxml2xmlin \
 	domainsnapshotxml2xmlout \
 	fchostdata \
+	scsihostdata \
 	interfaceschemadata \
 	lxcconf2xmldata \
 	lxcxml2xmldata \
@@ -185,6 +186,7 @@ endif WITH_REMOTE
 
 if WITH_LINUX
 test_programs += fchosttest
+test_programs += scsihosttest
 endif WITH_LINUX
 
 if WITH_LIBVIRTD
@@ -1129,8 +1131,13 @@ fchosttest_SOURCES = \
        fchosttest.c testutils.h testutils.c
 fchosttest_LDADD = $(LDADDS)
 
+scsihosttest_SOURCES = \
+       scsihosttest.c testutils.h testutils.c
+scsihosttest_LDADD = $(LDADDS)
+
 else ! WITH_LINUX
 EXTRA_DIST += fchosttest.c
+EXTRA_DIST += scsihosttest.c
 endif  ! WITH_LINUX
 
 if WITH_LINUX
diff --git a/tests/scsihosttest.c b/tests/scsihosttest.c
new file mode 100644
index 0000000..990fe80
--- /dev/null
+++ b/tests/scsihosttest.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <config.h>
+
+#include "testutils.h"
+
+#ifdef __linux__
+
+# include <fcntl.h>
+# include <sys/stat.h>
+# include "virstring.h"
+# include "virutil.h"
+# include "virerror.h"
+# include "virlog.h"
+
+# define VIR_FROM_THIS VIR_FROM_NONE
+
+VIR_LOG_INIT("tests.scsihosttest");
+
+char *scsihost_class_path;
+# define TEST_SCSIHOST_CLASS_PATH scsihost_class_path
+
+/*
+ * Initialized/create a mock sysfs environment with 4 scsi_host devices
+ * located on "0000:00:1f.1" and "0000:00:1f.2".  Each directory will
+ * contain 4 unique_id files having the same value.
+ *
+ * The environment is:
+ *
+ *  4 files:
+ *
+ *     sys/devices/pci0000:00/0000:00:1f.1/ata1/host0/scsi_host/host0/unique_id
+ *     sys/devices/pci0000:00/0000:00:1f.1/ata2/host1/scsi_host/host1/unique_id
+ *     sys/devices/pci0000:00/0000:00:1f.2/ata1/host0/scsi_host/host0/unique_id
+ *     sys/devices/pci0000:00/0000:00:1f.2/ata2/host1/scsi_host/host1/unique_id
+ *
+ *  4 symlinks:
+ *
+ *     sys/class/scsi_host/host0 -> link to 1f.1 host 0
+ *     sys/class/scsi_host/host1 -> link to 1f.1 host 1
+ *     sys/class/scsi_host/host2 -> link to 1f.2 host 0
+ *     sys/class/scsi_host/host3 -> link to 1f.2 host 1
+ *
+ *  The unique_id's for host0 and host2 are set to "1"
+ *  The unique_id's for host1 and host3 are set to "2"
+ */
+
+static int
+create_scsihost(const char *fakesysfsdir, const char *devicepath,
+                const char *unique_id, const char *hostname)
+{
+    char *unique_id_path = NULL;
+    char *link_path = NULL;
+    char *spot;
+    int ret = -1;
+    int fd = -1;
+
+    if (virAsprintfQuiet(&unique_id_path, "%s/devices/pci0000:00/%s/unique_id",
+                         fakesysfsdir, devicepath) < 0 ||
+        virAsprintfQuiet(&link_path, "%s/class/scsi_host/%s",
+                         fakesysfsdir, hostname) < 0) {
+        fprintf(stderr, "Out of memory\n");
+        goto cleanup;
+    }
+
+    /* Rather than create path & file, temporarily snip off the file to
+     * create the path
+     */
+    if (!(spot = strstr(unique_id_path, "unique_id"))) {
+        fprintf(stderr, "Did not find unique_id in path\n");
+        goto cleanup;
+    }
+    spot--;
+    *spot = '\0';
+    if (virFileMakePathWithMode(unique_id_path, 0755) < 0) {
+        fprintf(stderr, "Unable to make path to '%s'\n", unique_id_path);
+        goto cleanup;
+    }
+    *spot = '/';
+
+    /* Rather than create path & file, temporarily snip off the file to
+     * create the path
+     */
+    if (!(spot = strstr(link_path, hostname))) {
+        fprintf(stderr, "Did not find hostname in path\n");
+        goto cleanup;
+    }
+    spot--;
+    *spot = '\0';
+    if (virFileMakePathWithMode(link_path, 0755) < 0) {
+        fprintf(stderr, "Unable to make path to '%s'\n", link_path);
+        goto cleanup;
+    }
+    *spot = '/';
+
+    if ((fd = open(unique_id_path, O_CREAT|O_WRONLY, 0444)) < 0) {
+        fprintf(stderr, "Unable to create '%s'\n", unique_id_path);
+        goto cleanup;
+    }
+
+    if (safewrite(fd, unique_id, 1) != 1) {
+        fprintf(stderr, "Unable to write '%s'\n", unique_id);
+        goto cleanup;
+    }
+    VIR_DEBUG("Created unique_id '%s'", unique_id_path);
+
+    /* The link is to the path not the file - so remove the file */
+    if (!(spot = strstr(unique_id_path, "unique_id"))) {
+        fprintf(stderr, "Did not find unique_id in path\n");
+        goto cleanup;
+    }
+    spot--;
+    *spot = '\0';
+    if (symlink(unique_id_path, link_path) < 0) {
+        fprintf(stderr, "Unable to create symlink '%s' to '%s'\n",
+                link_path, unique_id_path);
+        goto cleanup;
+    }
+    VIR_DEBUG("Created symlink '%s'", link_path);
+
+    ret = 0;
+
+ cleanup:
+    VIR_FORCE_CLOSE(fd);
+    VIR_FREE(unique_id_path);
+    VIR_FREE(link_path);
+    return ret;
+}
+
+static int
+init_scsihost_sysfs(const char *fakesysfsdir)
+{
+    int ret = 0;
+
+    if (create_scsihost(fakesysfsdir,
+                        "0000:00:1f.1/ata1/host0/scsi_host/host0",
+                        "1", "host0") < 0 ||
+        create_scsihost(fakesysfsdir,
+                        "0000:00:1f.1/ata2/host1/scsi_host/host1",
+                        "2", "host1") < 0 ||
+        create_scsihost(fakesysfsdir,
+                        "0000:00:1f.2/ata1/host0/scsi_host/host0",
+                        "1", "host2") < 0 ||
+        create_scsihost(fakesysfsdir,
+                        "0000:00:1f.2/ata2/host1/scsi_host/host1",
+                        "2", "host3") < 0)
+        ret = -1;
+
+    return ret;
+}
+
+/* Test virReadSCSIUniqueId */
+static int
+testVirReadSCSIUniqueId(const void *data ATTRIBUTE_UNUSED)
+{
+    int hostnum, unique_id;
+
+    for (hostnum = 0; hostnum < 4; hostnum++) {
+        if (virReadSCSIUniqueId(TEST_SCSIHOST_CLASS_PATH,
+                                hostnum, &unique_id) < 0) {
+            fprintf(stderr, "Failed to read hostnum=%d unique_id\n", hostnum);
+            return -1;
+        }
+
+        /* host0 and host2 have unique_id == 1
+         * host1 and host3 have unique_id == 2
+         */
+        if ((hostnum == 0 || hostnum == 2) && unique_id != 1) {
+            fprintf(stderr, "The unique_id='%d' for hostnum=%d is wrong\n",
+                    unique_id, hostnum);
+            return -1;
+        } else if ((hostnum == 1 || hostnum == 3) && unique_id != 2) {
+            fprintf(stderr, "The unique_id='%d' for hostnum=%d is wrong\n",
+                    unique_id, hostnum);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+# define FAKESYSFSDIRTEMPLATE abs_builddir "/fakesysfsdir-XXXXXX"
+
+static int
+mymain(void)
+{
+    int ret = -1;
+    char *fakesysfsdir = NULL;
+
+    if (VIR_STRDUP_QUIET(fakesysfsdir, FAKESYSFSDIRTEMPLATE) < 0) {
+        fprintf(stderr, "Out of memory\n");
+        goto cleanup;
+    }
+
+    if (!mkdtemp(fakesysfsdir)) {
+        fprintf(stderr, "Cannot create fakesysfsdir");
+        goto cleanup;
+    }
+
+    setenv("LIBVIRT_FAKE_SYSFS_DIR", fakesysfsdir, 1);
+
+    if (init_scsihost_sysfs(fakesysfsdir) < 0) {
+        fprintf(stderr, "Failed to create fakesysfs='%s'\n", fakesysfsdir);
+        goto cleanup;
+    }
+
+    if (virAsprintfQuiet(&scsihost_class_path, "%s/class/scsi_host",
+                         fakesysfsdir) < 0) {
+        fprintf(stderr, "Out of memory\n");
+        goto cleanup;
+    }
+    VIR_DEBUG("Reading from '%s'", scsihost_class_path);
+
+    if (virtTestRun("testVirReadSCSIUniqueId",
+                    testVirReadSCSIUniqueId, NULL) < 0) {
+        ret = -1;
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    if (getenv("LIBVIRT_SKIP_CLEANUP") == NULL)
+        virFileDeleteTree(fakesysfsdir);
+    VIR_FREE(fakesysfsdir);
+    VIR_FREE(scsihost_class_path);
+    return ret;
+}
+
+VIRT_TEST_MAIN(mymain)
+#else
+int
+main(void)
+{
+    return EXIT_AM_SKIP;
+}
+#endif
-- 
1.9.3




More information about the libvir-list mailing list