JSON output support for zfs version and zfs get

This commit adds support for JSON output for zfs version and zfs get
commands. '-j' flag can be used to get output in JSON format.

Information is collected in nvlist objects which is later printed in
JSON format. Existing options that work for zfs get and zfs version
also work with '-j' flag.

man pages for zfs get and zfs version are updated accordingly.

Reviewed-by: Tony Hutter <hutter2@llnl.gov>
Reviewed-by: Ameer Hamza <ahamza@ixsystems.com>
Signed-off-by: Umer Saleem <usaleem@ixsystems.com>
Closes #16217
This commit is contained in:
Umer Saleem 2024-04-05 21:02:30 +05:00 committed by Brian Behlendorf
parent 6c7d41a643
commit aa15b60e58
5 changed files with 412 additions and 15 deletions

View File

@ -134,6 +134,10 @@ static int zfs_do_unzone(int argc, char **argv);
static int zfs_do_help(int argc, char **argv); static int zfs_do_help(int argc, char **argv);
enum zfs_options {
ZFS_OPTION_JSON_NUMS_AS_INT = 1024
};
/* /*
* Enable a reasonable set of defaults for libumem debugging on DEBUG builds. * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
*/ */
@ -272,6 +276,8 @@ static zfs_command_t command_table[] = {
#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))
#define MAX_CMD_LEN 256
zfs_command_t *current_command; zfs_command_t *current_command;
static const char * static const char *
@ -292,7 +298,7 @@ get_usage(zfs_help_t idx)
"<filesystem|volume>@<snap>[%<snap>][,...]\n" "<filesystem|volume>@<snap>[%<snap>][,...]\n"
"\tdestroy <filesystem|volume>#<bookmark>\n")); "\tdestroy <filesystem|volume>#<bookmark>\n"));
case HELP_GET: case HELP_GET:
return (gettext("\tget [-rHp] [-d max] " return (gettext("\tget [-rHp] [-j [--json-int]] [-d max] "
"[-o \"all\" | field[,...]]\n" "[-o \"all\" | field[,...]]\n"
"\t [-t type[,...]] [-s source[,...]]\n" "\t [-t type[,...]] [-s source[,...]]\n"
"\t <\"all\" | property[,...]> " "\t <\"all\" | property[,...]> "
@ -420,7 +426,7 @@ get_usage(zfs_help_t idx)
"\t <filesystem|volume>\n" "\t <filesystem|volume>\n"
"\tchange-key -i [-l] <filesystem|volume>\n")); "\tchange-key -i [-l] <filesystem|volume>\n"));
case HELP_VERSION: case HELP_VERSION:
return (gettext("\tversion\n")); return (gettext("\tversion [-j]\n"));
case HELP_REDACT: case HELP_REDACT:
return (gettext("\tredact <snapshot> <bookmark> " return (gettext("\tredact <snapshot> <bookmark> "
"<redaction_snapshot> ...\n")); "<redaction_snapshot> ...\n"));
@ -1885,7 +1891,109 @@ is_recvd_column(zprop_get_cbdata_t *cbp)
} }
/* /*
* zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...] * Generates an nvlist with output version for every command based on params.
* Purpose of this is to add a version of JSON output, considering the schema
* format might be updated for each command in future.
*
* Schema:
*
* "output_version": {
* "command": string,
* "vers_major": integer,
* "vers_minor": integer,
* }
*/
static nvlist_t *
zfs_json_schema(int maj_v, int min_v)
{
nvlist_t *sch = NULL;
nvlist_t *ov = NULL;
char cmd[MAX_CMD_LEN];
snprintf(cmd, MAX_CMD_LEN, "zfs %s", current_command->name);
sch = fnvlist_alloc();
ov = fnvlist_alloc();
fnvlist_add_string(ov, "command", cmd);
fnvlist_add_uint32(ov, "vers_major", maj_v);
fnvlist_add_uint32(ov, "vers_minor", min_v);
fnvlist_add_nvlist(sch, "output_version", ov);
fnvlist_free(ov);
return (sch);
}
static void
fill_dataset_info(nvlist_t *list, zfs_handle_t *zhp, boolean_t as_int)
{
char createtxg[ZFS_MAXPROPLEN];
zfs_type_t type = zfs_get_type(zhp);
nvlist_add_string(list, "name", zfs_get_name(zhp));
switch (type) {
case ZFS_TYPE_FILESYSTEM:
fnvlist_add_string(list, "type", "FILESYSTEM");
break;
case ZFS_TYPE_VOLUME:
fnvlist_add_string(list, "type", "VOLUME");
break;
case ZFS_TYPE_SNAPSHOT:
fnvlist_add_string(list, "type", "SNAPSHOT");
break;
case ZFS_TYPE_POOL:
fnvlist_add_string(list, "type", "POOL");
break;
case ZFS_TYPE_BOOKMARK:
fnvlist_add_string(list, "type", "BOOKMARK");
break;
default:
fnvlist_add_string(list, "type", "UNKNOWN");
break;
}
if (type != ZFS_TYPE_POOL)
fnvlist_add_string(list, "pool", zfs_get_pool_name(zhp));
if (as_int) {
fnvlist_add_uint64(list, "createtxg", zfs_prop_get_int(zhp,
ZFS_PROP_CREATETXG));
} else {
if (zfs_prop_get(zhp, ZFS_PROP_CREATETXG, createtxg,
sizeof (createtxg), NULL, NULL, 0, B_TRUE) == 0)
fnvlist_add_string(list, "createtxg", createtxg);
}
if (type == ZFS_TYPE_SNAPSHOT) {
char *ds, *snap;
ds = snap = strdup(zfs_get_name(zhp));
ds = strsep(&snap, "@");
fnvlist_add_string(list, "dataset", ds);
fnvlist_add_string(list, "snapshot_name", snap);
free(ds);
}
}
static int
zprop_collect_property(const char *name, zprop_get_cbdata_t *cbp,
const char *propname, const char *value, zprop_source_t sourcetype,
const char *source, const char *recvd_value, nvlist_t *nvl)
{
if (cbp->cb_json) {
if ((sourcetype & cbp->cb_sources) == 0)
return (0);
else {
return (zprop_nvlist_one_property(propname, value,
sourcetype, source, recvd_value, nvl,
cbp->cb_json_as_int));
}
} else {
zprop_print_one_property(name, cbp,
propname, value, sourcetype, source, recvd_value);
return (0);
}
}
/*
* zfs get [-rHp] [-j [--json-int]] [-o all | field[,field]...]
* [-s source[,source]...]
* < all | property[,property]... > < fs | snap | vol > ... * < all | property[,property]... > < fs | snap | vol > ...
* *
* -r recurse over any child datasets * -r recurse over any child datasets
@ -1898,6 +2006,8 @@ is_recvd_column(zprop_get_cbdata_t *cbp)
* "local,default,inherited,received,temporary,none". Default is * "local,default,inherited,received,temporary,none". Default is
* all six. * all six.
* -p Display values in parsable (literal) format. * -p Display values in parsable (literal) format.
* -j Display output in JSON format.
* --json-int Display numbers as integers instead of strings.
* *
* Prints properties for the given datasets. The user can control which * Prints properties for the given datasets. The user can control which
* columns to display as well as which property types to allow. * columns to display as well as which property types to allow.
@ -1917,9 +2027,21 @@ get_callback(zfs_handle_t *zhp, void *data)
nvlist_t *user_props = zfs_get_user_props(zhp); nvlist_t *user_props = zfs_get_user_props(zhp);
zprop_list_t *pl = cbp->cb_proplist; zprop_list_t *pl = cbp->cb_proplist;
nvlist_t *propval; nvlist_t *propval;
nvlist_t *item, *d, *props;
item = d = props = NULL;
const char *strval; const char *strval;
const char *sourceval; const char *sourceval;
boolean_t received = is_recvd_column(cbp); boolean_t received = is_recvd_column(cbp);
int err = 0;
if (cbp->cb_json) {
d = fnvlist_lookup_nvlist(cbp->cb_jsobj, "datasets");
if (d == NULL) {
fprintf(stderr, "datasets obj not found.\n");
exit(1);
}
props = fnvlist_alloc();
}
for (; pl != NULL; pl = pl->pl_next) { for (; pl != NULL; pl = pl->pl_next) {
char *recvdval = NULL; char *recvdval = NULL;
@ -1954,9 +2076,9 @@ get_callback(zfs_handle_t *zhp, void *data)
cbp->cb_literal) == 0)) cbp->cb_literal) == 0))
recvdval = rbuf; recvdval = rbuf;
zprop_print_one_property(zfs_get_name(zhp), cbp, err = zprop_collect_property(zfs_get_name(zhp), cbp,
zfs_prop_to_name(pl->pl_prop), zfs_prop_to_name(pl->pl_prop),
buf, sourcetype, source, recvdval); buf, sourcetype, source, recvdval, props);
} else if (zfs_prop_userquota(pl->pl_user_prop)) { } else if (zfs_prop_userquota(pl->pl_user_prop)) {
sourcetype = ZPROP_SRC_LOCAL; sourcetype = ZPROP_SRC_LOCAL;
@ -1966,8 +2088,9 @@ get_callback(zfs_handle_t *zhp, void *data)
(void) strlcpy(buf, "-", sizeof (buf)); (void) strlcpy(buf, "-", sizeof (buf));
} }
zprop_print_one_property(zfs_get_name(zhp), cbp, err = zprop_collect_property(zfs_get_name(zhp), cbp,
pl->pl_user_prop, buf, sourcetype, source, NULL); pl->pl_user_prop, buf, sourcetype, source, NULL,
props);
} else if (zfs_prop_written(pl->pl_user_prop)) { } else if (zfs_prop_written(pl->pl_user_prop)) {
sourcetype = ZPROP_SRC_LOCAL; sourcetype = ZPROP_SRC_LOCAL;
@ -1977,8 +2100,9 @@ get_callback(zfs_handle_t *zhp, void *data)
(void) strlcpy(buf, "-", sizeof (buf)); (void) strlcpy(buf, "-", sizeof (buf));
} }
zprop_print_one_property(zfs_get_name(zhp), cbp, err = zprop_collect_property(zfs_get_name(zhp), cbp,
pl->pl_user_prop, buf, sourcetype, source, NULL); pl->pl_user_prop, buf, sourcetype, source, NULL,
props);
} else { } else {
if (nvlist_lookup_nvlist(user_props, if (nvlist_lookup_nvlist(user_props,
pl->pl_user_prop, &propval) != 0) { pl->pl_user_prop, &propval) != 0) {
@ -2010,9 +2134,24 @@ get_callback(zfs_handle_t *zhp, void *data)
cbp->cb_literal) == 0)) cbp->cb_literal) == 0))
recvdval = rbuf; recvdval = rbuf;
zprop_print_one_property(zfs_get_name(zhp), cbp, err = zprop_collect_property(zfs_get_name(zhp), cbp,
pl->pl_user_prop, strval, sourcetype, pl->pl_user_prop, strval, sourcetype,
source, recvdval); source, recvdval, props);
}
if (err != 0)
return (err);
}
if (cbp->cb_json) {
if (!nvlist_empty(props)) {
item = fnvlist_alloc();
fill_dataset_info(item, zhp, cbp->cb_json_as_int);
fnvlist_add_nvlist(item, "properties", props);
fnvlist_add_nvlist(d, zfs_get_name(zhp), item);
fnvlist_free(props);
fnvlist_free(item);
} else {
fnvlist_free(props);
} }
} }
@ -2029,6 +2168,7 @@ zfs_do_get(int argc, char **argv)
int ret = 0; int ret = 0;
int limit = 0; int limit = 0;
zprop_list_t fake_name = { 0 }; zprop_list_t fake_name = { 0 };
nvlist_t *data;
/* /*
* Set up default columns and sources. * Set up default columns and sources.
@ -2040,8 +2180,14 @@ zfs_do_get(int argc, char **argv)
cb.cb_columns[3] = GET_COL_SOURCE; cb.cb_columns[3] = GET_COL_SOURCE;
cb.cb_type = ZFS_TYPE_DATASET; cb.cb_type = ZFS_TYPE_DATASET;
struct option long_options[] = {
{"json-int", no_argument, NULL, ZFS_OPTION_JSON_NUMS_AS_INT},
{0, 0, 0, 0}
};
/* check options */ /* check options */
while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) { while ((c = getopt_long(argc, argv, ":d:o:s:jrt:Hp", long_options,
NULL)) != -1) {
switch (c) { switch (c) {
case 'p': case 'p':
cb.cb_literal = B_TRUE; cb.cb_literal = B_TRUE;
@ -2055,6 +2201,17 @@ zfs_do_get(int argc, char **argv)
case 'H': case 'H':
cb.cb_scripted = B_TRUE; cb.cb_scripted = B_TRUE;
break; break;
case 'j':
cb.cb_json = B_TRUE;
cb.cb_jsobj = zfs_json_schema(0, 1);
data = fnvlist_alloc();
fnvlist_add_nvlist(cb.cb_jsobj, "datasets", data);
fnvlist_free(data);
break;
case ZFS_OPTION_JSON_NUMS_AS_INT:
cb.cb_json_as_int = B_TRUE;
cb.cb_literal = B_TRUE;
break;
case ':': case ':':
(void) fprintf(stderr, gettext("missing argument for " (void) fprintf(stderr, gettext("missing argument for "
"'%c' option\n"), optopt); "'%c' option\n"), optopt);
@ -2178,7 +2335,6 @@ found2:;
found3:; found3:;
} }
break; break;
case '?': case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"), (void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt); optopt);
@ -2195,6 +2351,12 @@ found3:;
usage(B_FALSE); usage(B_FALSE);
} }
if (!cb.cb_json && cb.cb_json_as_int) {
(void) fprintf(stderr, gettext("'--json-int' only works with"
" '-j' option\n"));
usage(B_FALSE);
}
fields = argv[0]; fields = argv[0];
/* /*
@ -2235,6 +2397,11 @@ found3:;
ret = zfs_for_each(argc, argv, flags, types, NULL, ret = zfs_for_each(argc, argv, flags, types, NULL,
&cb.cb_proplist, limit, get_callback, &cb); &cb.cb_proplist, limit, get_callback, &cb);
if (ret == 0 && cb.cb_json)
zcmd_print_json(cb.cb_jsobj);
else if (ret != 0 && cb.cb_json)
nvlist_free(cb.cb_jsobj);
if (cb.cb_proplist == &fake_name) if (cb.cb_proplist == &fake_name)
zprop_free_list(fake_name.pl_next); zprop_free_list(fake_name.pl_next);
else else
@ -8811,8 +8978,39 @@ found:;
static int static int
zfs_do_version(int argc, char **argv) zfs_do_version(int argc, char **argv)
{ {
(void) argc, (void) argv; int c;
return (zfs_version_print() != 0); nvlist_t *jsobj = NULL, *zfs_ver = NULL;
boolean_t json = B_FALSE;
while ((c = getopt(argc, argv, "j")) != -1) {
switch (c) {
case 'j':
json = B_TRUE;
jsobj = zfs_json_schema(0, 1);
break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
usage(B_FALSE);
}
}
argc -= optind;
if (argc != 0) {
(void) fprintf(stderr, "too many arguments\n");
usage(B_FALSE);
}
if (json) {
zfs_ver = zfs_version_nvlist();
if (zfs_ver) {
fnvlist_add_nvlist(jsobj, "zfs_version", zfs_ver);
zcmd_print_json(jsobj);
fnvlist_free(zfs_ver);
return (0);
} else
return (-1);
} else
return (zfs_version_print() != 0);
} }
/* Display documentation */ /* Display documentation */

View File

@ -631,6 +631,8 @@ _LIBZFS_H int zprop_get_list(libzfs_handle_t *, char *, zprop_list_t **,
zfs_type_t); zfs_type_t);
_LIBZFS_H void zprop_free_list(zprop_list_t *); _LIBZFS_H void zprop_free_list(zprop_list_t *);
_LIBZFS_H void zcmd_print_json(nvlist_t *);
#define ZFS_GET_NCOLS 5 #define ZFS_GET_NCOLS 5
typedef enum { typedef enum {
@ -658,9 +660,12 @@ typedef struct zprop_get_cbdata {
boolean_t cb_scripted; boolean_t cb_scripted;
boolean_t cb_literal; boolean_t cb_literal;
boolean_t cb_first; boolean_t cb_first;
boolean_t cb_json;
zprop_list_t *cb_proplist; zprop_list_t *cb_proplist;
zfs_type_t cb_type; zfs_type_t cb_type;
vdev_cbdata_t cb_vdevs; vdev_cbdata_t cb_vdevs;
nvlist_t *cb_jsobj;
boolean_t cb_json_as_int;
} zprop_get_cbdata_t; } zprop_get_cbdata_t;
#define ZFS_SET_NOMOUNT 1 #define ZFS_SET_NOMOUNT 1
@ -674,6 +679,9 @@ _LIBZFS_H void zprop_print_one_property(const char *, zprop_get_cbdata_t *,
const char *, const char *, zprop_source_t, const char *, const char *, const char *, zprop_source_t, const char *,
const char *); const char *);
_LIBZFS_H int zprop_nvlist_one_property(const char *, const char *,
zprop_source_t, const char *, const char *, nvlist_t *, boolean_t);
/* /*
* Iterator functions. * Iterator functions.
*/ */
@ -979,6 +987,7 @@ _LIBZFS_H boolean_t libzfs_envvar_is_set(const char *);
_LIBZFS_H const char *zfs_version_userland(void); _LIBZFS_H const char *zfs_version_userland(void);
_LIBZFS_H char *zfs_version_kernel(void); _LIBZFS_H char *zfs_version_kernel(void);
_LIBZFS_H int zfs_version_print(void); _LIBZFS_H int zfs_version_print(void);
_LIBZFS_H nvlist_t *zfs_version_nvlist(void);
/* /*
* Given a device or file, determine if it is part of a pool. * Given a device or file, determine if it is part of a pool.

View File

@ -68,6 +68,7 @@
* as necessary. * as necessary.
*/ */
#define URI_REGEX "^\\([A-Za-z][A-Za-z0-9+.\\-]*\\):" #define URI_REGEX "^\\([A-Za-z][A-Za-z0-9+.\\-]*\\):"
#define STR_NUMS "0123456789"
int int
libzfs_errno(libzfs_handle_t *hdl) libzfs_errno(libzfs_handle_t *hdl)
@ -1267,6 +1268,14 @@ zcmd_read_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t **nvlp)
* ================================================================ * ================================================================
*/ */
void
zcmd_print_json(nvlist_t *nvl)
{
nvlist_print_json(stdout, nvl);
(void) putchar('\n');
nvlist_free(nvl);
}
static void static void
zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type) zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
{ {
@ -1393,6 +1402,103 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
(void) printf("\n"); (void) printf("\n");
} }
/*
* Add property value and source to provided nvlist, according to
* settings in cb structure. Later to be printed in JSON format.
*/
int
zprop_nvlist_one_property(const char *propname,
const char *value, zprop_source_t sourcetype, const char *source,
const char *recvd_value, nvlist_t *nvl, boolean_t as_int)
{
int ret = 0;
nvlist_t *src_nv, *prop;
boolean_t all_numeric = strspn(value, STR_NUMS) == strlen(value);
src_nv = prop = NULL;
if ((nvlist_alloc(&prop, NV_UNIQUE_NAME, 0) != 0) ||
(nvlist_alloc(&src_nv, NV_UNIQUE_NAME, 0) != 0)) {
ret = -1;
goto err;
}
if (as_int && all_numeric) {
uint64_t val;
sscanf(value, "%lld", (u_longlong_t *)&val);
if (nvlist_add_uint64(prop, "value", val) != 0) {
ret = -1;
goto err;
}
} else {
if (nvlist_add_string(prop, "value", value) != 0) {
ret = -1;
goto err;
}
}
switch (sourcetype) {
case ZPROP_SRC_NONE:
if (nvlist_add_string(src_nv, "type", "NONE") != 0 ||
(nvlist_add_string(src_nv, "data", "-") != 0)) {
ret = -1;
goto err;
}
break;
case ZPROP_SRC_DEFAULT:
if (nvlist_add_string(src_nv, "type", "DEFAULT") != 0 ||
(nvlist_add_string(src_nv, "data", "-") != 0)) {
ret = -1;
goto err;
}
break;
case ZPROP_SRC_LOCAL:
if (nvlist_add_string(src_nv, "type", "LOCAL") != 0 ||
(nvlist_add_string(src_nv, "data", "-") != 0)) {
ret = -1;
goto err;
}
break;
case ZPROP_SRC_TEMPORARY:
if (nvlist_add_string(src_nv, "type", "TEMPORARY") != 0 ||
(nvlist_add_string(src_nv, "data", "-") != 0)) {
ret = -1;
goto err;
}
break;
case ZPROP_SRC_INHERITED:
if (nvlist_add_string(src_nv, "type", "INHERITED") != 0 ||
(nvlist_add_string(src_nv, "data", source) != 0)) {
ret = -1;
goto err;
}
break;
case ZPROP_SRC_RECEIVED:
if (nvlist_add_string(src_nv, "type", "RECEIVED") != 0 ||
(nvlist_add_string(src_nv, "data",
(recvd_value == NULL ? "-" : recvd_value)) != 0)) {
ret = -1;
goto err;
}
break;
default:
assert(!"unhandled zprop_source_t");
if (nvlist_add_string(src_nv, "type",
"unhandled zprop_source_t") != 0) {
ret = -1;
goto err;
}
}
if ((nvlist_add_nvlist(prop, "source", src_nv) != 0) ||
(nvlist_add_nvlist(nvl, propname, prop)) != 0) {
ret = -1;
goto err;
}
err:
nvlist_free(src_nv);
nvlist_free(prop);
return (ret);
}
/* /*
* Display a single line of output, according to the settings in the callback * Display a single line of output, according to the settings in the callback
* structure. * structure.
@ -1999,6 +2105,34 @@ zfs_version_print(void)
return (0); return (0);
} }
/*
* Returns an nvlist with both zfs userland and kernel versions.
* Returns NULL on error.
*/
nvlist_t *
zfs_version_nvlist(void)
{
nvlist_t *nvl;
char kmod_ver[64];
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
return (NULL);
if (nvlist_add_string(nvl, "userland", ZFS_META_ALIAS) != 0)
goto err;
char *kver = zfs_version_kernel();
if (kver == NULL) {
fprintf(stderr, "zfs_version_kernel() failed: %s\n",
zfs_strerror(errno));
goto err;
}
(void) snprintf(kmod_ver, 64, "zfs-kmod-%s", kver);
if (nvlist_add_string(nvl, "kernel", kmod_ver) != 0)
goto err;
return (nvl);
err:
nvlist_free(nvl);
return (NULL);
}
/* /*
* Return 1 if the user requested ANSI color output, and our terminal supports * Return 1 if the user requested ANSI color output, and our terminal supports
* it. Return 0 for no color. * it. Return 0 for no color.

View File

@ -46,6 +46,7 @@
.Cm get .Cm get
.Op Fl r Ns | Ns Fl d Ar depth .Op Fl r Ns | Ns Fl d Ar depth
.Op Fl Hp .Op Fl Hp
.Op Fl j Op Ar --json-int
.Oo Fl o Ar field Ns Oo , Ns Ar field Oc Ns Oc .Oo Fl o Ar field Ns Oo , Ns Ar field Oc Ns Oc
.Oo Fl s Ar source Ns Oo , Ns Ar source Oc Ns Oc .Oo Fl s Ar source Ns Oo , Ns Ar source Oc Ns Oc
.Oo Fl t Ar type Ns Oo , Ns Ar type Oc Ns Oc .Oo Fl t Ar type Ns Oo , Ns Ar type Oc Ns Oc
@ -91,6 +92,7 @@ dataset.
.Cm get .Cm get
.Op Fl r Ns | Ns Fl d Ar depth .Op Fl r Ns | Ns Fl d Ar depth
.Op Fl Hp .Op Fl Hp
.Op Fl j Op Ar --json-int
.Oo Fl o Ar field Ns Oo , Ns Ar field Oc Ns Oc .Oo Fl o Ar field Ns Oo , Ns Ar field Oc Ns Oc
.Oo Fl s Ar source Ns Oo , Ns Ar source Oc Ns Oc .Oo Fl s Ar source Ns Oo , Ns Ar source Oc Ns Oc
.Oo Fl t Ar type Ns Oo , Ns Ar type Oc Ns Oc .Oo Fl t Ar type Ns Oo , Ns Ar type Oc Ns Oc
@ -128,6 +130,11 @@ The value
can be used to display all properties that apply to the given dataset's type can be used to display all properties that apply to the given dataset's type
.Pq Sy filesystem , volume , snapshot , No or Sy bookmark . .Pq Sy filesystem , volume , snapshot , No or Sy bookmark .
.Bl -tag -width "-s source" .Bl -tag -width "-s source"
.It Fl j Op Ar --json-int
Display the output in JSON format.
Specify
.Sy --json-int
to display numbers in integer format instead of strings for JSON output.
.It Fl H .It Fl H
Display output in a form more easily parsed by scripts. Display output in a form more easily parsed by scripts.
Any headers are omitted, and fields are explicitly separated by a single tab Any headers are omitted, and fields are explicitly separated by a single tab
@ -283,6 +290,50 @@ The following command gets a single property value:
on on
.Ed .Ed
.Pp .Pp
The following command gets a single property value recursively in JSON format:
.Bd -literal -compact -offset Ds
.No # Nm zfs Cm get Fl j Fl r Sy mountpoint Ar pool/home | Nm jq
{
"output_version": {
"command": "zfs get",
"vers_major": 0,
"vers_minor": 1
},
"datasets": {
"pool/home": {
"name": "pool/home",
"type": "FILESYSTEM",
"pool": "pool",
"createtxg": "10",
"properties": {
"mountpoint": {
"value": "/pool/home",
"source": {
"type": "DEFAULT",
"data": "-"
}
}
}
},
"pool/home/bob": {
"name": "pool/home/bob",
"type": "FILESYSTEM",
"pool": "pool",
"createtxg": "1176",
"properties": {
"mountpoint": {
"value": "/pool/home/bob",
"source": {
"type": "DEFAULT",
"data": "-"
}
}
}
}
}
}
.Ed
.Pp
The following command lists all properties with local settings for The following command lists all properties with local settings for
.Ar pool/home/bob : .Ar pool/home/bob :
.Bd -literal -compact -offset Ds .Bd -literal -compact -offset Ds

View File

@ -48,6 +48,7 @@
.Fl ?V .Fl ?V
.Nm .Nm
.Cm version .Cm version
.Op Fl j
.Nm .Nm
.Cm subcommand .Cm subcommand
.Op Ar arguments .Op Ar arguments
@ -153,10 +154,14 @@ Displays a help message.
.It Xo .It Xo
.Nm .Nm
.Cm version .Cm version
.Op Fl j
.Xc .Xc
Displays the software version of the Displays the software version of the
.Nm .Nm
userland utility and the zfs kernel module. userland utility and the zfs kernel module.
Use
.Fl j
option to output in JSON format.
.El .El
. .
.Ss Dataset Management .Ss Dataset Management