From b8e3b427f47876ffa65b4198e096219aaa9798d4 Mon Sep 17 00:00:00 2001 From: Marc Vef Date: Wed, 12 Feb 2025 17:16:38 +0100 Subject: [PATCH] LU-18715 utils: Extend lctl nodemap_info with property values 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,] 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 Change-Id: Ic09710233de490d8eb2f50a74e2b7e4765ca4f3d Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/58052 Tested-by: jenkins Tested-by: Maloo Reviewed-by: Sebastien Buisson Reviewed-by: Andreas Dilger Reviewed-by: Oleg Drokin --- lustre/doc/Makefile.am | 2 + lustre/doc/lctl-nodemap-info.8 | 77 +++++++++++++ lustre/doc/lctl-nodemap_info.8 | 1 + lustre/tests/sanity-sec.sh | 77 +++++++++++-- lustre/utils/lctl.c | 4 +- lustre/utils/lustre_cfg.c | 48 --------- lustre/utils/obd.c | 239 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 392 insertions(+), 56 deletions(-) create mode 100644 lustre/doc/lctl-nodemap-info.8 create mode 100644 lustre/doc/lctl-nodemap_info.8 diff --git a/lustre/doc/Makefile.am b/lustre/doc/Makefile.am index b6f7cdf..edf6f7f 100644 --- a/lustre/doc/Makefile.am +++ b/lustre/doc/Makefile.am @@ -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 index 0000000..c8f59a3 --- /dev/null +++ b/lustre/doc/lctl-nodemap-info.8 @@ -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 index 0000000..01b709d --- /dev/null +++ b/lustre/doc/lctl-nodemap_info.8 @@ -0,0 +1 @@ +.so man8/lctl-nodemap-info.8 diff --git a/lustre/tests/sanity-sec.sh b/lustre/tests/sanity-sec.sh index 91271cf..5b1eb4c 100755 --- a/lustre/tests/sanity-sec.sh +++ b/lustre/tests/sanity-sec.sh @@ -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 diff --git a/lustre/utils/lctl.c b/lustre/utils/lctl.c index 67a42ab..f93fa62 100644 --- a/lustre/utils/lctl.c +++ b/lustre/utils/lctl.c @@ -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 */ diff --git a/lustre/utils/lustre_cfg.c b/lustre/utils/lustre_cfg.c index 52e550f..0998b31 100644 --- a/lustre/utils/lustre_cfg.c +++ b/lustre/utils/lustre_cfg.c @@ -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 diff --git a/lustre/utils/obd.c b/lustre/utils/obd.c index 64a763c..ba415ae 100644 --- a/lustre/utils/obd.c +++ b/lustre/utils/obd.c @@ -58,6 +58,7 @@ #include #include #include "obdctl.h" +#include "lstddef.h" #include "lustreapi_internal.h" #include #include @@ -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(¶m, "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(¶m); + 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(¶m); + + /* list all nodemaps */ + printf("\nDefined nodemaps:\n"); + rc = cfs_get_param_paths(¶m, "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(¶m); + + /* 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 -- 1.8.3.1