aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUmer Saleem <[email protected]>2024-04-24 19:58:17 +0500
committerBrian Behlendorf <[email protected]>2024-08-06 12:47:00 -0700
commiteb2b824bdec95377a4f0612e96814383b87414f6 (patch)
tree9eec9b865bb4bd57901125aee5e78a45b8225e4e
parent5cbdd5ea4f5fc097c90f4f7ca950b3f4f689955d (diff)
JSON output support for zpool get
This commit adds support for zpool get command to output the list of properties for ZFS Pools and VDEVS in JSON format using '-j' option. Man page for zpool get is updated to include '-j' option. Reviewed-by: Tony Hutter <[email protected]> Reviewed-by: Ameer Hamza <[email protected]> Signed-off-by: Umer Saleem <[email protected]> Closes #16217
-rw-r--r--cmd/zfs/zfs_main.c20
-rw-r--r--cmd/zpool/zpool_main.c287
-rw-r--r--include/libzfs.h5
-rw-r--r--lib/libzfs/libzfs_util.c20
-rw-r--r--man/man8/zpool-get.817
5 files changed, 287 insertions, 62 deletions
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index bbc63cc61..51a0fbd43 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -1972,26 +1972,6 @@ fill_dataset_info(nvlist_t *list, zfs_handle_t *zhp, boolean_t as_int)
}
}
-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]...]
diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c
index 99baf88cc..1aaa4d77e 100644
--- a/cmd/zpool/zpool_main.c
+++ b/cmd/zpool/zpool_main.c
@@ -139,7 +139,9 @@ enum zpool_options {
ZPOOL_OPTION_POWER = 1024,
ZPOOL_OPTION_ALLOW_INUSE,
ZPOOL_OPTION_ALLOW_REPLICATION_MISMATCH,
- ZPOOL_OPTION_ALLOW_ASHIFT_MISMATCH
+ ZPOOL_OPTION_ALLOW_ASHIFT_MISMATCH,
+ ZPOOL_OPTION_POOL_KEY_GUID,
+ ZPOOL_OPTION_JSON_NUMS_AS_INT
};
/*
@@ -440,7 +442,8 @@ get_usage(zpool_help_t idx)
case HELP_EVENTS:
return (gettext("\tevents [-vHf [pool] | -c]\n"));
case HELP_GET:
- return (gettext("\tget [-Hp] [-o \"all\" | field[,...]] "
+ return (gettext("\tget [-Hp] [-j [--json-int, "
+ "--json-pool-key-guid]] [-o \"all\" | field[,...]] "
"<\"all\" | property[,...]> <pool> ...\n"));
case HELP_SET:
return (gettext("\tset <property=value> <pool>\n"
@@ -898,6 +901,109 @@ print_spare_list(nvlist_t *nv, int indent)
}
}
+/*
+ * 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 *
+zpool_json_schema(int maj_v, int min_v)
+{
+ char cmd[MAX_CMD_LEN];
+ nvlist_t *sch = fnvlist_alloc();
+ nvlist_t *ov = fnvlist_alloc();
+
+ snprintf(cmd, MAX_CMD_LEN, "zpool %s", current_command->name);
+ 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_pool_info(nvlist_t *list, zpool_handle_t *zhp, boolean_t addtype,
+ boolean_t as_int)
+{
+ nvlist_t *config = zpool_get_config(zhp, NULL);
+ uint64_t guid = fnvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID);
+ uint64_t txg = fnvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG);
+
+ fnvlist_add_string(list, "name", zpool_get_name(zhp));
+ if (addtype)
+ fnvlist_add_string(list, "type", "POOL");
+ fnvlist_add_string(list, "state", zpool_get_state_str(zhp));
+ if (as_int) {
+ if (guid)
+ fnvlist_add_uint64(list, ZPOOL_CONFIG_POOL_GUID, guid);
+ if (txg)
+ fnvlist_add_uint64(list, ZPOOL_CONFIG_POOL_TXG, txg);
+ fnvlist_add_uint64(list, "spa_version", SPA_VERSION);
+ fnvlist_add_uint64(list, "zpl_version", ZPL_VERSION);
+ } else {
+ char value[ZFS_MAXPROPLEN];
+ if (guid) {
+ snprintf(value, ZFS_MAXPROPLEN, "%llu",
+ (u_longlong_t)guid);
+ fnvlist_add_string(list, ZPOOL_CONFIG_POOL_GUID, value);
+ }
+ if (txg) {
+ snprintf(value, ZFS_MAXPROPLEN, "%llu",
+ (u_longlong_t)txg);
+ fnvlist_add_string(list, ZPOOL_CONFIG_POOL_TXG, value);
+ }
+ fnvlist_add_string(list, "spa_version", SPA_VERSION_STRING);
+ fnvlist_add_string(list, "zpl_version", ZPL_VERSION_STRING);
+ }
+}
+
+static void
+fill_vdev_info(nvlist_t *list, zpool_handle_t *zhp, char *name,
+ boolean_t as_int)
+{
+ boolean_t spare, l2c, log;
+ const char *path, *phys, *devid;
+ nvlist_t *nvdev = zpool_find_vdev(zhp, name, &spare, &l2c, &log);
+
+ fnvlist_add_string(list, "name", name);
+ fnvlist_add_string(list, "type", "VDEV");
+ if (nvdev) {
+ const char *type = fnvlist_lookup_string(nvdev,
+ ZPOOL_CONFIG_TYPE);
+ if (type)
+ fnvlist_add_string(list, "vdev_type", type);
+ uint64_t guid = fnvlist_lookup_uint64(nvdev, ZPOOL_CONFIG_GUID);
+ if (guid) {
+ if (as_int) {
+ fnvlist_add_uint64(list, "guid", guid);
+ } else {
+ char buf[ZFS_MAXPROPLEN];
+ snprintf(buf, ZFS_MAXPROPLEN, "%llu",
+ (u_longlong_t)guid);
+ fnvlist_add_string(list, "guid", buf);
+ }
+ }
+
+ if (nvlist_lookup_string(nvdev, ZPOOL_CONFIG_PATH, &path) == 0)
+ fnvlist_add_string(list, "path", path);
+ if (nvlist_lookup_string(nvdev, ZPOOL_CONFIG_PHYS_PATH,
+ &phys) == 0)
+ fnvlist_add_string(list, "phys_path", phys);
+ if (nvlist_lookup_string(nvdev, ZPOOL_CONFIG_DEVID,
+ &devid) == 0)
+ fnvlist_add_string(list, "devid", devid);
+ }
+}
+
static boolean_t
prop_list_contains_feature(nvlist_t *proplist)
{
@@ -10346,35 +10452,6 @@ zpool_do_history(int argc, char **argv)
return (ret);
}
-/*
- * 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 *
-zpool_json_schema(int maj_v, int min_v)
-{
- char cmd[MAX_CMD_LEN];
- nvlist_t *sch = fnvlist_alloc();
- nvlist_t *ov = fnvlist_alloc();
-
- snprintf(cmd, MAX_CMD_LEN, "zpool %s", current_command->name);
- 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);
-
- return (sch);
-}
-
typedef struct ev_opts {
int verbose;
int scripted;
@@ -10769,6 +10846,17 @@ get_callback_vdev(zpool_handle_t *zhp, char *vdevname, void *data)
zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data;
char value[ZFS_MAXPROPLEN];
zprop_source_t srctype;
+ nvlist_t *props, *item, *d;
+ props = item = d = NULL;
+
+ if (cbp->cb_json) {
+ d = fnvlist_lookup_nvlist(cbp->cb_jsobj, "vdevs");
+ if (d == NULL) {
+ fprintf(stderr, "vdevs obj not found.\n");
+ exit(1);
+ }
+ props = fnvlist_alloc();
+ }
for (zprop_list_t *pl = cbp->cb_proplist; pl != NULL;
pl = pl->pl_next) {
@@ -10790,11 +10878,24 @@ get_callback_vdev(zpool_handle_t *zhp, char *vdevname, void *data)
if (zpool_get_vdev_prop(zhp, vdevname, pl->pl_prop,
prop_name, value, sizeof (value), &srctype,
cbp->cb_literal) == 0) {
- zprop_print_one_property(vdevname, cbp, prop_name,
- value, srctype, NULL, NULL);
+ zprop_collect_property(vdevname, cbp, prop_name,
+ value, srctype, NULL, NULL, props);
}
}
+ if (cbp->cb_json) {
+ if (!nvlist_empty(props)) {
+ item = fnvlist_alloc();
+ fill_vdev_info(item, zhp, vdevname,
+ cbp->cb_json_as_int);
+ fnvlist_add_nvlist(item, "properties", props);
+ fnvlist_add_nvlist(d, vdevname, item);
+ fnvlist_add_nvlist(cbp->cb_jsobj, "vdevs", d);
+ fnvlist_free(item);
+ }
+ fnvlist_free(props);
+ }
+
return (0);
}
@@ -10836,8 +10937,18 @@ get_callback(zpool_handle_t *zhp, void *data)
zprop_source_t srctype;
zprop_list_t *pl;
int vid;
+ int err = 0;
+ nvlist_t *props, *item, *d;
+ props = item = d = NULL;
if (cbp->cb_type == ZFS_TYPE_VDEV) {
+ if (cbp->cb_json) {
+ nvlist_t *pool = fnvlist_alloc();
+ fill_pool_info(pool, zhp, B_FALSE, cbp->cb_json_as_int);
+ fnvlist_add_nvlist(cbp->cb_jsobj, "pool", pool);
+ fnvlist_free(pool);
+ }
+
if (strcmp(cbp->cb_vdevs.cb_names[0], "all-vdevs") == 0) {
for_each_vdev(zhp, get_callback_vdev_cb, data);
} else {
@@ -10857,6 +10968,14 @@ get_callback(zpool_handle_t *zhp, void *data)
}
} else {
assert(cbp->cb_type == ZFS_TYPE_POOL);
+ if (cbp->cb_json) {
+ d = fnvlist_lookup_nvlist(cbp->cb_jsobj, "pools");
+ if (d == NULL) {
+ fprintf(stderr, "pools obj not found.\n");
+ exit(1);
+ }
+ props = fnvlist_alloc();
+ }
for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) {
/*
* Skip the special fake placeholder. This will also
@@ -10874,9 +10993,9 @@ get_callback(zpool_handle_t *zhp, void *data)
value, sizeof (value), &srctype) != 0)
continue;
- zprop_print_one_property(zpool_get_name(zhp),
- cbp, pl->pl_user_prop, value, srctype,
- NULL, NULL);
+ err = zprop_collect_property(
+ zpool_get_name(zhp), cbp, pl->pl_user_prop,
+ value, srctype, NULL, NULL, props);
} else if (pl->pl_prop == ZPROP_INVAL &&
(zpool_prop_feature(pl->pl_user_prop) ||
zpool_prop_unsupported(pl->pl_user_prop))) {
@@ -10885,10 +11004,10 @@ get_callback(zpool_handle_t *zhp, void *data)
if (zpool_prop_get_feature(zhp,
pl->pl_user_prop, value,
sizeof (value)) == 0) {
- zprop_print_one_property(
+ err = zprop_collect_property(
zpool_get_name(zhp), cbp,
pl->pl_user_prop, value, srctype,
- NULL, NULL);
+ NULL, NULL, props);
}
} else {
if (zpool_get_prop(zhp, pl->pl_prop, value,
@@ -10896,10 +11015,37 @@ get_callback(zpool_handle_t *zhp, void *data)
cbp->cb_literal) != 0)
continue;
- zprop_print_one_property(zpool_get_name(zhp),
- cbp, zpool_prop_to_name(pl->pl_prop),
- value, srctype, NULL, NULL);
+ err = zprop_collect_property(
+ zpool_get_name(zhp), cbp,
+ zpool_prop_to_name(pl->pl_prop),
+ value, srctype, NULL, NULL, props);
}
+ if (err != 0)
+ return (err);
+ }
+
+ if (cbp->cb_json) {
+ if (!nvlist_empty(props)) {
+ item = fnvlist_alloc();
+ fill_pool_info(item, zhp, B_TRUE,
+ cbp->cb_json_as_int);
+ fnvlist_add_nvlist(item, "properties", props);
+ if (cbp->cb_json_pool_key_guid) {
+ char buf[256];
+ uint64_t guid = fnvlist_lookup_uint64(
+ zpool_get_config(zhp, NULL),
+ ZPOOL_CONFIG_POOL_GUID);
+ snprintf(buf, 256, "%llu",
+ (u_longlong_t)guid);
+ fnvlist_add_nvlist(d, buf, item);
+ } else {
+ const char *name = zpool_get_name(zhp);
+ fnvlist_add_nvlist(d, name, item);
+ }
+ fnvlist_add_nvlist(cbp->cb_jsobj, "pools", d);
+ fnvlist_free(item);
+ }
+ fnvlist_free(props);
}
}
@@ -10914,6 +11060,9 @@ get_callback(zpool_handle_t *zhp, void *data)
* -o List of columns to display. Defaults to
* "name,property,value,source".
* -p Display values in parsable (exact) format.
+ * -j Display output in JSON format.
+ * --json-int Display numbers as integers instead of strings.
+ * --json-pool-key-guid Set pool GUID as key for pool objects.
*
* Get properties of pools in the system. Output space statistics
* for each one as well as other attributes.
@@ -10927,6 +11076,7 @@ zpool_do_get(int argc, char **argv)
int c, i;
char *propstr = NULL;
char *vdev = NULL;
+ nvlist_t *data = NULL;
cb.cb_first = B_TRUE;
@@ -10942,8 +11092,16 @@ zpool_do_get(int argc, char **argv)
cb.cb_vdevs.cb_name_flags |= VDEV_NAME_TYPE_ID;
current_prop_type = cb.cb_type;
+ struct option long_options[] = {
+ {"json-int", no_argument, NULL, ZPOOL_OPTION_JSON_NUMS_AS_INT},
+ {"json-pool-key-guid", no_argument, NULL,
+ ZPOOL_OPTION_POOL_KEY_GUID},
+ {0, 0, 0, 0}
+ };
+
/* check options */
- while ((c = getopt(argc, argv, ":Hpo:")) != -1) {
+ while ((c = getopt_long(argc, argv, ":jHpo:", long_options,
+ NULL)) != -1) {
switch (c) {
case 'p':
cb.cb_literal = B_TRUE;
@@ -10951,6 +11109,18 @@ zpool_do_get(int argc, char **argv)
case 'H':
cb.cb_scripted = B_TRUE;
break;
+ case 'j':
+ cb.cb_json = B_TRUE;
+ cb.cb_jsobj = zpool_json_schema(0, 1);
+ data = fnvlist_alloc();
+ break;
+ case ZPOOL_OPTION_POOL_KEY_GUID:
+ cb.cb_json_pool_key_guid = B_TRUE;
+ break;
+ case ZPOOL_OPTION_JSON_NUMS_AS_INT:
+ cb.cb_json_as_int = B_TRUE;
+ cb.cb_literal = B_TRUE;
+ break;
case 'o':
memset(&cb.cb_columns, 0, sizeof (cb.cb_columns));
i = 0;
@@ -11005,6 +11175,18 @@ found:
argc -= optind;
argv += optind;
+ if (!cb.cb_json && cb.cb_json_as_int) {
+ (void) fprintf(stderr, gettext("'--json-int' only works with"
+ " '-j' option\n"));
+ usage(B_FALSE);
+ }
+
+ if (!cb.cb_json && cb.cb_json_pool_key_guid) {
+ (void) fprintf(stderr, gettext("'json-pool-key-guid' only"
+ " works with '-j' option\n"));
+ usage(B_FALSE);
+ }
+
if (argc < 1) {
(void) fprintf(stderr, gettext("missing property "
"argument\n"));
@@ -11039,6 +11221,10 @@ found:
cb.cb_type = ZFS_TYPE_VDEV;
argc = 1; /* One pool to process */
} else {
+ if (cb.cb_json) {
+ nvlist_free(cb.cb_jsobj);
+ nvlist_free(data);
+ }
fprintf(stderr, gettext("Expected a list of vdevs in"
" \"%s\", but got:\n"), argv[0]);
error_list_unresolved_vdevs(argc - 1, argv + 1,
@@ -11048,6 +11234,10 @@ found:
return (1);
}
} else {
+ if (cb.cb_json) {
+ nvlist_free(cb.cb_jsobj);
+ nvlist_free(data);
+ }
/*
* The first arg isn't the name of a valid pool.
*/
@@ -11070,9 +11260,22 @@ found:
cb.cb_proplist = &fake_name;
}
+ if (cb.cb_json) {
+ if (cb.cb_type == ZFS_TYPE_VDEV)
+ fnvlist_add_nvlist(cb.cb_jsobj, "vdevs", data);
+ else
+ fnvlist_add_nvlist(cb.cb_jsobj, "pools", data);
+ fnvlist_free(data);
+ }
+
ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist, cb.cb_type,
cb.cb_literal, 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)
zprop_free_list(fake_name.pl_next);
else
diff --git a/include/libzfs.h b/include/libzfs.h
index 42054d74b..c44887bcf 100644
--- a/include/libzfs.h
+++ b/include/libzfs.h
@@ -666,6 +666,7 @@ typedef struct zprop_get_cbdata {
vdev_cbdata_t cb_vdevs;
nvlist_t *cb_jsobj;
boolean_t cb_json_as_int;
+ boolean_t cb_json_pool_key_guid;
} zprop_get_cbdata_t;
#define ZFS_SET_NOMOUNT 1
@@ -682,6 +683,10 @@ _LIBZFS_H void zprop_print_one_property(const char *, zprop_get_cbdata_t *,
_LIBZFS_H int zprop_nvlist_one_property(const char *, const char *,
zprop_source_t, const char *, const char *, nvlist_t *, boolean_t);
+_LIBZFS_H int zprop_collect_property(const char *, zprop_get_cbdata_t *,
+ const char *, const char *, zprop_source_t, const char *,
+ const char *, nvlist_t *);
+
/*
* Iterator functions.
*/
diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c
index e2623577f..cba071a1a 100644
--- a/lib/libzfs/libzfs_util.c
+++ b/lib/libzfs/libzfs_util.c
@@ -1590,6 +1590,26 @@ zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp,
(void) printf("\n");
}
+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);
+ }
+}
+
/*
* Given a numeric suffix, convert the value into a number of bits that the
* resulting value must be shifted.
diff --git a/man/man8/zpool-get.8 b/man/man8/zpool-get.8
index 78a39b07d..5384906f1 100644
--- a/man/man8/zpool-get.8
+++ b/man/man8/zpool-get.8
@@ -37,6 +37,7 @@
.Nm zpool
.Cm get
.Op Fl Hp
+.Op Fl j Op Ar --json-int, --json-pool-key-guid
.Op Fl o Ar field Ns Oo , Ns Ar field Oc Ns …
.Sy all Ns | Ns Ar property Ns Oo , Ns Ar property Oc Ns …
.Oo Ar pool Oc Ns …
@@ -44,6 +45,7 @@
.Nm zpool
.Cm get
.Op Fl Hp
+.Op Fl j Op Ar --json-int
.Op Fl o Ar field Ns Oo , Ns Ar field Oc Ns …
.Sy all Ns | Ns Ar property Ns Oo , Ns Ar property Oc Ns …
.Ar pool
@@ -67,6 +69,7 @@
.Nm zpool
.Cm get
.Op Fl Hp
+.Op Fl j Op Ar --json-int, --json-pool-key-guid
.Op Fl o Ar field Ns Oo , Ns Ar field Oc Ns …
.Sy all Ns | Ns Ar property Ns Oo , Ns Ar property Oc Ns …
.Oo Ar pool Oc Ns …
@@ -95,6 +98,14 @@ See the
.Xr zpoolprops 7
manual page for more information on the available pool properties.
.Bl -tag -compact -offset Ds -width "-o field"
+.It Fl j Op Ar --json-int, --json-pool-key-guid
+Display the list of properties in JSON format.
+Specify
+.Sy --json-int
+to display the numbers in integer format instead of strings in JSON output.
+Specify
+.Sy --json-pool-key-guid
+to set pool GUID as key for pool objects instead of pool name.
.It Fl H
Scripted mode.
Do not display headers, and separate fields by a single tab instead of arbitrary
@@ -108,6 +119,7 @@ Display numbers in parsable (exact) values.
.It Xo
.Nm zpool
.Cm get
+.Op Fl j Op Ar --json-int
.Op Fl Hp
.Op Fl o Ar field Ns Oo , Ns Ar field Oc Ns …
.Sy all Ns | Ns Ar property Ns Oo , Ns Ar property Oc Ns …
@@ -145,6 +157,11 @@ See the
.Xr vdevprops 7
manual page for more information on the available pool properties.
.Bl -tag -compact -offset Ds -width "-o field"
+.It Fl j Op Ar --json-int
+Display the list of properties in JSON format.
+Specify
+.Sy --json-int
+to display the numbers in integer format instead of strings in JSON output.
.It Fl H
Scripted mode.
Do not display headers, and separate fields by a single tab instead of arbitrary