Path: blob/main/sys/contrib/openzfs/scripts/zimport.sh
48266 views
#!/usr/bin/env bash1#2# Verify that an assortment of known good reference pools can be imported3# using different versions of OpenZFS code.4#5# By default references pools for the major ZFS implementation will be6# checked against the most recent OpenZFS tags and the master development branch.7# Alternate tags or branches may be verified with the '-s <src-tag> option.8# Passing the keyword "installed" will instruct the script to test whatever9# version is installed.10#11# Preferentially a reference pool is used for all tests. However, if one12# does not exist and the pool-tag matches one of the src-tags then a new13# reference pool will be created using binaries from that source build.14# This is particularly useful when you need to test your changes before15# opening a pull request. The keyword 'all' can be used as short hand16# refer to all available reference pools.17#18# New reference pools may be added by placing a bzip2 compressed tarball19# of the pool in the scripts/zfs-images directory and then passing20# the -p <pool-tag> option. To increase the test coverage reference pools21# should be collected for all the major ZFS implementations. Having these22# pools easily available is also helpful to the developers.23#24# Care should be taken to run these tests with a kernel supported by all25# the listed tags. Otherwise build failure will cause false positives.26#27#28# EXAMPLES:29#30# The following example will verify the zfs-0.6.2 tag, the master branch,31# and the installed zfs version can correctly import the listed pools.32# Note there is no reference pool available for master and installed but33# because binaries are available one is automatically constructed. The34# working directory is also preserved between runs (-k) preventing the35# need to rebuild from source for multiple runs.36#37# zimport.sh -k -f /var/tmp/zimport \38# -s "zfs-0.6.2 master installed" \39# -p "zevo-1.1.1 zol-0.6.2 zol-0.6.2-173 master installed"40#41# ------------------------ OpenZFS Source Versions ----------------42# zfs-0.6.2 master 0.6.2-175_g36eb55443# -----------------------------------------------------------------44# Clone ZFS Local Local Skip45# Build ZFS Pass Pass Skip46# -----------------------------------------------------------------47# zevo-1.1.1 Pass Pass Pass48# zol-0.6.2 Pass Pass Pass49# zol-0.6.2-173 Fail Pass Pass50# master Pass Pass Pass51# installed Pass Pass Pass52#5354BASE_DIR=$(dirname "$0")55SCRIPT_COMMON=common.sh56if [[ -f "${BASE_DIR}/${SCRIPT_COMMON}" ]]; then57. "${BASE_DIR}/${SCRIPT_COMMON}"58else59echo "Missing helper script ${SCRIPT_COMMON}" && exit 160fi6162PROG=zimport.sh63SRC_TAGS="zfs-0.6.5.11 master"64POOL_TAGS="all master"65POOL_CREATE_OPTIONS=66TEST_DIR=$(mktemp -u -d -p /var/tmp zimport.XXXXXXXX)67KEEP="no"68VERBOSE="no"69COLOR="yes"70REPO="https://github.com/openzfs"71IMAGES_DIR="${BASE_DIR}/zfs-images/"72IMAGES_TAR="https://github.com/openzfs/zfs-images/tarball/master"73ERROR=07475CONFIG_LOG="configure.log"76CONFIG_OPTIONS=${CONFIG_OPTIONS:-""}77MAKE_LOG="make.log"78MAKE_OPTIONS=${MAKE_OPTIONS:-"-s -j$(nproc)"}7980COLOR_GREEN="\033[0;32m"81COLOR_RED="\033[0;31m"82COLOR_BROWN="\033[0;33m"83COLOR_RESET="\033[0m"8485usage() {86cat << EOF87USAGE:88zimport.sh [hvl] [-r repo] [-s src-tag] [-i pool-dir] [-p pool-tag]89[-f path] [-o options]9091DESCRIPTION:92ZPOOL import verification tests9394OPTIONS:95-h Show this message96-v Verbose97-c No color98-k Keep temporary directory99-r <repo> Source repository ($REPO)100-s <src-tag>... Verify OpenZFS versions with the listed tags101-i <pool-dir> Pool image directory102-p <pool-tag>... Verify pools created with the listed tags103-f <path> Temporary directory to use104-o <options> Additional options to pass to 'zpool create'105106EOF107}108109while getopts 'hvckr:s:i:p:f:o:?' OPTION; do110case $OPTION in111h)112usage113exit 1114;;115v)116VERBOSE="yes"117;;118c)119COLOR="no"120;;121k)122KEEP="yes"123;;124r)125REPO="$OPTARG"126;;127s)128SRC_TAGS="$OPTARG"129;;130i)131IMAGES_DIR="$OPTARG"132;;133p)134POOL_TAGS="$OPTARG"135;;136f)137TEST_DIR="$OPTARG"138;;139o)140POOL_CREATE_OPTIONS="$OPTARG"141;;142*)143usage144exit 1145;;146esac147done148149#150# Verify the module start is not loaded151#152if lsmod | grep zfs >/dev/null; then153echo "ZFS modules must be unloaded"154exit 1155fi156157#158# Create a random directory tree of files and sub-directories to159# to act as a copy source for the various regression tests.160#161populate() {162local ROOT=$1163local MAX_DIR_SIZE=$2164local MAX_FILE_SIZE=$3165166mkdir -p "$ROOT"/{a,b,c,d,e,f,g}/{h,i}167DIRS=$(find "$ROOT")168169for DIR in $DIRS; do170COUNT=$((RANDOM % MAX_DIR_SIZE))171172for _ in $(seq "$COUNT"); do173FILE=$(mktemp -p "$DIR")174SIZE=$((RANDOM % MAX_FILE_SIZE))175dd if=/dev/urandom of="$FILE" bs=1k \176count="$SIZE" &>/dev/null177done178done179180return 0181}182183SRC_DIR=$(mktemp -d -p /var/tmp/ zfs.src.XXXXXXXX)184trap 'rm -Rf "$SRC_DIR"' INT TERM EXIT185populate "$SRC_DIR" 10 100186187SRC_DIR="$TEST_DIR/src"188SRC_DIR_ZFS="$SRC_DIR/zfs"189190if [[ "$COLOR" = "no" ]]; then191COLOR_GREEN=""192COLOR_BROWN=""193COLOR_RED=""194COLOR_RESET=""195fi196197pass_nonewline() {198echo -n -e "${COLOR_GREEN}Pass${COLOR_RESET}\t\t"199}200201skip_nonewline() {202echo -n -e "${COLOR_BROWN}Skip${COLOR_RESET}\t\t"203}204205fail_nonewline() {206echo -n -e "${COLOR_RED}Fail${COLOR_RESET}\t\t"207}208209#210# Log a failure message, cleanup, and return an error.211#212fail() {213echo -e "$PROG: $1" >&2214$ZFS_SH -u >/dev/null 2>&1215exit 1216}217218#219# Set several helper variables which are derived from a source tag.220#221# ZFS_TAG - The passed zfs-x.y.z tag222# ZFS_DIR - The zfs directory name223# ZFS_URL - The zfs github URL to fetch the tarball224#225src_set_vars() {226local TAG=$1227228ZFS_TAG="$TAG"229ZFS_DIR="$SRC_DIR_ZFS/$ZFS_TAG"230ZFS_URL="$REPO/zfs/tarball/$ZFS_TAG"231232if [[ "$TAG" = "installed" ]]; then233ZPOOL_CMD=$(command -v zpool)234ZFS_CMD=$(command -v zfs)235ZFS_SH="/usr/share/zfs/zfs.sh"236else237ZPOOL_CMD="./zpool"238ZFS_CMD="./zfs"239ZFS_SH="./scripts/zfs.sh"240fi241}242243#244# Set several helper variables which are derived from a pool name such245# as zol-0.6.x, zevo-1.1.1, etc. These refer to example pools from various246# ZFS implementations which are used to verify compatibility.247#248# POOL_TAG - The example pools name in scripts/zfs-images/.249# POOL_BZIP - The full path to the example bzip2 compressed pool.250# POOL_DIR - The top level test path for this pool.251# POOL_DIR_PRISTINE - The directory containing a pristine version of the pool.252# POOL_DIR_COPY - The directory containing a working copy of the pool.253# POOL_DIR_SRC - Location of a source build if it exists for this pool.254#255pool_set_vars() {256local TAG=$1257258POOL_TAG=$TAG259POOL_BZIP=$IMAGES_DIR/$POOL_TAG.tar.bz2260POOL_DIR=$TEST_DIR/pools/$POOL_TAG261POOL_DIR_PRISTINE=$POOL_DIR/pristine262POOL_DIR_COPY=$POOL_DIR/copy263POOL_DIR_SRC="$SRC_DIR_ZFS/${POOL_TAG//zol/zfs}"264}265266#267# Construct a non-trivial pool given a specific version of the source. More268# interesting pools provide better test coverage so this function should269# extended as needed to create more realistic pools.270#271pool_create() {272pool_set_vars "$1"273src_set_vars "$1"274275if [[ "$POOL_TAG" != "installed" ]]; then276cd "$POOL_DIR_SRC" || fail "Failed 'cd $POOL_DIR_SRC'"277fi278279$ZFS_SH zfs="spa_config_path=$POOL_DIR_PRISTINE" || \280fail "Failed to load kmods"281282# Create a file vdev RAIDZ pool.283truncate -s 1G \284"$POOL_DIR_PRISTINE/vdev1" "$POOL_DIR_PRISTINE/vdev2" \285"$POOL_DIR_PRISTINE/vdev3" "$POOL_DIR_PRISTINE/vdev4" || \286fail "Failed 'truncate -s 1G ...'"287# shellcheck disable=SC2086288$ZPOOL_CMD create $POOL_CREATE_OPTIONS "$POOL_TAG" raidz \289"$POOL_DIR_PRISTINE/vdev1" "$POOL_DIR_PRISTINE/vdev2" \290"$POOL_DIR_PRISTINE/vdev3" "$POOL_DIR_PRISTINE/vdev4" || \291fail "Failed '$ZPOOL_CMD create $POOL_CREATE_OPTIONS $POOL_TAG ...'"292293# Create a pool/fs filesystem with some random contents.294$ZFS_CMD create "$POOL_TAG/fs" || \295fail "Failed '$ZFS_CMD create $POOL_TAG/fs'"296populate "/$POOL_TAG/fs/" 10 100297298# Snapshot that filesystem, clone it, remove the files/dirs,299# replace them with new files/dirs.300$ZFS_CMD snap "$POOL_TAG/fs@snap" || \301fail "Failed '$ZFS_CMD snap $POOL_TAG/fs@snap'"302$ZFS_CMD clone "$POOL_TAG/fs@snap" "$POOL_TAG/clone" || \303fail "Failed '$ZFS_CMD clone $POOL_TAG/fs@snap $POOL_TAG/clone'"304# shellcheck disable=SC2086305rm -Rf /$POOL_TAG/clone/*306populate "/$POOL_TAG/clone/" 10 100307308# Scrub the pool, delay slightly, then export it. It is now309# somewhat interesting for testing purposes.310$ZPOOL_CMD scrub "$POOL_TAG" || \311fail "Failed '$ZPOOL_CMD scrub $POOL_TAG'"312sleep 10313$ZPOOL_CMD export "$POOL_TAG" || \314fail "Failed '$ZPOOL_CMD export $POOL_TAG'"315316$ZFS_SH -u || fail "Failed to unload kmods"317}318319# If the zfs-images directory doesn't exist fetch a copy from Github then320# cache it in the $TEST_DIR and update $IMAGES_DIR.321if [[ ! -d "$IMAGES_DIR" ]]; then322IMAGES_DIR="$TEST_DIR/zfs-images"323mkdir -p "$IMAGES_DIR"324curl -sL "$IMAGES_TAR" | \325tar -xz -C "$IMAGES_DIR" --strip-components=1 || \326fail "Failed to download pool images"327fi328329# Given the available images in the zfs-images directory substitute the330# list of available images for the reserved keyword 'all'.331for TAG in $POOL_TAGS; do332333if [[ "$TAG" = "all" ]]; then334ALL_TAGS=$(echo "$IMAGES_DIR"/*.tar.bz2 | \335sed "s|$IMAGES_DIR/||g;s|.tar.bz2||g")336NEW_TAGS="$NEW_TAGS $ALL_TAGS"337else338NEW_TAGS="$NEW_TAGS $TAG"339fi340done341POOL_TAGS="$NEW_TAGS"342343if [[ "$VERBOSE" = "yes" ]]; then344echo "---------------------------- Options ----------------------------"345echo "VERBOSE=$VERBOSE"346echo "KEEP=$KEEP"347echo "REPO=$REPO"348echo "SRC_TAGS=$SRC_TAGS"349echo "POOL_TAGS=$POOL_TAGS"350echo "PATH=$TEST_DIR"351echo "POOL_CREATE_OPTIONS=$POOL_CREATE_OPTIONS"352echo353fi354355if [[ ! -d "$TEST_DIR" ]]; then356mkdir -p "$TEST_DIR"357fi358359if [[ ! -d "$SRC_DIR" ]]; then360mkdir -p "$SRC_DIR"361fi362363# Print a header for all tags which are being tested.364echo "------------------------ OpenZFS Source Versions ----------------"365printf "%-16s" " "366for TAG in $SRC_TAGS; do367src_set_vars "$TAG"368369if [[ "$TAG" = "installed" ]]; then370ZFS_VERSION=$(modinfo zfs | awk '/version:/ { print $2; exit }')371if [[ -n "$ZFS_VERSION" ]]; then372printf "%-16s" "$ZFS_VERSION"373else374fail "ZFS is not installed"375fi376else377printf "%-16s" "$TAG"378fi379done380echo -e "\n-----------------------------------------------------------------"381382#383# Attempt to generate the tarball from your local git repository, if that384# fails then attempt to download the tarball from Github.385#386printf "%-16s" "Clone ZFS"387for TAG in $SRC_TAGS; do388src_set_vars "$TAG"389390if [[ -d "$ZFS_DIR" ]]; then391skip_nonewline392elif [[ "$ZFS_TAG" = "installed" ]]; then393skip_nonewline394else395cd "$SRC_DIR" || fail "Failed 'cd $SRC_DIR'"396397if [[ ! -d "$SRC_DIR_ZFS" ]]; then398mkdir -p "$SRC_DIR_ZFS"399fi400401git archive --format=tar --prefix="$ZFS_TAG/ $ZFS_TAG" \402-o "$SRC_DIR_ZFS/$ZFS_TAG.tar" &>/dev/null || \403rm "$SRC_DIR_ZFS/$ZFS_TAG.tar"404if [[ -s "$SRC_DIR_ZFS/$ZFS_TAG.tar" ]]; then405tar -xf "$SRC_DIR_ZFS/$ZFS_TAG.tar" -C "$SRC_DIR_ZFS"406rm "$SRC_DIR_ZFS/$ZFS_TAG.tar"407echo -n -e "${COLOR_GREEN}Local${COLOR_RESET}\t\t"408else409mkdir -p "$ZFS_DIR" || fail "Failed to create $ZFS_DIR"410curl -sL "$ZFS_URL" | tar -xz -C "$ZFS_DIR" \411--strip-components=1 || \412fail "Failed to download $ZFS_URL"413echo -n -e "${COLOR_GREEN}Remote${COLOR_RESET}\t\t"414fi415fi416done417printf "\n"418419# Build the listed tags420printf "%-16s" "Build ZFS"421for TAG in $SRC_TAGS; do422src_set_vars "$TAG"423424if [[ -f "$ZFS_DIR/module/zfs/zfs.ko" ]]; then425skip_nonewline426elif [[ "$ZFS_TAG" = "installed" ]]; then427skip_nonewline428else429cd "$ZFS_DIR" || fail "Failed 'cd $ZFS_DIR'"430make distclean &>/dev/null431./autogen.sh >>"$CONFIG_LOG" 2>&1 || \432fail "Failed ZFS 'autogen.sh'"433# shellcheck disable=SC2086434./configure $CONFIG_OPTIONS >>"$CONFIG_LOG" 2>&1 || \435fail "Failed ZFS 'configure $CONFIG_OPTIONS'"436# shellcheck disable=SC2086437make $MAKE_OPTIONS >>"$MAKE_LOG" 2>&1 || \438fail "Failed ZFS 'make $MAKE_OPTIONS'"439pass_nonewline440fi441done442printf "\n"443echo "-----------------------------------------------------------------"444445# Either create a new pool using 'zpool create', or alternately restore an446# existing pool from another ZFS implementation for compatibility testing.447for TAG in $POOL_TAGS; do448pool_set_vars "$TAG"449SKIP=0450451printf "%-16s" "$POOL_TAG"452rm -Rf "$POOL_DIR"453mkdir -p "$POOL_DIR_PRISTINE"454455# Use the existing compressed image if available.456if [[ -f "$POOL_BZIP" ]]; then457tar -xjf "$POOL_BZIP" -C "$POOL_DIR_PRISTINE" \458--strip-components=1 || \459fail "Failed 'tar -xjf $POOL_BZIP"460# Use the installed version to create the pool.461elif [[ "$TAG" = "installed" ]]; then462pool_create "$TAG"463# A source build is available to create the pool.464elif [[ -d "$POOL_DIR_SRC" ]]; then465pool_create "$TAG"466else467SKIP=1468fi469470# Verify 'zpool import' works for all listed source versions.471for SRC_TAG in $SRC_TAGS; do472473if [[ "$SKIP" -eq 1 ]]; then474skip_nonewline475continue476fi477478src_set_vars "$SRC_TAG"479if [[ "$SRC_TAG" != "installed" ]]; then480cd "$ZFS_DIR" || fail "Failed 'cd $ZFS_DIR'"481fi482$ZFS_SH zfs="spa_config_path=$POOL_DIR_COPY"483484cp -a --sparse=always "$POOL_DIR_PRISTINE" \485"$POOL_DIR_COPY" || \486fail "Failed to copy $POOL_DIR_PRISTINE to $POOL_DIR_COPY"487POOL_NAME=$($ZPOOL_CMD import -d "$POOL_DIR_COPY" | \488awk '/pool:/ { print $2; exit }')489490if ! $ZPOOL_CMD import -N -d "$POOL_DIR_COPY"491"$POOL_NAME" &>/dev/null; then492fail_nonewline493ERROR=1494else495$ZPOOL_CMD export "$POOL_NAME" || \496fail "Failed to export pool"497pass_nonewline498fi499500rm -Rf "$POOL_DIR_COPY"501502$ZFS_SH -u || fail "Failed to unload kmods"503done504printf "\n"505done506507if [[ "$KEEP" = "no" ]]; then508rm -Rf "$TEST_DIR"509fi510511exit "$ERROR"512513514