Whamcloud - gitweb
LU-18715 utils: Extend lctl nodemap_info with property values 52/58052/8
authorMarc Vef <mvef@whamcloud.com>
Wed, 12 Feb 2025 16:16:38 +0000 (17:16 +0100)
committerOleg Drokin <green@whamcloud.com>
Tue, 27 May 2025 04:05:04 +0000 (04:05 +0000)
The command "lctl nodemap_info" only presents information about which
property names exist for a given nodemap. It does not show the values
for each property, and the command's usefulness is therefore limited.
Further, nodemap property values are generally retrieved via the "lctl
get_param" interface which is inconsistent with how nodemap properties
are set, i.e., by using the "lctl nodemap_*" interface rather than the
"lctl set_param" interface.

This patch extends the "lctl nodemap_info" command to improve
usability, presenting in-depth information, including the global
nodemap state (active/inactive), property availability and
descriptions, all defined nodemaps, and the values for all nodemap
properties.

Extended options:
lctl nodemap_info [-l/--list] [-n/--name NODEMAP_NAME]
[-p/--property PROPERTY_NAME]

List the values of all properties for all nodemaps:
"lctl nodemap_info"

List the value of the specific "ranges" property for the "remote"
nodemap: "lctl nodemap_info --name remote --property ranges"

"--name" and "--property" can be used individually to show the
properties of all nodemaps or all properties of a specific nodemap.
Note the previous positional arguments [all,list,<nodemap>] are
retained for backward-compatibility.

Added sanity-sec 25a to test these modifications.

Test-Parameters: trivial testlist=sanity-sec env=ONLY="25 25a"
Signed-off-by: Marc Vef <mvef@whamcloud.com>
Change-Id: Ic09710233de490d8eb2f50a74e2b7e4765ca4f3d
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/58052
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Sebastien Buisson <sbuisson@ddn.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/doc/Makefile.am
lustre/doc/lctl-nodemap-info.8 [new file with mode: 0644]
lustre/doc/lctl-nodemap_info.8 [new file with mode: 0644]
lustre/tests/sanity-sec.sh
lustre/utils/lctl.c
lustre/utils/lustre_cfg.c
lustre/utils/obd.c

index b6f7cdf..edf6f7f 100644 (file)
@@ -145,6 +145,8 @@ SERVER_MANFILES =                           \
        lctl-nodemap_del_range.8                \
        lctl-nodemap-del.8                      \
        lctl-nodemap_del.8                      \
+       lctl-nodemap-info.8                     \
+       lctl-nodemap_info.8                     \
        lctl-nodemap-modify.8                   \
        lctl-nodemap_modify.8                   \
        lctl-nodemap-set-fileset.8              \
diff --git a/lustre/doc/lctl-nodemap-info.8 b/lustre/doc/lctl-nodemap-info.8
new file mode 100644 (file)
index 0000000..c8f59a3
--- /dev/null
@@ -0,0 +1,77 @@
+.TH LCTL-NODEMAP_INFO 8 2025-03-10 Lustre "Lustre Configuration Utilities"
+.SH NAME
+lctl-nodemap_info \- present information about nodemaps
+.SH SYNOPSIS
+.SY "lctl nodemap_info"
+or
+.SY "lctl nodemap info"
+.BI --name " NODEMAP_NAME"
+.BI --property " PROPERTY_NAME"
+.B --list
+.YS
+.SH DESCRIPTION
+.B nodemap_info
+presents information about the defined nodemaps. This includes whether nodemaps
+are active, a list of the defined nodemaps, and the values of specific nodemap
+parameters. If no options are specified, all nodemaps and their properties
+are presented.
+.SH OPTIONS
+.TP
+.BI --name " NODEMAP_NAME"
+Specifies the name of the nodemap to present information about. If not
+specified, all nodemaps are presented. This option can be used in conjunction
+with the
+.B --property
+option to present the value of a specific property for a specific nodemap.
+.TP
+.BI --property " PROPERTY_NAME"
+Specifies the name of the property to present the value of. If not specified,
+all properties are presented. This option can be used in conjunction with the
+.B --name
+option to present the value of a specific property for a specific nodemap.
+.TP
+.B --list
+Presents the global nodemap state and Lists all defined nodemaps and available
+nodemap properties.
+.SH EXAMPLES
+List general nodemap information:
+.EX
+.B # lctl nodemap_info --list
+.EE
+.PP
+List the values of all properties for all nodemaps:
+.EX
+.B # lctl nodemap_info
+.EE
+.PP
+List the values for all properties for the "remote" nodemap:
+.EX
+.B # lctl nodemap_info --name remote
+.EE
+.PP
+List the value of the specific "ranges" property for the "remote" nodemap:
+.EX
+.B # lctl nodemap_info --name remote --property ranges
+.EE
+.PP
+List the value of the specific "ranges" property for all nodemaps:
+.EX
+.B # lctl nodemap_info --property ranges
+.EE
+.SH AVAILABILITY
+.B lctl nodemap_info
+is part of the
+.BR lustre (7)
+filesystem package since release 2.16.52
+.\" Added in commit v2.16.52_105_g03b41c3
+.SH SEE ALSO
+.BR lustre (7),
+.BR lctl-nodemap-activate (8),
+.BR lctl-nodemap-add (8),
+.BR lctl-nodemap-add-idmap (8),
+.BR lctl-nodemap-add-range (8),
+.BR lctl-nodemap-del (8),
+.BR lctl-nodemap-del-idmap (8),
+.BR lctl-nodemap-del-range (8),
+.BR lctl-nodemap-modify (8),
+.BR lctl-nodemap-set-fileset (8)
diff --git a/lustre/doc/lctl-nodemap_info.8 b/lustre/doc/lctl-nodemap_info.8
new file mode 100644 (file)
index 0000000..01b709d
--- /dev/null
@@ -0,0 +1 @@
+.so man8/lctl-nodemap-info.8
index 91271cf..5b1eb4c 100755 (executable)
@@ -104,6 +104,7 @@ if (( $MDS1_VERSION >= $(version_code 2.16.51) )); then
        nodemap_activate="nodemap activate"
        nodemap_add="nodemap add"
        nodemap_del="nodemap del"
+       nodemap_info="nodemap info"
        nodemap_modify="nodemap modify"
        nodemap_add_range="nodemap add_range"
        nodemap_del_range="nodemap del_range"
@@ -117,6 +118,7 @@ else
        nodemap_activate="nodemap_activate"
        nodemap_add="nodemap_add"
        nodemap_del="nodemap_del"
+       nodemap_info="nodemap_info"
        nodemap_modify="nodemap_modify"
        nodemap_add_range="nodemap_add_range"
        nodemap_del_range="nodemap_del_range"
@@ -2096,8 +2098,8 @@ test_25() {
 
        wait_nm_sync test25 id
 
-       do_facet mgs $LCTL nodemap_info > $tmpfile
-       do_facet mds $LCTL nodemap_info > $tmpfile2
+       do_facet mgs $LCTL $nodemap_info > $tmpfile
+       do_facet mds $LCTL $nodemap_info > $tmpfile2
 
        if ! $SHARED_KEY; then
                # will conflict with SK's nodemaps
@@ -2107,13 +2109,13 @@ test_25() {
        zconf_umount_clients $CLIENTS $MOUNT ||
            error "unable to umount clients $CLIENTS"
 
-       do_facet mgs $LCTL nodemap_info > $tmpfile3
+       do_facet mgs $LCTL $nodemap_info > $tmpfile3
        diff -q $tmpfile3 $tmpfile >& /dev/null ||
-               error "nodemap_info diff on MGS after remount"
+               error "$nodemap_info diff on MGS after remount"
 
-       do_facet mds $LCTL nodemap_info > $tmpfile4
+       do_facet mds $LCTL $nodemap_info > $tmpfile4
        diff -q $tmpfile4 $tmpfile2 >& /dev/null ||
-               error "nodemap_info diff on MDS after remount"
+               error "$nodemap_info diff on MDS after remount"
 
        # cleanup nodemap
        do_facet mgs $LCTL nodemap_del test25 ||
@@ -2128,6 +2130,69 @@ test_25() {
 }
 run_test 25 "test save and reload nodemap config"
 
+test_25a() {
+       local nm="c0"
+       local info_dump=$(mktemp)
+       local param_dump=$(mktemp)
+
+       (( $MGS_VERSION >= $(version_code 2.16.52) )) ||
+               skip "Need MGS >= 2.16.52 for updated nodemap_info"
+
+       nodemap_test_setup
+       stack_trap nodemap_test_cleanup EXIT
+
+       if $SHARED_KEY; then
+               export SK_UNIQUE_NM=true
+       fi
+
+       # fill some more values on nodemap
+       # We test only local here, so no wait_nm_sync required
+       do_facet mgs $LCTL nodemap_add_offset --name $nm \
+               --offset 1000000 --limit 100000 ||
+               error "cannot set offset $nm"
+       do_facet mgs $LCTL nodemap_set_fileset --name $nm \
+               --fileset "/somedir" ||
+               error "unable to add fileset info"
+
+       # full nodemap dump
+       do_facet mgs $LCTL $nodemap_info > $info_dump ||
+               error "$nodemap_info failed"
+       stack_trap "rm -f $info_dump" EXIT
+       do_facet mgs $LCTL get_param -R nodemap > $param_dump
+       stack_trap "rm -f $param_dump" EXIT
+
+       diff -q $info_dump $param_dump >& /dev/null ||
+               error "$nodemap_info differs from get_param output"
+
+       # nodemap dump for $nm
+       do_facet mgs $LCTL $nodemap_info --name $nm > $info_dump ||
+               error "$nodemap_info failed"
+       do_facet mgs $LCTL get_param -R nodemap.$nm > $param_dump
+
+       diff -q $info_dump $param_dump >& /dev/null ||
+               error "$nodemap_info differs from get_param output"
+
+       # nodemap dump for $nm and property fileset
+       do_facet mgs $LCTL $nodemap_info --name $nm \
+               --property fileset > $info_dump ||
+               error "$nodemap_info failed"
+       do_facet mgs $LCTL get_param nodemap.$nm.fileset > $param_dump
+
+       diff -q $info_dump $param_dump >& /dev/null ||
+               error "$nodemap_info differs from get_param output"
+
+       # cross nodemap dump for property ranges
+       do_facet mgs $LCTL $nodemap_info --property ranges > $info_dump ||
+               error "$nodemap_info failed"
+       do_facet mgs $LCTL get_param -R nodemap.*.ranges > $param_dump
+
+       # back to non-nodemap setup
+       if $SHARED_KEY; then
+               export SK_UNIQUE_NM=false
+       fi
+}
+run_test 25a "test nodemap info values"
+
 test_26() {
        nodemap_version_check || return 0
 
index 67a42ab..f93fa62 100644 (file)
@@ -209,7 +209,7 @@ command_t nodemap_cmdlist[] = {
         "usage: nodemap test_id --nid NID --idtype {uid|gid|projid} --id ID"},
        {.pc_name = "info", .pc_func = jt_nodemap_info,
         .pc_help = "print nodemap information\n"
-        "usage: nodemap info {list|nodemap_name|all}"},
+        "usage: nodemap info --list --name NODEMAP_NAME --property PROPERTY_NAME"},
        {.pc_help = NULL }
 };
 JT_SUBCMD(nodemap);
@@ -634,7 +634,7 @@ command_t cmdlist[] = {
         "Usage: nodemap_test_id --nid NID --idtype ID_TYPE --id ID"},
        {"nodemap_info", jt_nodemap_info, 0,
         "print nodemap information\n"
-        "Usage: nodemap_info [list|nodemap_name|all]"},
+        "usage: nodemap_info --list --name NODEMAP_NAME --property PROPERTY_NAME"},
        {"nodemap", jt_nodemap, nodemap_cmdlist, ""},
 
        /* Changelog commands */
index 52e550f..0998b31 100644 (file)
@@ -989,54 +989,6 @@ enum paramtype {
        PT_CONFPARAM
 };
 
-#ifdef HAVE_SERVER_SUPPORT
-/**
- * Output information about nodemaps.
- * \param      argc            number of args
- * \param      argv[]          variable string arguments
- *
- * [list|nodemap_name|all]     \a list will list all nodemaps (default).
- *                             Specifying a \a nodemap_name will
- *                             display info about that specific nodemap.
- *                             \a all will display info for all nodemaps.
- * \retval                     0 on success
- */
-int jt_nodemap_info(int argc, char **argv)
-{
-       const char usage_str[] = "usage: nodemap_info [list|nodemap_name|all]\n";
-       struct param_opts popt;
-       int rc = 0;
-
-       memset(&popt, 0, sizeof(popt));
-       popt.po_show_name = 1;
-
-       if (argc > 2) {
-               fprintf(stderr, usage_str);
-               return -1;
-       }
-
-       if (argc == 1 || strcmp("list", argv[1]) == 0) {
-               popt.po_only_dir = 1;
-               rc = jt_lcfg_listparam(3, (char*[3])
-                                      { "list_param", "-D", "nodemap/*" });
-       } else if (strcmp("all", argv[1]) == 0) {
-               rc = jt_lcfg_getparam(3, (char*[3])
-                                     { "get_param", "-N", "nodemap/*/*" });
-       } else {
-               char    pattern[PATH_MAX];
-
-               snprintf(pattern, sizeof(pattern), "nodemap/%s/*", argv[1]);
-               rc = jt_lcfg_getparam(3, (char*[3])
-                                     { "get_param", "-N", pattern });
-               if (rc == -ESRCH)
-                       fprintf(stderr,
-                               "error: nodemap_info: cannot find nodemap %s\n",
-                               argv[1]);
-       }
-       return rc;
-}
-#endif
-
 #define PS_NONE 0
 #define PS_PARAM_FOUND 1
 #define PS_PARAM_SET 2
index 64a763c..ba415ae 100644 (file)
@@ -58,6 +58,7 @@
 #include <unistd.h>
 #include <limits.h>
 #include "obdctl.h"
+#include "lstddef.h"
 #include "lustreapi_internal.h"
 #include <libcfs/util/list.h>
 #include <libcfs/util/ioctl.h>
@@ -4776,6 +4777,244 @@ int jt_nodemap_modify(int argc, char **argv)
 }
 
 /**
+ * Output information about nodemaps.
+ * \param      argc            number of args
+ * \param      argv[]          variable string arguments
+ *
+ *
+ * --name                      nodemap to present info about
+ * --property                  nodemap property to present
+ * --list                      list nodemap state, all nodemaps, and properties
+ *
+ * deprecated positional parameters:
+ * [list|nodemap_name|all]     \a list will list all nodemaps (default).
+ *                             Specifying a \a nodemap_name will
+ *                             display info about that specific nodemap.
+ *                             \a all will display info for all nodemaps.
+ * \retval                     0 on success
+ */
+int jt_nodemap_info(int argc, char **argv)
+{
+       char *nodemap_name = NULL;
+       char *property = NULL;
+       bool list = false;
+       char pattern[PATH_MAX];
+       bool is_active = false;
+       char *active_str = NULL;
+       size_t buflen;
+       glob_t param;
+       int c, i;
+       int rc = 0;
+
+       struct nodemap_info_param_desc {
+               const char *param;
+               const char *desc;
+       };
+
+       static const struct nodemap_info_param_desc param_desc[] = {
+               { "admin_nodemap", "root is not squashed on policy group" },
+               { "audit_mode",
+                 "client can record FS access events to the Changelogs" },
+               { "deny_mount", "disable client mounts" },
+               { "deny_unknown", "deny access for unknown (squashed) users" },
+               { "exports",
+                 "list of client connections (NIDs) for this nodemap" },
+               { "fileset", "fileset restrictions for this nodemap" },
+               { "forbid_encryption",
+                 "prevent clients from using encryption" },
+               { "id", "unique identifier for this nodemap" },
+               { "idmap", "identity mapping rules for UID/GID/PROJID translation" },
+               { "map_mode", "identity mapping mode" },
+               { "offset", "idmap range offset for identity translation" },
+               { "ranges", "NID ranges assigned to this nodemap" },
+               { "rbac", "role-based admin control settings" },
+               { "readonly_mount", "force clients to mount read-only" },
+               { "sepol", "SELinux policy for this nodemap" },
+               { "squash_gid", "GID for unmapped users" },
+               { "squash_projid", "project ID for unmapped projects" },
+               { "squash_uid", "UID for unmapped users" },
+               { "trusted_nodemap",
+                 "accept client identities without mapping" },
+       };
+
+       static struct option long_opts[] = {
+               { .val = 'l',   .name = "list",         .has_arg = no_argument },
+               { .val = 'p',   .name = "property",     .has_arg = required_argument },
+               { .val = 'n',   .name = "name",         .has_arg = required_argument },
+               { .val = 'h',   .name = "help",         .has_arg = no_argument },
+               { .name = NULL }
+       };
+
+       while ((c = getopt_long(argc, argv, "hln:p:", long_opts, NULL)) != -1) {
+               switch (c) {
+               case 'n':
+                       nodemap_name = optarg;
+                       break;
+               case 'p':
+                       property = optarg;
+                       break;
+               case 'l':
+                       list = true;
+                       break;
+               case 'h':
+               case '?':
+               default:
+                       return CMD_HELP;
+               }
+       }
+
+       if ((nodemap_name || property || list) && optind < argc) {
+               fprintf(stderr,
+                       "error: using both positional and named arguments is not allowed\n");
+               return CMD_HELP;
+       }
+
+       if ((nodemap_name || property) && list) {
+               fprintf(stderr,
+                       "error: using both --list and --name or --property is not allowed\n");
+               return CMD_HELP;
+       }
+
+       /* Legacy positional arguments are handled here */
+       if (optind < argc) {
+#if LUSTRE_VERSION_CODE > OBD_OCD_VERSION(2, 17, 53, 0)
+               fprintf(stdout,
+                       "Positional parameters are deprecated. Please use --name and --property instead.\n");
+#endif
+               if ((argc - optind) > 1) {
+                       fprintf(stderr,
+                               "error: only one positional parameter allowed\n");
+                       return CMD_HELP;
+               }
+
+               if (strcmp("list", argv[optind]) == 0) {
+                       rc = snprintf(pattern, sizeof(pattern), "nodemap.*");
+                       if (rc < 0 || rc >= sizeof(pattern)) {
+                               fprintf(stderr,
+                                       "error: setting list pattern failed\n");
+                               return -EINVAL;
+                       }
+
+                       rc = jt_lcfg_listparam(3, (char * [3]) { "list_param",
+                                                                "-D",
+                                                                pattern });
+                       return rc;
+               }
+
+               if (strcmp("all", argv[optind]) == 0) {
+                       rc = snprintf(pattern, sizeof(pattern), "nodemap.*.*");
+               } else {
+                       rc = snprintf(pattern, sizeof(pattern), "nodemap.%s.*",
+                                     argv[optind]);
+               }
+
+               if (rc < 0 || rc >= sizeof(pattern)) {
+                       fprintf(stderr,
+                               "error: get_param pattern too long.\n");
+                       return -EINVAL;
+               }
+
+               rc = jt_lcfg_getparam(3, (char * [3]) { "get_param", "-N",
+                                                       pattern });
+               if (rc == -ENOENT) {
+                       fprintf(stderr,
+                               "error: nodemap_info: cannot find nodemap or property %s\n",
+                               argv[optind]);
+               }
+               return rc;
+       }
+
+       /* Handle -l argument here */
+       if (list) {
+               /* Get nodemap active state */
+               rc = cfs_get_param_paths(&param, "nodemap/active");
+               if (rc) {
+                       fprintf(stderr,
+                               "error: cannot get nodemap active param: %s\n",
+                               strerror(errno));
+                       return rc;
+               }
+
+               rc = llapi_param_get_value(param.gl_pathv[0], &active_str,
+                                          &buflen);
+               if (rc || !active_str) {
+                       fprintf(stderr,
+                               "error: cannot get nodemap active state\n");
+                       cfs_free_param_data(&param);
+                       return rc;
+               }
+
+               is_active = (active_str[0] == '1');
+               printf("Global nodemap state:\n\t%s\n",
+                      is_active ? "active" : "inactive");
+               free(active_str);
+               cfs_free_param_data(&param);
+
+               /* list all nodemaps */
+               printf("\nDefined nodemaps:\n");
+               rc = cfs_get_param_paths(&param, "nodemap/*");
+               if (rc) {
+                       fprintf(stderr, "error: cannot get nodemap list: %s\n",
+                               strerror(errno));
+                       return rc;
+               }
+
+               for (i = 0; i < param.gl_pathc; i++) {
+                       /* move to last '/' to skip nodemap prefix */
+                       nodemap_name = strrchr(param.gl_pathv[i], '/');
+                       /* skip '/' and check nodemap isn't empty or "active" */
+                       if (nodemap_name && *(++nodemap_name) &&
+                           strcmp(nodemap_name, "active") != 0)
+                               printf("\t%s\n", nodemap_name);
+               }
+               cfs_free_param_data(&param);
+
+               /* list all nodemap parameters */
+               printf("\nAvailable nodemap parameters:\n");
+               for (i = 0; i < ARRAY_SIZE(param_desc); i++) {
+                       printf("\t%-20s %s\n", param_desc[i].param,
+                              param_desc[i].desc);
+               }
+               return rc;
+       }
+
+       /* Handle -n and -p arguments and default case here */
+       if (nodemap_name && property) {
+               rc = snprintf(pattern, sizeof(pattern), "nodemap.%s.%s",
+                             nodemap_name, property);
+       } else if (nodemap_name) {
+               rc = snprintf(pattern, sizeof(pattern), "nodemap.%s.*",
+                             nodemap_name);
+       } else if (property) {
+               rc = snprintf(pattern, sizeof(pattern), "nodemap.*.%s",
+                             property);
+       } else {
+               rc = snprintf(pattern, sizeof(pattern), "nodemap.active");
+               if (rc < 0 || rc >= sizeof(pattern)) {
+                       fprintf(stderr,
+                               "error: setting active pattern failed\n");
+                       return -EINVAL;
+               }
+
+               rc = jt_lcfg_getparam(2, (char * [2]) { "get_param",
+                                                       pattern });
+               if (rc)
+                       return rc;
+
+               rc = snprintf(pattern, sizeof(pattern), "nodemap.*.*");
+       }
+
+       if (rc < 0 || rc >= sizeof(pattern)) {
+               fprintf(stderr, "error: nodemap name or property too long\n");
+               return -EINVAL;
+       }
+
+       rc = jt_lcfg_getparam(2, (char * [2]) { "get_param", pattern });
+
+       return rc;
+}
+
+/**
  * Add a nodemap's UID/GID/PROJID offset
  *
  * \param      argc            number of args