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	if zpool import -d $DEVICE_DIR -T $txg $TESTPOOL1; then
137		verify_data_md5sums $MD5FILE && retval=0
138
139		log_must check_pool_config $TESTPOOL1 "$poolcheck"
140		log_must zpool destroy $TESTPOOL1
141	fi
142
143	# Cleanup
144	if [[ -n $pathstochange ]]; then
145		for dev in $pathstochange; do
146			log_must mv "${dev}_new" $dev
147		done
148	fi
149	# Fast way to clear vdev labels
150	log_must zpool create -f $TESTPOOL2 $VDEV0 $VDEV1 $VDEV2 $VDEV3 $VDEV4
151	log_must zpool destroy $TESTPOOL2
152
153	log_note ""
154	return $retval
155}
156
157function test_add_vdevs
158{
159	typeset poolcreate="$1"
160	typeset addvdevs="$2"
161
162	log_note "$0: pool '$poolcreate', add $addvdevs."
163
164	for retry in $(seq 1 5); do
165		test_common "$poolcreate" "$addvdevs" && return
166		log_note "Retry $retry / 5 for test_add_vdevs()"
167	done
168
169	log_fail "Exhausted all 5 retries for test_add_vdevs()"
170}
171
172function test_attach_vdev
173{
174	typeset poolcreate="$1"
175	typeset attachto="$2"
176	typeset attachvdev="$3"
177
178	log_note "$0: pool '$poolcreate', attach $attachvdev to $attachto."
179
180	for retry in $(seq 1 5); do
181		test_common "$poolcreate" "" "$attachto $attachvdev" && return
182		log_note "Retry $retry / 5 for test_attach_vdev()"
183	done
184
185	log_fail "Exhausted all 5 retries for test_attach_vdev()"
186}
187
188function test_detach_vdev
189{
190	typeset poolcreate="$1"
191	typeset detachvdev="$2"
192
193	log_note "$0: pool '$poolcreate', detach $detachvdev."
194
195	for retry in $(seq 1 5); do
196		test_common "$poolcreate" "" "" "$detachvdev" && return
197		log_note "Retry $retry / 5 for test_detach_vdev()"
198	done
199
200	log_fail "Exhausted all 5 retries for test_detach_vdev()"
201}
202
203function test_attach_detach_vdev
204{
205	typeset poolcreate="$1"
206	typeset attachto="$2"
207	typeset attachvdev="$3"
208	typeset detachvdev="$4"
209
210	log_note "$0: pool '$poolcreate', attach $attachvdev to $attachto," \
211	    "then detach $detachvdev."
212
213	for retry in $(seq 1 5); do
214		test_common "$poolcreate" "" "$attachto $attachvdev" \
215		    "$detachvdev" && return
216		log_note "Retry $retry / 5 for test_attach_detach_vdev()"
217	done
218
219	log_fail "Exhausted all 5 retries for test_attach_detach_vdev()"
220}
221
222function test_remove_vdev
223{
224	typeset poolcreate="$1"
225	typeset removevdev="$2"
226	typeset finalpool="$3"
227
228	log_note "$0: pool '$poolcreate', remove $removevdev."
229
230	for retry in $(seq 1 5); do
231		test_common "$poolcreate" "" "" "" "$removevdev" \
232		    "$finalpool" && return
233		log_note "Retry $retry / 5 for test_remove_vdev()"
234	done
235
236	log_fail "Exhausted all 5 retries for test_remove_vdev()"
237}
238
239# Record txg history
240is_linux && log_must set_tunable32 TXG_HISTORY 100
241
242# Make the devices bigger to reduce chances of overwriting MOS metadata.
243increase_device_sizes $(( FILE_SIZE * 4 ))
244
245# Increase the number of metaslabs for small pools temporarily to
246# reduce the chance of reusing a metaslab that holds old MOS metadata.
247log_must set_tunable64 VDEV_MIN_MS_COUNT 150
248
249# Part of the rewind test is to see how it reacts to path changes
250typeset pathstochange="$VDEV0 $VDEV1 $VDEV2 $VDEV3"
251
252log_note " == test rewind after device addition == "
253
254test_add_vdevs "$VDEV0" "$VDEV1"
255test_add_vdevs "$VDEV0 $VDEV1" "$VDEV2"
256test_add_vdevs "$VDEV0" "$VDEV1 $VDEV2"
257test_add_vdevs "mirror $VDEV0 $VDEV1" "mirror $VDEV2 $VDEV3"
258test_add_vdevs "$VDEV0" "raidz $VDEV1 $VDEV2 $VDEV3"
259test_add_vdevs "$VDEV0" "draid $VDEV1 $VDEV2 $VDEV3"
260test_add_vdevs "$VDEV0" "log $VDEV1"
261test_add_vdevs "$VDEV0 log $VDEV1" "$VDEV2"
262
263log_note " == test rewind after device attach == "
264
265test_attach_vdev "$VDEV0" "$VDEV0" "$VDEV1"
266test_attach_vdev "mirror $VDEV0 $VDEV1" "$VDEV0" "$VDEV2"
267test_attach_vdev "$VDEV0 $VDEV1" "$VDEV0" "$VDEV2"
268
269log_note " == test rewind after device removal == "
270
271# Once we remove a device it will be overlooked in the device scan, so we must
272# preserve its original path
273pathstochange="$VDEV0 $VDEV2"
274test_remove_vdev "$VDEV0 $VDEV1 $VDEV2" "$VDEV1" "$VDEV0 $VDEV2"
275
276#
277# Path change and detach are incompatible. Detach changes the guid of the vdev
278# so we have no direct way to link the new path to an existing vdev.
279#
280pathstochange=""
281
282log_note " == test rewind after device detach == "
283
284test_detach_vdev "mirror $VDEV0 $VDEV1" "$VDEV1"
285test_detach_vdev "mirror $VDEV0 $VDEV1 mirror $VDEV2 $VDEV3" "$VDEV1"
286test_detach_vdev "$VDEV0 log mirror $VDEV1 $VDEV2" "$VDEV2"
287
288log_note " == test rewind after device attach followed by device detach == "
289
290#
291# We need to disable vdev validation since once we detach VDEV1, VDEV0 will
292# inherit the mirror tvd's guid and lose its original guid.
293#
294set_vdev_validate_skip 1
295test_attach_detach_vdev "$VDEV0" "$VDEV0" "$VDEV1" "$VDEV1"
296set_vdev_validate_skip 0
297
298log_pass "zpool import rewind after configuration change passed."
299