2014-02-21 09:59:10 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2014 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* 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, see
|
|
|
|
* <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2014-03-21 11:14:12 +00:00
|
|
|
#include "testutils.h"
|
|
|
|
|
2014-02-21 09:59:10 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
int
|
|
|
|
main(void)
|
|
|
|
{
|
|
|
|
return EXIT_AM_SKIP;
|
|
|
|
}
|
|
|
|
#else
|
2018-12-13 14:53:50 +00:00
|
|
|
# define LIBVIRT_VIRCOMMANDPRIV_H_ALLOW
|
2014-02-21 09:59:10 +00:00
|
|
|
# include "vircommandpriv.h"
|
|
|
|
# include "viriscsi.h"
|
|
|
|
|
|
|
|
# define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
|
|
|
static const char *iscsiadmSessionOutput =
|
|
|
|
"tcp: [1] 10.20.30.40:3260,1 iqn.2004-06.example:example1:iscsi.test\n"
|
|
|
|
"tcp: [2] 10.20.30.41:3260,1 iqn.2005-05.example:example1:iscsi.hello\n"
|
|
|
|
"tcp: [3] 10.20.30.42:3260,1 iqn.2006-04.example:example1:iscsi.world\n"
|
|
|
|
"tcp: [5] 10.20.30.43:3260,1 iqn.2007-04.example:example1:iscsi.foo\n"
|
|
|
|
"tcp: [6] 10.20.30.44:3260,1 iqn.2008-04.example:example1:iscsi.bar\n"
|
|
|
|
"tcp: [7] 10.20.30.45:3260,1 iqn.2009-04.example:example1:iscsi.seven\n";
|
|
|
|
|
|
|
|
static const char *iscsiadmSessionOutputNonFlash =
|
|
|
|
"tcp: [1] 10.20.30.40:3260,1 iqn.2004-06.example:example1:iscsi.test (non-flash)\n"
|
|
|
|
"tcp: [2] 10.20.30.41:3260,1 iqn.2005-05.example:example1:iscsi.hello (non-flash)\n"
|
|
|
|
"tcp: [3] 10.20.30.42:3260,1 iqn.2006-04.example:example1:iscsi.world (non-flash)\n"
|
|
|
|
"tcp: [5] 10.20.30.43:3260,1 iqn.2007-04.example:example1:iscsi.foo (non-flash)\n"
|
|
|
|
"tcp: [6] 10.20.30.44:3260,1 iqn.2008-04.example:example1:iscsi.bar (non-flash)\n"
|
|
|
|
"tcp: [7] 10.20.30.45:3260,1 iqn.2009-04.example:example1:iscsi.seven (non-flash)\n";
|
|
|
|
|
2014-03-19 13:02:09 +00:00
|
|
|
const char *iscsiadmSendtargetsOutput =
|
|
|
|
"10.20.30.40:3260,1 iqn.2004-06.example:example1:iscsi.test\n"
|
|
|
|
"10.20.30.40:3260,1 iqn.2005-05.example:example1:iscsi.hello\n"
|
|
|
|
"10.20.30.40:3260,1 iqn.2006-04.example:example1:iscsi.world\n"
|
|
|
|
"10.20.30.40:3260,1 iqn.2007-04.example:example1:iscsi.foo\n"
|
|
|
|
"10.20.30.40:3260,1 iqn.2008-04.example:example1:iscsi.bar\n"
|
|
|
|
"10.20.30.40:3260,1 iqn.2009-04.example:example1:iscsi.seven\n";
|
|
|
|
|
2018-07-04 08:41:54 +00:00
|
|
|
const char *iscsiadmIfaceDefaultOutput =
|
|
|
|
"default tcp,<empty>,<empty>,<empty>,<empty>\n"
|
|
|
|
"iser iser,<empty>,<empty>,<empty>,<empty>\n";
|
|
|
|
|
|
|
|
const char *iscsiadmIfaceIfaceOutput =
|
|
|
|
"default tcp,<empty>,<empty>,<empty>,<empty>\n"
|
|
|
|
"iser iser,<empty>,<empty>,<empty>,<empty>\n"
|
|
|
|
"libvirt-iface-03020100 tcp,<empty>,<empty>,<empty>,iqn.2004-06.example:example1:initiator\n";
|
|
|
|
|
|
|
|
|
2018-07-04 08:47:07 +00:00
|
|
|
struct testIscsiadmCbData {
|
|
|
|
bool output_version;
|
2018-07-04 08:41:54 +00:00
|
|
|
bool iface_created;
|
2018-07-04 08:47:07 +00:00
|
|
|
};
|
|
|
|
|
2014-02-21 09:59:10 +00:00
|
|
|
static void testIscsiadmCb(const char *const*args,
|
|
|
|
const char *const*env ATTRIBUTE_UNUSED,
|
|
|
|
const char *input ATTRIBUTE_UNUSED,
|
|
|
|
char **output,
|
|
|
|
char **error ATTRIBUTE_UNUSED,
|
|
|
|
int *status,
|
|
|
|
void *opaque)
|
|
|
|
{
|
2018-07-04 08:47:07 +00:00
|
|
|
struct testIscsiadmCbData *data = opaque;
|
|
|
|
|
2014-02-21 09:59:10 +00:00
|
|
|
if (args[0] && STREQ(args[0], ISCSIADM) &&
|
|
|
|
args[1] && STREQ(args[1], "--mode") &&
|
|
|
|
args[2] && STREQ(args[2], "session") &&
|
|
|
|
args[3] == NULL) {
|
2018-07-04 08:47:07 +00:00
|
|
|
if (data->output_version)
|
2014-02-21 09:59:10 +00:00
|
|
|
ignore_value(VIR_STRDUP(*output, iscsiadmSessionOutputNonFlash));
|
|
|
|
else
|
|
|
|
ignore_value(VIR_STRDUP(*output, iscsiadmSessionOutput));
|
2014-03-19 13:02:09 +00:00
|
|
|
} else if (args[0] && STREQ(args[0], ISCSIADM) &&
|
|
|
|
args[1] && STREQ(args[1], "--mode") &&
|
|
|
|
args[2] && STREQ(args[2], "discovery") &&
|
|
|
|
args[3] && STREQ(args[3], "--type") &&
|
|
|
|
args[4] && STREQ(args[4], "sendtargets") &&
|
|
|
|
args[5] && STREQ(args[5], "--portal") &&
|
|
|
|
args[6] && STREQ(args[6], "10.20.30.40:3260,1") &&
|
2016-05-13 15:19:09 +00:00
|
|
|
args[7] && STREQ(args[7], "--op") &&
|
|
|
|
args[8] && STREQ(args[8], "nonpersistent") &&
|
|
|
|
args[9] == NULL) {
|
2014-03-19 13:02:09 +00:00
|
|
|
ignore_value(VIR_STRDUP(*output, iscsiadmSendtargetsOutput));
|
2018-07-04 08:08:48 +00:00
|
|
|
} else if (args[0] && STREQ(args[0], ISCSIADM) &&
|
|
|
|
args[1] && STREQ(args[1], "--mode") &&
|
|
|
|
args[2] && STREQ(args[2], "node") &&
|
|
|
|
args[3] && STREQ(args[3], "--portal") &&
|
|
|
|
args[4] && STREQ(args[4], "10.20.30.40:3260,1") &&
|
|
|
|
args[5] && STREQ(args[5], "--targetname") &&
|
|
|
|
args[6] && STREQ(args[6], "iqn.2004-06.example:example1:iscsi.test") &&
|
|
|
|
args[7] && STREQ(args[7], "--login") &&
|
|
|
|
args[8] == NULL) {
|
|
|
|
/* Mocking real environment output is not needed for now.
|
|
|
|
* Example output from real environment:
|
|
|
|
*
|
|
|
|
* Logging in to [iface: default, \
|
|
|
|
* target: iqn.2004-06.example:example1:iscsi.test, \
|
|
|
|
* portal: 10.20.30.40:3260,1] (multiple)
|
|
|
|
* Login to [iface: default, \
|
|
|
|
* target: iqn.2004-06.example:example1:iscsi.test, \
|
|
|
|
* portal: 10.20.30.40:3260,1] successful.
|
|
|
|
*/
|
2018-07-04 08:41:54 +00:00
|
|
|
} else if (args[0] && STREQ(args[0], ISCSIADM) &&
|
|
|
|
args[1] && STREQ(args[1], "--mode") &&
|
|
|
|
args[2] && STREQ(args[2], "iface") &&
|
|
|
|
args[3] == NULL) {
|
|
|
|
if (data->iface_created)
|
|
|
|
ignore_value(VIR_STRDUP(*output, iscsiadmIfaceIfaceOutput));
|
|
|
|
else
|
|
|
|
ignore_value(VIR_STRDUP(*output, iscsiadmIfaceDefaultOutput));
|
|
|
|
} else if (args[0] && STREQ(args[0], ISCSIADM) &&
|
|
|
|
args[1] && STREQ(args[1], "--mode") &&
|
|
|
|
args[2] && STREQ(args[2], "iface") &&
|
|
|
|
args[3] && STREQ(args[3], "--interface") &&
|
|
|
|
args[4] && STREQ(args[4], "libvirt-iface-03020100") &&
|
|
|
|
args[5] && STREQ(args[5], "--op") &&
|
|
|
|
args[6] && STREQ(args[6], "new") &&
|
|
|
|
args[7] == NULL) {
|
|
|
|
/* Mocking real environment output is not needed for now.
|
|
|
|
* Example output from real environment:
|
|
|
|
*
|
|
|
|
* New interface libvirt-iface-03020100 added
|
|
|
|
*/
|
|
|
|
data->iface_created = true;
|
|
|
|
} else if (args[0] && STREQ(args[0], ISCSIADM) &&
|
|
|
|
args[1] && STREQ(args[1], "--mode") &&
|
|
|
|
args[2] && STREQ(args[2], "iface") &&
|
|
|
|
args[3] && STREQ(args[3], "--interface") &&
|
|
|
|
args[4] && STREQ(args[4], "libvirt-iface-03020100") &&
|
|
|
|
args[5] && STREQ(args[5], "--op") &&
|
|
|
|
args[6] && STREQ(args[6], "update") &&
|
|
|
|
args[7] && STREQ(args[7], "--name") &&
|
|
|
|
args[8] && STREQ(args[8], "iface.initiatorname") &&
|
|
|
|
args[9] && STREQ(args[9], "--value") &&
|
|
|
|
args[10] && STREQ(args[10], "iqn.2004-06.example:example1:initiator") &&
|
|
|
|
args[11] == NULL &&
|
|
|
|
data->iface_created) {
|
|
|
|
/* Mocking real environment output is not needed for now.
|
|
|
|
* Example output from real environment:
|
|
|
|
*
|
|
|
|
* libvirt-iface-03020100 updated.
|
|
|
|
*/
|
|
|
|
} else if (args[0] && STREQ(args[0], ISCSIADM) &&
|
|
|
|
args[1] && STREQ(args[1], "--mode") &&
|
|
|
|
args[2] && STREQ(args[2], "discovery") &&
|
|
|
|
args[3] && STREQ(args[3], "--type") &&
|
|
|
|
args[4] && STREQ(args[4], "sendtargets") &&
|
|
|
|
args[5] && STREQ(args[5], "--portal") &&
|
|
|
|
args[6] && STREQ(args[6], "10.20.30.40:3260,1") &&
|
|
|
|
args[7] && STREQ(args[7], "--interface") &&
|
|
|
|
args[8] && STREQ(args[8], "libvirt-iface-03020100") &&
|
|
|
|
args[9] == NULL &&
|
|
|
|
data->iface_created) {
|
|
|
|
ignore_value(VIR_STRDUP(*output, iscsiadmSendtargetsOutput));
|
|
|
|
} else if (args[0] && STREQ(args[0], ISCSIADM) &&
|
|
|
|
args[1] && STREQ(args[1], "--mode") &&
|
|
|
|
args[2] && STREQ(args[2], "node") &&
|
|
|
|
args[3] && STREQ(args[3], "--portal") &&
|
|
|
|
args[4] && STREQ(args[4], "10.20.30.40:3260,1") &&
|
|
|
|
args[5] && STREQ(args[5], "--targetname") &&
|
|
|
|
args[6] && STREQ(args[6], "iqn.2004-06.example:example1:iscsi.test") &&
|
|
|
|
args[7] && STREQ(args[7], "--login") &&
|
|
|
|
args[8] && STREQ(args[8], "--interface") &&
|
|
|
|
args[9] && STREQ(args[9], "libvirt-iface-03020100") &&
|
|
|
|
args[10] == NULL &&
|
|
|
|
data->iface_created) {
|
|
|
|
/* Mocking real environment output is not needed for now.
|
|
|
|
* Example output from real environment:
|
|
|
|
*
|
|
|
|
* Logging in to [iface: libvirt-iface-03020100, \
|
|
|
|
* target: iqn.2004-06.example:example1:iscsi.test, \
|
|
|
|
* portal: 10.20.30.40:3260,1] (multiple)
|
|
|
|
* Login to [iface: libvirt-iface-03020100, \
|
|
|
|
* target: iqn.2004-06.example:example1:iscsi.test, \
|
|
|
|
* portal: 10.20.30.40:3260,1] successful.
|
|
|
|
*/
|
2014-02-21 09:59:10 +00:00
|
|
|
} else {
|
|
|
|
*status = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-04 08:43:36 +00:00
|
|
|
struct testSessionInfo {
|
|
|
|
const char *device_path;
|
2018-07-04 08:47:07 +00:00
|
|
|
bool output_version;
|
2018-07-04 08:43:36 +00:00
|
|
|
const char *expected_session;
|
|
|
|
};
|
|
|
|
|
2014-02-21 09:59:10 +00:00
|
|
|
static int
|
|
|
|
testISCSIGetSession(const void *data)
|
|
|
|
{
|
|
|
|
const struct testSessionInfo *info = data;
|
2018-07-04 08:47:07 +00:00
|
|
|
struct testIscsiadmCbData cbData = { 0 };
|
2014-02-21 09:59:10 +00:00
|
|
|
char *actual_session = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
2018-07-04 08:47:07 +00:00
|
|
|
cbData.output_version = info->output_version;
|
|
|
|
|
|
|
|
virCommandSetDryRun(NULL, testIscsiadmCb, &cbData);
|
2014-02-21 09:59:10 +00:00
|
|
|
|
|
|
|
actual_session = virISCSIGetSession(info->device_path, true);
|
|
|
|
|
|
|
|
if (STRNEQ_NULLABLE(actual_session, info->expected_session)) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"Expected session: '%s' got: '%s'",
|
|
|
|
NULLSTR(info->expected_session),
|
|
|
|
NULLSTR(actual_session));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:53:44 +00:00
|
|
|
cleanup:
|
2014-02-21 09:59:10 +00:00
|
|
|
virCommandSetDryRun(NULL, NULL, NULL);
|
|
|
|
VIR_FREE(actual_session);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-03-19 13:02:09 +00:00
|
|
|
struct testScanTargetsInfo {
|
|
|
|
const char *fake_cmd_output;
|
|
|
|
const char *portal;
|
|
|
|
const char **expected_targets;
|
|
|
|
size_t nexpected;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
testISCSIScanTargets(const void *data)
|
|
|
|
{
|
|
|
|
const struct testScanTargetsInfo *info = data;
|
|
|
|
size_t ntargets = 0;
|
|
|
|
char **targets = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
virCommandSetDryRun(NULL, testIscsiadmCb, NULL);
|
|
|
|
|
2018-06-29 14:18:23 +00:00
|
|
|
if (virISCSIScanTargets(info->portal, NULL,
|
|
|
|
false, &ntargets, &targets) < 0)
|
2014-03-19 13:02:09 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (info->nexpected != ntargets) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"Expected %zu targets, got %zu",
|
|
|
|
info->nexpected, ntargets);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ntargets; i++) {
|
|
|
|
if (STRNEQ(info->expected_targets[i], targets[i])) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"Expected target '%s', got '%s'",
|
|
|
|
info->expected_targets[i], targets[i]);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:53:44 +00:00
|
|
|
cleanup:
|
2014-03-19 13:02:09 +00:00
|
|
|
virCommandSetDryRun(NULL, NULL, NULL);
|
|
|
|
for (i = 0; i < ntargets; i++)
|
|
|
|
VIR_FREE(targets[i]);
|
|
|
|
VIR_FREE(targets);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-07-04 08:08:48 +00:00
|
|
|
|
|
|
|
struct testConnectionInfoLogin {
|
|
|
|
const char *portal;
|
|
|
|
const char *initiatoriqn;
|
|
|
|
const char *target;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
testISCSIConnectionLogin(const void *data)
|
|
|
|
{
|
|
|
|
const struct testConnectionInfoLogin *info = data;
|
2018-07-04 08:41:54 +00:00
|
|
|
struct testIscsiadmCbData cbData = { 0 };
|
2018-07-04 08:08:48 +00:00
|
|
|
int ret = -1;
|
|
|
|
|
2018-07-04 08:41:54 +00:00
|
|
|
virCommandSetDryRun(NULL, testIscsiadmCb, &cbData);
|
2018-07-04 08:08:48 +00:00
|
|
|
|
|
|
|
if (virISCSIConnectionLogin(info->portal, info->initiatoriqn, info->target) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
virCommandSetDryRun(NULL, NULL, NULL);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-21 09:59:10 +00:00
|
|
|
static int
|
|
|
|
mymain(void)
|
|
|
|
{
|
|
|
|
int rv = 0;
|
|
|
|
|
2017-11-03 12:09:47 +00:00
|
|
|
# define DO_SESSION_TEST(name, session) \
|
|
|
|
do { \
|
2018-07-04 08:47:07 +00:00
|
|
|
struct testSessionInfo info = {name, false, session}; \
|
2017-11-03 12:09:47 +00:00
|
|
|
if (virTestRun("ISCSI get session test" name, \
|
|
|
|
testISCSIGetSession, &info) < 0) \
|
|
|
|
rv = -1; \
|
2018-07-04 08:47:07 +00:00
|
|
|
info.output_version = true; \
|
2017-11-03 12:09:47 +00:00
|
|
|
if (virTestRun("ISCSI get (non-flash) session test" name, \
|
|
|
|
testISCSIGetSession, &info) < 0) \
|
|
|
|
rv = -1; \
|
2014-02-21 09:59:10 +00:00
|
|
|
} while (0)
|
|
|
|
|
|
|
|
DO_SESSION_TEST("iqn.2004-06.example:example1:iscsi.test", "1");
|
|
|
|
DO_SESSION_TEST("iqn.2009-04.example:example1:iscsi.seven", "7");
|
|
|
|
DO_SESSION_TEST("iqn.2009-04.example:example1:iscsi.eight", NULL);
|
|
|
|
|
2014-03-19 13:02:09 +00:00
|
|
|
const char *targets[] = {
|
|
|
|
"iqn.2004-06.example:example1:iscsi.test",
|
|
|
|
"iqn.2005-05.example:example1:iscsi.hello",
|
|
|
|
"iqn.2006-04.example:example1:iscsi.world",
|
|
|
|
"iqn.2007-04.example:example1:iscsi.foo",
|
|
|
|
"iqn.2008-04.example:example1:iscsi.bar",
|
|
|
|
"iqn.2009-04.example:example1:iscsi.seven"
|
|
|
|
};
|
|
|
|
struct testScanTargetsInfo infoTargets = {
|
|
|
|
.fake_cmd_output = "iscsiadm_sendtargets",
|
|
|
|
.portal = "10.20.30.40:3260,1",
|
|
|
|
.expected_targets = targets,
|
|
|
|
.nexpected = ARRAY_CARDINALITY(targets),
|
|
|
|
};
|
2016-05-26 15:01:50 +00:00
|
|
|
if (virTestRun("ISCSI scan targets", testISCSIScanTargets, &infoTargets) < 0)
|
2014-03-19 13:02:09 +00:00
|
|
|
rv = -1;
|
|
|
|
|
2018-07-04 08:08:48 +00:00
|
|
|
# define DO_LOGIN_TEST(portal, iqn, target) \
|
|
|
|
do { \
|
|
|
|
struct testConnectionInfoLogin info = {portal, iqn, target }; \
|
|
|
|
if (virTestRun("ISCSI login " portal, \
|
|
|
|
testISCSIConnectionLogin, &info) < 0) \
|
|
|
|
rv = -1; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
DO_LOGIN_TEST("10.20.30.40:3260,1", NULL, "iqn.2004-06.example:example1:iscsi.test");
|
2018-07-04 08:41:54 +00:00
|
|
|
DO_LOGIN_TEST("10.20.30.40:3260,1", "iqn.2004-06.example:example1:initiator",
|
|
|
|
"iqn.2004-06.example:example1:iscsi.test");
|
2018-07-04 08:08:48 +00:00
|
|
|
|
2014-02-21 09:59:10 +00:00
|
|
|
if (rv < 0)
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2019-08-21 16:13:16 +00:00
|
|
|
VIR_TEST_MAIN_PRELOAD(mymain, VIR_TEST_MOCK("virrandom"))
|
2014-02-21 09:59:10 +00:00
|
|
|
#endif /* WIN32 */
|