storage_backend_iscsi.c 21.8 KB
Newer Older
1 2 3
/*
 * storage_backend_iscsi.c: storage backend for iSCSI handling
 *
4
 * Copyright (C) 2007-2008, 2010-2011 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 * 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
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * Author: Daniel P. Berrange <berrange@redhat.com>
 */

#include <config.h>

#include <sys/socket.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <regex.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
D
David Allan 已提交
36
#include <sys/stat.h>
37

38
#include "virterror_internal.h"
39
#include "storage_backend_scsi.h"
40 41
#include "storage_backend_iscsi.h"
#include "util.h"
42
#include "memory.h"
D
David Allan 已提交
43
#include "logging.h"
44
#include "files.h"
45
#include "command.h"
46

47 48
#define VIR_FROM_THIS VIR_FROM_STORAGE

49
static int
50
virStorageBackendISCSITargetIP(const char *hostname,
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
                               char *ipaddr,
                               size_t ipaddrlen)
{
    struct addrinfo hints;
    struct addrinfo *result = NULL;
    int ret;

    memset(&hints, 0, sizeof hints);
    hints.ai_flags = AI_ADDRCONFIG;
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = 0;

    ret = getaddrinfo(hostname, NULL, &hints, &result);
    if (ret != 0) {
66
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
67 68 69 70 71 72
                              _("host lookup failed %s"),
                              gai_strerror(ret));
        return -1;
    }

    if (result == NULL) {
73
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
74 75 76 77 78 79 80 81
                              _("no IP address for target %s"),
                              hostname);
        return -1;
    }

    if (getnameinfo(result->ai_addr, result->ai_addrlen,
                    ipaddr, ipaddrlen, NULL, 0,
                    NI_NUMERICHOST) < 0) {
82
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
83 84 85 86 87 88 89 90 91 92
                              _("cannot format ip addr for %s"),
                              hostname);
        freeaddrinfo(result);
        return -1;
    }

    freeaddrinfo(result);
    return 0;
}

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
static char *
virStorageBackendISCSIPortal(virStoragePoolSourcePtr source)
{
    char ipaddr[NI_MAXHOST];
    char *portal;

    if (virStorageBackendISCSITargetIP(source->host.name,
                                       ipaddr, sizeof(ipaddr)) < 0)
        return NULL;

    if (virAsprintf(&portal, "%s:%d,1", ipaddr,
                    source->host.port ?
                    source->host.port : 3260) < 0) {
        virReportOOMError();
        return NULL;
    }

    return portal;
}


114
static int
115
virStorageBackendISCSIExtractSession(virStoragePoolObjPtr pool,
116 117 118 119 120 121 122
                                     char **const groups,
                                     void *data)
{
    char **session = data;

    if (STREQ(groups[1], pool->def->source.devices[0].path)) {
        if ((*session = strdup(groups[0])) == NULL) {
123
            virReportOOMError();
124 125 126 127 128 129 130 131
            return -1;
        }
    }

    return 0;
}

static char *
132
virStorageBackendISCSISession(virStoragePoolObjPtr pool,
133
                              int probe)
134 135
{
    /*
136
     * # iscsiadm --mode session
137 138 139 140 141 142 143 144 145 146 147
     * 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,
    };
148
    const char *const prog[] = {
149
        ISCSIADM, "--mode", "session", NULL
150 151 152
    };
    char *session = NULL;

153 154 155 156
    /* 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.
     */
157
    if (virStorageBackendRunProgRegex(pool,
158 159 160 161 162
                                      prog,
                                      1,
                                      regexes,
                                      vars,
                                      virStorageBackendISCSIExtractSession,
163
                                      &session) < 0)
164 165
        return NULL;

166 167
    if (session == NULL &&
        !probe) {
168
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
169
                              "%s", _("cannot find session"));
170 171 172 173 174 175
        return NULL;
    }

    return session;
}

D
David Allan 已提交
176 177 178 179

#define LINE_SIZE 4096

static int
180
virStorageBackendIQNFound(const char *initiatoriqn,
D
David Allan 已提交
181 182 183 184 185
                          char **ifacename)
{
    int ret = IQN_MISSING, fd = -1;
    char ebuf[64];
    FILE *fp = NULL;
E
Eric Blake 已提交
186
    char *line = NULL, *newline = NULL, *iqn = NULL, *token = NULL;
187 188
    virCommandPtr cmd = virCommandNewArgList(ISCSIADM,
                                             "--mode", "iface", NULL);
D
David Allan 已提交
189 190 191

    if (VIR_ALLOC_N(line, LINE_SIZE) != 0) {
        ret = IQN_ERROR;
192
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
D
David Allan 已提交
193
                              _("Could not allocate memory for output of '%s'"),
194
                              ISCSIADM);
D
David Allan 已提交
195 196 197 198 199
        goto out;
    }

    memset(line, 0, LINE_SIZE);

200 201
    virCommandSetOutputFD(cmd, &fd);
    if (virCommandRunAsync(cmd, NULL) < 0) {
D
David Allan 已提交
202 203 204 205
        ret = IQN_ERROR;
        goto out;
    }

206
    if ((fp = VIR_FDOPEN(fd, "r")) == NULL) {
207
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
D
David Allan 已提交
208 209
                              _("Failed to open stream for file descriptor "
                                "when reading output from '%s': '%s'"),
210
                              ISCSIADM, virStrerror(errno, ebuf, sizeof ebuf));
D
David Allan 已提交
211 212 213 214 215 216 217 218
        ret = IQN_ERROR;
        goto out;
    }

    while (fgets(line, LINE_SIZE, fp) != NULL) {
        newline = strrchr(line, '\n');
        if (newline == NULL) {
            ret = IQN_ERROR;
219
            virStorageReportError(VIR_ERR_INTERNAL_ERROR,
D
David Allan 已提交
220 221
                                  _("Unexpected line > %d characters "
                                    "when parsing output of '%s'"),
222
                                  LINE_SIZE, ISCSIADM);
D
David Allan 已提交
223 224 225 226 227 228 229 230 231 232
            goto out;
        }
        *newline = '\0';

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

233
        if (STREQ(iqn, initiatoriqn)) {
E
Eric Blake 已提交
234 235 236 237 238 239 240 241 242
            token = strchr(line, ' ');
            if (!token) {
                ret = IQN_ERROR;
                virStorageReportError(VIR_ERR_INTERNAL_ERROR,
                                      _("Missing space when parsing output "
                                        "of '%s'"), ISCSIADM);
                goto out;
            }
            *ifacename = strndup(line, token - line);
D
David Allan 已提交
243 244
            if (*ifacename == NULL) {
                ret = IQN_ERROR;
245
                virReportOOMError();
D
David Allan 已提交
246 247 248 249 250 251 252 253
                goto out;
            }
            VIR_DEBUG("Found interface '%s' with IQN '%s'", *ifacename, iqn);
            ret = IQN_FOUND;
            break;
        }
    }

254 255 256
    if (virCommandWait(cmd, NULL) < 0)
        ret = IQN_ERROR;

D
David Allan 已提交
257 258
out:
    if (ret == IQN_MISSING) {
259
        VIR_DEBUG("Could not find interface with IQN '%s'", iqn);
D
David Allan 已提交
260 261 262
    }

    VIR_FREE(line);
263 264
    VIR_FORCE_FCLOSE(fp);
    VIR_FORCE_CLOSE(fd);
265
    virCommandFree(cmd);
D
David Allan 已提交
266 267 268 269 270 271

    return ret;
}


static int
272
virStorageBackendCreateIfaceIQN(const char *initiatoriqn,
273
                                char **ifacename)
D
David Allan 已提交
274 275 276
{
    int ret = -1, exitstatus = -1;
    char temp_ifacename[32];
277 278 279 280 281 282 283 284 285
    const char *const cmdargv1[] = {
        ISCSIADM, "--mode", "iface", "--interface",
        temp_ifacename, "--op", "new", NULL
    };
    const char *const cmdargv2[] = {
        ISCSIADM, "--mode", "iface", "--interface", temp_ifacename,
        "--op", "update", "--name", "iface.initiatorname", "--value",
        initiatoriqn, NULL
    };
D
David Allan 已提交
286 287

    if (virRandomInitialize(time(NULL) ^ getpid()) == -1) {
288
        virStorageReportError(VIR_ERR_INTERNAL_ERROR, "%s",
D
David Allan 已提交
289 290 291 292 293
                              _("Failed to initialize random generator "
                                "when creating iscsi interface"));
        goto out;
    }

294 295
    snprintf(temp_ifacename, sizeof(temp_ifacename), "libvirt-iface-%08x",
             virRandom(1024 * 1024 * 1024));
D
David Allan 已提交
296 297

    VIR_DEBUG("Attempting to create interface '%s' with IQN '%s'",
298
              &temp_ifacename[0], initiatoriqn);
D
David Allan 已提交
299 300 301 302 303

    /* 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. */
304
    if (virRun(cmdargv1, &exitstatus) < 0) {
305
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
D
David Allan 已提交
306 307 308 309 310 311 312 313
                              _("Failed to run command '%s' to create new iscsi interface"),
                              cmdargv1[0]);
        goto out;
    }

    /* 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. */
314
    if (virRun(cmdargv2, &exitstatus) < 0) {
315
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
D
David Allan 已提交
316
                              _("Failed to run command '%s' to update iscsi interface with IQN '%s'"),
317
                              cmdargv2[0], initiatoriqn);
D
David Allan 已提交
318 319 320 321
        goto out;
    }

    /* Check again to make sure the interface was created. */
322
    if (virStorageBackendIQNFound(initiatoriqn, ifacename) != IQN_FOUND) {
D
David Allan 已提交
323 324
        VIR_DEBUG("Failed to find interface '%s' with IQN '%s' "
                  "after attempting to create it",
325
                  &temp_ifacename[0], initiatoriqn);
D
David Allan 已提交
326 327 328
        goto out;
    } else {
        VIR_DEBUG("Interface '%s' with IQN '%s' was created successfully",
329
                  *ifacename, initiatoriqn);
D
David Allan 已提交
330 331 332 333 334 335 336 337 338 339 340
    }

    ret = 0;

out:
    if (ret != 0)
        VIR_FREE(*ifacename);
    return ret;
}


341

D
David Allan 已提交
342
static int
343 344 345 346
virStorageBackendISCSIConnection(const char *portal,
                                 const char *initiatoriqn,
                                 const char *target,
                                 const char **extraargv)
D
David Allan 已提交
347 348
{
    int ret = -1;
349 350 351 352 353 354 355
    const char *const baseargv[] = {
        ISCSIADM,
        "--mode", "node",
        "--portal", portal,
        "--targetname", target,
        NULL
    };
356
    virCommandPtr cmd;
D
David Allan 已提交
357 358
    char *ifacename = NULL;

359 360
    cmd = virCommandNewArgs(baseargv);
    virCommandAddArgSet(cmd, extraargv);
D
David Allan 已提交
361

362 363 364 365 366 367
    if (initiatoriqn) {
        switch (virStorageBackendIQNFound(initiatoriqn, &ifacename)) {
        case IQN_FOUND:
            VIR_DEBUG("ifacename: '%s'", ifacename);
            break;
        case IQN_MISSING:
368 369
            if (virStorageBackendCreateIfaceIQN(initiatoriqn,
                                                &ifacename) != 0) {
370 371 372 373 374 375 376
                goto cleanup;
            }
            break;
        case IQN_ERROR:
        default:
            goto cleanup;
        }
377
        virCommandAddArgList(cmd, "--interface", ifacename, NULL);
D
David Allan 已提交
378
    }
379

380
    if (virCommandRun(cmd, NULL) < 0)
381
        goto cleanup;
D
David Allan 已提交
382 383 384

    ret = 0;

385
cleanup:
386
    virCommandFree(cmd);
D
David Allan 已提交
387 388 389
    VIR_FREE(ifacename);

    return ret;
390 391
}

392

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

401 402 403 404 405
    if (virAsprintf(&sysfs_path,
                    "/sys/class/iscsi_session/session%s/device", session) < 0) {
        virReportOOMError();
        return -1;
    }
406

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

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

421 422
    VIR_FREE(sysfs_path);

423 424
    return retval;
}
425 426

static int
427
virStorageBackendISCSIRescanLUNs(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
428 429
                                 const char *session)
{
430
    const char *const cmdargv[] = {
431 432 433
        ISCSIADM, "--mode", "session", "-r", session, "-R", NULL,
    };

434
    if (virRun(cmdargv, NULL) < 0)
435 436 437 438 439
        return -1;

    return 0;
}

440 441 442 443
struct virStorageBackendISCSITargetList {
    size_t ntargets;
    char **targets;
};
444 445

static int
446 447 448
virStorageBackendISCSIGetTargets(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                                 char **const groups,
                                 void *data)
449
{
450 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 497 498 499 500 501 502
    struct virStorageBackendISCSITargetList *list = data;
    char *target;

    if (!(target = strdup(groups[1]))) {
        virReportOOMError();
        return -1;
    }

    if (VIR_REALLOC_N(list->targets, list->ntargets + 1) < 0) {
        VIR_FREE(target);
        virReportOOMError();
        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*$"
503
    };
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
    int vars[] = { 2 };
    const char *const cmdsendtarget[] = {
        ISCSIADM, "--mode", "discovery", "--type", "sendtargets",
        "--portal", portal, NULL
    };
    struct virStorageBackendISCSITargetList list;
    int i;

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

    if (virStorageBackendRunProgRegex(NULL, /* No pool for callback */
                                      cmdsendtarget,
                                      1,
                                      regexes,
                                      vars,
                                      virStorageBackendISCSIGetTargets,
520
                                      &list) < 0) {
521
        return -1;
522
    }
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545

    for (i = 0 ; i < list.ntargets ; i++) {
        /* 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 {
        for (i = 0 ; i < list.ntargets ; i++) {
            VIR_FREE(list.targets[i]);
        }
        VIR_FREE(list.targets);
    }

546
    return 0;
547 548 549
}


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

E
Eric Blake 已提交
567 568
    virCheckFlags(0, NULL);

569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
    if (!(source = virStoragePoolDefParseSourceString(srcSpec,
                                                      list.type)))
        return NULL;

    if (!(portal = virStorageBackendISCSIPortal(source)))
        goto cleanup;

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

    if (VIR_ALLOC_N(list.sources, ntargets) < 0) {
        virReportOOMError();
        goto cleanup;
    }

    for (i = 0 ; i < ntargets ; i++) {
        if (VIR_ALLOC_N(list.sources[i].devices, 1) < 0) {
            virReportOOMError();
            goto cleanup;
        }
        list.sources[i].host = source->host;
        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))) {
        virReportOOMError();
        goto cleanup;
    }

cleanup:
    if (list.sources) {
        for (i = 0 ; i < ntargets ; i++)
            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;
}

617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
static int
virStorageBackendISCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED,
                                virStoragePoolObjPtr pool,
                                bool *isActive)
{
    char *session = NULL;
    int ret = -1;

    *isActive = false;

    if (pool->def->source.host.name == NULL) {
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
                              "%s", _("missing source host"));
        return -1;
    }

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

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

    return ret;
}


650
static int
651
virStorageBackendISCSIStartPool(virConnectPtr conn ATTRIBUTE_UNUSED,
652 653 654
                                virStoragePoolObjPtr pool)
{
    char *portal = NULL;
655 656 657
    char *session = NULL;
    int ret = -1;
    const char *loginargv[] = { "--login", NULL };
658 659

    if (pool->def->source.host.name == NULL) {
660
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
661
                              "%s", _("missing source host"));
662 663 664 665 666
        return -1;
    }

    if (pool->def->source.ndevice != 1 ||
        pool->def->source.devices[0].path == NULL) {
667
        virStorageReportError(VIR_ERR_INTERNAL_ERROR,
J
Jim Meyering 已提交
668
                              "%s", _("missing source device"));
669 670 671
        return -1;
    }

672
    if ((session = virStorageBackendISCSISession(pool, 1)) == NULL) {
673 674 675 676 677 678
        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 :-(
         */
679 680 681
        if (virStorageBackendISCSIScanTargets(portal,
                                              pool->def->source.initiator.iqn,
                                              NULL, NULL) < 0)
682 683 684 685 686 687 688
            goto cleanup;

        if (virStorageBackendISCSIConnection(portal,
                                             pool->def->source.initiator.iqn,
                                             pool->def->source.devices[0].path,
                                             loginargv) < 0)
            goto cleanup;
689
    }
690 691 692 693 694
    ret = 0;

cleanup:
    VIR_FREE(session);
    return ret;
695 696 697
}

static int
698
virStorageBackendISCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
699 700 701 702 703 704
                                  virStoragePoolObjPtr pool)
{
    char *session = NULL;

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

705
    if ((session = virStorageBackendISCSISession(pool, 0)) == NULL)
706
        goto cleanup;
707
    if (virStorageBackendISCSIRescanLUNs(pool, session) < 0)
708
        goto cleanup;
709
    if (virStorageBackendISCSIFindLUs(pool, session) < 0)
710
        goto cleanup;
711
    VIR_FREE(session);
712 713 714 715

    return 0;

 cleanup:
716
    VIR_FREE(session);
717 718 719 720 721
    return -1;
}


static int
722
virStorageBackendISCSIStopPool(virConnectPtr conn ATTRIBUTE_UNUSED,
723 724
                               virStoragePoolObjPtr pool)
{
725
    const char *logoutargv[] = { "--logout", NULL };
726
    char *portal;
727
    int ret = -1;
728

729
    if ((portal = virStorageBackendISCSIPortal(&pool->def->source)) == NULL)
730 731
        return -1;

732 733 734 735 736 737
    if (virStorageBackendISCSIConnection(portal,
                                         pool->def->source.initiator.iqn,
                                         pool->def->source.devices[0].path,
                                         logoutargv) < 0)
        goto cleanup;
    ret = 0;
738

739 740 741
cleanup:
    VIR_FREE(portal);
    return ret;
742 743 744
}

virStorageBackend virStorageBackendISCSI = {
745
    .type = VIR_STORAGE_POOL_ISCSI,
746

747
    .checkPool = virStorageBackendISCSICheckPool,
748 749 750
    .startPool = virStorageBackendISCSIStartPool,
    .refreshPool = virStorageBackendISCSIRefreshPool,
    .stopPool = virStorageBackendISCSIStopPool,
751
    .findPoolSources = virStorageBackendISCSIFindPoolSources,
752
};