diff options
author | Brian Behlendorf <[email protected]> | 2014-02-21 12:12:20 -0800 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2014-02-21 12:20:46 -0800 |
commit | 3965d2e6ee3ce3ef646c29736ce311695fd8ebc3 (patch) | |
tree | f8c2c1a1452705f8e886c9b023186ac5d936687f | |
parent | 98fad862937cddc3aeb4d527e357cd7265eae91b (diff) | |
parent | 4f2dcb3eee0e6c922f1f54955c3e15fd20b3b4f6 (diff) |
Merge branch 'issue-2094'
This patch stack was designed to accomplish the following:
* Cleanly address the pool incompatibility which was accidentally
introduced post-0.6.2 but pre-0.6.3. This required adding
infrastructure which can be used from now on to notify system
administrators of errata which affect their pool.
* Add additional automated regression testing to ensure this type
of compatibility issue is caught prior to the change being merged.
Signed-off-by: Brian Behlendorf <[email protected]>
Closes #2094
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | cmd/zpool/zpool_main.c | 65 | ||||
-rw-r--r-- | include/libzfs.h | 7 | ||||
-rw-r--r-- | include/sys/dsl_scan.h | 2 | ||||
-rw-r--r-- | include/sys/fs/zfs.h | 12 | ||||
-rw-r--r-- | include/sys/spa_impl.h | 1 | ||||
-rw-r--r-- | include/sys/zio.h | 2 | ||||
-rw-r--r-- | lib/libzfs/libzfs_status.c | 21 | ||||
-rw-r--r-- | module/zfs/dsl_scan.c | 39 | ||||
-rw-r--r-- | module/zfs/spa.c | 2 | ||||
-rw-r--r-- | module/zfs/spa_config.c | 2 | ||||
-rw-r--r-- | module/zfs/spa_stats.c | 1 | ||||
-rw-r--r-- | scripts/Makefile.am | 2 | ||||
m--------- | scripts/zfs-images | 0 | ||||
-rwxr-xr-x | scripts/zimport.sh | 495 |
15 files changed, 640 insertions, 14 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..d400f10a7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "scripts/zfs-images"] + path = scripts/zfs-images + url = https://github.com/zfsonlinux/zfs-images diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index a856fd49e..41e13af9a 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -1608,7 +1608,8 @@ show_import(nvlist_t *config) uint64_t guid; char *msgid; nvlist_t *nvroot; - int reason; + zpool_status_t reason; + zpool_errata_t errata; const char *health; uint_t vsc; int namewidth; @@ -1627,7 +1628,7 @@ show_import(nvlist_t *config) (uint64_t **)&vs, &vsc) == 0); health = zpool_state_to_name(vs->vs_state, vs->vs_aux); - reason = zpool_import_status(config, &msgid); + reason = zpool_import_status(config, &msgid, &errata); (void) printf(gettext(" pool: %s\n"), name); (void) printf(gettext(" id: %llu\n"), (u_longlong_t)guid); @@ -1715,6 +1716,11 @@ show_import(nvlist_t *config) "resilvered.\n")); break; + case ZPOOL_STATUS_ERRATA: + (void) printf(gettext(" status: Errata #%d detected.\n"), + errata); + break; + default: /* * No other status can be seen when importing pools. @@ -1736,6 +1742,34 @@ show_import(nvlist_t *config) (void) printf(gettext(" action: The pool can be " "imported using its name or numeric " "identifier and\n\tthe '-f' flag.\n")); + } else if (reason == ZPOOL_STATUS_ERRATA) { + switch (errata) { + case ZPOOL_ERRATA_NONE: + break; + + case ZPOOL_ERRATA_ZOL_2094_SCRUB: + (void) printf(gettext(" action: The pool can " + "be imported using its name or numeric " + "identifier,\n\thowever there is a compat" + "ibility issue which should be corrected" + "\n\tby running 'zpool scrub'\n")); + break; + + case ZPOOL_ERRATA_ZOL_2094_ASYNC_DESTROY: + (void) printf(gettext(" action: The pool can" + "not be imported with this version of ZFS " + "due to\n\tan active asynchronous destroy. " + "Revert to an earlier version\n\tand " + "allow the destroy to complete before " + "updating.\n")); + break; + + default: + /* + * All errata must contain an action message. + */ + assert(0); + } } else { (void) printf(gettext(" action: The pool can be " "imported using its name or numeric " @@ -4125,13 +4159,14 @@ status_callback(zpool_handle_t *zhp, void *data) status_cbdata_t *cbp = data; nvlist_t *config, *nvroot; char *msgid; - int reason; + zpool_status_t reason; + zpool_errata_t errata; const char *health; uint_t c; vdev_stat_t *vs; config = zpool_get_config(zhp, NULL); - reason = zpool_get_status(zhp, &msgid); + reason = zpool_get_status(zhp, &msgid, &errata); cbp->cb_count++; @@ -4349,6 +4384,28 @@ status_callback(zpool_handle_t *zhp, void *data) "'zpool clear'.\n")); break; + case ZPOOL_STATUS_ERRATA: + (void) printf(gettext("status: Errata #%d detected.\n"), + errata); + + switch (errata) { + case ZPOOL_ERRATA_NONE: + break; + + case ZPOOL_ERRATA_ZOL_2094_SCRUB: + (void) printf(gettext("action: To correct the issue " + "run 'zpool scrub'.\n")); + break; + + default: + /* + * All errata which allow the pool to be imported + * must contain an action message. + */ + assert(0); + } + break; + default: /* * The remaining errors can't actually be generated, yet. diff --git a/include/libzfs.h b/include/libzfs.h index 742f39f94..55dd34c99 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -312,6 +312,7 @@ typedef enum { ZPOOL_STATUS_IO_FAILURE_WAIT, /* failed I/O, failmode 'wait' */ ZPOOL_STATUS_IO_FAILURE_CONTINUE, /* failed I/O, failmode 'continue' */ ZPOOL_STATUS_BAD_LOG, /* cannot read log chain(s) */ + ZPOOL_STATUS_ERRATA, /* informational errata available */ /* * If the pool has unsupported features but can still be opened in @@ -347,8 +348,10 @@ typedef enum { ZPOOL_STATUS_OK } zpool_status_t; -extern zpool_status_t zpool_get_status(zpool_handle_t *, char **); -extern zpool_status_t zpool_import_status(nvlist_t *, char **); +extern zpool_status_t zpool_get_status(zpool_handle_t *, char **, + zpool_errata_t *); +extern zpool_status_t zpool_import_status(nvlist_t *, char **, + zpool_errata_t *); extern void zpool_dump_ddt(const ddt_stat_t *dds, const ddt_histogram_t *ddh); /* diff --git a/include/sys/dsl_scan.h b/include/sys/dsl_scan.h index bf8c5ac82..bcb85d67d 100644 --- a/include/sys/dsl_scan.h +++ b/include/sys/dsl_scan.h @@ -72,6 +72,8 @@ typedef enum dsl_scan_flags { DSF_VISIT_DS_AGAIN = 1<<0, } dsl_scan_flags_t; +#define DSL_SCAN_FLAGS_MASK (DSF_VISIT_DS_AGAIN) + /* * Every pool will have one dsl_scan_t and this structure will contain * in-memory information about the scan and a pointer to the on-disk diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index c54721155..df5692d14 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -548,6 +548,7 @@ typedef struct zpool_rewind_policy { #define ZPOOL_CONFIG_CAN_RDONLY "can_rdonly" /* not stored on disk */ #define ZPOOL_CONFIG_FEATURES_FOR_READ "features_for_read" #define ZPOOL_CONFIG_FEATURE_STATS "feature_stats" /* not stored on disk */ +#define ZPOOL_CONFIG_ERRATA "errata" /* not stored on disk */ /* * The persistent vdev state is stored as separate values rather than a single * 'vdev_state' entry. This is because a device can be in multiple states, such @@ -704,6 +705,17 @@ typedef enum dsl_scan_state { DSS_NUM_STATES } dsl_scan_state_t; +/* + * Errata described by http://zfsonlinux.org/msg/ZFS-8000-ER. The ordering + * of this enum must be maintained to ensure the errata identifiers map to + * the correct documentation. New errata may only be appended to the list + * and must contain corresponding documentation at the above link. + */ +typedef enum zpool_errata { + ZPOOL_ERRATA_NONE, + ZPOOL_ERRATA_ZOL_2094_SCRUB, + ZPOOL_ERRATA_ZOL_2094_ASYNC_DESTROY, +} zpool_errata_t; /* * Vdev statistics. Note: all fields should be 64-bit because this diff --git a/include/sys/spa_impl.h b/include/sys/spa_impl.h index 55515c1fc..90a32d3f0 100644 --- a/include/sys/spa_impl.h +++ b/include/sys/spa_impl.h @@ -236,6 +236,7 @@ struct spa { uint64_t spa_deadman_calls; /* number of deadman calls */ hrtime_t spa_sync_starttime; /* starting time of spa_sync */ uint64_t spa_deadman_synctime; /* deadman expiration timer */ + uint64_t spa_errata; /* errata issues detected */ spa_stats_t spa_stats; /* assorted spa statistics */ /* diff --git a/include/sys/zio.h b/include/sys/zio.h index d4350badc..129e2bcb9 100644 --- a/include/sys/zio.h +++ b/include/sys/zio.h @@ -260,7 +260,6 @@ struct zbookmark { uint64_t zb_object; int64_t zb_level; uint64_t zb_blkid; - char * zb_func; }; #define SET_BOOKMARK(zb, objset, object, level, blkid) \ @@ -269,7 +268,6 @@ struct zbookmark { (zb)->zb_object = object; \ (zb)->zb_level = level; \ (zb)->zb_blkid = blkid; \ - (zb)->zb_func = FTAG; \ } #define ZB_DESTROYED_OBJSET (-1ULL) diff --git a/lib/libzfs/libzfs_status.c b/lib/libzfs/libzfs_status.c index 0ef5f36d6..534ff853a 100644 --- a/lib/libzfs/libzfs_status.c +++ b/lib/libzfs/libzfs_status.c @@ -67,6 +67,7 @@ static char *zfs_msgid_table[] = { "ZFS-8000-HC", "ZFS-8000-JQ", "ZFS-8000-K4", + "ZFS-8000-ER", }; #define NMSGID (sizeof (zfs_msgid_table) / sizeof (zfs_msgid_table[0])) @@ -182,7 +183,7 @@ find_vdev_problem(nvlist_t *vdev, int (*func)(uint64_t, uint64_t, uint64_t)) * only picks the most damaging of all the current errors to report. */ static zpool_status_t -check_status(nvlist_t *config, boolean_t isimport) +check_status(nvlist_t *config, boolean_t isimport, zpool_errata_t *erratap) { nvlist_t *nvroot; vdev_stat_t *vs; @@ -193,6 +194,7 @@ check_status(nvlist_t *config, boolean_t isimport) uint64_t stateval; uint64_t suspended; uint64_t hostid = 0; + uint64_t errata = 0; unsigned long system_hostid = gethostid() & 0xffffffff; verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, @@ -356,13 +358,22 @@ check_status(nvlist_t *config, boolean_t isimport) } } + /* + * Informational errata available. + */ + (void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRATA, &errata); + if (errata) { + *erratap = errata; + return (ZPOOL_STATUS_ERRATA); + } + return (ZPOOL_STATUS_OK); } zpool_status_t -zpool_get_status(zpool_handle_t *zhp, char **msgid) +zpool_get_status(zpool_handle_t *zhp, char **msgid, zpool_errata_t *errata) { - zpool_status_t ret = check_status(zhp->zpool_config, B_FALSE); + zpool_status_t ret = check_status(zhp->zpool_config, B_FALSE, errata); if (ret >= NMSGID) *msgid = NULL; @@ -373,9 +384,9 @@ zpool_get_status(zpool_handle_t *zhp, char **msgid) } zpool_status_t -zpool_import_status(nvlist_t *config, char **msgid) +zpool_import_status(nvlist_t *config, char **msgid, zpool_errata_t *errata) { - zpool_status_t ret = check_status(config, B_TRUE); + zpool_status_t ret = check_status(config, B_TRUE, errata); if (ret >= NMSGID) *msgid = NULL; diff --git a/module/zfs/dsl_scan.c b/module/zfs/dsl_scan.c index ea0450781..7807f8485 100644 --- a/module/zfs/dsl_scan.c +++ b/module/zfs/dsl_scan.c @@ -123,6 +123,42 @@ dsl_scan_init(dsl_pool_t *dp, uint64_t txg) err = zap_lookup(dp->dp_meta_objset, DMU_POOL_DIRECTORY_OBJECT, DMU_POOL_SCAN, sizeof (uint64_t), SCAN_PHYS_NUMINTS, &scn->scn_phys); + /* + * Detect if the pool contains the signature of #2094. If it + * does properly update the scn->scn_phys structure and notify + * the administrator by setting an errata for the pool. + */ + if (err == EOVERFLOW) { + uint64_t zaptmp[SCAN_PHYS_NUMINTS + 1]; + VERIFY3S(SCAN_PHYS_NUMINTS, ==, 24); + VERIFY3S(offsetof(dsl_scan_phys_t, scn_flags), ==, + (23 * sizeof (uint64_t))); + + err = zap_lookup(dp->dp_meta_objset, + DMU_POOL_DIRECTORY_OBJECT, DMU_POOL_SCAN, + sizeof (uint64_t), SCAN_PHYS_NUMINTS + 1, &zaptmp); + if (err == 0) { + uint64_t overflow = zaptmp[SCAN_PHYS_NUMINTS]; + + if (overflow & ~DSL_SCAN_FLAGS_MASK || + scn->scn_async_destroying) { + spa->spa_errata = + ZPOOL_ERRATA_ZOL_2094_ASYNC_DESTROY; + return (EOVERFLOW); + } + + bcopy(zaptmp, &scn->scn_phys, + SCAN_PHYS_NUMINTS * sizeof (uint64_t)); + scn->scn_phys.scn_flags = overflow; + + /* Required scrub already in progress. */ + if (scn->scn_phys.scn_state == DSS_FINISHED || + scn->scn_phys.scn_state == DSS_CANCELED) + spa->spa_errata = + ZPOOL_ERRATA_ZOL_2094_SCRUB; + } + } + if (err == ENOENT) return (0); else if (err) @@ -319,6 +355,9 @@ dsl_scan_done(dsl_scan_t *scn, boolean_t complete, dmu_tx_t *tx) } scn->scn_phys.scn_end_time = gethrestime_sec(); + + if (spa->spa_errata == ZPOOL_ERRATA_ZOL_2094_SCRUB) + spa->spa_errata = 0; } /* ARGSUSED */ diff --git a/module/zfs/spa.c b/module/zfs/spa.c index 7052eec4a..9e7a7b785 100644 --- a/module/zfs/spa.c +++ b/module/zfs/spa.c @@ -4083,6 +4083,8 @@ spa_tryimport(nvlist_t *tryconfig) spa->spa_uberblock.ub_timestamp) == 0); VERIFY(nvlist_add_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, spa->spa_load_info) == 0); + VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_ERRATA, + spa->spa_errata) == 0); /* * If the bootfs property exists on this pool then we diff --git a/module/zfs/spa_config.c b/module/zfs/spa_config.c index 9efa05361..5b95a8e81 100644 --- a/module/zfs/spa_config.c +++ b/module/zfs/spa_config.c @@ -365,6 +365,8 @@ spa_config_generate(spa_t *spa, vdev_t *vd, uint64_t txg, int getstats) txg) == 0); VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_POOL_GUID, spa_guid(spa)) == 0); + VERIFY(nvlist_add_uint64(config, ZPOOL_CONFIG_ERRATA, + spa->spa_errata) == 0); VERIFY(spa->spa_comment == NULL || nvlist_add_string(config, ZPOOL_CONFIG_COMMENT, spa->spa_comment) == 0); diff --git a/module/zfs/spa_stats.c b/module/zfs/spa_stats.c index a94fecfe8..a35f5df65 100644 --- a/module/zfs/spa_stats.c +++ b/module/zfs/spa_stats.c @@ -207,7 +207,6 @@ spa_read_history_add(spa_t *spa, const zbookmark_t *zb, uint32_t aflags) return; srh = kmem_zalloc(sizeof (spa_read_history_t), KM_PUSHPAGE); - strlcpy(srh->origin, zb->zb_func, sizeof (srh->origin)); strlcpy(srh->comm, getcomm(), sizeof (srh->comm)); srh->start = gethrtime(); srh->objset = zb->zb_objset; diff --git a/scripts/Makefile.am b/scripts/Makefile.am index bc8fe99cf..7894db49e 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -7,6 +7,7 @@ dist_pkgdata_SCRIPTS = \ $(top_builddir)/scripts/common.sh \ $(top_srcdir)/scripts/zconfig.sh \ $(top_srcdir)/scripts/zfault.sh \ + $(top_srcdir)/scripts/zimport.sh \ $(top_srcdir)/scripts/zfs.sh \ $(top_srcdir)/scripts/zpool-create.sh \ $(top_srcdir)/scripts/zpios.sh \ @@ -17,6 +18,7 @@ dist_pkgdata_SCRIPTS = \ ZFS=$(top_builddir)/scripts/zfs.sh ZCONFIG=$(top_builddir)/scripts/zconfig.sh ZFAULT=$(top_builddir)/scripts/zfault.sh +ZIMPORT=$(top_builddir)/scripts/zimport.sh ZTEST=$(top_builddir)/cmd/ztest/ztest ZPIOS_SANITY=$(top_builddir)/scripts/zpios-sanity.sh diff --git a/scripts/zfs-images b/scripts/zfs-images new file mode 160000 +Subproject 3331601f6dc50ef2c9779c1656218701b48b276 diff --git a/scripts/zimport.sh b/scripts/zimport.sh new file mode 100755 index 000000000..eeaec11cf --- /dev/null +++ b/scripts/zimport.sh @@ -0,0 +1,495 @@ +#!/bin/sh +# +# Verify that an assortment of known good reference pools can be imported +# using different versions of the ZoL code. +# +# By default references pools for the major ZFS implementation will be +# checked against the most recent ZoL tags and the master development branch. +# Alternate tags or branches may be verified with the '-s <src-tag> option. +# Passing the keyword "installed" will instruct the script to test whatever +# version is installed. +# +# Preferentially a reference pool is used for all tests. However, if one +# does not exist and the pool-tag matches one of the src-tags then a new +# reference pool will be created using binaries from that source build. +# This is particularly useful when you need to test your changes before +# opening a pull request. The keyword 'all' can be used as short hand +# refer to all available reference pools. +# +# New reference pools may be added by placing a bzip2 compressed tarball +# of the pool in the scripts/zfs-images directory and then passing +# the -p <pool-tag> option. To increase the test coverage reference pools +# should be collected for all the major ZFS implementations. Having these +# pools easily available is also helpful to the developers. +# +# Care should be taken to run these tests with a kernel supported by all +# the listed tags. Otherwise build failure will cause false positives. +# +# +# EXAMPLES: +# +# The following example will verify the zfs-0.6.2 tag, the master branch, +# and the installed zfs version can correctly import the listed pools. +# Note there is no reference pool available for master and installed but +# because binaries are available one is automatically constructed. The +# working directory is also preserved between runs (-k) preventing the +# need to rebuild from source for multiple runs. +# +# zimport.sh -k -f /var/tmp/zimport \ +# -s "zfs-0.6.2 master installed" \ +# -p "zevo-1.1.1 zol-0.6.2 zol-0.6.2-173 master installed" +# +# --------------------- ZFS on Linux Source Versions -------------- +# zfs-0.6.2 master 0.6.2-175_g36eb554 +# ----------------------------------------------------------------- +# Clone SPL Local Local Skip +# Clone ZFS Local Local Skip +# Build SPL Pass Pass Skip +# Build ZFS Pass Pass Skip +# ----------------------------------------------------------------- +# zevo-1.1.1 Pass Pass Pass +# zol-0.6.2 Pass Pass Pass +# zol-0.6.2-173 Fail Pass Pass +# master Pass Pass Pass +# installed Pass Pass Pass +# +basedir="$(dirname $0)" + +SCRIPT_COMMON=common.sh +if [ -f "${basedir}/${SCRIPT_COMMON}" ]; then +. "${basedir}/${SCRIPT_COMMON}" +else +echo "Missing helper script ${SCRIPT_COMMON}" && exit 1 +fi + +PROG=zimport.sh + +SRC_TAGS="zfs-0.6.1 zfs-0.6.2 master" +POOL_TAGS="all master" +TEST_DIR=`mktemp -u -d -p /var/tmp zimport.XXXXXXXX` +KEEP=0 +VERBOSE=0 +COLOR=1 +REPO="https://github.com/zfsonlinux" +IMAGES_DIR="$SCRIPTDIR/zfs-images/" +IMAGES_TAR="https://github.com/zfsonlinux/zfs-images/tarball/master" +CPUS=`grep -c ^processor /proc/cpuinfo` +ERROR=0 + +usage() { +cat << EOF +USAGE: +zimport.sh [hvl] [-r repo] [-s src-tag] [-i pool-dir] [-p pool-tag] [-f path] + +DESCRIPTION: + ZPOOL import verification tests + +OPTIONS: + -h Show this message + -v Verbose + -c No color + -k Keep temporary directory + -r <repo> Source repository ($REPO) + -s <src-tag>... Verify ZoL versions with the listed tags + -i <pool-dir> Pool image directory + -p <pool-tag>... Verify pools created with the listed tags + -f <path> Temporary directory to use + +EOF +} + +while getopts 'hvckr:s:i:p:f:?' OPTION; do + case $OPTION in + h) + usage + exit 1 + ;; + v) + VERBOSE=1 + ;; + c) + COLOR=0 + ;; + k) + KEEP=1 + ;; + r) + REPO="$OPTARG" + ;; + s) + SRC_TAGS="$OPTARG" + ;; + i) + IMAGES_DIR="$OPTARG" + ;; + p) + POOL_TAGS="$OPTARG" + ;; + f) + TEST_DIR="$OPTARG" + ;; + ?) + usage + exit + ;; + esac +done + +# Initialize the test suite +init +check_modules || die "ZFS modules must be unloaded" + +SRC_DIR="$TEST_DIR/src" +SRC_DIR_SPL="$SRC_DIR/spl" +SRC_DIR_ZFS="$SRC_DIR/zfs" + +if [ $COLOR -eq 0 ]; then + COLOR_GREEN="" + COLOR_BROWN="" + COLOR_RED="" + COLOR_RESET="" +fi + +pass_nonewline() { + echo -n -e "${COLOR_GREEN}Pass${COLOR_RESET}\t\t" +} + +skip_nonewline() { + echo -n -e "${COLOR_BROWN}Skip${COLOR_RESET}\t\t" +} + +fail_nonewline() { + echo -n -e "${COLOR_RED}Fail${COLOR_RESET}\t\t" +} + +# +# Set several helper variables which are derived from a source tag. +# +# SPL_TAG - The tag zfs-x.y.z is translated to spl-x.y.z. +# SPL_DIR - The spl directory name. +# SPL_URL - The spl github URL to fetch the tarball. +# ZFS_TAG - The passed zfs-x.y.z tag +# ZFS_DIR - The zfs directory name +# ZFS_URL - The zfs github URL to fetch the tarball +# +src_set_vars() { + local TAG=$1 + + SPL_TAG=`echo $TAG | sed -e 's/zfs/spl/'` + SPL_DIR=$SRC_DIR_SPL/$SPL_TAG + SPL_URL=$REPO/spl/tarball/$SPL_TAG + + ZFS_TAG=$TAG + ZFS_DIR=$SRC_DIR_ZFS/$ZFS_TAG + ZFS_URL=$REPO/zfs/tarball/$ZFS_TAG + + if [ "$TAG" = "installed" ]; then + ZPOOL_CMD=`which zpool` + ZFS_CMD=`which zfs` + ZFS_SH="/usr/share/zfs/zfs.sh" + ZPOOL_CREATE="/usr/share/zfs/zpool-create.sh" + else + ZPOOL_CMD="./cmd/zpool/zpool" + ZFS_CMD="./cmd/zfs/zfs" + ZFS_SH="./scripts/zfs.sh" + ZPOOL_CREATE="./scripts/zpool-create.sh" + fi +} + +# +# Set several helper variables which are derived from a pool name such +# as zol-0.6.x, zevo-1.1.1, etc. These refer to example pools from various +# ZFS implementations which are used to verify compatibility. +# +# POOL_TAG - The example pools name in scripts/zfs-images/. +# POOL_BZIP - The full path to the example bzip2 compressed pool. +# POOL_DIR - The top level test path for this pool. +# POOL_DIR_PRISTINE - The directory containing a pristine version of the pool. +# POOL_DIR_COPY - The directory containing a working copy of the pool. +# POOL_DIR_SRC - Location of a source build if it exists for this pool. +# +pool_set_vars() { + local TAG=$1 + + POOL_TAG=$TAG + POOL_BZIP=$IMAGES_DIR/$POOL_TAG.tar.bz2 + POOL_DIR=$TEST_DIR/pools/$POOL_TAG + POOL_DIR_PRISTINE=$POOL_DIR/pristine + POOL_DIR_COPY=$POOL_DIR/copy + POOL_DIR_SRC=`echo -n "$SRC_DIR_ZFS/"; \ + echo "$POOL_TAG" | sed -e 's/zol/zfs/'` +} + +# +# Construct a non-trivial pool given a specific version of the source. More +# interesting pools provide better test coverage so this function should +# extended as needed to create more realistic pools. +# +pool_create() { + pool_set_vars $1 + src_set_vars $1 + + if [ "$POOL_TAG" != "installed" ]; then + cd $POOL_DIR_SRC + fi + + $ZFS_SH zfs="spa_config_path=$POOL_DIR_PRISTINE" || fail + + # Create a file vdev RAIDZ pool. + FILEDIR="$POOL_DIR_PRISTINE" $ZPOOL_CREATE \ + -c file-raidz -p $POOL_TAG -v >/dev/null || fail + + # Create a pool/fs filesystem with some random contents. + $ZFS_CMD create $POOL_TAG/fs || fail + populate /$POOL_TAG/fs/ 10 100 + + # Snapshot that filesystem, clone it, remove the files/dirs, + # replace them with new files/dirs. + $ZFS_CMD snap $POOL_TAG/fs@snap || fail + $ZFS_CMD clone $POOL_TAG/fs@snap $POOL_TAG/clone || fail + rm -Rf /$POOL_TAG/clone/* || fail + populate /$POOL_TAG/clone/ 10 100 + + # Scrub the pool, delay slightly, then export it. It is now + # somewhat interesting for testing purposes. + $ZPOOL_CMD scrub $POOL_TAG || fail + sleep 10 + $ZPOOL_CMD export $POOL_TAG || fail + + $ZFS_SH -u || fail +} + +# If the zfs-images directory doesn't exist fetch a copy from Github then +# cache it in the $TEST_DIR and update $IMAGES_DIR. +if [ ! -d $IMAGES_DIR ]; then + IMAGES_DIR="$TEST_DIR/zfs-images" + mkdir -p $IMAGES_DIR + curl -sL $IMAGES_TAR | \ + tar -xz -C $IMAGES_DIR --strip-components=1 || fail +fi + +# Given the available images in the zfs-images directory substitute the +# list of available images for the reserved keywork 'all'. +for TAG in $POOL_TAGS; do + + if [ "$TAG" = "all" ]; then + ALL_TAGS=`ls $IMAGES_DIR | grep "tar.bz2" | \ + sed 's/.tar.bz2//' | tr '\n' ' '` + NEW_TAGS="$NEW_TAGS $ALL_TAGS" + else + NEW_TAGS="$NEW_TAGS $TAG" + fi +done +POOL_TAGS="$NEW_TAGS" + +if [ $VERBOSE -ne 0 ]; then + echo "---------------------------- Options ----------------------------" + echo "VERBOSE=$VERBOSE" + echo "KEEP=$KEEP" + echo "REPO=$REPO" + echo "SRC_TAGS="$SRC_TAGS"" + echo "POOL_TAGS="$POOL_TAGS"" + echo "PATH=$TEST_DIR" + echo +fi + +if [ ! -d $TEST_DIR ]; then + mkdir -p $TEST_DIR +fi + +# Print a header for all tags which are being tested. +echo "--------------------- ZFS on Linux Source Versions --------------" +printf "%-16s" " " +for TAG in $SRC_TAGS; do + src_set_vars $TAG + + if [ "$TAG" = "installed" ]; then + ZFS_VERSION=`modinfo zfs | awk '/version:/ { print $2; exit }'` + if [ -n "$ZFS_VERSION" ]; then + printf "%-16s" $ZFS_VERSION + else + echo "ZFS is not installed\n" + fail + fi + else + printf "%-16s" $TAG + fi +done +echo -e "\n-----------------------------------------------------------------" + +# +# Attempt to generate the tarball from your local git repository, if that +# fails then attempt to download the tarball from Github. +# +printf "%-16s" "Clone SPL" +for TAG in $SRC_TAGS; do + src_set_vars $TAG + + if [ -d $SPL_DIR ]; then + skip_nonewline + elif [ "$SPL_TAG" = "installed" ]; then + skip_nonewline + else + cd $SPLSRC + + if [ ! -d $SRC_DIR_SPL ]; then + mkdir -p $SRC_DIR_SPL + fi + + git archive --format=tar --prefix=$SPL_TAG/ $SPL_TAG \ + -o $SRC_DIR_SPL/$SPL_TAG.tar &>/dev/nul || \ + rm $SRC_DIR_SPL/$SPL_TAG.tar + if [ -s $SRC_DIR_SPL/$SPL_TAG.tar ]; then + tar -xf $SRC_DIR_SPL/$SPL_TAG.tar -C $SRC_DIR_SPL + rm $SRC_DIR_SPL/$SPL_TAG.tar + echo -n -e "${COLOR_GREEN}Local${COLOR_RESET}\t\t" + else + mkdir -p $SPL_DIR || fail + curl -sL $SPL_URL | tar -xz -C $SPL_DIR \ + --strip-components=1 || fail + echo -n -e "${COLOR_GREEN}Remote${COLOR_RESET}\t\t" + fi + fi +done +printf "\n" + +# +# Attempt to generate the tarball from your local git repository, if that +# fails then attempt to download the tarball from Github. +# +printf "%-16s" "Clone ZFS" +for TAG in $SRC_TAGS; do + src_set_vars $TAG + + if [ -d $ZFS_DIR ]; then + skip_nonewline + elif [ "$ZFS_TAG" = "installed" ]; then + skip_nonewline + else + cd $SRCDIR + + if [ ! -d $SRC_DIR_ZFS ]; then + mkdir -p $SRC_DIR_ZFS + fi + + git archive --format=tar --prefix=$ZFS_TAG/ $ZFS_TAG \ + -o $SRC_DIR_ZFS/$ZFS_TAG.tar &>/dev/nul || \ + rm $SRC_DIR_ZFS/$ZFS_TAG.tar + if [ -s $SRC_DIR_ZFS/$ZFS_TAG.tar ]; then + tar -xf $SRC_DIR_ZFS/$ZFS_TAG.tar -C $SRC_DIR_ZFS + rm $SRC_DIR_ZFS/$ZFS_TAG.tar + echo -n -e "${COLOR_GREEN}Local${COLOR_RESET}\t\t" + else + mkdir -p $ZFS_DIR || fail + curl -sL $ZFS_URL | tar -xz -C $ZFS_DIR \ + --strip-components=1 || fail + echo -n -e "${COLOR_GREEN}Remote${COLOR_RESET}\t\t" + fi + fi +done +printf "\n" + +# Build the listed tags +printf "%-16s" "Build SPL" +for TAG in $SRC_TAGS; do + src_set_vars $TAG + + if [ -f $SPL_DIR/module/spl/spl.ko ]; then + skip_nonewline + elif [ "$SPL_TAG" = "installed" ]; then + skip_nonewline + else + cd $SPL_DIR + make distclean &>/dev/null + sh ./autogen.sh &>/dev/null || fail + ./configure &>/dev/null || fail + make -s -j$CPUS &>/dev/null || fail + pass_nonewline + fi +done +printf "\n" + +# Build the listed tags +printf "%-16s" "Build ZFS" +for TAG in $SRC_TAGS; do + src_set_vars $TAG + + if [ -f $ZFS_DIR/module/zfs/zfs.ko ]; then + skip_nonewline + elif [ "$ZFS_TAG" = "installed" ]; then + skip_nonewline + else + cd $ZFS_DIR + make distclean &>/dev/null + sh ./autogen.sh &>/dev/null || fail + ./configure --with-spl=$SPL_DIR &>/dev/null || fail + make -s -j$CPUS &>/dev/null || fail + pass_nonewline + fi +done +printf "\n" +echo "-----------------------------------------------------------------" + +# Either create a new pool using 'zpool create', or alternately restore an +# existing pool from another ZFS implementation for compatibility testing. +for TAG in $POOL_TAGS; do + pool_set_vars $TAG + SKIP=0 + + printf "%-16s" $POOL_TAG + rm -Rf $POOL_DIR + mkdir -p $POOL_DIR_PRISTINE + + # Use the existing compressed image if available. + if [ -f $POOL_BZIP ]; then + tar -xjf $POOL_BZIP -C $POOL_DIR_PRISTINE \ + --strip-components=1 || fail + # Use the installed version to create the pool. + elif [ "$TAG" = "installed" ]; then + pool_create $TAG + # A source build is available to create the pool. + elif [ -d $POOL_DIR_SRC ]; then + pool_create $TAG + else + SKIP=1 + fi + + # Verify 'zpool import' works for all listed source versions. + for TAG in $SRC_TAGS; do + + if [ $SKIP -eq 1 ]; then + skip_nonewline + continue + fi + + src_set_vars $TAG + if [ "$TAG" != "installed" ]; then + cd $ZFS_DIR + fi + $ZFS_SH zfs="spa_config_path=$POOL_DIR_COPY" + + cp -a --sparse=always $POOL_DIR_PRISTINE $POOL_DIR_COPY || fail + POOL_NAME=`$ZPOOL_CMD import -d $POOL_DIR_COPY | \ + awk '/pool:/ { print $2; exit 0 }'` + + $ZPOOL_CMD import -N -d $POOL_DIR_COPY $POOL_NAME &>/dev/null + if [ $? -ne 0 ]; then + fail_nonewline + ERROR=1 + else + $ZPOOL_CMD export $POOL_NAME || fail + pass_nonewline + fi + + rm -Rf $POOL_DIR_COPY + + $ZFS_SH -u || fail + done + printf "\n" +done + +if [ ! $KEEP ]; then + rm -Rf $TEST_DIR +fi + +exit $ERROR |