aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/zfs/zfs_main.c114
-rw-r--r--man/man8/zfs.898
-rw-r--r--tests/runfiles/linux.run2
-rw-r--r--tests/zfs-tests/tests/functional/cli_root/zfs_create/Makefile.am4
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_dryrun.ksh169
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_verbose.ksh165
6 files changed, 523 insertions, 29 deletions
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index 0ebd16f6d..53b76e25d 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -254,9 +254,9 @@ get_usage(zfs_help_t idx)
return (gettext("\tclone [-p] [-o property=value] ... "
"<snapshot> <filesystem|volume>\n"));
case HELP_CREATE:
- return (gettext("\tcreate [-p] [-o property=value] ... "
+ return (gettext("\tcreate [-Pnpv] [-o property=value] ... "
"<filesystem>\n"
- "\tcreate [-ps] [-b blocksize] [-o property=value] ... "
+ "\tcreate [-Pnpsv] [-b blocksize] [-o property=value] ... "
"-V <size> <volume>\n"));
case HELP_DESTROY:
return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
@@ -867,8 +867,8 @@ usage:
}
/*
- * zfs create [-p] [-o prop=value] ... fs
- * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size
+ * zfs create [-Pnpv] [-o prop=value] ... fs
+ * zfs create [-Pnpsv] [-b blocksize] [-o prop=value] ... -V vol size
*
* Create a new dataset. This command can be used to create filesystems
* and volumes. Snapshot creation is handled by 'zfs snapshot'.
@@ -880,16 +880,29 @@ usage:
* SPA_VERSION_REFRESERVATION, we set a refreservation instead.
*
* The '-p' flag creates all the non-existing ancestors of the target first.
+ *
+ * The '-n' flag is no-op (dry run) mode. This will perform a user-space sanity
+ * check of arguments and properties, but does not check for permissions,
+ * available space, etc.
+ *
+ * The '-v' flag is for verbose output.
+ *
+ * The '-P' flag is used for parseable output. It implies '-v'.
*/
static int
zfs_do_create(int argc, char **argv)
{
zfs_type_t type = ZFS_TYPE_FILESYSTEM;
+ zpool_handle_t *zpool_handle = NULL;
+ nvlist_t *real_props = NULL;
uint64_t volsize = 0;
int c;
boolean_t noreserve = B_FALSE;
boolean_t bflag = B_FALSE;
boolean_t parents = B_FALSE;
+ boolean_t dryrun = B_FALSE;
+ boolean_t verbose = B_FALSE;
+ boolean_t parseable = B_FALSE;
int ret = 1;
nvlist_t *props;
uint64_t intval;
@@ -898,7 +911,7 @@ zfs_do_create(int argc, char **argv)
nomem();
/* check options */
- while ((c = getopt(argc, argv, ":V:b:so:p")) != -1) {
+ while ((c = getopt(argc, argv, ":PV:b:nso:pv")) != -1) {
switch (c) {
case 'V':
type = ZFS_TYPE_VOLUME;
@@ -914,6 +927,10 @@ zfs_do_create(int argc, char **argv)
nomem();
volsize = intval;
break;
+ case 'P':
+ verbose = B_TRUE;
+ parseable = B_TRUE;
+ break;
case 'p':
parents = B_TRUE;
break;
@@ -931,6 +948,9 @@ zfs_do_create(int argc, char **argv)
intval) != 0)
nomem();
break;
+ case 'n':
+ dryrun = B_TRUE;
+ break;
case 'o':
if (!parseprop(props, optarg))
goto error;
@@ -938,6 +958,9 @@ zfs_do_create(int argc, char **argv)
case 's':
noreserve = B_TRUE;
break;
+ case 'v':
+ verbose = B_TRUE;
+ break;
case ':':
(void) fprintf(stderr, gettext("missing size "
"argument\n"));
@@ -969,14 +992,9 @@ zfs_do_create(int argc, char **argv)
goto badusage;
}
- if (type == ZFS_TYPE_VOLUME && !noreserve) {
- zpool_handle_t *zpool_handle;
- nvlist_t *real_props = NULL;
- uint64_t spa_version;
+ if (dryrun || (type == ZFS_TYPE_VOLUME && !noreserve)) {
+ char msg[ZFS_MAX_DATASET_NAME_LEN * 2];
char *p;
- zfs_prop_t resv_prop;
- char *strval;
- char msg[1024];
if ((p = strchr(argv[0], '/')) != NULL)
*p = '\0';
@@ -985,25 +1003,31 @@ zfs_do_create(int argc, char **argv)
*p = '/';
if (zpool_handle == NULL)
goto error;
- spa_version = zpool_get_prop_int(zpool_handle,
- ZPOOL_PROP_VERSION, NULL);
- if (spa_version >= SPA_VERSION_REFRESERVATION)
- resv_prop = ZFS_PROP_REFRESERVATION;
- else
- resv_prop = ZFS_PROP_RESERVATION;
(void) snprintf(msg, sizeof (msg),
+ dryrun ? gettext("cannot verify '%s'") :
gettext("cannot create '%s'"), argv[0]);
if (props && (real_props = zfs_valid_proplist(g_zfs, type,
props, 0, NULL, zpool_handle, B_TRUE, msg)) == NULL) {
zpool_close(zpool_handle);
goto error;
}
+ }
+
+ if (type == ZFS_TYPE_VOLUME && !noreserve) {
+ uint64_t spa_version;
+ zfs_prop_t resv_prop;
+ char *strval;
+
+ spa_version = zpool_get_prop_int(zpool_handle,
+ ZPOOL_PROP_VERSION, NULL);
+ if (spa_version >= SPA_VERSION_REFRESERVATION)
+ resv_prop = ZFS_PROP_REFRESERVATION;
+ else
+ resv_prop = ZFS_PROP_RESERVATION;
volsize = zvol_volsize_to_reservation(zpool_handle, volsize,
real_props);
- nvlist_free(real_props);
- zpool_close(zpool_handle);
if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),
&strval) != 0) {
@@ -1014,6 +1038,10 @@ zfs_do_create(int argc, char **argv)
}
}
}
+ if (zpool_handle != NULL) {
+ zpool_close(zpool_handle);
+ nvlist_free(real_props);
+ }
if (parents && zfs_name_valid(argv[0], type)) {
/*
@@ -1025,8 +1053,50 @@ zfs_do_create(int argc, char **argv)
ret = 0;
goto error;
}
- if (zfs_create_ancestors(g_zfs, argv[0]) != 0)
- goto error;
+ if (verbose) {
+ (void) printf(parseable ? "create_ancestors\t%s\n" :
+ dryrun ? "would create ancestors of %s\n" :
+ "create ancestors of %s\n", argv[0]);
+ }
+ if (!dryrun) {
+ if (zfs_create_ancestors(g_zfs, argv[0]) != 0) {
+ goto error;
+ }
+ }
+ }
+
+ if (verbose) {
+ nvpair_t *nvp = NULL;
+ (void) printf(parseable ? "create\t%s\n" :
+ dryrun ? "would create %s\n" : "create %s\n", argv[0]);
+ while ((nvp = nvlist_next_nvpair(props, nvp)) != NULL) {
+ uint64_t uval;
+ char *sval;
+
+ switch (nvpair_type(nvp)) {
+ case DATA_TYPE_UINT64:
+ VERIFY0(nvpair_value_uint64(nvp, &uval));
+ (void) printf(parseable ?
+ "property\t%s\t%llu\n" : "\t%s=%llu\n",
+ nvpair_name(nvp), (u_longlong_t)uval);
+ break;
+ case DATA_TYPE_STRING:
+ VERIFY0(nvpair_value_string(nvp, &sval));
+ (void) printf(parseable ?
+ "property\t%s\t%s\n" : "\t%s=%s\n",
+ nvpair_name(nvp), sval);
+ break;
+ default:
+ (void) fprintf(stderr, "property '%s' "
+ "has illegal type %d\n",
+ nvpair_name(nvp), nvpair_type(nvp));
+ abort();
+ }
+ }
+ }
+ if (dryrun) {
+ ret = 0;
+ goto error;
}
/* pass to libzfs */
diff --git a/man/man8/zfs.8 b/man/man8/zfs.8
index 3fc142f5e..cbbd76503 100644
--- a/man/man8/zfs.8
+++ b/man/man8/zfs.8
@@ -28,9 +28,9 @@
.\" Copyright (c) 2014 Integros [integros.com]
.\" Copyright 2019 Richard Laager. All rights reserved.
.\" Copyright 2018 Nexenta Systems, Inc.
-.\" Copyright 2018 Joyent, Inc.
+.\" Copyright 2019 Joyent, Inc.
.\"
-.Dd April 30, 2019
+.Dd June 30, 2019
.Dt ZFS 8 SMM
.Os Linux
.Sh NAME
@@ -41,12 +41,12 @@
.Fl ?V
.Nm
.Cm create
-.Op Fl p
+.Op Fl Pnpv
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
.Ar filesystem
.Nm
.Cm create
-.Op Fl ps
+.Op Fl Pnpsv
.Op Fl b Ar blocksize
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
.Fl V Ar size Ar volume
@@ -2556,7 +2556,7 @@ subcommand.
.It Xo
.Nm
.Cm create
-.Op Fl p
+.Op Fl Pnpv
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
.Ar filesystem
.Xc
@@ -2585,6 +2585,48 @@ Any property specified on the command line using the
.Fl o
option is ignored.
If the target filesystem already exists, the operation completes successfully.
+.It Fl n
+Do a dry-run
+.Pq Qq No-op
+creation.
+No datasets will be created.
+This is useful in conjunction with the
+.Fl v
+or
+.Fl P
+flags to validate properties that are passed via
+.Fl o
+options and those implied by other options.
+The actual dataset creation can still fail due to insufficient privileges or
+available capacity.
+.It Fl P
+Print machine-parsable verbose information about the created dataset.
+Each line of output contains a key and one or two values, all separated by tabs.
+The
+.Sy create_ancestors
+and
+.Sy create
+keys have
+.Em filesystem
+as their only value.
+The
+.Sy create_ancestors
+key only appears if the
+.Fl p
+option is used.
+The
+.Sy property
+key has two values, a property name that property's value.
+The
+.Sy property
+key may appear zero or more times, once for each property that will be set local
+to
+.Em filesystem
+due to the use of the
+.Fl o
+option.
+.It Fl v
+Print verbose information about the created dataset.
.El
.It Xo
.Nm
@@ -2641,6 +2683,52 @@ See
in the
.Sx Native Properties
section for more information about sparse volumes.
+.It Fl n
+Do a dry-run
+.Pq Qq No-op
+creation.
+No datasets will be created.
+This is useful in conjunction with the
+.Fl v
+or
+.Fl P
+flags to validate properties that are passed via
+.Fl o
+options and those implied by other options.
+The actual dataset creation can still fail due to insufficient privileges or
+available capacity.
+.It Fl P
+Print machine-parsable verbose information about the created dataset.
+Each line of output contains a key and one or two values, all separated by tabs.
+The
+.Sy create_ancestors
+and
+.Sy create
+keys have
+.Em volume
+as their only value.
+The
+.Sy create_ancestors
+key only appears if the
+.Fl p
+option is used.
+The
+.Sy property
+key has two values, a property name that property's value.
+The
+.Sy property
+key may appear zero or more times, once for each property that will be set local
+to
+.Em volume
+due to the use of the
+.Fl b
+or
+.Fl o
+options, as well as
+.Sy refreservation
+if the volume is not sparse.
+.It Fl v
+Print verbose information about the created dataset.
.El
.It Xo
.Nm
diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run
index 36ab5ef22..4a0e151f4 100644
--- a/tests/runfiles/linux.run
+++ b/tests/runfiles/linux.run
@@ -143,7 +143,7 @@ tests = ['zfs_create_001_pos', 'zfs_create_002_pos', 'zfs_create_003_pos',
'zfs_create_007_pos', 'zfs_create_008_neg', 'zfs_create_009_neg',
'zfs_create_010_neg', 'zfs_create_011_pos', 'zfs_create_012_pos',
'zfs_create_013_pos', 'zfs_create_014_pos', 'zfs_create_encrypted',
- 'zfs_create_crypt_combos']
+ 'zfs_create_crypt_combos', 'zfs_create_dryrun', 'zfs_create_verbose']
tags = ['functional', 'cli_root', 'zfs_create']
[tests/functional/cli_root/zfs_destroy]
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_create/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zfs_create/Makefile.am
index a36d02161..cb65507ae 100644
--- a/tests/zfs-tests/tests/functional/cli_root/zfs_create/Makefile.am
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_create/Makefile.am
@@ -17,7 +17,9 @@ dist_pkgdata_SCRIPTS = \
zfs_create_013_pos.ksh \
zfs_create_014_pos.ksh \
zfs_create_encrypted.ksh \
- zfs_create_crypt_combos.ksh
+ zfs_create_crypt_combos.ksh \
+ zfs_create_dryrun.ksh \
+ zfs_create_verbose.ksh
dist_pkgdata_DATA = \
properties.kshlib \
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_dryrun.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_dryrun.ksh
new file mode 100755
index 000000000..64b8296f4
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_dryrun.ksh
@@ -0,0 +1,169 @@
+#!/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 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zfs_create/zfs_create_common.kshlib
+
+#
+# DESCRIPTION:
+# zfs create -n should perform basic sanity checking but should never create a
+# dataset. If -v and/or -P are used, it should verbose about what would be
+# created if sanity checks pass.
+#
+# STRATEGY:
+# 1. Attempt to create a file system and a volume using various combinations of
+# -n with -v and -P.
+#
+
+verify_runnable "both"
+
+#
+# Verifies that valid commands with -n and without -[vP]:
+# - succeed
+# - do not create a dataset
+# - do not generate output
+#
+function dry_create_no_output
+{
+ typeset -a cmd=(zfs create -n "$@")
+
+ log_note "$0: ${cmd[@]}"
+ log_must "${cmd[@]}"
+ datasetexists "$TESTPOOL/$TESTFS1" &&
+ log_fail "$TESTPOOL/$TESTFS1 unexpectedly created by '${cmd[@]}'"
+ typeset out=$("${cmd[@]}" 2>&1)
+ [[ -z "$out" ]] ||
+ log_fail "unexpected output '$out' from '${cmd[@]}'"
+}
+
+#
+# Verifies that commands with invalid properties or invalid property values
+# - fail
+# - do not create a dataset
+# - generate a message on stderr
+#
+function dry_create_error
+{
+ typeset -a cmd=(zfs create -n "$@")
+
+ log_note "$0: ${cmd[@]}"
+ log_mustnot "${cmd[@]}"
+ datasetexists "$TESTPOOL/$TESTFS1" &&
+ log_fail "$TESTPOOL/$TESTFS1 unexpectedly created by '${cmd[@]}'"
+ typeset out=$("${cmd[@]}" 2>&1 >/dev/null)
+ [[ -z "$out" ]] &&
+ log_fail "expected an error message but got none from '${cmd[@]}'"
+}
+
+#
+# Verifies that dry-run commands with parseable output
+# - succeed
+# - do not create datasets
+# - generate parseable output on stdout
+# - output matches expectations
+#
+function dry_create_parseable
+{
+ typeset -n exp=$1
+ shift
+ typeset -a cmd=(zfs create -Pn "$@")
+ typeset ds=${cmd[${#cmd[@]} - 1]}
+ typeset out
+ typeset -a toks
+ typeset -a props
+ typeset found_create=false
+
+ log_note "$0: ${cmd[@]}"
+ out=$("${cmd[@]}")
+ (( $? == 0 )) ||
+ log_fail "unexpected failure getting stdout from '${cmd[@]}'"
+ datasetexists "$TESTPOOL/$TESTFS1" &&
+ log_fail "$TESTPOOL/$TESTFS1 unexpectedly created by '${cmd[@]}'"
+ echo "$out" | while IFS=$'\t' read -A toks; do
+ log_note "verifying ${toks[@]}"
+ case ${toks[0]} in
+ create)
+ log_must test "${#toks[@]}" -eq 2
+ log_must test "${toks[1]}" == "$ds"
+ found_create="yes, I found create"
+ ;;
+ property)
+ log_must test "${#toks[@]}" -eq 3
+ typeset prop=${toks[1]}
+ typeset val=${toks[2]}
+ if [[ -z "${exp[$prop]}" ]]; then
+ log_fail "unexpectedly got property '$prop'"
+ fi
+ # We may not know the exact value a property will take
+ # on. This is the case for at least refreservation.
+ if [[ ${exp[$prop]} != "*" ]]; then
+ log_must test "${exp[$prop]}" == "$val"
+ fi
+ unset exp[$prop]
+ ;;
+ *)
+ log_fail "Unexpected line ${toks[@]}"
+ ;;
+ esac
+ done
+
+ log_must test "$found_create" == "yes, I found create"
+ log_must test "extra props: ${!exp[@]}" == "extra props: "
+}
+
+function cleanup
+{
+ if datasetexists "$TESTPOOL/$TESTFS1"; then
+ log_must zfs destroy -r "$TESTPOOL/$TESTFS1"
+ fi
+}
+log_onexit cleanup
+
+log_assert "zfs create -n creates nothing but can describe what would be" \
+ "created"
+
+# Typical creations should succeed
+dry_create_no_output "$TESTPOOL/$TESTFS1"
+dry_create_no_output -V 10m "$TESTPOOL/$TESTFS1"
+# It shouldn't do a space check right now
+dry_create_no_output -V 100t "$TESTPOOL/$TESTFS1"
+# It shouldn't create parent datasets either
+dry_create_no_output -p "$TESTPOOL/$TESTFS1/$TESTFS2"
+dry_create_no_output -pV 10m "$TESTPOOL/$TESTFS1/$TESTFS2"
+
+# Various invalid properties should be recognized and result in an error
+dry_create_error -o nosuchprop=42 "$TESTPOOL/$TESTFS1"
+dry_create_error -b 1234 -V 10m "$TESTPOOL/$TESTFS1"
+
+# Parseable output should be parseable.
+typeset -A expect
+expect=([compression]=on)
+dry_create_parseable expect -o compression=on "$TESTPOOL/$TESTFS1"
+
+# Sparse volumes should not get a gratuitous refreservation
+expect=([volblocksize]=4096 [volsize]=$((1024 * 1024 * 10)))
+dry_create_parseable expect -b 4k -V 10m -s "$TESTPOOL/$TESTFS1"
+
+# Non-sparse volumes should have refreservation
+expect=(
+ [volblocksize]=4096
+ [volsize]=$((1024 * 1024 * 10))
+ [refreservation]="*"
+)
+dry_create_parseable expect -b 4k -V 10m "$TESTPOOL/$TESTFS1"
+
+log_pass "zfs create -n creates nothing but can describe what would be" \
+ "created"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_verbose.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_verbose.ksh
new file mode 100755
index 000000000..a07ccc7e9
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_create/zfs_create_verbose.ksh
@@ -0,0 +1,165 @@
+#!/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 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zfs_create/zfs_create_common.kshlib
+
+#
+# DESCRIPTION:
+# zfs create -P without -n should be verbose about dataset creation.
+#
+# STRATEGY:
+# 1. Attempt to create a file system and a volume using various properties
+# and -P
+# 2. Exercise the combination of -p and -P.
+#
+
+verify_runnable "both"
+
+#
+# Verifies that non dry-run commands with parseable output
+# - succeed
+# - create datasets
+# - generate parseable output on stdout
+# - output matches expectations
+#
+function dry_create_parseable
+{
+ typeset -n exp=$1
+ shift
+ typeset -a cmd=(zfs create -P "$@")
+ typeset ds=${cmd[${#cmd[@]} - 1]}
+ typeset out
+ typeset -a toks
+ typeset -a props
+ typeset found_create=false
+ typeset create_ancestors=
+ typeset opt
+
+ # Parse the arguments to see if -p was used.
+ while getopts :PV:b:ospv opt; do
+ case $opt in
+ p) create_ancestors=needed ;;
+ *) continue ;;
+ esac
+ done
+
+ log_note "$0: ${cmd[@]}"
+ out=$("${cmd[@]}")
+ (( $? == 0 )) ||
+ log_fail "unexpected failure getting stdout from '${cmd[@]}'"
+ datasetexists "$TESTPOOL/$TESTFS1" ||
+ log_fail "$TESTPOOL/$TESTFS1 unexpectedly created by '${cmd[@]}'"
+ echo "$out" | while IFS=$'\t' read -A toks; do
+ log_note "verifying ${toks[@]}"
+ case ${toks[0]} in
+ create_ancestors)
+ case "$create_ancestors" in
+ needed)
+ log_must test "${toks[1]}" == "$ds"
+ create_ancestors="found ${toks[1]}"
+ ;;
+ found*)
+ log_fail "multiple ancestor creation" \
+ "$create_ancestors and ${toks[1]}"
+ ;;
+ "")
+ log_fail "unexpected create_ancestors"
+ ;;
+ *)
+ log_fail "impossible error: fix the test"
+ ;;
+ esac
+ ;;
+ create)
+ log_must test "${#toks[@]}" -eq 2
+ log_must test "${toks[1]}" == "$ds"
+ found_create="yes, I found create"
+ ;;
+ property)
+ log_must test "${#toks[@]}" -eq 3
+ typeset prop=${toks[1]}
+ typeset val=${toks[2]}
+ if [[ -z "${exp[$prop]}" ]]; then
+ log_fail "unexpectedly got property '$prop'"
+ fi
+ # We may not know the exact value a property will take
+ # on. This is the case for at least refreservation.
+ if [[ ${exp[$prop]} != "*" ]]; then
+ log_must test "${exp[$prop]}" == "$val"
+ fi
+ unset exp[$prop]
+ ;;
+ *)
+ log_fail "Unexpected line ${toks[@]}"
+ ;;
+ esac
+ done
+
+ log_must test "$found_create" == "yes, I found create"
+ log_must test "extra props: ${!exp[@]}" == "extra props: "
+
+ case "$create_ancestors" in
+ "")
+ log_must_busy zfs destroy "$ds"
+ ;;
+ "found $ds")
+ log_must_busy zfs destroy -r "$(echo "$ds" | cut -d/ -f1-2)"
+ ;;
+ needed)
+ log_fail "Expected but did not find create_ancestors"
+ ;;
+ *)
+ log_fail "Unexpected value for create_ancestors:" \
+ "$create_ancestors"
+ ;;
+ esac
+}
+
+function cleanup
+{
+ if datasetexists "$TESTPOOL/$TESTFS1"; then
+ log_must_busy zfs destroy -r "$TESTPOOL/$TESTFS1"
+ fi
+}
+log_onexit cleanup
+
+log_assert "zfs create -v creates datasets verbosely"
+
+# Parseable output should be parseable.
+typeset -A expect
+expect=([compression]=on)
+dry_create_parseable expect -o compression=on "$TESTPOOL/$TESTFS1"
+
+# Ancestor creation with -p should emit relevant line
+expect=([compression]=on)
+dry_create_parseable expect -p -o compression=on "$TESTPOOL/$TESTFS1"
+expect=([compression]=on)
+dry_create_parseable expect -p -o compression=on "$TESTPOOL/$TESTFS1/$TESTVOL"
+
+# Sparse volumes should not get a gratuitous refreservation
+expect=([volblocksize]=4096 [volsize]=$((1024 * 1024 * 10)))
+dry_create_parseable expect -b 4k -V 10m -s "$TESTPOOL/$TESTFS1"
+
+# Non-sparse volumes should have refreservation
+expect=(
+ [volblocksize]=4096
+ [volsize]=$((1024 * 1024 * 10))
+ [refreservation]="*"
+)
+dry_create_parseable expect -b 4k -V 10m "$TESTPOOL/$TESTFS1"
+
+log_pass "zfs create -v creates datasets verbosely"