Add zfs create dryrun
Adds the ability to sanity check zfs create arguments and to see the value of any additional properties that will local to the dataset. For example, automation that may need to adjust quota on a parent filesystem before creating a volume may call `zfs create -nP -V <size> <volume>` to obtain the value of refreservation. This adds the following options to zfs create: - -n dry-run (no-op) - -v verbose - -P parseable (implies verbose) Reviewed-by: Ryan Moeller <ryan@ixsystems.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Matt Ahrens <matt@delphix.com> Reviewed-by: Jerry Jelinek <jerry.jelinek@joyent.com> Signed-off-by: Mike Gerdts <mike.gerdts@joyent.com> Closes #8974
This commit is contained in:
parent
93e28d661e
commit
d45d7f08fa
|
@ -254,9 +254,9 @@ get_usage(zfs_help_t idx)
|
||||||
return (gettext("\tclone [-p] [-o property=value] ... "
|
return (gettext("\tclone [-p] [-o property=value] ... "
|
||||||
"<snapshot> <filesystem|volume>\n"));
|
"<snapshot> <filesystem|volume>\n"));
|
||||||
case HELP_CREATE:
|
case HELP_CREATE:
|
||||||
return (gettext("\tcreate [-p] [-o property=value] ... "
|
return (gettext("\tcreate [-Pnpv] [-o property=value] ... "
|
||||||
"<filesystem>\n"
|
"<filesystem>\n"
|
||||||
"\tcreate [-ps] [-b blocksize] [-o property=value] ... "
|
"\tcreate [-Pnpsv] [-b blocksize] [-o property=value] ... "
|
||||||
"-V <size> <volume>\n"));
|
"-V <size> <volume>\n"));
|
||||||
case HELP_DESTROY:
|
case HELP_DESTROY:
|
||||||
return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
|
return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
|
||||||
|
@ -867,8 +867,8 @@ usage:
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* zfs create [-p] [-o prop=value] ... fs
|
* zfs create [-Pnpv] [-o prop=value] ... fs
|
||||||
* zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size
|
* zfs create [-Pnpsv] [-b blocksize] [-o prop=value] ... -V vol size
|
||||||
*
|
*
|
||||||
* Create a new dataset. This command can be used to create filesystems
|
* Create a new dataset. This command can be used to create filesystems
|
||||||
* and volumes. Snapshot creation is handled by 'zfs snapshot'.
|
* and volumes. Snapshot creation is handled by 'zfs snapshot'.
|
||||||
|
@ -880,16 +880,29 @@ usage:
|
||||||
* SPA_VERSION_REFRESERVATION, we set a refreservation instead.
|
* SPA_VERSION_REFRESERVATION, we set a refreservation instead.
|
||||||
*
|
*
|
||||||
* The '-p' flag creates all the non-existing ancestors of the target first.
|
* 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
|
static int
|
||||||
zfs_do_create(int argc, char **argv)
|
zfs_do_create(int argc, char **argv)
|
||||||
{
|
{
|
||||||
zfs_type_t type = ZFS_TYPE_FILESYSTEM;
|
zfs_type_t type = ZFS_TYPE_FILESYSTEM;
|
||||||
|
zpool_handle_t *zpool_handle = NULL;
|
||||||
|
nvlist_t *real_props = NULL;
|
||||||
uint64_t volsize = 0;
|
uint64_t volsize = 0;
|
||||||
int c;
|
int c;
|
||||||
boolean_t noreserve = B_FALSE;
|
boolean_t noreserve = B_FALSE;
|
||||||
boolean_t bflag = B_FALSE;
|
boolean_t bflag = B_FALSE;
|
||||||
boolean_t parents = 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;
|
int ret = 1;
|
||||||
nvlist_t *props;
|
nvlist_t *props;
|
||||||
uint64_t intval;
|
uint64_t intval;
|
||||||
|
@ -898,7 +911,7 @@ zfs_do_create(int argc, char **argv)
|
||||||
nomem();
|
nomem();
|
||||||
|
|
||||||
/* check options */
|
/* check options */
|
||||||
while ((c = getopt(argc, argv, ":V:b:so:p")) != -1) {
|
while ((c = getopt(argc, argv, ":PV:b:nso:pv")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'V':
|
case 'V':
|
||||||
type = ZFS_TYPE_VOLUME;
|
type = ZFS_TYPE_VOLUME;
|
||||||
|
@ -914,6 +927,10 @@ zfs_do_create(int argc, char **argv)
|
||||||
nomem();
|
nomem();
|
||||||
volsize = intval;
|
volsize = intval;
|
||||||
break;
|
break;
|
||||||
|
case 'P':
|
||||||
|
verbose = B_TRUE;
|
||||||
|
parseable = B_TRUE;
|
||||||
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
parents = B_TRUE;
|
parents = B_TRUE;
|
||||||
break;
|
break;
|
||||||
|
@ -931,6 +948,9 @@ zfs_do_create(int argc, char **argv)
|
||||||
intval) != 0)
|
intval) != 0)
|
||||||
nomem();
|
nomem();
|
||||||
break;
|
break;
|
||||||
|
case 'n':
|
||||||
|
dryrun = B_TRUE;
|
||||||
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
if (!parseprop(props, optarg))
|
if (!parseprop(props, optarg))
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -938,6 +958,9 @@ zfs_do_create(int argc, char **argv)
|
||||||
case 's':
|
case 's':
|
||||||
noreserve = B_TRUE;
|
noreserve = B_TRUE;
|
||||||
break;
|
break;
|
||||||
|
case 'v':
|
||||||
|
verbose = B_TRUE;
|
||||||
|
break;
|
||||||
case ':':
|
case ':':
|
||||||
(void) fprintf(stderr, gettext("missing size "
|
(void) fprintf(stderr, gettext("missing size "
|
||||||
"argument\n"));
|
"argument\n"));
|
||||||
|
@ -969,14 +992,9 @@ zfs_do_create(int argc, char **argv)
|
||||||
goto badusage;
|
goto badusage;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == ZFS_TYPE_VOLUME && !noreserve) {
|
if (dryrun || (type == ZFS_TYPE_VOLUME && !noreserve)) {
|
||||||
zpool_handle_t *zpool_handle;
|
char msg[ZFS_MAX_DATASET_NAME_LEN * 2];
|
||||||
nvlist_t *real_props = NULL;
|
|
||||||
uint64_t spa_version;
|
|
||||||
char *p;
|
char *p;
|
||||||
zfs_prop_t resv_prop;
|
|
||||||
char *strval;
|
|
||||||
char msg[1024];
|
|
||||||
|
|
||||||
if ((p = strchr(argv[0], '/')) != NULL)
|
if ((p = strchr(argv[0], '/')) != NULL)
|
||||||
*p = '\0';
|
*p = '\0';
|
||||||
|
@ -985,6 +1003,22 @@ zfs_do_create(int argc, char **argv)
|
||||||
*p = '/';
|
*p = '/';
|
||||||
if (zpool_handle == NULL)
|
if (zpool_handle == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
(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,
|
spa_version = zpool_get_prop_int(zpool_handle,
|
||||||
ZPOOL_PROP_VERSION, NULL);
|
ZPOOL_PROP_VERSION, NULL);
|
||||||
if (spa_version >= SPA_VERSION_REFRESERVATION)
|
if (spa_version >= SPA_VERSION_REFRESERVATION)
|
||||||
|
@ -992,18 +1026,8 @@ zfs_do_create(int argc, char **argv)
|
||||||
else
|
else
|
||||||
resv_prop = ZFS_PROP_RESERVATION;
|
resv_prop = ZFS_PROP_RESERVATION;
|
||||||
|
|
||||||
(void) snprintf(msg, sizeof (msg),
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
volsize = zvol_volsize_to_reservation(zpool_handle, volsize,
|
volsize = zvol_volsize_to_reservation(zpool_handle, volsize,
|
||||||
real_props);
|
real_props);
|
||||||
nvlist_free(real_props);
|
|
||||||
zpool_close(zpool_handle);
|
|
||||||
|
|
||||||
if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),
|
if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),
|
||||||
&strval) != 0) {
|
&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)) {
|
if (parents && zfs_name_valid(argv[0], type)) {
|
||||||
/*
|
/*
|
||||||
|
@ -1025,7 +1053,49 @@ zfs_do_create(int argc, char **argv)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (zfs_create_ancestors(g_zfs, argv[0]) != 0)
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,9 @@
|
||||||
.\" Copyright (c) 2014 Integros [integros.com]
|
.\" Copyright (c) 2014 Integros [integros.com]
|
||||||
.\" Copyright 2019 Richard Laager. All rights reserved.
|
.\" Copyright 2019 Richard Laager. All rights reserved.
|
||||||
.\" Copyright 2018 Nexenta Systems, Inc.
|
.\" 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
|
.Dt ZFS 8 SMM
|
||||||
.Os Linux
|
.Os Linux
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
@ -41,12 +41,12 @@
|
||||||
.Fl ?V
|
.Fl ?V
|
||||||
.Nm
|
.Nm
|
||||||
.Cm create
|
.Cm create
|
||||||
.Op Fl p
|
.Op Fl Pnpv
|
||||||
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
|
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
|
||||||
.Ar filesystem
|
.Ar filesystem
|
||||||
.Nm
|
.Nm
|
||||||
.Cm create
|
.Cm create
|
||||||
.Op Fl ps
|
.Op Fl Pnpsv
|
||||||
.Op Fl b Ar blocksize
|
.Op Fl b Ar blocksize
|
||||||
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
|
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
|
||||||
.Fl V Ar size Ar volume
|
.Fl V Ar size Ar volume
|
||||||
|
@ -2556,7 +2556,7 @@ subcommand.
|
||||||
.It Xo
|
.It Xo
|
||||||
.Nm
|
.Nm
|
||||||
.Cm create
|
.Cm create
|
||||||
.Op Fl p
|
.Op Fl Pnpv
|
||||||
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
|
.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
|
||||||
.Ar filesystem
|
.Ar filesystem
|
||||||
.Xc
|
.Xc
|
||||||
|
@ -2585,6 +2585,48 @@ Any property specified on the command line using the
|
||||||
.Fl o
|
.Fl o
|
||||||
option is ignored.
|
option is ignored.
|
||||||
If the target filesystem already exists, the operation completes successfully.
|
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
|
.El
|
||||||
.It Xo
|
.It Xo
|
||||||
.Nm
|
.Nm
|
||||||
|
@ -2641,6 +2683,52 @@ See
|
||||||
in the
|
in the
|
||||||
.Sx Native Properties
|
.Sx Native Properties
|
||||||
section for more information about sparse volumes.
|
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
|
.El
|
||||||
.It Xo
|
.It Xo
|
||||||
.Nm
|
.Nm
|
||||||
|
|
|
@ -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_007_pos', 'zfs_create_008_neg', 'zfs_create_009_neg',
|
||||||
'zfs_create_010_neg', 'zfs_create_011_pos', 'zfs_create_012_pos',
|
'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_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']
|
tags = ['functional', 'cli_root', 'zfs_create']
|
||||||
|
|
||||||
[tests/functional/cli_root/zfs_destroy]
|
[tests/functional/cli_root/zfs_destroy]
|
||||||
|
|
|
@ -17,7 +17,9 @@ dist_pkgdata_SCRIPTS = \
|
||||||
zfs_create_013_pos.ksh \
|
zfs_create_013_pos.ksh \
|
||||||
zfs_create_014_pos.ksh \
|
zfs_create_014_pos.ksh \
|
||||||
zfs_create_encrypted.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 = \
|
dist_pkgdata_DATA = \
|
||||||
properties.kshlib \
|
properties.kshlib \
|
||||||
|
|
|
@ -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"
|
|
@ -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"
|
Loading…
Reference in New Issue