storage_backend_iscsi.c 26.9 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 31 32
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <regex.h>
#include <fcntl.h>
#include <unistd.h>
D
David Allan 已提交
33
#include <sys/stat.h>
34

35 36
#include "datatypes.h"
#include "driver.h"
37
#include "virerror.h"
38
#include "storage_backend_scsi.h"
39
#include "storage_backend_iscsi.h"
40
#include "viralloc.h"
41
#include "virlog.h"
E
Eric Blake 已提交
42
#include "virfile.h"
43
#include "vircommand.h"
44
#include "virobject.h"
45
#include "virrandom.h"
46
#include "virstring.h"
47
#include "viruuid.h"
48

49 50
#define VIR_FROM_THIS VIR_FROM_STORAGE

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

    return portal;
}


81
static int
82
virStorageBackendISCSIExtractSession(virStoragePoolObjPtr pool,
83 84 85 86 87
                                     char **const groups,
                                     void *data)
{
    char **session = data;

88 89
    if (STREQ(groups[1], pool->def->source.devices[0].path))
        return VIR_STRDUP(*session, groups[0]);
90 91 92 93
    return 0;
}

static char *
94
virStorageBackendISCSISession(virStoragePoolObjPtr pool,
95
                              bool probe)
96 97
{
    /*
98
     * # iscsiadm --mode session
99 100 101 102 103 104
     * tcp: [1] 192.168.122.170:3260,1 demo-tgt-b
     * tcp: [2] 192.168.122.170:3260,1 demo-tgt-a
     *
     * Pull out 2nd and 4th fields
     */
    const char *regexes[] = {
105
        "^tcp:\\s+\\[(\\S+)\\]\\s+\\S+\\s+(\\S+).*$"
106 107 108 109 110 111
    };
    int vars[] = {
        2,
    };
    char *session = NULL;

112 113
    virCommandPtr cmd = virCommandNewArgList(ISCSIADM, "--mode", "session", NULL);

114
    if (virStorageBackendRunProgRegex(pool,
115
                                      cmd,
116 117 118 119
                                      1,
                                      regexes,
                                      vars,
                                      virStorageBackendISCSIExtractSession,
120
                                      &session, NULL) < 0)
121
        goto cleanup;
122

123 124
    if (session == NULL &&
        !probe) {
125 126
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("cannot find session"));
127
        goto cleanup;
128 129
    }

130 131
cleanup:
    virCommandFree(cmd);
132 133 134
    return session;
}

D
David Allan 已提交
135 136 137 138

#define LINE_SIZE 4096

static int
139
virStorageBackendIQNFound(const char *initiatoriqn,
D
David Allan 已提交
140 141 142 143 144
                          char **ifacename)
{
    int ret = IQN_MISSING, fd = -1;
    char ebuf[64];
    FILE *fp = NULL;
E
Eric Blake 已提交
145
    char *line = NULL, *newline = NULL, *iqn = NULL, *token = NULL;
146 147
    virCommandPtr cmd = virCommandNewArgList(ISCSIADM,
                                             "--mode", "iface", NULL);
D
David Allan 已提交
148 149 150

    if (VIR_ALLOC_N(line, LINE_SIZE) != 0) {
        ret = IQN_ERROR;
151 152 153
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Could not allocate memory for output of '%s'"),
                       ISCSIADM);
D
David Allan 已提交
154 155 156 157 158
        goto out;
    }

    memset(line, 0, LINE_SIZE);

159 160
    virCommandSetOutputFD(cmd, &fd);
    if (virCommandRunAsync(cmd, NULL) < 0) {
D
David Allan 已提交
161 162 163 164
        ret = IQN_ERROR;
        goto out;
    }

165
    if ((fp = VIR_FDOPEN(fd, "r")) == NULL) {
166 167 168 169
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Failed to open stream for file descriptor "
                         "when reading output from '%s': '%s'"),
                       ISCSIADM, virStrerror(errno, ebuf, sizeof(ebuf)));
D
David Allan 已提交
170 171 172 173 174 175 176 177
        ret = IQN_ERROR;
        goto out;
    }

    while (fgets(line, LINE_SIZE, fp) != NULL) {
        newline = strrchr(line, '\n');
        if (newline == NULL) {
            ret = IQN_ERROR;
178 179 180 181
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Unexpected line > %d characters "
                             "when parsing output of '%s'"),
                           LINE_SIZE, ISCSIADM);
D
David Allan 已提交
182 183 184 185 186 187 188 189 190 191
            goto out;
        }
        *newline = '\0';

        iqn = strrchr(line, ',');
        if (iqn == NULL) {
            continue;
        }
        iqn++;

192
        if (STREQ(iqn, initiatoriqn)) {
E
Eric Blake 已提交
193 194 195
            token = strchr(line, ' ');
            if (!token) {
                ret = IQN_ERROR;
196 197 198
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("Missing space when parsing output "
                                 "of '%s'"), ISCSIADM);
E
Eric Blake 已提交
199 200
                goto out;
            }
201
            if (VIR_STRNDUP(*ifacename, line, token - line) < 0) {
D
David Allan 已提交
202 203 204 205 206 207 208 209 210
                ret = IQN_ERROR;
                goto out;
            }
            VIR_DEBUG("Found interface '%s' with IQN '%s'", *ifacename, iqn);
            ret = IQN_FOUND;
            break;
        }
    }

211 212 213
    if (virCommandWait(cmd, NULL) < 0)
        ret = IQN_ERROR;

D
David Allan 已提交
214 215
out:
    if (ret == IQN_MISSING) {
216
        VIR_DEBUG("Could not find interface with IQN '%s'", iqn);
D
David Allan 已提交
217 218 219
    }

    VIR_FREE(line);
220 221
    VIR_FORCE_FCLOSE(fp);
    VIR_FORCE_CLOSE(fd);
222
    virCommandFree(cmd);
D
David Allan 已提交
223 224 225 226 227 228

    return ret;
}


static int
229
virStorageBackendCreateIfaceIQN(const char *initiatoriqn,
230
                                char **ifacename)
D
David Allan 已提交
231 232
{
    int ret = -1, exitstatus = -1;
233 234
    char *temp_ifacename;
    virCommandPtr cmd = NULL;
D
David Allan 已提交
235

236 237
    if (virAsprintf(&temp_ifacename,
                    "libvirt-iface-%08llx",
238
                    (unsigned long long)virRandomBits(30)) < 0)
239
        return -1;
D
David Allan 已提交
240 241

    VIR_DEBUG("Attempting to create interface '%s' with IQN '%s'",
242
              temp_ifacename, initiatoriqn);
D
David Allan 已提交
243

244 245 246 247 248
    cmd = virCommandNewArgList(ISCSIADM,
                               "--mode", "iface",
                               "--interface", temp_ifacename,
                               "--op", "new",
                               NULL);
D
David Allan 已提交
249 250 251 252
    /* Note that we ignore the exitstatus.  Older versions of iscsiadm
     * tools returned an exit status of > 0, even if they succeeded.
     * We will just rely on whether the interface got created
     * properly. */
253
    if (virCommandRun(cmd, &exitstatus) < 0) {
254 255 256
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Failed to run command '%s' to create new iscsi interface"),
                       ISCSIADM);
257
        goto cleanup;
D
David Allan 已提交
258
    }
259
    virCommandFree(cmd);
D
David Allan 已提交
260

261 262 263 264 265 266 267 268
    cmd = virCommandNewArgList(ISCSIADM,
                               "--mode", "iface",
                               "--interface", temp_ifacename,
                               "--op", "update",
                               "--name", "iface.initiatorname",
                               "--value",
                               initiatoriqn,
                               NULL);
D
David Allan 已提交
269 270 271
    /* Note that we ignore the exitstatus.  Older versions of iscsiadm tools
     * returned an exit status of > 0, even if they succeeded.  We will just
     * rely on whether iface file got updated properly. */
272
    if (virCommandRun(cmd, &exitstatus) < 0) {
273 274 275
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Failed to run command '%s' to update iscsi interface with IQN '%s'"),
                       ISCSIADM, initiatoriqn);
276
        goto cleanup;
D
David Allan 已提交
277 278 279
    }

    /* Check again to make sure the interface was created. */
280
    if (virStorageBackendIQNFound(initiatoriqn, ifacename) != IQN_FOUND) {
D
David Allan 已提交
281 282
        VIR_DEBUG("Failed to find interface '%s' with IQN '%s' "
                  "after attempting to create it",
283
                  &temp_ifacename[0], initiatoriqn);
284
        goto cleanup;
D
David Allan 已提交
285 286
    } else {
        VIR_DEBUG("Interface '%s' with IQN '%s' was created successfully",
287
                  *ifacename, initiatoriqn);
D
David Allan 已提交
288 289 290 291
    }

    ret = 0;

292 293 294
cleanup:
    virCommandFree(cmd);
    VIR_FREE(temp_ifacename);
D
David Allan 已提交
295 296 297 298 299 300
    if (ret != 0)
        VIR_FREE(*ifacename);
    return ret;
}


301

D
David Allan 已提交
302
static int
303 304 305 306
virStorageBackendISCSIConnection(const char *portal,
                                 const char *initiatoriqn,
                                 const char *target,
                                 const char **extraargv)
D
David Allan 已提交
307 308
{
    int ret = -1;
309 310 311 312 313 314 315
    const char *const baseargv[] = {
        ISCSIADM,
        "--mode", "node",
        "--portal", portal,
        "--targetname", target,
        NULL
    };
316
    virCommandPtr cmd;
D
David Allan 已提交
317 318
    char *ifacename = NULL;

319 320
    cmd = virCommandNewArgs(baseargv);
    virCommandAddArgSet(cmd, extraargv);
D
David Allan 已提交
321

322 323 324 325 326 327
    if (initiatoriqn) {
        switch (virStorageBackendIQNFound(initiatoriqn, &ifacename)) {
        case IQN_FOUND:
            VIR_DEBUG("ifacename: '%s'", ifacename);
            break;
        case IQN_MISSING:
328 329
            if (virStorageBackendCreateIfaceIQN(initiatoriqn,
                                                &ifacename) != 0) {
330 331 332 333 334 335 336
                goto cleanup;
            }
            break;
        case IQN_ERROR:
        default:
            goto cleanup;
        }
337
        virCommandAddArgList(cmd, "--interface", ifacename, NULL);
D
David Allan 已提交
338
    }
339

340
    if (virCommandRun(cmd, NULL) < 0)
341
        goto cleanup;
D
David Allan 已提交
342 343 344

    ret = 0;

345
cleanup:
346
    virCommandFree(cmd);
D
David Allan 已提交
347 348 349
    VIR_FREE(ifacename);

    return ret;
350 351
}

352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
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;
}
388

389
static int
390
virStorageBackendISCSIFindLUs(virStoragePoolObjPtr pool,
391
                              const char *session)
392
{
393
    char *sysfs_path;
394 395
    int retval = 0;
    uint32_t host;
396

397
    if (virAsprintf(&sysfs_path,
398
                    "/sys/class/iscsi_session/session%s/device", session) < 0)
399
        return -1;
400

401
    if (virStorageBackendISCSIGetHostNumber(sysfs_path, &host) < 0) {
402
        virReportSystemError(errno,
403 404
                             _("Failed to get host number for iSCSI session "
                               "with path '%s'"),
405
                             sysfs_path);
406
        retval = -1;
407 408
    }

409
    if (virStorageBackendSCSIFindLUs(pool, host) < 0) {
410
        virReportSystemError(errno,
411 412
                             _("Failed to find LUs on host %u"), host);
        retval = -1;
413 414
    }

415 416
    VIR_FREE(sysfs_path);

417 418
    return retval;
}
419 420

static int
421
virStorageBackendISCSIRescanLUNs(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
422 423
                                 const char *session)
{
424 425 426 427 428 429 430 431
    virCommandPtr cmd = virCommandNewArgList(ISCSIADM,
                                             "--mode", "session",
                                             "-r", session,
                                             "-R",
                                             NULL);
    int ret = virCommandRun(cmd, NULL);
    virCommandFree(cmd);
    return ret;
432 433
}

434 435 436 437
struct virStorageBackendISCSITargetList {
    size_t ntargets;
    char **targets;
};
438 439

static int
440 441 442
virStorageBackendISCSIGetTargets(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                                 char **const groups,
                                 void *data)
443
{
444 445 446
    struct virStorageBackendISCSITargetList *list = data;
    char *target;

447
    if (VIR_STRDUP(target, groups[1]) < 0)
448 449
        return -1;

450
    if (VIR_APPEND_ELEMENT(list->targets, list->ntargets, target) < 0) {
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
        VIR_FREE(target);
        return -1;
    }

    return 0;
}

static int
virStorageBackendISCSITargetAutologin(const char *portal,
                                      const char *initiatoriqn,
                                      const char *target,
                                      bool enable)
{
    const char *extraargv[] = { "--op", "update",
                                "--name", "node.startup",
                                "--value", enable ? "automatic" : "manual",
                                NULL };

    return virStorageBackendISCSIConnection(portal, initiatoriqn, target, extraargv);
}


static int
virStorageBackendISCSIScanTargets(const char *portal,
                                  const char *initiatoriqn,
                                  size_t *ntargetsret,
                                  char ***targetsret)
{
    /**
     *
     * The output of sendtargets is very simple, just two columns,
     * portal then target name
     *
     * 192.168.122.185:3260,1 iqn.2004-04.com:fedora14:iscsi.demo0.bf6d84
     * 192.168.122.185:3260,1 iqn.2004-04.com:fedora14:iscsi.demo1.bf6d84
     * 192.168.122.185:3260,1 iqn.2004-04.com:fedora14:iscsi.demo2.bf6d84
     * 192.168.122.185:3260,1 iqn.2004-04.com:fedora14:iscsi.demo3.bf6d84
     */
    const char *regexes[] = {
        "^\\s*(\\S+)\\s+(\\S+)\\s*$"
491
    };
492 493
    int vars[] = { 2 };
    struct virStorageBackendISCSITargetList list;
494 495 496 497 498 499 500
    size_t i;
    int ret = -1;
    virCommandPtr cmd = virCommandNewArgList(ISCSIADM,
                                             "--mode", "discovery",
                                             "--type", "sendtargets",
                                             "--portal", portal,
                                             NULL);
501 502 503 504

    memset(&list, 0, sizeof(list));

    if (virStorageBackendRunProgRegex(NULL, /* No pool for callback */
505
                                      cmd,
506 507 508 509
                                      1,
                                      regexes,
                                      vars,
                                      virStorageBackendISCSIGetTargets,
510 511
                                      &list, NULL) < 0)
        goto cleanup;
512

513
    for (i = 0; i < list.ntargets; i++) {
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
        /* We have to ignore failure, because we can't undo
         * the results of 'sendtargets', unless we go scrubbing
         * around in the dirt in /var/lib/iscsi.
         */
        if (virStorageBackendISCSITargetAutologin(portal,
                                                  initiatoriqn,
                                                  list.targets[i], false) < 0)
            VIR_WARN("Unable to disable auto-login on iSCSI target %s: %s",
                     portal, list.targets[i]);
    }

    if (ntargetsret && targetsret) {
        *ntargetsret = list.ntargets;
        *targetsret = list.targets;
    } else {
529
        for (i = 0; i < list.ntargets; i++) {
530 531 532 533 534
            VIR_FREE(list.targets[i]);
        }
        VIR_FREE(list.targets);
    }

535 536 537 538
    ret = 0;
cleanup:
    virCommandFree(cmd);
    return ret;
539 540 541
}


542 543 544
static char *
virStorageBackendISCSIFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
                                      const char *srcSpec,
E
Eric Blake 已提交
545
                                      unsigned int flags)
546 547 548 549 550
{
    virStoragePoolSourcePtr source = NULL;
    size_t ntargets = 0;
    char **targets = NULL;
    char *ret = NULL;
551
    size_t i;
552 553 554 555 556 557 558
    virStoragePoolSourceList list = {
        .type = VIR_STORAGE_POOL_ISCSI,
        .nsources = 0,
        .sources = NULL
    };
    char *portal = NULL;

E
Eric Blake 已提交
559 560
    virCheckFlags(0, NULL);

561 562 563 564 565 566 567
    if (!srcSpec) {
        virReportError(VIR_ERR_INVALID_ARG,
                       "%s", _("hostname and device path "
                               "must be specified for iscsi sources"));
        return NULL;
    }

568 569 570 571
    if (!(source = virStoragePoolDefParseSourceString(srcSpec,
                                                      list.type)))
        return NULL;

572
    if (source->nhost != 1) {
573 574
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Expected exactly 1 host for the storage pool"));
575 576 577
        goto cleanup;
    }

578 579 580 581 582 583 584 585
    if (!(portal = virStorageBackendISCSIPortal(source)))
        goto cleanup;

    if (virStorageBackendISCSIScanTargets(portal,
                                          source->initiator.iqn,
                                          &ntargets, &targets) < 0)
        goto cleanup;

586
    if (VIR_ALLOC_N(list.sources, ntargets) < 0)
587 588
        goto cleanup;

589
    for (i = 0; i < ntargets; i++) {
E
Eric Blake 已提交
590
        if (VIR_ALLOC_N(list.sources[i].devices, 1) < 0 ||
591
            VIR_ALLOC_N(list.sources[i].hosts, 1) < 0)
592
            goto cleanup;
E
Eric Blake 已提交
593 594
        list.sources[i].nhost = 1;
        list.sources[i].hosts[0] = source->hosts[0];
595 596 597 598 599 600
        list.sources[i].initiator = source->initiator;
        list.sources[i].ndevice = 1;
        list.sources[i].devices[0].path = targets[i];
        list.nsources++;
    }

601
    if (!(ret = virStoragePoolSourceListFormat(&list)))
602 603 604 605
        goto cleanup;

cleanup:
    if (list.sources) {
606
        for (i = 0; i < ntargets; i++) {
E
Eric Blake 已提交
607
            VIR_FREE(list.sources[i].hosts);
608
            VIR_FREE(list.sources[i].devices);
E
Eric Blake 已提交
609
        }
610 611
        VIR_FREE(list.sources);
    }
612
    for (i = 0; i < ntargets; i++)
613 614 615 616 617 618 619
        VIR_FREE(targets[i]);
    VIR_FREE(targets);
    VIR_FREE(portal);
    virStoragePoolSourceFree(source);
    return ret;
}

620 621 622 623 624 625 626 627 628 629
static int
virStorageBackendISCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED,
                                virStoragePoolObjPtr pool,
                                bool *isActive)
{
    char *session = NULL;
    int ret = -1;

    *isActive = false;

630
    if (pool->def->source.nhost != 1) {
631 632
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                        _("Expected exactly 1 host for the storage pool"));
633 634 635 636
        return -1;
    }

    if (pool->def->source.hosts[0].name == NULL) {
637 638
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("missing source host"));
639 640 641 642 643
        return -1;
    }

    if (pool->def->source.ndevice != 1 ||
        pool->def->source.devices[0].path == NULL) {
644 645
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("missing source device"));
646 647 648
        return -1;
    }

649
    if ((session = virStorageBackendISCSISession(pool, true)) != NULL) {
650 651 652 653 654 655 656 657
        *isActive = true;
        VIR_FREE(session);
    }
    ret = 0;

    return ret;
}

658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
static int
virStorageBackendISCSINodeUpdate(const char *portal,
                                 const char *target,
                                 const char *name,
                                 const char *value)
{
     virCommandPtr cmd = NULL;
     int status;
     int ret = -1;

     cmd = virCommandNewArgList(ISCSIADM,
                                "--mode", "node",
                                "--portal", portal,
                                "--target", target,
                                "--op", "update",
                                "--name", name,
                                "--value", value,
                                NULL);

677
    /* Ignore non-zero status.  */
678 679 680 681 682 683 684 685 686 687 688 689
    if (virCommandRun(cmd, &status) < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Failed to update '%s' of node mode for target '%s'"),
                       name, target);
        goto cleanup;
    }

    ret = 0;
cleanup:
    virCommandFree(cmd);
    return ret;
}
690

691
static int
692 693 694 695 696 697 698 699
virStorageBackendISCSISetAuth(const char *portal,
                              virConnectPtr conn,
                              virStoragePoolDefPtr def)
{
    virSecretPtr secret = NULL;
    unsigned char *secret_value = NULL;
    virStoragePoolAuthChap chap;
    int ret = -1;
700
    char uuidStr[VIR_UUID_STRING_BUFLEN];
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730

    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) {
731
            if (chap.secret.uuidUsable) {
732
                virUUIDFormat(chap.secret.uuid, uuidStr);
733 734 735
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("could not get the value of the secret "
                                 "for username %s using uuid '%s'"),
736
                                 chap.username, uuidStr);
737 738 739 740 741 742
            } 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);
            }
743 744 745
            goto cleanup;
        }
    } else {
746
        if (chap.secret.uuidUsable) {
747
            virUUIDFormat(chap.secret.uuid, uuidStr);
748 749
            virReportError(VIR_ERR_NO_SECRET,
                           _("no secret matches uuid '%s'"),
750
                           uuidStr);
751 752 753 754 755
        } else {
            virReportError(VIR_ERR_NO_SECRET,
                           _("no secret matches usage value '%s'"),
                           chap.secret.usage);
        }
756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
        goto cleanup;
    }

    if (virStorageBackendISCSINodeUpdate(portal,
                                         def->source.devices[0].path,
                                         "node.session.auth.authmethod",
                                         "CHAP") < 0 ||
        virStorageBackendISCSINodeUpdate(portal,
                                         def->source.devices[0].path,
                                         "node.session.auth.username",
                                         chap.username) < 0 ||
        virStorageBackendISCSINodeUpdate(portal,
                                         def->source.devices[0].path,
                                         "node.session.auth.password",
                                         (const char *)secret_value) < 0)
        goto cleanup;

    ret = 0;

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

static int
virStorageBackendISCSIStartPool(virConnectPtr conn,
783 784 785
                                virStoragePoolObjPtr pool)
{
    char *portal = NULL;
786 787 788
    char *session = NULL;
    int ret = -1;
    const char *loginargv[] = { "--login", NULL };
789

790
    if (pool->def->source.nhost != 1) {
791 792
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                        _("Expected exactly 1 host for the storage pool"));
793 794 795 796
        return -1;
    }

    if (pool->def->source.hosts[0].name == NULL) {
797 798
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("missing source host"));
799 800 801 802 803
        return -1;
    }

    if (pool->def->source.ndevice != 1 ||
        pool->def->source.devices[0].path == NULL) {
804 805
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("missing source device"));
806 807 808
        return -1;
    }

809
    if ((session = virStorageBackendISCSISession(pool, true)) == NULL) {
810 811 812 813 814 815
        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 :-(
         */
816 817 818
        if (virStorageBackendISCSIScanTargets(portal,
                                              pool->def->source.initiator.iqn,
                                              NULL, NULL) < 0)
819 820
            goto cleanup;

821 822 823
        if (virStorageBackendISCSISetAuth(portal, conn, pool->def) < 0)
            goto cleanup;

824 825 826 827 828
        if (virStorageBackendISCSIConnection(portal,
                                             pool->def->source.initiator.iqn,
                                             pool->def->source.devices[0].path,
                                             loginargv) < 0)
            goto cleanup;
829
    }
830 831 832
    ret = 0;

cleanup:
833
    VIR_FREE(portal);
834 835
    VIR_FREE(session);
    return ret;
836 837 838
}

static int
839
virStorageBackendISCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
840 841 842 843 844 845
                                  virStoragePoolObjPtr pool)
{
    char *session = NULL;

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

846
    if ((session = virStorageBackendISCSISession(pool, false)) == NULL)
847
        goto cleanup;
848
    if (virStorageBackendISCSIRescanLUNs(pool, session) < 0)
849
        goto cleanup;
850
    if (virStorageBackendISCSIFindLUs(pool, session) < 0)
851
        goto cleanup;
852
    VIR_FREE(session);
853 854 855 856

    return 0;

 cleanup:
857
    VIR_FREE(session);
858 859 860 861 862
    return -1;
}


static int
863
virStorageBackendISCSIStopPool(virConnectPtr conn ATTRIBUTE_UNUSED,
864 865
                               virStoragePoolObjPtr pool)
{
866
    const char *logoutargv[] = { "--logout", NULL };
867
    char *portal;
868
    int ret = -1;
869

870
    if ((portal = virStorageBackendISCSIPortal(&pool->def->source)) == NULL)
871 872
        return -1;

873 874 875 876 877 878
    if (virStorageBackendISCSIConnection(portal,
                                         pool->def->source.initiator.iqn,
                                         pool->def->source.devices[0].path,
                                         logoutargv) < 0)
        goto cleanup;
    ret = 0;
879

880 881 882
cleanup:
    VIR_FREE(portal);
    return ret;
883 884 885
}

virStorageBackend virStorageBackendISCSI = {
886
    .type = VIR_STORAGE_POOL_ISCSI,
887

888
    .checkPool = virStorageBackendISCSICheckPool,
889 890 891
    .startPool = virStorageBackendISCSIStartPool,
    .refreshPool = virStorageBackendISCSIRefreshPool,
    .stopPool = virStorageBackendISCSIStopPool,
892
    .findPoolSources = virStorageBackendISCSIFindPoolSources,
893
};