summaryrefslogtreecommitdiffstats
path: root/src/spdk/test/json_config/json_config.sh
blob: 03d6bd5bd144c936720915107ffa9f71eccf3d64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
#!/usr/bin/env bash

rootdir=$(readlink -f $(dirname $0)/../..)
source "$rootdir/test/common/autotest_common.sh"
source "$rootdir/test/nvmf/common.sh"

if [[ $SPDK_TEST_ISCSI -eq 1 ]]; then
	source "$rootdir/test/iscsi_tgt/common.sh"
fi

if [[ $SPDK_TEST_VHOST -ne 1 && $SPDK_TEST_VHOST_INIT -eq 1 ]]; then
	SPDK_TEST_VHOST=1
	echo "WARNING: Virtio initiator JSON_config test requires vhost target."
	echo "         Setting SPDK_TEST_VHOST=1 for duration of current script."
fi

if ((SPDK_TEST_BLOCKDEV + \
	SPDK_TEST_ISCSI + \
	SPDK_TEST_NVMF + \
	SPDK_TEST_VHOST + \
	SPDK_TEST_VHOST_INIT + \
	SPDK_TEST_PMDK + \
	SPDK_TEST_RBD == 0)); then
	echo "WARNING: No tests are enabled so not running JSON configuration tests"
	exit 0
fi

declare -A app_pid=([target]="" [initiator]="")
declare -A app_socket=([target]='/var/tmp/spdk_tgt.sock' [initiator]='/var/tmp/spdk_initiator.sock')
declare -A app_params=([target]='-m 0x1 -s 1024' [initiator]='-m 0x2 -g -u -s 1024')
declare -A configs_path=([target]="$rootdir/spdk_tgt_config.json" [initiator]="$rootdir/spdk_initiator_config.json")

function tgt_rpc() {
	$rootdir/scripts/rpc.py -s "${app_socket[target]}" "$@"
}

function initiator_rpc() {
	$rootdir/scripts/rpc.py -s "${app_socket[initiator]}" "$@"
}

RE_UUID="[[:alnum:]-]+"
last_event_id=0

function tgt_check_notification_types() {
	timing_enter "${FUNCNAME[0]}"

	local ret=0
	local enabled_types=("bdev_register" "bdev_unregister")

	local get_types=($(tgt_rpc notify_get_types | jq -r '.[]'))
	if [[ ${enabled_types[*]} != "${get_types[*]}" ]]; then
		echo "ERROR: expected types: ${enabled_types[*]}, but got: ${get_types[*]}"
		ret=1
	fi

	timing_exit "${FUNCNAME[0]}"
	return $ret
}

function tgt_check_notifications() {
	local event_line event ev_type ev_ctx
	local rc=""

	while read -r event_line; do
		# remove ID
		event="${event_line%:*}"

		ev_type=${event%:*}
		ev_ctx=${event#*:}

		ex_ev_type=${1%%:*}
		ex_ev_ctx=${1#*:}

		last_event_id=${event_line##*:}

		# set rc=false in case of failure so all errors can be printed
		if (($# == 0)); then
			echo "ERROR: got extra event: $event_line"
			rc=false
			continue
		elif ! echo "$ev_type" | grep -E -q "^${ex_ev_type}\$" || ! echo "$ev_ctx" | grep -E -q "^${ex_ev_ctx}\$"; then
			echo "ERROR: expected event '$1' but got '$event' (whole event line: $event_line)"
			rc=false
		fi

		shift
	done < <(tgt_rpc notify_get_notifications -i ${last_event_id} | jq -r '.[] | "\(.type):\(.ctx):\(.id)"')

	$rc

	if (($# != 0)); then
		echo "ERROR: missing events:"
		echo "$@"
		return 1
	fi
}

# $1 - target / initiator
# $2..$n app parameters
function json_config_test_start_app() {
	local app=$1
	shift

	[[ -n "${#app_socket[$app]}" ]] # Check app type
	[[ -z "${app_pid[$app]}" ]]     # Assert if app is not running

	local app_extra_params=""
	if [[ $SPDK_TEST_VHOST -eq 1 || $SPDK_TEST_VHOST_INIT -eq 1 ]]; then
		# If PWD is nfs/sshfs we can't create UNIX sockets there. Always use safe location instead.
		app_extra_params='-S /var/tmp'
	fi

	$SPDK_BIN_DIR/spdk_tgt ${app_params[$app]} ${app_extra_params} -r ${app_socket[$app]} "$@" &
	app_pid[$app]=$!

	echo "Waiting for $app to run..."
	waitforlisten ${app_pid[$app]} ${app_socket[$app]}
	echo ""
}

# $1 - target / initiator
function json_config_test_shutdown_app() {
	local app=$1

	# Check app type && assert app was started
	[[ -n "${#app_socket[$app]}" ]]
	[[ -n "${app_pid[$app]}" ]]

	# spdk_kill_instance RPC will trigger ASAN
	kill -SIGINT ${app_pid[$app]}

	for ((i = 0; i < 30; i++)); do
		if ! kill -0 ${app_pid[$app]} 2> /dev/null; then
			app_pid[$app]=
			break
		fi
		sleep 0.5
	done

	if [[ -n "${app_pid[$app]}" ]]; then
		echo "SPDK $app shutdown timeout"
		return 1
	fi

	echo "SPDK $app shutdown done"
}

function create_bdev_subsystem_config() {
	timing_enter "${FUNCNAME[0]}"

	local expected_notifications=()

	if [[ $SPDK_TEST_BLOCKDEV -eq 1 ]]; then
		local lvol_store_base_bdev=Nvme0n1
		if ! tgt_rpc get_bdevs --name ${lvol_store_base_bdev} > /dev/null; then
			if [[ $(uname -s) = Linux ]]; then
				lvol_store_base_bdev=aio_disk
				echo "WARNING: No NVMe drive found. Using '$lvol_store_base_bdev' instead."
			else
				echo "ERROR: No NVMe drive found and bdev_aio is not supported on $(uname -s)."
				timing_exit "${FUNCNAME[0]}"
				return 1
			fi
		fi

		tgt_rpc bdev_split_create $lvol_store_base_bdev 2
		tgt_rpc bdev_split_create Malloc0 3
		tgt_rpc bdev_malloc_create 8 4096 --name Malloc3
		tgt_rpc bdev_passthru_create -b Malloc3 -p PTBdevFromMalloc3

		tgt_rpc bdev_null_create Null0 32 512

		tgt_rpc bdev_malloc_create 32 512 --name Malloc0
		tgt_rpc bdev_malloc_create 16 4096 --name Malloc1

		expected_notifications+=(
			bdev_register:${lvol_store_base_bdev}
			bdev_register:${lvol_store_base_bdev}p0
			bdev_register:${lvol_store_base_bdev}p1
			bdev_register:Malloc3
			bdev_register:PTBdevFromMalloc3
			bdev_register:Null0
			bdev_register:Malloc0p0
			bdev_register:Malloc0p1
			bdev_register:Malloc0p2
			bdev_register:Malloc0
			bdev_register:Malloc1
		)

		if [[ $(uname -s) = Linux ]]; then
			# This AIO bdev must be large enough to be used as LVOL store
			dd if=/dev/zero of="$SPDK_TEST_STORAGE/sample_aio" bs=1024 count=102400
			tgt_rpc bdev_aio_create "$SPDK_TEST_STORAGE/sample_aio" aio_disk 1024
			expected_notifications+=(bdev_register:aio_disk)
		fi

		# For LVOLs use split to check for proper order of initialization.
		# If LVOLs cofniguration will be reordered (eg moved before splits or AIO/NVMe)
		# it should fail loading JSON config from file.
		tgt_rpc bdev_lvol_create_lvstore -c 1048576 ${lvol_store_base_bdev}p0 lvs_test
		tgt_rpc bdev_lvol_create -l lvs_test lvol0 32
		tgt_rpc bdev_lvol_create -l lvs_test -t lvol1 32
		tgt_rpc bdev_lvol_snapshot lvs_test/lvol0 snapshot0
		tgt_rpc bdev_lvol_clone lvs_test/snapshot0 clone0

		expected_notifications+=(
			"bdev_register:$RE_UUID"
			"bdev_register:$RE_UUID"
			"bdev_register:$RE_UUID"
			"bdev_register:$RE_UUID"
		)
	fi

	if [[ $SPDK_TEST_CRYPTO -eq 1 ]]; then
		tgt_rpc bdev_malloc_create 8 1024 --name MallocForCryptoBdev
		if [[ $(lspci -d:37c8 | wc -l) -eq 0 ]]; then
			local crypto_dirver=crypto_aesni_mb
		else
			local crypto_dirver=crypto_qat
		fi

		tgt_rpc bdev_crypto_create MallocForCryptoBdev CryptoMallocBdev $crypto_dirver 0123456789123456
		expected_notifications+=(
			bdev_register:MallocForCryptoBdev
			bdev_register:CryptoMallocBdev
		)
	fi

	if [[ $SPDK_TEST_PMDK -eq 1 ]]; then
		pmem_pool_file=$(mktemp /tmp/pool_file1.XXXXX)
		rm -f $pmem_pool_file
		tgt_rpc create_pmem_pool $pmem_pool_file 128 4096
		tgt_rpc bdev_pmem_create -n pmem1 $pmem_pool_file
		expected_notifications+=(bdev_register:pmem1)
	fi

	if [[ $SPDK_TEST_RBD -eq 1 ]]; then
		rbd_setup 127.0.0.1
		tgt_rpc bdev_rbd_create $RBD_POOL $RBD_NAME 4096
		expected_notifications+=(bdev_register:Ceph0)
	fi

	tgt_check_notifications "${expected_notifications[@]}"

	timing_exit "${FUNCNAME[0]}"
}

function cleanup_bdev_subsystem_config() {
	timing_enter "${FUNCNAME[0]}"

	if [[ $SPDK_TEST_BLOCKDEV -eq 1 ]]; then
		tgt_rpc bdev_lvol_delete lvs_test/clone0
		tgt_rpc bdev_lvol_delete lvs_test/lvol0
		tgt_rpc bdev_lvol_delete lvs_test/snapshot0
		tgt_rpc bdev_lvol_delete_lvstore -l lvs_test
	fi

	if [[ $(uname -s) = Linux ]]; then
		rm -f "$SPDK_TEST_STORAGE/sample_aio"
	fi

	if [[ $SPDK_TEST_PMDK -eq 1 && -n "$pmem_pool_file" && -f "$pmem_pool_file" ]]; then
		tgt_rpc bdev_pmem_delete pmem1
		tgt_rpc bdev_pmem_delete_pool $pmem_pool_file
		rm -f $pmem_pool_file
	fi

	if [[ $SPDK_TEST_RBD -eq 1 ]]; then
		rbd_cleanup
	fi

	timing_exit "${FUNCNAME[0]}"
}

function create_vhost_subsystem_config() {
	timing_enter "${FUNCNAME[0]}"

	tgt_rpc bdev_malloc_create 64 1024 --name MallocForVhost0
	tgt_rpc bdev_split_create MallocForVhost0 8

	tgt_rpc vhost_create_scsi_controller VhostScsiCtrlr0
	tgt_rpc vhost_scsi_controller_add_target VhostScsiCtrlr0 0 MallocForVhost0p3
	tgt_rpc vhost_scsi_controller_add_target VhostScsiCtrlr0 -1 MallocForVhost0p4
	tgt_rpc vhost_controller_set_coalescing VhostScsiCtrlr0 1 100

	tgt_rpc vhost_create_blk_controller VhostBlkCtrlr0 MallocForVhost0p5

	# FIXME: enable after vhost-nvme is properly implemented against the latest rte_vhost (DPDK 19.05+)
	#	tgt_rpc vhost_create_nvme_controller   VhostNvmeCtrlr0 16
	#	tgt_rpc vhost_nvme_controller_add_ns                 VhostNvmeCtrlr0 MallocForVhost0p6

	timing_exit "${FUNCNAME[0]}"
}

function create_iscsi_subsystem_config() {
	timing_enter "${FUNCNAME[0]}"
	tgt_rpc bdev_malloc_create 64 1024 --name MallocForIscsi0
	tgt_rpc iscsi_create_portal_group $PORTAL_TAG 127.0.0.1:$ISCSI_PORT
	tgt_rpc iscsi_create_initiator_group $INITIATOR_TAG $INITIATOR_NAME $NETMASK
	tgt_rpc iscsi_create_target_node Target3 Target3_alias 'MallocForIscsi0:0' $PORTAL_TAG:$INITIATOR_TAG 64 -d
	timing_exit "${FUNCNAME[0]}"
}

function create_nvmf_subsystem_config() {
	timing_enter "${FUNCNAME[0]}"

	RDMA_IP_LIST=$(get_available_rdma_ips)
	NVMF_FIRST_TARGET_IP=$(echo "$RDMA_IP_LIST" | head -n 1)
	if [[ -z $NVMF_FIRST_TARGET_IP ]]; then
		echo "Error: no NIC for nvmf test"
		return 1
	fi

	tgt_rpc bdev_malloc_create 8 512 --name MallocForNvmf0
	tgt_rpc bdev_malloc_create 4 1024 --name MallocForNvmf1

	tgt_rpc nvmf_create_transport -t RDMA -u 8192 -c 0
	tgt_rpc nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s SPDK00000000000001
	tgt_rpc nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 MallocForNvmf0
	tgt_rpc nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 MallocForNvmf1
	tgt_rpc nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t RDMA -a $NVMF_FIRST_TARGET_IP -s "$NVMF_PORT"

	timing_exit "${FUNCNAME[0]}"
}

function create_virtio_initiator_config() {
	timing_enter "${FUNCNAME[0]}"
	initiator_rpc bdev_virtio_attach_controller -t user -a /var/tmp/VhostScsiCtrlr0 -d scsi VirtioScsiCtrlr0
	initiator_rpc bdev_virtio_attach_controller -t user -a /var/tmp/VhostBlkCtrlr0 -d blk VirtioBlk0
	# TODO: initiator_rpc bdev_virtio_attach_controller -t user -a /var/tmp/VhostNvmeCtrlr0 -d nvme VirtioNvme0
	timing_exit "${FUNCNAME[0]}"
}

function json_config_test_init() {
	timing_enter "${FUNCNAME[0]}"
	timing_enter json_config_setup_target

	json_config_test_start_app target --wait-for-rpc

	#TODO: global subsystem params

	# Load nvme configuration. The load_config will issue framework_start_init automatically
	(
		echo '{"subsystems": ['
		$rootdir/scripts/gen_nvme.sh --json | jq -r "del(.config[] | select(.params.name!=\"Nvme0\"))"
		echo ']}'
	) | tgt_rpc load_config

	tgt_check_notification_types

	if [[ $SPDK_TEST_BLOCKDEV -eq 1 ]]; then
		create_bdev_subsystem_config
	fi

	if [[ $SPDK_TEST_VHOST -eq 1 ]]; then
		create_vhost_subsystem_config
	fi

	if [[ $SPDK_TEST_ISCSI -eq 1 ]]; then
		create_iscsi_subsystem_config
	fi

	if [[ $SPDK_TEST_NVMF -eq 1 ]]; then
		create_nvmf_subsystem_config
	fi
	timing_exit json_config_setup_target

	if [[ $SPDK_TEST_VHOST_INIT -eq 1 ]]; then
		json_config_test_start_app initiator
		create_virtio_initiator_config
	fi

	tgt_rpc bdev_malloc_create 8 512 --name MallocBdevForConfigChangeCheck

	timing_exit "${FUNCNAME[0]}"
}

function json_config_test_fini() {
	timing_enter "${FUNCNAME[0]}"
	local ret=0

	if [[ -n "${app_pid[initiator]}" ]]; then
		killprocess ${app_pid[initiator]}
	fi

	if [[ -n "${app_pid[target]}" ]]; then

		# Remove any artifacts we created (files, lvol etc)
		cleanup_bdev_subsystem_config

		# SPDK_TEST_NVMF: Should we clear something?
		killprocess ${app_pid[target]}
	fi

	rm -f "${configs_path[@]}"
	timing_exit "${FUNCNAME[0]}"
	return $ret
}

function json_config_clear() {
	[[ -n "${#app_socket[$1]}" ]] # Check app type
	$rootdir/test/json_config/clear_config.py -s ${app_socket[$1]} clear_config

	# Check if config is clean.
	# Global params can't be cleared so need to filter them out.
	local config_filter="$rootdir/test/json_config/config_filter.py"

	# RPC's used to cleanup configuration (e.g. to delete split and nvme bdevs)
	# complete immediately and they don't wait for the unregister callback.
	# It causes that configuration may not be fully cleaned at this moment and
	# we should to wait a while. (See github issue #789)
	count=100
	while [ $count -gt 0 ]; do
		$rootdir/scripts/rpc.py -s "${app_socket[$1]}" save_config | $config_filter -method delete_global_parameters | $config_filter -method check_empty && break
		count=$((count - 1))
		sleep 0.1
	done

	if [ $count -eq 0 ]; then
		return 1
	fi
}

on_error_exit() {
	set -x
	set +e
	print_backtrace
	trap - ERR
	echo "Error on $1 - $2"
	json_config_test_fini
	exit 1
}

trap 'on_error_exit "${FUNCNAME}" "${LINENO}"' ERR
echo "INFO: JSON configuration test init"
json_config_test_init

tgt_rpc save_config > ${configs_path[target]}

echo "INFO: shutting down applications..."
if [[ $SPDK_TEST_VHOST_INIT -eq 1 ]]; then
	initiator_rpc save_config > ${configs_path[initiator]}
	json_config_clear initiator
	json_config_test_shutdown_app initiator
fi

json_config_clear target
json_config_test_shutdown_app target

echo "INFO: relaunching applications..."
json_config_test_start_app target --json ${configs_path[target]}
if [[ $SPDK_TEST_VHOST_INIT -eq 1 ]]; then
	json_config_test_start_app initiator --json ${configs_path[initiator]}
fi

echo "INFO: Checking if target configuration is the same..."
$rootdir/test/json_config/json_diff.sh <(tgt_rpc save_config) "${configs_path[target]}"
if [[ $SPDK_TEST_VHOST_INIT -eq 1 ]]; then
	echo "INFO: Checking if virtio initiator configuration is the same..."
	$rootdir/test/json_config/json_diff.sh <(initiator_rpc save_config) "${configs_path[initiator]}"
fi

echo "INFO: changing configuration and checking if this can be detected..."
# Self test to check if configuration diff can be detected.
tgt_rpc bdev_malloc_delete MallocBdevForConfigChangeCheck
if $rootdir/test/json_config/json_diff.sh <(tgt_rpc save_config) "${configs_path[target]}" > /dev/null; then
	echo "ERROR: intentional configuration difference not detected!"
	false
else
	echo "INFO: configuration change detected."
fi

json_config_test_fini

echo "INFO: Success"