From d2f3e292dccab23e47ade3c67677a10f353b9e85 Mon Sep 17 00:00:00 2001 From: Richard Yao Date: Sun, 29 Dec 2013 13:40:46 -0500 Subject: [PATCH] Add -gLp to zpool subcommands for alt vdev names MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The following options have been added to the zpool add, iostat, list, status, and split subcommands. The default behavior was not modified, from zfs(8). -g Display vdev GUIDs instead of the normal short device names. These GUIDs can be used in-place of device names for the zpool detach/off‐ line/remove/replace commands. -L Display real paths for vdevs resolving all symbolic links. This can be used to lookup the current block device name regardless of the /dev/disk/ path used to open it. -p Display full paths for vdevs instead of only the last component of the path. This can be used in conjunction with the -L flag. This behavior may also be enabled using the following environment variables. ZPOOL_VDEV_NAME_GUID ZPOOL_VDEV_NAME_FOLLOW_LINKS ZPOOL_VDEV_NAME_PATH This change is based on worked originally started by Richard Yao to add a -g option. Then extended by @ilovezfs to add a -L option for openzfsonosx. Those changes have been merged, re-factored, a -p option added and extended to all relevant zpool subcommands. Original-patch-by: Richard Yao Extended-by: ilovezfs Extended-by: Brian Behlendorf Signed-off-by: ilovezfs Signed-off-by: Brian Behlendorf Closes #2011 Closes #4341 --- cmd/zpool/zpool_main.c | 242 +++++++++++++++++++++++++++------------ include/libzfs.h | 10 +- lib/libzfs/libzfs_pool.c | 58 +++++++--- man/man8/zpool.8 | 201 ++++++++++++++++++++++++++++++-- 4 files changed, 407 insertions(+), 104 deletions(-) diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index 9bf28c29f..420e95730 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -207,7 +207,7 @@ static const char * get_usage(zpool_help_t idx) { switch (idx) { case HELP_ADD: - return (gettext("\tadd [-fn] [-o property=value] " + return (gettext("\tadd [-fgLnp] [-o property=value] " " ...\n")); case HELP_ATTACH: return (gettext("\tattach [-f] [-o property=value] " @@ -237,12 +237,12 @@ get_usage(zpool_help_t idx) { "[-R root] [-F [-n]]\n" "\t [newpool]\n")); case HELP_IOSTAT: - return (gettext("\tiostat [-v] [-T d|u] [-y] [pool] ... " + return (gettext("\tiostat [-gLpvy] [-T d|u] [pool] ... " "[interval [count]]\n")); case HELP_LABELCLEAR: return (gettext("\tlabelclear [-f] \n")); case HELP_LIST: - return (gettext("\tlist [-Hv] [-o property[,...]] " + return (gettext("\tlist [-gHLpv] [-o property[,...]] " "[-T d|u] [pool] ... [interval [count]]\n")); case HELP_OFFLINE: return (gettext("\toffline [-t] ...\n")); @@ -258,8 +258,8 @@ get_usage(zpool_help_t idx) { case HELP_SCRUB: return (gettext("\tscrub [-s] ...\n")); case HELP_STATUS: - return (gettext("\tstatus [-vxD] [-T d|u] [pool] ... [interval " - "[count]]\n")); + return (gettext("\tstatus [-gLpvxD] [-T d|u] [pool] ... " + "[interval [count]]\n")); case HELP_UPGRADE: return (gettext("\tupgrade\n" "\tupgrade -v\n" @@ -272,7 +272,7 @@ get_usage(zpool_help_t idx) { case HELP_SET: return (gettext("\tset \n")); case HELP_SPLIT: - return (gettext("\tsplit [-n] [-R altroot] [-o mntopts]\n" + return (gettext("\tsplit [-gLnp] [-R altroot] [-o mntopts]\n" "\t [-o property=value] " "[ ...]\n")); case HELP_REGUID: @@ -371,7 +371,7 @@ usage(boolean_t requested) void print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent, - boolean_t print_logs) + boolean_t print_logs, int name_flags) { nvlist_t **child; uint_t c, children; @@ -392,9 +392,9 @@ print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent, if ((is_log && !print_logs) || (!is_log && print_logs)) continue; - vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); + vname = zpool_vdev_name(g_zfs, zhp, child[c], name_flags); print_vdev_tree(zhp, vname, child[c], indent + 2, - B_FALSE); + B_FALSE, name_flags); free(vname); } } @@ -502,12 +502,15 @@ add_prop_list_default(const char *propname, char *propval, nvlist_t **props, } /* - * zpool add [-fn] [-o property=value] ... + * zpool add [-fgLnp] [-o property=value] ... * * -f Force addition of devices, even if they appear in use + * -g Display guid for individual vdev name. + * -L Follow links when resolving vdev path name. * -n Do not add the devices, but display the resulting layout if * they were to be added. * -o Set property=value. + * -p Display full path for vdev name. * * Adds the given vdevs to 'pool'. As with create, the bulk of this work is * handled by get_vdev_spec(), which constructs the nvlist needed to pass to @@ -518,6 +521,7 @@ zpool_do_add(int argc, char **argv) { boolean_t force = B_FALSE; boolean_t dryrun = B_FALSE; + int name_flags = 0; int c; nvlist_t *nvroot; char *poolname; @@ -528,11 +532,17 @@ zpool_do_add(int argc, char **argv) char *propval; /* check options */ - while ((c = getopt(argc, argv, "fno:")) != -1) { + while ((c = getopt(argc, argv, "fgLno:p")) != -1) { switch (c) { case 'f': force = B_TRUE; break; + case 'g': + name_flags |= VDEV_NAME_GUID; + break; + case 'L': + name_flags |= VDEV_NAME_FOLLOW_LINKS; + break; case 'n': dryrun = B_TRUE; break; @@ -549,6 +559,9 @@ zpool_do_add(int argc, char **argv) (add_prop_list(optarg, propval, &props, B_TRUE))) usage(B_FALSE); break; + case 'p': + name_flags |= VDEV_NAME_PATH; + break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); @@ -606,15 +619,19 @@ zpool_do_add(int argc, char **argv) "configuration:\n"), zpool_get_name(zhp)); /* print original main pool and new tree */ - print_vdev_tree(zhp, poolname, poolnvroot, 0, B_FALSE); - print_vdev_tree(zhp, NULL, nvroot, 0, B_FALSE); + print_vdev_tree(zhp, poolname, poolnvroot, 0, B_FALSE, + name_flags); + print_vdev_tree(zhp, NULL, nvroot, 0, B_FALSE, name_flags); /* Do the same for the logs */ if (num_logs(poolnvroot) > 0) { - print_vdev_tree(zhp, "logs", poolnvroot, 0, B_TRUE); - print_vdev_tree(zhp, NULL, nvroot, 0, B_TRUE); + print_vdev_tree(zhp, "logs", poolnvroot, 0, B_TRUE, + name_flags); + print_vdev_tree(zhp, NULL, nvroot, 0, B_TRUE, + name_flags); } else if (num_logs(nvroot) > 0) { - print_vdev_tree(zhp, "logs", nvroot, 0, B_TRUE); + print_vdev_tree(zhp, "logs", nvroot, 0, B_TRUE, + name_flags); } /* Do the same for the caches */ @@ -624,7 +641,7 @@ zpool_do_add(int argc, char **argv) (void) printf(gettext("\tcache\n")); for (c = 0; c < l2children; c++) { vname = zpool_vdev_name(g_zfs, NULL, - l2child[c], B_FALSE); + l2child[c], name_flags); (void) printf("\t %s\n", vname); free(vname); } @@ -635,7 +652,7 @@ zpool_do_add(int argc, char **argv) (void) printf(gettext("\tcache\n")); for (c = 0; c < l2children; c++) { vname = zpool_vdev_name(g_zfs, NULL, - l2child[c], B_FALSE); + l2child[c], name_flags); (void) printf("\t %s\n", vname); free(vname); } @@ -1082,9 +1099,9 @@ zpool_do_create(int argc, char **argv) (void) printf(gettext("would create '%s' with the " "following layout:\n\n"), poolname); - print_vdev_tree(NULL, poolname, nvroot, 0, B_FALSE); + print_vdev_tree(NULL, poolname, nvroot, 0, B_FALSE, 0); if (num_logs(nvroot) > 0) - print_vdev_tree(NULL, "logs", nvroot, 0, B_TRUE); + print_vdev_tree(NULL, "logs", nvroot, 0, B_TRUE, 0); ret = 0; } else { @@ -1311,13 +1328,15 @@ zpool_do_export(int argc, char **argv) * name column. */ static int -max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max) +max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max, + int name_flags) { - char *name = zpool_vdev_name(g_zfs, zhp, nv, B_TRUE); + char *name; nvlist_t **child; uint_t c, children; int ret; + name = zpool_vdev_name(g_zfs, zhp, nv, name_flags | VDEV_NAME_TYPE_ID); if (strlen(name) + depth > max) max = strlen(name) + depth; @@ -1327,7 +1346,7 @@ max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max) &child, &children) == 0) { for (c = 0; c < children; c++) if ((ret = max_width(zhp, child[c], depth + 2, - max)) > max) + max, name_flags)) > max) max = ret; } @@ -1335,7 +1354,7 @@ max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max) &child, &children) == 0) { for (c = 0; c < children; c++) if ((ret = max_width(zhp, child[c], depth + 2, - max)) > max) + max, name_flags)) > max) max = ret; } @@ -1343,11 +1362,10 @@ max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max) &child, &children) == 0) { for (c = 0; c < children; c++) if ((ret = max_width(zhp, child[c], depth + 2, - max)) > max) + max, name_flags)) > max) max = ret; } - return (max); } @@ -1399,9 +1417,9 @@ find_spare(zpool_handle_t *zhp, void *data) /* * Print out configuration state as requested by status_callback. */ -void +static void print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, - int namewidth, int depth, boolean_t isspare) + int namewidth, int depth, boolean_t isspare, int name_flags) { nvlist_t **child; uint_t c, children; @@ -1537,20 +1555,21 @@ print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, &ishole); if (islog || ishole) continue; - vname = zpool_vdev_name(g_zfs, zhp, child[c], B_TRUE); + vname = zpool_vdev_name(g_zfs, zhp, child[c], + name_flags | VDEV_NAME_TYPE_ID); print_status_config(zhp, vname, child[c], - namewidth, depth + 2, isspare); + namewidth, depth + 2, isspare, name_flags); free(vname); } } - /* * Print the configuration of an exported pool. Iterate over all vdevs in the * pool, printing out the name and status for each one. */ -void -print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth) +static void +print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth, + int name_flags) { nvlist_t **child; uint_t c, children; @@ -1615,8 +1634,10 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth) if (is_log) continue; - vname = zpool_vdev_name(g_zfs, NULL, child[c], B_TRUE); - print_import_config(vname, child[c], namewidth, depth + 2); + vname = zpool_vdev_name(g_zfs, NULL, child[c], + name_flags | VDEV_NAME_TYPE_ID); + print_import_config(vname, child[c], namewidth, depth + 2, + name_flags); free(vname); } @@ -1624,7 +1645,8 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth) &child, &children) == 0) { (void) printf(gettext("\tcache\n")); for (c = 0; c < children; c++) { - vname = zpool_vdev_name(g_zfs, NULL, child[c], B_FALSE); + vname = zpool_vdev_name(g_zfs, NULL, child[c], + name_flags); (void) printf("\t %s\n", vname); free(vname); } @@ -1634,7 +1656,8 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth) &child, &children) == 0) { (void) printf(gettext("\tspares\n")); for (c = 0; c < children; c++) { - vname = zpool_vdev_name(g_zfs, NULL, child[c], B_FALSE); + vname = zpool_vdev_name(g_zfs, NULL, child[c], + name_flags); (void) printf("\t %s\n", vname); free(vname); } @@ -1650,7 +1673,8 @@ print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth) * works because only the top level vdev is marked "is_log" */ static void -print_logs(zpool_handle_t *zhp, nvlist_t *nv, int namewidth, boolean_t verbose) +print_logs(zpool_handle_t *zhp, nvlist_t *nv, int namewidth, boolean_t verbose, + int name_flags) { uint_t c, children; nvlist_t **child; @@ -1669,12 +1693,14 @@ print_logs(zpool_handle_t *zhp, nvlist_t *nv, int namewidth, boolean_t verbose) &is_log); if (!is_log) continue; - name = zpool_vdev_name(g_zfs, zhp, child[c], B_TRUE); + name = zpool_vdev_name(g_zfs, zhp, child[c], + name_flags | VDEV_NAME_TYPE_ID); if (verbose) print_status_config(zhp, name, child[c], namewidth, - 2, B_FALSE); + 2, B_FALSE, name_flags); else - print_import_config(name, child[c], namewidth, 2); + print_import_config(name, child[c], namewidth, 2, + name_flags); free(name); } } @@ -1923,13 +1949,13 @@ show_import(nvlist_t *config) (void) printf(gettext(" config:\n\n")); - namewidth = max_width(NULL, nvroot, 0, 0); + namewidth = max_width(NULL, nvroot, 0, 0, 0); if (namewidth < 10) namewidth = 10; - print_import_config(name, nvroot, namewidth, 0); + print_import_config(name, nvroot, namewidth, 0, 0); if (num_logs(nvroot) > 0) - print_logs(NULL, nvroot, namewidth, B_FALSE); + print_logs(NULL, nvroot, namewidth, B_FALSE, 0); if (reason == ZPOOL_STATUS_BAD_GUID_SUM) { (void) printf(gettext("\n\tAdditional devices are known to " @@ -2438,6 +2464,7 @@ error: typedef struct iostat_cbdata { boolean_t cb_verbose; + int cb_name_flags; int cb_namewidth; int cb_iteration; zpool_list_t *cb_list; @@ -2560,7 +2587,8 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, if (ishole || islog) continue; - vname = zpool_vdev_name(g_zfs, zhp, newchild[c], B_FALSE); + vname = zpool_vdev_name(g_zfs, zhp, newchild[c], + cb->cb_name_flags); print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL, newchild[c], cb, depth + 2); free(vname); @@ -2581,7 +2609,7 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, if (islog) { vname = zpool_vdev_name(g_zfs, zhp, newchild[c], - B_FALSE); + cb->cb_name_flags); print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL, newchild[c], cb, depth + 2); @@ -2607,7 +2635,7 @@ print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, "-\n", cb->cb_namewidth, "cache"); for (c = 0; c < children; c++) { vname = zpool_vdev_name(g_zfs, zhp, newchild[c], - B_FALSE); + cb->cb_name_flags); print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL, newchild[c], cb, depth + 2); free(vname); @@ -2700,7 +2728,7 @@ get_namewidth(zpool_handle_t *zhp, void *data) cb->cb_namewidth = strlen(zpool_get_name(zhp)); else cb->cb_namewidth = max_width(zhp, nvroot, 0, - cb->cb_namewidth); + cb->cb_namewidth, cb->cb_name_flags); } /* @@ -2800,8 +2828,11 @@ get_timestamp_arg(char c) } /* - * zpool iostat [-v] [-T d|u] [pool] ... [interval [count]] + * zpool iostat [-gLpv] [-T d|u] [pool] ... [interval [count]] * + * -g Display guid for individual vdev name. + * -L Follow links when resolving vdev path name. + * -p Display full path for vdev name. * -v Display statistics for individual vdevs * -T Display a timestamp in date(1) or Unix format * @@ -2821,11 +2852,23 @@ zpool_do_iostat(int argc, char **argv) zpool_list_t *list; boolean_t verbose = B_FALSE; boolean_t omit_since_boot = B_FALSE; - iostat_cbdata_t cb; + boolean_t guid = B_FALSE; + boolean_t follow_links = B_FALSE; + boolean_t full_name = B_FALSE; + iostat_cbdata_t cb = { 0 }; /* check options */ - while ((c = getopt(argc, argv, "T:vy")) != -1) { + while ((c = getopt(argc, argv, "gLpT:vy")) != -1) { switch (c) { + case 'g': + guid = B_TRUE; + break; + case 'L': + follow_links = B_TRUE; + break; + case 'p': + full_name = B_TRUE; + break; case 'T': get_timestamp_arg(*optarg); break; @@ -2870,6 +2913,12 @@ zpool_do_iostat(int argc, char **argv) */ cb.cb_list = list; cb.cb_verbose = verbose; + if (guid) + cb.cb_name_flags |= VDEV_NAME_GUID; + if (follow_links) + cb.cb_name_flags |= VDEV_NAME_FOLLOW_LINKS; + if (full_name) + cb.cb_name_flags |= VDEV_NAME_PATH; cb.cb_iteration = 0; cb.cb_namewidth = 0; @@ -2953,6 +3002,7 @@ zpool_do_iostat(int argc, char **argv) typedef struct list_cbdata { boolean_t cb_verbose; + int cb_name_flags; int cb_namewidth; boolean_t cb_scripted; zprop_list_t *cb_proplist; @@ -3187,7 +3237,8 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv, continue; } - vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); + vname = zpool_vdev_name(g_zfs, zhp, child[c], + cb->cb_name_flags); print_list_stats(zhp, vname, child[c], cb, depth + 2); free(vname); } @@ -3199,7 +3250,8 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv, if (nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, &islog) != 0 || !islog) continue; - vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); + vname = zpool_vdev_name(g_zfs, zhp, child[c], + cb->cb_name_flags); print_list_stats(zhp, vname, child[c], cb, depth + 2); free(vname); } @@ -3210,7 +3262,8 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv, /* LINTED E_SEC_PRINTF_VAR_FMT */ (void) printf(dashes, cb->cb_namewidth, "cache"); for (c = 0; c < children; c++) { - vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); + vname = zpool_vdev_name(g_zfs, zhp, child[c], + cb->cb_name_flags); print_list_stats(zhp, vname, child[c], cb, depth + 2); free(vname); } @@ -3221,7 +3274,8 @@ print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv, /* LINTED E_SEC_PRINTF_VAR_FMT */ (void) printf(dashes, cb->cb_namewidth, "spare"); for (c = 0; c < children; c++) { - vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); + vname = zpool_vdev_name(g_zfs, zhp, child[c], + cb->cb_name_flags); print_list_stats(zhp, vname, child[c], cb, depth + 2); free(vname); } @@ -3253,13 +3307,16 @@ list_callback(zpool_handle_t *zhp, void *data) } /* - * zpool list [-H] [-o prop[,prop]*] [-T d|u] [pool] ... [interval [count]] + * zpool list [-gHLp] [-o prop[,prop]*] [-T d|u] [pool] ... [interval [count]] * + * -g Display guid for individual vdev name. * -H Scripted mode. Don't display headers, and separate properties * by a single tab. + * -L Follow links when resolving vdev path name. * -o List of properties to display. Defaults to * "name,size,allocated,free,expandsize,fragmentation,capacity," * "dedupratio,health,altroot" + * -p Display full path for vdev name. * -T Display a timestamp in date(1) or Unix format * * List all pools in the system, whether or not they're healthy. Output space @@ -3280,14 +3337,23 @@ zpool_do_list(int argc, char **argv) boolean_t first = B_TRUE; /* check options */ - while ((c = getopt(argc, argv, ":Ho:T:v")) != -1) { + while ((c = getopt(argc, argv, ":gHLo:pT:v")) != -1) { switch (c) { + case 'g': + cb.cb_name_flags |= VDEV_NAME_GUID; + break; case 'H': cb.cb_scripted = B_TRUE; break; + case 'L': + cb.cb_name_flags |= VDEV_NAME_FOLLOW_LINKS; + break; case 'o': props = optarg; break; + case 'p': + cb.cb_name_flags |= VDEV_NAME_PATH; + break; case 'T': get_timestamp_arg(*optarg); break; @@ -3543,13 +3609,16 @@ zpool_do_detach(int argc, char **argv) } /* - * zpool split [-n] [-o prop=val] ... + * zpool split [-gLnp] [-o prop=val] ... * [-o mntopt] ... * [-R altroot] [ ...] * + * -g Display guid for individual vdev name. + * -L Follow links when resolving vdev path name. * -n Do not split the pool, but display the resulting layout if * it were to be split. * -o Set property=value, or set mount options. + * -p Display full path for vdev name. * -R Mount the split-off pool under an alternate root. * * Splits the named pool and gives it the new pool name. Devices to be split @@ -3573,10 +3642,17 @@ zpool_do_split(int argc, char **argv) flags.dryrun = B_FALSE; flags.import = B_FALSE; + flags.name_flags = 0; /* check options */ - while ((c = getopt(argc, argv, ":R:no:")) != -1) { + while ((c = getopt(argc, argv, ":gLR:no:p")) != -1) { switch (c) { + case 'g': + flags.name_flags |= VDEV_NAME_GUID; + break; + case 'L': + flags.name_flags |= VDEV_NAME_FOLLOW_LINKS; + break; case 'R': flags.import = B_TRUE; if (add_prop_list( @@ -3604,6 +3680,9 @@ zpool_do_split(int argc, char **argv) mntopts = optarg; } break; + case 'p': + flags.name_flags |= VDEV_NAME_PATH; + break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); @@ -3651,7 +3730,8 @@ zpool_do_split(int argc, char **argv) if (flags.dryrun) { (void) printf(gettext("would create '%s' with the " "following layout:\n\n"), newpool); - print_vdev_tree(NULL, newpool, config, 0, B_FALSE); + print_vdev_tree(NULL, newpool, config, 0, B_FALSE, + flags.name_flags); } nvlist_free(config); } @@ -4057,6 +4137,7 @@ zpool_do_scrub(int argc, char **argv) typedef struct status_cbdata { int cb_count; + int cb_name_flags; boolean_t cb_allpools; boolean_t cb_verbose; boolean_t cb_explain; @@ -4213,7 +4294,7 @@ print_error_log(zpool_handle_t *zhp) static void print_spares(zpool_handle_t *zhp, nvlist_t **spares, uint_t nspares, - int namewidth) + int namewidth, int name_flags) { uint_t i; char *name; @@ -4224,16 +4305,16 @@ print_spares(zpool_handle_t *zhp, nvlist_t **spares, uint_t nspares, (void) printf(gettext("\tspares\n")); for (i = 0; i < nspares; i++) { - name = zpool_vdev_name(g_zfs, zhp, spares[i], B_FALSE); + name = zpool_vdev_name(g_zfs, zhp, spares[i], name_flags); print_status_config(zhp, name, spares[i], - namewidth, 2, B_TRUE); + namewidth, 2, B_TRUE, name_flags); free(name); } } static void print_l2cache(zpool_handle_t *zhp, nvlist_t **l2cache, uint_t nl2cache, - int namewidth) + int namewidth, int name_flags) { uint_t i; char *name; @@ -4244,9 +4325,9 @@ print_l2cache(zpool_handle_t *zhp, nvlist_t **l2cache, uint_t nl2cache, (void) printf(gettext("\tcache\n")); for (i = 0; i < nl2cache; i++) { - name = zpool_vdev_name(g_zfs, zhp, l2cache[i], B_FALSE); + name = zpool_vdev_name(g_zfs, zhp, l2cache[i], name_flags); print_status_config(zhp, name, l2cache[i], - namewidth, 2, B_FALSE); + namewidth, 2, B_FALSE, name_flags); free(name); } } @@ -4588,7 +4669,7 @@ status_callback(zpool_handle_t *zhp, void *data) ZPOOL_CONFIG_SCAN_STATS, (uint64_t **)&ps, &c); print_scan_status(ps); - namewidth = max_width(zhp, nvroot, 0, 0); + namewidth = max_width(zhp, nvroot, 0, 0, cbp->cb_name_flags); if (namewidth < 10) namewidth = 10; @@ -4596,17 +4677,20 @@ status_callback(zpool_handle_t *zhp, void *data) (void) printf(gettext("\t%-*s %-8s %5s %5s %5s\n"), namewidth, "NAME", "STATE", "READ", "WRITE", "CKSUM"); print_status_config(zhp, zpool_get_name(zhp), nvroot, - namewidth, 0, B_FALSE); + namewidth, 0, B_FALSE, cbp->cb_name_flags); if (num_logs(nvroot) > 0) - print_logs(zhp, nvroot, namewidth, B_TRUE); + print_logs(zhp, nvroot, namewidth, B_TRUE, + cbp->cb_name_flags); if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE, &l2cache, &nl2cache) == 0) - print_l2cache(zhp, l2cache, nl2cache, namewidth); + print_l2cache(zhp, l2cache, nl2cache, namewidth, + cbp->cb_name_flags); if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, &spares, &nspares) == 0) - print_spares(zhp, spares, nspares, namewidth); + print_spares(zhp, spares, nspares, namewidth, + cbp->cb_name_flags); if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRCOUNT, &nerr) == 0) { @@ -4654,8 +4738,11 @@ status_callback(zpool_handle_t *zhp, void *data) } /* - * zpool status [-vx] [-T d|u] [pool] ... [interval [count]] + * zpool status [-gLpvx] [-T d|u] [pool] ... [interval [count]] * + * -g Display guid for individual vdev name. + * -L Follow links when resolving vdev path name. + * -p Display full path for vdev name. * -v Display complete error logs * -x Display only pools with potential problems * -D Display dedup status (undocumented) @@ -4672,8 +4759,17 @@ zpool_do_status(int argc, char **argv) status_cbdata_t cb = { 0 }; /* check options */ - while ((c = getopt(argc, argv, "vxDT:")) != -1) { + while ((c = getopt(argc, argv, "gLpvxDT:")) != -1) { switch (c) { + case 'g': + cb.cb_name_flags |= VDEV_NAME_GUID; + break; + case 'L': + cb.cb_name_flags |= VDEV_NAME_FOLLOW_LINKS; + break; + case 'p': + cb.cb_name_flags |= VDEV_NAME_PATH; + break; case 'v': cb.cb_verbose = B_TRUE; break; diff --git a/include/libzfs.h b/include/libzfs.h index 82e8647df..b8419e2e9 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -248,6 +248,7 @@ typedef struct splitflags { /* after splitting, import the pool */ int import : 1; + int name_flags; } splitflags_t; /* @@ -406,8 +407,15 @@ struct zfs_cmd; extern const char *zfs_history_event_names[]; +typedef enum { + VDEV_NAME_PATH = 1 << 0, + VDEV_NAME_GUID = 1 << 1, + VDEV_NAME_FOLLOW_LINKS = 1 << 2, + VDEV_NAME_TYPE_ID = 1 << 3, +} vdev_name_t; + extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *, - boolean_t verbose); + int name_flags); extern int zpool_upgrade(zpool_handle_t *, uint64_t); extern int zpool_get_history(zpool_handle_t *, nvlist_t **); extern int zpool_history_unpack(char *, uint64_t, uint64_t *, diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index 9a276d061..9fc4bfc5b 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -1393,8 +1393,7 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "device '%s' contains an EFI label and " "cannot be used on root pools."), - zpool_vdev_name(hdl, NULL, spares[s], - B_FALSE)); + zpool_vdev_name(hdl, NULL, spares[s], 0)); return (zfs_error(hdl, EZFS_POOL_NOTSUP, msg)); } } @@ -1715,7 +1714,7 @@ print_vdev_tree(libzfs_handle_t *hdl, const char *name, nvlist_t *nv, return; for (c = 0; c < children; c++) { - vname = zpool_vdev_name(hdl, NULL, child[c], B_TRUE); + vname = zpool_vdev_name(hdl, NULL, child[c], VDEV_NAME_TYPE_ID); print_vdev_tree(hdl, vname, child[c], indent + 2); free(vname); } @@ -2704,7 +2703,7 @@ zpool_vdev_attach(zpool_handle_t *zhp, verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL), ZPOOL_CONFIG_VDEV_TREE, &config_root) == 0); - if ((newname = zpool_vdev_name(NULL, NULL, child[0], B_FALSE)) == NULL) + if ((newname = zpool_vdev_name(NULL, NULL, child[0], 0)) == NULL) return (-1); /* @@ -2895,11 +2894,11 @@ find_vdev_entry(zpool_handle_t *zhp, nvlist_t **mchild, uint_t mchildren, for (mc = 0; mc < mchildren; mc++) { uint_t sc; char *mpath = zpool_vdev_name(zhp->zpool_hdl, zhp, - mchild[mc], B_FALSE); + mchild[mc], 0); for (sc = 0; sc < schildren; sc++) { char *spath = zpool_vdev_name(zhp->zpool_hdl, zhp, - schild[sc], B_FALSE); + schild[sc], 0); boolean_t result = (strcmp(mpath, spath) == 0); free(spath); @@ -3442,21 +3441,34 @@ strip_partition(libzfs_handle_t *hdl, char *path) */ char * zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv, - boolean_t verbose) + int name_flags) { - char *path, *devid, *type; + char *path, *devid, *type, *env; uint64_t value; char buf[PATH_BUF_LEN]; char tmpbuf[PATH_BUF_LEN]; vdev_stat_t *vs; uint_t vsc; - if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, - &value) == 0) { - verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, - &value) == 0); - (void) snprintf(buf, sizeof (buf), "%llu", - (u_longlong_t)value); + env = getenv("ZPOOL_VDEV_NAME_PATH"); + if (env && (strtoul(env, NULL, 0) > 0 || + !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2))) + name_flags |= VDEV_NAME_PATH; + + env = getenv("ZPOOL_VDEV_NAME_GUID"); + if (env && (strtoul(env, NULL, 0) > 0 || + !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2))) + name_flags |= VDEV_NAME_GUID; + + env = getenv("ZPOOL_VDEV_NAME_FOLLOW_LINKS"); + if (env && (strtoul(env, NULL, 0) > 0 || + !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2))) + name_flags |= VDEV_NAME_FOLLOW_LINKS; + + if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, &value) == 0 || + name_flags & VDEV_NAME_GUID) { + nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &value); + (void) snprintf(buf, sizeof (buf), "%llu", (u_longlong_t)value); path = buf; } else if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0) { /* @@ -3497,11 +3509,21 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv, devid_str_free(newdevid); } + if (name_flags & VDEV_NAME_FOLLOW_LINKS) { + char *rp = realpath(path, NULL); + if (rp) { + strlcpy(buf, rp, sizeof (buf)); + path = buf; + free(rp); + } + } + /* * For a block device only use the name. */ verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0); - if (strcmp(type, VDEV_TYPE_DISK) == 0) { + if ((strcmp(type, VDEV_TYPE_DISK) == 0) && + !(name_flags & VDEV_NAME_PATH)) { path = strrchr(path, '/'); path++; } @@ -3509,8 +3531,8 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv, /* * Remove the partition from the path it this is a whole disk. */ - if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, - &value) == 0 && value) { + if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, &value) + == 0 && value && !(name_flags & VDEV_NAME_PATH)) { return (strip_partition(hdl, path)); } } else { @@ -3532,7 +3554,7 @@ zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv, * We identify each top-level vdev by using a * naming convention. */ - if (verbose) { + if (name_flags & VDEV_NAME_TYPE_ID) { uint64_t id; verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ID, diff --git a/man/man8/zpool.8 b/man/man8/zpool.8 index f16826ae2..db435e32e 100644 --- a/man/man8/zpool.8 +++ b/man/man8/zpool.8 @@ -26,7 +26,7 @@ zpool \- configures ZFS storage pools .LP .nf -\fBzpool add\fR [\fB-fn\fR] [\fB-o\fR \fIproperty=value\fR] \fIpool\fR \fIvdev\fR ... +\fBzpool add\fR [\fB-fgLnp\fR] [\fB-o\fR \fIproperty=value\fR] \fIpool\fR \fIvdev\fR ... .fi .LP @@ -94,7 +94,7 @@ zpool \- configures ZFS storage pools .LP .nf -\fBzpool iostat\fR [\fB-T\fR d | u ] [\fB-v\fR] [\fB-y\fR] [\fIpool\fR] ... [\fIinterval\fR[\fIcount\fR]] +\fBzpool iostat\fR [\fB-T\fR d | u ] [\fB-gLpvy\fR] [\fIpool\fR] ... [\fIinterval\fR[\fIcount\fR]] .fi .LP @@ -104,7 +104,7 @@ zpool \- configures ZFS storage pools .LP .nf -\fBzpool list\fR [\fB-T\fR d | u ] [\fB-Hv\fR] [\fB-o\fR \fIproperty\fR[,...]] [\fIpool\fR] ... +\fBzpool list\fR [\fB-T\fR d | u ] [\fB-HgLpv\fR] [\fB-o\fR \fIproperty\fR[,...]] [\fIpool\fR] ... [\fIinterval\fR[\fIcount\fR]] .fi @@ -150,12 +150,12 @@ zpool \- configures ZFS storage pools .LP .nf -\fBzpool split\fR [\fB-n\fR] [\fB-R\fR \fIaltroot\fR] [\fB-o\fR \fIproperty=value\fR] \fIpool\fR \fInewpool\fR [\fIdevice\fR ...] +\fBzpool split\fR [\fB-gLnp\fR] [\fB-R\fR \fIaltroot\fR] [\fB-o\fR \fIproperty=value\fR] \fIpool\fR \fInewpool\fR [\fIdevice\fR ...] .fi .LP .nf -\fBzpool status\fR [\fB-xvD\fR] [\fB-T\fR d | u] [\fIpool\fR] ... [\fIinterval\fR [\fIcount\fR]] +\fBzpool status\fR [\fB-gLpvxD\fR] [\fB-T\fR d | u] [\fIpool\fR] ... [\fIinterval\fR [\fIcount\fR]] .fi .LP @@ -836,7 +836,7 @@ Displays a help message. .ne 2 .mk .na -\fB\fBzpool add\fR [\fB-fn\fR] [\fB-o\fR \fIproperty=value\fR] \fIpool\fR \fIvdev\fR ...\fR +\fB\fBzpool add\fR [\fB-fgLnp\fR] [\fB-o\fR \fIproperty=value\fR] \fIpool\fR \fIvdev\fR ...\fR .ad .sp .6 .RS 4n @@ -852,6 +852,28 @@ Adds the specified virtual devices to the given pool. The \fIvdev\fR specificati Forces use of \fBvdev\fRs, even if they appear in use or specify a conflicting replication level. Not all devices can be overridden in this manner. .RE +.sp +.ne 2 +.mk +.na +\fB\fB-g\fR\fR +.ad +.RS 6n +.rt +Display vdev GUIDs instead of the normal device names. These GUIDs can be used in place of device names for the zpool detach/offline/remove/replace commands. +.RE + +.sp +.ne 2 +.mk +.na +\fB\fB-L\fR\fR +.ad +.RS 6n +.rt +Display real paths for vdevs resolving all symbolic links. This can be used to look up the current block device name regardless of the /dev/disk/ path used to open it. +.RE + .sp .ne 2 .mk @@ -863,6 +885,17 @@ Forces use of \fBvdev\fRs, even if they appear in use or specify a conflicting r Displays the configuration that would be used without actually adding the \fBvdev\fRs. The actual pool creation can still fail due to insufficient privileges or device sharing. .RE +.sp +.ne 2 +.mk +.na +\fB\fB-p\fR\fR +.ad +.RS 6n +.rt +Display full paths for vdevs instead of only the last component of the path. This can be used in conjunction with the \fB-L\fR flag. +.RE + .sp .ne 2 .mk @@ -1608,7 +1641,7 @@ Allows a pool to import when there is a missing log device. .ne 2 .mk .na -\fB\fBzpool iostat\fR [\fB-T\fR \fBd\fR | \fBu\fR] [\fB-v\fR] [\fB-y\fR] [\fIpool\fR] ... [\fIinterval\fR[\fIcount\fR]]\fR +\fB\fBzpool iostat\fR [\fB-T\fR \fBd\fR | \fBu\fR] [\fB-gLpvy\fR] [\fIpool\fR] ... [\fIinterval\fR[\fIcount\fR]]\fR .ad .sp .6 .RS 4n @@ -1626,6 +1659,39 @@ Display a time stamp. Specify \fBu\fR for a printed representation of the internal representation of time. See \fBtime\fR(2). Specify \fBd\fR for standard date format. See \fBdate\fR(1). .RE +.sp +.ne 2 +.mk +.na +\fB\fB-g\fR\fR +.ad +.RS 12n +.rt +Display vdev GUIDs instead of the normal device names. These GUIDs can be used in place of device names for the zpool detach/offline/remove/replace commands. +.RE + +.sp +.ne 2 +.mk +.na +\fB\fB-L\fR\fR +.ad +.RS 12n +.rt +Display real paths for vdevs resolving all symbolic links. This can be used to look up the current block device name regardless of the /dev/disk/ path used to open it. +.RE + +.sp +.ne 2 +.mk +.na +\fB\fB-p\fR\fR +.ad +.RS 12n +.rt +Display full paths for vdevs instead of only the last component of the path. This can be used in conjunction with the \fB-L\fR flag. +.RE + .sp .ne 2 .mk @@ -1676,7 +1742,7 @@ Treat exported or foreign devices as inactive. .ne 2 .mk .na -\fB\fBzpool list\fR [\fB-T\fR \fBd\fR | \fBu\fR] [\fB-Hv\fR] [\fB-o\fR \fIprops\fR[,...]] [\fIpool\fR] ... [\fIinterval\fR[\fIcount\fR]]\fR +\fB\fBzpool list\fR [\fB-T\fR \fBd\fR | \fBu\fR] [\fB-HgLpv\fR] [\fB-o\fR \fIprops\fR[,...]] [\fIpool\fR] ... [\fIinterval\fR[\fIcount\fR]]\fR .ad .sp .6 .RS 4n @@ -1692,6 +1758,39 @@ Lists the given pools along with a health status and space usage. If no \fIpools Scripted mode. Do not display headers, and separate fields by a single tab instead of arbitrary space. .RE +.sp +.ne 2 +.mk +.na +\fB\fB-g\fR\fR +.ad +.RS 12n +.rt +Display vdev GUIDs instead of the normal device names. These GUIDs can be used in place of device names for the zpool detach/offline/remove/replace commands. +.RE + +.sp +.ne 2 +.mk +.na +\fB\fB-L\fR\fR +.ad +.RS 12n +.rt +Display real paths for vdevs resolving all symbolic links. This can be used to look up the current block device name regardless of the /dev/disk/ path used to open it. +.RE + +.sp +.ne 2 +.mk +.na +\fB\fB-p\fR\fR +.ad +.RS 12n +.rt +Display full paths for vdevs instead of only the last component of the path. This can be used in conjunction with the \fB-L\fR flag. +.RE + .ne 2 .mk .na @@ -1886,7 +1985,7 @@ Sets the given property on the specified pool. See the "Properties" section for .ne 2 .mk .na -\fBzpool split\fR [\fB-n\fR] [\fB-R\fR \fIaltroot\fR] [\fB-o\fR \fIproperty=value\fR] \fIpool\fR \fInewpool\fR [\fIdevice\fR ...] +\fBzpool split\fR [\fB-gLnp\fR] [\fB-R\fR \fIaltroot\fR] [\fB-o\fR \fIproperty=value\fR] \fIpool\fR \fInewpool\fR [\fIdevice\fR ...] .ad .sp .6 .RS 4n @@ -1894,6 +1993,28 @@ Split devices off \fIpool\fR creating \fInewpool\fR. All \fBvdev\fRs in \fIpool\ The optional \fIdevice\fR specification causes the specified device(s) to be included in the new pool and, should any devices remain unspecified, the last device in each mirror is used as would be by default. +.sp +.ne 2 +.mk +.na +\fB\fB-g\fR\fR +.ad +.RS 6n +.rt +Display vdev GUIDs instead of the normal device names. These GUIDs can be used in place of device names for the zpool detach/offline/remove/replace commands. +.RE + +.sp +.ne 2 +.mk +.na +\fB\fB-L\fR\fR +.ad +.RS 6n +.rt +Display real paths for vdevs resolving all symbolic links. This can be used to look up the current block device name regardless of the /dev/disk/ path used to open it. +.RE + .sp .ne 2 .mk @@ -1905,6 +2026,17 @@ The optional \fIdevice\fR specification causes the specified device(s) to be inc Do dry run, do not actually perform the split. Print out the expected configuration of \fInewpool\fR. .RE +.sp +.ne 2 +.mk +.na +\fB\fB-p\fR\fR +.ad +.RS 6n +.rt +Display full paths for vdevs instead of only the last component of the path. This can be used in conjunction with the \fB-L\fR flag. +.RE + .sp .ne 2 .mk @@ -1933,22 +2065,45 @@ Sets the specified property for \fInewpool\fR. See the “Properties” section .ne 2 .mk .na -\fBzpool status\fR [\fB-xvD\fR] [\fB-T\fR d | u] [\fIpool\fR] ... [\fIinterval\fR [\fIcount\fR]] +\fBzpool status\fR [\fB-gLpvxD\fR] [\fB-T\fR d | u] [\fIpool\fR] ... [\fIinterval\fR [\fIcount\fR]] .ad .sp .6 .RS 4n Displays the detailed health status for the given pools. If no \fIpool\fR is specified, then the status of each pool in the system is displayed. For more information on pool and device health, see the "Device Failure and Recovery" section. .sp If a scrub or resilver is in progress, this command reports the percentage done and the estimated time to completion. Both of these are only approximate, because the amount of data in the pool and the other workloads on the system can change. + .sp .ne 2 .mk .na -\fB\fB-x\fR\fR +\fB\fB-g\fR\fR .ad .RS 12n .rt -Only display status for pools that are exhibiting errors or are otherwise unavailable. Warnings about pools not using the latest on-disk format will not be included. +Display vdev GUIDs instead of the normal device names. These GUIDs can be used innplace of device names for the zpool detach/offline/remove/replace commands. +.RE + +.sp +.ne 2 +.mk +.na +\fB\fB-L\fR\fR +.ad +.RS 12n +.rt +Display real paths for vdevs resolving all symbolic links. This can be used to look up the current block device name regardless of the /dev/disk/ path used to open it. +.RE + +.sp +.ne 2 +.mk +.na +\fB\fB-p\fR\fR +.ad +.RS 12n +.rt +Display full paths for vdevs instead of only the last component of the path. This can be used in conjunction with the \fB-L\fR flag. .RE .sp @@ -1962,6 +2117,17 @@ Only display status for pools that are exhibiting errors or are otherwise unavai Displays verbose data error information, printing out a complete list of all data errors since the last complete pool scrub. .RE +.sp +.ne 2 +.mk +.na +\fB\fB-x\fR\fR +.ad +.RS 12n +.rt +Only display status for pools that are exhibiting errors or are otherwise unavailable. Warnings about pools not using the latest on-disk format will not be included. +.RE + .sp .ne 2 .mk @@ -2403,6 +2569,17 @@ Cause \fBzpool\fR to dump core on exit for the purposes of running \fB::findleak .B "ZPOOL_IMPORT_PATH" The search path for devices or files to use with the pool. This is a colon-separated list of directories in which \fBzpool\fR looks for device nodes and files. Similar to the \fB-d\fR option in \fIzpool import\fR. +.TP +.B "ZPOOL_VDEV_NAME_GUID" +Cause \fBzpool\fR subcommands to output vdev guids by default. This behavior +is identical to the \fBzpool status -g\fR command line option. +.TP +.B "ZPOOL_VDEV_NAME_FOLLOW_LINKS" +Cause \fBzpool\fR subcommands to follow links for vdev names by default. This behavior is identical to the \fBzpool status -L\fR command line option. +.TP +.B "ZPOOL_VDEV_NAME_PATH" +Cause \fBzpool\fR subcommands to output full vdev path names by default. This +behavior is identical to the \fBzpool status -p\fR command line option. .SH SEE ALSO .sp -- 2.40.0