1#!/bin/ksh -p
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) 2017 by Delphix. All rights reserved.
16#
17
18. $STF_SUITE/tests/functional/cli_root/zpool_import/zpool_import.kshlib
19
20#
21# DESCRIPTION:
22#	It should be possible to rewind a pool beyond a configuration change.
23#
24# STRATEGY:
25#	1. Create a pool.
26#	2. Generate files and remember their md5sum.
27#	3. Note last synced txg.
28#	4. Take a snapshot to make sure old blocks are not overwritten.
29#	5. Perform zpool add/attach/detach/remove operation.
30#	6. Change device paths if requested and re-import pool.
31#	7. Checkpoint the pool as one last attempt to preserve old blocks.
32#	8. Overwrite the files.
33#	9. Export the pool.
34#	10. Verify that we can rewind the pool to the noted txg.
35#	11. Verify that the files are readable and retain their old data.
36#
37# DISCLAIMER:
38#	This test can fail since nothing guarantees that old MOS blocks aren't
39#	overwritten. Snapshots protect datasets and data files but not the MOS.
40#	sync_some_data_a_few_times interleaves file data and MOS data for a few
41#	txgs, thus increasing the odds that some txgs will have their MOS data
42#	left untouched.
43#
44
45verify_runnable "global"
46
47function custom_cleanup
48{
49	set_vdev_validate_skip 0
50	cleanup
51	log_must set_tunable64 VDEV_MIN_MS_COUNT 16
52}
53
54log_onexit custom_cleanup
55
56function test_common
57{
58	typeset poolcreate="$1"
59	typeset addvdevs="$2"
60	typeset attachargs="${3:-}"
61	typeset detachvdev="${4:-}"
62	typeset removevdev="${5:-}"
63	typeset finalpool="${6:-}"
64	typeset retval=1
65
66	typeset poolcheck="$poolcreate"
67
68	log_must zpool create $TESTPOOL1 $poolcreate
69
70	log_must generate_data $TESTPOOL1 $MD5FILE
71
72	# syncing a few times while writing new data increases the odds that MOS
73	# metadata for some of the txgs will survive
74	log_must sync_some_data_a_few_times $TESTPOOL1
75	typeset txg
76	txg=$(get_last_txg_synced $TESTPOOL1)
77	log_must zfs snapshot -r $TESTPOOL1@snap1
78
79	#
80	# Perform config change operations
81	#
82	if [[ -n $addvdevs ]]; then
83		log_must zpool add -f $TESTPOOL1 $addvdevs
84	fi
85	if [[ -n $attachargs ]]; then
86		log_must zpool attach $TESTPOOL1 $attachargs
87	fi
88	if [[ -n $detachvdev ]]; then
89		log_must zpool detach $TESTPOOL1 $detachvdev
90	fi
91	if [[ -n $removevdev ]]; then
92		[[ -z $finalpool ]] &&
93		    log_fail "Must provide final pool status!"
94		log_must zpool remove $TESTPOOL1 $removevdev
95		log_must wait_for_pool_config $TESTPOOL1 "$finalpool"
96	fi
97	if [[ -n $pathstochange ]]; then
98		#
99		# Change device paths and re-import pool to update labels
100		#
101		zpool export $TESTPOOL1
102		for dev in $pathstochange; do
103			log_must mv $dev "${dev}_new"
104			poolcheck=$(echo "$poolcheck" | \
105			    sed "s:$dev:${dev}_new:g")
106		done
107		zpool import -d $DEVICE_DIR $TESTPOOL1
108	fi
109
110	#
111	# In an attempt to leave MOS data untouched so extreme
112	# rewind is successful during import we checkpoint the
113	# pool and hope that these MOS data are part of the
114	# checkpoint (e.g they stay around). If this goes as
115	# expected, then extreme rewind should rewind back even
116	# further than the time that we took the checkpoint.
117	#
118	# Note that, ideally we would want to take a checkpoint
119	# right after we record the txg we plan to rewind to.
120	# But since we can't attach, detach or remove devices
121	# while having a checkpoint, we take it after the
122	# operation that changes the config.
123	#
124	# However, it is possible the MOS data was overwritten
125	# in which case the pool will either be unimportable, or
126	# may have been rewound prior to the data being written.
127	# In which case an error is returned and test_common()
128	# is retried by the caller to minimize false positives.
129	#
130	log_must zpool checkpoint $TESTPOOL1
131
132	log_must overwrite_data $TESTPOOL1 ""
133
134	log_must zpool export $TESTPOOL1
135
136	zpool import -d $DEVICE_DIR -T $txg $TESTPOOL1
137	if (( $? == 0 )); then
138		verify_data_md5sums $MD5FILE
139		if (( $? == 0 )); then
140			retval=0
141		fi
142
143		log_must check_pool_config $TESTPOOL1 "$poolcheck"
144		log_must zpool destroy $TESTPOOL1
145	fi
146
147	# Cleanup
148	if [[ -n $pathstochange ]]; then
149		for dev in $pathstochange; do
150			log_must mv "${dev}_new" $dev
151		done
152	fi
153	# Fast way to clear vdev labels
154	log_must zpool create -f $TESTPOOL2 $VDEV0 $VDEV1 $VDEV2 $VDEV3 $VDEV4
155	log_must zpool destroy $TESTPOOL2
156
157	log_note ""
158	return $retval
159}
160
161function test_add_vdevs
162{
163	typeset poolcreate="$1"
164	typeset addvdevs="$2"
165
166	log_note "$0: pool '$poolcreate', add $addvdevs."
167
168	for retry in $(seq 1 5); do
169		test_common "$poolcreate" "$addvdevs" && return
170		log_note "Retry $retry / 5 for test_add_vdevs()"
171	done
172
173	log_fail "Exhausted all 5 retries for test_add_vdevs()"
174}
175
176function test_attach_vdev
177{
178	typeset poolcreate="$1"
179	typeset attachto="$2"
180	typeset attachvdev="$3"
181
182	log_note "$0: pool '$poolcreate', attach $attachvdev to $attachto."
183
184	for retry in $(seq 1 5); do
185		test_common "$poolcreate" "" "$attachto $attachvdev" && return
186		log_note "Retry $retry / 5 for test_attach_vdev()"
187	done
188
189	log_fail "Exhausted all 5 retries for test_attach_vdev()"
190}
191
192function test_detach_vdev
193{
194	typeset poolcreate="$1"
195	typeset detachvdev="$2"
196
197	log_note "$0: pool '$poolcreate', detach $detachvdev."
198
199	for retry in $(seq 1 5); do
200		test_common "$poolcreate" "" "" "$detachvdev" && return
201		log_note "Retry $retry / 5 for test_detach_vdev()"
202	done
203
204	log_fail "Exhausted all 5 retries for test_detach_vdev()"
205}
206
207function test_attach_detach_vdev
208{
209	typeset poolcreate="$1"
210	typeset attachto="$2"
211	typeset attachvdev="$3"
212	typeset detachvdev="$4"
213
214	log_note "$0: pool '$poolcreate', attach $attachvdev to $attachto," \
215	    "then detach $detachvdev."
216
217	for retry in $(seq 1 5); do
218		test_common "$poolcreate" "" "$attachto $attachvdev" \
219		    "$detachvdev" && return
220		log_note "Retry $retry / 5 for test_attach_detach_vdev()"
221	done
222
223	log_fail "Exhausted all 5 retries for test_attach_detach_vdev()"
224}
225
226function test_remove_vdev
227{
228	typeset poolcreate="$1"
229	typeset removevdev="$2"
230	typeset finalpool="$3"
231
232	log_note "$0: pool '$poolcreate', remove $removevdev."
233
234	for retry in $(seq 1 5); do
235		test_common "$poolcreate" "" "" "" "$removevdev" \
236		    "$finalpool" && return
237		log_note "Retry $retry / 5 for test_remove_vdev()"
238	done
239
240	log_fail "Exhausted all 5 retries for test_remove_vdev()"
241}
242
243# Record txg history
244is_linux && log_must set_tunable32 TXG_HISTORY 100
245
246# Make the devices bigger to reduce chances of overwriting MOS metadata.
247increase_device_sizes $(( FILE_SIZE * 4 ))
248
249# Increase the number of metaslabs for small pools temporarily to
250# reduce the chance of reusing a metaslab that holds old MOS metadata.
251log_must set_tunable64 VDEV_MIN_MS_COUNT 150
252
253# Part of the rewind test is to see how it reacts to path changes
254typeset pathstochange="$VDEV0 $VDEV1 $VDEV2 $VDEV3"
255
256log_note " == test rewind after device addition == "
257
258test_add_vdevs "$VDEV0" "$VDEV1"
259test_add_vdevs "$VDEV0 $VDEV1" "$VDEV2"
260test_add_vdevs "$VDEV0" "$VDEV1 $VDEV2"
261test_add_vdevs "mirror $VDEV0 $VDEV1" "mirror $VDEV2 $VDEV3"
262test_add_vdevs "$VDEV0" "raidz $VDEV1 $VDEV2 $VDEV3"
263test_add_vdevs "$VDEV0" "draid $VDEV1 $VDEV2 $VDEV3"
264test_add_vdevs "$VDEV0" "log $VDEV1"
265test_add_vdevs "$VDEV0 log $VDEV1" "$VDEV2"
266
267log_note " == test rewind after device attach == "
268
269test_attach_vdev "$VDEV0" "$VDEV0" "$VDEV1"
270test_attach_vdev "mirror $VDEV0 $VDEV1" "$VDEV0" "$VDEV2"
271test_attach_vdev "$VDEV0 $VDEV1" "$VDEV0" "$VDEV2"
272
273log_note " == test rewind after device removal == "
274
275# Once we remove a device it will be overlooked in the device scan, so we must
276# preserve its original path
277pathstochange="$VDEV0 $VDEV2"
278test_remove_vdev "$VDEV0 $VDEV1 $VDEV2" "$VDEV1" "$VDEV0 $VDEV2"
279
280#
281# Path change and detach are incompatible. Detach changes the guid of the vdev
282# so we have no direct way to link the new path to an existing vdev.
283#
284pathstochange=""
285
286log_note " == test rewind after device detach == "
287
288test_detach_vdev "mirror $VDEV0 $VDEV1" "$VDEV1"
289test_detach_vdev "mirror $VDEV0 $VDEV1 mirror $VDEV2 $VDEV3" "$VDEV1"
290test_detach_vdev "$VDEV0 log mirror $VDEV1 $VDEV2" "$VDEV2"
291
292log_note " == test rewind after device attach followed by device detach == "
293
294#
295# We need to disable vdev validation since once we detach VDEV1, VDEV0 will
296# inherit the mirror tvd's guid and lose its original guid.
297#
298set_vdev_validate_skip 1
299test_attach_detach_vdev "$VDEV0" "$VDEV0" "$VDEV1" "$VDEV1"
300set_vdev_validate_skip 0
301
302log_pass "zpool import rewind after configuration change passed."
303