From e7b898661983da4ca218837f657d507ffba97cda Mon Sep 17 00:00:00 2001 From: Frederick Dilger Date: Mon, 27 May 2024 17:38:51 -0400 Subject: [PATCH] LU-17416 utils: option for lctl get_param to skip links Added new --links and --no-links options for 'lctl get_param' and 'lctl list_param' to avoid following symlinks. Useful when combined with a command like "lctl get_param -R '*'" which can dump a lot of duplicate data due to symlinks under lov.*.target_obds and lmv.*.target_obds pointing back to their respective osc.* and mdc.* trees. By default --links is enabled to for lctl get_param to continue to operate as it did before this patch. Additionally, long options have been added for all previous options in {list, get, set}_param to update command options to current standards. This will also facilitate adding new options in the future as well as code maintenance and readability. Signed-off-by: Frederick Dilger Change-Id: I24115835f5045623f78fa2045dc3e0ce0b795316 Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/55236 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Andreas Dilger Reviewed-by: Jian Yu Reviewed-by: Oleg Drokin --- lustre/doc/lctl-get_param.8 | 34 +++++++++++++++++------- lustre/doc/lctl-list_param.8 | 54 +++++++++++++++++++++++++++++++++----- lustre/doc/lctl-set_param.8 | 33 +++++++++++++---------- lustre/tests/sanity.sh | 12 +++++++-- lustre/utils/lctl.c | 43 +++++++++++------------------- lustre/utils/lctl_thread.h | 1 + lustre/utils/lustre_cfg.c | 62 +++++++++++++++++++++++++++++++++++++++++--- 7 files changed, 174 insertions(+), 65 deletions(-) diff --git a/lustre/doc/lctl-get_param.8 b/lustre/doc/lctl-get_param.8 index 9ea1529..2455406 100644 --- a/lustre/doc/lctl-get_param.8 +++ b/lustre/doc/lctl-get_param.8 @@ -2,9 +2,16 @@ .SH NAME lctl-get_param \- retrieve configuration parameters .SH SYNOPSIS -.br -.IR "\fBlctl get_param " [ -F "] [" -H "] [" -n | -N "] [" -R "] <" parameter ...> -.br +.B "\fBlctl get_param " +.RB [ --classify | -F ] +.RB [ --header | -H ] +.RB [ --links | -l ] +.RB [ --no-links | -L ] +.RB [ --no-name | -n ] +.RB [ --only-name | -N ] +.RB [ --recursive | -R ] +.RB [ --yaml | -y ] +.IR PARAM_PATH1 " " PARAM_PATH2 " ..." .SH DESCRIPTION Get the value of the named Lustre or LNet .I parameter @@ -29,31 +36,38 @@ some of them are accessible only by the root user for security or implementation reasons. .SH OPTIONS .TP -.B -F +.B -F ", " --classify Append a '/', '@', or '=' suffix for directories, symlinks, and writeable parameters, respectively. .B "lctl get_param -NF" is equivalent to .BR "lctl list_param -F" . .TP -.B -H +.B -H ", " --header Prefix each parameter value line with the parameter name, as a header. It also print a line for empty values. It could be useful when wildcards are used and filtering the output. .TP -.B -n +.B -l ", " --links +Follow symlinks while searching for parameters. (enabled by default) +.TP +.B -L ", " --no-links +Do not follow symlinks while searching for parameters. +.TP +.B -n ", " --no-name Print only the parameter value and not parameter name. This may be confusing if multiple parameter names are specified, as the parameters are not identified, and may not be returned in the order that they are specified. .TP -.B -N +.B -N ", " --only-name Print only matched parameter names and not the values. This is especially -useful when using patterns. +useful when using patterns. This option is equivalent to +.BR "lctl list_param". .TP -.B -R +.B -R ", " --recursive Recursively show all of the parameter names below the specified name. .TP -.B -y +.B -y ", " --yaml Some paramters can be presented in a YAML format but are not by default. This will format the parameter data in YAML. If the YAML provides a source: field it can be suppressed with the -n option. diff --git a/lustre/doc/lctl-list_param.8 b/lustre/doc/lctl-list_param.8 index 55d760b..f43eadc 100644 --- a/lustre/doc/lctl-list_param.8 +++ b/lustre/doc/lctl-list_param.8 @@ -2,25 +2,41 @@ .SH NAME lctl-list_param \- list configuration parameter names .SH SYNOPSIS -.BR list_param " ["-D | -F | -p | -R ] -.RI < param_search " ...>" +.B "\fBlctl list_param " +.RB [ --dir-only | -D ] +.RB [ --classify | -F ] +.RB [ --links | -l ] +.RB [ --no-links | -L ] +.RB [ --path | -p ] +.RB [ --recursive | -R ] +.IR PARAM_PATH1 " " PARAM_PATH2 " ..." .SH DESCRIPTION List the Lustre or LNet parameter name(s) matching .IR param_search . The parameter name(s) may contain wildcards using .BR glob (3) pathname patterns. +.SH OPTIONS +The various options supported by +.B lctl list_param +are listed and explained below: .TP -.B -D +.B -D ", " --dir-only Only list directories. .TP -.B -F +.B -F ", " --classify Append '/', '@' or '=' for dirs, symlinks and writeable files, respectively. .TP -.B -p -Print the pathname instead of the parameter name. +.B -l ", " --links +Follow symlinks while searching for parameters. (enabled by default) .TP -.B -R +.B -L ", " --no-links +Do not follow symlinks while searching for parameters. +.TP +.b -p ", " --path +Print the path name instead of the parameter name. +.TP +.B -R ", " --recursive Recursively list all parameters under the specified parameter search string. If .I param_search is unspecified, all the parameters will be shown. @@ -68,6 +84,30 @@ is unspecified, all the parameters will be shown. mdt.lustre-MDT0000.evict_client .br ... +.br +.B +# lctl list_param -L -R mgs.MGS | grep -c .osd +.br + 0 +.br +# mgs.MGS.osd is a \fIsymlink\fR -> ../../osd-ldiskfs/lustre-MDT0000 +.br +Compare this to the same command with --links enabled +.br +.B +# lctl list_param -l -R mgs.MGS | grep .osd +.br + mgs.MGS.osd +.br + mgs.MGS.osd.auto_scrub +.br + mgs.MGS.osd.blocksize +.br + mgs.MGS.osd.enable_projid.xattr +.br + mgs.MGS.osd.extent_bytes_allocation +.br + ... .SH SEE ALSO .BR lustre (7), .BR lctl-get_param (8), diff --git a/lustre/doc/lctl-set_param.8 b/lustre/doc/lctl-set_param.8 index fbf77068..9f48adf 100644 --- a/lustre/doc/lctl-set_param.8 +++ b/lustre/doc/lctl-set_param.8 @@ -3,14 +3,15 @@ lctl-set_param \- Lustre filesystem set parameter utility .SH SYNOPSIS .B "\fBlctl set_param " -.RB [ \-d ] -.RB [ \-h ] -.RB [ \-n ] -.RB [ \-P ] -.RB [ \-t [ \fITHREAD_COUNT ]] -.RI < parameter \= value ...> -.br -.IR "\fBlctl set_param -F " < filename > +.RB [ --delete | -d ] +.RB [ --file | -F ] +.RB [ --no-name | -n ] +.RB [ --permanent | -P ] +.RB [ --thread | -t [ \fITHREAD_COUNT ]] +.IR PARAMETER \= VALUE " ..." +.br +.B lctl set_param -F +.IR FILENAME .SH DESCRIPTION Set the value of the named Lustre or LNet .I parameter @@ -34,26 +35,30 @@ is the name of a Lustre device, like but may be a specific component, or contain wildcards to match some or all devices on the node. Parameters can only be modified by the root user for security reasons. +.SH OPTIONS +The various options supported by +.B lctl list_param +are listed and explained below: .TP -.B -d +.B -d ", " --delete Remove the permanent setting (only for parameters set with the .B -P option). .TP -.B -F +.B -F ", " --file Set parameters from .I filename instead of from the command-line. The contents of .I filename is YAML format, created as an output from -.BR ' "lctl --device MGS llog_print " < \fIfsname\fR >- client ' +.RB ' "lctl --device MGS llog_print " \fIFSNAME\fB "-client" ' or any other valid llog configuration log as listed by .RB ' "lctl --device MGS llog_catlist" ' .TP -.B -n +.B -n ", " --no-name Disable printing of the parameter name after setting it. .TP -.B -P +.B -P ", " --permanent Set .I parameter permanently on @@ -70,7 +75,7 @@ such as the filesystem and/or target name. This option is only available in Lustre 2.5.0 and later clients, older clients cannot set persistent parameters, nor will they see them. .TP -.B -t +.B -t ", " --thread Spawn threads to set multiple parameters in parallel, optionally specifying the maximum number of threads to run (with no space between .B -t diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh index e4f1d67..36c3c7d 100755 --- a/lustre/tests/sanity.sh +++ b/lustre/tests/sanity.sh @@ -28645,7 +28645,7 @@ test_401a() { #LU-7437 sort -u | wc -l) [ $params -eq $procs ] || - error "found $params parameters vs. $procs proc files" + error "found $params parameters vs. $procs proc files '-D'" } run_test 401a "Verify if 'lctl list_param -R' can list parameters recursively" @@ -28713,7 +28713,7 @@ test_401d() { if $LCTL list_param jobid_name > /dev/null 2>&1; then local testname=jobid_name new_value='foo=bar%p' else - local testname=jobid_var new_valuie=foo=bar + local testname=jobid_var new_value=foo=bar fi local jobid_var_old=$($LCTL get_param -n $testname) @@ -28755,6 +28755,14 @@ test_401e() { # LU-14779 } run_test 401e "verify 'lctl get_param' works with NID in parameter" +test_401f() { + $LCTL list_param -RpL "*" | while read path; do + [[ ! -L $path ]] || + error "list_param -RpL returned the symlink: '$path'" + done +} +run_test 401f "check 'lctl list_param' doesn't follow symlinks with --no-links" + test_402() { [[ $MDS1_VERSION -ge $(version_code 2.7.66) ]] || [[ $MDS1_VERSION -ge $(version_code 2.7.18.4) && diff --git a/lustre/utils/lctl.c b/lustre/utils/lctl.c index 8ac743c..0f91894 100644 --- a/lustre/utils/lctl.c +++ b/lustre/utils/lctl.c @@ -185,43 +185,30 @@ command_t cmdlist[] = { {"local_param", jt_lcfg_param, 0, "set a temporary, local param\n" "usage: local_param \n"}, {"get_param", jt_lcfg_getparam, 0, "get the Lustre or LNET parameter\n" - "usage: get_param [-F|n|-N|-R] \n" + "usage: get_param [--classify|-F] [--header|-H] [--links|-l]\n" + " [--no-links|-L] [--no-name|-n] [--only-name|-N]\n" + " [--recursive|-R] [--yaml|-y]\n" + " \n" "Get the value of Lustre or LNET parameter from the specified path.\n" - "The path can contain shell-style filename patterns.\n" - " -F When -N specified, add '/', '@' or '=' for directories,\n" - " symlinks and writeable files, respectively.\n" - " -H Prefix each output line with the parameter name.\n" - " -n Print only the value and not parameter name.\n" - " -N Print only matched parameter names and not the values.\n" - " (Especially useful when using patterns.)\n" - " -R Get parameters recursively from the specified entry.\n"}, + "The path can contain shell-style filename patterns.\n"}, {"set_param", jt_lcfg_setparam, 0, "set the Lustre or LNET parameter\n" - "usage: set_param [-n] [-P] [-d] [-F] " + "usage: set_param [--delete|-d] [--file|-F] [--no-name|-n]\n" + " [--permanent|-P]" #ifdef HAVE_LIBPTHREAD - "[-t[THREAD_COUNT]] " + " [--thread|-t [THREAD_COUNT]]" #endif - "PARAM1=VALUE1 [PARAM2=VALUE2 ...]\n" - "Set the value of the Lustre or LNET parameter at the specified path.\n" - " -n Disable printing of the key name when printing values.\n" - " -P Set the parameter permanently, filesystem-wide.\n" - " -d Remove the permanent setting (only with -P option).\n" - " -F Read permanent configuration from a YAML file.\n" -#ifdef HAVE_LIBPTHREAD - " -t Set parameters in parallel, max THREAD_COUNT threads\n" - " (default " STRINGIFY(LCFG_THREADS_DEF) ").\n" -#endif - }, + "\n" + " PARAM1=VALUE1 [PARAM2=VALUE2 ...]\n" + "Set the value of the Lustre or LNET parameter at the specified path.\n"}, {"apply_yaml", jt_lcfg_applyyaml, 0, "set/config the Lustre or LNET " "parameters using configuration from a YAML file.\n" "usage: apply_yaml file\n"}, {"list_param", jt_lcfg_listparam, 0, "list the Lustre or LNET parameter name\n" - "usage: list_param [-D|-F|-p|-R] \n" - "List the name of Lustre or LNET parameter from the specified path.\n" - " -D Only list directories.\n" - " -F Add '/', '@' or '=' for dirs, symlinks and writeable files, respectively.\n" - " -p Prints the pathname instead of the parameter name.\n" - " -R Recursively list all parameters under the specified path.\n"}, + "usage: list_param [--dir-only|-D] [--classify|-F] [--links|-l]\n" + " [--no-links|-L] [--path|-p] [--recursive|-R]\n" + " \n" + "List the name of Lustre or LNet parameter from the specified path.\n"}, {"del_ost", jt_del_ost, 0, "permanently delete OST records\n" "usage: del_ost [--dryrun] --target <$fsname-OSTxxxx>\n" "Cancel the config records for a specific OST to forget about it.\n"}, diff --git a/lustre/utils/lctl_thread.h b/lustre/utils/lctl_thread.h index 667f49f..b0b2aa9 100644 --- a/lustre/utils/lctl_thread.h +++ b/lustre/utils/lctl_thread.h @@ -48,6 +48,7 @@ struct param_opts { unsigned int po_yaml:1; unsigned int po_detail:1; unsigned int po_header:1; + unsigned int po_follow_symlinks:1; unsigned int po_parallel_threads; }; diff --git a/lustre/utils/lustre_cfg.c b/lustre/utils/lustre_cfg.c index 32e9b60..57c09d9 100644 --- a/lustre/utils/lustre_cfg.c +++ b/lustre/utils/lustre_cfg.c @@ -1161,7 +1161,12 @@ do_param_op(struct param_opts *popt, char *pattern, char *value, struct stat st; int rc2, j; - if (stat(paths.gl_pathv[i], &st) == -1) { + if (!popt->po_follow_symlinks) + rc2 = lstat(paths.gl_pathv[i], &st); + else + rc2 = stat(paths.gl_pathv[i], &st); + + if (rc2 == -1) { fprintf(stderr, "error: %s: stat '%s': %s\n", opname, paths.gl_pathv[i], strerror(errno)); if (!rc) @@ -1169,6 +1174,8 @@ do_param_op(struct param_opts *popt, char *pattern, char *value, continue; } + if (S_ISLNK(st.st_mode) && !popt->po_follow_symlinks) + continue; if (popt->po_only_dir && !S_ISDIR(st.st_mode)) continue; @@ -1327,12 +1334,23 @@ out_param: static int listparam_cmdline(int argc, char **argv, struct param_opts *popt) { + struct option long_opts[] = { + { .val = 'D', .name = "dir-only", .has_arg = no_argument}, + { .val = 'D', .name = "directory-only", .has_arg = no_argument}, + { .val = 'F', .name = "classify", .has_arg = no_argument}, + { .val = 'l', .name = "links", .has_arg = no_argument}, + { .val = 'L', .name = "no-links", .has_arg = no_argument}, + { .val = 'R', .name = "recursive", .has_arg = no_argument}, + }; + int ch; popt->po_show_name = 1; popt->po_only_name = 1; + popt->po_follow_symlinks = 1; - while ((ch = getopt(argc, argv, "DFpR")) != -1) { + while ((ch = getopt_long(argc, argv, "DFlLpR", + long_opts, NULL)) != -1) { switch (ch) { case 'D': popt->po_only_dir = 1; @@ -1340,6 +1358,12 @@ static int listparam_cmdline(int argc, char **argv, struct param_opts *popt) case 'F': popt->po_show_type = 1; break; + case 'l': + popt->po_follow_symlinks = 1; + break; + case 'L': + popt->po_follow_symlinks = 0; + break; case 'p': popt->po_only_pathname = 1; break; @@ -1402,11 +1426,24 @@ int jt_lcfg_listparam(int argc, char **argv) static int getparam_cmdline(int argc, char **argv, struct param_opts *popt) { + struct option long_opts[] = { + { .val = 'F', .name = "classify", .has_arg = no_argument}, + { .val = 'H', .name = "header", .has_arg = no_argument}, + { .val = 'l', .name = "links", .has_arg = no_argument}, + { .val = 'L', .name = "no-links", .has_arg = no_argument}, + { .val = 'n', .name = "no-name", .has_arg = no_argument}, + { .val = 'N', .name = "only-name", .has_arg = no_argument}, + { .val = 'R', .name = "recursive", .has_arg = no_argument}, + { .val = 'y', .name = "yaml", .has_arg = no_argument}, + }; + int ch; popt->po_show_name = 1; + popt->po_follow_symlinks = 1; - while ((ch = getopt(argc, argv, "FHnNRy")) != -1) { + while ((ch = getopt_long(argc, argv, "FHlLnNRy", + long_opts, NULL)) != -1) { switch (ch) { case 'F': popt->po_show_type = 1; @@ -1414,6 +1451,12 @@ static int getparam_cmdline(int argc, char **argv, struct param_opts *popt) case 'H': popt->po_header = 1; break; + case 'l': + popt->po_follow_symlinks = 1; + break; + case 'L': + popt->po_follow_symlinks = 0; + break; case 'n': popt->po_show_name = 0; break; @@ -1679,6 +1722,15 @@ int jt_nodemap_info(int argc, char **argv) */ static int setparam_cmdline(int argc, char **argv, struct param_opts *popt) { + struct option long_opts[] = { + { .val = 'd', .name = "delete", .has_arg = no_argument}, + { .val = 'F', .name = "file", .has_arg = no_argument}, + { .val = 'n', .name = "no-name", .has_arg = no_argument}, + { .val = 'P', .name = "perm", .has_arg = no_argument}, + { .val = 'P', .name = "permanent", .has_arg = no_argument}, + { .val = 't', .name = "thread", .has_arg = optional_argument}, + }; + int ch; popt->po_show_name = 1; @@ -1689,9 +1741,11 @@ static int setparam_cmdline(int argc, char **argv, struct param_opts *popt) popt->po_delete = 0; popt->po_file = 0; popt->po_parallel_threads = 0; + popt->po_follow_symlinks = 1; opterr = 0; - while ((ch = getopt(argc, argv, "dFnPt::")) != -1) { + while ((ch = getopt_long(argc, argv, "dFnPt::", + long_opts, NULL)) != -1) { switch (ch) { case 'n': popt->po_show_name = 0; -- 1.8.3.1