Illumos #1948: zpool list should show more detailed pool info

Reviewed by: Adam Leventhal <ahl@delphix.com>
Reviewed by: Matt Ahrens <matt@delphix.com>
Reviewed by: Eric Schrock <eric.schrock@delphix.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Reviewed by: Albert Lee <trisk@nexenta.com>
Reviewed by: Dan McDonald <danmcd@nexenta.com>
Reviewed by: Garrett D'Amore <garrett@damore.org>
Approved by: Eric Schrock <eric.schrock@delphix.com>

References:
  https://www.illumos.org/issues/1948

Ported by:	Martin Matuska <martin@matuska.org>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #685
This commit is contained in:
Chris Siden
2012-01-23 18:43:32 -08:00
committed by Brian Behlendorf
parent 95fd8c9a7f
commit 1bd201e70d
16 changed files with 383 additions and 67 deletions
+207 -35
View File
@@ -22,7 +22,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2011 by Delphix. All rights reserved.
* Copyright (c) 2012 by Delphix. All rights reserved.
* Copyright (c) 2012 by Frederik Wessels. All rights reserved.
*/
@@ -43,6 +43,7 @@
#include <priv.h>
#include <pwd.h>
#include <zone.h>
#include <zfs_prop.h>
#include <sys/fs/zfs.h>
#include <sys/stat.h>
#include <sys/fm/util.h>
@@ -68,6 +69,7 @@ static int zpool_do_status(int, char **);
static int zpool_do_online(int, char **);
static int zpool_do_offline(int, char **);
static int zpool_do_clear(int, char **);
static int zpool_do_reopen(int, char **);
static int zpool_do_reguid(int, char **);
@@ -131,7 +133,8 @@ typedef enum {
HELP_GET,
HELP_SET,
HELP_SPLIT,
HELP_REGUID
HELP_REGUID,
HELP_REOPEN
} zpool_help_t;
@@ -164,6 +167,7 @@ static zpool_command_t command_table[] = {
{ "online", zpool_do_online, HELP_ONLINE },
{ "offline", zpool_do_offline, HELP_OFFLINE },
{ "clear", zpool_do_clear, HELP_CLEAR },
{ "reopen", zpool_do_reopen, HELP_REOPEN },
{ NULL },
{ "attach", zpool_do_attach, HELP_ATTACH },
{ "detach", zpool_do_detach, HELP_DETACH },
@@ -238,6 +242,8 @@ get_usage(zpool_help_t idx) {
"[new-device]\n"));
case HELP_REMOVE:
return (gettext("\tremove <pool> <device> ...\n"));
case HELP_REOPEN:
return (""); /* Undocumented command */
case HELP_SCRUB:
return (gettext("\tscrub [-s] <pool> ...\n"));
case HELP_STATUS:
@@ -2018,10 +2024,10 @@ error:
}
typedef struct iostat_cbdata {
zpool_list_t *cb_list;
int cb_verbose;
int cb_iteration;
boolean_t cb_verbose;
int cb_namewidth;
int cb_iteration;
zpool_list_t *cb_list;
} iostat_cbdata_t;
static void
@@ -2518,8 +2524,9 @@ zpool_do_iostat(int argc, char **argv)
}
typedef struct list_cbdata {
boolean_t cb_verbose;
int cb_namewidth;
boolean_t cb_scripted;
boolean_t cb_first;
zprop_list_t *cb_proplist;
} list_cbdata_t;
@@ -2527,16 +2534,27 @@ typedef struct list_cbdata {
* Given a list of columns to display, output appropriate headers for each one.
*/
static void
print_header(zprop_list_t *pl)
print_header(list_cbdata_t *cb)
{
zprop_list_t *pl = cb->cb_proplist;
const char *header;
boolean_t first = B_TRUE;
boolean_t right_justify;
size_t width = 0;
for (; pl != NULL; pl = pl->pl_next) {
if (pl->pl_prop == ZPROP_INVAL)
continue;
width = pl->pl_width;
if (first && cb->cb_verbose) {
/*
* Reset the width to accommodate the verbose listing
* of devices.
*/
width = cb->cb_namewidth;
}
if (!first)
(void) printf(" ");
else
@@ -2548,9 +2566,9 @@ print_header(zprop_list_t *pl)
if (pl->pl_next == NULL && !right_justify)
(void) printf("%s", header);
else if (right_justify)
(void) printf("%*s", (int)pl->pl_width, header);
(void) printf("%*s", (int)width, header);
else
(void) printf("%-*s", (int)pl->pl_width, header);
(void) printf("%-*s", (int)width, header);
}
(void) printf("\n");
@@ -2561,17 +2579,28 @@ print_header(zprop_list_t *pl)
* to the described layout.
*/
static void
print_pool(zpool_handle_t *zhp, zprop_list_t *pl, int scripted)
print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
{
zprop_list_t *pl = cb->cb_proplist;
boolean_t first = B_TRUE;
char property[ZPOOL_MAXPROPLEN];
char *propstr;
boolean_t right_justify;
int width;
size_t width;
for (; pl != NULL; pl = pl->pl_next) {
width = pl->pl_width;
if (first && cb->cb_verbose) {
/*
* Reset the width to accommodate the verbose listing
* of devices.
*/
width = cb->cb_namewidth;
}
if (!first) {
if (scripted)
if (cb->cb_scripted)
(void) printf("\t");
else
(void) printf(" ");
@@ -2581,7 +2610,10 @@ print_pool(zpool_handle_t *zhp, zprop_list_t *pl, int scripted)
right_justify = B_FALSE;
if (pl->pl_prop != ZPROP_INVAL) {
if (zpool_get_prop(zhp, pl->pl_prop, property,
if (pl->pl_prop == ZPOOL_PROP_EXPANDSZ &&
zpool_get_prop_int(zhp, pl->pl_prop, NULL) == 0)
propstr = "-";
else if (zpool_get_prop(zhp, pl->pl_prop, property,
sizeof (property), NULL) != 0)
propstr = "-";
else
@@ -2592,24 +2624,118 @@ print_pool(zpool_handle_t *zhp, zprop_list_t *pl, int scripted)
propstr = "-";
}
width = pl->pl_width;
/*
* If this is being called in scripted mode, or if this is the
* last column and it is left-justified, don't include a width
* format specifier.
*/
if (scripted || (pl->pl_next == NULL && !right_justify))
if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify))
(void) printf("%s", propstr);
else if (right_justify)
(void) printf("%*s", width, propstr);
(void) printf("%*s", (int)width, propstr);
else
(void) printf("%-*s", width, propstr);
(void) printf("%-*s", (int)width, propstr);
}
(void) printf("\n");
}
static void
print_one_column(zpool_prop_t prop, uint64_t value, boolean_t scripted)
{
char propval[64];
boolean_t fixed;
size_t width = zprop_width(prop, &fixed, ZFS_TYPE_POOL);
zfs_nicenum(value, propval, sizeof (propval));
if (prop == ZPOOL_PROP_EXPANDSZ && value == 0)
(void) strlcpy(propval, "-", sizeof (propval));
if (scripted)
(void) printf("\t%s", propval);
else
(void) printf(" %*s", (int)width, propval);
}
void
print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
list_cbdata_t *cb, int depth)
{
nvlist_t **child;
vdev_stat_t *vs;
uint_t c, children;
char *vname;
boolean_t scripted = cb->cb_scripted;
verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
(uint64_t **)&vs, &c) == 0);
if (name != NULL) {
if (scripted)
(void) printf("\t%s", name);
else if (strlen(name) + depth > cb->cb_namewidth)
(void) printf("%*s%s", depth, "", name);
else
(void) printf("%*s%s%*s", depth, "", name,
(int)(cb->cb_namewidth - strlen(name) - depth), "");
/* only toplevel vdevs have capacity stats */
if (vs->vs_space == 0) {
if (scripted)
(void) printf("\t-\t-\t-");
else
(void) printf(" - - -");
} else {
print_one_column(ZPOOL_PROP_SIZE, vs->vs_space,
scripted);
print_one_column(ZPOOL_PROP_CAPACITY, vs->vs_alloc,
scripted);
print_one_column(ZPOOL_PROP_FREE,
vs->vs_space - vs->vs_alloc, scripted);
}
print_one_column(ZPOOL_PROP_EXPANDSZ, vs->vs_esize,
scripted);
(void) printf("\n");
}
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
&child, &children) != 0)
return;
for (c = 0; c < children; c++) {
uint64_t ishole = B_FALSE;
if (nvlist_lookup_uint64(child[c],
ZPOOL_CONFIG_IS_HOLE, &ishole) == 0 && ishole)
continue;
vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE);
print_list_stats(zhp, vname, child[c], cb, depth + 2);
free(vname);
}
/*
* Include level 2 ARC devices in iostat output
*/
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
&child, &children) != 0)
return;
if (children > 0) {
(void) printf("%-*s - - - - - "
"-\n", cb->cb_namewidth, "cache");
for (c = 0; c < children; c++) {
vname = zpool_vdev_name(g_zfs, zhp, child[c],
B_FALSE);
print_list_stats(zhp, vname, child[c], cb, depth + 2);
free(vname);
}
}
}
/*
* Generic callback function to list a pool.
*/
@@ -2617,14 +2743,18 @@ int
list_callback(zpool_handle_t *zhp, void *data)
{
list_cbdata_t *cbp = data;
nvlist_t *config;
nvlist_t *nvroot;
if (cbp->cb_first) {
if (!cbp->cb_scripted)
print_header(cbp->cb_proplist);
cbp->cb_first = B_FALSE;
}
config = zpool_get_config(zhp, NULL);
print_pool(zhp, cbp->cb_proplist, cbp->cb_scripted);
print_pool(zhp, cbp);
if (!cbp->cb_verbose)
return (0);
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
print_list_stats(zhp, NULL, nvroot, cbp, 0);
return (0);
}
@@ -2648,12 +2778,15 @@ zpool_do_list(int argc, char **argv)
int ret;
list_cbdata_t cb = { 0 };
static char default_props[] =
"name,size,allocated,free,capacity,dedupratio,health,altroot";
"name,size,allocated,free,capacity,dedupratio,"
"health,altroot";
char *props = default_props;
unsigned long interval = 0, count = 0;
zpool_list_t *list;
boolean_t first = B_TRUE;
/* check options */
while ((c = getopt(argc, argv, ":Ho:T:")) != -1) {
while ((c = getopt(argc, argv, ":Ho:T:v")) != -1) {
switch (c) {
case 'H':
cb.cb_scripted = B_TRUE;
@@ -2664,6 +2797,9 @@ zpool_do_list(int argc, char **argv)
case 'T':
get_timestamp_arg(*optarg);
break;
case 'v':
cb.cb_verbose = B_TRUE;
break;
case ':':
(void) fprintf(stderr, gettext("missing argument for "
"'%c' option\n"), optopt);
@@ -2684,23 +2820,29 @@ zpool_do_list(int argc, char **argv)
if (zprop_get_list(g_zfs, props, &cb.cb_proplist, ZFS_TYPE_POOL) != 0)
usage(B_FALSE);
cb.cb_first = B_TRUE;
if ((list = pool_list_get(argc, argv, &cb.cb_proplist, &ret)) == NULL)
return (1);
if (argc == 0 && !cb.cb_scripted && pool_list_count(list) == 0) {
(void) printf(gettext("no pools available\n"));
zprop_free_list(cb.cb_proplist);
return (0);
}
for (;;) {
pool_list_update(list);
if (pool_list_count(list) == 0)
break;
if (timestamp_fmt != NODATE)
print_timestamp(timestamp_fmt);
ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist,
list_callback, &cb);
if (argc == 0 && cb.cb_first)
(void) fprintf(stderr, gettext("no pools available\n"));
else if (argc && cb.cb_first) {
/* cannot open the given pool */
zprop_free_list(cb.cb_proplist);
return (1);
if (!cb.cb_scripted && (first || cb.cb_verbose)) {
print_header(&cb);
first = B_FALSE;
}
ret = pool_list_iter(list, B_TRUE, list_callback, &cb);
if (interval == 0)
break;
@@ -3293,6 +3435,36 @@ zpool_do_reguid(int argc, char **argv)
}
/*
* zpool reopen <pool>
*
* Reopen the pool so that the kernel can update the sizes of all vdevs.
*
* NOTE: This command is currently undocumented. If the command is ever
* exposed then the appropriate usage() messages will need to be made.
*/
int
zpool_do_reopen(int argc, char **argv)
{
int ret = 0;
zpool_handle_t *zhp;
char *pool;
argc--;
argv++;
if (argc != 1)
return (2);
pool = argv[0];
if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL)
return (1);
ret = zpool_reopen(zhp);
zpool_close(zhp);
return (ret);
}
typedef struct scrub_cbdata {
int cb_type;
int cb_argc;