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:
parent
6c7d41a643
commit
aa15b60e58
|
@ -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 */
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue