[libvirt] [PATCH v4 3/4] storage: Implement iscsi_direct pool backend

clem at lse.epita.fr clem at lse.epita.fr
Tue Jul 31 13:11:24 UTC 2018


From: Clementine Hayat <clem at lse.epita.fr>

We need here libiscsi for the storgae pool backend.
For the iscsi-direct storage pool, only checkPool and refreshPool should
be necessary for basic support.
The pool is state-less and just need the informations within the volume
to work.

Signed-off-by: Clementine Hayat <clem at lse.epita.fr>
---
 m4/virt-storage-iscsi-direct.m4            |   3 +
 src/storage/Makefile.inc.am                |   2 +
 src/storage/storage_backend_iscsi_direct.c | 400 ++++++++++++++++++++-
 3 files changed, 402 insertions(+), 3 deletions(-)

diff --git a/m4/virt-storage-iscsi-direct.m4 b/m4/virt-storage-iscsi-direct.m4
index cc2d490352..dab4414169 100644
--- a/m4/virt-storage-iscsi-direct.m4
+++ b/m4/virt-storage-iscsi-direct.m4
@@ -29,6 +29,9 @@ AC_DEFUN([LIBVIRT_STORAGE_CHECK_ISCSI_DIRECT], [
     with_storage_iscsi_direct=$with_libiscsi
   fi
   if test "$with_storage_iscsi_direct" = "yes"; then
+    if test "$with_libiscsi" = "no"; then
+      AC_MSG_ERROR([Need libiscsi for iscsi-direct storage driver])
+    fi
     AC_DEFINE_UNQUOTED([WITH_STORAGE_ISCSI_DIRECT], [1],
                        [whether iSCSI backend for storage driver is enabled])
   fi
diff --git a/src/storage/Makefile.inc.am b/src/storage/Makefile.inc.am
index b2714fd960..bd5ea06f8b 100644
--- a/src/storage/Makefile.inc.am
+++ b/src/storage/Makefile.inc.am
@@ -203,6 +203,7 @@ if WITH_STORAGE_ISCSI_DIRECT
 libvirt_storage_backend_iscsi_direct_la_SOURCES = $(STORAGE_DRIVER_ISCSI_DIRECT_SOURCES)
 libvirt_storage_backend_iscsi_direct_la_CFLAGS = \
 	-I$(srcdir)/conf \
+	-I$(srcdir)/secret \
 	$(LIBISCSI_CFLAGS) \
 	$(AM_CFLAGS) \
 	$(NULL)
@@ -211,6 +212,7 @@ storagebackend_LTLIBRARIES += libvirt_storage_backend_iscsi-direct.la
 libvirt_storage_backend_iscsi_direct_la_LDFLAGS = $(AM_LDFLAGS_MOD)
 libvirt_storage_backend_iscsi_direct_la_LIBADD = \
 	libvirt.la \
+	$(LIBISCSI_LIBS) \
 	../gnulib/lib/libgnu.la \
 	$(NULL)
 endif WITH_STORAGE_ISCSI_DIRECT
diff --git a/src/storage/storage_backend_iscsi_direct.c b/src/storage/storage_backend_iscsi_direct.c
index 94c4c989ff..ab192730fb 100644
--- a/src/storage/storage_backend_iscsi_direct.c
+++ b/src/storage/storage_backend_iscsi_direct.c
@@ -22,28 +22,422 @@
 
 #include <config.h>
 
+#include <iscsi/iscsi.h>
+#include <iscsi/scsi-lowlevel.h>
+
+#include "datatypes.h"
+#include "secret_util.h"
 #include "storage_backend_iscsi_direct.h"
 #include "storage_util.h"
+#include "viralloc.h"
+#include "virerror.h"
 #include "virlog.h"
+#include "virobject.h"
+#include "virstring.h"
+#include "virtime.h"
+#include "viruuid.h"
 
 #define VIR_FROM_THIS VIR_FROM_STORAGE
 
+#define ISCSI_DEFAULT_TARGET_PORT 3260
+#define VIR_ISCSI_TEST_UNIT_TIMEOUT 30 * 1000
+
 VIR_LOG_INIT("storage.storage_backend_iscsi_direct");
 
+static struct iscsi_context *
+virISCSIDirectCreateContext(const char* initiator_iqn)
+{
+    struct iscsi_context *iscsi = NULL;
+
+    iscsi = iscsi_create_context(initiator_iqn);
+    if (!iscsi)
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to create iscsi context for %s"),
+                       initiator_iqn);
+    return iscsi;
+}
+
+static char *
+virStorageBackendISCSIDirectPortal(virStoragePoolSourcePtr source)
+{
+    char *portal = NULL;
+
+    if (source->nhost != 1) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("Expected exactly 1 host for the storage pool"));
+        return NULL;
+    }
+    if (source->hosts[0].port == 0) {
+        ignore_value(virAsprintf(&portal, "%s:%d",
+                                 source->hosts[0].name,
+                                 ISCSI_DEFAULT_TARGET_PORT));
+    } else if (strchr(source->hosts[0].name, ':')) {
+        ignore_value(virAsprintf(&portal, "[%s]:%d",
+                                 source->hosts[0].name,
+                                 source->hosts[0].port));
+    } else {
+        ignore_value(virAsprintf(&portal, "%s:%d",
+                                 source->hosts[0].name,
+                                 source->hosts[0].port));
+    }
+    return portal;
+}
+
+static int
+virStorageBackendISCSIDirectSetAuth(struct iscsi_context *iscsi,
+                                    virStoragePoolSourcePtr source)
+{
+    unsigned char *secret_value = NULL;
+    size_t secret_size;
+    virStorageAuthDefPtr authdef = source->auth;
+    int ret = -1;
+    virConnectPtr conn = NULL;
+
+    if (!authdef || authdef->authType == VIR_STORAGE_AUTH_TYPE_NONE)
+        return 0;
+
+    VIR_DEBUG("username='%s' authType=%d seclookupdef.type=%d",
+              authdef->username, authdef->authType, authdef->seclookupdef.type);
+
+    if (authdef->authType != VIR_STORAGE_AUTH_TYPE_CHAP) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("iscsi-direct pool only supports 'chap' auth type"));
+        return ret;
+    }
+
+    if (!(conn = virGetConnectSecret()))
+        return ret;
+
+    if (virSecretGetSecretString(conn, &authdef->seclookupdef,
+                                 VIR_SECRET_USAGE_TYPE_ISCSI,
+                                 &secret_value, &secret_size) < 0)
+        goto cleanup;
+
+    if (iscsi_set_initiator_username_pwd(iscsi,
+                                         authdef->username,
+                                         (const char *)secret_value) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to set credential: %s"),
+                       iscsi_get_error(iscsi));
+        goto cleanup;
+    }
+
+    ret = 0;
+ cleanup:
+    VIR_DISPOSE_N(secret_value, secret_size);
+    virObjectUnref(conn);
+    return ret;
+}
+
+static int
+virISCSIDirectSetContext(struct iscsi_context *iscsi,
+                         const char *target_name)
+{
+    if (iscsi_init_transport(iscsi, TCP_TRANSPORT) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to init transport: %s"),
+                       iscsi_get_error(iscsi));
+        return -1;
+    }
+    if (iscsi_set_targetname(iscsi, target_name) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to set target name: %s"),
+                       iscsi_get_error(iscsi));
+        return -1;
+    }
+    if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to set session type: %s"),
+                       iscsi_get_error(iscsi));
+        return -1;
+    }
+    return 0;
+}
 
 static int
-virStorageBackendISCSIDirectCheckPool(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
-                                      bool *isActive ATTRIBUTE_UNUSED)
+virISCSIDirectConnect(struct iscsi_context *iscsi,
+                      const char *portal)
 {
+    if (iscsi_connect_sync(iscsi, portal) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to connect: %s"),
+                       iscsi_get_error(iscsi));
+        return -1;
+    }
+    if (iscsi_login_sync(iscsi) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to login: %s"),
+                       iscsi_get_error(iscsi));
+        return -1;
+    }
     return 0;
 }
 
 static int
-virStorageBackendISCSIDirectRefreshPool(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED)
+virISCSIDirectTestUnitReady(struct iscsi_context *iscsi,
+                            int lun)
+{
+    struct scsi_task *task = NULL;
+    int ret = -1;
+    virTimeBackOffVar timebackoff;
+
+    if (virTimeBackOffStart(&timebackoff, 1,
+                            VIR_ISCSI_TEST_UNIT_TIMEOUT) < 0)
+        goto cleanup;
+
+    do {
+        if (!(task = iscsi_testunitready_sync(iscsi, lun))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Failed testunitready: %s"),
+                           iscsi_get_error(iscsi));
+            goto cleanup;
+        }
+
+        if (task->status != SCSI_STATUS_CHECK_CONDITION ||
+            task->sense.key != SCSI_SENSE_UNIT_ATTENTION ||
+            task->sense.ascq != SCSI_SENSE_ASCQ_BUS_RESET)
+            break;
+
+        scsi_free_scsi_task(task);
+    } while (virTimeBackOffWait(&timebackoff));
+
+    if (task->status != SCSI_STATUS_GOOD) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed testunitready: %s"),
+                       iscsi_get_error(iscsi));
+        goto cleanup;
+    }
+
+    ret = 0;
+ cleanup:
+    scsi_free_scsi_task(task);
+    return ret;
+}
+
+static int
+virISCSIDirectSetVolumeAttributes(virStoragePoolObjPtr pool,
+                                  virStorageVolDefPtr vol,
+                                  int lun,
+                                  char *portal)
 {
+    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
+
+    if (virAsprintf(&vol->name, "unit:0:0:%u", lun) < 0)
+        return -1;
+    if (virAsprintf(&vol->key, "ip-%s-iscsi-%s-lun-%u", portal,
+                    def->source.devices[0].path, lun) < 0)
+        return -1;
+    if (virAsprintf(&vol->target.path, "ip-%s-iscsi-%s-lun-%u", portal,
+                    def->source.devices[0].path, lun) < 0)
+        return -1;
     return 0;
 }
 
+static int
+virISCSIDirectSetVolumeCapacity(struct iscsi_context *iscsi,
+                                virStorageVolDefPtr vol,
+                                int lun)
+{
+    struct scsi_task *task = NULL;
+    struct scsi_inquiry_standard *inq = NULL;
+    long long size = 0;
+    int ret = -1;
+
+    if (!(task = iscsi_inquiry_sync(iscsi, lun, 0, 0, 64)) ||
+        task->status != SCSI_STATUS_GOOD) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to send inquiry command: %s"),
+                       iscsi_get_error(iscsi));
+        goto cleanup;
+    }
+
+    if (!(inq = scsi_datain_unmarshall(task))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to unmarshall reply: %s"),
+                       iscsi_get_error(iscsi));
+        goto cleanup;
+    }
+
+    if (inq->device_type == SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS) {
+        struct scsi_readcapacity10 *rc10 = NULL;
+
+        scsi_free_scsi_task(task);
+        task = NULL;
+
+        if (!(task = iscsi_readcapacity10_sync(iscsi, lun, 0, 0)) ||
+            task->status != SCSI_STATUS_GOOD) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Failed to get capacity of lun: %s"),
+                           iscsi_get_error(iscsi));
+            goto cleanup;
+        }
+
+        if (!(rc10 = scsi_datain_unmarshall(task))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Failed to unmarshall reply: %s"),
+                           iscsi_get_error(iscsi));
+            goto cleanup;
+        }
+
+        size  = rc10->block_size;
+        size *= rc10->lba;
+        vol->target.capacity = size;
+        vol->target.allocation = size;
+
+    }
+
+    ret = 0;
+ cleanup:
+    scsi_free_scsi_task(task);
+    return ret;
+}
+
+static int
+virISCSIDirectRefreshVol(virStoragePoolObjPtr pool,
+                         struct iscsi_context *iscsi,
+                         int lun,
+                         char *portal)
+{
+    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
+    virStorageVolDefPtr vol = NULL;
+    int ret = -1;
+
+    virStoragePoolObjClearVols(pool);
+    if (virISCSIDirectTestUnitReady(iscsi, lun) < 0)
+        goto cleanup;
+
+    if (VIR_ALLOC(vol) < 0)
+        goto cleanup;
+
+    vol->type = VIR_STORAGE_VOL_NETWORK;
+
+    if (virISCSIDirectSetVolumeCapacity(iscsi, vol, lun) < 0)
+        goto cleanup;
+
+    def->capacity += vol->target.capacity;
+    def->allocation += vol->target.allocation;
+
+    if (virISCSIDirectSetVolumeAttributes(pool, vol, lun, portal) < 0)
+        goto cleanup;
+
+    if (virStoragePoolObjAddVol(pool, vol) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to create volume: %d"),
+                       lun);
+        goto cleanup;
+    }
+    vol = NULL;
+
+    ret = 0;
+ cleanup:
+    virStorageVolDefFree(vol);
+    return ret;
+}
+
+static int
+virISCSIDirectReportLuns(virStoragePoolObjPtr pool,
+                         struct iscsi_context *iscsi,
+                         char *portal)
+{
+    struct scsi_task *task = NULL;
+    struct scsi_reportluns_list *list = NULL;
+    int full_size;
+    size_t i;
+    int ret = -1;
+
+    if (!(task = iscsi_reportluns_sync(iscsi, 0, 16))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to reportluns: %s"),
+                       iscsi_get_error(iscsi));
+        goto cleanup;
+    }
+
+    full_size = scsi_datain_getfullsize(task);
+
+    if (full_size > task->datain.size) {
+        scsi_free_scsi_task(task);
+        if (!(task = iscsi_reportluns_sync(iscsi, 0, full_size))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Failed to reportluns: %s"),
+                           iscsi_get_error(iscsi));
+            goto cleanup;
+        }
+    }
+
+    if (!(list = scsi_datain_unmarshall(task))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to unmarshall reportluns: %s"),
+                       iscsi_get_error(iscsi));
+        goto cleanup;
+    }
+
+    for (i = 0; i < list->num; i++) {
+        if (virISCSIDirectRefreshVol(pool, iscsi, list->luns[i], portal) < 0)
+            goto cleanup;
+    }
+
+    ret = 0;
+ cleanup:
+    scsi_free_scsi_task(task);
+    return ret;
+}
+
+static int
+virISCSIDirectDisconnect(struct iscsi_context *iscsi)
+{
+    if (iscsi_logout_sync(iscsi) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to logout: %s"),
+                       iscsi_get_error(iscsi));
+        return -1;
+    }
+    if (iscsi_disconnect(iscsi) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Failed to disconnect: %s"),
+                       iscsi_get_error(iscsi));
+        return -1;
+    }
+    return 0;
+}
+
+static int
+virStorageBackendISCSIDirectCheckPool(virStoragePoolObjPtr pool,
+                                      bool *isActive)
+{
+    *isActive = virStoragePoolObjIsActive(pool);
+    return 0;
+}
+
+static int
+virStorageBackendISCSIDirectRefreshPool(virStoragePoolObjPtr pool)
+{
+    virStoragePoolDefPtr def = virStoragePoolObjGetDef(pool);
+    struct iscsi_context *iscsi = NULL;
+    char *portal = NULL;
+    int ret = -1;
+
+    if (!(iscsi = virISCSIDirectCreateContext(def->source.initiator.iqn)))
+        goto cleanup;
+    if (!(portal = virStorageBackendISCSIDirectPortal(&def->source)))
+        goto cleanup;
+    if (virStorageBackendISCSIDirectSetAuth(iscsi, &def->source) < 0)
+        goto cleanup;
+    if (virISCSIDirectSetContext(iscsi, def->source.devices[0].path) < 0)
+        goto cleanup;
+    if (virISCSIDirectConnect(iscsi, portal) < 0)
+        goto cleanup;
+    if (virISCSIDirectReportLuns(pool, iscsi, portal) < 0)
+        goto disconect;
+
+    ret = 0;
+ disconect:
+    virISCSIDirectDisconnect(iscsi);
+ cleanup:
+    VIR_FREE(portal);
+    iscsi_destroy_context(iscsi);
+    return ret;
+}
+
 virStorageBackend virStorageBackendISCSIDirect = {
     .type = VIR_STORAGE_POOL_ISCSI_DIRECT,
 
-- 
2.18.0




More information about the libvir-list mailing list