1#!/usr/bin/ksh
2
3#
4# This file and its contents are supplied under the terms of the
5# Common Development and Distribution License ("CDDL"), version 1.0.
6# You may only use this file in accordance with the terms of version
7# 1.0 of the CDDL.
8#
9# A full copy of the text of the CDDL should have accompanied this
10# source.  A copy of the CDDL is also available via the Internet at
11# http://www.illumos.org/license/CDDL.
12#
13
14#
15# Copyright (c) 2016 by Delphix. All rights reserved.
16#
17
18. $STF_SUITE/include/libtest.shlib
19. $STF_SUITE/tests/functional/cli_root/zpool_import/zpool_import.cfg
20
21#
22# Prototype cleanup function for zpool_import tests.
23#
24function cleanup
25{
26	# clear any remaining zinjections
27	log_must zinject -c all > /dev/null
28
29	destroy_pool $TESTPOOL1
30
31	log_must rm -f $CPATH $CPATHBKP $CPATHBKP2 $MD5FILE $MD5FILE2
32
33	log_must rm -rf $DEVICE_DIR/*
34	typeset i=0
35	while (( i < $MAX_NUM )); do
36		log_must mkfile $FILE_SIZE ${DEVICE_DIR}/${DEVICE_FILE}$i
37		((i += 1))
38	done
39}
40
41#
42# Write a bit of data and sync several times.
43# This function is intended to be used by zpool rewind tests.
44#
45function sync_some_data_a_few_times
46{
47	typeset pool=$1
48	typeset -i a_few_times=${2:-10}
49
50	typeset file="/$pool/tmpfile"
51	for i in {0..$a_few_times}; do
52		dd if=/dev/urandom of=${file}_$i bs=128k count=10
53		sync
54	done
55
56	return 0
57}
58
59#
60# Just write a moderate amount of data to the pool.
61#
62function write_some_data
63{
64	typeset pool=$1
65	typeset files10mb=${2:-10}
66
67	typeset ds="$pool/fillerds"
68	zfs create $ds
69	[[ $? -ne 0 ]] && return 1
70
71	# Create 100 MB of data
72	typeset file="/$ds/fillerfile"
73	for i in {1..$files10mb}; do
74		dd if=/dev/urandom of=$file.$i bs=128k count=80
75		[[ $? -ne 0 ]] && return 1
76	done
77
78	return 0
79}
80
81#
82# Create/overwrite a few datasets with files.
83# Apply md5sum on all the files and store checksums in a file.
84#
85# newdata: overwrite existing files if false.
86# md5file: file where to store md5sums
87# datasetname: base name for datasets
88#
89function _generate_data_common
90{
91	typeset pool=$1
92	typeset newdata=$2
93	typeset md5file=$3
94	typeset datasetname=$4
95
96	typeset -i datasets=3
97	typeset -i files=5
98	typeset -i blocks=10
99
100	[[ -n $md5file ]] && rm -f $md5file
101	for i in {1..$datasets}; do
102		( $newdata ) && log_must zfs create "$pool/$datasetname$i"
103		for j in {1..$files}; do
104			typeset file="/$pool/$datasetname$i/file$j"
105			dd if=/dev/urandom of=$file bs=128k count=$blocks > /dev/null
106			[[ -n $md5file ]] && md5sum $file >> $md5file
107		done
108		( $newdata ) && sync
109	done
110
111	return 0
112}
113
114function generate_data
115{
116	typeset pool=$1
117	typeset md5file="$2"
118	typeset datasetname=${3:-ds}
119
120	_generate_data_common $pool true "$md5file" $datasetname
121}
122
123function overwrite_data
124{
125	typeset pool=$1
126	typeset md5file="$2"
127	typeset datasetname=${3:-ds}
128
129	_generate_data_common $1 false "$md5file" $datasetname
130}
131
132#
133# Verify md5sums of every file in md5sum file $1.
134#
135function verify_data_md5sums
136{
137	typeset md5file=$1
138
139	if [[ ! -f $md5file ]]; then
140		log_note "md5 sums file '$md5file' doesn't exist"
141		return 1
142	fi
143
144	md5sum -c --quiet $md5file
145	return $?
146}
147
148#
149# Set devices size in DEVICE_DIR to $1.
150#
151function increase_device_sizes
152{
153	typeset newfilesize=$1
154
155	typeset -i i=0
156	while (( i < $MAX_NUM )); do
157		log_must mkfile $newfilesize ${DEVICE_DIR}/${DEVICE_FILE}$i
158		((i += 1))
159	done
160}
161
162#
163# Translate vdev names returned by zpool status into more generic names.
164#
165# eg: mirror-2 --> mirror
166#
167function _translate_vdev
168{
169	typeset vdev=$1
170
171	typeset keywords="mirror replacing raidz1 raidz2 raidz3 indirect"
172	for word in $keywords; do
173		echo $vdev | egrep "^${word}-[0-9]+\$" > /dev/null
174		if [[ $? -eq 0 ]]; then
175			vdev=$word
176			break
177		fi
178	done
179
180	[[ $vdev == "logs" ]] && echo "log" && return 0
181	[[ $vdev == "raidz1" ]] && echo "raidz" && return 0
182
183	echo $vdev
184	return 0
185}
186
187#
188# Check that pool configuration returned by zpool status matches expected
189# configuration. Format for the check string is same as the vdev arguments for
190# creating a pool
191# Add -q for quiet mode.
192#
193# eg: check_pool_config pool1 "mirror c0t0d0s0 c0t1d0s0 log c1t1d0s0"
194#
195function check_pool_config
196{
197	typeset logfailure=true
198	if [[ $1 == '-q' ]]; then
199		logfailure=false
200		shift
201	fi
202
203	typeset poolname=$1
204	typeset expected=$2
205
206	typeset status
207	status=$(zpool status $poolname 2>&1)
208	if [[ $? -ne 0 ]]; then
209		if ( $logfailure ); then
210			log_note "zpool status $poolname failed: $status"
211		fi
212		return 1
213	fi
214
215	typeset actual=""
216	typeset began=false
217	printf "$status\n" | while read line; do
218		typeset vdev=$(echo "$line" | awk '{printf $1}')
219		if ( ! $began ) && [[ $vdev == NAME ]]; then
220			began=true
221			continue
222		fi
223		( $began ) && [[ -z $vdev ]] && break;
224
225		if ( $began ); then
226			[[ -z $actual ]] && actual="$vdev" && continue
227			vdev=$(_translate_vdev $vdev)
228			actual="$actual $vdev"
229		fi
230	done
231
232	expected="$poolname $expected"
233
234	if [[ "$actual" != "$expected" ]]; then
235		if ( $logfailure ); then
236			log_note "expected pool vdevs:"
237			log_note "> '$expected'"
238			log_note "actual pool vdevs:"
239			log_note "> '$actual'"
240		fi
241		return 1
242	fi
243
244	return 0
245}
246
247#
248# Check that pool configuration returned by zpool status matches expected
249# configuration within a given timeout in seconds. See check_pool_config().
250#
251# eg: wait_for_pool_config pool1 "mirror c0t0d0s0 c0t1d0s0" 60
252#
253function wait_for_pool_config
254{
255	typeset poolname=$1
256	typeset expectedconfig="$2"
257	typeset -i timeout=${3:-60}
258
259	timeout=$(( $timeout + $(date +%s) ))
260
261	while  (( $(date +%s) < $timeout )); do
262		check_pool_config -q $poolname "$expectedconfig"
263		[[ $? -eq 0 ]] && return 0
264		sleep 3
265	done
266
267	check_pool_config $poolname "$expectedconfig"
268	return $?
269}
270
271#
272# Check that pool status is ONLINE
273#
274function check_pool_healthy
275{
276	typeset pool=$1
277
278	typeset status
279	status=$(zpool status $pool 2>&1)
280	if [[ $? -ne 0 ]]; then
281		log_note "zpool status $pool failed: $status"
282		return 1
283	fi
284
285	status=$(echo "$status" | grep "$pool" | grep -v "pool:" | \
286	    awk '{print $2}')
287
288	if [[ $status != "ONLINE" ]]; then
289		log_note "Invalid zpool status for '$pool': '$status'" \
290		    "!= 'ONLINE'"
291		return 1
292	fi
293
294	return 0
295}
296
297#
298# Return 0 if a device is currently being replaced in the pool.
299#
300function pool_is_replacing
301{
302	typeset pool=$1
303
304	zpool status $pool | grep "replacing" | grep "ONLINE" > /dev/null
305
306	return $?
307}
308
309function set_vdev_validate_skip
310{
311	mdb_set_uint32 "vdev_validate_skip" "$1"
312}
313
314function get_zfs_txg_timeout
315{
316	echo $(mdb_get_uint32 "zfs_txg_timeout")
317}
318
319function set_zfs_txg_timeout
320{
321	mdb_set_uint32 "zfs_txg_timeout" "$1"
322}
323
324function set_spa_load_verify_metadata
325{
326	mdb_set_uint32 "spa_load_verify_metadata" "$1"
327}
328
329function set_spa_load_verify_data
330{
331	mdb_set_uint32 "spa_load_verify_data" "$1"
332}
333
334function set_zfs_max_missing_tvds
335{
336	mdb_set_uint32 "zfs_max_missing_tvds" "$1"
337}
338
339#
340# Use mdb to find the last txg that was synced in an active pool.
341#
342function get_last_txg_synced
343{
344	typeset pool=$1
345
346	if is_linux; then
347		txg=$(tail "/proc/spl/kstat/zfs/$pool/txgs" |
348		    awk '$3=="C" {print $1}' | tail -1)
349		[[ "$txg" ]] || txg=0
350		echo $txg
351		return 0
352	fi
353
354	typeset spas
355	spas=$(mdb -k -e "::spa")
356	[[ $? -ne 0 ]] && return 1
357
358	typeset spa=""
359	print "$spas\n" | while read line; do
360		typeset poolname=$(echo "$line" | awk '{print $3}')
361		typeset addr=$(echo "$line" | awk '{print $1}')
362		if [[ $poolname == $pool ]]; then
363			spa=$addr
364			break
365		fi
366	done
367	if [[ -z $spa ]]; then
368		log_fail "Couldn't find pool '$pool'"
369		return 1
370	fi
371	typeset mdbcmd="$spa::print spa_t spa_ubsync.ub_txg | ::eval '.=E'"
372	typeset -i txg
373	txg=$(mdb -k -e "$mdbcmd")
374	[[ $? -ne 0 ]] && return 1
375
376	echo $txg
377	return 0
378}
379