Whamcloud - gitweb
LU-17416 utils: option for lctl get_param to skip links 36/55236/12
authorFrederick Dilger <fdilger@whamcloud.com>
Mon, 27 May 2024 21:38:51 +0000 (17:38 -0400)
committerOleg Drokin <green@whamcloud.com>
Wed, 19 Jun 2024 01:13:33 +0000 (01:13 +0000)
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 <fdilger@whamcloud.com>
Change-Id: I24115835f5045623f78fa2045dc3e0ce0b795316
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/55236
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Jian Yu <yujian@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/doc/lctl-get_param.8
lustre/doc/lctl-list_param.8
lustre/doc/lctl-set_param.8
lustre/tests/sanity.sh
lustre/utils/lctl.c
lustre/utils/lctl_thread.h
lustre/utils/lustre_cfg.c

index 9ea1529..2455406 100644 (file)
@@ -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.
index 55d760b..f43eadc 100644 (file)
@@ -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),
index fbf7706..9f48adf 100644 (file)
@@ -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
index e4f1d67..36c3c7d 100755 (executable)
@@ -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) &&
index 8ac743c..0f91894 100644 (file)
@@ -185,43 +185,30 @@ command_t cmdlist[] = {
        {"local_param", jt_lcfg_param, 0, "set a temporary, local param\n"
         "usage: local_param <target.keyword=val>\n"},
        {"get_param", jt_lcfg_getparam, 0, "get the Lustre or LNET parameter\n"
-        "usage: get_param [-F|n|-N|-R] <param_path1 param_path2 ...>\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"
+        "                 <param_path1 param_path2 ...>\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] <param_path1 param_path2 ...>\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"
+        "                  <param_path1 param_path2 ...>\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"},
index 667f49f..b0b2aa9 100644 (file)
@@ -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;
 };
 
index 32e9b60..57c09d9 100644 (file)
@@ -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;