diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
commit | 483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch) | |
tree | e5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/spdk/test/unit/lib/iscsi | |
parent | Initial commit. (diff) | |
download | ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.tar.xz ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.zip |
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/test/unit/lib/iscsi')
22 files changed, 4524 insertions, 0 deletions
diff --git a/src/spdk/test/unit/lib/iscsi/Makefile b/src/spdk/test/unit/lib/iscsi/Makefile new file mode 100644 index 00000000..396c5a05 --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/Makefile @@ -0,0 +1,44 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk + +DIRS-y = conn.c init_grp.c iscsi.c param.c portal_grp.c tgt_node.c + +.PHONY: all clean $(DIRS-y) + +all: $(DIRS-y) +clean: $(DIRS-y) + +include $(SPDK_ROOT_DIR)/mk/spdk.subdirs.mk diff --git a/src/spdk/test/unit/lib/iscsi/common.c b/src/spdk/test/unit/lib/iscsi/common.c new file mode 100644 index 00000000..9ef4f9ab --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/common.c @@ -0,0 +1,256 @@ +#include "iscsi/task.h" +#include "iscsi/iscsi.h" +#include "iscsi/conn.h" +#include "iscsi/acceptor.h" + +#include "spdk/env.h" +#include "spdk/event.h" +#include "spdk/sock.h" +#include "spdk_cunit.h" + +#include "spdk_internal/log.h" + +#include "scsi/scsi_internal.h" + +SPDK_LOG_REGISTER_COMPONENT("iscsi", SPDK_LOG_ISCSI) + +TAILQ_HEAD(, spdk_iscsi_pdu) g_write_pdu_list; + +struct spdk_iscsi_task * +spdk_iscsi_task_get(struct spdk_iscsi_conn *conn, + struct spdk_iscsi_task *parent, + spdk_scsi_task_cpl cpl_fn) +{ + struct spdk_iscsi_task *task; + + task = calloc(1, sizeof(*task)); + + return task; +} + +void +spdk_scsi_task_put(struct spdk_scsi_task *task) +{ + free(task); +} + +void +spdk_put_pdu(struct spdk_iscsi_pdu *pdu) +{ + if (!pdu) { + return; + } + + pdu->ref--; + if (pdu->ref < 0) { + CU_FAIL("negative ref count"); + pdu->ref = 0; + } + + if (pdu->ref == 0) { + if (pdu->data && !pdu->data_from_mempool) { + free(pdu->data); + } + free(pdu); + } +} + +struct spdk_iscsi_pdu * +spdk_get_pdu(void) +{ + struct spdk_iscsi_pdu *pdu; + + pdu = malloc(sizeof(*pdu)); + if (!pdu) { + return NULL; + } + + memset(pdu, 0, offsetof(struct spdk_iscsi_pdu, ahs)); + pdu->ref = 1; + + return pdu; +} + +void +spdk_scsi_task_process_null_lun(struct spdk_scsi_task *task) +{ +} + +void +spdk_scsi_dev_queue_task(struct spdk_scsi_dev *dev, + struct spdk_scsi_task *task) +{ +} + +struct spdk_scsi_port * +spdk_scsi_dev_find_port_by_id(struct spdk_scsi_dev *dev, uint64_t id) +{ + return NULL; +} + +void +spdk_scsi_dev_queue_mgmt_task(struct spdk_scsi_dev *dev, + struct spdk_scsi_task *task, + enum spdk_scsi_task_func func) +{ +} + +const char * +spdk_scsi_dev_get_name(const struct spdk_scsi_dev *dev) +{ + if (dev != NULL) { + return dev->name; + } + + return NULL; +} + +void +spdk_iscsi_acceptor_start(struct spdk_iscsi_portal *p) +{ +} + +void +spdk_iscsi_acceptor_stop(struct spdk_iscsi_portal *p) +{ +} + +struct spdk_sock * +spdk_sock_listen(const char *ip, int port) +{ + static int g_sock; + + return (struct spdk_sock *)&g_sock; +} + +int +spdk_sock_close(struct spdk_sock **sock) +{ + *sock = NULL; + + return 0; +} + +static struct spdk_cpuset *g_app_core_mask; + +struct spdk_cpuset * +spdk_app_get_core_mask(void) +{ + int i; + if (!g_app_core_mask) { + g_app_core_mask = spdk_cpuset_alloc(); + for (i = 0; i < SPDK_CPUSET_SIZE; i++) { + spdk_cpuset_set_cpu(g_app_core_mask, i, true); + } + } + return g_app_core_mask; +} + +int +spdk_app_parse_core_mask(const char *mask, struct spdk_cpuset *cpumask) +{ + int rc; + + if (mask == NULL || cpumask == NULL) { + return -1; + } + + rc = spdk_cpuset_parse(cpumask, mask); + if (rc < 0) { + return -1; + } + return 0; +} + +uint32_t +spdk_env_get_current_core(void) +{ + return 0; +} + +struct spdk_event * +spdk_event_allocate(uint32_t core, spdk_event_fn fn, void *arg1, void *arg2) +{ + return NULL; +} + +struct spdk_scsi_dev * + spdk_scsi_dev_construct(const char *name, const char **bdev_name_list, + int *lun_id_list, int num_luns, uint8_t protocol_id, + void (*hotremove_cb)(const struct spdk_scsi_lun *, void *), + void *hotremove_ctx) +{ + return NULL; +} + +void +spdk_scsi_dev_destruct(struct spdk_scsi_dev *dev) +{ +} + +int +spdk_scsi_dev_add_port(struct spdk_scsi_dev *dev, uint64_t id, const char *name) +{ + return 0; +} + +int +spdk_iscsi_drop_conns(struct spdk_iscsi_conn *conn, const char *conn_match, + int drop_all) +{ + return 0; +} + +int +spdk_scsi_dev_delete_port(struct spdk_scsi_dev *dev, uint64_t id) +{ + return 0; +} + +void +spdk_shutdown_iscsi_conns(void) +{ +} + +void +spdk_iscsi_task_cpl(struct spdk_scsi_task *scsi_task) +{ + +} + +void +spdk_iscsi_task_mgmt_cpl(struct spdk_scsi_task *scsi_task) +{ + +} + +int +spdk_iscsi_conn_read_data(struct spdk_iscsi_conn *conn, int bytes, + void *buf) +{ + return 0; +} + +void +spdk_iscsi_conn_write_pdu(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu) +{ + TAILQ_INSERT_TAIL(&g_write_pdu_list, pdu, tailq); +} + +void +spdk_iscsi_conn_logout(struct spdk_iscsi_conn *conn) +{ +} + +void +spdk_scsi_task_set_status(struct spdk_scsi_task *task, int sc, int sk, int asc, int ascq) +{ +} + +void +spdk_scsi_task_set_data(struct spdk_scsi_task *task, void *data, uint32_t len) +{ + SPDK_CU_ASSERT_FATAL(task->iovs != NULL); + task->iovs[0].iov_base = data; + task->iovs[0].iov_len = len; +} diff --git a/src/spdk/test/unit/lib/iscsi/conn.c/.gitignore b/src/spdk/test/unit/lib/iscsi/conn.c/.gitignore new file mode 100644 index 00000000..3bb0afd8 --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/conn.c/.gitignore @@ -0,0 +1 @@ +conn_ut diff --git a/src/spdk/test/unit/lib/iscsi/conn.c/Makefile b/src/spdk/test/unit/lib/iscsi/conn.c/Makefile new file mode 100644 index 00000000..96f2f5d7 --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/conn.c/Makefile @@ -0,0 +1,42 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk +include $(SPDK_ROOT_DIR)/mk/spdk.app.mk + +SPDK_LIB_LIST = trace + +TEST_FILE = conn_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/src/spdk/test/unit/lib/iscsi/conn.c/conn_ut.c b/src/spdk/test/unit/lib/iscsi/conn.c/conn_ut.c new file mode 100644 index 00000000..88d23423 --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/conn.c/conn_ut.c @@ -0,0 +1,404 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/stdinc.h" + +#include "common/lib/test_env.c" +#include "spdk_cunit.h" + +#include "iscsi/conn.c" + +SPDK_LOG_REGISTER_COMPONENT("iscsi", SPDK_LOG_ISCSI) + +#define DMIN32(A,B) ((uint32_t) ((uint32_t)(A) > (uint32_t)(B) ? (uint32_t)(B) : (uint32_t)(A))) + +struct spdk_iscsi_globals g_spdk_iscsi; +static TAILQ_HEAD(, spdk_iscsi_task) g_ut_read_tasks = TAILQ_HEAD_INITIALIZER(g_ut_read_tasks); + +int +spdk_app_get_shm_id(void) +{ + return 0; +} + +uint32_t +spdk_env_get_current_core(void) +{ + return 0; +} + +uint32_t +spdk_env_get_first_core(void) +{ + return 0; +} + +uint32_t +spdk_env_get_last_core(void) +{ + return 0; +} + +uint32_t +spdk_env_get_next_core(uint32_t prev_core) +{ + return 0; +} + +struct spdk_event * +spdk_event_allocate(uint32_t lcore, spdk_event_fn fn, void *arg1, void *arg2) +{ + return NULL; +} + +void +spdk_event_call(struct spdk_event *event) +{ +} + +int +spdk_sock_getaddr(struct spdk_sock *sock, char *saddr, int slen, uint16_t *sport, + char *caddr, int clen, uint16_t *cport) +{ + return 0; +} + +int +spdk_sock_close(struct spdk_sock **sock) +{ + *sock = NULL; + return 0; +} + +ssize_t +spdk_sock_recv(struct spdk_sock *sock, void *buf, size_t len) +{ + return 0; +} + +ssize_t +spdk_sock_writev(struct spdk_sock *sock, struct iovec *iov, int iovcnt) +{ + return 0; +} + +int +spdk_sock_set_recvlowat(struct spdk_sock *s, int nbytes) +{ + return 0; +} + +int +spdk_sock_set_recvbuf(struct spdk_sock *sock, int sz) +{ + return 0; +} + +int +spdk_sock_set_sendbuf(struct spdk_sock *sock, int sz) +{ + return 0; +} + +int +spdk_sock_group_add_sock(struct spdk_sock_group *group, struct spdk_sock *sock, + spdk_sock_cb cb_fn, void *cb_arg) +{ + return 0; +} + +int +spdk_sock_group_remove_sock(struct spdk_sock_group *group, struct spdk_sock *sock) +{ + return 0; +} + +void +spdk_scsi_task_put(struct spdk_scsi_task *task) +{ +} + +struct spdk_scsi_lun * +spdk_scsi_dev_get_lun(struct spdk_scsi_dev *dev, int lun_id) +{ + return NULL; +} + +bool +spdk_scsi_dev_has_pending_tasks(const struct spdk_scsi_dev *dev) +{ + return true; +} + +int +spdk_scsi_lun_open(struct spdk_scsi_lun *lun, spdk_scsi_remove_cb_t hotremove_cb, + void *hotremove_ctx, struct spdk_scsi_desc **desc) +{ + return 0; +} + +void +spdk_scsi_lun_close(struct spdk_scsi_desc *desc) +{ +} + +int spdk_scsi_lun_allocate_io_channel(struct spdk_scsi_desc *desc) +{ + return 0; +} + +void spdk_scsi_lun_free_io_channel(struct spdk_scsi_desc *desc) +{ +} + +int +spdk_scsi_lun_get_id(const struct spdk_scsi_lun *lun) +{ + return 0; +} + +const char * +spdk_scsi_port_get_name(const struct spdk_scsi_port *port) +{ + return NULL; +} + +void +spdk_scsi_task_copy_status(struct spdk_scsi_task *dst, + struct spdk_scsi_task *src) +{ +} + +void +spdk_put_pdu(struct spdk_iscsi_pdu *pdu) +{ +} + +void +spdk_iscsi_param_free(struct iscsi_param *params) +{ +} + +int +spdk_iscsi_conn_params_init(struct iscsi_param **params) +{ + return 0; +} + +void spdk_clear_all_transfer_task(struct spdk_iscsi_conn *conn, + struct spdk_scsi_lun *lun) +{ +} + +int +spdk_iscsi_build_iovecs(struct spdk_iscsi_conn *conn, struct iovec *iovec, + struct spdk_iscsi_pdu *pdu) +{ + return 0; +} + +bool spdk_iscsi_is_deferred_free_pdu(struct spdk_iscsi_pdu *pdu) +{ + return false; +} + +void spdk_iscsi_task_response(struct spdk_iscsi_conn *conn, + struct spdk_iscsi_task *task) +{ +} + +void +spdk_iscsi_task_mgmt_response(struct spdk_iscsi_conn *conn, + struct spdk_iscsi_task *task) +{ +} + +void spdk_iscsi_send_nopin(struct spdk_iscsi_conn *conn) +{ +} + +int +spdk_iscsi_execute(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu) +{ + return 0; +} + +void spdk_del_transfer_task(struct spdk_iscsi_conn *conn, uint32_t task_tag) +{ +} + +int spdk_iscsi_conn_handle_queued_datain_tasks(struct spdk_iscsi_conn *conn) +{ + return 0; +} + +int +spdk_iscsi_read_pdu(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu **_pdu) +{ + return 0; +} + +void spdk_free_sess(struct spdk_iscsi_sess *sess) +{ +} + +int +spdk_iscsi_tgt_node_cleanup_luns(struct spdk_iscsi_conn *conn, + struct spdk_iscsi_tgt_node *target) +{ + return 0; +} + +void +spdk_shutdown_iscsi_conns_done(void) +{ +} + +static struct spdk_iscsi_task * +ut_conn_task_get(struct spdk_iscsi_task *parent) +{ + struct spdk_iscsi_task *task; + + task = calloc(1, sizeof(*task)); + SPDK_CU_ASSERT_FATAL(task != NULL); + + if (parent) { + task->parent = parent; + } + return task; +} + +static void +ut_conn_create_read_tasks(int transfer_len) +{ + struct spdk_iscsi_task *task, *subtask; + int32_t remaining_size = 0; + + task = ut_conn_task_get(NULL); + + task->scsi.transfer_len = transfer_len; + task->scsi.offset = 0; + task->scsi.length = DMIN32(SPDK_BDEV_LARGE_BUF_MAX_SIZE, task->scsi.transfer_len); + task->scsi.status = SPDK_SCSI_STATUS_GOOD; + + remaining_size = task->scsi.transfer_len - task->scsi.length; + task->current_datain_offset = 0; + + if (remaining_size == 0) { + TAILQ_INSERT_TAIL(&g_ut_read_tasks, task, link); + return; + } + + while (1) { + if (task->current_datain_offset == 0) { + task->current_datain_offset = task->scsi.length; + TAILQ_INSERT_TAIL(&g_ut_read_tasks, task, link); + continue; + } + + if (task->current_datain_offset < task->scsi.transfer_len) { + remaining_size = task->scsi.transfer_len - task->current_datain_offset; + + subtask = ut_conn_task_get(task); + + subtask->scsi.offset = task->current_datain_offset; + subtask->scsi.length = DMIN32(SPDK_BDEV_LARGE_BUF_MAX_SIZE, remaining_size); + subtask->scsi.status = SPDK_SCSI_STATUS_GOOD; + + task->current_datain_offset += subtask->scsi.length; + + TAILQ_INSERT_TAIL(&g_ut_read_tasks, subtask, link); + } + + if (task->current_datain_offset == task->scsi.transfer_len) { + break; + } + } +} + +static void +read_task_split_in_order_case(void) +{ + struct spdk_iscsi_task *primary, *task, *tmp; + + ut_conn_create_read_tasks(SPDK_BDEV_LARGE_BUF_MAX_SIZE * 8); + + TAILQ_FOREACH(task, &g_ut_read_tasks, link) { + primary = spdk_iscsi_task_get_primary(task); + process_read_task_completion(NULL, task, primary); + } + + primary = TAILQ_FIRST(&g_ut_read_tasks); + SPDK_CU_ASSERT_FATAL(primary != NULL); + + if (primary != NULL) { + CU_ASSERT(primary->bytes_completed == primary->scsi.transfer_len); + } + + TAILQ_FOREACH_SAFE(task, &g_ut_read_tasks, link, tmp) { + TAILQ_REMOVE(&g_ut_read_tasks, task, link); + free(task); + } + + CU_ASSERT(TAILQ_EMPTY(&g_ut_read_tasks)); +} + +int +main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + if (CU_initialize_registry() != CUE_SUCCESS) { + return CU_get_error(); + } + + suite = CU_add_suite("conn_suite", NULL, NULL); + if (suite == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if ( + CU_add_test(suite, "read task split in order", read_task_split_in_order_case) == NULL + ) { + CU_cleanup_registry(); + return CU_get_error(); + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + return num_failures; +} diff --git a/src/spdk/test/unit/lib/iscsi/init_grp.c/.gitignore b/src/spdk/test/unit/lib/iscsi/init_grp.c/.gitignore new file mode 100644 index 00000000..8fbc2b63 --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/init_grp.c/.gitignore @@ -0,0 +1 @@ +init_grp_ut diff --git a/src/spdk/test/unit/lib/iscsi/init_grp.c/Makefile b/src/spdk/test/unit/lib/iscsi/init_grp.c/Makefile new file mode 100644 index 00000000..9c87ef55 --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/init_grp.c/Makefile @@ -0,0 +1,41 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk +include $(SPDK_ROOT_DIR)/mk/spdk.app.mk + +SPDK_LIB_LIST = conf +TEST_FILE = init_grp_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/src/spdk/test/unit/lib/iscsi/init_grp.c/init_grp.conf b/src/spdk/test/unit/lib/iscsi/init_grp.c/init_grp.conf new file mode 100644 index 00000000..aaa660de --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/init_grp.c/init_grp.conf @@ -0,0 +1,31 @@ +[IG_Valid0] +# Success is expected. + InitiatorName iqn.2017-10.spdk.io:0001 + Netmask 192.168.2.0 + +[IG_Valid1] +# Success is expected. + InitiatorName iqn.2017-10.spdk.io:0001 + Netmask 192.168.2.0 + Netmask 192.168.2.1 + +[IG_Valid2] +# Success is expected. + InitiatorName iqn.2017-10.spdk.io:0001 + InitiatorName iqn.2017-10.spdk.io:0002 + Netmask 192.168.2.0 + +[IG_Valid3] +# Success is expected. + InitiatorName iqn.2017-10.spdk.io:0001 + InitiatorName iqn.2017-10.spdk.io:0002 + Netmask 192.168.2.0 + Netmask 192.168.2.1 + +[IG_Invalid0] +# Failure is expected. + InitiatorName iqn.2017-10.spdk.io:0001 + +[IG_Invalid1] +# Failure is expected. + Netmask 192.168.2.0 diff --git a/src/spdk/test/unit/lib/iscsi/init_grp.c/init_grp_ut.c b/src/spdk/test/unit/lib/iscsi/init_grp.c/init_grp_ut.c new file mode 100644 index 00000000..5fcce81b --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/init_grp.c/init_grp_ut.c @@ -0,0 +1,702 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "spdk/stdinc.h" + +#include "spdk_cunit.h" +#include "CUnit/Basic.h" + +#include "iscsi/init_grp.c" +#include "unit/lib/json_mock.c" + +SPDK_LOG_REGISTER_COMPONENT("iscsi", SPDK_LOG_ISCSI) + +struct spdk_iscsi_globals g_spdk_iscsi; + +const char *config_file; + +static int +test_setup(void) +{ + TAILQ_INIT(&g_spdk_iscsi.ig_head); + return 0; +} + +static void +create_from_config_file_cases(void) +{ + struct spdk_conf *config; + struct spdk_conf_section *sp; + char section_name[64]; + int section_index; + int rc; + + config = spdk_conf_allocate(); + + rc = spdk_conf_read(config, config_file); + CU_ASSERT(rc == 0); + + section_index = 0; + while (true) { + snprintf(section_name, sizeof(section_name), "IG_Valid%d", section_index); + + sp = spdk_conf_find_section(config, section_name); + if (sp == NULL) { + break; + } + + rc = spdk_iscsi_parse_init_grp(sp); + CU_ASSERT(rc == 0); + + spdk_iscsi_init_grps_destroy(); + + section_index++; + } + + section_index = 0; + while (true) { + snprintf(section_name, sizeof(section_name), "IG_Invalid%d", section_index); + + sp = spdk_conf_find_section(config, section_name); + if (sp == NULL) { + break; + } + + rc = spdk_iscsi_parse_init_grp(sp); + CU_ASSERT(rc != 0); + + spdk_iscsi_init_grps_destroy(); + + section_index++; + } + + spdk_conf_free(config); +} + + +static void +create_initiator_group_success_case(void) +{ + struct spdk_iscsi_init_grp *ig; + + ig = spdk_iscsi_init_grp_create(1); + CU_ASSERT(ig != NULL); + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +find_initiator_group_success_case(void) +{ + struct spdk_iscsi_init_grp *ig, *tmp; + int rc; + + ig = spdk_iscsi_init_grp_create(1); + CU_ASSERT(ig != NULL); + + rc = spdk_iscsi_init_grp_register(ig); + CU_ASSERT(rc == 0); + + ig = spdk_iscsi_init_grp_find_by_tag(1); + CU_ASSERT(ig != NULL); + + tmp = spdk_iscsi_init_grp_unregister(1); + CU_ASSERT(ig == tmp); + spdk_iscsi_init_grp_destroy(ig); + + ig = spdk_iscsi_init_grp_find_by_tag(1); + CU_ASSERT(ig == NULL); +} + +static void +register_initiator_group_twice_case(void) +{ + struct spdk_iscsi_init_grp *ig, *tmp; + int rc; + + ig = spdk_iscsi_init_grp_create(1); + CU_ASSERT(ig != NULL); + + rc = spdk_iscsi_init_grp_register(ig); + CU_ASSERT(rc == 0); + + rc = spdk_iscsi_init_grp_register(ig); + CU_ASSERT(rc != 0); + + ig = spdk_iscsi_init_grp_find_by_tag(1); + CU_ASSERT(ig != NULL); + + tmp = spdk_iscsi_init_grp_unregister(1); + CU_ASSERT(tmp == ig); + spdk_iscsi_init_grp_destroy(ig); + + ig = spdk_iscsi_init_grp_find_by_tag(1); + CU_ASSERT(ig == NULL); +} + +static void +add_initiator_name_success_case(void) +{ + + int rc; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_name *iname; + char *name1 = "iqn.2017-10.spdk.io:0001"; + char *name2 = "iqn.2017-10.spdk.io:0002"; + + ig = spdk_iscsi_init_grp_create(1); + CU_ASSERT(ig != NULL); + + /* add two different names to the empty name list */ + rc = spdk_iscsi_init_grp_add_initiator(ig, name1); + CU_ASSERT(rc == 0); + + rc = spdk_iscsi_init_grp_add_initiator(ig, name2); + CU_ASSERT(rc == 0); + + /* check if two names are added correctly. */ + iname = spdk_iscsi_init_grp_find_initiator(ig, name1); + CU_ASSERT(iname != NULL); + + iname = spdk_iscsi_init_grp_find_initiator(ig, name2); + CU_ASSERT(iname != NULL); + + /* restore the initial state */ + rc = spdk_iscsi_init_grp_delete_initiator(ig, name1); + CU_ASSERT(rc == 0); + + iname = spdk_iscsi_init_grp_find_initiator(ig, name1); + CU_ASSERT(iname == NULL); + + rc = spdk_iscsi_init_grp_delete_initiator(ig, name2); + CU_ASSERT(rc == 0); + + iname = spdk_iscsi_init_grp_find_initiator(ig, name2); + CU_ASSERT(iname == NULL); + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +add_initiator_name_fail_case(void) +{ + int rc; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_name *iname; + char *name1 = "iqn.2017-10.spdk.io:0001"; + + ig = spdk_iscsi_init_grp_create(1); + CU_ASSERT(ig != NULL); + + /* add an name to the full name list */ + ig->ninitiators = MAX_INITIATOR; + + rc = spdk_iscsi_init_grp_add_initiator(ig, name1); + CU_ASSERT(rc != 0); + + ig->ninitiators = 0; + + /* add the same name to the name list twice */ + rc = spdk_iscsi_init_grp_add_initiator(ig, name1); + CU_ASSERT(rc == 0); + + rc = spdk_iscsi_init_grp_add_initiator(ig, name1); + CU_ASSERT(rc != 0); + + /* restore the initial state */ + rc = spdk_iscsi_init_grp_delete_initiator(ig, name1); + CU_ASSERT(rc == 0); + + iname = spdk_iscsi_init_grp_find_initiator(ig, name1); + CU_ASSERT(iname == NULL); + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +delete_all_initiator_names_success_case(void) +{ + int rc; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_name *iname; + char *name1 = "iqn.2017-10.spdk.io:0001"; + char *name2 = "iqn.2017-10.spdk.io:0002"; + + ig = spdk_iscsi_init_grp_create(1); + CU_ASSERT(ig != NULL); + + /* add two different names to the empty name list */ + rc = spdk_iscsi_init_grp_add_initiator(ig, name1); + CU_ASSERT(rc == 0); + + rc = spdk_iscsi_init_grp_add_initiator(ig, name2); + CU_ASSERT(rc == 0); + + /* delete all initiator names */ + spdk_iscsi_init_grp_delete_all_initiators(ig); + + /* check if two names are deleted correctly. */ + iname = spdk_iscsi_init_grp_find_initiator(ig, name1); + CU_ASSERT(iname == NULL); + + iname = spdk_iscsi_init_grp_find_initiator(ig, name2); + CU_ASSERT(iname == NULL); + + /* restore the initial state */ + spdk_iscsi_init_grp_destroy(ig); +} + +static void +add_netmask_success_case(void) +{ + int rc; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_netmask *imask; + char *netmask1 = "192.168.2.0"; + char *netmask2 = "192.168.2.1"; + + ig = spdk_iscsi_init_grp_create(1); + CU_ASSERT(ig != NULL); + + /* add two different netmasks to the empty netmask list */ + rc = spdk_iscsi_init_grp_add_netmask(ig, netmask1); + CU_ASSERT(rc == 0); + + rc = spdk_iscsi_init_grp_add_netmask(ig, netmask2); + CU_ASSERT(rc == 0); + + /* check if two netmasks are added correctly. */ + imask = spdk_iscsi_init_grp_find_netmask(ig, netmask1); + CU_ASSERT(imask != NULL); + + imask = spdk_iscsi_init_grp_find_netmask(ig, netmask2); + CU_ASSERT(imask != NULL); + + /* restore the initial state */ + rc = spdk_iscsi_init_grp_delete_netmask(ig, netmask1); + CU_ASSERT(rc == 0); + + imask = spdk_iscsi_init_grp_find_netmask(ig, netmask1); + CU_ASSERT(imask == NULL); + + rc = spdk_iscsi_init_grp_delete_netmask(ig, netmask2); + CU_ASSERT(rc == 0); + + imask = spdk_iscsi_init_grp_find_netmask(ig, netmask2); + CU_ASSERT(imask == NULL); + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +add_netmask_fail_case(void) +{ + int rc; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_netmask *imask; + char *netmask1 = "192.168.2.0"; + + ig = spdk_iscsi_init_grp_create(1); + CU_ASSERT(ig != NULL); + + /* add an netmask to the full netmask list */ + ig->nnetmasks = MAX_NETMASK; + + rc = spdk_iscsi_init_grp_add_netmask(ig, netmask1); + CU_ASSERT(rc != 0); + + ig->nnetmasks = 0; + + /* add the same netmask to the netmask list twice */ + rc = spdk_iscsi_init_grp_add_netmask(ig, netmask1); + CU_ASSERT(rc == 0); + + rc = spdk_iscsi_init_grp_add_netmask(ig, netmask1); + CU_ASSERT(rc != 0); + + /* restore the initial state */ + rc = spdk_iscsi_init_grp_delete_netmask(ig, netmask1); + CU_ASSERT(rc == 0); + + imask = spdk_iscsi_init_grp_find_netmask(ig, netmask1); + CU_ASSERT(imask == NULL); + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +delete_all_netmasks_success_case(void) +{ + int rc; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_netmask *imask; + char *netmask1 = "192.168.2.0"; + char *netmask2 = "192.168.2.1"; + + ig = spdk_iscsi_init_grp_create(1); + CU_ASSERT(ig != NULL); + + /* add two different netmasks to the empty netmask list */ + rc = spdk_iscsi_init_grp_add_netmask(ig, netmask1); + CU_ASSERT(rc == 0); + + rc = spdk_iscsi_init_grp_add_netmask(ig, netmask2); + CU_ASSERT(rc == 0); + + /* delete all netmasks */ + spdk_iscsi_init_grp_delete_all_netmasks(ig); + + /* check if two netmasks are deleted correctly. */ + imask = spdk_iscsi_init_grp_find_netmask(ig, netmask1); + CU_ASSERT(imask == NULL); + + imask = spdk_iscsi_init_grp_find_netmask(ig, netmask2); + CU_ASSERT(imask == NULL); + + /* restore the initial state */ + spdk_iscsi_init_grp_destroy(ig); +} + +static void +initiator_name_overwrite_all_to_any_case(void) +{ + int rc; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_name *iname; + char *all = "ALL"; + char *any = "ANY"; + char *all_not = "!ALL"; + char *any_not = "!ANY"; + + ig = spdk_iscsi_init_grp_create(1); + CU_ASSERT(ig != NULL); + + rc = spdk_iscsi_init_grp_add_initiator(ig, all); + CU_ASSERT(rc == 0); + + iname = spdk_iscsi_init_grp_find_initiator(ig, all); + CU_ASSERT(iname == NULL); + + iname = spdk_iscsi_init_grp_find_initiator(ig, any); + CU_ASSERT(iname != NULL); + + rc = spdk_iscsi_init_grp_delete_initiator(ig, any); + CU_ASSERT(rc == 0); + + rc = spdk_iscsi_init_grp_add_initiator(ig, all_not); + CU_ASSERT(rc == 0); + + iname = spdk_iscsi_init_grp_find_initiator(ig, all_not); + CU_ASSERT(iname == NULL); + + iname = spdk_iscsi_init_grp_find_initiator(ig, any_not); + CU_ASSERT(iname != NULL); + + rc = spdk_iscsi_init_grp_delete_initiator(ig, any_not); + CU_ASSERT(rc == 0); + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +netmask_overwrite_all_to_any_case(void) +{ + int rc; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_netmask *imask; + char *all = "ALL"; + char *any = "ANY"; + + ig = spdk_iscsi_init_grp_create(1); + CU_ASSERT(ig != NULL); + + rc = spdk_iscsi_init_grp_add_netmask(ig, all); + CU_ASSERT(rc == 0); + + imask = spdk_iscsi_init_grp_find_netmask(ig, all); + CU_ASSERT(imask == NULL); + + imask = spdk_iscsi_init_grp_find_netmask(ig, any); + CU_ASSERT(imask != NULL); + + rc = spdk_iscsi_init_grp_delete_netmask(ig, any); + CU_ASSERT(rc == 0); + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +add_delete_initiator_names_case(void) +{ + int rc, i; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_name *iname; + char *names[3] = {"iqn.2018-02.spdk.io:0001", "iqn.2018-02.spdk.io:0002", "iqn.2018-02.spdk.io:0003"}; + + ig = spdk_iscsi_init_grp_create(1); + SPDK_CU_ASSERT_FATAL(ig != NULL); + + rc = spdk_iscsi_init_grp_add_initiators(ig, 3, names); + CU_ASSERT(rc == 0); + + for (i = 0; i < 3; i++) { + iname = spdk_iscsi_init_grp_find_initiator(ig, names[i]); + CU_ASSERT(iname != NULL); + } + + rc = spdk_iscsi_init_grp_delete_initiators(ig, 3, names); + CU_ASSERT(rc == 0); + + if (ig != NULL) { + CU_ASSERT(TAILQ_EMPTY(&ig->initiator_head)); + } + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +add_duplicated_initiator_names_case(void) +{ + int rc; + struct spdk_iscsi_init_grp *ig; + char *names[3] = {"iqn.2018-02.spdk.io:0001", "iqn.2018-02.spdk.io:0002", "iqn.2018-02.spdk.io:0001"}; + + ig = spdk_iscsi_init_grp_create(1); + SPDK_CU_ASSERT_FATAL(ig != NULL); + + rc = spdk_iscsi_init_grp_add_initiators(ig, 3, names); + CU_ASSERT(rc != 0); + + if (ig != NULL) { + CU_ASSERT(TAILQ_EMPTY(&ig->initiator_head)); + } + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +delete_nonexisting_initiator_names_case(void) +{ + int rc, i; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_name *iname; + char *names1[3] = {"iqn.2018-02.spdk.io:0001", "iqn.2018-02.spdk.io:0002", "iqn.2018-02.spdk.io:0003"}; + char *names2[3] = {"iqn.2018-02.spdk.io:0001", "iqn.2018-02.spdk.io:0002", "iqn.2018-02.spdk.io:0004"}; + + ig = spdk_iscsi_init_grp_create(1); + SPDK_CU_ASSERT_FATAL(ig != NULL); + + rc = spdk_iscsi_init_grp_add_initiators(ig, 3, names1); + CU_ASSERT(rc == 0); + + for (i = 0; i < 3; i++) { + iname = spdk_iscsi_init_grp_find_initiator(ig, names1[i]); + CU_ASSERT(iname != NULL); + } + + rc = spdk_iscsi_init_grp_delete_initiators(ig, 3, names2); + CU_ASSERT(rc != 0); + + for (i = 0; i < 3; i++) { + iname = spdk_iscsi_init_grp_find_initiator(ig, names1[i]); + CU_ASSERT(iname != NULL); + } + + rc = spdk_iscsi_init_grp_delete_initiators(ig, 3, names1); + CU_ASSERT(rc == 0); + + if (ig != NULL) { + CU_ASSERT(TAILQ_EMPTY(&ig->initiator_head)); + } + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +add_delete_netmasks_case(void) +{ + int rc, i; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_netmask *netmask; + char *netmasks[3] = {"192.168.2.0", "192.168.2.1", "192.168.2.2"}; + + ig = spdk_iscsi_init_grp_create(1); + SPDK_CU_ASSERT_FATAL(ig != NULL); + + rc = spdk_iscsi_init_grp_add_netmasks(ig, 3, netmasks); + CU_ASSERT(rc == 0); + + for (i = 0; i < 3; i++) { + netmask = spdk_iscsi_init_grp_find_netmask(ig, netmasks[i]); + CU_ASSERT(netmask != NULL); + } + + rc = spdk_iscsi_init_grp_delete_netmasks(ig, 3, netmasks); + CU_ASSERT(rc == 0); + + if (ig != NULL) { + CU_ASSERT(TAILQ_EMPTY(&ig->netmask_head)); + } + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +add_duplicated_netmasks_case(void) +{ + int rc; + struct spdk_iscsi_init_grp *ig; + char *netmasks[3] = {"192.168.2.0", "192.168.2.1", "192.168.2.0"}; + + ig = spdk_iscsi_init_grp_create(1); + SPDK_CU_ASSERT_FATAL(ig != NULL); + + rc = spdk_iscsi_init_grp_add_netmasks(ig, 3, netmasks); + CU_ASSERT(rc != 0); + + if (ig != NULL) { + CU_ASSERT(TAILQ_EMPTY(&ig->netmask_head)); + } + + spdk_iscsi_init_grp_destroy(ig); +} + +static void +delete_nonexisting_netmasks_case(void) +{ + int rc, i; + struct spdk_iscsi_init_grp *ig; + struct spdk_iscsi_initiator_netmask *netmask; + char *netmasks1[3] = {"192.168.2.0", "192.168.2.1", "192.168.2.2"}; + char *netmasks2[3] = {"192.168.2.0", "192.168.2.1", "192.168.2.3"}; + + ig = spdk_iscsi_init_grp_create(1); + SPDK_CU_ASSERT_FATAL(ig != NULL); + + rc = spdk_iscsi_init_grp_add_netmasks(ig, 3, netmasks1); + CU_ASSERT(rc == 0); + + for (i = 0; i < 3; i++) { + netmask = spdk_iscsi_init_grp_find_netmask(ig, netmasks1[i]); + CU_ASSERT(netmask != NULL); + } + + rc = spdk_iscsi_init_grp_delete_netmasks(ig, 3, netmasks2); + CU_ASSERT(rc != 0); + + for (i = 0; i < 3; i++) { + netmask = spdk_iscsi_init_grp_find_netmask(ig, netmasks1[i]); + CU_ASSERT(netmask != NULL); + } + + rc = spdk_iscsi_init_grp_delete_netmasks(ig, 3, netmasks1); + CU_ASSERT(rc == 0); + + if (ig != NULL) { + CU_ASSERT(TAILQ_EMPTY(&ig->netmask_head)); + } + + spdk_iscsi_init_grp_destroy(ig); +} + + +int +main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + if (argc < 2) { + fprintf(stderr, "usage: %s <config file>\n", argv[0]); + exit(1); + } + + if (CU_initialize_registry() != CUE_SUCCESS) { + return CU_get_error(); + } + + config_file = argv[1]; + + suite = CU_add_suite("init_grp_suite", test_setup, NULL); + if (suite == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if ( + CU_add_test(suite, "create from config file cases", + create_from_config_file_cases) == NULL + || CU_add_test(suite, "create initiator group success case", + create_initiator_group_success_case) == NULL + || CU_add_test(suite, "find initiator group success case", + find_initiator_group_success_case) == NULL + || CU_add_test(suite, "register initiator group twice case", + register_initiator_group_twice_case) == NULL + || CU_add_test(suite, "add initiator name success case", + add_initiator_name_success_case) == NULL + || CU_add_test(suite, "add initiator name fail case", + add_initiator_name_fail_case) == NULL + || CU_add_test(suite, "delete all initiator names success case", + delete_all_initiator_names_success_case) == NULL + || CU_add_test(suite, "add initiator netmask success case", + add_netmask_success_case) == NULL + || CU_add_test(suite, "add initiator netmask fail case", + add_netmask_fail_case) == NULL + || CU_add_test(suite, "delete all initiator netmasks success case", + delete_all_netmasks_success_case) == NULL + || CU_add_test(suite, "overwrite all to any for name case", + initiator_name_overwrite_all_to_any_case) == NULL + || CU_add_test(suite, "overwrite all to any for netmask case", + netmask_overwrite_all_to_any_case) == NULL + || CU_add_test(suite, "add/delete initiator names case", + add_delete_initiator_names_case) == NULL + || CU_add_test(suite, "add duplicated initiator names case", + add_duplicated_initiator_names_case) == NULL + || CU_add_test(suite, "delete nonexisting initiator names case", + delete_nonexisting_initiator_names_case) == NULL + || CU_add_test(suite, "add/delete netmasks case", + add_delete_netmasks_case) == NULL + || CU_add_test(suite, "add duplicated netmasks case", + add_duplicated_netmasks_case) == NULL + || CU_add_test(suite, "delete nonexisting netmasks case", + delete_nonexisting_netmasks_case) == NULL + ) { + CU_cleanup_registry(); + return CU_get_error(); + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + return num_failures; +} diff --git a/src/spdk/test/unit/lib/iscsi/iscsi.c/.gitignore b/src/spdk/test/unit/lib/iscsi/iscsi.c/.gitignore new file mode 100644 index 00000000..4d41887c --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/iscsi.c/.gitignore @@ -0,0 +1 @@ +iscsi_ut diff --git a/src/spdk/test/unit/lib/iscsi/iscsi.c/Makefile b/src/spdk/test/unit/lib/iscsi/iscsi.c/Makefile new file mode 100644 index 00000000..bc9a9d8b --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/iscsi.c/Makefile @@ -0,0 +1,48 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk +include $(SPDK_ROOT_DIR)/mk/spdk.app.mk + +SPDK_LIB_LIST = trace conf util + +SCSI_OBJS = port +ISCSI_OBJS = md5 param +LIBS += $(SCSI_OBJS:%=$(SPDK_ROOT_DIR)/lib/scsi/%.o) +LIBS += $(ISCSI_OBJS:%=$(SPDK_ROOT_DIR)/lib/iscsi/%.o) +LIBS += -lcunit $(ENV_LINKER_ARGS) + +TEST_FILE = iscsi_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/src/spdk/test/unit/lib/iscsi/iscsi.c/iscsi_ut.c b/src/spdk/test/unit/lib/iscsi/iscsi.c/iscsi_ut.c new file mode 100644 index 00000000..4038a1e4 --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/iscsi.c/iscsi_ut.c @@ -0,0 +1,972 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/stdinc.h" + +#include "spdk/endian.h" +#include "spdk/scsi.h" +#include "spdk_cunit.h" + +#include "CUnit/Basic.h" + +#include "iscsi/iscsi.c" + +#include "../common.c" +#include "iscsi/acceptor.h" +#include "iscsi/portal_grp.h" +#include "scsi/scsi_internal.h" + +#define UT_TARGET_NAME1 "iqn.2017-11.spdk.io:t0001" +#define UT_TARGET_NAME2 "iqn.2017-11.spdk.io:t0002" +#define UT_INITIATOR_NAME1 "iqn.2017-11.spdk.io:i0001" +#define UT_INITIATOR_NAME2 "iqn.2017-11.spdk.io:i0002" + +struct spdk_iscsi_tgt_node * +spdk_iscsi_find_tgt_node(const char *target_name) +{ + if (strcasecmp(target_name, UT_TARGET_NAME1) == 0) { + return (struct spdk_iscsi_tgt_node *)1; + } else { + return NULL; + } +} + +bool +spdk_iscsi_tgt_node_access(struct spdk_iscsi_conn *conn, + struct spdk_iscsi_tgt_node *target, + const char *iqn, const char *addr) +{ + if (strcasecmp(conn->initiator_name, UT_INITIATOR_NAME1) == 0) { + return true; + } else { + return false; + } +} + +int +spdk_iscsi_send_tgts(struct spdk_iscsi_conn *conn, const char *iiqn, + const char *iaddr, + const char *tiqn, uint8_t *data, int alloc_len, int data_len) +{ + return 0; +} + +void +spdk_iscsi_portal_grp_close_all(void) +{ +} + +void +spdk_iscsi_conn_migration(struct spdk_iscsi_conn *conn) +{ +} + +void +spdk_iscsi_conn_free_pdu(struct spdk_iscsi_conn *conn, struct spdk_iscsi_pdu *pdu) +{ +} + +int +spdk_iscsi_chap_get_authinfo(struct iscsi_chap_auth *auth, const char *authuser, + int ag_tag) +{ + return 0; +} + +int +spdk_scsi_lun_get_id(const struct spdk_scsi_lun *lun) +{ + return lun->id; +} + +bool +spdk_scsi_lun_is_removing(const struct spdk_scsi_lun *lun) +{ + return true; +} + +struct spdk_scsi_lun * +spdk_scsi_dev_get_lun(struct spdk_scsi_dev *dev, int lun_id) +{ + if (lun_id < 0 || lun_id >= SPDK_SCSI_DEV_MAX_LUN) { + return NULL; + } + + return dev->lun[lun_id]; +} + +static void +op_login_check_target_test(void) +{ + struct spdk_iscsi_conn conn; + struct spdk_iscsi_pdu rsp_pdu; + struct spdk_iscsi_tgt_node *target; + int rc; + + /* expect success */ + snprintf(conn.initiator_name, sizeof(conn.initiator_name), + "%s", UT_INITIATOR_NAME1); + + rc = spdk_iscsi_op_login_check_target(&conn, &rsp_pdu, + UT_TARGET_NAME1, &target); + CU_ASSERT(rc == 0); + + /* expect failure */ + snprintf(conn.initiator_name, sizeof(conn.initiator_name), + "%s", UT_INITIATOR_NAME1); + + rc = spdk_iscsi_op_login_check_target(&conn, &rsp_pdu, + UT_TARGET_NAME2, &target); + CU_ASSERT(rc != 0); + + /* expect failure */ + snprintf(conn.initiator_name, sizeof(conn.initiator_name), + "%s", UT_INITIATOR_NAME2); + + rc = spdk_iscsi_op_login_check_target(&conn, &rsp_pdu, + UT_TARGET_NAME1, &target); + CU_ASSERT(rc != 0); +} + +static void +maxburstlength_test(void) +{ + struct spdk_iscsi_sess sess; + struct spdk_iscsi_conn conn; + struct spdk_scsi_dev dev; + struct spdk_scsi_lun lun; + struct spdk_iscsi_pdu *req_pdu, *data_out_pdu, *r2t_pdu; + struct iscsi_bhs_scsi_req *req; + struct iscsi_bhs_r2t *r2t; + struct iscsi_bhs_data_out *data_out; + struct spdk_iscsi_pdu *response_pdu; + int rc; + + memset(&sess, 0, sizeof(sess)); + memset(&conn, 0, sizeof(conn)); + memset(&dev, 0, sizeof(dev)); + memset(&lun, 0, sizeof(lun)); + + req_pdu = spdk_get_pdu(); + data_out_pdu = spdk_get_pdu(); + + sess.ExpCmdSN = 0; + sess.MaxCmdSN = 64; + sess.session_type = SESSION_TYPE_NORMAL; + sess.MaxBurstLength = 1024; + + lun.id = 0; + + dev.lun[0] = &lun; + + conn.full_feature = 1; + conn.sess = &sess; + conn.dev = &dev; + conn.state = ISCSI_CONN_STATE_RUNNING; + TAILQ_INIT(&conn.write_pdu_list); + TAILQ_INIT(&conn.active_r2t_tasks); + + TAILQ_INIT(&g_write_pdu_list); + + req_pdu->bhs.opcode = ISCSI_OP_SCSI; + req_pdu->data_segment_len = 0; + + req = (struct iscsi_bhs_scsi_req *)&req_pdu->bhs; + + to_be32(&req->cmd_sn, 0); + to_be32(&req->expected_data_xfer_len, 1028); + to_be32(&req->itt, 0x1234); + req->write_bit = 1; + req->final_bit = 1; + + rc = spdk_iscsi_execute(&conn, req_pdu); + CU_ASSERT(rc == 0); + + response_pdu = TAILQ_FIRST(&g_write_pdu_list); + SPDK_CU_ASSERT_FATAL(response_pdu != NULL); + + /* + * Confirm that a correct R2T reply was sent in response to the + * SCSI request. + */ + TAILQ_REMOVE(&g_write_pdu_list, response_pdu, tailq); + CU_ASSERT(response_pdu->bhs.opcode == ISCSI_OP_R2T); + r2t = (struct iscsi_bhs_r2t *)&response_pdu->bhs; + CU_ASSERT(from_be32(&r2t->desired_xfer_len) == 1024); + CU_ASSERT(from_be32(&r2t->buffer_offset) == 0); + CU_ASSERT(from_be32(&r2t->itt) == 0x1234); + + data_out_pdu->bhs.opcode = ISCSI_OP_SCSI_DATAOUT; + data_out_pdu->bhs.flags = ISCSI_FLAG_FINAL; + data_out_pdu->data_segment_len = 1028; + data_out = (struct iscsi_bhs_data_out *)&data_out_pdu->bhs; + data_out->itt = r2t->itt; + data_out->ttt = r2t->ttt; + DSET24(data_out->data_segment_len, 1028); + + rc = spdk_iscsi_execute(&conn, data_out_pdu); + CU_ASSERT(rc == SPDK_ISCSI_CONNECTION_FATAL); + + SPDK_CU_ASSERT_FATAL(response_pdu->task != NULL); + spdk_iscsi_task_disassociate_pdu(response_pdu->task); + spdk_iscsi_task_put(response_pdu->task); + spdk_put_pdu(response_pdu); + + r2t_pdu = TAILQ_FIRST(&g_write_pdu_list); + CU_ASSERT(r2t_pdu != NULL); + TAILQ_REMOVE(&g_write_pdu_list, r2t_pdu, tailq); + spdk_put_pdu(r2t_pdu); + + spdk_put_pdu(data_out_pdu); + spdk_put_pdu(req_pdu); +} + +static void +underflow_for_read_transfer_test(void) +{ + struct spdk_iscsi_sess sess; + struct spdk_iscsi_conn conn; + struct spdk_iscsi_task task; + struct spdk_iscsi_pdu *pdu; + struct iscsi_bhs_scsi_req *scsi_req; + struct iscsi_bhs_data_in *datah; + uint32_t residual_count = 0; + + TAILQ_INIT(&g_write_pdu_list); + + memset(&sess, 0, sizeof(sess)); + memset(&conn, 0, sizeof(conn)); + memset(&task, 0, sizeof(task)); + + sess.MaxBurstLength = SPDK_ISCSI_MAX_BURST_LENGTH; + + conn.sess = &sess; + conn.MaxRecvDataSegmentLength = 8192; + + pdu = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu != NULL); + + scsi_req = (struct iscsi_bhs_scsi_req *)&pdu->bhs; + scsi_req->read_bit = 1; + + spdk_iscsi_task_set_pdu(&task, pdu); + task.parent = NULL; + + task.scsi.iovs = &task.scsi.iov; + task.scsi.iovcnt = 1; + task.scsi.length = 512; + task.scsi.transfer_len = 512; + task.bytes_completed = 512; + task.scsi.data_transferred = 256; + task.scsi.status = SPDK_SCSI_STATUS_GOOD; + + spdk_iscsi_task_response(&conn, &task); + spdk_put_pdu(pdu); + + /* + * In this case, a SCSI Data-In PDU should contain the Status + * for the data transfer. + */ + to_be32(&residual_count, 256); + + pdu = TAILQ_FIRST(&g_write_pdu_list); + SPDK_CU_ASSERT_FATAL(pdu != NULL); + + CU_ASSERT(pdu->bhs.opcode == ISCSI_OP_SCSI_DATAIN); + + datah = (struct iscsi_bhs_data_in *)&pdu->bhs; + + CU_ASSERT(datah->flags == (ISCSI_DATAIN_UNDERFLOW | ISCSI_FLAG_FINAL | ISCSI_DATAIN_STATUS)); + CU_ASSERT(datah->res_cnt == residual_count); + + TAILQ_REMOVE(&g_write_pdu_list, pdu, tailq); + spdk_put_pdu(pdu); + + CU_ASSERT(TAILQ_EMPTY(&g_write_pdu_list)); +} + +static void +underflow_for_zero_read_transfer_test(void) +{ + struct spdk_iscsi_sess sess; + struct spdk_iscsi_conn conn; + struct spdk_iscsi_task task; + struct spdk_iscsi_pdu *pdu; + struct iscsi_bhs_scsi_req *scsi_req; + struct iscsi_bhs_scsi_resp *resph; + uint32_t residual_count = 0, data_segment_len; + + TAILQ_INIT(&g_write_pdu_list); + + memset(&sess, 0, sizeof(sess)); + memset(&conn, 0, sizeof(conn)); + memset(&task, 0, sizeof(task)); + + sess.MaxBurstLength = SPDK_ISCSI_MAX_BURST_LENGTH; + + conn.sess = &sess; + conn.MaxRecvDataSegmentLength = 8192; + + pdu = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu != NULL); + + scsi_req = (struct iscsi_bhs_scsi_req *)&pdu->bhs; + scsi_req->read_bit = 1; + + spdk_iscsi_task_set_pdu(&task, pdu); + task.parent = NULL; + + task.scsi.length = 512; + task.scsi.transfer_len = 512; + task.bytes_completed = 512; + task.scsi.data_transferred = 0; + task.scsi.status = SPDK_SCSI_STATUS_GOOD; + + spdk_iscsi_task_response(&conn, &task); + spdk_put_pdu(pdu); + + /* + * In this case, only a SCSI Response PDU is expected and + * underflow must be set in it. + * */ + to_be32(&residual_count, 512); + + pdu = TAILQ_FIRST(&g_write_pdu_list); + SPDK_CU_ASSERT_FATAL(pdu != NULL); + + CU_ASSERT(pdu->bhs.opcode == ISCSI_OP_SCSI_RSP); + + resph = (struct iscsi_bhs_scsi_resp *)&pdu->bhs; + + CU_ASSERT(resph->flags == (ISCSI_SCSI_UNDERFLOW | 0x80)); + + data_segment_len = DGET24(resph->data_segment_len); + CU_ASSERT(data_segment_len == 0); + CU_ASSERT(resph->res_cnt == residual_count); + + TAILQ_REMOVE(&g_write_pdu_list, pdu, tailq); + spdk_put_pdu(pdu); + + CU_ASSERT(TAILQ_EMPTY(&g_write_pdu_list)); +} + +static void +underflow_for_request_sense_test(void) +{ + struct spdk_iscsi_sess sess; + struct spdk_iscsi_conn conn; + struct spdk_iscsi_task task; + struct spdk_iscsi_pdu *pdu1, *pdu2; + struct iscsi_bhs_scsi_req *scsi_req; + struct iscsi_bhs_data_in *datah; + struct iscsi_bhs_scsi_resp *resph; + uint32_t residual_count = 0, data_segment_len; + + TAILQ_INIT(&g_write_pdu_list); + + memset(&sess, 0, sizeof(sess)); + memset(&conn, 0, sizeof(conn)); + memset(&task, 0, sizeof(task)); + + sess.MaxBurstLength = SPDK_ISCSI_MAX_BURST_LENGTH; + + conn.sess = &sess; + conn.MaxRecvDataSegmentLength = 8192; + + pdu1 = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu1 != NULL); + + scsi_req = (struct iscsi_bhs_scsi_req *)&pdu1->bhs; + scsi_req->read_bit = 1; + + spdk_iscsi_task_set_pdu(&task, pdu1); + task.parent = NULL; + + task.scsi.iovs = &task.scsi.iov; + task.scsi.iovcnt = 1; + task.scsi.length = 512; + task.scsi.transfer_len = 512; + task.bytes_completed = 512; + + task.scsi.sense_data_len = 18; + task.scsi.data_transferred = 18; + task.scsi.status = SPDK_SCSI_STATUS_GOOD; + + spdk_iscsi_task_response(&conn, &task); + spdk_put_pdu(pdu1); + + /* + * In this case, a SCSI Data-In PDU and a SCSI Response PDU are returned. + * Sense data are set both in payload and sense area. + * The SCSI Data-In PDU sets FINAL and the SCSI Response PDU sets UNDERFLOW. + * + * Probably there will be different implementation but keeping current SPDK + * implementation by adding UT will be valuable for any implementation. + */ + to_be32(&residual_count, 494); + + pdu1 = TAILQ_FIRST(&g_write_pdu_list); + SPDK_CU_ASSERT_FATAL(pdu1 != NULL); + + CU_ASSERT(pdu1->bhs.opcode == ISCSI_OP_SCSI_DATAIN); + + datah = (struct iscsi_bhs_data_in *)&pdu1->bhs; + + CU_ASSERT(datah->flags == ISCSI_FLAG_FINAL); + + data_segment_len = DGET24(datah->data_segment_len); + CU_ASSERT(data_segment_len == 18); + CU_ASSERT(datah->res_cnt == 0); + + TAILQ_REMOVE(&g_write_pdu_list, pdu1, tailq); + spdk_put_pdu(pdu1); + + pdu2 = TAILQ_FIRST(&g_write_pdu_list); + /* inform scan-build (clang 6) that these pointers are not the same */ + SPDK_CU_ASSERT_FATAL(pdu1 != pdu2); + SPDK_CU_ASSERT_FATAL(pdu2 != NULL); + + CU_ASSERT(pdu2->bhs.opcode == ISCSI_OP_SCSI_RSP); + + resph = (struct iscsi_bhs_scsi_resp *)&pdu2->bhs; + + CU_ASSERT(resph->flags == (ISCSI_SCSI_UNDERFLOW | 0x80)); + + data_segment_len = DGET24(resph->data_segment_len); + CU_ASSERT(data_segment_len == task.scsi.sense_data_len + 2); + CU_ASSERT(resph->res_cnt == residual_count); + + TAILQ_REMOVE(&g_write_pdu_list, pdu2, tailq); + spdk_put_pdu(pdu2); + + CU_ASSERT(TAILQ_EMPTY(&g_write_pdu_list)); +} + +static void +underflow_for_check_condition_test(void) +{ + struct spdk_iscsi_sess sess; + struct spdk_iscsi_conn conn; + struct spdk_iscsi_task task; + struct spdk_iscsi_pdu *pdu; + struct iscsi_bhs_scsi_req *scsi_req; + struct iscsi_bhs_scsi_resp *resph; + uint32_t data_segment_len; + + TAILQ_INIT(&g_write_pdu_list); + + memset(&sess, 0, sizeof(sess)); + memset(&conn, 0, sizeof(conn)); + memset(&task, 0, sizeof(task)); + + sess.MaxBurstLength = SPDK_ISCSI_MAX_BURST_LENGTH; + + conn.sess = &sess; + conn.MaxRecvDataSegmentLength = 8192; + + pdu = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu != NULL); + + scsi_req = (struct iscsi_bhs_scsi_req *)&pdu->bhs; + scsi_req->read_bit = 1; + + spdk_iscsi_task_set_pdu(&task, pdu); + task.parent = NULL; + + task.scsi.iovs = &task.scsi.iov; + task.scsi.iovcnt = 1; + task.scsi.length = 512; + task.scsi.transfer_len = 512; + task.bytes_completed = 512; + + task.scsi.sense_data_len = 18; + task.scsi.data_transferred = 18; + task.scsi.status = SPDK_SCSI_STATUS_CHECK_CONDITION; + + spdk_iscsi_task_response(&conn, &task); + spdk_put_pdu(pdu); + + /* + * In this case, a SCSI Response PDU is returned. + * Sense data is set in sense area. + * Underflow is not set. + */ + pdu = TAILQ_FIRST(&g_write_pdu_list); + SPDK_CU_ASSERT_FATAL(pdu != NULL); + + CU_ASSERT(pdu->bhs.opcode == ISCSI_OP_SCSI_RSP); + + resph = (struct iscsi_bhs_scsi_resp *)&pdu->bhs; + + CU_ASSERT(resph->flags == 0x80); + + data_segment_len = DGET24(resph->data_segment_len); + CU_ASSERT(data_segment_len == task.scsi.sense_data_len + 2); + CU_ASSERT(resph->res_cnt == 0); + + TAILQ_REMOVE(&g_write_pdu_list, pdu, tailq); + spdk_put_pdu(pdu); + + CU_ASSERT(TAILQ_EMPTY(&g_write_pdu_list)); +} + +static void +add_transfer_task_test(void) +{ + struct spdk_iscsi_sess sess; + struct spdk_iscsi_conn conn; + struct spdk_iscsi_task task; + struct spdk_iscsi_pdu *pdu, *tmp; + struct iscsi_bhs_r2t *r2th; + int rc, count = 0; + uint32_t buffer_offset, desired_xfer_len; + + memset(&sess, 0, sizeof(sess)); + memset(&conn, 0, sizeof(conn)); + memset(&task, 0, sizeof(task)); + + sess.MaxBurstLength = SPDK_ISCSI_MAX_BURST_LENGTH; /* 1M */ + sess.MaxOutstandingR2T = DEFAULT_MAXR2T; /* 4 */ + + conn.sess = &sess; + TAILQ_INIT(&conn.queued_r2t_tasks); + TAILQ_INIT(&conn.active_r2t_tasks); + + pdu = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu != NULL); + + pdu->data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; /* 64K */ + task.scsi.transfer_len = 16 * 1024 * 1024; + spdk_iscsi_task_set_pdu(&task, pdu); + + /* The following tests if the task is queued because R2T tasks are full. */ + conn.pending_r2t = DEFAULT_MAXR2T; + + rc = spdk_add_transfer_task(&conn, &task); + + CU_ASSERT(rc == SPDK_SUCCESS); + CU_ASSERT(TAILQ_FIRST(&conn.queued_r2t_tasks) == &task); + + TAILQ_REMOVE(&conn.queued_r2t_tasks, &task, link); + CU_ASSERT(TAILQ_EMPTY(&conn.queued_r2t_tasks)); + + /* The following tests if multiple R2Ts are issued. */ + conn.pending_r2t = 0; + + rc = spdk_add_transfer_task(&conn, &task); + + CU_ASSERT(rc == SPDK_SUCCESS); + CU_ASSERT(TAILQ_FIRST(&conn.active_r2t_tasks) == &task); + + TAILQ_REMOVE(&conn.active_r2t_tasks, &task, link); + CU_ASSERT(TAILQ_EMPTY(&conn.active_r2t_tasks)); + + CU_ASSERT(conn.data_out_cnt == 255); + CU_ASSERT(conn.pending_r2t == 1); + CU_ASSERT(conn.outstanding_r2t_tasks[0] == &task); + CU_ASSERT(conn.ttt == 1); + + CU_ASSERT(task.data_out_cnt == 255); + CU_ASSERT(task.ttt == 1); + CU_ASSERT(task.outstanding_r2t == sess.MaxOutstandingR2T); + CU_ASSERT(task.next_r2t_offset == + pdu->data_segment_len + sess.MaxBurstLength * sess.MaxOutstandingR2T); + + + while (!TAILQ_EMPTY(&g_write_pdu_list)) { + tmp = TAILQ_FIRST(&g_write_pdu_list); + TAILQ_REMOVE(&g_write_pdu_list, tmp, tailq); + + r2th = (struct iscsi_bhs_r2t *)&tmp->bhs; + + buffer_offset = from_be32(&r2th->buffer_offset); + CU_ASSERT(buffer_offset == pdu->data_segment_len + sess.MaxBurstLength * count); + + desired_xfer_len = from_be32(&r2th->desired_xfer_len); + CU_ASSERT(desired_xfer_len == sess.MaxBurstLength); + + spdk_put_pdu(tmp); + count++; + } + + CU_ASSERT(count == DEFAULT_MAXR2T); + + spdk_put_pdu(pdu); +} + +static void +get_transfer_task_test(void) +{ + struct spdk_iscsi_sess sess; + struct spdk_iscsi_conn conn; + struct spdk_iscsi_task task1, task2, *task; + struct spdk_iscsi_pdu *pdu1, *pdu2, *pdu; + int rc; + + memset(&sess, 0, sizeof(sess)); + memset(&conn, 0, sizeof(conn)); + memset(&task1, 0, sizeof(task1)); + memset(&task2, 0, sizeof(task2)); + + sess.MaxBurstLength = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + sess.MaxOutstandingR2T = 1; + + conn.sess = &sess; + TAILQ_INIT(&conn.active_r2t_tasks); + + pdu1 = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu1 != NULL); + + pdu1->data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task1.scsi.transfer_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + spdk_iscsi_task_set_pdu(&task1, pdu1); + + rc = spdk_add_transfer_task(&conn, &task1); + CU_ASSERT(rc == SPDK_SUCCESS); + + pdu2 = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu2 != NULL); + + pdu2->data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task2.scsi.transfer_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + spdk_iscsi_task_set_pdu(&task2, pdu2); + + rc = spdk_add_transfer_task(&conn, &task2); + CU_ASSERT(rc == SPDK_SUCCESS); + + task = spdk_get_transfer_task(&conn, 1); + CU_ASSERT(task == &task1); + + task = spdk_get_transfer_task(&conn, 2); + CU_ASSERT(task == &task2); + + while (!TAILQ_EMPTY(&conn.active_r2t_tasks)) { + task = TAILQ_FIRST(&conn.active_r2t_tasks); + TAILQ_REMOVE(&conn.active_r2t_tasks, task, link); + } + + while (!TAILQ_EMPTY(&g_write_pdu_list)) { + pdu = TAILQ_FIRST(&g_write_pdu_list); + TAILQ_REMOVE(&g_write_pdu_list, pdu, tailq); + spdk_put_pdu(pdu); + } + + spdk_put_pdu(pdu2); + spdk_put_pdu(pdu1); +} + +static void +del_transfer_task_test(void) +{ + struct spdk_iscsi_sess sess; + struct spdk_iscsi_conn conn; + struct spdk_iscsi_task task1, task2, task3, task4, task5, *task; + struct spdk_iscsi_pdu *pdu1, *pdu2, *pdu3, *pdu4, *pdu5, *pdu; + int rc; + + memset(&sess, 0, sizeof(sess)); + memset(&conn, 0, sizeof(conn)); + memset(&task1, 0, sizeof(task1)); + memset(&task2, 0, sizeof(task2)); + memset(&task3, 0, sizeof(task3)); + memset(&task4, 0, sizeof(task4)); + memset(&task5, 0, sizeof(task5)); + + sess.MaxBurstLength = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + sess.MaxOutstandingR2T = 1; + + conn.sess = &sess; + TAILQ_INIT(&conn.active_r2t_tasks); + TAILQ_INIT(&conn.queued_r2t_tasks); + + pdu1 = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu1 != NULL); + + pdu1->data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task1.scsi.transfer_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + spdk_iscsi_task_set_pdu(&task1, pdu1); + task1.tag = 11; + + rc = spdk_add_transfer_task(&conn, &task1); + CU_ASSERT(rc == SPDK_SUCCESS); + + pdu2 = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu2 != NULL); + + pdu2->data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task2.scsi.transfer_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + spdk_iscsi_task_set_pdu(&task2, pdu2); + task2.tag = 12; + + rc = spdk_add_transfer_task(&conn, &task2); + CU_ASSERT(rc == SPDK_SUCCESS); + + pdu3 = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu3 != NULL); + + pdu3->data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task3.scsi.transfer_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + spdk_iscsi_task_set_pdu(&task3, pdu3); + task3.tag = 13; + + rc = spdk_add_transfer_task(&conn, &task3); + CU_ASSERT(rc == SPDK_SUCCESS); + + pdu4 = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu4 != NULL); + + pdu4->data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task4.scsi.transfer_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + spdk_iscsi_task_set_pdu(&task4, pdu4); + task4.tag = 14; + + rc = spdk_add_transfer_task(&conn, &task4); + CU_ASSERT(rc == SPDK_SUCCESS); + + pdu5 = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu5 != NULL); + + pdu5->data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task5.scsi.transfer_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + spdk_iscsi_task_set_pdu(&task5, pdu5); + task5.tag = 15; + + rc = spdk_add_transfer_task(&conn, &task5); + CU_ASSERT(rc == SPDK_SUCCESS); + + CU_ASSERT(spdk_get_transfer_task(&conn, 1) == &task1); + CU_ASSERT(spdk_get_transfer_task(&conn, 5) == NULL); + spdk_del_transfer_task(&conn, 11); + CU_ASSERT(spdk_get_transfer_task(&conn, 1) == NULL); + CU_ASSERT(spdk_get_transfer_task(&conn, 5) == &task5); + + CU_ASSERT(spdk_get_transfer_task(&conn, 2) == &task2); + spdk_del_transfer_task(&conn, 12); + CU_ASSERT(spdk_get_transfer_task(&conn, 2) == NULL); + + CU_ASSERT(spdk_get_transfer_task(&conn, 3) == &task3); + spdk_del_transfer_task(&conn, 13); + CU_ASSERT(spdk_get_transfer_task(&conn, 3) == NULL); + + CU_ASSERT(spdk_get_transfer_task(&conn, 4) == &task4); + spdk_del_transfer_task(&conn, 14); + CU_ASSERT(spdk_get_transfer_task(&conn, 4) == NULL); + + CU_ASSERT(spdk_get_transfer_task(&conn, 5) == &task5); + spdk_del_transfer_task(&conn, 15); + CU_ASSERT(spdk_get_transfer_task(&conn, 5) == NULL); + + while (!TAILQ_EMPTY(&conn.active_r2t_tasks)) { + task = TAILQ_FIRST(&conn.active_r2t_tasks); + TAILQ_REMOVE(&conn.active_r2t_tasks, task, link); + } + + while (!TAILQ_EMPTY(&g_write_pdu_list)) { + pdu = TAILQ_FIRST(&g_write_pdu_list); + TAILQ_REMOVE(&g_write_pdu_list, pdu, tailq); + spdk_put_pdu(pdu); + } + + spdk_put_pdu(pdu5); + spdk_put_pdu(pdu4); + spdk_put_pdu(pdu3); + spdk_put_pdu(pdu2); + spdk_put_pdu(pdu1); +} + +static void +clear_all_transfer_tasks_test(void) +{ + struct spdk_iscsi_sess sess; + struct spdk_iscsi_conn conn; + struct spdk_iscsi_task *task1, *task2, *task3, *task4, *task5; + struct spdk_iscsi_pdu *pdu1, *pdu2, *pdu3, *pdu4, *pdu5, *pdu; + struct spdk_scsi_lun lun1, lun2; + int rc; + + memset(&sess, 0, sizeof(sess)); + memset(&conn, 0, sizeof(conn)); + memset(&lun1, 0, sizeof(lun1)); + memset(&lun2, 0, sizeof(lun2)); + + sess.MaxBurstLength = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + sess.MaxOutstandingR2T = 1; + + conn.sess = &sess; + TAILQ_INIT(&conn.active_r2t_tasks); + TAILQ_INIT(&conn.queued_r2t_tasks); + + task1 = spdk_iscsi_task_get(&conn, NULL, NULL); + SPDK_CU_ASSERT_FATAL(task1 != NULL); + pdu1 = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu1 != NULL); + + pdu1->data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task1->scsi.transfer_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task1->scsi.lun = &lun1; + spdk_iscsi_task_set_pdu(task1, pdu1); + + rc = spdk_add_transfer_task(&conn, task1); + CU_ASSERT(rc == SPDK_SUCCESS); + + task2 = spdk_iscsi_task_get(&conn, NULL, NULL); + SPDK_CU_ASSERT_FATAL(task2 != NULL); + pdu2 = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu2 != NULL); + + pdu2->data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task2->scsi.transfer_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task2->scsi.lun = &lun1; + spdk_iscsi_task_set_pdu(task2, pdu2); + + rc = spdk_add_transfer_task(&conn, task2); + CU_ASSERT(rc == SPDK_SUCCESS); + + task3 = spdk_iscsi_task_get(&conn, NULL, NULL); + SPDK_CU_ASSERT_FATAL(task3 != NULL); + pdu3 = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu3 != NULL); + + pdu3->data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task3->scsi.transfer_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task3->scsi.lun = &lun1; + spdk_iscsi_task_set_pdu(task3, pdu3); + + rc = spdk_add_transfer_task(&conn, task3); + CU_ASSERT(rc == SPDK_SUCCESS); + + task4 = spdk_iscsi_task_get(&conn, NULL, NULL); + SPDK_CU_ASSERT_FATAL(task4 != NULL); + pdu4 = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu4 != NULL); + + pdu4->data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task4->scsi.transfer_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task4->scsi.lun = &lun2; + spdk_iscsi_task_set_pdu(task4, pdu4); + + rc = spdk_add_transfer_task(&conn, task4); + CU_ASSERT(rc == SPDK_SUCCESS); + + task5 = spdk_iscsi_task_get(&conn, NULL, NULL); + SPDK_CU_ASSERT_FATAL(task5 != NULL); + pdu5 = spdk_get_pdu(); + SPDK_CU_ASSERT_FATAL(pdu5 != NULL); + + pdu5->data_segment_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task5->scsi.transfer_len = SPDK_ISCSI_MAX_RECV_DATA_SEGMENT_LENGTH; + task5->scsi.lun = &lun2; + spdk_iscsi_task_set_pdu(task5, pdu5); + + rc = spdk_add_transfer_task(&conn, task5); + CU_ASSERT(rc == SPDK_SUCCESS); + + CU_ASSERT(conn.ttt == 4); + + CU_ASSERT(spdk_get_transfer_task(&conn, 1) == task1); + CU_ASSERT(spdk_get_transfer_task(&conn, 2) == task2); + CU_ASSERT(spdk_get_transfer_task(&conn, 3) == task3); + CU_ASSERT(spdk_get_transfer_task(&conn, 4) == task4); + CU_ASSERT(spdk_get_transfer_task(&conn, 5) == NULL); + + spdk_clear_all_transfer_task(&conn, &lun1); + + CU_ASSERT(TAILQ_EMPTY(&conn.queued_r2t_tasks)); + CU_ASSERT(spdk_get_transfer_task(&conn, 1) == NULL); + CU_ASSERT(spdk_get_transfer_task(&conn, 2) == NULL); + CU_ASSERT(spdk_get_transfer_task(&conn, 3) == NULL); + CU_ASSERT(spdk_get_transfer_task(&conn, 4) == task4); + CU_ASSERT(spdk_get_transfer_task(&conn, 5) == task5); + + spdk_clear_all_transfer_task(&conn, NULL); + + CU_ASSERT(spdk_get_transfer_task(&conn, 4) == NULL); + CU_ASSERT(spdk_get_transfer_task(&conn, 5) == NULL); + + CU_ASSERT(TAILQ_EMPTY(&conn.active_r2t_tasks)); + while (!TAILQ_EMPTY(&g_write_pdu_list)) { + pdu = TAILQ_FIRST(&g_write_pdu_list); + TAILQ_REMOVE(&g_write_pdu_list, pdu, tailq); + spdk_put_pdu(pdu); + } + + spdk_put_pdu(pdu5); + spdk_put_pdu(pdu4); + spdk_put_pdu(pdu3); + spdk_put_pdu(pdu2); + spdk_put_pdu(pdu1); +} + +int +main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + if (CU_initialize_registry() != CUE_SUCCESS) { + return CU_get_error(); + } + + suite = CU_add_suite("iscsi_suite", NULL, NULL); + if (suite == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if ( + CU_add_test(suite, "login check target test", op_login_check_target_test) == NULL + || CU_add_test(suite, "maxburstlength test", maxburstlength_test) == NULL + || CU_add_test(suite, "underflow for read transfer test", + underflow_for_read_transfer_test) == NULL + || CU_add_test(suite, "underflow for zero read transfer test", + underflow_for_zero_read_transfer_test) == NULL + || CU_add_test(suite, "underflow for request sense test", + underflow_for_request_sense_test) == NULL + || CU_add_test(suite, "underflow for check condition test", + underflow_for_check_condition_test) == NULL + || CU_add_test(suite, "add transfer task test", add_transfer_task_test) == NULL + || CU_add_test(suite, "get transfer task test", get_transfer_task_test) == NULL + || CU_add_test(suite, "del transfer task test", del_transfer_task_test) == NULL + || CU_add_test(suite, "clear all transfer tasks test", + clear_all_transfer_tasks_test) == NULL + ) { + CU_cleanup_registry(); + return CU_get_error(); + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + return num_failures; +} diff --git a/src/spdk/test/unit/lib/iscsi/param.c/.gitignore b/src/spdk/test/unit/lib/iscsi/param.c/.gitignore new file mode 100644 index 00000000..26992146 --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/param.c/.gitignore @@ -0,0 +1 @@ +param_ut diff --git a/src/spdk/test/unit/lib/iscsi/param.c/Makefile b/src/spdk/test/unit/lib/iscsi/param.c/Makefile new file mode 100644 index 00000000..bc944ae9 --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/param.c/Makefile @@ -0,0 +1,40 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk +include $(SPDK_ROOT_DIR)/mk/spdk.app.mk + +TEST_FILE = param_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/src/spdk/test/unit/lib/iscsi/param.c/param_ut.c b/src/spdk/test/unit/lib/iscsi/param.c/param_ut.c new file mode 100644 index 00000000..40941923 --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/param.c/param_ut.c @@ -0,0 +1,397 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/stdinc.h" + +#include "spdk/scsi.h" + +#include "spdk_cunit.h" + +#include "../common.c" +#include "iscsi/param.c" + +struct spdk_iscsi_globals g_spdk_iscsi; + +struct spdk_iscsi_tgt_node * +spdk_iscsi_find_tgt_node(const char *target_name) +{ + return NULL; +} + +bool +spdk_iscsi_tgt_node_access(struct spdk_iscsi_conn *conn, + struct spdk_iscsi_tgt_node *target, + const char *iqn, const char *addr) +{ + return false; +} + +int +spdk_iscsi_send_tgts(struct spdk_iscsi_conn *conn, const char *iiqn, + const char *iaddr, + const char *tiqn, uint8_t *data, int alloc_len, int data_len) +{ + return 0; +} + +static void +burst_length_param_negotation(int FirstBurstLength, int MaxBurstLength, + int initialR2T) +{ + struct spdk_iscsi_sess sess; + struct spdk_iscsi_conn conn; + struct iscsi_param *params; + struct iscsi_param **params_p; + char data[8192]; + int rc; + int total, len; + + total = 0; + params = NULL; + params_p = ¶ms; + + memset(&sess, 0, sizeof(sess)); + memset(&conn, 0, sizeof(conn)); + memset(data, 0, 8192); + + sess.ExpCmdSN = 0; + sess.MaxCmdSN = 64; + sess.session_type = SESSION_TYPE_NORMAL; + sess.params = NULL; + sess.MaxBurstLength = 65536; + sess.InitialR2T = true; + sess.FirstBurstLength = SPDK_ISCSI_FIRST_BURST_LENGTH; + sess.MaxOutstandingR2T = 1; + + /* set default params */ + rc = spdk_iscsi_sess_params_init(&sess.params); + CU_ASSERT(rc == 0); + + rc = spdk_iscsi_param_set_int(sess.params, "FirstBurstLength", + sess.FirstBurstLength); + CU_ASSERT(rc == 0); + + rc = spdk_iscsi_param_set_int(sess.params, "MaxBurstLength", + sess.MaxBurstLength); + CU_ASSERT(rc == 0); + + rc = spdk_iscsi_param_set(sess.params, "InitialR2T", + sess.InitialR2T ? "Yes" : "No"); + CU_ASSERT(rc == 0); + + conn.full_feature = 1; + conn.sess = &sess; + conn.MaxRecvDataSegmentLength = 65536; + + rc = spdk_iscsi_conn_params_init(&conn.params); + CU_ASSERT(rc == 0); + + /* construct the data */ + len = snprintf(data + total, 8192 - total, "%s=%d", + "FirstBurstLength", FirstBurstLength); + total += len + 1; + + len = snprintf(data + total, 8192 - total, "%s=%d", + "MaxBurstLength", MaxBurstLength); + total += len + 1; + + len = snprintf(data + total, 8192 - total, "%s=%d", + "InitialR2T", initialR2T); + total += len + 1; + + /* add one extra NUL byte at the end to match real iSCSI params */ + total++; + + /* store incoming parameters */ + rc = spdk_iscsi_parse_params(params_p, data, total, false, NULL); + CU_ASSERT(rc == 0); + + /* negotiate parameters */ + rc = spdk_iscsi_negotiate_params(&conn, params_p, + data, 8192, rc); + CU_ASSERT(rc > 0); + + rc = spdk_iscsi_copy_param2var(&conn); + CU_ASSERT(rc == 0); + CU_ASSERT(conn.sess->FirstBurstLength <= SPDK_ISCSI_FIRST_BURST_LENGTH); + CU_ASSERT(conn.sess->FirstBurstLength <= conn.sess->MaxBurstLength); + CU_ASSERT(conn.sess->MaxBurstLength <= SPDK_ISCSI_MAX_BURST_LENGTH); + CU_ASSERT(conn.sess->MaxOutstandingR2T == 1); + + spdk_iscsi_param_free(sess.params); + spdk_iscsi_param_free(conn.params); + spdk_iscsi_param_free(*params_p); +} + +static void +param_negotiation_test(void) +{ + burst_length_param_negotation(8192, 16384, 0); + burst_length_param_negotation(8192, 16384, 1); + burst_length_param_negotation(8192, 1024, 1); + burst_length_param_negotation(8192, 1024, 0); + burst_length_param_negotation(512, 1024, 1); + burst_length_param_negotation(512, 1024, 0); +} + +static void +list_negotiation_test(void) +{ + int add_param_value = 0; + struct iscsi_param param = {}; + char *new_val; + char valid_list_buf[1024]; + char in_val_buf[1024]; + +#define TEST_LIST(valid_list, in_val, expected_result) \ + do { \ + snprintf(valid_list_buf, sizeof(valid_list_buf), "%s", valid_list); \ + snprintf(in_val_buf, sizeof(in_val_buf), "%s", in_val); \ + new_val = spdk_iscsi_negotiate_param_list(&add_param_value, ¶m, valid_list_buf, in_val_buf, NULL); \ + if (expected_result) { \ + SPDK_CU_ASSERT_FATAL(new_val != NULL); \ + CU_ASSERT_STRING_EQUAL(new_val, expected_result); \ + } \ + } while (0) + + TEST_LIST("None", "None", "None"); + TEST_LIST("CHAP,None", "None", "None"); + TEST_LIST("CHAP,None", "CHAP", "CHAP"); + TEST_LIST("KRB5,SRP,CHAP,None", "SRP,CHAP,None", "SRP"); + TEST_LIST("KRB5,SRP,CHAP,None", "CHAP,SRP,None", "CHAP"); + TEST_LIST("KRB5,SRP,CHAP,None", "SPKM1,SRP,CHAP,None", "SRP"); + TEST_LIST("KRB5,SRP,None", "CHAP,None", "None"); +} + +#define PARSE(strconst, partial_enabled, partial_text) \ + data = strconst; \ + len = sizeof(strconst); \ + rc = spdk_iscsi_parse_params(¶ms, data, len, partial_enabled, partial_text) + +#define EXPECT_VAL(key, expected_value) \ + { \ + const char *val = spdk_iscsi_param_get_val(params, key); \ + CU_ASSERT(val != NULL); \ + if (val != NULL) { \ + CU_ASSERT(strcmp(val, expected_value) == 0); \ + } \ + } + +#define EXPECT_NULL(key) \ + CU_ASSERT(spdk_iscsi_param_get_val(params, key) == NULL) + +static void +parse_valid_test(void) +{ + struct iscsi_param *params = NULL; + int rc; + char *data; + int len; + char *partial_parameter = NULL; + + /* simple test with a single key=value */ + PARSE("Abc=def\0", false, NULL); + CU_ASSERT(rc == 0); + EXPECT_VAL("Abc", "def"); + + /* multiple key=value pairs */ + PARSE("Aaa=bbbbbb\0Xyz=test\0", false, NULL); + CU_ASSERT(rc == 0); + EXPECT_VAL("Aaa", "bbbbbb"); + EXPECT_VAL("Xyz", "test"); + + /* value with embedded '=' */ + PARSE("A=b=c\0", false, NULL); + CU_ASSERT(rc == 0); + EXPECT_VAL("A", "b=c"); + + /* CHAP_C=AAAA.... with value length 8192 */ + len = strlen("CHAP_C=") + ISCSI_TEXT_MAX_VAL_LEN + 1/* null terminators */; + data = malloc(len); + SPDK_CU_ASSERT_FATAL(data != NULL); + memset(data, 'A', len); + memcpy(data, "CHAP_C", 6); + data[6] = '='; + data[len - 1] = '\0'; + rc = spdk_iscsi_parse_params(¶ms, data, len, false, NULL); + CU_ASSERT(rc == 0); + free(data); + + /* partial parameter: value is partial */ + PARSE("C=AAA\0D=B", true, &partial_parameter); + SPDK_CU_ASSERT_FATAL(partial_parameter != NULL); + CU_ASSERT_STRING_EQUAL(partial_parameter, "D=B"); + CU_ASSERT(rc == 0); + EXPECT_VAL("C", "AAA"); + EXPECT_NULL("D"); + PARSE("XXXX\0E=UUUU\0", false, &partial_parameter); + CU_ASSERT(rc == 0); + EXPECT_VAL("D", "BXXXX"); + EXPECT_VAL("E", "UUUU"); + CU_ASSERT_PTR_NULL(partial_parameter); + + /* partial parameter: key is partial */ + PARSE("IAMAFAK", true, &partial_parameter); + CU_ASSERT_STRING_EQUAL(partial_parameter, "IAMAFAK"); + CU_ASSERT(rc == 0); + EXPECT_NULL("IAMAFAK"); + PARSE("EDKEY=TTTT\0F=IIII", false, &partial_parameter); + CU_ASSERT(rc == 0); + EXPECT_VAL("IAMAFAKEDKEY", "TTTT"); + EXPECT_VAL("F", "IIII"); + CU_ASSERT_PTR_NULL(partial_parameter); + + /* Second partial parameter is the only parameter */ + PARSE("OOOO", true, &partial_parameter); + CU_ASSERT_STRING_EQUAL(partial_parameter, "OOOO"); + CU_ASSERT(rc == 0); + EXPECT_NULL("OOOO"); + PARSE("LL=MMMM", false, &partial_parameter); + CU_ASSERT(rc == 0); + EXPECT_VAL("OOOOLL", "MMMM"); + CU_ASSERT_PTR_NULL(partial_parameter); + + spdk_iscsi_param_free(params); +} + +static void +parse_invalid_test(void) +{ + struct iscsi_param *params = NULL; + int rc; + char *data; + int len; + + /* key without '=' */ + PARSE("Abc\0", false, NULL); + CU_ASSERT(rc != 0); + EXPECT_NULL("Abc"); + + /* multiple key=value pairs, one missing '=' */ + PARSE("Abc=def\0Xyz\0Www=test\0", false, NULL); + CU_ASSERT(rc != 0); + EXPECT_VAL("Abc", "def"); + EXPECT_NULL("Xyz"); + EXPECT_NULL("Www"); + + /* empty key */ + PARSE("=abcdef", false, NULL); + CU_ASSERT(rc != 0); + EXPECT_NULL(""); + + /* CHAP_C=AAAA.... with value length 8192 + 1 */ + len = strlen("CHAP_C=") + ISCSI_TEXT_MAX_VAL_LEN + 1 /* max value len + 1 */ + + 1 /* null terminators */; + data = malloc(len); + SPDK_CU_ASSERT_FATAL(data != NULL); + memset(data, 'A', len); + memcpy(data, "CHAP_C", 6); + data[6] = '='; + data[len - 1] = '\0'; + rc = spdk_iscsi_parse_params(¶ms, data, len, false, NULL); + free(data); + CU_ASSERT(rc != 0); + EXPECT_NULL("CHAP_C"); + + /* Test simple value, length of value bigger than 255 */ + len = strlen("A=") + ISCSI_TEXT_MAX_SIMPLE_VAL_LEN + 1 /* max simple value len + 1 */ + + 1 /* null terminators */; + data = malloc(len); + SPDK_CU_ASSERT_FATAL(data != NULL); + memset(data, 'A', len); + data[1] = '='; + data[len - 1] = '\0'; + rc = spdk_iscsi_parse_params(¶ms, data, len, false, NULL); + free(data); + CU_ASSERT(rc != 0); + EXPECT_NULL("A"); + + /* key length bigger than 63 */ + len = ISCSI_TEXT_MAX_KEY_LEN + 1 /* max key length + 1 */ + 1 /* = */ + 1 /* A */ + + 1 /* null terminators */; + data = malloc(len); + SPDK_CU_ASSERT_FATAL(data != NULL); + memset(data, 'A', len); + data[64] = '='; + data[len - 1] = '\0'; + rc = spdk_iscsi_parse_params(¶ms, data, len, false, NULL); + free(data); + CU_ASSERT(rc != 0); + EXPECT_NULL("A"); + + /* duplicated key */ + PARSE("B=BB", false, NULL); + CU_ASSERT(rc == 0); + PARSE("B=BBBB", false, NULL); + CU_ASSERT(rc != 0); + EXPECT_VAL("B", "BB"); + + spdk_iscsi_param_free(params); +} + +int +main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + if (CU_initialize_registry() != CUE_SUCCESS) { + return CU_get_error(); + } + + suite = CU_add_suite("iscsi_suite", NULL, NULL); + if (suite == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if ( + CU_add_test(suite, "param negotiation test", + param_negotiation_test) == NULL || + CU_add_test(suite, "list negotiation test", + list_negotiation_test) == NULL || + CU_add_test(suite, "parse valid test", + parse_valid_test) == NULL || + CU_add_test(suite, "parse invalid test", + parse_invalid_test) == NULL + ) { + CU_cleanup_registry(); + return CU_get_error(); + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + return num_failures; +} diff --git a/src/spdk/test/unit/lib/iscsi/portal_grp.c/.gitignore b/src/spdk/test/unit/lib/iscsi/portal_grp.c/.gitignore new file mode 100644 index 00000000..106ffebc --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/portal_grp.c/.gitignore @@ -0,0 +1 @@ +portal_grp_ut diff --git a/src/spdk/test/unit/lib/iscsi/portal_grp.c/Makefile b/src/spdk/test/unit/lib/iscsi/portal_grp.c/Makefile new file mode 100644 index 00000000..ab28cabb --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/portal_grp.c/Makefile @@ -0,0 +1,42 @@ +# +## BSD LICENSE +# +## Copyright (c) Intel Corporation. +# All rights reserved. +# # +# Redistribution and use in source and binary forms, with or without +# # modification, are permitted provided that the following conditions +# are met: +# # +# * Redistributions of source code must retain the above copyright +# # notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# # notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# # distribution. +# * Neither the name of Intel Corporation nor the names of its +# # contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# # +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# # + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk +include $(SPDK_ROOT_DIR)/mk/spdk.app.mk + +SPDK_LIB_LIST = conf + +TEST_FILE = portal_grp_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/src/spdk/test/unit/lib/iscsi/portal_grp.c/portal_grp_ut.c b/src/spdk/test/unit/lib/iscsi/portal_grp.c/portal_grp_ut.c new file mode 100644 index 00000000..77351f0a --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/portal_grp.c/portal_grp_ut.c @@ -0,0 +1,477 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/stdinc.h" +#include "spdk/event.h" + +#include "spdk_cunit.h" + +#include "../common.c" +#include "iscsi/portal_grp.c" +#include "unit/lib/json_mock.c" + +struct spdk_iscsi_globals g_spdk_iscsi; + +static int +test_setup(void) +{ + TAILQ_INIT(&g_spdk_iscsi.portal_head); + TAILQ_INIT(&g_spdk_iscsi.pg_head); + pthread_mutex_init(&g_spdk_iscsi.mutex, NULL); + return 0; +} + +static void +portal_create_ipv4_normal_case(void) +{ + struct spdk_iscsi_portal *p; + + const char *host = "192.168.2.0"; + const char *port = "3260"; + const char *cpumask = "1"; + + p = spdk_iscsi_portal_create(host, port, cpumask); + CU_ASSERT(p != NULL); + + spdk_iscsi_portal_destroy(p); + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); +} + +static void +portal_create_ipv6_normal_case(void) +{ + struct spdk_iscsi_portal *p; + + const char *host = "[2001:ad6:1234::]"; + const char *port = "3260"; + const char *cpumask = "1"; + + p = spdk_iscsi_portal_create(host, port, cpumask); + CU_ASSERT(p != NULL); + + spdk_iscsi_portal_destroy(p); + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); +} + +static void +portal_create_ipv4_wildcard_case(void) +{ + struct spdk_iscsi_portal *p; + + const char *host = "*"; + const char *port = "3260"; + const char *cpumask = "1"; + + p = spdk_iscsi_portal_create(host, port, cpumask); + CU_ASSERT(p != NULL); + + spdk_iscsi_portal_destroy(p); + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); +} + +static void +portal_create_ipv6_wildcard_case(void) +{ + struct spdk_iscsi_portal *p; + + const char *host = "[*]"; + const char *port = "3260"; + const char *cpumask = "1"; + + p = spdk_iscsi_portal_create(host, port, cpumask); + CU_ASSERT(p != NULL); + + spdk_iscsi_portal_destroy(p); + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); +} + +static void +portal_create_cpumask_null_case(void) +{ + struct spdk_iscsi_portal *p; + + const char *host = "192.168.2.0"; + const char *port = "3260"; + const char *cpumask = NULL; + + p = spdk_iscsi_portal_create(host, port, cpumask); + CU_ASSERT(p != NULL); + + spdk_iscsi_portal_destroy(p); + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); +} + +static void +portal_create_cpumask_no_bit_on_case(void) +{ + struct spdk_iscsi_portal *p; + + const char *host = "192.168.2.0"; + const char *port = "3260"; + const char *cpumask = "0"; + + p = spdk_iscsi_portal_create(host, port, cpumask); + CU_ASSERT(p == NULL); +} + +static void +portal_create_twice_case(void) +{ + struct spdk_iscsi_portal *p1, *p2; + + const char *host = "192.168.2.0"; + const char *port = "3260"; + const char *cpumask = "1"; + + p1 = spdk_iscsi_portal_create(host, port, cpumask); + CU_ASSERT(p1 != NULL); + + p2 = spdk_iscsi_portal_create(host, port, cpumask); + CU_ASSERT(p2 == NULL); + + spdk_iscsi_portal_destroy(p1); + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); +} + +static void +parse_portal_ipv4_normal_case(void) +{ + const char *string = "192.168.2.0:3260@1"; + const char *host_str = "192.168.2.0"; + const char *port_str = "3260"; + struct spdk_cpuset *cpumask_val; + struct spdk_iscsi_portal *p = NULL; + int rc; + + cpumask_val = spdk_cpuset_alloc(); + SPDK_CU_ASSERT_FATAL(cpumask_val != NULL); + + spdk_cpuset_set_cpu(cpumask_val, 0, true); + + rc = spdk_iscsi_parse_portal(string, &p, 0); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(p != NULL); + CU_ASSERT(strcmp(p->host, host_str) == 0); + CU_ASSERT(strcmp(p->port, port_str) == 0); + CU_ASSERT(spdk_cpuset_equal(p->cpumask, cpumask_val)); + + spdk_iscsi_portal_destroy(p); + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); + + spdk_cpuset_free(cpumask_val); +} + +static void +parse_portal_ipv6_normal_case(void) +{ + const char *string = "[2001:ad6:1234::]:3260@1"; + const char *host_str = "[2001:ad6:1234::]"; + const char *port_str = "3260"; + struct spdk_cpuset *cpumask_val; + struct spdk_iscsi_portal *p = NULL; + int rc; + + cpumask_val = spdk_cpuset_alloc(); + SPDK_CU_ASSERT_FATAL(cpumask_val != NULL); + + spdk_cpuset_set_cpu(cpumask_val, 0, true); + + rc = spdk_iscsi_parse_portal(string, &p, 0); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(p != NULL); + CU_ASSERT(strcmp(p->host, host_str) == 0); + CU_ASSERT(strcmp(p->port, port_str) == 0); + CU_ASSERT(spdk_cpuset_equal(p->cpumask, cpumask_val)); + + spdk_iscsi_portal_destroy(p); + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); + + spdk_cpuset_free(cpumask_val); +} + +static void +parse_portal_ipv4_skip_cpumask_case(void) +{ + const char *string = "192.168.2.0:3260"; + const char *host_str = "192.168.2.0"; + const char *port_str = "3260"; + struct spdk_cpuset *cpumask_val; + struct spdk_iscsi_portal *p = NULL; + int rc; + + cpumask_val = spdk_app_get_core_mask(); + + rc = spdk_iscsi_parse_portal(string, &p, 0); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(p != NULL); + CU_ASSERT(strcmp(p->host, host_str) == 0); + CU_ASSERT(strcmp(p->port, port_str) == 0); + CU_ASSERT(spdk_cpuset_equal(p->cpumask, cpumask_val)); + + spdk_iscsi_portal_destroy(p); + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); +} + +static void +parse_portal_ipv6_skip_cpumask_case(void) +{ + const char *string = "[2001:ad6:1234::]:3260"; + const char *host_str = "[2001:ad6:1234::]"; + const char *port_str = "3260"; + struct spdk_cpuset *cpumask_val; + struct spdk_iscsi_portal *p = NULL; + int rc; + + cpumask_val = spdk_app_get_core_mask(); + + rc = spdk_iscsi_parse_portal(string, &p, 0); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(p != NULL); + CU_ASSERT(strcmp(p->host, host_str) == 0); + CU_ASSERT(strcmp(p->port, port_str) == 0); + CU_ASSERT(spdk_cpuset_equal(p->cpumask, cpumask_val)); + + spdk_iscsi_portal_destroy(p); + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); +} + +static void +parse_portal_ipv4_skip_port_and_cpumask_case(void) +{ + const char *string = "192.168.2.0"; + const char *host_str = "192.168.2.0"; + const char *port_str = "3260"; + struct spdk_cpuset *cpumask_val; + struct spdk_iscsi_portal *p = NULL; + int rc; + + cpumask_val = spdk_app_get_core_mask(); + + rc = spdk_iscsi_parse_portal(string, &p, 0); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(p != NULL); + CU_ASSERT(strcmp(p->host, host_str) == 0); + CU_ASSERT(strcmp(p->port, port_str) == 0); + CU_ASSERT(spdk_cpuset_equal(p->cpumask, cpumask_val)); + + spdk_iscsi_portal_destroy(p); + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); +} + +static void +parse_portal_ipv6_skip_port_and_cpumask_case(void) +{ + const char *string = "[2001:ad6:1234::]"; + const char *host_str = "[2001:ad6:1234::]"; + const char *port_str = "3260"; + struct spdk_cpuset *cpumask_val; + struct spdk_iscsi_portal *p = NULL; + int rc; + + cpumask_val = spdk_app_get_core_mask(); + + rc = spdk_iscsi_parse_portal(string, &p, 0); + CU_ASSERT(rc == 0); + SPDK_CU_ASSERT_FATAL(p != NULL); + CU_ASSERT(strcmp(p->host, host_str) == 0); + CU_ASSERT(strcmp(p->port, port_str) == 0); + CU_ASSERT(spdk_cpuset_equal(p->cpumask, cpumask_val)); + + spdk_iscsi_portal_destroy(p); + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); +} + +static void +portal_grp_register_unregister_case(void) +{ + struct spdk_iscsi_portal *p; + struct spdk_iscsi_portal_grp *pg1, *pg2; + int rc; + const char *host = "192.168.2.0"; + const char *port = "3260"; + const char *cpumask = "1"; + + pg1 = spdk_iscsi_portal_grp_create(1); + CU_ASSERT(pg1 != NULL); + + p = spdk_iscsi_portal_create(host, port, cpumask); + CU_ASSERT(p != NULL); + + spdk_iscsi_portal_grp_add_portal(pg1, p); + + rc = spdk_iscsi_portal_grp_register(pg1); + CU_ASSERT(rc == 0); + + pg2 = spdk_iscsi_portal_grp_unregister(1); + CU_ASSERT(pg2 != NULL); + CU_ASSERT(pg1 == pg2); + + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.pg_head)); + + spdk_iscsi_portal_grp_destroy(pg1); + + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); +} + +static void +portal_grp_register_twice_case(void) +{ + struct spdk_iscsi_portal *p; + struct spdk_iscsi_portal_grp *pg1, *pg2; + int rc; + const char *host = "192.168.2.0"; + const char *port = "3260"; + const char *cpumask = "1"; + + pg1 = spdk_iscsi_portal_grp_create(1); + CU_ASSERT(pg1 != NULL); + + p = spdk_iscsi_portal_create(host, port, cpumask); + CU_ASSERT(p != NULL); + + spdk_iscsi_portal_grp_add_portal(pg1, p); + + rc = spdk_iscsi_portal_grp_register(pg1); + CU_ASSERT(rc == 0); + + rc = spdk_iscsi_portal_grp_register(pg1); + CU_ASSERT(rc != 0); + + pg2 = spdk_iscsi_portal_grp_unregister(1); + CU_ASSERT(pg2 != NULL); + CU_ASSERT(pg1 == pg2); + + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.pg_head)); + + spdk_iscsi_portal_grp_destroy(pg1); + + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); +} + +static void +portal_grp_add_delete_case(void) +{ + struct spdk_iscsi_portal_grp *pg1, *pg2; + struct spdk_iscsi_portal *p; + int rc; + + const char *host = "192.168.2.0"; + const char *port = "3260"; + const char *cpumask = "1"; + + /* internal of add_portal_group */ + pg1 = spdk_iscsi_portal_grp_create(1); + CU_ASSERT(pg1 != NULL); + + p = spdk_iscsi_portal_create(host, port, cpumask); + CU_ASSERT(p != NULL); + + spdk_iscsi_portal_grp_add_portal(pg1, p); + + rc = spdk_iscsi_portal_grp_open(pg1); + CU_ASSERT(rc == 0); + + rc = spdk_iscsi_portal_grp_register(pg1); + CU_ASSERT(rc == 0); + + /* internal of delete_portal_group */ + pg2 = spdk_iscsi_portal_grp_unregister(1); + CU_ASSERT(pg2 != NULL); + CU_ASSERT(pg1 == pg2); + + spdk_iscsi_portal_grp_release(pg2); + + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.portal_head)); + CU_ASSERT(TAILQ_EMPTY(&g_spdk_iscsi.pg_head)); +} + +int +main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + if (CU_initialize_registry() != CUE_SUCCESS) { + return CU_get_error(); + } + + suite = CU_add_suite("portal_grp_suite", test_setup, NULL); + if (suite == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if ( + CU_add_test(suite, "portal create ipv4 normal case", + portal_create_ipv4_normal_case) == NULL + || CU_add_test(suite, "portal create ipv6 normal case", + portal_create_ipv6_normal_case) == NULL + || CU_add_test(suite, "portal create ipv4 wildcard case", + portal_create_ipv4_wildcard_case) == NULL + || CU_add_test(suite, "portal create ipv6 wildcard case", + portal_create_ipv6_wildcard_case) == NULL + || CU_add_test(suite, "portal create cpumask NULL case", + portal_create_cpumask_null_case) == NULL + || CU_add_test(suite, "portal create cpumask no bit on case", + portal_create_cpumask_no_bit_on_case) == NULL + || CU_add_test(suite, "portal create twice case", + portal_create_twice_case) == NULL + || CU_add_test(suite, "parse portal ipv4 normal case", + parse_portal_ipv4_normal_case) == NULL + || CU_add_test(suite, "parse portal ipv6 normal case", + parse_portal_ipv6_normal_case) == NULL + || CU_add_test(suite, "parse portal ipv4 skip cpumask case", + parse_portal_ipv4_skip_cpumask_case) == NULL + || CU_add_test(suite, "parse portal ipv6 skip cpumask case", + parse_portal_ipv6_skip_cpumask_case) == NULL + || CU_add_test(suite, "parse portal ipv4 skip port and cpumask case", + parse_portal_ipv4_skip_port_and_cpumask_case) == NULL + || CU_add_test(suite, "parse portal ipv6 skip port and cpumask case", + parse_portal_ipv6_skip_port_and_cpumask_case) == NULL + || CU_add_test(suite, "portal group register/unregister case", + portal_grp_register_unregister_case) == NULL + || CU_add_test(suite, "portal group register twice case", + portal_grp_register_twice_case) == NULL + || CU_add_test(suite, "portal group add/delete case", + portal_grp_add_delete_case) == NULL + ) { + CU_cleanup_registry(); + return CU_get_error(); + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + return num_failures; +} diff --git a/src/spdk/test/unit/lib/iscsi/tgt_node.c/.gitignore b/src/spdk/test/unit/lib/iscsi/tgt_node.c/.gitignore new file mode 100644 index 00000000..010d84b8 --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/tgt_node.c/.gitignore @@ -0,0 +1 @@ +tgt_node_ut diff --git a/src/spdk/test/unit/lib/iscsi/tgt_node.c/Makefile b/src/spdk/test/unit/lib/iscsi/tgt_node.c/Makefile new file mode 100644 index 00000000..8cdf3ef3 --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/tgt_node.c/Makefile @@ -0,0 +1,41 @@ +# +# BSD LICENSE +# +# Copyright (c) Intel Corporation. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../../..) +include $(SPDK_ROOT_DIR)/mk/spdk.common.mk +include $(SPDK_ROOT_DIR)/mk/spdk.app.mk + +SPDK_LIB_LIST = conf +TEST_FILE = tgt_node_ut.c + +include $(SPDK_ROOT_DIR)/mk/spdk.unittest.mk diff --git a/src/spdk/test/unit/lib/iscsi/tgt_node.c/tgt_node.conf b/src/spdk/test/unit/lib/iscsi/tgt_node.c/tgt_node.conf new file mode 100644 index 00000000..6bf5aa66 --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/tgt_node.c/tgt_node.conf @@ -0,0 +1,95 @@ +[Global] + +# Test that parsing fails if there is no TargetName +[Failure0] + TargetAlias "Data Disk1" + Mapping PortalGroup1 InitiatorGroup1 + AuthMethod Auto + AuthGroup AuthGroup1 + UseDigest Auto + QueueDepth 128 + LUN0 Malloc0 + LUN1 Malloc1 + +# Test that parsing fails if there is no Mapping +[Failure1] + TargetName target1 + TargetAlias "Data Disk1" + AuthMethod Auto + AuthGroup AuthGroup1 + UseDigest Auto + QueueDepth 128 + LUN0 Malloc0 + LUN1 Malloc1 + +# Test that parsing fails if Mapping does not define Portal or InitiatorGroup +[Failure2] + TargetName target1 + TargetAlias "Data Disk1" + Mapping + AuthMethod Auto + AuthGroup AuthGroup1 + UseDigest Auto + QueueDepth 128 + LUN0 Malloc0 + LUN1 Malloc1 + +# Test that parsing fails if Mapping does not define InitiatorGroup +[Failure3] + TargetName target1 + TargetAlias "Data Disk1" + Mapping PortalGroup1 + AuthMethod Auto + AuthGroup AuthGroup1 + UseDigest Auto + QueueDepth 128 + LUN0 Malloc0 + LUN1 Malloc1 + +# Test that parsing fails if Mapping switches PortalGroup/InitiatorGroup order +[Failure4] + TargetName target1 + TargetAlias "Data Disk1" + Mapping InitiatorGroup1 PortalGroup1 + AuthMethod Auto + AuthGroup AuthGroup1 + UseDigest Auto + QueueDepth 128 + LUN0 Malloc0 + LUN1 Malloc1 + +# Test that parsing fails if Mapping uses invalid InitiatorGroup0 +[Failure5] + TargetName target1 + TargetAlias "Data Disk1" + Mapping PortalGroup1 InitiatorGroup0 + AuthMethod Auto + AuthGroup AuthGroup1 + UseDigest Auto + QueueDepth 128 + LUN0 Malloc0 + LUN1 Malloc1 + +# Test that parsing fails if Mapping uses invalid PortalGroup0 +[Failure6] + TargetName target1 + TargetAlias "Data Disk1" + Mapping PortalGroup0 InitiatorGroup1 + AuthMethod Auto + AuthGroup AuthGroup1 + UseDigest Auto + QueueDepth 128 + LUN0 Malloc0 + LUN1 Malloc1 + +# Test that parsing fails if AuthMethod is invalid +[Failure7] + TargetName target1 + TargetAlias "Data Disk1" + Mapping PortalGroup1 InitiatorGroup1 + AuthMethod SomeGarbage + AuthGroup AuthGroup1 + UseDigest Auto + QueueDepth 128 + LUN0 Malloc0 + LUN1 Malloc1 diff --git a/src/spdk/test/unit/lib/iscsi/tgt_node.c/tgt_node_ut.c b/src/spdk/test/unit/lib/iscsi/tgt_node.c/tgt_node_ut.c new file mode 100644 index 00000000..eda02db6 --- /dev/null +++ b/src/spdk/test/unit/lib/iscsi/tgt_node.c/tgt_node_ut.c @@ -0,0 +1,886 @@ +/*- + * BSD LICENSE + * + * Copyright (c) Intel Corporation. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "spdk/stdinc.h" + +#include "spdk/scsi.h" + +#include "CUnit/Basic.h" +#include "spdk_internal/mock.h" + +#include "../common.c" +#include "iscsi/tgt_node.c" +#include "scsi/scsi_internal.h" +#include "unit/lib/json_mock.c" + +struct spdk_iscsi_globals g_spdk_iscsi; + +const char *config_file; + +DEFINE_STUB(spdk_scsi_dev_get_id, + int, + (const struct spdk_scsi_dev *dev), + 0); + +DEFINE_STUB(spdk_scsi_lun_get_bdev_name, + const char *, + (const struct spdk_scsi_lun *lun), + NULL); + +DEFINE_STUB(spdk_scsi_lun_get_id, + int, + (const struct spdk_scsi_lun *lun), + 0); + +bool +spdk_sock_is_ipv6(struct spdk_sock *sock) +{ + return false; +} + +bool +spdk_sock_is_ipv4(struct spdk_sock *sock) +{ + return false; +} + +struct spdk_iscsi_portal_grp * +spdk_iscsi_portal_grp_find_by_tag(int tag) +{ + return NULL; +} + +struct spdk_iscsi_init_grp * +spdk_iscsi_init_grp_find_by_tag(int tag) +{ + return NULL; +} + +struct spdk_scsi_lun * +spdk_scsi_dev_get_lun(struct spdk_scsi_dev *dev, int lun_id) +{ + if (lun_id < 0 || lun_id >= SPDK_SCSI_DEV_MAX_LUN) { + return NULL; + } + + return dev->lun[lun_id]; +} + +int +spdk_scsi_dev_add_lun(struct spdk_scsi_dev *dev, const char *bdev_name, int lun_id, + void (*hotremove_cb)(const struct spdk_scsi_lun *, void *), + void *hotremove_ctx) +{ + if (bdev_name == NULL) { + return -1; + } else { + return 0; + } +} + +static void +add_lun_test_cases(void) +{ + struct spdk_iscsi_tgt_node tgtnode; + int lun_id = 0; + char *bdev_name = NULL; + struct spdk_scsi_dev scsi_dev; + int rc; + + memset(&tgtnode, 0, sizeof(struct spdk_iscsi_tgt_node)); + memset(&scsi_dev, 0, sizeof(struct spdk_scsi_dev)); + + /* case 1 */ + tgtnode.num_active_conns = 1; + + rc = spdk_iscsi_tgt_node_add_lun(&tgtnode, bdev_name, lun_id); + CU_ASSERT(rc != 0); + + /* case 2 */ + tgtnode.num_active_conns = 0; + lun_id = -2; + + rc = spdk_iscsi_tgt_node_add_lun(&tgtnode, bdev_name, lun_id); + CU_ASSERT(rc != 0); + + /* case 3 */ + lun_id = SPDK_SCSI_DEV_MAX_LUN; + + rc = spdk_iscsi_tgt_node_add_lun(&tgtnode, bdev_name, lun_id); + CU_ASSERT(rc != 0); + + /* case 4 */ + lun_id = -1; + tgtnode.dev = NULL; + + rc = spdk_iscsi_tgt_node_add_lun(&tgtnode, bdev_name, lun_id); + CU_ASSERT(rc != 0); + + /* case 5 */ + tgtnode.dev = &scsi_dev; + + rc = spdk_iscsi_tgt_node_add_lun(&tgtnode, bdev_name, lun_id); + CU_ASSERT(rc != 0); + + /* case 6 */ + bdev_name = "LUN0"; + + rc = spdk_iscsi_tgt_node_add_lun(&tgtnode, bdev_name, lun_id); + CU_ASSERT(rc == 0); +} + +static void +config_file_fail_cases(void) +{ + struct spdk_conf *config; + struct spdk_conf_section *sp; + char section_name[64]; + int section_index; + int rc; + + config = spdk_conf_allocate(); + + rc = spdk_conf_read(config, config_file); + CU_ASSERT(rc == 0); + + section_index = 0; + while (true) { + snprintf(section_name, sizeof(section_name), "Failure%d", section_index); + sp = spdk_conf_find_section(config, section_name); + if (sp == NULL) { + break; + } + rc = spdk_iscsi_parse_tgt_node(sp); + CU_ASSERT(rc < 0); + section_index++; + } + + spdk_conf_free(config); +} + +static void +allow_any_allowed(void) +{ + bool result; + char *netmask; + char *addr1, *addr2; + + netmask = "ANY"; + addr1 = "2001:ad6:1234:5678:9abc::"; + addr2 = "192.168.2.1"; + + result = spdk_iscsi_netmask_allow_addr(netmask, addr1); + CU_ASSERT(result == true); + + result = spdk_iscsi_netmask_allow_addr(netmask, addr2); + CU_ASSERT(result == true); +} + +static void +allow_ipv6_allowed(void) +{ + bool result; + char *netmask; + char *addr; + + netmask = "[2001:ad6:1234::]/48"; + addr = "2001:ad6:1234:5678:9abc::"; + + result = spdk_iscsi_ipv6_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == true); + + result = spdk_iscsi_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == true); + + /* Netmask prefix bits == 128 (all bits must match) */ + netmask = "[2001:ad6:1234:5678:9abc::1]/128"; + addr = "2001:ad6:1234:5678:9abc::1"; + result = spdk_iscsi_ipv6_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == true); +} + +static void +allow_ipv6_denied(void) +{ + bool result; + char *netmask; + char *addr; + + netmask = "[2001:ad6:1234::]/56"; + addr = "2001:ad6:1234:5678:9abc::"; + + result = spdk_iscsi_ipv6_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == false); + + result = spdk_iscsi_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == false); + + /* Netmask prefix bits == 128 (all bits must match) */ + netmask = "[2001:ad6:1234:5678:9abc::1]/128"; + addr = "2001:ad6:1234:5678:9abc::2"; + result = spdk_iscsi_ipv6_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == false); +} + +static void +allow_ipv6_invalid(void) +{ + bool result; + char *netmask; + char *addr; + + /* Netmask prefix bits > 128 */ + netmask = "[2001:ad6:1234::]/129"; + addr = "2001:ad6:1234:5678:9abc::"; + result = spdk_iscsi_ipv6_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == false); + + /* Netmask prefix bits == 0 */ + netmask = "[2001:ad6:1234::]/0"; + addr = "2001:ad6:1234:5678:9abc::"; + result = spdk_iscsi_ipv6_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == false); + + /* Netmask prefix bits < 0 */ + netmask = "[2001:ad6:1234::]/-1"; + addr = "2001:ad6:1234:5678:9abc::"; + result = spdk_iscsi_ipv6_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == false); +} + +static void +allow_ipv4_allowed(void) +{ + bool result; + char *netmask; + char *addr; + + netmask = "192.168.2.0/24"; + addr = "192.168.2.1"; + + result = spdk_iscsi_ipv4_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == true); + + result = spdk_iscsi_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == true); + + /* Netmask prefix == 32 (all bits must match) */ + netmask = "192.168.2.1/32"; + addr = "192.168.2.1"; + result = spdk_iscsi_ipv4_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == true); +} + +static void +allow_ipv4_denied(void) +{ + bool result; + char *netmask; + char *addr; + + netmask = "192.168.2.0"; + addr = "192.168.2.1"; + + result = spdk_iscsi_ipv4_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == false); + + result = spdk_iscsi_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == false); + + /* Netmask prefix == 32 (all bits must match) */ + netmask = "192.168.2.1/32"; + addr = "192.168.2.2"; + result = spdk_iscsi_ipv4_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == false); +} + +static void +allow_ipv4_invalid(void) +{ + bool result; + char *netmask; + char *addr; + + /* Netmask prefix bits > 32 */ + netmask = "192.168.2.0/33"; + addr = "192.168.2.1"; + result = spdk_iscsi_ipv4_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == false); + + /* Netmask prefix bits == 0 */ + netmask = "192.168.2.0/0"; + addr = "192.168.2.1"; + result = spdk_iscsi_ipv4_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == false); + + /* Netmask prefix bits < 0 */ + netmask = "192.168.2.0/-1"; + addr = "192.168.2.1"; + result = spdk_iscsi_ipv4_netmask_allow_addr(netmask, addr); + CU_ASSERT(result == false); +} + +static void +node_access_allowed(void) +{ + struct spdk_iscsi_tgt_node tgtnode; + struct spdk_iscsi_portal_grp pg; + struct spdk_iscsi_init_grp ig; + struct spdk_iscsi_conn conn; + struct spdk_iscsi_portal portal; + struct spdk_iscsi_initiator_name iname; + struct spdk_iscsi_initiator_netmask imask; + struct spdk_scsi_dev scsi_dev; + struct spdk_iscsi_pg_map *pg_map; + char *iqn, *addr; + bool result; + + /* portal group initialization */ + memset(&pg, 0, sizeof(struct spdk_iscsi_portal_grp)); + pg.tag = 1; + + /* initiator group initialization */ + memset(&ig, 0, sizeof(struct spdk_iscsi_init_grp)); + ig.tag = 1; + + ig.ninitiators = 1; + iname.name = "iqn.2017-10.spdk.io:0001"; + TAILQ_INIT(&ig.initiator_head); + TAILQ_INSERT_TAIL(&ig.initiator_head, &iname, tailq); + + ig.nnetmasks = 1; + imask.mask = "192.168.2.0/24"; + TAILQ_INIT(&ig.netmask_head); + TAILQ_INSERT_TAIL(&ig.netmask_head, &imask, tailq); + + /* target initialization */ + memset(&tgtnode, 0, sizeof(struct spdk_iscsi_tgt_node)); + tgtnode.name = "iqn.2017-10.spdk.io:0001"; + TAILQ_INIT(&tgtnode.pg_map_head); + + memset(&scsi_dev, 0, sizeof(struct spdk_scsi_dev)); + snprintf(scsi_dev.name, sizeof(scsi_dev.name), "iqn.2017-10.spdk.io:0001"); + tgtnode.dev = &scsi_dev; + + pg_map = spdk_iscsi_tgt_node_add_pg_map(&tgtnode, &pg); + spdk_iscsi_pg_map_add_ig_map(pg_map, &ig); + + /* portal initialization */ + memset(&portal, 0, sizeof(struct spdk_iscsi_portal)); + portal.group = &pg; + portal.host = "192.168.2.0"; + portal.port = "3260"; + + /* input for UT */ + memset(&conn, 0, sizeof(struct spdk_iscsi_conn)); + conn.portal = &portal; + + iqn = "iqn.2017-10.spdk.io:0001"; + addr = "192.168.2.1"; + + result = spdk_iscsi_tgt_node_access(&conn, &tgtnode, iqn, addr); + CU_ASSERT(result == true); + + spdk_iscsi_pg_map_delete_ig_map(pg_map, &ig); + spdk_iscsi_tgt_node_delete_pg_map(&tgtnode, &pg); +} + +static void +node_access_denied_by_empty_netmask(void) +{ + struct spdk_iscsi_tgt_node tgtnode; + struct spdk_iscsi_portal_grp pg; + struct spdk_iscsi_init_grp ig; + struct spdk_iscsi_conn conn; + struct spdk_iscsi_portal portal; + struct spdk_iscsi_initiator_name iname; + struct spdk_scsi_dev scsi_dev; + struct spdk_iscsi_pg_map *pg_map; + char *iqn, *addr; + bool result; + + /* portal group initialization */ + memset(&pg, 0, sizeof(struct spdk_iscsi_portal_grp)); + pg.tag = 1; + + /* initiator group initialization */ + memset(&ig, 0, sizeof(struct spdk_iscsi_init_grp)); + ig.tag = 1; + + ig.ninitiators = 1; + iname.name = "iqn.2017-10.spdk.io:0001"; + TAILQ_INIT(&ig.initiator_head); + TAILQ_INSERT_TAIL(&ig.initiator_head, &iname, tailq); + + ig.nnetmasks = 0; + TAILQ_INIT(&ig.netmask_head); + + /* target initialization */ + memset(&tgtnode, 0, sizeof(struct spdk_iscsi_tgt_node)); + tgtnode.name = "iqn.2017-10.spdk.io:0001"; + TAILQ_INIT(&tgtnode.pg_map_head); + + memset(&scsi_dev, 0, sizeof(struct spdk_scsi_dev)); + snprintf(scsi_dev.name, sizeof(scsi_dev.name), "iqn.2017-10.spdk.io:0001"); + tgtnode.dev = &scsi_dev; + + pg_map = spdk_iscsi_tgt_node_add_pg_map(&tgtnode, &pg); + spdk_iscsi_pg_map_add_ig_map(pg_map, &ig); + + /* portal initialization */ + memset(&portal, 0, sizeof(struct spdk_iscsi_portal)); + portal.group = &pg; + portal.host = "192.168.2.0"; + portal.port = "3260"; + + /* input for UT */ + memset(&conn, 0, sizeof(struct spdk_iscsi_conn)); + conn.portal = &portal; + + iqn = "iqn.2017-10.spdk.io:0001"; + addr = "192.168.3.1"; + + result = spdk_iscsi_tgt_node_access(&conn, &tgtnode, iqn, addr); + CU_ASSERT(result == false); + + spdk_iscsi_pg_map_delete_ig_map(pg_map, &ig); + spdk_iscsi_tgt_node_delete_pg_map(&tgtnode, &pg); +} + +#define IQN1 "iqn.2017-11.spdk.io:0001" +#define NO_IQN1 "!iqn.2017-11.spdk.io:0001" +#define IQN2 "iqn.2017-11.spdk.io:0002" +#define IP1 "192.168.2.0" +#define IP2 "192.168.2.1" + +static void +node_access_multi_initiator_groups_cases(void) +{ + struct spdk_iscsi_tgt_node tgtnode; + struct spdk_iscsi_conn conn; + struct spdk_iscsi_portal_grp pg; + struct spdk_iscsi_portal portal; + struct spdk_iscsi_init_grp ig1, ig2; + struct spdk_iscsi_initiator_name iname1, iname2; + struct spdk_iscsi_initiator_netmask imask1, imask2; + struct spdk_scsi_dev scsi_dev; + struct spdk_iscsi_pg_map *pg_map; + char *iqn, *addr; + bool result; + + /* target initialization */ + memset(&tgtnode, 0, sizeof(struct spdk_iscsi_tgt_node)); + tgtnode.name = IQN1; + TAILQ_INIT(&tgtnode.pg_map_head); + + memset(&scsi_dev, 0, sizeof(struct spdk_scsi_dev)); + snprintf(scsi_dev.name, sizeof(scsi_dev.name), IQN1); + tgtnode.dev = &scsi_dev; + + /* initiator group initialization */ + memset(&ig1, 0, sizeof(struct spdk_iscsi_init_grp)); + ig1.tag = 1; + TAILQ_INIT(&ig1.initiator_head); + TAILQ_INIT(&ig1.netmask_head); + + ig1.ninitiators = 1; + iname1.name = NULL; + TAILQ_INSERT_TAIL(&ig1.initiator_head, &iname1, tailq); + + ig1.nnetmasks = 1; + imask1.mask = NULL; + TAILQ_INSERT_TAIL(&ig1.netmask_head, &imask1, tailq); + + memset(&ig2, 0, sizeof(struct spdk_iscsi_init_grp)); + ig2.tag = 2; + TAILQ_INIT(&ig2.initiator_head); + TAILQ_INIT(&ig2.netmask_head); + + ig2.ninitiators = 1; + iname2.name = NULL; + TAILQ_INSERT_TAIL(&ig2.initiator_head, &iname2, tailq); + + ig2.nnetmasks = 1; + imask2.mask = NULL; + TAILQ_INSERT_TAIL(&ig2.netmask_head, &imask2, tailq); + + /* portal group initialization */ + memset(&pg, 0, sizeof(struct spdk_iscsi_portal_grp)); + pg.tag = 1; + + pg_map = spdk_iscsi_tgt_node_add_pg_map(&tgtnode, &pg); + spdk_iscsi_pg_map_add_ig_map(pg_map, &ig1); + spdk_iscsi_pg_map_add_ig_map(pg_map, &ig2); + + /* portal initialization */ + memset(&portal, 0, sizeof(struct spdk_iscsi_portal)); + portal.group = &pg; + portal.host = IP1; + portal.port = "3260"; + + /* connection initialization */ + memset(&conn, 0, sizeof(struct spdk_iscsi_conn)); + conn.portal = &portal; + + iqn = IQN1; + addr = IP1; + + /* + * case 1: + * +-------------------------------------------+---------+ + * | IG1 | IG2 | | + * +-------------------------------------------+ | + * | name | addr | name | addr | result | + * +-------------------------------------------+---------+ + * +-------------------------------------------+---------+ + * | denied | - | - | - | denied | + * +-------------------------------------------+---------+ + */ + iname1.name = NO_IQN1; + + result = spdk_iscsi_tgt_node_access(&conn, &tgtnode, iqn, addr); + CU_ASSERT(result == false); + + /* + * case 2: + * +-------------------------------------------+---------+ + * | IG1 | IG2 | | + * +-------------------------------------------+ | + * | name | addr | name | addr | result | + * +-------------------------------------------+---------+ + * +-------------------------------------------+---------+ + * | allowed | allowed | - | - | allowed | + * +-------------------------------------------+---------+ + */ + iname1.name = IQN1; + imask1.mask = IP1; + + result = spdk_iscsi_tgt_node_access(&conn, &tgtnode, iqn, addr); + CU_ASSERT(result == true); + + /* + * case 3: + * +-------------------------------------------+---------+ + * | IG1 | IG2 | | + * +-------------------------------------------+ | + * | name | addr | name | addr | result | + * +-------------------------------------------+---------+ + * +-------------------------------------------+---------+ + * | allowed | denied | denied | - | denied | + * +-------------------------------------------+---------+ + */ + iname1.name = IQN1; + imask1.mask = IP2; + iname2.name = NO_IQN1; + + result = spdk_iscsi_tgt_node_access(&conn, &tgtnode, iqn, addr); + CU_ASSERT(result == false); + + /* + * case 4: + * +-------------------------------------------+---------+ + * | IG1 | IG2 | | + * +-------------------------------------------+ | + * | name | addr | name | addr | result | + * +-------------------------------------------+---------+ + * +-------------------------------------------+---------+ + * | allowed | denied | allowed | allowed | allowed | + * +-------------------------------------------+---------+ + */ + iname1.name = IQN1; + imask1.mask = IP2; + iname2.name = IQN1; + imask2.mask = IP1; + + result = spdk_iscsi_tgt_node_access(&conn, &tgtnode, iqn, addr); + CU_ASSERT(result == true); + + /* + * case 5: + * +---------------------------------------------+---------+ + * | IG1 | IG2 | | + * +---------------------------------------------+ | + * | name | addr | name | addr | result | + * +---------------------------------------------+---------+ + * +---------------------------------------------+---------+ + * | allowed | denied | allowed | denied | denied | + * +---------------------------------------------+---------+ + */ + iname1.name = IQN1; + imask1.mask = IP2; + iname2.name = IQN1; + imask2.mask = IP2; + + result = spdk_iscsi_tgt_node_access(&conn, &tgtnode, iqn, addr); + CU_ASSERT(result == false); + + /* + * case 6: + * +---------------------------------------------+---------+ + * | IG1 | IG2 | | + * +---------------------------------------------+ | + * | name | addr | name | addr | result | + * +---------------------------------------------+---------+ + * +---------------------------------------------+---------+ + * | allowed | denied | not found | - | denied | + * +---------------------------------------------+---------+ + */ + iname1.name = IQN1; + imask1.mask = IP2; + iname2.name = IQN2; + + result = spdk_iscsi_tgt_node_access(&conn, &tgtnode, iqn, addr); + CU_ASSERT(result == false); + + /* + * case 7: + * +---------------------------------------------+---------+ + * | IG1 | IG2 | | + * +---------------------------------------------+ | + * | name | addr | name | addr | result | + * +---------------------------------------------+---------+ + * +---------------------------------------------+---------+ + * | not found | - | denied | - | denied | + * +---------------------------------------------+---------+ + */ + iname1.name = IQN2; + iname2.name = NO_IQN1; + + result = spdk_iscsi_tgt_node_access(&conn, &tgtnode, iqn, addr); + CU_ASSERT(result == false); + + /* + * case 8: + * +---------------------------------------------+---------+ + * | IG1 | IG2 | | + * +---------------------------------------------+ | + * | name | addr | name | addr | result | + * +---------------------------------------------+---------+ + * +---------------------------------------------+---------+ + * | not found | - | allowed | allowed | allowed | + * +---------------------------------------------+---------+ + */ + iname1.name = IQN2; + iname2.name = IQN1; + imask2.mask = IP1; + + result = spdk_iscsi_tgt_node_access(&conn, &tgtnode, iqn, addr); + CU_ASSERT(result == true); + + /* + * case 9: + * +---------------------------------------------+---------+ + * | IG1 | IG2 | | + * +---------------------------------------------+ | + * | name | addr | name | addr | result | + * +---------------------------------------------+---------+ + * +---------------------------------------------+---------+ + * | not found | - | allowed | denied | denied | + * +---------------------------------------------+---------+ + */ + iname1.name = IQN2; + iname2.name = IQN1; + imask2.mask = IP2; + + result = spdk_iscsi_tgt_node_access(&conn, &tgtnode, iqn, addr); + CU_ASSERT(result == false); + + /* + * case 10: + * +---------------------------------------------+---------+ + * | IG1 | IG2 | | + * +---------------------------------------------+ | + * | name | addr | name | addr | result | + * +---------------------------------------------+---------+ + * +---------------------------------------------+---------+ + * | not found | - | not found | - | denied | + * +---------------------------------------------+---------+ + */ + iname1.name = IQN2; + iname2.name = IQN2; + + result = spdk_iscsi_tgt_node_access(&conn, &tgtnode, iqn, addr); + CU_ASSERT(result == false); + + spdk_iscsi_pg_map_delete_ig_map(pg_map, &ig1); + spdk_iscsi_pg_map_delete_ig_map(pg_map, &ig2); + spdk_iscsi_tgt_node_delete_pg_map(&tgtnode, &pg); +} + +static void +allow_iscsi_name_multi_maps_case(void) +{ + struct spdk_iscsi_tgt_node tgtnode; + struct spdk_iscsi_portal_grp pg1, pg2; + struct spdk_iscsi_init_grp ig; + struct spdk_iscsi_initiator_name iname; + struct spdk_iscsi_pg_map *pg_map1, *pg_map2; + struct spdk_scsi_dev scsi_dev; + char *iqn; + bool result; + + /* target initialization */ + memset(&tgtnode, 0, sizeof(struct spdk_iscsi_tgt_node)); + TAILQ_INIT(&tgtnode.pg_map_head); + + memset(&scsi_dev, 0, sizeof(struct spdk_scsi_dev)); + snprintf(scsi_dev.name, sizeof(scsi_dev.name), IQN1); + tgtnode.dev = &scsi_dev; + + /* initiator group initialization */ + memset(&ig, 0, sizeof(struct spdk_iscsi_init_grp)); + TAILQ_INIT(&ig.initiator_head); + + ig.ninitiators = 1; + iname.name = NULL; + TAILQ_INSERT_TAIL(&ig.initiator_head, &iname, tailq); + + /* portal group initialization */ + memset(&pg1, 0, sizeof(struct spdk_iscsi_portal_grp)); + pg1.tag = 1; + memset(&pg2, 0, sizeof(struct spdk_iscsi_portal_grp)); + pg2.tag = 1; + + pg_map1 = spdk_iscsi_tgt_node_add_pg_map(&tgtnode, &pg1); + pg_map2 = spdk_iscsi_tgt_node_add_pg_map(&tgtnode, &pg2); + spdk_iscsi_pg_map_add_ig_map(pg_map1, &ig); + spdk_iscsi_pg_map_add_ig_map(pg_map2, &ig); + + /* test for IG1 <-> PG1, PG2 case */ + iqn = IQN1; + + iname.name = IQN1; + + result = spdk_iscsi_tgt_node_allow_iscsi_name(&tgtnode, iqn); + CU_ASSERT(result == true); + + iname.name = IQN2; + + result = spdk_iscsi_tgt_node_allow_iscsi_name(&tgtnode, iqn); + CU_ASSERT(result == false); + + spdk_iscsi_pg_map_delete_ig_map(pg_map1, &ig); + spdk_iscsi_pg_map_delete_ig_map(pg_map2, &ig); + spdk_iscsi_tgt_node_delete_pg_map(&tgtnode, &pg1); + spdk_iscsi_tgt_node_delete_pg_map(&tgtnode, &pg2); +} + +/* + * static bool + * spdk_iscsi_check_chap_params(bool disable_chap, bool require_chap, + * bool mutual_chap, int chap_group); + */ +static void +chap_param_test_cases(void) +{ + /* Auto */ + CU_ASSERT(spdk_iscsi_check_chap_params(false, false, false, 0) == true); + + /* None */ + CU_ASSERT(spdk_iscsi_check_chap_params(true, false, false, 0) == true); + + /* CHAP */ + CU_ASSERT(spdk_iscsi_check_chap_params(false, true, false, 0) == true); + + /* CHAP Mutual */ + CU_ASSERT(spdk_iscsi_check_chap_params(false, true, true, 0) == true); + + /* Check mutual exclusiveness of disabled and required */ + CU_ASSERT(spdk_iscsi_check_chap_params(true, true, false, 0) == false); + + /* Mutual requires Required */ + CU_ASSERT(spdk_iscsi_check_chap_params(false, false, true, 0) == false); + + /* Remaining combinations */ + CU_ASSERT(spdk_iscsi_check_chap_params(true, false, true, 0) == false); + CU_ASSERT(spdk_iscsi_check_chap_params(true, true, true, 0) == false); + + /* Valid auth group ID */ + CU_ASSERT(spdk_iscsi_check_chap_params(false, false, false, 1) == true); + + /* Invalid auth group ID */ + CU_ASSERT(spdk_iscsi_check_chap_params(false, false, false, -1) == false); +} + +int +main(int argc, char **argv) +{ + CU_pSuite suite = NULL; + unsigned int num_failures; + + if (argc < 2) { + fprintf(stderr, "usage: %s <config file>\n", argv[0]); + exit(1); + } + + if (CU_initialize_registry() != CUE_SUCCESS) { + return CU_get_error(); + } + + config_file = argv[1]; + + suite = CU_add_suite("iscsi_target_node_suite", NULL, NULL); + if (suite == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + if ( + CU_add_test(suite, "add lun test cases", add_lun_test_cases) == NULL + || CU_add_test(suite, "config file fail cases", config_file_fail_cases) == NULL + || CU_add_test(suite, "allow any allowed case", allow_any_allowed) == NULL + || CU_add_test(suite, "allow ipv6 allowed case", allow_ipv6_allowed) == NULL + || CU_add_test(suite, "allow ipv6 denied case", allow_ipv6_denied) == NULL + || CU_add_test(suite, "allow ipv6 invalid case", allow_ipv6_invalid) == NULL + || CU_add_test(suite, "allow ipv4 allowed case", allow_ipv4_allowed) == NULL + || CU_add_test(suite, "allow ipv4 denied case", allow_ipv4_denied) == NULL + || CU_add_test(suite, "allow ipv4 invalid case", allow_ipv4_invalid) == NULL + || CU_add_test(suite, "node access allowed case", node_access_allowed) == NULL + || CU_add_test(suite, "node access denied case (empty netmask)", + node_access_denied_by_empty_netmask) == NULL + || CU_add_test(suite, "node access multiple initiator groups cases", + node_access_multi_initiator_groups_cases) == NULL + || CU_add_test(suite, "allow iscsi name case", + allow_iscsi_name_multi_maps_case) == NULL + || CU_add_test(suite, "chap param test cases", chap_param_test_cases) == NULL + ) { + CU_cleanup_registry(); + return CU_get_error(); + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_failures = CU_get_number_of_failures(); + CU_cleanup_registry(); + return num_failures; +} |