storage_backend_iscsi.c 27.0 KB
Newer Older
1 2 3
/*
 * storage_backend_iscsi.c: storage backend for iSCSI handling
 *
E
Eric Blake 已提交
4
 * Copyright (C) 2007-2008, 2010-2012 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

48 49
#define VIR_FROM_THIS VIR_FROM_STORAGE

50 51
#define ISCSI_DEFAULT_TARGET_PORT 3260

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

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

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

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

    return portal;
}


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

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

static char *
93
virStorageBackendISCSISession(virStoragePoolObjPtr pool,
94
                              int probe)
95 96
{
    /*
97
     * # iscsiadm --mode session
98 99 100 101 102 103 104 105 106 107 108 109 110
     * 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[] = {
        "^tcp:\\s+\\[(\\S+)\\]\\s+\\S+\\s+(\\S+)\\s*$"
    };
    int vars[] = {
        2,
    };
    char *session = NULL;

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

113 114 115 116
    /* 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 session got filled in properly.
     */
117
    if (virStorageBackendRunProgRegex(pool,
118
                                      cmd,
119 120 121 122
                                      1,
                                      regexes,
                                      vars,
                                      virStorageBackendISCSIExtractSession,
123
                                      &session, NULL) < 0)
124
        goto cleanup;
125

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

133 134
cleanup:
    virCommandFree(cmd);
135 136 137
    return session;
}

D
David Allan 已提交
138 139 140 141

#define LINE_SIZE 4096

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

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

    memset(line, 0, LINE_SIZE);

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

168
    if ((fp = VIR_FDOPEN(fd, "r")) == NULL) {
169 170 171 172
        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 已提交
173 174 175 176 177 178 179 180
        ret = IQN_ERROR;
        goto out;
    }

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

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

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

214 215 216
    if (virCommandWait(cmd, NULL) < 0)
        ret = IQN_ERROR;

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

    VIR_FREE(line);
223 224
    VIR_FORCE_FCLOSE(fp);
    VIR_FORCE_CLOSE(fd);
225
    virCommandFree(cmd);
D
David Allan 已提交
226 227 228 229 230 231

    return ret;
}


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

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

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

247 248 249 250 251
    cmd = virCommandNewArgList(ISCSIADM,
                               "--mode", "iface",
                               "--interface", temp_ifacename,
                               "--op", "new",
                               NULL);
D
David Allan 已提交
252 253 254 255
    /* 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. */
256
    if (virCommandRun(cmd, &exitstatus) < 0) {
257 258 259
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Failed to run command '%s' to create new iscsi interface"),
                       ISCSIADM);
260
        goto cleanup;
D
David Allan 已提交
261
    }
262
    virCommandFree(cmd);
D
David Allan 已提交
263

264 265 266 267 268 269 270 271
    cmd = virCommandNewArgList(ISCSIADM,
                               "--mode", "iface",
                               "--interface", temp_ifacename,
                               "--op", "update",
                               "--name", "iface.initiatorname",
                               "--value",
                               initiatoriqn,
                               NULL);
D
David Allan 已提交
272 273 274
    /* 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. */
275
    if (virCommandRun(cmd, &exitstatus) < 0) {
276 277 278
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Failed to run command '%s' to update iscsi interface with IQN '%s'"),
                       ISCSIADM, initiatoriqn);
279
        goto cleanup;
D
David Allan 已提交
280 281 282
    }

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

    ret = 0;

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


304

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

322 323
    cmd = virCommandNewArgs(baseargv);
    virCommandAddArgSet(cmd, extraargv);
D
David Allan 已提交
324

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

343
    if (virCommandRun(cmd, NULL) < 0)
344
        goto cleanup;
D
David Allan 已提交
345 346 347

    ret = 0;

348
cleanup:
349
    virCommandFree(cmd);
D
David Allan 已提交
350 351 352
    VIR_FREE(ifacename);

    return ret;
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 388 389 390
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;
}
391

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

400
    if (virAsprintf(&sysfs_path,
401
                    "/sys/class/iscsi_session/session%s/device", session) < 0)
402
        return -1;
403

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

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

418 419
    VIR_FREE(sysfs_path);

420 421
    return retval;
}
422 423

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

437 438 439 440
struct virStorageBackendISCSITargetList {
    size_t ntargets;
    char **targets;
};
441 442

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

450
    if (VIR_STRDUP(target, groups[1]) < 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 491 492 493 494 495 496
        return -1;

    if (VIR_REALLOC_N(list->targets, list->ntargets + 1) < 0) {
        VIR_FREE(target);
        return -1;
    }

    list->targets[list->ntargets] = target;
    list->ntargets++;

    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*$"
497
    };
498 499
    int vars[] = { 2 };
    struct virStorageBackendISCSITargetList list;
500 501 502 503 504 505 506
    size_t i;
    int ret = -1;
    virCommandPtr cmd = virCommandNewArgList(ISCSIADM,
                                             "--mode", "discovery",
                                             "--type", "sendtargets",
                                             "--portal", portal,
                                             NULL);
507 508 509 510

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

    if (virStorageBackendRunProgRegex(NULL, /* No pool for callback */
511
                                      cmd,
512 513 514 515
                                      1,
                                      regexes,
                                      vars,
                                      virStorageBackendISCSIGetTargets,
516 517
                                      &list, NULL) < 0)
        goto cleanup;
518

519
    for (i = 0; i < list.ntargets; i++) {
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
        /* 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 {
535
        for (i = 0; i < list.ntargets; i++) {
536 537 538 539 540
            VIR_FREE(list.targets[i]);
        }
        VIR_FREE(list.targets);
    }

541 542 543 544
    ret = 0;
cleanup:
    virCommandFree(cmd);
    return ret;
545 546 547
}


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

E
Eric Blake 已提交
565 566
    virCheckFlags(0, NULL);

567 568 569 570 571 572 573
    if (!srcSpec) {
        virReportError(VIR_ERR_INVALID_ARG,
                       "%s", _("hostname and device path "
                               "must be specified for iscsi sources"));
        return NULL;
    }

574 575 576 577
    if (!(source = virStoragePoolDefParseSourceString(srcSpec,
                                                      list.type)))
        return NULL;

578
    if (source->nhost != 1) {
579 580
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Expected exactly 1 host for the storage pool"));
581 582 583
        goto cleanup;
    }

584 585 586 587 588 589 590 591
    if (!(portal = virStorageBackendISCSIPortal(source)))
        goto cleanup;

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

592
    if (VIR_ALLOC_N(list.sources, ntargets) < 0)
593 594
        goto cleanup;

595
    for (i = 0; i < ntargets; i++) {
E
Eric Blake 已提交
596
        if (VIR_ALLOC_N(list.sources[i].devices, 1) < 0 ||
597
            VIR_ALLOC_N(list.sources[i].hosts, 1) < 0)
598
            goto cleanup;
E
Eric Blake 已提交
599 600
        list.sources[i].nhost = 1;
        list.sources[i].hosts[0] = source->hosts[0];
601 602 603 604 605 606
        list.sources[i].initiator = source->initiator;
        list.sources[i].ndevice = 1;
        list.sources[i].devices[0].path = targets[i];
        list.nsources++;
    }

607
    if (!(ret = virStoragePoolSourceListFormat(&list)))
608 609 610 611
        goto cleanup;

cleanup:
    if (list.sources) {
612
        for (i = 0; i < ntargets; i++) {
E
Eric Blake 已提交
613
            VIR_FREE(list.sources[i].hosts);
614
            VIR_FREE(list.sources[i].devices);
E
Eric Blake 已提交
615
        }
616 617
        VIR_FREE(list.sources);
    }
618
    for (i = 0; i < ntargets; i++)
619 620 621 622 623 624 625
        VIR_FREE(targets[i]);
    VIR_FREE(targets);
    VIR_FREE(portal);
    virStoragePoolSourceFree(source);
    return ret;
}

626 627 628 629 630 631 632 633 634 635
static int
virStorageBackendISCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED,
                                virStoragePoolObjPtr pool,
                                bool *isActive)
{
    char *session = NULL;
    int ret = -1;

    *isActive = false;

636
    if (pool->def->source.nhost != 1) {
637 638
         virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                        _("Expected exactly 1 host for the storage pool"));
639 640 641 642
        return -1;
    }

    if (pool->def->source.hosts[0].name == NULL) {
643 644
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("missing source host"));
645 646 647 648 649
        return -1;
    }

    if (pool->def->source.ndevice != 1 ||
        pool->def->source.devices[0].path == NULL) {
650 651
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("missing source device"));
652 653 654 655 656 657 658 659 660 661 662 663
        return -1;
    }

    if ((session = virStorageBackendISCSISession(pool, 1)) != NULL) {
        *isActive = true;
        VIR_FREE(session);
    }
    ret = 0;

    return ret;
}

664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
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);

    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;
}
695

696
static int
697 698 699 700 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 731 732 733 734
virStorageBackendISCSISetAuth(const char *portal,
                              virConnectPtr conn,
                              virStoragePoolDefPtr def)
{
    virSecretPtr secret = NULL;
    unsigned char *secret_value = NULL;
    virStoragePoolAuthChap chap;
    int ret = -1;

    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) {
735 736 737 738 739 740 741 742 743 744 745
            if (chap.secret.uuidUsable) {
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("could not get the value of the secret "
                                 "for username %s using uuid '%s'"),
                                 chap.username, chap.secret.uuid);
            } 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);
            }
746 747 748
            goto cleanup;
        }
    } else {
749 750 751 752 753 754 755 756 757
        if (chap.secret.uuidUsable) {
            virReportError(VIR_ERR_NO_SECRET,
                           _("no secret matches uuid '%s'"),
                           chap.secret.uuid);
        } else {
            virReportError(VIR_ERR_NO_SECRET,
                           _("no secret matches usage value '%s'"),
                           chap.secret.usage);
        }
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 783 784
        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,
785 786 787
                                virStoragePoolObjPtr pool)
{
    char *portal = NULL;
788 789 790
    char *session = NULL;
    int ret = -1;
    const char *loginargv[] = { "--login", NULL };
791

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

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

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

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

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

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

cleanup:
835
    VIR_FREE(portal);
836 837
    VIR_FREE(session);
    return ret;
838 839 840
}

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

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

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

    return 0;

 cleanup:
859
    VIR_FREE(session);
860 861 862 863 864
    return -1;
}


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

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

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

882 883 884
cleanup:
    VIR_FREE(portal);
    return ret;
885 886 887
}

virStorageBackend virStorageBackendISCSI = {
888
    .type = VIR_STORAGE_POOL_ISCSI,
889

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