storage_backend_iscsi.c 13.7 KB
Newer Older
1 2 3
/*
 * storage_backend_iscsi.c: storage backend for iSCSI handling
 *
4
 * Copyright (C) 2007-2014 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17
 * 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
18
 * License along with this library.  If not, see
O
Osier Yang 已提交
19
 * <http://www.gnu.org/licenses/>.
20 21 22 23 24 25
 *
 * Author: Daniel P. Berrange <berrange@redhat.com>
 */

#include <config.h>

26
#include <dirent.h>
27 28 29 30
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
D
David Allan 已提交
31
#include <sys/stat.h>
32

33 34
#include "datatypes.h"
#include "driver.h"
35
#include "storage_backend_scsi.h"
36
#include "storage_backend_iscsi.h"
37
#include "viralloc.h"
38
#include "vircommand.h"
39 40
#include "virerror.h"
#include "virfile.h"
41
#include "viriscsi.h"
42
#include "virlog.h"
43
#include "virobject.h"
44
#include "virstring.h"
45
#include "viruuid.h"
46

47 48
#define VIR_FROM_THIS VIR_FROM_STORAGE

49 50
VIR_LOG_INIT("storage.storage_backend_iscsi");

51 52
#define ISCSI_DEFAULT_TARGET_PORT 3260

53 54 55
static char *
virStorageBackendISCSIPortal(virStoragePoolSourcePtr source)
{
56
    char *portal = NULL;
57

58
    if (source->nhost != 1) {
59 60
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Expected exactly 1 host for the storage pool"));
61 62 63
        return NULL;
    }

64 65
    if (source->hosts[0].port == 0)
        source->hosts[0].port = ISCSI_DEFAULT_TARGET_PORT;
66

67 68 69 70
    if (strchr(source->hosts[0].name, ':')) {
        ignore_value(virAsprintf(&portal, "[%s]:%d,1",
                                 source->hosts[0].name,
                                 source->hosts[0].port));
71
    } else {
72 73 74
        ignore_value(virAsprintf(&portal, "%s:%d,1",
                                 source->hosts[0].name,
                                 source->hosts[0].port));
75 76 77 78 79
    }

    return portal;
}

80

81 82 83 84
static char *
virStorageBackendISCSISession(virStoragePoolObjPtr pool,
                              bool probe)
{
85
    return virISCSIGetSession(pool->def->source.devices[0].path, probe);
86 87 88
}


89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
static int
virStorageBackendISCSIGetHostNumber(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);

    virFileWaitForDevices();

    sysdir = opendir(sysfs_path);

    if (sysdir == NULL) {
        virReportSystemError(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;
}
125

126
static int
127
virStorageBackendISCSIFindLUs(virStoragePoolObjPtr pool,
128
                              const char *session)
129
{
130
    char *sysfs_path;
131 132
    int retval = 0;
    uint32_t host;
133

134
    if (virAsprintf(&sysfs_path,
135
                    "/sys/class/iscsi_session/session%s/device", session) < 0)
136
        return -1;
137

138
    if (virStorageBackendISCSIGetHostNumber(sysfs_path, &host) < 0) {
139
        virReportSystemError(errno,
140 141
                             _("Failed to get host number for iSCSI session "
                               "with path '%s'"),
142
                             sysfs_path);
143
        retval = -1;
144 145
    }

146
    if (virStorageBackendSCSIFindLUs(pool, host) < 0) {
147
        virReportSystemError(errno,
148 149
                             _("Failed to find LUs on host %u"), host);
        retval = -1;
150 151
    }

152 153
    VIR_FREE(sysfs_path);

154 155
    return retval;
}
156 157


158 159 160
static char *
virStorageBackendISCSIFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
                                      const char *srcSpec,
E
Eric Blake 已提交
161
                                      unsigned int flags)
162 163 164 165 166
{
    virStoragePoolSourcePtr source = NULL;
    size_t ntargets = 0;
    char **targets = NULL;
    char *ret = NULL;
167
    size_t i;
168 169 170 171 172 173 174
    virStoragePoolSourceList list = {
        .type = VIR_STORAGE_POOL_ISCSI,
        .nsources = 0,
        .sources = NULL
    };
    char *portal = NULL;

E
Eric Blake 已提交
175 176
    virCheckFlags(0, NULL);

177 178 179 180 181 182 183
    if (!srcSpec) {
        virReportError(VIR_ERR_INVALID_ARG,
                       "%s", _("hostname and device path "
                               "must be specified for iscsi sources"));
        return NULL;
    }

184 185 186 187
    if (!(source = virStoragePoolDefParseSourceString(srcSpec,
                                                      list.type)))
        return NULL;

188
    if (source->nhost != 1) {
189 190
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Expected exactly 1 host for the storage pool"));
191 192 193
        goto cleanup;
    }

194 195 196
    if (!(portal = virStorageBackendISCSIPortal(source)))
        goto cleanup;

197 198 199
    if (virISCSIScanTargets(portal,
                            source->initiator.iqn,
                            &ntargets, &targets) < 0)
200 201
        goto cleanup;

202
    if (VIR_ALLOC_N(list.sources, ntargets) < 0)
203 204
        goto cleanup;

205
    for (i = 0; i < ntargets; i++) {
E
Eric Blake 已提交
206
        if (VIR_ALLOC_N(list.sources[i].devices, 1) < 0 ||
207
            VIR_ALLOC_N(list.sources[i].hosts, 1) < 0)
208
            goto cleanup;
E
Eric Blake 已提交
209 210
        list.sources[i].nhost = 1;
        list.sources[i].hosts[0] = source->hosts[0];
211 212 213 214 215 216
        list.sources[i].initiator = source->initiator;
        list.sources[i].ndevice = 1;
        list.sources[i].devices[0].path = targets[i];
        list.nsources++;
    }

217
    if (!(ret = virStoragePoolSourceListFormat(&list)))
218 219 220 221
        goto cleanup;

cleanup:
    if (list.sources) {
222
        for (i = 0; i < ntargets; i++) {
E
Eric Blake 已提交
223
            VIR_FREE(list.sources[i].hosts);
224
            VIR_FREE(list.sources[i].devices);
E
Eric Blake 已提交
225
        }
226 227
        VIR_FREE(list.sources);
    }
228
    for (i = 0; i < ntargets; i++)
229 230 231 232 233 234 235
        VIR_FREE(targets[i]);
    VIR_FREE(targets);
    VIR_FREE(portal);
    virStoragePoolSourceFree(source);
    return ret;
}

236 237 238 239 240 241 242 243 244 245
static int
virStorageBackendISCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED,
                                virStoragePoolObjPtr pool,
                                bool *isActive)
{
    char *session = NULL;
    int ret = -1;

    *isActive = false;

246
    if (pool->def->source.nhost != 1) {
247 248
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                        _("Expected exactly 1 host for the storage pool"));
249 250 251 252
        return -1;
    }

    if (pool->def->source.hosts[0].name == NULL) {
253 254
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("missing source host"));
255 256 257 258 259
        return -1;
    }

    if (pool->def->source.ndevice != 1 ||
        pool->def->source.devices[0].path == NULL) {
260 261
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("missing source device"));
262 263 264
        return -1;
    }

265
    if ((session = virStorageBackendISCSISession(pool, true)) != NULL) {
266 267 268 269 270 271 272 273 274
        *isActive = true;
        VIR_FREE(session);
    }
    ret = 0;

    return ret;
}


275
static int
276 277 278 279 280 281 282 283
virStorageBackendISCSISetAuth(const char *portal,
                              virConnectPtr conn,
                              virStoragePoolDefPtr def)
{
    virSecretPtr secret = NULL;
    unsigned char *secret_value = NULL;
    virStoragePoolAuthChap chap;
    int ret = -1;
284
    char uuidStr[VIR_UUID_STRING_BUFLEN];
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314

    if (def->source.authType == VIR_STORAGE_POOL_AUTH_NONE)
        return 0;

    if (def->source.authType != VIR_STORAGE_POOL_AUTH_CHAP) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("iscsi pool only supports 'chap' auth type"));
        return -1;
    }

    if (!conn) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("iscsi 'chap' authentication not supported "
                         "for autostarted pools"));
        return -1;
    }

    chap = def->source.auth.chap;
    if (chap.secret.uuidUsable)
        secret = virSecretLookupByUUID(conn, chap.secret.uuid);
    else
        secret = virSecretLookupByUsage(conn, VIR_SECRET_USAGE_TYPE_ISCSI,
                                        chap.secret.usage);

    if (secret) {
        size_t secret_size;
        secret_value =
            conn->secretDriver->secretGetValue(secret, &secret_size, 0,
                                               VIR_SECRET_GET_VALUE_INTERNAL_CALL);
        if (!secret_value) {
315
            if (chap.secret.uuidUsable) {
316
                virUUIDFormat(chap.secret.uuid, uuidStr);
317 318 319
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("could not get the value of the secret "
                                 "for username %s using uuid '%s'"),
320
                                 chap.username, uuidStr);
321 322 323 324 325 326
            } else {
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("could not get the value of the secret "
                                 "for username %s using usage value '%s'"),
                                 chap.username, chap.secret.usage);
            }
327 328 329
            goto cleanup;
        }
    } else {
330
        if (chap.secret.uuidUsable) {
331
            virUUIDFormat(chap.secret.uuid, uuidStr);
332 333
            virReportError(VIR_ERR_NO_SECRET,
                           _("no secret matches uuid '%s'"),
334
                           uuidStr);
335 336 337 338 339
        } else {
            virReportError(VIR_ERR_NO_SECRET,
                           _("no secret matches usage value '%s'"),
                           chap.secret.usage);
        }
340 341 342
        goto cleanup;
    }

343 344 345 346 347 348 349 350 351 352 353 354
    if (virISCSINodeUpdate(portal,
                           def->source.devices[0].path,
                           "node.session.auth.authmethod",
                           "CHAP") < 0 ||
        virISCSINodeUpdate(portal,
                           def->source.devices[0].path,
                           "node.session.auth.username",
                           chap.username) < 0 ||
        virISCSINodeUpdate(portal,
                           def->source.devices[0].path,
                           "node.session.auth.password",
                           (const char *)secret_value) < 0)
355 356 357 358 359 360 361 362 363 364 365 366
        goto cleanup;

    ret = 0;

cleanup:
    virObjectUnref(secret);
    VIR_FREE(secret_value);
    return ret;
}

static int
virStorageBackendISCSIStartPool(virConnectPtr conn,
367 368 369
                                virStoragePoolObjPtr pool)
{
    char *portal = NULL;
370 371
    char *session = NULL;
    int ret = -1;
372

373
    if (pool->def->source.nhost != 1) {
374 375
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                        _("Expected exactly 1 host for the storage pool"));
376 377 378 379
        return -1;
    }

    if (pool->def->source.hosts[0].name == NULL) {
380 381
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("missing source host"));
382 383 384 385 386
        return -1;
    }

    if (pool->def->source.ndevice != 1 ||
        pool->def->source.devices[0].path == NULL) {
387 388
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("missing source device"));
389 390 391
        return -1;
    }

392
    if ((session = virStorageBackendISCSISession(pool, true)) == NULL) {
393 394 395 396 397 398
        if ((portal = virStorageBackendISCSIPortal(&pool->def->source)) == NULL)
            goto cleanup;
        /*
         * iscsiadm doesn't let you login to a target, unless you've
         * first issued a 'sendtargets' command to the portal :-(
         */
399 400 401
        if (virISCSIScanTargets(portal,
                                pool->def->source.initiator.iqn,
                                NULL, NULL) < 0)
402 403
            goto cleanup;

404 405 406
        if (virStorageBackendISCSISetAuth(portal, conn, pool->def) < 0)
            goto cleanup;

407 408 409
        if (virISCSIConnectionLogin(portal,
                                    pool->def->source.initiator.iqn,
                                    pool->def->source.devices[0].path) < 0)
410
            goto cleanup;
411
    }
412 413 414
    ret = 0;

cleanup:
415
    VIR_FREE(portal);
416 417
    VIR_FREE(session);
    return ret;
418 419 420
}

static int
421
virStorageBackendISCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
422 423 424 425 426 427
                                  virStoragePoolObjPtr pool)
{
    char *session = NULL;

    pool->def->allocation = pool->def->capacity = pool->def->available = 0;

428
    if ((session = virStorageBackendISCSISession(pool, false)) == NULL)
429
        goto cleanup;
430
    if (virISCSIRescanLUNs(session) < 0)
431
        goto cleanup;
432
    if (virStorageBackendISCSIFindLUs(pool, session) < 0)
433
        goto cleanup;
434
    VIR_FREE(session);
435 436 437 438

    return 0;

 cleanup:
439
    VIR_FREE(session);
440 441 442 443 444
    return -1;
}


static int
445
virStorageBackendISCSIStopPool(virConnectPtr conn ATTRIBUTE_UNUSED,
446 447 448
                               virStoragePoolObjPtr pool)
{
    char *portal;
449
    int ret = -1;
450

451
    if ((portal = virStorageBackendISCSIPortal(&pool->def->source)) == NULL)
452 453
        return -1;

454 455 456
    if (virISCSIConnectionLogout(portal,
                                 pool->def->source.initiator.iqn,
                                 pool->def->source.devices[0].path) < 0)
457 458
        goto cleanup;
    ret = 0;
459

460 461 462
cleanup:
    VIR_FREE(portal);
    return ret;
463 464 465
}

virStorageBackend virStorageBackendISCSI = {
466
    .type = VIR_STORAGE_POOL_ISCSI,
467

468
    .checkPool = virStorageBackendISCSICheckPool,
469 470 471
    .startPool = virStorageBackendISCSIStartPool,
    .refreshPool = virStorageBackendISCSIRefreshPool,
    .stopPool = virStorageBackendISCSIStopPool,
472
    .findPoolSources = virStorageBackendISCSIFindPoolSources,
473
};