xref: /spdk/test/json_config/json_config.sh (revision c680e3a05b1a903c18bf3f75b732765607126f45)
1#!/usr/bin/env bash
2#  SPDX-License-Identifier: BSD-3-Clause
3#  Copyright (C) 2018 Intel Corporation
4#  All rights reserved.
5#
6rootdir=$(readlink -f $(dirname $0)/../..)
7source "$rootdir/test/common/autotest_common.sh"
8source "$rootdir/test/nvmf/common.sh"
9
10if [[ $SPDK_TEST_ISCSI -eq 1 ]]; then
11	source "$rootdir/test/iscsi_tgt/common.sh"
12fi
13
14if [[ $SPDK_TEST_VHOST -ne 1 && $SPDK_TEST_VHOST_INIT -eq 1 ]]; then
15	SPDK_TEST_VHOST=1
16	echo "WARNING: Virtio initiator JSON_config test requires vhost target."
17	echo "         Setting SPDK_TEST_VHOST=1 for duration of current script."
18fi
19
20if ((SPDK_TEST_BLOCKDEV + \
21	SPDK_TEST_ISCSI + \
22	SPDK_TEST_NVMF + \
23	SPDK_TEST_VHOST + \
24	SPDK_TEST_VHOST_INIT + \
25	SPDK_TEST_PMDK + \
26	SPDK_TEST_RBD == 0)); then
27	echo "WARNING: No tests are enabled so not running JSON configuration tests"
28	exit 0
29fi
30
31declare -A app_pid=([target]="" [initiator]="")
32declare -A app_socket=([target]='/var/tmp/spdk_tgt.sock' [initiator]='/var/tmp/spdk_initiator.sock')
33declare -A app_params=([target]='-m 0x1 -s 1024' [initiator]='-m 0x2 -g -u -s 1024')
34declare -A configs_path=([target]="$rootdir/spdk_tgt_config.json" [initiator]="$rootdir/spdk_initiator_config.json")
35
36function tgt_rpc() {
37	$rootdir/scripts/rpc.py -s "${app_socket[target]}" "$@"
38}
39
40function initiator_rpc() {
41	$rootdir/scripts/rpc.py -s "${app_socket[initiator]}" "$@"
42}
43
44last_event_id=0
45
46function tgt_check_notification_types() {
47	timing_enter "${FUNCNAME[0]}"
48
49	local ret=0
50	local enabled_types=("bdev_register" "bdev_unregister")
51
52	local get_types=($(tgt_rpc notify_get_types | jq -r '.[]'))
53	if [[ ${enabled_types[*]} != "${get_types[*]}" ]]; then
54		echo "ERROR: expected types: ${enabled_types[*]}, but got: ${get_types[*]}"
55		ret=1
56	fi
57
58	timing_exit "${FUNCNAME[0]}"
59	return $ret
60}
61
62get_notifications() {
63	local ev_type ev_ctx event_id
64
65	while IFS=":" read -r ev_type ev_ctx event_id; do
66		echo "$ev_type:$ev_ctx"
67	done < <(tgt_rpc notify_get_notifications -i "$last_event_id" | jq -r '.[] | "\(.type):\(.ctx):\(.id)"')
68}
69
70function tgt_check_notifications() {
71	local events_to_check
72	local recorded_events
73
74	events_to_check=("$@")
75	recorded_events=($(get_notifications))
76
77	# These should be in order hence compare entire arrays
78	if [[ ${events_to_check[*]} != "${recorded_events[*]}" ]]; then
79		cat <<- ERROR
80			Expected events did not match.
81
82			Expected:
83			$(printf ' %s\n' "${events_to_check[@]}")
84			Recorded:
85			$(printf ' %s\n' "${recorded_events[@]}")
86		ERROR
87		return 1
88	fi
89
90	cat <<- INFO
91		Expected events matched:
92		$(printf ' %s\n' "${recorded_events[@]}")
93	INFO
94}
95
96# $1 - target / initiator
97# $2..$n app parameters
98function json_config_test_start_app() {
99	local app=$1
100	shift
101
102	[[ -n "${#app_socket[$app]}" ]] # Check app type
103	[[ -z "${app_pid[$app]}" ]]     # Assert if app is not running
104
105	local app_extra_params=""
106	if [[ $SPDK_TEST_VHOST -eq 1 || $SPDK_TEST_VHOST_INIT -eq 1 ]]; then
107		# If PWD is nfs/sshfs we can't create UNIX sockets there. Always use safe location instead.
108		app_extra_params='-S /var/tmp'
109	fi
110
111	$SPDK_BIN_DIR/spdk_tgt ${app_params[$app]} ${app_extra_params} -r ${app_socket[$app]} "$@" &
112	app_pid[$app]=$!
113
114	echo "Waiting for $app to run..."
115	waitforlisten ${app_pid[$app]} ${app_socket[$app]}
116	echo ""
117}
118
119# $1 - target / initiator
120function json_config_test_shutdown_app() {
121	local app=$1
122
123	# Check app type && assert app was started
124	[[ -n "${#app_socket[$app]}" ]]
125	[[ -n "${app_pid[$app]}" ]]
126
127	# spdk_kill_instance RPC will trigger ASAN
128	kill -SIGINT ${app_pid[$app]}
129
130	for ((i = 0; i < 30; i++)); do
131		if ! kill -0 ${app_pid[$app]} 2> /dev/null; then
132			app_pid[$app]=
133			break
134		fi
135		sleep 0.5
136	done
137
138	if [[ -n "${app_pid[$app]}" ]]; then
139		echo "SPDK $app shutdown timeout"
140		return 1
141	fi
142
143	echo "SPDK $app shutdown done"
144}
145
146function create_bdev_subsystem_config() {
147	timing_enter "${FUNCNAME[0]}"
148
149	local expected_notifications=()
150
151	# Consider multiple nvme devices loaded into the subsystem prior running
152	# the tests.
153	expected_notifications+=($(get_notifications))
154
155	if [[ $SPDK_TEST_BLOCKDEV -eq 1 ]]; then
156		local lvol_store_base_bdev=Nvme0n1
157
158		tgt_rpc bdev_split_create $lvol_store_base_bdev 2
159		tgt_rpc bdev_split_create Malloc0 3
160		tgt_rpc bdev_malloc_create 8 4096 --name Malloc3
161		tgt_rpc bdev_passthru_create -b Malloc3 -p PTBdevFromMalloc3
162
163		tgt_rpc bdev_null_create Null0 32 512
164
165		tgt_rpc bdev_malloc_create 32 512 --name Malloc0
166		tgt_rpc bdev_malloc_create 16 4096 --name Malloc1
167
168		expected_notifications+=(
169			bdev_register:${lvol_store_base_bdev}p1
170			bdev_register:${lvol_store_base_bdev}p0
171			bdev_register:Malloc3
172			bdev_register:PTBdevFromMalloc3
173			bdev_register:Null0
174			bdev_register:Malloc0
175			bdev_register:Malloc0p2
176			bdev_register:Malloc0p1
177			bdev_register:Malloc0p0
178			bdev_register:Malloc1
179		)
180
181		if [[ $(uname -s) = Linux ]]; then
182			# This AIO bdev must be large enough to be used as LVOL store
183			dd if=/dev/zero of="$SPDK_TEST_STORAGE/sample_aio" bs=1024 count=102400
184			tgt_rpc bdev_aio_create "$SPDK_TEST_STORAGE/sample_aio" aio_disk 1024
185			expected_notifications+=(bdev_register:aio_disk)
186		fi
187
188		# For LVOLs use split to check for proper order of initialization.
189		# If LVOLs configuration will be reordered (eg moved before splits or AIO/NVMe)
190		# it should fail loading JSON config from file.
191		tgt_rpc bdev_lvol_create_lvstore -c 1048576 ${lvol_store_base_bdev}p0 lvs_test
192
193		expected_notifications+=(
194			"bdev_register:$(tgt_rpc bdev_lvol_create -l lvs_test lvol0 32)"
195			"bdev_register:$(tgt_rpc bdev_lvol_create -l lvs_test -t lvol1 32)"
196			"bdev_register:$(tgt_rpc bdev_lvol_snapshot lvs_test/lvol0 snapshot0)"
197			"bdev_register:$(tgt_rpc bdev_lvol_clone lvs_test/snapshot0 clone0)"
198		)
199	fi
200
201	if [[ $SPDK_TEST_CRYPTO -eq 1 ]]; then
202		tgt_rpc bdev_malloc_create 8 1024 --name MallocForCryptoBdev
203		if [[ $(lspci -d:37c8 | wc -l) -eq 0 ]]; then
204			local crypto_driver=crypto_aesni_mb
205		else
206			local crypto_driver=crypto_qat
207		fi
208
209		tgt_rpc bdev_crypto_create MallocForCryptoBdev CryptoMallocBdev $crypto_driver 01234567891234560123456789123456
210		expected_notifications+=(
211			bdev_register:MallocForCryptoBdev
212			bdev_register:CryptoMallocBdev
213		)
214	fi
215
216	if [[ $SPDK_TEST_PMDK -eq 1 ]]; then
217		pmem_pool_file=$(mktemp /tmp/pool_file1.XXXXX)
218		rm -f $pmem_pool_file
219		tgt_rpc bdev_pmem_create_pool $pmem_pool_file 128 4096
220		tgt_rpc bdev_pmem_create -n pmem1 $pmem_pool_file
221		expected_notifications+=(bdev_register:pmem1)
222	fi
223
224	if [[ $SPDK_TEST_RBD -eq 1 ]]; then
225		rbd_setup 127.0.0.1
226		tgt_rpc bdev_rbd_create $RBD_POOL $RBD_NAME 4096
227		expected_notifications+=(bdev_register:Ceph0)
228	fi
229
230	tgt_check_notifications "${expected_notifications[@]}"
231
232	timing_exit "${FUNCNAME[0]}"
233}
234
235function cleanup_bdev_subsystem_config() {
236	timing_enter "${FUNCNAME[0]}"
237
238	if [[ $SPDK_TEST_BLOCKDEV -eq 1 ]]; then
239		tgt_rpc bdev_lvol_delete lvs_test/clone0
240		tgt_rpc bdev_lvol_delete lvs_test/lvol0
241		tgt_rpc bdev_lvol_delete lvs_test/snapshot0
242		tgt_rpc bdev_lvol_delete_lvstore -l lvs_test
243	fi
244
245	if [[ $(uname -s) = Linux ]]; then
246		rm -f "$SPDK_TEST_STORAGE/sample_aio"
247	fi
248
249	if [[ $SPDK_TEST_PMDK -eq 1 && -n "$pmem_pool_file" && -f "$pmem_pool_file" ]]; then
250		tgt_rpc bdev_pmem_delete pmem1
251		tgt_rpc bdev_pmem_delete_pool $pmem_pool_file
252		rm -f $pmem_pool_file
253	fi
254
255	if [[ $SPDK_TEST_RBD -eq 1 ]]; then
256		rbd_cleanup
257	fi
258
259	timing_exit "${FUNCNAME[0]}"
260}
261
262function create_vhost_subsystem_config() {
263	timing_enter "${FUNCNAME[0]}"
264
265	tgt_rpc bdev_malloc_create 64 1024 --name MallocForVhost0
266	tgt_rpc bdev_split_create MallocForVhost0 8
267
268	tgt_rpc vhost_create_scsi_controller VhostScsiCtrlr0
269	tgt_rpc vhost_scsi_controller_add_target VhostScsiCtrlr0 0 MallocForVhost0p3
270	tgt_rpc vhost_scsi_controller_add_target VhostScsiCtrlr0 -1 MallocForVhost0p4
271	tgt_rpc vhost_controller_set_coalescing VhostScsiCtrlr0 1 100
272
273	tgt_rpc vhost_create_blk_controller VhostBlkCtrlr0 MallocForVhost0p5
274
275	timing_exit "${FUNCNAME[0]}"
276}
277
278function create_iscsi_subsystem_config() {
279	timing_enter "${FUNCNAME[0]}"
280	tgt_rpc bdev_malloc_create 64 1024 --name MallocForIscsi0
281	tgt_rpc iscsi_create_portal_group $PORTAL_TAG 127.0.0.1:$ISCSI_PORT
282	tgt_rpc iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK
283	tgt_rpc iscsi_create_target_node Target3 Target3_alias 'MallocForIscsi0:0' $PORTAL_TAG:$INITIATOR_TAG 64 -d
284	timing_exit "${FUNCNAME[0]}"
285}
286
287function create_nvmf_subsystem_config() {
288	timing_enter "${FUNCNAME[0]}"
289
290	NVMF_FIRST_TARGET_IP="127.0.0.1"
291	if [[ $SPDK_TEST_NVMF_TRANSPORT == "rdma" ]]; then
292		TEST_TRANSPORT=$SPDK_TEST_NVMF_TRANSPORT nvmftestinit
293	fi
294
295	if [[ -z $NVMF_FIRST_TARGET_IP ]]; then
296		echo "Error: no NIC for nvmf test"
297		return 1
298	fi
299
300	tgt_rpc bdev_malloc_create 8 512 --name MallocForNvmf0
301	tgt_rpc bdev_malloc_create 4 1024 --name MallocForNvmf1
302
303	tgt_rpc nvmf_create_transport -t $SPDK_TEST_NVMF_TRANSPORT -u 8192 -c 0
304	tgt_rpc nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s SPDK00000000000001
305	tgt_rpc nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 MallocForNvmf0
306	tgt_rpc nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 MallocForNvmf1
307	tgt_rpc nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t $SPDK_TEST_NVMF_TRANSPORT -a $NVMF_FIRST_TARGET_IP -s "$NVMF_PORT"
308
309	timing_exit "${FUNCNAME[0]}"
310}
311
312function create_virtio_initiator_config() {
313	timing_enter "${FUNCNAME[0]}"
314	initiator_rpc bdev_virtio_attach_controller -t user -a /var/tmp/VhostScsiCtrlr0 -d scsi VirtioScsiCtrlr0
315	initiator_rpc bdev_virtio_attach_controller -t user -a /var/tmp/VhostBlkCtrlr0 -d blk VirtioBlk0
316	timing_exit "${FUNCNAME[0]}"
317}
318
319function json_config_test_init() {
320	timing_enter "${FUNCNAME[0]}"
321	timing_enter json_config_setup_target
322
323	json_config_test_start_app target --wait-for-rpc
324
325	#TODO: global subsystem params
326
327	# Load nvme configuration. The load_config will issue framework_start_init automatically
328	(
329		$rootdir/scripts/gen_nvme.sh --json-with-subsystems
330	) | tgt_rpc load_config
331
332	tgt_check_notification_types
333
334	if [[ $SPDK_TEST_BLOCKDEV -eq 1 ]]; then
335		create_bdev_subsystem_config
336	fi
337
338	if [[ $SPDK_TEST_VHOST -eq 1 ]]; then
339		create_vhost_subsystem_config
340	fi
341
342	if [[ $SPDK_TEST_ISCSI -eq 1 ]]; then
343		create_iscsi_subsystem_config
344	fi
345
346	if [[ $SPDK_TEST_NVMF -eq 1 ]]; then
347		create_nvmf_subsystem_config
348	fi
349	timing_exit json_config_setup_target
350
351	if [[ $SPDK_TEST_VHOST_INIT -eq 1 ]]; then
352		json_config_test_start_app initiator
353		create_virtio_initiator_config
354	fi
355
356	tgt_rpc bdev_malloc_create 8 512 --name MallocBdevForConfigChangeCheck
357
358	timing_exit "${FUNCNAME[0]}"
359}
360
361function json_config_test_fini() {
362	timing_enter "${FUNCNAME[0]}"
363	local ret=0
364
365	if [[ -n "${app_pid[initiator]}" ]]; then
366		killprocess ${app_pid[initiator]}
367	fi
368
369	if [[ -n "${app_pid[target]}" ]]; then
370
371		# Remove any artifacts we created (files, lvol etc)
372		cleanup_bdev_subsystem_config
373
374		# SPDK_TEST_NVMF: Should we clear something?
375		killprocess ${app_pid[target]}
376	fi
377
378	rm -f "${configs_path[@]}"
379	timing_exit "${FUNCNAME[0]}"
380	return $ret
381}
382
383function json_config_clear() {
384	[[ -n "${#app_socket[$1]}" ]] # Check app type
385	$rootdir/test/json_config/clear_config.py -s ${app_socket[$1]} clear_config
386
387	# Check if config is clean.
388	# Global params can't be cleared so need to filter them out.
389	local config_filter="$rootdir/test/json_config/config_filter.py"
390
391	# RPC's used to cleanup configuration (e.g. to delete split and nvme bdevs)
392	# complete immediately and they don't wait for the unregister callback.
393	# It causes that configuration may not be fully cleaned at this moment and
394	# we should to wait a while. (See github issue #789)
395	count=100
396	while [ $count -gt 0 ]; do
397		$rootdir/scripts/rpc.py -s "${app_socket[$1]}" save_config | $config_filter -method delete_global_parameters | $config_filter -method check_empty && break
398		count=$((count - 1))
399		sleep 0.1
400	done
401
402	if [ $count -eq 0 ]; then
403		return 1
404	fi
405}
406
407on_error_exit() {
408	set -x
409	set +e
410	print_backtrace
411	trap - ERR
412	echo "Error on $1 - $2"
413	json_config_test_fini
414	exit 1
415}
416
417trap 'on_error_exit "${FUNCNAME}" "${LINENO}"' ERR
418echo "INFO: JSON configuration test init"
419json_config_test_init
420
421tgt_rpc save_config > ${configs_path[target]}
422
423echo "INFO: shutting down applications..."
424if [[ $SPDK_TEST_VHOST_INIT -eq 1 ]]; then
425	initiator_rpc save_config > ${configs_path[initiator]}
426	json_config_clear initiator
427	json_config_test_shutdown_app initiator
428fi
429
430json_config_clear target
431json_config_test_shutdown_app target
432
433echo "INFO: relaunching applications..."
434json_config_test_start_app target --json ${configs_path[target]}
435if [[ $SPDK_TEST_VHOST_INIT -eq 1 ]]; then
436	json_config_test_start_app initiator --json ${configs_path[initiator]}
437fi
438
439echo "INFO: Checking if target configuration is the same..."
440$rootdir/test/json_config/json_diff.sh <(tgt_rpc save_config) "${configs_path[target]}"
441if [[ $SPDK_TEST_VHOST_INIT -eq 1 ]]; then
442	echo "INFO: Checking if virtio initiator configuration is the same..."
443	$rootdir/test/json_config/json_diff.sh <(initiator_rpc save_config) "${configs_path[initiator]}"
444fi
445
446echo "INFO: changing configuration and checking if this can be detected..."
447# Self test to check if configuration diff can be detected.
448tgt_rpc bdev_malloc_delete MallocBdevForConfigChangeCheck
449if $rootdir/test/json_config/json_diff.sh <(tgt_rpc save_config) "${configs_path[target]}" > /dev/null; then
450	echo "ERROR: intentional configuration difference not detected!"
451	false
452else
453	echo "INFO: configuration change detected."
454fi
455
456json_config_test_fini
457
458echo "INFO: Success"
459