diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/spdk/test/iscsi_tgt | |
parent | Initial commit. (diff) | |
download | ceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/spdk/test/iscsi_tgt')
29 files changed, 3009 insertions, 0 deletions
diff --git a/src/spdk/test/iscsi_tgt/bdev_io_wait/bdev_io_wait.sh b/src/spdk/test/iscsi_tgt/bdev_io_wait/bdev_io_wait.sh new file mode 100755 index 000000000..15dfe1165 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/bdev_io_wait/bdev_io_wait.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +iscsitestinit $1 $2 + +MALLOC_BDEV_SIZE=64 +MALLOC_BLOCK_SIZE=512 + +rpc_py="$rootdir/scripts/rpc.py" + +timing_enter start_iscsi_tgt + +"${ISCSI_APP[@]}" -m 0x2 -p 1 -s 512 --wait-for-rpc & +pid=$! +echo "iSCSI target launched. pid: $pid" +trap 'killprocess $pid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT +waitforlisten $pid +$rpc_py iscsi_set_options -o 30 -a 4 +# Minimal number of bdev io pool (5) and cache (1) +$rpc_py bdev_set_options -p 5 -c 1 +$rpc_py framework_start_init +echo "iscsi_tgt is listening. Running tests..." + +timing_exit start_iscsi_tgt + +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +$rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK +$rpc_py bdev_malloc_create $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE +# "Malloc0:0" ==> use Malloc0 blockdev for LUN0 +# "1:2" ==> map PortalGroup1 to InitiatorGroup2 +# "64" ==> iSCSI queue depth 64 +# "-d" ==> disable CHAP authentication +$rpc_py iscsi_create_target_node disk1 disk1_alias 'Malloc0:0' $PORTAL_TAG:$INITIATOR_TAG 256 -d +sleep 1 +trap 'killprocess $pid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +"$rootdir/test/bdev/bdevperf/bdevperf" --json <(initiator_json_config) -q 128 -o 4096 -w write -t 1 +"$rootdir/test/bdev/bdevperf/bdevperf" --json <(initiator_json_config) -q 128 -o 4096 -w read -t 1 +"$rootdir/test/bdev/bdevperf/bdevperf" --json <(initiator_json_config) -q 128 -o 4096 -w flush -t 1 +"$rootdir/test/bdev/bdevperf/bdevperf" --json <(initiator_json_config) -q 128 -o 4096 -w unmap -t 1 + +trap - SIGINT SIGTERM EXIT + +killprocess $pid + +iscsitestfini $1 $2 diff --git a/src/spdk/test/iscsi_tgt/calsoft/calsoft.py b/src/spdk/test/iscsi_tgt/calsoft/calsoft.py new file mode 100755 index 000000000..5be658e61 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/calsoft/calsoft.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 + +import os +import time +import sys +import subprocess +import threading +import json + +CALSOFT_BIN_PATH = "/usr/local/calsoft/iscsi-pcts-v1.5/bin" + +''' +11/26/2015 disable tc_login_11_2 and tc_login_11_4 +RFC 7143 6.3 +Neither the initiator nor the target should attempt to declare or +negotiate a parameter more than once during login, except for +responses to specific keys that explicitly allow repeated key +declarations (e.g., TargetAddress) + +The spec didn't make it clear what other keys could be re-declare +Disscussed this with UNH and get the conclusion that TargetName/ +TargetAddress/MaxRecvDataSegmentLength could be re-declare. +''' +''' +12/1/2015 add tc_login_2_2 to known_failed_cases +RFC 7143 6.1 +A standard-label MUST begin with a capital letter and must not exceed +63 characters. +key name: A standard-label +''' +''' +06/10/2020 add tc_login_29_1 to known_failed_cases +RFC 3720 12.19. DataSequenceInOrder +Irrelevant when: SessionType=Discovery +''' + +known_failed_cases = ['tc_ffp_15_2', 'tc_ffp_29_2', 'tc_ffp_29_3', 'tc_ffp_29_4', + 'tc_err_1_1', 'tc_err_1_2', 'tc_err_2_8', + 'tc_err_3_1', 'tc_err_3_2', 'tc_err_3_3', + 'tc_err_3_4', 'tc_err_5_1', 'tc_login_3_1', + 'tc_login_11_2', 'tc_login_11_4', 'tc_login_2_2', 'tc_login_29_1'] + + +def run_case(case, result_list, log_dir_path): + try: + case_log = subprocess.check_output("{}/{}".format(CALSOFT_BIN_PATH, case), stderr=subprocess.STDOUT, shell=True).decode('utf-8') + except subprocess.CalledProcessError as e: + result_list.append({"Name": case, "Result": "FAIL"}) + case_log = e.output.decode('utf-8') + else: + result_list.append({"Name": case, "Result": "PASS"}) + with open(log_dir_path + case + '.txt', 'w') as f: + f.write(case_log) + + +def main(): + if not os.path.exists(CALSOFT_BIN_PATH): + print("The Calsoft test suite is not available on this machine.") + sys.exit(1) + + output_dir = sys.argv[1] + if len(sys.argv) > 2: + output_file = sys.argv[2] + else: + output_file = "%s/calsoft.json" % (output_dir) + + log_dir = "%s/calsoft/" % output_dir + + all_cases = [x for x in os.listdir(CALSOFT_BIN_PATH) if x.startswith('tc')] + all_cases.sort() + + case_result_list = [] + + result = {"Calsoft iSCSI tests": case_result_list} + + if not os.path.exists(log_dir): + os.mkdir(log_dir) + for case in known_failed_cases: + print("Skipping %s. It is known to fail." % (case)) + case_result_list.append({"Name": case, "Result": "SKIP"}) + + thread_objs = [] + left_cases = list(set(all_cases) - set(known_failed_cases)) + index = 0 + max_thread_count = 32 + + while index < len(left_cases): + cur_thread_count = 0 + for thread_obj in thread_objs: + if thread_obj.is_alive(): + cur_thread_count += 1 + while cur_thread_count < max_thread_count and index < len(left_cases): + thread_obj = threading.Thread(target=run_case, args=(left_cases[index], case_result_list, log_dir, )) + thread_obj.start() + time.sleep(0.02) + thread_objs.append(thread_obj) + index += 1 + cur_thread_count += 1 + end_time = time.time() + 30 + while time.time() < end_time: + for thread_obj in thread_objs: + if thread_obj.is_alive(): + break + else: + break + else: + print("Thread timeout") + exit(1) + with open(output_file, 'w') as f: + json.dump(obj=result, fp=f, indent=2) + + failed = 0 + for x in case_result_list: + if x["Result"] == "FAIL": + print("Test case %s failed." % (x["Name"])) + failed = 1 + exit(failed) + + +if __name__ == '__main__': + main() diff --git a/src/spdk/test/iscsi_tgt/calsoft/calsoft.sh b/src/spdk/test/iscsi_tgt/calsoft/calsoft.sh new file mode 100755 index 000000000..048b529d5 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/calsoft/calsoft.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +delete_tmp_conf_files() { + rm -f /usr/local/etc/its.conf +} + +MALLOC_BDEV_SIZE=64 +MALLOC_BLOCK_SIZE=512 + +rpc_py="$rootdir/scripts/rpc.py" +calsoft_py="$testdir/calsoft.py" + +# Copy the calsoft config file to /usr/local/etc +mkdir -p /usr/local/etc +cp $testdir/its.conf /usr/local/etc/ + +# Append target ip to calsoft config +echo "IP=$TARGET_IP" >> /usr/local/etc/its.conf + +timing_enter start_iscsi_tgt + +"${ISCSI_APP[@]}" -m 0x1 --wait-for-rpc & +pid=$! +echo "Process pid: $pid" + +trap 'killprocess $pid; delete_tmp_conf_files; exit 1 ' SIGINT SIGTERM EXIT + +waitforlisten $pid +$rpc_py load_subsystem_config < $testdir/iscsi.json +$rpc_py framework_start_init +echo "iscsi_tgt is listening. Running tests..." + +timing_exit start_iscsi_tgt + +$rpc_py iscsi_create_auth_group 1 -c 'user:root secret:tester' +$rpc_py iscsi_set_discovery_auth -g 1 +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +$rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK +$rpc_py bdev_malloc_create -b MyBdev $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE +# "MyBdev:0" ==> use MyBdev blockdev for LUN0 +# "1:2" ==> map PortalGroup1 to InitiatorGroup2 +# "64" ==> iSCSI queue depth 64 +# "0 0 0 1" ==> enable CHAP authentication using auth group 1 +$rpc_py iscsi_create_target_node Target3 Target3_alias 'MyBdev:0' $PORTAL_TAG:$INITIATOR_TAG 64 -g 1 +sleep 1 + +if [ "$1" ]; then + $calsoft_py "$output_dir" "$1" + failed=$? +else + $calsoft_py "$output_dir" + failed=$? +fi + +trap - SIGINT SIGTERM EXIT + +killprocess $pid +delete_tmp_conf_files +exit $failed diff --git a/src/spdk/test/iscsi_tgt/calsoft/iscsi.json b/src/spdk/test/iscsi_tgt/calsoft/iscsi.json new file mode 100644 index 000000000..64b4a9595 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/calsoft/iscsi.json @@ -0,0 +1,15 @@ +{ + "subsystem": "iscsi", + "config": [ + { + "params": { + "allow_duplicated_isid": true, + "nop_timeout": 30, + "nop_in_interval": 10, + "max_sessions": 256, + "error_recovery_level": 2 + }, + "method": "iscsi_set_options" + } + ] +} diff --git a/src/spdk/test/iscsi_tgt/calsoft/its.conf b/src/spdk/test/iscsi_tgt/calsoft/its.conf new file mode 100644 index 000000000..6469dab63 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/calsoft/its.conf @@ -0,0 +1,7 @@ +InitiatorName=iqn.1994-05.com.redhat:b3283535dc3b +TargetName=iqn.2016-06.io.spdk:Target3 +DefaultTime2Retain=20 +DefaultTime2Wait=2 +AuthMethod=CHAP,None +UserName=root +PassWord=tester diff --git a/src/spdk/test/iscsi_tgt/common.sh b/src/spdk/test/iscsi_tgt/common.sh new file mode 100644 index 000000000..d42a2a3a2 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/common.sh @@ -0,0 +1,209 @@ +# Network configuration +TARGET_INTERFACE="spdk_tgt_int" +INITIATOR_INTERFACE="spdk_init_int" +TARGET_NAMESPACE="spdk_iscsi_ns" +TARGET_NS_CMD=(ip netns exec "$TARGET_NAMESPACE") + +# iSCSI target configuration +TARGET_IP=10.0.0.1 +INITIATOR_IP=10.0.0.2 +ISCSI_PORT=3260 +NETMASK=$INITIATOR_IP/32 +INITIATOR_TAG=2 +INITIATOR_NAME=ANY +PORTAL_TAG=1 +ISCSI_APP=("${TARGET_NS_CMD[@]}" "${ISCSI_APP[@]}") +if [ $SPDK_TEST_VPP -eq 1 ]; then + ISCSI_APP+=(-L sock_vpp) +fi +ISCSI_TEST_CORE_MASK=0xFF + +function create_veth_interfaces() { + # $1 = test type (posix/vpp) + ip netns del $TARGET_NAMESPACE || true + ip link delete $INITIATOR_INTERFACE || true + + trap 'cleanup_veth_interfaces $1; exit 1' SIGINT SIGTERM EXIT + + # Create veth (Virtual ethernet) interface pair + ip link add $INITIATOR_INTERFACE type veth peer name $TARGET_INTERFACE + ip addr add $INITIATOR_IP/24 dev $INITIATOR_INTERFACE + ip link set $INITIATOR_INTERFACE up + + # Create and add interface for target to network namespace + ip netns add $TARGET_NAMESPACE + ip link set $TARGET_INTERFACE netns $TARGET_NAMESPACE + + # Accept connections from veth interface + iptables -I INPUT 1 -i $INITIATOR_INTERFACE -p tcp --dport $ISCSI_PORT -j ACCEPT + + "${TARGET_NS_CMD[@]}" ip link set $TARGET_INTERFACE up + + if [ "$1" == "posix" ]; then + "${TARGET_NS_CMD[@]}" ip link set lo up + "${TARGET_NS_CMD[@]}" ip addr add $TARGET_IP/24 dev $TARGET_INTERFACE + + # Verify connectivity + ping -c 1 $TARGET_IP + ip netns exec $TARGET_NAMESPACE ping -c 1 $INITIATOR_IP + else + start_vpp + fi +} + +function cleanup_veth_interfaces() { + # $1 = test type (posix/vpp) + if [ "$1" == "vpp" ]; then + kill_vpp + fi + + # Cleanup veth interfaces and network namespace + # Note: removing one veth, removes the pair + ip link delete $INITIATOR_INTERFACE + ip netns del $TARGET_NAMESPACE +} + +function iscsitestinit() { + if [ "$1" == "iso" ]; then + $rootdir/scripts/setup.sh + if [ -n "$2" ]; then + create_veth_interfaces $2 + else + # default to posix + create_veth_interfaces "posix" + fi + fi +} + +function waitforiscsidevices() { + local num=$1 + + for ((i = 1; i <= 20; i++)); do + n=$(iscsiadm -m session -P 3 | grep -c "Attached scsi disk sd[a-z]*" || true) + if [ $n -ne $num ]; then + sleep 0.1 + else + return 0 + fi + done + + return 1 +} + +function iscsitestfini() { + if [ "$1" == "iso" ]; then + if [ -n "$2" ]; then + cleanup_veth_interfaces $2 + else + # default to posix + cleanup_veth_interfaces "posix" + fi + $rootdir/scripts/setup.sh reset + fi +} + +function start_vpp() { + # We need to make sure that posix side doesn't send jumbo packets while + # for VPP side maximal size of MTU for TCP is 1460 and tests doesn't work + # stable with larger packets + MTU=1460 + MTU_W_HEADER=$((MTU + 20)) + ip link set dev $INITIATOR_INTERFACE mtu $MTU + ethtool -K $INITIATOR_INTERFACE tso off + ethtool -k $INITIATOR_INTERFACE + + # Start VPP process in SPDK target network namespace + "${TARGET_NS_CMD[@]}" vpp \ + unix { nodaemon cli-listen /run/vpp/cli.sock } \ + dpdk { no-pci } \ + session { evt_qs_memfd_seg } \ + socksvr { socket-name /run/vpp-api.sock } \ + plugins { \ + plugin default { disable } \ + plugin dpdk_plugin.so { enable } \ + } & + + vpp_pid=$! + echo "VPP Process pid: $vpp_pid" + + gdb_attach $vpp_pid & + + # Wait until VPP starts responding + xtrace_disable + counter=40 + while [ $counter -gt 0 ]; do + vppctl show version | grep -E "vpp v[0-9]+\.[0-9]+" && break + counter=$((counter - 1)) + sleep 0.5 + done + xtrace_restore + if [ $counter -eq 0 ]; then + return 1 + fi + + # Below VPP commands are masked with "|| true" for the sake of + # running the test in the CI system. For reasons unknown when + # run via CI these commands result in 141 return code (pipefail) + # even despite producing valid output. + # Using "|| true" does not impact the "-e" flag used in test scripts + # because vppctl cli commands always return with 0, even if + # there was an error. + # As a result - grep checks on command outputs must be used to + # verify vpp configuration and connectivity. + + # Setup host interface + vppctl create host-interface name $TARGET_INTERFACE || true + VPP_TGT_INT="host-$TARGET_INTERFACE" + vppctl set interface state $VPP_TGT_INT up || true + vppctl set interface ip address $VPP_TGT_INT $TARGET_IP/24 || true + vppctl set interface mtu $MTU $VPP_TGT_INT || true + + vppctl show interface | tr -s " " | grep -E "host-$TARGET_INTERFACE [0-9]+ up $MTU/0/0/0" + + # Disable session layer + # NOTE: VPP net framework should enable it itself. + vppctl session disable || true + + # Verify connectivity + vppctl show int addr | grep -E "$TARGET_IP/24" + ip addr show $INITIATOR_INTERFACE + ip netns exec $TARGET_NAMESPACE ip addr show $TARGET_INTERFACE + sleep 3 + # SC1010: ping -M do - in this case do is an option not bash special word + # shellcheck disable=SC1010 + ping -c 1 $TARGET_IP -s $((MTU - 28)) -M do + vppctl ping $INITIATOR_IP repeat 1 size $((MTU - (28 + 8))) verbose | grep -E "$MTU_W_HEADER bytes from $INITIATOR_IP" +} + +function kill_vpp() { + vppctl delete host-interface name $TARGET_INTERFACE || true + + # Dump VPP configuration before kill + vppctl show api clients || true + vppctl show session || true + vppctl show errors || true + + killprocess $vpp_pid +} +function initiator_json_config() { + # Prepare config file for iSCSI initiator + jq . <<- JSON + { + "subsystems": [ + { + "subsystem": "bdev", + "config": [ + { + "method": "bdev_iscsi_create", + "params": { + "name": "iSCSI0", + "url": "iscsi://$TARGET_IP/iqn.2016-06.io.spdk:disk1/0", + "initiator_iqn": "iqn.2016-06.io.spdk:disk1/0" + } + }${*:+,$*} + ] + } + ] + } + JSON +} diff --git a/src/spdk/test/iscsi_tgt/digests/digests.sh b/src/spdk/test/iscsi_tgt/digests/digests.sh new file mode 100755 index 000000000..3a03c10ec --- /dev/null +++ b/src/spdk/test/iscsi_tgt/digests/digests.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +function node_login_fio_logout() { + for arg in "$@"; do + iscsiadm -m node -p $TARGET_IP:$ISCSI_PORT -o update -n node.conn[0].iscsi.$arg + done + iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT + waitforiscsidevices 1 + $fio_py -p iscsi -i 512 -d 1 -t write -r 2 + $fio_py -p iscsi -i 512 -d 1 -t read -r 2 + iscsiadm -m node --logout -p $TARGET_IP:$ISCSI_PORT + waitforiscsidevices 0 +} + +function iscsi_header_digest_test() { + # Enable HeaderDigest to CRC32C + node_login_fio_logout "HeaderDigest -v CRC32C" + + # Let iscsi target to decide its preference on + # HeaderDigest based on its capability. + node_login_fio_logout "HeaderDigest -v CRC32C,None" +} + +function iscsi_header_data_digest_test() { + # Only enable HeaderDigest to CRC32C + node_login_fio_logout "HeaderDigest -v CRC32C" "DataDigest -v None" + + # Only enable DataDigest to CRC32C + node_login_fio_logout "HeaderDigest -v None" "DataDigest -v CRC32C" + + # Let iscsi target to decide its preference on both + # HeaderDigest and DataDigest based on its capability. + node_login_fio_logout "HeaderDigest -v CRC32C,None" "DataDigest -v CRC32C,None" + + # Enable HeaderDigest and DataDigest both. + node_login_fio_logout "HeaderDigest -v CRC32C" "DataDigest -v CRC32C" +} + +MALLOC_BDEV_SIZE=64 +MALLOC_BLOCK_SIZE=512 + +rpc_py="$rootdir/scripts/rpc.py" +fio_py="$rootdir/scripts/fio.py" + +timing_enter start_iscsi_tgt + +"${ISCSI_APP[@]}" -m $ISCSI_TEST_CORE_MASK --wait-for-rpc & +pid=$! +echo "Process pid: $pid" + +trap 'killprocess $pid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +waitforlisten $pid +$rpc_py iscsi_set_options -o 30 -a 16 +$rpc_py framework_start_init +echo "iscsi_tgt is listening. Running tests..." + +timing_exit start_iscsi_tgt + +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +$rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK +$rpc_py bdev_malloc_create $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE +# "Malloc0:0" ==> use Malloc0 blockdev for LUN0 +# "1:2" ==> map PortalGroup1 to InitiatorGroup2 +# "64" ==> iSCSI queue depth 64 +# "-d" ==> disable CHAP authentication +$rpc_py iscsi_create_target_node Target3 Target3_alias 'Malloc0:0' $PORTAL_TAG:$INITIATOR_TAG 64 -d +sleep 1 + +iscsiadm -m discovery -t sendtargets -p $TARGET_IP:$ISCSI_PORT + +# iscsiadm installed by some Fedora releases loses the ability to set DataDigest parameter. +# Check and avoid setting DataDigest. +DataDigestAbility=$(iscsiadm -m node -p $TARGET_IP:$ISCSI_PORT -o update -n node.conn[0].iscsi.DataDigest -v None 2>&1 || true) +if [ "$DataDigestAbility"x != x ]; then + run_test "iscsi_tgt_digest" iscsi_header_digest_test +else + run_test "iscsi_tgt_data_digest" iscsi_header_data_digest_test +fi + +trap - SIGINT SIGTERM EXIT + +iscsicleanup +killprocess $pid +iscsitestfini $1 $2 diff --git a/src/spdk/test/iscsi_tgt/ext4test/ext4test.sh b/src/spdk/test/iscsi_tgt/ext4test/ext4test.sh new file mode 100755 index 000000000..8de417367 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/ext4test/ext4test.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +rpc_py="$rootdir/scripts/rpc.py" +node_base="iqn.2013-06.com.intel.ch.spdk" + +timing_enter start_iscsi_tgt + +"${ISCSI_APP[@]}" --wait-for-rpc & +pid=$! +echo "Process pid: $pid" + +trap '$rpc_py bdev_split_delete Name0n1 || true; killprocess $pid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +waitforlisten $pid +$rpc_py iscsi_set_options -o 30 -a 4 -b $node_base +$rpc_py framework_start_init +$rootdir/scripts/gen_nvme.sh --json | $rpc_py load_subsystem_config +$rpc_py bdev_malloc_create 512 4096 --name Malloc0 +echo "iscsi_tgt is listening. Running tests..." + +timing_exit start_iscsi_tgt + +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +$rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK +$rpc_py bdev_error_create 'Malloc0' +# "1:2" ==> map PortalGroup1 to InitiatorGroup2 +# "64" ==> iSCSI queue depth 64 +# "-d" ==> disable CHAP authentication +$rpc_py iscsi_create_target_node Target0 Target0_alias EE_Malloc0:0 1:2 64 -d +sleep 1 + +iscsiadm -m discovery -t sendtargets -p $TARGET_IP:$ISCSI_PORT +iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT +waitforiscsidevices 1 + +trap 'for new_dir in $(dir -d /mnt/*dir); do umount $new_dir; rm -rf $new_dir; done; + iscsicleanup; killprocess $pid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +echo "Test error injection" +$rpc_py bdev_error_inject_error EE_Malloc0 'all' 'failure' -n 1000 + +dev=$(iscsiadm -m session -P 3 | grep "Attached scsi disk" | awk '{print $4}') + +set +e +waitforfile /dev/${dev} +if make_filesystem ext4 /dev/${dev}; then + echo "mkfs successful - expected failure" + iscsicleanup + killprocess $pid + exit 1 +else + echo "mkfs failed as expected" +fi +set -e + +iscsicleanup +$rpc_py bdev_error_inject_error EE_Malloc0 'clear' 'failure' +$rpc_py iscsi_delete_target_node $node_base:Target0 +echo "Error injection test done" + +if [ -z "$NO_NVME" ]; then + bdev_size=$(get_bdev_size Nvme0n1) + split_size=$((bdev_size / 2)) + if [ $split_size -gt 10000 ]; then + split_size=10000 + fi + $rpc_py bdev_split_create Nvme0n1 2 -s $split_size + $rpc_py iscsi_create_target_node Target1 Target1_alias Nvme0n1p0:0 1:2 64 -d +fi + +iscsiadm -m discovery -t sendtargets -p $TARGET_IP:$ISCSI_PORT +iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT +waitforiscsidevices 1 + +devs=$(iscsiadm -m session -P 3 | grep "Attached scsi disk" | awk '{print $4}') + +for dev in $devs; do + make_filesystem ext4 /dev/${dev} + mkdir -p /mnt/${dev}dir + mount -o sync /dev/${dev} /mnt/${dev}dir + + rsync -qav --exclude=".git" --exclude="*.o" $rootdir/ /mnt/${dev}dir/spdk + + make -C /mnt/${dev}dir/spdk clean + (cd /mnt/${dev}dir/spdk && ./configure $(get_config_params)) + make -C /mnt/${dev}dir/spdk -j16 + + # Print out space consumed on target device to help decide + # if/when we need to increase the size of the malloc LUN + df -h /dev/$dev + + rm -rf /mnt/${dev}dir/spdk +done + +for dev in $devs; do + umount /mnt/${dev}dir + rm -rf /mnt/${dev}dir + + stats=($(cat /sys/block/$dev/stat)) + echo "" + echo "$dev stats" + printf "READ IO cnt: % 8u merges: % 8u sectors: % 8u ticks: % 8u\n" \ + ${stats[0]} ${stats[1]} ${stats[2]} ${stats[3]} + printf "WRITE IO cnt: % 8u merges: % 8u sectors: % 8u ticks: % 8u\n" \ + ${stats[4]} ${stats[5]} ${stats[6]} ${stats[7]} + printf "in flight: % 8u io ticks: % 8u time in queue: % 8u\n" \ + ${stats[8]} ${stats[9]} ${stats[10]} + echo "" +done + +trap - SIGINT SIGTERM EXIT + +iscsicleanup +$rpc_py bdev_split_delete Nvme0n1 +$rpc_py bdev_error_delete EE_Malloc0 + +if [ -z "$NO_NVME" ]; then + $rpc_py bdev_nvme_detach_controller Nvme0 +fi + +killprocess $pid +iscsitestfini $1 $2 diff --git a/src/spdk/test/iscsi_tgt/filesystem/filesystem.sh b/src/spdk/test/iscsi_tgt/filesystem/filesystem.sh new file mode 100755 index 000000000..156b5bde3 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/filesystem/filesystem.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh +source $rootdir/scripts/common.sh + +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +rpc_py="$rootdir/scripts/rpc.py" +# Remove lvol bdevs and stores. +function remove_backends() { + echo "INFO: Removing lvol bdev" + $rpc_py bdev_lvol_delete "lvs_0/lbd_0" + + echo "INFO: Removing lvol stores" + $rpc_py bdev_lvol_delete_lvstore -l lvs_0 + + echo "INFO: Removing NVMe" + $rpc_py bdev_nvme_detach_controller Nvme0 + + return 0 +} + +timing_enter start_iscsi_tgt + +"${ISCSI_APP[@]}" -m $ISCSI_TEST_CORE_MASK --wait-for-rpc & +pid=$! +echo "Process pid: $pid" + +trap 'killprocess $pid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +waitforlisten $pid +$rpc_py iscsi_set_options -o 30 -a 16 +$rpc_py framework_start_init +echo "iscsi_tgt is listening. Running tests..." + +timing_exit start_iscsi_tgt + +bdf=$(get_first_nvme_bdf) +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +$rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK +$rpc_py bdev_nvme_attach_controller -b "Nvme0" -t "pcie" -a $bdf + +ls_guid=$($rpc_py bdev_lvol_create_lvstore Nvme0n1 lvs_0) +free_mb=$(get_lvs_free_mb "$ls_guid") +# Using maximum 2048MiB to reduce the test time +if [ $free_mb -gt 2048 ]; then + $rpc_py bdev_lvol_create -u $ls_guid lbd_0 2048 +else + $rpc_py bdev_lvol_create -u $ls_guid lbd_0 $free_mb +fi +# "lvs_0/lbd_0:0" ==> use lvs_0/lbd_0 blockdev for LUN0 +# "1:2" ==> map PortalGroup1 to InitiatorGroup2 +# "256" ==> iSCSI queue depth 256 +# "-d" ==> disable CHAP authentication +$rpc_py iscsi_create_target_node Target1 Target1_alias 'lvs_0/lbd_0:0' $PORTAL_TAG:$INITIATOR_TAG 256 -d +sleep 1 + +iscsiadm -m discovery -t sendtargets -p $TARGET_IP:$ISCSI_PORT +iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT +waitforiscsidevices 1 + +trap 'iscsicleanup; remove_backends; umount /mnt/device; rm -rf /mnt/device; killprocess $pid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +mkdir -p /mnt/device + +dev=$(iscsiadm -m session -P 3 | grep "Attached scsi disk" | awk '{print $4}') + +waitforfile /dev/$dev +parted -s /dev/$dev mklabel msdos +parted -s /dev/$dev mkpart primary '0%' '100%' +sleep 1 + +function filesystem_test() { + fstype=$1 + + make_filesystem ${fstype} /dev/${dev}1 + mount /dev/${dev}1 /mnt/device + if [ $RUN_NIGHTLY -eq 1 ]; then + fio -filename=/mnt/device/test -direct=1 -iodepth 64 -thread=1 -invalidate=1 -rw=randwrite -ioengine=libaio -bs=4k \ + -size=1024M -name=job0 + umount /mnt/device + + iscsiadm -m node --logout + waitforiscsidevices 0 + iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT + waitforiscsidevices 1 + + dev=$(iscsiadm -m session -P 3 | grep "Attached scsi disk" | awk '{print $4}') + + waitforfile /dev/${dev}1 + mount -o rw /dev/${dev}1 /mnt/device + if [ -f "/mnt/device/test" ]; then + echo "File existed." + fio -filename=/mnt/device/test -direct=1 -iodepth 64 -thread=1 -invalidate=1 -rw=randread \ + -ioengine=libaio -bs=4k -runtime=20 -time_based=1 -name=job0 + else + echo "File doesn't exist." + exit 1 + fi + + rm -rf /mnt/device/test + umount /mnt/device + else + touch /mnt/device/aaa + umount /mnt/device + + iscsiadm -m node --logout + waitforiscsidevices 0 + iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT + waitforiscsidevices 1 + + dev=$(iscsiadm -m session -P 3 | grep "Attached scsi disk" | awk '{print $4}') + + waitforfile /dev/${dev}1 + mount -o rw /dev/${dev}1 /mnt/device + + if [ -f "/mnt/device/aaa" ]; then + echo "File existed." + else + echo "File doesn't exist." + exit 1 + fi + + rm -rf /mnt/device/aaa + umount /mnt/device + fi +} + +run_test "iscsi_tgt_filesystem_ext4" filesystem_test "ext4" +run_test "iscsi_tgt_filesystem_btrfs" filesystem_test "btrfs" +run_test "iscsi_tgt_filesystem_xfs" filesystem_test "xfs" + +rm -rf /mnt/device + +trap - SIGINT SIGTERM EXIT + +iscsicleanup +remove_backends +killprocess $pid +iscsitestfini $1 $2 diff --git a/src/spdk/test/iscsi_tgt/fio/fio.sh b/src/spdk/test/iscsi_tgt/fio/fio.sh new file mode 100755 index 000000000..ae3a2f308 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/fio/fio.sh @@ -0,0 +1,150 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +delete_tmp_files() { + rm -f $testdir/iscsi2.json + rm -f ./local-job0-0-verify.state + rm -f ./local-job1-1-verify.state +} + +function running_config() { + # dump a config file from the running iscsi_tgt + $rpc_py save_config > $testdir/iscsi2.json + sleep 1 + + # now start iscsi_tgt again using the generated config file + # keep the same iscsiadm configuration to confirm that the + # config file matched the running configuration + killprocess $pid + trap 'iscsicleanup; delete_tmp_files; exit 1' SIGINT SIGTERM EXIT + + timing_enter start_iscsi_tgt2 + + "${ISCSI_APP[@]}" --wait-for-rpc & + pid=$! + echo "Process pid: $pid" + trap 'iscsicleanup; killprocess $pid; delete_tmp_files; exit 1' SIGINT SIGTERM EXIT + waitforlisten $pid + + $rpc_py load_config < $testdir/iscsi2.json + + echo "iscsi_tgt is listening. Running tests..." + + timing_exit start_iscsi_tgt2 + + sleep 1 + $fio_py -p iscsi -i 4096 -d 1 -t randrw -r 5 +} + +if [ -z "$TARGET_IP" ]; then + echo "TARGET_IP not defined in environment" + exit 1 +fi + +if [ -z "$INITIATOR_IP" ]; then + echo "INITIATOR_IP not defined in environment" + exit 1 +fi + +MALLOC_BDEV_SIZE=64 +MALLOC_BLOCK_SIZE=4096 + +rpc_py="$rootdir/scripts/rpc.py" +fio_py="$rootdir/scripts/fio.py" + +timing_enter start_iscsi_tgt + +"${ISCSI_APP[@]}" --wait-for-rpc & +pid=$! +echo "Process pid: $pid" + +trap 'killprocess $pid; exit 1' SIGINT SIGTERM EXIT + +waitforlisten $pid + +$rpc_py load_config < $testdir/iscsi.json + +echo "iscsi_tgt is listening. Running tests..." + +timing_exit start_iscsi_tgt + +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +$rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK +# Create a RAID-0 bdev from two malloc bdevs +malloc_bdevs="$($rpc_py bdev_malloc_create $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE) " +malloc_bdevs+="$($rpc_py bdev_malloc_create $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE)" +$rpc_py bdev_raid_create -n raid0 -z 64 -r 0 -b "$malloc_bdevs" +bdev=$($rpc_py bdev_malloc_create 1024 512) +# "raid0:0" ==> use raid0 blockdev for LUN0 +# "1:2" ==> map PortalGroup1 to InitiatorGroup2 +# "64" ==> iSCSI queue depth 64 +# "-d" ==> disable CHAP authentication +$rpc_py iscsi_create_target_node Target3 Target3_alias "raid0:0 ${bdev}:1" $PORTAL_TAG:$INITIATOR_TAG 64 -d +sleep 1 + +iscsiadm -m discovery -t sendtargets -p $TARGET_IP:$ISCSI_PORT +iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT +waitforiscsidevices 2 + +trap 'iscsicleanup; killprocess $pid; iscsitestfini $1 $2; delete_tmp_files; exit 1' SIGINT SIGTERM EXIT + +$fio_py -p iscsi -i 4096 -d 1 -t randrw -r 1 -v +$fio_py -p iscsi -i 131072 -d 32 -t randrw -r 1 -v +$fio_py -p iscsi -i 524288 -d 128 -t randrw -r 1 -v +$fio_py -p iscsi -i 1048576 -d 1024 -t read -r 1 -n 4 + +if [ $RUN_NIGHTLY -eq 1 ]; then + $fio_py -p iscsi -i 4096 -d 1 -t write -r 300 -v + + # Run the running_config test which will generate a config file from the + # running iSCSI target, then kill and restart the iSCSI target using the + # generated config file + # Temporarily disabled + # running_config +fi + +# Start hotplug test case. +$fio_py -p iscsi -i 1048576 -d 128 -t rw -r 10 & +fio_pid=$! + +sleep 3 + +# Delete raid0 blockdev +$rpc_py bdev_raid_delete 'raid0' + +# Delete all allocated malloc blockdevs +for malloc_bdev in $malloc_bdevs; do + $rpc_py bdev_malloc_delete $malloc_bdev +done + +# Delete malloc device +$rpc_py bdev_malloc_delete ${bdev} + +fio_status=0 +wait $fio_pid || fio_status=$? + +if [ $fio_status -eq 0 ]; then + echo "iscsi hotplug test: fio successful - expected failure" + exit 1 +else + echo "iscsi hotplug test: fio failed as expected" +fi + +iscsicleanup +$rpc_py iscsi_delete_target_node 'iqn.2016-06.io.spdk:Target3' + +delete_tmp_files + +trap - SIGINT SIGTERM EXIT + +killprocess $pid + +iscsitestfini $1 $2 diff --git a/src/spdk/test/iscsi_tgt/fio/iscsi.json b/src/spdk/test/iscsi_tgt/fio/iscsi.json new file mode 100644 index 000000000..d901fc78b --- /dev/null +++ b/src/spdk/test/iscsi_tgt/fio/iscsi.json @@ -0,0 +1,32 @@ +{ + "subsystems": [ + { + "subsystem": "bdev", + "config": [ + { + "params": { + "retry_count": 4, + "nvme_adminq_poll_period_us": 100000, + "nvme_ioq_poll_period_us": 0, + "action_on_timeout": "none" + }, + "method": "bdev_nvme_set_options" + } + ] + }, + { + "subsystem": "iscsi", + "config": [ + { + "method": "iscsi_set_options", + "params": { + "nop_timeout": 30, + "node_base": "iqn.2016-06.io.spdk", + "max_sessions": 16, + "error_recovery_level": 0 + } + } + ] + } + ] +} diff --git a/src/spdk/test/iscsi_tgt/fuzz/fuzz.sh b/src/spdk/test/iscsi_tgt/fuzz/fuzz.sh new file mode 100755 index 000000000..bc290fa8f --- /dev/null +++ b/src/spdk/test/iscsi_tgt/fuzz/fuzz.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +if [ -z "$TARGET_IP" ]; then + echo "TARGET_IP not defined in environment" + exit 1 +fi + +if [ -z "$INITIATOR_IP" ]; then + echo "INITIATOR_IP not defined in environment" + exit 1 +fi + +timing_enter iscsi_fuzz + +MALLOC_BDEV_SIZE=64 +MALLOC_BLOCK_SIZE=4096 + +rpc_py="$rootdir/scripts/rpc.py" + +timing_enter start_iscsi_tgt + +"${ISCSI_APP[@]}" -m $ISCSI_TEST_CORE_MASK --wait-for-rpc & +iscsipid=$! +echo "Process iscsipid: $iscsipid" + +trap 'killprocess $iscsipid; exit 1' SIGINT SIGTERM EXIT + +waitforlisten $iscsipid +# Fuzz initiator do not support nop-in yet. So we increase nop-in timeout value for now. +$rpc_py iscsi_set_options -o 60 -a 16 +$rpc_py framework_start_init +echo "iscsi_tgt is listening. Running tests..." +timing_exit start_iscsi_tgt + +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +$rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK +$rpc_py bdev_malloc_create $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE +$rpc_py iscsi_create_target_node disk1 disk1_alias 'Malloc0:0' $PORTAL_TAG:$INITIATOR_TAG 256 -d +sleep 1 + +trap 'killprocess $iscsipid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +$rootdir/test/app/fuzz/iscsi_fuzz/iscsi_fuzz -m 0xF0 -T $TARGET_IP -t 30 2> $output_dir/iscsi_autofuzz_logs.txt + +$rpc_py iscsi_delete_target_node 'iqn.2016-06.io.spdk:disk1' + +# Delete malloc device +$rpc_py bdev_malloc_delete Malloc0 + +trap - SIGINT SIGTERM EXIT + +killprocess $iscsipid + +iscsitestfini $1 $2 + +timing_exit iscsi_fuzz diff --git a/src/spdk/test/iscsi_tgt/initiator/initiator.sh b/src/spdk/test/iscsi_tgt/initiator/initiator.sh new file mode 100755 index 000000000..5da1f320b --- /dev/null +++ b/src/spdk/test/iscsi_tgt/initiator/initiator.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +MALLOC_BDEV_SIZE=64 +MALLOC_BLOCK_SIZE=512 + +rpc_py="$rootdir/scripts/rpc.py" + +timing_enter start_iscsi_tgt + +"${ISCSI_APP[@]}" -m 0x2 -p 1 -s 512 --wait-for-rpc & +pid=$! +echo "iSCSI target launched. pid: $pid" +trap 'killprocess $pid;exit 1' SIGINT SIGTERM EXIT +waitforlisten $pid +$rpc_py iscsi_set_options -o 30 -a 4 +$rpc_py framework_start_init +echo "iscsi_tgt is listening. Running tests..." + +timing_exit start_iscsi_tgt + +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +$rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK +$rpc_py bdev_malloc_create $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE +# "Malloc0:0" ==> use Malloc0 blockdev for LUN0 +# "1:2" ==> map PortalGroup1 to InitiatorGroup2 +# "64" ==> iSCSI queue depth 64 +# "-d" ==> disable CHAP authentication +$rpc_py iscsi_create_target_node disk1 disk1_alias 'Malloc0:0' $PORTAL_TAG:$INITIATOR_TAG 256 -d +sleep 1 +trap 'killprocess $pid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +"$rootdir/test/bdev/bdevperf/bdevperf" --json <(initiator_json_config) -q 128 -o 4096 -w verify -t 5 -s 512 +if [ $RUN_NIGHTLY -eq 1 ]; then + "$rootdir/test/bdev/bdevperf/bdevperf" --json <(initiator_json_config) -q 128 -o 4096 -w unmap -t 5 -s 512 + "$rootdir/test/bdev/bdevperf/bdevperf" --json <(initiator_json_config) -q 128 -o 4096 -w flush -t 5 -s 512 + "$rootdir/test/bdev/bdevperf/bdevperf" --json <(initiator_json_config) -q 128 -o 4096 -w reset -t 10 -s 512 +fi + +trap - SIGINT SIGTERM EXIT + +killprocess $pid + +iscsitestfini $1 $2 diff --git a/src/spdk/test/iscsi_tgt/ip_migration/ip_migration.sh b/src/spdk/test/iscsi_tgt/ip_migration/ip_migration.sh new file mode 100755 index 000000000..d737e01b3 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/ip_migration/ip_migration.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +rpc_py="$rootdir/scripts/rpc.py" +fio_py="$rootdir/scripts/fio.py" + +# Namespaces are NOT used here on purpose. This test requires changes to detect +# ifc_index for interface that was put into namespace. Needed for net_interface_add_ip_address. +# Reset ISCSI_APP[] to use only the plain app for this test without TARGET_NS_CMD preset. +source "$rootdir/test/common/applications.sh" +NETMASK=127.0.0.0/24 +MIGRATION_ADDRESS=127.0.0.2 + +function kill_all_iscsi_target() { + for ((i = 0; i < 2; i++)); do + rpc_addr="/var/tmp/spdk${i}.sock" + $rpc_py -s $rpc_addr spdk_kill_instance SIGTERM + done +} + +function rpc_config() { + # $1 = RPC server address + # $2 = Netmask + $rpc_py -s $1 iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $2 + $rpc_py -s $1 bdev_malloc_create 64 512 +} + +function rpc_validate_ip() { + # Always delete the IP first in case it is there already + cmd="$rpc_py -s $1 net_interface_delete_ip_address 1 $MIGRATION_ADDRESS" + if $cmd; then + echo "Delete existing IP succeeded." + else + echo "Ignore the failure as IP did not exist." + fi + + cmd="$rpc_py -s $1 net_interface_add_ip_address 1 $MIGRATION_ADDRESS" + if $cmd; then + echo "Add new IP succeeded." + else + echo "Add new IP failed. Expected to succeed..." + exit 1 + fi + # Add same IP again + if $cmd; then + echo "Same IP existed. Expected to fail..." + exit 1 + fi + + cmd="$rpc_py -s $1 net_interface_delete_ip_address 1 $MIGRATION_ADDRESS" + if $cmd; then + echo "Delete existing IP succeeded." + else + echo "Delete existing IP failed. Expected to succeed..." + exit 1 + fi + # Delete same IP again + if $cmd; then + echo "No required IP existed. Expected to fail..." + exit 1 + fi +} + +function rpc_add_target_node() { + $rpc_py -s $1 net_interface_add_ip_address 1 $MIGRATION_ADDRESS + $rpc_py -s $1 iscsi_create_portal_group $PORTAL_TAG $MIGRATION_ADDRESS:$ISCSI_PORT + $rpc_py -s $1 iscsi_create_target_node target1 target1_alias 'Malloc0:0' $PORTAL_TAG:$INITIATOR_TAG 64 -d + $rpc_py -s $1 net_interface_delete_ip_address 1 $MIGRATION_ADDRESS +} + +echo "Running ip migration tests" +for ((i = 0; i < 2; i++)); do + timing_enter start_iscsi_tgt_$i + + rpc_addr="/var/tmp/spdk${i}.sock" + + # TODO: run the different iSCSI instances on non-overlapping CPU masks + "${ISCSI_APP[@]}" -r $rpc_addr -i $i -m $ISCSI_TEST_CORE_MASK --wait-for-rpc & + pid=$! + echo "Process pid: $pid" + + trap 'kill_all_iscsi_target; exit 1' SIGINT SIGTERM EXIT + + waitforlisten $pid $rpc_addr + $rpc_py -s $rpc_addr iscsi_set_options -o 30 -a 64 + $rpc_py -s $rpc_addr framework_start_init + echo "iscsi_tgt is listening. Running tests..." + + timing_exit start_iscsi_tgt_$i + + rpc_config $rpc_addr $NETMASK + trap 'kill_all_iscsi_target; iscsitestfini $1 $2; exit 1' \ + SIGINT SIGTERM EXIT +done + +rpc_first_addr="/var/tmp/spdk0.sock" +rpc_validate_ip $rpc_first_addr +rpc_add_target_node $rpc_first_addr + +sleep 1 +iscsiadm -m discovery -t sendtargets -p $MIGRATION_ADDRESS:$ISCSI_PORT +sleep 1 +iscsiadm -m node --login -p $MIGRATION_ADDRESS:$ISCSI_PORT +waitforiscsidevices 1 + +# fio tests for multi-process +$fio_py -p iscsi -i 4096 -d 32 -t randrw -r 12 & +fiopid=$! +sleep 3 + +$rpc_py -s $rpc_first_addr spdk_kill_instance SIGTERM + +rpc_second_addr="/var/tmp/spdk1.sock" +rpc_add_target_node $rpc_second_addr + +wait $fiopid + +trap - SIGINT SIGTERM EXIT + +iscsicleanup + +$rpc_py -s $rpc_second_addr spdk_kill_instance SIGTERM +iscsitestfini $1 $2 diff --git a/src/spdk/test/iscsi_tgt/iscsi_tgt.sh b/src/spdk/test/iscsi_tgt/iscsi_tgt.sh new file mode 100755 index 000000000..0316229b6 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/iscsi_tgt.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../..) +source $rootdir/test/common/autotest_common.sh + +if [ ! $(uname -s) = Linux ]; then + exit 0 +fi + +source $rootdir/test/iscsi_tgt/common.sh + +# $1 = test type (posix/vpp) +if [ "$1" == "posix" ] || [ "$1" == "vpp" ]; then + TEST_TYPE=$1 +else + echo "No iSCSI test type specified" + exit 1 +fi + +# Run cleanup once to make sure we remove any stale iscsiadm +# entries if they were missed in previous runs +iscsicleanup + +# Network configuration +create_veth_interfaces $TEST_TYPE + +trap 'cleanup_veth_interfaces $TEST_TYPE; exit 1' SIGINT SIGTERM EXIT + +run_test "iscsi_tgt_sock" ./test/iscsi_tgt/sock/sock.sh $TEST_TYPE +if [ "$TEST_TYPE" == "posix" ]; then + # calsoft doesn't handle TCP stream properly and fails decoding iSCSI + # requests when are divided by TCP segmentation. This is very common + # situation for VPP and causes that calsoft.sh never PASS. + if [[ -d /usr/local/calsoft ]]; then + run_test "iscsi_tgt_calsoft" ./test/iscsi_tgt/calsoft/calsoft.sh + else + skip_run_test_with_warning "WARNING: Calsoft binaries not found, skipping test!" + fi +fi +run_test "iscsi_tgt_filesystem" ./test/iscsi_tgt/filesystem/filesystem.sh +run_test "iscsi_tgt_reset" ./test/iscsi_tgt/reset/reset.sh +run_test "iscsi_tgt_rpc_config" ./test/iscsi_tgt/rpc_config/rpc_config.sh $TEST_TYPE +run_test "iscsi_tgt_iscsi_lvol" ./test/iscsi_tgt/lvol/iscsi_lvol.sh +run_test "iscsi_tgt_fio" ./test/iscsi_tgt/fio/fio.sh +run_test "iscsi_tgt_qos" ./test/iscsi_tgt/qos/qos.sh + +# IP Migration tests do not support network namespaces, +# they can only be run on posix sockets. +if [ "$TEST_TYPE" == "posix" ]; then + run_test "iscsi_tgt_ip_migration" ./test/iscsi_tgt/ip_migration/ip_migration.sh +fi +run_test "iscsi_tgt_trace_record" ./test/iscsi_tgt/trace_record/trace_record.sh + +if [ $RUN_NIGHTLY -eq 1 ]; then + if [ $SPDK_TEST_PMDK -eq 1 ]; then + run_test "iscsi_tgt_pmem" ./test/iscsi_tgt/pmem/iscsi_pmem.sh 4096 10 + fi + run_test "iscsi_tgt_ext4test" ./test/iscsi_tgt/ext4test/ext4test.sh + run_test "iscsi_tgt_digests" ./test/iscsi_tgt/digests/digests.sh +fi +if [ $SPDK_TEST_RBD -eq 1 ]; then + # RBD tests do not support network namespaces, + # they can only be run on posix sockets. + if [ "$TEST_TYPE" == "posix" ]; then + if ! hash ceph; then + echo "ERROR: SPDK_TEST_RBD requested but no ceph installed!" + false + fi + run_test "iscsi_tgt_rbd" ./test/iscsi_tgt/rbd/rbd.sh + fi +fi + +trap 'cleanup_veth_interfaces $TEST_TYPE; exit 1' SIGINT SIGTERM EXIT + +if [ $SPDK_TEST_NVMF -eq 1 ]; then + # NVMe-oF tests do not support network namespaces, + # they can only be run on posix sockets. + if [ "$TEST_TYPE" == "posix" ]; then + # Test configure remote NVMe device from rpc and conf file + run_test "iscsi_tgt_fio_remote_nvme" ./test/iscsi_tgt/nvme_remote/fio_remote_nvme.sh + fi +fi + +if [ $RUN_NIGHTLY -eq 1 ]; then + if [ "$TEST_TYPE" == "posix" ]; then + run_test "iscsi_tgt_fuzz" ./test/iscsi_tgt/fuzz/fuzz.sh + fi + run_test "iscsi_tgt_multiconnection" ./test/iscsi_tgt/multiconnection/multiconnection.sh +fi + +if [ $SPDK_TEST_ISCSI_INITIATOR -eq 1 ]; then + run_test "iscsi_tgt_initiator" ./test/iscsi_tgt/initiator/initiator.sh + run_test "iscsi_tgt_bdev_io_wait" ./test/iscsi_tgt/bdev_io_wait/bdev_io_wait.sh +fi + +cleanup_veth_interfaces $TEST_TYPE +trap - SIGINT SIGTERM EXIT diff --git a/src/spdk/test/iscsi_tgt/lvol/iscsi_lvol.sh b/src/spdk/test/iscsi_tgt/lvol/iscsi_lvol.sh new file mode 100755 index 000000000..e55899d56 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/lvol/iscsi_lvol.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +MALLOC_BDEV_SIZE=128 +MALLOC_BLOCK_SIZE=512 +if [ $RUN_NIGHTLY -eq 1 ]; then + NUM_LVS=10 + NUM_LVOL=10 +else + NUM_LVS=2 + NUM_LVOL=2 +fi + +rpc_py="$rootdir/scripts/rpc.py" +fio_py="$rootdir/scripts/fio.py" + +timing_enter start_iscsi_tgt + +"${ISCSI_APP[@]}" -m $ISCSI_TEST_CORE_MASK --wait-for-rpc & +pid=$! +echo "Process pid: $pid" + +trap 'iscsicleanup; killprocess $pid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +waitforlisten $pid +$rpc_py iscsi_set_options -o 30 -a 16 +$rpc_py framework_start_init +echo "iscsi_tgt is listening. Running tests..." + +timing_exit start_iscsi_tgt + +timing_enter setup +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +# Create the first LVS from a Raid-0 bdev, which is created from two malloc bdevs +# Create remaining LVSs from a malloc bdev, respectively +for i in $(seq 1 $NUM_LVS); do + INITIATOR_TAG=$((i + 2)) + $rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK + if [ $i -eq 1 ]; then + # construct RAID bdev and put its name in $bdev + malloc_bdevs="$($rpc_py bdev_malloc_create $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE) " + malloc_bdevs+="$($rpc_py bdev_malloc_create $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE)" + $rpc_py bdev_raid_create -n raid0 -z 64 -r 0 -b "$malloc_bdevs" + bdev="raid0" + else + # construct malloc bdev and put its name in $bdev + bdev=$($rpc_py bdev_malloc_create $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE) + fi + ls_guid=$($rpc_py bdev_lvol_create_lvstore $bdev lvs_$i -c 1048576) + LUNs="" + for j in $(seq 1 $NUM_LVOL); do + lb_name=$($rpc_py bdev_lvol_create -u $ls_guid lbd_$j 10) + LUNs+="$lb_name:$((j - 1)) " + done + $rpc_py iscsi_create_target_node Target$i Target${i}_alias "$LUNs" "1:$INITIATOR_TAG" 256 -d +done +timing_exit setup + +sleep 1 + +timing_enter discovery +iscsiadm -m discovery -t sendtargets -p $TARGET_IP:$ISCSI_PORT +iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT +waitforiscsidevices $((NUM_LVS * NUM_LVOL)) +timing_exit discovery + +timing_enter fio +$fio_py -p iscsi -i 131072 -d 8 -t randwrite -r 10 -v +timing_exit fio + +rm -f ./local-job0-0-verify.state +trap - SIGINT SIGTERM EXIT + +rm -f ./local-job* +iscsicleanup +killprocess $pid +iscsitestfini $1 $2 diff --git a/src/spdk/test/iscsi_tgt/multiconnection/multiconnection.sh b/src/spdk/test/iscsi_tgt/multiconnection/multiconnection.sh new file mode 100755 index 000000000..badf70197 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/multiconnection/multiconnection.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +rpc_py="$rootdir/scripts/rpc.py" +fio_py="$rootdir/scripts/fio.py" + +CONNECTION_NUMBER=30 + +# Remove lvol bdevs and stores. +function remove_backends() { + echo "INFO: Removing lvol bdevs" + for i in $(seq 1 $CONNECTION_NUMBER); do + lun="lvs0/lbd_$i" + $rpc_py bdev_lvol_delete $lun + echo -e "\tINFO: lvol bdev $lun removed" + done + sleep 1 + + echo "INFO: Removing lvol stores" + $rpc_py bdev_lvol_delete_lvstore -l lvs0 + echo "INFO: lvol store lvs0 removed" + + echo "INFO: Removing NVMe" + $rpc_py bdev_nvme_detach_controller Nvme0 + + return 0 +} + +timing_enter start_iscsi_tgt +"${ISCSI_APP[@]}" --wait-for-rpc & +iscsipid=$! +echo "iSCSI target launched. pid: $iscsipid" +trap 'remove_backends; iscsicleanup; killprocess $iscsipid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +waitforlisten $iscsipid +$rpc_py iscsi_set_options -o 30 -a 128 +$rpc_py framework_start_init +$rootdir/scripts/gen_nvme.sh --json | $rpc_py load_subsystem_config +timing_exit start_iscsi_tgt + +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +$rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK + +echo "Creating an iSCSI target node." +ls_guid=$($rpc_py bdev_lvol_create_lvstore "Nvme0n1" "lvs0" -c 1048576) + +# Assign even size for each lvol_bdev. +get_lvs_free_mb $ls_guid +lvol_bdev_size=$((free_mb / CONNECTION_NUMBER)) +for i in $(seq 1 $CONNECTION_NUMBER); do + $rpc_py bdev_lvol_create -u $ls_guid lbd_$i $lvol_bdev_size +done + +for i in $(seq 1 $CONNECTION_NUMBER); do + lun="lvs0/lbd_$i:0" + $rpc_py iscsi_create_target_node Target$i Target${i}_alias "$lun" $PORTAL_TAG:$INITIATOR_TAG 256 -d +done +sleep 1 + +echo "Logging into iSCSI target." +iscsiadm -m discovery -t sendtargets -p $TARGET_IP:$ISCSI_PORT +iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT +waitforiscsidevices $CONNECTION_NUMBER + +echo "Running FIO" +$fio_py -p iscsi -i 131072 -d 64 -t randrw -r 5 +$fio_py -p iscsi -i 262144 -d 16 -t randwrite -r 10 +sync + +trap - SIGINT SIGTERM EXIT + +rm -f ./local-job* +iscsicleanup +remove_backends +killprocess $iscsipid +iscsitestfini $1 $2 diff --git a/src/spdk/test/iscsi_tgt/nvme_remote/fio_remote_nvme.sh b/src/spdk/test/iscsi_tgt/nvme_remote/fio_remote_nvme.sh new file mode 100755 index 000000000..38329dc43 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/nvme_remote/fio_remote_nvme.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/nvmf/common.sh +source $rootdir/test/iscsi_tgt/common.sh + +nvmftestinit +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +rpc_py="$rootdir/scripts/rpc.py" +fio_py="$rootdir/scripts/fio.py" + +# Namespaces are NOT used here on purpose. Rxe_cfg utilility used for NVMf tests do not support namespaces. +TARGET_IP=127.0.0.1 +INITIATOR_IP=127.0.0.1 +NETMASK=$INITIATOR_IP/32 + +function run_nvme_remote() { + echo "now use $1 method to run iscsi tgt." + + iscsi_rpc_addr="/var/tmp/spdk-iscsi.sock" + "${ISCSI_APP[@]}" -r "$iscsi_rpc_addr" -m 0x1 -p 0 -s 512 --wait-for-rpc & + iscsipid=$! + echo "iSCSI target launched. pid: $iscsipid" + trap 'killprocess $iscsipid; iscsitestfini $1 $2; nvmftestfini; exit 1' SIGINT SIGTERM EXIT + waitforlisten $iscsipid "$iscsi_rpc_addr" + $rpc_py -s "$iscsi_rpc_addr" iscsi_set_options -o 30 -a 16 + $rpc_py -s "$iscsi_rpc_addr" framework_start_init + if [ "$1" = "remote" ]; then + $rpc_py -s $iscsi_rpc_addr bdev_nvme_attach_controller -b "Nvme0" -t "rdma" -f "ipv4" -a $NVMF_FIRST_TARGET_IP -s $NVMF_PORT -n nqn.2016-06.io.spdk:cnode1 + fi + + echo "iSCSI target has started." + + timing_exit start_iscsi_tgt + + echo "Creating an iSCSI target node." + $rpc_py -s "$iscsi_rpc_addr" iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT + $rpc_py -s "$iscsi_rpc_addr" iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK + if [ "$1" = "local" ]; then + $rpc_py -s "$iscsi_rpc_addr" bdev_nvme_attach_controller -b "Nvme0" -t "rdma" -f "ipv4" -a $NVMF_FIRST_TARGET_IP -s $NVMF_PORT -n nqn.2016-06.io.spdk:cnode1 + fi + $rpc_py -s "$iscsi_rpc_addr" iscsi_create_target_node Target1 Target1_alias 'Nvme0n1:0' $PORTAL_TAG:$INITIATOR_TAG 64 -d + sleep 1 + + echo "Logging in to iSCSI target." + iscsiadm -m discovery -t sendtargets -p $TARGET_IP:$ISCSI_PORT + iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT +} + +# Start the NVMf target +"${NVMF_APP[@]}" -m 0x2 -p 1 -s 512 --wait-for-rpc & +nvmfpid=$! +echo "NVMf target launched. pid: $nvmfpid" +trap 'iscsitestfini $1 $2; nvmftestfini; exit 1' SIGINT SIGTERM EXIT +waitforlisten $nvmfpid +$rpc_py framework_start_init +$rpc_py nvmf_create_transport -t RDMA -u 8192 +echo "NVMf target has started." +bdevs=$($rpc_py bdev_malloc_create 64 512) +$rpc_py nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s SPDK00000000000001 +$rpc_py nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t rdma -a $NVMF_FIRST_TARGET_IP -s $NVMF_PORT +for bdev in $bdevs; do + $rpc_py nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 $bdev +done +echo "NVMf subsystem created." + +timing_enter start_iscsi_tgt + +run_nvme_remote "local" + +trap 'iscsicleanup; killprocess $iscsipid; + rm -f ./local-job0-0-verify.state; iscsitestfini $1 $2; nvmftestfini; exit 1' SIGINT SIGTERM EXIT + +echo "Running FIO" +$fio_py -p iscsi -i 4096 -d 1 -t randrw -r 1 -v + +rm -f ./local-job0-0-verify.state +iscsicleanup +killprocess $iscsipid + +run_nvme_remote "remote" + +echo "Running FIO" +$fio_py -p iscsi -i 4096 -d 1 -t randrw -r 1 -v + +rm -f ./local-job0-0-verify.state +trap - SIGINT SIGTERM EXIT + +iscsicleanup +killprocess $iscsipid +$rpc_py nvmf_delete_subsystem nqn.2016-06.io.spdk:cnode1 + +iscsitestfini $1 $2 +nvmftestfini diff --git a/src/spdk/test/iscsi_tgt/perf/iscsi_initiator.sh b/src/spdk/test/iscsi_tgt/perf/iscsi_initiator.sh new file mode 100755 index 000000000..b7d08bbc2 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/perf/iscsi_initiator.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +testdir=$(readlink -f $(dirname $0)) +ISCSI_PORT=3260 +FIO_PATH=$1 +IP_T=$2 + +set -xe +trap "exit 1" ERR SIGTERM SIGABRT + +if [ ! -x $FIO_PATH/fio ]; then + error "Invalid path of fio binary" +fi + +function run_spdk_iscsi_fio() { + $FIO_PATH/fio $testdir/perf.job "$@" --output-format=json +} + +mkdir -p $testdir/perf_output +iscsi_fio_results="$testdir/perf_output/iscsi_fio.json" +trap "iscsiadm -m node --logout; iscsiadm -m node -o delete; exit 1" ERR SIGTERM SIGABRT +iscsiadm -m discovery -t sendtargets -p $IP_T:$ISCSI_PORT +iscsiadm -m node --login -p $IP_T:$ISCSI_PORT +waitforiscsidevices 1 + +disks=($(iscsiadm -m session -P 3 | grep "Attached scsi disk" | awk '{print $4}')) +for ((i = 0; i < ${#disks[@]}; i++)); do + filename+=$(printf /dev/%s: "${disks[i]}") + waitforfile $filename + echo noop > /sys/block/${disks[i]}/queue/scheduler + echo "2" > /sys/block/${disks[i]}/queue/nomerges + echo "1024" > /sys/block/${disks[i]}/queue/nr_requests +done + +run_spdk_iscsi_fio --filename=$filename "--output=$iscsi_fio_results" + +iscsiadm -m node --logout || true +iscsiadm -m node -o delete || true diff --git a/src/spdk/test/iscsi_tgt/perf/iscsi_target.sh b/src/spdk/test/iscsi_tgt/perf/iscsi_target.sh new file mode 100755 index 000000000..ec02f9e0c --- /dev/null +++ b/src/spdk/test/iscsi_tgt/perf/iscsi_target.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +rpc_py="$rootdir/scripts/rpc.py -s $testdir/rpc_iscsi.sock" + +BLK_SIZE=4096 +RW=randrw +MIX=100 +IODEPTH=128 +RUNTIME=60 +RAMP_TIME=10 +FIO_PATH=$CONFIG_FIO_SOURCE_DIR +DISKNO="ALL" +CPUMASK=0x02 +NUM_JOBS=1 +ISCSI_TGT_CM=0x02 + +# Performance test for iscsi_tgt, run on devices with proper hardware support (target and inititator) +function usage() { + [[ -n $2 ]] && ( + echo "$2" + echo "" + ) + echo "Usage: $(basename $1) [options]" + echo "-h, --help Print help and exit" + echo " --fiopath=PATH Path to fio directory on initiator. [default=$FIO_PATH]" + echo " --disk_no=INT,ALL Number of disks to test on, if =ALL then test on all found disks. [default=$DISKNO]" + echo " --target_ip=IP The IP address of target used for test." + echo " --initiator_ip=IP The IP address of initiator used for test." + echo " --init_mgmnt_ip=IP The IP address of initiator used for communication." + echo " --iscsi_tgt_mask=HEX iscsi_tgt core mask. [default=$ISCSI_TGT_CM]" +} + +while getopts 'h-:' optchar; do + case "$optchar" in + -) + case "$OPTARG" in + help) + usage $0 + exit 0 + ;; + fiopath=*) FIO_BIN="${OPTARG#*=}" ;; + disk_no=*) DISKNO="${OPTARG#*=}" ;; + target_ip=*) TARGET_IP="${OPTARG#*=}" ;; + initiator_ip=*) INITIATOR_IP="${OPTARG#*=}" ;; + init_mgmnt_ip=*) IP_I_SSH="${OPTARG#*=}" ;; + iscsi_tgt_mask=*) ISCSI_TGT_CM="${OPTARG#*=}" ;; + *) + usage $0 echo "Invalid argument '$OPTARG'" + exit 1 + ;; + esac + ;; + h) + usage $0 + exit 0 + ;; + *) + usage $0 "Invalid argument '$optchar'" + exit 1 + ;; + esac +done + +if [ -z "$TARGET_IP" ]; then + error "No IP address of iscsi target is given" +fi + +if [ -z "$INITIATOR_IP" ]; then + error "No IP address of iscsi initiator is given" +fi + +if [ -z "$IP_I_SSH" ]; then + error "No IP address of initiator is given" +fi + +if [ $EUID -ne 0 ]; then + error "INFO: This script must be run with root privileges" +fi + +function ssh_initiator() { + # shellcheck disable=SC2029 + # (we want to expand $@ immediately, not on the VM) + ssh -i $HOME/.ssh/spdk_vhost_id_rsa root@$IP_I_SSH "$@" +} + +NETMASK=$INITIATOR_IP/32 +iscsi_fio_results="$testdir/perf_output/iscsi_fio.json" +rm -rf $iscsi_fio_results +mkdir -p $testdir/perf_output +touch $iscsi_fio_results + +timing_enter run_iscsi_app +$SPDK_BIN_DIR/iscsi_tgt -m $ISCSI_TGT_CM -r $testdir/rpc_iscsi.sock --wait-for-rpc & +pid=$! +trap 'rm -f $testdir/perf.job; killprocess $pid; print_backtrace; exit 1' ERR SIGTERM SIGABRT +waitforlisten "$pid" "$testdir/rpc_iscsi.sock" +$rpc_py iscsi_set_options -b "iqn.2016-06.io.spdk" -f "/usr/local/etc/spdk/auth.conf" -o 30 -i -l 0 -a 16 +$rpc_py framework_start_init +$rootdir/scripts/gen_nvme.sh --json | $rpc_py load_subsystem_config +sleep 1 +timing_exit run_iscsi_app + +timing_enter iscsi_config +bdevs=($($rpc_py bdev_get_bdevs | jq -r '.[].name')) +if [[ $DISKNO == "ALL" ]] || [[ $DISKNO == "all" ]]; then + DISKNO=${#bdevs[@]} +elif [[ $DISKNO -gt ${#bdevs[@]} ]] || [[ ! $DISKNO =~ ^[0-9]+$ ]]; then + error "Required device number ($DISKNO) is not a valid number or it's larger than the number of devices found (${#bdevs[@]})" +fi + +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +$rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK + +for ((i = 0; i < DISKNO; i++)); do + $rpc_py iscsi_create_target_node Target${i} Target${i}_alias "${bdevs[i]}:0" "$PORTAL_TAG:$INITIATOR_TAG" 64 -d +done + +ssh_initiator "cat > perf.job" < $testdir/perf.job +rm -f $testdir/perf.job +timing_exit iscsi_config + +timing_enter iscsi_initiator +ssh_initiator bash -s - $FIO_PATH $TARGET_IP < $testdir/iscsi_initiator.sh +timing_exit iscsi_initiator + +ssh_initiator "cat perf_output/iscsi_fio.json" > $iscsi_fio_results +ssh_initiator "rm -rf perf_output perf.job" + +killprocess $pid diff --git a/src/spdk/test/iscsi_tgt/perf/perf.job b/src/spdk/test/iscsi_tgt/perf/perf.job new file mode 100644 index 000000000..0f169d4ab --- /dev/null +++ b/src/spdk/test/iscsi_tgt/perf/perf.job @@ -0,0 +1,19 @@ +[global] +thread=1 +group_reporting=1 +direct=1 +norandommap=1 +ioengine=libaio +percentile_list=50:90:99:99.5:99.9:99.99:99.999 + +[4k_rand_read_qd_128] +stonewall +time_based=1 +runtime=60 +ramp_time=10 +bs=4096 +rw=randrw +rwmixread=100 +iodepth=128 +cpumask=0x02 +numjobs=1 diff --git a/src/spdk/test/iscsi_tgt/pmem/iscsi_pmem.sh b/src/spdk/test/iscsi_tgt/pmem/iscsi_pmem.sh new file mode 100755 index 000000000..da6fd77f4 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/pmem/iscsi_pmem.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +BLOCKSIZE=$1 +RUNTIME=$2 +PMEM_BDEVS="" +PMEM_SIZE=128 +PMEM_BLOCK_SIZE=512 +TGT_NR=10 +PMEM_PER_TGT=1 +rpc_py="$rootdir/scripts/rpc.py" +fio_py="$rootdir/scripts/fio.py" + +timing_enter start_iscsi_target +"${ISCSI_APP[@]}" -m $ISCSI_TEST_CORE_MASK --wait-for-rpc & +pid=$! +echo "Process pid: $pid" + +trap 'iscsicleanup; killprocess $pid; rm -f /tmp/pool_file*; exit 1' SIGINT SIGTERM EXIT + +waitforlisten $pid +$rpc_py iscsi_set_options -o 30 -a 16 +$rpc_py framework_start_init +echo "iscsi_tgt is listening. Running tests..." +timing_exit start_iscsi_target + +timing_enter setup +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +for i in $(seq 1 $TGT_NR); do + INITIATOR_TAG=$((i + 1)) + $rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK + + luns="" + for j in $(seq 1 $PMEM_PER_TGT); do + $rpc_py create_pmem_pool /tmp/pool_file${i}_${j} $PMEM_SIZE $PMEM_BLOCK_SIZE + bdevs_name="$($rpc_py bdev_pmem_create -n pmem${i}_${j} /tmp/pool_file${i}_${j})" + PMEM_BDEVS+="$bdevs_name " + luns+="$bdevs_name:$((j - 1)) " + done + $rpc_py iscsi_create_target_node Target$i Target${i}_alias "$luns" "1:$INITIATOR_TAG " 256 -d +done +timing_exit setup +sleep 1 + +timing_enter discovery +iscsiadm -m discovery -t sendtargets -p $TARGET_IP:$ISCSI_PORT +iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT +timing_exit discovery + +timing_enter fio_test +$fio_py -p iscsi -i $BLOCKSIZE -d 64 -t randwrite -r $RUNTIME -v +timing_exit fio_test + +iscsicleanup + +for pmem in $PMEM_BDEVS; do + $rpc_py bdev_pmem_delete $pmem +done + +for i in $(seq 1 $TGT_NR); do + for c in $(seq 1 $PMEM_PER_TGT); do + $rpc_py bdev_pmem_delete_pool /tmp/pool_file${i}_${c} + done +done + +trap - SIGINT SIGTERM EXIT + +rm -f ./local-job* +rm -f /tmp/pool_file* +killprocess $pid diff --git a/src/spdk/test/iscsi_tgt/qos/qos.sh b/src/spdk/test/iscsi_tgt/qos/qos.sh new file mode 100755 index 000000000..0a8015e18 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/qos/qos.sh @@ -0,0 +1,145 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +function run_fio() { + local bdev_name=$1 + local iostats + local start_io_count + local start_bytes_read + local end_io_count + local end_bytes_read + local run_time=5 + + iostats=$($rpc_py bdev_get_iostat -b $bdev_name) + start_io_count=$(jq -r '.bdevs[0].num_read_ops' <<< "$iostats") + start_bytes_read=$(jq -r '.bdevs[0].bytes_read' <<< "$iostats") + + $fio_py -p iscsi -i 1024 -d 128 -t randread -r $run_time + + iostats=$($rpc_py bdev_get_iostat -b $bdev_name) + end_io_count=$(jq -r '.bdevs[0].num_read_ops' <<< "$iostats") + end_bytes_read=$(jq -r '.bdevs[0].bytes_read' <<< "$iostats") + + IOPS_RESULT=$(((end_io_count - start_io_count) / run_time)) + BANDWIDTH_RESULT=$(((end_bytes_read - start_bytes_read) / run_time)) +} + +function verify_qos_limits() { + local result=$1 + local limit=$2 + + [ "$(bc <<< "$result > $limit*0.85")" -eq 1 ] \ + && [ "$(bc <<< "$result < $limit*1.05")" -eq 1 ] +} + +if [ -z "$TARGET_IP" ]; then + echo "TARGET_IP not defined in environment" + exit 1 +fi + +if [ -z "$INITIATOR_IP" ]; then + echo "INITIATOR_IP not defined in environment" + exit 1 +fi + +MALLOC_BDEV_SIZE=64 +MALLOC_BLOCK_SIZE=512 +IOPS_RESULT= +BANDWIDTH_RESULT= +rpc_py="$rootdir/scripts/rpc.py" +fio_py="$rootdir/scripts/fio.py" + +timing_enter start_iscsi_tgt + +"${ISCSI_APP[@]}" & +pid=$! +echo "Process pid: $pid" +trap 'killprocess $pid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT +waitforlisten $pid +echo "iscsi_tgt is listening. Running tests..." + +timing_exit start_iscsi_tgt + +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +$rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK +$rpc_py bdev_malloc_create $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE +# "Malloc0:0" ==> use Malloc0 blockdev for LUN0 +# "1:2" ==> map PortalGroup1 to InitiatorGroup2 +# "64" ==> iSCSI queue depth 64 +# "-d" ==> disable CHAP authentication +$rpc_py iscsi_create_target_node Target1 Target1_alias 'Malloc0:0' $PORTAL_TAG:$INITIATOR_TAG 64 -d +sleep 1 + +iscsiadm -m discovery -t sendtargets -p $TARGET_IP:$ISCSI_PORT +iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT + +trap 'iscsicleanup; killprocess $pid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +# Run FIO without any QOS limits to determine the raw performance +run_fio Malloc0 + +# Set IOPS/bandwidth limit to 50% of the actual unrestrained performance. +IOPS_LIMIT=$((IOPS_RESULT / 2)) +BANDWIDTH_LIMIT=$((BANDWIDTH_RESULT / 2)) +# Set READ bandwidth limit to 50% of the RW bandwidth limit to be able +# to differentiate those two. +READ_BANDWIDTH_LIMIT=$((BANDWIDTH_LIMIT / 2)) + +# Also round them down to nearest multiple of either 1000 IOPS or 1MB BW +# which are the minimal QoS granularities +IOPS_LIMIT=$((IOPS_LIMIT / 1000 * 1000)) +BANDWIDTH_LIMIT_MB=$((BANDWIDTH_LIMIT / 1024 / 1024)) +BANDWIDTH_LIMIT=$((BANDWIDTH_LIMIT_MB * 1024 * 1024)) +READ_BANDWIDTH_LIMIT_MB=$((READ_BANDWIDTH_LIMIT / 1024 / 1024)) +READ_BANDWIDTH_LIMIT=$((READ_BANDWIDTH_LIMIT_MB * 1024 * 1024)) + +# Limit the I/O rate by RPC, then confirm the observed rate matches. +$rpc_py bdev_set_qos_limit Malloc0 --rw_ios_per_sec $IOPS_LIMIT +run_fio Malloc0 +verify_qos_limits $IOPS_RESULT $IOPS_LIMIT + +# Now disable the rate limiting, and confirm the observed rate is not limited anymore. +$rpc_py bdev_set_qos_limit Malloc0 --rw_ios_per_sec 0 +run_fio Malloc0 +[ "$IOPS_RESULT" -gt "$IOPS_LIMIT" ] + +# Limit the I/O rate again. +$rpc_py bdev_set_qos_limit Malloc0 --rw_ios_per_sec $IOPS_LIMIT +run_fio Malloc0 +verify_qos_limits $IOPS_RESULT $IOPS_LIMIT + +echo "I/O rate limiting tests successful" + +# Limit the I/O bandwidth rate by RPC, then confirm the observed rate matches. +$rpc_py bdev_set_qos_limit Malloc0 --rw_ios_per_sec 0 --rw_mbytes_per_sec $BANDWIDTH_LIMIT_MB +run_fio Malloc0 +verify_qos_limits $BANDWIDTH_RESULT $BANDWIDTH_LIMIT + +# Now disable the bandwidth rate limiting, and confirm the observed rate is not limited anymore. +$rpc_py bdev_set_qos_limit Malloc0 --rw_mbytes_per_sec 0 +run_fio Malloc0 +[ "$BANDWIDTH_RESULT" -gt "$BANDWIDTH_LIMIT" ] + +# Limit the I/O bandwidth rate again with both read/write and read/only. +$rpc_py bdev_set_qos_limit Malloc0 --rw_mbytes_per_sec $BANDWIDTH_LIMIT_MB --r_mbytes_per_sec $READ_BANDWIDTH_LIMIT_MB +run_fio Malloc0 +verify_qos_limits $BANDWIDTH_RESULT $READ_BANDWIDTH_LIMIT + +echo "I/O bandwidth limiting tests successful" + +iscsicleanup +$rpc_py iscsi_delete_target_node 'iqn.2016-06.io.spdk:Target1' + +rm -f ./local-job0-0-verify.state +trap - SIGINT SIGTERM EXIT +killprocess $pid + +iscsitestfini $1 $2 diff --git a/src/spdk/test/iscsi_tgt/rbd/rbd.sh b/src/spdk/test/iscsi_tgt/rbd/rbd.sh new file mode 100755 index 000000000..060cc7af0 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/rbd/rbd.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +timing_enter rbd_setup +rbd_setup $TARGET_IP $TARGET_NAMESPACE +trap 'rbd_cleanup; exit 1' SIGINT SIGTERM EXIT +timing_exit rbd_setup + +rpc_py="$rootdir/scripts/rpc.py" +fio_py="$rootdir/scripts/fio.py" + +timing_enter start_iscsi_tgt + +"${ISCSI_APP[@]}" -m $ISCSI_TEST_CORE_MASK --wait-for-rpc & +pid=$! + +trap 'killprocess $pid; rbd_cleanup; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +waitforlisten $pid +$rpc_py iscsi_set_options -o 30 -a 16 +$rpc_py framework_start_init +echo "iscsi_tgt is listening. Running tests..." + +timing_exit start_iscsi_tgt + +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +$rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK +rbd_bdev="$($rpc_py bdev_rbd_create $RBD_POOL $RBD_NAME 4096)" +$rpc_py bdev_get_bdevs + +$rpc_py bdev_rbd_resize $rbd_bdev 2000 +num_block=$($rpc_py bdev_get_bdevs | grep num_blocks | sed 's/[^[:digit:]]//g') +# get the bdev size in MiB. +total_size=$((num_block * 4096 / 1048576)) +if [ $total_size != 2000 ]; then + echo "resize failed." + exit 1 +fi +# "Ceph0:0" ==> use Ceph0 blockdev for LUN0 +# "1:2" ==> map PortalGroup1 to InitiatorGroup2 +# "64" ==> iSCSI queue depth 64 +# "-d" ==> disable CHAP authentication +$rpc_py iscsi_create_target_node Target3 Target3_alias 'Ceph0:0' $PORTAL_TAG:$INITIATOR_TAG 64 -d +sleep 1 + +iscsiadm -m discovery -t sendtargets -p $TARGET_IP:$ISCSI_PORT +iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT +waitforiscsidevices 1 + +trap 'iscsicleanup; killprocess $pid; rbd_cleanup; exit 1' SIGINT SIGTERM EXIT + +$fio_py -p iscsi -i 4096 -d 1 -t randrw -r 1 -v +$fio_py -p iscsi -i 131072 -d 32 -t randrw -r 1 -v + +rm -f ./local-job0-0-verify.state + +trap - SIGINT SIGTERM EXIT + +iscsicleanup +$rpc_py bdev_rbd_delete $rbd_bdev +killprocess $pid +rbd_cleanup + +iscsitestfini $1 $2 diff --git a/src/spdk/test/iscsi_tgt/reset/reset.sh b/src/spdk/test/iscsi_tgt/reset/reset.sh new file mode 100755 index 000000000..406a10c45 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/reset/reset.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +MALLOC_BDEV_SIZE=64 +MALLOC_BLOCK_SIZE=512 + +rpc_py="$rootdir/scripts/rpc.py" +fio_py="$rootdir/scripts/fio.py" + +if ! hash sg_reset; then + exit 1 +fi + +timing_enter start_iscsi_tgt + +"${ISCSI_APP[@]}" --wait-for-rpc & +pid=$! +echo "Process pid: $pid" + +trap 'killprocess $pid; exit 1' SIGINT SIGTERM EXIT + +waitforlisten $pid +$rpc_py iscsi_set_options -o 30 -a 16 +$rpc_py framework_start_init +echo "iscsi_tgt is listening. Running tests..." + +timing_exit start_iscsi_tgt + +$rpc_py iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT +$rpc_py iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK +$rpc_py bdev_malloc_create $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE +# "Malloc0:0" ==> use Malloc0 blockdev for LUN0 +# "1:2" ==> map PortalGroup1 to InitiatorGroup2 +# "64" ==> iSCSI queue depth 64 +# "-d" ==> disable CHAP authentication +$rpc_py iscsi_create_target_node Target3 Target3_alias 'Malloc0:0' $PORTAL_TAG:$INITIATOR_TAG 64 -d +sleep 1 + +iscsiadm -m discovery -t sendtargets -p $TARGET_IP:$ISCSI_PORT +iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT +waitforiscsidevices 1 + +dev=$(iscsiadm -m session -P 3 | grep "Attached scsi disk" | awk '{print $4}') + +$fio_py -p iscsi -i 512 -d 1 -t read -r 60 & +fiopid=$! +echo "FIO pid: $fiopid" + +trap 'iscsicleanup; killprocess $pid; killprocess $fiopid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +# Do 3 resets while making sure iscsi_tgt and fio are still running +for i in 1 2 3; do + sleep 1 + kill -s 0 $pid + kill -s 0 $fiopid + sg_reset -d /dev/$dev + sleep 1 + kill -s 0 $pid + kill -s 0 $fiopid +done + +kill $fiopid +wait $fiopid || true + +trap - SIGINT SIGTERM EXIT + +iscsicleanup +killprocess $pid +iscsitestfini $1 $2 diff --git a/src/spdk/test/iscsi_tgt/rpc_config/rpc_config.py b/src/spdk/test/iscsi_tgt/rpc_config/rpc_config.py new file mode 100755 index 000000000..9f8e32909 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/rpc_config/rpc_config.py @@ -0,0 +1,481 @@ +#!/usr/bin/env python3 + + +import os +import os.path +import re +import sys +import time +import json +import random +from subprocess import check_call, call, check_output, Popen, PIPE, CalledProcessError + +if (len(sys.argv) == 8): + target_ip = sys.argv[2] + initiator_ip = sys.argv[3] + port = sys.argv[4] + netmask = sys.argv[5] + namespace = sys.argv[6] + test_type = sys.argv[7] + +ns_cmd = 'ip netns exec ' + namespace +other_ip = '127.0.0.6' +initiator_name = 'ANY' +portal_tag = '1' +initiator_tag = '1' + +rpc_param = { + 'target_ip': target_ip, + 'initiator_ip': initiator_ip, + 'port': port, + 'initiator_name': initiator_name, + 'netmask': netmask, + 'lun_total': 3, + 'malloc_bdev_size': 64, + 'malloc_block_size': 512, + 'queue_depth': 64, + 'target_name': 'Target3', + 'alias_name': 'Target3_alias', + 'disable_chap': True, + 'mutual_chap': False, + 'require_chap': False, + 'chap_group': 0, + 'header_digest': False, + 'data_digest': False, + 'log_flag': 'rpc', + 'cpumask': 0x1 +} + + +class RpcException(Exception): + + def __init__(self, retval, msg): + super(RpcException, self).__init__(msg) + self.retval = retval + self.message = msg + + +class spdk_rpc(object): + + def __init__(self, rpc_py): + self.rpc_py = rpc_py + + def __getattr__(self, name): + def call(*args): + cmd = "{} {}".format(self.rpc_py, name) + for arg in args: + cmd += " {}".format(arg) + return check_output(cmd, shell=True).decode("utf-8") + return call + + +def verify(expr, retcode, msg): + if not expr: + raise RpcException(retcode, msg) + + +def verify_log_flag_rpc_methods(rpc_py, rpc_param): + rpc = spdk_rpc(rpc_py) + output = rpc.log_get_flags() + jsonvalue = json.loads(output) + verify(not jsonvalue[rpc_param['log_flag']], 1, + "log_get_flags returned {}, expected false".format(jsonvalue)) + rpc.log_set_flag(rpc_param['log_flag']) + output = rpc.log_get_flags() + jsonvalue = json.loads(output) + verify(jsonvalue[rpc_param['log_flag']], 1, + "log_get_flags returned {}, expected true".format(jsonvalue)) + rpc.log_clear_flag(rpc_param['log_flag']) + output = rpc.log_get_flags() + jsonvalue = json.loads(output) + verify(not jsonvalue[rpc_param['log_flag']], 1, + "log_get_flags returned {}, expected false".format(jsonvalue)) + + print("verify_log_flag_rpc_methods passed") + + +def verify_iscsi_connection_rpc_methods(rpc_py): + rpc = spdk_rpc(rpc_py) + output = rpc.iscsi_get_connections() + jsonvalue = json.loads(output) + verify(not jsonvalue, 1, + "iscsi_get_connections returned {}, expected empty".format(jsonvalue)) + + rpc.bdev_malloc_create(rpc_param['malloc_bdev_size'], rpc_param['malloc_block_size']) + rpc.iscsi_create_portal_group(portal_tag, "{}:{}".format(rpc_param['target_ip'], str(rpc_param['port']))) + rpc.iscsi_create_initiator_group(initiator_tag, rpc_param['initiator_name'], rpc_param['netmask']) + + lun_mapping = "Malloc" + str(rpc_param['lun_total']) + ":0" + net_mapping = portal_tag + ":" + initiator_tag + rpc.iscsi_create_target_node(rpc_param['target_name'], rpc_param['alias_name'], lun_mapping, + net_mapping, rpc_param['queue_depth'], '-d') + check_output('iscsiadm -m discovery -t st -p {}'.format(rpc_param['target_ip']), shell=True) + check_output('iscsiadm -m node --login', shell=True) + name = json.loads(rpc.iscsi_get_target_nodes())[0]['name'] + output = rpc.iscsi_get_connections() + jsonvalues = json.loads(output) + verify(jsonvalues[0]['target_node_name'] == rpc_param['target_name'], 1, + "target node name vaule is {}, expected {}".format(jsonvalues[0]['target_node_name'], rpc_param['target_name'])) + verify(jsonvalues[0]['initiator_addr'] == rpc_param['initiator_ip'], 1, + "initiator address values is {}, expected {}".format(jsonvalues[0]['initiator_addr'], rpc_param['initiator_ip'])) + verify(jsonvalues[0]['target_addr'] == rpc_param['target_ip'], 1, + "target address values is {}, expected {}".format(jsonvalues[0]['target_addr'], rpc_param['target_ip'])) + + check_output('iscsiadm -m node --logout', shell=True) + check_output('iscsiadm -m node -o delete', shell=True) + rpc.iscsi_delete_initiator_group(initiator_tag) + rpc.iscsi_delete_portal_group(portal_tag) + rpc.iscsi_delete_target_node(name) + output = rpc.iscsi_get_connections() + jsonvalues = json.loads(output) + verify(not jsonvalues, 1, + "iscsi_get_connections returned {}, expected empty".format(jsonvalues)) + + print("verify_iscsi_connection_rpc_methods passed") + + +def verify_scsi_devices_rpc_methods(rpc_py): + rpc = spdk_rpc(rpc_py) + output = rpc.scsi_get_devices() + jsonvalue = json.loads(output) + verify(not jsonvalue, 1, + "scsi_get_devices returned {}, expected empty".format(jsonvalue)) + + rpc.bdev_malloc_create(rpc_param['malloc_bdev_size'], rpc_param['malloc_block_size']) + rpc.iscsi_create_portal_group(portal_tag, "{}:{}".format(rpc_param['target_ip'], str(rpc_param['port']))) + rpc.iscsi_create_initiator_group(initiator_tag, rpc_param['initiator_name'], rpc_param['netmask']) + + lun_mapping = "Malloc" + str(rpc_param['lun_total']) + ":0" + net_mapping = portal_tag + ":" + initiator_tag + rpc.iscsi_create_target_node(rpc_param['target_name'], rpc_param['alias_name'], lun_mapping, + net_mapping, rpc_param['queue_depth'], '-d') + check_output('iscsiadm -m discovery -t st -p {}'.format(rpc_param['target_ip']), shell=True) + check_output('iscsiadm -m node --login', shell=True) + name = json.loads(rpc.iscsi_get_target_nodes())[0]['name'] + output = rpc.iscsi_get_options() + jsonvalues = json.loads(output) + nodebase = jsonvalues['node_base'] + output = rpc.scsi_get_devices() + jsonvalues = json.loads(output) + verify(jsonvalues[0]['device_name'] == nodebase + ":" + rpc_param['target_name'], 1, + "device name vaule is {}, expected {}".format(jsonvalues[0]['device_name'], rpc_param['target_name'])) + verify(jsonvalues[0]['id'] == 0, 1, + "device id value is {}, expected 0".format(jsonvalues[0]['id'])) + + check_output('iscsiadm -m node --logout', shell=True) + check_output('iscsiadm -m node -o delete', shell=True) + rpc.iscsi_delete_initiator_group(initiator_tag) + rpc.iscsi_delete_portal_group(portal_tag) + rpc.iscsi_delete_target_node(name) + output = rpc.scsi_get_devices() + jsonvalues = json.loads(output) + verify(not jsonvalues, 1, + "scsi_get_devices returned {}, expected empty".format(jsonvalues)) + + print("verify_scsi_devices_rpc_methods passed") + + +def create_malloc_bdevs_rpc_methods(rpc_py, rpc_param): + rpc = spdk_rpc(rpc_py) + + for i in range(1, rpc_param['lun_total'] + 1): + rpc.bdev_malloc_create(rpc_param['malloc_bdev_size'], rpc_param['malloc_block_size']) + + print("create_malloc_bdevs_rpc_methods passed") + + +def verify_portal_groups_rpc_methods(rpc_py, rpc_param): + rpc = spdk_rpc(rpc_py) + output = rpc.iscsi_get_portal_groups() + jsonvalues = json.loads(output) + verify(not jsonvalues, 1, + "iscsi_get_portal_groups returned {} groups, expected empty".format(jsonvalues)) + + lo_ip = (target_ip, other_ip) + nics = json.loads(rpc.net_get_interfaces()) + for x in nics: + if x["ifc_index"] == 'lo': + rpc.net_interface_add_ip_address(x["ifc_index"], lo_ip[1]) + for idx, value in enumerate(lo_ip): + # The portal group tag must start at 1 + tag = idx + 1 + rpc.iscsi_create_portal_group(tag, "{}:{}".format(value, rpc_param['port'])) + output = rpc.iscsi_get_portal_groups() + jsonvalues = json.loads(output) + verify(len(jsonvalues) == tag, 1, + "iscsi_get_portal_groups returned {} groups, expected {}".format(len(jsonvalues), tag)) + + tag_list = [] + for idx, value in enumerate(jsonvalues): + verify(value['portals'][0]['host'] == lo_ip[idx], 1, + "host value is {}, expected {}".format(value['portals'][0]['host'], rpc_param['target_ip'])) + verify(value['portals'][0]['port'] == str(rpc_param['port']), 1, + "port value is {}, expected {}".format(value['portals'][0]['port'], str(rpc_param['port']))) + tag_list.append(value['tag']) + verify(value['tag'] == idx + 1, 1, + "tag value is {}, expected {}".format(value['tag'], idx + 1)) + + for idx, value in enumerate(tag_list): + rpc.iscsi_delete_portal_group(value) + output = rpc.iscsi_get_portal_groups() + jsonvalues = json.loads(output) + verify(len(jsonvalues) == (len(tag_list) - (idx + 1)), 1, + "get_portal_group returned {} groups, expected {}".format(len(jsonvalues), (len(tag_list) - (idx + 1)))) + if not jsonvalues: + break + + for jidx, jvalue in enumerate(jsonvalues): + verify(jvalue['portals'][0]['host'] == lo_ip[idx + jidx + 1], 1, + "host value is {}, expected {}".format(jvalue['portals'][0]['host'], lo_ip[idx + jidx + 1])) + verify(jvalue['portals'][0]['port'] == str(rpc_param['port']), 1, + "port value is {}, expected {}".format(jvalue['portals'][0]['port'], str(rpc_param['port']))) + verify(jvalue['tag'] != value or jvalue['tag'] == tag_list[idx + jidx + 1], 1, + "tag value is {}, expected {} and not {}".format(jvalue['tag'], tag_list[idx + jidx + 1], value)) + + for x in nics: + if x["ifc_index"] == 'lo': + rpc.net_interface_delete_ip_address(x["ifc_index"], lo_ip[1]) + + print("verify_portal_groups_rpc_methods passed") + + +def verify_initiator_groups_rpc_methods(rpc_py, rpc_param): + rpc = spdk_rpc(rpc_py) + output = rpc.iscsi_get_initiator_groups() + jsonvalues = json.loads(output) + verify(not jsonvalues, 1, + "iscsi_get_initiator_groups returned {}, expected empty".format(jsonvalues)) + for idx, value in enumerate(rpc_param['netmask']): + # The initiator group tag must start at 1 + tag = idx + 1 + rpc.iscsi_create_initiator_group(tag, rpc_param['initiator_name'], value) + output = rpc.iscsi_get_initiator_groups() + jsonvalues = json.loads(output) + verify(len(jsonvalues) == tag, 1, + "iscsi_get_initiator_groups returned {} groups, expected {}".format(len(jsonvalues), tag)) + + tag_list = [] + for idx, value in enumerate(jsonvalues): + verify(value['initiators'][0] == rpc_param['initiator_name'], 1, + "initiator value is {}, expected {}".format(value['initiators'][0], rpc_param['initiator_name'])) + tag_list.append(value['tag']) + verify(value['tag'] == idx + 1, 1, + "tag value is {}, expected {}".format(value['tag'], idx + 1)) + verify(value['netmasks'][0] == rpc_param['netmask'][idx], 1, + "netmasks value is {}, expected {}".format(value['netmasks'][0], rpc_param['netmask'][idx])) + + for idx, value in enumerate(rpc_param['netmask']): + tag = idx + 1 + rpc.iscsi_initiator_group_remove_initiators(tag, '-n', rpc_param['initiator_name'], '-m', value) + + output = rpc.iscsi_get_initiator_groups() + jsonvalues = json.loads(output) + verify(len(jsonvalues) == tag, 1, + "iscsi_get_initiator_groups returned {} groups, expected {}".format(len(jsonvalues), tag)) + + for idx, value in enumerate(jsonvalues): + verify(value['tag'] == idx + 1, 1, + "tag value is {}, expected {}".format(value['tag'], idx + 1)) + initiators = value.get('initiators') + verify(len(initiators) == 0, 1, + "length of initiator list is {}, expected 0".format(len(initiators))) + netmasks = value.get('netmasks') + verify(len(netmasks) == 0, 1, + "length of netmask list is {}, expected 0".format(len(netmasks))) + + for idx, value in enumerate(rpc_param['netmask']): + tag = idx + 1 + rpc.iscsi_initiator_group_add_initiators(tag, '-n', rpc_param['initiator_name'], '-m', value) + output = rpc.iscsi_get_initiator_groups() + jsonvalues = json.loads(output) + verify(len(jsonvalues) == tag, 1, + "iscsi_get_initiator_groups returned {} groups, expected {}".format(len(jsonvalues), tag)) + + tag_list = [] + for idx, value in enumerate(jsonvalues): + verify(value['initiators'][0] == rpc_param['initiator_name'], 1, + "initiator value is {}, expected {}".format(value['initiators'][0], rpc_param['initiator_name'])) + tag_list.append(value['tag']) + verify(value['tag'] == idx + 1, 1, + "tag value is {}, expected {}".format(value['tag'], idx + 1)) + verify(value['netmasks'][0] == rpc_param['netmask'][idx], 1, + "netmasks value is {}, expected {}".format(value['netmasks'][0], rpc_param['netmask'][idx])) + + for idx, value in enumerate(tag_list): + rpc.iscsi_delete_initiator_group(value) + output = rpc.iscsi_get_initiator_groups() + jsonvalues = json.loads(output) + verify(len(jsonvalues) == (len(tag_list) - (idx + 1)), 1, + "iscsi_get_initiator_groups returned {} groups, expected {}".format(len(jsonvalues), (len(tag_list) - (idx + 1)))) + if not jsonvalues: + break + for jidx, jvalue in enumerate(jsonvalues): + verify(jvalue['initiators'][0] == rpc_param['initiator_name'], 1, + "initiator value is {}, expected {}".format(jvalue['initiators'][0], rpc_param['initiator_name'])) + verify(jvalue['tag'] != value or jvalue['tag'] == tag_list[idx + jidx + 1], 1, + "tag value is {}, expected {} and not {}".format(jvalue['tag'], tag_list[idx + jidx + 1], value)) + verify(jvalue['netmasks'][0] == rpc_param['netmask'][idx + jidx + 1], 1, + "netmasks value is {}, expected {}".format(jvalue['netmasks'][0], rpc_param['netmask'][idx + jidx + 1])) + + print("verify_initiator_groups_rpc_method passed.") + + +def verify_target_nodes_rpc_methods(rpc_py, rpc_param): + rpc = spdk_rpc(rpc_py) + output = rpc.iscsi_get_options() + jsonvalues = json.loads(output) + nodebase = jsonvalues['node_base'] + output = rpc.iscsi_get_target_nodes() + jsonvalues = json.loads(output) + verify(not jsonvalues, 1, + "iscsi_get_target_nodes returned {}, expected empty".format(jsonvalues)) + + rpc.bdev_malloc_create(rpc_param['malloc_bdev_size'], rpc_param['malloc_block_size']) + rpc.iscsi_create_portal_group(portal_tag, "{}:{}".format(rpc_param['target_ip'], str(rpc_param['port']))) + rpc.iscsi_create_initiator_group(initiator_tag, rpc_param['initiator_name'], rpc_param['netmask']) + + lun_mapping = "Malloc" + str(rpc_param['lun_total']) + ":0" + net_mapping = portal_tag + ":" + initiator_tag + rpc.iscsi_create_target_node(rpc_param['target_name'], rpc_param['alias_name'], lun_mapping, + net_mapping, rpc_param['queue_depth'], '-d') + output = rpc.iscsi_get_target_nodes() + jsonvalues = json.loads(output) + verify(len(jsonvalues) == 1, 1, + "iscsi_get_target_nodes returned {} nodes, expected 1".format(len(jsonvalues))) + bdev_name = jsonvalues[0]['luns'][0]['bdev_name'] + verify(bdev_name == "Malloc" + str(rpc_param['lun_total']), 1, + "bdev_name value is {}, expected Malloc{}".format(jsonvalues[0]['luns'][0]['bdev_name'], str(rpc_param['lun_total']))) + name = jsonvalues[0]['name'] + verify(name == nodebase + ":" + rpc_param['target_name'], 1, + "target name value is {}, expected {}".format(name, nodebase + ":" + rpc_param['target_name'])) + verify(jsonvalues[0]['alias_name'] == rpc_param['alias_name'], 1, + "target alias_name value is {}, expected {}".format(jsonvalues[0]['alias_name'], rpc_param['alias_name'])) + verify(jsonvalues[0]['luns'][0]['lun_id'] == 0, 1, + "lun id value is {}, expected 0".format(jsonvalues[0]['luns'][0]['lun_id'])) + verify(jsonvalues[0]['pg_ig_maps'][0]['ig_tag'] == int(initiator_tag), 1, + "initiator group tag value is {}, expected {}".format(jsonvalues[0]['pg_ig_maps'][0]['ig_tag'], initiator_tag)) + verify(jsonvalues[0]['queue_depth'] == rpc_param['queue_depth'], 1, + "queue depth value is {}, expected {}".format(jsonvalues[0]['queue_depth'], rpc_param['queue_depth'])) + verify(jsonvalues[0]['pg_ig_maps'][0]['pg_tag'] == int(portal_tag), 1, + "portal group tag value is {}, expected {}".format(jsonvalues[0]['pg_ig_maps'][0]['pg_tag'], portal_tag)) + verify(jsonvalues[0]['disable_chap'] == rpc_param['disable_chap'], 1, + "disable chap value is {}, expected {}".format(jsonvalues[0]['disable_chap'], rpc_param['disable_chap'])) + verify(jsonvalues[0]['mutual_chap'] == rpc_param['mutual_chap'], 1, + "chap mutual value is {}, expected {}".format(jsonvalues[0]['mutual_chap'], rpc_param['mutual_chap'])) + verify(jsonvalues[0]['require_chap'] == rpc_param['require_chap'], 1, + "chap required value is {}, expected {}".format(jsonvalues[0]['require_chap'], rpc_param['require_chap'])) + verify(jsonvalues[0]['chap_group'] == rpc_param['chap_group'], 1, + "chap auth group value is {}, expected {}".format(jsonvalues[0]['chap_group'], rpc_param['chap_group'])) + verify(jsonvalues[0]['header_digest'] == rpc_param['header_digest'], 1, + "header digest value is {}, expected {}".format(jsonvalues[0]['header_digest'], rpc_param['header_digest'])) + verify(jsonvalues[0]['data_digest'] == rpc_param['data_digest'], 1, + "data digest value is {}, expected {}".format(jsonvalues[0]['data_digest'], rpc_param['data_digest'])) + lun_id = '1' + rpc.iscsi_target_node_add_lun(name, bdev_name, "-i", lun_id) + output = rpc.iscsi_get_target_nodes() + jsonvalues = json.loads(output) + verify(jsonvalues[0]['luns'][1]['bdev_name'] == "Malloc" + str(rpc_param['lun_total']), 1, + "bdev_name value is {}, expected Malloc{}".format(jsonvalues[0]['luns'][0]['bdev_name'], str(rpc_param['lun_total']))) + verify(jsonvalues[0]['luns'][1]['lun_id'] == 1, 1, + "lun id value is {}, expected 1".format(jsonvalues[0]['luns'][1]['lun_id'])) + + rpc.iscsi_delete_target_node(name) + output = rpc.iscsi_get_target_nodes() + jsonvalues = json.loads(output) + verify(not jsonvalues, 1, + "iscsi_get_target_nodes returned {}, expected empty".format(jsonvalues)) + + rpc.iscsi_create_target_node(rpc_param['target_name'], rpc_param['alias_name'], lun_mapping, + net_mapping, rpc_param['queue_depth'], '-d') + + rpc.iscsi_delete_portal_group(portal_tag) + rpc.iscsi_delete_initiator_group(initiator_tag) + rpc.iscsi_delete_target_node(name) + output = rpc.iscsi_get_target_nodes() + jsonvalues = json.loads(output) + if not jsonvalues: + print("This issue will be fixed later.") + + print("verify_target_nodes_rpc_methods passed.") + + +def verify_net_get_interfaces(rpc_py): + rpc = spdk_rpc(rpc_py) + nics = json.loads(rpc.net_get_interfaces()) + nics_names = set(x["name"] for x in nics) + # parse ip link show to verify the net_get_interfaces result + ip_show = ns_cmd + " ip link show" + ifcfg_nics = set(re.findall(r'\S+:\s(\S+?)(?:@\S+){0,1}:\s<.*', check_output(ip_show.split()).decode())) + verify(nics_names == ifcfg_nics, 1, "net_get_interfaces returned {}".format(nics)) + print("verify_net_get_interfaces passed.") + + +def help_get_interface_ip_list(rpc_py, nic_name): + rpc = spdk_rpc(rpc_py) + nics = json.loads(rpc.net_get_interfaces()) + nic = list([x for x in nics if x["name"] == nic_name]) + verify(len(nic) != 0, 1, + "Nic name: {} is not found in {}".format(nic_name, [x["name"] for x in nics])) + return nic[0]["ip_addr"] + + +def verify_net_interface_add_delete_ip_address(rpc_py): + rpc = spdk_rpc(rpc_py) + nics = json.loads(rpc.net_get_interfaces()) + # add ip on up to first 2 nics + for x in nics[:2]: + faked_ip = "123.123.{}.{}".format(random.randint(1, 254), random.randint(1, 254)) + ping_cmd = ns_cmd + " ping -c 1 -W 1 " + faked_ip + rpc.net_interface_add_ip_address(x["ifc_index"], faked_ip) + verify(faked_ip in help_get_interface_ip_list(rpc_py, x["name"]), 1, + "add ip {} to nic {} failed.".format(faked_ip, x["name"])) + try: + check_call(ping_cmd.split()) + except BaseException: + verify(False, 1, + "ping ip {} for {} was failed(adding was successful)".format + (faked_ip, x["name"])) + rpc.net_interface_delete_ip_address(x["ifc_index"], faked_ip) + verify(faked_ip not in help_get_interface_ip_list(rpc_py, x["name"]), 1, + "delete ip {} from nic {} failed.(adding and ping were successful)".format + (faked_ip, x["name"])) + # ping should be failed and throw an CalledProcessError exception + try: + check_call(ping_cmd.split()) + except CalledProcessError as _: + pass + except Exception as e: + verify(False, 1, + "Unexpected exception was caught {}(adding/ping/delete were successful)".format + (str(e))) + else: + verify(False, 1, + "ip {} for {} could be pinged after delete ip(adding/ping/delete were successful)".format + (faked_ip, x["name"])) + print("verify_net_interface_add_delete_ip_address passed.") + + +if __name__ == "__main__": + + rpc_py = sys.argv[1] + + try: + verify_log_flag_rpc_methods(rpc_py, rpc_param) + verify_net_get_interfaces(rpc_py) + # Add/delete IP will not be supported in VPP. + # It has separate vppctl utility for that. + if test_type == 'posix': + verify_net_interface_add_delete_ip_address(rpc_py) + create_malloc_bdevs_rpc_methods(rpc_py, rpc_param) + verify_portal_groups_rpc_methods(rpc_py, rpc_param) + verify_initiator_groups_rpc_methods(rpc_py, rpc_param) + verify_target_nodes_rpc_methods(rpc_py, rpc_param) + verify_scsi_devices_rpc_methods(rpc_py) + verify_iscsi_connection_rpc_methods(rpc_py) + except RpcException as e: + print("{}. Exiting with status {}".format(e.message, e.retval)) + raise e + except Exception as e: + raise e + + sys.exit(0) diff --git a/src/spdk/test/iscsi_tgt/rpc_config/rpc_config.sh b/src/spdk/test/iscsi_tgt/rpc_config/rpc_config.sh new file mode 100755 index 000000000..ce54b4ab2 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/rpc_config/rpc_config.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +# $1 = test type posix or vpp. +# $2 = "iso" - triggers isolation mode (setting up required environment). +iscsitestinit $2 $1 + +if [ "$1" == "posix" ] || [ "$1" == "vpp" ]; then + TEST_TYPE=$1 +else + echo "No iSCSI test type specified" + exit 1 +fi + +MALLOC_BDEV_SIZE=64 + +rpc_py=$rootdir/scripts/rpc.py +rpc_config_py="$testdir/rpc_config.py" + +timing_enter start_iscsi_tgt + +"${ISCSI_APP[@]}" --wait-for-rpc & +pid=$! +echo "Process pid: $pid" + +trap 'killprocess $pid; exit 1' SIGINT SIGTERM EXIT + +waitforlisten $pid +$rpc_py framework_wait_init & +rpc_wait_pid=$! +$rpc_py iscsi_set_options -o 30 -a 16 + +# RPC framework_wait_init should be blocked, so its process must be existed +ps $rpc_wait_pid + +$rpc_py framework_start_init +echo "iscsi_tgt is listening. Running tests..." + +# RPC framework_wait_init should be already returned, so its process must be non-existed +! ps $rpc_wait_pid + +# RPC framework_wait_init will directly returned after subsystem initialized. +$rpc_py framework_wait_init & +rpc_wait_pid=$! +sleep 1 +! ps $rpc_wait_pid + +timing_exit start_iscsi_tgt + +$rpc_config_py $rpc_py $TARGET_IP $INITIATOR_IP $ISCSI_PORT $NETMASK $TARGET_NAMESPACE $TEST_TYPE + +$rpc_py bdev_get_bdevs + +trap - SIGINT SIGTERM EXIT + +iscsicleanup +killprocess $pid + +iscsitestfini $2 $1 diff --git a/src/spdk/test/iscsi_tgt/sock/sock.sh b/src/spdk/test/iscsi_tgt/sock/sock.sh new file mode 100755 index 000000000..14615d3bc --- /dev/null +++ b/src/spdk/test/iscsi_tgt/sock/sock.sh @@ -0,0 +1,142 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +function waitfortcp() { + local addr="$2" + + if hash ip &> /dev/null; then + local have_ip_cmd=true + else + local have_ip_cmd=false + fi + + if hash ss &> /dev/null; then + local have_ss_cmd=true + else + local have_ss_cmd=false + fi + + echo "Waiting for process to start up and listen on address $addr..." + # turn off trace for this loop + xtrace_disable + local ret=0 + local i + for ((i = 40; i != 0; i--)); do + # if the process is no longer running, then exit the script + # since it means the application crashed + if ! kill -s 0 $1; then + echo "ERROR: process (pid: $1) is no longer running" + ret=1 + break + fi + + if $have_ip_cmd; then + namespace=$(ip netns identify $1) + if [ -n "$namespace" ]; then + ns_cmd="ip netns exec $namespace" + fi + fi + + if $have_ss_cmd; then + if $ns_cmd ss -ln | grep -E -q "\s+$addr\s+"; then + break + fi + elif [[ "$(uname -s)" == "Linux" ]]; then + # For Linux, if system doesn't have ss, just assume it has netstat + if $ns_cmd netstat -an | grep -iw LISTENING | grep -E -q "\s+$addr\$"; then + break + fi + fi + sleep 0.5 + done + + xtrace_restore + if ((i == 0)); then + echo "ERROR: timeout while waiting for process (pid: $1) to start listening on '$addr'" + ret=1 + fi + return $ret +} + +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +if [ "$1" == "iso" ]; then + TEST_TYPE=$2 +else + TEST_TYPE=$1 +fi + +if [ -z "$TEST_TYPE" ]; then + TEST_TYPE="posix" +fi + +if [ "$TEST_TYPE" != "posix" ] && [ "$TEST_TYPE" != "vpp" ]; then + echo "No correct sock implmentation specified" + exit 1 +fi + +HELLO_SOCK_APP="${TARGET_NS_CMD[*]} $SPDK_EXAMPLE_DIR/hello_sock" +if [ $SPDK_TEST_VPP -eq 1 ]; then + HELLO_SOCK_APP+=" -L sock_vpp" +fi +SOCAT_APP="socat" + +# ---------------- +# Test client path +# ---------------- +timing_enter sock_client +echo "Testing client path" + +# start echo server using socat +$SOCAT_APP tcp-l:$ISCSI_PORT,fork,bind=$INITIATOR_IP exec:'/bin/cat' & +server_pid=$! +trap 'killprocess $server_pid;iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +waitfortcp $server_pid $INITIATOR_IP:$ISCSI_PORT + +# send message using hello_sock client +message="**MESSAGE:This is a test message from the client**" +response=$(echo $message | $HELLO_SOCK_APP -H $INITIATOR_IP -P $ISCSI_PORT -N $TEST_TYPE) + +if ! echo "$response" | grep -q "$message"; then + exit 1 +fi + +trap '-' SIGINT SIGTERM EXIT +# NOTE: socat returns code 143 on SIGINT +killprocess $server_pid || true + +timing_exit sock_client + +# ---------------- +# Test server path +# ---------------- + +timing_enter sock_server + +# start echo server using hello_sock echo server +$HELLO_SOCK_APP -H $TARGET_IP -P $ISCSI_PORT -S -N $TEST_TYPE & +server_pid=$! +trap 'killprocess $server_pid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT +waitforlisten $server_pid + +# send message to server using socat +message="**MESSAGE:This is a test message to the server**" +response=$(echo $message | $SOCAT_APP - tcp:$TARGET_IP:$ISCSI_PORT 2> /dev/null) + +if [ "$message" != "$response" ]; then + exit 1 +fi + +trap - SIGINT SIGTERM EXIT + +killprocess $server_pid + +iscsitestfini $1 $2 +timing_exit sock_server diff --git a/src/spdk/test/iscsi_tgt/trace_record/trace_record.sh b/src/spdk/test/iscsi_tgt/trace_record/trace_record.sh new file mode 100755 index 000000000..baa7f39d4 --- /dev/null +++ b/src/spdk/test/iscsi_tgt/trace_record/trace_record.sh @@ -0,0 +1,135 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/iscsi_tgt/common.sh + +# $1 = "iso" - triggers isolation mode (setting up required environment). +# $2 = test type posix or vpp. defaults to posix. +iscsitestinit $1 $2 + +TRACE_TMP_FOLDER=./tmp-trace +TRACE_RECORD_OUTPUT=${TRACE_TMP_FOLDER}/record.trace +TRACE_RECORD_NOTICE_LOG=${TRACE_TMP_FOLDER}/record.notice +TRACE_TOOL_LOG=${TRACE_TMP_FOLDER}/trace.log + +delete_tmp_files() { + rm -rf $TRACE_TMP_FOLDER +} + +if [ -z "$TARGET_IP" ]; then + echo "TARGET_IP not defined in environment" + exit 1 +fi + +if [ -z "$INITIATOR_IP" ]; then + echo "INITIATOR_IP not defined in environment" + exit 1 +fi + +NUM_TRACE_ENTRIES=4096 +MALLOC_BDEV_SIZE=64 +MALLOC_BLOCK_SIZE=4096 + +rpc_py="$rootdir/scripts/rpc.py" +fio_py="$rootdir/scripts/fio.py" + +timing_enter start_iscsi_tgt + +echo "start iscsi_tgt with trace enabled" +"${ISCSI_APP[@]}" -m 0xf --num-trace-entries $NUM_TRACE_ENTRIES --tpoint-group-mask 0xf & +iscsi_pid=$! +echo "Process pid: $iscsi_pid" + +trap 'killprocess $iscsi_pid; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +waitforlisten $iscsi_pid + +echo "iscsi_tgt is listening. Running tests..." + +timing_exit start_iscsi_tgt + +mkdir -p ${TRACE_TMP_FOLDER} +./build/bin/spdk_trace_record -s iscsi -p ${iscsi_pid} -f ${TRACE_RECORD_OUTPUT} -q 1> ${TRACE_RECORD_NOTICE_LOG} & +record_pid=$! +echo "Trace record pid: $record_pid" + +RPCS= +RPCS+="iscsi_create_portal_group $PORTAL_TAG $TARGET_IP:$ISCSI_PORT\n" +RPCS+="iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK\n" + +echo "Create bdevs and target nodes" +CONNECTION_NUMBER=15 +for i in $(seq 0 $CONNECTION_NUMBER); do + RPCS+="bdev_malloc_create $MALLOC_BDEV_SIZE $MALLOC_BLOCK_SIZE -b Malloc$i\n" + RPCS+="iscsi_create_target_node Target$i Target${i}_alias "Malloc$i:0" $PORTAL_TAG:$INITIATOR_TAG 256 -d\n" +done +echo -e $RPCS | $rpc_py + +sleep 1 + +iscsiadm -m discovery -t sendtargets -p $TARGET_IP:$ISCSI_PORT +iscsiadm -m node --login -p $TARGET_IP:$ISCSI_PORT +waitforiscsidevices $((CONNECTION_NUMBER + 1)) + +trap 'iscsicleanup; killprocess $iscsi_pid; killprocess $record_pid; delete_tmp_files; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +echo "Running FIO" +$fio_py -p iscsi -i 131072 -d 32 -t randrw -r 1 + +iscsicleanup + +RPCS= +# Delete Malloc blockdevs and targets +for i in $(seq 0 $CONNECTION_NUMBER); do + RPCS+="iscsi_delete_target_node iqn.2016-06.io.spdk:Target$i\n" + RPCS+="bdev_malloc_delete Malloc$i\n" +done +echo -e $RPCS | $rpc_py + +trap 'delete_tmp_files; iscsitestfini $1 $2; exit 1' SIGINT SIGTERM EXIT + +killprocess $iscsi_pid +killprocess $record_pid +./build/bin/spdk_trace -f ${TRACE_RECORD_OUTPUT} > ${TRACE_TOOL_LOG} + +#verify trace record and trace tool +#trace entries str in trace-record, like "Trace Size of lcore (0): 4136" +record_num="$(grep "trace entries for lcore" ${TRACE_RECORD_NOTICE_LOG} | cut -d ' ' -f 2)" + +#trace entries str in trace-tool, like "Port 4096 trace entries for lcore (0) in 441871 msec" +trace_tool_num="$(grep "Trace Size of lcore" ${TRACE_TOOL_LOG} | cut -d ' ' -f 6)" + +delete_tmp_files + +echo "entries numbers from trace record are:" $record_num +echo "entries numbers from trace tool are:" $trace_tool_num + +arr_record_num=($record_num) +arr_trace_tool_num=($trace_tool_num) +len_arr_record_num=${#arr_record_num[@]} +len_arr_trace_tool_num=${#arr_trace_tool_num[@]} + +#lcore num check +if [ $len_arr_record_num -ne $len_arr_trace_tool_num ]; then + echo "trace record test on iscsi: failure on lcore number check" + set -e + exit 1 +fi +#trace entries num check +for i in $(seq 0 $((len_arr_record_num - 1))); do + if [ ${arr_record_num[$i]} -le ${NUM_TRACE_ENTRIES} ]; then + echo "trace record test on iscsi: failure on inefficient entries number check" + set -e + exit 1 + fi + if [ ${arr_record_num[$i]} -ne ${arr_trace_tool_num[$i]} ]; then + echo "trace record test on iscsi: failure on entries number check" + set -e + exit 1 + fi +done + +trap - SIGINT SIGTERM EXIT +iscsitestfini $1 $2 |