mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-10-14 18:19:22 +00:00
01dd26811a
Change the SetContext function to be able to take the session type in argument. Took the function findPoolSources of iscsi backend and wired it to my function since the formatting is essentially the same. Signed-off-by: Clementine Hayat <clem@lse.epita.fr> Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
600 lines
17 KiB
C
600 lines
17 KiB
C
/*
|
|
* storage_backend_iscsi_direct.c: storage backend for iSCSI using libiscsi
|
|
*
|
|
* Copyright (C) 2018 Clementine Hayat.
|
|
*
|
|
* 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/>.
|
|
*
|
|
* Author: Clementine Hayat <clem@lse.epita.fr>
|
|
*/
|
|
|
|
#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,
|
|
enum iscsi_session_type session)
|
|
{
|
|
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 (session == ISCSI_SESSION_NORMAL) {
|
|
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, session) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to set session type: %s"),
|
|
iscsi_get_error(iscsi));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
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
|
|
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)
|
|
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
|
|
virISCSIDirectUpdateTargets(struct iscsi_context *iscsi,
|
|
size_t *ntargets,
|
|
char ***targets)
|
|
{
|
|
int ret = -1;
|
|
struct iscsi_discovery_address *addr;
|
|
struct iscsi_discovery_address *tmp_addr;
|
|
size_t tmp_ntargets = 0;
|
|
char **tmp_targets = NULL;
|
|
|
|
if (!(addr = iscsi_discovery_sync(iscsi))) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to discover session: %s"),
|
|
iscsi_get_error(iscsi));
|
|
return ret;
|
|
}
|
|
|
|
for (tmp_addr = addr; tmp_addr; tmp_addr = tmp_addr->next) {
|
|
char *target = NULL;
|
|
|
|
if (VIR_STRDUP(target, tmp_addr->target_name) < 0)
|
|
goto cleanup;
|
|
|
|
if (VIR_APPEND_ELEMENT(tmp_targets, tmp_ntargets, target) < 0) {
|
|
VIR_FREE(target);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
VIR_STEAL_PTR(*targets, tmp_targets);
|
|
*ntargets = tmp_ntargets;
|
|
tmp_ntargets = 0;
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
iscsi_free_discovery_data(iscsi, addr);
|
|
virStringListFreeCount(tmp_targets, tmp_ntargets);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
virISCSIDirectScanTargets(char *initiator_iqn,
|
|
char *portal,
|
|
size_t *ntargets,
|
|
char ***targets)
|
|
{
|
|
struct iscsi_context *iscsi = NULL;
|
|
int ret = -1;
|
|
|
|
if (!(iscsi = virISCSIDirectCreateContext(initiator_iqn)))
|
|
goto cleanup;
|
|
if (virISCSIDirectSetContext(iscsi, NULL, ISCSI_SESSION_DISCOVERY) < 0)
|
|
goto cleanup;
|
|
if (virISCSIDirectConnect(iscsi, portal) < 0)
|
|
goto cleanup;
|
|
if (virISCSIDirectUpdateTargets(iscsi, ntargets, targets) < 0)
|
|
goto disconnect;
|
|
|
|
ret = 0;
|
|
disconnect:
|
|
virISCSIDirectDisconnect(iscsi);
|
|
cleanup:
|
|
iscsi_destroy_context(iscsi);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
virStorageBackendISCSIDirectCheckPool(virStoragePoolObjPtr pool,
|
|
bool *isActive)
|
|
{
|
|
*isActive = virStoragePoolObjIsActive(pool);
|
|
return 0;
|
|
}
|
|
|
|
static char *
|
|
virStorageBackendISCSIDirectFindPoolSources(const char *srcSpec,
|
|
unsigned int flags)
|
|
{
|
|
virStoragePoolSourcePtr source = NULL;
|
|
size_t ntargets = 0;
|
|
char **targets = NULL;
|
|
char *ret = NULL;
|
|
size_t i;
|
|
virStoragePoolSourceList list = {
|
|
.type = VIR_STORAGE_POOL_ISCSI_DIRECT,
|
|
.nsources = 0,
|
|
.sources = NULL
|
|
};
|
|
char *portal = NULL;
|
|
|
|
virCheckFlags(0, NULL);
|
|
|
|
if (!srcSpec) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("hostname must be specified for iscsi sources"));
|
|
return NULL;
|
|
}
|
|
|
|
if (!(source = virStoragePoolDefParseSourceString(srcSpec, list.type)))
|
|
return NULL;
|
|
|
|
if (source->nhost != 1) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Expected exactly 1 host for the storage pool"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!source->initiator.iqn) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("missing initiator IQN"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(portal = virStorageBackendISCSIDirectPortal(source)))
|
|
goto cleanup;
|
|
|
|
if (virISCSIDirectScanTargets(source->initiator.iqn, portal, &ntargets, &targets) < 0)
|
|
goto cleanup;
|
|
|
|
if (VIR_ALLOC_N(list.sources, ntargets) < 0)
|
|
goto cleanup;
|
|
|
|
for (i = 0; i < ntargets; i++) {
|
|
if (VIR_ALLOC_N(list.sources[i].devices, 1) < 0 ||
|
|
VIR_ALLOC_N(list.sources[i].hosts, 1) < 0)
|
|
goto cleanup;
|
|
list.sources[i].nhost = 1;
|
|
list.sources[i].hosts[0] = source->hosts[0];
|
|
list.sources[i].initiator = source->initiator;
|
|
list.sources[i].ndevice = 1;
|
|
list.sources[i].devices[0].path = targets[i];
|
|
list.nsources++;
|
|
}
|
|
|
|
if (!(ret = virStoragePoolSourceListFormat(&list)))
|
|
goto cleanup;
|
|
|
|
cleanup:
|
|
if (list.sources) {
|
|
for (i = 0; i < ntargets; i++) {
|
|
VIR_FREE(list.sources[i].hosts);
|
|
VIR_FREE(list.sources[i].devices);
|
|
}
|
|
VIR_FREE(list.sources);
|
|
}
|
|
for (i = 0; i < ntargets; i++)
|
|
VIR_FREE(targets[i]);
|
|
VIR_FREE(targets);
|
|
VIR_FREE(portal);
|
|
virStoragePoolSourceFree(source);
|
|
return ret;
|
|
}
|
|
|
|
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, ISCSI_SESSION_NORMAL) < 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,
|
|
|
|
.checkPool = virStorageBackendISCSIDirectCheckPool,
|
|
.findPoolSources = virStorageBackendISCSIDirectFindPoolSources,
|
|
.refreshPool = virStorageBackendISCSIDirectRefreshPool,
|
|
};
|
|
|
|
int
|
|
virStorageBackendISCSIDirectRegister(void)
|
|
{
|
|
return virStorageBackendRegister(&virStorageBackendISCSIDirect);
|
|
}
|