| #!/bin/ksh -p |
| |
| # |
| # This file and its contents are supplied under the terms of the |
| # Common Development and Distribution License ("CDDL"), version 1.0. |
| # You may only use this file in accordance with the terms of version |
| # 1.0 of the CDDL. |
| # |
| # A full copy of the text of the CDDL should have accompanied this |
| # source. A copy of the CDDL is also available via the Internet at |
| # http://www.illumos.org/license/CDDL. |
| # |
| |
| # |
| # Copyright (c) 2017 by Delphix. All rights reserved. |
| # |
| |
| . $STF_SUITE/tests/functional/cli_root/zpool_import/zpool_import.kshlib |
| |
| # |
| # DESCRIPTION: |
| # It should be possible to rewind a pool beyond a configuration change. |
| # |
| # STRATEGY: |
| # 1. Create a pool. |
| # 2. Generate files and remember their md5sum. |
| # 3. Note last synced txg. |
| # 4. Take a snapshot to make sure old blocks are not overwritten. |
| # 5. Perform zpool add/attach/detach/remove operation. |
| # 6. Change device paths if requested and re-import pool. |
| # 7. Checkpoint the pool as one last attempt to preserve old blocks. |
| # 8. Overwrite the files. |
| # 9. Export the pool. |
| # 10. Verify that we can rewind the pool to the noted txg. |
| # 11. Verify that the files are readable and retain their old data. |
| # |
| # DISCLAIMER: |
| # This test can fail since nothing guarantees that old MOS blocks aren't |
| # overwritten. Snapshots protect datasets and data files but not the MOS. |
| # sync_some_data_a_few_times interleaves file data and MOS data for a few |
| # txgs, thus increasing the odds that some txgs will have their MOS data |
| # left untouched. |
| # |
| |
| verify_runnable "global" |
| |
| function custom_cleanup |
| { |
| set_vdev_validate_skip 0 |
| cleanup |
| log_must set_tunable64 VDEV_MIN_MS_COUNT 16 |
| } |
| |
| log_onexit custom_cleanup |
| |
| function test_common |
| { |
| typeset poolcreate="$1" |
| typeset addvdevs="$2" |
| typeset attachargs="${3:-}" |
| typeset detachvdev="${4:-}" |
| typeset removevdev="${5:-}" |
| typeset finalpool="${6:-}" |
| typeset retval=1 |
| |
| typeset poolcheck="$poolcreate" |
| |
| log_must zpool create $TESTPOOL1 $poolcreate |
| |
| log_must generate_data $TESTPOOL1 $MD5FILE |
| |
| # syncing a few times while writing new data increases the odds that MOS |
| # metadata for some of the txgs will survive |
| log_must sync_some_data_a_few_times $TESTPOOL1 |
| typeset txg |
| txg=$(get_last_txg_synced $TESTPOOL1) |
| log_must zfs snapshot -r $TESTPOOL1@snap1 |
| |
| # |
| # Perform config change operations |
| # |
| if [[ -n $addvdevs ]]; then |
| log_must zpool add -f $TESTPOOL1 $addvdevs |
| fi |
| if [[ -n $attachargs ]]; then |
| log_must zpool attach $TESTPOOL1 $attachargs |
| fi |
| if [[ -n $detachvdev ]]; then |
| log_must zpool detach $TESTPOOL1 $detachvdev |
| fi |
| if [[ -n $removevdev ]]; then |
| [[ -z $finalpool ]] && |
| log_fail "Must provide final pool status!" |
| log_must zpool remove $TESTPOOL1 $removevdev |
| log_must wait_for_pool_config $TESTPOOL1 "$finalpool" |
| fi |
| if [[ -n $pathstochange ]]; then |
| # |
| # Change device paths and re-import pool to update labels |
| # |
| zpool export $TESTPOOL1 |
| for dev in $pathstochange; do |
| log_must mv $dev "${dev}_new" |
| poolcheck=$(echo "$poolcheck" | \ |
| sed "s:$dev:${dev}_new:g") |
| done |
| zpool import -d $DEVICE_DIR $TESTPOOL1 |
| fi |
| |
| # |
| # In an attempt to leave MOS data untouched so extreme |
| # rewind is successful during import we checkpoint the |
| # pool and hope that these MOS data are part of the |
| # checkpoint (e.g they stay around). If this goes as |
| # expected, then extreme rewind should rewind back even |
| # further than the time that we took the checkpoint. |
| # |
| # Note that, ideally we would want to take a checkpoint |
| # right after we record the txg we plan to rewind to. |
| # But since we can't attach, detach or remove devices |
| # while having a checkpoint, we take it after the |
| # operation that changes the config. |
| # |
| # However, it is possible the MOS data was overwritten |
| # in which case the pool will either be unimportable, or |
| # may have been rewound prior to the data being written. |
| # In which case an error is returned and test_common() |
| # is retried by the caller to minimize false positives. |
| # |
| log_must zpool checkpoint $TESTPOOL1 |
| |
| log_must overwrite_data $TESTPOOL1 "" |
| |
| log_must zpool export $TESTPOOL1 |
| |
| zpool import -d $DEVICE_DIR -T $txg $TESTPOOL1 |
| if (( $? == 0 )); then |
| verify_data_md5sums $MD5FILE |
| if (( $? == 0 )); then |
| retval=0 |
| fi |
| |
| log_must check_pool_config $TESTPOOL1 "$poolcheck" |
| log_must zpool destroy $TESTPOOL1 |
| fi |
| |
| # Cleanup |
| if [[ -n $pathstochange ]]; then |
| for dev in $pathstochange; do |
| log_must mv "${dev}_new" $dev |
| done |
| fi |
| # Fast way to clear vdev labels |
| log_must zpool create -f $TESTPOOL2 $VDEV0 $VDEV1 $VDEV2 $VDEV3 $VDEV4 |
| log_must zpool destroy $TESTPOOL2 |
| |
| log_note "" |
| return $retval |
| } |
| |
| function test_add_vdevs |
| { |
| typeset poolcreate="$1" |
| typeset addvdevs="$2" |
| |
| log_note "$0: pool '$poolcreate', add $addvdevs." |
| |
| for retry in $(seq 1 5); do |
| test_common "$poolcreate" "$addvdevs" && return |
| log_note "Retry $retry / 5 for test_add_vdevs()" |
| done |
| |
| log_fail "Exhausted all 5 retries for test_add_vdevs()" |
| } |
| |
| function test_attach_vdev |
| { |
| typeset poolcreate="$1" |
| typeset attachto="$2" |
| typeset attachvdev="$3" |
| |
| log_note "$0: pool '$poolcreate', attach $attachvdev to $attachto." |
| |
| for retry in $(seq 1 5); do |
| test_common "$poolcreate" "" "$attachto $attachvdev" && return |
| log_note "Retry $retry / 5 for test_attach_vdev()" |
| done |
| |
| log_fail "Exhausted all 5 retries for test_attach_vdev()" |
| } |
| |
| function test_detach_vdev |
| { |
| typeset poolcreate="$1" |
| typeset detachvdev="$2" |
| |
| log_note "$0: pool '$poolcreate', detach $detachvdev." |
| |
| for retry in $(seq 1 5); do |
| test_common "$poolcreate" "" "" "$detachvdev" && return |
| log_note "Retry $retry / 5 for test_detach_vdev()" |
| done |
| |
| log_fail "Exhausted all 5 retries for test_detach_vdev()" |
| } |
| |
| function test_attach_detach_vdev |
| { |
| typeset poolcreate="$1" |
| typeset attachto="$2" |
| typeset attachvdev="$3" |
| typeset detachvdev="$4" |
| |
| log_note "$0: pool '$poolcreate', attach $attachvdev to $attachto," \ |
| "then detach $detachvdev." |
| |
| for retry in $(seq 1 5); do |
| test_common "$poolcreate" "" "$attachto $attachvdev" \ |
| "$detachvdev" && return |
| log_note "Retry $retry / 5 for test_attach_detach_vdev()" |
| done |
| |
| log_fail "Exhausted all 5 retries for test_attach_detach_vdev()" |
| } |
| |
| function test_remove_vdev |
| { |
| typeset poolcreate="$1" |
| typeset removevdev="$2" |
| typeset finalpool="$3" |
| |
| log_note "$0: pool '$poolcreate', remove $removevdev." |
| |
| for retry in $(seq 1 5); do |
| test_common "$poolcreate" "" "" "" "$removevdev" \ |
| "$finalpool" && return |
| log_note "Retry $retry / 5 for test_remove_vdev()" |
| done |
| |
| log_fail "Exhausted all 5 retries for test_remove_vdev()" |
| } |
| |
| # Record txg history |
| is_linux && log_must set_tunable32 TXG_HISTORY 100 |
| |
| # Make the devices bigger to reduce chances of overwriting MOS metadata. |
| increase_device_sizes $(( FILE_SIZE * 4 )) |
| |
| # Increase the number of metaslabs for small pools temporarily to |
| # reduce the chance of reusing a metaslab that holds old MOS metadata. |
| log_must set_tunable64 VDEV_MIN_MS_COUNT 150 |
| |
| # Part of the rewind test is to see how it reacts to path changes |
| typeset pathstochange="$VDEV0 $VDEV1 $VDEV2 $VDEV3" |
| |
| log_note " == test rewind after device addition == " |
| |
| test_add_vdevs "$VDEV0" "$VDEV1" |
| test_add_vdevs "$VDEV0 $VDEV1" "$VDEV2" |
| test_add_vdevs "$VDEV0" "$VDEV1 $VDEV2" |
| test_add_vdevs "mirror $VDEV0 $VDEV1" "mirror $VDEV2 $VDEV3" |
| test_add_vdevs "$VDEV0" "raidz $VDEV1 $VDEV2 $VDEV3" |
| test_add_vdevs "$VDEV0" "draid $VDEV1 $VDEV2 $VDEV3" |
| test_add_vdevs "$VDEV0" "log $VDEV1" |
| test_add_vdevs "$VDEV0 log $VDEV1" "$VDEV2" |
| |
| log_note " == test rewind after device attach == " |
| |
| test_attach_vdev "$VDEV0" "$VDEV0" "$VDEV1" |
| test_attach_vdev "mirror $VDEV0 $VDEV1" "$VDEV0" "$VDEV2" |
| test_attach_vdev "$VDEV0 $VDEV1" "$VDEV0" "$VDEV2" |
| |
| log_note " == test rewind after device removal == " |
| |
| # Once we remove a device it will be overlooked in the device scan, so we must |
| # preserve its original path |
| pathstochange="$VDEV0 $VDEV2" |
| test_remove_vdev "$VDEV0 $VDEV1 $VDEV2" "$VDEV1" "$VDEV0 $VDEV2" |
| |
| # |
| # Path change and detach are incompatible. Detach changes the guid of the vdev |
| # so we have no direct way to link the new path to an existing vdev. |
| # |
| pathstochange="" |
| |
| log_note " == test rewind after device detach == " |
| |
| test_detach_vdev "mirror $VDEV0 $VDEV1" "$VDEV1" |
| test_detach_vdev "mirror $VDEV0 $VDEV1 mirror $VDEV2 $VDEV3" "$VDEV1" |
| test_detach_vdev "$VDEV0 log mirror $VDEV1 $VDEV2" "$VDEV2" |
| |
| log_note " == test rewind after device attach followed by device detach == " |
| |
| # |
| # We need to disable vdev validation since once we detach VDEV1, VDEV0 will |
| # inherit the mirror tvd's guid and lose its original guid. |
| # |
| set_vdev_validate_skip 1 |
| test_attach_detach_vdev "$VDEV0" "$VDEV0" "$VDEV1" "$VDEV1" |
| set_vdev_validate_skip 0 |
| |
| log_pass "zpool import rewind after configuration change passed." |