PageRenderTime 29ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/libvirt-0.9.11/src/storage/storage_backend_iscsi.c

#
C | 745 lines | 572 code | 114 blank | 59 comment | 91 complexity | 7674255de14c4c082167a4cf2865f6a9 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /*
  2. * storage_backend_iscsi.c: storage backend for iSCSI handling
  3. *
  4. * Copyright (C) 2007-2008, 2010-2011 Red Hat, Inc.
  5. * Copyright (C) 2007-2008 Daniel P. Berrange
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20. *
  21. * Author: Daniel P. Berrange <berrange@redhat.com>
  22. */
  23. #include <config.h>
  24. #include <sys/socket.h>
  25. #include <netdb.h>
  26. #include <sys/types.h>
  27. #include <sys/wait.h>
  28. #include <string.h>
  29. #include <stdio.h>
  30. #include <regex.h>
  31. #include <fcntl.h>
  32. #include <unistd.h>
  33. #include <sys/stat.h>
  34. #include "virterror_internal.h"
  35. #include "storage_backend_scsi.h"
  36. #include "storage_backend_iscsi.h"
  37. #include "util.h"
  38. #include "memory.h"
  39. #include "logging.h"
  40. #include "virfile.h"
  41. #include "command.h"
  42. #include "virrandom.h"
  43. #define VIR_FROM_THIS VIR_FROM_STORAGE
  44. static int
  45. virStorageBackendISCSITargetIP(const char *hostname,
  46. char *ipaddr,
  47. size_t ipaddrlen)
  48. {
  49. struct addrinfo hints;
  50. struct addrinfo *result = NULL;
  51. int ret;
  52. memset(&hints, 0, sizeof(hints));
  53. hints.ai_flags = AI_ADDRCONFIG;
  54. hints.ai_family = AF_INET;
  55. hints.ai_socktype = SOCK_STREAM;
  56. hints.ai_protocol = 0;
  57. ret = getaddrinfo(hostname, NULL, &hints, &result);
  58. if (ret != 0) {
  59. virStorageReportError(VIR_ERR_INTERNAL_ERROR,
  60. _("host lookup failed %s"),
  61. gai_strerror(ret));
  62. return -1;
  63. }
  64. if (result == NULL) {
  65. virStorageReportError(VIR_ERR_INTERNAL_ERROR,
  66. _("no IP address for target %s"),
  67. hostname);
  68. return -1;
  69. }
  70. if (getnameinfo(result->ai_addr, result->ai_addrlen,
  71. ipaddr, ipaddrlen, NULL, 0,
  72. NI_NUMERICHOST) < 0) {
  73. virStorageReportError(VIR_ERR_INTERNAL_ERROR,
  74. _("cannot format ip addr for %s"),
  75. hostname);
  76. freeaddrinfo(result);
  77. return -1;
  78. }
  79. freeaddrinfo(result);
  80. return 0;
  81. }
  82. static char *
  83. virStorageBackendISCSIPortal(virStoragePoolSourcePtr source)
  84. {
  85. char ipaddr[NI_MAXHOST];
  86. char *portal;
  87. if (virStorageBackendISCSITargetIP(source->host.name,
  88. ipaddr, sizeof(ipaddr)) < 0)
  89. return NULL;
  90. if (virAsprintf(&portal, "%s:%d,1", ipaddr,
  91. source->host.port ?
  92. source->host.port : 3260) < 0) {
  93. virReportOOMError();
  94. return NULL;
  95. }
  96. return portal;
  97. }
  98. static int
  99. virStorageBackendISCSIExtractSession(virStoragePoolObjPtr pool,
  100. char **const groups,
  101. void *data)
  102. {
  103. char **session = data;
  104. if (STREQ(groups[1], pool->def->source.devices[0].path)) {
  105. if ((*session = strdup(groups[0])) == NULL) {
  106. virReportOOMError();
  107. return -1;
  108. }
  109. }
  110. return 0;
  111. }
  112. static char *
  113. virStorageBackendISCSISession(virStoragePoolObjPtr pool,
  114. int probe)
  115. {
  116. /*
  117. * # iscsiadm --mode session
  118. * tcp: [1] 192.168.122.170:3260,1 demo-tgt-b
  119. * tcp: [2] 192.168.122.170:3260,1 demo-tgt-a
  120. *
  121. * Pull out 2nd and 4th fields
  122. */
  123. const char *regexes[] = {
  124. "^tcp:\\s+\\[(\\S+)\\]\\s+\\S+\\s+(\\S+)\\s*$"
  125. };
  126. int vars[] = {
  127. 2,
  128. };
  129. const char *const prog[] = {
  130. ISCSIADM, "--mode", "session", NULL
  131. };
  132. char *session = NULL;
  133. /* Note that we ignore the exitstatus. Older versions of iscsiadm tools
  134. * returned an exit status of > 0, even if they succeeded. We will just
  135. * rely on whether session got filled in properly.
  136. */
  137. if (virStorageBackendRunProgRegex(pool,
  138. prog,
  139. 1,
  140. regexes,
  141. vars,
  142. virStorageBackendISCSIExtractSession,
  143. &session, NULL) < 0)
  144. return NULL;
  145. if (session == NULL &&
  146. !probe) {
  147. virStorageReportError(VIR_ERR_INTERNAL_ERROR,
  148. "%s", _("cannot find session"));
  149. return NULL;
  150. }
  151. return session;
  152. }
  153. #define LINE_SIZE 4096
  154. static int
  155. virStorageBackendIQNFound(const char *initiatoriqn,
  156. char **ifacename)
  157. {
  158. int ret = IQN_MISSING, fd = -1;
  159. char ebuf[64];
  160. FILE *fp = NULL;
  161. char *line = NULL, *newline = NULL, *iqn = NULL, *token = NULL;
  162. virCommandPtr cmd = virCommandNewArgList(ISCSIADM,
  163. "--mode", "iface", NULL);
  164. if (VIR_ALLOC_N(line, LINE_SIZE) != 0) {
  165. ret = IQN_ERROR;
  166. virStorageReportError(VIR_ERR_INTERNAL_ERROR,
  167. _("Could not allocate memory for output of '%s'"),
  168. ISCSIADM);
  169. goto out;
  170. }
  171. memset(line, 0, LINE_SIZE);
  172. virCommandSetOutputFD(cmd, &fd);
  173. if (virCommandRunAsync(cmd, NULL) < 0) {
  174. ret = IQN_ERROR;
  175. goto out;
  176. }
  177. if ((fp = VIR_FDOPEN(fd, "r")) == NULL) {
  178. virStorageReportError(VIR_ERR_INTERNAL_ERROR,
  179. _("Failed to open stream for file descriptor "
  180. "when reading output from '%s': '%s'"),
  181. ISCSIADM, virStrerror(errno, ebuf, sizeof(ebuf)));
  182. ret = IQN_ERROR;
  183. goto out;
  184. }
  185. while (fgets(line, LINE_SIZE, fp) != NULL) {
  186. newline = strrchr(line, '\n');
  187. if (newline == NULL) {
  188. ret = IQN_ERROR;
  189. virStorageReportError(VIR_ERR_INTERNAL_ERROR,
  190. _("Unexpected line > %d characters "
  191. "when parsing output of '%s'"),
  192. LINE_SIZE, ISCSIADM);
  193. goto out;
  194. }
  195. *newline = '\0';
  196. iqn = strrchr(line, ',');
  197. if (iqn == NULL) {
  198. continue;
  199. }
  200. iqn++;
  201. if (STREQ(iqn, initiatoriqn)) {
  202. token = strchr(line, ' ');
  203. if (!token) {
  204. ret = IQN_ERROR;
  205. virStorageReportError(VIR_ERR_INTERNAL_ERROR,
  206. _("Missing space when parsing output "
  207. "of '%s'"), ISCSIADM);
  208. goto out;
  209. }
  210. *ifacename = strndup(line, token - line);
  211. if (*ifacename == NULL) {
  212. ret = IQN_ERROR;
  213. virReportOOMError();
  214. goto out;
  215. }
  216. VIR_DEBUG("Found interface '%s' with IQN '%s'", *ifacename, iqn);
  217. ret = IQN_FOUND;
  218. break;
  219. }
  220. }
  221. if (virCommandWait(cmd, NULL) < 0)
  222. ret = IQN_ERROR;
  223. out:
  224. if (ret == IQN_MISSING) {
  225. VIR_DEBUG("Could not find interface with IQN '%s'", iqn);
  226. }
  227. VIR_FREE(line);
  228. VIR_FORCE_FCLOSE(fp);
  229. VIR_FORCE_CLOSE(fd);
  230. virCommandFree(cmd);
  231. return ret;
  232. }
  233. static int
  234. virStorageBackendCreateIfaceIQN(const char *initiatoriqn,
  235. char **ifacename)
  236. {
  237. int ret = -1, exitstatus = -1;
  238. char temp_ifacename[32];
  239. const char *const cmdargv1[] = {
  240. ISCSIADM, "--mode", "iface", "--interface",
  241. temp_ifacename, "--op", "new", NULL
  242. };
  243. const char *const cmdargv2[] = {
  244. ISCSIADM, "--mode", "iface", "--interface", temp_ifacename,
  245. "--op", "update", "--name", "iface.initiatorname", "--value",
  246. initiatoriqn, NULL
  247. };
  248. snprintf(temp_ifacename, sizeof(temp_ifacename), "libvirt-iface-%08llx",
  249. (unsigned long long)virRandomBits(30));
  250. VIR_DEBUG("Attempting to create interface '%s' with IQN '%s'",
  251. &temp_ifacename[0], initiatoriqn);
  252. /* Note that we ignore the exitstatus. Older versions of iscsiadm
  253. * tools returned an exit status of > 0, even if they succeeded.
  254. * We will just rely on whether the interface got created
  255. * properly. */
  256. if (virRun(cmdargv1, &exitstatus) < 0) {
  257. virStorageReportError(VIR_ERR_INTERNAL_ERROR,
  258. _("Failed to run command '%s' to create new iscsi interface"),
  259. cmdargv1[0]);
  260. goto out;
  261. }
  262. /* Note that we ignore the exitstatus. Older versions of iscsiadm tools
  263. * returned an exit status of > 0, even if they succeeded. We will just
  264. * rely on whether iface file got updated properly. */
  265. if (virRun(cmdargv2, &exitstatus) < 0) {
  266. virStorageReportError(VIR_ERR_INTERNAL_ERROR,
  267. _("Failed to run command '%s' to update iscsi interface with IQN '%s'"),
  268. cmdargv2[0], initiatoriqn);
  269. goto out;
  270. }
  271. /* Check again to make sure the interface was created. */
  272. if (virStorageBackendIQNFound(initiatoriqn, ifacename) != IQN_FOUND) {
  273. VIR_DEBUG("Failed to find interface '%s' with IQN '%s' "
  274. "after attempting to create it",
  275. &temp_ifacename[0], initiatoriqn);
  276. goto out;
  277. } else {
  278. VIR_DEBUG("Interface '%s' with IQN '%s' was created successfully",
  279. *ifacename, initiatoriqn);
  280. }
  281. ret = 0;
  282. out:
  283. if (ret != 0)
  284. VIR_FREE(*ifacename);
  285. return ret;
  286. }
  287. static int
  288. virStorageBackendISCSIConnection(const char *portal,
  289. const char *initiatoriqn,
  290. const char *target,
  291. const char **extraargv)
  292. {
  293. int ret = -1;
  294. const char *const baseargv[] = {
  295. ISCSIADM,
  296. "--mode", "node",
  297. "--portal", portal,
  298. "--targetname", target,
  299. NULL
  300. };
  301. virCommandPtr cmd;
  302. char *ifacename = NULL;
  303. cmd = virCommandNewArgs(baseargv);
  304. virCommandAddArgSet(cmd, extraargv);
  305. if (initiatoriqn) {
  306. switch (virStorageBackendIQNFound(initiatoriqn, &ifacename)) {
  307. case IQN_FOUND:
  308. VIR_DEBUG("ifacename: '%s'", ifacename);
  309. break;
  310. case IQN_MISSING:
  311. if (virStorageBackendCreateIfaceIQN(initiatoriqn,
  312. &ifacename) != 0) {
  313. goto cleanup;
  314. }
  315. break;
  316. case IQN_ERROR:
  317. default:
  318. goto cleanup;
  319. }
  320. virCommandAddArgList(cmd, "--interface", ifacename, NULL);
  321. }
  322. if (virCommandRun(cmd, NULL) < 0)
  323. goto cleanup;
  324. ret = 0;
  325. cleanup:
  326. virCommandFree(cmd);
  327. VIR_FREE(ifacename);
  328. return ret;
  329. }
  330. static int
  331. virStorageBackendISCSIFindLUs(virStoragePoolObjPtr pool,
  332. const char *session)
  333. {
  334. char *sysfs_path;
  335. int retval = 0;
  336. uint32_t host;
  337. if (virAsprintf(&sysfs_path,
  338. "/sys/class/iscsi_session/session%s/device", session) < 0) {
  339. virReportOOMError();
  340. return -1;
  341. }
  342. if (virStorageBackendSCSIGetHostNumber(sysfs_path, &host) < 0) {
  343. virReportSystemError(errno,
  344. _("Failed to get host number for iSCSI session "
  345. "with path '%s'"),
  346. sysfs_path);
  347. retval = -1;
  348. }
  349. if (virStorageBackendSCSIFindLUs(pool, host) < 0) {
  350. virReportSystemError(errno,
  351. _("Failed to find LUs on host %u"), host);
  352. retval = -1;
  353. }
  354. VIR_FREE(sysfs_path);
  355. return retval;
  356. }
  357. static int
  358. virStorageBackendISCSIRescanLUNs(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
  359. const char *session)
  360. {
  361. const char *const cmdargv[] = {
  362. ISCSIADM, "--mode", "session", "-r", session, "-R", NULL,
  363. };
  364. if (virRun(cmdargv, NULL) < 0)
  365. return -1;
  366. return 0;
  367. }
  368. struct virStorageBackendISCSITargetList {
  369. size_t ntargets;
  370. char **targets;
  371. };
  372. static int
  373. virStorageBackendISCSIGetTargets(virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
  374. char **const groups,
  375. void *data)
  376. {
  377. struct virStorageBackendISCSITargetList *list = data;
  378. char *target;
  379. if (!(target = strdup(groups[1]))) {
  380. virReportOOMError();
  381. return -1;
  382. }
  383. if (VIR_REALLOC_N(list->targets, list->ntargets + 1) < 0) {
  384. VIR_FREE(target);
  385. virReportOOMError();
  386. return -1;
  387. }
  388. list->targets[list->ntargets] = target;
  389. list->ntargets++;
  390. return 0;
  391. }
  392. static int
  393. virStorageBackendISCSITargetAutologin(const char *portal,
  394. const char *initiatoriqn,
  395. const char *target,
  396. bool enable)
  397. {
  398. const char *extraargv[] = { "--op", "update",
  399. "--name", "node.startup",
  400. "--value", enable ? "automatic" : "manual",
  401. NULL };
  402. return virStorageBackendISCSIConnection(portal, initiatoriqn, target, extraargv);
  403. }
  404. static int
  405. virStorageBackendISCSIScanTargets(const char *portal,
  406. const char *initiatoriqn,
  407. size_t *ntargetsret,
  408. char ***targetsret)
  409. {
  410. /**
  411. *
  412. * The output of sendtargets is very simple, just two columns,
  413. * portal then target name
  414. *
  415. * 192.168.122.185:3260,1 iqn.2004-04.com:fedora14:iscsi.demo0.bf6d84
  416. * 192.168.122.185:3260,1 iqn.2004-04.com:fedora14:iscsi.demo1.bf6d84
  417. * 192.168.122.185:3260,1 iqn.2004-04.com:fedora14:iscsi.demo2.bf6d84
  418. * 192.168.122.185:3260,1 iqn.2004-04.com:fedora14:iscsi.demo3.bf6d84
  419. */
  420. const char *regexes[] = {
  421. "^\\s*(\\S+)\\s+(\\S+)\\s*$"
  422. };
  423. int vars[] = { 2 };
  424. const char *const cmdsendtarget[] = {
  425. ISCSIADM, "--mode", "discovery", "--type", "sendtargets",
  426. "--portal", portal, NULL
  427. };
  428. struct virStorageBackendISCSITargetList list;
  429. int i;
  430. memset(&list, 0, sizeof(list));
  431. if (virStorageBackendRunProgRegex(NULL, /* No pool for callback */
  432. cmdsendtarget,
  433. 1,
  434. regexes,
  435. vars,
  436. virStorageBackendISCSIGetTargets,
  437. &list, NULL) < 0) {
  438. return -1;
  439. }
  440. for (i = 0 ; i < list.ntargets ; i++) {
  441. /* We have to ignore failure, because we can't undo
  442. * the results of 'sendtargets', unless we go scrubbing
  443. * around in the dirt in /var/lib/iscsi.
  444. */
  445. if (virStorageBackendISCSITargetAutologin(portal,
  446. initiatoriqn,
  447. list.targets[i], false) < 0)
  448. VIR_WARN("Unable to disable auto-login on iSCSI target %s: %s",
  449. portal, list.targets[i]);
  450. }
  451. if (ntargetsret && targetsret) {
  452. *ntargetsret = list.ntargets;
  453. *targetsret = list.targets;
  454. } else {
  455. for (i = 0 ; i < list.ntargets ; i++) {
  456. VIR_FREE(list.targets[i]);
  457. }
  458. VIR_FREE(list.targets);
  459. }
  460. return 0;
  461. }
  462. static char *
  463. virStorageBackendISCSIFindPoolSources(virConnectPtr conn ATTRIBUTE_UNUSED,
  464. const char *srcSpec,
  465. unsigned int flags)
  466. {
  467. virStoragePoolSourcePtr source = NULL;
  468. size_t ntargets = 0;
  469. char **targets = NULL;
  470. char *ret = NULL;
  471. int i;
  472. virStoragePoolSourceList list = {
  473. .type = VIR_STORAGE_POOL_ISCSI,
  474. .nsources = 0,
  475. .sources = NULL
  476. };
  477. char *portal = NULL;
  478. virCheckFlags(0, NULL);
  479. if (!(source = virStoragePoolDefParseSourceString(srcSpec,
  480. list.type)))
  481. return NULL;
  482. if (!(portal = virStorageBackendISCSIPortal(source)))
  483. goto cleanup;
  484. if (virStorageBackendISCSIScanTargets(portal,
  485. source->initiator.iqn,
  486. &ntargets, &targets) < 0)
  487. goto cleanup;
  488. if (VIR_ALLOC_N(list.sources, ntargets) < 0) {
  489. virReportOOMError();
  490. goto cleanup;
  491. }
  492. for (i = 0 ; i < ntargets ; i++) {
  493. if (VIR_ALLOC_N(list.sources[i].devices, 1) < 0) {
  494. virReportOOMError();
  495. goto cleanup;
  496. }
  497. list.sources[i].host = source->host;
  498. list.sources[i].initiator = source->initiator;
  499. list.sources[i].ndevice = 1;
  500. list.sources[i].devices[0].path = targets[i];
  501. list.nsources++;
  502. }
  503. if (!(ret = virStoragePoolSourceListFormat(&list))) {
  504. virReportOOMError();
  505. goto cleanup;
  506. }
  507. cleanup:
  508. if (list.sources) {
  509. for (i = 0 ; i < ntargets ; i++)
  510. VIR_FREE(list.sources[i].devices);
  511. VIR_FREE(list.sources);
  512. }
  513. for (i = 0 ; i < ntargets ; i++)
  514. VIR_FREE(targets[i]);
  515. VIR_FREE(targets);
  516. VIR_FREE(portal);
  517. virStoragePoolSourceFree(source);
  518. return ret;
  519. }
  520. static int
  521. virStorageBackendISCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED,
  522. virStoragePoolObjPtr pool,
  523. bool *isActive)
  524. {
  525. char *session = NULL;
  526. int ret = -1;
  527. *isActive = false;
  528. if (pool->def->source.host.name == NULL) {
  529. virStorageReportError(VIR_ERR_INTERNAL_ERROR,
  530. "%s", _("missing source host"));
  531. return -1;
  532. }
  533. if (pool->def->source.ndevice != 1 ||
  534. pool->def->source.devices[0].path == NULL) {
  535. virStorageReportError(VIR_ERR_INTERNAL_ERROR,
  536. "%s", _("missing source device"));
  537. return -1;
  538. }
  539. if ((session = virStorageBackendISCSISession(pool, 1)) != NULL) {
  540. *isActive = true;
  541. VIR_FREE(session);
  542. }
  543. ret = 0;
  544. return ret;
  545. }
  546. static int
  547. virStorageBackendISCSIStartPool(virConnectPtr conn ATTRIBUTE_UNUSED,
  548. virStoragePoolObjPtr pool)
  549. {
  550. char *portal = NULL;
  551. char *session = NULL;
  552. int ret = -1;
  553. const char *loginargv[] = { "--login", NULL };
  554. if (pool->def->source.host.name == NULL) {
  555. virStorageReportError(VIR_ERR_INTERNAL_ERROR,
  556. "%s", _("missing source host"));
  557. return -1;
  558. }
  559. if (pool->def->source.ndevice != 1 ||
  560. pool->def->source.devices[0].path == NULL) {
  561. virStorageReportError(VIR_ERR_INTERNAL_ERROR,
  562. "%s", _("missing source device"));
  563. return -1;
  564. }
  565. if ((session = virStorageBackendISCSISession(pool, 1)) == NULL) {
  566. if ((portal = virStorageBackendISCSIPortal(&pool->def->source)) == NULL)
  567. goto cleanup;
  568. /*
  569. * iscsiadm doesn't let you login to a target, unless you've
  570. * first issued a 'sendtargets' command to the portal :-(
  571. */
  572. if (virStorageBackendISCSIScanTargets(portal,
  573. pool->def->source.initiator.iqn,
  574. NULL, NULL) < 0)
  575. goto cleanup;
  576. if (virStorageBackendISCSIConnection(portal,
  577. pool->def->source.initiator.iqn,
  578. pool->def->source.devices[0].path,
  579. loginargv) < 0)
  580. goto cleanup;
  581. }
  582. ret = 0;
  583. cleanup:
  584. VIR_FREE(session);
  585. return ret;
  586. }
  587. static int
  588. virStorageBackendISCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
  589. virStoragePoolObjPtr pool)
  590. {
  591. char *session = NULL;
  592. pool->def->allocation = pool->def->capacity = pool->def->available = 0;
  593. if ((session = virStorageBackendISCSISession(pool, 0)) == NULL)
  594. goto cleanup;
  595. if (virStorageBackendISCSIRescanLUNs(pool, session) < 0)
  596. goto cleanup;
  597. if (virStorageBackendISCSIFindLUs(pool, session) < 0)
  598. goto cleanup;
  599. VIR_FREE(session);
  600. return 0;
  601. cleanup:
  602. VIR_FREE(session);
  603. return -1;
  604. }
  605. static int
  606. virStorageBackendISCSIStopPool(virConnectPtr conn ATTRIBUTE_UNUSED,
  607. virStoragePoolObjPtr pool)
  608. {
  609. const char *logoutargv[] = { "--logout", NULL };
  610. char *portal;
  611. int ret = -1;
  612. if ((portal = virStorageBackendISCSIPortal(&pool->def->source)) == NULL)
  613. return -1;
  614. if (virStorageBackendISCSIConnection(portal,
  615. pool->def->source.initiator.iqn,
  616. pool->def->source.devices[0].path,
  617. logoutargv) < 0)
  618. goto cleanup;
  619. ret = 0;
  620. cleanup:
  621. VIR_FREE(portal);
  622. return ret;
  623. }
  624. virStorageBackend virStorageBackendISCSI = {
  625. .type = VIR_STORAGE_POOL_ISCSI,
  626. .checkPool = virStorageBackendISCSICheckPool,
  627. .startPool = virStorageBackendISCSIStartPool,
  628. .refreshPool = virStorageBackendISCSIRefreshPool,
  629. .stopPool = virStorageBackendISCSIStopPool,
  630. .findPoolSources = virStorageBackendISCSIFindPoolSources,
  631. };