diff --git a/autotest.sh b/autotest.sh index dcfdbdbd7..20a85dc9f 100755 --- a/autotest.sh +++ b/autotest.sh @@ -126,6 +126,7 @@ if [ $SPDK_TEST_VHOST -eq 1 ]; then run_test ./test/vhost/spdk_vhost.sh --integrity run_test ./test/vhost/spdk_vhost.sh --integrity-lvol-scsi run_test ./test/vhost/spdk_vhost.sh --integrity-lvol-blk + run_test ./test/lvol/lvol.sh --test-cases=1,2,3,5,6,7,9,10,11,12,13,16,17,21 timing_exit vhost fi diff --git a/test/lvol/lvol.sh b/test/lvol/lvol.sh new file mode 100755 index 000000000..75fb403e6 --- /dev/null +++ b/test/lvol/lvol.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +set -xe +BASE_DIR=$(readlink -f $(dirname $0)) +[[ -z "$TEST_DIR" ]] && TEST_DIR="$(cd $BASE_DIR/../../ && pwd)" + +total_size=64 +block_size=512 +test_cases=all +x="" + +rpc_py="$TEST_DIR/scripts/rpc.py " +RPC_PORT=5260 + +function usage() { + [[ ! -z $2 ]] && ( echo "$2"; echo ""; ) + echo "Shortcut script for doing automated lvol tests" + echo "Usage: $(basename $1) [OPTIONS]" + echo + echo "-h, --help print help and exit" + echo " --total-size Size of malloc bdev in MB (int > 0)" + echo " --block-size Block size for this bdev" + echo "-x set -x for script debug" + echo " --test-cases= List test cases which will be run: + 1: 'construct_lvs_positive', + 2: 'construct_logical_volume_positive', + 3: 'construct_multi_logical_volumes_positive', + 4: 'resize_lvol_bdev_positive', + 5: 'destroy_lvol_store_positive', + 6: 'destroy_lvol_store_with_lvol_bdev_positive', + 7: 'destroy_multi_logical_volumes_positive', + 8: 'nested construct_logical_volume_positive', + 9: 'destroy_after_resize_lvol_bdev_positive', + 10: 'construct_lvs_nonexistent_bdev', + 11: 'construct_lvs_on_bdev_twice_negative', + 12: 'construct_logical_volume_nonexistent_lvs_uuid', + 13: 'construct_logical_volumes_on_busy_bdev', + 14: 'resize_logical_volume_nonexistent_logical_volume', + 15: 'resize_logical_volume_with_size_out_of_range', + 16: 'destroy_lvol_store_nonexistent_lvs_uuid', + 17: 'destroy_lvol_store_nonexistent_bdev', + 18: 'nested construct_logical_volume_on_busy_bdev', + 19: 'nested destroy_logical_volume_positive', + 20: 'delete_bdev_positive', + 21: 'SIGTERM_on_lvol_store', + or + all: This parameter runs all tests + Ex: \"1,2,19,20\", default: all" + echo + echo + exit 0 +} + +while getopts 'xh-:' optchar; do + case "$optchar" in + -) + case "$OPTARG" in + help) usage $0 ;; + total-size=*) total_size="${OPTARG#*=}" ;; + block-size=*) block_size="${OPTARG#*=}" ;; + test-cases=*) test_cases="${OPTARG#*=}" ;; + *) usage $0 "Invalid argument '$OPTARG'" ;; + esac + ;; + h) usage $0 ;; + x) set -x + x="-x" ;; + *) usage $0 "Invalid argument '$OPTARG'" + esac +done +shift $(( OPTIND - 1 )) + +source $TEST_DIR/scripts/autotest_common.sh + +### Function starts vhost app +function vhost_start() +{ + $TEST_DIR/app/vhost/vhost -c $BASE_DIR/vhost.conf.in & + vhost_pid=$! + echo $vhost_pid > $BASE_DIR/vhost.pid + waitforlisten $vhost_pid $RPC_PORT +} + +### Function stops vhost app +function vhost_kill() +{ + ### Kill with SIGKILL param + if pkill -F $BASE_DIR/vhost.pid; then + sleep 1 + fi + rm $BASE_DIR/vhost.pid || true +} + +trap "vhost_kill; exit 1" SIGINT SIGTERM EXIT + +vhost_start + +$BASE_DIR/lvol_test.py $rpc_py $total_size $block_size $BASE_DIR "${test_cases[@]}" + +trap - SIGINT SIGTERM EXIT +vhost_kill diff --git a/test/lvol/lvol_test.py b/test/lvol/lvol_test.py new file mode 100755 index 000000000..77154ab4b --- /dev/null +++ b/test/lvol/lvol_test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +import sys +from test_cases import * + +def check_fail_count(fail_count, num_test): + if not fail_count: + print("Test: {num_test} - PASS".format(num_test=num_test)) + else: + print("Test: {num_test} - FAIL".format(num_test=num_test)) + +if __name__ == "__main__": + rpc_py = None + total_size = None + block_size = None + cluster_size = "-c 1048576" # 1MB cluster size for tests + num_test = None + fail_count = 0 + tc_failed = [] + tc_list = [] + + if len(sys.argv) >= 5 and len(sys.argv) <= test_counter(): + rpc_py = sys.argv[1] + total_size = int(sys.argv[2]) + block_size = int(sys.argv[3]) + base_dir_path = sys.argv[4] + tc_list = sys.argv[5].split(',') + else: + print("Invalid argument") + try: + tc = TestCases(rpc_py, total_size, block_size, cluster_size, base_dir_path) + + if "all" in tc_list: + for num_test in range(1, test_counter() + 1): + fail_count = 0 + exec("fail_count += tc.test_case{num_test}" + "()".format(num_test=num_test)) + check_fail_count(fail_count, num_test) + if fail_count: + tc_failed.append(num_test) + else: + for num_test in tc_list: + fail_count = 0 + exec("fail_count += tc.test_case{num_test}" + "()".format(num_test=num_test)) + check_fail_count(fail_count, num_test) + if fail_count: + tc_failed.append(num_test) + + if not tc_failed: + print("RESULT: All test cases - PASS") + elif tc_failed: + print("RESULT: Some test cases FAIL") + print(tc_failed) + sys.exit(1) + except: + print("Test: {num_test} - FAIL".format(num_test=num_test)) + sys.exit(1) diff --git a/test/lvol/rpc_commands_lib.py b/test/lvol/rpc_commands_lib.py new file mode 100644 index 000000000..f0ce3776b --- /dev/null +++ b/test/lvol/rpc_commands_lib.py @@ -0,0 +1,110 @@ +import json +from subprocess import check_output, CalledProcessError + +class Spdk_Rpc(object): + def __init__(self, rpc_py): + self.rpc_py = rpc_py + + def __getattr__(self, name): + def call(*args): + cmd = "python {} {}".format(self.rpc_py, name) + for arg in args: + cmd += " {}".format(arg) + try: + output = check_output(cmd, shell=True) + return output.rstrip('\n'), 0 + except CalledProcessError as e: + print("ERROR: RPC Command {cmd} " + "execution failed:". format(cmd=cmd)) + print("Failed command output:") + print(e.output) + return e.output, e.returncode + return call + +class Commands_Rpc(object): + def __init__(self, rpc_py): + self.rpc = Spdk_Rpc(rpc_py) + + def check_get_bdevs_methods(self, uuid_bdev, bdev_size_mb): + print("INFO: Check RPC COMMAND get_bdevs") + output = self.rpc.get_bdevs()[0] + json_value = json.loads(output) + for i in range(len(json_value)): + uuid_json = json_value[i]['name'] + if uuid_bdev in [uuid_json]: + print("Info: UUID:{uuid} is found in RPC Commnad: " + "gets_bdevs response".format(uuid=uuid_bdev)) + # num_block and block_size have values in bytes + num_blocks = json_value[i]['num_blocks'] + block_size = json_value[i]['block_size'] + if num_blocks * block_size == bdev_size_mb * 1024 * 1024: + print("Info: Response get_bdevs command is " + "correct. Params: uuid_bdevs: {uuid}, bdev_size " + "{size}".format(uuid=uuid_bdev, + size=bdev_size_mb)) + return 0 + print("INFO: UUID:{uuid} or bdev_size:{bdev_size_mb} not found in " + "RPC COMMAND get_bdevs: " + "{json_value}".format(uuid=uuid_bdev, bdev_size_mb=bdev_size_mb, + json_value=json_value)) + return 1 + + def check_get_lvol_stores(self, base_name, uuid): + print("INFO: RPC COMMAND get_lvol_stores") + output = self.rpc.get_lvol_stores()[0] + json_value = json.loads(output) + if json_value: + for i in range(len(json_value)): + uuid_json_response = json_value[i]['uuid'] + base_bdev_json_reponse = json_value[i]['base_bdev'] + if base_name in [base_bdev_json_reponse] \ + and uuid in [uuid_json_response]: + print("INFO: base_name:{base_name} is found in RPC " + "Command: get_lvol_stores " + "response".format(base_name=base_name)) + print("INFO: UUID:{uuid} is found in RPC Commnad: " + "get_lvol_stores response".format(uuid=uuid)) + return 0 + print("FAILED: UUID: {uuid} or base_name: {base_name} not found " + "in RPC COMMAND get_bdevs:" + "{json_value}".format(uuid=uuid, base_name=base_name, + json_value=json_value)) + return 1 + else: + print("INFO: Lvol store not exist") + return 0 + + def construct_malloc_bdev(self, total_size, block_size): + print("INFO: RPC COMMAND construct_malloc_bdev") + output = self.rpc.construct_malloc_bdev(total_size, block_size)[0] + return output.rstrip('\n') + + def construct_lvol_store(self, base_name, cluster_size): + print("INFO: RPC COMMAND construct_lvol_store") + output = self.rpc.construct_lvol_store(base_name, cluster_size)[0] + return output.rstrip('\n') + + def construct_lvol_bdev(self, uuid, size): + print("INFO: RPC COMMAND construct_lvol_bdev") + output = self.rpc.construct_lvol_bdev(uuid, size)[0] + return output.rstrip('\n') + + def destroy_lvol_store(self, uuid): + print("INFO: RPC COMMAND destroy_lvol_store") + output, rc = self.rpc.destroy_lvol_store(uuid) + return rc + + def delete_bdev(self, base_name): + print("INFO: RPC COMMAND delete_bdev") + output, rc = self.rpc.delete_bdev(base_name) + return rc + + def resize_lvol_bdev(self, uuid, new_size): + print("INFO: RPC COMMAND resize_lvol_bdev") + output, rc = self.rpc.resize_lvol_bdev(uuid, new_size) + return rc + + def get_lvol_stores(self): + print("INFO: RPC COMMAND get_lvol_stores") + output = self.rpc.get_lvol_stores()[0] + return output.rstrip('\n') diff --git a/test/lvol/test_cases.py b/test/lvol/test_cases.py new file mode 100644 index 000000000..a7993e419 --- /dev/null +++ b/test/lvol/test_cases.py @@ -0,0 +1,364 @@ +#!/usr/bin/env python +import io +import random +import signal + +from errno import ESRCH +from os import kill, path +from rpc_commands_lib import Commands_Rpc +from time import sleep +from uuid import uuid4 + +def test_counter(): + ''' + :return: the number of tests + ''' + return 21 + +def header(num): + test_name = { + 1: 'construct_lvs_positive', + 2: 'construct_logical_volume_positive', + 3: 'construct_multi_logical_volumes_positive', + 4: 'resize_lvol_bdev_positive', + 5: 'destroy_lvol_store_positive', + 6: 'destroy_lvol_store_with_lvol_bdev_positive', + 7: 'destroy_multi_logical_volumes_positive', + 8: 'nested_construct_logical_volume_positive', + 9: 'destroy_after_resize_lvol_bdev_positive', + 10: 'construct_lvs_nonexistent_bdev', + 11: 'construct_lvs_on_bdev_twic_negative', + 12: 'construct_logical_volume_nonexistent_lvs_uuid', + 13: 'construct_lvol_bdev_on_full_lvol_store', + 14: 'resize_logical_volume_nonexistent_logical_volume', + 15: 'resize_logical_volume_with_size_out_of_range', + 16: 'destroy_lvol_store_nonexistent_lvs_uuid', + 17: 'destroy_lvol_store_nonexistent_bdev', + 18: 'nested_construct_lvol_bdev_on_full_lvol_store', + 19: 'nested_destroy_logical_volume_positive', + 20: 'delete_bdev_positive', + 21: 'SIGTERM_on_lvol_store', + } + print("========================================================") + print("Test Case {num}: Start".format(num=num)) + print("Test Name: {name}".format(name=test_name[num])) + print("========================================================") + +def footer(num): + print("Test Case {num}: END\n".format(num=num)) + print("========================================================") + +class TestCases(object): + def __init__(self, rpc_py, total_size, block_size, cluster_size, base_dir_path): + self.c = Commands_Rpc(rpc_py) + self.total_size = total_size + self.block_size = block_size + self.cluster_size = cluster_size + self.path = base_dir_path + + def _gen_lvs_uudi(self): + return str(uuid4()) + + def _gen_lvb_uudi(self): + return "_".join([str(uuid4()), str(random.randrange(9999999999))]) + + # positive tests + def test_case1(self): + header(1) + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + uuid_store = self.c.construct_lvol_store(base_name, self.cluster_size) + fail_count = self.c.check_get_lvol_stores(base_name, uuid_store) + self.c.destroy_lvol_store(uuid_store) + self.c.delete_bdev(base_name) + fail_count += self.c.check_get_lvol_stores("", "") + footer(1) + return fail_count + + def test_case2(self): + header(2) + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + uuid_store = self.c.construct_lvol_store(base_name, self.cluster_size) + fail_count = self.c.check_get_lvol_stores(base_name, uuid_store) + uuid_bdev = self.c.construct_lvol_bdev(uuid_store, self.total_size - 1) + fail_count += self.c.check_get_bdevs_methods(uuid_bdev, + self.total_size - 1) + self.c.delete_bdev(uuid_bdev) + self.c.destroy_lvol_store(uuid_store) + self.c.delete_bdev(base_name) + footer(2) + return fail_count + + def test_case3(self): + header(3) + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + uuid_store = self.c.construct_lvol_store(base_name, self.cluster_size) + fail_count = self.c.check_get_lvol_stores(base_name, uuid_store) + size = ((self.total_size - 1) / 4) + + uuid_bdevs = [] + for _ in range(4): + uuid_bdev = self.c.construct_lvol_bdev(uuid_store, size) + uuid_bdevs.append(uuid_bdev) + fail_count += self.c.check_get_bdevs_methods(uuid_bdev, size) + + for uuid_bdev in uuid_bdevs: + self.c.delete_bdev(uuid_bdev) + + self.c.destroy_lvol_store(uuid_store) + self.c.delete_bdev(base_name) + footer(3) + return fail_count + + def test_case4(self): + header(4) + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + uuid_store = self.c.construct_lvol_store(base_name, self.cluster_size) + fail_count = self.c.check_get_lvol_stores(base_name, uuid_store) + # size is equal to one quarter of size malloc bdev + uuid_bdev = self.c.construct_lvol_bdev(uuid_store, self.total_size / 4) + fail_count += self.c.check_get_bdevs_methods(uuid_bdev, + self.total_size / 4) + # size is equal to half of size malloc bdev + self.c.resize_lvol_bdev(uuid_bdev, self.total_size / 2) + fail_count += self.c.check_get_bdevs_methods(uuid_bdev, + self.total_size / 2) + # size is smaller by 1 MB + self.c.resize_lvol_bdev(uuid_bdev, self.total_size - 1) + fail_count += self.c.check_get_bdevs_methods(uuid_bdev, + self.total_size - 1) + # size is equal 0 MiB + self.c.resize_lvol_bdev(uuid_bdev, 0) + fail_count += self.c.check_get_bdevs_methods(uuid_bdev, 0) + + self.c.delete_bdev(uuid_bdev) + self.c.destroy_lvol_store(uuid_store) + self.c.delete_bdev(base_name) + footer(4) + return fail_count + + def test_case5(self): + header(5) + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + uuid_store = self.c.construct_lvol_store(base_name, self.cluster_size) + fail_count = self.c.check_get_lvol_stores(base_name, uuid_store) + self.c.destroy_lvol_store(uuid_store) + fail_count += self.c.check_get_lvol_stores("", "") + self.c.delete_bdev(base_name) + footer(5) + return fail_count + + def test_case6(self): + header(6) + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + uuid_store = self.c.construct_lvol_store(base_name, self.cluster_size) + fail_count = self.c.check_get_lvol_stores(base_name, uuid_store) + uuid_bdev = self.c.construct_lvol_bdev(uuid_store, self.total_size - 1) + fail_count += self.c.check_get_bdevs_methods(uuid_bdev, + self.total_size - 1) + if self.c.destroy_lvol_store(uuid_store) != 0: + fail_count += 1 + + fail_count += self.c.check_get_lvol_stores("", "") + self.c.delete_bdev(base_name) + footer(6) + return fail_count + + def test_case7(self): + header(7) + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + uuid_store = self.c.construct_lvol_store(base_name, self.cluster_size) + fail_count = self.c.check_get_lvol_stores(base_name, uuid_store) + size = ((self.total_size - 1) / 4) + + for _ in range(4): + uuid_bdev = self.c.construct_lvol_bdev(uuid_store, size) + fail_count += self.c.check_get_bdevs_methods(uuid_bdev, size) + + self.c.destroy_lvol_store(uuid_store) + fail_count += self.c.check_get_lvol_stores("", "") + self.c.delete_bdev(base_name) + footer(7) + return fail_count + + def test_case8(self): + print("Test of this feature not yet implemented.") + pass + return 0 + + def test_case9(self): + header(9) + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + uuid_store = self.c.construct_lvol_store(base_name, self.cluster_size) + fail_count = self.c.check_get_lvol_stores(base_name, uuid_store) + size = ((self.total_size - 1) / 4) + uuid_bdev = self.c.construct_lvol_bdev(uuid_store, size) + fail_count += self.c.check_get_bdevs_methods(uuid_bdev, + size) + self.c.resize_lvol_bdev(uuid_bdev, size + 1) + self.c.resize_lvol_bdev(uuid_bdev, size * 2) + self.c.resize_lvol_bdev(uuid_bdev, size * 3) + self.c.resize_lvol_bdev(uuid_bdev, (size * 4) - 1) + self.c.resize_lvol_bdev(uuid_bdev, 0) + + self.c.destroy_lvol_store(uuid_store) + fail_count += self.c.check_get_lvol_stores("", "") + self.c.delete_bdev(base_name) + footer(9) + return fail_count + + # negative tests + def test_case10(self): + header(10) + fail_count = 0 + bad_bdev_id = random.randrange(999999999) + if self.c.construct_lvol_store(bad_bdev_id, self.cluster_size) == 0: + fail_count += 1 + footer(10) + return fail_count + + def test_case11(self): + header(11) + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + uuid_store = self.c.construct_lvol_store(base_name, self.cluster_size) + fail_count = self.c.check_get_lvol_stores(base_name, uuid_store) + if self.c.construct_lvol_store(base_name, self.cluster_size) == 0: + fail_count += 1 + self.c.destroy_lvol_store(uuid_store) + self.c.delete_bdev(base_name) + footer(11) + return fail_count + + def test_case12(self): + header(12) + fail_count = 0 + if self.c.construct_lvol_bdev(self._gen_lvs_uudi(), 32) == 0: + fail_count += 1 + footer(12) + return fail_count + + def test_case13(self): + header(13) + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + uuid_store = self.c.construct_lvol_store(base_name, self.cluster_size) + fail_count = self.c.check_get_lvol_stores(base_name, uuid_store) + uuid_bdev = self.c.construct_lvol_bdev(uuid_store, self.total_size - 1) + fail_count += self.c.check_get_bdevs_methods(uuid_bdev, + self.total_size - 1) + if self.c.construct_lvol_bdev(uuid_store, self.total_size - 1) == 0: + fail_count += 1 + + self.c.delete_bdev(uuid_bdev) + self.c.destroy_lvol_store(uuid_store) + self.c.delete_bdev(base_name) + footer(13) + return fail_count + + def test_case14(self): + header(14) + fail_count = 0 + if self.c.resize_lvol_bdev(self._gen_lvb_uudi(), 16) == 0: + fail_count += 1 + footer(14) + return fail_count + + def test_case15(self): + header(15) + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + uuid_store = self.c.construct_lvol_store(base_name, self.cluster_size) + fail_count = self.c.check_get_lvol_stores(base_name, uuid_store) + uuid_bdev = self.c.construct_lvol_bdev(uuid_store, self.total_size - 1) + fail_count += self.c.check_get_bdevs_methods(uuid_bdev, + self.total_size - 1) + if self.c.resize_lvol_bdev(uuid_bdev, self.total_size + 1) == 0: + fail_count += 1 + + self.c.delete_bdev(uuid_bdev) + self.c.destroy_lvol_store(uuid_store) + self.c.delete_bdev(base_name) + footer(15) + return fail_count + + def test_case16(self): + header(16) + fail_count = 0 + if self.c.destroy_lvol_store(self._gen_lvs_uudi()) == 0: + fail_count += 1 + footer(16) + return fail_count + + def test_case17(self): + header(17) + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + uuid_store = self.c.construct_lvol_store(base_name, self.cluster_size) + fail_count = self.c.check_get_lvol_stores(base_name, uuid_store) + + if self.c.delete_bdev(base_name) != 0: + fail_count += 1 + + if self.c.destroy_lvol_store(uuid_store) == 0: + fail_count += 1 + + footer(17) + return fail_count + + def test_case18(self): + print("Test of this feature not yet implemented.") + pass + return 0 + + def test_case19(self): + print("Test of this feature not yet implemented.") + pass + return 0 + + def test_case20(self): + header(20) + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + uuid_store = self.c.construct_lvol_store(base_name) + fail_count = self.c.check_get_lvol_stores(base_name, uuid_store) + self.c.delete_bdev(base_name) + fail_count += self.c.check_get_lvol_stores("", "") + footer(20) + return fail_count + + def test_case21(self): + header(21) + print self.block_size + print self.total_size + base_name = self.c.construct_malloc_bdev(self.total_size, + self.block_size) + uuid_store = self.c.construct_lvol_store(base_name, self.cluster_size) + fail_count = self.c.check_get_lvol_stores(base_name, uuid_store) + pid_path = path.join(self.path, 'vhost.pid') + with io.open(pid_path, 'r') as vhost_pid: + pid = int(vhost_pid.readline()) + if pid: + try: + kill(pid, signal.SIGTERM) + for count in range(30): + sleep(1) + kill(pid, 0) + except OSError, err: + if err.errno == ESRCH: + pass + else: + return 1 + else: + return 1 + else: + return 1 + footer(21) + return fail_count diff --git a/test/lvol/vhost.conf.in b/test/lvol/vhost.conf.in new file mode 100755 index 000000000..d7664685b --- /dev/null +++ b/test/lvol/vhost.conf.in @@ -0,0 +1,5 @@ +[Global] + LogFacility "local7" +[Rpc] + Enable Yes + Listen 127.0.0.1