JSON output support for zpool list

This commit adds support for zpool list command to output the list of
ZFS pools in JSON format using '-j' option.. Information about available
pools is collected in nvlist which is later printed to stdout in JSON
format.

Existing options for zfs list command work with '-j' flag. man page for
zpool list is 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-25 17:59:41 +05:00 committed by Brian Behlendorf
parent eb2b824bde
commit 4e6b3f7e1d
4 changed files with 428 additions and 82 deletions

View File

@ -403,8 +403,8 @@ get_usage(zpool_help_t idx)
return (gettext("\tlabelclear [-f] <vdev>\n")); return (gettext("\tlabelclear [-f] <vdev>\n"));
case HELP_LIST: case HELP_LIST:
return (gettext("\tlist [-gHLpPv] [-o property[,...]] " return (gettext("\tlist [-gHLpPv] [-o property[,...]] "
"[-T d|u] [pool] ... \n" "[-j [--json-int, --json-pool-key-guid]]\n"
"\t [interval [count]]\n")); "\t [-T d|u] [pool] [interval [count]]\n"));
case HELP_PREFETCH: case HELP_PREFETCH:
return (gettext("\tprefetch -t <type> [<type opts>] <pool>\n" return (gettext("\tprefetch -t <type> [<type opts>] <pool>\n"
"\t -t ddt <pool>\n")); "\t -t ddt <pool>\n"));
@ -968,13 +968,14 @@ fill_pool_info(nvlist_t *list, zpool_handle_t *zhp, boolean_t addtype,
static void static void
fill_vdev_info(nvlist_t *list, zpool_handle_t *zhp, char *name, fill_vdev_info(nvlist_t *list, zpool_handle_t *zhp, char *name,
boolean_t as_int) boolean_t addtype, boolean_t as_int)
{ {
boolean_t spare, l2c, log; boolean_t spare, l2c, log;
const char *path, *phys, *devid; const char *path, *phys, *devid;
nvlist_t *nvdev = zpool_find_vdev(zhp, name, &spare, &l2c, &log); nvlist_t *nvdev = zpool_find_vdev(zhp, name, &spare, &l2c, &log);
fnvlist_add_string(list, "name", name); fnvlist_add_string(list, "name", name);
if (addtype)
fnvlist_add_string(list, "type", "VDEV"); fnvlist_add_string(list, "type", "VDEV");
if (nvdev) { if (nvdev) {
const char *type = fnvlist_lookup_string(nvdev, const char *type = fnvlist_lookup_string(nvdev,
@ -6483,9 +6484,13 @@ typedef struct list_cbdata {
boolean_t cb_verbose; boolean_t cb_verbose;
int cb_name_flags; int cb_name_flags;
int cb_namewidth; int cb_namewidth;
boolean_t cb_json;
boolean_t cb_scripted; boolean_t cb_scripted;
zprop_list_t *cb_proplist; zprop_list_t *cb_proplist;
boolean_t cb_literal; boolean_t cb_literal;
nvlist_t *cb_jsobj;
boolean_t cb_json_as_int;
boolean_t cb_json_pool_key_guid;
} list_cbdata_t; } list_cbdata_t;
@ -6546,7 +6551,7 @@ print_header(list_cbdata_t *cb)
* to the described layout. Used by zpool_do_list(). * to the described layout. Used by zpool_do_list().
*/ */
static void static void
print_pool(zpool_handle_t *zhp, list_cbdata_t *cb) collect_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
{ {
zprop_list_t *pl = cb->cb_proplist; zprop_list_t *pl = cb->cb_proplist;
boolean_t first = B_TRUE; boolean_t first = B_TRUE;
@ -6554,6 +6559,20 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
const char *propstr; const char *propstr;
boolean_t right_justify; boolean_t right_justify;
size_t width; size_t width;
zprop_source_t sourcetype = ZPROP_SRC_NONE;
nvlist_t *item, *d, *props;
item = d = props = NULL;
if (cb->cb_json) {
item = fnvlist_alloc();
props = fnvlist_alloc();
d = fnvlist_lookup_nvlist(cb->cb_jsobj, "pools");
if (d == NULL) {
fprintf(stderr, "pools obj not found.\n");
exit(1);
}
fill_pool_info(item, zhp, B_TRUE, cb->cb_json_as_int);
}
for (; pl != NULL; pl = pl->pl_next) { for (; pl != NULL; pl = pl->pl_next) {
@ -6566,7 +6585,7 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
width = cb->cb_namewidth; width = cb->cb_namewidth;
} }
if (!first) { if (!cb->cb_json && !first) {
if (cb->cb_scripted) if (cb->cb_scripted)
(void) fputc('\t', stdout); (void) fputc('\t', stdout);
else else
@ -6578,7 +6597,8 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
right_justify = B_FALSE; right_justify = B_FALSE;
if (pl->pl_prop != ZPROP_USERPROP) { if (pl->pl_prop != ZPROP_USERPROP) {
if (zpool_get_prop(zhp, pl->pl_prop, property, if (zpool_get_prop(zhp, pl->pl_prop, property,
sizeof (property), NULL, cb->cb_literal) != 0) sizeof (property), &sourcetype,
cb->cb_literal) != 0)
propstr = "-"; propstr = "-";
else else
propstr = property; propstr = property;
@ -6589,33 +6609,61 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
zpool_prop_get_feature(zhp, pl->pl_user_prop, property, zpool_prop_get_feature(zhp, pl->pl_user_prop, property,
sizeof (property)) == 0) { sizeof (property)) == 0) {
propstr = property; propstr = property;
sourcetype = ZPROP_SRC_LOCAL;
} else if (zfs_prop_user(pl->pl_user_prop) && } else if (zfs_prop_user(pl->pl_user_prop) &&
zpool_get_userprop(zhp, pl->pl_user_prop, property, zpool_get_userprop(zhp, pl->pl_user_prop, property,
sizeof (property), NULL) == 0) { sizeof (property), &sourcetype) == 0) {
propstr = property; propstr = property;
} else { } else {
propstr = "-"; propstr = "-";
} }
if (cb->cb_json) {
if (pl->pl_prop == ZPOOL_PROP_NAME)
continue;
(void) zprop_nvlist_one_property(
zpool_prop_to_name(pl->pl_prop), propstr,
sourcetype, NULL, NULL, props, cb->cb_json_as_int);
} else {
/* /*
* If this is being called in scripted mode, or if this is the * If this is being called in scripted mode, or if this
* last column and it is left-justified, don't include a width * is the last column and it is left-justified, don't
* format specifier. * include a width format specifier.
*/ */
if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify)) if (cb->cb_scripted || (pl->pl_next == NULL &&
!right_justify))
(void) fputs(propstr, stdout); (void) fputs(propstr, stdout);
else if (right_justify) else if (right_justify)
(void) printf("%*s", (int)width, propstr); (void) printf("%*s", (int)width, propstr);
else else
(void) printf("%-*s", (int)width, propstr); (void) printf("%-*s", (int)width, propstr);
} }
}
if (cb->cb_json) {
fnvlist_add_nvlist(item, "properties", props);
if (cb->cb_json_pool_key_guid) {
char pool_guid[256];
uint64_t guid = fnvlist_lookup_uint64(
zpool_get_config(zhp, NULL),
ZPOOL_CONFIG_POOL_GUID);
snprintf(pool_guid, 256, "%llu",
(u_longlong_t)guid);
fnvlist_add_nvlist(d, pool_guid, item);
} else {
fnvlist_add_nvlist(d, zpool_get_name(zhp),
item);
}
fnvlist_free(props);
fnvlist_free(item);
} else
(void) fputc('\n', stdout); (void) fputc('\n', stdout);
} }
static void static void
print_one_column(zpool_prop_t prop, uint64_t value, const char *str, collect_vdev_prop(zpool_prop_t prop, uint64_t value, const char *str,
boolean_t scripted, boolean_t valid, enum zfs_nicenum_format format) boolean_t scripted, boolean_t valid, enum zfs_nicenum_format format,
boolean_t json, nvlist_t *nvl, boolean_t as_int)
{ {
char propval[64]; char propval[64];
boolean_t fixed; boolean_t fixed;
@ -6665,10 +6713,15 @@ print_one_column(zpool_prop_t prop, uint64_t value, const char *str,
if (!valid) if (!valid)
(void) strlcpy(propval, "-", sizeof (propval)); (void) strlcpy(propval, "-", sizeof (propval));
if (json) {
zprop_nvlist_one_property(zpool_prop_to_name(prop), propval,
ZPROP_SRC_NONE, NULL, NULL, nvl, as_int);
} else {
if (scripted) if (scripted)
(void) printf("\t%s", propval); (void) printf("\t%s", propval);
else else
(void) printf(" %*s", (int)width, propval); (void) printf(" %*s", (int)width, propval);
}
} }
/* /*
@ -6676,15 +6729,17 @@ print_one_column(zpool_prop_t prop, uint64_t value, const char *str,
* not compatible with '-o' <proplist> option * not compatible with '-o' <proplist> option
*/ */
static void static void
print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv, collect_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
list_cbdata_t *cb, int depth, boolean_t isspare) list_cbdata_t *cb, int depth, boolean_t isspare, nvlist_t *item)
{ {
nvlist_t **child; nvlist_t **child;
vdev_stat_t *vs; vdev_stat_t *vs;
uint_t c, children; uint_t c, children = 0;
char *vname; char *vname;
boolean_t scripted = cb->cb_scripted; boolean_t scripted = cb->cb_scripted;
uint64_t islog = B_FALSE; uint64_t islog = B_FALSE;
nvlist_t *props, *ent, *ch, *obj, *l2c, *sp;
props = ent = ch = obj = sp = l2c = NULL;
const char *dashes = "%-*s - - - - " const char *dashes = "%-*s - - - - "
"- - - - -\n"; "- - - - -\n";
@ -6705,13 +6760,21 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
if (strcmp(name, VDEV_TYPE_INDIRECT) == 0) if (strcmp(name, VDEV_TYPE_INDIRECT) == 0)
return; return;
if (cb->cb_json) {
props = fnvlist_alloc();
ent = fnvlist_alloc();
fill_vdev_info(ent, zhp, (char *)name, B_FALSE,
cb->cb_json_as_int);
} else {
if (scripted) if (scripted)
(void) printf("\t%s", name); (void) printf("\t%s", name);
else if (strlen(name) + depth > cb->cb_namewidth) else if (strlen(name) + depth > cb->cb_namewidth)
(void) printf("%*s%s", depth, "", name); (void) printf("%*s%s", depth, "", name);
else else
(void) printf("%*s%s%*s", depth, "", name, (void) printf("%*s%s%*s", depth, "", name,
(int)(cb->cb_namewidth - strlen(name) - depth), ""); (int)(cb->cb_namewidth - strlen(name) -
depth), "");
}
/* /*
* Print the properties for the individual vdevs. Some * Print the properties for the individual vdevs. Some
@ -6719,30 +6782,39 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
* 'toplevel' boolean value is passed to the print_one_column() * 'toplevel' boolean value is passed to the print_one_column()
* to indicate that the value is valid. * to indicate that the value is valid.
*/ */
if (VDEV_STAT_VALID(vs_pspace, c) && vs->vs_pspace) if (VDEV_STAT_VALID(vs_pspace, c) && vs->vs_pspace) {
print_one_column(ZPOOL_PROP_SIZE, vs->vs_pspace, NULL, collect_vdev_prop(ZPOOL_PROP_SIZE, vs->vs_pspace, NULL,
scripted, B_TRUE, format); scripted, B_TRUE, format, cb->cb_json, props,
else cb->cb_json_as_int);
print_one_column(ZPOOL_PROP_SIZE, vs->vs_space, NULL, } else {
scripted, toplevel, format); collect_vdev_prop(ZPOOL_PROP_SIZE, vs->vs_space, NULL,
print_one_column(ZPOOL_PROP_ALLOCATED, vs->vs_alloc, NULL, scripted, toplevel, format, cb->cb_json, props,
scripted, toplevel, format); cb->cb_json_as_int);
print_one_column(ZPOOL_PROP_FREE, vs->vs_space - vs->vs_alloc, }
NULL, scripted, toplevel, format); collect_vdev_prop(ZPOOL_PROP_ALLOCATED, vs->vs_alloc, NULL,
print_one_column(ZPOOL_PROP_CHECKPOINT, scripted, toplevel, format, cb->cb_json, props,
vs->vs_checkpoint_space, NULL, scripted, toplevel, format); cb->cb_json_as_int);
print_one_column(ZPOOL_PROP_EXPANDSZ, vs->vs_esize, NULL, collect_vdev_prop(ZPOOL_PROP_FREE, vs->vs_space - vs->vs_alloc,
scripted, B_TRUE, format); NULL, scripted, toplevel, format, cb->cb_json, props,
print_one_column(ZPOOL_PROP_FRAGMENTATION, cb->cb_json_as_int);
collect_vdev_prop(ZPOOL_PROP_CHECKPOINT,
vs->vs_checkpoint_space, NULL, scripted, toplevel, format,
cb->cb_json, props, cb->cb_json_as_int);
collect_vdev_prop(ZPOOL_PROP_EXPANDSZ, vs->vs_esize, NULL,
scripted, B_TRUE, format, cb->cb_json, props,
cb->cb_json_as_int);
collect_vdev_prop(ZPOOL_PROP_FRAGMENTATION,
vs->vs_fragmentation, NULL, scripted, vs->vs_fragmentation, NULL, scripted,
(vs->vs_fragmentation != ZFS_FRAG_INVALID && toplevel), (vs->vs_fragmentation != ZFS_FRAG_INVALID && toplevel),
format); format, cb->cb_json, props, cb->cb_json_as_int);
cap = (vs->vs_space == 0) ? 0 : cap = (vs->vs_space == 0) ? 0 :
(vs->vs_alloc * 10000 / vs->vs_space); (vs->vs_alloc * 10000 / vs->vs_space);
print_one_column(ZPOOL_PROP_CAPACITY, cap, NULL, collect_vdev_prop(ZPOOL_PROP_CAPACITY, cap, NULL,
scripted, toplevel, format); scripted, toplevel, format, cb->cb_json, props,
print_one_column(ZPOOL_PROP_DEDUPRATIO, 0, NULL, cb->cb_json_as_int);
scripted, toplevel, format); collect_vdev_prop(ZPOOL_PROP_DEDUPRATIO, 0, NULL,
scripted, toplevel, format, cb->cb_json, props,
cb->cb_json_as_int);
state = zpool_state_to_name(vs->vs_state, vs->vs_aux); state = zpool_state_to_name(vs->vs_state, vs->vs_aux);
if (isspare) { if (isspare) {
if (vs->vs_aux == VDEV_AUX_SPARED) if (vs->vs_aux == VDEV_AUX_SPARED)
@ -6750,14 +6822,28 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
else if (vs->vs_state == VDEV_STATE_HEALTHY) else if (vs->vs_state == VDEV_STATE_HEALTHY)
state = "AVAIL"; state = "AVAIL";
} }
print_one_column(ZPOOL_PROP_HEALTH, 0, state, scripted, collect_vdev_prop(ZPOOL_PROP_HEALTH, 0, state, scripted,
B_TRUE, format); B_TRUE, format, cb->cb_json, props, cb->cb_json_as_int);
if (cb->cb_json) {
fnvlist_add_nvlist(ent, "properties", props);
fnvlist_free(props);
} else
(void) fputc('\n', stdout); (void) fputc('\n', stdout);
} }
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
&child, &children) != 0) &child, &children) != 0) {
if (cb->cb_json) {
fnvlist_add_nvlist(item, name, ent);
fnvlist_free(ent);
}
return; return;
}
if (cb->cb_json) {
ch = fnvlist_alloc();
}
/* list the normal vdevs first */ /* list the normal vdevs first */
for (c = 0; c < children; c++) { for (c = 0; c < children; c++) {
@ -6776,14 +6862,28 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
vname = zpool_vdev_name(g_zfs, zhp, child[c], vname = zpool_vdev_name(g_zfs, zhp, child[c],
cb->cb_name_flags | VDEV_NAME_TYPE_ID); cb->cb_name_flags | VDEV_NAME_TYPE_ID);
print_list_stats(zhp, vname, child[c], cb, depth + 2, B_FALSE);
if (name == NULL || cb->cb_json != B_TRUE)
collect_list_stats(zhp, vname, child[c], cb, depth + 2,
B_FALSE, item);
else if (cb->cb_json) {
collect_list_stats(zhp, vname, child[c], cb, depth + 2,
B_FALSE, ch);
}
free(vname); free(vname);
} }
if (cb->cb_json) {
if (!nvlist_empty(ch))
fnvlist_add_nvlist(ent, "vdevs", ch);
fnvlist_free(ch);
}
/* list the classes: 'logs', 'dedup', and 'special' */ /* list the classes: 'logs', 'dedup', and 'special' */
for (uint_t n = 0; n < ARRAY_SIZE(class_name); n++) { for (uint_t n = 0; n < ARRAY_SIZE(class_name); n++) {
boolean_t printed = B_FALSE; boolean_t printed = B_FALSE;
if (cb->cb_json)
obj = fnvlist_alloc();
for (c = 0; c < children; c++) { for (c = 0; c < children; c++) {
const char *bias = NULL; const char *bias = NULL;
const char *type = NULL; const char *type = NULL;
@ -6802,7 +6902,7 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
if (!islog && strcmp(type, VDEV_TYPE_INDIRECT) == 0) if (!islog && strcmp(type, VDEV_TYPE_INDIRECT) == 0)
continue; continue;
if (!printed) { if (!printed && !cb->cb_json) {
/* LINTED E_SEC_PRINTF_VAR_FMT */ /* LINTED E_SEC_PRINTF_VAR_FMT */
(void) printf(dashes, cb->cb_namewidth, (void) printf(dashes, cb->cb_namewidth,
class_name[n]); class_name[n]);
@ -6810,36 +6910,64 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
} }
vname = zpool_vdev_name(g_zfs, zhp, child[c], vname = zpool_vdev_name(g_zfs, zhp, child[c],
cb->cb_name_flags | VDEV_NAME_TYPE_ID); cb->cb_name_flags | VDEV_NAME_TYPE_ID);
print_list_stats(zhp, vname, child[c], cb, depth + 2, collect_list_stats(zhp, vname, child[c], cb, depth + 2,
B_FALSE); B_FALSE, obj);
free(vname); free(vname);
} }
if (cb->cb_json) {
if (!nvlist_empty(obj))
fnvlist_add_nvlist(item, class_name[n], obj);
fnvlist_free(obj);
}
} }
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE, if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
&child, &children) == 0 && children > 0) { &child, &children) == 0 && children > 0) {
if (cb->cb_json) {
l2c = fnvlist_alloc();
} else {
/* LINTED E_SEC_PRINTF_VAR_FMT */ /* LINTED E_SEC_PRINTF_VAR_FMT */
(void) printf(dashes, cb->cb_namewidth, "cache"); (void) printf(dashes, cb->cb_namewidth, "cache");
}
for (c = 0; c < children; c++) { for (c = 0; c < children; c++) {
vname = zpool_vdev_name(g_zfs, zhp, child[c], vname = zpool_vdev_name(g_zfs, zhp, child[c],
cb->cb_name_flags); cb->cb_name_flags);
print_list_stats(zhp, vname, child[c], cb, depth + 2, collect_list_stats(zhp, vname, child[c], cb, depth + 2,
B_FALSE); B_FALSE, l2c);
free(vname); free(vname);
} }
if (cb->cb_json) {
if (!nvlist_empty(l2c))
fnvlist_add_nvlist(item, "l2cache", l2c);
fnvlist_free(l2c);
}
} }
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, &child, if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, &child,
&children) == 0 && children > 0) { &children) == 0 && children > 0) {
if (cb->cb_json) {
sp = fnvlist_alloc();
} else {
/* LINTED E_SEC_PRINTF_VAR_FMT */ /* LINTED E_SEC_PRINTF_VAR_FMT */
(void) printf(dashes, cb->cb_namewidth, "spare"); (void) printf(dashes, cb->cb_namewidth, "spare");
}
for (c = 0; c < children; c++) { for (c = 0; c < children; c++) {
vname = zpool_vdev_name(g_zfs, zhp, child[c], vname = zpool_vdev_name(g_zfs, zhp, child[c],
cb->cb_name_flags); cb->cb_name_flags);
print_list_stats(zhp, vname, child[c], cb, depth + 2, collect_list_stats(zhp, vname, child[c], cb, depth + 2,
B_TRUE); B_TRUE, sp);
free(vname); free(vname);
} }
if (cb->cb_json) {
if (!nvlist_empty(sp))
fnvlist_add_nvlist(item, "spares", sp);
fnvlist_free(sp);
}
}
if (name != NULL && cb->cb_json) {
fnvlist_add_nvlist(item, name, ent);
fnvlist_free(ent);
} }
} }
@ -6849,17 +6977,44 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
static int static int
list_callback(zpool_handle_t *zhp, void *data) list_callback(zpool_handle_t *zhp, void *data)
{ {
nvlist_t *p, *d, *nvdevs;
uint64_t guid;
char pool_guid[256];
const char *pool_name = zpool_get_name(zhp);
list_cbdata_t *cbp = data; list_cbdata_t *cbp = data;
p = d = nvdevs = NULL;
print_pool(zhp, cbp); collect_pool(zhp, cbp);
if (cbp->cb_verbose) { if (cbp->cb_verbose) {
nvlist_t *config, *nvroot; nvlist_t *config, *nvroot;
config = zpool_get_config(zhp, NULL); config = zpool_get_config(zhp, NULL);
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0); &nvroot) == 0);
print_list_stats(zhp, NULL, nvroot, cbp, 0, B_FALSE); if (cbp->cb_json) {
d = fnvlist_lookup_nvlist(cbp->cb_jsobj,
"pools");
if (cbp->cb_json_pool_key_guid) {
guid = fnvlist_lookup_uint64(config,
ZPOOL_CONFIG_POOL_GUID);
snprintf(pool_guid, 256, "%llu",
(u_longlong_t)guid);
p = fnvlist_lookup_nvlist(d, pool_guid);
} else {
p = fnvlist_lookup_nvlist(d, pool_name);
}
nvdevs = fnvlist_alloc();
}
collect_list_stats(zhp, NULL, nvroot, cbp, 0, B_FALSE, nvdevs);
if (cbp->cb_json) {
fnvlist_add_nvlist(p, "vdevs", nvdevs);
if (cbp->cb_json_pool_key_guid)
fnvlist_add_nvlist(d, pool_guid, p);
else
fnvlist_add_nvlist(d, pool_name, p);
fnvlist_add_nvlist(cbp->cb_jsobj, "pools", d);
fnvlist_free(nvdevs);
}
} }
return (0); return (0);
@ -6899,6 +7054,9 @@ get_namewidth_list(zpool_handle_t *zhp, void *data)
* -p Display values in parsable (exact) format. * -p Display values in parsable (exact) format.
* -P Display full path for vdev name. * -P Display full path for vdev name.
* -T Display a timestamp in date(1) or Unix format * -T Display a timestamp in date(1) or Unix format
* -j Display the output in JSON format
* --json-int Display the numbers as integer instead of strings.
* --json-pool-key-guid Set pool GUID as key for pool objects.
* *
* List all pools in the system, whether or not they're healthy. Output space * List all pools in the system, whether or not they're healthy. Output space
* statistics for each one, as well as health status summary. * statistics for each one, as well as health status summary.
@ -6917,10 +7075,19 @@ zpool_do_list(int argc, char **argv)
unsigned long count = 0; unsigned long count = 0;
zpool_list_t *list; zpool_list_t *list;
boolean_t first = B_TRUE; boolean_t first = B_TRUE;
nvlist_t *data = NULL;
current_prop_type = ZFS_TYPE_POOL; current_prop_type = ZFS_TYPE_POOL;
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 */ /* check options */
while ((c = getopt(argc, argv, ":gHLo:pPT:v")) != -1) { while ((c = getopt_long(argc, argv, ":gjHLo:pPT:v", long_options,
NULL)) != -1) {
switch (c) { switch (c) {
case 'g': case 'g':
cb.cb_name_flags |= VDEV_NAME_GUID; cb.cb_name_flags |= VDEV_NAME_GUID;
@ -6940,6 +7107,16 @@ zpool_do_list(int argc, char **argv)
case 'p': case 'p':
cb.cb_literal = B_TRUE; cb.cb_literal = B_TRUE;
break; break;
case 'j':
cb.cb_json = B_TRUE;
break;
case ZPOOL_OPTION_JSON_NUMS_AS_INT:
cb.cb_json_as_int = B_TRUE;
cb.cb_literal = B_TRUE;
break;
case ZPOOL_OPTION_POOL_KEY_GUID:
cb.cb_json_pool_key_guid = B_TRUE;
break;
case 'T': case 'T':
get_timestamp_arg(*optarg); get_timestamp_arg(*optarg);
break; break;
@ -6962,6 +7139,18 @@ zpool_do_list(int argc, char **argv)
argc -= optind; argc -= optind;
argv += 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);
}
get_interval_count(&argc, argv, &interval, &count); get_interval_count(&argc, argv, &interval, &count);
if (zprop_get_list(g_zfs, props, &cb.cb_proplist, ZFS_TYPE_POOL) != 0) if (zprop_get_list(g_zfs, props, &cb.cb_proplist, ZFS_TYPE_POOL) != 0)
@ -6975,18 +7164,43 @@ zpool_do_list(int argc, char **argv)
if (pool_list_count(list) == 0) if (pool_list_count(list) == 0)
break; break;
if (cb.cb_json) {
cb.cb_jsobj = zpool_json_schema(0, 1);
data = fnvlist_alloc();
fnvlist_add_nvlist(cb.cb_jsobj, "pools", data);
fnvlist_free(data);
}
cb.cb_namewidth = 0; cb.cb_namewidth = 0;
(void) pool_list_iter(list, B_FALSE, get_namewidth_list, &cb); (void) pool_list_iter(list, B_FALSE, get_namewidth_list, &cb);
if (timestamp_fmt != NODATE) if (timestamp_fmt != NODATE) {
if (cb.cb_json) {
if (cb.cb_json_as_int) {
fnvlist_add_uint64(cb.cb_jsobj, "time",
time(NULL));
} else {
char ts[128];
get_timestamp(timestamp_fmt, ts, 128);
fnvlist_add_string(cb.cb_jsobj, "time",
ts);
}
} else
print_timestamp(timestamp_fmt); print_timestamp(timestamp_fmt);
}
if (!cb.cb_scripted && (first || cb.cb_verbose)) { if (!cb.cb_scripted && (first || cb.cb_verbose) &&
!cb.cb_json) {
print_header(&cb); print_header(&cb);
first = B_FALSE; first = B_FALSE;
} }
ret = pool_list_iter(list, B_TRUE, list_callback, &cb); ret = pool_list_iter(list, B_TRUE, list_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 (interval == 0) if (interval == 0)
break; break;
@ -6999,7 +7213,8 @@ zpool_do_list(int argc, char **argv)
(void) fsleep(interval); (void) fsleep(interval);
} }
if (argc == 0 && !cb.cb_scripted && pool_list_count(list) == 0) { if (argc == 0 && !cb.cb_scripted && !cb.cb_json &&
pool_list_count(list) == 0) {
(void) printf(gettext("no pools available\n")); (void) printf(gettext("no pools available\n"));
ret = 0; ret = 0;
} }
@ -10886,7 +11101,7 @@ get_callback_vdev(zpool_handle_t *zhp, char *vdevname, void *data)
if (cbp->cb_json) { if (cbp->cb_json) {
if (!nvlist_empty(props)) { if (!nvlist_empty(props)) {
item = fnvlist_alloc(); item = fnvlist_alloc();
fill_vdev_info(item, zhp, vdevname, fill_vdev_info(item, zhp, vdevname, B_TRUE,
cbp->cb_json_as_int); cbp->cb_json_as_int);
fnvlist_add_nvlist(item, "properties", props); fnvlist_add_nvlist(item, "properties", props);
fnvlist_add_nvlist(d, vdevname, item); fnvlist_add_nvlist(d, vdevname, item);

View File

@ -37,5 +37,7 @@
/* Print a timestamp in either Unix or standard format. */ /* Print a timestamp in either Unix or standard format. */
void print_timestamp(uint_t); void print_timestamp(uint_t);
/* Return timestamp in either Unix or standard format in provided buffer */
void get_timestamp(uint_t, char *, int);
#endif /* _STATCOMMON_H */ #endif /* _STATCOMMON_H */

View File

@ -62,3 +62,25 @@ print_timestamp(uint_t timestamp_fmt)
(void) printf("%s\n", dstr); (void) printf("%s\n", dstr);
} }
} }
/*
* Return timestamp as decimal reprentation (in string) of time_t
* value (-T u was specified) or in date(1) format (-T d was specified).
*/
void
get_timestamp(uint_t timestamp_fmt, char *buf, int len)
{
time_t t = time(NULL);
static const char *fmt = NULL;
/* We only need to retrieve this once per invocation */
if (fmt == NULL)
fmt = nl_langinfo(_DATE_FMT);
if (timestamp_fmt == UDATE) {
(void) snprintf(buf, len, "%lld", (longlong_t)t);
} else if (timestamp_fmt == DDATE) {
struct tm tm;
strftime(buf, len, fmt, localtime_r(&t, &tm));
}
}

View File

@ -37,6 +37,7 @@
.Nm zpool .Nm zpool
.Cm list .Cm list
.Op Fl HgLpPv .Op Fl HgLpPv
.Op Fl j Op Ar --json-int, --json-pool-key-guid
.Op Fl o Ar property Ns Oo , Ns Ar property Oc Ns .Op Fl o Ar property Ns Oo , Ns Ar property Oc Ns
.Op Fl T Sy u Ns | Ns Sy d .Op Fl T Sy u Ns | Ns Sy d
.Oo Ar pool Oc Ns .Oo Ar pool Oc Ns
@ -58,6 +59,14 @@ is specified, the command exits after
.Ar count .Ar count
reports are printed. reports are printed.
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl j Op Ar --json-int, --json-pool-key-guid
Display the list of pools in JSON format.
Specify
.Sy --json-int
to display the numbers in integer format instead of strings.
Specify
.Sy --json-pool-key-guid
to set pool GUID as key for pool objects instead of pool names.
.It Fl g .It Fl g
Display vdev GUIDs instead of the normal device names. Display vdev GUIDs instead of the normal device names.
These GUIDs can be used in place of device names for the zpool These GUIDs can be used in place of device names for the zpool
@ -139,6 +148,104 @@ data 23.9G 14.6G 9.30G - 48% 61% 1.00x ONLINE -
sda - - - - - sda - - - - -
sdb - - - 10G - sdb - - - 10G -
sdc - - - - - sdc - - - - -
.Ed
.
.Ss Example 3 : No Displaying expanded space on a device
The following command lists all available pools on the system in JSON
format.
.Bd -literal -compact -offset Ds
.No # Nm zpool Cm list Fl j | Nm jq
{
"output_version": {
"command": "zpool list",
"vers_major": 0,
"vers_minor": 1
},
"pools": {
"tank": {
"name": "tank",
"type": "POOL",
"state": "ONLINE",
"guid": "15220353080205405147",
"txg": "2671",
"spa_version": "5000",
"zpl_version": "5",
"properties": {
"size": {
"value": "111G",
"source": {
"type": "NONE",
"data": "-"
}
},
"allocated": {
"value": "30.8G",
"source": {
"type": "NONE",
"data": "-"
}
},
"free": {
"value": "80.2G",
"source": {
"type": "NONE",
"data": "-"
}
},
"checkpoint": {
"value": "-",
"source": {
"type": "NONE",
"data": "-"
}
},
"expandsize": {
"value": "-",
"source": {
"type": "NONE",
"data": "-"
}
},
"fragmentation": {
"value": "0%",
"source": {
"type": "NONE",
"data": "-"
}
},
"capacity": {
"value": "27%",
"source": {
"type": "NONE",
"data": "-"
}
},
"dedupratio": {
"value": "1.00x",
"source": {
"type": "NONE",
"data": "-"
}
},
"health": {
"value": "ONLINE",
"source": {
"type": "NONE",
"data": "-"
}
},
"altroot": {
"value": "-",
"source": {
"type": "DEFAULT",
"data": "-"
}
}
}
}
}
}
.Ed .Ed
. .
.Sh SEE ALSO .Sh SEE ALSO